tagbar/plugin/tagbar.vim

1877 lines
53 KiB
VimL
Raw Normal View History

2011-01-11 17:36:49 +08:00
" ============================================================================
" File: tagbar.vim
" Description: List the current file's tags in a sidebar, ordered by class etc
2011-02-16 20:19:24 +08:00
" Author: Jan Larres <jan@majutsushi.net>
2011-01-11 17:36:49 +08:00
" Licence: Vim licence
2011-02-20 06:04:09 +08:00
" Website: http://majutsushi.github.com/tagbar/
2011-02-28 13:12:36 +08:00
" Version: 1.2
2011-01-11 17:36:49 +08:00
" Note: This plugin was heavily inspired by the 'Taglist' plugin by
2011-02-13 14:20:15 +08:00
" Yegappan Lakshmanan and uses a small amount of code from it.
2011-02-14 17:02:44 +08:00
"
" 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.
2011-01-11 17:36:49 +08:00
" ============================================================================
if &cp || exists('g:loaded_tagbar')
finish
endif
2011-01-24 17:37:52 +08:00
" Initialization {{{1
if !exists('g:tagbar_ctags_bin')
if executable('ctags-exuberant')
let g:tagbar_ctags_bin = 'ctags-exuberant'
2011-01-11 17:36:49 +08:00
elseif executable('exctags')
let g:tagbar_ctags_bin = 'exctags'
2011-01-11 17:36:49 +08:00
elseif executable('ctags')
let g:tagbar_ctags_bin = 'ctags'
2011-01-11 17:36:49 +08:00
elseif executable('ctags.exe')
let g:tagbar_ctags_bin = 'ctags.exe'
2011-01-11 17:36:49 +08:00
elseif executable('tags')
let g:tagbar_ctags_bin = 'tags'
2011-01-11 17:36:49 +08:00
else
echomsg 'Tagbar: Exuberant ctags not found, skipping plugin'
finish
endif
endif
let g:loaded_tagbar = 1
if !exists('g:tagbar_left')
let g:tagbar_left = 0
endif
if !exists('g:tagbar_width')
2011-01-14 18:12:26 +08:00
let g:tagbar_width = 40
2011-01-11 17:36:49 +08:00
endif
2011-01-12 18:30:38 +08:00
if !exists('g:tagbar_autoclose')
let g:tagbar_autoclose = 0
endif
if !exists('g:tagbar_autofocus')
let g:tagbar_autofocus = 0
endif
if !exists('g:tagbar_sort')
let g:tagbar_sort = 1
endif
2011-02-14 11:11:59 +08:00
if !exists('g:tagbar_compact')
let g:tagbar_compact = 0
endif
if !exists('g:tagbar_expand')
let g:tagbar_expand = 0
endif
let s:type_init_done = 0
let s:key_mapping_done = 0
let s:autocommands_done = 0
let s:window_expanded = 0
2011-02-14 18:47:18 +08:00
" s:InitTypes() {{{2
2011-01-12 17:48:04 +08:00
function! s:InitTypes()
" Dictionary of the already processed files, indexed by file name with
2011-01-20 11:47:29 +08:00
" complete path.
" The entries are again dictionaries with the following fields:
" - mtime: File modification time
2011-01-20 13:16:57 +08:00
" - ftype: The vim file type
" - tags: List of the tags that are present in the file, sorted
" according to the value of 'g:tagbar_sort'
2011-01-20 11:47:29 +08:00
" - fline: Dictionary of the tags, indexed by line number in the file
" - tline: Dictionary of the tags, indexed by line number in the tagbar
2011-01-12 17:48:04 +08:00
let s:known_files = {}
2011-01-20 11:47:29 +08:00
2011-01-12 17:48:04 +08:00
let s:known_types = {}
2011-02-14 18:47:18 +08:00
" Ant {{{3
2011-01-25 14:32:30 +08:00
let type_ant = {}
let type_ant.ctagstype = 'ant'
let type_ant.kinds = [
\ 'p:projects',
\ 't:targets'
\ ]
let s:known_types.ant = type_ant
2011-02-14 18:47:18 +08:00
" Asm {{{3
2011-01-25 15:12:57 +08:00
let type_asm = {}
let type_asm.ctagstype = 'asm'
let type_asm.kinds = [
\ 'm:macros',
\ 't:types',
\ 'd:defines',
\ 'l:labels'
\ ]
let s:known_types.asm = type_asm
2011-02-14 18:47:18 +08:00
" ASP {{{3
2011-01-26 14:17:09 +08:00
let type_aspvbs = {}
let type_aspvbs.ctagstype = 'asp'
let type_aspvbs.kinds = [
\ 'd:constants',
\ 'c:classes',
\ 'f:functions',
\ 's:subroutines',
\ 'v:variables'
\ ]
let s:known_types.aspvbs = type_aspvbs
2011-02-14 18:47:18 +08:00
" Awk {{{3
2011-01-26 14:17:09 +08:00
let type_awk = {}
let type_awk.ctagstype = 'awk'
let type_awk.kinds = [
\ 'f:functions'
\ ]
let s:known_types.awk = type_awk
2011-02-14 18:47:18 +08:00
" Basic {{{3
2011-01-26 14:17:09 +08:00
let type_basic = {}
let type_basic.ctagstype = 'basic'
let type_basic.kinds = [
\ 'c:constants',
\ 'g:enumerations',
\ 'f:functions',
\ 'l:labels',
\ 't:types',
\ 'v:variables'
\ ]
let s:known_types.basic = type_basic
2011-02-14 18:47:18 +08:00
" BETA {{{3
2011-01-26 14:17:09 +08:00
let type_beta = {}
let type_beta.ctagstype = 'beta'
let type_beta.kinds = [
\ 'f:fragments',
\ 's:slots',
\ 'v:patterns'
\ ]
let s:known_types.beta = type_beta
2011-02-14 18:47:18 +08:00
" C {{{3
2011-01-24 18:23:40 +08:00
let type_c = {}
let type_c.ctagstype = 'c'
let type_c.scopes = ['enum', 'struct', 'union']
let type_c.sro = '::'
let type_c.kinds = [
\ 'd:macros',
\ 'p:prototypes',
\ 'g:enums',
\ 'e:enumerators',
\ 't:typedefs',
\ 's:structs',
\ 'u:unions',
\ 'm:members',
\ 'v:variables',
\ 'f:functions'
\ ]
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
2011-02-14 18:47:18 +08:00
" C++ {{{3
2011-01-12 17:48:04 +08:00
let type_cpp = {}
let type_cpp.ctagstype = 'c++'
2011-01-20 20:38:14 +08:00
let type_cpp.scopes = [
\ 'namespace',
\ 'class',
\ 'struct',
\ 'enum',
\ 'union'
\ ]
2011-01-14 18:12:26 +08:00
let type_cpp.sro = '::'
2011-01-12 17:48:04 +08:00
let type_cpp.kinds = [
\ 'd:macros',
2011-01-24 18:23:40 +08:00
\ 'p:prototypes',
2011-01-26 14:17:09 +08:00
\ 'g:enums',
2011-01-24 18:23:40 +08:00
\ 'e:enumerators',
\ 't:typedefs',
2011-01-12 17:48:04 +08:00
\ 'n:namespaces',
\ 'c:classes',
\ 's:structs',
\ 'u:unions',
2011-01-16 22:19:11 +08:00
\ 'f:functions',
\ 'm:members',
\ 'v:variables'
2011-01-14 18:12:26 +08:00
\ ]
2011-01-16 12:46:14 +08:00
let type_cpp.kind2scope = {
2011-01-24 18:23:40 +08:00
\ 'g' : 'enum',
2011-01-16 12:46:14 +08:00
\ 'n' : 'namespace',
\ 'c' : 'class',
2011-01-20 20:38:14 +08:00
\ 's' : 'struct',
\ 'u' : 'union'
2011-01-16 12:46:14 +08:00
\ }
2011-01-17 17:58:31 +08:00
let type_cpp.scope2kind = {
2011-01-24 18:23:40 +08:00
\ 'enum' : 'g',
2011-01-17 17:58:31 +08:00
\ 'namespace' : 'n',
\ 'class' : 'c',
2011-01-20 20:38:14 +08:00
\ 'struct' : 's',
\ 'union' : 'u'
2011-01-17 17:58:31 +08:00
\ }
2011-01-12 17:48:04 +08:00
let s:known_types.cpp = type_cpp
2011-02-14 18:47:18 +08:00
" C# {{{3
2011-01-26 14:17:09 +08:00
let type_cs = {}
let type_cs.ctagstype = 'c#'
let type_cs.scopes = [
\ 'namespace',
\ 'interface',
\ 'class',
\ 'struct',
\ 'enum'
\ ]
let type_cs.sro = '.'
let type_cs.kinds = [
\ 'd:macros',
\ 'f:fields',
\ 'g:enums',
\ 'e:enumerators',
\ 't:typedefs',
\ 'n:namespaces',
\ 'i:interfaces',
\ 'c:classes',
\ 's:structs',
\ 'E:events',
\ 'm:methods',
\ 'p:properties'
\ ]
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
2011-02-14 18:47:18 +08:00
" COBOL {{{3
2011-01-26 14:17:09 +08:00
let type_cobol = {}
let type_cobol.ctagstype = 'cobol'
let type_cobol.kinds = [
\ 'd:data items',
\ 'f:file descriptions',
\ 'g:group items',
\ 'p:paragraphs',
\ 'P:program ids',
\ 's:sections'
\ ]
let s:known_types.cobol = type_cobol
2011-02-14 18:47:18 +08:00
" DOS Batch {{{3
2011-01-26 14:17:09 +08:00
let type_dosbatch = {}
let type_dosbatch.ctagstype = 'dosbatch'
let type_dosbatch.kinds = [
\ 'l:labels',
\ 'v:variables'
\ ]
let s:known_types.dosbatch = type_dosbatch
2011-02-14 18:47:18 +08:00
" Eiffel {{{3
2011-01-26 14:17:09 +08:00
let type_eiffel = {}
let type_eiffel.ctagstype = 'eiffel'
let type_eiffel.scopes = ['class', 'feature']
let type_eiffel.sro = '.' " Not sure, is nesting even possible?
let type_eiffel.kinds = [
\ 'c:classes',
\ 'f:features'
\ ]
let type_eiffel.kind2scope = {
\ 'c' : 'class',
\ 'f' : 'feature'
\ }
let type_eiffel.scope2kind = {
\ 'class' : 'c',
\ 'feature' : 'f'
\ }
let s:known_types.eiffel = type_eiffel
2011-02-14 18:47:18 +08:00
" Erlang {{{3
2011-02-04 13:29:34 +08:00
let type_erlang = {}
let type_erlang.ctagstype = 'erlang'
let type_erlang.scopes = ['module']
2011-02-05 14:33:18 +08:00
let type_erlang.sro = '.' " Not sure, is nesting even possible?
2011-02-04 13:29:34 +08:00
let type_erlang.kinds = [
\ 'm:modules',
\ 'd:macro definitions',
\ 'f:functions',
\ 'r:record definitions'
\ ]
let type_erlang.kind2scope = {
\ 'm' : 'module'
\ }
let type_erlang.scope2kind = {
\ 'module' : 'm'
\ }
let s:known_types.erlang = type_erlang
2011-02-14 18:47:18 +08:00
" Flex {{{3
2011-02-05 14:33:18 +08:00
" 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_mxml = {}
let type_mxml.ctagstype = 'flex'
let type_mxml.scopes = ['class']
let type_mxml.sro = '.'
let type_mxml.kinds = [
\ 'v:global variables',
\ 'c:classes',
\ 'm:methods',
\ 'p:properties',
\ 'f:functions',
\ 'x:mxtags'
\ ]
let type_mxml.kind2scope = {
\ 'c' : 'class'
\ }
let type_mxml.scope2kind = {
\ 'class' : 'c'
\ }
let s:known_types.mxml = type_mxml
2011-02-14 18:47:18 +08:00
" Fortran {{{3
2011-02-05 14:33:18 +08:00
let type_fortran = {}
let type_fortran.ctagstype = 'fortran'
let type_fortran.scopes = ['module', 'program', 'function', 'subroutine']
let type_fortran.sro = '.' " Not sure, is nesting even possible?
let type_fortran.kinds = [
\ 'm:modules',
\ 'p:programs',
\ 'k:components',
\ 't:derived types and structures',
\ 'c:common blocks',
\ 'b:block data',
\ 'e:entry points',
\ 'f:functions',
\ 's:subroutines',
\ 'l:labels',
\ 'n:namelists',
\ 'v:variables'
\ ]
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
2011-02-14 18:47:18 +08:00
" HTML {{{3
2011-02-05 14:33:18 +08:00
let type_html = {}
let type_html.ctagstype = 'html'
let type_html.kinds = [
\ 'f:JavaScript funtions',
\ 'a:named anchors'
\ ]
let s:known_types.html = type_html
2011-02-14 18:47:18 +08:00
" Java {{{3
2011-01-24 18:23:40 +08:00
let type_java = {}
let type_java.ctagstype = 'java'
let type_java.scopes = ['enum', 'interface', 'class']
let type_java.sro = '.'
let type_java.kinds = [
\ 'p:packages',
\ 'f:fields',
\ 'g:enum types',
\ 'e:enum constants',
\ 'i:interfaces',
\ 'c:classes',
\ 'm:methods'
\ ]
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
2011-02-14 18:47:18 +08:00
" JavaScript {{{3
2011-02-05 14:33:18 +08:00
" JavaScript is weird -- it does have scopes, but ctags doesn't seem to
" properly generate the information for them, instead it simply uses the
" complete name. So ctags has to be fixed before I can do anything here.
let type_javascript = {}
let type_javascript.ctagstype = 'javascript'
let type_javascript.kinds = [
\ 'v:global variables',
\ 'c:classes',
\ 'p:properties',
\ 'm:methods',
\ 'f:functions'
\ ]
let s:known_types.javascript = type_javascript
2011-02-14 18:47:18 +08:00
" Lisp {{{3
2011-02-05 14:33:18 +08:00
let type_lisp = {}
let type_lisp.ctagstype = 'lisp'
let type_lisp.kinds = [
\ 'f:functions'
\ ]
let s:known_types.lisp = type_lisp
2011-02-14 18:47:18 +08:00
" Lua {{{3
2011-02-05 14:33:18 +08:00
let type_lua = {}
let type_lua.ctagstype = 'lua'
let type_lua.kinds = [
\ 'f:functions'
\ ]
let s:known_types.lua = type_lua
2011-02-14 18:47:18 +08:00
" Make {{{3
2011-02-05 14:33:18 +08:00
let type_make = {}
let type_make.ctagstype = 'make'
let type_make.kinds = [
\ 'm:macros'
\ ]
let s:known_types.make = type_make
2011-02-14 18:47:18 +08:00
" Matlab {{{3
2011-02-05 14:33:18 +08:00
let type_matlab = {}
let type_matlab.ctagstype = 'matlab'
let type_matlab.kinds = [
\ 'f:functions'
\ ]
let s:known_types.matlab = type_matlab
2011-02-14 18:47:18 +08:00
" Ocaml {{{3
2011-02-05 14:33:18 +08:00
let type_ocaml = {}
let type_ocaml.ctagstype = 'ocaml'
let type_ocaml.scopes = ['Module', 'class', 'type']
let type_ocaml.sro = '.' " Not sure, is nesting even possible?
let type_ocaml.kinds = [
\ 'M:modules or functors',
\ 'v:global variables',
\ 'c:classes',
\ 'C:constructors',
\ 'm:methods',
\ 'e:exceptions',
\ 't:type names',
\ 'f:functions',
\ 'r:structure fields'
\ ]
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
2011-02-14 18:47:18 +08:00
" Pascal {{{3
let type_pascal = {}
let type_pascal.ctagstype = 'pascal'
let type_pascal.kinds = [
\ 'f:functions',
\ 'p:procedures'
\ ]
let s:known_types.pascal = type_pascal
2011-02-14 18:47:18 +08:00
" Perl {{{3
let type_perl = {}
let type_perl.ctagstype = 'perl'
let type_perl.kinds = [
\ 'p:packages',
\ 'c:constants',
\ 'f:formats',
\ 'l:labels',
\ 's:subroutines'
\ ]
let s:known_types.perl = type_perl
2011-02-14 18:47:18 +08:00
" PHP {{{3
let type_php = {}
let type_php.ctagstype = 'php'
let type_php.kinds = [
\ 'i:interfaces',
\ 'c:classes',
\ 'd:constant definitions',
\ 'f:functions',
\ 'v:variables',
\ 'j:javascript functions'
\ ]
let s:known_types.php = type_php
2011-02-14 18:47:18 +08:00
" Python {{{3
2011-01-14 18:12:26 +08:00
let type_python = {}
let type_python.ctagstype = 'python'
let type_python.scopes = ['class', 'function']
let type_python.sro = '.'
let type_python.kinds = [
\ 'i:imports',
\ 'c:classes',
\ 'f:functions',
\ 'm:members',
\ 'v:variables'
\ ]
2011-01-16 12:46:14 +08:00
let type_python.kind2scope = {
\ 'c' : 'class',
\ 'f' : 'function',
\ 'm' : 'function'
2011-01-14 18:12:26 +08:00
\ }
2011-01-17 17:58:31 +08:00
let type_python.scope2kind = {
\ 'class' : 'c',
\ 'function' : 'f'
\ }
2011-01-14 18:12:26 +08:00
let s:known_types.python = type_python
2011-02-14 18:47:18 +08:00
" REXX {{{3
let type_rexx = {}
let type_rexx.ctagstype = 'rexx'
let type_rexx.kinds = [
\ 's:subroutines'
\ ]
let s:known_types.rexx = type_rexx
2011-02-14 18:47:18 +08:00
" Ruby {{{3
let type_ruby = {}
let type_ruby.ctagstype = 'ruby'
let type_ruby.scopes = ['class']
let type_ruby.sro = '.'
let type_ruby.kinds = [
\ 'm:modules',
\ 'c:classes',
\ 'f:methods',
\ 'F:singleton methods'
\ ]
2011-02-28 13:08:31 +08:00
let type_ruby.kind2scope = {
\ 'c' : 'class',
\ 'm' : 'class'
\ }
let type_ruby.scope2kind = {
\ 'class' : 'c'
\ }
let s:known_types.ruby = type_ruby
2011-02-14 18:47:18 +08:00
" Scheme {{{3
let type_scheme = {}
let type_scheme.ctagstype = 'scheme'
let type_scheme.kinds = [
\ 'f:functions',
\ 's:sets'
\ ]
let s:known_types.scheme = type_scheme
2011-02-14 18:47:18 +08:00
" Shell script {{{3
let type_sh = {}
let type_sh.ctagstype = 'sh'
let type_sh.kinds = [
\ 'f:functions'
\ ]
let s:known_types.sh = type_sh
let s:known_types.csh = type_sh
let s:known_types.zsh = type_sh
2011-02-14 18:47:18 +08:00
" SLang {{{3
let type_slang = {}
let type_slang.ctagstype = 'slang'
let type_slang.kinds = [
\ 'n:namespaces',
\ 'f:functions'
\ ]
let s:known_types.slang = type_slang
2011-02-14 18:47:18 +08:00
" SML {{{3
let type_sml = {}
let type_sml.ctagstype = 'sml'
let type_sml.kinds = [
\ 'e:exception declarations',
\ 'f:function definitions',
\ 'c:functor definitions',
\ 's:signature declarations',
\ 'r:structure declarations',
\ 't:type definitions',
\ 'v:value bindings'
\ ]
let s:known_types.sml = type_sml
2011-02-14 18:47:18 +08:00
" 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 = {}
let type_sql.ctagstype = 'sql'
let type_sql.kinds = [
\ 'c:cursors',
\ 'f:functions',
\ 'F:record fields',
\ 'L:block label',
\ 'P:packages',
\ 'p:procedures',
\ 's:subtypes',
\ 't:tables',
\ 'T:triggers',
\ 'v:variables',
\ 'i:indexes',
\ 'e:events',
\ 'U:publications',
\ 'R:services',
\ 'D:domains',
\ 'V:views',
\ 'n:synonyms',
\ 'x:MobiLink Table Scripts',
\ 'y:MobiLink Conn Scripts'
\ ]
let s:known_types.sql = type_sql
2011-02-14 18:47:18 +08:00
" Tcl {{{3
let type_tcl = {}
let type_tcl.ctagstype = 'tcl'
let type_tcl.kinds = [
\ 'c:classes',
\ 'm:methods',
\ 'p:procedures'
\ ]
let s:known_types.tcl = type_tcl
2011-02-14 18:47:18 +08:00
" LaTeX {{{3
let type_tex = {}
let type_tex.ctagstype = 'tex'
let type_tex.kinds = [
\ 'p:parts',
\ 'c:chapters',
\ 's:sections',
\ 'u:subsections',
\ 'b:subsubsections',
\ 'P:paragraphs',
\ 'G:subparagraphs',
\ ]
let s:known_types.tex = type_tex
2011-02-14 18:47:18 +08:00
" Vera {{{3
" Why are variables 'virtual'?
let type_vera = {}
let type_vera.ctagstype = 'vera'
let type_vera.scopes = ['enum', 'class', 'virtual']
let type_vera.sro = '.' " Nesting doesn't seem to be possible
let type_vera.kinds = [
\ 'd:macros',
\ 'g:enums',
\ 'T:typedefs',
\ 'c:classes',
\ 'e:enumerators',
\ 'm:members',
\ 'f:functions',
\ 't:tasks',
\ 'v:variables',
\ 'p:programs'
\ ]
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
2011-02-14 18:47:18 +08:00
" Verilog {{{3
let type_verilog = {}
let type_verilog.ctagstype = 'verilog'
let type_verilog.kinds = [
\ 'c:constants',
\ 'e:events',
\ 'f:functions',
\ 'm:modules',
\ 'n:net data types',
\ 'p:ports',
\ 'r:register data types',
\ 't:tasks'
\ ]
let s:known_types.verilog = type_verilog
2011-02-14 18:47:18 +08:00
" VHDL {{{3
" The VHDL ctags parser unfortunately doesn't generate proper scopes
let type_vhdl = {}
let type_vhdl.ctagstype = 'vhdl'
let type_vhdl.kinds = [
\ 'c:constants',
\ 't:types',
\ 'T:subtypes',
\ 'r:records',
\ 'e:entities',
\ 'f:functions',
\ 'p:procedures',
\ 'P:packages'
\ ]
let s:known_types.vhdl = type_vhdl
2011-02-14 18:47:18 +08:00
" Vim {{{3
let type_vim = {}
let type_vim.ctagstype = 'vim'
let type_vim.kinds = [
\ 'v:variables',
\ 'f:functions',
\ 'a:autocommand groups',
\ 'c:commands',
\ 'm:maps'
\ ]
let s:known_types.vim = type_vim
2011-02-14 18:47:18 +08:00
" YACC {{{3
let type_yacc = {}
let type_yacc.ctagstype = 'yacc'
let type_yacc.kinds = [
\ 'l:labels'
\ ]
let s:known_types.yacc = type_yacc
2011-02-14 18:47:18 +08:00
" }}}3
2011-01-14 18:12:26 +08:00
2011-02-07 14:19:55 +08:00
let user_defs = s:GetUserTypeDefs()
for [key, value] in items(user_defs)
2011-02-14 17:02:44 +08:00
if !has_key(s:known_types, key) ||
\ (has_key(value, 'replace') && value.replace)
2011-02-07 14:19:55 +08:00
let s:known_types[key] = value
else
call extend(s:known_types[key], value)
endif
endfor
" Create a dictionary of the kind order for fast
" access in sorting functions
for type in values(s:known_types)
let i = 0
let type.kinddict = {}
for kind in type.kinds
let type.kinddict[kind[0]] = i
let i += 1
endfor
endfor
2011-01-12 17:48:04 +08:00
2011-02-13 14:20:15 +08:00
let s:access_symbols = {
\ 'public' : '+',
\ 'protected' : '#',
\ 'private' : '-'
\ }
2011-01-23 13:10:50 +08:00
let s:type_init_done = 1
endfunction
2011-01-11 17:36:49 +08:00
2011-02-14 18:47:18 +08:00
" s:GetUserTypeDefs() {{{2
2011-02-07 14:19:55 +08:00
function! s:GetUserTypeDefs()
redir => defs
silent! execute 'let g:'
redir END
let deflist = split(defs, '\n')
call map(deflist, 'substitute(v:val, ''^\S\+\zs.*'', "", "")')
call filter(deflist, 'v:val =~ "^tagbar_type_"')
let defdict = {}
for defstr in deflist
let type = substitute(defstr, '^tagbar_type_', '', '')
execute 'let defdict["' . type . '"] = g:' . defstr
endfor
" If the user only specified one of kind2scope and scope2kind use it to
" generate the other one
for def in values(defdict)
if has_key(def, 'kind2scope') && !has_key(def, 'scope2kind')
let def.scope2kind = {}
for [key, value] in items(def.kind2scope)
let def.scope2kind[value] = key
endfor
elseif has_key(def, 'scope2kind') && !has_key(def, 'kind2scope')
let def.kind2scope = {}
for [key, value] in items(def.scope2kind)
let def.kind2scope[value] = key
endfor
endif
2011-02-07 14:19:55 +08:00
endfor
return defdict
endfunction
" s:MapKeys() {{{2
function! s:MapKeys()
nnoremap <script> <silent> <buffer> <CR> :call <SID>JumpToTag()<CR>
nnoremap <script> <silent> <buffer> <2-LeftMouse>
\ :call <SID>JumpToTag()<CR>
nnoremap <script> <silent> <buffer> <Space> :call <SID>ShowPrototype()<CR>
nnoremap <script> <silent> <buffer> + :silent! foldopen<CR>
nnoremap <script> <silent> <buffer> <kPlus> :silent! foldopen<CR>
nnoremap <script> <silent> <buffer> - :silent! foldclose<CR>
nnoremap <script> <silent> <buffer> <kMinus> :silent! foldclose<CR>
nnoremap <script> <silent> <buffer> * :silent! %foldopen!<CR>
nnoremap <script> <silent> <buffer> <kMultiply> :silent! %foldopen!<CR>
nnoremap <script> <silent> <buffer> = :silent! %foldclose!<CR>
nnoremap <script> <silent> <buffer> s :call <SID>ToggleSort()<CR>
nnoremap <script> <silent> <buffer> x :call <SID>ZoomWindow()<CR>
nnoremap <script> <silent> <buffer> q :close<CR>
nnoremap <script> <silent> <buffer> <F1> :call <SID>ToggleHelp()<CR>
let s:key_mapping_done = 1
endfunction
" s:CreateAutocommands() {{{2
function! s:CreateAutocommands()
augroup TagbarAutoCmds
autocmd!
autocmd BufEnter __Tagbar__ nested call s:QuitIfOnlyWindow()
autocmd BufUnload __Tagbar__ call s:CleanUp()
autocmd CursorHold __Tagbar__ call s:ShowPrototype()
autocmd BufEnter,CursorHold * silent call
\ s:AutoUpdate(fnamemodify(bufname('%'), ':p'))
augroup END
let s:autocommands_done = 1
endfunction
2011-02-14 18:47:18 +08:00
" Window management {{{1
" s:ToggleWindow() {{{2
2011-01-11 17:36:49 +08:00
function! s:ToggleWindow()
let tagbarwinnr = bufwinnr("__Tagbar__")
if tagbarwinnr != -1
call s:CloseWindow()
return
endif
call s:OpenWindow(0)
2011-01-11 17:36:49 +08:00
endfunction
2011-02-14 18:47:18 +08:00
" s:OpenWindow() {{{2
function! s:OpenWindow(autoclose)
if !s:type_init_done
call s:InitTypes()
endif
2011-01-11 17:36:49 +08:00
" If the tagbar window is already open jump to it
let tagbarwinnr = bufwinnr('__Tagbar__')
2011-03-04 05:54:19 +08:00
if tagbarwinnr != -1
if winnr() != tagbarwinnr
execute tagbarwinnr . 'wincmd w'
endif
return
endif
" Expand the Vim window to accomodate for the Tagbar window if requested
if g:tagbar_expand && !s:window_expanded && has('gui_running')
let &columns += g:tagbar_width + 1
let s:window_expanded = 1
endif
2011-01-11 17:36:49 +08:00
let openpos = g:tagbar_left ? 'topleft vertical ' : 'botright vertical '
2011-01-20 17:18:33 +08:00
exe 'silent! keepalt ' . openpos . g:tagbar_width . 'split ' . '__Tagbar__'
2011-01-11 17:36:49 +08:00
setlocal noreadonly " in case the "view" mode is used
setlocal buftype=nofile
setlocal bufhidden=hide
2011-01-11 17:36:49 +08:00
setlocal noswapfile
setlocal nobuflisted
setlocal nomodifiable
setlocal filetype=tagbar
setlocal nolist
setlocal nonumber
setlocal nowrap
setlocal winfixwidth
2011-02-05 14:32:49 +08:00
setlocal textwidth=0
2011-01-11 17:36:49 +08:00
2011-01-18 10:03:27 +08:00
if exists('+relativenumber')
setlocal norelativenumber
endif
2011-01-11 17:36:49 +08:00
setlocal foldenable
setlocal foldminlines=0
2011-02-14 15:48:24 +08:00
setlocal foldmethod=expr
setlocal foldexpr=s:GetFoldLevel(v:lnum)
2011-01-11 17:36:49 +08:00
setlocal foldlevel=9999
setlocal foldcolumn=1
2011-02-14 15:48:24 +08:00
setlocal foldtext=getline(v:foldstart)
2011-01-11 17:36:49 +08:00
setlocal statusline=%!TagbarGenerateStatusline()
2011-01-20 13:16:57 +08:00
" Variable for saving the current file for functions that are called from
" the tagbar window
let s:current_file = ''
2011-01-21 19:27:50 +08:00
" Script-local variable needed since compare functions can't
" take extra arguments
let s:compare_typeinfo = {}
2011-01-20 16:20:29 +08:00
let s:is_maximized = 0
2011-01-21 19:27:50 +08:00
let s:short_help = 1
2011-01-20 13:16:57 +08:00
let w:autoclose = a:autoclose
if has('balloon_eval')
setlocal balloonexpr=TagbarBalloonExpr()
set ballooneval
endif
2011-01-11 17:36:49 +08:00
let cpoptions_save = &cpoptions
set cpoptions&vim
if !s:key_mapping_done
call s:MapKeys()
endif
2011-01-11 17:36:49 +08:00
if !s:autocommands_done
call s:CreateAutocommands()
endif
2011-01-11 17:36:49 +08:00
let &cpoptions = cpoptions_save
execute 'wincmd p'
" Jump back to the tagbar window if autoclose or autofocus is set. Can't
" just stay in it since it wouldn't trigger the update event
if g:tagbar_autoclose || a:autoclose || g:tagbar_autofocus
let tagbarwinnr = bufwinnr('__Tagbar__')
execute tagbarwinnr . 'wincmd w'
endif
2011-01-11 17:36:49 +08:00
endfunction
2011-02-14 18:47:18 +08:00
" s:CloseWindow() {{{2
2011-01-11 17:36:49 +08:00
function! s:CloseWindow()
let tagbarwinnr = bufwinnr('__Tagbar__')
if tagbarwinnr == -1
return
endif
let tagbarbufnr = winbufnr(tagbarwinnr)
2011-01-11 17:36:49 +08:00
if winnr() == tagbarwinnr
if winbufnr(2) != -1
" Other windows are open, only close the tagbar one
close
endif
else
" Go to the tagbar window, close it and then come back to the
" original window
let curbufnr = bufnr('%')
2011-01-20 11:47:29 +08:00
execute tagbarwinnr . 'wincmd w'
2011-01-11 17:36:49 +08:00
close
" Need to jump back to the original window only if we are not
" already in that window
let winnum = bufwinnr(curbufnr)
if winnr() != winnum
exe winnum . 'wincmd w'
endif
endif
" If the Vim window has been expanded, and Tagbar is not open in any other
" tabpages, shrink the window again
if s:window_expanded
let tablist = []
for i in range(tabpagenr('$'))
call extend(tablist, tabpagebuflist(i + 1))
endfor
if index(tablist, tagbarbufnr) == -1
let &columns -= g:tagbar_width + 1
let s:window_expanded = 0
endif
endif
2011-01-11 17:36:49 +08:00
endfunction
2011-02-14 18:47:18 +08:00
" s:ZoomWindow() {{{2
2011-01-20 16:20:29 +08:00
function! s:ZoomWindow()
if s:is_maximized
execute 'vert resize ' . g:tagbar_width
let s:is_maximized = 0
else
vert resize
let s:is_maximized = 1
endif
endfunction
2011-02-14 18:47:18 +08:00
" Tag processing {{{1
" s:ProcessFile() {{{2
2011-01-11 17:36:49 +08:00
function! s:ProcessFile(fname, ftype)
2011-01-16 17:02:52 +08:00
if !s:IsValidFile(a:fname, a:ftype)
return
endif
let typeinfo = s:known_types[a:ftype]
2011-01-19 13:44:08 +08:00
let ctags_args = ' -f - '
let ctags_args .= ' --format=2 '
let ctags_args .= ' --excmd=pattern '
let ctags_args .= ' --fields=nksSaz '
let ctags_args .= ' --extra= '
let ctags_args .= ' --sort=yes '
2011-01-12 17:48:04 +08:00
" Include extra type definitions
if has_key(typeinfo, 'deffile')
let ctags_args .= ' --options=' . typeinfo.deffile . ' '
endif
let ctags_type = typeinfo.ctagstype
2011-01-17 18:40:50 +08:00
2011-01-12 17:48:04 +08:00
let ctags_kinds = ""
for kind in typeinfo.kinds
2011-01-12 17:48:04 +08:00
let [short, full] = split(kind, ':')
let ctags_kinds .= short
endfor
let ctags_args .= ' --language-force=' . ctags_type .
\ ' --' . ctags_type . '-kinds=' . ctags_kinds . ' '
2011-01-11 17:36:49 +08:00
let ctags_cmd = g:tagbar_ctags_bin . ctags_args . shellescape(a:fname)
2011-01-11 17:36:49 +08:00
let ctags_output = system(ctags_cmd)
if v:shell_error
let msg = 'Tagbar: Could not generate tags for ' . a:fname
2011-02-09 13:17:22 +08:00
echohl WarningMsg | echomsg msg | echohl None
2011-01-11 17:36:49 +08:00
if !empty(ctags_output)
2011-02-09 13:17:22 +08:00
echohl WarningMsg | echomsg ctags_output | echohl None
2011-01-11 17:36:49 +08:00
endif
return
endif
let fileinfo = {}
let fileinfo.mtime = getftime(a:fname)
let rawtaglist = split(ctags_output, '\n\+')
2011-01-11 17:36:49 +08:00
2011-01-20 13:16:57 +08:00
let fileinfo.ftype = a:ftype
let fileinfo.tags = []
let fileinfo.fline = {}
let fileinfo.tline = {}
2011-01-11 17:36:49 +08:00
for line in rawtaglist
2011-01-18 10:03:27 +08:00
let parts = split(line, ';"')
if len(parts) == 2 " Is a valid tag line
let taginfo = s:ParseTagline(parts[0], parts[1], typeinfo)
let fileinfo.fline[taginfo.fields.line] = taginfo
2011-01-18 10:03:27 +08:00
call add(fileinfo.tags, taginfo)
endif
2011-01-11 17:36:49 +08:00
endfor
if has_key(typeinfo, 'scopes') && !empty(typeinfo.scopes)
let scopedtags = []
2011-02-28 13:08:47 +08:00
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 . ')')
let processedtags = []
2011-02-13 14:20:15 +08:00
call s:AddScopedTags(scopedtags, processedtags, '', '', 0, typeinfo)
2011-01-17 17:58:31 +08:00
2011-02-13 14:20:15 +08:00
if !empty(scopedtags)
2011-02-14 17:02:44 +08:00
echoerr 'Tagbar: ''scopedtags'' not empty after processing,'
\ 'this should never happen!'
\ 'Please contact the script maintainer with an example.'
2011-02-13 14:20:15 +08:00
endif
2011-01-21 15:11:15 +08:00
call extend(fileinfo.tags, processedtags)
endif
2011-01-19 12:36:10 +08:00
let s:compare_typeinfo = typeinfo
2011-02-14 12:37:34 +08:00
if has_key(typeinfo, 'sort')
if typeinfo.sort
call s:SortTags(fileinfo.tags, 's:CompareByKind')
else
call s:SortTags(fileinfo.tags, 's:CompareByLine')
endif
elseif g:tagbar_sort
2011-01-20 12:11:27 +08:00
call s:SortTags(fileinfo.tags, 's:CompareByKind')
else
call s:SortTags(fileinfo.tags, 's:CompareByLine')
endif
2011-01-11 17:36:49 +08:00
let s:known_files[a:fname] = fileinfo
endfunction
2011-02-14 18:47:18 +08:00
" s:ParseTagline() {{{2
2011-01-21 15:11:15 +08:00
" Structure of a tag line:
" tagname<TAB>filename<TAB>expattern;"fields
2011-01-14 18:12:26 +08:00
" fields: <TAB>name:value
" fields that are always present: kind, line
function! s:ParseTagline(part1, part2, typeinfo)
2011-01-11 17:36:49 +08:00
let taginfo = {}
2011-01-18 10:03:27 +08:00
let basic_info = split(a:part1, '\t')
2011-01-12 17:48:04 +08:00
let taginfo.name = basic_info[0]
let taginfo.file = basic_info[1]
2011-01-24 15:14:28 +08:00
" 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\^' . pattern . dollar
let prototype = substitute(pattern, '^[[:space:]]\+', '', '')
let prototype = substitute(prototype, '[[:space:]]\+$', '', '')
let taginfo.prototype = prototype
2011-01-11 17:36:49 +08:00
2011-01-14 18:12:26 +08:00
let taginfo.fields = {}
2011-01-18 10:03:27 +08:00
let fields = split(a:part2, '\t')
2011-01-11 17:36:49 +08:00
for field in fields
2011-01-12 17:48:04 +08:00
" can't use split() since the value can contain ':'
2011-01-14 18:12:26 +08:00
let delimit = stridx(field, ':')
let key = strpart(field, 0, delimit)
let val = strpart(field, delimit + 1)
let taginfo.fields[key] = val
2011-01-11 17:36:49 +08:00
endfor
" Make some information easier accessible
let taginfo.path = ''
let taginfo.fullpath = taginfo.name
2011-01-25 14:32:30 +08:00
if has_key(a:typeinfo, 'scopes')
for scope in a:typeinfo.scopes
if has_key(taginfo.fields, scope)
let taginfo.scope = scope
let taginfo.path = taginfo.fields[scope]
2011-01-25 14:32:30 +08:00
let taginfo.fullpath = taginfo.path . a:typeinfo.sro .
\ taginfo.name
break
endif
endfor
2011-02-07 12:58:33 +08:00
let taginfo.depth = len(split(taginfo.path, '\V' . a:typeinfo.sro))
2011-01-25 14:32:30 +08:00
endif
2011-01-11 17:36:49 +08:00
return taginfo
endfunction
2011-02-14 18:47:18 +08:00
" s:AddScopedTags() {{{2
2011-02-13 14:20:15 +08:00
" 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, curpath, pscope, depth, typeinfo)
let is_cur_tag = 'v:val.depth == a:depth'
if !empty(a: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 == a:curpath ||
\ match(v:val.path, ''\V\^\C'' . a:curpath . a:typeinfo.sro) == 0) &&
\ (v:val.path == a:curpath ? (v:val.scope == a:pscope) : 1)'
2011-01-21 15:11:15 +08:00
endif
2011-02-13 14:20:15 +08:00
let curtags = filter(copy(a:tags), is_cur_tag)
2011-01-21 15:11:15 +08:00
2011-02-13 14:20:15 +08:00
if !empty(curtags)
call filter(a:tags, '!(' . is_cur_tag . ')')
2011-02-13 14:20:15 +08:00
let realtags = []
let pseudotags = []
2011-01-19 12:19:15 +08:00
2011-02-13 14:20:15 +08:00
while !empty(curtags)
let tag = remove(curtags, 0)
if tag.path != a: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:curpath,
\ a:pscope, a:typeinfo)
call add(pseudotags, pseudotag)
2011-01-17 17:58:31 +08:00
else
2011-02-13 14:20:15 +08:00
call add(realtags, tag)
2011-01-17 17:58:31 +08:00
endif
2011-02-13 14:20:15 +08:00
endwhile
2011-01-17 17:58:31 +08:00
2011-02-13 14:20:15 +08:00
" Recursively add the children of the tags on the current level
for tag in realtags
if !has_key(a:typeinfo.kind2scope, tag.fields.kind)
2011-01-19 12:19:15 +08:00
continue
endif
2011-02-13 14:20:15 +08:00
if !has_key(tag, 'children')
let tag.children = []
2011-01-19 12:19:15 +08:00
endif
2011-02-13 14:20:15 +08:00
2011-01-21 15:11:15 +08:00
let parentscope = a:typeinfo.kind2scope[tag.fields.kind]
2011-02-13 14:20:15 +08:00
call s:AddScopedTags(a:tags, tag.children, tag.fullpath,
\ parentscope, a:depth + 1, a:typeinfo)
2011-01-17 17:58:31 +08:00
endfor
2011-02-13 14:20:15 +08:00
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)
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(a:curpath)
let is_grandchild .=
\ ' && match(v:val.path, ''\V\^\C'' . a:curpath . a:typeinfo.sro) == 0'
2011-01-19 12:19:15 +08:00
endif
2011-01-17 17:58:31 +08:00
2011-01-21 15:11:15 +08:00
let grandchildren = filter(copy(a:tags), is_grandchild)
if !empty(grandchildren)
2011-02-13 14:20:15 +08:00
call s:AddScopedTags(a:tags, a:processedtags, a:curpath,
\ a:pscope, a:depth + 1, a:typeinfo)
2011-01-21 15:11:15 +08:00
endif
endfunction
2011-02-14 18:47:18 +08:00
" s:ProcessPseudoTag() {{{2
2011-02-13 14:20:15 +08:00
function! s:ProcessPseudoTag(curtags, tag, curpath, pscope, typeinfo)
let pseudoname = substitute(a:tag.path, a:curpath, '', '')
let pseudoname = substitute(pseudoname, '\V\^' . a:typeinfo.sro, '', '')
let pseudotag = s:CreatePseudoTag(pseudoname, a:curpath, a:pscope,
\ a:tag.scope, a:typeinfo)
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)
2011-01-22 17:16:49 +08:00
endif
2011-02-13 14:20:15 +08:00
return pseudotag
2011-01-22 17:16:49 +08:00
endfunction
2011-02-14 18:47:18 +08:00
" s:ProcessPseudoChildren() {{{2
2011-02-13 14:20:15 +08:00
function! s:ProcessPseudoChildren(tags, tag, depth, typeinfo)
for childtag in a:tag.children
if !has_key(a:typeinfo.kind2scope, childtag.fields.kind)
continue
endif
2011-01-22 17:16:49 +08:00
2011-02-13 14:20:15 +08:00
if !has_key(childtag, 'children')
let childtag.children = []
2011-01-22 17:16:49 +08:00
endif
2011-02-13 14:20:15 +08:00
let parentscope = a:typeinfo.kind2scope[childtag.fields.kind]
call s:AddScopedTags(a:tags, childtag.children, childtag.fullpath,
\ parentscope, a:depth + 1, a:typeinfo)
endfor
let is_grandchild = 'v:val.depth > a:depth &&
\ match(v:val.path, ''^\C'' . a:tag.fullpath) == 0'
let grandchildren = filter(copy(a:tags), is_grandchild)
if !empty(grandchildren)
let parentscope = a:typeinfo.kind2scope[a:tag.fields.kind]
call s:AddScopedTags(a:tags, a:tag.children, a:tag.fullpath,
\ parentscope, a:depth + 1, a:typeinfo)
endif
2011-01-22 17:16:49 +08:00
endfunction
2011-02-14 18:47:18 +08:00
" s:CreatePseudoTag() {{{2
2011-01-22 17:16:49 +08:00
function! s:CreatePseudoTag(name, curpath, pscope, scope, typeinfo)
let pseudotag = {}
let pseudotag.name = a:name
let pseudotag.fields = {}
let pseudotag.fields.kind = a:typeinfo.scope2kind[a:scope]
let pseudotag.fields.line = 0
let parentscope = substitute(a:curpath, a:name . '$', '', '')
let parentscope = substitute(parentscope,
\ '\V\^' . a:typeinfo.sro . '\$', '', '')
let pseudotag.path = ''
let pseudotag.fullpath = pseudotag.name
2011-01-22 17:16:49 +08:00
if a:pscope != ''
let pseudotag.fields[a:pscope] = parentscope
let pseudotag.scope = a:pscope
let pseudotag.path = parentscope
let pseudotag.fullpath =
\ pseudotag.path . a:typeinfo.sro . pseudotag.name
2011-01-22 17:16:49 +08:00
endif
2011-02-06 16:22:56 +08:00
let pseudotag.depth = len(split(pseudotag.path, '\V' . a:typeinfo.sro))
2011-01-22 17:16:49 +08:00
return pseudotag
2011-01-17 17:58:31 +08:00
endfunction
2011-02-14 18:47:18 +08:00
" Sorting {{{1
" s:SortTags() {{{2
2011-01-20 13:16:57 +08:00
function! s:SortTags(tags, comparemethod)
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
2011-02-14 18:47:18 +08:00
" s:CompareByKind() {{{2
function! s:CompareByKind(tag1, tag2)
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
2011-02-14 18:47:18 +08:00
" s:CompareByLine() {{{2
function! s:CompareByLine(tag1, tag2)
return a:tag1.fields.line - a:tag2.fields.line
endfunction
2011-02-14 18:47:18 +08:00
" s:ToggleSort() {{{2
function! s:ToggleSort()
if !has_key(s:known_files, s:current_file)
return
endif
let curline = line('.')
let fileinfo = s:known_files[s:current_file]
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
if has_key(s:compare_typeinfo, 'sort')
if s:compare_typeinfo.sort
call s:SortTags(fileinfo.tags, 's:CompareByKind')
else
call s:SortTags(fileinfo.tags, 's:CompareByLine')
endif
elseif g:tagbar_sort
call s:SortTags(fileinfo.tags, 's:CompareByKind')
else
call s:SortTags(fileinfo.tags, 's:CompareByLine')
endif
call s:RenderContent(s:current_file, fileinfo.ftype)
execute curline
endfunction
" Display {{{1
" s:RenderContent() {{{2
2011-01-11 17:36:49 +08:00
function! s:RenderContent(fname, ftype)
let tagbarwinnr = bufwinnr('__Tagbar__')
2011-01-20 13:16:57 +08:00
if &filetype == 'tagbar'
let in_tagbar = 1
else
let in_tagbar = 0
let prevwinnr = winnr()
2011-01-20 13:16:57 +08:00
execute tagbarwinnr . 'wincmd w'
endif
2011-01-11 17:36:49 +08:00
2011-01-12 17:48:04 +08:00
let lazyredraw_save = &lazyredraw
set lazyredraw
2011-01-11 17:36:49 +08:00
setlocal modifiable
silent! %delete _
2011-01-21 19:27:50 +08:00
call s:PrintHelp()
2011-02-17 12:33:59 +08:00
" If we don't have an entry for the file by now something must have gone
" wrong
2011-02-05 14:32:49 +08:00
if !has_key(s:known_files, a:fname)
silent! put ='There was an error processing the file. Please run ' .
\ 'ctags manually to determine what the problem is.'
normal! gqq
let s:current_file = ''
setlocal nomodifiable
let &lazyredraw = lazyredraw_save
if !in_tagbar
execute prevwinnr . 'wincmd w'
2011-02-05 14:32:49 +08:00
endif
return
endif
let fileinfo = s:known_files[a:fname]
2011-01-16 12:46:14 +08:00
2011-02-05 14:32:49 +08:00
let typeinfo = s:known_types[a:ftype]
" Print tags
2011-01-12 17:48:04 +08:00
for kind in typeinfo.kinds
2011-01-20 17:18:33 +08:00
let curtags = filter(copy(fileinfo.tags),
2011-02-05 14:30:03 +08:00
\ 'v:val.fields.kind ==# kind[0]')
2011-01-12 17:48:04 +08:00
if empty(curtags)
continue
endif
2011-01-25 14:32:30 +08:00
if has_key(typeinfo, 'kind2scope') &&
\ has_key(typeinfo.kind2scope, kind[0])
" Scoped tags
for tag in curtags
2011-01-19 12:19:15 +08:00
let taginfo = ''
if tag.fields.line == 0 " Tag is a pseudo-tag
let taginfo .= '*'
endif
if has_key(tag.fields, 'signature')
let taginfo .= tag.fields.signature
endif
let taginfo .= ' : ' . typeinfo.kind2scope[kind[0]]
2011-01-23 13:10:50 +08:00
let prefix = s:GetPrefix(tag)
2011-02-14 11:11:59 +08:00
if g:tagbar_compact && line('.') == 1
silent! 0put =prefix . tag.name . taginfo
else
silent! put =prefix . tag.name . taginfo
endif
2011-01-12 17:48:04 +08:00
" Save the current tagbar line in the tag for easy
" highlighting access
let curline = line('.')
let tag.tline = curline
let fileinfo.tline[curline] = tag
2011-01-21 15:11:15 +08:00
if has_key(tag, 'children')
for childtag in tag.children
call s:PrintTag(childtag, 1, fileinfo, typeinfo)
endfor
endif
2011-01-12 17:48:04 +08:00
2011-02-14 11:11:59 +08:00
if !g:tagbar_compact
silent! put _
endif
endfor
else
" Non-scoped tags
2011-02-14 11:11:59 +08:00
if g:tagbar_compact && line('.') == 1
silent! 0put =' ' . strpart(kind, 2)
else
silent! put =' ' . strpart(kind, 2)
endif
for tag in curtags
let taginfo = ''
if has_key(tag.fields, 'signature')
let taginfo .= tag.fields.signature
endif
2011-01-23 13:10:50 +08:00
let prefix = s:GetPrefix(tag)
silent! put =' ' . prefix . tag.name . taginfo
" Save the current tagbar line in the tag for easy
" highlighting access
let curline = line('.')
let tag.tline = curline
let fileinfo.tline[curline] = tag
endfor
2011-02-14 11:11:59 +08:00
if !g:tagbar_compact
silent! put _
endif
endif
2011-01-11 17:36:49 +08:00
endfor
setlocal nomodifiable
" Go to top of window so jumps to the Tagbar window before any
" highlighting is done won't end up at the bottom
execute 1
2011-01-12 17:48:04 +08:00
let &lazyredraw = lazyredraw_save
2011-01-20 13:16:57 +08:00
if !in_tagbar
execute prevwinnr . 'wincmd w'
2011-01-20 13:16:57 +08:00
endif
2011-01-11 17:36:49 +08:00
endfunction
2011-02-14 18:47:18 +08:00
" s:PrintTag() {{{2
function! s:PrintTag(tag, depth, fileinfo, typeinfo)
2011-01-16 12:46:14 +08:00
let taginfo = ''
2011-01-19 12:19:15 +08:00
if a:tag.fields.line == 0 " Tag is a pseudo-tag
let taginfo .= '*'
endif
2011-01-16 12:46:14 +08:00
if has_key(a:tag.fields, 'signature')
let taginfo .= a:tag.fields.signature
endif
if has_key(a:typeinfo.kind2scope, a:tag.fields.kind)
let taginfo .= ' : ' . a:typeinfo.kind2scope[a:tag.fields.kind]
endif
2011-01-23 13:10:50 +08:00
let prefix = s:GetPrefix(a:tag)
2011-01-16 12:46:14 +08:00
" Print tag indented according to depth
2011-01-23 13:10:50 +08:00
silent! put =repeat(' ', a:depth * 2) . prefix . a:tag.name . taginfo
2011-01-16 12:46:14 +08:00
" 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
2011-01-16 12:46:14 +08:00
" Recursively print children
if has_key(a:tag, 'children')
for childtag in a:tag.children
call s:PrintTag(childtag, a:depth + 1, a:fileinfo, a:typeinfo)
2011-01-16 12:46:14 +08:00
endfor
endif
endfunction
2011-02-14 18:47:18 +08:00
" s:GetPrefix() {{{2
2011-01-23 13:10:50 +08:00
function! s:GetPrefix(tag)
if has_key(a:tag.fields, 'access') &&
\ has_key(s:access_symbols, a:tag.fields.access)
let prefix = s:access_symbols[a:tag.fields.access]
else
let prefix = ' '
endif
return prefix
endfunction
2011-02-14 18:47:18 +08:00
" s:PrintHelp() {{{2
function! s:PrintHelp()
if !g:tagbar_compact && s:short_help
call append(0, '" Press <F1> for help')
elseif !s:short_help
call append(0, '" <Enter> : Jump to tag definition')
call append(1, '" <Space> : Display tag prototype')
call append(2, '" + : Open fold')
call append(3, '" - : Close fold')
call append(4, '" * : Open all folds')
call append(5, '" = : Close all folds')
call append(6, '" s : Toggle sort')
call append(7, '" x : Zoom window in/out')
call append(8, '" q : Close window')
call append(9, '" <F1> : Remove help')
endif
endfunction
" s:ToggleHelp() {{{2
function! s:ToggleHelp()
let s:short_help = !s:short_help
" Prevent highlighting from being off after adding/removing the help text
match none
if s:current_file == ''
call s:RenderContent(s:current_file, '')
else
let fileinfo = s:known_files[s:current_file]
call s:RenderContent(s:current_file, fileinfo.ftype)
endif
execute 1
redraw
2011-02-14 18:47:18 +08:00
endfunction
" User actions {{{1
" s:HighlightTag() {{{2
function! s:HighlightTag(fname)
let fileinfo = s:known_files[a:fname]
let curline = line('.')
let tagline = 0
2011-01-21 17:29:48 +08:00
" 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)
2011-01-21 16:16:46 +08:00
if has_key(fileinfo.fline, line) &&
\ has_key(fileinfo.fline[line], 'tline')
let tagline = fileinfo.fline[line].tline
break
endif
endfor
let eventignore_save = &eventignore
set eventignore=all
let tagbarwinnr = bufwinnr('__Tagbar__')
let prevwinnr = winnr()
execute tagbarwinnr . 'wincmd w'
match none
" No tag above cursor position so don't do anything
if tagline == 0
if line('$') < winheight(0) - 1
execute 1
call winline()
endif
execute prevwinnr . 'wincmd w'
let &eventignore = eventignore_save
redraw
return
endif
" If the Tagbar contents are shorter than the window height make sure
" that the whole content is shown by jumping to the top of the window
if line('$') < winheight(0) - 1
execute 1
call winline()
endif
" Go to the line containing the tag
execute tagline
if foldclosed('.') != -1
.foldopen!
endif
" Make sure the tag is visible in the window
call winline()
2011-01-24 16:27:35 +08:00
let pattern = '/^\%' . tagline . 'l\s*[-+#]\?\zs[^( ]\+\ze/'
execute 'match Search ' . pattern
execute prevwinnr . 'wincmd w'
let &eventignore = eventignore_save
redraw
endfunction
2011-02-14 18:47:18 +08:00
" s:JumpToTag() {{{2
function! s:JumpToTag()
let taginfo = s:GetTagInfo(line('.'))
let autoclose = w:autoclose
if empty(taginfo)
return
endif
execute 'wincmd p'
" 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
" Center the tag in the window
normal! z.
if foldclosed('.') != -1
.foldopen!
endif
redraw
if g:tagbar_autoclose || autoclose
call s:CloseWindow()
else
call s:HighlightTag(s:current_file)
endif
endfunction
2011-02-14 18:47:18 +08:00
" s:ShowPrototype() {{{2
function! s:ShowPrototype()
let taginfo = s:GetTagInfo(line('.'))
if empty(taginfo)
return
endif
echo taginfo.prototype
endfunction
2011-02-14 18:47:18 +08:00
" Helper functions {{{1
" s:CleanUp() {{{2
function! s:CleanUp()
silent! autocmd! TagbarAutoCmds
2011-02-14 18:47:18 +08:00
unlet s:current_file
unlet s:is_maximized
unlet s:compare_typeinfo
unlet s:short_help
endfunction
" s:QuitIfOnlyWindow() {{{2
function! s:QuitIfOnlyWindow()
" Before quitting Vim, delete the tagbar buffer so that
" the '0 mark is correctly set to the previous buffer.
if winbufnr(2) == -1
" Check if there is more than one tab page
if tabpagenr('$') == 1
bdelete
quit
else
close
endif
endif
endfunction
" s:AutoUpdate() {{{2
function! s:AutoUpdate(fname)
2011-02-17 12:33:59 +08:00
" Don't do anything if tagbar is not open or if we're in the tagbar window
2011-02-14 18:47:18 +08:00
let tagbarwinnr = bufwinnr('__Tagbar__')
if tagbarwinnr == -1 || &filetype == 'tagbar'
return
endif
2011-02-17 12:33:59 +08:00
" Don't do anything if the file isn't supported
if !s:IsValidFile(a:fname, &filetype)
2011-02-14 18:47:18 +08:00
return
endif
2011-02-17 12:33:59 +08:00
" Process the file if it's unknown or the information is outdated
2011-02-14 18:47:18 +08:00
if has_key(s:known_files, a:fname)
if s:known_files[a:fname].mtime != getftime(a:fname)
call s:ProcessFile(a:fname, &filetype)
endif
else
call s:ProcessFile(a:fname, &filetype)
endif
2011-02-17 12:33:59 +08:00
" Display the tagbar content
call s:RenderContent(a:fname, &filetype)
2011-02-14 18:47:18 +08:00
2011-02-17 12:33:59 +08:00
" If we don't have an entry for the file by now something must have gone
" wrong
if !has_key(s:known_files, a:fname)
return
2011-02-14 18:47:18 +08:00
endif
2011-02-17 12:33:59 +08:00
let s:current_file = a:fname
call s:HighlightTag(a:fname)
2011-02-14 18:47:18 +08:00
endfunction
" s:IsValidFile() {{{2
function! s:IsValidFile(fname, ftype)
if a:fname == '' || a:ftype == ''
return 0
endif
if !filereadable(a:fname)
return 0
endif
if !has_key(s:known_types, a:ftype)
return 0
endif
return 1
endfunction
" s:GetFoldLevel() {{{2
2011-02-14 15:48:24 +08:00
function! s:GetFoldLevel(lnum)
2011-02-14 17:10:30 +08:00
let curline = getline(a:lnum)
2011-02-14 18:47:18 +08:00
" Don't fold comments
if curline[0] == '"'
2011-02-14 17:10:30 +08:00
return 0
endif
let nextline = getline(a:lnum + 1)
let curindent = len(matchstr(curline[1:], '^[ ]*[-+#]\?')) / 2
let nextindent = len(matchstr(nextline[1:], '^[ ]*[-+#]\?')) / 2
2011-02-14 15:48:24 +08:00
if curindent < nextindent
return '>' . (curindent + 1)
else
return curindent
endif
endfunction
2011-02-14 18:47:18 +08:00
" 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)
if !has_key(s:known_files, s:current_file)
return {}
endif
" Don't do anything in empty and comment lines
let curline = getline(a:linenr)
if curline =~ '^\s*$' || curline[0] == '"'
return {}
endif
let fileinfo = s:known_files[s:current_file]
" 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 taginfo.fields.line == 0
return {}
endif
return taginfo
endfunction
2011-02-14 18:47:18 +08:00
" TagbarBalloonExpr() {{{2
function! TagbarBalloonExpr()
let taginfo = s:GetTagInfo(v:beval_lnum)
2011-01-22 17:16:49 +08:00
2011-02-14 18:47:18 +08:00
if empty(taginfo)
return
2011-01-21 19:27:50 +08:00
endif
2011-02-14 18:47:18 +08:00
return taginfo.prototype
2011-01-21 19:27:50 +08:00
endfunction
2011-02-14 18:47:18 +08:00
" TagbarGenerateStatusline() {{{2
function! TagbarGenerateStatusline()
if g:tagbar_sort
let text = '[Name]'
else
let text = '[Order]'
endif
let filename = fnamemodify(s:current_file, ':t')
let text .= ' ' . filename
return text
endfunction
2011-01-24 17:37:52 +08:00
" Commands {{{1
command! -nargs=0 TagbarToggle call s:ToggleWindow()
command! -nargs=0 TagbarOpen call s:OpenWindow(0)
command! -nargs=0 TagbarOpenAutoClose call s:OpenWindow(1)
command! -nargs=0 TagbarClose call s:CloseWindow()
2011-01-11 17:36:49 +08:00
2011-01-24 17:37:52 +08:00
" Modeline {{{1
" vim: ts=8 sw=4 sts=4 et foldenable foldmethod=marker foldcolumn=1