5月27日 14:01

Vim filetype 文件类型检测怎么配置?自定义语法怎么写?

为什么你的 Vim 没有语法高亮

打开一个 .py 文件,满屏灰白;编辑 .js,缩进全靠手敲——大概率是 filetype 没开。Vim 的文件类型检测是语法高亮、缩进、文件类型插件这三套机制的地基,没它后面全白搭。

filetype 三件套:on / plugin / indent

在 vimrc 里加上这一行就够了:

vim
filetype plugin indent on

它实际等价于三条独立命令:

  • filetype on —— 启用文件类型检测,Vim 会根据文件名和内容自动设置 &filetype 选项
  • filetype plugin on —— 检测到类型后,加载对应的 ftplugin 脚本($VIMRUNTIME/ftplugin/<type>.vim
  • filetype indent on —— 加载对应的缩进脚本($VIMRUNTIME/indent/<type>.vim

想看当前状态,直接输入:

vim
:filetype

输出类似 filetype detection:ON plugin:ON indent:OFF,一目了然。

很多人的 vimrc 里同时写了 syntax onfiletype on,其实 syntax on 会自动安装 filetype 检测,重复写不算错但多余。如果只需要语法高亮不需要插件和缩进,单独 syntax on 就行;需要完整功能就用 filetype plugin indent onsyntax enableenable 不会覆盖已有配色方案,比 on 更温和)。

检测原理:文件名优先,内容兜底

Vim 检测文件类型分两步走:

  1. 文件名匹配 —— 读取 $VIMRUNTIME/filetype.vim(超过 2300 行),按扩展名和文件名模式匹配。.c 是 c,Makefile 是 make,.tsx 是 typescriptreact,覆盖了绝大部分常见文件。

  2. 内容检测 —— 文件名没匹配上时,Vim 调用 $VIMRUNTIME/scripts.vim,检查文件头部的 shebang、DOCTYPE、特定关键词等。比如第一行是 #!/usr/bin/env python3,即使文件没有 .py 后缀也能识别为 Python。

如果两步都失败了,&filetype 保持空值。这时候可以手动设置:

vim
:set filetype=python

或者在文件里加 modeline(写在文件末尾的注释中):

vim
# vim: set filetype=python:

ftplugin 目录:给特定文件类型加配置

ftplugin 是 filetype 和编辑器设置之间的桥梁。当你打开一个 Python 文件且 filetype plugin on 已启用,Vim 自动加载 ftplugin/python.vim

系统自带的 ftplugin 在 $VIMRUNTIME/ftplugin/ 下,但你应该把自定义的放在用户目录:

shell
~/.vim/ftplugin/python.vim " Linux/macOS ~/vimfiles/ftplugin/python.vim " Windows

一个典型的 ftplugin 长这样:

vim
" ftplugin/python.vim setlocal expandtab setlocal shiftwidth=4 setlocal softtabstop=4 setlocal textwidth=88 setlocal commentstring=#\ %s

注意用 setlocal 而非 set,这样设置只作用于当前 buffer,不会污染其他文件类型。

如果你想覆盖系统 ftplugin 的某些设置而非完全替换,用 after/ 目录:

shell
~/.vim/after/ftplugin/python.vim

Vim 加载顺序是:系统 ftplugin → 用户 ftplugin → after/ftplugin,后面可以覆盖前面的设置。

syntax 目录:自定义语法高亮

语法高亮文件放在 ~/.vim/syntax/ 下,文件名就是 filetype 值。比如为一种叫 mylang 的自定义语言写高亮:

vim
" syntax/mylang.vim syntax keyword mylangKeywords if else while for return syntax keyword mylangTodos TODO FIXME XXX syntax match mylangNumber "\<\d\+\>" syntax region mylangString start=+"+ end=+"+ highlight default link mylangKeywords Statement highlight default link mylangTodos Todo highlight default link mylangNumber Constant highlight default link mylangString String

几个要点:

  • highlight default link 而非 highlight link,前者不会覆盖用户配色方案里的定义,后者会强制覆盖
  • 语法组按语义命名(Statement、Constant、Todo),不要按颜色命名,这样换配色方案时不会出问题
  • 如果不想从零写,可以用 syn include 把现有语法定义嵌入进来,比如在模板文件里同时高亮 HTML 和 JavaScript

indent 目录:控制缩进行为

缩进文件放在 ~/.vim/indent/ 下,同样以 filetype 命名。系统自带的缩进脚本已经覆盖了主流语言,你通常不需要自己写。

但如果你想微调,可以在 ftplugin 里用 setlocal 调整:

vim
" ftplugin/go.vim setlocal noexpandtab setlocal shiftwidth=4 setlocal tabstop=4

或者写一个完整的 indent 脚本放在 ~/.vim/indent/ 下,利用 indentexpr 实现复杂的缩进逻辑。Vim 自带的 indent/python.vim 就是很好的参考。

自定义文件类型检测:ftdetect 目录

如果你在写一种 Vim 不认识的文件类型,需要告诉 Vim 怎么识别它。创建 ~/.vim/ftdetect/ 目录,在里面放检测脚本:

vim
" ftdetect/mylang.vim autocmd BufNewFile,BufRead *.mylang setfiletype mylang

setfiletype 而不是 set filetype=,区别在于 setfiletype 只在 filetype 尚未设置时生效,不会覆盖已有的检测结果。

更复杂的检测可以检查文件内容:

vim
" ftdetect/mylang.vim autocmd BufNewFile,BufRead * if getline(1) =~# '^#!.*/mylang' \ | setfiletype mylang \ | endif

推荐用 augroup 包裹以避免重复注册:

vim
augroup filetypedetect_mylang autocmd! autocmd BufNewFile,BufRead *.mylang setfiletype mylang augroup END

目录结构速查

把自定义的文件类型相关文件整理到一起,标准目录结构如下:

shell
~/.vim/ ├── ftdetect/ │ └── mylang.vim " 文件类型检测规则 ├── ftplugin/ │ └── mylang.vim " 文件类型专属设置 ├── indent/ │ └── mylang.vim " 缩进规则 ├── syntax/ │ └── mylang.vim " 语法高亮定义 └── after/ └── ftplugin/ └── python.vim " 覆盖/补充系统 ftplugin

检测到 filetype → 加载 syntax → 加载 ftplugin → 加载 indent,这是 Vim 处理一个文件时自动执行的完整链路。

常见问题

检测对了但没高亮? 检查 :syntax 是否开启,以及配色方案是否支持你用的语法组。

改了 ftdetect 不生效? 改完重启 Vim,或者对当前 buffer 执行 :filetype detect 强制重新检测。

想临时禁用某个 ftplugin? 在 vimrc 里设 let g:did_load_filetypes = 1 可以阻止系统 ftplugin 加载,但更精准的做法是针对单个类型设 let b:did_ftplugin = 1 阻止加载,然后自己写替代脚本。

Vim 的文件类型系统看起来层级多,但拆开看每层职责清晰:ftdetect 负责识别,syntax 负责着色,ftplugin 负责行为,indent 负责格式。把它们配齐了,编辑体验和现代 IDE 比也不差什么。

标签:Vim