관리-도구
편집 파일: cctree.vim
" C Call-Tree Explorer (CCTree) <CCTree.vim> " " " Script Info and Documentation "============================================================================= " Copyright: Copyright (C) August 2008 - 2011, Hari Rangarajan " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright " notice is copied with it. Like anything else that's free, " cctree.vim is provided *as is* and comes with no " warranty of any kind, either expressed or implied. In no " event will the copyright holder be liable for any damamges " resulting from the use of this software. " " Name Of File: CCTree.vim " Description: C Call-Tree Explorer Vim Plugin " Maintainer: Hari Rangarajan <hari.rangarajan@gmail.com> " URL: http://vim.sourceforge.net/scripts/script.php?script_id=2368 " Last Change: May 18, 2011 " Version: 1.51 " "============================================================================= " " {{{ Description: " Plugin generates dependency-trees for symbols using a cscope database " in Vim. " }}} " {{{ Requirements: 1) Vim 7.xx , 2) Cscope " " Tested on Unix and the following Win32 versions: " + Cscope, mlcscope (WIN32) " http://code.google.com/p/cscope-win32/ " http://www.bell-labs.com/project/wwexptools/packages.html " }}} " {{{ Installation: " Copy this file to ~/.vim/plugins/ " or to /vimfiles/plugins/ (on Win32 platforms) " " It might also be possible to load it as a filetype plugin " ~/.vim/ftplugin/c/ " " Need to set :filetype plugin on " " }}} " {{{ Usage: " Build cscope database, for example: " > cscope -b -i cscope.files " [Tip: add -c option to build uncompressed databases for faster " load speeds] " " Load database with command ":CCTreeLoadDB" " (Please note that it might take a while depending on the " database size) " " Append database with command ":CCTreeAppendDB" " Allows multiple cscope files to be loaded and cross-referenced " Illustration: " :CCTreeAppendDB ./cscope.out " :CCTreeAppendDB ./dir1/cscope.out " :CCTreeAppendDB ./dir2/cscope.out " " A database name, i.e., my_cscope.out, can be specified with " the command. If not provided, a prompt will ask for the " filename; default is cscope.out. " " To show loaded databases, use command ":CCTreeShowLoadedDBs" " " To unload all databases, use command ":CCTreeUnLoadDB" " Note: There is no provision to unload databases individually " " To save the current set of databases loaded in to memory onto disk " in native CCTree XRef format, use command ":CCTreeSaveXRefDB" " " To load a saved native CCTree XRef format file, use " command ":CCTreeLoadXRefDB" " " To load a saved native CCTree XRef format file, use " command ":CCTreeLoadXRefDBFromDisk" " " Notes: No merging database support for CCTree native DB's [at present]. " " " To have multiple CCTree preview windows, use ":CCTreeWindowSaveCopy" " Note: Once saved, only the depth of the preview window can be changed " " Default Mappings: " Get reverse call tree for symbol <C-\>< " Get forward call tree for symbol <C-\>> " Increase depth of tree and update <C-\>= " Decrease depth of tree and update <C-\>- " " Open symbol in other window <CR> " Preview symbol in other window <Ctrl-P> " " Save copy of preview window <C-\>y " Highlight current call-tree flow <C-l> " Compress(Fold) call tree view zs " (This is useful for viewing long " call trees which span across " multiple pages) " " Custom user-mappings: " Users can custom-map the short-cut keys by " overriding the following variables in their " Vim start-up configuration " " g:CCTreeKeyTraceForwardTree = '<C-\>>' " g:CCTreeKeyTraceReverseTree = '<C-\><' " g:CCTreeKeyHilightTree = '<C-l>' " Static highlighting " g:CCTreeKeySaveWindow = '<C-\>y' " g:CCTreeKeyToggleWindow = '<C-\>w' " g:CCTreeKeyCompressTree = 'zs' " Compress call-tree " g:CCTreeKeyDepthPlus = '<C-\>=' " g:CCTreeKeyDepthMinus = '<C-\>-' " " Command List: " CCTreeLoadDB <dbname> " CCTreeAppendDB <dbname> " CCTreeLoadXRefDB <dbname> " CCTreeSaveXRefDB <dbname> " CCTreeLoadXRefDBFromDisk <dbname> " " CCTreeUnLoadDB " CCTreeShowLoadedDBs " " CCTreeTraceForward <symbolname> " CCTreeTraceReverse <symbolname> " CCTreeRecurseDepthPlus " CCTreeRecurseDepthMinus " CCTreeWindowSaveCopy " " Only in preview window: " CCTreeWindowHiCallTree (same as <C-l> shorcut) " Highlight calling tree for keyword at cursor " " Dynamic configuration: " CCTreeOptsEnable <option> (<tab> for auto-complete) " CCTreeOptsDisable <option> (<tab> for auto-complete) " CCTreeOptsToggle <option> (<tab> for auto-complete) " Options: " DynamicTreeHiLights: Control dynamic tree highlighting " UseUnicodeSymbols: Use of UTF-8 special characters for " tree " UseConceal: Use (+Conceal) feature instead of 'ignore' " syntax highlighting. Allows CCTree window " to be exported in HTML without syntax markup " characters. (Vim 7.3+ only) " EnhancedSymbolProcessing: Cross-reference enums, macros, " global variables, typedefs (Warning: Database " processing speeds will be slow). " " " " Settings: " Customize behavior by changing the variable settings " " UTF-8 usage: " UTF-8 symbols should work fine on the majority of " X11 systems; however, some terminals might cause problems. " " To use symbols for drawing the tree, this option can be enabled. " g:CCTreeUseUTF8Symbols = 1 " The options interface (CCTreeOptsxxx) can be used to " modify options on-the-fly. " " Cscope database file, g:CCTreeCscopeDb = "cscope.out" " Maximum call levels, g:CCTreeRecursiveDepth = 3 " Maximum visible(unfolded) level, g:CCTreeMinVisibleDepth = 3 " Orientation of window, g:CCTreeOrientation = "topleft" " (standard vim options for split: [right|left][above|below]) " " Use Vertical window, g:CCTreeWindowVertical = 1 " Min width for window, g:CCTreeWindowMinWidth = 40 " g:CCTreeWindowWidth = -1, auto-select best width to fit " " Horizontal window, g:CCTreeWindowHeight, default is -1 " " " Display format, g:CCTreeDisplayMode, default 1 " " Values: 1 -- Ultra-compact (takes minimum screen width) " 2 -- Compact (Takes little more space) " 3 -- Wide (Takes copious amounts of space) " " For vertical splits, 1 and 2 are good, while 3 is good for " horizontal displays " " NOTE: To get older behavior, add the following to your vimrc " let g:CCTreeDisplayMode = 3 " let g:CCTreeWindowVertical = 0 " " Syntax Coloring: " CCTreeSymbol is the symbol name " CCTreeMarkers include "|","+--->" " " CCTreeHiSymbol is the highlighted call tree functions " CCTreeHiMarkers is the same as CCTreeMarkers except " these denote the highlighted call-tree " " " CCTreeHiXXXX allows dynamic highlighting of the call-tree. " To observe the effect, move the cursor to the function to " highlight the current call-tree. This option can be " turned off using the setting, g:CCTreeHilightCallTree. " For faster highlighting, the value of 'updatetime' can be " changed. " " Support for large database files: " Vimscript does not have an API for reading files line-by-line. This " becomes a problem when parsing large databases. CCTree can overcome " the limitation using an external utility (i.e., GNU coreutils: split) " or VimScript's perl interpreter interface (:version must indicate +perl) " " The following settings are tailored to suit GNU coreutils split; the default " settings should work with no changes on typical linux/unix distros " (Monopoly OSes will require installation of unixutils or equivalent) " " External command is setup with the following parameters: " g:CCTreeSplitProgCmd = 'PROG_SPLIT SPLIT_OPT SPLIT_SIZE IN_FILE OUT_FILE_PREFIX' " " Break-down of individual parameters: " The split utility is assumed to be on the path; otherwise, specify full path " g:CCTreeSplitProg = 'split' " " Option for splitting files (-C or -l) " g:CCTreeSplitProgOption = '-C' " If split program does not support -C, then this parameter must be set to " the number of lines in the split files " g:CCTreeDbFileSplitLines = -1 " Largest filesize Vimscript can handle; file sizes greater than this will " be temporarily split " g:CCTreeDbFileMaxSize = 40000000 (40 Mbytes) " " Sample system command: " Typical: " split -C 40000000 inputFile outputFilePrefix " " When g:CCTreeDbFileSplitLines is set to 10000 (-C options will be ignored) " split -l 10000 inputFile outputFilePrefix " " " Using perl interface: " By default, perl usage is disabled. Set " g:CCTreeUsePerl = 1 to enable the perl interface. " " Perl interface is typically faster than native Vimscript. " This option can be used independent of the file size " " For more info on setting up perl interface " :help perl-using or :help perl-dynamic " " Writing large Xref Databases: " CCTree can use external utilities to write extremely large files beyond " VimScripts capabilities. It requires the use of an external tool that can " join text files (i.e., 'cat' in unix). This utility is triggered if the size " of the file being written exceeds g:CCTreeDbFileMaxSize (40 Mb or as configured) " " The join utility command is configured by default as follows: " let CCTreeJoinProgCmd = 'PROG_JOIN JOIN_OPT IN_FILES > OUT_FILE' " " let g:CCTreeJoinProg = 'cat' " PROG_JOIN " let g:CCTreeJoinProgOpts = "" " JOIN_OPT " " " }}} " {{{ Limitations: " Basic Symbol Processing: " The accuracy of the call-tree will only be as good as the cscope " database generation. " NOTE: Different flavors of Cscope have some known " limitations due to the lexical analysis engine. This results " in incorrectly identified function blocks, etc. " Enhanced Symbol Processing: " (1) Cscope does not mark-up nameless enums correctly; hence, " CCTree cannot recognize nameless enum symbols. " }}} " {{{ History: " Version 1.51: May 18, 2011 " 1. Robust error reporting when external (split/cat) utils fail " Version 1.50: May 6, 2011 " 1. Support cross-referencing of global variables, macros, " enums, and typedefs. " Version 1.40: April 22, 2011 " 1. Maintain order of functions called during forward tracing " Version 1.39: April 18, 2011 " 1. Use +Conceal feature for highlighting (only Vim 7.3) " Version 1.33: April 5, 2011 " 1. Load and trace CCTree native XRefDb directly from disk " 2. Fix AppendDB command when 'ignorecase' is set " Version 1.26: March 28, 2011 " 1. Fix macro cross-referencing limitation " 2. Correct native xref file format " Version 1.21: March 21, 2011 " 1. Support serialization of loaded " cscope databases (for faster loading) " Version 1.07: March 09, 2011 " 1. Fix new keymaps incorrectly applied to buffer " 2. CCTreeOptsToggle command for toggling options " " Version 1.04: March 06, 2011 " 1. Customization for key mappings " 2. Dynamic configuration of UI variables " 3. Folding long call-trees to show current path dynamically " " Version 1.01: March 04, 2011 " 1. Make UTF-8 symbols for tree optional " " Version 1.00: March 02, 2011 " 1. Staging release for upcoming features " - Complete refactoring of code to take " advantage of VimScript's OO features " 2. Faster decompression of symbols " 3. Display related changes " - Use of unicode symbols for tree " 4. Bugfixes related to multi-database loading " " Version 0.90: February 18, 2011 " 1. Support for large databases using external split utility or perl " interface " " Version 0.85: February 9, 2011 " 1. Significant increase in database loading and decompression speeds " " Version 0.80: February 4, 2011 " 1. Reduce memory usage by removing unused xref symbols " " Version 0.75: June 23, 2010 " 1. Support for saving CCTree preview window; multiple " CCTree windows can now be open " " Version 0.71: May 11, 2010 " 1. Fix script bug " Version 0.70: May 8, 2010 " 1. Functionality to load multiple cscope databases " " Version 0.65: July 12, 2009 " 1. Toggle preview window " " Version 0.61: December 24, 2008 " 1. Fixed bug when processing include files " 2. Remove 'set ruler' option " " Version 0.60: November 26, 2008 " 1. Added support for source-file dependency tree " " Version 0.50: October 17, 2008 " 1. Optimizations for compact memory foot-print and " improved compressed-database load speeds " " Version 0.41: October 6, 2008 " 1. Minor fix: Compressed cscope databases will load " incorrectly if encoding is not 8-bit " " Version 0.4: September 28, 2008 " 1. Rewrite of "tree-display" code " 2. New syntax hightlighting " 3. Dynamic highlighting for call-trees " 4. Support for new window modes (vertical, horizontal) " 5. New display format option for compact or wide call-trees " NOTE: defaults for tree-orientation set to vertical " " Version 0.3: " September 21, 2008 " 1. Support compressed cscope databases " 2. Display window related bugs fixed " 3. More intuitive display and folding capabilities " " Version 0.2: " September 12, 2008 " (Patches from Yegappan Lakshmanan, thanks!) " 1. Support for using the plugin in Vi-compatible mode. " 2. Filtering out unwanted lines before processing the db. " 3. Command-line completion for the commands. " 4. Using the cscope db from any directory. " " Version 0.1: " August 31,2008 " 1. Cross-referencing support for only functions and macros " Functions inside macro definitions will be incorrectly " attributed to the top level calling function " " }}} " {{{ Thanks: " " Qaiser Durrani (ver 1.51 -- Reporting issues with SunOS) " Ben Fritz (ver 1.39 -- Suggestion/Testing for conceal feature) " Ben Fritz (ver 1.26 -- Bug report) " Frank Chang (ver 1.0x -- testing/UI enhancement ideas/bug fixes) " Arun Chaganty/Timo Tiefel (Ver 0.60 -- bug report) " Michael Wookey (Ver 0.4 -- Testing/bug report/patches) " Yegappan Lakshmanan (Ver 0.2 -- Patches) " " The Vim Community, ofcourse :) "============================================================================= " }}} " {{{ Init if !exists('loaded_cctree') && v:version >= 700 " First time loading the cctree plugin let loaded_cctree = 1 else "finish endif " Line continuation used here let s:cpo_save = &cpoptions set cpoptions&vim " Trick to get the current script ID map <SID>xx <SID>xx let s:sid = substitute(maparg('<SID>xx'), '<SNR>\(\d\+_\)xx$', '\1', '') unmap <SID>xx "}}} " {{{ Global variables; Modify in .vimrc to modify default behavior " {{{General if !exists('CCTreeCscopeDb') let CCTreeCscopeDb = "cscope.out" endif " revisit if !exists('CCTreeDb') let CCTreeDb = "cctree.out" endif if !exists('CCTreeRecursiveDepth') let CCTreeRecursiveDepth = 3 endif if !exists('CCTreeMinVisibleDepth') let CCTreeMinVisibleDepth = 3 endif if !exists('CCTreeEnhancedSymbolProcessing') let CCTreeEnhancedSymbolProcessing = 0 endif " }}} " {{{ Custom user-key mappings if !exists('CCTreeKeyTraceForwardTree') let g:CCTreeKeyTraceForwardTree = '<C-\>>' endif if !exists('CCTreeKeyTraceReverseTree') let g:CCTreeKeyTraceReverseTree = '<C-\><' endif if !exists('CCTreeKeyHilightTree') let g:CCTreeKeyHilightTree = '<C-l>' " Static highlighting endif if !exists('CCTreeKeySaveWindow ') let g:CCTreeKeySaveWindow = '<C-\>y' endif if !exists('CCTreeKeyToggleWindow ') let g:CCTreeKeyToggleWindow = '<C-\>w' endif if !exists('CCTreeKeyCompressTree ') let g:CCTreeKeyCompressTree = 'zs' " Compress call-tree endif if !exists('CCTreeKeyDepthPlus') let g:CCTreeKeyDepthPlus = '<C-\>=' endif if !exists('CCTreeKeyDepthMinus') let g:CCTreeKeyDepthMinus = '<C-\>-' endif " }}} " {{{ CCTree UI settings if !exists('CCTreeOrientation') let CCTreeOrientation = "topleft" endif if !exists('CCTreeWindowVertical') let CCTreeWindowVertical = 1 endif if !exists('CCTreeWindowWidth') " -1 is auto select best width let CCTreeWindowWidth = -1 endif if !exists('CCTreeWindowMinWidth') let CCTreeWindowMinWidth = 25 endif if !exists('CCTreeWindowHeight') let CCTreeWindowHeight = -1 endif if !exists('CCTreeDisplayMode') let CCTreeDisplayMode = 1 endif if !exists('CCTreeHilightCallTree') let CCTreeHilightCallTree = 1 endif " }}} " {{{ Split prog if !exists('CCTreeSplitProgCmd') let CCTreeSplitProgCmd = 'PROG_SPLIT SPLIT_OPT SPLIT_SIZE IN_FILE OUT_FILE_PREFIX' endif if !exists('CCTreeSplitProg') "PROG_SPLIT let CCTreeSplitProg = 'split' endif if !exists('CCTreeSplitProgOption') "SPLIT_OPT let CCTreeSplitProgOption = '-C' endif if !exists('CCTreeDbFileSplitLines') " if SPLIT_OPT is -l " If split program does not support -C, then this parameter must be set to " the number of lines in the split files let CCTreeDbFileSplitLines = -1 endif if !exists('CCTreeSplitProgCmd') let CCTreeSplitProgCmd = 'PROG_SPLIT SPLIT_OPT SPLIT_SIZE IN_FILE OUT_FILE_PREFIX' endif if !exists('CCTreeDbFileMaxSize') " if SPLIT_OPT is -C let CCTreeDbFileMaxSize = 40000000 "40 Mbytes endif " }}} " {{{ Join/Cat prog if !exists('CCTreeJoinProgCmd') let CCTreeJoinProgCmd = 'PROG_JOIN JOIN_OPT IN_FILES > OUT_FILE' endif if !exists('CCTreeJoinProg') "PROG_JOIN let CCTreeJoinProg = 'cat' endif if !exists('CCTreeJoinProgOpts') let CCTreeJoinProgOpts = "" endif " }}} " {{{ Misc (perl) if !exists('CCTreeUsePerl') " Disabled by default let CCTreeUsePerl = 0 if 0 " Auto-detect perl interface (Experimental code) if has('perl) perl << PERL_EOF VIM::DoCommand("let CCTreeUsePerl = 1"); PERL_EOF endif endif endif if has('conceal') let s:CCTreeUseConceal = 1 else let s:CCTreeUseConceal = 0 endif if !exists('CCTreeUseUTF8Symbols') let CCTreeUseUTF8Symbols = 0 endif " }}} " }}} " {{{ Plugin related local variables let s:pluginname = 'CCTree' let s:windowtitle = 'CCTree-View' let s:windowsavetitle = 'CCTree-View-Copy' let s:DBClasses = { 'cscopeid': 'Cscope', 'cctreexref' : 'CCTree XRef'} let s:DBStorage = { 'memory': 'Memory', 'disk' : 'Disk'} " }}} " {{{ Turn on/off debugs let s:tag_debug=0 " Use the Decho plugin for debugging function! DBGecho(...) if s:tag_debug Decho(a:000) endif endfunction function! DBGredir(...) if s:tag_debug Decho(a:000) endif endfunction function! Pause() call input("sasasD", "asdads") endfunction " }}} " {{{ Progress bar (generic, numeric, rolling) let s:GenericProgressBar= { \ 'depth': 0, \ 'depthChar': '', \ 'currentChar': 0, \ 'updateTime': 0, \ 'rangeChars': [], \ 'formatStr' : '', \ 'units' : '' \ } function! s:GenericProgressBar.mCreate(rangechars, depthchar, fmtStr) let pbr = deepcopy(s:GenericProgressBar) unlet pbr.mCreate let pbr.rangeChars = a:rangechars let pbr.depthChar = a:depthchar let pbr.formatStr = a:fmtStr return pbr endfunction function! s:GenericProgressBar.mSetDepth(val) dict let self.depth = a:val endfunction function! s:GenericProgressBar.mUpdate() dict let staticchars = repeat(self.depthChar, self.depth) let displayStr = substitute(self.formatStr, "\@PROGRESS\@", \ staticchars . self.rangeChars[self.currentChar], "") call s:StatusLine.mSetExtraInfo(displayStr) endfunction function! s:GenericProgressBar.mDone() call s:StatusLine.mSetExtraInfo("") endfunction let s:ProgressBarRoll = { \ 'updateTime' : 0, \ 'curTime' : 0 \} function! s:ProgressBarRoll.mCreate(rollchars, depthChar) dict let gpbr = s:GenericProgressBar.mCreate(a:rollchars, a:depthChar, "\@PROGRESS\@") let pbr = extend(gpbr, deepcopy(s:ProgressBarRoll)) unlet pbr.mCreate let pbr.curTime = localtime() return pbr endfunction function! s:ProgressBarRoll.mTick(count) dict if (localtime() - self.curTime) > self.updateTime let self.currentChar += 1 if self.currentChar == len(self.rangeChars) let self.currentChar = 0 endif let self.curTime = localtime() call self.mUpdate() endif endfunction let s:ProgressBarNumeric = { \ 'progress1current' : 0, \ 'progressmax' : 0, \ 'progress1percent' : 0, \ 'progresspercent' : 0, \} function! s:ProgressBarNumeric.mCreate(maxcount, unit) dict let gpbr = s:GenericProgressBar.mCreate(range(0,200), '', \ "Processing \@PROGRESS\@\%, total ". a:maxcount . " " . a:unit) let progressbar = extend(gpbr, deepcopy(s:ProgressBarNumeric)) unlet progressbar.mCreate let progressbar.progressmax = a:maxcount let progressbar.progress1percent = a:maxcount/100 let progressbar.units = a:unit return progressbar endfunction function! s:ProgressBarNumeric.mTick(count) dict let self.progress1current += a:count if self.progress1percent <= self.progress1current let tmp = (self.progress1current/self.progress1percent) let self.progresspercent += tmp let self.progress1current -= tmp * self.progress1percent let self.currentChar += 1 call self.mUpdate() endif endfunction " }}} " {{{ Status line let s:StatusLine = { \ 'symlastprogress' : 0, \ 'symprogress' : 0, \ 'cursym' : 0, \ 'savedStatusLine' : '', \ 'statusextra' : '', \ 'local':0 \} function! s:StatusLine.mInit() dict let self.savedStatusLine = &l:statusline setlocal statusline=%{CCTreeStatusLine()} endfunction function! s:StatusLine.mRestore() dict let self.currentstatus = '' let self.statusextra = '' let &l:statusline = s:StatusLine.savedStatusLine redrawstatus endfunction function! s:StatusLine.mSetInfo(msg) dict let s:StatusLine.currentstatus = a:msg redrawstatus endfunction function! s:StatusLine.mSetExtraInfo(msg) dict let s:StatusLine.statusextra = a:msg redrawstatus endfunction function! CCTreeStatusLine() return s:pluginname. " ". \ s:StatusLine.currentstatus . " -- ". \ s:StatusLine.statusextra endfunction "}}} " {{{ Shell command interface let s:ShellCmds = {'shellOutput': ''} function! s:ShellCmds.mSplit(inFile, outFile) let cmdEx = substitute(g:CCTreeSplitProgCmd, "PROG_SPLIT", g:CCTreeSplitProg,"") let cmdEx = substitute(cmdEx, "SPLIT_OPT", g:CCTreeSplitProgOption,"") if g:CCTreeDbFileSplitLines != -1 let cmdEx = substitute(cmdEx, "SPLIT_SIZE", g:CCTreeDbFileSplitLines,"") else let cmdEx = substitute(cmdEx, "SPLIT_SIZE", g:CCTreeDbFileMaxSize,"") endif let cmdEx = substitute(cmdEx, "IN_FILE", a:inFile,"") let cmdEx = substitute(cmdEx, "OUT_FILE_PREFIX", a:outFile,"") return cmdEx endfunction function! s:ShellCmds.mJoin(inFileList, outFile) let cmdEx = substitute(g:CCTreeJoinProgCmd, "PROG_JOIN", g:CCTreeJoinProg,"") let cmdEx = substitute(cmdEx, "JOIN_OPT", g:CCTreeJoinProgOpts,"") let cmdEx = substitute(cmdEx, "IN_FILES", a:inFileList,"") let cmdEx = substitute(cmdEx, "OUT_FILE", a:outFile,"") return cmdEx endfunction function! s:ShellCmds.mExec(cmd) let s:shellOutput= system(a:cmd) if s:shellOutput != '' " Failed return s:CCTreeRC.Error endif return s:CCTreeRC.Success endfunction " }}} " {{{ Virtual file interface let s:vFile = {} function! s:vFile.mCreate(fname, mode) if a:mode == 'r' return s:vFileR.mCreate(a:fname) elseif a:mode == 'w' return s:vFileW.mCreate(a:fname) endif return -1 endfunction let s:vFileW = { \ 'splitfiles' : [], \ 'totSplits' : 0, \ 'lines' : [], \ 'fileSize' : 0 \} function! s:vFileW.mCreate(fname) dict let vfile = deepcopy(s:vFileW) unlet vfile.mCreate let vfile.link = a:fname return vfile endfunction function! s:vFileW.mCreateSplit() dict " first split, create name if self.totSplits == 0 let self.tlink = tempname() endif let fname = self.tlink .'_'. self.totSplits call writefile(self.lines, fname) call add(self.splitfiles, fname) let self.lines = [] let self.totSplits += 1 endfunction function! s:vFileW.mTestForSplit() dict if self.fileSize > g:CCTreeDbFileMaxSize call self.mCreateSplit() endif endfunction function! s:vFileW.mAddFileSize(size) dict let self.fileSize += a:size endfunction function! s:vFileW.mWriteList(linelist) dict call extend(self.lines, a:linelist) call self.mTestForSplit() endfunction function! s:vFileW.mWriteLine(line) dict call add(self.lines, a:line) call self.mAddFileSize(len(a:line)) call self.mTestForSplit() endfunction function! s:vFileW.mClose() dict if self.totSplits == 0 call writefile(self.lines, self.link) else " force remaining lines into a new split call self.mCreateSplit() " now join all of them let filelist = join(self.splitfiles, " ") let cmdEx = s:ShellCmds.mJoin(filelist, self.link) if s:ShellCmds.mExec(cmdEx) != s:CCTreeRC.Success let msg = s:shellOutput ."Shell command: ".cmdEx. " failed!". \ " Refer help to setup split/join utils." call s:CCTreeUtils.mWarningPrompt(msg) endif endif for afile in self.splitfiles call delete(afile) endfor return 0 endfunction let s:vFileR = { \ 'splitfiles' : [], \ 'currentSplitIdx' : 0, \ 'totSplits' : 0, \ 'lines' : [], \ 'valid' : 0, \ 'mode' : "" \} function! s:vFileR.mIsLargeFile() dict if (getfsize(self.link) > g:CCTreeDbFileMaxSize) return 1 endif return 0 endfunction function! s:vFileR.mCreate(fname) dict let vfile = deepcopy(s:vFileR) unlet vfile.mCreate let vfile.link = a:fname let vfile.valid = filereadable(a:fname) let vfile.size = getfsize(a:fname) return vfile endfunction function! s:vFileR.mOpen() dict if self.mode == 'w' " no need to do anything return 0 endif if self.mIsLargeFile() == 0 "little trick to keep interface uniform when we don't split call add(self.splitfiles, self.link) let self.totSplits = 1 else let tmpDb = tempname() let cmdEx = s:ShellCmds.mSplit(self.link, tmpDb) if s:ShellCmds.mExec(cmdEx) != s:CCTreeRC.Success let msg = s:shellOutput ."Shell command: ".cmdEx. " failed!". \ " Refer help to setup split/join utils." call s:CCTreeUtils.mWarningPrompt(msg) return -1 else let self.splitfiles = split(expand(tmpDb."*"), "\n") endif if empty(self.splitfiles) return -1 endif endif let self.totSplits = len(self.splitfiles) return 0 endfunction function! s:vFileR.mRead() dict if (self.currentSplitIdx >= len(self.splitfiles)) " out of bounds return -1 endif let self.lines = readfile(self.splitfiles[self.currentSplitIdx]) let self.currentSplitIdx += 1 return 0 endfunction function! s:vFileR.mRewind() dict let self.currentSplitIdx = 0 let self.lines = [] endfunction function! s:vFileR.mClose() dict if self.totSplits == 1 return endif for afile in self.splitfiles call delete(afile) endfor endfunction "}}} " {{{Stop watch let s:StopWatch = { \ 'text' : "(no reltime feature)", \} function! s:StopWatch.mCreate() dict let stopWatch = deepcopy(s:StopWatch) unlet stopWatch.mCreate call stopWatch.mReset() return stopWatch endfunction function! s:StopWatch.mReset() dict if has('reltime') let self.startRTime = reltime() else let self.startRTime = localtime() endif endfunction function! s:StopWatch.mSnapElapsed() dict if has('reltime') let self.text = reltimestr(reltime(self.startRTime)) else let self.text = localtime() - self.startRTime endif endfunction function! s:StopWatch.mGetText() dict return self.text endfunction "}}} " {{{ Digraph character compression/decompression routines let s:CharMaps = { \'savedEncoding' : '', \'mapkind' : '' \} " The encoding needs to be changed to 8-bit, otherwise we can't swap special " 8-bit characters; restore after done function! s:CharMaps.mInitTranslator() dict if self.mapkind == 'Alpha' let self.savedEncoding = &encoding let &encoding="latin1" endif endfunction function! s:CharMaps.mDoneTranslator() dict if self.mapkind == 'Alpha' let &encoding=self.savedEncoding endif endfunction function! s:CharMaps.CrossProduct(seq1, seq2) dict let cpSeq = [] for dc1 in range(strlen(a:seq1)) for dc2 in range(strlen(a:seq2)) call add(cpSeq, a:seq1[dc1].a:seq2[dc2]) endfor endfor return cpSeq endfunction let s:TranslateMap = {} function! s:TranslateMap.mCreate (srcsym, destsym, mapkind, regex) dict let dicttable = extend(deepcopy(s:CharMaps), deepcopy(s:TranslateMap)) unlet dicttable.CrossProduct let dicttable.mappings = {} " map lower let maxsym = min([len(a:srcsym),len (a:destsym)]) let index = 0 while (index < maxsym) let dicttable.mappings[a:srcsym[index]] = a:destsym[index] let index += 1 endwhile " Need mapping lens, we assume it's constant across the board let dicttable.mapsrclen = len(a:srcsym[0]) let dicttable.regex = a:regex if a:mapkind == 'Alpha' let dicttable.mTranslate = dicttable.mTranslateAlpha elseif a:mapkind == 'Numeric' let dicttable.mTranslate = dicttable.mTranslateNumeric endif let dicttable.mapkind = a:mapkind unlet dicttable.mTranslateNumeric unlet dicttable.mTranslateAlpha return dicttable endfunction function! s:TranslateMap.mTranslateNumeric(value) dict let index = 0 let retval = "" " remember to deal with multi-byte characters while index < len(a:value) let char1 = char2nr(a:value[index]) if has_key(self.mappings, char1) let newmap = self.mappings[char1] else " take only the first character let newmap = a:value[index] endif let retval .= newmap let index += 1 endwhile return retval endfunction function! s:TranslateMap.mTranslateAlpha(value) dict let retval = substitute(a:value, self.regex, '\=self.mappings[submatch(1)]', "g") return retval endfunction function! s:CCTreeGetXRefDbMaps(maptype, mapkind) let dichar1 = ",0123456789" let dichar2 = ",0123456789" return s:CCTreeCreateGenericMaps(a:maptype, a:mapkind, dichar1, dichar2) endfunction function! s:CCTreeGetCscopeMaps(maptype, mapkind) let dichar1 = " teisaprnl(of)=c" let dichar2 = " tnerpla" return s:CCTreeCreateGenericMaps(a:maptype, a:mapkind, dichar1, dichar2) endfunction function! s:CCTreeCreateGenericMaps(maptype, mapkind, dichar1, dichar2) let s:CharMaps.mapkind = a:mapkind call s:CharMaps.mInitTranslator() if a:mapkind == 'Numeric' let ab = map(range(128,255), 'v:val') elseif a:mapkind == 'Alpha' let ab = map(range(128,255), 'nr2char(v:val)') else return {} endif let ac = s:CharMaps.CrossProduct(a:dichar1, a:dichar2) if a:maptype == 'Compress' let maps = s:TranslateMap.mCreate(ac, ab, a:mapkind, \'\(['.a:dichar1.']['.a:dichar2.']\)\C') elseif a:maptype == 'Uncompress' let maps = s:TranslateMap.mCreate(ab, ac, a:mapkind, \'\([\d128-\d255]\)') endif call s:CharMaps.mDoneTranslator() return maps endfunction " }}} " {{{ Unique list filter object let s:UniqList = {} function! s:UniqList.mFilterEntries(lstval) dict let valdict = {} let reslist = '' for aval in a:lstval if !has_key(valdict, aval) let valdict[aval] = '' let reslist .= (aval . ",") endif endfor return reslist endfunction let s:CCTreeUniqListFilter = deepcopy(s:UniqList) function! s:CCTreeMakeCommaListUnique(clist) let entries = split(a:clist, ",") if len(entries) > 0 return s:CCTreeUniqListFilter.mFilterEntries(entries) endif return "" endfunction " }}} " {{{ Buffer/Window func! s:FindOpenBuffer(filename) let bnrHigh = bufnr("$") "tabpagebuflist(tabpagenr()) for bufnrs in range(1, bnrHigh) if (bufexists(bufnrs) == 1 && bufname(bufnrs) == a:filename && bufloaded(bufnrs) != 0 ) return bufnrs endif endfor " Could not find the buffer return 0 endfunction func! s:FindOpenWindow(filename) let bufnr = s:FindOpenBuffer(a:filename) if (bufnr > 0) let newWinnr = bufwinnr(bufnr) if newWinnr != -1 exec newWinnr.'wincmd w' return 1 endif endif " Could not find the buffer return 0 endfunction " }}} " {{{ Utils library let s:Utils = {} " Use this function to determine the correct "g" flag " for substitution function! s:Utils.mGetSearchFlag(gvalue) let ret = (!a:gvalue)* (&gdefault) + (!&gdefault)*(a:gvalue) if ret == 1 return 'g' endif return '' endfunc " Strlen works for multibyte characters function! s:Utils.mStrlenEx(val) return strlen(substitute(a:val, ".", "x", "g")) endfunc " }}} " {{{ Generic db loader interface let s:GenericDbLdr = { \ 'fDBName' : '', \ 'class' : 'Generic', \ } function! s:GenericDbLdr.mCreate(fname) dict let gdb = deepcopy(s:GenericDbLdr) unlet gdb.mCreate let gdb.fDBName = a:fname if !filereadable(a:fname) return s:CCTreeRC.Error endif return gdb endfunction function! s:GenericDbLdr.mParseDbHeader(gRdr) let header = readfile(self.fDBName, "", a:gRdr.headerLines) return a:gRdr.mParseDbHeader(header) endfunction let s:XRefMemDbLdr = { \ 'class' : s:DBStorage.memory \} function! s:XRefMemDbLdr.mCreate(fname) dict let gdb = s:GenericDbLdr.mCreate(a:fname) if type(gdb) != type({}) return gdb endif let mdb = extend(gdb, deepcopy(s:XRefMemDbLdr)) unlet mdb.mCreate return mdb endfunction if has('perl') && g:CCTreeUsePerl == 1 " Perl function function! s:XRefMemDbLdr.mLoadFileIntoXRefDb(xRefDb, gRdr) dict let stage = 1 for afltr in a:gRdr.opts let stageidxstr = 'Stage ('.stage.'/'.len(a:gRdr.opts).') ' call s:StatusLine.mSetInfo(stageidxstr. ': (PERL) Loading database ') call a:gRdr.mProcessingStateInit() let pBar = s:ProgressBarNumeric.mCreate(getfsize(self.fDBName), "bytes") echomsg 'filtering '. afltr perl << PERL_EOF #use strict; #use warnings FATAL => 'all'; #use warnings NONFATAL => 'redefine'; my $filebytes = 0; my $filterpat = VIM::Eval("afltr"); open (CSCOPEDB, VIM::Eval("self.fDBName")) or die "File trouble!"; #VIM::DoCommand("echomsg '".$filterpat."'"); while (<CSCOPEDB>) { $filebytes += length($_); chomp($_); if ($_ !~ $filterpat) { next; } VIM::DoCommand("call pBar.mTick(".$filebytes.")"); $filebytes = 0; VIM::DoCommand("call a:gRdr.mProcessSymbol(a:xRefDb, '".$_."')"); } VIM::DoCommand("call pBar.mDone()"); close(CSCOPEDB); PERL_EOF call a:gRdr.mProcessingStateDone() let stage += 1 endfor endfunction else " Native Vim function function! s:XRefMemDbLdr.mLoadFileIntoXRefDb(xRefDb, gRdr) dict let vDbFile = s:vFile.mCreate(self.fDBName, "r") if vDbFile.valid == 0 return -1 endif if vDbFile.mIsLargeFile() == 1 call s:StatusLine.mSetExtraInfo('Database ' \.' >'.g:CCTreeDbFileMaxSize .' bytes. Splitting '. \'into smaller chunks... (this may take some time)') endif try if vDbFile.mOpen() == 0 call self.mReadFileIntoXRefDb(vDbFile, \ a:xRefDb, \ a:gRdr) endif finally call vDbFile.mClose() endtry endfunction endif function! s:XRefMemDbLdr.mReadFileIntoXRefDb(vDbFile, xrefdb, gRdr) let stage = 0 for afltr in a:gRdr.opts call a:vDbFile.mRewind() let stage += 1 call a:gRdr.mProcessingStateInit() while 1 == 1 if a:vDbFile.mRead() == -1 break endif let stageidxstr = 'Stage ('.stage.'/'.len(a:gRdr.opts).') ' let fileidxstr = '('.a:vDbFile.currentSplitIdx.'/'.a:vDbFile.totSplits.') ' call s:StatusLine.mSetInfo(stageidxstr. ': Reading database chunk '.fileidxstr) " Filter-out lines that doesn't have relevant information let plist = a:gRdr.mReadLinesFromFile(a:vDbFile, afltr) let pBar = s:ProgressBarNumeric.mCreate(len(plist), "items") call s:StatusLine.mSetInfo(stageidxstr.': Analyzing database chunk '.fileidxstr) call self.mProcessListIntoXrefDb(plist, a:gRdr, a:xrefdb, pBar) call pBar.mDone() " clean-up memory call garbagecollect() endwhile call a:gRdr.mProcessingStateDone() endfor endfunction function! s:XRefMemDbLdr.mProcessListIntoXrefDb(symbols, rdr, xrefdb, pbar) for a in a:symbols call a:pbar.mTick(1) call a:rdr.mProcessSymbol(a:xrefdb, a) endfor endfunction function! s:GenericDbLdr.mParseDbHeader(gRdr) let header = readfile(self.fDBName, "", a:gRdr.headerLines) return a:gRdr.mParseDbHeader(header) endfunction " }}} " {{{ Generic Disk DB Ldr let s:XRefDiskDbLdr = { \ 'class' : s:DBStorage.disk \ } function! s:XRefDiskDbLdr.mCreate(fname) dict let gdb = s:GenericDbLdr.mCreate(a:fname) if type(gdb) != type({}) return gdb endif let mdb = extend(gdb, deepcopy(s:XRefDiskDbLdr)) unlet mdb.mCreate return mdb endfunction function! s:XRefDiskDbLdr.mLoadFileIntoXRefDb(xRefDb, gRdr) dict call a:xRefDb.mSetLink(self.fDBName) endfunction "}}} " {{{ Xref disk DB let s:XRefDiskDb = { \ 'link':'', \ 'savedTags': '', \ 'class' : s:DBStorage.disk \ } function! s:XRefDiskDb.mCreate() dict let fdb = deepcopy(s:XRefDiskDb) unlet fdb.mCreate let fdb.maps = s:CCTreeGetXRefDbMaps('Uncompress', 'Numeric') return fdb endfunction function! s:XRefDiskDb.mSetLink(filedb) dict let self.link = a:filedb " revisit, do parse header here endfunction function! s:XRefDiskDb.mClear() dict " do nothing endfunction function! s:XRefDiskDb.mInitState() dict let self.savedTags = &tags let &tags = self.link endfunction function! s:XRefDiskDb.mRestoreState() dict let &tags = self.savedTags endfunction function! s:XRefDiskDb.mDecodeTagEntry(tagentry) dict let itms = split(a:tagentry.name, "#") let a:tagentry.n = itms[1] let a:tagentry.idx = itms[0] " Vim taglist() drops empty fields, so need to protect if has_key(a:tagentry, 'c') let a:tagentry.c = self.maps.mTranslate(a:tagentry.c) else let a:tagentry.c = '' endif if has_key(a:tagentry, 'p') let a:tagentry.p = self.maps.mTranslate(a:tagentry.p) else let a:tagentry.p = '' endif return a:tagentry endfunction function! s:XRefDiskDb.mGetSymbolIdFromName(symname) dict let symtagline = taglist('\#'.a:symname.'$') let g:xyz = symtagline let asym = self.mDecodeTagEntry(symtagline[0]) return asym.idx endfunction function! s:XRefDiskDb.mGetSymbolFromId(symid) dict let symtagline = taglist('^'.a:symid.'\#') if empty(symtagline) echomsg "Failed to locate ".a:symid else return self.mDecodeTagEntry(symtagline[0]) endif return {} endfunction function! s:XRefDiskDb.mGetSymbolIds() dict " illegal let symtaglines = taglist('^.') return keys(self.symidhash) endfunction function! s:XRefDiskDb.mGetSymbolNames(lead) dict if empty(a:lead) let symtaglines = taglist('^.') else let symtaglines = taglist('#'.a:lead) endif let alist = [] for atag in symtaglines let acctreesym = self.mDecodeTagEntry(atag) call add(alist, acctreesym.n) endfor return alist endfunction " }}} " {{{ TagFile utils let s:CCTreeTagDbRdr = {'class': 'CCTreeXrefDb', \ 'headerLines' : 4, \ 'compressed' : 0, \ 'opts': ['v:val !~ "^[\!]"'], \ 'perl_opts': "^[^\!]", \ 'mapPreKeys': {'c':'','p':''}, \ 'mapPostKeys': {'c':'','p':''} \ } function! s:CCTreeTagDbRdr.mCreate(fname) dict let cctxdbrdr = deepcopy(s:CCTreeTagDbRdr) unlet cctxdbrdr.mCreate return cctxdbrdr endfunction function! s:CCTreeTagDbRdr.mRequirePreProcessing() dict return s:CCTreeRC.False endfunction function! s:CCTreeTagDbRdr.mRequirePostProcessing() dict return s:CCTreeRC.True endfunction function! s:CCTreeTagDbRdr.mRequireCleanup() dict " Clean-up all symbols [never] return s:CCTreeRC.False endfunction function! s:CCTreeTagDbRdr.mGetPreProcessingMaps() dict return s:CCTreeGetXRefDbMaps('Compress', 'Alpha') endfunction function! s:CCTreeTagDbRdr.mGetPostProcessingMaps() dict return s:CCTreeGetXRefDbMaps('Uncompress', 'Alpha') endfunction function! s:CCTreeTagDbRdr.mParseDbHeader(hdr) dict " just check line 3 for sanity if a:hdr[2] =~ "CCTree" return s:CCTreeRC.Success endif return s:CCTreeRC.Error endfunction function! s:CCTreeTagDbRdr.mProcessingStateInit() dict endfunction function! s:CCTreeTagDbRdr.mProcessingStateDone() dict endfunction function! s:CCTreeTagDbRdr.mReadLinesFromFile(vdbFile, filtercmds) dict " Hard-coded assumptions here about format for performance if empty(get(a:vdbFile.lines, 0)) != 1 && a:vdbFile.lines[0][0] == "!" " filter out the first few lines starting with "!" call remove(a:vdbFile.lines, 0, self.headerLines-1) endif return a:vdbFile.lines endfunction function! s:CCTreeTagDbRdr.mProcessSymbol(xrefdb, aline) dict let cctreesym = self.mDecodeTagLine(a:aline) call a:xrefdb.mInsertSym(cctreesym.idx, cctreesym) " we really don't need idx any longer unlet cctreesym.idx endfunction function! s:CCTreeTagDbRdr.mDecodeTagLine(tagline) dict let items = split(a:tagline, "\t") let newsym = s:CCTreeSym.mCreate("") try let [newsym.idx, newsym.n] = split(items[0], '#') catch echomsg "problem decoding ". a:tagline endtry "let newsym.idx = strpart(items[0], 0, idxBr) "let newsym.n = items[0][ idxBr+1 : -2] "strpart(items[0], idxBr+1, strlen(items[0])-1) if empty(get(items, 3)) != 1 let newsym.c = items[3][2:] endif if empty(get(items, 4)) != 1 let newsym.p = items[4][2:] endif return newsym endfunction " }}} " {{{ Generic Db Serializer let s:GenericDbSerializer = {} function! s:GenericDbSerializer.mCreate(xrefdb) dict let gDbSerializer = deepcopy(s:GenericDbSerializer) let gDbSerializer.xrefdb = a:xrefdb return gDbSerializer endfunction function! s:GenericDbSerializer.mWriteXRefDbToFile(fname, \ gWriter) dict call s:StatusLine.mInit() try call s:StatusLine.mSetInfo('Writing XRefDb') let vDbFile = s:vFile.mCreate(a:fname, "w") call vDbFile.mWriteList(a:gWriter.mBuildHeader()) call self.mWriteSymsToFile(vDbFile, a:gWriter) finally call vDbFile.mClose() call s:StatusLine.mRestore() endtry endfunction function! s:GenericDbSerializer.mWriteSymsToFile(dstVFile, \ gWriter) dict let pBar = s:ProgressBarNumeric.mCreate(self.xrefdb.mGetSymbolCount(), \ "items") call a:gWriter.mInitWriting() " write syms for asymid in sort(self.xrefdb.mGetSymbolIds()) let acctreesym = self.xrefdb.mGetSymbolFromId(asymid) call a:dstVFile.mWriteLine(a:gWriter.mBuildTagLine(acctreesym, \ asymid)) call pBar.mTick(1) endfor call pBar.mDone() call a:gWriter.mDoneWriting() endfunction " }}} " {{{ CCTreeTagDb Writer let s:CCTreeTagDbWriter = {} function! s:CCTreeTagDbWriter.mCreate(tmaps) dict let dbwriter = deepcopy(s:CCTreeTagDbWriter) unlet dbwriter.mCreate let dbwriter.tmaps = a:tmaps return dbwriter endfunction function! s:CCTreeTagDbWriter.mInitWriting() dict call self.tmaps.mInitTranslator() endfunction function! s:CCTreeTagDbWriter.mDoneWriting() dict call self.tmaps.mDoneTranslator() endfunction function! s:CCTreeTagDbWriter.mBuildHeader() dict let hdr = [] call add(hdr, "!_TAG_FILE_FORMAT\t2\t/extended format; --format=1 will not append ;\" to lines/") call add(hdr, "!_TAG_FILE_SORTED\t1\t/0=unsorted, 1=sorted, 2=foldcase/") call add(hdr, "!_TAG_PROGRAM_NAME\t\tCCTree (Vim plugin)//") call add(hdr, "!_TAG_PROGRAM_URL\thttp://vim.sourceforge.net/scripts/script.php?script_id=2368\t/site/") return hdr endfunction function! s:CCTreeTagDbWriter.mBuildTagLine(sym, symid) dict let basetag = a:symid .'#'. a:sym.n."\t"."\t"."/^\$/".";\"" let cm = self.tmaps.mTranslate(a:sym.c) let pm = self.tmaps.mTranslate(a:sym.p) let basetag .= "\tc:". self.tmaps.mTranslate(a:sym.c) let basetag .= "\tp:". self.tmaps.mTranslate(a:sym.p) return basetag endfunction " }}} " {{{ CCTree constants let s:CCTreeRC = { \ 'Error' : -1, \ 'True' : 1, \ 'False' : 0, \ 'Success' : 2 \ } "}}} " {{{ CCTree DB Obj " Symbol definition let s:CCTreeSym = { \'k': "", \'n': "", \'c': "", \'p': "" \} function! s:CCTreeSym.mCreate(name, kind) let sym = deepcopy(s:CCTreeSym) unlet sym.mCreate let sym.n = a:name let sym.k = a:kind return sym endfunction " }}} " {{{ GenericXref, XrefDb let s:GenericXRef = {} function! s:GenericXRef.mCreate(filedb) dict let gxref = deepcopy(s:GenericXRef) return gxref endfunction function! s:GenericXRef.mInitState() dict endfunction function! s:GenericXRef.mRestoreState() dict endfunction " {{{ XRef Database object let s:xRefMemDb = { \ 'symuniqid': 0, \ 'symidhash' : {}, \ 'symnamehash' : {}, \ 'class' : s:DBStorage.memory \} function s:xRefMemDb.mCreate() dict let dbObj = deepcopy(s:xRefMemDb) unlet dbObj.mCreate return dbObj endfunction function s:xRefMemDb.mInitState() dict endfunction function s:xRefMemDb.mRestoreState() dict endfunction function s:xRefMemDb.mClear() dict let self.symidhash = {} let self.symnamehash = {} let self.symuniqid = 0 endfunction function! s:xRefMemDb.mInsertSym(idx, cctreesym) dict let self.symuniqid = max([self.symuniqid, a:idx]) let self.symidhash[a:idx] = a:cctreesym let self.symnamehash[a:cctreesym.n] = a:idx endfunction function! s:xRefMemDb.mRemoveSymById(symidx) dict call self.mRemoveSymByName(acctreesym.n) call remove(self.symidhash, a:symidx) endfunction function! s:xRefMemDb.mRemoveSymByName(symname) dict call remove(self.symnamehash, a:symname) endfunction function! s:xRefMemDb.mAddSym(name, kind) dict if !has_key(self.symnamehash, a:name) let self.symnamehash[a:name] = self.symuniqid let self.symidhash[self.symuniqid] = \s:CCTreeSym.mCreate(a:name, a:kind) let self.symuniqid += 1 endif let asymid = self.symnamehash[a:name] if a:kind != "" let asym = self.symidhash[asymid] let asym.k = a:kind endif return asymid endfunction function! s:xRefMemDb.mMarkXRefSyms(funcentryidx, newfuncidx) dict let self.symidhash[a:funcentryidx]['c'] .= (",". a:newfuncidx) let self.symidhash[a:newfuncidx]['p'] .= (",". a:funcentryidx) endfunction function! s:xRefMemDb.mGetSymbolFromName(symname) dict return self.symidhash[self.symnamehash[a:symname]] endfunction function! s:xRefMemDb.mGetSymbolIdFromName(symname) dict if has_key(self.symnamehash, a:symname) return self.symnamehash[a:symname] else return s:CCTreeRC.Error endif endfunction function! s:xRefMemDb.mGetSymbolFromId(symid) dict return self.symidhash[a:symid] endfunction function! s:xRefMemDb.mGetSymbolIds() dict return keys(self.symidhash) endfunction function! s:xRefMemDb.mGetSymbolNames(lead) dict let syms = keys(self.symnamehash) if empty(a:lead) != 1 return filter(syms, 'v:val =~? a:lead') endif return syms endfunction function! s:xRefMemDb.mGetSymbolCount() dict return len(self.symnamehash) endfunction function! s:xRefMemDb.mTranslateSymbols(map, tkeys) dict call a:map.mInitTranslator() let pBar = s:ProgressBarNumeric.mCreate(len(self.symnamehash), "items") for asym in keys(self.symnamehash) let idx = self.symnamehash[asym] let val = self.symidhash[idx] if has_key(a:tkeys, 'n') let uncmpname = a:map.mTranslate(asym) if (asym != uncmpname) "Set up new entry let self.symnamehash[uncmpname] = idx " free the old entry call remove(self.symnamehash, asym) " Set uncompressed name let val.n = uncmpname endif endif if has_key(a:tkeys, 'p') let val.p = a:map.mTranslate(val.p) endif if has_key(a:tkeys, 'c') let val.c = a:map.mTranslate(val.c) endif call pBar.mTick(1) endfor call pBar.mDone() call a:map.mDoneTranslator() endfunction function! s:xRefMemDb.mCleanSymbols () dict let pBar = s:ProgressBarNumeric.mCreate(len(self.symnamehash), "items") for asym in keys(self.symnamehash) let idx = self.symnamehash[asym] let val = self.symidhash[idx] if empty(val.p) && empty(val.c) call remove(self.symnamehash, asym) call remove(self.symidhash, idx) else let val.p = s:CCTreeMakeCommaListUnique(val.p) let val.c = s:CCTreeMakeCommaListUnique(val.c) endif call pBar.mTick(1) endfor call pBar.mDone() endfunction "}}} "}}} End of Xref " {{{ Tracer let s:CallTree = { \ 'symbol' : "" \ } function! s:CallTree.mCreate(name) dict let ct = deepcopy(s:CallTree) unlet ct.mCreate let ct.symbol = a:name return ct endfunction function! s:CallTree.mAddChildLink(childTree) dict if !has_key(self, 'childlinks') let self.childlinks = [] endif call add(self.childlinks, a:childTree) endfunction let s:XRefTracer = { \} function! s:XRefTracer.mCreate(xrefdb) dict let xreftracer = deepcopy(s:XRefTracer) let xreftracer.xrefdb = a:xrefdb return xreftracer endfunction function! s:XRefTracer.mInitTracing() dict call self.xrefdb.mInitState() endfunction function! s:XRefTracer.mDoneTracing() dict call self.xrefdb.mRestoreState() endfunction function! s:XRefTracer.mGetSymbolIdXRef(symid, direction) dict let acctreesym = self.xrefdb.mGetSymbolFromId(a:symid) let symidslist = split( \s:CCTreeMakeCommaListUnique(acctreesym[a:direction]), ",") return symidslist endfunction function! s:XRefTracer.mBuildForSymbol(symid, curdepth, maxdepth, \ direction, pbar) dict if (a:curdepth > a:maxdepth) return {} endif call a:pbar.mSetDepth(a:curdepth) let asym = self.xrefdb.mGetSymbolFromId(a:symid) " revisit if empty(asym) return {} endif let rtree = s:CallTree.mCreate(asym['n']) for entry in self.mGetSymbolIdXRef(a:symid, a:direction) call a:pbar.mTick(1) let ctree = \self.mBuildForSymbol(entry, a:curdepth+1, a:maxdepth, \a:direction, a:pbar) call rtree.mAddChildLink(ctree) endfor return rtree endfunction " }}} " {{{ Cscope Reader let s:CscopeDbRdrState = { \'curfuncidx': -1, \'curfileidx': -1, \'curmacroidx': -1, \'curenumidx': -1, \ } function! s:CscopeDbRdrState.mCreate() dict return deepcopy(s:CscopeDbRdrState) endfunction let s:CscopeDbRdrSymTags = { \'func' : '$}', \'macro' : '#\)', \'file' : '@\~', \'enum' : 'em', \'global' : 'g', \'typedef' : 't', \} let s:CscopeDbSymFilter = ['v:val =~ "^\t[#`$}@\~\)]"'] let s:CscopeDbSymEnhFilter = ['v:val =~ "^\t[emgt#`$}@~)]"', \ 'v:val =~ "^\\a\\|^\t[)$}#]"'] let s:CscopeDbSymFilterPerl = ['^\t[\`\#\$\}\@\~\)]'] let s:CscopeDbSymEnhFilterPerl = ['^\t[\`\#\$\}\@\~\)emgt]', \ '^[A-Za-z]|^\t[\#\$\}\)]'] let s:CscopeDbRdr = { \ 'class': 'Cscope', \ 'headerLines' : 1, \ 'compressed' : 0, \ 'opts': [], \ 'perl_opts': '', \ 'mapPreKeys': {'n':''}, \ 'mapPostKeys': {'n':''} \} function! s:CscopeDbRdr.mCreate(fname, enhanced) dict let csdbrdr = deepcopy(s:CscopeDbRdr) unlet csdbrdr.mCreate if a:enhanced == 1 if g:CCTreeUsePerl == 1 let csdbrdr.opts = s:CscopeDbSymEnhFilterPerl else let csdbrdr.opts = s:CscopeDbSymEnhFilter endif else if g:CCTreeUsePerl == 1 let csdbrdr.opts = s:CscopeDbSymFilterPerl else let csdbrdr.opts = s:CscopeDbSymFilter endif endif return csdbrdr endfunction function! s:CscopeDbRdr.mProcessingStateInit() dict let self.iState = s:CscopeDbRdrState.mCreate() endfunction function! s:CscopeDbRdr.mProcessingStateDone() dict " discard state unlet self.iState endfunction function! s:CscopeDbRdr.mReadLinesFromFile(vDbFile, filtercmds) dict return s:CCTreeUtils.mFilter(a:vDbFile.lines, a:filtercmds) endfunction function! s:CscopeDbRdr.mParseDbHeader(dbHeader) dict if a:dbHeader[0] =~ "cscope" if (a:dbHeader[0] !~ "cscope.*\-c") let self.compressed = s:CCTreeRC.True else let self.compressed = s:CCTreeRC.False endif return s:CCTreeRC.Success endif return s:CCTreeRC.Error endfunction function! s:CscopeDbRdr.mRequirePreProcessing() dict return (self.compressed == 1)? s:CCTreeRC.True : s:CCTreeRC.False endfunction function! s:CscopeDbRdr.mRequirePostProcessing() dict return (self.compressed == 1)? s:CCTreeRC.True : s:CCTreeRC.False endfunction function! s:CscopeDbRdr.mRequireCleanup() dict " Clean-up all symbols [always] return s:CCTreeRC.True endfunction function! s:CscopeDbRdr.mGetPreProcessingMaps() dict return s:CCTreeGetCscopeMaps('Compress', 'Alpha') endfunction function! s:CscopeDbRdr.mGetPostProcessingMaps() dict return s:CCTreeGetCscopeMaps('Uncompress', 'Alpha') endfunction function! s:CscopeDbRdr.mProcessSymbol(xrefdb, symbol) dict try if a:symbol[0] == "\t" return self.mProcessTaggedSymbol(a:xrefdb, a:symbol) else return self.mProcessUnTaggedSymbol(a:xrefdb, a:symbol) endif catch echomsg 'Problem with '. a:symbol endtry endfunction function! s:CscopeDbRdr.mProcessUnTaggedSymbol(xrefdb, symbol) dict let cursymidx = a:xrefdb.mGetSymbolIdFromName(a:symbol) if cursymidx != s:CCTreeRC.Error if self.iState.curfuncidx != -1 call a:xrefdb.mMarkXRefSyms(self.iState.curfuncidx, cursymidx) elseif self.iState.curmacroidx != -1 call a:xrefdb.mMarkXRefSyms(self.iState.curmacroidx, cursymidx) endif endif endfunction function! s:CscopeDbRdr.mProcessTaggedSymbol(xrefdb, symbol) dict if self.iState.curmacroidx != -1 if a:symbol[1] == "`" call a:xrefdb.mMarkXRefSyms(self.iState.curmacroidx, \ a:xrefdb.mAddSym(a:symbol[2:], "")) elseif a:symbol[1] == ')' let self.iState.curmacroidx = -1 endif elseif self.iState.curfuncidx != -1 " inside function if a:symbol[1] == "`" call a:xrefdb.mMarkXRefSyms(self.iState.curfuncidx, \ a:xrefdb.mAddSym(a:symbol[2:], "")) elseif a:symbol[1] == "}" let self.iState.curfuncidx = -1 elseif a:symbol[1] == "#" let self.iState.curmacroidx = a:xrefdb.mAddSym(a:symbol[2:], 'm') endif elseif self.iState.curenumidx != -1 if a:symbol[1] == "m" call a:xrefdb.mMarkXRefSyms(self.iState.curenumidx, \ a:xrefdb.mAddSym(a:symbol[2:], "em")) else " just reprocess the symbol after changing state let self.iState.curenumidx = -1 call self.mProcessTaggedSymbol(a:xrefdb, a:symbol) endif elseif a:symbol[1] == "$" let self.iState.curfuncidx = a:xrefdb.mAddSym(a:symbol[2:], "f") elseif a:symbol[1] == "#" let self.iState.curmacroidx = a:xrefdb.mAddSym(a:symbol[2:], "d") elseif a:symbol[1] == "~" call a:xrefdb.mMarkXRefSyms(self.iState.curfileidx, \a:xrefdb.mAddSym(a:symbol[3:], "i")) elseif a:symbol[1] == "e" let self.iState.curenumidx = a:xrefdb.mAddSym(a:symbol[2:], "e") elseif a:symbol[1] == "g" call a:xrefdb.mAddSym(a:symbol[2:], "g") elseif a:symbol[1] == "@" if a:symbol[2] != "" let self.iState.curfileidx = \a:xrefdb.mAddSym(a:symbol[2:], "F") endif endif endfunction " }}} " {{{ CCTree helper library let s:CCTreeUtils = {} function! s:CCTreeUtils.mDetectDB(class) if a:class == s:DBClasses.cctreexref if filereadable(g:CCTreeDb) return g:CCTreeDb endif elseif a:class == s:DBClasses.cscopeid if filereadable(g:CCTreeCscopeDb) return g:CCTreeCscopeDb endif endif return '' endfunction function! s:CCTreeUtils.mFilter(lines, filtercmd) dict let retlst = [] let progr = len(a:lines)/100 let pBar = s:ProgressBarNumeric.mCreate(len(a:lines), "items") while len(a:lines) > 0 if progr <= len(a:lines) let tmplist = remove(a:lines, 0, progr) else let tmplist = remove(a:lines, 0, len(a:lines)-1) endif call filter(tmplist, a:filtercmd) call pBar.mTick(progr) call extend(retlst, tmplist) endwhile call pBar.mDone() return retlst endfunction function! s:CCTreeUtils.mWarningPrompt(msg) dict echohl WarningMsg let a = input(s:pluginname. ": ". a:msg) echohl None endfunction function! s:CCTreeUtils.mWarningMsg(msg) dict echohl WarningMsg echomsg s:pluginname. ": ". a:msg echohl None endfunction function! s:CCTreeUtils.mInfoMsg(msg) dict echohl Title echomsg s:pluginname. ": ". a:msg echohl None endfunction function! s:CCTreeUtils.mWrite(msg) dict echo s:pluginname. ": ". a:msg endfunction " }}} " {{{ CCTree DB management let s:CCTreeXrefDbEntry = { \ 'type': '', \ 'fname' : '', \ 'fsize' : 0, \ 'fdate' : 0 \} function! s:CCTreeXrefDbEntry.mCreate(fname, type) dict let xrefdbent = deepcopy(s:CCTreeXrefDbEntry) unlet xrefdbent.mCreate let xrefdbent.type = a:type let xrefdbent.fname = simplify(getcwd().'/'.a:fname) let xrefdbent.fsize = getfsize(a:fname) let xrefdbent.fdate = strftime("%c", getftime(a:fname)) return xrefdbent endfunction let s:CCTreeDBList = { \'loadedDBs' : [] \ } function! s:CCTreeDBList.mCreate() dict let dbList = deepcopy(s:CCTreeDBList) unlet dbList.mCreate return dbList endfunction function! s:CCTreeDBList.mShowLoaded() dict let i = 1 call s:CCTreeUtils.mWrite(s:pluginname.": List of loaded cscope databases") call s:CCTreeUtils.mWrite("---------------------------------------") for aDBEnt in self.loadedDBs call s:CCTreeUtils.mWrite(i." ".aDBEnt.fname. " ". \ " (".aDBEnt.type.") ". \ aDBEnt.fsize. " bytes ". \ aDBEnt.fdate) let i = i + 1 endfor endfunction function! s:CCTreeDBList.mClearAll() dict let self.loadedDBs = [] endfunction function! s:CCTreeDBList.mIsEmpty() dict if empty(self.loadedDBs) return s:CCTreeRC.True endif return s:CCTreeRC.False endfunction " Load the cscope db into the global cctree xref db function! s:CCTreeDBList.mCreateDbLoaderAndReader(dbName, dbclass, storageclass) dict let dbUser = s:CCTreeCmdLine.mInputDBName('Load', a:dbName, a:dbclass) if dbUser == '' call s:CCTreeUtils.mWarningMsg('Filename required') "User cancel, do nothing return endif " Create generic Db loader object if a:storageclass == s:DBStorage.disk let gDbLdr = s:XRefDiskDbLdr.mCreate(dbUser) elseif a:storageclass == s:DBStorage.memory let gDbLdr = s:XRefMemDbLdr.mCreate(dbUser) endif if type(gDbLdr) != type({}) call s:CCTreeUtils.mWarningMsg(a:dbclass.' database ' . a:dbName . \ ' not found.') return s:CCTreeRC.Error endif " Create new DB reader object if a:storageclass == s:DBStorage.memory if a:dbclass == s:DBClasses.cscopeid let gDbRdr = s:CscopeDbRdr.mCreate(dbUser, g:CCTreeEnhancedSymbolProcessing) elseif a:dbclass == s:DBClasses.cctreexref let gDbRdr = s:CCTreeTagDbRdr.mCreate(dbUser) else return s:CCTreeRC.Error endif if gDbLdr.mParseDbHeader(gDbRdr) == s:CCTreeRC.Error call s:CCTreeUtils.mWarningMsg(gDbRdr.class.' database ' . a:dbName . \ ' format is not parseable.') return s:CCTreeRC.Error endif else let gDbRdr = {} endif return {'loader': gDbLdr, 'reader': gDbRdr} endfunction function! s:CCTreeDBList.mAddDbToList(dbName, type) let aDBEnt = s:CCTreeXrefDbEntry.mCreate(a:dbName, a:type) call add(self.loadedDBs, aDBEnt) endfunction " Merge the cscope db into the global cctree xref db function! s:CCTreeDBList.mMerge(dbName, xRefDb, class) " Check if merge can be supported if self.loadedDBs[0].type == s:DBStorage.disk call s:CCTreeUtils.mInfoMsg("Cannot merge with DBs traced from disk") return endif " Create db loader, reader let gObjs = self.mCreateDbLoaderAndReader(a:dbName, a:class, s:DBStorage.memory) if type(gObjs) == type({}) " if Db is compressed, then we need to compress our symbols first let swatch = s:StopWatch.mCreate() if self.mLoadDB(gObjs.loader, a:xRefDb, \ gObjs.reader) != s:CCTreeRC.Error call self.mAddDbToList(gObjs.loader.fDBName, gObjs.loader.class) let msg = "Done merging databases. xRef Symbol Count: " \.a:xRefDb.mGetSymbolCount() \.". Time taken: ".swatch.mGetText()." secs" call s:CCTreeUtils.mInfoMsg(msg) endif " Load will auto decompress the symbols endif endfunction " Load the cscope db into the global cctree xref db function! s:CCTreeDBList.mAddNew(dbName, xRefDb, dbclass, storageclass) " Create db loader, reader let gObjs = self.mCreateDbLoaderAndReader(a:dbName, a:dbclass, a:storageclass) if type(gObjs) == type({}) let swatch = s:StopWatch.mCreate() if self.mLoadDB(gObjs.loader, a:xRefDb, \ gObjs.reader) != s:CCTreeRC.Error call self.mAddDbToList(gObjs.loader.fDBName, gObjs.loader.class) call swatch.mSnapElapsed() if a:storageclass == s:DBStorage.memory let msg = "Done loading database. xRef Symbol Count: " \.a:xRefDb.mGetSymbolCount() \.". Time taken: ".swatch.mGetText()." secs" else let msg = "Disk Xref database loaded for tracing" endif call s:CCTreeUtils.mInfoMsg(msg) endif endif endfunction function! s:CCTreeDBList.mLoadDB(gDbLdr, xRefDb, gRdr) let rc = s:CCTreeRC.Success try let swatch = s:StopWatch.mCreate() call s:StatusLine.mInit() " if compression, then we need to compress our symbols first if !empty(a:gRdr) && a:gRdr.mRequirePreProcessing() == s:CCTreeRC.True call s:StatusLine.mSetInfo('Pre-processing existing symbols') call a:xRefDb.mTranslateSymbols(a:gRdr.mGetPreProcessingMaps(), \ a:gRdr.mapPreKeys) endif call garbagecollect() call s:StatusLine.mSetInfo('Loading database') call a:gDbLdr.mLoadFileIntoXRefDb(a:xRefDb, a:gRdr) if !empty(a:gRdr) && a:gRdr.mRequireCleanup() == s:CCTreeRC.True call s:StatusLine.mSetInfo('Symbol clean-up') call a:xRefDb.mCleanSymbols() endif call garbagecollect() if !empty(a:gRdr) && a:gRdr.mRequirePostProcessing() == s:CCTreeRC.True call s:StatusLine.mSetInfo('Post-processing loaded symbols') call a:xRefDb.mTranslateSymbols(a:gRdr.mGetPostProcessingMaps(), \ a:gRdr.mapPostKeys) endif call swatch.mSnapElapsed() " restore normalcy call garbagecollect() redraw catch /^Vim:Interrupt$/ " catch interrupts (CTRL-C) call s:CCTreeUtils.mWarningMsg('Loading aborted.') let rc = s:CCTreeRC.Error finally call s:StatusLine.mRestore() endtry return rc endfunction "}}} " {{{ UI Input related let s:CCTreeUI = {} function! s:CCTreeUI.mInputDBName(dbName, class, action) let dbUser = a:dbName let dbUser = input(a:action. ' database ('. a:class. '): ', a:dbName, 'file') return dbUser endfunction " }}} " {{{ CCTree Markers let s:TreeMarkers_UTF8 = { \ 'splitT' : nr2char(0x251c), \ 'arrowR' : nr2char(0x25c0), \ 'arrowF' : nr2char(0x25B6), \ 'extV' : nr2char(0x2502), \ 'extH': nr2char(0x2500), \ 'depth': nr2char(0x25BC) \} let s:TreeMarkers_Text = { \ 'splitT' : '+', \ 'arrowF' : '>', \ 'arrowR' : '<', \ 'extV' : '|', \ 'extH': '-', \ 'depth': 'depth:' \} let s:CCTreeMarkers = { \ 'icons':{} \ } function! s:CCTreeMarkers.mCreate() dict let treeMarkers = deepcopy(s:CCTreeMarkers) if &encoding == 'utf-8' && g:CCTreeUseUTF8Symbols == 1 let treeMarkers.icons = deepcopy(s:TreeMarkers_UTF8) else " default choice let treeMarkers.icons = deepcopy(s:TreeMarkers_Text) endif let treeMarkers.icons.arrowSyms = treeMarkers.icons.arrowF . treeMarkers.icons.arrowR let treeMarkers.icons.vertSyms = treeMarkers.icons.splitT . treeMarkers.icons.extV return treeMarkers endfunction function! s:CCTreeMarkers.mGetArrow(direction) dict if a:direction == 'p' return self.icons.arrowR elseif a:direction == 'c' return self.icons.arrowF endif return '?' endfunction " }}} " {{{ User key mappings let s:CCTreeKeyMappings = { \ 'CTreeF': g:CCTreeKeyTraceForwardTree, \ 'CTreeR': g:CCTreeKeyTraceReverseTree, \ 'CTreeHilight': g:CCTreeKeyHilightTree, \ 'CTreeWSave': g:CCTreeKeySaveWindow, \ 'CTreeWToggle': g:CCTreeKeyToggleWindow, \ 'CTreeCompress': g:CCTreeKeyCompressTree, \ 'CTreeDepthMinus': g:CCTreeKeyDepthMinus, \ 'CTreeDepthPlus': g:CCTreeKeyDepthPlus \} " }}} " {{{ CCTreeWindow let s:CCTreeWindow = { \ 'hiKeyword': '', \ 'hiKeywordLine':'', \ 'lastbufname':'', \ 'treeMarkers': s:CCTreeMarkers.mCreate()} function! s:CCTreeWindow.mCreate() dict let win = deepcopy(s:CCTreeWindow) unlet win.mCreate return win endfunction function! s:CCTreeWindow.mLeave() call s:FindOpenWindow(self.lastbufname) endfunction " Definition of a keyword... let s:CCTreeKeywordRegEx = '[A-Za-z0-9_\\\.\/]\+' function! s:CCTreeWindow.mGetKeywordAtCursor() dict let curline = line(".") let self.hiKeyword = '' if foldclosed(curline) == -1 let curkeyword = matchstr(getline("."), s:CCTreeKeywordRegEx) if curkeyword != '' if curkeyword != self.hiKeyword || curline != self.hiKeywordLine let self.hiKeyword = curkeyword let self.hiKeywordLine = line(".") return s:CCTreeRC.Success endif else return s:CCTreeRC.Error endif endif if self.hiKeyword == '' return s:CCTreeRC.Error endif return s:CCTreeRC.Success endfunction function! s:CCTreeWindow.mBuildStatusLine(pState, title, items) let needcomma = 0 let rtitle = a:title. ' ('. a:pState.keyword let rtitle .= '[' if has_key(a:items, "depth") let rtitle .= self.treeMarkers.icons.depth let rtitle .= a:pState.depth let needcomma = 1 endif if has_key(a:items, "direction") if needcomma == 1 let rtitle .= ',' endif let rtitle .= self.treeMarkers.mGetArrow(a:pState.direction) endif let rtitle .= '])' return rtitle endfunction function! CCTreeWindowPreviewStatusLine() " get global " this is a hack let pState = s:CCTreeGlobals.PreviewState let tMarkers = s:CCTreeGlobals.Window.treeMarkers return s:CCTreeGlobals.Window.mBuildStatusLine( \ s:CCTreeGlobals.PreviewState, \ s:windowtitle, \ {'depth':''} \) endfunction function! s:CCTreeWindow.mPreviewSave(savetitle) dict if s:FindOpenWindow(s:windowtitle) == 1 setlocal modifiable call self.mClearMarks(b:displayTree) setlocal nomodifiable setlocal statusline=%-F silent! exec ":f ". a:savetitle return s:CCTreeRC.Success endif return s:CCTreeRC.Error endfunction function! s:CCTreeWindow.mIsOpen() dict if s:FindOpenBuffer(s:windowtitle) > 0 return s:CCTreeRC.True endif return s:CCTreeRC.False endfunction function! s:CCTreeWindow.mClose() dict if s:FindOpenWindow(s:windowtitle) == 1 silent! q! endif endfunction function! s:CCTreeWindow.mDisplayToggle() dict if s:FindOpenWindow(s:windowtitle) == 1 silent! hide else let winbufnr = s:FindOpenBuffer(s:windowtitle) if winbufnr > 0 call self.mEnter() silent! exec "buf ".winbufnr call self.mResize() silent! wincmd p else call s:CCTreeUtils.mWarningMsg(" No active window found.") endif endif endfunction function! s:CCTreeWindow.mResize() dict if g:CCTreeWindowVertical == 1 if g:CCTreeWindowWidth == -1 exec "vertical resize ". b:maxwindowlen else exec "vertical resize ". g:CCTreeWindowWidth endif else if g:CCTreeWindowHeight != -1 let &winminheight = g:CCTreeWindowHeight exec "resize".g:CCTreeWindowHeight endif endif endfunction function! s:CCTreeWindow.mDisplayTree(atree, direction) dict let incctreewin = 1 if (bufname('%') != s:windowtitle) let incctreewin = self.mEnter() endif setlocal modifiable silent 1,$d let b:maxwindowlen = g:CCTreeWindowMinWidth let b:displayTree = s:DisplayTree.mCreate(a:atree, \ a:direction, self.treeMarkers) call s:CCTreeDisplay.mPopulateTreeInCurrentBuffer(b:displayTree) exec "normal gg" " Need to force this again let &l:foldlevel=g:CCTreeMinVisibleDepth setlocal nomodifiable call self.mResize() if (incctreewin == 0) call s:CCTreeWindow.mLeave() endif endfunction function! s:CCTreeWindow.mExtractTreeSymbols(dtree) let symlist = {} for aentry in a:dtree.entries let symlist[aentry.symbol] = 0 endfor return symlist endfunction function! s:CCTreeWindow.mEnter() dict let self.lastbufname = bufname("%") let foundWindow = s:FindOpenWindow(s:windowtitle) if foundWindow == 0 if g:CCTreeWindowVertical == 1 exec g:CCTreeOrientation." vsplit ". s:windowtitle set winfixwidth else exec g:CCTreeOrientation." split ". s:windowtitle set winfixheight endif setlocal buftype=nofile setlocal bufhidden=hide setlocal noswapfile setlocal nonumber setlocal nowrap setlocal nobuflisted if s:CCTreeUseConceal == 1 setlocal cole=3 setlocal cocu=nv endif setlocal statusline=%=%{CCTreeWindowPreviewStatusLine()} call self.mInitSyntax(self.treeMarkers.icons) let cpo_save = &cpoptions set cpoptions&vim call s:CCTreeBufferKeyMappingsCreate(s:CCTreeKeyMappings) command! -buffer -nargs=0 CCTreeWindowHiCallTree \ call s:CCTreeGlobals.mCursorHoldHandleEvent() exec 'nnoremap <buffer> <silent> '.s:CCTreeKeyMappings.CTreeHilight. \' :CCTreeWindowHiCallTree<CR>' exec 'nnoremap <buffer> <silent> '.s:CCTreeKeyMappings.CTreeCompress. \ ' :2,.foldclose!<CR>zv' nnoremap <buffer> <silent> <C-p> :CCTreePreviewBufferUsingTag<CR> nnoremap <buffer> <silent> <CR> :CCTreeLoadBufferUsingTag<CR> nnoremap <buffer> <silent> <2-LeftMouse> :CCTreeLoadBufferUsingTag<CR> let &cpoptions = cpo_save endif setlocal foldmethod=expr setlocal foldexpr=CCTreeFoldExpr(getline(v:lnum)) setlocal foldtext=CCTreeFoldText() let &l:foldlevel=g:CCTreeMinVisibleDepth return foundWindow endfunction " }}} " {{{ Dynamic call-tree highlighting using " syntax highlight tricks " " There are 3 types of lines, marked with the start character [\s, !, #] " Also @ is used to mark the path that is going up function! s:CCTreeWindow.mMarkCallTree(dtree, keyword) dict let declevel = -1 let treelst = a:dtree.entries let curLine = line(".") let declevel = treelst[curLine-1].level let targetlevel = declevel for idx in range(curLine, 1, -1) let aentry = treelst[idx-1] " Find our keyword let linemarker = 0 " Skip folds if declevel != -1 && foldclosed(idx) == -1 if targetlevel == aentry.level let linemarker = 1 let targetlevel -= 1 endif let aline = a:dtree.mGetNotationalTxt(aentry.level, targetlevel+1, linemarker, 1) \ . aentry.symbol call setline(idx, aline) endif endfor endfunction function! s:CCTreeWindow.mClearMarks(dtree) dict for idx in range(line(".")+1, line("$")) let breakout = (getline(idx)[0] !~ "[!#]") if breakout == 1 break endif let aentry = a:dtree.entries[idx-1] let aline = a:dtree.mGetNotationalTxt(aentry.level, -1, 0, 0) \ . aentry.symbol call setline(idx, aline) endfor endfunction function! s:CCTreeWindow.mInitSyntax(markers) dict "syntax match CCTreePathMark /\s[|+]/ contained exec 'syntax match CCTreePathMark /\s['. a:markers.vertSyms . ']/ contained' "syntax match CCTreeArrow /-*[<>]/ contained exec 'syntax match CCTreeArrow /'.a:markers.extH.'*['. a:markers.arrowSyms .']/ contained' syntax match CCTreeSymbol / [A-Za-z0-9_\.\\\/]\+/ contained syntax region CCTreeSymbolLine start="^\s" end="$" contains=CCTreeArrow,CCTreePathMark,CCTreeSymbol oneline "syntax match CCTreeHiArrow /-*[<>]/ contained exec 'syntax match CCTreeHiArrow /'. a:markers.extH .'*['. a:markers.arrowSyms .']/ contained' syntax match CCTreeHiSymbol / [A-Za-z0-9_\.\\\/]\+/ contained "syntax match CCTreeHiPathMark /\s[|+]/ contained exec 'syntax match CCTreeHiPathMark /\s[' . a:markers.vertSyms . ']/ contained' if s:CCTreeUseConceal == 1 syntax match CCTreeMarkExcl /^[!#]/ contained conceal syntax match CCTreeMarkTilde /@/ contained conceal else syntax match CCTreeMarkExcl /^[!#]/ contained syntax match CCTreeMarkTilde /@/ contained endif "syntax region CCTreeUpArrowBlock start="@" end=/[|+]/ contains=CCTreeMarkTilde contained oneline exec 'syntax region CCTreeUpArrowBlock start="@" end=/['. a:markers.vertSyms .']/ contains=CCTreeMarkTilde contained oneline' syntax region CCTreeHiSymbolLine start="!" end="$" contains=CCTreeMarkExcl, \ CCTreeUpArrowBlock, \ CCTreeHiSymbol,CCTreeHiArrow,CCTreeHiPathMark oneline syntax region CCTreeMarkedSymbolLine start="#" end="$" contains=CCTreeMarkExcl, \ CCTreeMarkTilde,CCTreePathMark, \ CCTreeArrow,CCTreeSymbol,CCTreeUpArrowBlock oneline endfunction " }}} " {{{ CCTreeDisplay let s:CCTreeDisplay = {} function! s:CCTreeDisplay.mPopulateTreeInCurrentBuffer(dtree) let linelist = [] for aentry in a:dtree.entries let aline = a:dtree.mGetNotationalTxt(aentry.level, -1, 0, 0) \ . aentry.symbol let len = s:Utils.mStrlenEx(aline) let b:maxwindowlen = max([len+1, b:maxwindowlen]) call add(linelist, aline) endfor call setline(".", linelist) endfunction " }}} " {{{ CCTree command line interface let s:CCTreeCmdLine = {} function! s:CCTreeCmdLine.mLoadDBFromDisk(dbName) dict call s:CCTreeGlobals.mUnLoadDBs() let s:CCTreeGlobals.XRefDb = s:XRefDiskDb.mCreate() call s:CCTreeGlobals.DbList.mAddNew(a:dbName, \ s:CCTreeGlobals.XRefDb, s:DBClasses.cctreexref, "Disk") endfunction " Unload current db's and load new one " There is no selective unloading function! s:CCTreeCmdLine.mLoadDB(db_name, class) dict call s:CCTreeGlobals.mSetupEncodingChangeAutoCmd(0) call s:CCTreeGlobals.mUnLoadDBs() let s:CCTreeGlobals.XRefDb = s:xRefMemDb.mCreate() call s:CCTreeGlobals.DbList.mAddNew(a:db_name, \ s:CCTreeGlobals.XRefDb, a:class, s:DBStorage.memory) call s:CCTreeGlobals.mSetupAutoCmds() endfunction function! s:CCTreeCmdLine.mInputDBName(action, dbName, class) dict if a:dbName == '' let dbUser = s:CCTreeUI.mInputDBName( \ s:CCTreeUtils.mDetectDB(a:class), \ a:class, a:action) else let dbUser = a:dbName endif return dbUser endfunction function! s:CCTreeCmdLine.mSaveDB(dbName, class) dict let dbUser = self.mInputDBName('Save', a:dbName, a:class) if dbUser == '' call s:CCTreeUtils.mWarningMsg('Filename required') return endif call s:CCTreeGlobals.Window.mClose() call s:CCTreeGlobals.mSetupEncodingChangeAutoCmd(0) call s:CCTreeGlobals.mWriteXRefDbToFile(dbUser) call s:CCTreeGlobals.mSetupAutoCmds() call s:CCTreeGlobals.mUpdateForCurrentSymbol() endfunction " Merge current db with new one function! s:CCTreeCmdLine.mMergeDB(db_name, class) dict "call s:CCTreeGlobals.Window.mClose() call s:CCTreeGlobals.DbList.mMerge(a:db_name, s:CCTreeGlobals.XRefDb, a:class) endfunction " }}} " {{{ CCTree Buffer mappings function! s:CCTreeWindowGetHiKeyword() let keyw = expand("<cword>") let keyf = expand("<cfile>") let syms = s:CCTreeGlobals.mGetPreviewTreeSymbols() if keyw != keyf if has_key(syms, keyf) return keyf elseif has_key(syms, keyw) return keyw endif else return keyw endif return '' endfunction " Keymappings used common to source files and CCTree window function! s:CCTreeBufferKeyMappingsCreate(kmaps) let func_expr = '<SNR>'.s:sid.'CCTreeWindowGetHiKeyword()' exec 'nnoremap <buffer> <silent> '.a:kmaps.CTreeR.' :CCTreeTraceReverse <C-R>='. \ func_expr.'<CR><CR>' exec 'nnoremap <buffer> <silent> '.a:kmaps.CTreeF.' :CCTreeTraceForward <C-R>=' \ .func_expr.'<CR><CR>' exec 'nnoremap <silent> '.a:kmaps.CTreeWSave. ' :CCTreeWindowSaveCopy<CR>' exec 'nnoremap <silent> '.a:kmaps.CTreeWToggle. ' :CCTreeWindowToggle<CR>' exec 'nnoremap <buffer> <silent> '.a:kmaps.CTreeDepthPlus. \ ' :CCTreeRecurseDepthPlus<CR>' exec 'nnoremap <buffer> <silent> '.a:kmaps.CTreeDepthMinus. \ ' :CCTreeRecurseDepthMinus<CR>' endfunction augroup CCTreeMaps au! " Header files get detected as cpp? " This is a bug in Vim 7.2, a patch needs to be applied to the runtime c " syntax files " For now, use this hack to make *.h files work autocmd FileType * if &ft == 'c'|| &ft == 'cpp' | \ call s:CCTreeBufferKeyMappingsCreate(s:CCTreeKeyMappings)| \ endif augroup END " }}} " {{{ Tree building let s:DisplayTreeEntry = { \ 'symbol': "", \ 'level': -1 \ } function! s:DisplayTreeEntry.mCreate(sym, level) dict let te = deepcopy(s:DisplayTreeEntry) let te.symbol = a:sym let te.level = a:level unlet te.mCreate return te endfunction let s:calltreemaxdepth = 10 let s:DisplayTree = { \ 'entries': [], \ 'levelMaxLen': repeat([255], s:calltreemaxdepth), \ 'notTxt': {} \ } function! s:DisplayTree.mCreate(calltree, direction, markers) dict let dt = deepcopy(s:DisplayTree) call dt.mBuildTreeForLevel(a:calltree, 0) call dt.mBuildNotationalTxtMarkers(a:direction, a:markers.icons) unlet dt.mBuildTreeForLevel unlet dt.mCreate return dt endfunction function! s:DisplayTree.mBuildTreeForLevel(ctree, level) if !has_key(a:ctree, 'symbol') return endif if g:CCTreeDisplayMode == 3 let curlevellen = strlen(a:ctree.symbol) + a:level + 2 let self.levelMaxLen[a:level] = min([self.levelMaxLen[a:level], \ curlevellen]) endif let aentry = s:DisplayTreeEntry.mCreate(a:ctree.symbol, a:level) call add(self.entries, aentry) if has_key(a:ctree, 'childlinks') for alink in a:ctree['childlinks'] call self.mBuildTreeForLevel(alink, a:level+1) endfor endif endfunction function! s:DisplayTree.mBuildNotationalTxtMarkers(direction, markerSyms) dict " REVISIT if a:direction == 'p' let directiontxt = a:markerSyms.arrowR . " " elseif a:direction == 'c' let directiontxt = a:markerSyms.arrowF . " " endif let self.notTxt.arrowHead = a:markerSyms.splitT let self.notTxt.arrow = directiontxt let self.notTxt.arrowLead = a:markerSyms.extH let self.notTxt.sep = a:markerSyms.extV if s:CCTreeUseConceal == 1 let concealspace = " " else let concealspace = "" endif let self.notTxt.symHighlighter= concealspace . "@" let self.notTxt.hiSymbolMarker = "!".concealspace let self.notTxt.hiBranchMarker = "#".concealspace let self.notTxt.cache = {} endfunction function! s:DisplayTree.mGetNotationalTxt(depth, hiDepth, hiSym, hiPath) dict let notkey = join(a:000, ":") if has_key(self.notTxt.cache,notkey) == 1 return self.notTxt.cache[notkey] else return self.mBuildNotationalTxt(a:depth, a:hiDepth, a:hiSym, a:hiPath) endif endfunction function! s:DisplayTree.mBuildNotationalTxt(depth, hiDepth, hiSym, hiPath) dict let hiBranch = 0 let curDepth = a:depth if 0 let Aspace = "A" let Bspace = "B" let Cspace = "C" let Sspace = "S" let Xspace = "X" let Zspace = "Z" let Fspace = "1" else let Aspace = " " let Bspace = " " let Cspace = " " let Sspace = " " let Xspace = " " let Zspace = " " let Fspace = " " endif if g:CCTreeDisplayMode == 1 let arrowLeads = self.notTxt.arrowLead elseif g:CCTreeDisplayMode >= 2 let arrowLeads = repeat(self.notTxt.arrowLead, a:depth) endif let indentSpace = "" if g:CCTreeDisplayMode == 2 if curDepth > 0 let indentSpace = repeat(Aspace, curDepth) endif elseif g:CCTreeDisplayMode == 3 if curDepth > 0 let indentSpace = repeat(Aspace, self.levelMaxLen[curDepth-1]) endif endif let notTxt = self.notTxt.arrowHead. arrowLeads . self.notTxt.arrow if a:hiDepth == a:depth let notTxt = indentSpace . self.notTxt.symHighlighter . notTxt let hiBranch = 1 else let notTxt = indentSpace. Cspace. notTxt endif let curDepth -= 1 let indentSpace = "" while (curDepth > 0) if g:CCTreeDisplayMode == 2 let indentSpace = repeat(Bspace, curDepth) elseif g:CCTreeDisplayMode == 3 let indentSpace = repeat(Bspace, self.levelMaxLen[curDepth-1]) endif let notTxt = self.notTxt.sep . notTxt if a:hiDepth == curDepth && a:hiPath == 1 let notTxt = indentSpace . self.notTxt.symHighlighter . notTxt let hiBranch = 1 else let notTxt = indentSpace. Cspace. notTxt endif let curDepth -= 1 endwhile if curDepth == 0 " curdepth is 0 if a:hiDepth == curDepth && a:hiPath == 1 let notTxt = self.notTxt.symHighlighter . notTxt let hiBranch = 1 else let notTxt = Fspace . notTxt endif let curDepth -= 1 endif " adjust space if a:depth > 0 let notTxt = Xspace . notTxt endif if hiBranch == 1 if a:hiSym == 1 let notTxt = self.notTxt.hiSymbolMarker . notTxt else let notTxt = self.notTxt.hiBranchMarker . notTxt endif else let notTxt = Sspace . notTxt endif return notTxt endfunction "}}} " {{{ Preview window Folding function! CCTreeFoldExpr(line) if !exists('b:displayTree') || v:lnum > len(b:displayTree.entries) return 0 endif let lvl = b:displayTree.entries[v:lnum-1].level if lvl == 0 let lvl = 1 endif return '>'.lvl endfunction function! CCTreeFoldText() if s:CCTreeUseConceal == 1 let line = substitute(getline(v:foldstart), '[!@#]', '' , 'g') else let line = substitute(getline(v:foldstart), '[!@#]', ' ' , 'g') endif return line. " (+". (v:foldend - v:foldstart). \ ')'. repeat(" ", winwidth(0)) endfunction " }}} " {{{ Syntax coloring definitions "Standard display highlight default link CCTreeSymbol Function highlight default link CCTreeMarkers LineNr highlight default link CCTreeArrow CCTreeMarkers highlight default link CCTreePathMark CCTreeArrow highlight default link CCTreeHiPathMark CCTreePathMark " highlighted display highlight default link CCTreeHiKeyword Macro highlight default link CCTreeHiSymbol TODO highlight default link CCTreeHiMarkers NonText highlight default link CCTreeHiArrow CCTreeHiMarkers highlight default link CCTreeUpArrowBlock CCTreeHiArrow highlight default link CCTreeMarkExcl Ignore highlight default link CCTreeMarkTilde Ignore "}}} " {{{ CCTree global state let s:CCTreePreviewState = { \ 'keyword':'', \ 'direction': '', \ 'depth' : '' \} function! s:CCTreePreviewState.mCreate() let state = deepcopy(s:CCTreePreviewState) unlet state.mCreate return state endfunction function! s:CCTreePreviewState.mStore(symbol, direction) let self.keyword = a:symbol let self.direction = a:direction endfunction " }}} " {{{ CCTree global objects let s:CCTreeGlobals = { \ 'XRefDb': {}, \ 'DbList': s:CCTreeDBList.mCreate(), \ 'PreviewState': s:CCTreePreviewState.mCreate(), \ 'Window': s:CCTreeWindow.mCreate() \} let g:CCTreeGlobals = s:CCTreeGlobals function! s:CCTreeGlobals.mEnable(opt) dict if (has_key(s:CCTreeOptions, a:opt)) call s:CCTreeOptions[a:opt](1) else call s:CCTreeUtils.mWarningMsg('Invalid option') endif endfunction function! s:CCTreeGlobals.mDisable(opt) dict if (has_key(s:CCTreeOptions, a:opt)) call s:CCTreeOptions[a:opt](0) else call s:CCTreeUtils.mWarningMsg('Invalid option') endif endfunction function! s:CCTreeGlobals.mToggle(opt) dict if (has_key(s:CCTreeOptions, a:opt)) call s:CCTreeOptions[a:opt](-1) else call s:CCTreeUtils.mWarningMsg('Invalid option') endif endfunction function! s:CCTreeGlobals.mGetSymNames(lead) dict call self.XRefDb.mInitState() let syms = self.XRefDb.mGetSymbolNames(a:lead) call self.XRefDb.mRestoreState() return syms endfunction function! s:CCTreeGlobals.mGetCallsForSymbol(name, depth, direction) dict let pbar = s:ProgressBarRoll.mCreate(['-','\','|','/'], '*') call s:StatusLine.mSetInfo('Building ') redrawstatus! " Create tracer let xtracer = s:XRefTracer.mCreate(self.XRefDb) call xtracer.mInitTracing() let symid = self.XRefDb.mGetSymbolIdFromName(a:name) let xrefs = xtracer.mBuildForSymbol(symid, \ a:depth, self.PreviewState.depth, a:direction, pbar) call xtracer.mDoneTracing() return xrefs endfunction function! s:CCTreeGlobals.mShowLoadedDBs() dict call self.DbList.mShowLoaded() endfunction function! s:CCTreeGlobals.mUnLoadDBs() dict call s:CCTreeGlobals.Window.mClose() if !empty(s:CCTreeGlobals.XRefDb) call s:CCTreeGlobals.XRefDb.mClear() endif call s:CCTreeGlobals.DbList.mClearAll() endfunction function! s:CCTreeGlobals.mSetPreviewState(name, depth, direction) dict let self.PreviewState.keyword = a:name let self.PreviewState.direction = a:direction let self.PreviewState.depth = a:depth endfunction function! s:CCTreeGlobals.mUpdateForCurrentSymbol() dict if self.DbList.mIsEmpty() == s:CCTreeRC.True return s:CCTreeRC.Error endif if self.PreviewState.keyword != '' let swatch = s:StopWatch.mCreate() " Move this function to globals? call s:StatusLine.mInit() let atree = self.mGetCallsForSymbol(self.PreviewState.keyword, \ 0, \ self.PreviewState.direction) call s:StatusLine.mRestore() call self.Window.mDisplayTree(atree, self.PreviewState.direction) call swatch.mSnapElapsed() endif endfunction function! s:CCTreeGlobals.mGetPreviewTreeSymbols() " REVIST if exists('b:displayTree') return self.Window.mExtractTreeSymbols(b:displayTree) end return {} endfunction function! s:CCTreeGlobals.mSanitizeCallDepth() dict let error = 0 if self.PreviewState.depth >= s:calltreemaxdepth self.PreviewState.depth = s:calltreemaxdepth let error = 1 elseif self.PreviewState.depth < 1 let self.PreviewState.depth = 1 let error = 1 endif if error == 1 call s:CCTreeUtils.mWarningMsg('Depth out of bounds') endif return error endfunction function! s:CCTreeGlobals.mRecursiveDepthIncrease() dict let self.PreviewState.depth += 1 if self.mSanitizeCallDepth() == 0 call self.mUpdateForCurrentSymbol() endif endfunction function! s:CCTreeGlobals.mRecursiveDepthDecrease() dict let self.PreviewState.depth -= 1 if self.mSanitizeCallDepth() == 0 call self.mUpdateForCurrentSymbol() endif endfunction function! s:CCTreeGlobals.mDisplayToggle() dict call self.Window.mDisplayToggle() endfunction function! s:CCTreeGlobals.mSetupAutoCmds() dict augroup CCTreeGeneral au! augroup END call s:CCTreeGlobals.mSetupCursorMoveAutoCmd(g:CCTreeHilightCallTree) call s:CCTreeGlobals.mSetupEncodingChangeAutoCmd(g:CCTreeUseUTF8Symbols) endfunction function! s:CCTreeGlobals.mSetupCursorMoveAutoCmd(enable) dict if a:enable == 1 exec 'autocmd CCTreeGeneral CursorMoved '.s:windowtitle.' call s:CCTreeGlobals.mCursorHoldHandleEvent()' else exec 'autocmd! CCTreeGeneral CursorMoved '.s:windowtitle endif endfunction function! s:CCTreeGlobals.mSetupEncodingChangeAutoCmd(enable) dict return if a:enable == 1 autocmd CCTreeGeneral EncodingChanged * call s:CCTreeGlobals.mEncodingChangedHandleEvent() else autocmd! CCTreeGeneral EncodingChanged * endif endfunction function! s:CCTreeGlobals.mPreviewSave() dict let rtitle = s:CCTreeGlobals.Window.mBuildStatusLine( \ s:CCTreeGlobals.PreviewState, \ s:windowsavetitle, \ {'depth':'', 'direction':''} \) if self.Window.mPreviewSave(rtitle) == s:CCTreeRC.Success call s:CCTreeUtils.mInfoMsg('Window saved as '. rtitle . \ '. New window will be opened on next usage.') else call s:CCTreeUtils.mWarningMsg('No active window found to be saved.') endif endfunction function! s:CCTreeGlobals.mWriteXRefDbToFile(fname) dict " create db serializer and writer let gDbSz = s:GenericDbSerializer.mCreate(self.XRefDb) let gDbWriter = s:CCTreeTagDbWriter.mCreate( \ s:CCTreeGetXRefDbMaps('Compress', 'Alpha')) call gDbSz.mWriteXRefDbToFile(a:fname, gDbWriter) endfunction function! s:CCTreeGlobals.mReadToXRefDb(fname) dict call s:StatusLine.mInit() call s:StatusLine.mSetInfo('Reading XRefDb') let vDbFile = s:vFile.mCreate(a:fname, "r") if vDbFile.mIsLargeFile() == 1 call s:StatusLine.mSetExtraInfo('Xref DB ' \.' >'.g:CCTreeDbFileMaxSize .' bytes. Splitting '. \'into smaller chunks... (this may take some time)') endif try if vDbFile.mOpen() == 0 call s:TagFile.mReadToXRefDb(self.XRefDb, vDbFile) endif finally call vDbFile.mClose() call s:StatusLine.mRestore() call self.DbList.mAddDbToList(a:fname, s:DBStorage.memory) endtry endfunction function! s:CCTreeGlobals.mCursorHoldHandleEvent() dict if self.Window.mGetKeywordAtCursor() != s:CCTreeRC.Error setlocal modifiable call self.Window.mClearMarks(b:displayTree) call self.Window.mMarkCallTree(b:displayTree, \ self.Window.hiKeyword) setlocal nomodifiable endif endfunction function! s:CCTreeGlobals.mEncodingChangedHandleEvent() dict let self.Window.treeMarkers = s:CCTreeMarkers.mCreate() if self.Window.mIsOpen() == s:CCTreeRC.True call self.Window.mClose() call self.mUpdateForCurrentSymbol() endif endfunction function! s:CCTreeGlobals.mInit() dict call self.mSetupAutoCmds() endfunction " }}} " {{{ CCTree options function! s:CCTreeSetUseCallTreeHiLights(val) if a:val == -1 let g:CCTreeHilightCallTree = !g:CCTreeHilightCallTree else let g:CCTreeHilightCallTree = a:val endif call s:CCTreeGlobals.mSetupAutoCmds() endfunction function! s:CCTreeSetUseUtf8Symbols(val) if a:val == -1 let g:CCTreeUseUTF8Symbols = !g:CCTreeUseUTF8Symbols else let g:CCTreeUseUTF8Symbols = a:val endif call s:CCTreeGlobals.mEncodingChangedHandleEvent() endfunction function! s:CCTreeSetUseConceal(val) if a:val == -1 let s:CCTreeUseConceal = !s:CCTreeUseConceal else let s:CCTreeUseConceal = a:val endif if !has('conceal') call s:CCTreeUtils.mWarningMsg('+conceal feature not available') let s:CCTreeUseConceal = 0 endif endfunction function! s:CCTreeSetEnhancedSymbolProcessing(val) if a:val == -1 let g:CCTreeEnhancedSymbolProcessing = !g:CCTreeEnhancedSymbolProcessing else let g:CCTreeEnhancedSymbolProcessing = a:val endif endfunction function! s:CCTreeOptionsList(arglead, cmdline, cursorpos) let opts = keys(s:CCTreeOptions) if a:arglead == '' return opts else return filter(opts, 'v:val =~? a:arglead') endif endfunction let s:CCTreeOptions = {'UseUnicodeSymbols': function('s:CCTreeSetUseUtf8Symbols'), \ 'DynamicTreeHiLights': function('s:CCTreeSetUseCallTreeHiLights'), \ 'UseConceal': function('s:CCTreeSetUseConceal'), \ 'EnhancedSymbolProcessing': function('s:CCTreeSetEnhancedSymbolProcessing') \} " }}} " {{{ Vim tags interface " CCTreeCompleteKwd " Command line completion function to return names from the db function! s:CCTreeCompleteKwd(arglead, cmdline, cursorpos) let syms = s:CCTreeGlobals.mGetSymNames(a:arglead) if a:arglead == '' return syms else return filter(syms, 'v:val =~? a:arglead') endif endfunction function! s:CCTreeTraceTreeForSymbol(sym_arg, direction) if s:CCTreeGlobals.DbList.mIsEmpty() == s:CCTreeRC.True call s:CCTreeUtils.mWarningMsg('No database loaded') return endif let symbol = a:sym_arg if symbol == '' let symbol = input('Trace symbol: ', expand('<cword>'), \ 'customlist,<SNR>' . s:sid . 'CCTreeCompleteKwd') if symbol == '' return endif endif let symmatch = s:CCTreeGlobals.mGetSymNames(symbol) if len(symmatch) > 0 && index(symmatch, symbol) >= 0 call s:CCTreeGlobals.mSetPreviewState(symbol, \ g:CCTreeRecursiveDepth, \ a:direction) call s:CCTreeGlobals.mUpdateForCurrentSymbol() else call s:CCTreeUtils.mWarningMsg('Symbol not found') endif endfunction function! s:CCTreeGlobals.mLoadBufferFromKeyword() " REVISIT if s:CCTreeGlobals.Window.mGetKeywordAtCursor() == s:CCTreeRC.Error call s:CCTreeUtils.mWarningMsg('No keyword at cursor') return endif let hiKeyword = s:CCTreeGlobals.Window.hiKeyword try wincmd p catch call s:CCTreeUtils.mWarningMsg('No buffer to load file') finally if (cscope_connection() > 0) try exec "cs find g ". hiKeyword catch " cheap hack exec "cs find f ". hiKeyword endtry else try " Ctags is smart enough to figure the path exec "tag ".fnamemodify(hiKeyword, ":t") catch /^Vim\%((\a\+)\)\=:E433/ call s:CCTreeUtils.mWarningMsg('Tag file not found') catch /^Vim\%((\a\+)\)\=:E426/ call s:CCTreeUtils.mWarningMsg('Tag '. hiKeyword .' not found') wincmd p endtry endif endtry endfunction function! s:CCTreeGlobals.mPreviewBufferFromKeyword() if self.Window.mGetKeywordAtCursor() == s:CCTreeRC.Error call s:CCTreeUtils.mWarningMsg('No keyword found') return endif let hiKeyword = s:CCTreeGlobals.Window.hiKeyword silent! wincmd P if !&previewwindow wincmd p endif try exec "ptag ". hiKeyword catch call s:CCTreeUtils.mWarningMsg('Tag '.hiKeyword. ' not found') endtry endfunction " }}} " {{{ Define commands command! -nargs=? -complete=file CCTreeLoadXRefDBFromDisk \ call s:CCTreeCmdLine.mLoadDBFromDisk(<q-args>) command! -nargs=? -complete=file CCTreeLoadDB call s:CCTreeCmdLine.mLoadDB(<q-args>, s:DBClasses.cscopeid) command! -nargs=? -complete=file CCTreeLoadXRefDB call s:CCTreeCmdLine.mLoadDB(<q-args>, s:DBClasses.cctreexref) command! -nargs=? -complete=file CCTreeSaveXRefDB call s:CCTreeCmdLine.mSaveDB(<q-args>, s:DBClasses.cctreexref) command! -nargs=? -complete=file CCTreeAppendDB call s:CCTreeCmdLine.mMergeDB(<q-args>, s:DBClasses.cscopeid) command! -nargs=0 CCTreeUnLoadDB call s:CCTreeGlobals.mUnLoadDBs() command! -nargs=0 CCTreeShowLoadedDBs call s:CCTreeGlobals.mShowLoadedDBs() command! -nargs=? -complete=customlist,s:CCTreeCompleteKwd \ CCTreeTraceForward call s:CCTreeTraceTreeForSymbol(<q-args>, 'c') command! -nargs=? -complete=customlist,s:CCTreeCompleteKwd CCTreeTraceReverse \ call s:CCTreeTraceTreeForSymbol(<q-args>, 'p') command! -nargs=0 CCTreeLoadBufferUsingTag call s:CCTreeGlobals.mLoadBufferFromKeyword() command! -nargs=0 CCTreePreviewBufferUsingTag call s:CCTreeGlobals.mPreviewBufferFromKeyword() command! -nargs=0 CCTreeRecurseDepthPlus call s:CCTreeGlobals.mRecursiveDepthIncrease() command! -nargs=0 CCTreeRecurseDepthMinus call s:CCTreeGlobals.mRecursiveDepthDecrease() " Preview Window command! -nargs=0 CCTreeWindowToggle call s:CCTreeGlobals.mDisplayToggle() command! -nargs=0 CCTreeWindowSaveCopy call s:CCTreeGlobals.mPreviewSave() " Run-time dynamic options command! -nargs=1 -complete=customlist,s:CCTreeOptionsList CCTreeOptsEnable call s:CCTreeGlobals.mEnable(<q-args>) command! -nargs=1 -complete=customlist,s:CCTreeOptionsList CCTreeOptsDisable call s:CCTreeGlobals.mDisable(<q-args>) command! -nargs=1 -complete=customlist,s:CCTreeOptionsList CCTreeOptsToggle call s:CCTreeGlobals.mToggle(<q-args>) "}}} " {{{ finish (and init) call s:CCTreeGlobals.mInit() " restore 'cpo' let &cpoptions = s:cpo_save unlet s:cpo_save " vim: ts=8 sw=4 sts=4 et foldenable foldmethod=marker foldcolumn=1 " }}}