From acca348959f2fbd25f0a328b223ccda97c62c47e Mon Sep 17 00:00:00 2001 From: Jan Larres Date: Tue, 23 Apr 2013 18:07:29 +1200 Subject: [PATCH] Allow project-specific type config --- autoload/tagbar.vim | 139 +++++++++++++++++++++++--------------------- doc/tagbar.txt | 24 ++++++++ 2 files changed, 96 insertions(+), 67 deletions(-) diff --git a/autoload/tagbar.vim b/autoload/tagbar.vim index 20575e8..f422eef 100644 --- a/autoload/tagbar.vim +++ b/autoload/tagbar.vim @@ -819,47 +819,13 @@ function! s:LoadUserTypeDefs(...) abort let defdict = tagbar#getusertypes() endif - " Transform the 'kind' definitions into dictionary format - for def in values(defdict) - if has_key(def, 'kinds') - let kinds = def.kinds - let def.kinds = [] - for kind in kinds - let kindlist = split(kind, ':') - let kinddict = {'short' : kindlist[0], 'long' : kindlist[1]} - if len(kindlist) == 4 - let kinddict.fold = kindlist[2] - let kinddict.stl = kindlist[3] - elseif len(kindlist) == 3 - let kinddict.fold = kindlist[2] - let kinddict.stl = 1 - else - let kinddict.fold = 0 - let kinddict.stl = 1 - endif - call add(def.kinds, kinddict) - endfor - endif - - " If the user only specified one of kind2scope and scope2kind use it - " to generate the other one - if has_key(def, 'kind2scope') && !has_key(def, 'scope2kind') - let def.scope2kind = {} - for [key, value] in items(def.kind2scope) - let def.scope2kind[value] = key - endfor - elseif has_key(def, 'scope2kind') && !has_key(def, 'kind2scope') - let def.kind2scope = {} - for [key, value] in items(def.scope2kind) - let def.kind2scope[value] = key - endfor - endif + let transformed = {} + for [type, def] in items(defdict) + let transformed[type] = s:TransformUserTypeDef(def) endfor - unlet! key value - for [key, value] in items(defdict) - if !has_key(s:known_types, key) || - \ (has_key(value, 'replace') && value.replace) + for [key, value] in items(transformed) + if !has_key(s:known_types, key) || get(value, 'replace', 0) let s:known_types[key] = s:TypeInfo.New(value) else call extend(s:known_types[key], value) @@ -871,7 +837,42 @@ function! s:LoadUserTypeDefs(...) abort endif endfunction +" s:TransformUserTypeDef() {{{2 +" Transform the user definitions into the internal format +function! s:TransformUserTypeDef(def) abort + let newdef = copy(a:def) + + if has_key(a:def, 'kinds') + let newdef.kinds = [] + let kinds = a:def.kinds + for kind in kinds + let kindlist = split(kind, ':') + let kinddict = {'short' : kindlist[0], 'long' : kindlist[1]} + let kinddict.fold = get(kindlist, 2, 0) + let kinddict.stl = get(kindlist, 3, 1) + call add(newdef.kinds, kinddict) + endfor + endif + + " If the user only specified one of kind2scope and scope2kind then use it + " to generate the respective other + if has_key(a:def, 'kind2scope') && !has_key(a:def, 'scope2kind') + let newdef.scope2kind = {} + for [key, value] in items(a:def.kind2scope) + let newdef.scope2kind[value] = key + endfor + elseif has_key(a:def, 'scope2kind') && !has_key(a:def, 'kind2scope') + let newdef.kind2scope = {} + for [key, value] in items(a:def.scope2kind) + let newdef.kind2scope[value] = key + endfor + endif + + return newdef +endfunction + " s:CreateTypeKinddict() {{{2 +" TODO: make instance method function! s:CreateTypeKinddict(type) abort " Create a dictionary of the kind order for fast access in sorting " functions @@ -968,7 +969,7 @@ function! s:CreateAutocommands() abort autocmd BufEnter __Tagbar__ nested call s:QuitIfOnlyWindow() autocmd CursorHold __Tagbar__ call s:ShowPrototype(1) - autocmd BufReadPost,BufWritePost * call + autocmd BufWritePost * call \ s:AutoUpdate(fnamemodify(expand(''), ':p'), 1) autocmd BufEnter,CursorHold,FileType * call \ s:AutoUpdate(fnamemodify(expand(''), ':p'), 0) @@ -1507,7 +1508,7 @@ endfunction let s:FileInfo = {} " s:FileInfo.New() {{{3 -function! s:FileInfo.New(fname, ftype) abort dict +function! s:FileInfo.New(fname, ftype, typeinfo) abort dict let newobj = copy(self) " The complete file path @@ -1533,10 +1534,9 @@ function! s:FileInfo.New(fname, ftype) abort dict " Dictionary of the folding state of 'kind's, indexed by short name let newobj.kindfolds = {} - let typeinfo = s:known_types[a:ftype] - let newobj.typeinfo = typeinfo + let newobj.typeinfo = a:typeinfo " copy the default fold state from the type info - for kind in typeinfo.kinds + for kind in a:typeinfo.kinds let newobj.kindfolds[kind.short] = \ g:tagbar_foldlevel == 0 ? 1 : kind.fold endfor @@ -1544,7 +1544,7 @@ function! s:FileInfo.New(fname, ftype) abort dict " Dictionary of dictionaries of the folding state of individual tags, " indexed by kind and full path let newobj.tagfolds = {} - for kind in typeinfo.kinds + for kind in a:typeinfo.kinds let newobj.tagfolds[kind.short] = {} endfor @@ -1566,8 +1566,7 @@ function! s:FileInfo.reset() abort dict let self._tagfolds_old = self.tagfolds let self.tagfolds = {} - let typeinfo = s:known_types[self.ftype] - for kind in typeinfo.kinds + for kind in self.typeinfo.kinds let self.tagfolds[kind.short] = {} endfor endfunction @@ -1905,13 +1904,22 @@ function! s:ProcessFile(fname, ftype) abort return endif + let typeinfo = s:known_types[a:ftype] + " If the file has only been updated preserve the fold states, otherwise " create a new entry if s:known_files.has(a:fname) && !empty(s:known_files.get(a:fname)) let fileinfo = s:known_files.get(a:fname) + let typeinfo = fileinfo.typeinfo call fileinfo.reset() else - let fileinfo = s:FileInfo.New(a:fname, a:ftype) + silent! execute 'doautocmd TagbarProjects User ' . a:fname + if exists('b:tagbar_type') + let typeinfo = extend(copy(typeinfo), + \ s:TransformUserTypeDef(b:tagbar_type)) + call s:CreateTypeKinddict(typeinfo) + endif + let fileinfo = s:FileInfo.New(a:fname, a:ftype, typeinfo) endif " Use a temporary files for ctags processing instead of the original one. @@ -1926,7 +1934,7 @@ function! s:ProcessFile(fname, ftype) abort call writefile(getbufline(fileinfo.bufnr, 1, '$'), tempfile) let fileinfo.mtime = getftime(tempfile) - let ctags_output = s:ExecuteCtagsOnFile(tempfile, a:fname, a:ftype) + let ctags_output = s:ExecuteCtagsOnFile(tempfile, a:fname, typeinfo) call delete(tempfile) @@ -1940,12 +1948,11 @@ function! s:ProcessFile(fname, ftype) abort call s:LogDebugMessage('Ctags output empty') " No need to go through the tag processing if there are no tags, and " preserving the old fold state isn't necessary either - call s:known_files.put(s:FileInfo.New(a:fname, a:ftype), a:fname) + call s:known_files.put(s:FileInfo.New(a:fname, a:ftype, + \ s:known_types[a:ftype]), a:fname) return endif - let typeinfo = fileinfo.typeinfo - call s:LogDebugMessage('Filetype tag kinds: ' . \ string(keys(typeinfo.kinddict))) @@ -2025,17 +2032,15 @@ function! s:ProcessFile(fname, ftype) abort endfunction " s:ExecuteCtagsOnFile() {{{2 -function! s:ExecuteCtagsOnFile(fname, realfname, ftype) abort +function! s:ExecuteCtagsOnFile(fname, realfname, typeinfo) abort call s:LogDebugMessage('ExecuteCtagsOnFile called [' . a:fname . ']') - let typeinfo = s:known_types[a:ftype] - - if has_key(typeinfo, 'ctagsargs') && type(typeinfo.ctagsargs)==type(' ') - "if ctagsargs is a string, prepend and append space seperators - let ctags_args = ' ' . typeinfo.ctagsargs . ' ' - elseif has_key(typeinfo, 'ctagsargs') && type(typeinfo.ctagsargs)==type([]) - let ctags_args = typeinfo.ctagsargs - "otherwise ctagsargs is not defined or not defined as a valid type + if has_key(a:typeinfo, 'ctagsargs') && type(a:typeinfo.ctagsargs) == type('') + " if ctagsargs is a string, prepend and append space separators + let ctags_args = ' ' . a:typeinfo.ctagsargs . ' ' + elseif has_key(a:typeinfo, 'ctagsargs') && type(a:typeinfo.ctagsargs) == type([]) + let ctags_args = a:typeinfo.ctagsargs + " otherwise ctagsargs is not defined or not defined as a valid type else "Prefer constructing ctags_args as a list rather than a string "See s:EscapeCtagsCmd() - It's a best practice to shellescape() @@ -2053,14 +2058,14 @@ function! s:ExecuteCtagsOnFile(fname, realfname, ftype) abort \ ] " Include extra type definitions - if has_key(typeinfo, 'deffile') - let ctags_args += ['--options=' . typeinfo.deffile] + if has_key(a:typeinfo, 'deffile') + let ctags_args += ['--options=' . a:typeinfo.deffile] endif - let ctags_type = typeinfo.ctagstype + let ctags_type = a:typeinfo.ctagstype let ctags_kinds = '' - for kind in typeinfo.kinds + for kind in a:typeinfo.kinds let ctags_kinds .= kind.short endfor @@ -2068,11 +2073,11 @@ function! s:ExecuteCtagsOnFile(fname, realfname, ftype) abort let ctags_args += ['--' . ctags_type . '-kinds=' . ctags_kinds] endif - if has_key(typeinfo, 'ctagsbin') + if has_key(a:typeinfo, 'ctagsbin') " reset 'wildignore' temporarily in case *.exe is included in it let wildignore_save = &wildignore set wildignore& - let ctags_bin = expand(typeinfo.ctagsbin) + let ctags_bin = expand(a:typeinfo.ctagsbin) let &wildignore = wildignore_save else let ctags_bin = g:tagbar_ctags_bin diff --git a/doc/tagbar.txt b/doc/tagbar.txt index ef48e3e..52172c5 100644 --- a/doc/tagbar.txt +++ b/doc/tagbar.txt @@ -980,6 +980,30 @@ LaTeX parser that works better than the example configuration presented here. So if you are using a development build newer than that or a stable version newer than 5.8 you should use the built-in support instead of this example. +Project-specific configuration~ + +In addition to the normal global configuration it is also possible to have +project-specific settings. This is mostly useful for additional ctags options, +like for example macros to ignore. Or maybe you want to do things like folding +certain tag kinds in some projects. + +In order to use this feature you need to create User |autocommand|s in an +augroup called "TagbarProjects" and have it create a buffer-local variable +called "b:tagbar_type". This variable has to hold a type definition just like +the normal ones described in this chapter. This definition will then be +applied only to the files matched by the autocommand. + +Note that there can be multiple definitions of the augroup with their own +autocommands (for example in separate project directories); they will get +merged automatically by Vim. + +Example: +> + augroup TagbarProjects + autocmd User ~/myproject/*.c let b:tagbar_type = {'deffile' : '~/myproject/ctags.cnf'} + augroup END +< + Writing your own tag-generating program~ If you want to write your own program for generating tags then here are some imporant tips to get it to integrate well with Tagbar: