понедельник, 28 ноября 2011 г.

vim: TagHighlight и tagbar - немного магии цвета

Про плагин TagHighlight (который раньше назывался ctags_highlighting) я уже писал (см. здесь). C тех пор в TagHighlight многое изменилось, прежде всего сам движок, но кроме этого было добавлено большое число новых фич. Самая главная фича - это возможность использовать разные подсветки для разных проектов. Однако для ранжира подсветок по проектам я по-прежнему использую скрипт MakeVimHlTags - так уж у меня сложилось - который, естественно, тоже обновился (новая версия доступна для скачивания здесь). В данном посте я хочу продемонстрировать другую возможность нового TagHighlight - подсветку буферов вспомогательного типа (FileType) тегами из некоторого проекта. Такими буферами могут быть, например, поля редактирования при коммите svn (в этом случае буфер имеет тип svn). Чтобы понять, что это такое и для чего это нужно представьте, что вы делаете svn commit, открывается ваш любимый редактор vim, вы вводите сообщение об изменениях, и названия объектов тут же подсвечиваются в соответствии с тегами для данного проекта - на мой взгляд это очень удобно.

Другой вспомогательный тип буфера - и тут я плавно перехожу ко второму герою повествования - это тип tagbar. Плагин tagbar - это современная замена taglist. Когда я решил посмотреть, что он из себя представляет, он сразу стал моим фаворитом. Это действительно лучший вариант taglist. Во-первых, у него нет глюков, связанных с переходом между табами. Во-вторых, он представляет информацию о тегах в более систематизированном виде. В-третьих, имеет очень приличный look-n-feel. Ну и то, что он предоставляет собственный FileType, можно использовать для подсветки TagHighlight!

Кстати, для того, чтобы tagbar не сортировал теги по алфавиту, а показывал их в той последовательности, в которой они объявлены в исходном файле, добавьте в .vimrc строку:
let g:tagbar_sort = 0
Итак, вот картинка (кликните для увеличения):


На этой картинке представлено два обычных буфера - исходники на C++, и буфер tagbar (самое правое окно). Все три буфера расцвечены с помощью TagHighlight. Для создания тегов в MakeVimHlTags использовался пропатченный ctags (см. здесь), который я настоятельно рекомендую, так как за время его использования c августа этого года никаких ошибок я не обнаружил, а вот проблемы с пропуском тегов исчезли.

Теперь о том, как добиться подсветки tagbar c помощью TagHighlight. Я по-прежнему предполагаю (по крайней мере так сделано у меня), что мы используем понятие проектов, которые находятся в директории, на которую ссылается переменная окружения $TAGSDIR (лично я установил ее в файле .bash_profile в значение $HOME/opt/tags), и состоят из файлов с тегами ctags и cscope (для TagHighlight эти файлы не нужны - просто они упоминаются в коде, который я приведу ниже), и файла с подсветкой для TagHighlight, который был создан с помощью MakeVimHlTags и имеет имя типа projectname_c.vim, где projectname - имя конкретного проекта. Итак, вот участок кода, который отвечает за запуск подсветок для проектов (вы можете вставить его в ваш .vimrc, лично я положил его в отдельный файл $HOME/.vim-myprojects.vim, который сорсится в конце .vimrc):

" ---- Setting tags and tag highlights
" ----
fun<SID>SetTags(ctags_db, cscope_db)
    if filereadable(a:ctags_db)
        execute 'set tags+=' . a:ctags_db
    endif
    if filereadable(a:cscope_db)
        execute 'cscope add ' . a:cscope_db
    endif
endfun

