从零开始配置 vim(8)——文件类型检测

在上一章介绍自动命令的时候,我们提到可以使用 FileType来根据文件类型来触发事件,但是关于文件类型并没有深入的介绍,本篇我们来补充关于文件类型相关的内容,让大家更好的理解,看不懂也没关系,你只需要知道vim能识别各种编程语言的文件并启用事先定义好的配置即可。

事先做几点声明:

  1. 跳过这篇文章对后面介绍的内容的理解不会有任何障碍,如果你不想看,直接拉到最后看结论即可
  2. 本篇文章会针对 neovim 的部分代码进行简单的剖析以便深入讲解文件类型。涉及到的 neovim 版本为 0.7.2,如果你使用的是更早版本,代码可能会不太一样,但是重点代码应该是一样的
  3. 里面的代码可能有些小伙伴并不能理解,但是我们只是通过代码来描述它的一些流程,不理解代码能理解这个流程也是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 的内容。这两个脚本用来识别文件类型。$VIMRUNTIMEvim 里面的环境变量与 $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 会被分割为 aaabbb。然后根据 aaabbb 来分别执行下面的循环

exe 是用来执行对应文件内代码的语句。后面是一个字符串的拼接,假设当前打开的是一个.py 结尾的文件,对应的这句话就可以拼接为 exe 'runtim! ftplugin/py.vim ftplugin/py__*.vim' ftplugin/py/*.vim 。后面两句拼接的内容相似,只是一个是给vim 脚本用的,一个是给 lua 脚本用的。这里我们以 lua脚本为例。

runtime! 你可以理解成 pythonimport 或者 c/c++ 中的 #include,加载文件的路径一个是 $VIMRUNTIME 所在路径,我们可以在 $VIMRUNTIME/ftplugin 目录中找到很多语言预定义的设置,还有一个是配置文件所在的根目录。对于 neovim 来说,这个路径就是 ~/.config/nvim

这样我们就明白了,我们可以将对应文件类型的个性化配置放到 ~/.config/nvim/ftplugin 目录中。以 python 为例来说的话。它会加载 ftplugin/py.luaftplugin/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 目录中,以类型名命名。

posted @ 2022-08-05 10:06  masimaro  阅读(279)  评论(0编辑  收藏  举报