Skip to content

Commit 9abe348

Browse files
committed
2x faster syntax highlighting using Python Interface to Vim :-)
1 parent 980843e commit 9abe348

File tree

7 files changed

+230
-58
lines changed

7 files changed

+230
-58
lines changed

README.md

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ When you're familiar with integrated development environments you may recognize
66

77
There's just one problem: You have to manually keep your tags files up-to-date and this turns out to be a royal pain in the ass! So I set out to write a Vim plug-in that would do this boring work for me. When I finished the plug-in's basic functionality (one automatic command and a call to [system()] [system] later) I became interested in dynamic syntax highlighting, so I added that as well to see if it would work -- surprisingly well I'm happy to report!
88

9-
## Install & usage
9+
## Installation
1010

1111
Unzip the most recent [ZIP archive](http://peterodding.com/code/vim/downloads/easytags.zip) file inside your Vim profile directory (usually this is `~/.vim` on UNIX and `%USERPROFILE%\vimfiles` on Windows), restart Vim and execute the command `:helptags ~/.vim/doc` (use `:helptags ~\vimfiles\doc` instead on Windows). Now try it out: Edit any file type supported by Exuberant Ctags and within ten seconds the plug-in should create/update your tags file (`~/.vimtags` on UNIX, `~/_vimtags` on Windows) with the tags defined in the file you just edited! This means that whatever file you're editing in Vim (as long as it's on the local file system), tags will always be available by the time you need them!
1212

@@ -18,10 +18,12 @@ Note that if the plug-in warns you `ctags` isn't installed you'll have to downlo
1818

1919
$ sudo apt-get install exuberant-ctags
2020

21-
### If you're using Windows
21+
### A note about Windows
2222

2323
On Windows the [system()] [system] function used by `easytags.vim` causes a command prompt window to pop up while Exuberant Ctags is executing. If this bothers you then you can install my [shell.vim](http://peterodding.com/code/vim/shell/) plug-in which includes a [DLL](http://en.wikipedia.org/wiki/Dynamic-link_library) that works around this issue. Once you've installed both plug-ins it should work out of the box! Please let me know if this doesn't work for you.
2424

25+
## Commands
26+
2527
### The `:UpdateTags` command
2628

2729
This command executes [Exuberant Ctags] [exuberant_ctags] from inside Vim to update the global tags file defined by `g:easytags_file`. When no arguments are given the tags for the current file are updated, otherwise the arguments are passed on to `ctags`. For example when you execute the Vim command `:UpdateTags -R ~/.vim` (or `:UpdateTags -R ~\vimfiles` on Windows) the plug-in will execute `ctags -R ~/.vim` for you (with some additional arguments, see the troubleshooting section "`:HighlightTags` only works for the tags file created by `:UpdateTags`" for more information).
@@ -36,9 +38,11 @@ When you execute this command while editing one of the supported file types (see
3638

3739
Note that this command will be executed automatically every once in a while, assuming you haven't changed `g:easytags_on_cursorhold`.
3840

41+
## Options
42+
3943
### The `g:easytags_cmd` option
4044

41-
The plug-in will try to determine the location where Exuberant Ctags is installed on its own but this might not always work because any given executable named `ctags` in your `$PATH` might not in fact be Exuberant Ctags but some older, more primitive `ctags` implementation which doesn't support the same command line options and thus breaks the `easytags.vim` plug-in. If this is the case you can set the global variable `g:easytags_cmd` to the location where you've installed Exuberant Ctags, e.g.:
45+
The plug-in will try to determine the location where Exuberant Ctags is installed on its own but this might not always work because any given executable named `ctags` in your `$PATH` might not in fact be Exuberant Ctags but some older, more primitive `ctags` implementation which doesn't support the same command line options and thus breaks the easytags plug-in. If this is the case you can set the global variable `g:easytags_cmd` to the location where you've installed Exuberant Ctags, e.g.:
4246

4347
:let g:easytags_cmd = '/usr/local/bin/ctags'
4448

@@ -124,7 +128,19 @@ If this is set and not false, it will suppress the warning on startup if ctags i
124128

125129
:let g:easytags_suppress_ctags_warning = 1
126130

127-
### How to customize the highlighting colors?
131+
## Faster syntax highlighting using Python
132+
133+
The Vim script implementation of dynamic syntax highlighting is quite slow on large tags files. When the Python Interface to Vim is enabled the easytags plug-in will therefor automatically use a Python script that performs dynamic syntax highlighting about twice as fast as the Vim script implementation. The following options are available to change the default configuration.
134+
135+
### The `g:easytags_python_enabled` option
136+
137+
To disable the Python implementation of dynamic syntax highlighting you can set this option to true (1).
138+
139+
### The `g:easytags_python_script` option
140+
141+
This option defines the pathname of the script that contains the Python implementation of dynamic syntax highlighting.
142+
143+
## How to customize the highlighting colors?
128144

129145
The easytags plug-in defines new highlighting groups for dynamically highlighted tags. These groups are linked to Vim's default groups so that they're colored out of the box, but if you want you can change the styles. To do so use a `highlight` command such as the ones given a few paragraphs back. Of course you'll need to change the group name. Here are the group names used by the easytags plug-in:
130146

autoload/xolox/easytags.vim

Lines changed: 109 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -240,41 +240,59 @@ function! xolox#easytags#highlight() " {{{2
240240
let tagkinds = get(s:tagkinds, filetype, [])
241241
if exists('g:syntax_on') && !empty(tagkinds) && !exists('b:easytags_nohl')
242242
let starttime = xolox#misc#timer#start()
243-
if !has_key(s:aliases, filetype)
244-
let ctags_filetype = xolox#easytags#to_ctags_ft(filetype)
245-
let taglist = filter(taglist('.'), "get(v:val, 'language', '') ==? ctags_filetype")
246-
else
247-
let aliases = s:aliases[&ft]
248-
let taglist = filter(taglist('.'), "has_key(aliases, tolower(get(v:val, 'language', '')))")
249-
endif
243+
let used_python = 0
250244
for tagkind in tagkinds
251245
let hlgroup_tagged = tagkind.hlgroup . 'Tag'
252-
if hlexists(hlgroup_tagged)
253-
execute 'syntax clear' hlgroup_tagged
254-
else
246+
" Define style on first run, clear highlighting on later runs.
247+
if !hlexists(hlgroup_tagged)
255248
execute 'highlight def link' hlgroup_tagged tagkind.hlgroup
249+
else
250+
execute 'syntax clear' hlgroup_tagged
256251
endif
257-
let matches = filter(copy(taglist), tagkind.filter)
258-
if matches != []
259-
call map(matches, 'xolox#misc#escape#pattern(get(v:val, "name"))')
260-
let pattern = tagkind.pattern_prefix . '\%(' . join(xolox#misc#list#unique(matches), '\|') . '\)' . tagkind.pattern_suffix
261-
let template = 'syntax match %s /%s/ containedin=ALLBUT,.*String.*,.*Comment.*,cIncluded'
262-
let command = printf(template, hlgroup_tagged, escape(pattern, '/'))
263-
try
264-
execute command
265-
catch /^Vim\%((\a\+)\)\=:E339/
266-
let msg = "easytags.vim: Failed to highlight %i %s tags because pattern is too big! (%i KB)"
267-
call xolox#misc#msg#warn(printf(msg, len(matches), tagkind.hlgroup, len(pattern) / 1024))
268-
endtry
252+
" Try to perform the highlighting using the fast Python script.
253+
if s:highlight_with_python(hlgroup_tagged, tagkind)
254+
let used_python = 1
255+
else
256+
" Fall back to the slow and naive Vim script implementation.
257+
if !exists('taglist')
258+
" Get the list of tags when we need it and remember the results.
259+
if !has_key(s:aliases, filetype)
260+
let ctags_filetype = xolox#easytags#to_ctags_ft(filetype)
261+
let taglist = filter(taglist('.'), "get(v:val, 'language', '') ==? ctags_filetype")
262+
else
263+
let aliases = s:aliases[&ft]
264+
let taglist = filter(taglist('.'), "has_key(aliases, tolower(get(v:val, 'language', '')))")
265+
endif
266+
endif
267+
" Filter a copy of the list of tags to the relevant kinds.
268+
if has_key(tagkind, 'tagkinds')
269+
let filter = 'v:val.kind =~ tagkind.tagkinds'
270+
else
271+
let filter = tagkind.vim_filter
272+
endif
273+
let matches = filter(copy(taglist), filter)
274+
if matches != []
275+
" Convert matched tags to :syntax command and execute it.
276+
call map(matches, 'xolox#misc#escape#pattern(get(v:val, "name"))')
277+
let pattern = tagkind.pattern_prefix . '\%(' . join(xolox#misc#list#unique(matches), '\|') . '\)' . tagkind.pattern_suffix
278+
let template = 'syntax match %s /%s/ containedin=ALLBUT,.*String.*,.*Comment.*,cIncluded'
279+
let command = printf(template, hlgroup_tagged, escape(pattern, '/'))
280+
try
281+
execute command
282+
catch /^Vim\%((\a\+)\)\=:E339/
283+
let msg = "easytags.vim: Failed to highlight %i %s tags because pattern is too big! (%i KB)"
284+
call xolox#misc#msg#warn(printf(msg, len(matches), tagkind.hlgroup, len(pattern) / 1024))
285+
endtry
286+
endif
269287
endif
270288
endfor
271289
redraw
272290
let bufname = expand('%:p:~')
273291
if bufname == ''
274292
let bufname = 'unnamed buffer #' . bufnr('%')
275293
endif
276-
let msg = "%s: Highlighted tags in %s in %s."
277-
call xolox#misc#timer#stop(msg, s:script, bufname, starttime)
294+
let msg = "%s: Highlighted tags in %s in %s%s."
295+
call xolox#misc#timer#stop(msg, s:script, bufname, starttime, used_python ? " (using Python)" : "")
278296
return 1
279297
endif
280298
catch
@@ -561,6 +579,49 @@ endfunction
561579

562580
let s:cached_filenames = {}
563581

582+
function! s:python_available() " {{{2
583+
if !exists('s:is_python_available')
584+
try
585+
execute 'pyfile' fnameescape(g:easytags_python_script)
586+
redir => output
587+
silent python easytags_ping()
588+
redir END
589+
let s:is_python_available = (output =~ 'it works!')
590+
catch
591+
let s:is_python_available = 0
592+
endtry
593+
endif
594+
return s:is_python_available
595+
endfunction
596+
597+
function! s:highlight_with_python(syntax_group, tagkind) " {{{2
598+
if g:easytags_python_enabled && s:python_available()
599+
" Gather arguments for Python function.
600+
let context = {}
601+
let context['tagsfiles'] = tagfiles()
602+
let context['syntaxgroup'] = a:syntax_group
603+
let context['filetype'] = xolox#easytags#to_ctags_ft(&ft)
604+
let context['tagkinds'] = get(a:tagkind, 'tagkinds', '')
605+
let context['prefix'] = get(a:tagkind, 'pattern_prefix', '')
606+
let context['suffix'] = get(a:tagkind, 'pattern_suffix', '')
607+
let context['filters'] = get(a:tagkind, 'python_filter', {})
608+
" Call the Python function and intercept the output.
609+
try
610+
redir => commands
611+
python import vim
612+
silent python print easytags_gensyncmd(**vim.eval('context'))
613+
redir END
614+
execute commands
615+
return 1
616+
catch
617+
redir END
618+
" If the Python script raised an error, don't run it again.
619+
let g:easytags_python_enabled = 0
620+
endtry
621+
endif
622+
return 0
623+
endfunction
624+
564625
" Built-in file type & tag kind definitions. {{{1
565626

566627
" Don't bother redefining everything below when this script is sourced again.
@@ -591,29 +652,29 @@ set cpo&vim
591652
call xolox#easytags#define_tagkind({
592653
\ 'filetype': 'lua',
593654
\ 'hlgroup': 'luaFunc',
594-
\ 'filter': 'get(v:val, "kind") ==# "f"'})
655+
\ 'tagkinds': 'f'})
595656

596657
" C. {{{2
597658

598659
call xolox#easytags#define_tagkind({
599660
\ 'filetype': 'c',
600661
\ 'hlgroup': 'cType',
601-
\ 'filter': 'get(v:val, "kind") =~# "[cgstu]"'})
662+
\ 'tagkinds': '[cgstu]'})
602663

