My dotfiles. Period.

ctags.vim 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. " Ctags module for Gutentags
  2. " Global Options {{{
  3. let g:gutentags_ctags_executable = get(g:, 'gutentags_ctags_executable', 'ctags')
  4. let g:gutentags_ctags_tagfile = get(g:, 'gutentags_ctags_tagfile', 'tags')
  5. let g:gutentags_ctags_auto_set_tags = get(g:, 'gutentags_ctags_auto_set_tags', 1)
  6. let g:gutentags_ctags_options_file = get(g:, 'gutentags_ctags_options_file', '.gutctags')
  7. let g:gutentags_ctags_check_tagfile = get(g:, 'gutentags_ctags_check_tagfile', 0)
  8. let g:gutentags_ctags_extra_args = get(g:, 'gutentags_ctags_extra_args', [])
  9. let g:gutentags_ctags_post_process_cmd = get(g:, 'gutentags_ctags_post_process_cmd', '')
  10. let g:gutentags_ctags_exclude = get(g:, 'gutentags_ctags_exclude', [])
  11. let g:gutentags_ctags_exclude_wildignore = get(g:, 'gutentags_ctags_exclude_wildignore', 1)
  12. " Backwards compatibility.
  13. function! s:_handleOldOptions() abort
  14. let l:renamed_options = {
  15. \'gutentags_exclude': 'gutentags_ctags_exclude',
  16. \'gutentags_tagfile': 'gutentags_ctags_tagfile',
  17. \'gutentags_auto_set_tags': 'gutentags_ctags_auto_set_tags'
  18. \}
  19. for key in keys(l:renamed_options)
  20. if exists('g:'.key)
  21. let newname = l:renamed_options[key]
  22. echom "gutentags: Option 'g:'".key." has been renamed to ".
  23. \"'g:'".newname." Please update your vimrc."
  24. let g:[newname] = g:[key]
  25. endif
  26. endfor
  27. endfunction
  28. call s:_handleOldOptions()
  29. " }}}
  30. " Gutentags Module Interface {{{
  31. let s:did_check_exe = 0
  32. let s:runner_exe = gutentags#get_plat_file('update_tags')
  33. let s:unix_redir = (&shellredir =~# '%s') ? &shellredir : &shellredir . ' %s'
  34. let s:wildignores_options_path = ''
  35. let s:last_wildignores = ''
  36. function! gutentags#ctags#init(project_root) abort
  37. " Figure out the path to the tags file.
  38. " Check the old name for this option, too, before falling back to the
  39. " globally defined name.
  40. let l:tagfile = getbufvar("", 'gutentags_ctags_tagfile',
  41. \getbufvar("", 'gutentags_tagfile',
  42. \g:gutentags_ctags_tagfile))
  43. let b:gutentags_files['ctags'] = gutentags#get_cachefile(
  44. \a:project_root, l:tagfile)
  45. " Set the tags file for Vim to use.
  46. if g:gutentags_ctags_auto_set_tags
  47. execute 'setlocal tags^=' . fnameescape(b:gutentags_files['ctags'])
  48. endif
  49. " Check if the ctags executable exists.
  50. if s:did_check_exe == 0
  51. if g:gutentags_enabled && executable(expand(g:gutentags_ctags_executable, 1)) == 0
  52. let g:gutentags_enabled = 0
  53. echoerr "Executable '".g:gutentags_ctags_executable."' can't be found. "
  54. \."Gutentags will be disabled. You can re-enable it by "
  55. \."setting g:gutentags_enabled back to 1."
  56. endif
  57. let s:did_check_exe = 1
  58. endif
  59. endfunction
  60. function! gutentags#ctags#generate(proj_dir, tags_file, gen_opts) abort
  61. let l:write_mode = a:gen_opts['write_mode']
  62. let l:tags_file_exists = filereadable(a:tags_file)
  63. let l:tags_file_relative = fnamemodify(a:tags_file, ':.')
  64. let l:tags_file_is_local = len(l:tags_file_relative) < len(a:tags_file)
  65. if l:tags_file_exists && g:gutentags_ctags_check_tagfile
  66. let l:first_lines = readfile(a:tags_file, '', 1)
  67. if len(l:first_lines) == 0 || stridx(l:first_lines[0], '!_TAG_') != 0
  68. call gutentags#throw(
  69. \"File ".a:tags_file." doesn't appear to be ".
  70. \"a ctags file. Please delete it and run ".
  71. \":GutentagsUpdate!.")
  72. return
  73. endif
  74. endif
  75. if empty(g:gutentags_cache_dir) && l:tags_file_is_local
  76. " If we don't use the cache directory, we can pass relative paths
  77. " around.
  78. "
  79. " Note that if we don't do this and pass a full path for the project
  80. " root, some `ctags` implementations like Exhuberant Ctags can get
  81. " confused if the paths have spaces -- but not if you're *in* the root
  82. " directory, for some reason... (which we are, our caller in
  83. " `autoload/gutentags.vim` changed it).
  84. let l:actual_proj_dir = '.'
  85. let l:actual_tags_file = l:tags_file_relative
  86. else
  87. " else: the tags file goes in a cache directory, so we need to specify
  88. " all the paths absolutely for `ctags` to do its job correctly.
  89. let l:actual_proj_dir = a:proj_dir
  90. let l:actual_tags_file = a:tags_file
  91. endif
  92. " Build the command line.
  93. let l:cmd = [s:runner_exe]
  94. let l:cmd += ['-e', '"' . s:get_ctags_executable(a:proj_dir) . '"']
  95. let l:cmd += ['-t', '"' . l:actual_tags_file . '"']
  96. let l:cmd += ['-p', '"' . l:actual_proj_dir . '"']
  97. if l:write_mode == 0 && l:tags_file_exists
  98. let l:cur_file_path = expand('%:p')
  99. if empty(g:gutentags_cache_dir) && l:tags_file_is_local
  100. let l:cur_file_path = fnamemodify(l:cur_file_path, ':.')
  101. endif
  102. let l:cmd += ['-s', '"' . l:cur_file_path . '"']
  103. else
  104. let l:file_list_cmd = gutentags#get_project_file_list_cmd(l:actual_proj_dir)
  105. if !empty(l:file_list_cmd)
  106. if match(l:file_list_cmd, '///') > 0
  107. let l:suffopts = split(l:file_list_cmd, '///')
  108. let l:suffoptstr = l:suffopts[1]
  109. let l:file_list_cmd = l:suffopts[0]
  110. if l:suffoptstr == 'absolute'
  111. let l:cmd += ['-A']
  112. endif
  113. endif
  114. let l:cmd += ['-L', '"' . l:file_list_cmd. '"']
  115. endif
  116. endif
  117. if empty(get(l:, 'file_list_cmd', ''))
  118. " Pass the Gutentags recursive options file before the project
  119. " options file, so that users can override --recursive.
  120. " Omit --recursive if this project uses a file list command.
  121. let l:cmd += ['-o', '"' . gutentags#get_res_file('ctags_recursive.options') . '"']
  122. endif
  123. if !empty(g:gutentags_ctags_extra_args)
  124. let l:cmd += ['-O', shellescape(join(g:gutentags_ctags_extra_args))]
  125. endif
  126. if !empty(g:gutentags_ctags_post_process_cmd)
  127. let l:cmd += ['-P', shellescape(g:gutentags_ctags_post_process_cmd)]
  128. endif
  129. let l:proj_options_file = a:proj_dir . '/' .
  130. \g:gutentags_ctags_options_file
  131. if filereadable(l:proj_options_file)
  132. let l:proj_options_file = s:process_options_file(
  133. \a:proj_dir, l:proj_options_file)
  134. let l:cmd += ['-o', '"' . l:proj_options_file . '"']
  135. endif
  136. if g:gutentags_ctags_exclude_wildignore
  137. call s:generate_wildignore_options()
  138. if !empty(s:wildignores_options_path)
  139. let l:cmd += ['-x', shellescape('@'.s:wildignores_options_path, 1)]
  140. endif
  141. endif
  142. for exc in g:gutentags_ctags_exclude
  143. let l:cmd += ['-x', '"' . exc . '"']
  144. endfor
  145. if g:gutentags_pause_after_update
  146. let l:cmd += ['-c']
  147. endif
  148. if g:gutentags_trace
  149. let l:cmd += ['-l', '"' . l:actual_tags_file . '.log"']
  150. endif
  151. let l:cmd = gutentags#make_args(l:cmd)
  152. call gutentags#trace("Running: " . string(l:cmd))
  153. call gutentags#trace("In: " . getcwd())
  154. if !g:gutentags_fake
  155. let l:job_opts = gutentags#build_default_job_options('ctags')
  156. let l:job = gutentags#start_job(l:cmd, l:job_opts)
  157. call gutentags#add_job('ctags', a:tags_file, l:job)
  158. else
  159. call gutentags#trace("(fake... not actually running)")
  160. endif
  161. endfunction
  162. function! gutentags#ctags#on_job_exit(job, exit_val) abort
  163. call gutentags#remove_job_by_data('ctags', a:job)
  164. if a:exit_val != 0
  165. call gutentags#warning("ctags job failed, returned: ".
  166. \string(a:exit_val))
  167. endif
  168. endfunction
  169. " }}}
  170. " Utilities {{{
  171. " Get final ctags executable depending whether a filetype one is defined
  172. function! s:get_ctags_executable(proj_dir) abort
  173. "Only consider the main filetype in cases like 'python.django'
  174. let l:ftype = get(split(&filetype, '\.'), 0, '')
  175. let l:proj_info = gutentags#get_project_info(a:proj_dir)
  176. let l:type = get(l:proj_info, 'type', l:ftype)
  177. let exepath = exists('g:gutentags_ctags_executable_{l:type}')
  178. \ ? g:gutentags_ctags_executable_{l:type} : g:gutentags_ctags_executable
  179. return expand(exepath, 1)
  180. endfunction
  181. function! s:generate_wildignore_options() abort
  182. if s:last_wildignores == &wildignore
  183. " The 'wildignore' setting didn't change since last time we did this.
  184. call gutentags#trace("Wildignore options file is up to date.")
  185. return
  186. endif
  187. if s:wildignores_options_path == ''
  188. if empty(g:gutentags_cache_dir)
  189. let s:wildignores_options_path = tempname()
  190. else
  191. let s:wildignores_options_path =
  192. \gutentags#stripslash(g:gutentags_cache_dir).
  193. \'/_wildignore.options'
  194. endif
  195. endif
  196. call gutentags#trace("Generating wildignore options: ".s:wildignores_options_path)
  197. let l:opt_lines = []
  198. for ign in split(&wildignore, ',')
  199. call add(l:opt_lines, ign)
  200. endfor
  201. call writefile(l:opt_lines, s:wildignores_options_path)
  202. let s:last_wildignores = &wildignore
  203. endfunction
  204. function! s:process_options_file(proj_dir, path) abort
  205. if empty(g:gutentags_cache_dir)
  206. " If we're not using a cache directory to store tag files, we can
  207. " use the options file straight away.
  208. return a:path
  209. endif
  210. " See if we need to process the options file.
  211. let l:do_process = 0
  212. let l:proj_dir = gutentags#stripslash(a:proj_dir)
  213. let l:out_path = gutentags#get_cachefile(l:proj_dir, 'options')
  214. if !filereadable(l:out_path)
  215. call gutentags#trace("Processing options file '".a:path."' because ".
  216. \"it hasn't been processed yet.")
  217. let l:do_process = 1
  218. elseif getftime(a:path) > getftime(l:out_path)
  219. call gutentags#trace("Processing options file '".a:path."' because ".
  220. \"it has changed.")
  221. let l:do_process = 1
  222. endif
  223. if l:do_process == 0
  224. " Nothing's changed, return the existing processed version of the
  225. " options file.
  226. return l:out_path
  227. endif
  228. " We have to process the options file. Right now this only means capturing
  229. " all the 'exclude' rules, and rewrite them to make them absolute.
  230. "
  231. " This is because since `ctags` is run with absolute paths (because we
  232. " want the tag file to be in a cache directory), it will do its path
  233. " matching with absolute paths too, so the exclude rules need to be
  234. " absolute.
  235. let l:lines = readfile(a:path)
  236. let l:outlines = []
  237. for line in l:lines
  238. let l:exarg_idx = matchend(line, '\v^\-\-exclude=')
  239. if l:exarg_idx < 0
  240. call add(l:outlines, line)
  241. continue
  242. endif
  243. " Don't convert things that don't look like paths.
  244. let l:exarg = strpart(line, l:exarg_idx + 1)
  245. let l:do_convert = 1
  246. if l:exarg[0] == '@' " Manifest file path
  247. let l:do_convert = 0
  248. endif
  249. if stridx(l:exarg, '/') < 0 && stridx(l:exarg, '\\') < 0 " Filename
  250. let l:do_convert = 0
  251. endif
  252. if l:do_convert == 0
  253. call add(l:outlines, line)
  254. continue
  255. endif
  256. let l:fullp = l:proj_dir . gutentags#normalizepath('/'.l:exarg)
  257. let l:ol = '--exclude='.l:fullp
  258. call add(l:outlines, l:ol)
  259. endfor
  260. call writefile(l:outlines, l:out_path)
  261. return l:out_path
  262. endfunction
  263. " }}}