СПРАВОЧНИК ПО VIM - Брам Мооленаар

Этот документ посвящён отступам в программах на языке C и в других текстах.

1. Отступы в программах на языке C
2. Форматирование отступов с помощью выражений                  

1. Отступы в программах на языке C

Основы форматирования отступов в программах на языке C разъясняются в разделе 30.2 Руководства Пользователя.

Vim располагает набором опций для автоматического форматирования отступов в программах, написанных на языке C. Эти опции затрагивают только форматирование отступов и не выполняют какого-либо иного форматирования. Подробнее о форматировании комментариев см. |форматирование-комментарии|.

Замечание: обсуждаемые здесь возможности работают только в том случае, если Vim собран с включёнными особенностями |+smartindent| и |+cindent|.

В общей сложности для форматирования отступов доступно четыре метода:

'autoindent' Используется отступ предыдущей строки.
'smartindent' То же, что и 'autoindent', но с возможностью разбора основных особенностей синтаксиса языка C для увеличения или уменьшения отступов там, где это требуется.
'cindent' Более разумное форматирование отступов в программах на языке C, чем вышеперечисленные методы. Позволяет настраивать форматирование для различных стилей записи кода на языке C.
'indentexpr' Наиболее гибкий метод форматирования отступов: для выяснения величины отступа строки вычисляется специально заданное выражение. Если значение этой опции не пустое, то этот метод имеет наибольший приоритет по отношению к остальным методам. См. Форматирование отступов с помощью выражений.

Далее в этом разделе подробно рассматривается работа опции 'cindent'.

Замечание: форматирование отступов при помощи 'cindent' работает не во всех случаях. Vim не C-компилятор: он не в состоянии распознать весь синтаксис.

Отступы в программах на языке C управляются четырьмя опциями:

'cindent' Позволяет осуществлять автоматическое форматирование отступов в программах на языке C.
'cinkeys' Определяет символы, ввод которых вызывает изменение отступа в режиме Вставки.
'cinoptions' Позволяет установить предпочтительный для вас стиль форматирования отступов.
'cinwords' Определяет ключевые слова, которые добавляют отступ в следующей строке.

Если опция 'lisp' отключена, а значение опции - 'equalprg' пустая строка, то оператор "=" использует форматирование отступов с помощью встроенного алгоритма Vim, вместо вызова внешней программы.

О том, как настроить редактор, чтобы опция 'cindent' автоматически включалась для файлов с исходным текстом программ на языке C и отключалась для других типов файла, читайте в разделе |автокоманда|.

Значением опции 'cinkeys' является строка, которая управляет форматированием отступов в редакторе Vim в ответ на ввод определённых символов или команд в определённом контексте. Это относится не только к отступам для C. Если опция 'indentexpr' не пустая, то вместо значения 'cinkeys' используется значение 'indentkeys'. Формат опций 'cinkeys' и 'indentkeys' одинаковый.

Значением по умолчанию является строка "0{,0},0),:,0#,!^F,o,O,e", что подразумевает следующие правила форматирования отступов:

"0{" при вводе '{' в качестве первого символа в строке
"0}" при вводе '}' в качестве первого символа в строке
"0)" при вводе ')' в качестве первого символа в строке
":" при вводе ':' после метки или в выражении case
"0#" при вводе '#' в качестве первого символа в строке
"!^F" при вводе CTRL-F (в текст не вставляется)
"o" при вводе <CR> в любом месте или при использовании команды "o" (не в режиме Вставки!)
"O" при использовании команды "O" (не в режиме Вставки!)
"e" при вводе второго 'e' в "else" в начале строки

Символы, которые предшествуют каждому ключу:

! Если ключу предшествует '!', то Vim не будет вставлять этот ключ, но вместо этого выполнит переформатирование текущей строки. Это позволяет определить кнопку для команды переформатирования текущей строки. Кнопкой по умолчанию является CTRL-F. Будьте осторожны, чтобы случайно не определить для этой цели кнопку CTRL-I, поскольку CTRL-I имеет тот же код, что и <Tab>.
* Если ключу предшествует '*', то перед вставкой ключа Vim выполнит переформатирование строки. Если в значении опции 'cinkeys' встречается определение "*<Return>", то Vim выполняет переформатирование текущей строки перед тем, как открыть новую строку.
0 Если ключу предшествует 0, который появляется после '!' или '*', то Vim будет выполнять переформатирование строки только в том случае, когда ключ является первым символом, набранным в этой строке. Если 0 используется после "=", то Vim будет переформатировать отступ в строке только в том случае, если перед словом есть пробел.

