Справочник по VIM

Быстрое исправления ошибок в программах

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

Введение в данную тему представлено в разделе 30.1 Руководства пользователя.

  1. Использование команд быстрого исправления
  2. Окно ошибок
  3. Использование нескольких списков ошибок
  4. Использование :make
  5. Использование :grep
  6. Выбор компилятора
  7. Формат ошибок
  8. Стек каталогов
  9. Частные случаи формата ошибок

Vi не имеет ни одной из описываемых здесь команд.

Команды быстрого исправления доступны только в том случае, если Vim был скомпилирован со включённой особенностью |+quickfix|.

1. Использование команд быстрого исправления

Vim имеет специальный режим для повышения эффективности при выполнении цикла отредактировать программу — скомпилировать — исправить ошибки. Идея такой возможности была заимствована из компилятора Manx’s Aztec C на Amiga. Она заключается в том, что сообщения компилятора об ошибках сохраняются в специальном файле и затем могут быть использованы в Vim для перехода от одной ошибки к другой. Вы получаете возможность разобраться с каждой проблемой и внести требуемые исправления без необходимости запоминать все сообщения компилятора об ошибках.

Если вы используете компилятор Manx’s Aztec C на Amiga, то в разделе |быстрое_исправление-manx| ниже рассматриваются вопросы взаимодействия этого компилятора с Vim. При использовании другого компилятора необходимо сохранять сообщения об ошибках в файл и запускать Vim с помощью команды «vim -q filename». Проще всего сделать это с помощью команды |:make| (см. ниже). Опция ‘errorformat’ должна быть настроена таким образом, чтобы соответствовать формату сообщений об ошибках вашего компилятора (см. раздел |формат_ошибок| ниже).

Вы можете использовать следующие команды для быстрого исправления ошибок:

:cc[!] [номер]Показать ошибку по заданному [номеру]. Если [номер] не указан, то повторно выводится та же самая ошибка. Команда не работает без модификатора [!], если при этом происходит переход в другой буфер, в то время как активный буфер содержит несохранённые изменения и для него открыто только одно окно, а опции ‘hidden’ и ‘autowrite’ выключены. Модификатор [!] позволяет выполнять переход к другому буферу в таких ситуациях, но при этом все изменения в активном буфере будут потеряны, если только не включена опция ‘hidden’, либо если данный буфер не открыт также в другом окне. При переходе к другому буферу учитывается значениеопции ‘switchbuf’.
:[число]cn[ext][!]Показать следующую по списку (с учётом заданного [числа]) ошибку, включающую имя файла. Если в списке ошибок нет строк с именем файла, то будет просто показана следующая по списку (с учётом заданного [числа]) ошибка. Использование модификатора [!] и опции ‘switchbuf’ рассматривается в справке по команде :cc.
:[число]cN[ext][!]
:[число]cp[revious][!]
Показать предыдущую по списку (с учётом заданного [числа]) ошибку, включающую имя файла. Если в списке ошибок нет строк с именем файла, то будет просто показана предыдущая по списку (с учётом заданного [числа]) ошибка. Использование модификатора [!] и опции ‘switchbuf’ рассматривается в справке по команде :cc.
:[число]cnf[ile][!]Показать первую ошибку в следующем по порядку (с учётом заданного [числа]) файле в списке ошибок, включающих имя файла. Если в списке ошибок нет строк с именем файла или следующего файла не существует, то будет выполнен переход к следующей по списку (с учётом заданного [числа]) ошибке. Использование модификатора [!] и опции ‘switchbuf’ рассматривается в справке по команде :cc.
:[число]cNf[ile][!]
:[число]cpf[ile][!]
Показать последнюю ошибку в предыдущем по порядку (с учётом заданного [числа]) файле в списке ошибок, включающих имя файла. Если в списке ошибок нет строк с именем файла или следующего файла не существует, то будет выполнен переход к предыдущей по списку (с учётом заданного [числа]) ошибке. Использование модификатора [!] и опции ‘switchbuf’ рассматривается в справке по команде :cc.
:cr[ewind][!] [номер]Показать ошибку с заданным [номером]. Если [номер] не задан, то будет показана ПЕРВАЯ ошибка в списке.
См. также :cc.
:cfir[st][!] [номер]То же, что и «:crewind».
:cla[st][!] [номер]Показать ошибку с заданным [номером]. Если [номер] не задан, то будет показана ПОСЛЕДНЯЯ ошибка в списке.
См. также :cc.
:cq[uit]Завершение работы Vim с кодом ошибки, позволяющее компилятору не выполнять компиляцию того же самого файла заново.
:cf[ile][!] [файл_ошибок]Прочитать файл ошибок и выполнить переход к первой ошибке в списке. Эта команда выполняется автоматически, если Vim запускается с аргументом -q. Вы можете использовать данную команду, чтобы держать Vim запущенным во время компиляции. Если задано имя файла ошибок, то значение опции ‘errorfile’ будет изменено соответствующим образом. Использование модификатора [!] рассматривается в справке по команде :cc.
:cg[etfile][!] [файл_ошибок]Прочитать файл ошибок. Команда работает как «:cfile», но переход к первой ошибке в списке не выполняется.
:cl[ist] [от] [, [до]]Вывести список всех известных ошибок (см. |быстрое_исправление-известные_ошибки|). Если заданы числа [от] и/или [до], то будет выведена соответствующая часть списка. Отрицательные числа используются для указания на номер ошибки, предшествующей последней ошибке, причём последняя ошибка выражается числом -1. При переходе к другому буферу принимается во внимание значение опции ‘switchbuf’.
:cl[ist]! [от] [, [до]]Вывести список всех ошибок.

В большинстве случаев положение ошибки может быть найдено даже при вставке и удалении строк, так как Vim использует невидимые отметки. Если отметка по каким-то причинам будет удалена, то выводится сообщение «строка изменена», предупреждающее, что положение ошибки может быть не совсем верным. При выходе из Vim отметки утрачиваются, так что при следующем запуске все положения ошибок могут не соответствовать действительности.

2. Окно ошибок

