根本原因是VSCode的editor.detectIndentation会在文件打开时强制覆盖当前编辑器实例的tabSize和insertSpaces值,且不通知插件;插件必须读取editor.options而非全局配置,并结合语言模式与.indentationRules显式校准,才能获取真实生效的缩进参数。

VSCode 插件开发中无法可靠识别缩进,根本原因不在你写的代码里,而在编辑器对 editor.detectIndentation 的接管逻辑和语言模式绑定机制上。
为什么插件里调用 editor.tabSize 或 editor.insertSpaces 常返回错误值
插件运行时读取的编辑器配置,是当前文档实例(TextDocument)对应的 TextEditor 对象所持有的“快照值”,它已被 editor.detectIndentation 覆盖过——哪怕你在 settings.json 里设了 "editor.insertSpaces": true,只要文件开头有 3 个空格 + 1 个 Tab,VSCode 就会在打开时强制把该编辑器实例设为 insertSpaces: false,且不通知插件。
- 插件无法监听
detectIndentation的自动覆盖行为,onDidChangeConfiguration不触发 - 调用
workspace.getConfiguration("editor").get("tabSize")返回的是全局默认值,不是当前文件实际生效值 - 正确做法是:先用
editor.options.tabSize和editor.options.insertSpaces读取当前编辑器实例值,再 fallback 到workspace.getConfiguration("editor") - 若需还原用户原始意图(比如做格式化建议),必须检查项目级
.vscode/settings.json或.editorconfig,不能只信编辑器实例
onTypeFormattingEditProvider 中缩进对齐为何常错位
这个 Provider 是插件实现“输入 { 自动换行缩进”的核心入口,但它的 provideOnTypeFormattingEdits 回调接收的 position 是光标位置,而缩进计算依赖语言服务器返回的 indentationRules —— 这个规则由语言 ID 决定,且会被 editor.detectIndentation 绕过。
- 若用户语言模式是
plaintext,即使文件是.py,VSCode 也不会加载 Python 的indentationRules,Provider 收到的rules为null - Provider 内部调用
computeIndentLevel时,若未显式传入insertSpaces和tabSize,会误用编辑器默认值而非当前文件实际值 - 常见错位现象:输入
if True:后回车,新行缩进 2 空格(JS 默认),而非 4(Python 应有)——本质是语言 ID 错,不是 Provider 逻辑错 - 修复关键:在 Provider 初始化时,监听
onDidChangeActiveTextEditor,并缓存activeEditor?.options,后续所有缩进计算都基于它
自定义语言扩展中 indentationRules 不生效的硬性条件
VSCode 只在满足全部以下条件时才启用你注册的 indentationRules:
- 语言 ID 必须与
package.json中contributes.languages.id完全一致(区分大小写,如mylang≠MyLang) - 该语言未被
.editorconfig或settings.json中的"[mylang]": {...}显式禁用editor.detectIndentation(注意:禁用后 rules 仍加载,但不会被用于自动缩进) - 用户没有手动点击状态栏语言标识切换成其他模式(比如从
mylang切成plaintext) -
indentationRules中的increaseIndentPattern正则必须能匹配整行(^和$必须显式写出),否则 VSCode 视为无效规则
插件里模拟 Shift+Alt+F 格式化时缩进丢失的根源
调用 commands.executeCommand("editor.action.formatDocument") 看似等价,但实际绕过了 VSCode 的“格式化前缩进校准”阶段——该阶段会根据当前文件实际缩进风格(非配置值)临时重写 editor.tabSize 和 editor.insertSpaces,而插件命令不会触发它。
- 结果就是:Prettier 插件收到的
tabWidth是prettier.config.js里的值,不是当前文件正在用的缩进宽度 - 若用户文件混用空格与 Tab,Prettier 默认按
useTabs: false处理,导致部分行缩进被强行统一,破坏原有结构 - 安全做法:不要直接调用格式化命令;改用
languages.registerDocumentFormattingEditProvider,在provideDocumentFormattingEdits中主动读取editor.options并注入到 Prettier options - 更底层的坑:某些语言服务器(如
pylsp)在格式化前会先调用textDocument/rangeFormatting,此时插件若未实现DocumentRangeFormattingEditProvider,就会退回到不带上下文的全量格式化
真正难处理的不是怎么写缩进逻辑,而是如何在 VSCode 已经把缩进变成“上下文快照+语言绑定+检测覆盖”三层嵌套的状态下,让插件读到那个“此刻真实生效”的值——它既不在配置里,也不在文档对象上,而是在编辑器实例的私有选项里,且随时可能被 detectIndentation 覆盖。


















