1. 保存一次diff 全红先搞清楚问题从哪来WebStorm、IntelliJ IDEA 这类 JetBrains 系 IDE保存代码时自动去掉行尾空格、在文件末尾插入空白行这事儿看着小真碰上能烦死人。先说我自己的经历。之前接手一个老项目改一行日志输出保存之后顺手 git diff 看一眼差点以为电脑被黑了——两百多行全标了修改。逐行看下来只有一行是我的改动其余全是行尾空格被删、文件末尾多了个空行。后来一问团队里好几个人用的都是 JetBrains 系 IDE默认清理行为加上项目里一个 .editorconfig谁保存谁洗一遍。这个问题的本质是JetBrains 系 IDE 在保存时会默认做两件好事——清理行尾空格、确保文件末尾是换行符甚至追加一个空行。单独看每一项都挺合理毕竟代码整洁是好习惯。但放到实际项目里尤其是历史遗留项目、生成代码、测试快照、特定格式的配置文件这就变成了灾难Git 历史被无关 diff 污染、review 时真实改动被淹没还有可能把对字节敏感的文件改坏。更坑的是这类行为不是由一个开关控制的。你可能关了保存时清理空格又发现格式化时照样给你删你关了 IDE 自带的项目里的 .editorconfig 又跳出来接管你以为查完了插件里的 Save Actions 又补一刀。本文就围绕这两件事——自动去空格、末尾加空白行——把 JetBrains 系 IDE 里所有可能控制它的开关都过一遍最后给一套完整排查链路。以下内容在 WebStorm 和 IntelliJ IDEA 上实测有效其他 JetBrains 系 IDEPyCharm、PhpStorm、RubyMine、Goland界面路径基本一致。Windows/Linux 的设置入口是 File → SettingsmacOS 是 IDE 名称 → Preferences快捷键分别是 CtrlAltS 和 Cmd,下文统一用Settings指代。2. 保存时自动去空格On Save 设置的位置与操作先说自动去掉空格。绝大多数情况下大家遇到的其实是行尾空格被自动删除——也就是一行代码末尾那些看不见的多余空白字符。如果你遇到的是运算符两侧空格被删、花括号里面空格被删这种格式化级变化那是另一套逻辑我会在第 5 章里讲这里先解决保存时删空格的问题。2.1 新版 IDE 的路径Editor → General → On SaveJetBrains IDE 2020.1 之后的版本这个选项统一放在Settings → Editor → General右侧顶部就是 On Save 区域。On Save 区域第一项是下拉框名字叫Remove trailing spaces on:中文界面可能显示删除尾随空格。下拉选项在不同版本有细微差异常见的是选项行为适合场景Save有的版本叫 All lines保存时清理所有行尾空格新项目、团队统一整洁风格Modified lines只清理当前文件中被修改过的行想保持整洁又不想全文件洗一遍None有的版本叫 Never不主动清理历史遗留项目、格式敏感文件想彻底关掉就选 None。注意一个容易踩的坑如果你选的是 Modified lines你亲手改过的行在保存时仍然会被清理只是别人写的那部分老代码不被碰而已。所以真正的完全不删只有 None。2.2 旧版本的位置和其他入口2020 年之前的旧版 IDE这个选项在 Settings → Editor → General → Other其他区域名字叫Strip trailing spaces on save。如果你在 On Save 区域翻不到就去 Other 区域找。还有一个通用技巧Settings 窗口左上角有搜索框直接搜 trailing 或者 行尾空格不管版本怎么改都能一键定位到对应选项。这个技巧对后面要讲的末尾空行设置同样好用。我个人的建议是如果你只是被行尾空格清理搞得头疼先别急着全关。可以选 Modified lines 过渡一下既不影响自己新写代码的整洁又不会把老文件整片洗一遍。但如果你正在处理测试快照、Markdown 文件、或者任何空格有意义的文件直接上 None。Markdown 里的坑特别值得说一句Markdown 语法中一行结尾的两个空格表示强制换行。IDE 一旦把行尾空格删掉整个排版就塌了这是很多写文档的人没意识到的原因。2.3 顺手查一下保存时自动保存类行为有些版本里On Save 区域还有别的选项比如把代码变更后自动保存相关的东西打开会导致你刚切走窗口就触发保存清理行为也被连带触发。如果你发现我没按 CtrlS 文件也被改了就去 Settings → Appearance Behavior → System Settings → 找 Automatic Save 相关设置把关闭其他应用时自动保存、切换窗口时自动保存这些选项理一遍。这类自动保存和自动清理叠加在一起问题表现会更明显。3. 文件末尾的空白行两个独立开关都要查第二个问题在最后插入空白行。这里有个很大的误区很多人以为只要把换行符相关设置关了就行。实际上 JetBrains IDE 里有两个相互独立的设置一个控制末尾是否再加一个空行另一个控制末尾是否有换行符两个都要查。3.1 真正产生空白行的开关打开 Settings → Editor → Code Style在右侧选择你正在使用的语言比如 JavaScript在对应样式的 General 或其他标签里往下滚动可以找到一个复选框Add a blank line at the end of the file中文界面大概是在文件末尾添加空白行。这个选项勾选时保存或格式化后文件最后一行内容下面会多出一个真正的空行。你在编辑器里看到的效果就是光标停在末行下面手动删掉空行保存一次它又回来了。不同语言、不同版本的 IDE 放置位置可能不太一样但 Settings 右上角搜索框里搜 blank line 就能快速定位。想根治末尾空行第一件事就是把这个选项取消勾选。3.2 另一个容易混淆的开关Ensure single newline再回到 Settings → Editor → General → On Save 区域还有一个复选框Ensure single newline at end of file when saving旧版本叫 Ensure line feed at file end on Save 之类。这个选项的意思是文件末尾必须有一个换行符。如果一个文件最后一行没有换行符很多配置文件、生成代码、老式文本都有这种情况保存时 IDE 会帮你补上。补一个换行符和加一个空行在显示上很容易让人混淆。我拿一个最直观的例子说明假设文件内容是 var a 1EOF 表示文件结束末尾没有换行符var a 1[EOF]末尾有换行符Ensure single newline 的效果var a 1\n[EOF]末尾多一个空行Add a blank line 的效果var a 1\n\n[EOF]很多人看到的多了一行其实是第三种情况——真正多了一个空行。主力元凶是 Add a blank line at the end of the file其次才是 Ensure single newline。所以两个都要看别只关一个关完发现还有一半问题没解决。提示如果你要处理的文件对末尾字节有严格要求比如哈希校验、特定脚本解析、测试快照这两个都必须关。这类文件建议再配合第 4 章的 .editorconfig 一起处理否则项目配置一覆盖你这边白改。3.3 手动格式化也会触发别只看保存还有一个容易忽略的细节如果你不是保存时加空行而是按了格式化快捷键Windows/Linux 的 CtrlAltLmacOS 的 OptionCmdL之后出现空行那说明 Code Style 里的规则在起作用。Add a blank line at the end of the file 这个复选框不仅影响保存也影响手动格式化。换句话说只要它没取消无论保存还是格式化文件末尾都会被照顾到。另外格式化时去掉空格也是同一套 Code Style 规则在管。比如你在 Code Style 里把运算符两侧的默认空格配置改了格式化后let a 1就可能变成let a1。遇到这种情况别去 On Save 里找开关应该回到 Code Style 里检查每一项空格规则。这也是为什么很多人说我关了自动整理还是有格式变化——因为触发源根本不在 On Save而在格式化规则里。4. 优先级最高的幕后黑手.editorconfig 在接管这是整篇文章最容易被漏掉的一环也是最容易让前面所有设置形同虚设的一环。JetBrains IDE 对 .editorconfig 的支持是内置的而且优先级非常高。官方文档写得很明确当项目中存在 .editorconfig 文件时它声明的代码风格属性会覆盖 IDE 设置。你可以在 Settings 里把删除行尾空格选了 None但只要 .editorconfig 里写了 trim_trailing_whitespace true保存时照样给你删。4.1 一眼认出罪魁祸首配置常见的引发本项目问题的配置长这样root true [*] charset utf-8 indent_style space indent_size 2 trim_trailing_whitespace true insert_final_newline true这两行正好对应本文标题里的两个现象trim_trailing_whitespace true保存时清理所有行尾空格等价于把 IDE 的 Remove trailing spaces 强制设为 Save。insert_final_newline true保存时确保文件末尾有换行符等价于强制打开 Ensure single newline。如果你的项目 .editorconfig 里有这两条那你不管在 IDE 设置里怎么折腾都没用。正确做法是打开项目根目录下的 .editorconfig改成trim_trailing_whitespace false insert_final_newline false或者干脆删掉这两行。改完保存IDE 会立即按新配置生效连重启都不用。4.2 怎么确认 EditorConfig 是否接管了当前文件最直观的方法看 IDE 右下角状态栏。有 .editorconfig 生效时状态栏会出现一个 EditorConfig 图标长得像一个小文件加一个角标点击它会列出当前文件命中的所有属性包括值是从哪个路径来的。第二个方法Settings → Editor → Code Style页面顶部通常会有提示文字告诉你当前是否有 EditorConfig 接管、接管了哪些属性。如果你在 IDE 里改了设置但页面提示 EditorConfig 正在覆盖那就没必要继续在 Settings 里浪费时间直接去改 .editorconfig。4.3 向上递归查找和团队协作的坑再说一个只有老手才知道的细节.editorconfig 的查找是向上递归的。项目根目录没有不代表父目录没有。如果某个上级目录里有一个 .editorconfig而这个文件里没有 root true或者 root 没设为 trueIDE 会继续往上翻把上级的规则也套用到你的项目上。遇到明明项目里没有 .editorconfig 但还是被接管的怪事就去上级目录翻一翻。团队协作方面.editorconfig 大多是提交到版本库里的你改了之后别人 pull 下来也会跟着变。这其实是好事——与其让每个人在 IDE 设置里打架不如在项目配置里统一口径。如果项目只是你个人维护那就更没顾虑了直接改。提示如果团队确实需要 .editorconfig 来统一缩进和字符集但你就是不想被删空格/加换行打扰可以在 Settings → Editor → Code Style 页面取消勾选右上角的 Enable EditorConfig support。这个操作会让 IDE 完全忽略所有 .editorconfig 文件坏处是你本地格式可能和队友不一致只建议在极端情况下用。5. 隐藏来源Actions on Save、Save Actions 插件、Prettier 与 ESLint如果你把 IDE 自带设置和 .editorconfig 都查完了问题还在那就要考虑保存时还触发了别的动作。这是最容易被忽略的第三层。5.1 Tools → Actions on Save 里的 Reformat code新版 JetBrains IDE 把保存时附加操作统一放在 Settings → Tools → Actions on Save。这里面全是复选框常见的有Reformat code保存时重新格式化Optimize imports保存时整理 importRearrange code保存时重排代码结构按 Code Style 里的配置顺序Run code cleanup保存时清理代码Run external tool / Run eslint --fix on save 等如果 Reformat code 被勾选了保存时就会执行一次完整格式化。这时候问题就不只是行尾空格这么简单了它会按照 Code Style 的完整规则去调整空格、括号、对齐任何与规则不符的都会被改掉。你说你明明关了 On Save 里的清理选项保存后空格还是变了十有八九就是这个 Reformat code 勾着。处理方法把 Reformat code 取消勾选改成需要时手动按格式化快捷键。如果团队确实要求保存即格式化那就得确保 Code Style 规则符合大家预期而不是让默认规则在那乱改。5.2 Save Actions 系列插件社区里有一类插件干的事情和 IDE 自带清理一模一样名字就叫 Save Actions还有改进版 Save Actions Rewrite。装上之后默认行为就包含保存时删除行尾空格和保存时确保文件末尾有换行有些版本还会自带在文件末尾添加空行的选项。如果你装过这类插件去 Settings → Other Settings → Save Actions或者插件名对应的设置项里把 Remove trailing whitespace 和 Ensure newline at end of file 相关的选项关掉。插件设置和 IDE 会叠加作用表现就是怎么关都关不掉因为有两套开关同时在干活。5.3 Prettier 的 Run on save 和 ESLint 的 fix on saveWebStorm 对 Prettier 有深度集成。Settings → Languages Frameworks → JavaScript → Prettier里面有一个 Run on save保存时运行选项。勾选后保存文件就会调用 Prettier 做一次全量格式化。Prettier 是个立场非常坚定的工具它默认删除所有行尾空格并且保证文件末尾有换行符而且这些行为在 Prettier 的配置里没有关闭选项属于设计上就不可关闭的。因此只要 Prettier 在保存时跑一次你的文件必然被统一一遍。处理方法把 Run on save 关掉改成只在主动格式化时才触发On Reformat Code action。如果项目全团队统一用 Prettier保持 Run on save 其实也没问题diff 不会乱但如果你在维护历史代码建议关掉。另一个隐藏来源是 ESLint。WebStorm 里可以在 Settings → Languages Frameworks → JavaScript → Code Quality Tools → ESLint 中开启 Run eslint --fix on save保存时对当前文件执行 fix。如果项目 ESLint 配置里恰好有eol-last要求文件末尾换行或no-trailing-spaces禁止行尾空格这类规则--fix 就会帮你删空格、加换行。这个来源很隐蔽因为它看起来是IDE 在保存时改文件实际上是 ESLint 规则在发挥作用。验证方法在命令行对同一个文件执行npx eslint --fix 文件名如果文件被改了说明是 ESLint 的问题处理方向要么改 ESLint 规则要么关掉 IDE 里的 fix on save。6. VS Code 用户如果在看这篇对应设置一览JetBrains 文章下面总会有人问 VS Code 怎么办这次直接把对照写好。VS Code 里对应行为由这几个配置项控制在 settings.json 里改{ editor.formatOnSave: false, files.trimTrailingWhitespace: false, files.insertFinalNewline: false }现象JetBrains 设置VS Code 设置保存时自动格式化Tools → Actions on Save → Reformat codeeditor.formatOnSave保存时删除行尾空格Editor → General → On Save → Remove trailing spacesfiles.trimTrailingWhitespace保存时确保末尾换行Editor → General → On Save → Ensure single newlinefiles.insertFinalNewlineVS Code 还多一个 files.autoSave 的概念。如果设置了 autoSave 为 onFocusChange 或 afterDelay保存这件事不需要你手动按 CtrlS 就会发生配合 formatOnSave 就会在你不注意的时候偷偷改格式。排查时别忘了把它也考虑进去。另外VS Code 也支持 .editorconfig 插件。如果你装了 EditorConfig 插件.editorconfig 里的 trim_trailing_whitespace 和 insert_final_newline 同样优先于 settings.json 生效。处理方式和 JetBrains 一致改 .editorconfig或者停用插件。VS Code 有一点比 JetBrains 方便可以按文件类型设置。比如只对 JavaScript 关闭格式化{ [javascript]: { editor.formatOnSave: false } }JetBrains 也可以在 Editor → Code Style → 具体语言比如 JavaScript里按语言微调但 On Save 区域的全局开关没有按语言拆分这一点确实不如 VS Code 灵活。如果你的项目里只是 Markdown 文件被影响在 JetBrains 里可以先改 Code Style → Markdown 的相关选项但最省事的还是找到全局开关一关到底。7. 完整排查链路从复现到定位真凶最后给出一套完整的排查步骤。我平时处理这类问题基本就按这个顺序在 WebStorm 和 IntelliJ IDEA 上都验证有效。复现打开一个被污染过的文件手动在任意一行末尾加几个空格按 CtrlS 保存。如果空格被删问题出在保存链路继续往下查。如果空格还在说明不是保存时清理而是格式化时清理直接跳到第 5 步查 Actions on Save 和格式化规则。检查 EditorConfig看状态栏有没有 EditorConfig 图标点开看当前文件生效的属性。有 trim_trailing_whitespace true改 .editorconfig 为 false或者删掉该属性。有 insert_final_newline true同理改 false 或删除。如果项目里找不到 .editorconfig去上级目录找注意 root true 的作用范围。检查 IDE 自带设置Settings → Editor → General → On SaveRemove trailing spaces on 选择 None。Settings → Editor → General → On SaveEnsure single newline 取消勾选如果确实不想被加换行。Settings → Editor → Code Style → General或对应语言Add a blank line at the end of the file 取消勾选。检查 Actions on SaveSettings → Tools → Actions on Save取消 Reformat code 等意外动作。检查插件Settings → Plugins看是否装了 Save Actions / Save Actions Rewrite去对应设置页关掉相关选项。Settings → Languages Frameworks → JavaScript → Prettier看 Run on save 是否打开。Settings → Languages Frameworks → JavaScript → Code Quality Tools → ESLint看 Run eslint --fix on save 是否打开。最小项目验证新建一个空目录新建一个 .txt 或 .js 文件人为制造行尾空格和末行无换行保存观察。如果新项目一切正常说明问题在项目级配置.editorconfig 或团队的共享配置如果新项目也有问题说明是 IDE 全局设置或插件的问题。同步问题如果你用了 JetBrains 的 Settings Sync设置同步或从云端恢复过设置检查是不是同步回来的旧设置把你之前的修改覆盖了。这个问题很多人遇到——明明关了几次过几天又自己回来多半不是 bug而是同步把旧的配置拉回来了。排查完之后还有一个实用的 git 技巧如果你已经被大量空白差异折磨过了review 之前可以用 git diff -w 忽略所有空白差异查历史时用 git blame -w 忽略空白相关改动。这对老项目代码审查特别有用能让你第一时间看到真正改了什么而不是被空格刷屏。我个人在实际操作中的体会是这类问题最怕的不是关不掉而是今天关了明天又回来。所以排查时一定要把 .editorconfig 和 Settings Sync 这两项也纳入检查范围它们才是那个看不见的手。修好之后建议顺手在团队文档里记一笔项目采用什么规则、是否需要保存时清理、哪个文件是权威配置。不然换个人、换台电脑同样的坑还得重踩一遍。