Павел Ерофеев
Я занимаюсь разработкой на прикладных языках. Пишу про интересные мне вещи из мира IT и не только.
15 марта 2017 Разработка

Работа в Vim с проектами в разных кодировках

Часто такое бывает, что в большой и развивающейся компании с многолетней историей скопилось множество древнего кода. От мелких вспомогательных скриптов до больших развесистых проектов, от которых, по историческим причинам, очень сложно избавиться. Если проект старше 5 лет, писался он на дремучих версиях любимого старожилами языка, то проблема кодировок не пройдёт мимо вас.
 
Дело осложняет и то, что люди в это время продолжали работать, производя новые проекты уже с модным UTF-8, в то время когда старые клиенты уже не могут себе позволить отказ от привычного cp1251. И, по естественным причинами, вы будете поддерживать их оба. 
 
Далее я постараюсь изложить варианты решения задачи. Вы можете выбрать подходящий вам или предложить свой вариант. Все они решают поставленную задачу и имеют права на жизнь.
 
Итак, у вас на руках есть любимый vim, за спиной остались все gui редакторы, и вот вам нужно как-то работать. 
 
Сразу оговорюсь, рассказ идет с позиции того, что работаете вы на некой удалённой машине для разработки, и это накладывает определенные сложности на рабочий процесс с локальной машины.
 
Давайте рассмотрим варианты.

Изменения локали на cp1251

Достаточно радикальный метод. Идея в том, чтобы из-под пользователя, в котором работаете, полностью заменить кодировку на ту, что у вас в проекте.

#~/.bashrc
export LANG=ru_RU.CP1251

Проблемы следующие:

1) Кодировок у вас несколько, и каждый раз менять локаль будет проблематично;
2) Если вы пользуетесь различными плагинами в vim, которые напрямую используют возможности спецсимволов в таблице utf-8 ( vim-airline в частности ), то результат будет смотреться грустно
3) Придётся перенастроить кодировку в терминальной программе, что так же накроет тазом ваши другие проекты, на локальной машине
 
Перспектива так себе. Идём дальше.

Не работать в vim

Современные IDE достаточно умные и способны делать многие вещи, в том числе и кодирование на лету. Естественно, вы платитесь мобильностью, кастомизацией и, нередко, деньгами за лицензию. Всё это только для того что бы начать работать с проектом. 
Да, конечно, во многих IDE есть поддержка, так называемого, vim-mode, где вам предоставляются аналогичные горячие клавиши для работы с кодом. Но всё это лишь в рамках конкретно выбранной вами IDE. 
Отдельный квест - это его настройка. Многочисленные опции, запакованные в маленькие окна с мириадами вкладок. Я думаю, активные пользователи IDE, к примеру Eclipse, меня поймут.
На вас будут накладываться сложности с синхронизацией, тестированием в рабочей среде ( мы рассматриваем вариант, где у вас нет возможности полноценно развернуть рабочую среду на локальной машине ), синхронизацией файлов и прочие радости деплоя. Конечно, наверняка IDE всё это уже умеет, но, опять же, борьба с меню настроек. И если какой то функционал не будет работать, вы не сможете никак повлиять на его работоспособность, кроме как создать тикет в службе поддержки. 
Выводы: долго и дорого.

Переключение по горячим клавишам

Это уже более vim-way вариант. Данное решение я нашел в интернетах в поисках людей с похожей проблемой. 
Идея простая - написать некий контекстный выбор кодировок и развесить на горячие клавиши.
 
" в ~/.vimrc

menu Encoding.koi8-r :e ++enc=koi8-r ++ff=unix<CR>
menu Encoding.cp1251 :e ++enc=cp1251 ++ff=dos<CR>
menu Encoding.cp866  :e ++enc=cp866 ++ff=dos<CR>
menu Encoding.utf-8  :e ++enc=utf8<CR>
menu Encoding.koi8-u :e ++enc=koi8-u ++ff=unix<CR>

nmap <F3> :emenu Encoding.utf-8<CR>
nmap <F4> :emenu Encoding.cp1251<CR>

map <F8> :emenu Encoding.​

И вроде всё в порядке. Открываем файл, нажимаем F4 для декодирования в cp1251 и работаем. 

Минусы есть. Очевидно присутствует механическая работа и велик шанс на ошибку сохранения не в том формате. При наличие систем контроля версий откат не будет большой проблемой, но осадочек остаётся.

Попытка автоматизации

Решение задачи плавно перетекло из предложенного выше варианта. По сути, в рамках моей задачи, деление на кодировки завязано, в том числе, и на проектах. То есть, можно смело утверждать, что определенный проект закодирован определенной кодировкой. Это и послужит нашим условием.
Что бы добавить гибкости решению, пути будут проверяться регулярным выражением.
 
"~/.vimrc
function ChangeEncoding()
    let current_dir = expand("%:p:h")

    " словарь для кодировок: { regexp: encoding }
    let re_patterns = {
        \ 'some\.project\.name'     : "cp1251",
        \ 'another\.project\.regexp': "cp1251",
        \ }

    for [ pattern, encoding_name ] in items( re_patterns )
        let match = matchstr( current_dir, pattern )
        if match != ''
            execute 'e ++enc=' . encoding_name . ' %:p'
            " возвращаем подсветку синтаксиса 
            syntax enable
        endif
    endfor

endfunction

" функция будет выполнятся каждый раз 
" при создание нового или открытие существующего файла
autocmd BufNewFile,BufReadPost * call ChangeEncoding()​
На выходе, при создании или открытии файла, если его директория будет удовлетворять заданному регулярному выражению, он будет декодирован в заданную кодировку. Список правил нужно вносить в переменную re_patterns, представляющий собой хеш, где ключ - это регулярное выражения, а значение - желаемая кодировка.

Бонус

Вывод git diff с комментариями в cp1251:

#~/.bashrc
gdiff() { git diff $1 | iconv -f cp1251 -t utf8 }
​
# Пример использования
$ gdiff HEAD~

Поиск grep’ом по файлам с кодировокой cp1251. Создадим новый баш скрипт ~/bin/greprc.sh:

#!/bin/bash
Phrase="$(echo "$1" | iconv -f utf8 -t cp1251)"
export LANG="th_TH.TIS-620" LC_CTYPE="th_TH.TIS-620"
export LC_COLLATE='ru_RU.CP1251'
grep -rni "$Phrase" "$2" | iconv -f cp1251 -t utf8 | grep --color -E "$1"
# Добавим alias в ~/.bashrc, при необходимости
alias greprc="~/bin/greprc.sh"

# Пример использования
$ greprc "поисковая фраза" .