суббота, 13 августа 2011 г.

vim: практические шаги для комфортного использования 2

Продолжение темы советов по кастомизации vim, которые помогут повысить комфорт при использовании этого замечательного редактора. На этот раз ничего патчить не будем :)

1. Новый маппинг для плагина Mark. Плагин Mark позволяет легко подсвечивать слова или паттерны, соответствующие регулярным выражениям. В отличие от встроенного механизма поиска и подсветки, Mark позволяет одновременно подсвечивать разными цветами несколько слов или паттернов. К сожалению, маппинг, принятый в Mark по умолчанию, на мой взгляд, требует изменения. Именно благодаря неудачному маппингу я некоторое время не пользовался этим плагином, хотя он и был установлен мною собственноручно.

Итак, маппинг по умолчанию: \m - подсветить слово, \n - снять подсветку, \r - ввести паттерн для подсветки. Этот маппинг ужасно тормозил (требовалось ждать около секунды, чтобы изменения вступили в силу), и я сначала думал, что это особенность реализации подсветки в Mark. Но проблема оказалась весьма тонкой. Выяснилось, что маппинг пересекается с маппингом плагина c.vim и еще одного установленного мною плагина. Так, в c.vim определено множество маппингов, начинающихся с \n и \r (\nc, \ns, \ni и т.д.). Таким образом, при вводе символов, соответствующих маппингу Mark, vim некоторое время ждал, а не введет пользователь еще один символ, который можно будет интерпретировать как маппинг из c.vim - это нормальное поведение vim.

Чтобы в следующий раз не наступать на грабли, можно посмотреть все маппинги, начинающиеся с определенного символа, скажем с \n (первый символ \ как таковой в маппинг не входит, так как является спецсимволом - так называемым лидером). Для этого нужно в командной строке vim ввести
:map \n
В новом маппинге я задействовал сочетание клавиш Ctrl-m:
nmap <C-m><C-m> <Plug>MarkSet
nmap <C-m>n     <Plug>MarkClear
nmap <C-m>r     <Plug>MarkRegex
vmap <C-m><C-m> <Plug>MarkSet
vmap <C-m>r     <Plug>MarkRegex
nmap <C-m>*     <Plug>MarkSearchCurrentNext
nmap <C-m>#     <Plug>MarkSearchCurrentPrev
nmap <C-m>/     <Plug>MarkSearchAnyNext
nmap <C-m>?     <Plug>MarkSearchAnyPrev
nmap <C-m>c     <Plug>MarkAllClear
nmap <C-m>t     <Plug>MarkToggle
nmap <C-m>s     :MarkSave<CR>
nmap <C-m>l     :MarkLoad<CR>
В принципе, тут все понятно: вместо \m нужно, удерживая клавишу Ctrl, два раза нажать m, вместо \n - нажать Ctrl-m, а затем n, вместо \r - Ctrl-m, а затем r. Кроме того, я добавил маппинг для переключения подсветки, которого нет по умолчанию: <Ctrl-m>t.

Update. По иронии судьбы Ctrl-m тоже плохой маппинг, так как это сочетание в vim полностью соответствует клавише Enter. Так что если оставить такой маппинг, то Enter в нормальном режиме будет так же тормозить, как раньше тормозил сам Mark. Клавиша Enter потенциально используется в vim достаточно часто, например в плагине taglist при переходе к метке под курсором. Поэтому в приведенных выше маппингах следует заменить все вхождения <C-m> на какое-нибудь другое, неиспользуемое сочетание клавиш, например на <C-k>.

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

Здесь я покажу, как это сделать с помощью простого маппинга ,s (запятая - s), который будет включать или выключать соответствующую подсветку в зависимости от текущего состояния (т.е. реализует то, что в английском языке называется простым словом toggle).

Прежде всего объявим переменную g:RightBorder, которая будет содержать максимальную разрешенную длину строки (т.е. 80), и новую область подсветки FormatHints, с помощью которой мы будем подсвечивать код, не соответствующий стандарту:
let g:RightBorder = 80
highlight FormatHints term=standout ctermfg=250 ctermbg=229
            \ guifg=Red guibg=White