603664
call xolox#easytags#define_tagkind({
604665
\ 'filetype': 'c',
605666
\ 'hlgroup': 'cEnum',
606-
\ 'filter': 'get(v:val, "kind") ==# "e"'})
667+
\ 'tagkinds': 'e'})
607668

608669
call xolox#easytags#define_tagkind({
609670
\ 'filetype': 'c',
610671
\ 'hlgroup': 'cPreProc',
611-
\ 'filter': 'get(v:val, "kind") ==# "d"'})
672+
\ 'tagkinds': 'd'})
612673

613674
call xolox#easytags#define_tagkind({
614675
\ 'filetype': 'c',
615676
\ 'hlgroup': 'cFunction',
616-
\ 'filter': 'get(v:val, "kind") =~# "[fp]"'})
677+
\ 'tagkinds': '[fp]'})
617678

618679
highlight def link cEnum Identifier
619680
highlight def link cFunction Function
@@ -622,7 +683,7 @@ if g:easytags_include_members
622683
call xolox#easytags#define_tagkind({
623684
\ 'filetype': 'c',
624685
\ 'hlgroup': 'cMember',
625-
\ 'filter': 'get(v:val, "kind") ==# "m"'})
686+
\ 'tagkinds': 'm'})
626687
highlight def link cMember Identifier
627688
endif
628689