Если ключу не предшествует ни '!', ни '*', то Vim переформатирует отступ в строке после ввода ключа. Так, ';' устанавливает отступ для строки, которая включает символ ';'.

Специальные ключи:

<> В угловых скобках указываются названия кнопок. Например: "<Up>", "<Ins>" (см. |коды_клавиш|).
^ Буквы, которым предшествует символ ^, являются управляющими символами. Например: "^F" это то же, что и CTRL-F.
o Выполнять форматирование отступов в строке при использовании команды "o" или при открытии новой строки под текущей (например, при вводе <Enter> в режиме Вставки).
O Выполнять форматирование отступов в строке при использовании команды "O".
e Выполнять форматирование отступов в строке, которая начинается с "else" при вводе второго "e".
: Выполнять форматирование отступов в строке при вводе ':' после метки или выражения case. Не выполнять форматирование отступов при использовании ":" в "класс::метод" в C++. Для форматирования отступов при вводе символа ":" в любом контексте, используйте "<:>".
=слово Выполнять форматирование при вводе последнего символа "слова". При этом, "слово" может на самом деле быть частью другого слова. Так, "=end" будет приводить к форматированию отступов при вводе "d" в словах "endif" и "endwhile", но не при вводе слова "bend". Кроме того, форматирование отступов выполняется, когда дополнение приводит к подстановке слова, которое начинается с указанного "слова". "0=слово" выполняет форматирование отступов в тех случаях, когда перед словом в строке есть только пробелы.
=~слово Как =слово, но без учёта регистра символов.

Если вам действительно необходимо выполнять форматирование отступов при вводе 'o', 'O', 'e', '0', '<', '>', '*', ':' или '!', то используйте "<o>", "<O>", "<e>", "<0>", "<<>", "<>>", "<*>", "<:>" или "<!>" вместо указанных ключей соответственно.

Для форматирования отступов в стиле emacs, когда отступы форматируются не при нажатии Enter, но при нажатии Tab, я предлагаю использовать такую настройку:

:set cinkeys=0{,0},:,0#,!<Tab>,!^F

В этом случае вы также можете отключить опцию 'autoindent'.

Замечание: если вы вручную измените отступы в текущей строке, то Vim будет игнорировать установки 'cindent' в этой строке. Это позволяет заставить Vim не вмешиваться в форматирование отступов тех строк, в которых отступы были изменены вручную с помощью клавиш <BS>, <Tab> или <Space>, а также по команде CTRL-T или CTRL-D.

Опция 'cinoptions' задаёт метод форматирования отступов, который используется редактором Vim. В списке, который приводится ниже, "N" указывает на число, которое может быть в том числе отрицательным. Если после числа указывается 's', то Vim умножает это число на значение опции 'shiftwidth': "1s" это величина 'shiftwidth', "2s" это удвоенная величина 'shiftwidth' и т.д. Вы можете также пользоваться дробными значениями с десятичной точкой: "-0.5s" обозначает минус половину значения 'shiftwidth'. В примерах, которые приводятся ниже, предполагается, что значение опции 'shiftwidth' равно 4.

>N

Количество пробелов, добавляемых для "обычного отступа". Используется после строки, которая должна увеличить отступ (строки, которые начинаются с "if", открывающая фигурная скобка и т.д.). По умолчанию: величина, равная значению 'shiftwidth'.

cino=           cino=>2           cino=>2s
  if (cond)       if (cond)         if (cond)
  {               {                 {
      foo;          foo;                    foo;
  }               }                 }

eN

Добавить N к существующему отступу внутри фигурных скобок, если открывающая скобка находится в конце строки (а точнее, не является первым символом строки). Этим можно пользоваться для того, чтобы отступ, который используется после '{' в начале строки, отличался от отступа, который используется после '{' в конце строки. По умолчанию: 0.

cino=              cino=e2           cino=e-2
  if (cond) {        if (cond) {       if (cond) {
      foo;                 foo;          foo;
  }                  }                 }
  else               else              else
  {                  {                 {
      bar;               bar;              bar;
  }                  }                 }

 nN

Добавить N к существующему отступу для выражений после "if", "while" и т.д., если строка НЕ находится внутри фигурных скобок. Это полезно, когда требуется, чтобы строка, перед которой нет '{' имела отступ, отличный от строки, перед которой '{' есть. По умолчанию: 0.