fun<SID>SetCTagsHl(ctagshl_db, skipft)
    if !exists('g:loaded_TagHighlight')
        return
    endif
    if index(split(a:skipft), &ft) < 0
        "let g:TagHighlightSettings['DebugLevel'] = 'Information'
        let g:TagHighlightSettings['UserLibraries'= [a:ctagshl_db]
        " add 'FileType' to LanguageDetectionMethods to see highlights when
        " committed to svn (to achieve this, this function must be triggered
        " with BufReadPre event), tagbar is also to be highlighted
        let g:TagHighlightSettings['LanguageDetectionMethods'=
                                                \ ['Extension''FileType']
        let g:TagHighlightSettings['FileTypeLanguageOverrides'=
                                                \ {'svn''c''tagbar''c'}
    endif
endfun

fun<SID>SkipCTagsHlForFt(skipft)
    if !exists('g:loaded_TagHighlight')
        return
    endif
    if index(split(a:skipft), &ft) >= 0
        let g:TagHighlightSettings['UserLibraries'= []
        " trigger FileType event forcibly
        exe "set ft=" . &ft
        "ReadTypes
    endif
endfun

let mpr_def_skipft = 'perl sh'

let mpr_tags =
    \ [
        \ ['*/geant4/pnpi/*''pnpi', mpr_def_skipft],
        \ ['*/geant4/src/geant4*''geant4', mpr_def_skipft],
    \ ]

for item in mpr_tags
    let mpr_tagname = item[1]
    let mpr_hltags  = $TAGSDIR."/".mpr_tagname."_c.vim"
    let mpr_ctags   = $TAGSDIR."/".mpr_tagname.".ctags"
    let mpr_cstags  = $TAGSDIR."/".mpr_tagname.".cscope_out"
    if filereadable(mpr_hltags)
        exe "autocmd BufReadPre,BufNewFile ".item[0].
                    \ " call <SID>SetCTagsHl('".mpr_hltags."', '".item[2]."')"
        exe "autocmd BufRead ".item[0].
                    \ " call <SID>SkipCTagsHlForFt('".item[2]."')"
    endif
    exe "autocmd VimEnter ".item[0].
                \ " silent call <SID>SetTags('".mpr_ctags."', '".mpr_cstags."')"
endfor

Функция <SID>SetTags() к нашему разговору не относится - она нужна для загрузки тех самых ctags и cscope файлов проекта, о которых я упомянул выше, а они отвечают не за подсветку тегов, а за навигацию по ним. Интерес представляют функция <SID>SetCTagsHl(), объекты mpr_def_skipft, mpr_tags и цикл for по mpr_tags. Объект-строка mpr_def_skipft определяет, файлы каких типов (в данном случае perl и sh) не должны подсвечиваться TagHighlight. В объекте-коллекции mpr_tags указан полный список всех проектов, которые могут быть загружены из директории $TAGSDIR. В данном случае я утверждаю, что в директории $TAGSDIR (привязка к которой осуществляется ниже - в цикле for) находятся два проекта с именами pnpi и geant4, рабочие директории проектов (которые указаны в списках autocmd BufReadPre, BufNewFile, BufRead и VimEnter цикла for в качестве первого аргумента - через item[0] - и являются триггером для чтения тегов и запуска подсветки TagHighlight) - это */geant4/pnpi/* и */geant4/src/geant4/* соответственно. В качестве третьего параметра в элементах коллекции mpr_tags указан mpr_def_skipft - он будет использован в цикле for для отключения подсветки TagHighlight для файлов типа perl и sh (путем вызова функции <SID>SkipCTagsHlForFt() из autocmd BufRead).

Вызов подсветки tagbar происходит внутри функции <SID>SetCTagsHl() с помощью всего двух объявлений:
        let g:TagHighlightSettings['LanguageDetectionMethods'=
                                                \ ['Extension''FileType']
        let g:TagHighlightSettings['FileTypeLanguageOverrides'=
                                                \ {'svn''c''tagbar''c'}
В первой строке мы говорим, что будем определять нужна ли подсветка TagHighlight как по расширению читаемого файла, так и по заданному для него типу FileType, который устанавливается в недрах движка vim. Во второй строке мы проассоциировали FileType tagbar с FileType c, и это значит, что в окне tagbar появится подсветка, определенная для C-проектов, а это то, чего мы и добивались. Кроме того, мы проассоциировали FileType svn с FileType c - это нужно для подсветки коммитов svn, о чем я говорил раньше.