:cope[n] [высота]Показать текущий список ошибок в специальном окне. Окно ошибок может иметь заданную [высоту], если для такой высоты имеется достаточно экранного места. В противном случае высота окна равна 10 строкам. Окно ошибок используется для отображения специального буфера, значение опции ‘buftype’ для него равно «quickfix». Вы не должны менять значение этой опции для окна ошибок! Если окно ошибок уже имеется на экране, то оно становится активным окном. Второе окон ошибок создать нельзя.
:ccl[ose]Закрыть окно ошибок.
:cw[indow] [высота]Открыть окно ошибок, если есть известные ошибки. Если известных ошибок нет, а окно ошибок уже открыто, то окно ошибок будет закрыто.

Окно ошибок обычно помещается внизу экрана. Если окна на экране разделены по вертикали, то оно помещается внизу самого правого окна. Чтобы окно ошибок всегда разворачивалось на полную ширину окна, необходимо ввести команду:

:botright cwindow

Вы можете перемещать окно ошибок по экрану с помощью соответствующих команд (см. раздел |окно-перемещение|).
Например, чтобы поместить окно ошибок в верхней части экрана: CTRL-W K.

Для окна ошибок включается опция ‘winfixheight’, что приводит к тому, что окно будет по возможности сохранять свою высоту, не обращая внимания на значения опций ‘winheight’ и ‘equalalways’. Вы можете изменить высоту окна вручную (например, путём перемещения строки состояния вышерасположенного окна при помощи мыши).

Каждая строка в окне ошибок соответствует одной ошибке. Номер строки одновременно является и номером ошибки. Для перехода к ошибке, соответствующей строке в позиции курсора, можно использовать команду «:.cc». Того же самого эффекта можно также достичь при нажатии на кнопку <CR> или с помощью двойного щелчка мышью на строке с ошибкой. Файл, в котором находится ошибка, открывается в окне, расположенном над окном ошибок, либо, если окно для этого файла уже открыто, выполняется переход в это окно. Если буфер в открытом окне был изменён, а ошибка находится в другом файле, то переход к ошибке не сможет быть выполнен. Вам потребуется сначала убедиться, что окно содержит буфер, который можно бросить.

При заполнении окна ошибок срабатывает два автокомандных события. Сначала происходит изменение значения опции ‘filetype’ на «qf», что приводит к отработке события FileType. Затем срабатывает событие BufReadPost. Это можно использовать для выполнения каких-либо действий над списком ошибок. Например:

au BufReadPost quickfix  setlocal nomodifiable
\ | silent g/^/s//\=line(".")." "/
\ | setlocal modifiable

Эта команда позволяет добавлять к каждой строке её номер. Обратите внимание на использование «\=» в строке для замены в команде «:s», применяемое для вычисления выражения.

Замечание: внесение изменений в окне ошибок не оказывает влияния на список ошибок. Во избежание внесения изменений опция ‘modifiable’ в окне ошибок отключается. При вставке или удалении строк нарушается соответствие между номерами ошибок в списке и номерами строк в окне ошибок. Если вам так необходимо внести изменения в список ошибок, то вы можете записать содержимое окна ошибок в файл и затем прочитать этот файл при помощи команды «:cfile» для использования в качестве нового списка ошибок.

3. Использование нескольких списков ошибок

До сих пор подразумевалось, что мы используем только один список ошибок. На самом деле Vim запоминает до десяти последних использованных списков ошибок. При создании нового списка предыдущие сохраняются. Для доступа к более старым спискам ошибок могут использоваться две специальные команды. При их выполнении соответствующий список ошибок становится текущим списком.

:col[der] [число]Выполнить переход к более старому списку ошибок. При использовании [числа] описанная операция повторяется заданное [число] раз. Если вы уже используете самый старый список ошибок, то выводится сообщение об ошибке.
:cnew[er] [число]Выполнить переход к более свежему списку ошибок. При использовании [числа] описанная операция повторяется заданное [число] раз. Если вы уже используете самый свежий список ошибок, то выводится сообщение об ошибке.

При добавлении нового списка ошибок он становится текущим списком.

После использования команды «:colder» последующий вызов команды «:make» или «:grep» приводит к замене одного более свежего списка ошибок. Это особенно полезно при просмотре вывода команды «:grep» |grep|. Чтобы сохранить более свежие списки ошибок, следует сначала воспользоваться командой «:cnewer 99».

4. Использование :make

:mak[e][!] [аргументы]
  1. При включённой опции ‘autowrite’ выполняется запись всех изменённых буферов.
  2. Имя файла ошибок вычисляется в соответствии со значением опции ‘makeef’. Если значение опции ‘makeef’ не содержит «##», то уже существующий файл с таким именем будет удалён.
  3. Запускается программа с именем, заданным в значении опции ‘makeprg’ (по умолчанию: «make») с необязательным набором [аргументов], вывод которой перенаправляется в файл ошибок (в Unix вывод также отображается на экране).
  4. Выполняется чтение файла ошибок с использованием значения опции ‘errorformat’.
  5. Если модификатор [!] не задан, то происходит перемещение к первой ошибке из списка.
  6. Файл ошибок удаляется.
  7. Теперь вы можете перемещаться по списку ошибок с помощью команд вроде |:cnext| и |:cprevious|, см. выше.

Данная команда не может сопровождаться комментарием. Любой символ » считается частью переданных команде аргументов.

Команда «:make» выполняет программу, заданную в значении опции ‘makeprg’. Это происходит путём вызова команды из оболочки, заданной в значении опции ‘shell’. Иными словами, происходит практически то же самое, что и при вводе команды

":!{значение_makeprg} [аргументы] {значение_shellpipe} {файл_ошибок}".

Здесь {значение_makeprg} это строковое значение опции ‘makeprg’. Вы можете использовать любую необходимую программу, не только «make». При вводе командной строки выполняется обычная в таких случаях подстановка значений для символов ‘%’ и ‘#’. Вы можете использовать ‘%<‘ для подстановки имени текущего файла без расширения или ‘#<‘ для подстановки имени соседнего файла без расширения. Например:

:set makeprg=make\ #<.o

В качестве [аргументов] используется всё, что набрано в командной строке после «:make».
В качестве {значение_shellpipe} используется значение опции ‘shellpipe’. {файл_ошибок} указывает на значение опции ‘makeef’, причём символы ## заменяются уникальными символами.

В том случае, если команда {makeprog} требует ввода дополнительных символов после списка аргументов, можно использовать специальную «заглушку» в виде последовательности «$*». В этом случае $* заменяется всеми указанными аргументами. Например:

:set makeprg=latex\ \\\\nonstopmode\ \\\\input\\{$*}

или, более простой вариант:

:let &mp = 'latex \\nonstopmode \\input\{$*}'

«$*» можно указывать несколько раз. Например:

:set makeprg=gcc\ -o\ $*\ $*

Значение опции ‘shellpipe’ по умолчанию равно «>» на Amiga, MS-DOS и Win32. Это означает, что вывод компилятора сохраняется в файл и не отображается на экране напрямую. В Unix используется «| tee», что позволяет сохранять вывод работы компилятора в файл с одновременным отображением его на экране. В зависимости от используемой оболочки по умолчанию используется «|& tee» или «2>&1| tee», что позволяет также включать в вывод стандартный поток диагностики stderr.

Если значение опции ‘shellpipe’ не задано (пустая строка), то компонент {файл_ошибок} опускается.  Такое поведение может быть полезным при использовании компиляторов, которые могут выполнять вывод сообщений в файл ошибок самостоятельно (например, Manx’s Amiga C).

5. Использование :grep

Vim может взаимодействовать с «grep» и grep-подобными программами (такими как GNU id-utils) используя примерно тот же механизм, что и применяемый при работе с компиляторами (см. |:make| выше).

[Название команды Unix «grep» происходит от «:g/re/p», причём «re» обозначает регулярное выражение (Regular Expression).]

:gr[ep][!] [аргументы]Работает так же, как и «:make», но вместо ‘makeprg’ используется опция ‘grepprg’, а вместо ‘errorformat’ используется опция ‘grepformat’.
:grepa[dd][!] [аргументы]

То же, что и «:grep», но вместо создания нового списка ошибок происходит дополнение обнаруженных соответствий к существующему списку. Например:

:grep цололо %
:bufdo grepadd! пожар %

В этом примере первая команда создаёт новый пустой список ошибок. Вторая команда выполняет команду «grepadd» для каждого буфера в списке. Обратите внимание на использование !, что позволяет избежать перемещения к первой ошибке при использовании «:grepadd», что недопустимо при использовании команды |:bufdo|.

5.1 Настройка grep

Если вы используете стандартный вариант программы «grep», то команда :grep скорее всего будет успешно работать с настройками, используемыми по умолчанию. Синтаксис очень похож на использование стандартной программы:

:grep цололо *.c

Эта команда выполняет поиск подстроки «цололо» во всех файлах с расширением .c. Аргументы команды :grep напрямую передаются программе «grep», так что вы можете использовать любые аргументы, которые поддерживает ваш вариант программы «grep».

По умолчанию, :grep вызывает программу grep с аргументом -n (что позволяет отображать имена файлов и номера строк). Вы можете изменить вызываемую программу и набор её аргументов с помощью опции ‘grepprg’. Значение опции ‘grepprg’ следует изменять в следующих случаях:

  1. При использовании программы, которая называется не «grep».
  2. Вам необходимо вызывать grep с полным путевым именем к исполняемому файлу.
  3. Вы хотели бы автоматически передавать программе какие-либо аргументы (например, использовать регистронезависимый поиск).

После завершения исполнения программы «grep», Vim анализирует результат её работы на основании значения опции ‘grepformat’. Эта опция работает так же, как и опция ‘errorformat’ — см. подробности в справке по этой опции. Вам может потребоваться изменить значение опции ‘grepformat’, если ваша программа grep выполняет вывод в нестандартном формате, либо если вы используете какую-то другую программу со своим форматом вывода.

После разбора результатов работы программы grep, Vim загружает файл, в котором содержится первое соответствие и переносит курсор в соответствующую строку, в точности так же, как и при переходе к первой ошибке в режиме |быстрого_исправления|. После этого вы можете использовать команды |:cnext|, |:clist| и т.п. для перехода к другим соответствиям.

5.2 Использование :grep с id-utils

Вы можете настроить команду :grep для работы с GNU id-utils следующим образом:

:set grepprg=lid\ -Rgrep\ -s
:set grepformat=%f:%l:%m

после чего команда

:grep (регулярное выражение)

будет работать как надо (если вы, конечно, выполнили команду mkid : ).

5.3 Просмотр исходного кода с помощью :grep

Вы можете просматривать свои файлы в поиске функций и вызываемых из них других функций с помощью набора списков ошибок, которые Vim держит для вас наготове. Например, предположим, что вам необходимо добавить аргумент к функции read_file(). Вы можете ввести команду:

:grep read_file *.c

Затем вы можете использовать команду «:cn», чтобы пробежаться по списку соответствий и добавить необходимый аргумент. Возможно, в определённый момент вам понадобится добавить новый аргумент в функцию msg() более высокого уровня и внести необходимые изменения, и тогда вы вводите команду

:grep msg *.c

Пока вы изменяете функцию msg() вы можете обнаружить ещё одну функцию, которая должна получить аргумент с более высокого уровня. Вы опять используете «:grep», чтобы найти нужные функции. Как только вы закончите с внесением изменений в одной из функций, вы можете использовать команду

:colder

чтобы вернуться к предыдущей функции.

Это похоже на просмотр дерева: команда «:grep» переходит каждый раз на более глубокий уровень, создавая список ветвей. «:colder» позволяет перейти на предыдущий уровень. Вы можете совместно использовать команды «:grep» и «:colder» для просмотра всех интересующих вас мест в коде наподобие дерева. При последовательном использовании этой возможности можно найти все необходимые места и избежать создания списка «что сделать».

6. Выбор компилятора

:comp[iler][!] {имя}Назначает опции для работы с компилятором с указанным {именем}.
Без «!» опции настраиваются для текущего буфера. При использовании «!» настраиваются глобальные опции.
Если после выполнения команды «:compiler foo» в файле «file.foo» выполняется команда «:compiler! bar» в другом буфере, то Vim будет продолжать использовать «foo» для файла «file.foo».
Доступно только в том случае, если Vim скомпилирован с особенностью |+eval|.

Модули Vim в каталоге «compiler» используются для настройки опций для использования выбранного компилятора. При использовании команды «:compiler» происходит настройка местных опций, а при использовании «:compiler!» — глобальных опций.

