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

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

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

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

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


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

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



    Команда 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\)\=/



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

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



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

    :syn match Function /\(new\s\+\)\@<=\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=/"/



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

    «Складывание» (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
    



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


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

    :syn keyword htmlTagName habracut source hh video slideshow



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


    Каждый раз вводить команды для подсветки всех конструкций, безусловно, никто требует. Все команды нужно оформить в файле (например) 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 /\(new\s\+\)\@<=\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

    UPD: Спасибо пользователю Goder за большое число замечаний насчет ошибок в статье :)
    Share post

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 22

      0
      Спасибо! Лаконично и доступно.
        0
        А что за шрифт и цветовая схема на скринах?
          0
          шрифт на droid sans похож
            +2
            Шрифт Monaco — моноширный шрифт по умолчанию в Mac. Цветовая схема wombat
            0
            Вообще полезная статья, теперь можно подсвечивать собственные классы и функции, спасибо!
              0
              Для этого есть специальные плагины, TagHighlight например.
              0
              Не мешало бы упомянуть, что в vim шикарная встроенная документация, в частности исчерпывающая информация по сабжу доступна в :help syntax.
                0
                Да, думаю, большинству известно что справка у Vim весьма обширная и качественная.
                И тем не менее, даже после статьи и прочтения vim +«help highlight-link», я не понимаю как работаю link'и и как в итоге эти группы применяются. Разъясните пожалуйста.
                  0
                  :hi — отвечает, по сути, за представление подсветки ситнаксиса. В цветовых схемах используется следующий формат команды:

                  hi StatusLine   guifg=#f6f3e8 guibg=#444444 gui=italic
                  hi Comment              guifg=#99968b gui=italic
                  

                  Определяет стиль региона StatusLine как заданный цвето текста (guifg), цвет фона (guibg) и стиль начертания (gui). Стилем может быть любой идентификатор, но есть соглашение про несколько стандартных: что стиль Keyword обычно отвечает за ключевые слова, а Comment — за коментарий. Но вообще можете задать любо имя, хот PesNaLdine.

                  Далее, в описании подсветки вы задаете сначала стили с именами привязанными к структуре языка — например pascalFuncionCall, pascalCompilerOptions, и т.д. А далее задается «представление», путем отображения языковых стилей, на общие стили, которые определены в ваших цветовых схемах:

                  hi link pascalFunctionCall Function
                  hi link pascalCompilerOptions PreProc
                  

                  Подсвечивает регион pascalFunctionCall как регион Function, а pascalCompilerOptions как регион PreProc. Просто стиль слева берется целиком из стиля справа. Так как обычно в языках много конструкций, а в цветовых схемах определены только общие для всех языков конструкции, на один стиль часто ссылается несколько названий регинов разметки (как у меня в примерах — стили ключевых слови экранирующих символов «привязоны» к одному и томуже стили из цветовых схем Keyword).

                    0
                    В таком случае в хелпе я не нашёл полного перечня стилей (тех самых ключевых слов, которые в итоге и задают цвет текста). Какие-то файлы, судя по всему цветовых схем обнаружил тут (/usr/share/vim/vim72/colors/). Однако default.vim практически пустой.

                    Правильно ли я понял, что могу задать свой цвет с абсолютно любым именем, и как только я его свяжу (link) с каким либо keyword или region или еще чем, то эти самые куски текста будут подсвечены указанным мной стилем?

                    И всё же хотелось бы базировать свою раскраску на стандартной, где её можно увидеть?
                      0
                      >что могу задать свой цвет с абсолютно любым именем

                      Верно.

                      >И всё же хотелось бы базировать свою раскраску на стандартной, где её можно увидеть?

                      hi Comment              guifg=#99968b gui=italic
                      hi Todo                 guifg=#8f8f8f gui=italic
                      hi Constant     guifg=#8a11a8 gui=none
                      hi String               guifg=#95e454 gui=italic
                      hi Identifier   guifg=#cae682 gui=none
                      hi Function     guifg=#cae682 gui=none
                      hi Type                 guifg=#cae682 gui=none
                      hi Statement    guifg=#8ac6f2 gui=none
                      hi Keyword              guifg=#8ac6f2 gui=none
                      hi PreProc              guifg=#e5786d gui=none
                      hi Number               guifg=#e5786d gui=none
                      hi Special              guifg=#e7f6da gui=none
                      hi Delimiter    guifg=#e5786d gui=none
                      

                      Вот отсюда черпай названия и «линкуй» свои навзания к этим.
                    0
                    Статья мне показалась похожей на пересказ хелпа, возможно просто читал не слишком внимательно. По крайней мере одна тема из хелпа в статье не рассматривается (синхронизация подсветки при редактировании).
                  0
                  Как сказать vim «Этот файл, конечно, следует красить как ruby/C/C++/java/etc, но конкретно вот эта строка — SQL, раскрась её как положено»?
                    0
                    Смогу ответить, если вы скажете, как определить «вот это строка». Ведь вы файл редактируете, а значит опираться на оффсет по символам нельзя. Да и как сохранить эту информацию в файл? Идея умеет получать эту информацию на основе полного семантического анализа. Однако, если это легко регекспами получить — то можно сделать так (например подсвечивать во всех строках):

                    :syn region start=/"/ end=/"/ contains=sqlKeyword,sqlOperator,sqlStatement,sqlFunction,sqlNumber,sqlType
                    
                      0
                      ну как-как. внутри кавычек, натурально. не учитывая HEREDOCи, само собой. и экранирование кавычек. и переводы строк.
                      Идея умеет получать эту информацию на основе полного семантического анализа.
                      знаю. нечестно, правда?
                        0
                        >знаю. нечестно, правда?

                        Да ничего нечестного. Просто (лично мне) вим нравится легкостью, которая убивается таким вот инструментарем.

                        >ну как-как. внутри кавычек, натурально.

                        Так я этот вариант и привел. Только внутри кавычек может быть и не SQL — а XPath или просто строка.
                          0
                          я этот вариант и привел
                          оно переварит
                          var q = "SELECT * FROM table WHERE col1=\"foo\" GROUP BY col2"
                          ? сомнительно
                            0
                            Ключевые слова подсветит.Но, строку в заэскейплиных ковычках, очевидно, не переварит, можно и это сделать, только зачем.
                              0
                              только зачем
                              чтобы не мешать рефакторить говнокод например.
                    –2
                    Там опечатки «давайте раскарасим некотрое подмножество язык javascript:». Особенно порадовало слово «раскарасим».
                      –2
                      Спасибо за минусы, друзья. Я всего лишь хотел указать на опечатку.
                        –2
                        Минусуют потому, что сообщения об опечатках замусоривают коментарии, так как устаревают сразу же после правки, и поэтму их принято слать автору в личку. А для того чтобы слать в личку было удобно, стоит поставить расширение для хрома, которое будет слать сообщение автоматом по Ctrl+Enter

                    Only users with full accounts can post comments. Log in, please.