vim 多文件类型自动补全方案

概述

多文件类型也可以叫混合文件类型(multiple filetype), 比如 HTML 里面写 CSS, JS 代码,虽然不提倡,但偶尔也有这个需求。

根据常识,我们知道,往往针对不同的文本类型,vim 或其插件,会出发不同的事件,例如特定的语法高亮、特定的语法检测、特定的自动补全方案等。
几乎每个 vim user 的 vimrc 中都会有如下配置:

1
filetype plugin indent on

其中的 filetype on 表示:

每次一个新的或者已经存在的文件被编辑时,Vim 会试图识别文件的类型,并设置’filetype’ 选项。同时,也触发 FileType 事件。该事件可以设置语法高亮,特定选项,等等。———— 出自 《vim 参考手册》,译者: Willis、tocer

那么是否能直接通过修改文件的 filetype 值实现多文件类型的自动补全呢? 答案是否定的,因为这将引起语法检测,语法高亮等一系列的变化,这是我们所不能接受的。

github 上已经有人大致实现了这个功能,主要通过 deoplete.nvimcontext_filetype.vim两个插件。都是一个作者写的,前者提供一个可扩展的 vim 自动补全框架,后者支持前者实现多文件类型自动补全。

实际操作

修改 .vimrc 文件如下:
(下面提供的插件安装方式都是基于 vim-plug 的。不要再用 vundle 了,因为它目前不支持多线程,下载插件很慢。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
if has('nvim')
Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' }
else
Plug 'Shougo/deoplete.nvim'
Plug 'roxma/nvim-yarp'
Plug 'roxma/vim-hug-neovim-rpc'
endif

Plug 'Shougo/context_filetype.vim'

" context_filetype.vim 插件的配置
if !exists('g:context_filetype#same_filetypes')
let g:context_filetype#filetypes = {}
endif
let g:context_filetype#filetypes.svelte = [
\ {'filetype' :'javascript', 'start': '<script>', 'end': '</script>'},
\ {'filetype' :'css', 'start': '<style>', 'end': '</style>'},
\ ]

" deoplete.nvim 插件的配置
let g:deoplete#enable_at_startup = 1
autocmd InsertLeave,CompleteDone * if pumvisible() == 0 | pclose | endif

call deoplete#custom#var('omni', 'functions', {
\ 'css': ['csscomplete#CompleteCSS'],
\ 'javascript': ['javascriptcomplete#CompleteJS'],
\ 'html': ['htmlcomplete#CompleteTags'],
\})
call deoplete#custom#var('omni', 'input_patterns', {
\ 'javascript': '[^. *\t]\.\w*',
\ })
" 支持 <tab>
inoremap <silent><expr> <TAB> pumvisible() ? "\<C-n>" :
\ <SID>check_back_space() ? "\<TAB>" :
\ deoplete#mappings#manual_complete()
function! s:check_back_space() abort "{{{
let col = col('.') - 1
return !col || getline('.')[col - 1] =~ '\s'
endfunction"}}}

执行 :PlugInstall

代码分析

作为一个 vimscript 还没入门的新手,看了大半天代码,了解如下:

context_filetype.vim 插件,通过 context_filetype#get() 入口函数在光标处于<style> </style>范围内时,返回如下格式的字典:

1
{'range': [[9, 1], [12, 4]], 'filetype': 'css'}

该字典含义为 {范围:[ 标签起始位置,标签结束位置 ],文件类型 }

但是我没有明白 deoplete.nvim 是如何使用这个接口的。

作者让我看 context.py 这段代码,我只能弱弱的回复一个”Thanks”。