tagbar/autoload/tagbar.vim
Dan Church 40be9a2e5f Suppress status-line output when executing shell
Failing to suppress output can cause problems in some environments,
especially if the shell command fails or does something else spooky.

Example where failing to suppress the shell command causes issues with
[vim-airline](https://github.com/bling/vim-airline) with the tagbar
extension enabled:

![](http://i.imgur.com/ciigs8C.png)

Here's another example:

![](http://i.imgur.com/cl96sI8.png)

Both were taken using uxterm in Linux.
2015-04-24 17:44:58 -05:00

4264 lines
136 KiB
VimL

" ============================================================================
" File: tagbar.vim
" Description: List the current file's tags in a sidebar, ordered by class etc
" Author: Jan Larres <jan@majutsushi.net>
" Licence: Vim licence
" Website: http://majutsushi.github.com/tagbar/
" Version: 2.6.1
" Note: This plugin was heavily inspired by the 'Taglist' plugin by
" Yegappan Lakshmanan and uses a small amount of code from it.
"
" Original taglist copyright notice:
" 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,
" taglist.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.
" ============================================================================
scriptencoding utf-8
" Initialization {{{1
" If another plugin calls an autoloaded Tagbar function on startup before the
" plugin/tagbar.vim file got loaded, load it explicitly
if exists(':Tagbar') == 0
runtime plugin/tagbar.vim
endif
if exists(':Tagbar') == 0
echomsg 'Tagbar: Could not load plugin code, check your runtimepath!'
finish
endif
" Basic init {{{2
redir => s:ftype_out
silent filetype
redir END
if s:ftype_out !~# 'detection:ON'
echomsg 'Tagbar: Filetype detection is turned off, skipping plugin'
unlet s:ftype_out
finish
endif
unlet s:ftype_out
let s:icon_closed = g:tagbar_iconchars[0]
let s:icon_open = g:tagbar_iconchars[1]
let s:type_init_done = 0
let s:autocommands_done = 0
let s:statusline_in_use = 0
" 0: not checked yet; 1: checked and found; 2: checked and not found
let s:checked_ctags = 0
let s:checked_ctags_types = 0
let s:ctags_types = {}
let s:new_window = 1
let s:is_maximized = 0
let s:winrestcmd = ''
let s:short_help = 1
let s:nearby_disabled = 0
let s:paused = 0
let s:pwin_by_tagbar = 0
let s:window_expanded = 0
let s:expand_bufnr = -1
let s:window_pos = {
\ 'pre' : { 'x' : 0, 'y' : 0 },
\ 'post' : { 'x' : 0, 'y' : 0 }
\}
" Script-local variable needed since compare functions can't
" take extra arguments
let s:compare_typeinfo = {}
let s:visibility_symbols = {
\ 'public' : '+',
\ 'protected' : '#',
\ 'private' : '-'
\ }
let g:loaded_tagbar = 1
let s:last_highlight_tline = 0
let s:debug = 0
let s:debug_file = ''
let s:warnings = {
\ 'type': [],
\ 'encoding': 0
\ }
" s:Init() {{{2
function! s:Init(silent) abort
if s:checked_ctags == 2 && a:silent
return 0
elseif s:checked_ctags != 1
if !s:CheckForExCtags(a:silent)
return 0
endif
endif
if !s:checked_ctags_types
call s:GetSupportedFiletypes()
endif
if !s:type_init_done
call s:InitTypes()
endif
if !s:autocommands_done
call s:CreateAutocommands()
call s:AutoUpdate(fnamemodify(expand('%'), ':p'), 0)
endif
return 1
endfunction
" s:InitTypes() {{{2
function! s:InitTypes() abort
call s:debug('Initializing types')
let s:known_types = {}
" Ant {{{3
let type_ant = s:TypeInfo.New()
let type_ant.ctagstype = 'ant'
let type_ant.kinds = [
\ {'short' : 'p', 'long' : 'projects', 'fold' : 0, 'stl' : 1},
\ {'short' : 't', 'long' : 'targets', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.ant = type_ant
" Asm {{{3
let type_asm = s:TypeInfo.New()
let type_asm.ctagstype = 'asm'
let type_asm.kinds = [
\ {'short' : 'm', 'long' : 'macros', 'fold' : 0, 'stl' : 1},
\ {'short' : 't', 'long' : 'types', 'fold' : 0, 'stl' : 1},
\ {'short' : 'd', 'long' : 'defines', 'fold' : 0, 'stl' : 1},
\ {'short' : 'l', 'long' : 'labels', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.asm = type_asm
" ASP {{{3
let type_aspvbs = s:TypeInfo.New()
let type_aspvbs.ctagstype = 'asp'
let type_aspvbs.kinds = [
\ {'short' : 'd', 'long' : 'constants', 'fold' : 0, 'stl' : 1},
\ {'short' : 'c', 'long' : 'classes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 's', 'long' : 'subroutines', 'fold' : 0, 'stl' : 1},
\ {'short' : 'v', 'long' : 'variables', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.aspvbs = type_aspvbs
" Awk {{{3
let type_awk = s:TypeInfo.New()
let type_awk.ctagstype = 'awk'
let type_awk.kinds = [
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.awk = type_awk
" Basic {{{3
let type_basic = s:TypeInfo.New()
let type_basic.ctagstype = 'basic'
let type_basic.kinds = [
\ {'short' : 'c', 'long' : 'constants', 'fold' : 0, 'stl' : 1},
\ {'short' : 'g', 'long' : 'enumerations', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'l', 'long' : 'labels', 'fold' : 0, 'stl' : 1},
\ {'short' : 't', 'long' : 'types', 'fold' : 0, 'stl' : 1},
\ {'short' : 'v', 'long' : 'variables', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.basic = type_basic
" BETA {{{3
let type_beta = s:TypeInfo.New()
let type_beta.ctagstype = 'beta'
let type_beta.kinds = [
\ {'short' : 'f', 'long' : 'fragments', 'fold' : 0, 'stl' : 1},
\ {'short' : 's', 'long' : 'slots', 'fold' : 0, 'stl' : 1},
\ {'short' : 'v', 'long' : 'patterns', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.beta = type_beta
" C {{{3
let type_c = s:TypeInfo.New()
let type_c.ctagstype = 'c'
let type_c.kinds = [
\ {'short' : 'd', 'long' : 'macros', 'fold' : 1, 'stl' : 0},
\ {'short' : 'p', 'long' : 'prototypes', 'fold' : 1, 'stl' : 0},
\ {'short' : 'g', 'long' : 'enums', 'fold' : 0, 'stl' : 1},
\ {'short' : 'e', 'long' : 'enumerators', 'fold' : 0, 'stl' : 0},
\ {'short' : 't', 'long' : 'typedefs', 'fold' : 0, 'stl' : 0},
\ {'short' : 's', 'long' : 'structs', 'fold' : 0, 'stl' : 1},
\ {'short' : 'u', 'long' : 'unions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'm', 'long' : 'members', 'fold' : 0, 'stl' : 0},
\ {'short' : 'v', 'long' : 'variables', 'fold' : 0, 'stl' : 0},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1}
\ ]
let type_c.sro = '::'
let type_c.kind2scope = {
\ 'g' : 'enum',
\ 's' : 'struct',
\ 'u' : 'union'
\ }
let type_c.scope2kind = {
\ 'enum' : 'g',
\ 'struct' : 's',
\ 'union' : 'u'
\ }
let s:known_types.c = type_c
" C++ {{{3
let type_cpp = s:TypeInfo.New()
let type_cpp.ctagstype = 'c++'
let type_cpp.kinds = [
\ {'short' : 'd', 'long' : 'macros', 'fold' : 1, 'stl' : 0},
\ {'short' : 'p', 'long' : 'prototypes', 'fold' : 1, 'stl' : 0},
\ {'short' : 'g', 'long' : 'enums', 'fold' : 0, 'stl' : 1},
\ {'short' : 'e', 'long' : 'enumerators', 'fold' : 0, 'stl' : 0},
\ {'short' : 't', 'long' : 'typedefs', 'fold' : 0, 'stl' : 0},
\ {'short' : 'n', 'long' : 'namespaces', 'fold' : 0, 'stl' : 1},
\ {'short' : 'c', 'long' : 'classes', 'fold' : 0, 'stl' : 1},
\ {'short' : 's', 'long' : 'structs', 'fold' : 0, 'stl' : 1},
\ {'short' : 'u', 'long' : 'unions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'm', 'long' : 'members', 'fold' : 0, 'stl' : 0},
\ {'short' : 'v', 'long' : 'variables', 'fold' : 0, 'stl' : 0}
\ ]
let type_cpp.sro = '::'
let type_cpp.kind2scope = {
\ 'g' : 'enum',
\ 'n' : 'namespace',
\ 'c' : 'class',
\ 's' : 'struct',
\ 'u' : 'union'
\ }
let type_cpp.scope2kind = {
\ 'enum' : 'g',
\ 'namespace' : 'n',
\ 'class' : 'c',
\ 'struct' : 's',
\ 'union' : 'u'
\ }
let s:known_types.cpp = type_cpp
let s:known_types.cuda = type_cpp
" C# {{{3
let type_cs = s:TypeInfo.New()
let type_cs.ctagstype = 'c#'
let type_cs.kinds = [
\ {'short' : 'd', 'long' : 'macros', 'fold' : 1, 'stl' : 0},
\ {'short' : 'f', 'long' : 'fields', 'fold' : 0, 'stl' : 1},
\ {'short' : 'g', 'long' : 'enums', 'fold' : 0, 'stl' : 1},
\ {'short' : 'e', 'long' : 'enumerators', 'fold' : 0, 'stl' : 0},
\ {'short' : 't', 'long' : 'typedefs', 'fold' : 0, 'stl' : 1},
\ {'short' : 'n', 'long' : 'namespaces', 'fold' : 0, 'stl' : 1},
\ {'short' : 'i', 'long' : 'interfaces', 'fold' : 0, 'stl' : 1},
\ {'short' : 'c', 'long' : 'classes', 'fold' : 0, 'stl' : 1},
\ {'short' : 's', 'long' : 'structs', 'fold' : 0, 'stl' : 1},
\ {'short' : 'E', 'long' : 'events', 'fold' : 0, 'stl' : 1},
\ {'short' : 'm', 'long' : 'methods', 'fold' : 0, 'stl' : 1},
\ {'short' : 'p', 'long' : 'properties', 'fold' : 0, 'stl' : 1}
\ ]
let type_cs.sro = '.'
let type_cs.kind2scope = {
\ 'n' : 'namespace',
\ 'i' : 'interface',
\ 'c' : 'class',
\ 's' : 'struct',
\ 'g' : 'enum'
\ }
let type_cs.scope2kind = {
\ 'namespace' : 'n',
\ 'interface' : 'i',
\ 'class' : 'c',
\ 'struct' : 's',
\ 'enum' : 'g'
\ }
let s:known_types.cs = type_cs
" COBOL {{{3
let type_cobol = s:TypeInfo.New()
let type_cobol.ctagstype = 'cobol'
let type_cobol.kinds = [
\ {'short' : 'd', 'long' : 'data items', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'file descriptions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'g', 'long' : 'group items', 'fold' : 0, 'stl' : 1},
\ {'short' : 'p', 'long' : 'paragraphs', 'fold' : 0, 'stl' : 1},
\ {'short' : 'P', 'long' : 'program ids', 'fold' : 0, 'stl' : 1},
\ {'short' : 's', 'long' : 'sections', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.cobol = type_cobol
" DOS Batch {{{3
let type_dosbatch = s:TypeInfo.New()
let type_dosbatch.ctagstype = 'dosbatch'
let type_dosbatch.kinds = [
\ {'short' : 'l', 'long' : 'labels', 'fold' : 0, 'stl' : 1},
\ {'short' : 'v', 'long' : 'variables', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.dosbatch = type_dosbatch
" Eiffel {{{3
let type_eiffel = s:TypeInfo.New()
let type_eiffel.ctagstype = 'eiffel'
let type_eiffel.kinds = [
\ {'short' : 'c', 'long' : 'classes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'features', 'fold' : 0, 'stl' : 1}
\ ]
let type_eiffel.sro = '.' " Not sure, is nesting even possible?
let type_eiffel.kind2scope = {
\ 'c' : 'class',
\ 'f' : 'feature'
\ }
let type_eiffel.scope2kind = {
\ 'class' : 'c',
\ 'feature' : 'f'
\ }
let s:known_types.eiffel = type_eiffel
" Erlang {{{3
let type_erlang = s:TypeInfo.New()
let type_erlang.ctagstype = 'erlang'
let type_erlang.kinds = [
\ {'short' : 'm', 'long' : 'modules', 'fold' : 0, 'stl' : 1},
\ {'short' : 'd', 'long' : 'macro definitions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'r', 'long' : 'record definitions', 'fold' : 0, 'stl' : 1}
\ ]
let type_erlang.sro = '.' " Not sure, is nesting even possible?
let type_erlang.kind2scope = {
\ 'm' : 'module'
\ }
let type_erlang.scope2kind = {
\ 'module' : 'm'
\ }
let s:known_types.erlang = type_erlang
" Flex {{{3
" Vim doesn't support Flex out of the box, this is based on rough
" guesses and probably requires
" http://www.vim.org/scripts/script.php?script_id=2909
" Improvements welcome!
let type_as = s:TypeInfo.New()
let type_as.ctagstype = 'flex'
let type_as.kinds = [
\ {'short' : 'v', 'long' : 'global variables', 'fold' : 0, 'stl' : 0},
\ {'short' : 'c', 'long' : 'classes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'm', 'long' : 'methods', 'fold' : 0, 'stl' : 1},
\ {'short' : 'p', 'long' : 'properties', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'x', 'long' : 'mxtags', 'fold' : 0, 'stl' : 0}
\ ]
let type_as.sro = '.'
let type_as.kind2scope = {
\ 'c' : 'class'
\ }
let type_as.scope2kind = {
\ 'class' : 'c'
\ }
let s:known_types.mxml = type_as
let s:known_types.actionscript = type_as
" Fortran {{{3
let type_fortran = s:TypeInfo.New()
let type_fortran.ctagstype = 'fortran'
let type_fortran.kinds = [
\ {'short' : 'm', 'long' : 'modules', 'fold' : 0, 'stl' : 1},
\ {'short' : 'p', 'long' : 'programs', 'fold' : 0, 'stl' : 1},
\ {'short' : 'k', 'long' : 'components', 'fold' : 0, 'stl' : 1},
\ {'short' : 't', 'long' : 'derived types and structures', 'fold' : 0,
\ 'stl' : 1},
\ {'short' : 'c', 'long' : 'common blocks', 'fold' : 0, 'stl' : 1},
\ {'short' : 'b', 'long' : 'block data', 'fold' : 0, 'stl' : 0},
\ {'short' : 'e', 'long' : 'entry points', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 's', 'long' : 'subroutines', 'fold' : 0, 'stl' : 1},
\ {'short' : 'l', 'long' : 'labels', 'fold' : 0, 'stl' : 1},
\ {'short' : 'n', 'long' : 'namelists', 'fold' : 0, 'stl' : 1},
\ {'short' : 'v', 'long' : 'variables', 'fold' : 0, 'stl' : 0}
\ ]
let type_fortran.sro = '.' " Not sure, is nesting even possible?
let type_fortran.kind2scope = {
\ 'm' : 'module',
\ 'p' : 'program',
\ 'f' : 'function',
\ 's' : 'subroutine'
\ }
let type_fortran.scope2kind = {
\ 'module' : 'm',
\ 'program' : 'p',
\ 'function' : 'f',
\ 'subroutine' : 's'
\ }
let s:known_types.fortran = type_fortran
" HTML {{{3
let type_html = s:TypeInfo.New()
let type_html.ctagstype = 'html'
let type_html.kinds = [
\ {'short' : 'f', 'long' : 'JavaScript funtions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'a', 'long' : 'named anchors', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.html = type_html
" Java {{{3
let type_java = s:TypeInfo.New()
let type_java.ctagstype = 'java'
let type_java.kinds = [
\ {'short' : 'p', 'long' : 'packages', 'fold' : 1, 'stl' : 0},
\ {'short' : 'f', 'long' : 'fields', 'fold' : 0, 'stl' : 0},
\ {'short' : 'g', 'long' : 'enum types', 'fold' : 0, 'stl' : 1},
\ {'short' : 'e', 'long' : 'enum constants', 'fold' : 0, 'stl' : 0},
\ {'short' : 'i', 'long' : 'interfaces', 'fold' : 0, 'stl' : 1},
\ {'short' : 'c', 'long' : 'classes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'm', 'long' : 'methods', 'fold' : 0, 'stl' : 1}
\ ]
let type_java.sro = '.'
let type_java.kind2scope = {
\ 'g' : 'enum',
\ 'i' : 'interface',
\ 'c' : 'class'
\ }
let type_java.scope2kind = {
\ 'enum' : 'g',
\ 'interface' : 'i',
\ 'class' : 'c'
\ }
let s:known_types.java = type_java
" JavaScript {{{3
" jsctags/doctorjs will be used if available.
let type_javascript = s:TypeInfo.New()
let type_javascript.ctagstype = 'javascript'
let jsctags = s:CheckFTCtags('jsctags', 'javascript')
if jsctags != ''
let type_javascript.kinds = [
\ {'short' : 'v', 'long' : 'variables', 'fold' : 0, 'stl' : 0},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1}
\ ]
let type_javascript.sro = '.'
let type_javascript.kind2scope = {
\ 'v' : 'namespace',
\ 'f' : 'namespace'
\ }
let type_javascript.scope2kind = {
\ 'namespace' : 'v'
\ }
let type_javascript.ctagsbin = jsctags
let type_javascript.ctagsargs = '-f -'
else
let type_javascript.kinds = [
\ {'short': 'v', 'long': 'global variables', 'fold': 0, 'stl': 0},
\ {'short': 'c', 'long': 'classes', 'fold': 0, 'stl': 1},
\ {'short': 'p', 'long': 'properties', 'fold': 0, 'stl': 0},
\ {'short': 'm', 'long': 'methods', 'fold': 0, 'stl': 1},
\ {'short': 'f', 'long': 'functions', 'fold': 0, 'stl': 1},
\ ]
let type_javascript.sro = '.'
let type_javascript.kind2scope = {
\ 'c' : 'class',
\ 'f' : 'function',
\ 'm' : 'method',
\ 'p' : 'property',
\ }
let type_javascript.scope2kind = {
\ 'class' : 'c',
\ 'function' : 'f',
\ }
endif
let s:known_types.javascript = type_javascript
" Lisp {{{3
let type_lisp = s:TypeInfo.New()
let type_lisp.ctagstype = 'lisp'
let type_lisp.kinds = [
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.lisp = type_lisp
let s:known_types.clojure = type_lisp
" Lua {{{3
let type_lua = s:TypeInfo.New()
let type_lua.ctagstype = 'lua'
let type_lua.kinds = [
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.lua = type_lua
" Make {{{3
let type_make = s:TypeInfo.New()
let type_make.ctagstype = 'make'
let type_make.kinds = [
\ {'short' : 'm', 'long' : 'macros', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.make = type_make
" Matlab {{{3
let type_matlab = s:TypeInfo.New()
let type_matlab.ctagstype = 'matlab'
let type_matlab.kinds = [
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.matlab = type_matlab
" Ocaml {{{3
let type_ocaml = s:TypeInfo.New()
let type_ocaml.ctagstype = 'ocaml'
let type_ocaml.kinds = [
\ {'short' : 'M', 'long' : 'modules or functors', 'fold' : 0, 'stl' : 1},
\ {'short' : 'v', 'long' : 'global variables', 'fold' : 0, 'stl' : 0},
\ {'short' : 'c', 'long' : 'classes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'C', 'long' : 'constructors', 'fold' : 0, 'stl' : 1},
\ {'short' : 'm', 'long' : 'methods', 'fold' : 0, 'stl' : 1},
\ {'short' : 'e', 'long' : 'exceptions', 'fold' : 0, 'stl' : 1},
\ {'short' : 't', 'long' : 'type names', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'r', 'long' : 'structure fields', 'fold' : 0, 'stl' : 0}
\ ]
let type_ocaml.sro = '.' " Not sure, is nesting even possible?
let type_ocaml.kind2scope = {
\ 'M' : 'Module',
\ 'c' : 'class',
\ 't' : 'type'
\ }
let type_ocaml.scope2kind = {
\ 'Module' : 'M',
\ 'class' : 'c',
\ 'type' : 't'
\ }
let s:known_types.ocaml = type_ocaml
" Pascal {{{3
let type_pascal = s:TypeInfo.New()
let type_pascal.ctagstype = 'pascal'
let type_pascal.kinds = [
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'p', 'long' : 'procedures', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.pascal = type_pascal
" Perl {{{3
let type_perl = s:TypeInfo.New()
let type_perl.ctagstype = 'perl'
let type_perl.kinds = [
\ {'short' : 'p', 'long' : 'packages', 'fold' : 1, 'stl' : 0},
\ {'short' : 'c', 'long' : 'constants', 'fold' : 0, 'stl' : 0},
\ {'short' : 'f', 'long' : 'formats', 'fold' : 0, 'stl' : 0},
\ {'short' : 'l', 'long' : 'labels', 'fold' : 0, 'stl' : 1},
\ {'short' : 's', 'long' : 'subroutines', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.perl = type_perl
" PHP {{{3
let type_php = s:TypeInfo.New()
let type_php.ctagstype = 'php'
let type_php.kinds = [
\ {'short' : 'i', 'long' : 'interfaces', 'fold' : 0, 'stl' : 1},
\ {'short' : 'c', 'long' : 'classes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'd', 'long' : 'constant definitions', 'fold' : 0, 'stl' : 0},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'v', 'long' : 'variables', 'fold' : 0, 'stl' : 0},
\ {'short' : 'j', 'long' : 'javascript functions', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.php = type_php
" Python {{{3
let type_python = s:TypeInfo.New()
let type_python.ctagstype = 'python'
let type_python.kinds = [
\ {'short' : 'i', 'long' : 'imports', 'fold' : 1, 'stl' : 0},
\ {'short' : 'c', 'long' : 'classes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'm', 'long' : 'members', 'fold' : 0, 'stl' : 1},
\ {'short' : 'v', 'long' : 'variables', 'fold' : 0, 'stl' : 0}
\ ]
let type_python.sro = '.'
let type_python.kind2scope = {
\ 'c' : 'class',
\ 'f' : 'function',
\ 'm' : 'function'
\ }
let type_python.scope2kind = {
\ 'class' : 'c',
\ 'function' : 'f'
\ }
let s:known_types.python = type_python
let s:known_types.pyrex = type_python
let s:known_types.cython = type_python
" REXX {{{3
let type_rexx = s:TypeInfo.New()
let type_rexx.ctagstype = 'rexx'
let type_rexx.kinds = [
\ {'short' : 's', 'long' : 'subroutines', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.rexx = type_rexx
" Ruby {{{3
let type_ruby = s:TypeInfo.New()
let type_ruby.ctagstype = 'ruby'
let type_ruby.kinds = [
\ {'short' : 'm', 'long' : 'modules', 'fold' : 0, 'stl' : 1},
\ {'short' : 'c', 'long' : 'classes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'methods', 'fold' : 0, 'stl' : 1},
\ {'short' : 'F', 'long' : 'singleton methods', 'fold' : 0, 'stl' : 1}
\ ]
let type_ruby.sro = '.'
let type_ruby.kind2scope = {
\ 'c' : 'class',
\ 'm' : 'class'
\ }
let type_ruby.scope2kind = {
\ 'class' : 'c'
\ }
let s:known_types.ruby = type_ruby
" Scheme {{{3
let type_scheme = s:TypeInfo.New()
let type_scheme.ctagstype = 'scheme'
let type_scheme.kinds = [
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 's', 'long' : 'sets', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.scheme = type_scheme
let s:known_types.racket = type_scheme
" Shell script {{{3
let type_sh = s:TypeInfo.New()
let type_sh.ctagstype = 'sh'
let type_sh.kinds = [
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.sh = type_sh
let s:known_types.csh = type_sh
let s:known_types.zsh = type_sh
" SLang {{{3
let type_slang = s:TypeInfo.New()
let type_slang.ctagstype = 'slang'
let type_slang.kinds = [
\ {'short' : 'n', 'long' : 'namespaces', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.slang = type_slang
" SML {{{3
let type_sml = s:TypeInfo.New()
let type_sml.ctagstype = 'sml'
let type_sml.kinds = [
\ {'short' : 'e', 'long' : 'exception declarations', 'fold' : 0, 'stl' : 0},
\ {'short' : 'f', 'long' : 'function definitions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'c', 'long' : 'functor definitions', 'fold' : 0, 'stl' : 1},
\ {'short' : 's', 'long' : 'signature declarations', 'fold' : 0, 'stl' : 0},
\ {'short' : 'r', 'long' : 'structure declarations', 'fold' : 0, 'stl' : 0},
\ {'short' : 't', 'long' : 'type definitions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'v', 'long' : 'value bindings', 'fold' : 0, 'stl' : 0}
\ ]
let s:known_types.sml = type_sml
" SQL {{{3
" The SQL ctags parser seems to be buggy for me, so this just uses the
" normal kinds even though scopes should be available. Improvements
" welcome!
let type_sql = s:TypeInfo.New()
let type_sql.ctagstype = 'sql'
let type_sql.kinds = [
\ {'short' : 'P', 'long' : 'packages', 'fold' : 1, 'stl' : 1},
\ {'short' : 'd', 'long' : 'prototypes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'c', 'long' : 'cursors', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'F', 'long' : 'record fields', 'fold' : 0, 'stl' : 1},
\ {'short' : 'L', 'long' : 'block label', 'fold' : 0, 'stl' : 1},
\ {'short' : 'p', 'long' : 'procedures', 'fold' : 0, 'stl' : 1},
\ {'short' : 's', 'long' : 'subtypes', 'fold' : 0, 'stl' : 1},
\ {'short' : 't', 'long' : 'tables', 'fold' : 0, 'stl' : 1},
\ {'short' : 'T', 'long' : 'triggers', 'fold' : 0, 'stl' : 1},
\ {'short' : 'v', 'long' : 'variables', 'fold' : 0, 'stl' : 1},
\ {'short' : 'i', 'long' : 'indexes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'e', 'long' : 'events', 'fold' : 0, 'stl' : 1},
\ {'short' : 'U', 'long' : 'publications', 'fold' : 0, 'stl' : 1},
\ {'short' : 'R', 'long' : 'services', 'fold' : 0, 'stl' : 1},
\ {'short' : 'D', 'long' : 'domains', 'fold' : 0, 'stl' : 1},
\ {'short' : 'V', 'long' : 'views', 'fold' : 0, 'stl' : 1},
\ {'short' : 'n', 'long' : 'synonyms', 'fold' : 0, 'stl' : 1},
\ {'short' : 'x', 'long' : 'MobiLink Table Scripts', 'fold' : 0, 'stl' : 1},
\ {'short' : 'y', 'long' : 'MobiLink Conn Scripts', 'fold' : 0, 'stl' : 1},
\ {'short' : 'z', 'long' : 'MobiLink Properties', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.sql = type_sql
" Tcl {{{3
let type_tcl = s:TypeInfo.New()
let type_tcl.ctagstype = 'tcl'
let type_tcl.kinds = [
\ {'short' : 'c', 'long' : 'classes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'm', 'long' : 'methods', 'fold' : 0, 'stl' : 1},
\ {'short' : 'p', 'long' : 'procedures', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.tcl = type_tcl
" LaTeX {{{3
let type_tex = s:TypeInfo.New()
let type_tex.ctagstype = 'tex'
let type_tex.kinds = [
\ {'short' : 'i', 'long' : 'includes', 'fold' : 1, 'stl' : 0},
\ {'short' : 'p', 'long' : 'parts', 'fold' : 0, 'stl' : 1},
\ {'short' : 'c', 'long' : 'chapters', 'fold' : 0, 'stl' : 1},
\ {'short' : 's', 'long' : 'sections', 'fold' : 0, 'stl' : 1},
\ {'short' : 'u', 'long' : 'subsections', 'fold' : 0, 'stl' : 1},
\ {'short' : 'b', 'long' : 'subsubsections', 'fold' : 0, 'stl' : 1},
\ {'short' : 'P', 'long' : 'paragraphs', 'fold' : 0, 'stl' : 0},
\ {'short' : 'G', 'long' : 'subparagraphs', 'fold' : 0, 'stl' : 0},
\ {'short' : 'l', 'long' : 'labels', 'fold' : 0, 'stl' : 0}
\ ]
let type_tex.sro = '""'
let type_tex.kind2scope = {
\ 'p' : 'part',
\ 'c' : 'chapter',
\ 's' : 'section',
\ 'u' : 'subsection',
\ 'b' : 'subsubsection'
\ }
let type_tex.scope2kind = {
\ 'part' : 'p',
\ 'chapter' : 'c',
\ 'section' : 's',
\ 'subsection' : 'u',
\ 'subsubsection' : 'b'
\ }
let type_tex.sort = 0
let s:known_types.tex = type_tex
" Vala {{{3
" Vala is supported by the ctags fork provided by Anjuta, so only add the
" type if the fork is used to prevent error messages otherwise
if has_key(s:ctags_types, 'vala') || executable('anjuta-tags')
let type_vala = s:TypeInfo.New()
let type_vala.ctagstype = 'vala'
let type_vala.kinds = [
\ {'short' : 'e', 'long' : 'Enumerations', 'fold' : 0, 'stl' : 1},
\ {'short' : 'v', 'long' : 'Enumeration values', 'fold' : 0, 'stl' : 0},
\ {'short' : 's', 'long' : 'Structures', 'fold' : 0, 'stl' : 1},
\ {'short' : 'i', 'long' : 'Interfaces', 'fold' : 0, 'stl' : 1},
\ {'short' : 'd', 'long' : 'Delegates', 'fold' : 0, 'stl' : 1},
\ {'short' : 'c', 'long' : 'Classes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'p', 'long' : 'Properties', 'fold' : 0, 'stl' : 0},
\ {'short' : 'f', 'long' : 'Fields', 'fold' : 0, 'stl' : 0},
\ {'short' : 'm', 'long' : 'Methods', 'fold' : 0, 'stl' : 1},
\ {'short' : 'E', 'long' : 'Error domains', 'fold' : 0, 'stl' : 1},
\ {'short' : 'r', 'long' : 'Error codes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'S', 'long' : 'Signals', 'fold' : 0, 'stl' : 1}
\ ]
let type_vala.sro = '.'
" 'enum' doesn't seem to be used as a scope, but it can't hurt to have
" it here
let type_vala.kind2scope = {
\ 's' : 'struct',
\ 'i' : 'interface',
\ 'c' : 'class',
\ 'e' : 'enum'
\ }
let type_vala.scope2kind = {
\ 'struct' : 's',
\ 'interface' : 'i',
\ 'class' : 'c',
\ 'enum' : 'e'
\ }
let s:known_types.vala = type_vala
endif
if !has_key(s:ctags_types, 'vala') && executable('anjuta-tags')
let s:known_types.vala.ctagsbin = 'anjuta-tags'
endif
" Vera {{{3
" Why are variables 'virtual'?
let type_vera = s:TypeInfo.New()
let type_vera.ctagstype = 'vera'
let type_vera.kinds = [
\ {'short' : 'd', 'long' : 'macros', 'fold' : 1, 'stl' : 0},
\ {'short' : 'g', 'long' : 'enums', 'fold' : 0, 'stl' : 1},
\ {'short' : 'T', 'long' : 'typedefs', 'fold' : 0, 'stl' : 0},
\ {'short' : 'c', 'long' : 'classes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'e', 'long' : 'enumerators', 'fold' : 0, 'stl' : 0},
\ {'short' : 'm', 'long' : 'members', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 't', 'long' : 'tasks', 'fold' : 0, 'stl' : 1},
\ {'short' : 'v', 'long' : 'variables', 'fold' : 0, 'stl' : 0},
\ {'short' : 'p', 'long' : 'programs', 'fold' : 0, 'stl' : 1}
\ ]
let type_vera.sro = '.' " Nesting doesn't seem to be possible
let type_vera.kind2scope = {
\ 'g' : 'enum',
\ 'c' : 'class',
\ 'v' : 'virtual'
\ }
let type_vera.scope2kind = {
\ 'enum' : 'g',
\ 'class' : 'c',
\ 'virtual' : 'v'
\ }
let s:known_types.vera = type_vera
" Verilog {{{3
let type_verilog = s:TypeInfo.New()
let type_verilog.ctagstype = 'verilog'
let type_verilog.kinds = [
\ {'short' : 'c', 'long' : 'constants', 'fold' : 0, 'stl' : 0},
\ {'short' : 'e', 'long' : 'events', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'm', 'long' : 'modules', 'fold' : 0, 'stl' : 1},
\ {'short' : 'n', 'long' : 'net data types', 'fold' : 0, 'stl' : 1},
\ {'short' : 'p', 'long' : 'ports', 'fold' : 0, 'stl' : 1},
\ {'short' : 'r', 'long' : 'register data types', 'fold' : 0, 'stl' : 1},
\ {'short' : 't', 'long' : 'tasks', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.verilog = type_verilog
" VHDL {{{3
" The VHDL ctags parser unfortunately doesn't generate proper scopes
let type_vhdl = s:TypeInfo.New()
let type_vhdl.ctagstype = 'vhdl'
let type_vhdl.kinds = [
\ {'short' : 'P', 'long' : 'packages', 'fold' : 1, 'stl' : 0},
\ {'short' : 'c', 'long' : 'constants', 'fold' : 0, 'stl' : 0},
\ {'short' : 't', 'long' : 'types', 'fold' : 0, 'stl' : 1},
\ {'short' : 'T', 'long' : 'subtypes', 'fold' : 0, 'stl' : 1},
\ {'short' : 'r', 'long' : 'records', 'fold' : 0, 'stl' : 1},
\ {'short' : 'e', 'long' : 'entities', 'fold' : 0, 'stl' : 1},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'p', 'long' : 'procedures', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.vhdl = type_vhdl
" Vim {{{3
let type_vim = s:TypeInfo.New()
let type_vim.ctagstype = 'vim'
let type_vim.kinds = [
\ {'short' : 'n', 'long' : 'vimball filenames', 'fold' : 0, 'stl' : 1},
\ {'short' : 'v', 'long' : 'variables', 'fold' : 1, 'stl' : 0},
\ {'short' : 'f', 'long' : 'functions', 'fold' : 0, 'stl' : 1},
\ {'short' : 'a', 'long' : 'autocommand groups', 'fold' : 1, 'stl' : 1},
\ {'short' : 'c', 'long' : 'commands', 'fold' : 0, 'stl' : 0},
\ {'short' : 'm', 'long' : 'maps', 'fold' : 1, 'stl' : 0}
\ ]
let s:known_types.vim = type_vim
" YACC {{{3
let type_yacc = s:TypeInfo.New()
let type_yacc.ctagstype = 'yacc'
let type_yacc.kinds = [
\ {'short' : 'l', 'long' : 'labels', 'fold' : 0, 'stl' : 1}
\ ]
let s:known_types.yacc = type_yacc
" }}}3
for [type, typeinfo] in items(s:known_types)
let typeinfo.ftype = type
endfor
call s:LoadUserTypeDefs()
for typeinfo in values(s:known_types)
call typeinfo.createKinddict()
endfor
let s:type_init_done = 1
endfunction
" s:LoadUserTypeDefs() {{{2
function! s:LoadUserTypeDefs(...) abort
if a:0 > 0
let type = a:1
call s:debug("Initializing user type '" . type . "'")
let defdict = {}
let defdict[type] = g:tagbar_type_{type}
else
call s:debug('Initializing user types')
let defdict = tagbar#getusertypes()
endif
let transformed = {}
for [type, def] in items(defdict)
let transformed[type] = s:TransformUserTypeDef(def)
let transformed[type].ftype = type
endfor
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)
endif
endfor
if a:0 > 0
call s:known_types[type].createKinddict()
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:RestoreSession() {{{2
" Properly restore Tagbar after a session got loaded
function! s:RestoreSession() abort
call s:debug('Restoring session')
let curfile = fnamemodify(bufname('%'), ':p')
let tagbarwinnr = bufwinnr('__Tagbar__')
if tagbarwinnr == -1
" Tagbar wasn't open in the saved session, nothing to do
return
else
let in_tagbar = 1
if winnr() != tagbarwinnr
call s:goto_win(tagbarwinnr)
let in_tagbar = 0
endif
endif
let s:last_autofocus = 0
call s:Init(0)
call s:InitWindow(g:tagbar_autoclose)
call s:AutoUpdate(curfile, 0)
if !in_tagbar
call s:goto_win('p')
endif
endfunction
" s:MapKeys() {{{2
function! s:MapKeys() abort
call s:debug('Mapping keys')
nnoremap <script> <silent> <buffer> <2-LeftMouse>
\ :call <SID>JumpToTag(0)<CR>
nnoremap <script> <silent> <buffer> <LeftRelease>
\ <LeftRelease>:call <SID>CheckMouseClick()<CR>
inoremap <script> <silent> <buffer> <2-LeftMouse>
\ <C-o>:call <SID>JumpToTag(0)<CR>
inoremap <script> <silent> <buffer> <LeftRelease>
\ <LeftRelease><C-o>:call <SID>CheckMouseClick()<CR>
let maps = [
\ ['jump', 'JumpToTag(0)'],
\ ['preview', 'JumpToTag(1)'],
\ ['previewwin', 'ShowInPreviewWin()'],
\ ['nexttag', 'GotoNextToplevelTag(1)'],
\ ['prevtag', 'GotoNextToplevelTag(-1)'],
\ ['showproto', 'ShowPrototype(0)'],
\ ['hidenonpublic', 'ToggleHideNonPublicTags()'],
\
\ ['openfold', 'OpenFold()'],
\ ['closefold', 'CloseFold()'],
\ ['togglefold', 'ToggleFold()'],
\ ['openallfolds', 'SetFoldLevel(99, 1)'],
\ ['closeallfolds', 'SetFoldLevel(0, 1)'],
\ ['nextfold', 'GotoNextFold()'],
\ ['prevfold', 'GotoPrevFold()'],
\
\ ['togglesort', 'ToggleSort()'],
\ ['toggleautoclose', 'ToggleAutoclose()'],
\ ['zoomwin', 'ZoomWindow()'],
\ ['close', 'CloseWindow()'],
\ ['help', 'ToggleHelp()'],
\ ]
for [map, func] in maps
let def = get(g:, 'tagbar_map_' . map)
if type(def) == type("")
let keys = [def]
else
let keys = def
endif
for key in keys
execute 'nnoremap <script> <silent> <buffer> ' . key .
\ ' :call <SID>' . func . '<CR>'
endfor
unlet def
endfor
let b:tagbar_mapped_keys = 1
endfunction
" s:CreateAutocommands() {{{2
function! s:CreateAutocommands() abort
call s:debug('Creating autocommands')
augroup TagbarAutoCmds
autocmd!
autocmd CursorHold __Tagbar__ call s:ShowPrototype(1)
autocmd WinEnter __Tagbar__ call s:SetStatusLine('current')
autocmd WinLeave __Tagbar__ call s:SetStatusLine('noncurrent')
if g:tagbar_autopreview
autocmd CursorMoved __Tagbar__ nested call s:ShowInPreviewWin()
endif
autocmd WinEnter * nested call s:QuitIfOnlyWindow()
autocmd WinEnter * if bufwinnr('__Tagbar__') == -1 |
\ call s:ShrinkIfExpanded() |
\ endif
autocmd BufWritePost * call
\ s:AutoUpdate(fnamemodify(expand('<afile>'), ':p'), 1)
" BufReadPost is needed for reloading the current buffer if the file
" was changed by an external command; see commit 17d199f
autocmd BufReadPost,BufEnter,CursorHold,FileType * call
\ s:AutoUpdate(fnamemodify(expand('<afile>'), ':p'), 0)
autocmd BufDelete,BufWipeout * call
\ s:known_files.rm(fnamemodify(expand('<afile>'), ':p'))
autocmd QuickFixCmdPre * let s:tagbar_qf_active = 1
autocmd QuickFixCmdPost * if exists('s:tagbar_qf_active') |
\ unlet s:tagbar_qf_active |
\ endif
autocmd VimEnter * call s:CorrectFocusOnStartup()
augroup END
let s:autocommands_done = 1
endfunction
" s:CheckForExCtags() {{{2
" Test whether the ctags binary is actually Exuberant Ctags and not GNU ctags
" (or something else)
function! s:CheckForExCtags(silent) abort
call s:debug('Checking for Exuberant Ctags')
if !exists('g:tagbar_ctags_bin')
let ctagsbins = []
let ctagsbins += ['ctags-exuberant'] " Debian
let ctagsbins += ['exuberant-ctags']
let ctagsbins += ['exctags'] " FreeBSD, NetBSD
let ctagsbins += ['/usr/local/bin/ctags'] " Homebrew
let ctagsbins += ['/opt/local/bin/ctags'] " Macports
let ctagsbins += ['ectags'] " OpenBSD
let ctagsbins += ['ctags']
let ctagsbins += ['ctags.exe']
let ctagsbins += ['tags']
for ctags in ctagsbins
if executable(ctags)
let g:tagbar_ctags_bin = ctags
break
endif
endfor
if !exists('g:tagbar_ctags_bin')
let errmsg = 'Tagbar: Exuberant ctags not found!'
let infomsg = 'Please download Exuberant Ctags from' .
\ ' ctags.sourceforge.net and install it in a' .
\ ' directory in your $PATH or set g:tagbar_ctags_bin.'
call s:CtagsErrMsg(errmsg, infomsg, a:silent)
let s:checked_ctags = 2
return 0
endif
else
" reset 'wildignore' temporarily in case *.exe is included in it
let wildignore_save = &wildignore
set wildignore&
let g:tagbar_ctags_bin = expand(g:tagbar_ctags_bin)
let &wildignore = wildignore_save
if !executable(g:tagbar_ctags_bin)
let errmsg = "Tagbar: Exuberant ctags not found at " .
\ "'" . g:tagbar_ctags_bin . "'!"
let infomsg = 'Please check your g:tagbar_ctags_bin setting.'
call s:CtagsErrMsg(errmsg, infomsg, a:silent)
let s:checked_ctags = 2
return 0
endif
endif
let ctags_cmd = s:EscapeCtagsCmd(g:tagbar_ctags_bin, '--version')
if ctags_cmd == ''
let s:checked_ctags = 2
return 0
endif
let ctags_output = s:ExecuteCtags(ctags_cmd)
if v:shell_error || ctags_output !~# 'Exuberant Ctags'
let errmsg = 'Tagbar: Ctags doesn''t seem to be Exuberant Ctags!'
let infomsg = 'GNU ctags will NOT WORK.' .
\ ' Please download Exuberant Ctags from ctags.sourceforge.net' .
\ ' and install it in a directory in your $PATH' .
\ ' or set g:tagbar_ctags_bin.'
call s:CtagsErrMsg(errmsg, infomsg, a:silent, ctags_cmd, ctags_output)
let s:checked_ctags = 2
return 0
elseif !s:CheckExCtagsVersion(ctags_output)
let errmsg = 'Tagbar: Exuberant Ctags is too old!'
let infomsg = 'You need at least version 5.5 for Tagbar to work.' .
\ ' Please download a newer version from ctags.sourceforge.net.'
call s:CtagsErrMsg(errmsg, infomsg, a:silent, ctags_cmd, ctags_output)
let s:checked_ctags = 2
return 0
else
let s:checked_ctags = 1
return 1
endif
endfunction
" s:CtagsErrMsg() {{{2
function! s:CtagsErrMsg(errmsg, infomsg, silent, ...) abort
call s:debug(a:errmsg)
let ctags_cmd = a:0 > 0 ? a:1 : ''
let ctags_output = a:0 > 0 ? a:2 : ''
if ctags_output != ''
call s:debug("Command output:\n" . ctags_output)
endif
if !a:silent
call s:warning(a:errmsg)
echomsg a:infomsg
if ctags_cmd == ''
return
endif
echomsg 'Executed command: "' . ctags_cmd . '"'
if ctags_output != ''
echomsg 'Command output:'
for line in split(ctags_output, '\n')
echomsg line
endfor
else
echomsg 'Command output is empty.'
endif
endif
endfunction
" s:CheckExCtagsVersion() {{{2
function! s:CheckExCtagsVersion(output) abort
call s:debug('Checking Exuberant Ctags version')
if a:output =~ 'Exuberant Ctags Development'
call s:debug("Found development version, assuming compatibility")
return 1
endif
let matchlist = matchlist(a:output, '\vExuberant Ctags (\d+)\.(\d+)')
let major = matchlist[1]
let minor = matchlist[2]
call s:debug("Ctags version: major='" . major . "', minor='" . minor . "'")
return major >= 6 || (major == 5 && minor >= 5)
endfunction
" s:CheckFTCtags() {{{2
function! s:CheckFTCtags(bin, ftype) abort
if executable(a:bin)
return a:bin
endif
if exists('g:tagbar_type_' . a:ftype)
let userdef = g:tagbar_type_{a:ftype}
if has_key(userdef, 'ctagsbin')
return userdef.ctagsbin
else
return ''
endif
endif
return ''
endfunction
" s:GetSupportedFiletypes() {{{2
function! s:GetSupportedFiletypes() abort
call s:debug('Getting filetypes sypported by Exuberant Ctags')
let ctags_cmd = s:EscapeCtagsCmd(g:tagbar_ctags_bin, '--list-languages')
if ctags_cmd == ''
return
endif
let ctags_output = s:ExecuteCtags(ctags_cmd)
if v:shell_error
" this shouldn't happen as potential problems would have already been
" caught by the previous ctags checking
return
endif
let types = split(ctags_output, '\n\+')
for type in types
let s:ctags_types[tolower(type)] = 1
endfor
let s:checked_ctags_types = 1
endfunction
" Prototypes {{{1
" Base tag {{{2
let s:BaseTag = {}
" s:BaseTag.New() {{{3
function! s:BaseTag.New(name) abort dict
let newobj = copy(self)
call newobj._init(a:name)
return newobj
endfunction
" s:BaseTag._init() {{{3
function! s:BaseTag._init(name) abort dict
let self.name = a:name
let self.fields = {}
let self.fields.line = 0
let self.fields.column = 1
let self.prototype = ''
let self.path = ''
let self.fullpath = a:name
let self.depth = 0
let self.parent = {}
let self.tline = -1
let self.fileinfo = {}
let self.typeinfo = {}
endfunction
" s:BaseTag.isNormalTag() {{{3
function! s:BaseTag.isNormalTag() abort dict
return 0
endfunction
" s:BaseTag.isPseudoTag() {{{3
function! s:BaseTag.isPseudoTag() abort dict
return 0
endfunction
" s:BaseTag.isKindheader() {{{3
function! s:BaseTag.isKindheader() abort dict
return 0
endfunction
" s:BaseTag.getPrototype() {{{3
function! s:BaseTag.getPrototype(short) abort dict
return self.prototype
endfunction
" s:BaseTag._getPrefix() {{{3
function! s:BaseTag._getPrefix() abort dict
let fileinfo = self.fileinfo
if has_key(self, 'children') && !empty(self.children)
if fileinfo.tagfolds[self.fields.kind][self.fullpath]
let prefix = s:icon_closed
else
let prefix = s:icon_open
endif
else
let prefix = ' '
endif
" Visibility is called 'access' in the ctags output
if g:tagbar_show_visibility
if has_key(self.fields, 'access')
let prefix .= get(s:visibility_symbols, self.fields.access, ' ')
elseif has_key(self.fields, 'file')
let prefix .= s:visibility_symbols.private
else
let prefix .= ' '
endif
endif
return prefix
endfunction
" s:BaseTag.initFoldState() {{{3
function! s:BaseTag.initFoldState() abort dict
let fileinfo = self.fileinfo
if s:known_files.has(fileinfo.fpath) &&
\ has_key(fileinfo, '_tagfolds_old') &&
\ has_key(fileinfo._tagfolds_old[self.fields.kind], self.fullpath)
" The file has been updated and the tag was there before, so copy its
" old fold state
let fileinfo.tagfolds[self.fields.kind][self.fullpath] =
\ fileinfo._tagfolds_old[self.fields.kind][self.fullpath]
elseif self.depth >= fileinfo.foldlevel
let fileinfo.tagfolds[self.fields.kind][self.fullpath] = 1
else
let fileinfo.tagfolds[self.fields.kind][self.fullpath] =
\ fileinfo.kindfolds[self.fields.kind]
endif
endfunction
" s:BaseTag.getClosedParentTline() {{{3
function! s:BaseTag.getClosedParentTline() abort dict
let tagline = self.tline
" Find the first closed parent, starting from the top of the hierarchy.
let parents = []
let curparent = self.parent
while !empty(curparent)
call add(parents, curparent)
let curparent = curparent.parent
endwhile
for parent in reverse(parents)
if parent.isFolded()
let tagline = parent.tline
break
endif
endfor
return tagline
endfunction
" s:BaseTag.isFoldable() {{{3
function! s:BaseTag.isFoldable() abort dict
return has_key(self, 'children') && !empty(self.children)
endfunction
" s:BaseTag.isFolded() {{{3
function! s:BaseTag.isFolded() abort dict
return self.fileinfo.tagfolds[self.fields.kind][self.fullpath]
endfunction
" s:BaseTag.openFold() {{{3
function! s:BaseTag.openFold() abort dict
if self.isFoldable()
let self.fileinfo.tagfolds[self.fields.kind][self.fullpath] = 0
endif
endfunction
" s:BaseTag.closeFold() {{{3
function! s:BaseTag.closeFold() abort dict
let newline = line('.')
if !empty(self.parent) && self.parent.isKindheader()
" Tag is child of generic 'kind'
call self.parent.closeFold()
let newline = self.parent.tline
elseif self.isFoldable() && !self.isFolded()
" Tag is parent of a scope and is not folded
let self.fileinfo.tagfolds[self.fields.kind][self.fullpath] = 1
let newline = self.tline
elseif !empty(self.parent)
" Tag is normal child, so close parent
let parent = self.parent
let self.fileinfo.tagfolds[parent.fields.kind][parent.fullpath] = 1
let newline = parent.tline
endif
return newline
endfunction
" s:BaseTag.setFolded() {{{3
function! s:BaseTag.setFolded(folded) abort dict
let self.fileinfo.tagfolds[self.fields.kind][self.fullpath] = a:folded
endfunction
" s:BaseTag.openParents() {{{3
function! s:BaseTag.openParents() abort dict
let parent = self.parent
while !empty(parent)
call parent.openFold()
let parent = parent.parent
endwhile
endfunction
" Normal tag {{{2
let s:NormalTag = copy(s:BaseTag)
" s:NormalTag.isNormalTag() {{{3
function! s:NormalTag.isNormalTag() abort dict
return 1
endfunction
" s:NormalTag.strfmt() {{{3
function! s:NormalTag.strfmt() abort dict
let typeinfo = self.typeinfo
let suffix = get(self.fields, 'signature', '')
if has_key(self.fields, 'type')
let suffix .= ' : ' . self.fields.type
elseif has_key(typeinfo, 'kind2scope') &&
\ has_key(typeinfo.kind2scope, self.fields.kind)
let suffix .= ' : ' . typeinfo.kind2scope[self.fields.kind]
endif
return self._getPrefix() . self.name . suffix . "\n"
endfunction
" s:NormalTag.str() {{{3
function! s:NormalTag.str(longsig, full) abort dict
if a:full && self.path != ''
let str = self.path . self.typeinfo.sro . self.name
else
let str = self.name
endif
if has_key(self.fields, 'signature')
if a:longsig
let str .= self.fields.signature
else
let str .= '()'
endif
endif
return str
endfunction
" s:NormalTag.getPrototype() {{{3
function! s:NormalTag.getPrototype(short) abort dict
if self.prototype != ''
let prototype = self.prototype
else
let bufnr = self.fileinfo.bufnr
if self.fields.line == 0 || !bufloaded(bufnr)
" No linenumber available or buffer not loaded (probably due to
" 'nohidden'), try the pattern instead
return substitute(self.pattern, '^\\V\\^\\C\s*\(.*\)\\$$', '\1', '')
endif
let line = getbufline(bufnr, self.fields.line)[0]
let list = split(line, '\zs')
let start = index(list, '(')
if start == -1
return substitute(line, '^\s\+', '', '')
endif
let opening = count(list, '(', 0, start)
let closing = count(list, ')', 0, start)
if closing >= opening
return substitute(line, '^\s\+', '', '')
endif
let balance = opening - closing
let prototype = line
let curlinenr = self.fields.line + 1
while balance > 0
let curline = getbufline(bufnr, curlinenr)[0]
let curlist = split(curline, '\zs')
let balance += count(curlist, '(')
let balance -= count(curlist, ')')
let prototype .= "\n" . curline
let curlinenr += 1
endwhile
let self.prototype = prototype
endif
if a:short
" join all lines and remove superfluous spaces
let prototype = substitute(prototype, '^\s\+', '', '')
let prototype = substitute(prototype, '\_s\+', ' ', 'g')
let prototype = substitute(prototype, '(\s\+', '(', 'g')
let prototype = substitute(prototype, '\s\+)', ')', 'g')
" Avoid hit-enter prompts
let maxlen = &columns - 12
if len(prototype) > maxlen
let prototype = prototype[:maxlen - 1 - 3]
let prototype .= '...'
endif
endif
return prototype
endfunction
" Pseudo tag {{{2
let s:PseudoTag = copy(s:BaseTag)
" s:PseudoTag.isPseudoTag() {{{3
function! s:PseudoTag.isPseudoTag() abort dict
return 1
endfunction
" s:PseudoTag.strfmt() {{{3
function! s:PseudoTag.strfmt() abort dict
let typeinfo = self.typeinfo
let suffix = get(self.fields, 'signature', '')
if has_key(typeinfo.kind2scope, self.fields.kind)
let suffix .= ' : ' . typeinfo.kind2scope[self.fields.kind]
endif
return self._getPrefix() . self.name . '*' . suffix
endfunction
" Kind header {{{2
let s:KindheaderTag = copy(s:BaseTag)
" s:KindheaderTag.isKindheader() {{{3
function! s:KindheaderTag.isKindheader() abort dict
return 1
endfunction
" s:KindheaderTag.getPrototype() {{{3
function! s:KindheaderTag.getPrototype(short) abort dict
return self.name . ': ' .
\ self.numtags . ' ' . (self.numtags > 1 ? 'tags' : 'tag')
endfunction
" s:KindheaderTag.isFoldable() {{{3
function! s:KindheaderTag.isFoldable() abort dict
return 1
endfunction
" s:KindheaderTag.isFolded() {{{3
function! s:KindheaderTag.isFolded() abort dict
return self.fileinfo.kindfolds[self.short]
endfunction
" s:KindheaderTag.openFold() {{{3
function! s:KindheaderTag.openFold() abort dict
let self.fileinfo.kindfolds[self.short] = 0
endfunction
" s:KindheaderTag.closeFold() {{{3
function! s:KindheaderTag.closeFold() abort dict
let self.fileinfo.kindfolds[self.short] = 1
return line('.')
endfunction
" s:KindheaderTag.toggleFold() {{{3
function! s:KindheaderTag.toggleFold() abort dict
let fileinfo = s:known_files.getCurrent(0)
let fileinfo.kindfolds[self.short] = !fileinfo.kindfolds[self.short]
endfunction
" Type info {{{2
let s:TypeInfo = {}
" s:TypeInfo.New() {{{3
function! s:TypeInfo.New(...) abort dict
let newobj = copy(self)
let newobj.kinddict = {}
if a:0 > 0
call extend(newobj, a:1)
endif
return newobj
endfunction
" s:TypeInfo.getKind() {{{3
function! s:TypeInfo.getKind(kind) abort dict
let idx = self.kinddict[a:kind]
return self.kinds[idx]
endfunction
" s:TypeInfo.createKinddict() {{{3
" Create a dictionary of the kind order for fast access in sorting functions
function! s:TypeInfo.createKinddict() abort dict
let i = 0
for kind in self.kinds
let self.kinddict[kind.short] = i
let i += 1
endfor
endfunction
" File info {{{2
let s:FileInfo = {}
" s:FileInfo.New() {{{3
function! s:FileInfo.New(fname, ftype, typeinfo) abort dict
let newobj = copy(self)
" The complete file path
let newobj.fpath = a:fname
let newobj.bufnr = bufnr(a:fname)
" File modification time
let newobj.mtime = getftime(a:fname)
" The vim file type
let newobj.ftype = a:ftype
" List of the tags that are present in the file, sorted according to the
" value of 'g:tagbar_sort'
let newobj.tags = []
" Dictionary of the tags, indexed by line number in the file
let newobj.fline = {}
" Dictionary of the tags, indexed by line number in the tagbar
let newobj.tline = {}
" Dictionary of the folding state of 'kind's, indexed by short name
let newobj.kindfolds = {}
let newobj.typeinfo = a:typeinfo
" copy the default fold state from the type info
for kind in a:typeinfo.kinds
let newobj.kindfolds[kind.short] =
\ g:tagbar_foldlevel == 0 ? 1 : kind.fold
endfor
" Dictionary of dictionaries of the folding state of individual tags,
" indexed by kind and full path
let newobj.tagfolds = {}
for kind in a:typeinfo.kinds
let newobj.tagfolds[kind.short] = {}
endfor
" The current foldlevel of the file
let newobj.foldlevel = g:tagbar_foldlevel
return newobj
endfunction
" s:FileInfo.reset() {{{3
" Reset stuff that gets regenerated while processing a file and save the old
" tag folds
function! s:FileInfo.reset() abort dict
let self.mtime = getftime(self.fpath)
let self.tags = []
let self.fline = {}
let self.tline = {}
let self._tagfolds_old = self.tagfolds
let self.tagfolds = {}
for kind in self.typeinfo.kinds
let self.tagfolds[kind.short] = {}
endfor
endfunction
" s:FileInfo.clearOldFolds() {{{3
function! s:FileInfo.clearOldFolds() abort dict
if exists('self._tagfolds_old')
unlet self._tagfolds_old
endif
endfunction
" s:FileInfo.sortTags() {{{3
function! s:FileInfo.sortTags() abort dict
if get(s:compare_typeinfo, 'sort', g:tagbar_sort)
call s:SortTags(self.tags, 's:CompareByKind')
else
call s:SortTags(self.tags, 's:CompareByLine')
endif
endfunction
" s:FileInfo.openKindFold() {{{3
function! s:FileInfo.openKindFold(kind) abort dict
let self.kindfolds[a:kind.short] = 0
endfunction
" s:FileInfo.closeKindFold() {{{3
function! s:FileInfo.closeKindFold(kind) abort dict
let self.kindfolds[a:kind.short] = 1
endfunction
" Known files {{{2
let s:known_files = {
\ '_current' : {},
\ '_paused' : {},
\ '_files' : {}
\ }
" s:known_files.getCurrent() {{{3
function! s:known_files.getCurrent(forcecurrent) abort dict
if !s:paused || a:forcecurrent
return self._current
else
return self._paused
endif
endfunction
" s:known_files.setCurrent() {{{3
function! s:known_files.setCurrent(fileinfo) abort dict
let self._current = a:fileinfo
endfunction
" s:known_files.setPaused() {{{3
function! s:known_files.setPaused() abort dict
let self._paused = self._current
endfunction
" s:known_files.get() {{{3
function! s:known_files.get(fname) abort dict
return get(self._files, a:fname, {})
endfunction
" s:known_files.put() {{{3
" Optional second argument is the filename
function! s:known_files.put(fileinfo, ...) abort dict
if a:0 == 1
let self._files[a:1] = a:fileinfo
else
let fname = a:fileinfo.fpath
let self._files[fname] = a:fileinfo
endif
endfunction
" s:known_files.has() {{{3
function! s:known_files.has(fname) abort dict
return has_key(self._files, a:fname)
endfunction
" s:known_files.rm() {{{3
function! s:known_files.rm(fname) abort dict
if s:known_files.has(a:fname)
call s:debug('Removing fileinfo for [' . a:fname . ']')
call remove(self._files, a:fname)
endif
endfunction
" Window management {{{1
" s:ToggleWindow() {{{2
function! s:ToggleWindow() abort
call s:debug('ToggleWindow called')
let tagbarwinnr = bufwinnr("__Tagbar__")
if tagbarwinnr != -1
call s:CloseWindow()
return
endif
call s:OpenWindow('')
call s:debug('ToggleWindow finished')
endfunction
" s:OpenWindow() {{{2
function! s:OpenWindow(flags) abort
call s:debug("OpenWindow called with flags: '" . a:flags . "'")
let autofocus = a:flags =~# 'f'
let jump = a:flags =~# 'j'
let autoclose = a:flags =~# 'c'
let curfile = fnamemodify(bufname('%'), ':p')
let curline = line('.')
" If the tagbar window is already open check jump flag
" Also set the autoclose flag if requested
let tagbarwinnr = bufwinnr('__Tagbar__')
if tagbarwinnr != -1
if winnr() != tagbarwinnr && jump
call s:goto_win(tagbarwinnr)
call s:HighlightTag(g:tagbar_autoshowtag != 2, 1, curline)
endif
call s:debug("OpenWindow finished, Tagbar already open")
return
endif
" This is only needed for the CorrectFocusOnStartup() function
let s:last_autofocus = autofocus
if !s:Init(0)
return 0
endif
" Expand the Vim window to accomodate for the Tagbar window if requested
" and save the window positions to be able to restore them later.
if g:tagbar_expand >= 1 && !s:window_expanded &&
\ (has('gui_running') || g:tagbar_expand == 2)
let s:window_pos.pre.x = getwinposx()
let s:window_pos.pre.y = getwinposy()
let &columns += g:tagbar_width + 1
let s:window_pos.post.x = getwinposx()
let s:window_pos.post.y = getwinposy()
let s:window_expanded = 1
endif
let s:window_opening = 1
if g:tagbar_vertical == 0
let openpos = g:tagbar_left ? 'topleft vertical ' : 'botright vertical '
let width = g:tagbar_width
else
let openpos = g:tagbar_left ? 'leftabove ' : 'rightbelow '
let width = g:tagbar_vertical
endif
exe 'silent keepalt ' . openpos . width . 'split ' . '__Tagbar__'
unlet s:window_opening
call s:InitWindow(autoclose)
" If the current file exists, but is empty, it means that it had a
" processing error before opening the window, most likely due to a call to
" currenttag() in the statusline. Remove the entry so an error message
" will be shown if the processing still fails.
if empty(s:known_files.get(curfile))
call s:known_files.rm(curfile)
endif
call s:AutoUpdate(curfile, 0)
call s:HighlightTag(g:tagbar_autoshowtag != 2, 1, curline)
if !(g:tagbar_autoclose || autofocus || g:tagbar_autofocus)
call s:goto_win('p')
endif
call s:debug('OpenWindow finished')
endfunction
" s:InitWindow() {{{2
function! s:InitWindow(autoclose) abort
call s:debug('InitWindow called with autoclose: ' . a:autoclose)
setlocal filetype=tagbar
setlocal noreadonly " in case the "view" mode is used
setlocal buftype=nofile
setlocal bufhidden=hide
setlocal noswapfile
setlocal nobuflisted
setlocal nomodifiable
setlocal nolist
setlocal nowrap
setlocal winfixwidth
setlocal textwidth=0
setlocal nospell
if g:tagbar_show_linenumbers == 0
setlocal nonumber
if exists('+relativenumber')
setlocal norelativenumber
endif
elseif g:tagbar_show_linenumbers == 1
setlocal number
elseif g:tagbar_show_linenumbers == 2
setlocal relativenumber
else
set number<
if exists('+relativenumber')
set relativenumber<
endif
endif
setlocal nofoldenable
setlocal foldcolumn=0
" Reset fold settings in case a plugin set them globally to something
" expensive. Apparently 'foldexpr' gets executed even if 'foldenable' is
" off, and then for every appended line (like with :put).
setlocal foldmethod&
setlocal foldexpr&
let w:autoclose = a:autoclose
call s:SetStatusLine('current')
let s:new_window = 1
if has('balloon_eval')
setlocal balloonexpr=TagbarBalloonExpr()
set ballooneval
endif
let cpoptions_save = &cpoptions
set cpoptions&vim
if !exists('b:tagbar_mapped_keys')
call s:MapKeys()
endif
let &cpoptions = cpoptions_save
if g:tagbar_expand
let s:expand_bufnr = bufnr('%')
endif
call s:debug('InitWindow finished')
endfunction
" s:CloseWindow() {{{2
function! s:CloseWindow() abort
call s:debug('CloseWindow called')
let tagbarwinnr = bufwinnr('__Tagbar__')
if tagbarwinnr == -1
return
endif
" Close the preview window if it was opened by us
if s:pwin_by_tagbar
pclose
let tagbarwinnr = bufwinnr('__Tagbar__')
endif
if winnr() == tagbarwinnr
if winbufnr(2) != -1
" Other windows are open, only close the tagbar one
let curfile = s:known_files.getCurrent(0)
close
" Try to jump to the correct window after closing
call s:goto_win('p')
if !empty(curfile)
let filebufnr = bufnr(curfile.fpath)
if bufnr('%') != filebufnr
let filewinnr = bufwinnr(filebufnr)
if filewinnr != -1
call s:goto_win(filewinnr)
endif
endif
endif
endif
else
" Go to the tagbar window, close it and then come back to the original
" window. Save a win-local variable in the original window so we can
" jump back to it even if the window number changed.
call s:mark_window()
call s:goto_win(tagbarwinnr)
close
call s:goto_markedwin()
endif
call s:ShrinkIfExpanded()
" The window sizes may have changed due to the shrinking happening after
" the window closing, so equalize them again.
if &equalalways
wincmd =
endif
if s:autocommands_done && !s:statusline_in_use
autocmd! TagbarAutoCmds
let s:autocommands_done = 0
endif
call s:debug('CloseWindow finished')
endfunction
" s:ShrinkIfExpanded() {{{2
" If the Vim window has been expanded, and Tagbar is not open in any other
" tabpages, shrink the window again
function! s:ShrinkIfExpanded() abort
if !s:window_expanded || &filetype == 'tagbar' || s:expand_bufnr == -1
return
endif
let tablist = []
for i in range(tabpagenr('$'))
call extend(tablist, tabpagebuflist(i + 1))
endfor
if index(tablist, s:expand_bufnr) == -1
let &columns -= g:tagbar_width + 1
let s:window_expanded = 0
let s:expand_bufnr = -1
" Only restore window position if it is available and if the
" window hasn't been moved manually after the expanding
if getwinposx() != -1 &&
\ getwinposx() == s:window_pos.post.x &&
\ getwinposy() == s:window_pos.post.y
execute 'winpos ' . s:window_pos.pre.x .
\ ' ' . s:window_pos.pre.y
endif
endif
endfunction
" s:ZoomWindow() {{{2
function! s:ZoomWindow() abort
if s:is_maximized
execute 'vertical resize ' . g:tagbar_width
execute s:winrestcmd
let s:is_maximized = 0
else
let s:winrestcmd = winrestcmd()
if g:tagbar_zoomwidth == 1
vertical resize
elseif g:tagbar_zoomwidth == 0
let func = exists('*strdisplaywidth') ? 'strdisplaywidth' : 'strlen'
let maxline = max(map(getline(line('w0'), line('w$')),
\ func . '(v:val)'))
execute 'vertical resize ' . maxline
elseif g:tagbar_zoomwidth > 1
execute 'vertical resize ' . g:tagbar_zoomwidth
endif
let s:is_maximized = 1
endif
endfunction
" s:CorrectFocusOnStartup() {{{2
" For whatever reason the focus will be on the Tagbar window if
" tagbar#autoopen is used with a FileType autocommand on startup and
" g:tagbar_left is set. This should work around it by jumping to the window of
" the current file after startup.
function! s:CorrectFocusOnStartup() abort
if bufwinnr('__Tagbar__') != -1 && !g:tagbar_autofocus && !s:last_autofocus
let curfile = s:known_files.getCurrent(1)
if !empty(curfile) && curfile.fpath != fnamemodify(bufname('%'), ':p')
let winnr = bufwinnr(curfile.fpath)
if winnr != -1
call s:goto_win(winnr)
endif
endif
endif
endfunction
" Tag processing {{{1
" s:ProcessFile() {{{2
" Execute ctags and put the information into a 'FileInfo' object
function! s:ProcessFile(fname, ftype) abort
call s:debug('ProcessFile called [' . a:fname . ']')
if !s:IsValidFile(a:fname, a:ftype)
call s:debug('Not a valid file, returning')
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)) &&
\ s:known_files.get(a:fname).ftype == a:ftype
let fileinfo = s:known_files.get(a:fname)
let typeinfo = fileinfo.typeinfo
call fileinfo.reset()
else
if exists('#TagbarProjects#User')
execute 'doautocmd <nomodeline> TagbarProjects User ' . a:fname
if exists('b:tagbar_type')
let typeinfo = extend(copy(typeinfo),
\ s:TransformUserTypeDef(b:tagbar_type))
call typeinfo.createKinddict()
endif
endif
let fileinfo = s:FileInfo.New(a:fname, a:ftype, typeinfo)
endif
call s:debug('typeinfo for file to process: ' . string(typeinfo))
" Use a temporary files for ctags processing instead of the original one.
" This allows using Tagbar for files accessed with netrw, and also doesn't
" slow down Tagbar for files that sit on slow network drives.
let tempfile = tempname()
let ext = fnamemodify(fileinfo.fpath, ':e')
if ext != ''
let tempfile .= '.' . ext
endif
call writefile(getbufline(fileinfo.bufnr, 1, '$'), tempfile)
let fileinfo.mtime = getftime(tempfile)
let ctags_output = s:ExecuteCtagsOnFile(tempfile, a:fname, typeinfo)
call delete(tempfile)
if ctags_output == -1
call s:debug('Ctags error when processing file')
" Put an empty entry into known_files so the error message is only
" shown once
call s:known_files.put({}, a:fname)
return
elseif ctags_output == ''
call s:debug('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,
\ s:known_types[a:ftype]), a:fname)
return
endif
call s:debug('Filetype tag kinds: ' . string(keys(typeinfo.kinddict)))
" Parse the ctags output lines
call s:debug('Parsing ctags output')
let rawtaglist = split(ctags_output, '\n\+')
for line in rawtaglist
" skip comments
if line =~# '^!_TAG_'
continue
endif
let parts = split(line, ';"')
if len(parts) == 2 " Is a valid tag line
let taginfo = s:ParseTagline(parts[0], parts[1], typeinfo, fileinfo)
if !empty(taginfo)
let fileinfo.fline[taginfo.fields.line] = taginfo
call add(fileinfo.tags, taginfo)
endif
endif
endfor
" Process scoped tags
let processedtags = []
if has_key(typeinfo, 'kind2scope')
call s:debug('Processing scoped tags')
let scopedtags = []
let is_scoped = 'has_key(typeinfo.kind2scope, v:val.fields.kind) ||
\ has_key(v:val, "scope")'
let scopedtags += filter(copy(fileinfo.tags), is_scoped)
call filter(fileinfo.tags, '!(' . is_scoped . ')')
call s:AddScopedTags(scopedtags, processedtags, {}, 0,
\ typeinfo, fileinfo, line('$'))
if !empty(scopedtags)
echoerr 'Tagbar: ''scopedtags'' not empty after processing,'
\ 'this should never happen!'
\ 'Please contact the script maintainer with an example.'
endif
endif
call s:debug('Number of top-level tags: ' . len(processedtags))
" Create a placeholder tag for the 'kind' header for folding purposes
for kind in typeinfo.kinds
let curtags = filter(copy(fileinfo.tags),
\ 'v:val.fields.kind ==# kind.short')
call s:debug('Processing kind: ' . kind.short .
\ ', number of tags: ' . len(curtags))
if empty(curtags)
continue
endif
let kindtag = s:KindheaderTag.New(kind.long)
let kindtag.short = kind.short
let kindtag.numtags = len(curtags)
let kindtag.fileinfo = fileinfo
for tag in curtags
let tag.parent = kindtag
endfor
endfor
if !empty(processedtags)
call extend(fileinfo.tags, processedtags)
endif
" Clear old folding information from previous file version to prevent leaks
call fileinfo.clearOldFolds()
" Sort the tags
let s:compare_typeinfo = typeinfo
call fileinfo.sortTags()
call s:known_files.put(fileinfo)
endfunction
" s:ExecuteCtagsOnFile() {{{2
function! s:ExecuteCtagsOnFile(fname, realfname, typeinfo) abort
call s:debug('ExecuteCtagsOnFile called [' . a:fname . ']')
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()
"each arg separately because in special cases where space is
"intended to be in an argument, spaces in a single ctag_args
"string would be ambiguous. Is the space an argument separator
"or to be included in the argument
let ctags_args = [ '-f',
\ '-',
\ '--format=2',
\ '--excmd=pattern',
\ '--fields=nksSaf',
\ '--extra=',
\ '--sort=no',
\ '--append=no'
\ ]
" Include extra type definitions
if has_key(a:typeinfo, 'deffile')
let ctags_args += ['--options=' . expand(a:typeinfo.deffile)]
endif
" Third-party programs may not necessarily make use of this
if has_key(a:typeinfo, 'ctagstype')
let ctags_type = a:typeinfo.ctagstype
let ctags_kinds = ''
for kind in a:typeinfo.kinds
let ctags_kinds .= kind.short
endfor
let ctags_args += ['--language-force=' . ctags_type]
let ctags_args += ['--' . ctags_type . '-kinds=' . ctags_kinds]
endif
endif
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(a:typeinfo.ctagsbin)
let &wildignore = wildignore_save
else
let ctags_bin = g:tagbar_ctags_bin
endif
let ctags_cmd = s:EscapeCtagsCmd(ctags_bin, ctags_args, a:fname)
if ctags_cmd == ''
return ''
endif
let ctags_output = s:ExecuteCtags(ctags_cmd)
if v:shell_error || ctags_output =~ 'Warning: cannot open source file'
call s:debug('Command output:')
call s:debug(ctags_output)
" Only display an error message if the Tagbar window is open and we
" haven't seen the error before.
if bufwinnr("__Tagbar__") != -1 &&
\ (!s:known_files.has(a:realfname) ||
\ !empty(s:known_files.get(a:realfname)))
call s:warning('Tagbar: Could not execute ctags for ' . a:realfname . '!')
echomsg 'Executed command: "' . ctags_cmd . '"'
if !empty(ctags_output)
echomsg 'Command output:'
for line in split(ctags_output, '\n')
echomsg line
endfor
endif
endif
return -1
endif
call s:debug('Ctags executed successfully')
return ctags_output
endfunction
" s:ParseTagline() {{{2
" Structure of a tag line:
" tagname<TAB>filename<TAB>expattern;"fields
" fields: <TAB>name:value
" fields that are always present: kind, line
function! s:ParseTagline(part1, part2, typeinfo, fileinfo) abort
let basic_info = split(a:part1, '\t')
let taginfo = s:NormalTag.New(basic_info[0])
let taginfo.file = basic_info[1]
" the pattern can contain tabs and thus may have been split up, so join
" the rest of the items together again
let pattern = join(basic_info[2:], "\t")
let start = 2 " skip the slash and the ^
let end = strlen(pattern) - 1
if pattern[end - 1] ==# '$'
let end -= 1
let dollar = '\$'
else
let dollar = ''
endif
let pattern = strpart(pattern, start, end - start)
let taginfo.pattern = '\V\^\C' . pattern . dollar
" When splitting fields make sure not to create empty keys or values in
" case a value illegally contains tabs
let fields = split(a:part2, '^\t\|\t\ze\w\+:')
if fields[0] !~# ':'
let taginfo.fields.kind = remove(fields, 0)
endif
for field in fields
" can't use split() since the value can contain ':'
let delimit = stridx(field, ':')
let key = strpart(field, 0, delimit)
" Remove all tabs that may illegally be in the value
let val = substitute(strpart(field, delimit + 1), '\t', '', 'g')
" File-restricted scoping
if key == "file"
let taginfo.fields[key] = 'yes'
endif
if len(val) > 0
if key == 'line' || key == 'column'
let taginfo.fields[key] = str2nr(val)
else
let taginfo.fields[key] = val
endif
endif
endfor
" Needed for jsctags
if has_key(taginfo.fields, 'lineno')
let taginfo.fields.line = str2nr(taginfo.fields.lineno)
endif
" Do some sanity checking in case ctags reports invalid line numbers
if taginfo.fields.line < 0
let taginfo.fields.line = 0
endif
if !has_key(taginfo.fields, 'kind')
call s:debug("Warning: No 'kind' field found for tag " . basic_info[0] . "!")
if index(s:warnings.type, a:typeinfo.ftype) == -1
call s:warning("No 'kind' field found for tag " . basic_info[0] . "!" .
\ " Please read the last section of ':help tagbar-extend'.")
call add(s:warnings.type, a:typeinfo.ftype)
endif
return {}
endif
" Make some information easier accessible
if has_key(a:typeinfo, 'scope2kind')
for scope in keys(a:typeinfo.scope2kind)
if has_key(taginfo.fields, scope)
let taginfo.scope = scope
let taginfo.path = taginfo.fields[scope]
let taginfo.fullpath = taginfo.path . a:typeinfo.sro .
\ taginfo.name
break
endif
endfor
let taginfo.depth = len(split(taginfo.path, '\V' . a:typeinfo.sro))
endif
let taginfo.fileinfo = a:fileinfo
let taginfo.typeinfo = a:typeinfo
" Needed for folding
try
call taginfo.initFoldState()
catch /^Vim(\a\+):E716:/ " 'Key not present in Dictionary'
" The tag has a 'kind' that doesn't exist in the type definition
call s:debug('Warning: Unknown tag kind: ' . taginfo.fields.kind)
if index(s:warnings.type, a:typeinfo.ftype) == -1
call s:warning('Unknown tag kind encountered: ' .
\ '"' . taginfo.fields.kind . '".' .
\ ' Your ctags and Tagbar configurations are out of sync!' .
\ ' Please read '':help tagbar-extend''.')
call add(s:warnings.type, a:typeinfo.ftype)
endif
return {}
endtry
return taginfo
endfunction
" s:AddScopedTags() {{{2
" Recursively process tags. Unfortunately there is a problem: not all tags in
" a hierarchy are actually there. For example, in C++ a class can be defined
" in a header file and implemented in a .cpp file (so the class itself doesn't
" appear in the .cpp file and thus doesn't generate a tag). Another example
" are anonymous structures like namespaces, structs, enums, and unions, that
" also don't get a tag themselves. These tags are thus called 'pseudo-tags' in
" Tagbar. Properly parsing them is quite tricky, so try not to think about it
" too much.
function! s:AddScopedTags(tags, processedtags, parent, depth,
\ typeinfo, fileinfo, maxline) abort
if !empty(a:parent)
let curpath = a:parent.fullpath
let pscope = a:typeinfo.kind2scope[a:parent.fields.kind]
else
let curpath = ''
let pscope = ''
endif
let is_cur_tag = 'v:val.depth == a:depth'
if !empty(curpath)
" Check whether the tag is either a direct child at the current depth
" or at least a proper grandchild with pseudo-tags in between. If it
" is a direct child also check for matching scope.
let is_cur_tag .= ' &&
\ (v:val.path ==# curpath ||
\ match(v:val.path, ''\V\^\C'' . curpath . a:typeinfo.sro) == 0) &&
\ (v:val.path ==# curpath ? (v:val.scope ==# pscope) : 1) &&
\ v:val.fields.line >= a:parent.fields.line &&
\ v:val.fields.line <= a:maxline'
endif
let curtags = filter(copy(a:tags), is_cur_tag)
if !empty(curtags)
call filter(a:tags, '!(' . is_cur_tag . ')')
let realtags = []
let pseudotags = []
while !empty(curtags)
let tag = remove(curtags, 0)
if tag.path != curpath
" tag is child of a pseudo-tag, so create a new pseudo-tag and
" add all its children to it
let pseudotag = s:ProcessPseudoTag(curtags, tag, a:parent,
\ a:typeinfo, a:fileinfo)
call add(pseudotags, pseudotag)
else
call add(realtags, tag)
endif
endwhile
" Recursively add the children of the tags on the current level
for tag in realtags
let tag.parent = a:parent
if !has_key(a:typeinfo.kind2scope, tag.fields.kind)
continue
endif
if !has_key(tag, 'children')
let tag.children = []
endif
" Check for tags with the exact same name that may be created
" alternatively in a conditional (Issue #139). The only way to
" distinguish between them is by line number.
let twins = filter(copy(realtags),
\ "v:val.fullpath ==# '" .
\ substitute(tag.fullpath, "'", "''", 'g') . "'" .
\ " && v:val.fields.line != " . tag.fields.line)
let maxline = line('$')
for twin in twins
if twin.fields.line <= maxline &&
\ twin.fields.line > tag.fields.line
let maxline = twin.fields.line - 1
endif
endfor
call s:AddScopedTags(a:tags, tag.children, tag, a:depth + 1,
\ a:typeinfo, a:fileinfo, maxline)
endfor
call extend(a:processedtags, realtags)
" Recursively add the children of the tags that are children of the
" pseudo-tags on the current level
for tag in pseudotags
call s:ProcessPseudoChildren(a:tags, tag, a:depth, a:typeinfo,
\ a:fileinfo)
endfor
call extend(a:processedtags, pseudotags)
endif
" Now we have to check if there are any pseudo-tags at the current level
" so we have to check for real tags at a lower level, i.e. grandchildren
let is_grandchild = 'v:val.depth > a:depth'
if !empty(curpath)
let is_grandchild .=
\ ' && match(v:val.path, ''\V\^\C'' . curpath . a:typeinfo.sro) == 0'
endif
let grandchildren = filter(copy(a:tags), is_grandchild)
if !empty(grandchildren)
call s:AddScopedTags(a:tags, a:processedtags, a:parent, a:depth + 1,
\ a:typeinfo, a:fileinfo, a:maxline)
endif
endfunction
" s:ProcessPseudoTag() {{{2
function! s:ProcessPseudoTag(curtags, tag, parent, typeinfo, fileinfo) abort
let curpath = !empty(a:parent) ? a:parent.fullpath : ''
let pseudoname = substitute(a:tag.path, curpath, '', '')
let pseudoname = substitute(pseudoname, '\V\^' . a:typeinfo.sro, '', '')
let pseudotag = s:CreatePseudoTag(pseudoname, a:parent, a:tag.scope,
\ a:typeinfo, a:fileinfo)
let pseudotag.children = [a:tag]
" get all the other (direct) children of the current pseudo-tag
let ispseudochild = 'v:val.path ==# a:tag.path && v:val.scope ==# a:tag.scope'
let pseudochildren = filter(copy(a:curtags), ispseudochild)
if !empty(pseudochildren)
call filter(a:curtags, '!(' . ispseudochild . ')')
call extend(pseudotag.children, pseudochildren)
endif
return pseudotag
endfunction
" s:ProcessPseudoChildren() {{{2
function! s:ProcessPseudoChildren(tags, tag, depth, typeinfo, fileinfo) abort
for childtag in a:tag.children
let childtag.parent = a:tag
if !has_key(a:typeinfo.kind2scope, childtag.fields.kind)
continue
endif
if !has_key(childtag, 'children')
let childtag.children = []
endif
call s:AddScopedTags(a:tags, childtag.children, childtag, a:depth + 1,
\ a:typeinfo, a:fileinfo, line('$'))
endfor
let is_grandchild = 'v:val.depth > a:depth && ' .
\ 'match(v:val.path,' .
\ '''^\C'' . substitute(a:tag.fullpath, "''", "''''", "g")) == 0'
let grandchildren = filter(copy(a:tags), is_grandchild)
if !empty(grandchildren)
call s:AddScopedTags(a:tags, a:tag.children, a:tag, a:depth + 1,
\ a:typeinfo, a:fileinfo, line('$'))
endif
endfunction
" s:CreatePseudoTag() {{{2
function! s:CreatePseudoTag(name, parent, scope, typeinfo, fileinfo) abort
if !empty(a:parent)
let curpath = a:parent.fullpath
let pscope = a:typeinfo.kind2scope[a:parent.fields.kind]
else
let curpath = ''
let pscope = ''
endif
let pseudotag = s:PseudoTag.New(a:name)
let pseudotag.fields.kind = a:typeinfo.scope2kind[a:scope]
let parentscope = substitute(curpath, a:name . '$', '', '')
let parentscope = substitute(parentscope,
\ '\V\^' . a:typeinfo.sro . '\$', '', '')
if pscope != ''
let pseudotag.fields[pscope] = parentscope
let pseudotag.scope = pscope
let pseudotag.path = parentscope
let pseudotag.fullpath =
\ pseudotag.path . a:typeinfo.sro . pseudotag.name
endif
let pseudotag.depth = len(split(pseudotag.path, '\V' . a:typeinfo.sro))
let pseudotag.parent = a:parent
let pseudotag.fileinfo = a:fileinfo
let pseudotag.typeinfo = a:typeinfo
call pseudotag.initFoldState()
return pseudotag
endfunction
" Sorting {{{1
" s:SortTags() {{{2
function! s:SortTags(tags, comparemethod) abort
call sort(a:tags, a:comparemethod)
for tag in a:tags
if has_key(tag, 'children')
call s:SortTags(tag.children, a:comparemethod)
endif
endfor
endfunction
" s:CompareByKind() {{{2
function! s:CompareByKind(tag1, tag2) abort
let typeinfo = s:compare_typeinfo
if typeinfo.kinddict[a:tag1.fields.kind] <#
\ typeinfo.kinddict[a:tag2.fields.kind]
return -1
elseif typeinfo.kinddict[a:tag1.fields.kind] >#
\ typeinfo.kinddict[a:tag2.fields.kind]
return 1
else
" Ignore '~' prefix for C++ destructors to sort them directly under
" the constructors
if a:tag1.name[0] ==# '~'
let name1 = a:tag1.name[1:]
else
let name1 = a:tag1.name
endif
if a:tag2.name[0] ==# '~'
let name2 = a:tag2.name[1:]
else
let name2 = a:tag2.name
endif
if name1 <=# name2
return -1
else
return 1
endif
endif
endfunction
" s:CompareByLine() {{{2
function! s:CompareByLine(tag1, tag2) abort
return a:tag1.fields.line - a:tag2.fields.line
endfunction
" s:ToggleSort() {{{2
function! s:ToggleSort() abort
let fileinfo = s:known_files.getCurrent(0)
if empty(fileinfo)
return
endif
" Save the tag the cursor is currently on
let curline = line('.')
let taginfo = s:GetTagInfo(curline, 0)
match none
let s:compare_typeinfo = s:known_types[fileinfo.ftype]
if has_key(s:compare_typeinfo, 'sort')
let s:compare_typeinfo.sort = !s:compare_typeinfo.sort
else
let g:tagbar_sort = !g:tagbar_sort
endif
call fileinfo.sortTags()
call s:RenderContent()
call s:SetStatusLine('current')
" If we were on a tag before sorting then jump to it, otherwise restore
" the cursor to the current line
if !empty(taginfo)
execute taginfo.tline
else
execute curline
endif
endfunction
" Display {{{1
" s:RenderContent() {{{2
function! s:RenderContent(...) abort
call s:debug('RenderContent called')
let s:new_window = 0
if a:0 == 1
let fileinfo = a:1
else
let fileinfo = s:known_files.getCurrent(0)
endif
if empty(fileinfo)
call s:debug('Empty fileinfo, returning')
return
endif
let tagbarwinnr = bufwinnr('__Tagbar__')
if &filetype == 'tagbar'
let in_tagbar = 1
else
let in_tagbar = 0
let prevwinnr = winnr()
" Get the previous window number, so that we can reproduce
" the window entering history later. Do not run autocmd on
" this command, make sure nothing is interfering.
" let pprevwinnr = winnr('#') " Messes up windows for some reason
call s:goto_win('p', 1)
let pprevwinnr = winnr()
call s:goto_win(tagbarwinnr, 1)
endif
if !empty(s:known_files.getCurrent(0)) &&
\ fileinfo.fpath ==# s:known_files.getCurrent(0).fpath
" We're redisplaying the same file, so save the view
call s:debug('Redisplaying file [' . fileinfo.fpath . ']')
let saveline = line('.')
let savecol = col('.')
let topline = line('w0')
endif
let lazyredraw_save = &lazyredraw
set lazyredraw
let eventignore_save = &eventignore
set eventignore=all
setlocal modifiable
silent %delete _
call s:PrintHelp()
let typeinfo = fileinfo.typeinfo
if !empty(fileinfo.tags)
" Print tags
call s:PrintKinds(typeinfo, fileinfo)
else
call s:debug('No tags found, skipping printing.')
if g:tagbar_compact && s:short_help
silent 0put ='\" No tags found.'
else
silent put ='\" No tags found.'
endif
endif
" Delete empty lines at the end of the buffer
for linenr in range(line('$'), 1, -1)
if getline(linenr) =~ '^$'
execute 'silent ' . linenr . 'delete _'
else
break
endif
endfor
setlocal nomodifiable
if !empty(s:known_files.getCurrent(0)) &&
\ fileinfo.fpath ==# s:known_files.getCurrent(0).fpath
let scrolloff_save = &scrolloff
set scrolloff=0
call cursor(topline, 1)
normal! zt
call cursor(saveline, savecol)
let &scrolloff = scrolloff_save
else
" Make sure as much of the Tagbar content as possible is shown in the
" window by jumping to the top after drawing
execute 1
call winline()
" Invalidate highlight cache from old file
let s:last_highlight_tline = 0
endif
let &lazyredraw = lazyredraw_save
let &eventignore = eventignore_save
if !in_tagbar
call s:goto_win(pprevwinnr, 1)
call s:goto_win(prevwinnr, 1)
endif
endfunction
" s:PrintKinds() {{{2
function! s:PrintKinds(typeinfo, fileinfo) abort
call s:debug('PrintKinds called')
let is_first_tag = 1
for kind in a:typeinfo.kinds
let curtags = filter(copy(a:fileinfo.tags),
\ 'v:val.fields.kind ==# kind.short')
call s:debug('Printing kind: ' . kind.short .
\ ', number of (top-level) tags: ' . len(curtags))
if empty(curtags)
continue
endif
if has_key(a:typeinfo, 'kind2scope') &&
\ has_key(a:typeinfo.kind2scope, kind.short)
" Scoped tags
for tag in curtags
call s:PrintTag(tag, 0, is_first_tag, a:fileinfo, a:typeinfo)
if !g:tagbar_compact
silent put _
endif
let is_first_tag = 0
endfor
else
" Non-scoped tags
let kindtag = curtags[0].parent
if kindtag.isFolded()
let foldmarker = s:icon_closed
else
let foldmarker = s:icon_open
endif
let padding = g:tagbar_show_visibility ? ' ' : ''
if g:tagbar_compact && is_first_tag && s:short_help
silent 0put =foldmarker . padding . kind.long
else
silent put =foldmarker . padding . kind.long
endif
let curline = line('.')
let kindtag.tline = curline
let a:fileinfo.tline[curline] = kindtag
if !kindtag.isFolded()
for tag in curtags
let str = tag.strfmt()
silent put =repeat(' ', g:tagbar_indent) . str
" Save the current tagbar line in the tag for easy
" highlighting access
let curline = line('.')
let tag.tline = curline
let a:fileinfo.tline[curline] = tag
let tag.depth = 1
endfor
endif
if !g:tagbar_compact
silent put _
endif
let is_first_tag = 0
endif
endfor
endfunction
" s:PrintTag() {{{2
function! s:PrintTag(tag, depth, is_first, fileinfo, typeinfo) abort
if g:tagbar_hide_nonpublic &&
\ get(a:tag.fields, 'access', 'public') !=# 'public'
let a:tag.tline = -1
return
endif
" Print tag indented according to depth
let tagstr = repeat(' ', a:depth * g:tagbar_indent) . a:tag.strfmt()
if a:is_first && g:tagbar_compact && s:short_help
silent 0put =tagstr
else
silent put =tagstr
endif
" Save the current tagbar line in the tag for easy highlighting access
let curline = line('.')
let a:tag.tline = curline
let a:fileinfo.tline[curline] = a:tag
" Recursively print children
if a:tag.isFoldable() && !a:tag.isFolded()
for ckind in a:typeinfo.kinds
let childfilter = 'v:val.fields.kind ==# ckind.short'
if g:tagbar_hide_nonpublic
let childfilter .=
\ ' && get(v:val.fields, "access", "public") ==# "public"'
endif
let childtags = filter(copy(a:tag.children), childfilter)
if len(childtags) > 0
" Print 'kind' header of following children, but only if they
" are not scope-defining tags (since those already have an
" identifier)
if !has_key(a:typeinfo.kind2scope, ckind.short)
let indent = (a:depth + 1) * g:tagbar_indent
let indent += g:tagbar_show_visibility
let indent += 1 " fold symbol
silent put =repeat(' ', indent) . '[' . ckind.long . ']'
" Add basic tag to allow folding when on the header line
let headertag = s:BaseTag.New(ckind.long)
let headertag.parent = a:tag
let headertag.fileinfo = a:tag.fileinfo
let a:fileinfo.tline[line('.')] = headertag
endif
for childtag in childtags
call s:PrintTag(childtag, a:depth + 1, 0,
\ a:fileinfo, a:typeinfo)
endfor
endif
endfor
endif
endfunction
" s:PrintHelp() {{{2
function! s:PrintHelp() abort
if !g:tagbar_compact && s:short_help
silent 0put ='\" Press ' . s:get_map_str('help') . ' for help'
silent put _
elseif !s:short_help
silent 0put ='\" Tagbar keybindings'
silent put ='\"'
silent put ='\" --------- General ---------'
silent put ='\" ' . s:get_map_str('jump') . ': Jump to tag definition'
silent put ='\" ' . s:get_map_str('preview') . ': As above, but stay in'
silent put ='\" Tagbar window'
silent put ='\" ' . s:get_map_str('previewwin') . ': Show tag in preview window'
silent put ='\" ' . s:get_map_str('nexttag') . ': Go to next top-level tag'
silent put ='\" ' . s:get_map_str('prevtag') . ': Go to previous top-level tag'
silent put ='\" ' . s:get_map_str('showproto') . ': Display tag prototype'
silent put ='\" ' . s:get_map_str('hidenonpublic') . ': Hide non-public tags'
silent put ='\"'
silent put ='\" ---------- Folds ----------'
silent put ='\" ' . s:get_map_str('openfold') . ': Open fold'
silent put ='\" ' . s:get_map_str('closefold') . ': Close fold'
silent put ='\" ' . s:get_map_str('togglefold') . ': Toggle fold'
silent put ='\" ' . s:get_map_str('openallfolds') . ': Open all folds'
silent put ='\" ' . s:get_map_str('closeallfolds') . ': Close all folds'
silent put ='\" ' . s:get_map_str('nextfold') . ': Go to next fold'
silent put ='\" ' . s:get_map_str('prevfold') . ': Go to previous fold'
silent put ='\"'
silent put ='\" ---------- Misc -----------'
silent put ='\" ' . s:get_map_str('togglesort') . ': Toggle sort'
silent put ='\" ' . s:get_map_str('toggleautoclose') . ': Toggle autoclose option'
silent put ='\" ' . s:get_map_str('zoomwin') . ': Zoom window in/out'
silent put ='\" ' . s:get_map_str('close') . ': Close window'
silent put ='\" ' . s:get_map_str('help') . ': Toggle help'
silent put _
endif
endfunction
function! s:get_map_str(map) abort
let def = get(g:, 'tagbar_map_' . a:map)
if type(def) == type("")
return def
else
return join(def, ', ')
endif
endfunction
" s:RenderKeepView() {{{2
" The gist of this function was taken from NERDTree by Martin Grenfell.
function! s:RenderKeepView(...) abort
if a:0 == 1
let line = a:1
else
let line = line('.')
endif
let curcol = col('.')
let topline = line('w0')
call s:RenderContent()
let scrolloff_save = &scrolloff
set scrolloff=0
call cursor(topline, 1)
normal! zt
call cursor(line, curcol)
let &scrolloff = scrolloff_save
redraw
endfunction
" User actions {{{1
" s:HighlightTag() {{{2
function! s:HighlightTag(openfolds, ...) abort
let tagline = 0
let force = a:0 > 0 ? a:1 : 0
if a:0 > 1
let tag = s:GetNearbyTag(1, 0, a:2)
else
let tag = s:GetNearbyTag(1, 0)
endif
if !empty(tag)
let tagline = tag.tline
endif
" Don't highlight the tag again if it's the same one as last time.
" This prevents the Tagbar window from jumping back after scrolling with
" the mouse.
if !force && tagline == s:last_highlight_tline
return
else
let s:last_highlight_tline = tagline
endif
let tagbarwinnr = bufwinnr('__Tagbar__')
if tagbarwinnr == -1
return
endif
let prevwinnr = winnr()
call s:goto_win(tagbarwinnr)
match none
" No tag above cursor position so don't do anything
if tagline == 0
call s:goto_win(prevwinnr)
redraw
return
endif
if g:tagbar_autoshowtag == 1 || a:openfolds
call s:OpenParents(tag)
endif
" Check whether the tag is inside a closed fold and highlight the parent
" instead in that case
let tagline = tag.getClosedParentTline()
" Parent tag line number is invalid, better don't do anything
if tagline <= 0
call s:goto_win(prevwinnr)
redraw
return
endif
" Go to the line containing the tag
execute tagline
" Make sure the tag is visible in the window
call winline()
let foldpat = '[' . s:icon_open . s:icon_closed . ' ]'
let pattern = '/^\%' . tagline . 'l\s*' . foldpat . '[-+# ]\zs[^( ]\+\ze/'
call s:debug("Highlight pattern: '" . pattern . "'")
if hlexists('TagbarHighlight') " Safeguard in case syntax highlighting is disabled
execute 'match TagbarHighlight ' . pattern
else
execute 'match Search ' . pattern
endif
if a:0 <= 1 " no line explicitly given, so assume we were in the file window
call s:goto_win(prevwinnr)
endif
redraw
endfunction
" s:JumpToTag() {{{2
function! s:JumpToTag(stay_in_tagbar) abort
let taginfo = s:GetTagInfo(line('.'), 1)
let autoclose = w:autoclose
if empty(taginfo) || !taginfo.isNormalTag()
return
endif
let tagbarwinnr = winnr()
call s:GotoFileWindow(taginfo.fileinfo)
" Mark current position so it can be jumped back to
mark '
" Jump to the line where the tag is defined. Don't use the search pattern
" since it doesn't take the scope into account and thus can fail if tags
" with the same name are defined in different scopes (e.g. classes)
execute taginfo.fields.line
" If the file has been changed but not saved, the tag may not be on the
" saved line anymore, so search for it in the vicinity of the saved line
if match(getline('.'), taginfo.pattern) == -1
let interval = 1
let forward = 1
while search(taginfo.pattern, 'W' . forward ? '' : 'b') == 0
if !forward
if interval > line('$')
break
else
let interval = interval * 2
endif
endif
let forward = !forward
endwhile
endif
" If the tag is on a different line after unsaved changes update the tag
" and file infos/objects
let curline = line('.')
if taginfo.fields.line != curline
let taginfo.fields.line = curline
let taginfo.fileinfo.fline[curline] = taginfo
endif
" Center the tag in the window and jump to the correct column if available
normal! z.
call cursor(taginfo.fields.line, taginfo.fields.column)
normal! zv
if a:stay_in_tagbar
call s:HighlightTag(0)
call s:goto_win(tagbarwinnr)
redraw
elseif g:tagbar_autoclose || autoclose
" Also closes preview window
call s:CloseWindow()
else
" Close the preview window if it was opened by us
if s:pwin_by_tagbar
pclose
endif
call s:HighlightTag(0)
endif
endfunction
" s:ShowInPreviewWin() {{{2
function! s:ShowInPreviewWin() abort
let pos = getpos('.')
let taginfo = s:GetTagInfo(pos[1], 1)
if empty(taginfo) || !taginfo.isNormalTag()
return
endif
let pwin_open = 0
for win in range(1, winnr('$'))
if getwinvar(win, '&previewwindow')
let pwin_open = 1
break
endif
endfor
" We want the preview window to be relative to the file window in normal
" (horizontal) mode, and relative to the Tagbar window in vertical mode,
" to make the best use of space.
if g:tagbar_vertical == 0
call s:GotoFileWindow(taginfo.fileinfo, 1)
call s:mark_window()
endif
" Open the preview window if it is not already open. This has to be done
" explicitly before the :psearch below to better control its positioning.
if !pwin_open
silent execute
\ g:tagbar_previewwin_pos . ' pedit ' . taginfo.fileinfo.fpath
if g:tagbar_vertical != 0
silent execute 'vertical resize ' . g:tagbar_width
endif
" Remember that the preview window was opened by Tagbar so we can
" safely close it by ourselves
let s:pwin_by_tagbar = 1
endif
if g:tagbar_vertical != 0
call s:GotoFileWindow(taginfo.fileinfo, 1)
call s:mark_window()
endif
" Use psearch instead of pedit since pedit essentially reloads the file
" and creates an empty undo entry. psearch has to be called from the file
" window, and since we only want matches in the current file we disable
" the 'include' option. Also start searching at the correct line number to
" find the correct tag in case of tags with the same name and to speed up
" the searching. Unfortunately the /\%l pattern doesn't seem to work with
" psearch.
let include_save = &include
set include=
silent! execute taginfo.fields.line . ',$psearch! /' . taginfo.pattern . '/'
let &include = include_save
call s:goto_win('P', 1)
normal! zv
normal! zz
call s:goto_markedwin(1)
call s:goto_tagbar(1)
call cursor(pos[1], pos[2])
endfunction
" s:ShowPrototype() {{{2
function! s:ShowPrototype(short) abort
let taginfo = s:GetTagInfo(line('.'), 1)
if empty(taginfo)
return ''
endif
echo taginfo.getPrototype(a:short)
endfunction
" s:ToggleHelp() {{{2
function! s:ToggleHelp() abort
let s:short_help = !s:short_help
" Prevent highlighting from being off after adding/removing the help text
match none
call s:RenderContent()
execute 1
redraw
endfunction
" s:GotoNextToplevelTag() {{{2
function! s:GotoNextToplevelTag(direction) abort
let curlinenr = line('.')
let newlinenr = line('.')
if a:direction == 1
let range = range(line('.') + 1, line('$'))
else
let range = range(line('.') - 1, 1, -1)
endif
for tmplinenr in range
let taginfo = s:GetTagInfo(tmplinenr, 0)
if empty(taginfo)
continue
elseif empty(taginfo.parent)
let newlinenr = tmplinenr
break
endif
endfor
if curlinenr != newlinenr
execute newlinenr
call winline()
endif
redraw
endfunction
" Folding {{{1
" s:OpenFold() {{{2
function! s:OpenFold() abort
let fileinfo = s:known_files.getCurrent(0)
if empty(fileinfo)
return
endif
let curline = line('.')
let tag = s:GetTagInfo(curline, 0)
if empty(tag)
return
endif
call tag.openFold()
call s:RenderKeepView()
endfunction
" s:CloseFold() {{{2
function! s:CloseFold() abort
let fileinfo = s:known_files.getCurrent(0)
if empty(fileinfo)
return
endif
match none
let curline = line('.')
let curtag = s:GetTagInfo(curline, 0)
if empty(curtag)
return
endif
let newline = curtag.closeFold()
call s:RenderKeepView(newline)
endfunction
" s:ToggleFold() {{{2
function! s:ToggleFold() abort
let fileinfo = s:known_files.getCurrent(0)
if empty(fileinfo)
return
endif
match none
let curtag = s:GetTagInfo(line('.'), 0)
if empty(curtag)
return
endif
let newline = line('.')
if curtag.isKindheader()
call curtag.toggleFold()
elseif curtag.isFoldable()
if curtag.isFolded()
call curtag.openFold()
else
let newline = curtag.closeFold()
endif
else
let newline = curtag.closeFold()
endif
call s:RenderKeepView(newline)
endfunction
" s:SetFoldLevel() {{{2
function! s:SetFoldLevel(level, force) abort
if a:level < 0
call s:warning('Foldlevel can''t be negative')
return
endif
let fileinfo = s:known_files.getCurrent(0)
if empty(fileinfo)
return
endif
call s:SetFoldLevelRecursive(fileinfo, fileinfo.tags, a:level)
let typeinfo = fileinfo.typeinfo
" Apply foldlevel to 'kind's
if a:level == 0
for kind in typeinfo.kinds
call fileinfo.closeKindFold(kind)
endfor
else
for kind in typeinfo.kinds
if a:force || !kind.fold
call fileinfo.openKindFold(kind)
endif
endfor
endif
let fileinfo.foldlevel = a:level
call s:RenderContent()
endfunction
" s:SetFoldLevelRecursive() {{{2
" Apply foldlevel to normal tags
function! s:SetFoldLevelRecursive(fileinfo, tags, level) abort
for tag in a:tags
if tag.depth >= a:level
call tag.setFolded(1)
else
call tag.setFolded(0)
endif
if has_key(tag, 'children')
call s:SetFoldLevelRecursive(a:fileinfo, tag.children, a:level)
endif
endfor
endfunction
" s:OpenParents() {{{2
function! s:OpenParents(...) abort
if a:0 == 1
let tag = a:1
else
let tag = s:GetNearbyTag(1, 0)
endif
if !empty(tag)
call tag.openParents()
call s:RenderKeepView()
endif
endfunction
" s:GotoNextFold() {{{2
function! s:GotoNextFold() abort
let curlinenr = line('.')
let newlinenr = line('.')
let range = range(line('.') + 1, line('$'))
for linenr in range
let taginfo = s:GetTagInfo(linenr, 0)
if empty(taginfo)
continue
elseif !empty(get(taginfo, 'children', [])) || taginfo.isKindheader()
let newlinenr = linenr
break
endif
endfor
if curlinenr != newlinenr
execute linenr
call winline()
endif
redraw
endfunction
" s:GotoPrevFold() {{{2
function! s:GotoPrevFold() abort
let curlinenr = line('.')
let newlinenr = line('.')
let curtag = s:GetTagInfo(curlinenr, 0)
let curparent = get(curtag, 'parent', {})
let range = range(line('.') - 1, 1, -1)
for linenr in range
let taginfo = s:GetTagInfo(linenr, 0)
if empty(taginfo)
continue
" Check for the first tag that is either:
" - the last tag in an open fold, that is skip all tags that have the
" same parent as the current one, or
" - a closed parent fold.
elseif (!empty(taginfo.parent) && taginfo.parent != curparent &&
\ empty(get(taginfo, 'children', []))) ||
\ ((!empty(get(taginfo, 'children', [])) || taginfo.isKindheader()) &&
\ taginfo.isFolded())
let newlinenr = linenr
break
endif
endfor
if curlinenr != newlinenr
execute linenr
call winline()
endif
redraw
endfunction
" Helper functions {{{1
" s:AutoUpdate() {{{2
function! s:AutoUpdate(fname, force) abort
call s:debug('AutoUpdate called [' . a:fname . ']')
" This file is being loaded due to a quickfix command like vimgrep, so
" don't process it
if exists('s:tagbar_qf_active')
return
elseif exists('s:window_opening')
" This can happen if another plugin causes the active window to change
" with an autocmd during the initial Tagbar window creation. In that
" case InitWindow() hasn't had a chance to run yet and things can
" break. MiniBufExplorer does this, for example. Completely disabling
" autocmds at that point is also not ideal since for example
" statusline plugins won't be able to update.
call s:debug('Still opening window, stopping processing')
return
endif
" Get the filetype of the file we're about to process
let bufnr = bufnr(a:fname)
let ftype = getbufvar(bufnr, '&filetype')
" Don't do anything if we're in the tagbar window
if ftype == 'tagbar'
call s:debug('In Tagbar window, stopping processing')
return
endif
" Only consider the main filetype in cases like 'python.django'
let sftype = get(split(ftype, '\.'), 0, '')
call s:debug("Vim filetype: '" . ftype . "', " .
\ "sanitized filetype: '" . sftype . "'")
" Don't do anything if the file isn't supported
if !s:IsValidFile(a:fname, sftype)
call s:debug('Not a valid file, stopping processing')
let s:nearby_disabled = 1
return
endif
let updated = 0
" Process the file if it's unknown or the information is outdated.
" Testing the mtime of the file is necessary in case it got changed
" outside of Vim, for example by checking out a different version from a
" VCS.
if s:known_files.has(a:fname)
let curfile = s:known_files.get(a:fname)
" if a:force || getbufvar(curfile.bufnr, '&modified') ||
if a:force || empty(curfile) || curfile.ftype != sftype ||
\ (filereadable(a:fname) && getftime(a:fname) > curfile.mtime)
call s:debug('File data outdated, updating [' . a:fname . ']')
call s:ProcessFile(a:fname, sftype)
let updated = 1
else
call s:debug('File data seems up to date [' . a:fname . ']')
endif
elseif !s:known_files.has(a:fname)
call s:debug('New file, processing [' . a:fname . ']')
call s:ProcessFile(a:fname, sftype)
let updated = 1
endif
let fileinfo = s:known_files.get(a:fname)
" If we don't have an entry for the file by now something must have gone
" wrong, so don't change the tagbar content
if empty(fileinfo)
call s:debug('fileinfo empty after processing [' . a:fname . ']')
return
endif
" Display the tagbar content if the tags have been updated or a different
" file is being displayed
if bufwinnr('__Tagbar__') != -1 && !s:paused &&
\ (s:new_window || updated ||
\ (!empty(s:known_files.getCurrent(0)) &&
\ a:fname != s:known_files.getCurrent(0).fpath))
call s:RenderContent(fileinfo)
endif
" Call setCurrent after rendering so RenderContent can check whether the
" same file is being redisplayed
if !empty(fileinfo)
call s:debug('Setting current file [' . a:fname . ']')
call s:known_files.setCurrent(fileinfo)
let s:nearby_disabled = 0
endif
call s:HighlightTag(0)
call s:debug('AutoUpdate finished successfully')
endfunction
" s:CheckMouseClick() {{{2
function! s:CheckMouseClick() abort
let line = getline('.')
let curcol = col('.')
if (match(line, s:icon_open . '[-+ ]') + 1) == curcol
call s:CloseFold()
elseif (match(line, s:icon_closed . '[-+ ]') + 1) == curcol
call s:OpenFold()
elseif g:tagbar_singleclick
call s:JumpToTag(0)
endif
endfunction
" s:DetectFiletype() {{{2
function! s:DetectFiletype(bufnr) abort
" Filetype has already been detected for loaded buffers, but not
" necessarily for unloaded ones
let ftype = getbufvar(a:bufnr, '&filetype')
if bufloaded(a:bufnr)
return ftype
endif
if ftype != ''
return ftype
endif
" Unloaded buffer with non-detected filetype, need to detect filetype
" manually
let bufname = bufname(a:bufnr)
let eventignore_save = &eventignore
set eventignore=FileType
let filetype_save = &filetype
exe 'doautocmd filetypedetect BufRead ' . bufname
let ftype = &filetype
let &filetype = filetype_save
let &eventignore = eventignore_save
return ftype
endfunction
" s:EscapeCtagsCmd() {{{2
" Assemble the ctags command line in a way that all problematic characters are
" properly escaped and converted to the system's encoding
" Optional third parameter is a file name to run ctags on
" Note: The second parameter (a:args) can be a list of args or
" a single string of the args.
" When a:args is a list, each argument in the list will be escaped for the
" current &shell type.
" When a:args is a string, all arguments should be escaped appropriately
" (if required). In most use cases no escaping is required so a string
" is acceptable. But in cases where arguments may need to be escaped
" differently for each &shell type, then pass a list of arguments.
function! s:EscapeCtagsCmd(ctags_bin, args, ...) abort
call s:debug('EscapeCtagsCmd called')
call s:debug('ctags_bin: ' . a:ctags_bin)
if type(a:args)==type('')
call s:debug('ctags_args (is a string): ' . a:args)
elseif type(a:args)==type([])
call s:debug('ctags_args (is a list): ' . string(a:args))
endif
if exists('+shellslash')
let shellslash_save = &shellslash
set noshellslash
endif
"Set up 0th argument of ctags_cmd
"a:ctags_bin may have special characters that require escaping.
if &shell =~ 'cmd\.exe$' && a:ctags_bin !~ '\s'
"For windows cmd.exe, escaping the 0th argument can cause
"problems if it references a batch file and the batch file uses %~dp0.
"So for windows cmd.exe, only escape the 0th argument iff necessary.
"Only known necessary case is when ctags_bin executable filename has
"whitespace character(s).
" Example: If 0th argument is wrapped in double quotes AND it is not
" an absolute path to ctags_bin, but rather an executable in %PATH%,
" then %~dp0 resolves to the current working directory rather than
" the batch file's directory. Batch files like this generally exepect
" and depend on %~dp0 to resolve the batch file's directory.
" Note: Documentation such as `help cmd.exe` and
" http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/cmd.mspx?mfr=true
" suggest other special characters that require escaping for command
" line completion. But tagbar.vim does not use the command line
" completion feature of cmd.exe and testing shows that the only special
" character that needs to be escaped for tagbar.vim is <space> for
" windows cmd.exe.
let ctags_cmd = a:ctags_bin
else
let ctags_cmd = shellescape(a:ctags_bin)
endif
"Add additional arguments to ctags_cmd
if type(a:args)==type('')
"When a:args is a string, append the arguments
"Note: In this case, do not attempt to shell escape a:args string.
"This function expects the string to already be escaped properly for
"the shell type. Why not escape? Because it could be ambiguous about
"whether a space is an argument separator or included in the argument.
"Since escaping rules vary from shell to shell, it is better to pass a
"list of arguments to a:args. With a list, each argument is clearly
"separated, so shellescape() can calculate the appropriate escaping
"for each argument for the current &shell.
let ctags_cmd .= ' ' . a:args
elseif type(a:args)==type([])
"When a:args is a list, shellescape() each argument and append ctags_cmd
"Note: It's a better practice to shellescape() each argument separately so that
"spaces used as a separator between arguments can be distinguished with
"spaces used inside a single argument.
for arg in a:args
let ctags_cmd .= ' ' . shellescape(arg)
endfor
endif
"if a filename was specified, add filename as final argument to ctags_cmd.
if a:0 == 1
let ctags_cmd .= ' ' . shellescape(a:1)
endif
if exists('+shellslash')
let &shellslash = shellslash_save
endif
" Needed for cases where 'encoding' is different from the system's
" encoding
if has('multi_byte')
if g:tagbar_systemenc != &encoding
let ctags_cmd = iconv(ctags_cmd, &encoding, g:tagbar_systemenc)
elseif $LANG != ''
let ctags_cmd = iconv(ctags_cmd, &encoding, $LANG)
endif
endif
call s:debug('Escaped ctags command: ' . ctags_cmd)
if ctags_cmd == ''
if !s:warnings.encoding
call s:warning('Tagbar: Ctags command encoding conversion failed!' .
\ ' Please read ":h g:tagbar_systemenc".')
let s:warnings.encoding = 1
endif
endif
return ctags_cmd
endfunction
" s:ExecuteCtags() {{{2
" Execute ctags with necessary shell settings
" Partially based on the discussion at
" http://vim.1045645.n5.nabble.com/bad-default-shellxquote-in-Widows-td1208284.html
function! s:ExecuteCtags(ctags_cmd) abort
call s:debug('Executing ctags command: ' . a:ctags_cmd)
if &shell =~# 'fish$'
" Reset shell since fish isn't really compatible
let shell_save = &shell
set shell=sh
endif
if exists('+shellslash')
let shellslash_save = &shellslash
set noshellslash
endif
if &shell =~ 'cmd\.exe'
let shellxquote_save = &shellxquote
set shellxquote=\"
let shellcmdflag_save = &shellcmdflag
set shellcmdflag=/s\ /c
endif
if s:debug
silent 5verbose let ctags_output = system(a:ctags_cmd)
call s:debug(v:statusmsg)
redraw!
else
silent let ctags_output = system(a:ctags_cmd)
endif
if &shell =~ 'cmd\.exe'
let &shellxquote = shellxquote_save
let &shellcmdflag = shellcmdflag_save
endif
if exists('+shellslash')
let &shellslash = shellslash_save
endif
if exists('shell_save')
let &shell = shell_save
endif
return ctags_output
endfunction
" s:GetNearbyTag() {{{2
" Get the tag info for a file near the cursor in the current file
function! s:GetNearbyTag(all, forcecurrent, ...) abort
if s:nearby_disabled
return {}
endif
let fileinfo = s:known_files.getCurrent(a:forcecurrent)
if empty(fileinfo)
return {}
endif
let typeinfo = fileinfo.typeinfo
if a:0 > 0
let curline = a:1
else
let curline = line('.')
endif
let tag = {}
" If a tag appears in a file more than once (for example namespaces in
" C++) only one of them has a 'tline' entry and can thus be highlighted.
" The only way to solve this would be to go over the whole tag list again,
" making everything slower. Since this should be a rare occurence and
" highlighting isn't /that/ important ignore it for now.
for line in range(curline, 1, -1)
if has_key(fileinfo.fline, line)
let curtag = fileinfo.fline[line]
if a:all || typeinfo.getKind(curtag.fields.kind).stl
let tag = curtag
break
endif
endif
endfor
return tag
endfunction
" s:GetTagInfo() {{{2
" Return the info dictionary of the tag on the specified line. If the line
" does not contain a valid tag (for example because it is empty or only
" contains a pseudo-tag) return an empty dictionary.
function! s:GetTagInfo(linenr, ignorepseudo) abort
let fileinfo = s:known_files.getCurrent(0)
if empty(fileinfo)
return {}
endif
" Don't do anything in empty and comment lines
let curline = getbufline(bufnr('__Tagbar__'), a:linenr)[0]
if curline =~ '^\s*$' || curline[0] == '"'
return {}
endif
" Check if there is a tag on the current line
if !has_key(fileinfo.tline, a:linenr)
return {}
endif
let taginfo = fileinfo.tline[a:linenr]
" Check if the current tag is not a pseudo-tag
if a:ignorepseudo && taginfo.isPseudoTag()
return {}
endif
return taginfo
endfunction
" s:GetFileWinnr() {{{2
" Get the number of the window that has Tagbar's current file loaded into it,
" or 0 if no window has loaded it. It tries the previous window first, if that
" does not have the correct buffer loaded it will look for the first one with
" the correct buffer in it.
function! s:GetFileWinnr(fileinfo) abort
let filewinnr = 0
let prevwinnr = winnr("#")
if winbufnr(prevwinnr) == a:fileinfo.bufnr &&
\ !getwinvar(prevwinnr, '&previewwindow')
let filewinnr = prevwinnr
else
" Search for the first real window that has the correct buffer loaded
" in it. Similar to bufwinnr() but skips the previewwindow.
for i in range(1, winnr('$'))
call s:goto_win(i, 1)
if bufnr('%') == a:fileinfo.bufnr && !&previewwindow
let filewinnr = winnr()
break
endif
endfor
call s:goto_tagbar(1)
endif
return filewinnr
endfunction
" s:GotoFileWindow() {{{2
" Try to switch to the window that has Tagbar's current file loaded in it, or
" open the file in an existing window otherwise.
function! s:GotoFileWindow(fileinfo, ...) abort
let noauto = a:0 > 0 ? a:1 : 0
let filewinnr = s:GetFileWinnr(a:fileinfo)
" If there is no window with the correct buffer loaded then load it
" into the first window that has a non-special buffer in it.
if filewinnr == 0
for i in range(1, winnr('$'))
call s:goto_win(i, 1)
if &buftype == '' && !&previewwindow
execute 'buffer ' . a:fileinfo.bufnr
break
endif
endfor
else
call s:goto_win(filewinnr, 1)
endif
" To make ctrl-w_p work we switch between the Tagbar window and the
" correct window once
call s:goto_tagbar(noauto)
call s:goto_win('p', noauto)
endfunction
" s:ToggleHideNonPublicTags() {{{2
function! s:ToggleHideNonPublicTags() abort
let g:tagbar_hide_nonpublic = !g:tagbar_hide_nonpublic
call s:RenderKeepView()
call s:SetStatusLine('current')
endfunction
" s:ToggleAutoclose() {{{2
function! s:ToggleAutoclose() abort
let g:tagbar_autoclose = !g:tagbar_autoclose
call s:SetStatusLine('current')
endfunction
" s:IsValidFile() {{{2
function! s:IsValidFile(fname, ftype) abort
call s:debug('Checking if file is valid [' . a:fname . ']')
if a:fname == '' || a:ftype == ''
call s:debug('Empty filename or type')
return 0
endif
if !filereadable(a:fname) && getbufvar(a:fname, 'netrw_tmpfile') == ''
call s:debug('File not readable')
return 0
endif
if getbufvar(a:fname, 'tagbar_ignore') == 1
call s:debug('File is marked as ignored')
return 0
endif
if &previewwindow
call s:debug('In preview window')
return 0
endif
if !has_key(s:known_types, a:ftype)
if exists('g:tagbar_type_' . a:ftype)
" Filetype definition must have been specified in an 'ftplugin'
" file, so load it now
call s:LoadUserTypeDefs(a:ftype)
else
call s:debug('Unsupported filetype: ' . a:ftype)
return 0
endif
endif
return 1
endfunction
" s:SetStatusLine() {{{2
function! s:SetStatusLine(current)
" Make sure we're actually in the Tagbar window
let tagbarwinnr = bufwinnr('__Tagbar__')
if tagbarwinnr == -1
return
endif
if tagbarwinnr != winnr()
let in_tagbar = 0
call s:goto_win(tagbarwinnr)
else
let in_tagbar = 1
endif
let current = a:current == 'current'
let sort = g:tagbar_sort ? 'Name' : 'Order'
if !empty(s:known_files.getCurrent(0))
let fname = fnamemodify(s:known_files.getCurrent(0).fpath, ':t')
else
let fname = ''
endif
let flags = []
let flags += exists('w:autoclose') && w:autoclose ? ['c'] : []
let flags += g:tagbar_autoclose ? ['C'] : []
let flags += g:tagbar_hide_nonpublic ? ['v'] : []
if exists('g:tagbar_status_func')
let args = [current, sort, fname, flags]
let &l:statusline = call(g:tagbar_status_func, args)
else
let colour = current ? '%#StatusLine#' : '%#StatusLineNC#'
let flagstr = join(flags, '')
if flagstr != ''
let flagstr = '[' . flagstr . '] '
endif
let text = colour . '[' . sort . '] ' . flagstr . fname
let &l:statusline = text
endif
if !in_tagbar
call s:goto_win('p')
endif
endfunction
" s:QuitIfOnlyWindow() {{{2
function! s:QuitIfOnlyWindow() abort
let tagbarwinnr = bufwinnr('__Tagbar__')
if tagbarwinnr == -1
return
endif
let curwinnr = winnr()
let prevwinnr = winnr('#') == 0 ? curwinnr : winnr('#')
call s:goto_win(tagbarwinnr, 1)
" Check if there is more than one window
if s:NextNormalWindow() == -1
" Check if there is more than one tab page
if tabpagenr('$') == 1
" Before quitting Vim, delete the tagbar buffer so that
" the '0 mark is correctly set to the previous buffer.
" Also disable autocmd on this command to avoid unnecessary
" autocmd nesting.
if winnr('$') == 1
noautocmd bdelete
endif
quit
else
close
endif
endif
call s:goto_win(prevwinnr, 1)
call s:goto_win(curwinnr, 1)
endfunction
" s:NextNormalWindow() {{{2
function! s:NextNormalWindow() abort
for i in range(1, winnr('$'))
let buf = winbufnr(i)
" skip unlisted buffers
if !buflisted(buf)
continue
endif
" skip temporary buffers with buftype set
if getbufvar(buf, '&buftype') != ''
continue
endif
" skip the preview window
if getwinvar(i, '&previewwindow')
continue
endif
" skip current window
if i == winnr()
continue
endif
return i
endfor
return -1
endfunction
" s:goto_win() {{{2
function! s:goto_win(winnr, ...) abort
let cmd = type(a:winnr) == type(0) ? a:winnr . 'wincmd w'
\ : 'wincmd ' . a:winnr
let noauto = a:0 > 0 ? a:1 : 0
call s:debug("goto_win(): " . cmd . ", " . noauto)
if noauto
noautocmd execute cmd
else
execute cmd
endif
endfunction
" s:goto_tagbar() {{{2
function! s:goto_tagbar(...) abort
let noauto = a:0 > 0 ? a:1 : 0
call s:goto_win(bufwinnr('__Tagbar__'), noauto)
endfunction
" s:mark_window() {{{2
" Mark window with a window-local variable so we can jump back to it even if
" the window numbers have changed.
function! s:mark_window() abort
let w:tagbar_mark = 1
endfunction
" s:goto_markedwin() {{{2
" Go to a previously marked window and delete the mark.
function! s:goto_markedwin(...) abort
let noauto = a:0 > 0 ? a:1 : 0
for window in range(1, winnr('$'))
call s:goto_win(window, noauto)
if exists('w:tagbar_mark')
unlet w:tagbar_mark
break
endif
endfor
endfunction
" s:warning() {{{2
function! s:warning(msg) abort
echohl WarningMsg
echomsg a:msg
echohl None
endfunction
" TagbarBalloonExpr() {{{2
function! TagbarBalloonExpr() abort
let taginfo = s:GetTagInfo(v:beval_lnum, 1)
if empty(taginfo)
return ''
endif
return taginfo.getPrototype(0)
endfunction
" Debugging {{{1
" s:StartDebug() {{{2
function! s:StartDebug(filename) abort
if empty(a:filename)
let s:debug_file = 'tagbardebug.log'
else
let s:debug_file = a:filename
endif
" Empty log file
exe 'redir! > ' . s:debug_file
redir END
" Check whether the log file could be created
if !filewritable(s:debug_file)
echomsg 'Tagbar: Unable to create log file ' . s:debug_file
let s:debug_file = ''
return
endif
let s:debug = 1
endfunction
" s:StopDebug() {{{2
function! s:StopDebug() abort
let s:debug = 0
let s:debug_file = ''
endfunction
" s:debug() {{{2
if has('reltime')
function! s:gettime() abort
let time = split(reltimestr(reltime()), '\.')
return strftime('%Y-%m-%d %H:%M:%S.', time[0]) . time[1]
endfunction
else
function! s:gettime() abort
return strftime('%Y-%m-%d %H:%M:%S')
endfunction
endif
function! s:debug(msg) abort
if s:debug
execute 'redir >> ' . s:debug_file
silent echon s:gettime() . ': ' . a:msg . "\n"
redir END
endif
endfunction
" Autoload functions {{{1
" Wrappers {{{2
function! tagbar#ToggleWindow() abort
call s:ToggleWindow()
endfunction
function! tagbar#OpenWindow(...) abort
let flags = a:0 > 0 ? a:1 : ''
call s:OpenWindow(flags)
endfunction
function! tagbar#CloseWindow() abort
call s:CloseWindow()
endfunction
function! tagbar#SetFoldLevel(level, force) abort
call s:SetFoldLevel(a:level, a:force)
endfunction
function! tagbar#highlighttag(openfolds, force) abort
let tagbarwinnr = bufwinnr('__Tagbar__')
if tagbarwinnr == -1
echohl WarningMsg
echomsg "Warning: Can't highlight tag, Tagbar window not open"
echohl None
return
endif
call s:HighlightTag(a:openfolds, a:force)
endfunction
function! tagbar#StartDebug(...) abort
let filename = a:0 > 0 ? a:1 : ''
call s:StartDebug(filename)
endfunction
function! tagbar#StopDebug() abort
call s:StopDebug()
endfunction
function! tagbar#RestoreSession() abort
call s:RestoreSession()
endfunction
" }}}2
" tagbar#toggle_pause() {{{2
function! tagbar#toggle_pause() abort
let s:paused = !s:paused
if s:paused
call s:known_files.setPaused()
else
call s:AutoUpdate(fnamemodify(expand('%'), ':p'), 1)
endif
endfunction
" tagbar#getusertypes() {{{2
function! tagbar#getusertypes() abort
let userdefs = filter(copy(g:), 'v:key =~ "^tagbar_type_"')
let typedict = {}
for [key, val] in items(userdefs)
let type = substitute(key, '^tagbar_type_', '', '')
let typedict[type] = val
endfor
return typedict
endfunction
" tagbar#autoopen() {{{2
" Automatically open Tagbar if one of the open buffers contains a supported
" file
function! tagbar#autoopen(...) abort
call s:debug('tagbar#autoopen called [' . bufname('%') . ']')
let always = a:0 > 0 ? a:1 : 1
call s:Init(0)
for bufnr in range(1, bufnr('$'))
if buflisted(bufnr) && (always || bufwinnr(bufnr) != -1)
let ftype = s:DetectFiletype(bufnr)
if s:IsValidFile(bufname(bufnr), ftype)
call s:OpenWindow('')
call s:debug('tagbar#autoopen finished after finding valid ' .
\ 'file [' . bufname(bufnr) . ']')
return
endif
endif
endfor
call s:debug('tagbar#autoopen finished without finding valid file')
endfunction
" tagbar#currenttag() {{{2
function! tagbar#currenttag(fmt, default, ...) abort
" Indicate that the statusline functionality is being used. This prevents
" the CloseWindow() function from removing the autocommands.
let s:statusline_in_use = 1
if a:0 > 0
" also test for non-zero value for backwards compatibility
let longsig = a:1 =~# 's' || (type(a:1) == type(0) && a:1 != 0)
let fullpath = a:1 =~# 'f'
let prototype = a:1 =~# 'p'
else
let longsig = 0
let fullpath = 0
let prototype = 0
endif
if !s:Init(1)
return a:default
endif
let tag = s:GetNearbyTag(0, 1)
if !empty(tag)
if prototype
return tag.getPrototype(1)
else
return printf(a:fmt, tag.str(longsig, fullpath))
endif
else
return a:default
endif
endfunction
" tagbar#currentfile() {{{2
function! tagbar#currentfile() abort
let filename = ''
if !empty(s:known_files.getCurrent(1))
let filename = fnamemodify(s:known_files.getCurrent(1).fpath, ':t')
endif
return filename
endfunction
" tagbar#gettypeconfig() {{{2
function! tagbar#gettypeconfig(type) abort
if !s:Init(1)
return ''
endif
let typeinfo = get(s:known_types, a:type, {})
if empty(typeinfo)
call s:warning('Unknown type ' . a:type . '!')
return
endif
let output = "let g:tagbar_type_" . a:type . " = {\n"
let output .= " \\ 'kinds' : [\n"
for kind in typeinfo.kinds
let output .= " \\ '" . kind.short . ":" . kind.long
if kind.fold || !kind.stl
if kind.fold
let output .= ":1"
else
let output .= ":0"
endif
endif
if !kind.stl
let output .= ":0"
endif
let output .= "',\n"
endfor
let output .= " \\ ],\n"
let output .= "\\ }"
silent put =output
endfunction
" tagbar#inspect() {{{2
function! tagbar#inspect(var) abort
return get(s:, a:var)
endfunction
" Modeline {{{1
" vim: ts=8 sw=4 sts=4 et foldenable foldmethod=marker foldcolumn=1