Для поддержки старых версий Vim в модулях всегда используется переменная «current_compiler», а не «b:current_compiler». Данная команда на самом деле выполняет целый ряд операций:

  • Удаляет переменные «current_compiler» и «b:current_compiler».
  • Определяет пользовательскую команду «CompilerSet».  При использовании «!» выполняется команда «:set», а без «!» выполняется команда «:setlocal».
  • Выполняется сценарий «:runtime! compiler/{имя}.vim». Модули должны выполнять настройку опций по команде «CompilerSet» и назначать переменной «current_compiler» имя компилятора.
  • Команда пользователя CompilerSet удаляется.
  • Значение переменной «b:current_compiler» устанавливается равным значению переменной «current_compiler».
  • При запуске команды без «!» восстанавливается прежнее значение «current_compiler».

В разделе |создание_модулей_для_компиляторов| подробно рассматривается, как написать сценарий модуля поддержки компилятора.

MANX AZTEC C

Для использования Vim совместно с компилятором Manx’s Aztec C на Amiga вам необходимо сделать следующее:

  • Задайте переменную окружения CCEDIT с помощью команды:

    mset "CCEDIT=vim -q"
    
  • Выполняйте компиляцию с ключом -qf. Если компилятор обнаружит ошибки, то будет запущен Vim, причём курсор будет помещаться в строке с ошибкой. Само сообщение об ошибке отображается на последней строке. Для перемещения к другим ошибкам можно использовать команды, описанные выше. После исправления ошибок сохраните соответствующие файлы.
  • После обычного завершения работы Vim компилятор выполнит повторную компиляцию того же самого файла. Чтобы прекратить работу компилятора, используйте выход по команде :cq. Это может пригодиться, если вы не можете исправить ошибку, или вам необходимо сначала скомпилировать другой файл.

В режиме быстрых исправлений на Amiga имеется ряд ограничений. Например, компилятор выполняет запись в файл только первых 25 ошибок (документация по Manx не указывает, как получить большее число ошибок). Если вам необходимо получить информацию о других ошибках, то вы сможете это сделать не раньше, чем исправите несколько более ранних ошибок.

Если Vim запускается из под компилятора, то команды :sh и некоторые команды :! не будут работать, поскольку Vim запущен в том же самом процессе, что и компилятор, в силу чего стандартный поток ввода (stdin) будет неинтерактивным.

КОМПИЛЯТОР PYUNIT

На самом деле, это даже не компилятор, а платформа для тестирования программ, написанных на языке Python. Она включена в стандартную поставку Python, начиная с версии 2.0. Если у вас более старая версия Python, то вы можете получить её на сайте http://pyunit.sourceforge.net.

При выполнении тестов с помощью платформы pyunit возможные ошибки обрабатываются Vim и результаты отображаются в режиме быстрого исправления.

К сожалению, не существует стандартного способа выполнения тестов. Чаще всего используется сценарий alltests.py. В этом случае, полезными будут следующие настройки опции ‘makeprg’:

setlocal makeprg=./alltests.py " Выполнение полного набора тестов
setlocal makeprg=python % " Выполнение единственного теста

См. также http://vim.sourceforge.net/tip_view.php?tip_id=280.

КОМПИЛЯТОР TEX

Включённый в поставку компилятор для TeX ($VIMRUNTIME/compiler/tex.vim) по возможности использует программу «make». Если компилятор может найти файл «Makefile» или «makefile» в рабочем каталоге, то он считает, что вы хотите обрабатывать файлы *TeX с помощью make все необходимые действия описаны в файле makefile. В этом случае компилятор настраивает опцию ‘errorformat’ для перехвата вывода *TeX и не изменяет значение опции ‘makeprg’. Если файл «Makefile» или «makefile» не найден, то компилятор не будет применять программу make. Вы можете заставить компилятор игнорировать файлы makefile и Makefile, определив переменную b:tex_ignore_makefile или g:tex_ignore_makefile (проверяется только факт существования этих переменных).

В том случае, если компилятор решит не использовать make, он должен определиться, какую именно программу использовать для обработки исходного кода. Если существует переменная b:tex_flavor или g:tex_flavor (в указанном порядке приоритета), то она определяет формата TeX для команды :make (фактически, это имя исполняемой команды); с другой стороны, если ни одна из указанных переменных не существует, то по умолчанию используется «latex». Например, при редактировании файла chapter2.tex, включённого в файл AMS-TeX mypaper.tex с помощью \input, используются команды

:let b:tex_flavor = 'amstex'
:compiler tex

[редактирование…]

:make mypaper

Обратите внимание, что вы должны указать имя файла, подлежащего обработке, в качестве аргумента (чтобы обрабатывать правильный файл при редактировании файла, который включается в его состав при помощи \input или \include; предложения переносимых решений по подстановке % в случае отсутствия аргументов приветствуются). Это не совсем соответствует семантике make, который требует задавать цель, а не исходный файл, но вы можете также указывать имя файла без расширения «.tex» и думать, что на самом деле выполняется что-то вроде «make filename.dvi» или «make filename.pdf» или, в общем случае, «filename.расширение_результата_работы_компилятора».

Замечание: синтаксис командной строки tex может также использоваться в MikTeX (предложение Шринат Авадханула [Srinath Avadhanula]) и в teTeX (проверено Артёмом Чуприной). Предложения, приведённые в разделе |формат_ошибок-LaTeX|, слишком сложны, чтобы работать в различных оболочках и операционных системах и не позволяют использовать другие доступные опции TeX. Если ваша версия TeX не поддерживает опцию «-interaction=nonstopmode», то попробуйте использовать иные средства для выражения \nonstopmode из командной строки.

7. Формат ошибок

Опция ‘errorformat’ задаёт список форматов ошибок, которые распознаются режимом быстрых исправлений. Используется первый формат в списке, который соответствует данной ошибке. Вы можете задать несколько форматов для различных сообщений компилятора, и даже определить записи для разных компиляторов. См. |efm-записи|.

Каждая запись в значении опции ‘errorformat’ представляет собой строку в стиле scanf, которая описывает формат ошибки. Прежде всего вам необходимо понять, как работает функция scanf. Обратитесь к документации к компилятору языка C. Ниже приводится список %-элементов, которые Vim распознаёт в значении данной опции. Использование других элементов не допускается.

