Mercurial > vim-gutentags
view autoload/gutentags/ctags.vim @ 160:1b980f5071a0
Post-processing for `tags` files, extra args for `ctags`.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Fri, 17 Feb 2017 17:21:57 -0800 |
parents | bba76de4371a |
children | cbc1ebe23ef1 |
line wrap: on
line source
" Ctags module for Gutentags " Global Options {{{ let g:gutentags_ctags_executable = get(g:, 'gutentags_ctags_executable', 'ctags') let g:gutentags_tagfile = get(g:, 'gutentags_tagfile', 'tags') let g:gutentags_auto_set_tags = get(g:, 'gutentags_auto_set_tags', 1) let g:gutentags_ctags_options_file = get(g:, 'gutentags_ctags_options_file', '.gutctags') let g:gutentags_ctags_check_tagfile = get(g:, 'gutentags_ctags_check_tagfile', 0) let g:gutentags_ctags_extra_args = get(g:, 'gutentags_ctags_extra_args', []) let g:gutentags_ctags_post_process_cmd = get(g:, 'gutentags_ctags_post_process_cmd', '') " }}} " Gutentags Module Interface {{{ let s:runner_exe = gutentags#get_plat_file('update_tags') let s:unix_redir = (&shellredir =~# '%s') ? &shellredir : &shellredir . ' %s' function! gutentags#ctags#init(project_root) abort " Figure out the path to the tags file. let l:tagfile = getbufvar("", 'gutentags_tagfile', g:gutentags_tagfile) let b:gutentags_files['ctags'] = gutentags#get_cachefile( \a:project_root, l:tagfile) " Set the tags file for Vim to use. if g:gutentags_auto_set_tags execute 'setlocal tags^=' . fnameescape(b:gutentags_files['ctags']) endif " Check if the ctags executable exists. if g:gutentags_enabled && executable(expand(g:gutentags_ctags_executable, 1)) == 0 let g:gutentags_enabled = 0 echoerr "Executable '".g:gutentags_ctags_executable."' can't be found. " \."Gutentags will be disabled. You can re-enable it by " \."setting g:gutentags_enabled back to 1." endif endfunction function! gutentags#ctags#generate(proj_dir, tags_file, write_mode) abort " Get to the tags file directory because ctags is finicky about " these things. let l:prev_cwd = getcwd() execute "chdir " . fnameescape(a:proj_dir) let l:tags_file_exists = filereadable(a:tags_file) if l:tags_file_exists && g:gutentags_ctags_check_tagfile let l:first_lines = readfile(a:tags_file, '', 1) if len(l:first_lines) == 0 || stridx(l:first_lines[0], '!_TAG_') != 0 call gutentags#throwerr( \"File ".a:tags_file." doesn't appear to be ". \"a ctags file. Please delete it and run ". \":GutentagsUpdate!.") return endif endif if empty(g:gutentags_cache_dir) " If we don't use the cache directory, let's just use the tag filename " as specified by the user, since it's relative to the project root, " and we are already `chdir`'d into it. " Note that if we don't do this and pass a full path, `ctags` gets " confused if the paths have spaces -- but not if you're *in* the " root directory. let l:actual_proj_dir = '.' let l:actual_tags_file = g:gutentags_tagfile else " else: the tags file goes in a cache directory, so we need to specify " all the paths absolutely for `ctags` to do its job correctly. let l:actual_proj_dir = a:proj_dir let l:actual_tags_file = a:tags_file endif try " Build the command line. let l:cmd = gutentags#get_execute_cmd() . s:runner_exe let l:cmd .= ' -e "' . s:get_ctags_executable(a:proj_dir) . '"' let l:cmd .= ' -t "' . l:actual_tags_file . '"' let l:cmd .= ' -p "' . l:actual_proj_dir . '"' if a:write_mode == 0 && l:tags_file_exists let l:cur_file_path = expand('%:p') if empty(g:gutentags_cache_dir) let l:cur_file_path = fnamemodify(l:cur_file_path, ':.') endif let l:cmd .= ' -s "' . l:cur_file_path . '"' else let l:file_list_cmd = gutentags#get_project_file_list_cmd(l:actual_proj_dir) if !empty(l:file_list_cmd) let l:suffopts = matchstrpos(l:file_list_cmd, '///') if l:suffopts[1] > 0 let l:suffoptstr = strpart(l:file_list_cmd, l:suffopts[2]) let l:file_list_cmd = strpart(l:file_list_cmd, 0, l:suffopts[1]) if l:suffoptstr == 'absolute' let l:cmd .= ' -A' endif endif let l:cmd .= ' -L ' . '"' . l:file_list_cmd. '"' endif endif if empty(get(l:, 'file_list_cmd', '')) " Pass the Gutentags recursive options file before the project " options file, so that users can override --recursive. " Omit --recursive if this project uses a file list command. let l:cmd .= ' -o "' . gutentags#get_res_file('ctags_recursive.options') . '"' endif if !empty(g:gutentags_ctags_extra_args) let l:cmd .= ' -O '.shellescape(join(g:gutentags_ctags_extra_args)) endif if !empty(g:gutentags_ctags_post_process_cmd) let l:cmd .= ' -P '.shellescape(g:gutentags_ctags_post_process_cmd) endif let l:proj_options_file = a:proj_dir . '/' . \g:gutentags_ctags_options_file if filereadable(l:proj_options_file) let l:proj_options_file = s:process_options_file( \a:proj_dir, l:proj_options_file) let l:cmd .= ' -o "' . l:proj_options_file . '"' endif for ign in split(&wildignore, ',') let l:cmd .= ' -x ' . '"' . ign . '"' endfor for exc in g:gutentags_exclude let l:cmd .= ' -x ' . '"' . exc . '"' endfor if g:gutentags_pause_after_update let l:cmd .= ' -c' endif if g:gutentags_trace if has('win32') let l:cmd .= ' -l "' . l:actual_tags_file . '.log"' else let l:cmd .= ' ' . printf(s:unix_redir, '"' . l:actual_tags_file . '.log"') endif else if !has('win32') let l:cmd .= ' ' . printf(s:unix_redir, '/dev/null') endif endif let l:cmd .= gutentags#get_execute_cmd_suffix() call gutentags#trace("Running: " . l:cmd) call gutentags#trace("In: " . getcwd()) if !g:gutentags_fake " Run the background process. if !g:gutentags_trace silent execute l:cmd else execute l:cmd endif " Flag this tags file as being in progress let l:full_tags_file = fnamemodify(a:tags_file, ':p') call gutentags#add_progress('ctags', l:full_tags_file) else call gutentags#trace("(fake... not actually running)") endif call gutentags#trace("") finally " Restore the previous working directory. execute "chdir " . fnameescape(l:prev_cwd) endtry endfunction " }}} " Utilities {{{ " Get final ctags executable depending whether a filetype one is defined function! s:get_ctags_executable(proj_dir) abort "Only consider the main filetype in cases like 'python.django' let l:ftype = get(split(&filetype, '\.'), 0, '') let l:proj_info = gutentags#get_project_info(a:proj_dir) let l:type = get(l:proj_info, 'type', l:ftype) let exepath = exists('g:gutentags_ctags_executable_{l:type}') \ ? g:gutentags_ctags_executable_{l:type} : g:gutentags_ctags_executable return expand(exepath, 1) endfunction function! s:process_options_file(proj_dir, path) abort if empty(g:gutentags_cache_dir) " If we're not using a cache directory to store tag files, we can " use the options file straight away. return a:path endif " See if we need to process the options file. let l:do_process = 0 let l:proj_dir = gutentags#stripslash(a:proj_dir) let l:out_path = gutentags#get_cachefile(l:proj_dir, 'options') if !filereadable(l:out_path) call gutentags#trace("Processing options file '".a:path."' because ". \"it hasn't been processed yet.") let l:do_process = 1 elseif getftime(a:path) > getftime(l:out_path) call gutentags#trace("Processing options file '".a:path."' because ". \"it has changed.") let l:do_process = 1 endif if l:do_process == 0 " Nothing's changed, return the existing processed version of the " options file. return l:out_path endif " We have to process the options file. Right now this only means capturing " all the 'exclude' rules, and rewrite them to make them absolute. " " This is because since `ctags` is run with absolute paths (because we " want the tag file to be in a cache directory), it will do its path " matching with absolute paths too, so the exclude rules need to be " absolute. let l:lines = readfile(a:path) let l:outlines = [] for line in l:lines let l:exarg_idx = matchend(line, '\v^\-\-exclude=') if l:exarg_idx < 0 call add(l:outlines, line) continue endif " Don't convert things that don't look like paths. let l:exarg = strpart(line, l:exarg_idx + 1) let l:do_convert = 1 if l:exarg[0] == '@' " Manifest file path let l:do_convert = 0 endif if stridx(l:exarg, '/') < 0 && stridx(l:exarg, '\\') < 0 " Filename let l:do_convert = 0 endif if l:do_convert == 0 call add(l:outlines, line) continue endif let l:fullp = l:proj_dir . gutentags#normalizepath('/'.l:exarg) let l:ol = '--exclude='.l:fullp call add(l:outlines, l:ol) endfor call writefile(l:outlines, l:out_path) return l:out_path endfunction " }}}