@@ -631,27 +692,27 @@ endif
631692
call xolox#easytags#define_tagkind({
632693
\ 'filetype': 'php',
633694
\ 'hlgroup': 'phpFunctions',
634-
\ 'filter': 'get(v:val, "kind") ==# "f"',
695+
\ 'tagkinds': 'f',
635696
\ 'pattern_suffix': '(\@='})
636697

637698
call xolox#easytags#define_tagkind({
638699
\ 'filetype': 'php',
639700
\ 'hlgroup': 'phpClasses',
640-
\ 'filter': 'get(v:val, "kind") ==# "c"'})
701+
\ 'tagkinds': 'c'})
641702

642703
" Vim script. {{{2
643704

644705
call xolox#easytags#define_tagkind({
645706
\ 'filetype': 'vim',
646707
\ 'hlgroup': 'vimAutoGroup',
647-
\ 'filter': 'get(v:val, "kind") ==# "a"'})
708+
\ 'tagkinds': 'a'})
648709

649710
highlight def link vimAutoGroup vimAutoEvent
650711

651712
call xolox#easytags#define_tagkind({
652713
\ 'filetype': 'vim',
653714
\ 'hlgroup': 'vimCommand',
654-
\ 'filter': 'get(v:val, "kind") ==# "c"',
715+
\ 'tagkinds': 'c',
655716
\ 'pattern_prefix': '\(\(^\|\s\):\?\)\@<=',
656717
\ 'pattern_suffix': '\(!\?\(\s\|$\)\)\@='})
657718

