如何通过cinoptions和cinkeys实现仅在switch语句的case行触发:0缩进规则?
我之前也碰到过类似的困扰——Vim会把输入到一半的namespace:误判为goto标签,触发标签缩进规则导致缩进跳变,输入第二个冒号后才恢复正常。下面是几个针对性的解决方案,你可以根据自己的使用习惯选择:
方案一:移除冒号的实时缩进触发(最简易)
你的cinkeys配置里包含了:,这意味着输入冒号时Vim会立即重新计算当前行的缩进。我们把它去掉,就能避免输入第一个冒号时的意外跳变:
set cinkeys=0{,0},0),0#,!^F,o,O,e
修改后,输入冒号时不会立刻触发缩进调整,你可以先完整输入namespace::,再按回车或其他触发缩进的字符(比如大括号),此时Vim会正确识别这是命名空间作用域解析,保持当前代码块的缩进。
这个改动不会影响switch语句的case缩进——输入case 4:后,按回车或其他触发字符时,Vim依然会按照你的cinoptions配置正确计算缩进。
方案二:调整标签缩进规则(折中方案)
如果你想保留冒号的实时缩进触发,但不想让非标签的namespace:跳转到0缩进,可以修改cinoptions中的l选项(标签缩进规则),让标签和当前代码块保持相同缩进:
set cinoptions=:0,g0,(0,Ws,l1
这里的l1表示标签缩进为1个shiftwidth(和当前代码块的缩进一致)。这样即使namespace:被误判为标签,也不会突然顶格,而是保持和if/for块内相同的缩进。缺点是真正的goto标签也会变成缩进状态,如果你平时很少用goto,这个方案很省心。
方案三:自定义缩进表达式(精准控制)
如果需要严格区分case行、goto标签和命名空间行,可以用自定义的indentexpr来覆盖默认行为,精准控制每种场景的缩进:
set indentexpr=CustomCppIndent() set nocindent " 关闭默认cindent,启用自定义缩进逻辑 function! CustomCppIndent() " 先获取默认cindent的计算结果作为基础 let base_indent = cindent('.') let current_line = getline('.') " 处理namespace:的情况,保持和上一行相同的缩进 if current_line =~# '^\s*namespace:' return indent('.', line('.') - 1) endif " 处理case/default行,应用你需要的缩进规则 if current_line =~# '^\s*case\s.*:' || current_line =~# '^\s*default:' return base_indent " 这里可以根据你的需求调整,比如设为0就是顶格 endif " 其他场景沿用默认cindent的结果 return base_indent endfunction
这个方案可以完全按照你的需求控制缩进:既让case行保持正确的缩进逻辑,又避免namespace:被误判为标签,同时不影响真正的goto标签(如果需要它们顶格的话)。
内容的提问来源于stack exchange,提问作者user14695458