Определение FormatHints разделено на две строки как результат следования внутренней дисциплине и установленному правилу непревышения строкой 80 символов :) Здесь с помощью аргументов ctermfg и ctermbg определена подсветка для терминалов с поддержкой 256 цветов, которая, на мой взгляд, вполне подходит для темных цветовых схем (светло-серый текст на бледно-желтом фоне: в конце концов мы подсвечиваем "запрещенные" участки кода, поэтому светлое на светлом здесь вполне уместно). Поскольку я не использую GUI vim, значения для guibg и guifg выбраны произвольно.

Теперь определим функции для включения и выключения подсветки:
fun<SID>formathints()
    if !exists("w:m1") || w:m1 == 0
        let w:m1 = matchadd('FormatHints''\%>'.g:RightBorder.'v.\+'-1)
        let w:m2 = matchadd('FormatHints''[\t]'-1)
        let w:m3 = matchadd('FormatHints''[\t \r]\+$'-1)
    endif
endfun
fun<SID>formathints_hide()
    if exists("w:m1") && w:m1 > 0
        silentcall matchdelete(w:m1)
        silentcall matchdelete(w:m2)
        silentcall matchdelete(w:m3)
        let w:m1 = 0
        let w:m2 = 0
        let w:m3 = 0
    endif
endfun
С помощью переменных w:m1, w:m2 и w:m3 определяются паттерны, соответствующие областям кода, которые мы хотим подсвечивать. Приставка к переменной w: в vim означает, что будут созданы отдельные инстансы этой переменной в каждом отдельном окне vim, соответственно, наше переключение подсветки будет работать только в активном окне.

Теперь определим команды для включения и выключения подсветки:
command -bar ShowFormatHints call <SID>formathints()
command -bar HideFormatHints call <SID>formathints_hide()
Опция -bar нужна для использования команд в составе сложных выражений, содержащих элементы <Bar> в следующем итоговом маппинге:
nmap <silent> ,s :if !exists("w:m1") <Bar><Bar> w:m1 == 0 <Bar>
            \ ShowFormatHints <Bar> echo "Show format hints" <Bar> else <Bar>
            \ HideFormatHints <Bar> echo "Hide format hints" <Bar> endif<CR>
Напомним, что элементы <Bar> в маппингах расширяются в символ |, следовательно, без учета echo "Show format hints" и echo "Hide format hints", этому маппингу будет соответствовать команда
:if !exists("w:m1") || w:m1 == 0 | ShowFormatHints | else | HideFormatHints | endif
Если оставить этот маппинг без модификатора <silent> (и без дополнительных команд echo), то эта малоинформативная строка будет выводиться в командную строку vim каждый раз при нажатии ,s - добавление <silent> и команд echo делают использование этого маппинга намного более удобным за счет более информативных сообщений.

Итак, цель достигнута. Теперь при нажатии на клавиатуре комбинации ,s будет происходить переключение подсветки проблемных участков кода с выводом сопутствующей информации в командную строку vim.

3. Маппинг для переключения ограничительной колонки (colorcolumn). Цель почти такая же, как и в предыдущем пункте: создать маппинг для включения/выключения цветовой колонки, которая будет располагаться на 81-ом знаке и предупреждать пользователя о возможном нарушении стандарта кодирования, ограничивающего длину строк 80 знаками. Но реализовать эту задачу теперь проще:
nmap <silent> ,m :if &colorcolumn == 81 <Bar> set colorcolumn= <Bar>
            \ elseif !&colorcolumn <Bar> set colorcolumn=81 <Bar> endif<CR>
Новый маппинг соответствует комбинации клавиш ,m. На этот раз нет смысла выводить сообщения на дисплей, поскольку наличие или отсутствие ограничительной колонки заметно и так.

