diff --git a/plugin/cscope.vim b/plugin/cscope.vim index cdf923d..fed6f64 100755 --- a/plugin/cscope.vim +++ b/plugin/cscope.vim @@ -1,154 +1,125 @@ -" vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab foldmethod=marker " Copyright: Copyright (C) 2012-2015 Brook Hong " License: The MIT License " +let s:cscope_vim_db_dir = substitute($HOME,'\\','/','g')."/.cscope.vim" +let s:cscope_vim_db_index_file = s:cscope_vim_db_dir.'/index' +let s:cscope_vim_db_current_prepend_path = "" +let s:cscope_vim_db_entry_len = 5 +let s:cscope_vim_db_entry_idx_prepend_path = 0 +let s:cscope_vim_db_entry_idx_id = 1 +let s:cscope_vim_db_entry_idx_loadtimes = 2 +let s:cscope_vim_db_entry_idx_dirty = 3 +let s:cscope_vim_db_entry_idx_depedency = 4 +let s:cscope_vim_db_entry_key_id = 'id' +let s:cscope_vim_db_entry_key_loadtimes = 'loadtimes' +let s:cscope_vim_db_entry_key_dirty = 'dirty' +let s:cscope_vim_db_entry_key_depedency = 'depedency' -if !exists('g:cscope_silent') - let g:cscope_silent = 0 -endif - -if !exists('g:cscope_auto_update') - let g:cscope_auto_update = 1 -endif +function! CscopeFind(action, word, ...) + let l:current_path = tolower(substitute(expand('%:p:h'), '\\', '/', 'g')) + let l:prepend_path = GetPrependPath(l:current_path) + let l:in_dependency = 0 -if !exists('g:cscope_open_location') - let g:cscope_open_location = 1 -endif + " possible reasons for empty prepend path + " - DB not yet built + " - we are in depedency files + if l:prepend_path == "" && s:cscope_vim_db_current_prepend_path != "" + for l:d in split(s:dbs[s:cscope_vim_db_current_prepend_path][s:cscope_vim_db_entry_key_depedency], ';') + let l:d = substitute(l:d, "\/\\s*$", '', 'g') -if !exists('g:cscope_split_threshold') - let g:cscope_split_threshold = 10000 -endif + if stridx(l:current_path, l:d) == 0 + let l:in_dependency = 1 + break + endif + endfor + endif + + " build a brand new db + if l:prepend_path == "" && l:in_dependency == 0 + let l:prepend_path = InitDB(l:current_path) -function! ToggleLocationList() - let l:own = winnr() - lw - let l:cwn = winnr() - if(l:cwn == l:own) - if &buftype == 'quickfix' - lclose - elseif len(getloclist(winnr())) > 0 - lclose - else - echohl WarningMsg | echo "No location list." | echohl None + if l:prepend_path != "" + call BuildDB(l:prepend_path, 1) + call LoadDB(l:prepend_path) endif endif -endfunction -if !exists('g:cscope_cmd') - if executable('cscope') - let g:cscope_cmd = 'cscope' - else - echo 'cscope: command not found' - finish - endif -endif + if l:prepend_path != "" + let l:id = s:dbs[l:prepend_path][s:cscope_vim_db_entry_key_id] -if !exists('g:cscope_interested_files') - let files = readfile(expand(":p:h")."/interested.txt") - let g:cscope_interested_files = join(map(files, 'v:val."$"'), '\|') -endif + if cscope_connection(2, s:cscope_vim_db_dir.'/'.l:id.'.db') == 0 + call LoadDB(l:prepend_path) + endif + endif -let s:cscope_vim_dir = substitute($HOME,'\\','/','g')."/.cscope.vim" -let s:index_file = s:cscope_vim_dir.'/index' + try + if a:0 == 0 + exe 'cs f '.a:action.' '.a:word + elseif a:0 >= 1 && a:1 == 'horizontal' + exe 'scs f '.a:action.' '.a:word + elseif a:0 >= 1 && a:1 == 'vertical' + exe 'vert scs f '.a:action.' '.a:word + endif -function! s:GetBestPath(dir) - let f = substitute(a:dir,'\\','/','g') - let bestDir = "" - for d in keys(s:dbs) - if stridx(f, d) == 0 && len(d) > len(bestDir) - let bestDir = d + if g:cscope_open_location == 1 + cw endif - endfor - return bestDir + catch + echohl WarningMsg | echo 'Can not find '.a:word.' with querytype as '.a:action.'.' | echohl None + endtry endfunction -function! s:ListFiles(dir) - let d = [] - let f = [] - let cwd = a:dir - let sl = &l:stl - while cwd != '' - let a = split(globpath(cwd, "*"), "\n") - for fn in a - if getftype(fn) == 'dir' - call add(d, fn) - elseif getftype(fn) != 'file' - continue - elseif fn !~? g:cscope_interested_files - continue - else - if stridx(fn, ' ') != -1 - let fn = '"'.fn.'"' - endif - call add(f, fn) - endif - endfor - let cwd = len(d) ? remove(d, 0) : '' - sleep 1m | let &l:stl = 'Found '.len(f).' files, finding in '.cwd | redrawstatus - endwhile - sleep 1m | let &l:stl = sl | redrawstatus - return f -endfunction +function! CscopeFindInteractive(pat) + call inputsave() -function! s:RmDBfiles() - let odbs = split(globpath(s:cscope_vim_dir, "*"), "\n") - for f in odbs - call delete(f) - endfor + let qt = input("\nChoose a querytype for '".a:pat."'(:help cscope-find)\n c: functions calling this function\n d: functions called by this function\n e: this egrep pattern\n f: this file\n g: this definition\n i: files #including this file\n s: this C symbol\n t: this text string\n\n or\n to query `pattern` instead of '".a:pat."' as `querytype`, Ex. `smain` to query a C symbol named 'main'.\n> ") + + call inputrestore() + + if len(qt) > 1 + call CscopeFind(qt[0], qt[1:]) + elseif len(qt) > 0 + call CscopeFind(qt, a:pat) + endif endfunction -function! s:FlushIndex() - let lines = [] +function! s:CscopeUpdateAllDB() for d in keys(s:dbs) - call add(lines, d.'|'.s:dbs[d]['id'].'|'.s:dbs[d]['loadtimes'].'|'.s:dbs[d]['dirty']) + call BuildDB(d, 0) endfor - call writefile(lines, s:index_file) endfunction -function! s:CheckNewFile(dir, newfile) - let id = s:dbs[a:dir]['id'] - let cscope_files = s:cscope_vim_dir."/".id.".files" - let files = readfile(cscope_files) - if len(files) > g:cscope_split_threshold - let cscope_files = s:cscope_vim_dir."/".id."_inc.files" - if filereadable(cscope_files) - let files = readfile(cscope_files) - else - let files = [] +function! s:CscopeUpdateCurrentDB() + let l:current_path = expand('%:p:h') + let l:prepend_path = GetPrependPath(l:current_path) + + if l:prepend_path != "" + call BuildDB(l:prepend_path, 0) + else + let l:prepend_path = InitDB(l:current_path) + + if l:prepend_path != "" + call BuildDB(l:prepend_path, 1) + call LoadDB(l:prepend_path) endif endif - if count(files, a:newfile) == 0 - call add(files, a:newfile) - call writefile(files, cscope_files) - endif endfunction -function! s:_CreateDB(dir, init) - let id = s:dbs[a:dir]['id'] - let cscope_files = s:cscope_vim_dir."/".id."_inc.files" - let cscope_db = s:cscope_vim_dir.'/'.id.'_inc.db' - if ! filereadable(cscope_files) || a:init - let cscope_files = s:cscope_vim_dir."/".id.".files" - let cscope_db = s:cscope_vim_dir.'/'.id.'.db' - if ! filereadable(cscope_files) - let files = ListFiles(a:dir) - call writefile(files, cscope_files) - endif - endif - exec 'cs kill '.cscope_db - redir @x - exec 'silent !'.g:cscope_cmd.' -b -i '.cscope_files.' -f'.cscope_db - redi END - if @x =~ "\nCommand terminated\n" - echohl WarningMsg | echo "Failed to create cscope database for ".a:dir.", please check if " | echohl None - else - let s:dbs[a:dir]['dirty'] = 0 - exec 'cs add '.cscope_db +function! s:CscopeRebuildCurrentDB() + call ClearDBs(0) + + let l:current_path = expand('%:p:h') + let l:prepend_path = InitDB(l:current_path) + + if l:prepend_path != "" + call BuildDB(l:prepend_path, 1) + call LoadDB(l:prepend_path) endif endfunction function! s:CheckAbsolutePath(dir, defaultPath) let d = a:dir + while 1 if !isdirectory(d) echohl WarningMsg | echo "Please input a valid path." | echohl None @@ -160,109 +131,247 @@ function! s:CheckAbsolutePath(dir, defaultPath) break endif endwhile + let d = substitute(d,'\\','/','g') let d = substitute(d,'/\+$','','') - return d -endfunction -function! s:InitDB(dir) - let id = localtime() - let s:dbs[a:dir] = {} - let s:dbs[a:dir]['id'] = id - let s:dbs[a:dir]['loadtimes'] = 0 - let s:dbs[a:dir]['dirty'] = 0 - call _CreateDB(a:dir, 1) - call FlushIndex() + return d endfunction -function! s:LoadDB(dir) +" @param clearWhich: -1 all database +" 0 the current database +function! s:ClearDBs(clearWhich) cs kill -1 - exe 'cs add '.s:cscope_vim_dir.'/'.s:dbs[a:dir]['id'].'.db' - if filereadable(s:cscope_vim_dir.'/'.s:dbs[a:dir]['id'].'_inc.db') - exe 'cs add '.s:cscope_vim_dir.'/'.s:dbs[a:dir]['id'].'_inc.db' + + if a:clearWhich == -1 + let s:dbs = {} + call RmDBfiles() + call writefile([], s:cscope_vim_db_index_file) + elseif a:clearWhich == 0 + let l:current_path = expand('%:p:h') + let l:prepend_path = GetPrependPath(l:current_path) + + if l:prepend_path != "" + let l:current_db_files = split(globpath(s:cscope_vim_db_dir, s:dbs[l:prepend_path][s:cscope_vim_db_entry_key_id]."*"), "\n") + + for f in l:current_db_files + call delete(f) + endfor + + unlet s:dbs[l:prepend_path] + + call FlushIndex() + endif endif - let s:dbs[a:dir]['loadtimes'] = s:dbs[a:dir]['loadtimes']+1 - call FlushIndex() endfunction -function! s:AutoloadDB(dir) - let m_dir = GetBestPath(a:dir) - if m_dir == "" - echohl WarningMsg | echo "Can not find proper cscope db, please input a path to generate cscope db for." | echohl None - let m_dir = input("", a:dir, 'dir') - if m_dir != '' - let m_dir = CheckAbsolutePath(m_dir, a:dir) - call InitDB(m_dir) - call LoadDB(m_dir) - endif +function! s:BuildDB(prepend_path, init) + let l:id = s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_id] + let l:depedency = split(s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_depedency], ';') + let l:cscope_files = s:cscope_vim_db_dir."/".id."_inc.files" + let l:cscope_db = s:cscope_vim_db_dir.'/'.id.'_inc.db' + + if ! filereadable(cscope_files) || a:init + let l:cscope_files = s:cscope_vim_db_dir."/".id.".files" + let l:cscope_db = s:cscope_vim_db_dir.'/'.id.'.db' + endif + + " validate depedency + for i in range(len(l:depedency)) + let l:depedency[i] = CheckAbsolutePath(l:depedency[i], "") + endfor + + " force update file list + let files = [] + for d in [a:prepend_path] + l:depedency + let files += ListFiles(d) + endfor + call writefile(files, cscope_files) + + " build cscope database, must build in the + " prepend path otherwise there might be error + " in generating database, e.g. invalid path for + " symbols. + exec 'chdir '.a:prepend_path + exec 'cs kill '.cscope_db + redir @x + exec 'silent !'.g:cscope_cmd.' -b -i '.cscope_files.' -f '.cscope_db + redir END + + " check build result and add database + if @x =~ "\nCommand terminated\n" + echohl WarningMsg | echo "Failed to create cscope database for ".a:prepend_path.", please check if " | echohl None else - let id = s:dbs[m_dir]['id'] - if cscope_connection(2, s:cscope_vim_dir.'/'.id.'.db') == 0 - call LoadDB(m_dir) - endif + let s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_dirty] = 0 endif + + call FlushIndex() endfunction -function! s:updateDBs(dirs) - for d in a:dirs - call _CreateDB(d, 0) +function! s:FlushIndex() + let l:lines = [] + + for d in keys(s:dbs) + call add(l:lines, d.'|'.s:dbs[d][s:cscope_vim_db_entry_key_id].'|'.s:dbs[d][s:cscope_vim_db_entry_key_loadtimes].'|'.s:dbs[d][s:cscope_vim_db_entry_key_dirty].'|'.s:dbs[d][s:cscope_vim_db_entry_key_depedency].'|') endfor - call FlushIndex() + + call writefile(l:lines, s:cscope_vim_db_index_file) endfunction -function! s:echo(msg) - if g:cscope_silent == 0 - echo a:msg - endif +function! s:GetPrependPath(dir) + let f = tolower(substitute(a:dir,'\\','/','g')) + let bestDir = "" + + for d in keys(s:dbs) + if stridx(f, d) == 0 && len(d) > len(bestDir) + let bestDir = d + endif + endfor + + return bestDir endfunction -function! s:clearDBs() - cs kill -1 - let s:dbs = {} - call RmDBfiles() - call writefile([], s:index_file) +function! s:InitDB(current_path) + echohl WarningMsg | echo "Please input a path to generate cscope database." | echohl None + let l:prepend_path = tolower(substitute(input("", a:current_path, 'dir'),'\\','/','g')) + + if l:prepend_path != '' + let prepend_path = CheckAbsolutePath(l:prepend_path, a:current_path) + + echohl WarningMsg | echo "\nPlease input depedency paths (separated with ';'), if any." | echohl None + let l:depedency_path = tolower(substitute(input("", "", 'dir'),'\\','/','g')) + + let s:dbs[l:prepend_path] = {} + let s:dbs[l:prepend_path][s:cscope_vim_db_entry_key_id] = localtime() + let s:dbs[l:prepend_path][s:cscope_vim_db_entry_key_loadtimes] = 0 + let s:dbs[l:prepend_path][s:cscope_vim_db_entry_key_dirty] = 0 + let s:dbs[l:prepend_path][s:cscope_vim_db_entry_key_depedency] = l:depedency_path + + call FlushIndex() + + return l:prepend_path + else + echohl WarningMsg | echo "Error: path can not be empty." | echohl None + endif endfunction -function! s:listDBs() +function! s:ListDBs() let dirs = keys(s:dbs) + if len(dirs) == 0 echo "You have no cscope dbs now." else let s = [' ID LOADTIMES PATH'] + for d in dirs let id = s:dbs[d]['id'] - if cscope_connection(2, s:cscope_vim_dir.'/'.id.'.db') == 1 + + if cscope_connection(2, s:cscope_vim_db_dir.'/'.id.'.db') == 1 let l = printf("*%d %10d %s", id, s:dbs[d]['loadtimes'], d) else let l = printf(" %d %10d %s", id, s:dbs[d]['loadtimes'], d) endif + call add(s, l) endfor + echo join(s, "\n") endif endfunction -function! s:loadIndex() +function! s:ListFiles(dir) + let d = [] + let f = [] + let cwd = a:dir + let sl = &l:stl + + while cwd != '' + let a = split(globpath(cwd, "*"), "\n") + + for fn in a + if getftype(fn) == 'dir' + call add(d, fn) + elseif getftype(fn) != 'file' + continue + elseif fn !~? g:cscope_interested_files + continue + else + if stridx(fn, ' ') != -1 + let fn = '"'.fn.'"' + endif + call add(f, fn) + endif + endfor + + let cwd = len(d) ? remove(d, 0) : '' + + sleep 1m | let &l:stl = 'Found '.len(f).' files, finding in '.cwd | redrawstatus + endwhile + + sleep 1m | let &l:stl = sl | redrawstatus + return f +endfunction + +function! s:LoadDB(prepend_path) + cs kill -1 + + if g:cscope_search_case_insensitive == 1 + exe 'cs add '.s:cscope_vim_db_dir.'/'.s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_id].'.db '.a:prepend_path.' -C' + echo 'cscope db '.s:cscope_vim_db_dir.'/'.s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_id].'.db added, searching case insensitively.' + + if filereadable(s:cscope_vim_db_dir.'/'.s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_id].'_inc.db') + exe 'cs add '.s:cscope_vim_db_dir.'/'.s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_id].'_inc.db '.a:prepend_path.' -C' + echo 'cscope db '.s:cscope_vim_db_dir.'/'.s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_id].'_inc.db added, searching case insensitively.' + endif + else + exe 'cs add '.s:cscope_vim_db_dir.'/'.s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_id].'.db' + echo 'cscope db '.s:cscope_vim_db_dir.'/'.s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_id].'.db added, searching case sensitively.' + + if filereadable(s:cscope_vim_db_dir.'/'.s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_id].'_inc.db') + exe 'cs add '.s:cscope_vim_db_dir.'/'.s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_id].'_inc.db' + echo 'cscope db '.s:cscope_vim_db_dir.'/'.s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_id].'_inc.db added, searching case sensitively.' + endif + endif + + let s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_loadtimes] = s:dbs[a:prepend_path][s:cscope_vim_db_entry_key_loadtimes] + 1 + let s:cscope_vim_db_current_prepend_path = a:prepend_path + + call FlushIndex() +endfunction + +function! s:LoadIndex() + " s:dbs = { 'prepend1': {'id': '', + " 'loadtimes': '', + " 'dirty': 0|1, + " 'depedency': '...;...'}, + " ... + " } let s:dbs = {} - if ! isdirectory(s:cscope_vim_dir) - call mkdir(s:cscope_vim_dir) - elseif filereadable(s:index_file) - let idx = readfile(s:index_file) + + if ! isdirectory(s:cscope_vim_db_dir) + call mkdir(s:cscope_vim_db_dir) + elseif filereadable(s:cscope_vim_db_index_file) + let idx = readfile(s:cscope_vim_db_index_file) + for i in idx let e = split(i, '|') - if len(e) == 0 - call delete(s:index_file) + + if len(e) != s:cscope_vim_db_entry_len call RmDBfiles() else - let db_file = s:cscope_vim_dir.'/'.e[1].'.db' - if filereadable(db_file) - if isdirectory(e[0]) - let s:dbs[e[0]] = {} - let s:dbs[e[0]]['id'] = e[1] - let s:dbs[e[0]]['loadtimes'] = e[2] - let s:dbs[e[0]]['dirty'] = (len(e) > 3) ? e[3] :0 + let l:db_file = s:cscope_vim_db_dir.'/'.e[s:cscope_vim_db_entry_idx_id].'.db' + let l:db_file_list = s:cscope_vim_db_dir.'/'.e[s:cscope_vim_db_entry_idx_id].'.files' + + if filereadable(l:db_file) + if isdirectory(e[s:cscope_vim_db_entry_idx_prepend_path]) + let s:dbs[e[s:cscope_vim_db_entry_idx_prepend_path]] = {} + let s:dbs[e[s:cscope_vim_db_entry_idx_prepend_path]][s:cscope_vim_db_entry_key_id] = e[s:cscope_vim_db_entry_idx_id] + let s:dbs[e[s:cscope_vim_db_entry_idx_prepend_path]][s:cscope_vim_db_entry_key_loadtimes] = e[s:cscope_vim_db_entry_idx_loadtimes] + let s:dbs[e[s:cscope_vim_db_entry_idx_prepend_path]][s:cscope_vim_db_entry_key_dirty] = e[s:cscope_vim_db_entry_idx_dirty] + let s:dbs[e[s:cscope_vim_db_entry_idx_prepend_path]][s:cscope_vim_db_entry_key_depedency] = e[s:cscope_vim_db_entry_idx_depedency] else - call delete(db_file) + call delete(l:db_file) + call delete(l:db_file_list) endif endif endif @@ -272,75 +381,43 @@ function! s:loadIndex() endif endfunction -function! s:preloadDB() - let dirs = split(g:cscope_preload_path, ';') - for m_dir in dirs - let m_dir = CheckAbsolutePath(m_dir, m_dir) - if ! has_key(s:dbs, m_dir) - call InitDB(m_dir) - endif - call LoadDB(m_dir) - endfor -endfunction +function! s:RmDBfiles() + let odbs = split(globpath(s:cscope_vim_db_dir, "*"), "\n") -function! CscopeFind(action, word) - let dirtyDirs = [] - for d in keys(s:dbs) - if s:dbs[d]['dirty'] == 1 - call add(dirtyDirs, d) - endif + for f in odbs + call delete(f) endfor - if len(dirtyDirs) > 0 - call updateDBs(dirtyDirs) - endif - call AutoloadDB(expand('%:p:h')) - try - exe ':lcs f '.a:action.' '.a:word - if g:cscope_open_location == 1 - lw - endif - catch - echohl WarningMsg | echo 'Can not find '.a:word.' with querytype as '.a:action.'.' | echohl None - endtry endfunction -function! CscopeFindInteractive(pat) - call inputsave() - let qt = input("\nChoose a querytype for '".a:pat."'(:help cscope-find)\n c: functions calling this function\n d: functions called by this function\n e: this egrep pattern\n f: this file\n g: this definition\n i: files #including this file\n s: this C symbol\n t: this text string\n\n or\n to query `pattern` instead of '".a:pat."' as `querytype`, Ex. `smain` to query a C symbol named 'main'.\n> ") - call inputrestore() - if len(qt) > 1 - call CscopeFind(qt[0], qt[1:]) - elseif len(qt) > 0 - call CscopeFind(qt, a:pat) - endif - call feedkeys("\") -endfunction +if !exists('g:cscope_open_location') + let g:cscope_open_location = 1 +endif -function! s:onChange() - if expand('%:t') =~? g:cscope_interested_files - let m_dir = GetBestPath(expand('%:p:h')) - if m_dir != "" - let s:dbs[m_dir]['dirty'] = 1 - call FlushIndex() - call CheckNewFile(m_dir, expand('%:p')) - redraw - call echo('Your cscope db will be updated automatically, you can turn off this message by setting g:cscope_silent 1.') - endif - endif -endfunction +if !exists('g:cscope_search_case_insensitive') + let g:cscope_search_case_insensitive = 0 +endif -function! CscopeUpdateDB() - call updateDBs(keys(s:dbs)) -endfunction -if exists('g:cscope_preload_path') - call preloadDB() +if !exists('g:cscope_cmd') + if executable('cscope') + let g:cscope_cmd = 'cscope' + else + echo 'cscope: command not found' + finish + endif endif -if g:cscope_auto_update == 1 - au BufWritePost * call onChange() +if !exists('g:cscope_interested_files') + let files = readfile(expand(":p:h")."/interested.txt") + let g:cscope_interested_files = join(map(files, 'v:val."$"'), '\|') endif +command! -nargs=0 CscopeClearAllDB call ClearDBs(-1) +command! -nargs=0 CscopeClearCurrentDB call ClearDBs(0) +command! -nargs=0 CscopeList call ListDBs() +command! -nargs=0 CscopeRebuildCurrentDB call CscopeRebuildCurrentDB() +command! -nargs=0 CscopeUpdateAllDB call CscopeUpdateAllDB() +command! -nargs=0 CscopeUpdateCurrentDB call CscopeUpdateCurrentDB() + set cscopequickfix=s-,g-,d-,c-,t-,e-,f-,i- -com! -nargs=0 CscopeClear call clearDBs() -com! -nargs=0 CscopeList call listDBs() -call loadIndex() +call LoadIndex() +