Rainbow
[my-vim-dotfolder.git] / plugin / cctree.vim
blob49e66cc2cf8af11816799eaf25d4ceaf87dee210
1 " C Call-Tree Explorer (CCTree) <CCTree.vim>
4 " Script Info and Documentation 
5 "=============================================================================
6 "    Copyright: Copyright (C) August 2008 - 2010, Hari Rangarajan
7 "               Permission is hereby granted to use and distribute this code,
8 "               with or without modifications, provided that this copyright
9 "               notice is copied with it. Like anything else that's free,
10 "               cctree.vim is provided *as is* and comes with no
11 "               warranty of any kind, either expressed or implied. In no
12 "               event will the copyright holder be liable for any damamges
13 "               resulting from the use of this software.
15 " Name Of File: CCTree.vim
16 "  Description: C Call-Tree Explorer Vim Plugin
17 "   Maintainer: Hari Rangarajan <hari.rangarajan@gmail.com>
18 "          URL: http://vim.sourceforge.net/scripts/script.php?script_id=2368
19 "  Last Change: July 12, 2009
20 "      Version: 0.70
22 "=============================================================================
23
24 "  Description:
25 "       Plugin generates call-trees for any function or macro in real-time inside
26 "  Vim.
28 "  Requirements: 1) Cscope
29 "                2) Vim 7.xx 
31 "                Tested on Unix and the following Win32 versions:
32 "                + Cscope, mlcscope (WIN32)
33 "                       http://www.geocities.com/shankara_c/cscope.html
34 "                       http://www.bell-labs.com/project/wwexptools/packages.html
36 "                
38 "  Installation: 
39 "               Copy this file to ~/.vim/plugins/
40 "               or to /vimfiles/plugins/  (on Win32 platforms) 
41
42 "               It might also be possible to load it as a filetype plugin
43 "               ~/.vim/ftplugin/c/
45 "               Need to set :filetype plugin on 
46 "           
48 "  Usage:
49 "           Build cscope database, for example:
50 "           > cscope -b -i cscope.files
51 "               [Tip: add -c option to build uncompressed databases for faster
52 "               load speeds]
54 "           Load database with command ":CCTreeLoadDB"
55 "           (Please note that it might take a while depending on the 
56 "           database size)
58 "           Append database with command ":CCTreeAppendDB"
59 "           Allows multiple cscope files to be loaded and cross-referenced
60 "           Illustration:
61 "           :CCTreeAppendDB ./cscope.out
62 "           :CCTreeAppendDB ./dir1/cscope.out
63 "           :CCTreeAppendDB ./dir2/cscope.out
65 "           A database name, i.e., my_cscope.out, can be specified with 
66 "           the command. If not provided, a prompt will ask for the 
67 "           filename; default is cscope.out.
69 "           To show loaded databases, use command ":CCTreeShowLoadedDBs"
71 "           To unload all databases, use command ":CCTreeUnLoadDB"
72 "           Note: There is no provision to unload databases individually
74 "           Default Mappings:
75 "             Get reverse call tree for symbol  <C-\><
76 "             Get forward call tree for symbol  <C-\>>
77 "             Increase depth of tree and update <C-\>=
78 "             Decrease depth of tree and update <C-\>-
80 "             Open symbol in other window       <CR>
81 "             Preview symbol in other window    <Ctrl-P>
83 "          Command List:
84 "             CCTreeLoadDB                <dbname>
85 "             CCTreeAppendDB              <dbname>
86 "             CCTreeUnLoadDB             
87 "             CCTreeTraceForward          <symbolname>
88 "             CCTreeTraceReverse          <symbolname>     
89 "             CCTreeRecurseDepthPlus     
90 "             CCTreeRecurseDepthMinus    
94 "          Settings:
95 "               Customize behavior by changing the variable settings
97 "               Cscope database file, g:CCTreeCscopeDb = "cscope.out"
98 "               Maximum call levels,   g:CCTreeRecursiveDepth = 3
99 "               Maximum visible(unfolded) level, g:CCTreeMinVisibleDepth = 3
100 "               Orientation of window,  g:CCTreeOrientation = "topleft"
101 "                (standard vim options for split: [right|left][above|below])
103 "               Use Vertical window, g:CCTreeWindowVertical = 1
104 "                   Min width for window, g:CCTreeWindowMinWidth = 40
105 "                   g:CCTreeWindowWidth = -1, auto-select best width to fit
107 "               Horizontal window, g:CCTreeWindowHeight, default is -1
110 "               Display format, g:CCTreeDisplayMode, default 1
112 "               Values: 1 -- Ultra-compact (takes minimum screen width)
113 "                       2 -- Compact       (Takes little more space)
114 "                       3 -- Wide          (Takes copious amounts of space)
116 "               For vertical splits, 1 and 2 are good, while 3 is good for
117 "               horizontal displays
118 "   
119 "               NOTE: To get older behavior, add the following to your vimrc
120 "               let g:CCTreeDisplayMode = 3
121 "               let g:CCTreeWindowVertical = 0
123 "               Syntax Coloring:
124 "                    CCTreeSymbol is the symbol name
125 "                    CCTreeMarkers include  "|","+--->"
127 "                    CCTreeHiSymbol is the highlighted call tree functions
128 "                    CCTreeHiMarkers is the same as CCTreeMarkers except
129 "                           these denote the highlighted call-tree
132 "                    CCTreeHiXXXX allows dynamic highlighting of the call-tree.
133 "                    To observe the effect, move the cursor to the function to
134 "                    highlight the current call-tree. This option can be
135 "                    turned off using the setting, g:CCTreeHilightCallTree.
136 "                    For faster highlighting, the value of 'updatetime' can be
137 "                    changed
139 "  Limitations:
140 "           The accuracy of the call-tree will only be as good as the cscope 
141 "           database generation.
142 "           NOTE: Different flavors of Cscope have some known
143 "                 limitations due to the lexical analysis engine. This results
144 "                 in incorrectly identified function blocks, etc.
146 "  History:
147 "           Version 0.71: May 11, 2010
148 "                 1. Fix script bug
150 "           Version 0.70: May 8, 2010
151 "                 1. Functionality to load multiple cscope databases
153 "           Version 0.65: July 12, 2009
154 "                 1. Toggle preview window
156 "           Version 0.61: December 24, 2008
157 "                 1. Fixed bug when processing include files
158 "                 2. Remove 'set ruler' option
160 "           Version 0.60: November 26, 2008
161 "                 1. Added support for source-file dependency tree
163 "           Version 0.50: October 17, 2008
164 "                 1. Optimizations for compact memory foot-print and 
165 "                    improved compressed-database load speeds
167 "           Version 0.41: October 6, 2008
168 "                  1. Minor fix: Compressed cscope databases will load
169 "                  incorrectly if encoding is not 8-bit
171 "           Version 0.4: September 28, 2008
172 "                  1. Rewrite of "tree-display" code
173 "                  2. New syntax hightlighting
174 "                  3. Dynamic highlighting for call-trees
175 "                  4. Support for new window modes (vertical, horizontal)  
176 "                  5. New display format option for compact or wide call-trees
177 "                  NOTE: defaults for tree-orientation set to vertical
179 "           Version 0.3:
180 "               September 21, 2008
181 "                 1. Support compressed cscope databases
182 "                 2. Display window related bugs fixed
183 "                 3. More intuitive display and folding capabilities
184 "               
185 "           Version 0.2:
186 "               September 12, 2008
187 "               (Patches from Yegappan Lakshmanan, thanks!)
188 "                 1. Support for using the plugin in Vi-compatible mode.
189 "                 2. Filtering out unwanted lines before processing the db.
190 "                 3. Command-line completion for the commands.
191 "                 4. Using the cscope db from any directory.
193 "           Version 0.1:
194 "                August 31,2008
195 "                 1. Cross-referencing support for only functions and macros
196 "                    Functions inside macro definitions will be incorrectly
197 "                    attributed to the top level calling function
200 "   Thanks:
202 "    Arun Chaganty/Timo Tiefel      (Ver 0.60 -- bug report)
203 "    Michael Wookey                 (Ver 0.4 -- Testing/bug report/patches)
204 "    Yegappan Lakshmanan            (Ver 0.2 -- Patches)
206 "    The Vim Community, ofcourse :)
208 "=============================================================================
210 if !exists('loaded_cctree') && v:version >= 700
211   " First time loading the cctree plugin
212   let loaded_cctree = 1
213 else
214    finish 
215 endif
217 " Line continuation used here
218 let s:cpo_save = &cpoptions
219 set cpoptions&vim
221 " Global variables 
222 " Modify in .vimrc to modify default behavior
223 if !exists('CCTreeCscopeDb')
224     let CCTreeCscopeDb = "cscope.out"
225 endif
226 if !exists('CCTreeRecursiveDepth')
227     let CCTreeRecursiveDepth = 3
228 endif
229 if !exists('CCTreeMinVisibleDepth')
230     let CCTreeMinVisibleDepth = 3
231 endif
232 if !exists('CCTreeOrientation')
233     let CCTreeOrientation = "topleft"
234 endif
235 if !exists('CCTreeWindowVertical')
236     let CCTreeWindowVertical = 1
237 endif
238 if !exists('CCTreeWindowWidth')
239     " -1 is auto select best width
240     let CCTreeWindowWidth = -1
241 endif
242 if !exists('CCTreeWindowMinWidth')
243     let CCTreeWindowMinWidth = 40
244 endif
245 if !exists('CCTreeWindowHeight')
246     let CCTreeWindowHeight = -1
247 endif
248 if !exists('CCTreeDisplayMode')
249     let CCTreeDisplayMode = 1
250 endif
251 if !exists('CCTreeHilightCallTree')
252     let CCTreeHilightCallTree = 1
253 endif
255 " Plugin related local variables
256 let s:pluginname = 'CCTree'
257 let s:windowtitle = 'CCTree-Preview'
259 " There could be duplicate keywords on different lines
260 let s:CCTreekeyword = ''
261 let s:CCTreekeywordLine = -1
263 " Definition of a keyword...
264 let s:CCTreeKeywordRegEx = '[A-Za-z0-9_\\\.\/]\+'
266 " Other state variables
267 let s:currentkeyword = ''
268 let s:currentdirection = ''
269 let s:dbloaded = 0
270 let s:symhashtable = {}
271 let s:save_statusline = ''
272 let s:lastbufname = ''
273 let s:loadedDBs = []
275 " Turn on/off debugs
276 let s:tag_debug=0
278 " Use the Decho plugin for debugging
279 function! DBGecho(...)
280     if s:tag_debug
281         Decho(a:000)
282     endif
283 endfunction
285 function! DBGredir(...)
286     if s:tag_debug
287         Decho(a:000)
288     endif
289 endfunction
292 let s:symlistindex = 0
293 let s:symlisttable = []
295 function! s:CCTreeSymbolListAdd(name)
296     if !has_key(s:symhashtable, a:name)
297         let s:symhashtable[a:name] = s:symlistindex
298         call add(s:symlisttable, s:CCTreeSymbolDictCreate(a:name))
299         let s:symlistindex += 1
300     endif
301     return s:symhashtable[a:name]
302 endfunction
304 function! s:CCTreeSymbolDictCreate(name)
305     let retval = {}
306     
307     let retval['n'] = a:name
308     let retval['c'] = ""
309     let retval['p'] = ""
310     return retval
311 endfunction
313 function! s:CCTreeSymbolMarkXRef(funcentryidx, newfuncidx)
314     let s:symlisttable[a:funcentryidx]['c'] .= (a:newfuncidx. ",")
315     let s:symlisttable[a:newfuncidx]['p'] .= (a:funcentryidx. ",")
316 endfunction
319 function! s:CCTreeInitStatusLine()
320     let s:symlastprogress = 0
321     let s:symprogress = 0
322     let s:cursym = 0
323     let s:currentstatus = ''
324     let s:statusextra = ''
325     
326     let s:save_statusline = &statusline
327     setlocal statusline=%{CCTreeStatusLine()}
328 endfunction
329         
330 function! s:CCTreeRestoreStatusLine()
331     let &statusline = s:save_statusline
332 endfunction
334 function! s:CCTreeBusyStatusLineUpdate(msg)
335     let s:currentstatus = a:msg
336     redrawstatus
337 endfunction
339 function! s:CCTreeBusyStatusLineExtraInfo(msg)
340     let s:statusextra = a:msg
341     redrawstatus
342 endfunction
344 function! CCTreeStatusLine()
345     return s:pluginname. " ". s:currentstatus. " -- ". s:statusextra
346 endfunction
349 let s:progresscurrent = 0
350 let s:progressmax = 0
352 function! s:CCTreeProgressBarInit(maxcount)
353         let s:progressmax = a:maxcount
354         let s:progress1percent = a:maxcount/100
355         let s:progresspercent = 0
356 endfunction
359 function! s:CCTreeProgressBarTick(count)
360         let s:progresscurrent += a:count
361         if s:progress1percent < s:progresscurrent
362             let s:progresscurrent = 0
363             let s:progresspercent += 1
364             call s:CCTreeBusyStatusLineExtraInfo("Processing ". s:progresspercent . 
365                         \ "\%, total ". s:progressmax. " items")
366         endif
367 endfunction
370 function! s:CCTreeWarningMsg(msg)
371     echohl WarningMsg
372     echo a:msg
373     echohl None
374 endfunction
376 function! s:CCTreePreprocessFilter (val)
377     call s:CCTreeProgressBarTick(1)
378     return a:val =~ "^\t[`#$}]|^\k"
379 endfunction
382 function! s:CCTreeLoadDB(db_name)
383         call s:CCTreeLoadDBExt(a:db_name, 1)
384 endfunction
386 function! s:CCTreeAppendDB(db_name)
387         call s:CCTreeLoadDBExt(a:db_name, 0)
388 endfunction
391 function! s:CCTreeLoadDBExt(db_name, clear)
392     let curfuncidx = -1
393     let newfuncidx =  -1
394     let curfileidx = -1
395     let newfileidx =  -1
397     if a:clear == 1
398         call s:CCTreeUnloadDB()
399     endif
401     let cscope_db = a:db_name
402     if cscope_db == ''
403         let cscope_db = input('Cscope database: ', g:CCTreeCscopeDb, 'file')
404         if cscope_db == ''
405             return
406         endif
407     endif
409     if !filereadable(cscope_db)
410         call s:CCTreeWarningMsg('Cscope database ' . cscope_db . ' not found')
411         return
412     endif
413         
414     call add(s:loadedDBs, getcwd().'/'.a:db_name)
416     call s:CCTreeBusyStatusLineUpdate('Loading database')
417     let symbols = readfile(cscope_db)
418     if empty(symbols)
419         call s:CCTreeWarningMsg("Failed to read cscope database")
420         call s:CCTreeRestoreStatusLine()
421         return
422     endif
424     let symindex = 0
425     let symlocalstart = 0
426     let symlocalcount = 0
428     " Grab previous status line
429     call s:CCTreeInitStatusLine()
430     
431     call s:CCTreeBusyStatusLineUpdate('Reading database')
432     " Check if database was built uncompressed
433    if symbols[0] !~ "cscope.*\-c"
434         let s:dbcompressed = 1
435     else
436         let s:dbcompressed = 0
437     endif
438     " Filter-out lines that doesn't have relevant information
439     call filter(symbols, 'v:val =~ "^\t[`#$}@\~]"')
440     call s:CCTreeProgressBarInit(len(symbols))
442     call s:CCTreeBusyStatusLineUpdate('Analyzing database')
443     for a in symbols
444         call s:CCTreeProgressBarTick(1)
445         
446         if a[1] == "`"
447              if curfuncidx != -1
448                  let newfuncidx = s:CCTreeSymbolListAdd(a[2:])
449                  call s:CCTreeSymbolMarkXRef(curfuncidx, newfuncidx)
450              endif
451         elseif a[1] == "$"
452             let curfuncidx = s:CCTreeSymbolListAdd(a[2:])
453         elseif a[1] == "#"
454             call s:CCTreeSymbolListAdd(a[2:])
455         elseif a[1] == "}"
456             let curfuncidx = -1
457         elseif a[1] == "~"
458             let newfileidx = s:CCTreeSymbolListAdd(a[3:])
459             call s:CCTreeSymbolMarkXRef(curfileidx, newfileidx)
460         elseif a[1] == "@"
461             if a[2] != ""
462                 let curfileidx = s:CCTreeSymbolListAdd(a[2:])
463             endif
464         endif
465     endfor
466     
467     if s:dbcompressed == 1
468         call s:CCTreeBusyStatusLineUpdate('Uncompressing database')
469         " inplace uncompression
470         call s:Digraph_Uncompress(s:symlisttable, s:symhashtable)
471     endif
473     call s:CCTreeRestoreStatusLine()
474     let s:dbloaded = 1
475     echomsg "Done building database"
476 endfunction
478 function! s:CCTreeShowLoadedDBs()
479     let i = 1
480     echomsg "CCTree: List of loaded cscope databases"
481     echomsg "---------------------------------------"
482    for aDB in s:loadedDBs
483         echomsg i." ".aDB
484         let i = i + 1
485    endfor
486 endfunction
488 function! s:CCTreeUnloadDB()
489     unlet s:symlisttable
490     unlet s:symhashtable
491     unlet s:loadedDBs
493     let s:dbloaded = 0
494     " Force cleanup
495     call garbagecollect()
497     let s:symlisttable = []
498     let s:symhashtable = {}
500     let s:loadedDBs = []
501 endfunction 
503 function! s:CCTreeGetSymbolXRef(symname, direction)
504     let symentryidx = s:symhashtable[a:symname]
505     let symidslist = split(s:symlisttable[symentryidx][a:direction], ",")
506     let xrefs = {}
508     for asymid in symidslist
509         let xrefs[s:symlisttable[asymid]['n']] = 1
510     endfor
511     return xrefs
512 endfunction
514 function! s:CCTreeGetCallsForSymbol(symname, depth, direction)
515     if (a:depth > g:CCTreeRecursiveDepth) 
516         return {}
517     endif
519     if !has_key(s:symhashtable, a:symname)
520         return {}            
521     endif
523     let calltree_dict = {}
524     let calltree_dict['entry'] = a:symname
526     for entry in keys(s:CCTreeGetSymbolXRef(a:symname, a:direction))
527         if !has_key(calltree_dict, 'childlinks')
528             let calltree_dict['childlinks'] = []
529         endif
530         let tmpDict = 
531                 \s:CCTreeGetCallsForSymbol(entry, a:depth+1, a:direction)
532         call add(calltree_dict['childlinks'], tmpDict)
533     endfor
534     return calltree_dict
535 endfunction
537 func! s:FindOpenBuffer(filename)
538     let bnrHigh = bufnr("$")
539     "tabpagebuflist(tabpagenr())
541     for bufnrs in range(1, bnrHigh)
542         if (bufexists(bufnrs) == 1 && bufname(bufnrs) == a:filename)
543             return bufnrs
544         endif
545     endfor
546     " Could not find the buffer
547     return 0
548 endfunction
550 func! s:FindOpenWindow(filename)
551     let bufnr = s:FindOpenBuffer(a:filename)
552     if (bufnr > 0)
553        let newWinnr = bufwinnr(bufnr)
554        if newWinnr != -1 
555                exec newWinnr.'wincmd w'
556                return 1
557        endif 
558     endif
559     " Could not find the buffer
560     return 0
561 endfunction
563 function! s:CCTreePreviewWindowLeave()
564     call s:FindOpenWindow(s:lastbufname)
565 endfunction
567 function! CCTreePreviewStatusLine()
568     let rtitle= s:windowtitle. ' -- '. s:currentkeyword. 
569             \'[Depth: '. g:CCTreeRecursiveDepth.','
570     
571     if s:currentdirection == 'p' 
572         let rtitle .= "(Reverse)"
573     else
574         let rtitle .= "(Forward)"
575     endif
577     return rtitle.']'
578 endfunction
580 function! s:CCTreePreviewWindowEnter()
581     let s:lastbufname = bufname("%")
582     if s:FindOpenWindow(s:windowtitle) == 0
583         if g:CCTreeWindowVertical == 1
584             exec  g:CCTreeOrientation." vsplit ". s:windowtitle
585             set winfixwidth
586         else
587             exec  g:CCTreeOrientation." split ". s:windowtitle
588             set winfixheight
589         endif
591         setlocal buftype=nofile
592         setlocal bufhidden=hide
593         setlocal noswapfile
594         setlocal nonumber
595         setlocal statusline=%=%{CCTreePreviewStatusLine()}
598         syntax match CCTreePathMark /\s[|+]/ contained
599         syntax match CCTreeArrow  /-*[<>]/ contained
600         syntax match CCTreeSymbol  / [A-Za-z0-9_\.\\\/]\+/  contained
602         syntax region CCTreeSymbolLine start="^\s" end="$" contains=CCTreeArrow,CCTreePathMark,CCTreeSymbol oneline
604         syntax match CCTreeHiArrow  /-*[<>]/ contained
605         syntax match CCTreeHiSymbol  / [A-Za-z0-9_\.\\\/]\+/  contained
606         syntax match CCTreeHiPathMark /\s[|+]/ contained
607         
608         syntax match CCTreeMarkExcl  /^[!#]/ contained
609         syntax match CCTreeMarkTilde /@/  contained
610         syntax region CCTreeUpArrowBlock start="@"  end=/[|+]/  contains=CCTreeMarkTilde contained oneline
612         syntax region CCTreeHiSymbolLine start="!" end="$" contains=CCTreeMarkExcl, 
613                 \ CCTreeUpArrowBlock,
614                 \ CCTreeHiSymbol,CCTreeHiArrow,CCTreeHiPathMark oneline
616         syntax region CCTreeMarkedSymbolLine start="#" end="$" contains=CCTreeMarkExcl,
617                         \ CCTreeMarkTilde,CCTreePathMark,
618                         \ CCTreeArrow,CCTreeSymbol,CCTreeUpArrowBlock oneline
620         let cpo_save = &cpoptions
621         set cpoptions&vim
623         call s:CCTreeBufferKeyMappingsCreate() 
624         nnoremap <buffer> <silent> <C-p>  :CCTreePreviewBufferUsingTag<CR>
625         nnoremap <buffer> <silent> <CR>  :CCTreeLoadBufferUsingTag<CR>
626         nnoremap <buffer> <silent> <2-LeftMouse> :CCTreeLoadBufferUsingTag<CR>
628         let &cpoptions = cpo_save
629     endif
630     setlocal foldmethod=expr
631     setlocal foldexpr=s:CCTreeFoldExpr(getline(v:lnum))
632     setlocal foldtext=CCTreeFoldText()
633     let &l:foldlevel=g:CCTreeMinVisibleDepth
634 endfunction   
637 function! s:CCTreeBuildTreeForLevel(dict, level, treelist, lvllen)
638     if !has_key(a:dict, 'entry')
639         return
640     endif
642     if g:CCTreeDisplayMode == 1 
643        let curlevellen = 1
644     elseif g:CCTreeDisplayMode == 2
645        let curlevellen = a:level + 2
646     elseif g:CCTreeDisplayMode == 3
647        let curlevellen = strlen(a:dict['entry']) + a:level + 2
648     endif    
650     let a:lvllen[a:level] = min([a:lvllen[a:level], curlevellen])
653     call add(a:treelist, [a:dict['entry'], a:level])
654     if has_key(a:dict, 'childlinks')
655         for a in a:dict['childlinks']
656             call s:CCTreeBuildTreeForLevel(a, a:level+1, a:treelist, a:lvllen)
657         endfor
658     endif
659 endfunction
662 let s:calltreemaxdepth = 10
663 function! s:CCTreeBuildTreeDisplayItems(treedict, treesymlist)
664     let treexinfo = repeat([255], s:calltreemaxdepth)
665     call s:CCTreeBuildTreeForLevel(a:treedict, 0, a:treesymlist, treexinfo) 
666     return s:CCTreeBuildDisplayPrependText(treexinfo)
667 endfunction
670 function! s:CCTreeBuildDisplayPrependText(lenlist)
671     let pptxt = "  "
672     let treepptext = repeat([" "], s:calltreemaxdepth)
674    if s:currentdirection == 'p' 
675         let directiontxt = "< "
676     elseif s:currentdirection == 'c'
677         let directiontxt = "> "
678    endif
680    let treepptext[0] = pptxt."+".directiontxt
682     for idx in range(1, s:calltreemaxdepth-1)
683         if a:lenlist[idx] != 255
684             let pptxt .= repeat(" ", a:lenlist[idx-1])
685             let treepptext[idx] = pptxt."+"
687             if g:CCTreeDisplayMode == 1 
688                 let arrows = '-'
689             elseif g:CCTreeDisplayMode >= 2
690                 let arrows = repeat("-", idx)
691             endif
693             let treepptext[idx] = pptxt."+".arrows.directiontxt
694             let pptxt .= "|"
695         endif
696     endfor
697     return treepptext
698 endfunction
700 function! s:CCTreeDisplayTreeList(pptxtlst, treelst)
701     for aentry in a:treelst
702         call setline(".", a:pptxtlst[aentry[1]]. aentry[0])
703         let b:maxwindowlen = max([strlen(getline("."))+1, b:maxwindowlen])
704         exec "normal o"
705     endfor
706 endfunction
709 " Provide dynamic call-tree highlighting using 
710 " syntax highlight tricks 
712 " There are 3 types of lines, marked with the start character [\s, !, #]
713 " Also @ is used to mark the path that is going up
715 function! s:CCTreeMarkCallTree(treelst, keyword)
716     let declevel = -1
718     for idx in range(line("."), 1, -1)
719         " Find our keyword
720         if declevel == -1  
721             if a:treelst[idx-1][0] == a:keyword
722                 let declevel = a:treelst[idx-1][1] 
723             endif
724         endif
726         " Skip folds
727         if declevel != -1 && foldclosed(idx) == -1
728             let curline = getline(idx)
729             if declevel == a:treelst[idx-1][1]
730                 let linemarker = '!'
731                 let declevel -= 1
732             else
733                 let linemarker = '#'
734             endif
735             let pos = match(curline, '[+|]', 0, declevel+1)
736             " Unconventional change char
737             let curline = linemarker.strpart(curline, 1, pos-2).'@'.
738                         \ strpart(curline, pos, 1). strpart(curline, pos+1)
739             call setline(idx, curline)
740         endif
741     endfor
742 endfunction
744 function! s:CCTreeDisplayWindowToggle()
745     if s:FindOpenWindow(s:windowtitle) == 1
746         silent! exec "hide"
747     else 
748         let winbufnr = s:FindOpenBuffer(s:windowtitle)
749         if winbufnr !=0 
750            call s:CCTreePreviewWindowEnter()
751            silent! exec "buf ".winbufnr
752            call s:CCTreeWindowResize()
753            silent! exec "wincmd p"
754         endif
755     endif
756 endfunction
758 function! s:CCTreeWindowResize()
759     if g:CCTreeWindowVertical == 1
760         if g:CCTreeWindowWidth == -1
761             exec "vert resize". b:maxwindowlen
762         else
763             exec "vertical resize". g:CCTreeWindowWidth
764         endif
765     else
766         if g:CCTreeWindowHeight != -1
767             let &winminheight = g:CCTreeWindowHeight
768            exec "resize".g:CCTreeWindowHeight
769         endif
770     endif
771 endfunction
774 function! s:CCTreeDisplayTreeInWindow(atree)
775     let incctreewin = 1
776     if (bufname('%') != s:windowtitle) 
777         call s:CCTreePreviewWindowEnter()
778         let incctreewin = 0
779     endif
781     setlocal modifiable
782     1,$d
783     let b:treelist = []
784     let b:maxwindowlen = g:CCTreeWindowMinWidth
785     let treemarkertxtlist = s:CCTreeBuildTreeDisplayItems(a:atree, b:treelist)
786     call s:CCTreeDisplayTreeList(treemarkertxtlist, b:treelist)
788     call s:CCTreeWindowResize()
789     exec "normal gg"
791     " Need to force this again
792     let &l:foldlevel=g:CCTreeMinVisibleDepth
793     setlocal nomodifiable
794     if (incctreewin == 0)
795         call s:CCTreePreviewWindowLeave()
796     endif
797 endfunction
799 function! s:CCTreeFoldExpr(line)
800     let lvl = b:treelist[v:lnum-1][1]
801     if lvl == 0
802         let lvl = 1
803     endif
804     return '>'.lvl
805 endfunction
808 function! CCTreeFoldText()
809     let line = substitute(getline(v:foldstart), '[!@#]', ' ' , 'g')
810     return line. " (+". (v:foldend - v:foldstart). 
811                 \  ')'. repeat(" ", winwidth(0))
812 endfunction
815 function! s:CCTreeStoreState(symbol, direction)
816     let s:currentkeyword = a:symbol
817     let s:currentdirection = a:direction
818 endfunction
820 function! s:CCTreeDBIsLoaded()
821     if s:dbloaded == 0
822         call s:CCTreeWarningMsg('CCTree database not loaded')
823         return 0
824     endif
825     return 1
826 endfunction
828 " Trick to get the current script ID
829 map <SID>xx <SID>xx
830 let s:sid = substitute(maparg('<SID>xx'), '<SNR>\(\d\+_\)xx$', '\1', '')
831 unmap <SID>xx
833 function! s:CCTreeTraceTreeForSymbol(sym_arg, direction)
834     if s:CCTreeDBIsLoaded() == 0
835         return
836     endif
838     let symbol = a:sym_arg
839     if symbol == ''
840         let symbol = input('Trace symbol: ', expand('<cword>'),
841                     \ 'customlist,<SNR>' . s:sid . 'CCTreeCompleteKwd')
842         if symbol == ''
843             return
844         endif
845     endif
847     let atree = s:CCTreeGetCallsForSymbol(symbol, 0, a:direction)
848     call s:CCTreeStoreState(symbol, a:direction)
849     call s:CCTreeUpdateForCurrentSymbol()
850 endfunction
852 function! s:CCTreeUpdateForCurrentSymbol()
853     if s:currentkeyword != ''
854         let atree = s:CCTreeGetCallsForSymbol(s:currentkeyword, 0, s:currentdirection)
855         call s:CCTreeDisplayTreeInWindow(atree)
856     endif
857 endfunction
860 function! s:CCTreeGetCurrentKeyword()
861     let curline = line(".")
862     if foldclosed(curline) == -1
863         let curkeyword = matchstr(getline("."), s:CCTreeKeywordRegEx)
864         if curkeyword != ''
865             if curkeyword != s:CCTreekeyword || curline != s:CCTreekeywordLine
866                 let s:CCTreekeyword = curkeyword
867                 let s:CCTreekeywordLine = line(".")
868                 return 1
869             endif
870         endif 
871     endif  
872     return -1
873 endfunction
875 function! s:CCTreeLoadBufferFromKeyword()
876     if s:CCTreeGetCurrentKeyword() == -1
877         return
878     endif
880     try 
881         exec 'wincmd p'
882     catch
883         call s:CCTreeWarningMsg('No buffer to load file')
884     finally
885         if (cscope_connection() > 0)
886             try 
887                 exec "cs find g ".s:CCTreekeyword
888             catch
889                 " cheap hack
890                 exec "cs find f ".s:CCTreekeyword
891             endtry
892         else
893             try
894                 " Ctags is smart enough to figure the path
895                 exec "tag ".fnamemodify(s:CCTreekeyword, ":t")
896             catch /^Vim\%((\a\+)\)\=:E426/
897                 call s:CCTreeWarningMsg('Tag '. s:CCTreekeyword .' not found')
898                 wincmd p
899             endtry
900         endif
901     endtry
902 endfunction
903     
904 function! s:CCTreePreviewBufferFromKeyword()
905     call s:CCTreeGetCurrentKeyword()
906     if s:CCTreekeyword == ''
907         return
908     endif
909     silent! wincmd P
910     if !&previewwindow 
911         wincmd p
912     endif
913     exec "ptag ".s:CCTreekeyword
914 endfunction
917 function! s:CCTreeSanitizeCallDepth()
918     let error = 0
919     if g:CCTreeRecursiveDepth >= s:calltreemaxdepth
920         g:CCTreeRecursiveDepth = s:calltreemaxdepth
921         let error = 1
922     elseif g:CCTreeRecursiveDepth < 1 
923         g:CCTreeRecursiveDepth = 1
924         let error = 1
925     endif
927     if error == 1
928         call s:CCTreeWarningMsg('Depth out of bounds')
929     endif
930     return error
931 endfunction
933 function! s:CCTreeRecursiveDepthIncrease()
934     let g:CCTreeRecursiveDepth += 1
935     if s:CCTreeSanitizeCallDepth() == 0
936         call s:CCTreeUpdateForCurrentSymbol()
937     endif
938 endfunction
940 function! s:CCTreeRecursiveDepthDecrease()
941     let g:CCTreeRecursiveDepth -= 1
942     if s:CCTreeSanitizeCallDepth() == 0
943         call s:CCTreeUpdateForCurrentSymbol()
944     endif
945 endfunction
948 " Use this function to determine the correct "g" flag
949 " for substitution
950 function! s:CCTreeGetSearchFlag(gvalue)
951     let ret = (!a:gvalue)* (&gdefault) + (!&gdefault)*(a:gvalue)
952     if ret == 1
953         return 'g'
954     endif
955     return ''
956 endfunc
958 function! s:CCTreeClearMarks()
959    let windict = winsaveview()
960    silent! exec "1,$s/[!#@]/ /e".s:CCTreeGetSearchFlag(1)
961    call winrestview(windict)
962 endfunction
964 function! s:CCTreeCursorHoldHandle()
965     if g:CCTreeHilightCallTree && s:CCTreeGetCurrentKeyword() != -1 
966        setlocal modifiable
967        call s:CCTreeClearMarks()
968        call s:CCTreeMarkCallTree(b:treelist, s:CCTreekeyword)
969        setlocal nomodifiable
970     endif
971 endfunction
973 " CCTreeCompleteKwd
974 " Command line completion function to return names from the db
975 function! s:CCTreeCompleteKwd(arglead, cmdline, cursorpos)
976     if a:arglead == ''
977         return keys(s:symhashtable)
978     else
979         return filter(keys(s:symhashtable), 'v:val =~? a:arglead')
980     endif
981 endfunction
983 augroup CCTreeGeneral
984     au!
985     autocmd CursorHold CCTree-Preview call s:CCTreeCursorHoldHandle()
986 augroup END
990 "Standard display
991 highlight link CCTreeSymbol  Function
992 highlight link CCTreeMarkers LineNr
993 highlight link CCTreeArrow CCTreeMarkers
994 highlight link CCTreePathMark CCTreeArrow
995 highlight link CCTreeHiPathMark CCTreePathMark
997 " highlighted display
998 highlight link CCTreeHiSymbol  TODO
999 highlight link CCTreeHiMarkers StatusLine
1000 highlight link CCTreeHiArrow  CCTreeHiMarkers
1001 highlight link CCTreeUpArrowBlock CCTreeHiArrow
1003 highlight link CCTreeMarkExcl Ignore
1004 highlight link CCTreeMarkTilde Ignore
1007 " Define commands
1008 command! -nargs=? -complete=file CCTreeLoadDB  call s:CCTreeLoadDB(<q-args>)
1009 command! -nargs=? -complete=file CCTreeAppendDB  call s:CCTreeAppendDB(<q-args>)
1010 command! -nargs=0 CCTreeUnLoadDB               call s:CCTreeUnloadDB()
1011 command! -nargs=0 CCTreeShowLoadedDBs          call s:CCTreeShowLoadedDBs()
1012 command! -nargs=? -complete=customlist,s:CCTreeCompleteKwd
1013         \ CCTreeTraceForward call s:CCTreeTraceTreeForSymbol(<q-args>, 'c')
1014 command! -nargs=? -complete=customlist,s:CCTreeCompleteKwd CCTreeTraceReverse  
1015             \ call s:CCTreeTraceTreeForSymbol(<q-args>, 'p')
1016 command! -nargs=0 CCTreeLoadBufferUsingTag call s:CCTreeLoadBufferFromKeyword()
1017 command! -nargs=0 CCTreePreviewBufferUsingTag call s:CCTreePreviewBufferFromKeyword()
1018 command! -nargs=0 CCTreeRecurseDepthPlus call s:CCTreeRecursiveDepthIncrease()
1019 command! -nargs=0 CCTreeRecurseDepthMinus call s:CCTreeRecursiveDepthDecrease()
1020 command! -nargs=0 CCTreeWindowToggle    call s:CCTreeDisplayWindowToggle()
1023 function! s:CCTreeGetKeyword()
1024     let keyw = expand("<cword>")
1025     let keyf = expand("<cfile>")
1027     if keyw != keyf 
1028         if has_key(s:symhashtable, keyf)
1029             return keyf
1030         elseif has_key(s:symhashtable, keyw)
1031             return keyw
1032         endif
1033     else
1034         return keyw
1035     endif
1036     return ''
1037 endfunction
1040 function! s:CCTreeBufferKeyMappingsCreate()
1041      let func_expr = '<SNR>'.s:sid.'CCTreeGetKeyword()'
1042      exec 'nnoremap <buffer> <silent> <C-\>< :CCTreeTraceReverse <C-R>='.func_expr.'<CR><CR>'
1043      exec 'nnoremap <buffer> <silent> <C-\>> :CCTreeTraceForward <C-R>='.func_expr.'<CR><CR>'
1044      exec 'nnoremap <silent> <C-\>w :CCTreeWindowToggle<CR>'
1046      nnoremap <buffer> <silent> <C-\>= :CCTreeRecurseDepthPlus<CR> 
1047      nnoremap <buffer> <silent> <C-\>- :CCTreeRecurseDepthMinus<CR> 
1048 endfunction
1050 augroup CCTreeMaps
1052 " Header files get detected as cpp?
1053 " This is a bug in Vim 7.2, a patch needs to be applied to the runtime c
1054 " syntax files
1055 " For now, use this hack to make *.h files work
1056 autocmd FileType * if &ft == 'c'|| &ft == 'cpp' |call s:CCTreeBufferKeyMappingsCreate()| endif
1057 augroup END
1060 " Cscope Digraph character compression/decompression routines
1061 " the logic of these routines are based off the Cscope source code
1063 let s:dichar1 = " teisaprnl(of)=c"      
1064 let s:dichar2 = " tnerpla"
1066 function! s:Digraph_DictTable_Init ()
1067     let dicttable = []
1068     let index = 0
1070     for dc1 in range(strlen(s:dichar1))
1071         for dc2 in range(strlen(s:dichar2))
1072             call add(dicttable, s:dichar1[dc1].s:dichar2[dc2])
1073         endfor
1074     endfor
1076     return dicttable
1077 endfunction
1079 function! s:Digraph_Uncompress_Slow (value, dicttable)
1080     let retval = ""
1081     for idx in range(strlen(a:value))
1082         let charext = char2nr(a:value[idx])-128
1083         if charext >= 0 
1084             let retval .= a:dicttable[charext]
1085         else
1086             let retval .= a:value[idx]
1087         endif
1088     endfor
1089     return retval
1090 endfunction
1092 function! s:Digraph_Uncompress_Fast (value, dicttable)
1093     let dichar_list = split(a:value, '[^\d128-\d255]\{}')
1094     let retval = a:value
1095     for adichar in dichar_list
1096         let retval = substitute(retval, '\C'.adichar, a:dicttable[char2nr(adichar)-128], "g")
1097     endfor
1098     return retval
1099 endfunction
1102 function! s:Digraph_Uncompress_filter_loop(compressedsym, symlist, symhash, cmpdict)
1103     let idx = a:symhash[a:compressedsym]
1104     let uncmpname = s:Digraph_Uncompress_Fast(a:compressedsym, a:cmpdict)
1105     let a:symhash[uncmpname] = idx
1106     let a:symlist[idx]['n'] = uncmpname
1107     call s:CCTreeProgressBarTick(1)
1108     return 0
1109 endfunction
1111 function! s:Digraph_Uncompress (symlist, symhash)
1112     let compressdict = s:Digraph_DictTable_Init()
1114     call s:CCTreeProgressBarInit(len(a:symhash))
1115 " The encoding needs to be changed to 8-bit, otherwise we can't swap special 
1116 " 8-bit characters; restore after done
1117     let encoding_save=&encoding
1118     let &encoding="latin1"
1120     for compressedsym in keys(a:symhash)
1121         let idx = a:symhash[compressedsym]
1122         let uncmpname = s:Digraph_Uncompress_Fast(compressedsym, compressdict)
1123         let a:symhash[uncmpname] = idx
1124         " free the old entry
1125         unlet a:symhash[compressedsym]
1126         let a:symlist[idx]['n'] = uncmpname
1127         call s:CCTreeProgressBarTick(1)
1128     endfor
1129     let &encoding=encoding_save
1130 endfunction
1133 function! s:Digraph_Compress(value, dicttable)
1134     let index = 0
1135     let retval = ""
1137     while index < strlen(a:value)
1138         let dc1 = stridx(s:dichar1, a:value[index])
1139         if dc1 != -1
1140             let dc2 = stridx(s:dichar2, a:value[index+1])
1141             if dc2 != -1 
1142                 let retval .= nr2char(128 + (dc1*8) + dc2)  
1143                 " skip 2 chars
1144                 let index += 2
1145                 continue
1146             endif
1147         endif
1148         let retval .= a:value[index]
1149         let index += 1
1150     endwhile
1151     return retval
1152 endfunction
1155 " restore 'cpo'
1156 let &cpoptions = s:cpo_save
1157 unlet s:cpo_save