Mercurial > vim-gutentags
comparison plugin/autotags.vim @ 0:a3a37124558b
Initial commit.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Thu, 17 Jul 2014 17:26:48 -0700 |
parents | |
children | 60adce96ac2d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:a3a37124558b |
---|---|
1 " autotags.vim - Automatic ctags management for Vim | |
2 " Maintainer: Ludovic Chabant <http://ludovic.chabant.com> | |
3 " Version: 0.0.1 | |
4 | |
5 " Globals {{{ | |
6 | |
7 if !exists('g:autotags_debug') | |
8 let g:autotags_debug = 0 | |
9 endif | |
10 | |
11 if (exists('g:loaded_autotags') || &cp) && !g:autotags_debug | |
12 finish | |
13 endif | |
14 if (exists('g:loaded_autotags') && g:autotags_debug) | |
15 echom "Reloaded autotags." | |
16 endif | |
17 let g:loaded_autotags = 1 | |
18 | |
19 if !exists('g:autotags_trace') | |
20 let g:autotags_trace = 1 | |
21 endif | |
22 | |
23 if !exists('g:autotags_fake') | |
24 let g:autotags_fake = 0 | |
25 endif | |
26 | |
27 if !exists('g:autotags_background_update') | |
28 let g:autotags_background_update = 1 | |
29 endif | |
30 | |
31 if !exists('g:autotags_enabled') | |
32 let g:autotags_enabled = 1 | |
33 endif | |
34 | |
35 if !exists('g:autotags_executable') | |
36 let g:autotags_executable = 'ctags' | |
37 endif | |
38 | |
39 if !exists('g:autotags_tagfile') | |
40 let g:autotags_tagfile = 'tags' | |
41 endif | |
42 | |
43 if !exists('g:autotags_project_root') | |
44 let g:autotags_project_root = [] | |
45 endif | |
46 let g:autotags_project_root += ['.git', '.hg', '.bzr', '_darcs'] | |
47 | |
48 " }}} | |
49 | |
50 " Utilities {{{ | |
51 | |
52 " Throw an exception message. | |
53 function! s:throw(message) | |
54 let v:errmsg = "autotags: " . a:message | |
55 throw v:errmsg | |
56 endfunction | |
57 | |
58 " Prints a message if debug tracing is enabled. | |
59 function! s:trace(message, ...) | |
60 if g:autotags_trace || (a:0 && a:1) | |
61 let l:message = "autotags: " . a:message | |
62 echom l:message | |
63 endif | |
64 endfunction | |
65 | |
66 " Strips the ending slash in a path. | |
67 function! s:stripslash(path) | |
68 return fnamemodify(a:path, ':s?[/\\]$??') | |
69 endfunction | |
70 | |
71 " Normalizes the slashes in a path. | |
72 function! s:normalizepath(path) | |
73 if exists('+shellslash') && &shellslash | |
74 return substitute(a:path, '\v/', '\\', 'g') | |
75 elseif has('win32') | |
76 return substitute(a:path, '\v/', '\\', 'g') | |
77 else | |
78 return a:path | |
79 endif | |
80 endfunction | |
81 | |
82 " Shell-slashes the path (opposite of `normalizepath`). | |
83 function! s:shellslash(path) | |
84 if exists('+shellslash') && !&shellslash | |
85 return substitute(a:path, '\v\\', '/', 'g') | |
86 else | |
87 return a:path | |
88 endif | |
89 endfunction | |
90 | |
91 " }}} | |
92 | |
93 " Autotags Setup {{{ | |
94 | |
95 " Finds the tag file path for the given current directory | |
96 " (typically the directory of the file being edited) | |
97 function! s:get_tagfile_for(path) abort | |
98 let l:path = s:stripslash(a:path) | |
99 let l:previous_path = "" | |
100 while l:path != l:previous_path | |
101 for root in g:autotags_project_root | |
102 if getftype(l:path . '/' . root) != "" | |
103 return simplify(fnamemodify(l:path, ':p') . g:autotags_tagfile) | |
104 endif | |
105 endfor | |
106 let l:previous_path = l:path | |
107 let l:path = fnamemodify(l:path, ':h') | |
108 endwhile | |
109 call s:throw("Can't figure out what tag file to use for: " . a:path) | |
110 endfunction | |
111 | |
112 " Setup autotags for the current buffer. | |
113 function! s:setup_autotags() abort | |
114 call s:trace("Scanning buffer '" . bufname('%') . "' for autotags setup...") | |
115 if exists('b:autotags_file') | |
116 return | |
117 endif | |
118 try | |
119 let b:autotags_file = s:get_tagfile_for(expand('%:h')) | |
120 catch /^autotags\:/ | |
121 return | |
122 endtry | |
123 | |
124 call s:trace("Setting autotags for buffer '" . bufname('%') . "' with tagfile: " . b:autotags_file) | |
125 | |
126 let l:bn = bufnr('%') | |
127 execute 'augroup autotags_buffer_' . l:bn | |
128 execute ' autocmd!' | |
129 execute ' autocmd BufWritePost <buffer=' . l:bn . '> if g:autotags_enabled|call s:update_tags(0, 1)|endif' | |
130 execute 'augroup end' | |
131 | |
132 command! -buffer -bang AutotagsUpdate :call s:manual_update_tags(<bang>0) | |
133 endfunction | |
134 | |
135 augroup autotags_detect | |
136 autocmd! | |
137 autocmd BufNewFile,BufReadPost * call s:setup_autotags() | |
138 autocmd VimEnter * if expand('<amatch>')==''|call s:setup_autotags()|endif | |
139 augroup end | |
140 | |
141 " }}} | |
142 | |
143 " Tags File Management {{{ | |
144 | |
145 let s:runner_exe = expand('<sfile>:h:h') . '/plat/unix/update_tags.sh' | |
146 if has('win32') | |
147 let s:runner_exe = expand('<sfile>:h:h') . '\plat\win32\update_tags.cmd' | |
148 endif | |
149 | |
150 let s:update_queue = [] | |
151 | |
152 " Get how to execute an external command depending on debug settings. | |
153 function! s:get_execute_cmd() abort | |
154 if has('win32') | |
155 let l:cmd = '!start ' | |
156 if g:autotags_background_update | |
157 let l:cmd .= '/b ' | |
158 endif | |
159 return l:cmd | |
160 else | |
161 return '!' | |
162 endif | |
163 endfunction | |
164 | |
165 " (Re)Generate the tags file for the current buffer's file. | |
166 function! s:manual_update_tags(bang) abort | |
167 call s:update_tags(a:bang, 0) | |
168 endfunction | |
169 | |
170 " Update the tags file for the current buffer's file. | |
171 " write_mode: | |
172 " 0: update the tags file if it exists, generate it otherwise. | |
173 " 1: always generate (overwrite) the tags file. | |
174 " | |
175 " queue_mode: | |
176 " 0: if an update is already in progress, report it and abort. | |
177 " 1: if an update is already in progress, queue another one. | |
178 " | |
179 " An additional argument specifies where to write the tags file. If nothing | |
180 " is specified, it will go to the autotags-defined file. | |
181 function! s:update_tags(write_mode, queue_mode, ...) abort | |
182 " Figure out where to save. | |
183 let l:tags_file = 0 | |
184 if a:0 == 1 | |
185 let l:tags_file = a:1 | |
186 else | |
187 let l:tags_file = b:autotags_file | |
188 endif | |
189 | |
190 " Check that there's not already an update in progress. | |
191 let l:lock_file = l:tags_file . '.lock' | |
192 if filereadable(l:lock_file) | |
193 if a:queue_mode == 1 | |
194 let l:idx = index(s:update_queue, l:tags_file) | |
195 if l:idx < 0 | |
196 call add(s:update_queue, l:tags_file) | |
197 endif | |
198 call s:trace("Tag file '" . l:tags_file . "' is already being updated. Queuing it up...") | |
199 call s:trace("") | |
200 else | |
201 echom "autotags: The tags file is already being updated, please try again later." | |
202 echom "" | |
203 endif | |
204 return | |
205 endif | |
206 | |
207 " Switch to the project root to make the command line smaller, and make | |
208 " it possible to get the relative path of the filename to parse if we're | |
209 " doing an incremental update. | |
210 let l:prev_cwd = getcwd() | |
211 let l:work_dir = fnamemodify(l:tags_file, ':h') | |
212 execute "chdir " . l:work_dir | |
213 | |
214 try | |
215 " Build the command line. | |
216 let l:cmd = s:get_execute_cmd() . s:runner_exe | |
217 let l:cmd .= ' --exe "' . g:autotags_executable . '"' | |
218 let l:cmd .= ' --tags "' . fnamemodify(l:tags_file, ':t') . '"' | |
219 if a:write_mode == 0 && filereadable(l:tags_file) | |
220 " CTags specifies paths relative to the tags file with a `./` | |
221 " prefix, so we need to specify the same prefix otherwise it will | |
222 " think those are different files and we'll end up with duplicate | |
223 " entries. | |
224 let l:rel_path = s:normalizepath('./' . expand('%:.')) | |
225 let l:cmd .= ' --source "' . l:rel_path . '"' | |
226 endif | |
227 if g:autotags_trace | |
228 let l:cmd .= ' --log "' . fnamemodify(l:tags_file, ':t') . '.log"' | |
229 endif | |
230 call s:trace("Running: " . l:cmd) | |
231 call s:trace("In: " . l:work_dir) | |
232 if !g:autotags_fake | |
233 if !g:autotags_trace | |
234 silent execute l:cmd | |
235 else | |
236 execute l:cmd | |
237 endif | |
238 else | |
239 call s:trace("(fake... not actually running)") | |
240 endif | |
241 call s:trace("") | |
242 finally | |
243 " Restore the current directory... | |
244 execute "chdir " . l:prev_cwd | |
245 endtry | |
246 endfunction | |
247 | |
248 " }}} | |
249 | |
250 " Manual Tagfile Generation {{{ | |
251 | |
252 function! s:generate_tags(bang, ...) abort | |
253 call s:update_tags(1, 0, a:1) | |
254 endfunction | |
255 | |
256 command! -bang -nargs=1 -complete=file AutotagsGenerate :call s:generate_tags(<bang>0, <f-args>) | |
257 | |
258 " }}} | |
259 | |
260 " Toggles {{{ | |
261 | |
262 command! AutotagsToggleEnabled :let g:autotags_enabled=!g:autotags_enabled | |
263 command! AutotagsToggleTrace :call autotags#trace() | |
264 command! AutotagsToggleFake :call autotags#fake() | |
265 command! AutotagsUnlock :call delete(b:autotags_file . '.lock') | |
266 | |
267 " }}} | |
268 | |
269 " Autoload Functions {{{ | |
270 | |
271 function! autotags#rescan(...) | |
272 if exists('b:autotags_file') | |
273 unlet b:autotags_file | |
274 endif | |
275 if a:0 && a:1 | |
276 let l:trace_backup = g:autotags_trace | |
277 let l:autotags_trace = 1 | |
278 endif | |
279 call s:setup_autotags() | |
280 if a:0 && a:1 | |
281 let g:autotags_trace = l:trace_backup | |
282 endif | |
283 endfunction | |
284 | |
285 function! autotags#trace(...) | |
286 let g:autotags_trace = !g:autotags_trace | |
287 if a:0 > 0 | |
288 let g:autotags_trace = a:1 | |
289 endif | |
290 if g:autotags_trace | |
291 echom "autotags: Tracing is enabled." | |
292 else | |
293 echom "autotags: Tracing is disabled." | |
294 endif | |
295 echom "" | |
296 endfunction | |
297 | |
298 function! autotags#fake(...) | |
299 let g:autotags_fake = !g:autotags_fake | |
300 if a:0 > 0 | |
301 let g:autotags_fake = a:1 | |
302 endif | |
303 if g:autotags_fake | |
304 echom "autotags: Now faking autotags." | |
305 else | |
306 echom "autotags: Now running autotags for real." | |
307 endif | |
308 echom "" | |
309 endfunction | |
310 | |
311 " }}} | |
312 |