Подсветка синтаксиса в VIM: полное погружение

в 13:11, , рубрики: vim, метки:

VIM примечателен тем, как просто и гибко его можно настраивать. В этой статье я подробно опишу, как легко и просто можно создавать собственные правила подсветки синтаксиса на различных примерах.

Да, на хабре об этом уже была cтатья, однако в ней тема была рассмотрена очень уж поверхностно, а я постараюсь разобраться в этом подробно. Будут рассмотрены от самых простых примеров, включая улучшения подсветки html для подсветки тэгов хабраразметки, до создания подсветки для полноценных языков программирования, с «контекстно-зависимой» подсветкой синтаксиса.

Что особенно удобно — играться с подсветкой можно без каких либо дополнительных движений прямо во время редактирования вашего файла, сразу же наблюдая результат. Так что всем предлагаю открыть VIM и пробовать примеры, приведенные в моей статье.

Подсветка ключевых слов

Для начала рассмотрим простой пример: вы редактируете текст и для себя отсавляете в нем записи вида «todo — переписать это предложение что бы было легче читать». Давайте успростим себе чтение текста и будем подсвечивать. Для этого в командном режиме введите

:syn keyword Keyword todo

Вуаля! Теперь мы в редакторе видимо что-то вроде:

Подсветка синтаксиса в VIM: полное погружение

Команда syn(syntax) с первым параметром keyword означает следующее: подсветить слово todo стилем Keyword. Цвет этого стиля зависит от вашей цветовой схемы. Можно перечислять сразу много ключевых слов:

:syn keyword Keyword todo rewrite done

Подсветка по регулярным выраженям

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

:syn match AnyHighlightStyle /herer-is-regexp/

Главное что нужно помнить, что синтаксис вимовских регулярных выражений существенно отличается от Perl-like регулярных выражений (которым многие привыкли):

Perl-like VIM
oneOrMany+ oneOrMany+
(group) (group)
(optional) (optional)=
(notSoManyTimes){2, 4} (notSoManyTimes){2, 4}
something(?=lookAhead) something(lookAhead)@=
something(?!shouldNotBeThere) something(shouldNotBeThere)@!
(?<=lookBackward)something (lookBackword)@<=ssomething

В качестве первого примера рассмотрим подсветку чисел:

:syn match Float /d+(.d)=/

Подсветка синтаксиса в VIM: полное погружение

