Авто-отступ Vim: выравнивание инициализации массива, которая распространяется на несколько строк - PullRequest
11 голосов
/ 04 октября 2010

Иногда инициализация массива в 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 в этой конкретной ситуации?

ОБНОВЛЕНИЕ:

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

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

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

Ответы [ 2 ]

6 голосов
/ 04 октября 2010

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

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

ОБНОВЛЕНИЕ: Вот мод для файла отступа 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
0 голосов
/ 04 октября 2010

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

...