Vim自动缩进:对齐延伸到多行的数组初始化

qfa*_*fab 11 c vim indentation

有时,C中的数组初始化会延伸到多行,特别是如果数组是多维的.在Emacs中,自动缩进的结果如下所示:

int a[N][N] = {{0, 0, 6, 7, 0, 4, 0, 2, 0},
               {0, 5, 0, 6, 0, 0, 0, 0, 1},
               {2, 0, 0, 0, 0, 8, 0, 0, 4},
               {4, 0, 9, 5, 0, 7, 0, 0, 3},
               {0, 0, 0, 0, 0, 0, 0, 0, 0},
               {8, 0, 0, 2, 0, 1, 9, 0, 6},
               {6, 0, 0, 1, 0, 0, 0, 0, 7},
               {3, 0, 0, 0, 0, 5, 0, 6, 0},
               {0, 2, 0, 3, 0, 6, 1, 0, 0}};
Run Code Online (Sandbox Code Playgroud)

在Vim中,自动缩进功能:filetype indent on仅通过常量缩进行,shiftwidth从而导致以下内容:

int a[N][N] = {{0, 0, 6, 7, 0, 4, 0, 2, 0},
    {0, 5, 0, 6, 0, 0, 0, 0, 1},
    {2, 0, 0, 0, 0, 8, 0, 0, 4},
    {4, 0, 9, 5, 0, 7, 0, 0, 3},
    {0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 0, 2, 0, 1, 9, 0, 6},
    {6, 0, 0, 1, 0, 0, 0, 0, 7},
    {3, 0, 0, 0, 0, 5, 0, 6, 0},
    {0, 2, 0, 3, 0, 6, 1, 0, 0}};
Run Code Online (Sandbox Code Playgroud)

有没有办法让Vim在这种特殊情况下表现得像Emacs?

更新:

Herbert Sitz的答案确实很有帮助(谢谢!).我稍微修改了他的代码,看起来像这样:

setlocal indentexpr=GetMyCIndent()

function! GetMyCIndent()
    let theIndent = cindent(v:lnum)

    let m = matchstr(getline(v:lnum - 1),
    \                '^\s*\w\+\s\+\S\+.*=\s*{\ze[^;]*$')
    if !empty(m)
        let theIndent = len(m)
    endif

    return theIndent
endfunction
Run Code Online (Sandbox Code Playgroud)

将其保存到文件~/.vim/after/ftplugin/c.vim可以解决问题,即它使得Vim以与Emacs相同的方式对齐数组声明.

我改变了什么:

  • 使用matchstr()而不是matchlist()使代码更容易理解(len(m)代替len(m[0])).
  • 在行的开头允许使用空格,以便可以嵌套声明(例如,在函数中).
  • 在赋值运算符之前只允许两个单词.这需要处理static声明.
  • 仅检查第一个左括号({),以便表达式也匹配一维数组(或结构).
  • 不匹配包含分号(;)的表达式,因为这表示声明保存在一行中(即下一行不应在左括号下对齐).我认为这比在行尾查找右括号或逗号更通用.

如果我错过了重要的事情,请告诉我.

Her*_*itz 6

有人可能比我知道的更好,但这是第一次刺:是的,缩进可以定制.如果您的文件是可识别的语言"filetype",那么它将使用/ indent目录(e..g,vim/vim72/indent)中相应的*.vim文件中的规则/代码缩进.

您需要修改在多行数组上提供缩进的代码,这可能涉及向创建缩进的部分添加新的if块,if表达式匹配多行数组的所有且仅匹配多行数组的第一行.您应该能够通过检查/ indent目录中的文件来了解工作原理.

更新:这是一个c.vim缩进文件的mod,应该接近你想要的,似乎对我来说很好.这是整个文件:

" Last Change: 2005 Mar 27

" Only load this indent file when no other was loaded.
if exists("b:did_indent")
   finish
endif
let b:did_indent = 1

" C indenting is built-in, thus this is very simple
setlocal cindent

setlocal indentexpr=GetMyCIndent()
function! GetMyCIndent()
let theIndent = cindent(v:lnum)

let m = matchlist(getline(v:lnum - 1),'^\S\+\s\+\S\+\s*=\s*{\s*{\ze.*}[^}*]')
let m2 = matchlist(getline(v:lnum - 1),'}.*}')
if (!empty(m)) && (empty(m2))
    let theIndent = len(m[0]) - 1
endif

return theIndent

endfunction

let b:undo_indent = "setl cin<"
Run Code Online (Sandbox Code Playgroud)

唯一的问题(我认为)与此代码是,它将使相同的缩进到阵列intialization的阵列在一行完成.为避免模式需要修改以仅在行上有一个结束括号时匹配,而不是两个.(或者,你可以做一个单独的测试.)这需要一点点,但不应该太难.(另外,如果你扩展当前模式,你将需要在模式中使用\ ze标记来标记你想要存储在m [0]中的匹配的结尾,这将是在第二个开始括号之后的最后一个字符目前的模式.)我修改了上面的代码进行单独测试(使用变量m2)我认为解决了问题.不确定需要注意哪些其他小细节.

一种替代方案是,只要行上至少有两个左括号,而最后一行char是逗号,就会想要这种缩进行为.这实际上可能是最好的方法,因为它允许你在一行上有元素,三元组等元素:

let m = matchlist(getline(v:lnum - 1),'^\S\+\s\+\S\+\s*=\s*{\s*{\ze.*,\s*$')
if !empty(m)
    let theIndent = len(m[0]) - 1
endif
Run Code Online (Sandbox Code Playgroud)