1079 lines
32 KiB
VimL
1079 lines
32 KiB
VimL
" C Call-Tree Explorer (CCTree) <CCTree.vim>
|
|
"
|
|
"
|
|
" Script Info and Documentation
|
|
"=============================================================================
|
|
" Copyright: Copyright (C) August 2008, 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: December 24, 2008
|
|
" Version: 0.61
|
|
"
|
|
"=============================================================================
|
|
"
|
|
" Description:
|
|
" Plugin generates call-trees for any function or macro in real-time inside
|
|
" Vim.
|
|
"
|
|
" Requirements: 1) Cscope
|
|
" 2) Vim 7.xx
|
|
"
|
|
" Tested on Unix and the following Win32 versions:
|
|
" + Cscope, mlcscope (WIN32)
|
|
" http://www.geocities.com/shankara_c/cscope.html
|
|
" 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)
|
|
"
|
|
" 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 unload database, use command ":CCTreeUnLoadDB"
|
|
"
|
|
" 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>
|
|
"
|
|
" Command List:
|
|
" CCTreeLoadDB <dbname>
|
|
" CCTreeUnLoadDB
|
|
" CCTreeTraceForward <symbolname>
|
|
" CCTreeTraceReverse <symbolname>
|
|
" CCTreeRecurseDepthPlus
|
|
" CCTreeRecurseDepthMinus
|
|
"
|
|
"
|
|
"
|
|
" Settings:
|
|
" Customize behavior by changing the variable settings
|
|
"
|
|
" 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 = "leftabove"
|
|
" (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
|
|
"
|
|
" Limitations:
|
|
" 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.
|
|
"
|
|
" History:
|
|
"
|
|
" 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:
|
|
"
|
|
" 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 :)
|
|
"
|
|
"=============================================================================
|
|
|
|
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
|
|
|
|
" Global variables
|
|
" Modify in .vimrc to modify default behavior
|
|
if !exists('CCTreeCscopeDb')
|
|
let CCTreeCscopeDb = "cscope.out"
|
|
endif
|
|
if !exists('CCTreeRecursiveDepth')
|
|
let CCTreeRecursiveDepth = 3
|
|
endif
|
|
if !exists('CCTreeMinVisibleDepth')
|
|
let CCTreeMinVisibleDepth = 3
|
|
endif
|
|
if !exists('CCTreeOrientation')
|
|
let CCTreeOrientation = "leftabove"
|
|
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 = 40
|
|
endif
|
|
if !exists('CCTreeWindowHeight')
|
|
let CCTreeWindowHeight = -1
|
|
endif
|
|
if !exists('CCTreeDisplayMode')
|
|
let CCTreeDisplayMode = 1
|
|
endif
|
|
if !exists('CCTreeHilightCallTree')
|
|
let CCTreeHilightCallTree = 1
|
|
endif
|
|
|
|
" Plugin related local variables
|
|
let s:pluginname = 'CCTree'
|
|
let s:windowtitle = 'CCTree-Preview'
|
|
|
|
" There could be duplicate keywords on different lines
|
|
let s:CCTreekeyword = ''
|
|
let s:CCTreekeywordLine = -1
|
|
|
|
" Definition of a keyword...
|
|
let s:CCTreeKeywordRegEx = '[A-Za-z0-9_\\\.\/]\+'
|
|
|
|
" Other state variables
|
|
let s:currentkeyword = ''
|
|
let s:currentdirection = ''
|
|
let s:dbloaded = 0
|
|
let s:symhashtable = {}
|
|
let s:save_statusline = ''
|
|
let s:lastbufname = ''
|
|
|
|
" 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
|
|
|
|
|
|
let s:symlistindex = 0
|
|
let s:symlisttable = []
|
|
|
|
function! s:CCTreeSymbolListAdd(name)
|
|
if !has_key(s:symhashtable, a:name)
|
|
let s:symhashtable[a:name] = s:symlistindex
|
|
call add(s:symlisttable, s:CCTreeSymbolDictCreate(a:name))
|
|
let s:symlistindex += 1
|
|
endif
|
|
return s:symhashtable[a:name]
|
|
endfunction
|
|
|
|
function! s:CCTreeSymbolDictCreate(name)
|
|
let retval = {}
|
|
|
|
let retval['n'] = a:name
|
|
let retval['c'] = ""
|
|
let retval['p'] = ""
|
|
return retval
|
|
endfunction
|
|
|
|
function! s:CCTreeSymbolMarkXRef(funcentryidx, newfuncidx)
|
|
let s:symlisttable[a:funcentryidx]['c'] .= (a:newfuncidx. ",")
|
|
let s:symlisttable[a:newfuncidx]['p'] .= (a:funcentryidx. ",")
|
|
endfunction
|
|
|
|
|
|
function! s:CCTreeInitStatusLine()
|
|
let s:symlastprogress = 0
|
|
let s:symprogress = 0
|
|
let s:cursym = 0
|
|
let s:currentstatus = ''
|
|
let s:statusextra = ''
|
|
|
|
let s:save_statusline = &statusline
|
|
setlocal statusline=%{CCTreeStatusLine()}
|
|
endfunction
|
|
|
|
function! s:CCTreeRestoreStatusLine()
|
|
let &statusline = s:save_statusline
|
|
endfunction
|
|
|
|
function! s:CCTreeBusyStatusLineUpdate(msg)
|
|
let s:currentstatus = a:msg
|
|
redrawstatus
|
|
endfunction
|
|
|
|
function! s:CCTreeBusyStatusLineExtraInfo(msg)
|
|
let s:statusextra = a:msg
|
|
redrawstatus
|
|
endfunction
|
|
|
|
function! CCTreeStatusLine()
|
|
return s:pluginname. " ". s:currentstatus. " -- ". s:statusextra
|
|
endfunction
|
|
|
|
|
|
let s:progresscurrent = 0
|
|
let s:progressmax = 0
|
|
|
|
function! s:CCTreeProgressBarInit(maxcount)
|
|
let s:progressmax = a:maxcount
|
|
let s:progress1percent = a:maxcount/100
|
|
let s:progresspercent = 0
|
|
endfunction
|
|
|
|
|
|
function! s:CCTreeProgressBarTick(count)
|
|
let s:progresscurrent += a:count
|
|
if s:progress1percent < s:progresscurrent
|
|
let s:progresscurrent = 0
|
|
let s:progresspercent += 1
|
|
call s:CCTreeBusyStatusLineExtraInfo("Processing ". s:progresspercent .
|
|
\ "\%, total ". s:progressmax. " items")
|
|
endif
|
|
endfunction
|
|
|
|
|
|
function! s:CCTreeWarningMsg(msg)
|
|
echohl WarningMsg
|
|
echo a:msg
|
|
echohl None
|
|
endfunction
|
|
|
|
function! s:CCTreePreprocessFilter (val)
|
|
call s:CCTreeProgressBarTick(1)
|
|
return a:val =~ "^\t[`#$}]|^\k"
|
|
endfunction
|
|
|
|
function! s:CCTreeLoadDB(db_name)
|
|
let curfuncidx = -1
|
|
let newfuncidx = -1
|
|
let curfileidx = -1
|
|
let newfileidx = -1
|
|
|
|
call s:CCTreeUnloadDB()
|
|
|
|
let cscope_db = a:db_name
|
|
if cscope_db == ''
|
|
let cscope_db = input('Cscope database: ', g:CCTreeCscopeDb, 'file')
|
|
if cscope_db == ''
|
|
return
|
|
endif
|
|
endif
|
|
|
|
if !filereadable(cscope_db)
|
|
call s:CCTreeWarningMsg('Cscope database ' . cscope_db . ' not found')
|
|
return
|
|
endif
|
|
|
|
call s:CCTreeBusyStatusLineUpdate('Loading database')
|
|
let symbols = readfile(cscope_db)
|
|
if empty(symbols)
|
|
call s:CCTreeWarningMsg("Failed to read cscope database")
|
|
call s:CCTreeRestoreStatusLine()
|
|
return
|
|
endif
|
|
|
|
let symindex = 0
|
|
let symlocalstart = 0
|
|
let symlocalcount = 0
|
|
|
|
" Grab previous status line
|
|
call s:CCTreeInitStatusLine()
|
|
|
|
call s:CCTreeBusyStatusLineUpdate('Reading database')
|
|
" Check if database was built uncompressed
|
|
if symbols[0] !~ "cscope.*\-c"
|
|
let s:dbcompressed = 1
|
|
else
|
|
let s:dbcompressed = 0
|
|
endif
|
|
" Filter-out lines that doesn't have relevant information
|
|
call filter(symbols, 'v:val =~ "^\t[`#$}@\~]"')
|
|
call s:CCTreeProgressBarInit(len(symbols))
|
|
|
|
call s:CCTreeBusyStatusLineUpdate('Analyzing database')
|
|
for a in symbols
|
|
call s:CCTreeProgressBarTick(1)
|
|
|
|
if a[1] == "`"
|
|
if curfuncidx != -1
|
|
let newfuncidx = s:CCTreeSymbolListAdd(a[2:])
|
|
call s:CCTreeSymbolMarkXRef(curfuncidx, newfuncidx)
|
|
endif
|
|
elseif a[1] == "$"
|
|
let curfuncidx = s:CCTreeSymbolListAdd(a[2:])
|
|
elseif a[1] == "#"
|
|
call s:CCTreeSymbolListAdd(a[2:])
|
|
elseif a[1] == "}"
|
|
let curfuncidx = -1
|
|
elseif a[1] == "~"
|
|
let newfileidx = s:CCTreeSymbolListAdd(a[3:])
|
|
call s:CCTreeSymbolMarkXRef(curfileidx, newfileidx)
|
|
elseif a[1] == "@"
|
|
if a[2] != ""
|
|
let curfileidx = s:CCTreeSymbolListAdd(a[2:])
|
|
endif
|
|
endif
|
|
endfor
|
|
|
|
if s:dbcompressed == 1
|
|
call s:CCTreeBusyStatusLineUpdate('Uncompressing database')
|
|
" inplace uncompression
|
|
call s:Digraph_Uncompress(s:symlisttable, s:symhashtable)
|
|
endif
|
|
|
|
call s:CCTreeRestoreStatusLine()
|
|
let s:dbloaded = 1
|
|
echomsg "Done building database"
|
|
endfunction
|
|
|
|
|
|
function! s:CCTreeUnloadDB()
|
|
unlet s:symlisttable
|
|
unlet s:symhashtable
|
|
let s:dbloaded = 0
|
|
" Force cleanup
|
|
call garbagecollect()
|
|
|
|
let s:symlisttable = []
|
|
let s:symhashtable = {}
|
|
endfunction
|
|
|
|
function! s:CCTreeGetSymbolXRef(symname, direction)
|
|
let symentryidx = s:symhashtable[a:symname]
|
|
let symidslist = split(s:symlisttable[symentryidx][a:direction], ",")
|
|
let xrefs = {}
|
|
|
|
for asymid in symidslist
|
|
let xrefs[s:symlisttable[asymid]['n']] = 1
|
|
endfor
|
|
return xrefs
|
|
endfunction
|
|
|
|
function! s:CCTreeGetCallsForSymbol(symname, depth, direction)
|
|
if (a:depth > g:CCTreeRecursiveDepth)
|
|
return {}
|
|
endif
|
|
|
|
if !has_key(s:symhashtable, a:symname)
|
|
return {}
|
|
endif
|
|
|
|
let calltree_dict = {}
|
|
let calltree_dict['entry'] = a:symname
|
|
|
|
for entry in keys(s:CCTreeGetSymbolXRef(a:symname, a:direction))
|
|
if !has_key(calltree_dict, 'childlinks')
|
|
let calltree_dict['childlinks'] = []
|
|
endif
|
|
let tmpDict =
|
|
\s:CCTreeGetCallsForSymbol(entry, a:depth+1, a:direction)
|
|
call add(calltree_dict['childlinks'], tmpDict)
|
|
endfor
|
|
return calltree_dict
|
|
endfunction
|
|
|
|
func! s:FindOpenBuffer(filename)
|
|
let bnrList = tabpagebuflist(tabpagenr())
|
|
|
|
for bufnrs in bnrList
|
|
if (bufname(bufnrs) == a:filename)
|
|
let newWinnr = bufwinnr(bufnrs)
|
|
exec newWinnr.'wincmd w'
|
|
return 1
|
|
endif
|
|
endfor
|
|
" Could not find the buffer
|
|
return 0
|
|
endfunction
|
|
|
|
function! s:CCTreePreviewWindowLeave()
|
|
call s:FindOpenBuffer(s:lastbufname)
|
|
endfunction
|
|
|
|
function! CCTreePreviewStatusLine()
|
|
let rtitle= s:windowtitle. ' -- '. s:currentkeyword.
|
|
\'[Depth: '. g:CCTreeRecursiveDepth.','
|
|
|
|
if s:currentdirection == 'p'
|
|
let rtitle .= "(Reverse)"
|
|
else
|
|
let rtitle .= "(Forward)"
|
|
endif
|
|
|
|
return rtitle.']'
|
|
endfunction
|
|
|
|
function! s:CCTreePreviewWindowEnter()
|
|
let s:lastbufname = bufname("%")
|
|
if s:FindOpenBuffer(s:windowtitle) == 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=wipe
|
|
setlocal noswapfile
|
|
setlocal nonumber
|
|
setlocal statusline=%=%{CCTreePreviewStatusLine()}
|
|
|
|
|
|
syntax match CCTreePathMark /\s[|+]/ contained
|
|
syntax match CCTreeArrow /-*[<>]/ contained
|
|
syntax match CCTreeSymbol / [A-Za-z0-9_\.\\\/]\+/ contained
|
|
|
|
syntax region CCTreeSymbolLine start="^\s" end="$" contains=CCTreeArrow,CCTreePathMark,CCTreeSymbol oneline
|
|
|
|
syntax match CCTreeHiArrow /-*[<>]/ contained
|
|
syntax match CCTreeHiSymbol / [A-Za-z0-9_\.\\\/]\+/ contained
|
|
syntax match CCTreeHiPathMark /\s[|+]/ contained
|
|
|
|
syntax match CCTreeMarkExcl /^[!#]/ contained
|
|
syntax match CCTreeMarkTilde /@/ contained
|
|
syntax region CCTreeUpArrowBlock start="@" end=/[|+]/ 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
|
|
|
|
let cpo_save = &cpoptions
|
|
set cpoptions&vim
|
|
|
|
call s:CCTreeBufferKeyMappingsCreate()
|
|
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=s:CCTreeFoldExpr(getline(v:lnum))
|
|
setlocal foldtext=CCTreeFoldText()
|
|
let &l:foldlevel=g:CCTreeMinVisibleDepth
|
|
endfunction
|
|
|
|
|
|
function! s:CCTreeBuildTreeForLevel(dict, level, treelist, lvllen)
|
|
if !has_key(a:dict, 'entry')
|
|
return
|
|
endif
|
|
|
|
if g:CCTreeDisplayMode == 1
|
|
let curlevellen = 1
|
|
elseif g:CCTreeDisplayMode == 2
|
|
let curlevellen = a:level + 2
|
|
elseif g:CCTreeDisplayMode == 3
|
|
let curlevellen = strlen(a:dict['entry']) + a:level + 2
|
|
endif
|
|
|
|
let a:lvllen[a:level] = min([a:lvllen[a:level], curlevellen])
|
|
|
|
|
|
call add(a:treelist, [a:dict['entry'], a:level])
|
|
if has_key(a:dict, 'childlinks')
|
|
for a in a:dict['childlinks']
|
|
call s:CCTreeBuildTreeForLevel(a, a:level+1, a:treelist, a:lvllen)
|
|
endfor
|
|
endif
|
|
endfunction
|
|
|
|
|
|
let s:calltreemaxdepth = 10
|
|
function! s:CCTreeBuildTreeDisplayItems(treedict, treesymlist)
|
|
let treexinfo = repeat([255], s:calltreemaxdepth)
|
|
call s:CCTreeBuildTreeForLevel(a:treedict, 0, a:treesymlist, treexinfo)
|
|
return s:CCTreeBuildDisplayPrependText(treexinfo)
|
|
endfunction
|
|
|
|
|
|
function! s:CCTreeBuildDisplayPrependText(lenlist)
|
|
let pptxt = " "
|
|
let treepptext = repeat([" "], s:calltreemaxdepth)
|
|
|
|
if s:currentdirection == 'p'
|
|
let directiontxt = "< "
|
|
elseif s:currentdirection == 'c'
|
|
let directiontxt = "> "
|
|
endif
|
|
|
|
let treepptext[0] = pptxt."+".directiontxt
|
|
|
|
for idx in range(1, s:calltreemaxdepth-1)
|
|
if a:lenlist[idx] != 255
|
|
let pptxt .= repeat(" ", a:lenlist[idx-1])
|
|
let treepptext[idx] = pptxt."+"
|
|
|
|
if g:CCTreeDisplayMode == 1
|
|
let arrows = '-'
|
|
elseif g:CCTreeDisplayMode >= 2
|
|
let arrows = repeat("-", idx)
|
|
endif
|
|
|
|
let treepptext[idx] = pptxt."+".arrows.directiontxt
|
|
let pptxt .= "|"
|
|
endif
|
|
endfor
|
|
return treepptext
|
|
endfunction
|
|
|
|
function! s:CCTreeDisplayTreeList(pptxtlst, treelst)
|
|
for aentry in a:treelst
|
|
call setline(".", a:pptxtlst[aentry[1]]. aentry[0])
|
|
let b:maxwindowlen = max([strlen(getline("."))+1, b:maxwindowlen])
|
|
exec "normal o"
|
|
endfor
|
|
endfunction
|
|
|
|
|
|
" Provide 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:CCTreeMarkCallTree(treelst, keyword)
|
|
let declevel = -1
|
|
|
|
for idx in range(line("."), 1, -1)
|
|
" Find our keyword
|
|
if declevel == -1
|
|
if a:treelst[idx-1][0] == a:keyword
|
|
let declevel = a:treelst[idx-1][1]
|
|
endif
|
|
endif
|
|
|
|
" Skip folds
|
|
if declevel != -1 && foldclosed(idx) == -1
|
|
let curline = getline(idx)
|
|
if declevel == a:treelst[idx-1][1]
|
|
let linemarker = '!'
|
|
let declevel -= 1
|
|
else
|
|
let linemarker = '#'
|
|
endif
|
|
let pos = match(curline, '[+|]', 0, declevel+1)
|
|
" Unconventional change char
|
|
let curline = linemarker.strpart(curline, 1, pos-2).'@'.
|
|
\ strpart(curline, pos, 1). strpart(curline, pos+1)
|
|
call setline(idx, curline)
|
|
endif
|
|
endfor
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:CCTreeDisplayTreeInWindow(atree)
|
|
let incctreewin = 1
|
|
if (bufname('%') != s:windowtitle)
|
|
call s:CCTreePreviewWindowEnter()
|
|
let incctreewin = 0
|
|
endif
|
|
setlocal modifiable
|
|
1,$d
|
|
let b:treelist = []
|
|
let b:maxwindowlen = g:CCTreeWindowMinWidth
|
|
let treemarkertxtlist = s:CCTreeBuildTreeDisplayItems(a:atree, b:treelist)
|
|
call s:CCTreeDisplayTreeList(treemarkertxtlist, b:treelist)
|
|
|
|
if g:CCTreeWindowVertical == 1
|
|
if g:CCTreeWindowWidth == -1
|
|
exec "vert 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
|
|
|
|
exec "normal gg"
|
|
|
|
" Need to force this again
|
|
let &l:foldlevel=g:CCTreeMinVisibleDepth
|
|
setlocal nomodifiable
|
|
if (incctreewin == 0)
|
|
call s:CCTreePreviewWindowLeave()
|
|
endif
|
|
endfunction
|
|
|
|
function! s:CCTreeFoldExpr(line)
|
|
let lvl = b:treelist[v:lnum-1][1]
|
|
if lvl == 0
|
|
let lvl = 1
|
|
endif
|
|
return '>'.lvl
|
|
endfunction
|
|
|
|
|
|
function! CCTreeFoldText()
|
|
let line = substitute(getline(v:foldstart), '[!@#]', ' ' , 'g')
|
|
return line. " (+". (v:foldend - v:foldstart).
|
|
\ ')'. repeat(" ", winwidth(0))
|
|
endfunction
|
|
|
|
|
|
function! s:CCTreeStoreState(symbol, direction)
|
|
let s:currentkeyword = a:symbol
|
|
let s:currentdirection = a:direction
|
|
endfunction
|
|
|
|
function! s:CCTreeDBIsLoaded()
|
|
if s:dbloaded == 0
|
|
call s:CCTreeWarningMsg('CCTree database not loaded')
|
|
return 0
|
|
endif
|
|
return 1
|
|
endfunction
|
|
|
|
" 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
|
|
|
|
function! s:CCTreeTraceTreeForSymbol(sym_arg, direction)
|
|
if s:CCTreeDBIsLoaded() == 0
|
|
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 atree = s:CCTreeGetCallsForSymbol(symbol, 0, a:direction)
|
|
call s:CCTreeStoreState(symbol, a:direction)
|
|
call s:CCTreeUpdateForCurrentSymbol()
|
|
endfunction
|
|
|
|
function! s:CCTreeUpdateForCurrentSymbol()
|
|
if s:currentkeyword != ''
|
|
let atree = s:CCTreeGetCallsForSymbol(s:currentkeyword, 0, s:currentdirection)
|
|
call s:CCTreeDisplayTreeInWindow(atree)
|
|
endif
|
|
endfunction
|
|
|
|
|
|
function! s:CCTreeGetCurrentKeyword()
|
|
let curline = line(".")
|
|
if foldclosed(curline) == -1
|
|
let curkeyword = matchstr(getline("."), s:CCTreeKeywordRegEx)
|
|
if curkeyword != ''
|
|
if curkeyword != s:CCTreekeyword || curline != s:CCTreekeywordLine
|
|
let s:CCTreekeyword = curkeyword
|
|
let s:CCTreekeywordLine = line(".")
|
|
return 1
|
|
endif
|
|
endif
|
|
endif
|
|
return -1
|
|
endfunction
|
|
|
|
function! s:CCTreeLoadBufferFromKeyword()
|
|
if s:CCTreeGetCurrentKeyword() == -1
|
|
return
|
|
endif
|
|
|
|
try
|
|
exec 'wincmd p'
|
|
catch
|
|
call s:CCTreeWarningMsg('No buffer to load file')
|
|
finally
|
|
if (cscope_connection() > 0)
|
|
try
|
|
exec "cs find g ".s:CCTreekeyword
|
|
catch
|
|
" cheap hack
|
|
exec "cs find f ".s:CCTreekeyword
|
|
endtry
|
|
else
|
|
try
|
|
" Ctags is smart enough to figure the path
|
|
exec "tag ".fnamemodify(s:CCTreekeyword, ":t")
|
|
catch /^Vim\%((\a\+)\)\=:E426/
|
|
call s:CCTreeWarningMsg('Tag '. s:CCTreekeyword .' not found')
|
|
wincmd p
|
|
endtry
|
|
endif
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:CCTreePreviewBufferFromKeyword()
|
|
call s:CCTreeGetCurrentKeyword()
|
|
if s:CCTreekeyword == ''
|
|
return
|
|
endif
|
|
silent! wincmd P
|
|
if !&previewwindow
|
|
wincmd p
|
|
endif
|
|
exec "ptag ".s:CCTreekeyword
|
|
endfunction
|
|
|
|
|
|
function! s:CCTreeSanitizeCallDepth()
|
|
let error = 0
|
|
if g:CCTreeRecursiveDepth >= s:calltreemaxdepth
|
|
g:CCTreeRecursiveDepth = s:calltreemaxdepth
|
|
let error = 1
|
|
elseif g:CCTreeRecursiveDepth < 1
|
|
g:CCTreeRecursiveDepth = 1
|
|
let error = 1
|
|
endif
|
|
|
|
if error == 1
|
|
call s:CCTreeWarningMsg('Depth out of bounds')
|
|
endif
|
|
return error
|
|
endfunction
|
|
|
|
function! s:CCTreeRecursiveDepthIncrease()
|
|
let g:CCTreeRecursiveDepth += 1
|
|
if s:CCTreeSanitizeCallDepth() == 0
|
|
call s:CCTreeUpdateForCurrentSymbol()
|
|
endif
|
|
endfunction
|
|
|
|
function! s:CCTreeRecursiveDepthDecrease()
|
|
let g:CCTreeRecursiveDepth -= 1
|
|
if s:CCTreeSanitizeCallDepth() == 0
|
|
call s:CCTreeUpdateForCurrentSymbol()
|
|
endif
|
|
endfunction
|
|
|
|
|
|
" Use this function to determine the correct "g" flag
|
|
" for substitution
|
|
function! s:CCTreeGetSearchFlag(gvalue)
|
|
let ret = (!a:gvalue)* (&gdefault) + (!&gdefault)*(a:gvalue)
|
|
if ret == 1
|
|
return 'g'
|
|
endif
|
|
return ''
|
|
endfunc
|
|
|
|
function! s:CCTreeClearMarks()
|
|
let windict = winsaveview()
|
|
silent! exec "1,$s/[!#@]/ /e".s:CCTreeGetSearchFlag(1)
|
|
call winrestview(windict)
|
|
endfunction
|
|
|
|
function! s:CCTreeCursorHoldHandle()
|
|
if g:CCTreeHilightCallTree && s:CCTreeGetCurrentKeyword() != -1
|
|
setlocal modifiable
|
|
call s:CCTreeClearMarks()
|
|
call s:CCTreeMarkCallTree(b:treelist, s:CCTreekeyword)
|
|
setlocal nomodifiable
|
|
endif
|
|
endfunction
|
|
|
|
" CCTreeCompleteKwd
|
|
" Command line completion function to return names from the db
|
|
function! s:CCTreeCompleteKwd(arglead, cmdline, cursorpos)
|
|
if a:arglead == ''
|
|
return keys(s:symhashtable)
|
|
else
|
|
return filter(keys(s:symhashtable), 'v:val =~? a:arglead')
|
|
endif
|
|
endfunction
|
|
|
|
augroup CCTreeGeneral
|
|
au!
|
|
autocmd CursorHold CCTree-Preview call s:CCTreeCursorHoldHandle()
|
|
augroup END
|
|
|
|
|
|
|
|
"Standard display
|
|
highlight link CCTreeSymbol Function
|
|
highlight link CCTreeMarkers LineNr
|
|
highlight link CCTreeArrow CCTreeMarkers
|
|
highlight link CCTreePathMark CCTreeArrow
|
|
highlight link CCTreeHiPathMark CCTreePathMark
|
|
|
|
" highlighted display
|
|
highlight link CCTreeHiSymbol TODO
|
|
highlight link CCTreeHiMarkers StatusLine
|
|
highlight link CCTreeHiArrow CCTreeHiMarkers
|
|
highlight link CCTreeUpArrowBlock CCTreeHiArrow
|
|
|
|
highlight link CCTreeMarkExcl Ignore
|
|
highlight link CCTreeMarkTilde Ignore
|
|
|
|
|
|
" Define commands
|
|
command! -nargs=? -complete=file CCTreeLoadDB call s:CCTreeLoadDB(<q-args>)
|
|
command! -nargs=0 CCTreeUnLoadDB call s:CCTreeUnloadDB()
|
|
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:CCTreeLoadBufferFromKeyword()
|
|
command! -nargs=0 CCTreePreviewBufferUsingTag call s:CCTreePreviewBufferFromKeyword()
|
|
command! -nargs=0 CCTreeRecurseDepthPlus call s:CCTreeRecursiveDepthIncrease()
|
|
command! -nargs=0 CCTreeRecurseDepthMinus call s:CCTreeRecursiveDepthDecrease()
|
|
|
|
|
|
function! s:CCTreeGetKeyword()
|
|
let keyw = expand("<cword>")
|
|
let keyf = expand("<cfile>")
|
|
|
|
if keyw != keyf
|
|
if has_key(s:symhashtable, keyf)
|
|
return keyf
|
|
elseif has_key(s:symhashtable, keyw)
|
|
return keyw
|
|
endif
|
|
else
|
|
return keyw
|
|
endif
|
|
return ''
|
|
endfunction
|
|
|
|
|
|
function! s:CCTreeBufferKeyMappingsCreate()
|
|
let func_expr = '<SNR>'.s:sid.'CCTreeGetKeyword()'
|
|
exec 'nnoremap <buffer> <silent> <C-\>< :CCTreeTraceReverse <C-R>='.func_expr.'<CR><CR>'
|
|
exec 'nnoremap <buffer> <silent> <C-\>> :CCTreeTraceForward <C-R>='.func_expr.'<CR><CR>'
|
|
|
|
nnoremap <buffer> <silent> <C-\>= :CCTreeRecurseDepthPlus<CR>
|
|
nnoremap <buffer> <silent> <C-\>- :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()| endif
|
|
augroup END
|
|
|
|
|
|
" Cscope Digraph character compression/decompression routines
|
|
" the logic of these routines are based off the Cscope source code
|
|
|
|
let s:dichar1 = " teisaprnl(of)=c"
|
|
let s:dichar2 = " tnerpla"
|
|
|
|
function! s:Digraph_DictTable_Init ()
|
|
let dicttable = []
|
|
let index = 0
|
|
|
|
for dc1 in range(strlen(s:dichar1))
|
|
for dc2 in range(strlen(s:dichar2))
|
|
call add(dicttable, s:dichar1[dc1].s:dichar2[dc2])
|
|
endfor
|
|
endfor
|
|
|
|
return dicttable
|
|
endfunction
|
|
|
|
function! s:Digraph_Uncompress_Slow (value, dicttable)
|
|
let retval = ""
|
|
for idx in range(strlen(a:value))
|
|
let charext = char2nr(a:value[idx])-128
|
|
if charext >= 0
|
|
let retval .= a:dicttable[charext]
|
|
else
|
|
let retval .= a:value[idx]
|
|
endif
|
|
endfor
|
|
return retval
|
|
endfunction
|
|
|
|
function! s:Digraph_Uncompress_Fast (value, dicttable)
|
|
let dichar_list = split(a:value, '[^\d128-\d255]\{}')
|
|
let retval = a:value
|
|
for adichar in dichar_list
|
|
let retval = substitute(retval, '\C'.adichar, a:dicttable[char2nr(adichar)-128], "g")
|
|
endfor
|
|
return retval
|
|
endfunction
|
|
|
|
|
|
function! s:Digraph_Uncompress_filter_loop(compressedsym, symlist, symhash, cmpdict)
|
|
let idx = a:symhash[a:compressedsym]
|
|
let uncmpname = s:Digraph_Uncompress_Fast(a:compressedsym, a:cmpdict)
|
|
let a:symhash[uncmpname] = idx
|
|
let a:symlist[idx]['n'] = uncmpname
|
|
call s:CCTreeProgressBarTick(1)
|
|
return 0
|
|
endfunction
|
|
|
|
function! s:Digraph_Uncompress (symlist, symhash)
|
|
let compressdict = s:Digraph_DictTable_Init()
|
|
|
|
call s:CCTreeProgressBarInit(len(a:symhash))
|
|
" The encoding needs to be changed to 8-bit, otherwise we can't swap special
|
|
" 8-bit characters; restore after done
|
|
let encoding_save=&encoding
|
|
let &encoding="latin1"
|
|
|
|
for compressedsym in keys(a:symhash)
|
|
let idx = a:symhash[compressedsym]
|
|
let uncmpname = s:Digraph_Uncompress_Fast(compressedsym, compressdict)
|
|
let a:symhash[uncmpname] = idx
|
|
" free the old entry
|
|
unlet a:symhash[compressedsym]
|
|
let a:symlist[idx]['n'] = uncmpname
|
|
call s:CCTreeProgressBarTick(1)
|
|
endfor
|
|
let &encoding=encoding_save
|
|
endfunction
|
|
|
|
|
|
function! s:Digraph_Compress(value, dicttable)
|
|
let index = 0
|
|
let retval = ""
|
|
|
|
while index < strlen(a:value)
|
|
let dc1 = stridx(s:dichar1, a:value[index])
|
|
if dc1 != -1
|
|
let dc2 = stridx(s:dichar2, a:value[index+1])
|
|
if dc2 != -1
|
|
let retval .= nr2char(128 + (dc1*8) + dc2)
|
|
" skip 2 chars
|
|
let index += 2
|
|
continue
|
|
endif
|
|
endif
|
|
let retval .= a:value[index]
|
|
let index += 1
|
|
endwhile
|
|
return retval
|
|
endfunction
|
|
|
|
|
|
" restore 'cpo'
|
|
let &cpoptions = s:cpo_save
|
|
unlet s:cpo_save
|
|
|