从零开始配置 vim(8)——文件类型检测
在上一章介绍自动命令的时候,我们提到可以使用 FileType来根据文件类型来触发事件,但是关于文件类型并没有深入的介绍,本篇我们来补充关于文件类型相关的内容,让大家更好的理解,看不懂也没关系,你只需要知道vim能识别各种编程语言的文件并启用事先定义好的配置即可。
事先做几点声明:
- 跳过这篇文章对后面介绍的内容的理解不会有任何障碍,如果你不想看,直接拉到最后看结论即可
- 本篇文章会针对
neovim
的部分代码进行简单的剖析以便深入讲解文件类型。涉及到的neovim
版本为0.7.2
,如果你使用的是更早版本,代码可能会不太一样,但是重点代码应该是一样的 - 里面的代码可能有些小伙伴并不能理解,但是我们只是通过代码来描述它的一些流程,不理解代码能理解这个流程也是OK的,再退一步即使不理解流程,也没关系,毕竟我们只需要知道它有这个功能,不需要知道它的细节,不理解细节完全不会阻碍我们使用并对它进行配置
让我们进入相应的主题吧
文件类型简介
在 vim
中可以使用 filetype plugin indent on
来打开文件类型检测,而在 neovim
中已经默认打开了这些属性,因此我们可以不设置这些。我们可以使用 :filetype
来查看打开的状态。它会返回如下的内容 filetype detection:ON plugin:ON indent:ON
我们发现它包含了三个部分。
上述的设置语句我们可以将它拆分成3个部分:
filetype on
filetype plugin on
filetype indent on
它打开了三个东西,文件类型检测,针对文件类型相关的插件,针对文件类型相关的缩进和隐藏代码块的格式。下面我们依次来介绍这些东西
文件类型检测
filetype on
将打开文件类型检测。如果该项被打开,vim
在初始化的时候会读取脚本 $VIMRUNTIME/filetype.vim
和 $VIMRUNTIME/filetype.lua
的内容。这两个脚本用来识别文件类型。$VIMRUNTIME
是 vim
里面的环境变量与 $MYVIMRC
类似,我们可以通过使用 :echo $VIMRUNTIME
来查看具体的路径,也可以直接在命令模式中将它当做一个路径来使用
我们先来阅读以下 filetype.vim
的内容,在这段脚本中,我们可以发现大量这样的语句
au BufNewFile,BufRead *.cxx,*.c++,*.hh,*.hxx,*.hpp,*.ipp,*.moc,*.tcc,*.inl setf cpp
au BufNewFile,BufRead $VIMRUNTIME/doc/*.txt setf help
au BufNewFile,BufRead .htaccess,*/etc/httpd/*.conf setf apache
au BufNewFile,BufRead */etc/apache2/sites-*/*.com setf apache
结合我们之前学习的自动命令相关的内容可以知道,这些代码会根据文件路径和后缀来自动设置文件类型。
从这写代码中可以看到,vim
也是靠命令来设置文件类型的。使用 :setf
或者 使用 :set filetype=c
或者使用它的简写形式 set ft=c
来设置文件类型
除了根据文件后缀,vim
也可以根据文件内容来判别文件类型。我们进入到 filetype.lua
中可以看到,真正根据文件内容来决定类型是通过文件 script.vim
。该文件中主要使用正则表达式来匹配对应的特征值从而确定该文件类型,例如脚本中有这么一些代码
elseif s:line1 =~# '<?\s*xml.*?>'
set ft=xml
" XHTML (e.g.: PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN")
elseif s:line1 =~# '\<DTD\s\+XHTML\s'
set ft=xhtml
" HTML (e.g.: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN")
" Avoid "doctype html", used by slim.
elseif s:line1 =~? '<!DOCTYPE\s\+html\>'
set ft=html
elseif s:line1 =~? '-\*-.*C++.*-\*-'
set ft=cpp
如果我们的文件无法满足 vim
识别文件类型的要求,也可以在文件中添加注释来帮助 vim
进行识别
例如使用如下注释来使vim
确定它是一个 c
的代码
/* vim: ft=c */
可以在注释中使用 vim: ft=
来设置文件类型。除了设置文件类型,这类注释还是设置像文件是否显示行号、列宽等等信息。更多信息可以查看 :help modeline
。
文件类型插件
在得到文件类型之后,vim
会根据文件类型加载不同的文件插件。它也是一个脚本,该脚本为 $VIMRUMTIME/ftplugin.vim
。打开这个文件,我们需要重点关注这么几句代码
for name in split(s, '\.')
exe 'runtime! ftplugin/' . name . '.vim ftplugin/' . name . '_*.vim ftplugin/' . name . '/*.vim'
" Load lua ftplugins
exe printf('runtime! ftplugin/%s.lua ftplugin/%s_*.lua ftplugin/%s/*.lua', name, name, name)
endfor
其中 s
是一个字符串,它是一个包含了扩展名的文件名,在这里以 .
作为分割,读取它以.分割的所有内容。例如 aaa.bbb
会被分割为 aaa
和 bbb
。然后根据 aaa
和 bbb
来分别执行下面的循环
exe
是用来执行对应文件内代码的语句。后面是一个字符串的拼接,假设当前打开的是一个.py
结尾的文件,对应的这句话就可以拼接为 exe 'runtim! ftplugin/py.vim ftplugin/py__*.vim' ftplugin/py/*.vim
。后面两句拼接的内容相似,只是一个是给vim
脚本用的,一个是给lua
脚本用的。这里我们以 lua脚本为例。
runtime!
你可以理解成 python
的 import
或者 c/c++
中的 #include
,加载文件的路径一个是 $VIMRUNTIME
所在路径,我们可以在 $VIMRUNTIME/ftplugin
目录中找到很多语言预定义的设置,还有一个是配置文件所在的根目录。对于 neovim
来说,这个路径就是 ~/.config/nvim
。
这样我们就明白了,我们可以将对应文件类型的个性化配置放到 ~/.config/nvim/ftplugin
目录中。以 python
为例来说的话。它会加载 ftplugin/py.lua
,ftplugin/py_*.lua
(以py
开头,以 .lua
结尾的文件), ftplugin/py/*.lua
(py 目录下所有的lua文件)。
这样以后针对不同语言的设置完全可以在 ftplugin
中以对应名字命名。从而更好的组织我们的目录结构。
文件类型缩进
文件类型缩进运行我们为不同类型的文件设置不同格式的缩进,例如有的习惯使用4空格缩进,有的习惯使用 2空格或者8空格缩进。定义缩进格式的脚本是 $VIMRUNTIME/indent.vim
。在这个文件中我们又见到了类似的写法
for name in split(s, '\.')
exe 'runtime! indent/' . name . '.vim'
exe 'runtime! indent/' . name . '.lua'
endfor
有了上面讲解的基础,理解这段代码就容易多了,它这里加载的主要是 indent
目录中以后缀命名的缩进文件。但是它默认加载的文件比较少。从代码上看.py
文件如果使用 python.vim
应该是不会被加载的,但是它默认的目录中针对 python
的缩进仍然是以 python.vim
命名,就证明它是可以被加载的。这里我还不理解为什么会被加载。有知道的小伙伴可以在评论区留言,大家一起交流学习。
好了,本章内容就到这里了。前面分析了那么多内容,总结起来就很简单的几点
最后的结论
vim
可以根据文件后缀和文件内容来决定文件类型。如果无法决定也可以使用 set ft
来设置,或者在文件头部添加注释 vim: ft=
来知名类型
我们可以针对不同文件类型进行个性化配置,包括插件和缩进,插件的用户配置文件的路径在 ~/.config/nvim/ftplugin
中,以类型名命名。缩进的配置在 ~/.config/nvim/indent
目录中,以类型名命名。