4. Маппинг для переключения режима paste. Режим paste бывает полезен, когда нужно вставить из буфера обмена многострочный участок кода без дополнительного форматирования. Маппинг соответствует комбинации клавиш ,p и выглядит следующим образом:
nmap <silent> ,p :set paste! <Bar> set paste?<CR>
Команда set paste! переключает (toggle) текущий режим paste, а команда set paste? выводит его значение на дисплей.

12 комментариев:

  1. Спасибо за статью! Попробовал у себя добавить в .vimrc функцию из пункта два. Не хочет работать. Использую вызов через имя. Так как ваш мапинг у меня задействован другим плагином.

    ОтветитьУдалить
    Ответы
    1. Тогда используйте другой маппинг вместо ',s', например ',h'. Кстати, сейчас я использую именно ',h' для этого. Маппинг ',s' я задействовал для переключения проверки правописания:

      nmap <silent> ,s :set spell! <Bar> set spell?<CR>

      Удалить
    2. С маппингом разобрался. Выводит в статусною строку сообщение о включении и выключении функции, но визуально подсведки не вижу. Где может быть ошибка?

      Удалить
    3. 1. Вы используете GUI или терминал? (В зависимости от ответа будут различаться цвета подсветки).
      2. Если терминал, то поддерживает ли ваш терминал 256 цветов? Для проверки введите в определении FormatHints вместо ctermfg=250 и ctermfg=229 какие-нибудь стандартные цвета, например ctermfg=Red ctermbg=White. Если цвета появились, то ваш терминал не поддерживает 256 цветов и вам стОит включить эту поддержку, так как гамма из 8 или 16 цветов выглядит скудно (ну здесь на вкус и цвет).
      3. Если пункт 2 не сработал, то убедитесь, что в вашем тексте есть символы табуляции и строки длиннее 80 символов.

      Удалить
    4. У меня gvim. цвета guibg=White guibg=Red должны быть хорошо видны в моей теме. Строки с символами больше 80 есть, но подсвечиватся.

      Удалить
    5. Также попробовал в терминале и голой консоли. Результат негативный

      Удалить
    6. Я не использую gvim, но решил проверить, сработает ли у меня. Работает. За исключением того, что я опечатался и оба раза определил guibg. Должно быть guibg=White guifg=Red (красный текст на белом фоне)

      Удалить
    7. Проблема все таки из подсведкой. При замене 'FormatHints' на 'ErrorMsg'. Получается отобразить. Спс за полезную функцию. Она работает.Пусть красным цветом. Я уже сам попытаюст разобраться что не так у моего вима с подсветкой.

      Удалить
    8. Отсюда: http://vim.wikia.com/wiki/Highlight_unwanted_spaces:

      However, be aware that future colorscheme commands may clear all user-defined highlight groups.

      Возможно, это ваш случай и FormatHints затирается вашей цветовой схемой. Там же предлагают сделать так:

      autocmd ColorScheme * highlight FormatHints guifg=Red guibg=White

      (в общем то же определение для autocmd)

      Удалить
    9. Спасибо за помощь. Теперь все получилось! Специально смотрел цветовую схему перед этим. Но не нашел там такой группы.
      Функию немного упростил. Буду теперь использовтаь только для подсветки "лишних" знаков.

      Удалить
    10. А там и не может быть такой группы. Это пользовательская группа и она определена только в данном скрипте.

      Удалить
    11. Следствие показало, что пользовательские группы подсветки, такие как FormatHints, могут действительно удаляться при чтении цветовых схем, например это происходит, если использовать схему lucius. Выход из ситуации: в .vimrc определить не только цветовую группу, но и автокоманду для ее восстановления:

      highlight FormatHints term=standout
      \ ctermfg=250 ctermbg=229 guifg=Red guibg=White
      " restore FormatHints after switching to a color scheme that may clear it off
      autocmd ColorScheme * highlight FormatHints term=standout
      \ ctermfg=250 ctermbg=229 guifg=Red guibg=White

      Если в вашем .vimrc определение цветовой схемы (с помощью команды colorscheme) находится ниже по тексту, чем этот код, то достаточно определить только автокоманду.

      Удалить