cino=           cino=n2          cino=n-2
  if (cond)       if (cond)        if (cond)
      foo;         foo;         foo;
  else            else             else
  {               {                {
      bar;         bar;          bar;
  }               }                }

 fN

 

Поместить первую открывающую фигурную скобку функции или другого блока в колонку N. Это относится только к открывающей скобке, которая не находится внутри других скобок и которая находится в начале строки. То, что следует за открывающей фигурной скобкой, размещается относительно неё. По умолчанию: 0.

cino=           cino=f.5s          cino=f1s
func()          func()             func()
  {                 {                    {
      int foo;          int foo;             int foo;

 {N

Размещать открывающие фигурные скобки на N символов от существующего отступа.
Это относится только к открывающим скобкам, которые находятся внутри других фигурных скобок. По умолчанию: 0.

cino=            cino={.5s         cino={1s
  if (cond)        if (cond)         if (cond)
  {                  {                   {
      foo;             foo;              foo;

 }N

Размещать закрывающие фигурные скобки на N символов от соответствующей открывающей скобки. По умолчанию: 0.

cino=             cino={2,}-0.5s      cino=}2
  if (cond)         if (cond)           if (cond)
  {                   {                 {
      foo;              foo;                foo;
  }                 }                     }

 ^N

Добавить N к существующему отступу внутри фигурных скобок, если открывающая фигурная скобка находится в колонке 0. Благодаря этой возможности можно задать специальный отступ для всего тела функции (некоторым нравится, чтобы это было отрицательное число). По умолчанию: 0.

cino=               cino=^-2          cino=^-s
func()              func()            func()
  {                   {                 {
   if (cond)         if (cond)       if (cond)
      {                 {               {
       a = b;            a = b;          a = b;
      }                 }               }
  }                   }                 }
 :N

Помещать метки case на N символов от отступа switch(). По умолчанию: значение 'shiftwidth'.

cino=               cino=:0
  switch (x)          switch(x)
  {                   {
      case 1:         case 1:
          a = b;          a = b;
      default:        default:
  }                   }

N

Помещать выражения, встречающиеся после метки case, на N символов от отступа метки. По умолчанию: значение 'shiftwidth'.

cino=                cino==10
case 11:             case 11:  a = a + 1;
a = a + 1;                 b = b + 1;

lN

Если N != 0, то Vim будет выравнивать строку по метке case вместо выражения, следующего после неё в той же строке.

cino=                     cino=l1
  switch (a) {              switch (a) {
   case 1: {                 case 1: {
                  break;            break;
              }                 }

bN

Если N != 0, то Vim будет выравнивать последний "break" по метке case, так чтобы case..break выглядели одним блоком. (по умолчанию: 0).

cino=                 cino=b1
switch (x)            switch(x)
  {                     {
      case 1:               case 1:
          a = b;              a = b;
          break;          break;

      default:              default:
          a = 0;               a = 0;
          break;          break;
  }                      }

gN

Помещать декларации области видимости C++ на N символов от отступа блока, в котором они находятся. По умолчанию: значение 'shiftwidth'.

Декларация области видимости это "public:", "protected:" или "private:".

cino=                cino=g0
  {                    {
      public:          public:
          a = b;         a = b;
      private:         private:
  }                    }

hN

Помещать выражения, встречающиеся после декларации области видимости C++ на N символов от отступа метки. По умолчанию: значение 'shiftwidth'.

cino=                cino=h10
  public:              public:   a = a + 1;
      a = a + 1;                 b = b + 1;

pN

Объявления параметров функций в стиле K&R будут смещены на N символов от поля. По умолчанию: 'shiftwidth'.

cino=              cino=p0           cino=p2s
  func(a, b)         func(a, b)        func(a, b)
      int a;         int a;                    int a;
      char b;        char b;                   char b;

 tN

Смещать объявление типа возвращаемого значения функции на N символов от поля. По умолчанию: 'shiftwidth'.

cino=            cino=t0          cino=t7
  int          int                     int
  func()           func()         func()

 iN

 

Смещать объявления базовых классов C++ и инициализацию конструкторов, если они начинаются на новой строке (в противном случае они выравниваются по правой стороне ':'). (по умолчанию: 'shiftwidth').

cino=                     cino=i0
  class MyClass :           class MyClass :
      public BaseClass      public BaseClass
  {}                        {}
  MyClass::MyClass() :      MyClass::MyClass() :
      BaseClass(3)          BaseClass(3)
  {}                        {}

+N

Смещать продолжающуюся строку (строку, которая переходит на следующую) на N дополнительных символов. По умолчанию: 'shiftwidth'.

cino=              cino=+10
  a = b + 9 *        a = b + 9 *
      c;                       c;

cN

Смещать строки комментария после начала комментария, если при этом отсутствует какой либо другой текст для выравнивания, на N символов от начала комментария (по умолчанию: 3). См. также |форматирование-комментарии|.

cino=                cino=c5
  /*                   /*
   текст.                 текст.
   */                   */

CN

Если N не равен 0, то смещать строки комментария на число символов, указанных с помощью флага c (см. выше), даже если после начала комментария имеется какой-либо другой текст. По умолчанию: 0.

cino=c0           cino=c0,C1
  /********         /********
    текст.          текст.
  ********/         ********/

(В этом примере используется ":set comments& comments-=s1:/* comments^=s0:/*")

/N

Дополнительно смещать строки комментария на N символов. По умолчанию: 0.

cino=                  cino=/4
  a = b;                 a = b;
  /* комментарий */          /* комментарий */
  c = d;                 c = d;

(N

В незакрытых круглых скобках добавить отступ в N символов от строки с незакрытыми круглыми скобками. Для каждой незакрытой круглой скобки добавляется 'shiftwidth' символов. Если N равно 0 или незакрытые скобки являются первым непробельным символом в строке, то выравнивать к следующему непробельному символу после незакрытой скобки. По умолчанию: значение опции 'shiftwidth' * 2.

cino=                   cino=(0
  if (c1 && (c2 ||        if (c1 && (c2 ||
               c3))                  c3))
      foo;                    foo;
  if (c1 &&               if (c1 &&
          (c2 || c3))         (c2 || c3))
  {                       {

uN

То же, что и (N, но на один уровень глубже. По умолчанию: значение опции 'shiftwidth'.

cino=                    cino=u2
  if (c123456789           if (c123456789
          && (c22345               && (c22345
              || c3))                || c3))

UN

При N не равном 0, не игнорировать отступы, указанные в ( или u, в тех случаях, когда незакрытые скобки являются первым непробельным символом в строке. По умолчанию: 0.

cino= or cino=(s      cino=(s,U1
  c = c1 &&             c = c1 &&
      (                     (
       c2 ||                    c2 ||
       c3                       c3
      ) && c4;              ) && c4;

wN

В незакрытых скобках, если N не равно 0 и при этом используется, соответственно, "(0" или "u0", либо если используется "U0" и незакрытая скобка является первым непробельным символом в строке, то выравнивать по символу, следующему сразу после незакрытой скобки, вместо выравнивания по первому непробельному символу. По умолчанию: 0.

cino=(0                    cino=(0,w1
  if (   c1                  if (   c1
         && (   c2               && (   c2
                || c3))              || c3))
      foo;                       foo;

WN

В незакрытых скобках, если N не равно 0 и при этом используется, соответственно, "(0" или "u0", если незакрытая скобка является последним непробельным символом на своей строке и она не является закрывающей скобкой, выполнять сдвиг следующей строки на N символов относительно внешнего контекста (т.е. начала строки или следующей незакрытой скобки). (По умолчанию: 0).

cino=(0                   cino=(0,W4
  a_long_line(              a_long_line(
              argument,         argument,
              argument);        argument);
  a_short_line(argument,    a_short_line(argument,
               argument);                argument);

mN

Если N не равен нулю, то выравнивать строку, начинающуюся с закрывающей скобки с первым символом строки с соответствующей открывающей скобкой. По умолчанию: 0.

cino=(s               cino=(s,m1
  c = c1 && (           c = c1 && (
      c2 ||                 c2 ||
      c3                    c3
      ) && c4;          ) && c4;
  if (                  if (
      c1 && c2              c1 && c2
     )                  )
      foo;                  foo;

jN

Выравнивать анонимные классы в коде на языке Java правильно. Значение 'N' в данный момент не используется, но должно быть не равным 0 (например, 'j1'). 'j1' позволяет, к примеру, правильно расставить отступы в нижеследующем фрагменте кода:

object.add(new ChangeListener() {
    public void stateChanged(ChangeEvent e) {
        do_something();
    }
});

)N

Vim ищет незакрытую скобку не более, чем в N строках. Это позволяет ограничить время, необходимое для поиска скобки. По умолчанию: 20 строк.
*N Vim ищет незакрытые комментарии не более, чем в N строках. Это позволяет ограничить время, необходимое для поиска начала комментария. По умолчанию: 30 строк.

Полное определение значений, действующих по умолчанию:

cinoptions=>s,e0,n0,f0,{0,}0,^0,:s,=s,l0,gs,hs,ps,ts,+s,c3,C0,(2s,us,
           \U0,w0,m0,j0,)20,*30

Vim размещает строку в колонке 1 в следующих случаях:

  • Строка начинается с '#' (команда препроцессора), если в опции 'cinkeys' указан флаг '#'.
  • Строка начинается с метки (слова, за которым следует ':', кроме "case" и "default".
  • Сочетание правил форматирования отступа приводит к тому, что строка имеет отрицательные отступы.

2. Форматирование отступов с помощью выражений

Основы использования гибких правил форматирования отступов объясняются в разделе 30.3 Руководства Пользователя.

Если вы желаете написать свой собственный файл правил форматирования отступов, то вам потребуется установить значение опции 'indentexpr'. Также может быть полезным установить значение опции 'indentkeys'. Примеры можно посмотреть в каталоге $VIMRUNTIME/indent.


ЗАМЕЧАНИЯ ПО ОТДЕЛЬНЫМ ФАЙЛАМ С ПРАВИЛАМИ ФОРМАТИРОВАНИЯ ОТСТУПОВ
FORTRAN

Отступами выделяются блоковое if, select case и where. Комментарии, выражения с метками и продолжающиеся строки выделяются отступами в том случае, если код на Fortran'е находится в свободном формате, но не выделяются, если код на Fortran'е находится в жёстком формате, поскольку это накладывает ограничения на левое поле исходного текста. В этом случае для выражений с метками и продолжающихся строк может потребоваться ручная доработка форматирования отступов. Более подробное обсуждение используемого метода для определения формата исходного кода см. в разделе |fortran-синтаксис|.

Циклы do

По умолчанию, отступы во всех циклах do остаются без форматирования. Циклы do в Fortran'е могут быть неструктурированными, несколько циклов может заканчиваться на одном и том же выражении с меткой практически любого типа. Для правильного форматирования отступов требуется разбор исходного кода на уровне компилятора. Старый исходный код, в котором циклы do заканчиваются на выражения с меткой произвольного типа, может быть отформатирован при помощи продвинутой программы, например Tidy (https://www.unb.ca/chem/ajit/f_tidy.htm).

Структурированные циклы do/continue также остаются без внимания при форматировании отступов, поскольку выражение continue может использоваться для иных целей помимо завершения цикла do. Программы, подобные Tidy, способны преобразовывать циклы do/continue в формат do/enddo. Отступы в циклах do/enddo вполне могут быть отформатированы. Если вы пользуетесь исключительно структурированными циклами типа do/enddo, то об этом следует объявить, установив соответствующим образом переменную fortran_do_enddo в файле .vimrc.

Пример:

let fortran_do_enddo=1

В данном случае отступы в циклах do будут форматироваться. Если все циклы в файлах типа .f90 принадлежат типу do/enndo, то следует пользоваться переменной буфера и устанавливать её с помощью автокоманды. Например:

au! BufRead,BufNewFile *.f90 let b:fortran_do_enddo=1

Позволяет форматировать отступы в циклах do во всех файлах .f90, но оставляет в покое файлы с исходным кодом на Fortran'е с другими расширениями, такими как .for.

VERILOG

Форматируются отступы в блоковых выражениях общего назначения, таких как if, for, case, always, initial, function, specify, begin и т.д. Отступы в модульных блоковых выражениях, т.е. блоках первого уровня, по умолчанию не форматируются. Отступы в таких блоках можно включить с помощью соответствующей переменной в файле .vimrc:

let b:verilog_indent_modules = 1

В этом случае отступы в коде модульных блоков будут форматироваться. Для того, чтобы такое поведение остановить, нужно убрать указанную переменную:

:unlet b:verilog_indent_modules

Чтобы установить переменную только для файлов Verilog, можно использовать такой набор выражений:

au BufReadPost * if exists("b:current_syntax")
au BufReadPost *   if b:current_syntax == "verilog"
au BufReadPost *     let b:verilog_indent_modules = 1
au BufReadPost *   endif
au BufReadPost * endif

Кроме того, установка переменной b:verilog_indent_width позволяет изменять ширину отступа (по умолчанию: значение опции 'shiftwidth'):

let b:verilog_indent_width = 4
let b:verilog_indent_width = &sw * 2

Вы также можете в целях отладки включить режим диагностики:

let b:verilog_indent_verbose = 1

Убедитесь только, что перед этим выполнена команда ":set cmdheight=2", чтобы было где показывать сообщения диагностики.