VIM, Windows, quickfix — борьба с кодировкой компилятора

в 15:59, , рубрики: Без рубрики

Имеется инструментальный компьютер с Windows и ассемблер для одного замечательного, но специфического процессора. Для удобства была настроена рабочая среда на базе VIM — подсветка синтаксиса, вызов препроцессора, ассемблера и линкера через make с разбором сообщений об ошибках, ctags с прыганьем по коду. Но есть нюанс — выдача сообщений :make идет на русском. Разумеется, на консольном русском, cp866. А исходники на ассемблере и локаль VIM — cp1251. И как же быть?

Приводить всю кодировку — текстовых файлов и локали VIM — к cp866 из-за одного компилятора? Даже не смешно. Догадываться из контекста о смысле ошибки? До сих пор так и делал. Надоело. Надо разобраться, потратить пару вечеров и забыть об этом досадном недоразумении.

Система quickfix работает: вывод make попадает в файл ошибок, из него выковыриваются номера неправильных строк кода и вид ошибки, курсор после компиляции сразу ставится в нужное место. Вот только понять суть ошибки непросто:

рис. 1

И команда :cope не помогает в чтении, хотя по ошибочным строкам кода прыгать позволяет:

рис. 2

Примечание: Для примера использована опция makeef=error.err (чтобы сократить отображаемую строку команды make), хотя обычно она у меня пустая по умолчанию, и файл ошибок make создается в TEMP.

Казалось бы — ерунда какая! Ща поправим опцию encoding… Ан нет — она, зараза, глобальная. Поэтому кодировка поменяется везде, во всех открытых буферах, включая окно исходника. Комментариям на русском хана. Сообщениям самого VIM — тоже.

рис. 3рис. 4

Можно попробовать :set encoding=cp866 вручную, можно воткнуть это в $VIM/vimfile/ftplugin/qf.vim, чтобы срабатывало по команде :cope. Можно даже попробовать :setlocal — не поможет.

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

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

Получается, при открытии буфера qf по команде :cope файл ошибок уже не существует. Можно попробовать вернуть его неизящным способом — в $VIM/vimfile/ftplugin/qf.vim записать

setlocal encoding=cp866
setlocal fileencoding=cp1251
w! ./error.err
setlocal encoding=cp1251

То есть, при открытии буфера qf командой :cope сменить кодировку, вставить кодировку для файла, записать новый файл ошибок и вернуть кодировку назад. Кстати, local здесть можно опустить — все равно бесполезно. Теперь можно руками установить новый файл ошибок :cf ./error.err.

рис. 5

Некрасиво, неудобно и куча лишних движений руками. Получается, после каждой компиляции надо сначала :cope, потом :ccl, потом :cf ./error.err и снова :cope. Если попытаться вставить это все в qf.vim, то возникнет бешеная рекурсия, которую VIM удавит самостоятельно. И :cope ведет себя как-то странно: лишние палки в начале строк добавляются, даже неохота разбираться в причинах.

рис. 6

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

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

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

Здесь {значение_makeprg} это строковое значение опции 'makeprg'. Вы можете использовать любую необходимую программу, не только «make».

Восклицательный знак ":!{команда}" требует выполнить указанную {команду} в оболочке. Значит, отловить событие, возникающее с файлом при выполнении :make, не получится. Надежда использовать автокоманду рухнула. Но сдаваться рано. Посмотрим внимательнее на

'shellpipe' 'sp'	строка (по умолчанию: ">", "| tee", "|& tee" или
			        "2>&1| tee")
			глобальная опция
	Опция используется для указания строки, которая заставляет оболочку
	помещать вывод команды ":make" в файл ошибок. 

Оказалось, у моего VIM по умолчанию shellpipe=>%s 2>$1. Похоже, есть возможность взять внешнюю утилиту командной строки и как-нибудь засунуть ее в серединку этой самой shellpipe.

Возьмем iconv, которая есть в проекте GnuWin32 на страничке libiconv. Она может принимать данные из файла или из консольного потока. Я положил ее в C:bin, где уже лежат make, ctags, 7z, пара вспомогательных командных файлов и пара-тройка dll. Не забываем и про зависимости (Dependencies, а именно libintl3.dll, лежит на странице утилиты).

Поигрался с командной строкой отдельно от VIM, чтобы разобраться со способом подачи данных на вход и с опциями. Потом попробовал то же, но уже в командной строке VIM. Где-то со второй или третьей попытки получилось нечто такое:

:set shellpipe=|c:\bin\iconv\iconv.exe -c -f CP866 -t CP1251>%s 2>&1

Символ вертикальной черты, пробелы и обратные слэши в полном пути к утилите экранируются обратными слэшами. Насчет полного пути — можно, конечно, так не извращаться, а прописать путь к iconv в PATH, но лично я с некоторых пор не люблю этот способ: четыре IDE/CAD на рабочем компьютере имеют в своем составе make, не вполне совместимые между собой, и все прописаны в PATH. Полные пути надежнее.

В общем, заработало. Указанную строчку я записал (без двоеточего в начале) в файл $VIM/vimfile/ftplugin/a6403.vim. Теперь «труба оболочки» изменяется на перекодирующую только при открытии именно этого хитрого типа файла. Вот результат:

рис. 7рис. 8

Русские комментарии в исходнике читаемы, сообщения об ошибках — тоже, по ошибкам прыгается. Решение не идальное:

  • Пришлось использовать стороннюю утилиту при наличии встроенных средств перекодировки. Отмазка: Не особенно страшно в данном случае — все равно использовать сторонний компилятор, и сторонний ctags, и сторонний make. Ну, появились еще файлики в моем c:bin…
  • Опция shellpipe глобальная, будет применена ко всем буферам. Отмазка: если не открывать файлы этого специфического ассемблера — никто и не почувствует. Если открывать и при этом параллельно работать в других одновременно открытых буферах с другим транслятором — англоговорящим компиляторам будет фиолетово (проверено), русскоговорящим (на cp866) — только на пользу.

Для устранения второго недостатка можно локально (setlocal) переопределить опцию makeprg, добавив после собственно make специальную «заглушку» в виде последовательности "$*", которая будет заменена аргументами командной строки, и вызов iconv. Тогда shellpipe можно не трогать. А вертикальную черту придется экранировать аж тремя обратными слэшами: один для команды :set, второй — чтобы экранировать третий, нужный при разборе команды.

setlocal makeprg=c:binmake.exe $* \|c:\bin\iconv\iconv.exe -c -f CP866 -t CP1251

Более удачного способа в разного рода интернетах я не нашел. Вообще-то, никакого способа не нашел. Может, это мне одному так не повезло? Но если кому-нибудь поможет хотя бы сама идея внедрения в цепочку команды — мне будет приятно.

Автор: RomeoGolf

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js