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

时间:2010-10-04 15:50:21

标签: c indentation vim

有时,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}};

在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}};

在这种特殊情况下,有没有办法让Vim像Emacs一样?

UPDATE:

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

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

我改变了什么:

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

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

2 个答案:

答案 0 :(得分: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<"

这个代码唯一的问题(我认为)是它会给在一行上完成 的数组初始化数组提供相同的缩进。为了避免模式需要修改以仅在行上有一个结束括号时匹配,而不是两个。 (或者,你可以做一个单独的测试。)这需要一点点,但不应该太难。 (另外,如果你扩展当前模式,你将需要在模式中使用\ 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

答案 1 :(得分:0)

我认为您可以键入:set ai!然后缩进第二个维度行,然后当您按Enter键并键入第三个维度行时,它将正确缩进...抱歉,如果它不是一个有效的解决方案。