@@ -663,13 +724,15 @@ call xolox#easytags#define_tagkind({
663724
call xolox#easytags#define_tagkind({
664725
\ 'filetype': 'vim',
665726
\ 'hlgroup': 'vimFuncName',
666-
\ 'filter': 'get(v:val, "kind") ==# "f" && get(v:val, "cmd") !~? ''<sid>\w\|\<s:\w''',
727+
\ 'vim_filter': 'v:val.kind ==# "f" && get(v:val, "cmd", "") !~? ''<sid>\w\|\<s:\w''',
728+
\ 'python_filter': { 'kind': 'f', 'nomatch': '(?i)(<sid>\w|\bs:\w)' },
667729
\ 'pattern_prefix': '\C\%(\<s:\|<[sS][iI][dD]>\)\@<!\<'})
668730

669731
call xolox#easytags#define_tagkind({
670732
\ 'filetype': 'vim',
671733
\ 'hlgroup': 'vimScriptFuncName',
672-
\ 'filter': 'get(v:val, "kind") ==# "f" && get(v:val, "cmd") =~? ''<sid>\w\|\<s:\w''',
734+
\ 'vim_filter': 'v:val.kind ==# "f" && get(v:val, "cmd", "") =~? ''<sid>\w\|\<s:\w''',
735+
\ 'python_filter': { 'kind': 'f', 'match': '(?i)(<sid>\w|\bs:\w)' },
673736
\ 'pattern_prefix': '\C\%(\<s:\|<[sS][iI][dD]>\)'})
674737

