...

суббота, 5 октября 2013 г.

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

Имеется инструментальный компьютер с 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:\bin\make.exe\ $*\ \\\|c:\\bin\\iconv\\iconv.exe\ -c\ -f\ CP866\ -t\ CP1251




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

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий