Автоиндексирование 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:

Ответ Герберта Ситца действительно был очень полезен (спасибо!). Я немного изменил его код, чтобы выглядеть так:

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.
  • Проверить только первую открывающую скобку ({), чтобы выражение также соответствовало одномерным массивам (или структурам).
  • Не совпадают выражения, содержащие точку с запятой (;), поскольку это указывает на то, что объявление выполняется в одной строке (т.е. следующая строка не должна выравниваться под открытой скобкой). Я думаю, что это более общий подход, чем поиск закрывающих скобок или запятых в конце строки.

Пожалуйста, дайте мне знать, если я пропустил что-то важное.

Ответ 1

Кто-то может знать лучше, чем я, но здесь первый удар: Да, отступы могут быть настроены. Если ваш файл является признанным языком "filetype", он отступается с использованием правил/кода в соответствующем *.vim файле, найденном в каталоге /indent (e..g, vim/vim72/indent).

Вам нужно будет изменить код, предоставляющий отступ в вашем многострочном массиве, который может включать добавление нового блока if в раздел, который создает отступы, с выражением if, которое соответствует всем и только первым строкам вашего многострочного массивы. Вы должны иметь представление о том, как все работает, исследуя файлы в каталоге /indent.

UPDATE: здесь мода на файл отпечатка c.vim, который должен быть близок к тому, что вы хотите, кажется, работает отлично для меня. Это весь файл:

" 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

Ответ 2

Я думаю, вы можете набрать :set ai!, затем отступом второй размерной строки, а затем, когда вы нажмете Enter и введите третью размерную строку, она будет правильно отступна... извините, если это не эффективное решение.