675738
highlight def link vimScriptFuncName vimFuncName
@@ -679,19 +742,19 @@ highlight def link vimScriptFuncName vimFuncName
679742
call xolox#easytags#define_tagkind({
680743
\ 'filetype': 'python',
681744
\ 'hlgroup': 'pythonFunction',
682-
\ 'filter': 'get(v:val, "kind") ==# "f"',
745+
\ 'tagkinds': 'f',
683746
\ 'pattern_prefix': '\%(\<def\s\+\)\@<!\<'})
684747

685748
call xolox#easytags#define_tagkind({
686749
\ 'filetype': 'python',
687750
\ 'hlgroup': 'pythonMethod',
688-
\ 'filter': 'get(v:val, "kind") ==# "m"',
751+
\ 'tagkinds': 'm',
689752
\ 'pattern_prefix': '\.\@<='})
690753

691754
call xolox#easytags#define_tagkind({
692755
\ 'filetype': 'python',
693756
\ 'hlgroup': 'pythonClass',
694-
\ 'filter': 'get(v:val, "kind") ==# "c"'})
757+
\ 'tagkinds': 'c'})
695758

696759
highlight def link pythonMethodTag pythonFunction
697760
highlight def link pythonClassTag pythonFunction
@@ -701,12 +764,12 @@ highlight def link pythonClassTag pythonFunction
701764
call xolox#easytags#define_tagkind({
702765
\ 'filetype': 'java',
703766
\ 'hlgroup': 'javaClass',
704-
\ 'filter': 'get(v:val, "kind") ==# "c"'})
767+
\ 'tagkinds': 'c'})
705768

706769
call xolox#easytags#define_tagkind({
707770
\ 'filetype': 'java',
708771
\ 'hlgroup': 'javaMethod',
709-
\ 'filter': 'get(v:val, "kind") ==# "m"'})
772+
\ 'tagkinds': 'm'})
710773

711774
highlight def link javaClass Identifier
712775
highlight def link javaMethod Function
@@ -721,12 +784,12 @@ highlight def link javaMethod Function
721784
call xolox#easytags#define_tagkind({
722785
\ 'filetype': 'cs',
723786
\ 'hlgroup': 'csClassOrStruct',
724-
\ 'filter': 'get(v:val, "kind") ==# "c"'})
787+
\ 'tagkinds': 'c'})
725788

726789
call xolox#easytags#define_tagkind({
727790
\ 'filetype': 'cs',
728791
\ 'hlgroup': 'csMethod',
729-
\ 'filter': 'get(v:val, "kind") =~# "[ms]"'})
792+
\ 'tagkinds': '[ms]'})
730793

731794
highlight def link csClassOrStruct Identifier
732795
highlight def link csMethod Function
@@ -736,17 +799,17 @@ highlight def link csMethod Function
736799
call xolox#easytags#define_tagkind({
737800
\ 'filetype': 'ruby',
738801
\ 'hlgroup': 'rubyModuleName',
739-
\ 'filter': 'get(v:val, "kind") ==# "m"'})
802+
\ 'tagkinds': 'm'})
740803

741804
call xolox#easytags#define_tagkind({
742805
\ 'filetype': 'ruby',
743806
\ 'hlgroup': 'rubyClassName',
744-
\ 'filter': 'get(v:val, "kind") ==# "c"'})
807+
\ 'tagkinds': 'c'})
745808

746809
call xolox#easytags#define_tagkind({
747810
\ 'filetype': 'ruby',
748811
\ 'hlgroup': 'rubyMethodName',
749-
\ 'filter': 'get(v:val, "kind") =~# "[fF]"'})
812+
\ 'tagkinds': '[fF]'})
750813

751814
highlight def link rubyModuleName Type
752815
highlight def link rubyClassName Type

0 commit comments

Comments
 (0)