Запятая и символ обратной косой черты являются специальными символами в значении опции ‘errorformat’.  Их использование рассматривается в разделе |efm-записи|. Обратите также внимание, что символ ‘%’ не экранируется обратной косой, вместо этого используется специальный элемент ‘%%’.

Замечание: по умолчанию регистр символов не принимается во внимание. Если вам необходимо учитывать регистр символов, то следует добавить к шаблону «\C» |/\C|.

Основные элементы

%fимя файла (ищет строку)
%lномер строки (ищет число)
%cномер колонки (ищет число, соответствующее символьной колонке ошибки (1 <tab> == 1 символьная колонка)
%vномер виртуальной колонки (ищет число, соответствующее экранной колонке ошибки (1 <tab> == 8 экранных колонок)
%tтип ошибки (ищет одиночный символ)
%nномер ошибки (ищет номер)
%mсообщение об ошибке (ищет строку)
%rсоответствует «остальной» части однострочного сообщения с именем файла %O/P/Q
%pуказательная линейка (ищет последовательность символов ‘-‘, ‘.’ или ‘ ‘ и использует длину для нахождения номера колонки)
%*{преобр}любое неназначаемое преобразование в стиле scanf
%%одиночный символ ‘%’

Результат распознавания элемента «%f» зависит от текущего значения опции ‘isfname’.

Элементы «%f» и «%m» должны находить конец строки. После них должен следовать символ, который не может быть частью строки-результата. Все символы вплоть до такого символа включаются в строку-результат. Однако, если следующим символом является ‘%’ или символ обратной косой черты, то «%f» будет выполнять поиск любого символа, определённого в значении опции ‘isfname’, а «%m» будет находить все символы. Если «%f» или «%m» указаны в самом конце, то в строку включаются все символы вплоть до конца исходной строки.

На MS-DOS, MS-Windows и OS/2 начальные символы «C:» будут включаться в строку-значение «%f», даже при использовании «%f:».  Это означает, что имя файла, являющееся одиночным символом алфавита, определено не будет.

За преобразованием «%p» обычно следует символ «^». Такая строка используется компиляторами для вывода строк вроде

         ^

или

---------^

чтобы указать на колонку, в которой встречается ошибка. Это применяется в многострочных сообщениях об ошибках. Полезный пример приводится в разделе |формат_ошибок-javac|.

Изменение каталога

Рассмотренные ниже символы преобразования в верхнем регистре определяют специальный тип строки формата. Один (и только один) из этих символов может быть указан в качестве приставки в начале каждого из разделённых запятыми шаблонов формата.

Некоторые компиляторы выводят сообщения, включающие имена каталогов, которые должны быть добавлены к каждому имени файла, распознаваемому с помощью %f (например, GNU make). Указанные ниже коды могут применяться для определения имён этих каталогов; полученные значения сохраняются во внутреннем стеке каталогов.

%D«войти в каталог»: далее в строке ожидается присутствие элемента «%f», который и определяет имя каталога.
%X«покинуть каталог»: далее в строке ожидается присутствие элемента «%f»

При определении форматов «войти в каталог» и «покинуть каталог» соответствующий элемент «%D» или «%X» должен быть указан в самом начале соответствующей подстроки. Vim отслеживает изменения каталогов и подставляет текущий каталог в каждый файл с обнаруженными ошибками с относительным путевым именем. Подробности, советы и имеющиеся ограничения обсуждаются в разделе |быстрое_исправление-стек_каталогов|.

Многострочные сообщения

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

%Eначало многострочного сообщения об ошибке
%Wначало многострочного предупреждения
%Iначало многострочного информационного сообщения
%Aначало многострочного сообщения (без указания типа)
%Cпродолжение многострочного сообщения
%Zконец многострочного сообщения

Эти элементы могут использоваться совместно с ‘+’ и ‘-‘, см. ниже в разделе |efm-пропуск_сообщений|.

Пример: ваш компилятор выводит сообщения об ошибках в следующем формате (номера строк не входят в действительный вывод компилятора):

1  Error 275
2 line 42
3 column 3
4 ' ' expected after '--'

Соответствующая строка формата ошибки должна выглядеть следующим образом:

:set efm=%EError\ %n,%Cline\ %l,%Ccolumn\ %c,%Z%m

Сообщение, созданное этой ошибкой для |:clist| будет выглядеть так:

1:42 col 3 error 275:  ' ' expected after '--'

Другой пример: интерпретатор языка Python создаёт следующее сообщение об ошибке (номера строк не входят в действительный вывод интерпретатора):

 1  ==============================================================
2 FAIL: testGetTypeIdCachesResult (dbfacadeTest.DjsDBFacadeTest)
3 --------------------------------------------------------------
4 Traceback (most recent call last):
5 File "unittests/dbfacadeTest.py", line 89, in testFoo
6 self.assertEquals(34, dtid)
7 File "/usr/lib/python2.2/unittest.py", line 286, in
8 failUnlessEqual
9 raise self.failureException, \
10 AssertionError: 34 != 33
11
12 --------------------------------------------------------------
13 Ran 27 tests in 0.063s

Представим, что вам хотелось бы, чтобы команда |:clist| выводила только самую важную информацию из этого сообщения, а именно:

5 unittests/dbfacadeTest.py:89:  AssertionError: 34 != 33

В этом случае, строка формата ошибки может быть определена следующим образом:

:set efm=%C\ %.%#,%A\ \ File\ \"%f\"\\,\ line\ %l%.%#,%Z%[%^\ ]%\\@=%m

Обратите внимание, что строка %C приводится здесь перед строкой %A: поскольку выражение ‘ %.%#’ (используемое для регулярного выражения ‘ .*’) соответствует любой строке, начинающейся с пробела, за которым следуют любые символы вплоть до конца строки, это также позволяет спрятать строку 7, для которой в противном случае сработало бы распознавание отдельного сообщения об ошибке. Строки формата ошибок всегда разбираются шаблон за шаблоном, до тех пор, пока не будет обнаружено первое соответствие.

Отдельное имя файла

Указанные ниже приставки полезны, если имя файла указывается один раз, но последующие сообщения относятся к этому имени.

%Oоднострочное сообщение с именем файла: пропустить соответствующую часть
%Pоднострочное сообщение с именем файла: поместить файл %f в стек
%Qоднострочное сообщение с именем файла: снять последний файл из стека

Пример: предположим, что у нас есть компилятор, который создаёт следующий файл ошибок (номера строк не являются частью вывода компилятора):

 1  [a1.tt]
2 (1,17) error: ';' missing
3 (21,2) warning: variable 'z' not defined
4 (67,3) error: end of file found before string ended
5
6 [a2.tt]
7
8 [a3.tt]
9 NEW compiler v1.1
10 (2,2) warning: variable 'x' not defined
11 (67,3) warning: 's' already defined

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

:set efm=%+P[%f],(%l\\,%c)%*[\ ]%t%*[^:]:\ %m,%-Q

В этом случае обращение к команде |:clist| выводит сообщения об ошибках с правильно определёнными именами файлов:

2 a1.tt:1 col 17 error: ';' missing
3 a1.tt:21 col 2 warning: variable 'z' not defined
4 a1.tt:67 col 3 error: end of file found before string ended
8 a3.tt:2 col 2 warning: variable 'x' not defined
9 a3.tt:67 col 3 warning: 's' already defined

В отличие от других приставок, которые относятся к целым строкам, приставки %P, %Q и %O могут быть использованы для определения соответствий нескольким %шаблонам в одной строке. Таким образом, возможно также выполнять разбор %вложенных файлов, как например в такой строке:

{"file1" {"file2" error1} error2 {"file3" error3 {"file4" error4 error5}}}

В этом случае приставка %O позволяет пропускать строки, которые не содержат имён файлов для операций со стеком. Более детальный пример приводится в разделе |формат_ошибок-LaTeX|.

Пропуск и использование целых сообщений

Элементы, представленные прописными буквами, могут сочетаться с модификаторами ‘+’ или ‘-‘, которые ставятся непосредственно перед такой буквой. Например, ‘%+A’ или ‘%-G’:

%-соответствующая многострочная ошибка исключается из вывода
%+соответствующая строка целиком включается в строку ошибки, заменяя элемент %m

Одна из рассмотренных выше приставок имеет смысл только в сочетании с ‘+’ или ‘-‘, а именно %G. Эта приставка помогает пропускать сообщения, в которых содержится информация общего характера, например строки с версией компилятора или другие заголовки, которые можно пропустить.

%-Gигнорировать это сообщение
%+Gсообщение общего характера
Использование шаблонов для поиска соответствий

Синтаксис «%*[]» в стиле scanf() поддерживается для обеспечения обратной совместимости с предыдущими версиями Vim. Однако, вы можете также использовать в строке формата (практически) любое регулярное выражение Vim. Поскольку метасимволы языка регулярных выражений могут быть частью обычной строки или имени файла, они должны экранироваться с помощью символа %:

%\одиночный символ ‘\’. Обратите внимание, что при задании значения опции «:set errorformat=» символ ‘\’ сам по себе должен быть экранирован («%\\»).
%.одиночный символ ‘.’.
%#одиночный символ ‘*'(!).
%^одиночный символ ‘^’.
%$одиночный символ ‘$’.
%[одиночный символ ‘[‘, используемый для указания символьного диапазона [].
%~одиночный символ ‘~’.

При использовании в выражениях символьных классов (см. |/\i|), элементы, содержащие числитель «\+» могут быть также записаны в формате scanf() «%*». Например, «%\\d%\\+» («\d\+», «любое число») эквивалентно «%*\\d».

Важное замечание: При задании формата нельзя использовать группировку подсоответствий \(…\), поскольку она зарезервирована для выполнения внутренних преобразований.

Множественные записи в ‘errorformat’

Опция ‘errorformat’ позволяет задавать несколько шаблонов формата, разделённых запятыми (пробелы после запятых игнорируются). Это позволяет распознавать вывод сразу нескольких компиляторов. При этом используется первый подходящий шаблон из списка. Если не обнаружено соответствие ни одному из указанных в значении опции ‘errorformat’ шаблонов, то будут применяться совпадающие части первого шаблона, однако при этом удаляется имя файла и в качестве сообщения об ошибке используется всё сообщение целиком. В том случае, если одному и тому же шаблону может соответствовать вывод различных компиляторов (но не обязательно правильным образом), следует поместить такой шаблон после более строгого шаблона.

Для включения в шаблон символа запятой её необходимо предварять обратной косой чертой (при наборе команды «:set» в командной строке следует вводить два символа обратной косой). Чтобы включить символ обратной косой, необходимо вводить два символа обратной косой (в команде «:set» — четыре). Символ обратной косой черты также следует помещать перед пробелом при вводе команды «:set».

Известные ошибки

Если строка не соответствует полностью ни одной записи в значении опции ‘errorformat’, то вся строка помещается в сообщение об ошибке целиком и такая запись помечается как «неправильная». Такие строки пропускаются при выполнении команд «:cn» и «:cp», кроме ситуации, когда нет вообще ни одной правильной строки. Чтобы показать все сообщения об ошибках, используйте команду «:cl!».

Если формат ошибки не содержит имя файла, то Vim не сможет перейти к нужному файлу. В такой ситуации вам придётся сделать это вручную.

Примеры

Рассмотрим формат сообщений компилятора Amiga Aztec:

имя_файла>номер_строки:номер_колонки:тип_ошибки:номер_ошибки:сообщение
имя_файлаимя файла, в котором обнаружена ошибка
номер_строкиномер строки, в которой обнаружена ошибка
номер_колонкиномер колонки, в которой обнаружена ошибка
тип_ошибкитип ошибки, как правило символ ‘E’ или ‘W’
номер_ошибкиномер ошибки (для поиска в справочнике)
сообщениеописание ошибки

Такой формат сообщений может быть разобран с помощью следующей записи в значении опции ‘errorformat’:

%f>%l:%c:%t:%n:%m

Приведём несколько примеров для различных компиляторов C, которые выводят однострочные сообщения об ошибках:

%f:%l:\ %t%*[^0123456789]%n:\ %mсообщения об ошибках Manx/Aztec C (scanf() не понимает [0-9])
%f\ %l\ %t%*[^0-9]%n:\ %mдля SAS C
\»%f\»\\,%*[^0-9]%l:\ %mдля обычного компилятора C
%f:%l:\ %mдля GCC
%f:%l:\ %m,%Dgmake[%*\\d]:\ Entering\ directory\ `%f’,
%Dgmake[%*\\d]:\ Leaving\ directory\ `%f’
для GCC с gmake (в одну строку!)
%f(%l)\ :\ %*[^:]:\ %mдля старого компилятора SCO C (до OS5)
%f(%l)\ :\ %t%*[^0-9]%n:\ %mто же, с типом и номером ошибки
%f:%l:\ %m,In\ file\ included\ from\ %f:%l:,\^I\^Ifrom\ %f:%l%mдля GCC, с дополнительными данными

Более сложные примеры, с обработкой многострочных сообщений, приводятся ниже, см. |формат_ошибок-Jikes| и |формат_ошибок-LaTeX|.

Обратите внимание на использование символа обратной косой черты перед пробелом и двойной кавычкой. Это требуется для корректного ввода команды :set. Перед запятой используется два символа обратной косой: один для команды :set и один для того, чтобы запятая не распознавалась ошибочно как разделитель записей в формате ошибок.

Обработка сообщений с помощью программы-фильтра

Если ваш компилятор выводит сообщения, формат которых не укладывается в используемое вами описание формата ошибок, то вы можете также написать программу, которая бы обрабатывала поступающие от компилятора сообщения и переводила бы их в нужный формат. Такую программу можно использовать при выполнении команды :make, слегка изменив значение опции ‘makeprg’. Например:

:set mp=make\ \\\|&\ error_filter

Символы обратной косой перед символом трубы необходимы, чтобы он не воспринимался в качестве символа-разделителя команд. Обратная косая черта перед каждым пробелом необходима для выполнения правил ввода команды :set.

8. Стек каталогов

В режиме быстрых исправлений поддерживается стек всех каталогов, имена которых получены при разборе вывода команды make. При использовании GNU make это довольно просто, поскольку эта программа всегда выводит абсолютные пути для каждого каталога, который она посещает во время работы, независимо от того, происходит ли это при выполнении команды ‘cd’ в makefile или с помощью аргумента «-C каталог» (переход в каталог перед чтением makefile). При работе с GNU make также может оказаться полезным использование аргумента -w, который позволяет выводить имя каталога до и после работы.

Поддержка стека имён каталогов может оказаться более сложной задачей, если вы используете не GNU make, а какую-либо другую программу. Например, AIX make вообще не выводит никакой информации о рабочем каталоге. В таких ситуациях вам следует доработать сам makefile. В makefile для lesstiff имеется команда, которая выводит сообщение «Making {цель} in {каталог}». Однако, в этом случае проблема заключается в том, что программа не выводит информацию о выходе из каталога и не печатает абсолютные пути.

Чтобы разрешить проблему с относительными путями и отсутствующими сообщениями о выходе из каталога, Vim использует следующий алгоритм:

  1. Выполняется проверка, не является ли данный каталог подкаталогом текущего каталога. Если результат положительный, то каталог сохраняется в стеке в качестве текущего каталога.
  2. В противном случае проверяется, не является ли данный каталог подкаталогом одного из родительских каталогов текущего каталога.
  3. Если каталог не может быть найден, то считается, что он является подкаталогом текущего рабочего каталога Vim.

Кроме того, выполняется проверка существования каждого файла в определённом таким образом каталоге. Если эта проверка не проходит, то выполняется поиск во всех других каталогах в стеке каталогов (но НЕ в поддереве каталогов!). Если же файлы всё таки не найдены, то считается, что это подкаталог текущего рабочего каталога Vim.

Этот алгоритм не лишён недостатков. Приведём пример, в котором предполагается, что команда makе выводит только информацию о переходе в другой каталог в формате «Making all in dir».

  1. Предположим, у вас имеются следующие каталоги и файлы:

    ./dir1
    ./dir1/file1.c
    ./file1.c

    Если make обрабатывает каталог «./dir1» перед текущим каталогом и в файле «./file1.c» содержится ошибка, то Vim выполнит загрузку файла «./dir1/file.c».

    Такого рода проблема может быть разрешена только с помощью сообщения о выходе из каталога.

  2. Предположим, у вас имеются следующие каталоги и файлы:

    ./dir1
    ./dir1/dir2
    ./dir2

    В итоге вы получаете следующее:

    Вывод makeКаталоги, как их распознаёт Vim
    Making all in dir1./dir1
    Making all in dir2./dir1/dir2
    Making all in dir2./dir1/dir2

    Такого рода проблемы могут быть разрешены путём вывода абсолютных путей в сообщениях о переходе в каталог, или при помощи сообщений о выходе из каталога.

Чтобы избежать этих проблем стремитесь всегда выводить абсолютные пути к каталогам и сообщения о выходе из каталога.

Пример для Makefile:

Unix:

libs:
for dn in $(LIBDIRS); do \
(cd $$dn; echo "Entering dir '$$(pwd)'"; make); \
echo "Leaving dir"; \
done

Добавьте

%DEntering\ dir\ '%f',%XLeaving\ dir

к значению опции ‘errorformat’ для обработки указанных выше сообщений.

Обратите внимание, что Vim не проверяет, является ли имя каталога в сообщении о выходе из каталога текущим рабочим каталогом. Благодаря этому вы можете использовать простое сообщение о выходе из каталога, вроде «Leaving dir».

9. Частные случаи формата ошибок

Jikes(TM), компилятор исходного кода в байт-код Java от IBM Research, использует простые многострочные сообщения об ошибках.

Ниже приводится значение опции ‘errorformat’ для обработки этих сообщений. Это значение можно поместить в пользовательский сценарий |vimrc|, чтобы использовать его вместо принятого в Vim по умолчанию, либо использовать его наравне со значением по умолчанию, если воспользоваться командой |:set+=|.

:set efm=%A%f:%l:%c:%*\\d:%*\\d:,
\%C%*\\s%trror:%m,
\%+C%*[^:]%trror:%m,
\%C%*\\s%tarning:%m,
\%C%m

Jikes(TM) также может выводить однострочные сообщения об ошибках, если он вызывается с аргументом «+E». В этой ситуации можно использовать следующее значение:

:set efm=%f:%l:%v:%*\\d:%*\\d:%*\\s%m

Сообщается, что опция ‘errorformat’ прекрасно работает с компилятором javac, который выводит строку с символом «^», чтобы указать на колонку, в которой замечена ошибка:

:set efm=%A%f:%l:\ %m,%-Z%p^,%-C%.%#

или:

:set efm=%A%f:%l:\ %m,%+Z%p^,%+C%.%#,%-G%.%#

Для использования в ant (http://jakarta.apache.org/) указанный выше формат ошибок необходимо видоизменить, с тем, чтобы отбрасывать строку [javac], которая выводится перед каждой строкой вывода компилятора javac:

:set efm=%A\ %#[javac]\ %f:%l:\ %m,%-Z\ %#[javac]\ %p^,%-C%.%#

Опция ‘errorformat’ может также быть настроена таким образом, чтобы обрабатывать вывод ant при использовании как javac, так и jikes в качестве компилятора Java. Если вы используете jikes, то вам следует сообщить ant, что необходимо использовать аргумент jikes +E, который заставляет этот компилятор использовать однострочные сообщения. Этого можно добиться путём добавления следующих строк в файл build.xml:

<property name = "build.compiler"       value = "jikes"/>
<property name = "build.compiler.emacs" value = "true"/>

Опция ‘errorformat’, которая сможет работать с ant как при использовании javac, так и jikes, выглядит так:

:set efm=\ %#[javac]\ %#%f:%l:%c:%*\\d:%*\\d:\ %t%[%^:]%#:%m,
\%A\ %#[javac]\ %f:%l:\ %m,%-Z\ %#[javac]\ %p^,%-C%.%#

Разбор сообщений об ошибках jade (см. http://www.jclark.com/) выполняется элементарно:

:set efm=jade:%f:%l:%c:%t:%m

Ниже приводится пример использования опции ‘errorformat’ для разбора многострочных сообщений об ошибках, создаваемых системой типографского набора (La)TeX. При выполнении команд «:clist», «:cc» и т.п. многострочные сообщения отображаются в одну строку, причём начальные пробелы удаляются. Вы сможете легко приспособить формат ошибок LaTeX к выводу любого компилятора, использующего многострочные сообщения об ошибках.

Команды, показанные ниже, следует поместить в файле |vimrc|, либо в каком-нибудь другом сценарии Vim, например в сценарии, содержащем другие команды относящиеся к работе с файлами LaTeX, который будет загружаться только в тех случаях, когда это необходимо. Скопируйте все строки, приведённые в этом примере в указанном порядке в сценарий, после чего удалите строки комментариев. Об использовании ‘\’ в начале строки см. в разделе |продолжение_строки|.

        Прежде всего настроим значение опции 'makeprg' таким образом,
чтобы LaTeX не останавливался после появления первой ошибки, а
продолжал поиск ошибок в исходных файлах:
>
:set makeprg=latex\ \\\\nonstopmode\ \\\\input\\{$*}
<
Обработаем начало многострочных сообщений об ошибках:
>
:set efm=%E!\ LaTeX\ %trror:\ %m,
\%E!\ %m,
<
Далее займёмся многострочными предупреждениями: первые два из
них также будут содержать номер строки. Значения некоторых
использованных регулярных выражений:

- шаблону "%.%#" (".*") соответствует строка, возможно
пустая
- шаблону "%*\\d" ("\d\+") соответствует число
>
\%+WLaTeX\ %.%#Warning:\ %.%#line\ %l%.%#,
\%+W%.%#\ at\ lines\ %l--%*\\d,
\%WLaTeX\ %.%#Warning:\ %m,

< Возможные продолжения сообщений об ошибках и предупреждений;
первое также включает номер строки:
>
\%Cl.%l\ %m,
\%+C\ \ %m.,
\%+C%.%#-%.%#,
\%+C%.%#[]%.%#,
\%+C[]%.%#,
\%+C%.%#%[{}\\]%.%#,
\%+C<%.%#>%.%#,
\%C\ \ %m,
<
Строки, соответствующие следующим шаблонам, не содержать
важной информации; мы не будем включать их в сообщения об
ошибках:
>
\%-GSee\ the\ LaTeX%m,
\%-GType\ \ H\ <return>%m,
\%-G\ ...%.%#,
\%-G%.%#\ (C)\ %.%#,
\%-G(see\ the\ transcript%.%#),

< Кроме того, исключим любые пустые строки или строки, состоящие
из одних пробелов:
>
\%-G\\s%#,

< В выводе LaTeX не указываются имена исходных файлов, где
обнаружены ошибки, в каждой строке. Вместо этого, имена файлов
выводятся глобально, заключённые в круглые скобки.
Шаблоны, приведённые ниже, применяются для определения таких
имён и сохранения их во внутреннем стеке. Данные шаблоны могут
по порядку просматривать одну и ту же строку ввода; элемент
преобразования "%r" указывает, что "остальная" часть строки
будет разобрана при следующем проходе, до тех пор, пока не
будет разобрана вся строка целиком.

Пропуск имён файлов, окружённых круглыми скобками '('...')';
имена этих файлов не помещаются в стек, поскольку такие файлы
скорее всего не содержат ошибок:
>
\%+O(%f)%r,
<
Поместить имя файла, данное после '(', в стек:
>
\%+P(%f%r,
\%+P\ %\\=(%f%r,
\%+P%*[^()](%f%r,
\%+P[%\\d%[^()]%#(%f%r,

< Снять последнее сохранённое имя файла из стека, при
обнаружении ')':
>
\%+Q)%r,
\%+Q%*[^()])%r,
\%+Q[%\\d%*[^()])%r

В некоторых случаях имена файлов в журнале ошибок LaTeX не могут быть разобраны правильно. Это может быть связано с несбалансированными скобками в выводе. Приведённый выше пример пытается отловить только наиболее значимые события. Вы можете доработать указанные настройки в соответствии с собственными требованиями, например исключить из вывода все назойливые предупреждения «Overfull …».

Вместо чтения вывода компилятора LaTeX можно также напрямую читать протоколы *.log, которые создаются при работе LaTeX. В них может содержаться более полезная информация о возможных причинах возникновения ошибки. С другой стороны, для полноценного разбора такого сложного файла лучше всего использовать внешнюю программу-фильтр. Информация об использовании таких фильтров в Vim приведена выше.

В каталоге $VIMRUNTIME/tools можно найти сценарий efm_perl.pl, который фильтрует сообщения об ошибках Perl, приводя их в формат, удобоваримый для режима быстрых исправлений. О том, как использовать этот сценарий, написано в комментариях в самом начале файла.