1" File: gtags.vim
2" Author: Tama Communications Corporation
3" Version: 0.6.2
4" Last Modified: 2012 Oct 23
5"
6" Copyright and licence
7" ---------------------
8" Copyright (c) 2004, 2008, 2010, 2011 Tama Communications Corporation
9"
10" This file is part of GNU GLOBAL.
11"
12" This program is free software: you can redistribute it and/or modify
13" it under the terms of the GNU General Public License as published by
14" the Free Software Foundation, either version 3 of the License, or
15" (at your option) any later version.
16"
17" This program is distributed in the hope that it will be useful,
18" but WITHOUT ANY WARRANTY; without even the implied warranty of
19" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20" GNU General Public License for more details.
21"
22" You should have received a copy of the GNU General Public License
23" along with this program.  If not, see <http://www.gnu.org/licenses/>.
24"
25" Overview
26" --------
27" The gtags.vim plug-in script integrates the GNU GLOBAL source code tag system
28" with Vim. About the details, see http://www.gnu.org/software/global/.
29"
30" Installation
31" ------------
32" Drop the file in your plug-in directory or source it from your vimrc.
33" To use this script, you need the GNU GLOBAL-6.0 or later installed
34" in your machine.
35"
36" Usage
37" -----
38" First of all, you must execute gtags(1) at the root of source directory
39" to make tag files. Assuming that your source directory is '/var/src',
40" it is necessary to execute the following commands.
41"
42"	$ cd /var/src
43"	$ gtags
44"
45" And you will find four tag files in the directory.
46"
47" General form of Gtags command is as follows:
48"
49"	:Gtags [option] pattern
50"
51" To go to func, you can say
52"
53"       :Gtags func
54"
55" Input completion is available. If you forgot function name but recall
56" only some characters of the head, please input them and press <TAB> key.
57"
58"       :Gtags fu<TAB>
59"       :Gtags func			<- Vim will append 'nc'.
60"
61" If you omitted argument, vim ask it like this:
62"
63"       Gtags for pattern: <current token>
64"
65" Vim execute `global -x main', parse the output, list located
66" objects in quickfix window and load the first entry.  The quickfix
67" windows is like this:
68"
69"      gozilla/gozilla.c|200| main(int argc, char **argv)
70"      gtags-cscope/gtags-cscope.c|124| main(int argc, char **argv)
71"      gtags-parser/asm_scan.c|2056| int main()
72"      gtags-parser/gctags.c|157| main(int argc, char **argv)
73"      gtags-parser/php.c|2116| int main()
74"      gtags/gtags.c|152| main(int argc, char **argv)
75"      [Quickfix List]
76"
77" You can go to any entry using quickfix command.
78"
79" :cn'
80"      go to the next entry.
81"
82" :cp'
83"      go to the previous entry.
84"
85" :ccN'
86"      go to the Nth entry.
87"
88" :cl'
89"      list all entries.
90"
91" You can see the help of quickfix like this:
92"
93"          :h quickfix
94"
95" You can use POSIX regular expression too. It requires more execution time though.
96"
97"       :Gtags ^[sg]et_
98"
99" It will match to both of 'set_value' and 'get_value'.
100"
101" To go to the referenced point of func, add -r option.
102"
103"       :Gtags -r func
104"
105" To go to any symbols which are not defined in GTAGS, try this.
106"
107"       :Gtags -s func
108"
109" To go to any string other than symbol, try this.
110"
111"       :Gtags -g ^[sg]et_
112"
113" This command accomplishes the same function as grep(1) but is more convenient
114" because it retrieves the entire directory structure.
115"
116" To get list of objects in a file 'main.c', use -f command.
117"
118"       :Gtags -f main.c
119"
120" If you are editing `main.c' itself, you can use '%' instead.
121"
122"       :Gtags -f %
123"
124" You can browse project files whose path includes specified pattern.
125" For example:
126"
127"       :Gtags -P /vm/			<- all files under 'vm' directory.
128"       :Gtags -P \.h$			<- all include files.
129"	:Gtags -P init			<- all paths includes 'init'
130"
131" If you omitted the argument and input only <ENTER> key to the prompt,
132" vim shows list of all files in your project.
133"
134" You can use all options of global(1) except for the -c, -p, -u and
135" all long name options. They are sent to global(1) as is.
136" For example, if you want to ignore case distinctions in pattern.
137"
138"       :Gtags -gi paTtern
139"
140" It will match to both of 'PATTERN' and 'pattern'.
141"
142" If you want to search a pattern which starts with a hyphen like '-C'
143" then you can use the -e option like grep(1).
144"
145"	:Gtags -ge -C
146"
147" By default, Gtags command search only in source files. If you want to
148" search in both source files and text files, or only in text files then
149"
150"	:Gtags -go pattern		# both source and text
151"	:Gtags -gO pattern		# only text file
152"
153" See global(1) for other options.
154"
155" The GtagsCursor command brings you to the definition or reference of
156" the current token.
157"
158"       :GtagsCursor
159"
160" If you have the hypertext generated by htags(1) then you can display
161" the same place on mozilla browser. Let's load mozilla and try this:
162"
163"       :Gozilla
164"
165" If you want to load vim with all main()s then following command line is useful.
166"
167"	% vim '+Gtags main'
168"
169" Also see the chapter of 'vim editor' of the on-line manual of GLOBAL.
170"
171"	% info global
172"
173" You can use the suggested key mapping with the following code:
174"
175"	[$HOME/.vimrc]
176"	let Gtags_Auto_Map = 1
177"
178if exists("loaded_gtags")
179    finish
180endif
181
182"
183" global command name
184"
185let s:global_command = $GTAGSGLOBAL
186if s:global_command == ''
187        let s:global_command = "global"
188endif
189" Open the Gtags output window.  Set this variable to zero, to not open
190" the Gtags output window by default.  You can open it manually by using
191" the :cwindow command.
192" (This code was drived from 'grep.vim'.)
193if !exists("g:Gtags_OpenQuickfixWindow")
194    let g:Gtags_OpenQuickfixWindow = 1
195endif
196
197if !exists("g:Gtags_VerticalWindow")
198    let g:Gtags_VerticalWindow = 0
199endif
200
201if !exists("g:Gtags_Auto_Map")
202    let Gtags_Auto_Map = 0
203endif
204
205if !exists("Gtags_Auto_Update")
206    let Gtags_Auto_Update = 0
207endif
208
209" -- ctags-x format
210" let Gtags_Result = "ctags-x"
211" let Gtags_Efm = "%*\\S%*\\s%l%\\s%f%\\s%m"
212"
213" -- ctags format
214" let Gtags_Result = "ctags"
215" let Gtags_Efm = "%m\t%f\t%l"
216"
217" Gtags_Use_Tags_Format is obsoleted.
218if exists("g:Gtags_Use_Tags_Format")
219    let g:Gtags_Result = "ctags"
220    let g:Gtags_Efm = "%m\t%f\t%l"
221endif
222if !exists("g:Gtags_Result")
223    let g:Gtags_Result = "ctags-mod"
224endif
225if !exists("g:Gtags_Efm")
226    let g:Gtags_Efm = "%f\t%l\t%m"
227endif
228" Character to use to quote patterns and file names before passing to global.
229" (This code was drived from 'grep.vim'.)
230if !exists("g:Gtags_Shell_Quote_Char")
231    if has("win32") || has("win16") || has("win95")
232        let g:Gtags_Shell_Quote_Char = '"'
233    else
234        let g:Gtags_Shell_Quote_Char = "'"
235    endif
236endif
237if !exists("g:Gtags_Single_Quote_Char")
238    if has("win32") || has("win16") || has("win95")
239        let g:Gtags_Single_Quote_Char = "'"
240        let g:Gtags_Double_Quote_Char = '\"'
241    else
242        let s:sq = "'"
243        let s:dq = '"'
244        let g:Gtags_Single_Quote_Char = s:sq . s:dq . s:sq . s:dq . s:sq
245        let g:Gtags_Double_Quote_Char = '"'
246    endif
247endif
248
249"
250" Display error message.
251"
252function! s:Error(msg)
253    echohl WarningMsg |
254           \ echomsg 'Error: ' . a:msg |
255           \ echohl None
256endfunction
257"
258" Extract pattern or option string.
259"
260function! s:Extract(line, target)
261    let l:option = ''
262    let l:pattern = ''
263    let l:force_pattern = 0
264    let l:length = strlen(a:line)
265    let l:i = 0
266
267    " skip command name.
268    if a:line =~ '^Gtags'
269        let l:i = 5
270    endif
271    while l:i < l:length && a:line[l:i] == ' '
272       let l:i = l:i + 1
273    endwhile
274    while l:i < l:length
275        if a:line[l:i] == "-" && l:force_pattern == 0
276            let l:i = l:i + 1
277            " Ignore long name option like --help.
278            if l:i < l:length && a:line[l:i] == '-'
279                while l:i < l:length && a:line[l:i] != ' '
280                   let l:i = l:i + 1
281                endwhile
282            else
283                while l:i < l:length && a:line[l:i] != ' '
284                    let l:c = a:line[l:i]
285                    let l:option = l:option . l:c
286                    let l:i = l:i + 1
287                endwhile
288                if l:c == 'e'
289                    let l:force_pattern = 1
290                endif
291            endif
292        else
293            let l:pattern = ''
294            " allow pattern includes blanks.
295            while l:i < l:length
296                 if a:line[l:i] == "'"
297                     let l:pattern = l:pattern . g:Gtags_Single_Quote_Char
298                 elseif a:line[l:i] == '"'
299                     let l:pattern = l:pattern . g:Gtags_Double_Quote_Char
300                 else
301                     let l:pattern = l:pattern . a:line[l:i]
302                 endif
303                let l:i = l:i + 1
304            endwhile
305            if a:target == 'pattern'
306                return l:pattern
307            endif
308        endif
309        " Skip blanks.
310        while l:i < l:length && a:line[l:i] == ' '
311               let l:i = l:i + 1
312        endwhile
313    endwhile
314    if a:target == 'option'
315        return l:option
316    endif
317    return ''
318endfunction
319
320"
321" Trim options to avoid errors.
322"
323function! s:TrimOption(option)
324    let l:option = ''
325    let l:length = strlen(a:option)
326    let l:i = 0
327
328    while l:i < l:length
329        let l:c = a:option[l:i]
330        if l:c !~ '[cenpquv]'
331            let l:option = l:option . l:c
332        endif
333        let l:i = l:i + 1
334    endwhile
335    return l:option
336endfunction
337
338"
339" Execute global and load the result into quickfix window.
340"
341function! s:ExecLoad(option, long_option, pattern)
342    " Execute global(1) command and write the result to a temporary file.
343    let l:isfile = 0
344    let l:option = ''
345    let l:result = ''
346
347    if a:option =~ 'f'
348        let l:isfile = 1
349        if filereadable(a:pattern) == 0
350            call s:Error('File ' . a:pattern . ' not found.')
351            return
352        endif
353    endif
354    if a:long_option != ''
355        let l:option = a:long_option . ' '
356    endif
357    let l:option = l:option . '--result=' . g:Gtags_Result . ' -q'
358    let l:option = l:option . s:TrimOption(a:option)
359    if l:isfile == 1
360        let l:cmd = s:global_command . ' ' . l:option . ' ' . g:Gtags_Shell_Quote_Char . a:pattern . g:Gtags_Shell_Quote_Char
361    else
362        let l:cmd = s:global_command . ' ' . l:option . 'e ' . g:Gtags_Shell_Quote_Char . a:pattern . g:Gtags_Shell_Quote_Char
363    endif
364
365    let l:result = system(l:cmd)
366    if v:shell_error != 0
367        if v:shell_error != 0
368            if v:shell_error == 2
369                call s:Error('invalid arguments. please use the latest GLOBAL.')
370            elseif v:shell_error == 3
371                call s:Error('GTAGS not found.')
372            else
373                call s:Error('global command failed. command line: ' . l:cmd)
374            endif
375        endif
376        return
377    endif
378    if l:result == ''
379        if l:option =~ 'f'
380            call s:Error('Tag not found in ' . a:pattern . '.')
381        elseif l:option =~ 'P'
382            call s:Error('Path which matches to ' . a:pattern . ' not found.')
383        elseif l:option =~ 'g'
384            call s:Error('Line which matches to ' . a:pattern . ' not found.')
385        else
386            call s:Error('Tag which matches to ' . g:Gtags_Shell_Quote_Char . a:pattern . g:Gtags_Shell_Quote_Char . ' not found.')
387        endif
388        return
389    endif
390
391    " Open the quickfix window
392    if g:Gtags_OpenQuickfixWindow == 1
393        if g:Gtags_VerticalWindow == 1
394            topleft vertical copen
395        else
396            botright copen
397        endif
398    endif
399    " Parse the output of 'global -x or -t' and show in the quickfix window.
400    let l:efm_org = &efm
401    let &efm = g:Gtags_Efm
402    cexpr! l:result
403    let &efm = l:efm_org
404endfunction
405
406"
407" RunGlobal()
408"
409function! s:RunGlobal(line)
410    let l:pattern = s:Extract(a:line, 'pattern')
411
412    if l:pattern == '%'
413        let l:pattern = expand('%')
414    elseif l:pattern == '#'
415        let l:pattern = expand('#')
416    endif
417    let l:option = s:Extract(a:line, 'option')
418    " If no pattern supplied then get it from user.
419    if l:pattern == ''
420        let s:option = l:option
421        if l:option =~ 'f'
422            let l:line = input("Gtags for file: ", expand('%'), 'file')
423        else
424            let l:line = input("Gtags for pattern: ", expand('<cword>'), 'custom,GtagsCandidateCore')
425        endif
426        let l:pattern = s:Extract(l:line, 'pattern')
427        if l:pattern == ''
428            call s:Error('Pattern not specified.')
429            return
430        endif
431    endif
432    call s:ExecLoad(l:option, '', l:pattern)
433endfunction
434
435"
436" Execute RunGlobal() depending on the current position.
437"
438function! s:GtagsCursor()
439    let l:pattern = expand("<cword>")
440    let l:option = "--from-here=\"" . line('.') . ":" . expand("%") . "\""
441    call s:ExecLoad('', l:option, l:pattern)
442endfunction
443
444"
445" Show the current position on mozilla.
446" (You need to execute htags(1) in your source directory.)
447"
448function! s:Gozilla()
449    let l:lineno = line('.')
450    let l:filename = expand("%")
451    let l:result = system('gozilla +' . l:lineno . ' ' . l:filename)
452endfunction
453"
454" Auto update of tag files using incremental update facility.
455"
456function! s:GtagsAutoUpdate()
457    let l:result = system(s:global_command . " -u --single-update=\"" . expand("%") . "\"")
458endfunction
459
460"
461" Custom completion.
462"
463function! GtagsCandidate(lead, line, pos)
464    let s:option = s:Extract(a:line, 'option')
465    return GtagsCandidateCore(a:lead, a:line, a:pos)
466endfunction
467
468function! GtagsCandidateCore(lead, line, pos)
469    if s:option == 'g'
470        return ''
471    elseif s:option == 'f'
472        if isdirectory(a:lead)
473            if a:lead =~ '/$'
474                let l:pattern = a:lead . '*'
475            else
476                let l:pattern = a:lead . '/*'
477            endif
478        else
479            let l:pattern = a:lead . '*'
480        endif
481        return glob(l:pattern)
482    else
483        return system(s:global_command . ' ' . '-c' . s:option . ' ' . a:lead)
484    endif
485endfunction
486
487" Define the set of Gtags commands
488command! -nargs=* -complete=custom,GtagsCandidate Gtags call s:RunGlobal(<q-args>)
489command! -nargs=0 GtagsCursor call s:GtagsCursor()
490command! -nargs=0 Gozilla call s:Gozilla()
491command! -nargs=0 GtagsUpdate call s:GtagsAutoUpdate()
492if g:Gtags_Auto_Update == 1
493	:autocmd! BufWritePost * call s:GtagsAutoUpdate()
494endif
495" Suggested map:
496if g:Gtags_Auto_Map == 1
497	:nmap <F2> :copen<CR>
498	:nmap <F4> :cclose<CR>
499	:nmap <F5> :Gtags<SPACE>
500	:nmap <F6> :Gtags -f %<CR>
501	:nmap <F7> :GtagsCursor<CR>
502	:nmap <F8> :Gozilla<CR>
503	:nmap <C-n> :cn<CR>
504	:nmap <C-p> :cp<CR>
505	:nmap <C-\><C-]> :GtagsCursor<CR>
506endif
507let loaded_gtags = 1
508