Теперь рассмотрим пример посложнее — давайте подсветим вызовы функций, которы определяются следующим образом: идентификатор, за которым следует открывающаяся скобка (, но саму скобку подсвечивать не надо. Для этого воспольземся look-ahead конструкцией ze, которая требует наличия атома справа, однако не включает его в результат сопоставления (таким образом круглая скобка не будет подсвечена цветом функции):

:syn match Function /w+(()@=/

Подсветка синтаксиса в VIM: полное погружение

Или давайте подсветим вызов конструктора: new SomeClassName. Для этого воспользуется конструкцией, схожей с ze, только сканирующая «назад»:zs

:syn match Function /(news+)@<=w+/

Подсветка регионов

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

:syn region SomeHighlightStyle start=/start-regexp/ end=/end-regexp/ skip=/regexp-to-skip-and-not-treat-as-an-end-regexp/

Рассмотрим его на пример. Для начала подсветим C-style многострочные комментарии:

:syn region Comment start=//*/ end=/*//

Тпереь рассмотрим пример чуть посложнее: давайте подсветим строку в ограниченную двойным кавычками:

:syn region String start=/"/ end=/"/

Однако в такой конструкции есть недостаток — мы не можем включить в строку сам символ ". Давайте исправим этот недостаток и сделаем так, чтобы заэкранированные ковычки не считались концом строки. Именно дя этого и создан параметр skip, которы «проглатывает» выражение, не отдавая его проверке на конец региона:

:syn region String start=/"/ skip=/\"/ end=/"/

Подсветка синтаксиса в VIM: полное погружение

Теперь работает как надо.

«Складывание» (folding) регионов

Vim позволяет «сворачивать» блоки (например длинные комментарии или блоки кода). Для этого у команды :syn есть опциональый параметр fold. Так что если вы хотите позволить пользователю сворачивать комментарии, то просто добавте fold в конец команды syn:

:syn region Comment start=//*/ end=/*// fold

Нужно только убедиться, что для «складывания» используется информация от подсветки синтаксиса:

set foldmethod=syntax

Для того, чтобы описать сворачивание содержимое {} скобок в коде, но при этом оставить подсветку кода внутри (которую по умолчанию «съест» :syn region), можно воспользоваться параметром transparent у :syn region:

:syn region CodeBlock start=/{/ end=/}/ transparent fold

В этом случаи это правило не изменит внешний вид блока между {}, но даст возможность его сворачивать.

Стили подсветки

Второй аргумент команды :syn — стиль, которым надо разметить, может быть любой строчкой. И по-хорошему, для того чтобы разделить «структуру» и «представление», в :syn в качестве стилей указывают «структурные названия» с префиксом язык, например:

:syn keyword pascalKeyword begin end var procedure function
:syn keyword pascalBuiltinFunction WriteLn ReadLn Assign 

А потом сопоставляют эти стили с стилями, описываемым в цветовых схемах VIM при помощи команды hi (hilight):

:hi link pascalKeyword Keyword
:hi link pascalBuiltinFunction Keyword

Вложенные блоки

Теперь давайте рассмотрим следующий пример: мы хотим в комментариях выделять свои ключевые слова: TODO, NOTE, которые часто оставляются программистами. Для этого в VIM есть два параметра contained и contains у конструкци :syn. contained означает что правило подсветки не применяется «в глобальном» регионе. А contains=Style1,Style2 означает, что внутри правила стоит искать правила, описывющие стиль Style1 и Style2. Посмотрим к это выглядит:

:syn keyword CommentKeyword TODO NOTE contained
:syn region Comment  start=//*/ end=/*// contains CommentKeyword
:hi link CommentKeyword Keyword

Таким образом вне комметариев TODO и NOTE подсвечиваться не будут, а в комментариях будут подсвечиваться только они.

«Содержать» можно не только ключевые слова, но и любые правила подсветки. Например можно выделить в строке экранируемые символы: n, t, r, b:

:syn region String start=/"/ skip=/\"/ end=/"/ contains=EscapeSymbol
:syn match EscapeSymbol /\[ntrb"]/ contained
:highlight link EscapeSymbol Keyword

Подсветка синтаксиса в VIM: полное погружение

Практичный пример: подсветка хабратэгов в .html файлах

Если писать хабрапосты в VIM в .html файлах, то, к сожалению, хабраспецифичные типа habracut подсвеичваться не будут. Исправим это недоразумение. Для этого залезаем в файл подсветки html: .vim/syntax/html.vim и выясняем, что стиль подсветки имен тэгов называется htmlTagName, так что введя команду:

:syn keyword htmlTagName habracut source hh video slideshow

Подсветка синтаксиса в VIM: полное погружение

Подсветка по расширению

Каждый раз вводить команды для подсветки всех конструкций, безусловно некто требует. Все команды нужно оформить в файле (например) my.vim в директории (обязательно) .vim/syntax/ Теперь для файлов типа my будет автоматически применяться этот ваш скрипт. А что бы тип автоматически проставлялся в зависимости от расширения, необходимо в .vimrc добавить строкчу:

au BufRead,BufNewFile *.my set filetype=my

BufRead и BufNewFile означают применить правило как для открываемых существующих, так и для создаваемых новых файлов.

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

au BufRead,BufNewFile *.dump set filetype=sql

Автоматически при открытии раскрасит вам его как sql файл.

Пример целиком

Теперь рассмотрим простенький пример целиком: давайте раскарасим некотрое подмножество язык javascript:

"В javascript строчки бывают в одинарных и двойных кавычках, и экранировать 
if exists("b:current_syntax") 
  finish
endif

"В javascript строчки бывают в одинарных и двойных кавычках, и экранировать соответсвенно надо разные символы
syn region jsString start=/"/ skip=/\"/ end=/"/ contains=jsEscapeSymbol,jsDoubleQuoteEscape
syn region jsString start=/'/ skip=/\'/ end=/'/ contains=jsEscapeSymbol,jsSingleQuoteEscape

syn match jsEscapeSymbol /\[ntrb]/ contained
syn match jsSingleQuoteEscape /\'/ contained
syn match jsDoubleQuoteEscape /\"/ contained

syn match jsFunction /w+(()@=/
syn match jsFunction /(news+)@<=w+/

 "многострочный коментарий /* *
syn region jsComment start=//*/ end=/*// 
 "однострочный коментарий //
syn region jsComment start=//// end=/$/    

syn keyword jsKeyword if else while new for throw catch function
  
hi link jsKeyword Keyword
hi link jsString String
hi link jsEscapeSymbol jsEscape 
hi link jsDoubleQuoteEscape jsEscape
hi link jsSingleQuoteEscape jsEscape
hi link jsEscape Keyword 
hi link jsComment Comment
hi link jsFunction Function

"помечаем, что правила для языка javascript уже загружены
let b:current_syntax="javascript" 

Ну и на последок

Если в языке, для которого вы делаете подсветку не важен регистр — используйте команду

syn case ignore

в начале описания подсветки.

При редактировании подсветки хочется постоянно видеть результат. Для этого откройте в другом буфере раскрашивающий текст и когда захотите обновите результат просто введите :e или :syn off|syn on

Автор: meta4

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


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