2 " Author: Peter Odding <peter@peterodding.com>
3 " Last Change: September 17, 2011
4 " URL: http://peterodding.com/code/vim/easytags/
6 let g:xolox#easytags#version = '2.5.8'
8 " Public interface through (automatic) commands. {{{1
10 function! xolox#easytags#register(global) " {{{2
11 " Parse the &tags option and get a list of all tags files *including
12 " non-existing files* (this is why we can't just call tagfiles()).
13 let tagfiles = xolox#misc#option#split_tags(&tags)
14 let expanded = map(copy(tagfiles), 'resolve(expand(v:val))')
15 " Add the filename to the &tags option when the user hasn't done so already.
16 let tagsfile = a:global ? g:easytags_file : xolox#easytags#get_tagsfile()
17 if index(expanded, xolox#misc#path#absolute(tagsfile)) == -1
18 " This is a real mess because of bugs in Vim?! :let &tags = '...' doesn't
19 " work on UNIX and Windows, :set tags=... doesn't work on Windows. What I
20 " mean with "doesn't work" is that tagfiles() == [] after the :let/:set
21 " command even though the tags file exists! One easy way to confirm that
22 " this is a bug in Vim is to type :set tags= then press <Tab> followed by
23 " <CR>. Now you entered the exact same value that the code below also did
24 " but suddenly Vim sees the tags file and tagfiles() != [] :-S
25 call add(tagfiles, tagsfile)
26 let value = xolox#misc#option#join_tags(tagfiles)
27 let cmd = (a:global ? 'set' : 'setl') . ' tags=' . escape(value, '\ ')
28 if xolox#misc#os#is_win() && v:version < 703
29 " TODO How to clear the expression from Vim's status line?
30 call feedkeys(":" . cmd . "|let &ro=&ro\<CR>", 'n')
37 function! xolox#easytags#autoload(event) " {{{2
39 if a:event =~? 'cursorhold'
40 " Only for the CursorHold automatic command: check for unreasonable
41 " &updatetime values. The minimum value 4000 is kind of arbitrary
42 " (apart from being Vim's default) so I made it configurable:
43 let updatetime_min = xolox#misc#option#get('easytags_updatetime_min', 4000)
44 if &updatetime < updatetime_min
45 " Other plug-ins may lower &updatetime in certain contexts, e.g.
46 " insert mode in the case of the neocomplcache plug-in. The following
47 " option (disabled by default unless neocomplcache is loaded) silences
48 " the warning and makes the easytags plug-in skip the update and
49 " highlight. When the &updatetime is restored to a reasonable value
50 " the plug-in resumes.
51 if xolox#misc#option#get('easytags_updatetime_autodisable', exists('g:loaded_neocomplcache'))
54 call xolox#misc#msg#warn("easytags.vim %s: I'm being executed every %i milliseconds! Please :set updatetime=%i. To find where 'updatetime' was changed execute ':verb set ut?'", g:xolox#easytags#version, &updatetime, updatetime_min)
58 let do_update = xolox#misc#option#get('easytags_auto_update', 1)
59 let do_highlight = xolox#misc#option#get('easytags_auto_highlight', 1) && &eventignore !~? '\<syntax\>'
60 " Don't execute this function for unsupported file types (doesn't load
61 " the list of file types if updates and highlighting are both disabled).
62 if (do_update || do_highlight) && index(xolox#easytags#supported_filetypes(), &ft) >= 0
63 " Update entries for current file in tags file?
65 let pathname = s:resolve(expand('%:p'))
67 let tags_outdated = getftime(pathname) > getftime(xolox#easytags#get_tagsfile())
68 if tags_outdated || !xolox#easytags#file_has_tags(pathname)
69 call xolox#easytags#update(1, 0, [])
73 " Apply highlighting of tags to current buffer?
75 if !exists('b:easytags_last_highlighted')
76 call xolox#easytags#highlight()
78 for tagfile in tagfiles()
79 if getftime(tagfile) > b:easytags_last_highlighted
80 call xolox#easytags#highlight()
85 let b:easytags_last_highlighted = localtime()
89 call xolox#misc#msg#warn("easytags.vim %s: %s (at %s)", g:xolox#easytags#version, v:exception, v:throwpoint)
93 function! xolox#easytags#update(silent, filter_tags, filenames) " {{{2
95 let context = s:create_context()
96 let have_args = !empty(a:filenames)
97 let starttime = xolox#misc#timer#start()
98 let cfile = s:check_cfile(a:silent, a:filter_tags, have_args)
99 let tagsfile = xolox#easytags#get_tagsfile()
100 let firstrun = !filereadable(tagsfile)
101 let cmdline = s:prep_cmdline(cfile, tagsfile, firstrun, a:filenames, context)
102 let output = s:run_ctags(starttime, cfile, tagsfile, firstrun, cmdline)
104 if have_args && !empty(g:easytags_by_filetype)
105 " TODO Get the headers from somewhere?!
106 call s:save_by_filetype(a:filter_tags, [], output, context)
108 let num_filtered = s:filter_merge_tags(a:filter_tags, tagsfile, output, context)
111 let msg = "easytags.vim %s: Updated tags for %s in %s."
112 call xolox#misc#timer#stop(msg, g:xolox#easytags#version, expand('%:p:~'), starttime)
114 let msg = "easytags.vim %s: Updated tags in %s."
115 call xolox#misc#timer#stop(msg, g:xolox#easytags#version, starttime)
117 let msg = "easytags.vim %s: Filtered %i invalid tags in %s."
118 call xolox#misc#timer#stop(msg, g:xolox#easytags#version, num_filtered, starttime)
121 " When :UpdateTags was executed manually we'll refresh the dynamic
122 " syntax highlighting so that new tags are immediately visible.
128 call xolox#misc#msg#warn("easytags.vim %s: %s (at %s)", g:xolox#easytags#version, v:exception, v:throwpoint)
132 function! s:check_cfile(silent, filter_tags, have_args) " {{{3
136 let silent = a:silent || a:filter_tags
137 if xolox#misc#option#get('easytags_autorecurse', 0)
138 let cdir = s:resolve(expand('%:p:h'))
139 if !isdirectory(cdir)
140 if silent | return '' | endif
141 throw "The directory of the current file doesn't exist yet!"
145 let cfile = s:resolve(expand('%:p'))
146 if cfile == '' || !filereadable(cfile)
147 if silent | return '' | endif
148 throw "You'll need to save your file before using :UpdateTags!"
149 elseif g:easytags_ignored_filetypes != '' && &ft =~ g:easytags_ignored_filetypes
150 if silent | return '' | endif
151 throw "The " . string(&ft) . " file type is explicitly ignored."
152 elseif index(xolox#easytags#supported_filetypes(), &ft) == -1
153 if silent | return '' | endif
154 throw "Exuberant Ctags doesn't support the " . string(&ft) . " file type!"
159 function! s:prep_cmdline(cfile, tagsfile, firstrun, arguments, context) " {{{3
160 let program = xolox#misc#option#get('easytags_cmd')
161 let cmdline = [program, '--fields=+l', '--c-kinds=+p', '--c++-kinds=+p']
163 call add(cmdline, shellescape('-f' . a:tagsfile))
164 call add(cmdline, '--sort=' . (&ic ? 'foldcase' : 'yes'))
166 call add(cmdline, '--sort=no')
167 call add(cmdline, '-f-')
169 if xolox#misc#option#get('easytags_include_members', 0)
170 call add(cmdline, '--extra=+q')
174 if xolox#misc#option#get('easytags_autorecurse', 0)
175 call add(cmdline, '-R')
176 call add(cmdline, shellescape(a:cfile))
178 " TODO Should --language-force distinguish between C and C++?
179 " TODO --language-force doesn't make sense for JavaScript tags in HTML files?
180 let filetype = xolox#easytags#to_ctags_ft(&filetype)
181 call add(cmdline, shellescape('--language-force=' . filetype))
182 call add(cmdline, shellescape(a:cfile))
186 for arg in a:arguments
188 call add(cmdline, arg)
191 let matches = split(expand(arg), "\n")
193 call map(matches, 'shellescape(s:canonicalize(v:val, a:context))')
194 call extend(cmdline, matches)
200 " No need to run Exuberant Ctags without any filename arguments!
201 return have_args ? join(cmdline) : ''
204 function! s:run_ctags(starttime, cfile, tagsfile, firstrun, cmdline) " {{{3
207 call xolox#misc#msg#debug("easytags.vim %s: Executing %s.", g:xolox#easytags#version, a:cmdline)
209 let lines = xolox#shell#execute(a:cmdline, 1)
210 catch /^Vim\%((\a\+)\)\=:E117/
211 " Ignore missing shell.vim plug-in.
212 let output = system(a:cmdline)
214 let msg = "Failed to update tags file %s: %s!"
215 throw printf(msg, fnamemodify(a:tagsfile, ':~'), strtrans(output))
217 let lines = split(output, "\n")
221 call xolox#misc#timer#stop("easytags.vim %s: Created tags for %s in %s.", g:xolox#easytags#version, expand('%:p:~'), a:starttime)
223 call xolox#misc#timer#stop("easytags.vim %s: Created tags in %s.", g:xolox#easytags#version, a:starttime)
228 return xolox#easytags#parse_entries(lines)
231 function! s:filter_merge_tags(filter_tags, tagsfile, output, context) " {{{3
232 let [headers, entries] = xolox#easytags#read_tagsfile(a:tagsfile)
234 " Filter old tags that are to be replaced with the tags in {output}.
235 let tagged_files = s:find_tagged_files(a:output, a:context)
236 if !empty(tagged_files)
237 call add(filters, '!has_key(tagged_files, s:canonicalize(v:val[1], a:context))')
239 " Filter tags for non-existing files?
241 call add(filters, 'filereadable(v:val[1])')
243 let num_old_entries = len(entries)
246 call filter(entries, join(filters, ' && '))
248 let num_filtered = num_old_entries - len(entries)
249 " Merge old/new tags and write tags file.
250 call extend(entries, a:output)
251 if !xolox#easytags#write_tagsfile(a:tagsfile, headers, entries)
252 let msg = "Failed to write filtered tags file %s!"
253 throw printf(msg, fnamemodify(a:tagsfile, ':~'))
255 " We've already read the tags file, might as well cache the tagged files :-)
256 let fname = s:canonicalize(a:tagsfile, a:context)
257 call s:cache_tagged_files_in(fname, getftime(fname), entries, a:context)
261 function! s:find_tagged_files(entries, context) " {{{3
262 let tagged_files = {}
263 for entry in a:entries
264 let filename = s:canonicalize(entry[1], a:context)
266 if !has_key(tagged_files, filename)
267 let tagged_files[filename] = 1
274 function! xolox#easytags#highlight() " {{{2
276 " Treat C++ and Objective-C as plain C.
277 let filetype = get(s:canonical_aliases, &ft, &ft)
278 let tagkinds = get(s:tagkinds, filetype, [])
279 if exists('g:syntax_on') && !empty(tagkinds) && !exists('b:easytags_nohl')
280 let starttime = xolox#misc#timer#start()
282 for tagkind in tagkinds
283 let hlgroup_tagged = tagkind.hlgroup . 'Tag'
284 " Define style on first run, clear highlighting on later runs.
285 if !hlexists(hlgroup_tagged)
286 execute 'highlight def link' hlgroup_tagged tagkind.hlgroup
288 execute 'syntax clear' hlgroup_tagged
290 " Try to perform the highlighting using the fast Python script.
291 " TODO The tags files are read multiple times by the Python script
292 " within one run of xolox#easytags#highlight()
293 if s:highlight_with_python(hlgroup_tagged, tagkind)
296 " Fall back to the slow and naive Vim script implementation.
297 if !exists('taglist')
298 " Get the list of tags when we need it and remember the results.
299 if !has_key(s:aliases, filetype)
300 let ctags_filetype = xolox#easytags#to_ctags_ft(filetype)
301 let taglist = filter(taglist('.'), "get(v:val, 'language', '') ==? ctags_filetype")
303 let aliases = s:aliases[&ft]
304 let taglist = filter(taglist('.'), "has_key(aliases, tolower(get(v:val, 'language', '')))")
307 " Filter a copy of the list of tags to the relevant kinds.
308 if has_key(tagkind, 'tagkinds')
309 let filter = 'v:val.kind =~ tagkind.tagkinds'
311 let filter = tagkind.vim_filter
313 let matches = filter(copy(taglist), filter)
315 " Convert matched tags to :syntax command and execute it.
316 call map(matches, 'xolox#misc#escape#pattern(get(v:val, "name"))')
317 let pattern = tagkind.pattern_prefix . '\%(' . join(xolox#misc#list#unique(matches), '\|') . '\)' . tagkind.pattern_suffix
318 let template = 'syntax match %s /%s/ containedin=ALLBUT,.*String.*,.*Comment.*,cIncluded'
319 let command = printf(template, hlgroup_tagged, escape(pattern, '/'))
322 catch /^Vim\%((\a\+)\)\=:E339/
323 let msg = "easytags.vim %s: Failed to highlight %i %s tags because pattern is too big! (%i KB)"
324 call xolox#misc#msg#warn(msg, g:xolox#easytags#version, len(matches), tagkind.hlgroup, len(pattern) / 1024)
330 let bufname = expand('%:p:~')
332 let bufname = 'unnamed buffer #' . bufnr('%')
334 let msg = "easytags.vim %s: Highlighted tags in %s in %s%s."
335 call xolox#misc#timer#stop(msg, g:xolox#easytags#version, bufname, starttime, used_python ? " (using Python)" : "")
339 call xolox#misc#msg#warn("easytags.vim %s: %s (at %s)", g:xolox#easytags#version, v:exception, v:throwpoint)
343 function! xolox#easytags#by_filetype(undo) " {{{2
345 if empty(g:easytags_by_filetype)
346 throw "Please set g:easytags_by_filetype before running :TagsByFileType!"
348 let context = s:create_context()
349 let global_tagsfile = expand(g:easytags_file)
350 let disabled_tagsfile = global_tagsfile . '.disabled'
352 let [headers, entries] = xolox#easytags#read_tagsfile(global_tagsfile)
353 call s:save_by_filetype(0, headers, entries, context)
354 call rename(global_tagsfile, disabled_tagsfile)
355 let msg = "easytags.vim %s: Finished copying tags from %s to %s! Note that your old tags file has been renamed to %s instead of deleting it, should you want to restore it."
356 call xolox#misc#msg#info(msg, g:xolox#easytags#version, g:easytags_file, g:easytags_by_filetype, disabled_tagsfile)
360 for tagsfile in split(glob(g:easytags_by_filetype . '/*'), '\n')
361 let [headers, entries] = xolox#easytags#read_tagsfile(tagsfile)
362 call extend(all_entries, entries)
364 call xolox#easytags#write_tagsfile(global_tagsfile, headers, all_entries)
365 call xolox#misc#msg#info("easytags.vim %s: Finished copying tags from %s to %s!", g:xolox#easytags#version, g:easytags_by_filetype, g:easytags_file)
368 call xolox#misc#msg#warn("easytags.vim %s: %s (at %s)", g:xolox#easytags#version, v:exception, v:throwpoint)
372 function! s:save_by_filetype(filter_tags, headers, entries, context)
374 for entry in a:entries
375 let ctags_ft = matchstr(entry[2], '\tlanguage:\zs\S\+')
377 let vim_ft = xolox#easytags#to_vim_ft(ctags_ft)
378 if !has_key(filetypes, vim_ft)
379 let filetypes[vim_ft] = []
381 call add(filetypes[vim_ft], entry)
384 let directory = xolox#misc#path#absolute(g:easytags_by_filetype)
385 for vim_ft in keys(filetypes)
386 let tagsfile = xolox#misc#path#merge(directory, vim_ft)
387 if !filereadable(tagsfile)
388 call xolox#easytags#write_tagsfile(tagsfile, a:headers, filetypes[vim_ft])
390 call s:filter_merge_tags(a:filter_tags, tagsfile, filetypes[vim_ft], a:context)
395 " Public supporting functions (might be useful to others). {{{1
397 function! xolox#easytags#supported_filetypes() " {{{2
398 if !exists('s:supported_filetypes')
399 let starttime = xolox#misc#timer#start()
400 let command = g:easytags_cmd . ' --list-languages'
402 let listing = xolox#shell#execute(command, 1)
403 catch /^Vim\%((\a\+)\)\=:E117/
404 " Ignore missing shell.vim plug-in.
405 let listing = split(system(command), "\n")
407 let msg = "Failed to get supported languages! (output: %s)"
408 throw printf(msg, strtrans(join(listing, "\n")))
411 let s:supported_filetypes = map(copy(listing), 's:check_filetype(listing, v:val)')
412 let msg = "easytags.vim %s: Retrieved %i supported languages in %s."
413 call xolox#misc#timer#stop(msg, g:xolox#easytags#version, len(s:supported_filetypes), starttime)
415 return s:supported_filetypes
418 function! s:check_filetype(listing, cline)
419 if a:cline !~ '^\w\S*$'
420 let msg = "Failed to get supported languages! (output: %s)"
421 throw printf(msg, strtrans(join(a:listing, "\n")))
423 return xolox#easytags#to_vim_ft(a:cline)
426 function! xolox#easytags#read_tagsfile(tagsfile) " {{{2
427 " I'm not sure whether this is by design or an implementation detail but
428 " it's possible for the "!_TAG_FILE_SORTED" header to appear after one or
429 " more tags and Vim will apparently still use the header! For this reason
430 " the xolox#easytags#write_tagsfile() function should also recognize it,
431 " otherwise Vim might complain with "E432: Tags file not sorted".
435 for line in readfile(a:tagsfile)
436 if line =~# '^!_TAG_'
437 call add(headers, line)
439 let entry = xolox#easytags#parse_entry(line)
441 call add(entries, entry)
448 call xolox#misc#msg#warn("easytags.vim %s: Ignored %i invalid line(s) in %s!", g:xolox#easytags#version, num_invalid, a:tagsfile)
450 return [headers, entries]
453 function! xolox#easytags#parse_entry(line) " {{{2
454 let fields = split(a:line, '\t')
455 return len(fields) >= 3 ? fields : []
458 function! xolox#easytags#parse_entries(lines) " {{{2
459 call map(a:lines, 'xolox#easytags#parse_entry(v:val)')
460 return filter(a:lines, '!empty(v:val)')
463 function! xolox#easytags#write_tagsfile(tagsfile, headers, entries) " {{{2
464 " This function always sorts the tags file but understands "foldcase".
466 for line in a:headers
467 if match(line, '^!_TAG_FILE_SORTED\t2') == 0
471 call map(a:entries, 's:join_entry(v:val)')
475 call sort(a:entries, 1)
478 if xolox#misc#os#is_win()
479 " Exuberant Ctags on Windows requires \r\n but Vim's writefile() doesn't add them!
480 for line in a:headers
481 call add(lines, line . "\r")
483 for line in a:entries
484 call add(lines, line . "\r")
487 call extend(lines, a:headers)
488 call extend(lines, a:entries)
490 let tempname = a:tagsfile . '.easytags.tmp'
491 return writefile(lines, tempname) == 0 && rename(tempname, a:tagsfile) == 0
494 function! s:join_entry(value)
495 return type(a:value) == type([]) ? join(a:value, "\t") : a:value
498 function! xolox#easytags#file_has_tags(filename) " {{{2
499 " Check whether the given source file occurs in one of the tags files known
500 " to Vim. This function might not always give the right answer because of
501 " caching, but for the intended purpose that's no problem: When editing an
502 " existing file which has no tags defined the plug-in will run Exuberant
503 " Ctags to update the tags, *unless the file has already been tagged*.
504 call s:cache_tagged_files(s:create_context())
505 return has_key(s:tagged_files, s:resolve(a:filename))
508 if !exists('s:tagged_files')
509 let s:tagged_files = {}
510 let s:known_tagfiles = {}
513 function! s:cache_tagged_files(context) " {{{3
514 if empty(s:tagged_files)
515 " Initialize the cache of tagged files on first use. After initialization
516 " we'll only update the cache when we're reading a tags file from disk for
517 " other purposes anyway (so the cache doesn't introduce too much overhead).
518 let starttime = xolox#misc#timer#start()
519 for tagsfile in tagfiles()
520 if !filereadable(tagsfile)
521 call xolox#misc#msg#warn("easytags.vim %s: Skipping unreadable tags file %s!", fname)
523 let fname = s:canonicalize(tagsfile, a:context)
524 let ftime = getftime(fname)
525 if get(s:known_tagfiles, fname, 0) != ftime
526 let [headers, entries] = xolox#easytags#read_tagsfile(fname)
527 call s:cache_tagged_files_in(fname, ftime, entries, a:context)
531 call xolox#misc#timer#stop("easytags.vim %s: Initialized cache of tagged files in %s.", g:xolox#easytags#version, starttime)
535 function! s:cache_tagged_files_in(fname, ftime, entries, context) " {{{3
536 for entry in a:entries
537 let filename = s:canonicalize(entry[1], a:context)
539 let s:tagged_files[filename] = 1
542 let s:known_tagfiles[a:fname] = a:ftime
545 function! xolox#easytags#get_tagsfile() " {{{2
547 " Look for a suitable project specific tags file?
548 let dynamic_files = xolox#misc#option#get('easytags_dynamic_files', 0)
549 if dynamic_files == 1
550 let tagsfile = get(tagfiles(), 0, '')
551 elseif dynamic_files == 2
552 let tagsfile = xolox#misc#option#eval_tags(&tags, 1)
554 " Check if a file type specific tags file is useful?
555 if empty(tagsfile) && !empty(g:easytags_by_filetype) && index(xolox#easytags#supported_filetypes(), &ft) >= 0
556 let directory = xolox#misc#path#absolute(g:easytags_by_filetype)
557 let tagsfile = xolox#misc#path#merge(directory, &filetype)
559 " Default to the global tags file?
561 let tagsfile = expand(xolox#misc#option#get('easytags_file'))
563 " If the tags file exists, make sure it is writable!
564 if filereadable(tagsfile) && filewritable(tagsfile) != 1
565 let message = "The tags file %s isn't writable!"
566 throw printf(message, fnamemodify(tagsfile, ':~'))
571 " Public API for definition of file type specific dynamic syntax highlighting. {{{1
573 function! xolox#easytags#define_tagkind(object) " {{{2
574 if !has_key(a:object, 'pattern_prefix')
575 let a:object.pattern_prefix = '\C\<'
577 if !has_key(a:object, 'pattern_suffix')
578 let a:object.pattern_suffix = '\>'
580 if !has_key(s:tagkinds, a:object.filetype)
581 let s:tagkinds[a:object.filetype] = []
583 call add(s:tagkinds[a:object.filetype], a:object)
586 function! xolox#easytags#map_filetypes(vim_ft, ctags_ft) " {{{2
587 call add(s:vim_filetypes, a:vim_ft)
588 call add(s:ctags_filetypes, a:ctags_ft)
591 function! xolox#easytags#alias_filetypes(...) " {{{2
592 " TODO Simplify alias handling, this much complexity really isn't needed!
594 let s:canonical_aliases[type] = a:1
595 if !has_key(s:aliases, type)
596 let s:aliases[type] = {}
601 let vimft1 = a:000[i]
602 let ctagsft1 = xolox#easytags#to_ctags_ft(vimft1)
603 let vimft2 = a:000[j]
604 let ctagsft2 = xolox#easytags#to_ctags_ft(vimft2)
605 if !has_key(s:aliases[vimft1], ctagsft2)
606 let s:aliases[vimft1][ctagsft2] = 1
608 if !has_key(s:aliases[vimft2], ctagsft1)
609 let s:aliases[vimft2][ctagsft1] = 1
615 function! xolox#easytags#to_vim_ft(ctags_ft) " {{{2
616 let type = tolower(a:ctags_ft)
617 let index = index(s:ctags_filetypes, type)
618 return index >= 0 ? s:vim_filetypes[index] : type
621 function! xolox#easytags#to_ctags_ft(vim_ft) " {{{2
622 let type = tolower(a:vim_ft)
623 let index = index(s:vim_filetypes, type)
624 return index >= 0 ? s:ctags_filetypes[index] : type
627 " Miscellaneous script-local functions. {{{1
629 function! s:create_context() " {{{2
633 function! s:resolve(filename) " {{{2
634 if xolox#misc#option#get('easytags_resolve_links', 0)
635 return resolve(a:filename)
641 function! s:canonicalize(filename, context) " {{{2
643 if has_key(a:context.cache, a:filename)
644 return a:context.cache[a:filename]
646 let canonical = s:resolve(fnamemodify(a:filename, ':p'))
647 let a:context.cache[a:filename] = canonical
654 function! s:python_available() " {{{2
655 if !exists('s:is_python_available')
657 execute 'pyfile' fnameescape(g:easytags_python_script)
659 silent python easytags_ping()
661 let s:is_python_available = (output =~ 'it works!')
663 let s:is_python_available = 0
666 return s:is_python_available
669 function! s:highlight_with_python(syntax_group, tagkind) " {{{2
670 if xolox#misc#option#get('easytags_python_enabled', 1) && s:python_available()
671 " Gather arguments for Python function.
673 let context['tagsfiles'] = tagfiles()
674 let context['syntaxgroup'] = a:syntax_group
675 let context['filetype'] = xolox#easytags#to_ctags_ft(&ft)
676 let context['tagkinds'] = get(a:tagkind, 'tagkinds', '')
677 let context['prefix'] = get(a:tagkind, 'pattern_prefix', '')
678 let context['suffix'] = get(a:tagkind, 'pattern_suffix', '')
679 let context['filters'] = get(a:tagkind, 'python_filter', {})
680 " Call the Python function and intercept the output.
684 silent python print easytags_gensyncmd(**vim.eval('context'))
690 " If the Python script raised an error, don't run it again.
691 let g:easytags_python_enabled = 0
697 " Built-in file type & tag kind definitions. {{{1
699 " Don't bother redefining everything below when this script is sourced again.
700 if exists('s:tagkinds')
706 " Define the built-in Vim <=> Ctags file-type mappings.
707 let s:vim_filetypes = []
708 let s:ctags_filetypes = []
709 call xolox#easytags#map_filetypes('cpp', 'c++')
710 call xolox#easytags#map_filetypes('cs', 'c#')
711 call xolox#easytags#map_filetypes(exists('g:filetype_asp') ? g:filetype_asp : 'aspvbs', 'asp')
713 " Define the Vim file-types that are aliased by default.
715 let s:canonical_aliases = {}
716 call xolox#easytags#alias_filetypes('c', 'cpp', 'objc', 'objcpp')
718 " Enable line continuation.
719 let s:cpo_save = &cpo
724 call xolox#easytags#define_tagkind({
726 \ 'hlgroup': 'luaFunc',
731 call xolox#easytags#define_tagkind({
733 \ 'hlgroup': 'cType',
734 \ 'tagkinds': '[cgstu]'})
736 call xolox#easytags#define_tagkind({
738 \ 'hlgroup': 'cEnum',
741 call xolox#easytags#define_tagkind({
743 \ 'hlgroup': 'cPreProc',
746 call xolox#easytags#define_tagkind({
748 \ 'hlgroup': 'cFunction',
749 \ 'tagkinds': '[fp]'})
751 highlight def link cEnum Identifier
752 highlight def link cFunction Function
754 if xolox#misc#option#get('easytags_include_members', 0)
755 call xolox#easytags#define_tagkind({
757 \ 'hlgroup': 'cMember',
759 highlight def link cMember Identifier
764 call xolox#easytags#define_tagkind({
766 \ 'hlgroup': 'phpFunctions',
768 \ 'pattern_suffix': '(\@='})
770 call xolox#easytags#define_tagkind({
772 \ 'hlgroup': 'phpClasses',
777 call xolox#easytags#define_tagkind({
779 \ 'hlgroup': 'vimAutoGroup',
782 highlight def link vimAutoGroup vimAutoEvent
784 call xolox#easytags#define_tagkind({
786 \ 'hlgroup': 'vimCommand',
788 \ 'pattern_prefix': '\(\(^\|\s\):\?\)\@<=',
789 \ 'pattern_suffix': '\(!\?\(\s\|$\)\)\@='})
791 " Exuberant Ctags doesn't mark script local functions in Vim scripts as
792 " "static". When your tags file contains search patterns this plug-in can use
793 " those search patterns to check which Vim script functions are defined
794 " globally and which script local.
796 call xolox#easytags#define_tagkind({
798 \ 'hlgroup': 'vimFuncName',
799 \ 'vim_filter': 'v:val.kind ==# "f" && get(v:val, "cmd", "") !~? ''<sid>\w\|\<s:\w''',
800 \ 'python_filter': { 'kind': 'f', 'nomatch': '(?i)(<sid>\w|\bs:\w)' },
801 \ 'pattern_prefix': '\C\%(\<s:\|<[sS][iI][dD]>\)\@<!\<'})
803 call xolox#easytags#define_tagkind({
805 \ 'hlgroup': 'vimScriptFuncName',
806 \ 'vim_filter': 'v:val.kind ==# "f" && get(v:val, "cmd", "") =~? ''<sid>\w\|\<s:\w''',
807 \ 'python_filter': { 'kind': 'f', 'match': '(?i)(<sid>\w|\bs:\w)' },
808 \ 'pattern_prefix': '\C\%(\<s:\|<[sS][iI][dD]>\)'})
810 highlight def link vimScriptFuncName vimFuncName
814 call xolox#easytags#define_tagkind({
815 \ 'filetype': 'python',
816 \ 'hlgroup': 'pythonFunction',
818 \ 'pattern_prefix': '\%(\<def\s\+\)\@<!\<'})
820 call xolox#easytags#define_tagkind({
821 \ 'filetype': 'python',
822 \ 'hlgroup': 'pythonMethod',
824 \ 'pattern_prefix': '\.\@<='})
826 call xolox#easytags#define_tagkind({
827 \ 'filetype': 'python',
828 \ 'hlgroup': 'pythonClass',
831 highlight def link pythonMethodTag pythonFunction
832 highlight def link pythonClassTag pythonFunction
836 call xolox#easytags#define_tagkind({
837 \ 'filetype': 'java',
838 \ 'hlgroup': 'javaClass',
841 call xolox#easytags#define_tagkind({
842 \ 'filetype': 'java',
843 \ 'hlgroup': 'javaMethod',
846 highlight def link javaClass Identifier
847 highlight def link javaMethod Function
851 " TODO C# name spaces, interface names, enumeration member names, structure names?
853 call xolox#easytags#define_tagkind({
855 \ 'hlgroup': 'csClassOrStruct',
858 call xolox#easytags#define_tagkind({
860 \ 'hlgroup': 'csMethod',
861 \ 'tagkinds': '[ms]'})
863 highlight def link csClassOrStruct Identifier
864 highlight def link csMethod Function
868 call xolox#easytags#define_tagkind({
869 \ 'filetype': 'ruby',
870 \ 'hlgroup': 'rubyModuleName',
873 call xolox#easytags#define_tagkind({
874 \ 'filetype': 'ruby',
875 \ 'hlgroup': 'rubyClassName',
878 call xolox#easytags#define_tagkind({
879 \ 'filetype': 'ruby',
880 \ 'hlgroup': 'rubyMethodName',
881 \ 'tagkinds': '[fF]'})
883 highlight def link rubyModuleName Type
884 highlight def link rubyClassName Type
885 highlight def link rubyMethodName Function
889 " Restore "cpoptions".
890 let &cpo = s:cpo_save