团队混用 VS Code 和 JetBrains,EditorConfig 能消掉多少空白 diff?

同一仓库里有人用空格、有人用 Tab,保存时有的编辑器吞掉行尾空白、有的留下一串。换行符在 Linux 和 Windows 贡献者之间来回横跳。Pull Request 里一半噪音是「只改了空白」。想在进语言级 linter 之前卡住最低共识,EditorConfig 官网 给的方案是一份可进版本库的 .editorconfig,加上各编辑器的原生支持或插件去读它。

这份文件实际管什么

官网把 EditorConfig 拆成两块:定义编码风格的文件格式,以及让编辑器遵守这份格式的插件集合。文件本身可读,也适合跟 Git 一起走。支持的属性里,和日常最相关的几项是:

  • indent_style:tab 或 space
  • indent_size:每一级缩进占几列。设成 tab 时会跟 tab_width 走
  • tab_width:一个 Tab 显示成几列,默认常跟 indent_size 相同
  • end_of_line:lf / cr / crlf
  • charset:如 utf-8、utf-8-bom 等
  • trim_trailing_whitespace、insert_final_newline:行尾空白和文件末换行

官网还提醒:不是每个插件都实现了全部属性。某项没写时,一般继续用编辑器自己的设置。需要撤销上层文件里已经生效的项,可以用 unset。属性名和值解析时不分大小写。官网也说,有些项故意留空往往更好:例如 indent_style = tab 时可以不写死 indent_size,让阅读者按自己舒服的宽度看。项目里若换行风格还没共识,不如先别写 end_of_line,避免一份配置制造另一场争论。

查找顺序和 root = true

打开一个文件时,插件会从该文件所在目录开始,一路向父目录找名为 .editorconfig(全小写)的文件,直到文件系统根,或遇到写了 root = true 的那份为止。多份文件按从上到下读取,后读到的、更近的规则优先。实操里仓库根放一份 root = true,按 [*]、[*.py]、[*.{js,ts}] 这类 glob 分段,比在每个子项目复制半份残缺配置省事。glob 语法接近 gitignore:* 不跨路径分隔符,** 可以,还有 {a,b}、{1..9} 这类写法,官网表格写得很全。单体仓里若某个子包要完全另一套缩进,可以在子目录再放一份并设 root = true,截断继续向上合并。这能隔离实验目录,也会让「生效的是哪份」变难查,改之前先确认路径。

它不是完整 linter

EditorConfig 管的是「文件在编辑器里怎么排版」这一层:缩进、字符集、换行。它不替你查未使用变量、类型错误、导入顺序,也不替代语言生态里的格式化工具。站内已经写过 Biome 和 Ruff 时,可以把分工记成一句:.editorconfig 先对齐编辑器保存时的底线。Biome / Ruff 再在 CI 和保存钩子里做语言相关的 format / lint。三套叠在一起时,尽量别让缩进数字在三处写成三个值,否则保存一次、format 一次,diff 又开始抖。Makefile 一类「必须用 Tab」的文件,官网示例是单独开 [Makefile] 段设 indent_style = tab,别和 JS / Python 的空格规则糊在同一段。

落地时两点小坑

Windows 资源管理器里直接新建 .editorconfig 有时别扭,官网写过用 .editorconfig.(末尾多一个点)让资源管理器自动改名的办法。另外,部分编辑器要装插件才认这份文件,另一批(含不少 JetBrains 系、Vim、较新的 VS Code 发行版等)官网列在「No Plugin Necessary」。以你本机编辑器文档为准,装完用一个故意写错缩进的文件保存一次,看有没有被纠正,比只信列表可靠。文件本身建议 UTF-8,行分隔用 LF 或 CRLF 均可,注释用 # 或 ; 且单独成行。

团队若已经强制 Prettier / 语言自带 formatter,EditorConfig 仍然可以留:它给「还没跑 format 命令、只是在编辑器里敲」的人一个默认,也给 GitHub / GitLab 网页端等声明了支持的场景一点一致性。你仓库根目录现在有 .editorconfig 吗,还是只靠编辑器个人设置硬扛?