" Author: Kalimuthu Velappan " Version: 1.2 " Last Modified: June 04, 2012 " Email: kalmuthu@gmail.com " Desription: Gtag support for multiwindow " Copyright and licence " --------------------- " Copyright (c) 2012 Innovace Technology Solutions " " This program is free software: you can redistribute it and/or modify " it under the terms of the GNU General Public License as published by " the Free Software Foundation, either version 3 of the License, or " (at your option) any later version. " " This program is distributed in the hope that it will be useful, " but WITHOUT ANY WARRANTY; without even the implied warranty of " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " GNU General Public License for more details. " " You should have received a copy of the GNU General Public License " along with this program. If not, see . " " Overview " -------- " The gtags.vim plug-in script integrates the GNU GLOBAL source code tag system " with Vim. About the details, see http://www.gnu.org/software/global/. " " Installation " ------------ " Drop the file in your plug-in directory or source it from your vimrc. " To use this script, you need the GNU GLOBAL-5.7 or later installed " in your machine. " Usage " ----- " " 1. To go to symbol(variable/function) definition, place cursor cursor under the symbol " and press Ctl + \, you would get the similar to following: " " ============= TAG : [FILE_NAME_LENGTH] ================ " main/system/header.h 70 #define FILE_NAME_LENGTH 32 " main/control/views.h 17 #define FILE_NAME_LENGTH 24 " main/program/forms.h 23 #define FILE_NAME_LENGTH 12 " ... " Cursor is place under the list, Press key to select/navigate the tag " " 2. To go to reference of the symbol(variable/function) definition, place cursor cursor " under the symbol and press Ctl + R, you would get the similar to following: " " ============= TAG : [FILE_NAME_LENGTH] ================ " main/system/header.c 70 int len = FILE_NAME_LENGTH; " main/control/views.c 17 size = FILE_NAME_LENGTH+1; " main/program/forms.c 23 int max = FILE_NAME_LENGTH + 12; " ... " Cursor is place under the list, Press key to select/navigate the tag " " 3. To come back to previous tag/window, press Ctl + D. " " 4. Similarly, to get the variable symbol definition, place cursor cursor under the symbol " and press Ctl + S. " " 5. To browse the Tag Stack, press Ctl + T key, you would get similar to the following. " " ============================== TAG : [StackTag]======================== " main/system/process.c | 5 | FILE_NAME_LENGTH | FILE " 1_1_FILE_NAME_LENGTH_1 | 2 | FILE_NAME_LENGTH | TAG " main/system/header.c | 95 | FILE_NAME_LENGTH | FILE " " " If you dont want the multiwindow support, You can use the suggested key mapping with the following code: " " [$HOME/.vimrc] " let No_Gtags_Multi_Window_Auto_Map = 1 " if exists("loaded_gtags_multi_window") finish endif if !exists("g:Gtags_Result_Format") let g:Gtags_Result_Format = "ctags-mod" endif " Character to use to quote patterns and file names before passing to global. " (This code was drived from 'grep.vim'.) if !exists("g:Gtags_Shell_Quote_Char") if has("win32") || has("win16") || has("win95") let g:Gtags_Shell_Quote_Char = '"' else let g:Gtags_Shell_Quote_Char = "'" endif endif if !exists("g:Gtags_Single_Quote_Char") if has("win32") || has("win16") || has("win95") let g:Gtags_Single_Quote_Char = "'" let g:Gtags_Double_Quote_Char = '\"' else let s:sq = "'" let s:dq = '"' let g:Gtags_Single_Quote_Char = s:sq . s:dq . s:sq . s:dq . s:sq let g:Gtags_Double_Quote_Char = '"' endif endif " " Display error message. " function! s:Error(msg) echohl WarningMsg | \ echomsg 'Error: ' . a:msg | \ echohl None endfunction " " Trim options to avoid errors. " function! s:TrimOption(option) let l:option = '' let l:length = strlen(a:option) let l:i = 0 while l:i < l:length let l:c = a:option[l:i] if l:c !~ '[cenpquv]' let l:option = l:option . l:c endif let l:i = l:i + 1 endwhile return l:option endfunction " " Execute global and load the result into quickfix window. " function! s:ExecLoad(option, long_option, pattern) " Execute global(1) command and write the result to a temporary file. let l:isfile = 0 let l:option = '' let l:result = '' if a:option =~ 'f' let l:isfile = 1 if filereadable(a:pattern) == 0 call s:Error('File ' . a:pattern . ' not found.') return endif endif if a:long_option != '' let l:option = a:long_option . ' ' endif let l:option = l:option . '--result=' . g:Gtags_Result_Format let l:option = l:option . ' -q'. s:TrimOption(a:option) if l:isfile == 1 let l:cmd = 'global ' . l:option . ' ' . g:Gtags_Shell_Quote_Char . a:pattern . g:Gtags_Shell_Quote_Char else let l:cmd = 'global ' . l:option . 'e ' . g:Gtags_Shell_Quote_Char . a:pattern . g:Gtags_Shell_Quote_Char endif "echoerr "CMD = ".l:cmd let l:result = system(l:cmd) if v:shell_error != 0 if v:shell_error != 0 if v:shell_error == 2 call s:Error('invalid arguments. (gtags.vim requires GLOBAL 5.7 or later)') elseif v:shell_error == 3 call s:Error('GTAGS not found.') else call s:Error('global command failed. command line: ' . l:cmd) endif endif return endif if l:result == '' if l:option =~ 'f' call s:Error('Tag not found in ' . a:pattern . '.') elseif l:option =~ 'P' call s:Error('Path which matches to ' . a:pattern . ' not found.') elseif l:option =~ 'g' call s:Error('Line which matches to ' . a:pattern . ' not found.') else call s:Error('Tag which matches to ' . g:Gtags_Shell_Quote_Char . a:pattern . g:Gtags_Shell_Quote_Char . ' not found.') endif return endif call OpenTag(a:pattern, l:result) endfunction " ---------------------------- MULTIWINDOW GTAG ---------------------------------------- function! s:GtagsCursor_x() let l:pattern = expand("") call s:ExecLoad('x', ' ', l:pattern) endfunction function! s:GtagsCursor_r() let l:pattern = expand("") call s:ExecLoad('r', ' ', l:pattern) endfunction function! s:GtagsCursor_s() let l:pattern = expand("") call s:ExecLoad('s', ' ', l:pattern) endfunction " shortcut Keys " The key maps are assigned based on the easier and closer to the finger. " [$HOME/.vimrc] " let No_Gtags_Multi_Window_Auto_Map = 1 " if !exists("No_Gtags_Multi_Window_Auto_Map") let No_Gtags_Multi_Window_Auto_Map = 0 endif if g:No_Gtags_Multi_Window_Auto_Map == 0 :noremap :call GtagsCursor_x() :noremap :call GtagsCursor_r() :noremap :call GtagsCursor_s() :noremap :call SelectWindowStackTag() :noremap :call CloseTag() :noremap :call DisplayWindowStack() endif let s:Tag={"TagName":"NULL", "TagFileName":"NULL","CurPos":0, "TagType":"NULL" } let s:Stack=[] let s:TagStack={"TagStack":0, "TagIndex":0, "CurrentTag":0} let s:Window=[] silent highlight GtagsGroup ctermbg=DarkGrey guibg=DarkGrey if !exists("loaded_gtags_multi_window") call add(s:Window, copy(s:TagStack)) endif " Get the window number associated with it function! GetWindowNumber() while len(s:Window) <= winnr("$") call AddWindow() endwhile return winnr() endfunction " Debug functions function! DisplaySetup() call add(s:Stack, copy(s:Tag)) let s:TagStack.TagStack=copy(s:Stack) call add(s:Window, copy(s:TagStack) ) let s:Window[0].TagStack[0].TagName="TEST" echo "Tag name=". s:Window[0].TagStack[0].TagName endfunction function! GetTagFileName(tag_name) let win_idx = GetWindowNumber() let tag_idx = s:Window[win_idx].TagIndex return GetWindowNumber() ."_". winbufnr(0) ."_". a:tag_name."_".tag_idx endfunction " Open new Buffer function! OpenBuffer(tag_name, file_name, tag_type) if !bufexists(a:file_name) execute("badd ". a:file_name) endif endfunction " Close the buffer function! CloseBuffer(tag_name, file_name, tag_type) if bufexists(a:file_name) execute("bwipeout ". a:file_name) endif endfunction " Load the buffer for the given filename function! LoadBuffer(tag_name, file_name, line_no, tag_type, file_content) if !bufexists(a:file_name) call OpenBuffer(a:tag_name, a:file_name, a:tag_type) endif execute("buffer ". a:file_name) if a:tag_type == "TAG" setlocal modifiable silent put=a:file_content call setline(1,"=========================================TAG : [".a:tag_name."]=========================================") setlocal nomodifiable setlocal buftype=nowrite buflisted setlocal noswapfile setlocal cursorline silent noremap :call SelectTag() silent noremap else call system("echo \"history -s \"v ".a:file_name. "\"\" >> /tmp/vfiles.txt") endif call cursor(a:line_no,1) call search(a:tag_name, '', line(".")) call SetStackTopTag(a:tag_name, a:file_name, a:tag_type) call SelectMatch("GtagsGroup", a:tag_name) endfunction function! SelectMatch(group, tag_name) call clearmatches() if GetStackTopTagIndex() > 0 call matchadd(a:group, a:tag_name) endif endfunction " Select the buffer based on the Tag function! SelectBuffer(tag) if !bufexists(a:tag.TagFileName) call SetStackTopTag(a:tag.TagName, a:tag.TagFileName, a:tag.TagType) return CloseTag() endif execute("buffer ". a:tag.TagFileName) call setpos(".", a:tag.CurPos) call SelectMatch("GtagsGroup", a:tag.TagName) call SetStackTopTag(a:tag.TagName, a:tag.TagFileName, a:tag.TagType) endfunction " Copy the Current tag details to Stack top function! CopyCurrentToStackTop() let tag_type = GetStackTopTagType() if tag_type != "FILE" return endif if GetStackTopTagIndex() <= 0 return endif let tag = GetStackTopTag() let current_pos = getpos(".") if line(".") == tag.CurPos[1] return endif call PushCurrentTag(tag) endfunction " Open the new tag function! OpenTag(tag_name, content ) call CopyCurrentToStackTop() call PushTag(a:tag_name, bufname("%")) let tag_file_name = GetTagFileName(a:tag_name) call LoadBuffer(a:tag_name, tag_file_name, 2, "TAG", a:content ) call AutoSelectTag() endfunction " Close the tag function! CloseTag() let win_idx = GetWindowNumber() let tag_idx = s:Window[win_idx].TagIndex-1 if tag_idx < 0 echohl WarningMsg | echo "Bottom of Stack" | echohl None return endif let ctag=GetStackTopTag() let tag=PopTag() call SelectBuffer(tag) if ctag.TagType == "TAG" call CloseBuffer( ctag.TagName, ctag.TagFileName, ctag.TagType) endif call AutoCloseTag() endfunction " Automatically close the tag window when only one entry in the tag window function! AutoCloseTag() let tag=GetStackTopTag() if tag.TagType != "TAG" return endif if line(".") <= 1 return endif if line("$") > 2 return endif call CloseTag() endfunction " Select the Tag from Tag window function! SelectTag() if line(".") <= 1 return endif let tag = GetStackTopTag() let words = split(getline("."), '\t') let file_name = substitute( words[0], " ", "", "g") let line_no = words[1] let line_cnt = words[2] call PushTag(tag.TagName, bufname("%")) call LoadBuffer(tag.TagName, file_name, line_no, "FILE", "") endfunction " Automatically select the tag from tag window when there is only one entry function! AutoSelectTag() if line(".") <= 1 return endif if line("$") > 2 return endif call SelectTag() endfunction " Update the top stack contents function! PushCurrentTag(ctag) let win_idx = GetWindowNumber() let tag_idx = s:Window[win_idx].TagIndex let tag = copy(s:Tag) let tag.TagName = a:ctag.TagName let tag.TagFileName = a:ctag.TagFileName let tag.CurPos = copy(a:ctag.CurPos) let tag.TagType = a:ctag.TagType call add(s:Window[win_idx].TagStack, tag ) let s:Window[win_idx].TagIndex = tag_idx+1 endfunction " Push the tag to stack function! PushTag(tag_name, tag_filename) let win_idx = GetWindowNumber() let tag_idx = s:Window[win_idx].TagIndex let tag_type = GetStackTopTagType() let tag = copy(s:Tag) let tag.TagName = a:tag_name let tag.TagFileName = a:tag_filename let tag.CurPos = getpos(".") let tag.TagType = tag_type call add(s:Window[win_idx].TagStack, tag ) let s:Window[win_idx].TagIndex = tag_idx+1 endfunction " Pop the tag from stack function! PopTag() let win_idx = GetWindowNumber() let tag_idx = s:Window[win_idx].TagIndex-1 if tag_idx < 0 echo "Bottom of Stack" return 0 endif let tag = copy(s:Window[win_idx].TagStack[tag_idx]) call remove(s:Window[win_idx].TagStack, tag_idx) let s:Window[win_idx].TagIndex = tag_idx return tag endfunction " Get the top of the stack index function! GetStackTopTagIndex() let win_idx = GetWindowNumber() let tag_idx = s:Window[win_idx].TagIndex return tag_idx endfunction " Get the top of the stack tag function! GetStackTopTag() let win_idx = GetWindowNumber() let tag = copy(s:Window[win_idx].CurrentTag) return tag endfunction " Set the current top tag contents function! SetStackTopTag(tag_name, tag_filename, tag_type) let win_idx = GetWindowNumber() let s:Window[win_idx].CurrentTag.TagName = a:tag_name let s:Window[win_idx].CurrentTag.TagFileName = a:tag_filename let s:Window[win_idx].CurrentTag.CurPos = getpos(".") let s:Window[win_idx].CurrentTag.TagType = a:tag_type endfunction " Get the tag type either FILE or TAG function! GetStackTopTagType() let win_idx = GetWindowNumber() let tag_type = s:Window[win_idx].CurrentTag.TagType if tag_type == "NULL" let tag_type = "FILE" endif return tag_type endfunction " Debug functions function! DisplayStackTopTag(index) let tag = copy(s:Window[a:index].CurrentTag) call DisplayTag(tag) endfunction " Debug functions function! DisplayTag(tag) echo printf(" %-30.30s | %-9s | %-5s | %s ", a:tag.TagName, a:tag.TagType, a:tag.CurPos[1], a:tag.TagFileName ) endfunction function! GetTagContent(tag) return printf("%-50s\t| %-9s\t| %-5s\t| %s\n", a:tag.TagFileName, a:tag.CurPos[1], a:tag.TagName, a:tag.TagType ) endfunction " Init the stack function! OpenStack(index) let s:Window[a:index].TagStack = copy(s:Stack) let s:Window[a:index].CurrentTag = copy(s:Tag) let s:Window[a:index].TagIndex = 0 endfunction " Uninit stack function! CloseStack(index) if len(s:Window[a:index].TagStack) > 0 call remove(s:Window[a:index].TagStack, 0, len(s:Window[a:index].TagStack)-1) endif let s:Window[a:index].TagStack = 0 let s:Window[a:index].TagIndex = 0 endfunction " Display the tag stack function! DisplayStack(index) for i in range(len(s:Window[a:index].TagStack)-1, 0, -1) call DisplayTag( s:Window[a:index].TagStack[i] ) endfor endfunction " Get the top of the stack function! GetStackContent(index) let stack_content="" for i in range(len(s:Window[a:index].TagStack)-1, 0, -1) let stack_content .= GetTagContent( s:Window[a:index].TagStack[i] ) endfor return stack_content endfunction " Get the window index for tag function! OpenWindow(index) if a:index > len(s:Window) echoerr "Open: Out of index = ".a:index. ",len=". len(s:Window) return endif call insert(s:Window, copy(s:TagStack), a:index ) call OpenStack(a:index) endfunction " Uninitialize the window function! CloseWindow(index) if a:index > len(s:Window) || a:index < 1 echoerr "Close: Out of index = ".a:index. ",len=". len(s:Window) return endif call CloseStack(a:index) call remove(s:Window, a:index ) endfunction " Initialize the new window function! AddWindow() call add(s:Window, copy(s:TagStack)) call OpenStack(len(s:Window)-1) endfunction " Window debug function function! DisplayWindow() for i in range(1, len(s:Window)-1) echo "Window[".i."] TagIndex = ".s:Window[i].TagIndex endfor endfunction " Debug function to display the tag stack function! DisplayWindowStack() for i in range(1, len(s:Window)-1) echo "Window[".i."] TagIndex = ".s:Window[i].TagIndex echo "-------------------------------------Tag Stack------------------------------------" call DisplayStackTopTag(i) call DisplayStack(i) endfor endfunction " Select the tag from window tag stack function! SelectWindowStackTag() let win_idx = GetWindowNumber() let stack_content = GetStackContent(win_idx) if stack_content == "" echohl WarningMsg | echo "Stack is Empty" | echohl None return endif call OpenTag("StackTag", stack_content) endfunction " Setup the buffer for window let s:loadmsg="" let s:Event={"LastEvent":"NULL", "LastWinCount":0, "LastWinNo":1 } function! EnterBufWindow() if s:Event.LastWinCount > winnr("$") call CloseWindow(s:Event.LastWinNo) endif if s:Event.LastWinCount == 0 call OpenWindow(s:Event.LastWinNo) endif let s:Event.LastWinCount = winnr("$") let s:Event.LastWinNo = winnr() endfunction " Setup the window for tag function! EnterWindow() if s:Event.LastWinCount > winnr("$") call CloseWindow(s:Event.LastWinNo) endif let s:Event.LastWinCount = winnr("$") let s:Event.LastWinNo = winnr() endfunction " clearup while leaving from window function! LeaveWindow() if s:Event.LastWinCount < winnr("$") call OpenWindow(s:Event.LastWinNo) endif if s:Event.LastWinCount > winnr("$") call CloseWindow(s:Event.LastWinNo) endif let s:Event.LastEvent = "Leave" let s:Event.LastWinCount = winnr("$") let s:Event.LastWinNo = winnr() endfunction "window debug message function! LoadMsg() echo "Num of win =". s:loadmsg echo "Len = ".len(s:Window) endfunction autocmd BufWinEnter * :call EnterBufWindow() autocmd WinEnter * :call EnterWindow() autocmd WinLeave * :call LeaveWindow() let loaded_gtags_multi_window = 1