From 29fded230f8ed2354b7080603e1be169b0cb758c Mon Sep 17 00:00:00 2001 From: Peter Odding Date: Tue, 15 Mar 2011 23:18:06 +0100 Subject: Make Pathogen users happy :-) Let's hope this works the first time... (never used git submodules before) --- .gitmodules | 3 + autoload.vim | 631 -------------------------------------------- autoload/xolox/easytags.vim | 631 ++++++++++++++++++++++++++++++++++++++++++++ autoload/xolox/misc | 1 + easytags.vim | 175 ------------ plugin/easytags.vim | 175 ++++++++++++ 6 files changed, 810 insertions(+), 806 deletions(-) create mode 100644 .gitmodules delete mode 100644 autoload.vim create mode 100644 autoload/xolox/easytags.vim create mode 160000 autoload/xolox/misc delete mode 100644 easytags.vim create mode 100644 plugin/easytags.vim diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c98216e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "autoload/xolox/misc"] + path = autoload/xolox/misc + url = git://github.com/xolox/vim-misc.git diff --git a/autoload.vim b/autoload.vim deleted file mode 100644 index 5cd12c5..0000000 --- a/autoload.vim +++ /dev/null @@ -1,631 +0,0 @@ -" Vim script -" Author: Peter Odding -" Last Change: February 24, 2011 -" URL: http://peterodding.com/code/vim/easytags/ - -let s:script = expand(':p:~') - -" Public interface through (automatic) commands. {{{1 - -function! easytags#autoload() " {{{2 - try - " Update the entries for the current file in the global tags file? - let pathname = s:resolve(expand('%:p')) - if pathname != '' - let tags_outdated = getftime(pathname) > getftime(easytags#get_tagsfile()) - if tags_outdated || !easytags#file_has_tags(pathname) - call easytags#update(1, 0, []) - endif - endif - " Apply highlighting of tags in global tags file to current buffer? - if &eventignore !~? '\' - if !exists('b:easytags_last_highlighted') - call easytags#highlight() - else - for tagfile in tagfiles() - if getftime(tagfile) > b:easytags_last_highlighted - call easytags#highlight() - break - endif - endfor - endif - let b:easytags_last_highlighted = localtime() - endif - catch - call xolox#warning("%s: %s (at %s)", s:script, v:exception, v:throwpoint) - endtry -endfunction - -function! easytags#update(silent, filter_tags, filenames) " {{{2 - try - let s:cached_filenames = {} - let starttime = xolox#timer#start() - let cfile = s:check_cfile(a:silent, a:filter_tags, !empty(a:filenames)) - let tagsfile = easytags#get_tagsfile() - let firstrun = !filereadable(tagsfile) - let cmdline = s:prep_cmdline(cfile, tagsfile, firstrun, a:filenames) - let output = s:run_ctags(starttime, cfile, tagsfile, firstrun, cmdline) - if !firstrun - let num_filtered = s:filter_merge_tags(a:filter_tags, tagsfile, output) - if cfile != '' - let msg = "%s: Updated tags for %s in %s." - call xolox#timer#stop(msg, s:script, expand('%:p:~'), starttime) - elseif a:0 > 0 - let msg = "%s: Updated tags in %s." - call xolox#timer#stop(msg, s:script, starttime) - else - let msg = "%s: Filtered %i invalid tags in %s." - call xolox#timer#stop(msg, s:script, num_filtered, starttime) - endif - endif - return 1 - catch - call xolox#warning("%s: %s (at %s)", s:script, v:exception, v:throwpoint) - finally - unlet s:cached_filenames - endtry -endfunction - -function! s:check_cfile(silent, filter_tags, have_args) " {{{3 - if a:have_args - return '' - endif - let silent = a:silent || a:filter_tags - if g:easytags_autorecurse - let cdir = s:resolve(expand('%:p:h')) - if !isdirectory(cdir) - if silent | return '' | endif - throw "The directory of the current file doesn't exist yet!" - endif - return cdir - endif - let cfile = s:resolve(expand('%:p')) - if cfile == '' || !filereadable(cfile) - if silent | return '' | endif - throw "You'll need to save your file before using :UpdateTags!" - elseif g:easytags_ignored_filetypes != '' && &ft =~ g:easytags_ignored_filetypes - if silent | return '' | endif - throw "The " . string(&ft) . " file type is explicitly ignored." - elseif index(easytags#supported_filetypes(), &ft) == -1 - if silent | return '' | endif - throw "Exuberant Ctags doesn't support the " . string(&ft) . " file type!" - endif - return cfile -endfunction - -function! s:prep_cmdline(cfile, tagsfile, firstrun, arguments) " {{{3 - let cmdline = [g:easytags_cmd, '--fields=+l', '--c-kinds=+p', '--c++-kinds=+p'] - if a:firstrun - call add(cmdline, shellescape('-f' . a:tagsfile)) - call add(cmdline, '--sort=' . (&ic ? 'foldcase' : 'yes')) - else - call add(cmdline, '--sort=no') - call add(cmdline, '-f-') - endif - if g:easytags_include_members - call add(cmdline, '--extra=+q') - endif - let have_args = 0 - if a:cfile != '' - if g:easytags_autorecurse - call add(cmdline, '-R') - call add(cmdline, shellescape(a:cfile)) - else - let filetype = easytags#to_ctags_ft(&filetype) - call add(cmdline, shellescape('--language-force=' . filetype)) - call add(cmdline, shellescape(a:cfile)) - endif - let have_args = 1 - else - for arg in a:arguments - if arg =~ '^-' - call add(cmdline, arg) - let have_args = 1 - else - let matches = split(expand(arg), "\n") - if !empty(matches) - call map(matches, 'shellescape(s:canonicalize(v:val))') - call extend(cmdline, matches) - let have_args = 1 - endif - endif - endfor - endif - " No need to run Exuberant Ctags without any filename arguments! - return have_args ? join(cmdline) : '' -endfunction - -function! s:run_ctags(starttime, cfile, tagsfile, firstrun, cmdline) " {{{3 - let output = [] - if a:cmdline != '' - call xolox#debug("%s: Executing %s", s:script, a:cmdline) - try - let output = xolox#shell#execute(a:cmdline, 1) - catch /^Vim\%((\a\+)\)\=:E117/ - " Ignore missing shell.vim plug-in. - let output = split(system(a:cmdline), "\n") - if v:shell_error - let msg = "Failed to update tags file %s: %s!" - throw printf(msg, fnamemodify(a:tagsfile, ':~'), strtrans(join(output, "\n"))) - endif - endtry - if a:firstrun - if a:cfile != '' - call easytags#add_tagged_file(a:cfile) - call xolox#timer#stop("%s: Created tags for %s in %s.", s:script, expand('%:p:~'), a:starttime) - else - call xolox#timer#stop("%s: Created tags in %s.", s:script, a:starttime) - endif - endif - endif - return output -endfunction - -function! s:filter_merge_tags(filter_tags, tagsfile, output) " {{{3 - let [headers, entries] = easytags#read_tagsfile(a:tagsfile) - call s:set_tagged_files(entries) - let filters = [] - let tagged_files = s:find_tagged_files(a:output) - if !empty(tagged_files) - call add(filters, '!has_key(tagged_files, s:canonicalize(get(v:val, 1)))') - endif - if a:filter_tags - call add(filters, 'filereadable(get(v:val, 1))') - endif - let num_old_entries = len(entries) - if !empty(filters) - call filter(entries, join(filters, ' && ')) - endif - let num_filtered = num_old_entries - len(entries) - call map(entries, 'join(v:val, "\t")') - call extend(entries, a:output) - if !easytags#write_tagsfile(a:tagsfile, headers, entries) - let msg = "Failed to write filtered tags file %s!" - throw printf(msg, fnamemodify(a:tagsfile, ':~')) - endif - return num_filtered -endfunction - -function! s:find_tagged_files(new_entries) " {{{3 - let tagged_files = {} - for line in a:new_entries - " Never corrupt the tags file by merging an invalid line - " (probably an error message) with the existing tags! - if match(line, '^[^\t]\+\t[^\t]\+\t.\+$') == -1 - throw "Exuberant Ctags returned invalid data: " . strtrans(line) - endif - let filename = matchstr(line, '^[^\t]\+\t\zs[^\t]\+') - if !has_key(tagged_files, filename) - let filename = s:canonicalize(filename) - let tagged_files[filename] = 1 - call easytags#add_tagged_file(filename) - endif - endfor - return tagged_files -endfunction - -function! easytags#highlight() " {{{2 - try - " Treat C++ and Objective-C as plain C. - let filetype = get(s:canonical_aliases, &ft, &ft) - let tagkinds = get(s:tagkinds, filetype, []) - if exists('g:syntax_on') && !empty(tagkinds) && !exists('b:easytags_nohl') - let starttime = xolox#timer#start() - if !has_key(s:aliases, filetype) - let ctags_filetype = easytags#to_ctags_ft(filetype) - let taglist = filter(taglist('.'), "get(v:val, 'language', '') ==? ctags_filetype") - else - let aliases = s:aliases[&ft] - let taglist = filter(taglist('.'), "has_key(aliases, tolower(get(v:val, 'language', '')))") - endif - for tagkind in tagkinds - let hlgroup_tagged = tagkind.hlgroup . 'Tag' - if hlexists(hlgroup_tagged) - execute 'syntax clear' hlgroup_tagged - else - execute 'highlight def link' hlgroup_tagged tagkind.hlgroup - endif - let matches = filter(copy(taglist), tagkind.filter) - if matches != [] - call map(matches, 'xolox#escape#pattern(get(v:val, "name"))') - let pattern = tagkind.pattern_prefix . '\%(' . join(xolox#unique(matches), '\|') . '\)' . tagkind.pattern_suffix - let template = 'syntax match %s /%s/ containedin=ALLBUT,.*String.*,.*Comment.*,cIncluded' - let command = printf(template, hlgroup_tagged, escape(pattern, '/')) - try - execute command - catch /^Vim\%((\a\+)\)\=:E339/ - let msg = "easytags.vim: Failed to highlight %i %s tags because pattern is too big! (%i KB)" - call xolox#warning(printf(msg, len(matches), tagkind.hlgroup, len(pattern) / 1024)) - endtry - endif - endfor - redraw - let bufname = expand('%:p:~') - if bufname == '' - let bufname = 'unnamed buffer #' . bufnr('%') - endif - let msg = "%s: Highlighted tags in %s in %s." - call xolox#timer#stop(msg, s:script, bufname, starttime) - return 1 - endif - catch - call xolox#warning("%s: %s (at %s)", s:script, v:exception, v:throwpoint) - endtry -endfunction - -" Public supporting functions (might be useful to others). {{{1 - -function! easytags#supported_filetypes() " {{{2 - if !exists('s:supported_filetypes') - let starttime = xolox#timer#start() - let command = g:easytags_cmd . ' --list-languages' - try - let listing = xolox#shell#execute(command, 1) - catch /^Vim\%((\a\+)\)\=:E117/ - " Ignore missing shell.vim plug-in. - let listing = split(system(command), "\n") - if v:shell_error - let msg = "Failed to get supported languages! (output: %s)" - throw printf(msg, strtrans(join(listing, "\n"))) - endif - endtry - let s:supported_filetypes = map(copy(listing), 's:check_filetype(listing, v:val)') - let msg = "%s: Retrieved %i supported languages in %s." - call xolox#timer#stop(msg, s:script, len(s:supported_filetypes), starttime) - endif - return s:supported_filetypes -endfunction - -function! s:check_filetype(listing, cline) - if a:cline !~ '^\w\S*$' - let msg = "Failed to get supported languages! (output: %s)" - throw printf(msg, strtrans(join(a:listing, "\n"))) - endif - return easytags#to_vim_ft(a:cline) -endfunction - -function! easytags#read_tagsfile(tagsfile) " {{{2 - " I'm not sure whether this is by design or an implementation detail but - " it's possible for the "!_TAG_FILE_SORTED" header to appear after one or - " more tags and Vim will apparently still use the header! For this reason - " the easytags#write_tagsfile() function should also recognize it, otherwise - " Vim might complain with "E432: Tags file not sorted". - let headers = [] - let entries = [] - let pattern = '^\([^\t]\+\)\t\([^\t]\+\)\t\(.\+\)$' - for line in readfile(a:tagsfile) - if line =~# '^!_TAG_' - call add(headers, line) - else - call add(entries, matchlist(line, pattern)[1:3]) - endif - endfor - return [headers, entries] -endfunction - -function! easytags#write_tagsfile(tagsfile, headers, entries) " {{{2 - " This function always sorts the tags file but understands "foldcase". - let sort_order = 1 - for line in a:headers - if match(line, '^!_TAG_FILE_SORTED\t2') == 0 - let sort_order = 2 - endif - endfor - if sort_order == 1 - call sort(a:entries) - else - call sort(a:entries, 1) - endif - let lines = [] - if has('win32') || has('win64') - " Exuberant Ctags on Windows requires \r\n but Vim's writefile() doesn't add them! - for line in a:headers - call add(lines, line . "\r") - endfor - for line in a:entries - call add(lines, line . "\r") - endfor - else - call extend(lines, a:headers) - call extend(lines, a:entries) - endif - return writefile(lines, a:tagsfile) == 0 -endfunction - -function! easytags#file_has_tags(filename) " {{{2 - call s:cache_tagged_files() - return has_key(s:tagged_files, s:resolve(a:filename)) -endfunction - -function! easytags#add_tagged_file(filename) " {{{2 - call s:cache_tagged_files() - let filename = s:resolve(a:filename) - let s:tagged_files[filename] = 1 -endfunction - -function! easytags#get_tagsfile() " {{{2 - let tagsfile = expand(g:easytags_file) - if filereadable(tagsfile) && filewritable(tagsfile) != 1 - let message = "The tags file %s isn't writable!" - throw printf(message, fnamemodify(tagsfile, ':~')) - endif - return tagsfile -endfunction - -" Public API for file-type specific dynamic syntax highlighting. {{{1 - -function! easytags#define_tagkind(object) " {{{2 - if !has_key(a:object, 'pattern_prefix') - let a:object.pattern_prefix = '\C\<' - endif - if !has_key(a:object, 'pattern_suffix') - let a:object.pattern_suffix = '\>' - endif - if !has_key(s:tagkinds, a:object.filetype) - let s:tagkinds[a:object.filetype] = [] - endif - call add(s:tagkinds[a:object.filetype], a:object) -endfunction - -function! easytags#map_filetypes(vim_ft, ctags_ft) " {{{2 - call add(s:vim_filetypes, a:vim_ft) - call add(s:ctags_filetypes, a:ctags_ft) -endfunction - -function! easytags#alias_filetypes(...) " {{{2 - for type in a:000 - let s:canonical_aliases[type] = a:1 - if !has_key(s:aliases, type) - let s:aliases[type] = {} - endif - endfor - for i in range(a:0) - for j in range(a:0) - let vimft1 = a:000[i] - let ctagsft1 = easytags#to_ctags_ft(vimft1) - let vimft2 = a:000[j] - let ctagsft2 = easytags#to_ctags_ft(vimft2) - if !has_key(s:aliases[vimft1], ctagsft2) - let s:aliases[vimft1][ctagsft2] = 1 - endif - if !has_key(s:aliases[vimft2], ctagsft1) - let s:aliases[vimft2][ctagsft1] = 1 - endif - endfor - endfor -endfunction - -function! easytags#to_vim_ft(ctags_ft) " {{{2 - let type = tolower(a:ctags_ft) - let index = index(s:ctags_filetypes, type) - return index >= 0 ? s:vim_filetypes[index] : type -endfunction - -function! easytags#to_ctags_ft(vim_ft) " {{{2 - let type = tolower(a:vim_ft) - let index = index(s:vim_filetypes, type) - return index >= 0 ? s:ctags_filetypes[index] : type -endfunction - -" Miscellaneous script-local functions. {{{1 - -function! s:resolve(filename) " {{{2 - if g:easytags_resolve_links - return resolve(a:filename) - else - return a:filename - endif -endfunction - -function! s:canonicalize(filename) " {{{2 - if has_key(s:cached_filenames, a:filename) - return s:cached_filenames[a:filename] - endif - let canonical = s:resolve(fnamemodify(a:filename, ':p')) - let s:cached_filenames[a:filename] = canonical - return canonical - endif -endfunction - -function! s:cache_tagged_files() " {{{2 - if !exists('s:tagged_files') - let tagsfile = easytags#get_tagsfile() - try - let [headers, entries] = easytags#read_tagsfile(tagsfile) - call s:set_tagged_files(entries) - catch /\/ - " Ignore missing tags file. - call s:set_tagged_files([]) - endtry - endif -endfunction - -function! s:set_tagged_files(entries) " {{{2 - " TODO use taglist() instead of readfile() so that all tag files are - " automatically used :-) - let s:tagged_files = {} - for entry in a:entries - let filename = get(entry, 1, '') - if filename != '' - let s:tagged_files[s:resolve(filename)] = 1 - endif - endfor -endfunction - -" Built-in file type & tag kind definitions. {{{1 - -" Don't bother redefining everything below when this script is sourced again. -if exists('s:tagkinds') - finish -endif - -let s:tagkinds = {} - -" Define the built-in Vim <=> Ctags file-type mappings. -let s:vim_filetypes = [] -let s:ctags_filetypes = [] -call easytags#map_filetypes('cpp', 'c++') -call easytags#map_filetypes('cs', 'c#') -call easytags#map_filetypes(exists('g:filetype_asp') ? g:filetype_asp : 'aspvbs', 'asp') - -" Define the Vim file-types that are aliased by default. -let s:aliases = {} -let s:canonical_aliases = {} -call easytags#alias_filetypes('c', 'cpp', 'objc', 'objcpp') - -" Enable line continuation. -let s:cpo_save = &cpo -set cpo&vim - -" Lua. {{{2 - -call easytags#define_tagkind({ - \ 'filetype': 'lua', - \ 'hlgroup': 'luaFunc', - \ 'filter': 'get(v:val, "kind") ==# "f"'}) - -" C. {{{2 - -call easytags#define_tagkind({ - \ 'filetype': 'c', - \ 'hlgroup': 'cType', - \ 'filter': 'get(v:val, "kind") =~# "[cgstu]"'}) - -call easytags#define_tagkind({ - \ 'filetype': 'c', - \ 'hlgroup': 'cEnum', - \ 'filter': 'get(v:val, "kind") ==# "e"'}) - -call easytags#define_tagkind({ - \ 'filetype': 'c', - \ 'hlgroup': 'cPreProc', - \ 'filter': 'get(v:val, "kind") ==# "d"'}) - -call easytags#define_tagkind({ - \ 'filetype': 'c', - \ 'hlgroup': 'cFunction', - \ 'filter': 'get(v:val, "kind") =~# "[fp]"'}) - -highlight def link cEnum Identifier -highlight def link cFunction Function - -if g:easytags_include_members - call easytags#define_tagkind({ - \ 'filetype': 'c', - \ 'hlgroup': 'cMember', - \ 'filter': 'get(v:val, "kind") ==# "m"'}) - highlight def link cMember Identifier -endif - -" PHP. {{{2 - -call easytags#define_tagkind({ - \ 'filetype': 'php', - \ 'hlgroup': 'phpFunctions', - \ 'filter': 'get(v:val, "kind") ==# "f"', - \ 'pattern_suffix': '(\@='}) - -call easytags#define_tagkind({ - \ 'filetype': 'php', - \ 'hlgroup': 'phpClasses', - \ 'filter': 'get(v:val, "kind") ==# "c"'}) - -" Vim script. {{{2 - -call easytags#define_tagkind({ - \ 'filetype': 'vim', - \ 'hlgroup': 'vimAutoGroup', - \ 'filter': 'get(v:val, "kind") ==# "a"'}) - -highlight def link vimAutoGroup vimAutoEvent - -call easytags#define_tagkind({ - \ 'filetype': 'vim', - \ 'hlgroup': 'vimCommand', - \ 'filter': 'get(v:val, "kind") ==# "c"', - \ 'pattern_prefix': '\(\(^\|\s\):\?\)\@<=', - \ 'pattern_suffix': '\(!\?\(\s\|$\)\)\@='}) - -" Exuberant Ctags doesn't mark script local functions in Vim scripts as -" "static". When your tags file contains search patterns this plug-in can use -" those search patterns to check which Vim script functions are defined -" globally and which script local. - -call easytags#define_tagkind({ - \ 'filetype': 'vim', - \ 'hlgroup': 'vimFuncName', - \ 'filter': 'get(v:val, "kind") ==# "f" && get(v:val, "cmd") !~? ''\w\|\\)\@\w\|\\)'}) - -highlight def link vimScriptFuncName vimFuncName - -" Python. {{{2 - -call easytags#define_tagkind({ - \ 'filetype': 'python', - \ 'hlgroup': 'pythonFunction', - \ 'filter': 'get(v:val, "kind") ==# "f"', - \ 'pattern_prefix': '\%(\ +" Last Change: March 15, 2011 +" URL: http://peterodding.com/code/vim/easytags/ + +let s:script = expand(':p:~') + +" Public interface through (automatic) commands. {{{1 + +function! xolox#easytags#autoload() " {{{2 + try + " Update the entries for the current file in the global tags file? + let pathname = s:resolve(expand('%:p')) + if pathname != '' + let tags_outdated = getftime(pathname) > getftime(xolox#easytags#get_tagsfile()) + if tags_outdated || !xolox#easytags#file_has_tags(pathname) + call xolox#easytags#update(1, 0, []) + endif + endif + " Apply highlighting of tags in global tags file to current buffer? + if &eventignore !~? '\' + if !exists('b:easytags_last_highlighted') + call xolox#easytags#highlight() + else + for tagfile in tagfiles() + if getftime(tagfile) > b:easytags_last_highlighted + call xolox#easytags#highlight() + break + endif + endfor + endif + let b:easytags_last_highlighted = localtime() + endif + catch + call xolox#misc#msg#warn("%s: %s (at %s)", s:script, v:exception, v:throwpoint) + endtry +endfunction + +function! xolox#easytags#update(silent, filter_tags, filenames) " {{{2 + try + let s:cached_filenames = {} + let starttime = xolox#misc#timer#start() + let cfile = s:check_cfile(a:silent, a:filter_tags, !empty(a:filenames)) + let tagsfile = xolox#easytags#get_tagsfile() + let firstrun = !filereadable(tagsfile) + let cmdline = s:prep_cmdline(cfile, tagsfile, firstrun, a:filenames) + let output = s:run_ctags(starttime, cfile, tagsfile, firstrun, cmdline) + if !firstrun + let num_filtered = s:filter_merge_tags(a:filter_tags, tagsfile, output) + if cfile != '' + let msg = "%s: Updated tags for %s in %s." + call xolox#misc#timer#stop(msg, s:script, expand('%:p:~'), starttime) + elseif a:0 > 0 + let msg = "%s: Updated tags in %s." + call xolox#misc#timer#stop(msg, s:script, starttime) + else + let msg = "%s: Filtered %i invalid tags in %s." + call xolox#misc#timer#stop(msg, s:script, num_filtered, starttime) + endif + endif + return 1 + catch + call xolox#misc#msg#warn("%s: %s (at %s)", s:script, v:exception, v:throwpoint) + finally + unlet s:cached_filenames + endtry +endfunction + +function! s:check_cfile(silent, filter_tags, have_args) " {{{3 + if a:have_args + return '' + endif + let silent = a:silent || a:filter_tags + if g:easytags_autorecurse + let cdir = s:resolve(expand('%:p:h')) + if !isdirectory(cdir) + if silent | return '' | endif + throw "The directory of the current file doesn't exist yet!" + endif + return cdir + endif + let cfile = s:resolve(expand('%:p')) + if cfile == '' || !filereadable(cfile) + if silent | return '' | endif + throw "You'll need to save your file before using :UpdateTags!" + elseif g:easytags_ignored_filetypes != '' && &ft =~ g:easytags_ignored_filetypes + if silent | return '' | endif + throw "The " . string(&ft) . " file type is explicitly ignored." + elseif index(xolox#easytags#supported_filetypes(), &ft) == -1 + if silent | return '' | endif + throw "Exuberant Ctags doesn't support the " . string(&ft) . " file type!" + endif + return cfile +endfunction + +function! s:prep_cmdline(cfile, tagsfile, firstrun, arguments) " {{{3 + let cmdline = [g:easytags_cmd, '--fields=+l', '--c-kinds=+p', '--c++-kinds=+p'] + if a:firstrun + call add(cmdline, shellescape('-f' . a:tagsfile)) + call add(cmdline, '--sort=' . (&ic ? 'foldcase' : 'yes')) + else + call add(cmdline, '--sort=no') + call add(cmdline, '-f-') + endif + if g:easytags_include_members + call add(cmdline, '--extra=+q') + endif + let have_args = 0 + if a:cfile != '' + if g:easytags_autorecurse + call add(cmdline, '-R') + call add(cmdline, shellescape(a:cfile)) + else + let filetype = xolox#easytags#to_ctags_ft(&filetype) + call add(cmdline, shellescape('--language-force=' . filetype)) + call add(cmdline, shellescape(a:cfile)) + endif + let have_args = 1 + else + for arg in a:arguments + if arg =~ '^-' + call add(cmdline, arg) + let have_args = 1 + else + let matches = split(expand(arg), "\n") + if !empty(matches) + call map(matches, 'shellescape(s:canonicalize(v:val))') + call extend(cmdline, matches) + let have_args = 1 + endif + endif + endfor + endif + " No need to run Exuberant Ctags without any filename arguments! + return have_args ? join(cmdline) : '' +endfunction + +function! s:run_ctags(starttime, cfile, tagsfile, firstrun, cmdline) " {{{3 + let output = [] + if a:cmdline != '' + call xolox#misc#msg#debug("%s: Executing %s", s:script, a:cmdline) + try + let output = xolox#shell#execute(a:cmdline, 1) + catch /^Vim\%((\a\+)\)\=:E117/ + " Ignore missing shell.vim plug-in. + let output = split(system(a:cmdline), "\n") + if v:shell_error + let msg = "Failed to update tags file %s: %s!" + throw printf(msg, fnamemodify(a:tagsfile, ':~'), strtrans(join(output, "\n"))) + endif + endtry + if a:firstrun + if a:cfile != '' + call xolox#easytags#add_tagged_file(a:cfile) + call xolox#misc#timer#stop("%s: Created tags for %s in %s.", s:script, expand('%:p:~'), a:starttime) + else + call xolox#misc#timer#stop("%s: Created tags in %s.", s:script, a:starttime) + endif + endif + endif + return output +endfunction + +function! s:filter_merge_tags(filter_tags, tagsfile, output) " {{{3 + let [headers, entries] = xolox#easytags#read_tagsfile(a:tagsfile) + call s:set_tagged_files(entries) + let filters = [] + let tagged_files = s:find_tagged_files(a:output) + if !empty(tagged_files) + call add(filters, '!has_key(tagged_files, s:canonicalize(get(v:val, 1)))') + endif + if a:filter_tags + call add(filters, 'filereadable(get(v:val, 1))') + endif + let num_old_entries = len(entries) + if !empty(filters) + call filter(entries, join(filters, ' && ')) + endif + let num_filtered = num_old_entries - len(entries) + call map(entries, 'join(v:val, "\t")') + call extend(entries, a:output) + if !xolox#easytags#write_tagsfile(a:tagsfile, headers, entries) + let msg = "Failed to write filtered tags file %s!" + throw printf(msg, fnamemodify(a:tagsfile, ':~')) + endif + return num_filtered +endfunction + +function! s:find_tagged_files(new_entries) " {{{3 + let tagged_files = {} + for line in a:new_entries + " Never corrupt the tags file by merging an invalid line + " (probably an error message) with the existing tags! + if match(line, '^[^\t]\+\t[^\t]\+\t.\+$') == -1 + throw "Exuberant Ctags returned invalid data: " . strtrans(line) + endif + let filename = matchstr(line, '^[^\t]\+\t\zs[^\t]\+') + if !has_key(tagged_files, filename) + let filename = s:canonicalize(filename) + let tagged_files[filename] = 1 + call xolox#easytags#add_tagged_file(filename) + endif + endfor + return tagged_files +endfunction + +function! xolox#easytags#highlight() " {{{2 + try + " Treat C++ and Objective-C as plain C. + let filetype = get(s:canonical_aliases, &ft, &ft) + let tagkinds = get(s:tagkinds, filetype, []) + if exists('g:syntax_on') && !empty(tagkinds) && !exists('b:easytags_nohl') + let starttime = xolox#misc#timer#start() + if !has_key(s:aliases, filetype) + let ctags_filetype = xolox#easytags#to_ctags_ft(filetype) + let taglist = filter(taglist('.'), "get(v:val, 'language', '') ==? ctags_filetype") + else + let aliases = s:aliases[&ft] + let taglist = filter(taglist('.'), "has_key(aliases, tolower(get(v:val, 'language', '')))") + endif + for tagkind in tagkinds + let hlgroup_tagged = tagkind.hlgroup . 'Tag' + if hlexists(hlgroup_tagged) + execute 'syntax clear' hlgroup_tagged + else + execute 'highlight def link' hlgroup_tagged tagkind.hlgroup + endif + let matches = filter(copy(taglist), tagkind.filter) + if matches != [] + call map(matches, 'xolox#misc#escape#pattern(get(v:val, "name"))') + let pattern = tagkind.pattern_prefix . '\%(' . join(xolox#misc#list#unique(matches), '\|') . '\)' . tagkind.pattern_suffix + let template = 'syntax match %s /%s/ containedin=ALLBUT,.*String.*,.*Comment.*,cIncluded' + let command = printf(template, hlgroup_tagged, escape(pattern, '/')) + try + execute command + catch /^Vim\%((\a\+)\)\=:E339/ + let msg = "easytags.vim: Failed to highlight %i %s tags because pattern is too big! (%i KB)" + call xolox#misc#msg#warn(printf(msg, len(matches), tagkind.hlgroup, len(pattern) / 1024)) + endtry + endif + endfor + redraw + let bufname = expand('%:p:~') + if bufname == '' + let bufname = 'unnamed buffer #' . bufnr('%') + endif + let msg = "%s: Highlighted tags in %s in %s." + call xolox#misc#timer#stop(msg, s:script, bufname, starttime) + return 1 + endif + catch + call xolox#misc#msg#warn("%s: %s (at %s)", s:script, v:exception, v:throwpoint) + endtry +endfunction + +" Public supporting functions (might be useful to others). {{{1 + +function! xolox#easytags#supported_filetypes() " {{{2 + if !exists('s:supported_filetypes') + let starttime = xolox#misc#timer#start() + let command = g:easytags_cmd . ' --list-languages' + try + let listing = xolox#shell#execute(command, 1) + catch /^Vim\%((\a\+)\)\=:E117/ + " Ignore missing shell.vim plug-in. + let listing = split(system(command), "\n") + if v:shell_error + let msg = "Failed to get supported languages! (output: %s)" + throw printf(msg, strtrans(join(listing, "\n"))) + endif + endtry + let s:supported_filetypes = map(copy(listing), 's:check_filetype(listing, v:val)') + let msg = "%s: Retrieved %i supported languages in %s." + call xolox#misc#timer#stop(msg, s:script, len(s:supported_filetypes), starttime) + endif + return s:supported_filetypes +endfunction + +function! s:check_filetype(listing, cline) + if a:cline !~ '^\w\S*$' + let msg = "Failed to get supported languages! (output: %s)" + throw printf(msg, strtrans(join(a:listing, "\n"))) + endif + return xolox#easytags#to_vim_ft(a:cline) +endfunction + +function! xolox#easytags#read_tagsfile(tagsfile) " {{{2 + " I'm not sure whether this is by design or an implementation detail but + " it's possible for the "!_TAG_FILE_SORTED" header to appear after one or + " more tags and Vim will apparently still use the header! For this reason + " the xolox#easytags#write_tagsfile() function should also recognize it, otherwise + " Vim might complain with "E432: Tags file not sorted". + let headers = [] + let entries = [] + let pattern = '^\([^\t]\+\)\t\([^\t]\+\)\t\(.\+\)$' + for line in readfile(a:tagsfile) + if line =~# '^!_TAG_' + call add(headers, line) + else + call add(entries, matchlist(line, pattern)[1:3]) + endif + endfor + return [headers, entries] +endfunction + +function! xolox#easytags#write_tagsfile(tagsfile, headers, entries) " {{{2 + " This function always sorts the tags file but understands "foldcase". + let sort_order = 1 + for line in a:headers + if match(line, '^!_TAG_FILE_SORTED\t2') == 0 + let sort_order = 2 + endif + endfor + if sort_order == 1 + call sort(a:entries) + else + call sort(a:entries, 1) + endif + let lines = [] + if has('win32') || has('win64') + " Exuberant Ctags on Windows requires \r\n but Vim's writefile() doesn't add them! + for line in a:headers + call add(lines, line . "\r") + endfor + for line in a:entries + call add(lines, line . "\r") + endfor + else + call extend(lines, a:headers) + call extend(lines, a:entries) + endif + return writefile(lines, a:tagsfile) == 0 +endfunction + +function! xolox#easytags#file_has_tags(filename) " {{{2 + call s:cache_tagged_files() + return has_key(s:tagged_files, s:resolve(a:filename)) +endfunction + +function! xolox#easytags#add_tagged_file(filename) " {{{2 + call s:cache_tagged_files() + let filename = s:resolve(a:filename) + let s:tagged_files[filename] = 1 +endfunction + +function! xolox#easytags#get_tagsfile() " {{{2 + let tagsfile = expand(g:easytags_file) + if filereadable(tagsfile) && filewritable(tagsfile) != 1 + let message = "The tags file %s isn't writable!" + throw printf(message, fnamemodify(tagsfile, ':~')) + endif + return tagsfile +endfunction + +" Public API for file-type specific dynamic syntax highlighting. {{{1 + +function! xolox#easytags#define_tagkind(object) " {{{2 + if !has_key(a:object, 'pattern_prefix') + let a:object.pattern_prefix = '\C\<' + endif + if !has_key(a:object, 'pattern_suffix') + let a:object.pattern_suffix = '\>' + endif + if !has_key(s:tagkinds, a:object.filetype) + let s:tagkinds[a:object.filetype] = [] + endif + call add(s:tagkinds[a:object.filetype], a:object) +endfunction + +function! xolox#easytags#map_filetypes(vim_ft, ctags_ft) " {{{2 + call add(s:vim_filetypes, a:vim_ft) + call add(s:ctags_filetypes, a:ctags_ft) +endfunction + +function! xolox#easytags#alias_filetypes(...) " {{{2 + for type in a:000 + let s:canonical_aliases[type] = a:1 + if !has_key(s:aliases, type) + let s:aliases[type] = {} + endif + endfor + for i in range(a:0) + for j in range(a:0) + let vimft1 = a:000[i] + let ctagsft1 = xolox#easytags#to_ctags_ft(vimft1) + let vimft2 = a:000[j] + let ctagsft2 = xolox#easytags#to_ctags_ft(vimft2) + if !has_key(s:aliases[vimft1], ctagsft2) + let s:aliases[vimft1][ctagsft2] = 1 + endif + if !has_key(s:aliases[vimft2], ctagsft1) + let s:aliases[vimft2][ctagsft1] = 1 + endif + endfor + endfor +endfunction + +function! xolox#easytags#to_vim_ft(ctags_ft) " {{{2 + let type = tolower(a:ctags_ft) + let index = index(s:ctags_filetypes, type) + return index >= 0 ? s:vim_filetypes[index] : type +endfunction + +function! xolox#easytags#to_ctags_ft(vim_ft) " {{{2 + let type = tolower(a:vim_ft) + let index = index(s:vim_filetypes, type) + return index >= 0 ? s:ctags_filetypes[index] : type +endfunction + +" Miscellaneous script-local functions. {{{1 + +function! s:resolve(filename) " {{{2 + if g:easytags_resolve_links + return resolve(a:filename) + else + return a:filename + endif +endfunction + +function! s:canonicalize(filename) " {{{2 + if has_key(s:cached_filenames, a:filename) + return s:cached_filenames[a:filename] + endif + let canonical = s:resolve(fnamemodify(a:filename, ':p')) + let s:cached_filenames[a:filename] = canonical + return canonical + endif +endfunction + +function! s:cache_tagged_files() " {{{2 + if !exists('s:tagged_files') + let tagsfile = xolox#easytags#get_tagsfile() + try + let [headers, entries] = xolox#easytags#read_tagsfile(tagsfile) + call s:set_tagged_files(entries) + catch /\/ + " Ignore missing tags file. + call s:set_tagged_files([]) + endtry + endif +endfunction + +function! s:set_tagged_files(entries) " {{{2 + " TODO use taglist() instead of readfile() so that all tag files are + " automatically used :-) + let s:tagged_files = {} + for entry in a:entries + let filename = get(entry, 1, '') + if filename != '' + let s:tagged_files[s:resolve(filename)] = 1 + endif + endfor +endfunction + +" Built-in file type & tag kind definitions. {{{1 + +" Don't bother redefining everything below when this script is sourced again. +if exists('s:tagkinds') + finish +endif + +let s:tagkinds = {} + +" Define the built-in Vim <=> Ctags file-type mappings. +let s:vim_filetypes = [] +let s:ctags_filetypes = [] +call xolox#easytags#map_filetypes('cpp', 'c++') +call xolox#easytags#map_filetypes('cs', 'c#') +call xolox#easytags#map_filetypes(exists('g:filetype_asp') ? g:filetype_asp : 'aspvbs', 'asp') + +" Define the Vim file-types that are aliased by default. +let s:aliases = {} +let s:canonical_aliases = {} +call xolox#easytags#alias_filetypes('c', 'cpp', 'objc', 'objcpp') + +" Enable line continuation. +let s:cpo_save = &cpo +set cpo&vim + +" Lua. {{{2 + +call xolox#easytags#define_tagkind({ + \ 'filetype': 'lua', + \ 'hlgroup': 'luaFunc', + \ 'filter': 'get(v:val, "kind") ==# "f"'}) + +" C. {{{2 + +call xolox#easytags#define_tagkind({ + \ 'filetype': 'c', + \ 'hlgroup': 'cType', + \ 'filter': 'get(v:val, "kind") =~# "[cgstu]"'}) + +call xolox#easytags#define_tagkind({ + \ 'filetype': 'c', + \ 'hlgroup': 'cEnum', + \ 'filter': 'get(v:val, "kind") ==# "e"'}) + +call xolox#easytags#define_tagkind({ + \ 'filetype': 'c', + \ 'hlgroup': 'cPreProc', + \ 'filter': 'get(v:val, "kind") ==# "d"'}) + +call xolox#easytags#define_tagkind({ + \ 'filetype': 'c', + \ 'hlgroup': 'cFunction', + \ 'filter': 'get(v:val, "kind") =~# "[fp]"'}) + +highlight def link cEnum Identifier +highlight def link cFunction Function + +if g:easytags_include_members + call xolox#easytags#define_tagkind({ + \ 'filetype': 'c', + \ 'hlgroup': 'cMember', + \ 'filter': 'get(v:val, "kind") ==# "m"'}) + highlight def link cMember Identifier +endif + +" PHP. {{{2 + +call xolox#easytags#define_tagkind({ + \ 'filetype': 'php', + \ 'hlgroup': 'phpFunctions', + \ 'filter': 'get(v:val, "kind") ==# "f"', + \ 'pattern_suffix': '(\@='}) + +call xolox#easytags#define_tagkind({ + \ 'filetype': 'php', + \ 'hlgroup': 'phpClasses', + \ 'filter': 'get(v:val, "kind") ==# "c"'}) + +" Vim script. {{{2 + +call xolox#easytags#define_tagkind({ + \ 'filetype': 'vim', + \ 'hlgroup': 'vimAutoGroup', + \ 'filter': 'get(v:val, "kind") ==# "a"'}) + +highlight def link vimAutoGroup vimAutoEvent + +call xolox#easytags#define_tagkind({ + \ 'filetype': 'vim', + \ 'hlgroup': 'vimCommand', + \ 'filter': 'get(v:val, "kind") ==# "c"', + \ 'pattern_prefix': '\(\(^\|\s\):\?\)\@<=', + \ 'pattern_suffix': '\(!\?\(\s\|$\)\)\@='}) + +" Exuberant Ctags doesn't mark script local functions in Vim scripts as +" "static". When your tags file contains search patterns this plug-in can use +" those search patterns to check which Vim script functions are defined +" globally and which script local. + +call xolox#easytags#define_tagkind({ + \ 'filetype': 'vim', + \ 'hlgroup': 'vimFuncName', + \ 'filter': 'get(v:val, "kind") ==# "f" && get(v:val, "cmd") !~? ''\w\|\\)\@\w\|\\)'}) + +highlight def link vimScriptFuncName vimFuncName + +" Python. {{{2 + +call xolox#easytags#define_tagkind({ + \ 'filetype': 'python', + \ 'hlgroup': 'pythonFunction', + \ 'filter': 'get(v:val, "kind") ==# "f"', + \ 'pattern_prefix': '\%(\ -" Last Change: February 24, 2011 -" URL: http://peterodding.com/code/vim/easytags/ -" Requires: Exuberant Ctags (http://ctags.sf.net) -" License: MIT -" Version: 2.2 - -" Support for automatic update using the GLVS plug-in. -" GetLatestVimScripts: 3114 1 :AutoInstall: easytags.zip - -" Don't source the plug-in when its already been loaded or &compatible is set. -if &cp || exists('g:loaded_easytags') - finish -endif - -let s:script = expand(':p:~') - -" Configuration defaults and initialization. {{{1 - -if !exists('g:easytags_file') - if has('win32') || has('win64') - let g:easytags_file = '~\_vimtags' - else - let g:easytags_file = '~/.vimtags' - endif -endif - -if !exists('g:easytags_resolve_links') - let g:easytags_resolve_links = 0 -endif - -if !exists('g:easytags_always_enabled') - let g:easytags_always_enabled = 0 -endif - -if !exists('g:easytags_on_cursorhold') - let g:easytags_on_cursorhold = 1 -endif - -if !exists('g:easytags_ignored_filetypes') - let g:easytags_ignored_filetypes = '^tex$' -endif - -if !exists('g:easytags_autorecurse') - let g:easytags_autorecurse = 0 -endif - -if !exists('g:easytags_include_members') - let g:easytags_include_members = 0 -endif - -function! s:InitEasyTags(version) - " Check that the location of Exuberant Ctags has been configured or that the - " correct version of the program exists in one of its default locations. - if exists('g:easytags_cmd') && s:CheckCtags(g:easytags_cmd, a:version) - return 1 - endif - " On Ubuntu Linux, Exuberant Ctags is installed as `ctags'. On Debian Linux, - " Exuberant Ctags is installed as `exuberant-ctags'. On Free-BSD, Exuberant - " Ctags is installed as `exctags'. - for name in ['ctags', 'exuberant-ctags', 'exctags'] - if s:CheckCtags(name, a:version) - let g:easytags_cmd = name - return 1 - endif - endfor -endfunction - -function! s:CheckCtags(name, version) - " Not every executable out there named `ctags' is in fact Exuberant Ctags. - " This function makes sure it is because the easytags plug-in requires the - " --list-languages option. - if executable(a:name) - let command = a:name . ' --version' - try - let listing = join(xolox#shell#execute(command, 1), '\n') - catch /^Vim\%((\a\+)\)\=:E117/ - " Ignore missing shell.vim plug-in. - let listing = system(command) - catch - " xolox#shell#execute() converts shell errors to exceptions and since - " we're checking whether one of several executables exists we don't want - " to throw an error when the first one doesn't! - return - endtry - let pattern = 'Exuberant Ctags \zs\d\+\(\.\d\+\)*' - let g:easytags_ctags_version = matchstr(listing, pattern) - return s:VersionToNumber(g:easytags_ctags_version) >= a:version - endif -endfunction - -function! s:VersionToNumber(s) - let values = split(a:s, '\.') - if len(values) == 1 - return values[0] * 10 - elseif len(values) >= 2 - return values[0] * 10 + values[1][0] - endif -endfunction - -if !s:InitEasyTags(55) - if !exists('g:easytags_ctags_version') || empty(g:easytags_ctags_version) - let s:msg = "%s: Plug-in not loaded because Exuberant Ctags isn't installed!" - if executable('apt-get') - let s:msg .= " On Ubuntu & Debian you can install Exuberant Ctags by" - let s:msg .= " installing the package named `exuberant-ctags':" - let s:msg .= " sudo apt-get install exuberant-ctags" - else - let s:msg .= " Please download & install Exuberant Ctags from http://ctags.sf.net" - endif - echomsg printf(s:msg, s:script) - else - let s:msg = "%s: Plug-in not loaded because Exuberant Ctags 5.5" - let s:msg .= " or newer is required while you have version %s installed!" - echomsg printf(s:msg, s:script, g:easytags_ctags_version) - endif - unlet s:msg - finish -endif - -function! s:RegisterTagsFile() - " Parse the &tags option and get a list of all tags files *including - " non-existing files* (this is why we can't just call tagfiles()). - let tagfiles = xolox#option#split_tags(&tags) - let expanded = map(copy(tagfiles), 'resolve(expand(v:val))') - " Add the filename to the &tags option when the user hasn't done so already. - if index(expanded, resolve(expand(g:easytags_file))) == -1 - " This is a real mess because of bugs in Vim?! :let &tags = '...' doesn't - " work on UNIX and Windows, :set tags=... doesn't work on Windows. What I - " mean with "doesn't work" is that tagfiles() == [] after the :let/:set - " command even though the tags file exists! One easy way to confirm that - " this is a bug in Vim is to type :set tags= then press followed by - " . Now you entered the exact same value that the code below also did - " but suddenly Vim sees the tags file and tagfiles() != [] :-S - call insert(tagfiles, g:easytags_file) - let value = xolox#option#join_tags(tagfiles) - let cmd = ':set tags=' . escape(value, '\ ') - if has('win32') || has('win64') - " TODO How to clear the expression from Vim's status line? - call feedkeys(":" . cmd . "|let &ro=&ro\", 'n') - else - execute cmd - endif - endif -endfunction - -" Let Vim know about the global tags file created by this plug-in. -call s:RegisterTagsFile() - -" The :UpdateTags and :HighlightTags commands. {{{1 - -command! -bar -bang -nargs=* -complete=file UpdateTags call easytags#update(0, == '!', []) -command! -bar HighlightTags call easytags#highlight() - -" Automatic commands. {{{1 - -augroup PluginEasyTags - autocmd! - if g:easytags_always_enabled - " TODO Also on FocusGained because tags files might be updated externally? - autocmd BufReadPost,BufWritePost * call easytags#autoload() - endif - if g:easytags_on_cursorhold - autocmd CursorHold,CursorHoldI * call easytags#autoload() - autocmd BufReadPost * unlet! b:easytags_last_highlighted - endif -augroup END - -" }}}1 - -" Make sure the plug-in is only loaded once. -let g:loaded_easytags = 1 - -" vim: ts=2 sw=2 et diff --git a/plugin/easytags.vim b/plugin/easytags.vim new file mode 100644 index 0000000..5aa4293 --- /dev/null +++ b/plugin/easytags.vim @@ -0,0 +1,175 @@ +" Vim plug-in +" Author: Peter Odding +" Last Change: March 15, 2011 +" URL: http://peterodding.com/code/vim/easytags/ +" Requires: Exuberant Ctags (http://ctags.sf.net) +" License: MIT +" Version: 2.2 + +" Support for automatic update using the GLVS plug-in. +" GetLatestVimScripts: 3114 1 :AutoInstall: easytags.zip + +" Don't source the plug-in when its already been loaded or &compatible is set. +if &cp || exists('g:loaded_easytags') + finish +endif + +let s:script = expand(':p:~') + +" Configuration defaults and initialization. {{{1 + +if !exists('g:easytags_file') + if has('win32') || has('win64') + let g:easytags_file = '~\_vimtags' + else + let g:easytags_file = '~/.vimtags' + endif +endif + +if !exists('g:easytags_resolve_links') + let g:easytags_resolve_links = 0 +endif + +if !exists('g:easytags_always_enabled') + let g:easytags_always_enabled = 0 +endif + +if !exists('g:easytags_on_cursorhold') + let g:easytags_on_cursorhold = 1 +endif + +if !exists('g:easytags_ignored_filetypes') + let g:easytags_ignored_filetypes = '^tex$' +endif + +if !exists('g:easytags_autorecurse') + let g:easytags_autorecurse = 0 +endif + +if !exists('g:easytags_include_members') + let g:easytags_include_members = 0 +endif + +function! s:InitEasyTags(version) + " Check that the location of Exuberant Ctags has been configured or that the + " correct version of the program exists in one of its default locations. + if exists('g:easytags_cmd') && s:CheckCtags(g:easytags_cmd, a:version) + return 1 + endif + " On Ubuntu Linux, Exuberant Ctags is installed as `ctags'. On Debian Linux, + " Exuberant Ctags is installed as `exuberant-ctags'. On Free-BSD, Exuberant + " Ctags is installed as `exctags'. + for name in ['ctags', 'exuberant-ctags', 'exctags'] + if s:CheckCtags(name, a:version) + let g:easytags_cmd = name + return 1 + endif + endfor +endfunction + +function! s:CheckCtags(name, version) + " Not every executable out there named `ctags' is in fact Exuberant Ctags. + " This function makes sure it is because the easytags plug-in requires the + " --list-languages option. + if executable(a:name) + let command = a:name . ' --version' + try + let listing = join(xolox#shell#execute(command, 1), '\n') + catch /^Vim\%((\a\+)\)\=:E117/ + " Ignore missing shell.vim plug-in. + let listing = system(command) + catch + " xolox#shell#execute() converts shell errors to exceptions and since + " we're checking whether one of several executables exists we don't want + " to throw an error when the first one doesn't! + return + endtry + let pattern = 'Exuberant Ctags \zs\d\+\(\.\d\+\)*' + let g:easytags_ctags_version = matchstr(listing, pattern) + return s:VersionToNumber(g:easytags_ctags_version) >= a:version + endif +endfunction + +function! s:VersionToNumber(s) + let values = split(a:s, '\.') + if len(values) == 1 + return values[0] * 10 + elseif len(values) >= 2 + return values[0] * 10 + values[1][0] + endif +endfunction + +if !s:InitEasyTags(55) + if !exists('g:easytags_ctags_version') || empty(g:easytags_ctags_version) + let s:msg = "%s: Plug-in not loaded because Exuberant Ctags isn't installed!" + if executable('apt-get') + let s:msg .= " On Ubuntu & Debian you can install Exuberant Ctags by" + let s:msg .= " installing the package named `exuberant-ctags':" + let s:msg .= " sudo apt-get install exuberant-ctags" + else + let s:msg .= " Please download & install Exuberant Ctags from http://ctags.sf.net" + endif + echomsg printf(s:msg, s:script) + else + let s:msg = "%s: Plug-in not loaded because Exuberant Ctags 5.5" + let s:msg .= " or newer is required while you have version %s installed!" + echomsg printf(s:msg, s:script, g:easytags_ctags_version) + endif + unlet s:msg + finish +endif + +function! s:RegisterTagsFile() + " Parse the &tags option and get a list of all tags files *including + " non-existing files* (this is why we can't just call tagfiles()). + let tagfiles = xolox#misc#option#split_tags(&tags) + let expanded = map(copy(tagfiles), 'resolve(expand(v:val))') + " Add the filename to the &tags option when the user hasn't done so already. + if index(expanded, resolve(expand(g:easytags_file))) == -1 + " This is a real mess because of bugs in Vim?! :let &tags = '...' doesn't + " work on UNIX and Windows, :set tags=... doesn't work on Windows. What I + " mean with "doesn't work" is that tagfiles() == [] after the :let/:set + " command even though the tags file exists! One easy way to confirm that + " this is a bug in Vim is to type :set tags= then press followed by + " . Now you entered the exact same value that the code below also did + " but suddenly Vim sees the tags file and tagfiles() != [] :-S + call insert(tagfiles, g:easytags_file) + let value = xolox#misc#option#join_tags(tagfiles) + let cmd = ':set tags=' . escape(value, '\ ') + if has('win32') || has('win64') + " TODO How to clear the expression from Vim's status line? + call feedkeys(":" . cmd . "|let &ro=&ro\", 'n') + else + execute cmd + endif + endif +endfunction + +" Let Vim know about the global tags file created by this plug-in. +call s:RegisterTagsFile() + +" The :UpdateTags and :HighlightTags commands. {{{1 + +command! -bar -bang -nargs=* -complete=file UpdateTags call xolox#easytags#update(0, == '!', []) +command! -bar HighlightTags call xolox#easytags#highlight() + +" Automatic commands. {{{1 + +augroup PluginEasyTags + autocmd! + if g:easytags_always_enabled + " TODO Also on FocusGained because tags files might be updated externally? + autocmd BufReadPost,BufWritePost * call xolox#easytags#autoload() + endif + if g:easytags_on_cursorhold + autocmd CursorHold,CursorHoldI * call xolox#easytags#autoload() + autocmd BufReadPost * unlet! b:easytags_last_highlighted + endif +augroup END + +" }}}1 + +" Make sure the plug-in is only loaded once. +let g:loaded_easytags = 1 + +" vim: ts=2 sw=2 et -- cgit v1.2.3