aboutsummaryrefslogtreecommitdiff
path: root/files/.vim/plugin
diff options
context:
space:
mode:
authorAnton Bobov <bobov_a@sibsac.ru>2011-10-13 11:17:19 +0600
committerAnton Bobov <bobov_a@sibsac.ru>2011-10-13 11:17:19 +0600
commitda709cd9e90c3ab644bb92700f0ed40d965b80d3 (patch)
tree144e5470327f72b8489141cd7595b652c6c32616 /files/.vim/plugin
Initial commit.
Diffstat (limited to 'files/.vim/plugin')
-rwxr-xr-xfiles/.vim/plugin/NERD_commenter.vim3349
-rwxr-xr-xfiles/.vim/plugin/NERD_tree.vim3559
-rwxr-xr-xfiles/.vim/plugin/SyntaxFolds.vim323
-rwxr-xr-xfiles/.vim/plugin/filebrowser.vim251
-rwxr-xr-xfiles/.vim/plugin/imaps.vim831
-rwxr-xr-xfiles/.vim/plugin/libList.vim249
-rwxr-xr-xfiles/.vim/plugin/luarefvim.vim30
-rwxr-xr-xfiles/.vim/plugin/matchit.vim812
-rwxr-xr-xfiles/.vim/plugin/project.vim1293
-rwxr-xr-xfiles/.vim/plugin/rails.vim4666
-rwxr-xr-xfiles/.vim/plugin/remoteOpen.vim163
-rwxr-xr-xfiles/.vim/plugin/snipMate.vim190
-rwxr-xr-xfiles/.vim/plugin/sqlplus.vim257
-rwxr-xr-xfiles/.vim/plugin/taglist.vim4546
14 files changed, 20519 insertions, 0 deletions
diff --git a/files/.vim/plugin/NERD_commenter.vim b/files/.vim/plugin/NERD_commenter.vim
new file mode 100755
index 0000000..155f5ac
--- /dev/null
+++ b/files/.vim/plugin/NERD_commenter.vim
@@ -0,0 +1,3349 @@
+" ============================================================================
+" File: NERD_commenter.vim
+" Description: vim global plugin that provides easy code commenting
+" Maintainer: Martin Grenfell <martin_grenfell at msn dot com>
+" Version: 2.2.1
+" Last Change: 13th November, 2008
+" License: This program is free software. It comes without any warranty,
+" to the extent permitted by applicable law. You can redistribute
+" it and/or modify it under the terms of the Do What The Fuck You
+" Want To Public License, Version 2, as published by Sam Hocevar.
+" See http://sam.zoy.org/wtfpl/COPYING for more details.
+"
+" ============================================================================
+
+" Section: script init stuff {{{1
+if exists("loaded_nerd_comments")
+ finish
+endif
+if v:version < 700
+ echoerr "NERDCommenter: this plugin requires vim >= 7. DOWNLOAD IT! You'll thank me later!"
+ finish
+endif
+let loaded_nerd_comments = 1
+
+" Section: spaces init {{{2
+" Occasionally we need to grab a string of spaces so just make one here
+let s:spaces = ""
+while strlen(s:spaces) < 100
+ let s:spaces = s:spaces . " "
+endwhile
+
+" Function: s:InitVariable() function {{{2
+" This function is used to initialise a given variable to a given value. The
+" variable is only initialised if it does not exist prior
+"
+" Args:
+" -var: the name of the var to be initialised
+" -value: the value to initialise var to
+"
+" Returns:
+" 1 if the var is set, 0 otherwise
+function s:InitVariable(var, value)
+ if !exists(a:var)
+ exec 'let ' . a:var . ' = ' . "'" . a:value . "'"
+ return 1
+ endif
+ return 0
+endfunction
+
+" Section: space string init{{{2
+" When putting spaces after the left delim and before the right we use
+" s:spaceStr for the space char. This way we can make it add anything after
+" the left and before the right by modifying this variable
+let s:spaceStr = ' '
+let s:lenSpaceStr = strlen(s:spaceStr)
+
+" Section: variable init calls {{{2
+call s:InitVariable("g:NERDAllowAnyVisualDelims", 1)
+call s:InitVariable("g:NERDBlockComIgnoreEmpty", 0)
+call s:InitVariable("g:NERDCommentWholeLinesInVMode", 0)
+call s:InitVariable("g:NERDCompactSexyComs", 0)
+call s:InitVariable("g:NERDCreateDefaultMappings", 1)
+call s:InitVariable("g:NERDDefaultNesting", 1)
+call s:InitVariable("g:NERDMenuMode", 3)
+call s:InitVariable("g:NERDLPlace", "[>")
+call s:InitVariable("g:NERDUsePlaceHolders", 1)
+call s:InitVariable("g:NERDRemoveAltComs", 1)
+call s:InitVariable("g:NERDRemoveExtraSpaces", 1)
+call s:InitVariable("g:NERDRPlace", "<]")
+call s:InitVariable("g:NERDShutUp", '0')
+call s:InitVariable("g:NERDSpaceDelims", 0)
+call s:InitVariable("g:NERDDelimiterRequests", 1)
+
+
+
+let s:NERDFileNameEscape="[]#*$%'\" ?`!&();<>\\"
+
+" Section: Comment mapping functions, autocommands and commands {{{1
+" ============================================================================
+" Section: Comment enabler autocommands {{{2
+" ============================================================================
+
+augroup commentEnablers
+
+ "if the user enters a buffer or reads a buffer then we gotta set up
+ "the comment delimiters for that new filetype
+ autocmd BufEnter,BufRead * :call s:SetUpForNewFiletype(&filetype, 0)
+
+ "if the filetype of a buffer changes, force the script to reset the
+ "delims for the buffer
+ autocmd Filetype * :call s:SetUpForNewFiletype(&filetype, 1)
+augroup END
+
+
+" Function: s:SetUpForNewFiletype(filetype) function {{{2
+" This function is responsible for setting up buffer scoped variables for the
+" given filetype.
+"
+" These variables include the comment delimiters for the given filetype and calls
+" MapDelimiters or MapDelimitersWithAlternative passing in these delimiters.
+"
+" Args:
+" -filetype: the filetype to set delimiters for
+" -forceReset: 1 if the delimiters should be reset if they have already be
+" set for this buffer.
+"
+function s:SetUpForNewFiletype(filetype, forceReset)
+ "if we have already set the delimiters for this buffer then dont go thru
+ "it again
+ if !a:forceReset && exists("b:left") && b:left != ''
+ return
+ endif
+
+ let b:sexyComMarker = ''
+
+ "check the filetype against all known filetypes to see if we have
+ "hardcoded the comment delimiters to use
+ if a:filetype == ""
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "aap"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "abaqus"
+ call s:MapDelimiters('**', '')
+ elseif a:filetype == "abc"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "acedb"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "actionscript"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "ada"
+ call s:MapDelimitersWithAlternative('--','', '-- ', '')
+ elseif a:filetype == "ahdl"
+ call s:MapDelimiters('--', '')
+ elseif a:filetype == "ahk"
+ call s:MapDelimitersWithAlternative(';', '', '/*', '*/')
+ elseif a:filetype == "amiga"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "aml"
+ call s:MapDelimiters('/*', '')
+ elseif a:filetype == "ampl"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "ant"
+ call s:MapDelimiters('<!--','-->')
+ elseif a:filetype == "apache"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "apachestyle"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "asciidoc"
+ call s:MapDelimiters('//', '')
+ elseif a:filetype == "applescript"
+ call s:MapDelimitersWithAlternative('--', '', '(*', '*)')
+ elseif a:filetype == "asm68k"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "asm"
+ call s:MapDelimitersWithAlternative(';', '', '#', '')
+ elseif a:filetype == "asn"
+ call s:MapDelimiters('--', '')
+ elseif a:filetype == "aspvbs"
+ call s:MapDelimiters('''', '')
+ elseif a:filetype == "asterisk"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "asy"
+ call s:MapDelimiters('//', '')
+ elseif a:filetype == "atlas"
+ call s:MapDelimiters('C','$')
+ elseif a:filetype == "autohotkey"
+ call s:MapDelimiters(';','')
+ elseif a:filetype == "autoit"
+ call s:MapDelimiters(';','')
+ elseif a:filetype == "automake"
+ call s:MapDelimiters('##','')
+ elseif a:filetype == "ave"
+ call s:MapDelimiters("'",'')
+ elseif a:filetype == "awk"
+ call s:MapDelimiters('#','')
+ elseif a:filetype == "basic"
+ call s:MapDelimitersWithAlternative("'",'', 'REM ', '')
+ elseif a:filetype == "b"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "bbx"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "bc"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "bdf"
+ call s:MapDelimiters('COMMENT ', '')
+ elseif a:filetype == "bib"
+ call s:MapDelimiters('%','')
+ elseif a:filetype == "bindzone"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "bst"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "btm"
+ call s:MapDelimiters('::', '')
+ elseif a:filetype == "bzr"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "caos"
+ call s:MapDelimiters('*', '')
+ elseif a:filetype == "calibre"
+ call s:MapDelimiters('//','')
+ elseif a:filetype == "catalog"
+ call s:MapDelimiters('--','--')
+ elseif a:filetype == "c"
+ call s:MapDelimitersWithAlternative('/*','*/', '//', '')
+ elseif a:filetype == "cfg"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "cg"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "ch"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "changelog"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "cl"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "clean"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "clipper"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "clojure"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "cmake"
+ call s:MapDelimiters('#','')
+ elseif a:filetype == "cobol"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "conf"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "config"
+ call s:MapDelimiters('dnl ', '')
+ elseif a:filetype == "conkyrc"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "context"
+ call s:MapDelimiters('%','')
+ elseif a:filetype == "cpp"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "crontab"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "cs"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "csc"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "csp"
+ call s:MapDelimiters('--', '')
+ elseif a:filetype == "css"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "cterm"
+ call s:MapDelimiters('*', '')
+ elseif a:filetype == "cupl"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "csv"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "cvs"
+ call s:MapDelimiters('CVS:','')
+ elseif a:filetype == "CVSAnnotate"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "CVScommit"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "d"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "dcl"
+ call s:MapDelimiters('$!', '')
+ elseif a:filetype == "dakota"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "debchangelog"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "debcontrol"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "debsources"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "def"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "desktop"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "diff"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "django"
+ call s:MapDelimitersWithAlternative('<!--','-->', '{#', '#}')
+ elseif a:filetype == "docbk"
+ call s:MapDelimiters('<!--', '-->')
+ elseif a:filetype == "dns"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "dosbatch"
+ call s:MapDelimitersWithAlternative('REM ','', '::', '')
+ elseif a:filetype == "dosini"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "dot"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "dracula"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "dsl"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "dtd"
+ call s:MapDelimiters('<!--','-->')
+ elseif a:filetype == "dtml"
+ call s:MapDelimiters('<dtml-comment>','</dtml-comment>')
+ elseif a:filetype == "dtrace"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "dylan"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == 'ebuild'
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "ecd"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == 'eclass'
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "eiffel"
+ call s:MapDelimiters('--', '')
+ elseif a:filetype == "elf"
+ call s:MapDelimiters("'", '')
+ elseif a:filetype == "elmfilt"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "erlang"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "eruby"
+ call s:MapDelimitersWithAlternative('<!--', '-->', '<%#', '%>')
+ elseif a:filetype == "eterm"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "expect"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "exports"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "factor"
+ call s:MapDelimitersWithAlternative('! ', '', '!# ', '')
+ elseif a:filetype == "fetchmail"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "fgl"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "focexec"
+ call s:MapDelimiters('-*', '')
+ elseif a:filetype == "form"
+ call s:MapDelimiters('*', '')
+ elseif a:filetype == "fortran"
+ call s:MapDelimiters('!', '')
+ elseif a:filetype == "foxpro"
+ call s:MapDelimiters('*', '')
+ elseif a:filetype == "fstab"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "fvwm"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "fx"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "gams"
+ call s:MapDelimiters('*', '')
+ elseif a:filetype == "gdb"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "gdmo"
+ call s:MapDelimiters('--', '')
+ elseif a:filetype == "geek"
+ call s:MapDelimiters('GEEK_COMMENT:', '')
+ elseif a:filetype == "genshi"
+ call s:MapDelimitersWithAlternative('<!--','-->', '{#', '#}')
+ elseif a:filetype == "gentoo-conf-d"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "gentoo-env-d"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "gentoo-init-d"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "gentoo-make-conf"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == 'gentoo-package-keywords'
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == 'gentoo-package-mask'
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == 'gentoo-package-use'
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == 'git'
+ call s:MapDelimiters('', '')
+ elseif a:filetype == 'gitAnnotate'
+ call s:MapDelimiters('', '')
+ elseif a:filetype == 'gitcommit'
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == 'gitconfig'
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == 'gitdiff'
+ call s:MapDelimiters('', '')
+ elseif a:filetype == 'gitrebase'
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "gnuplot"
+ call s:MapDelimiters('#','')
+ elseif a:filetype == "groovy"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "group"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "grub"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "gtkrc"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "haskell"
+ call s:MapDelimitersWithAlternative('{-','-}', '--', '')
+ elseif a:filetype == "hb"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "h"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "haml"
+ call s:MapDelimitersWithAlternative('-#', '', '/', '')
+ elseif a:filetype == "help"
+ call s:MapDelimiters('"','')
+ elseif a:filetype == "hercules"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "hog"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "hostsaccess"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "html"
+ call s:MapDelimitersWithAlternative('<!--','-->', '//', '')
+ elseif a:filetype == "htmldjango"
+ call s:MapDelimitersWithAlternative('<!--','-->', '{#', '#}')
+ elseif a:filetype == "htmlos"
+ call s:MapDelimiters('#','/#')
+ elseif a:filetype == "ia64"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "icon"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "idlang"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "idl"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "indent"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "inform"
+ call s:MapDelimiters('!', '')
+ elseif a:filetype == "inittab"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "ishd"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "iss"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "ist"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "jam"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "java"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "javascript"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "jess"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "jgraph"
+ call s:MapDelimiters('(*','*)')
+ elseif a:filetype == "jproperties"
+ call s:MapDelimiters('#','')
+ elseif a:filetype == "jsp"
+ call s:MapDelimiters('<%--', '--%>')
+ elseif a:filetype == "kconfig"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "kix"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "kscript"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "lace"
+ call s:MapDelimiters('--', '')
+ elseif a:filetype == "ldif"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "lex"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "lftp"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "lhaskell"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "lifelines"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "lilo"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "lilypond"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "liquid"
+ call s:MapDelimiters('{%', '%}')
+ elseif a:filetype == "lisp"
+ call s:MapDelimitersWithAlternative(';','', '#|', '|#')
+ elseif a:filetype == "lite"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "llvm"
+ call s:MapDelimiters(';','')
+ elseif a:filetype == "lookupfile"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "lotos"
+ call s:MapDelimiters('(*','*)')
+ elseif a:filetype == "lout"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "lprolog"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "lscript"
+ call s:MapDelimiters("'", '')
+ elseif a:filetype == "lss"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "lua"
+ call s:MapDelimitersWithAlternative('--','', '--[[', ']]')
+ elseif a:filetype == "lynx"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "lytex"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "m4"
+ call s:MapDelimiters('dnl ', '')
+ elseif a:filetype == "mail"
+ call s:MapDelimiters('> ','')
+ elseif a:filetype == "mailcap"
+ call s:MapDelimiters('#','')
+ elseif a:filetype == "make"
+ call s:MapDelimiters('#','')
+ elseif a:filetype == "mako"
+ call s:MapDelimiters('##', '')
+ elseif a:filetype == "man"
+ call s:MapDelimiters('."', '')
+ elseif a:filetype == "map"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "maple"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "markdown"
+ call s:MapDelimiters('<!--', '-->')
+ elseif a:filetype == "masm"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "mason"
+ call s:MapDelimiters('<% #', '%>')
+ elseif a:filetype == "master"
+ call s:MapDelimiters('$', '')
+ elseif a:filetype == "matlab"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "mel"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "mf"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "mib"
+ call s:MapDelimiters('--', '')
+ elseif a:filetype == "mkd"
+ call s:MapDelimiters('>', '')
+ elseif a:filetype == "mma"
+ call s:MapDelimiters('(*','*)')
+ elseif a:filetype == "modconf"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "model"
+ call s:MapDelimiters('$','$')
+ elseif a:filetype =~ "moduala."
+ call s:MapDelimiters('(*','*)')
+ elseif a:filetype == "modula2"
+ call s:MapDelimiters('(*','*)')
+ elseif a:filetype == "modula3"
+ call s:MapDelimiters('(*','*)')
+ elseif a:filetype == "monk"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "mplayerconf"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "mrxvtrc"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "mush"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "muttrc"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "named"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "nasm"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "nastran"
+ call s:MapDelimiters('$', '')
+ elseif a:filetype == "natural"
+ call s:MapDelimiters('/*', '')
+ elseif a:filetype == "ncf"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "nerdtree"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "netdict"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "netrw"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "newlisp"
+ call s:MapDelimiters(';','')
+ elseif a:filetype == "nqc"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "nroff"
+ call s:MapDelimiters('\"', '')
+ elseif a:filetype == "nsis"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "objc"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "objcpp"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "objj"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "ocaml"
+ call s:MapDelimiters('(*','*)')
+ elseif a:filetype == "occam"
+ call s:MapDelimiters('--','')
+ elseif a:filetype == "omlet"
+ call s:MapDelimiters('(*','*)')
+ elseif a:filetype == "omnimark"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "openroad"
+ call s:MapDelimiters('//', '')
+ elseif a:filetype == "opl"
+ call s:MapDelimiters("REM", "")
+ elseif a:filetype == "ora"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "otl"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "ox"
+ call s:MapDelimiters('//', '')
+ elseif a:filetype == "pamconf"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "pascal"
+ call s:MapDelimitersWithAlternative('{','}', '(*', '*)')
+ elseif a:filetype == "passwd"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "patran"
+ call s:MapDelimitersWithAlternative('$','','/*', '*/')
+ elseif a:filetype == "pcap"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "pccts"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "perl"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "pfmain"
+ call s:MapDelimiters('//', '')
+ elseif a:filetype == "php"
+ call s:MapDelimitersWithAlternative('//','','/*', '*/')
+ elseif a:filetype == "phtml"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "pic"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "pike"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "pilrc"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "pine"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "plaintex"
+ call s:MapDelimiters('%','')
+ elseif a:filetype == "plm"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "plsql"
+ call s:MapDelimitersWithAlternative('--', '', '/*', '*/')
+ elseif a:filetype == "po"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "postscr"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "potwiki"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "pov"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "povini"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "ppd"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "ppwiz"
+ call s:MapDelimiters(';;', '')
+ elseif a:filetype == "processing"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "procmail"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "progress"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "prolog"
+ call s:MapDelimitersWithAlternative('%','','/*','*/')
+ elseif a:filetype == "ps1"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "psf"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "ptcap"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "pyrex"
+ call s:MapDelimiters('#','')
+ elseif a:filetype == "python"
+ call s:MapDelimiters('#','')
+ elseif a:filetype == "qf"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "radiance"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "Rails-log"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "ratpoison"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "r"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "rc"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "readline"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "rebol"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "registry"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "remind"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "rexx"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "rib"
+ call s:MapDelimiters('#','')
+ elseif a:filetype == "robots"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "rpl"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "rst"
+ call s:MapDelimiters('..', '')
+ elseif a:filetype == "rtf"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "ruby"
+ call s:MapDelimiters('#','')
+ elseif a:filetype == "sa"
+ call s:MapDelimiters('--','')
+ elseif a:filetype == "samba"
+ call s:MapDelimitersWithAlternative(';','', '#', '')
+ elseif a:filetype == "sas"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "sass"
+ call s:MapDelimitersWithAlternative('//','', '/*', '')
+ elseif a:filetype == "sather"
+ call s:MapDelimiters('--', '')
+ elseif a:filetype == "scala"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "scheme"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "scilab"
+ call s:MapDelimiters('//', '')
+ elseif a:filetype == "screen"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "scsh"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "sdl"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "sed"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "selectbuf"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "services"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "sgml"
+ call s:MapDelimiters('<!','>')
+ elseif a:filetype == "sgmldecl"
+ call s:MapDelimiters('--','--')
+ elseif a:filetype == "sgmllnx"
+ call s:MapDelimiters('<!--','-->')
+ elseif a:filetype == "sicad"
+ call s:MapDelimiters('*', '')
+ elseif a:filetype == "sieve"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "simula"
+ call s:MapDelimitersWithAlternative('%', '', '--', '')
+ elseif a:filetype == "sinda"
+ call s:MapDelimiters('$', '')
+ elseif a:filetype == "skill"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "slang"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "sl"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "slice"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "slrnrc"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "sm"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "smarty"
+ call s:MapDelimiters('{*', '*}')
+ elseif a:filetype == "smil"
+ call s:MapDelimiters('<!','>')
+ elseif a:filetype == "smith"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "sml"
+ call s:MapDelimiters('(*','*)')
+ elseif a:filetype == "snnsnet"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "snnspat"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "snnsres"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "snobol4"
+ call s:MapDelimiters('*', '')
+ elseif a:filetype == "spec"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "specman"
+ call s:MapDelimiters('//', '')
+ elseif a:filetype == "spectre"
+ call s:MapDelimitersWithAlternative('//', '', '*', '')
+ elseif a:filetype == "spice"
+ call s:MapDelimiters('$', '')
+ elseif a:filetype == "sql"
+ call s:MapDelimiters('--', '')
+ elseif a:filetype == "sqlforms"
+ call s:MapDelimiters('--', '')
+ elseif a:filetype == "sqlj"
+ call s:MapDelimiters('--', '')
+ elseif a:filetype == "sqr"
+ call s:MapDelimiters('!', '')
+ elseif a:filetype == "squid"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "st"
+ call s:MapDelimiters('"','')
+ elseif a:filetype == "stata"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "stp"
+ call s:MapDelimiters('--', '')
+ elseif a:filetype == "strace"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "sudoers"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "SVKAnnotate"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "svn"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "SVNannotate"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "SVNAnnotate"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "SVNcommit"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "SVNcommitlog"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "SVNdiff"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "SVNinfo"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "systemverilog"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "tads"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "taglist"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "tags"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "tak"
+ call s:MapDelimiters('$', '')
+ elseif a:filetype == "tar"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "tasm"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "tcl"
+ call s:MapDelimiters('#','')
+ elseif a:filetype == "terminfo"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "tex"
+ call s:MapDelimiters('%','')
+ elseif a:filetype == "text"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "texinfo"
+ call s:MapDelimiters("@c ", "")
+ elseif a:filetype == "texmf"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "tf"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "tidy"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "tli"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "trasys"
+ call s:MapDelimiters("$", "")
+ elseif a:filetype == "tsalt"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "tsscl"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "tssgm"
+ call s:MapDelimiters("comment = '","'")
+ elseif a:filetype == "txt2tags"
+ call s:MapDelimiters('%','')
+ elseif a:filetype == "uc"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "uil"
+ call s:MapDelimiters('!', '')
+ elseif a:filetype == "vb"
+ call s:MapDelimiters("'","")
+ elseif a:filetype == "vcscommit"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "velocity"
+ call s:MapDelimitersWithAlternative("##","", '#*', '*#')
+ elseif a:filetype == "vera"
+ call s:MapDelimitersWithAlternative('/*','*/','//','')
+ elseif a:filetype == "verilog"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "verilog_systemverilog"
+ call s:MapDelimitersWithAlternative('//','', '/*','*/')
+ elseif a:filetype == "vgrindefs"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "vhdl"
+ call s:MapDelimiters('--', '')
+ elseif a:filetype == "vim"
+ call s:MapDelimiters('"','')
+ elseif a:filetype == "viminfo"
+ call s:MapDelimiters('','')
+ elseif a:filetype == "vimperator"
+ call s:MapDelimiters('"','')
+ elseif a:filetype == "virata"
+ call s:MapDelimiters('%', '')
+ elseif a:filetype == "vo_base"
+ call s:MapDelimiters('', '')
+ elseif a:filetype == "vrml"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "vsejcl"
+ call s:MapDelimiters('/*', '')
+ elseif a:filetype == "webmacro"
+ call s:MapDelimiters('##', '')
+ elseif a:filetype == "wget"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype ==? "Wikipedia"
+ call s:MapDelimiters('<!--','-->')
+ elseif a:filetype == "winbatch"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "wml"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype =~ "[^w]*sh"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "wvdial"
+ call s:MapDelimiters(';', '')
+ elseif a:filetype == "xdefaults"
+ call s:MapDelimiters('!', '')
+ elseif a:filetype == "xf86conf"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "xhtml"
+ call s:MapDelimiters('<!--', '-->')
+ elseif a:filetype == "xkb"
+ call s:MapDelimiters('//', '')
+ elseif a:filetype == "xmath"
+ call s:MapDelimiters('#', '')
+ elseif a:filetype == "xml"
+ call s:MapDelimiters('<!--','-->')
+ elseif a:filetype == "xmodmap"
+ call s:MapDelimiters('!', '')
+ elseif a:filetype == "xpm2"
+ call s:MapDelimiters('!', '')
+ elseif a:filetype == "xpm"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "xsd"
+ call s:MapDelimiters('<!--','-->')
+ elseif a:filetype == "xslt"
+ call s:MapDelimiters('<!--','-->')
+ elseif a:filetype == "yacc"
+ call s:MapDelimiters('/*','*/')
+ elseif a:filetype == "yaml"
+ call s:MapDelimiters('#','')
+ elseif a:filetype == "xquery"
+ call s:MapDelimiters('(:',':)')
+ elseif a:filetype == "z8a"
+ call s:MapDelimiters(';', '')
+
+ elseif a:filetype == ""
+ call s:MapDelimitersWithAlternative("","", "", "")
+
+ "we have not hardcoded the comment delimiters to use for this filetype so
+ "get them from &commentstring.
+ else
+ "print a disclaimer to the user :)
+ if !g:NERDShutUp
+ call s:NerdEcho("Unknown filetype '".a:filetype."', setting delimiters by &commentstring.\nPleeeeease email the author of the NERD commenter with this filetype\nand its delimiters!", 0)
+ endif
+
+ "extract the delims from &commentstring
+ let left= substitute(&commentstring, '\(.*\)%s.*', '\1', '')
+ let right= substitute(&commentstring, '.*%s\(.*\)', '\1', 'g')
+
+ call s:MapDelimiters(left,right)
+ endif
+endfunction
+
+" Function: s:MapDelimiters(left, right) function {{{2
+" This function is a wrapper for s:MapDelimiters(left, right, leftAlt, rightAlt, useAlt) and is called when there
+" is no alternative comment delimiters for the current filetype
+"
+" Args:
+" -left: the left comment delimiter
+" -right: the right comment delimiter
+function s:MapDelimiters(left, right)
+ call s:MapDelimitersWithAlternative(a:left, a:right, "", "")
+endfunction
+
+" Function: s:MapDelimitersWithAlternative(left, right, leftAlt, rightAlt) function {{{2
+" this function sets up the comment delimiter buffer variables
+"
+" Args:
+" -left: the string defining the comment start delimiter
+" -right: the string defining the comment end delimiter
+" -leftAlt: the string for the alternative comment style defining the comment start delimiter
+" -rightAlt: the string for the alternative comment style defining the comment end delimiter
+function s:MapDelimitersWithAlternative(left, right, leftAlt, rightAlt)
+ if !exists('g:NERD_' . &filetype . '_alt_style')
+ let b:left = a:left
+ let b:right = a:right
+ let b:leftAlt = a:leftAlt
+ let b:rightAlt = a:rightAlt
+ else
+ let b:left = a:leftAlt
+ let b:right = a:rightAlt
+ let b:leftAlt = a:left
+ let b:rightAlt = a:right
+ endif
+endfunction
+
+" Function: s:SwitchToAlternativeDelimiters(printMsgs) function {{{2
+" This function is used to swap the delimiters that are being used to the
+" alternative delimiters for that filetype. For example, if a c++ file is
+" being edited and // comments are being used, after this function is called
+" /**/ comments will be used.
+"
+" Args:
+" -printMsgs: if this is 1 then a message is echoed to the user telling them
+" if this function changed the delimiters or not
+function s:SwitchToAlternativeDelimiters(printMsgs)
+ "if both of the alternative delimiters are empty then there is no
+ "alternative comment style so bail out
+ if b:leftAlt == "" && b:rightAlt == ""
+ if a:printMsgs
+ call s:NerdEcho("Cannot use alternative delimiters, none are specified", 0)
+ endif
+ return 0
+ endif
+
+ "save the current delimiters
+ let tempLeft = b:left
+ let tempRight = b:right
+
+ "swap current delimiters for alternative
+ let b:left = b:leftAlt
+ let b:right = b:rightAlt
+
+ "set the previously current delimiters to be the new alternative ones
+ let b:leftAlt = tempLeft
+ let b:rightAlt = tempRight
+
+ "tell the user what comment delimiters they are now using
+ if a:printMsgs
+ let leftNoEsc = b:left
+ let rightNoEsc = b:right
+ call s:NerdEcho("Now using " . leftNoEsc . " " . rightNoEsc . " to delimit comments", 1)
+ endif
+
+ return 1
+endfunction
+
+" Section: Comment delimiter add/removal functions {{{1
+" ============================================================================
+" Function: s:AppendCommentToLine(){{{2
+" This function appends comment delimiters at the EOL and places the cursor in
+" position to start typing the comment
+function s:AppendCommentToLine()
+ let left = s:GetLeft(0,1,0)
+ let right = s:GetRight(0,1,0)
+
+ " get the len of the right delim
+ let lenRight = strlen(right)
+
+ let isLineEmpty = strlen(getline(".")) == 0
+ let insOrApp = (isLineEmpty==1 ? 'i' : 'A')
+
+ "stick the delimiters down at the end of the line. We have to format the
+ "comment with spaces as appropriate
+ execute ":normal! " . insOrApp . (isLineEmpty ? '' : ' ') . left . right . " "
+
+ " if there is a right delimiter then we gotta move the cursor left
+ " by the len of the right delimiter so we insert between the delimiters
+ if lenRight > 0
+ let leftMoveAmount = lenRight
+ execute ":normal! " . leftMoveAmount . "h"
+ endif
+ startinsert
+endfunction
+
+" Function: s:CommentBlock(top, bottom, lSide, rSide, forceNested ) {{{2
+" This function is used to comment out a region of code. This region is
+" specified as a bounding box by arguments to the function.
+"
+" Args:
+" -top: the line number for the top line of code in the region
+" -bottom: the line number for the bottom line of code in the region
+" -lSide: the column number for the left most column in the region
+" -rSide: the column number for the right most column in the region
+" -forceNested: a flag indicating whether comments should be nested
+function s:CommentBlock(top, bottom, lSide, rSide, forceNested )
+ " we need to create local copies of these arguments so we can modify them
+ let top = a:top
+ let bottom = a:bottom
+ let lSide = a:lSide
+ let rSide = a:rSide
+
+ "if the top or bottom line starts with tabs we have to adjust the left and
+ "right boundaries so that they are set as though the tabs were spaces
+ let topline = getline(top)
+ let bottomline = getline(bottom)
+ if s:HasLeadingTabs(topline, bottomline)
+
+ "find out how many tabs are in the top line and adjust the left
+ "boundary accordingly
+ let numTabs = s:NumberOfLeadingTabs(topline)
+ if lSide < numTabs
+ let lSide = &ts * lSide
+ else
+ let lSide = (lSide - numTabs) + (&ts * numTabs)
+ endif
+
+ "find out how many tabs are in the bottom line and adjust the right
+ "boundary accordingly
+ let numTabs = s:NumberOfLeadingTabs(bottomline)
+ let rSide = (rSide - numTabs) + (&ts * numTabs)
+ endif
+
+ "we must check that bottom IS actually below top, if it is not then we
+ "swap top and bottom. Similarly for left and right.
+ if bottom < top
+ let temp = top
+ let top = bottom
+ let bottom = top
+ endif
+ if rSide < lSide
+ let temp = lSide
+ let lSide = rSide
+ let rSide = temp
+ endif
+
+ "if the current delimiters arent multipart then we will switch to the
+ "alternative delims (if THEY are) as the comment will be better and more
+ "accurate with multipart delims
+ let switchedDelims = 0
+ if !s:Multipart() && g:NERDAllowAnyVisualDelims && s:AltMultipart()
+ let switchedDelims = 1
+ call s:SwitchToAlternativeDelimiters(0)
+ endif
+
+ "start the commenting from the top and keep commenting till we reach the
+ "bottom
+ let currentLine=top
+ while currentLine <= bottom
+
+ "check if we are allowed to comment this line
+ if s:CanCommentLine(a:forceNested, currentLine)
+
+ "convert the leading tabs into spaces
+ let theLine = getline(currentLine)
+ let lineHasLeadTabs = s:HasLeadingTabs(theLine)
+ if lineHasLeadTabs
+ let theLine = s:ConvertLeadingTabsToSpaces(theLine)
+ endif
+
+ "dont comment lines that begin after the right boundary of the
+ "block unless the user has specified to do so
+ if theLine !~ '^ \{' . rSide . '\}' || !g:NERDBlockComIgnoreEmpty
+
+ "attempt to place the cursor in on the left of the boundary box,
+ "then check if we were successful, if not then we cant comment this
+ "line
+ call setline(currentLine, theLine)
+ if s:CanPlaceCursor(currentLine, lSide)
+
+ let leftSpaced = s:GetLeft(0,1,0)
+ let rightSpaced = s:GetRight(0,1,0)
+
+ "stick the left delimiter down
+ let theLine = strpart(theLine, 0, lSide-1) . leftSpaced . strpart(theLine, lSide-1)
+
+ if s:Multipart()
+ "stick the right delimiter down
+ let theLine = strpart(theLine, 0, rSide+strlen(leftSpaced)) . rightSpaced . strpart(theLine, rSide+strlen(leftSpaced))
+
+ let firstLeftDelim = s:FindDelimiterIndex(b:left, theLine)
+ let lastRightDelim = s:LastIndexOfDelim(b:right, theLine)
+
+ if firstLeftDelim != -1 && lastRightDelim != -1
+ let searchStr = strpart(theLine, 0, lastRightDelim)
+ let searchStr = strpart(searchStr, firstLeftDelim+strlen(b:left))
+
+ "replace the outter most delims in searchStr with
+ "place-holders
+ let theLineWithPlaceHolders = s:ReplaceDelims(b:left, b:right, g:NERDLPlace, g:NERDRPlace, searchStr)
+
+ "add the right delimiter onto the line
+ let theLine = strpart(theLine, 0, firstLeftDelim+strlen(b:left)) . theLineWithPlaceHolders . strpart(theLine, lastRightDelim)
+ endif
+ endif
+ endif
+ endif
+
+ "restore tabs if needed
+ if lineHasLeadTabs
+ let theLine = s:ConvertLeadingSpacesToTabs(theLine)
+ endif
+
+ call setline(currentLine, theLine)
+ endif
+
+ let currentLine = currentLine + 1
+ endwhile
+
+ "if we switched delims then we gotta go back to what they were before
+ if switchedDelims == 1
+ call s:SwitchToAlternativeDelimiters(0)
+ endif
+endfunction
+
+" Function: s:CommentLines(forceNested, alignLeft, alignRight, firstLine, lastLine) {{{2
+" This function comments a range of lines.
+"
+" Args:
+" -forceNested: a flag indicating whether the called is requesting the comment
+" to be nested if need be
+" -align: should be "left" or "both" or "none"
+" -firstLine/lastLine: the top and bottom lines to comment
+function s:CommentLines(forceNested, align, firstLine, lastLine)
+ " we need to get the left and right indexes of the leftmost char in the
+ " block of of lines and the right most char so that we can do alignment of
+ " the delimiters if the user has specified
+ let leftAlignIndx = s:LeftMostIndx(a:forceNested, 0, a:firstLine, a:lastLine)
+ let rightAlignIndx = s:RightMostIndx(a:forceNested, 0, a:firstLine, a:lastLine)
+
+ " gotta add the length of the left delimiter onto the rightAlignIndx cos
+ " we'll be adding a left delim to the line
+ let rightAlignIndx = rightAlignIndx + strlen(s:GetLeft(0,1,0))
+
+ " now we actually comment the lines. Do it line by line
+ let currentLine = a:firstLine
+ while currentLine <= a:lastLine
+
+ " get the next line, check commentability and convert spaces to tabs
+ let theLine = getline(currentLine)
+ let lineHasLeadingTabs = s:HasLeadingTabs(theLine)
+ let theLine = s:ConvertLeadingTabsToSpaces(theLine)
+ if s:CanCommentLine(a:forceNested, currentLine)
+ "if the user has specified forceNesting then we check to see if we
+ "need to switch delimiters for place-holders
+ if a:forceNested && g:NERDUsePlaceHolders
+ let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine)
+ endif
+
+ " find out if the line is commented using normal delims and/or
+ " alternate ones
+ let isCommented = s:IsCommented(b:left, b:right, theLine) || s:IsCommented(b:leftAlt, b:rightAlt, theLine)
+
+ " check if we can comment this line
+ if !isCommented || g:NERDUsePlaceHolders || s:Multipart()
+ if a:align == "left" || a:align == "both"
+ let theLine = s:AddLeftDelimAligned(s:GetLeft(0,1,0), theLine, leftAlignIndx)
+ else
+ let theLine = s:AddLeftDelim(s:GetLeft(0,1,0), theLine)
+ endif
+ if a:align == "both"
+ let theLine = s:AddRightDelimAligned(s:GetRight(0,1,0), theLine, rightAlignIndx)
+ else
+ let theLine = s:AddRightDelim(s:GetRight(0,1,0), theLine)
+ endif
+ endif
+ endif
+
+ " restore leading tabs if appropriate
+ if lineHasLeadingTabs
+ let theLine = s:ConvertLeadingSpacesToTabs(theLine)
+ endif
+
+ " we are done with this line
+ call setline(currentLine, theLine)
+ let currentLine = currentLine + 1
+ endwhile
+
+endfunction
+
+" Function: s:CommentLinesMinimal(firstLine, lastLine) {{{2
+" This function comments a range of lines in a minimal style. I
+"
+" Args:
+" -firstLine/lastLine: the top and bottom lines to comment
+function s:CommentLinesMinimal(firstLine, lastLine)
+ "check that minimal comments can be done on this filetype
+ if !s:HasMultipartDelims()
+ throw 'NERDCommenter.Delimiters exception: Minimal comments can only be used for filetypes that have multipart delimiters'
+ endif
+
+ "if we need to use place holders for the comment, make sure they are
+ "enabled for this filetype
+ if !g:NERDUsePlaceHolders && s:DoesBlockHaveMultipartDelim(a:firstLine, a:lastLine)
+ throw 'NERDCommenter.Settings exception: Placeoholders are required but disabled.'
+ endif
+
+ "get the left and right delims to smack on
+ let left = s:GetSexyComLeft(g:NERDSpaceDelims,0)
+ let right = s:GetSexyComRight(g:NERDSpaceDelims,0)
+
+ "make sure all multipart delims on the lines are replaced with
+ "placeholders to prevent illegal syntax
+ let currentLine = a:firstLine
+ while(currentLine <= a:lastLine)
+ let theLine = getline(currentLine)
+ let theLine = s:ReplaceDelims(left, right, g:NERDLPlace, g:NERDRPlace, theLine)
+ call setline(currentLine, theLine)
+ let currentLine = currentLine + 1
+ endwhile
+
+ "add the delim to the top line
+ let theLine = getline(a:firstLine)
+ let lineHasLeadingTabs = s:HasLeadingTabs(theLine)
+ let theLine = s:ConvertLeadingTabsToSpaces(theLine)
+ let theLine = s:AddLeftDelim(left, theLine)
+ if lineHasLeadingTabs
+ let theLine = s:ConvertLeadingSpacesToTabs(theLine)
+ endif
+ call setline(a:firstLine, theLine)
+
+ "add the delim to the bottom line
+ let theLine = getline(a:lastLine)
+ let lineHasLeadingTabs = s:HasLeadingTabs(theLine)
+ let theLine = s:ConvertLeadingTabsToSpaces(theLine)
+ let theLine = s:AddRightDelim(right, theLine)
+ if lineHasLeadingTabs
+ let theLine = s:ConvertLeadingSpacesToTabs(theLine)
+ endif
+ call setline(a:lastLine, theLine)
+endfunction
+
+" Function: s:CommentLinesSexy(topline, bottomline) function {{{2
+" This function is used to comment lines in the 'Sexy' style. eg in c:
+" /*
+" * This is a sexy comment
+" */
+" Args:
+" -topline: the line num of the top line in the sexy comment
+" -bottomline: the line num of the bottom line in the sexy comment
+function s:CommentLinesSexy(topline, bottomline)
+ let left = s:GetSexyComLeft(0, 0)
+ let right = s:GetSexyComRight(0, 0)
+
+ "check if we can do a sexy comment with the available delimiters
+ if left == -1 || right == -1
+ throw 'NERDCommenter.Delimiters exception: cannot perform sexy comments with available delimiters.'
+ endif
+
+ "make sure the lines arent already commented sexually
+ if !s:CanSexyCommentLines(a:topline, a:bottomline)
+ throw 'NERDCommenter.Nesting exception: cannot nest sexy comments'
+ endif
+
+
+ let sexyComMarker = s:GetSexyComMarker(0,0)
+ let sexyComMarkerSpaced = s:GetSexyComMarker(1,0)
+
+
+ " we jam the comment as far to the right as possible
+ let leftAlignIndx = s:LeftMostIndx(1, 1, a:topline, a:bottomline)
+
+ "check if we should use the compact style i.e that the left/right
+ "delimiters should appear on the first and last lines of the code and not
+ "on separate lines above/below the first/last lines of code
+ if g:NERDCompactSexyComs
+ let spaceString = (g:NERDSpaceDelims ? s:spaceStr : '')
+
+ "comment the top line
+ let theLine = getline(a:topline)
+ let lineHasTabs = s:HasLeadingTabs(theLine)
+ if lineHasTabs
+ let theLine = s:ConvertLeadingTabsToSpaces(theLine)
+ endif
+ let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine)
+ let theLine = s:AddLeftDelimAligned(left . spaceString, theLine, leftAlignIndx)
+ if lineHasTabs
+ let theLine = s:ConvertLeadingSpacesToTabs(theLine)
+ endif
+ call setline(a:topline, theLine)
+
+ "comment the bottom line
+ if a:bottomline != a:topline
+ let theLine = getline(a:bottomline)
+ let lineHasTabs = s:HasLeadingTabs(theLine)
+ if lineHasTabs
+ let theLine = s:ConvertLeadingTabsToSpaces(theLine)
+ endif
+ let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine)
+ endif
+ let theLine = s:AddRightDelim(spaceString . right, theLine)
+ if lineHasTabs
+ let theLine = s:ConvertLeadingSpacesToTabs(theLine)
+ endif
+ call setline(a:bottomline, theLine)
+ else
+
+ " add the left delimiter one line above the lines that are to be commented
+ call cursor(a:topline, 1)
+ execute 'normal! O'
+ call setline(a:topline, strpart(s:spaces, 0, leftAlignIndx) . left )
+
+ " add the right delimiter after bottom line (we have to add 1 cos we moved
+ " the lines down when we added the left delim
+ call cursor(a:bottomline+1, 1)
+ execute 'normal! o'
+ call setline(a:bottomline+2, strpart(s:spaces, 0, leftAlignIndx) . strpart(s:spaces, 0, strlen(left)-strlen(sexyComMarker)) . right )
+
+ endif
+
+ " go thru each line adding the sexyComMarker marker to the start of each
+ " line in the appropriate place to align them with the comment delims
+ let currentLine = a:topline+1
+ while currentLine <= a:bottomline + !g:NERDCompactSexyComs
+ " get the line and convert the tabs to spaces
+ let theLine = getline(currentLine)
+ let lineHasTabs = s:HasLeadingTabs(theLine)
+ if lineHasTabs
+ let theLine = s:ConvertLeadingTabsToSpaces(theLine)
+ endif
+
+ let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine)
+
+ " add the sexyComMarker
+ let theLine = strpart(s:spaces, 0, leftAlignIndx) . strpart(s:spaces, 0, strlen(left)-strlen(sexyComMarker)) . sexyComMarkerSpaced . strpart(theLine, leftAlignIndx)
+
+ if lineHasTabs
+ let theLine = s:ConvertLeadingSpacesToTabs(theLine)
+ endif
+
+
+ " set the line and move onto the next one
+ call setline(currentLine, theLine)
+ let currentLine = currentLine + 1
+ endwhile
+
+endfunction
+
+" Function: s:CommentLinesToggle(forceNested, firstLine, lastLine) {{{2
+" Applies "toggle" commenting to the given range of lines
+"
+" Args:
+" -forceNested: a flag indicating whether the called is requesting the comment
+" to be nested if need be
+" -firstLine/lastLine: the top and bottom lines to comment
+function s:CommentLinesToggle(forceNested, firstLine, lastLine)
+ let currentLine = a:firstLine
+ while currentLine <= a:lastLine
+
+ " get the next line, check commentability and convert spaces to tabs
+ let theLine = getline(currentLine)
+ let lineHasLeadingTabs = s:HasLeadingTabs(theLine)
+ let theLine = s:ConvertLeadingTabsToSpaces(theLine)
+ if s:CanToggleCommentLine(a:forceNested, currentLine)
+
+ "if the user has specified forceNesting then we check to see if we
+ "need to switch delimiters for place-holders
+ if g:NERDUsePlaceHolders
+ let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine)
+ endif
+
+ let theLine = s:AddLeftDelim(s:GetLeft(0, 1, 0), theLine)
+ let theLine = s:AddRightDelim(s:GetRight(0, 1, 0), theLine)
+ endif
+
+ " restore leading tabs if appropriate
+ if lineHasLeadingTabs
+ let theLine = s:ConvertLeadingSpacesToTabs(theLine)
+ endif
+
+ " we are done with this line
+ call setline(currentLine, theLine)
+ let currentLine = currentLine + 1
+ endwhile
+
+endfunction
+
+" Function: s:CommentRegion(topline, topCol, bottomLine, bottomCol) function {{{2
+" This function comments chunks of text selected in visual mode.
+" It will comment exactly the text that they have selected.
+" Args:
+" -topLine: the line num of the top line in the sexy comment
+" -topCol: top left col for this comment
+" -bottomline: the line num of the bottom line in the sexy comment
+" -bottomCol: the bottom right col for this comment
+" -forceNested: whether the caller wants comments to be nested if the
+" line(s) are already commented
+function s:CommentRegion(topLine, topCol, bottomLine, bottomCol, forceNested)
+
+ "switch delims (if we can) if the current set isnt multipart
+ let switchedDelims = 0
+ if !s:Multipart() && s:AltMultipart() && !g:NERDAllowAnyVisualDelims
+ let switchedDelims = 1
+ call s:SwitchToAlternativeDelimiters(0)
+ endif
+
+ "if there is only one line in the comment then just do it
+ if a:topLine == a:bottomLine
+ call s:CommentBlock(a:topLine, a:bottomLine, a:topCol, a:bottomCol, a:forceNested)
+
+ "there are multiple lines in the comment
+ else
+ "comment the top line
+ call s:CommentBlock(a:topLine, a:topLine, a:topCol, strlen(getline(a:topLine)), a:forceNested)
+
+ "comment out all the lines in the middle of the comment
+ let topOfRange = a:topLine+1
+ let bottomOfRange = a:bottomLine-1
+ if topOfRange <= bottomOfRange
+ call s:CommentLines(a:forceNested, "none", topOfRange, bottomOfRange)
+ endif
+
+ "comment the bottom line
+ let bottom = getline(a:bottomLine)
+ let numLeadingSpacesTabs = strlen(substitute(bottom, '^\([ \t]*\).*$', '\1', ''))
+ call s:CommentBlock(a:bottomLine, a:bottomLine, numLeadingSpacesTabs+1, a:bottomCol, a:forceNested)
+
+ endif
+
+ "stick the cursor back on the char it was on before the comment
+ call cursor(a:topLine, a:topCol + strlen(b:left) + g:NERDSpaceDelims)
+
+ "if we switched delims then we gotta go back to what they were before
+ if switchedDelims == 1
+ call s:SwitchToAlternativeDelimiters(0)
+ endif
+
+endfunction
+
+" Function: s:InvertComment(firstLine, lastLine) function {{{2
+" Inverts the comments on the lines between and including the given line
+" numbers i.e all commented lines are uncommented and vice versa
+" Args:
+" -firstLine: the top of the range of lines to be inverted
+" -lastLine: the bottom of the range of lines to be inverted
+function s:InvertComment(firstLine, lastLine)
+
+ " go thru all lines in the given range
+ let currentLine = a:firstLine
+ while currentLine <= a:lastLine
+ let theLine = getline(currentLine)
+
+ let sexyComBounds = s:FindBoundingLinesOfSexyCom(currentLine)
+
+ " if the line is commented normally, uncomment it
+ if s:IsCommentedFromStartOfLine(b:left, theLine) || s:IsCommentedFromStartOfLine(b:leftAlt, theLine)
+ call s:UncommentLines(currentLine, currentLine)
+ let currentLine = currentLine + 1
+
+ " check if the line is commented sexually
+ elseif !empty(sexyComBounds)
+ let numLinesBeforeSexyComRemoved = s:NumLinesInBuf()
+ call s:UncommentLinesSexy(sexyComBounds[0], sexyComBounds[1])
+
+ "move to the line after last line of the sexy comment
+ let numLinesAfterSexyComRemoved = s:NumLinesInBuf()
+ let currentLine = bottomBound - (numLinesBeforeSexyComRemoved - numLinesAfterSexyComRemoved) + 1
+
+ " the line isnt commented
+ else
+ call s:CommentLinesToggle(1, currentLine, currentLine)
+ let currentLine = currentLine + 1
+ endif
+
+ endwhile
+endfunction
+
+" Function: NERDComment(isVisual, type) function {{{2
+" This function is a Wrapper for the main commenting functions
+"
+" Args:
+" -isVisual: a flag indicating whether the comment is requested in visual
+" mode or not
+" -type: the type of commenting requested. Can be 'sexy', 'invert',
+" 'minimal', 'toggle', 'alignLeft', 'alignBoth', 'norm',
+" 'nested', 'toEOL', 'append', 'insert', 'uncomment', 'yank'
+function! NERDComment(isVisual, type) range
+ " we want case sensitivity when commenting
+ let oldIgnoreCase = &ignorecase
+ set noignorecase
+
+ if a:isVisual
+ let firstLine = line("'<")
+ let lastLine = line("'>")
+ let firstCol = col("'<")
+ let lastCol = col("'>") - (&selection == 'exclusive' ? 1 : 0)
+ else
+ let firstLine = a:firstline
+ let lastLine = a:lastline
+ endif
+
+ let countWasGiven = (a:isVisual == 0 && firstLine != lastLine)
+
+ let forceNested = (a:type == 'nested' || g:NERDDefaultNesting)
+
+ if a:type == 'norm' || a:type == 'nested'
+ if a:isVisual && visualmode() == ""
+ call s:CommentBlock(firstLine, lastLine, firstCol, lastCol, forceNested)
+ elseif a:isVisual && visualmode() == "v" && (g:NERDCommentWholeLinesInVMode==0 || (g:NERDCommentWholeLinesInVMode==2 && s:HasMultipartDelims()))
+ call s:CommentRegion(firstLine, firstCol, lastLine, lastCol, forceNested)
+ else
+ call s:CommentLines(forceNested, "none", firstLine, lastLine)
+ endif
+
+ elseif a:type == 'alignLeft' || a:type == 'alignBoth'
+ let align = "none"
+ if a:type == "alignLeft"
+ let align = "left"
+ elseif a:type == "alignBoth"
+ let align = "both"
+ endif
+ call s:CommentLines(forceNested, align, firstLine, lastLine)
+
+ elseif a:type == 'invert'
+ call s:InvertComment(firstLine, lastLine)
+
+ elseif a:type == 'sexy'
+ try
+ call s:CommentLinesSexy(firstLine, lastLine)
+ catch /NERDCommenter.Delimiters/
+ call s:CommentLines(forceNested, "none", firstLine, lastLine)
+ catch /NERDCommenter.Nesting/
+ call s:NerdEcho("Sexy comment aborted. Nested sexy cannot be nested", 0)
+ endtry
+
+ elseif a:type == 'toggle'
+ let theLine = getline(firstLine)
+
+ if s:IsInSexyComment(firstLine) || s:IsCommentedFromStartOfLine(b:left, theLine) || s:IsCommentedFromStartOfLine(b:leftAlt, theLine)
+ call s:UncommentLines(firstLine, lastLine)
+ else
+ call s:CommentLinesToggle(forceNested, firstLine, lastLine)
+ endif
+
+ elseif a:type == 'minimal'
+ try
+ call s:CommentLinesMinimal(firstLine, lastLine)
+ catch /NERDCommenter.Delimiters/
+ call s:NerdEcho("Minimal comments can only be used for filetypes that have multipart delimiters.", 0)
+ catch /NERDCommenter.Settings/
+ call s:NerdEcho("Place holders are required but disabled.", 0)
+ endtry
+
+ elseif a:type == 'toEOL'
+ call s:SaveScreenState()
+ call s:CommentBlock(firstLine, firstLine, col("."), col("$")-1, 1)
+ call s:RestoreScreenState()
+
+ elseif a:type == 'append'
+ call s:AppendCommentToLine()
+
+ elseif a:type == 'insert'
+ call s:PlaceDelimitersAndInsBetween()
+
+ elseif a:type == 'uncomment'
+ call s:UncommentLines(firstLine, lastLine)
+
+ elseif a:type == 'yank'
+ if a:isVisual
+ normal! gvy
+ elseif countWasGiven
+ execute firstLine .','. lastLine .'yank'
+ else
+ normal! yy
+ endif
+ execute firstLine .','. lastLine .'call NERDComment('. a:isVisual .', "norm")'
+ endif
+
+ let &ignorecase = oldIgnoreCase
+endfunction
+
+" Function: s:PlaceDelimitersAndInsBetween() function {{{2
+" This is function is called to place comment delimiters down and place the
+" cursor between them
+function s:PlaceDelimitersAndInsBetween()
+ " get the left and right delimiters without any escape chars in them
+ let left = s:GetLeft(0, 1, 0)
+ let right = s:GetRight(0, 1, 0)
+
+ let theLine = getline(".")
+ let lineHasLeadTabs = s:HasLeadingTabs(theLine) || (theLine =~ '^ *$' && !&expandtab)
+
+ "convert tabs to spaces and adjust the cursors column to take this into
+ "account
+ let untabbedCol = s:UntabbedCol(theLine, col("."))
+ call setline(line("."), s:ConvertLeadingTabsToSpaces(theLine))
+ call cursor(line("."), untabbedCol)
+
+ " get the len of the right delim
+ let lenRight = strlen(right)
+
+ let isDelimOnEOL = col(".") >= strlen(getline("."))
+
+ " if the cursor is in the first col then we gotta insert rather than
+ " append the comment delimiters here
+ let insOrApp = (col(".")==1 ? 'i' : 'a')
+
+ " place the delimiters down. We do it differently depending on whether
+ " there is a left AND right delimiter
+ if lenRight > 0
+ execute ":normal! " . insOrApp . left . right
+ execute ":normal! " . lenRight . "h"
+ else
+ execute ":normal! " . insOrApp . left
+
+ " if we are tacking the delim on the EOL then we gotta add a space
+ " after it cos when we go out of insert mode the cursor will move back
+ " one and the user wont be in position to type the comment.
+ if isDelimOnEOL
+ execute 'normal! a '
+ endif
+ endif
+ normal! l
+
+ "if needed convert spaces back to tabs and adjust the cursors col
+ "accordingly
+ if lineHasLeadTabs
+ let tabbedCol = s:TabbedCol(getline("."), col("."))
+ call setline(line("."), s:ConvertLeadingSpacesToTabs(getline(".")))
+ call cursor(line("."), tabbedCol)
+ endif
+
+ startinsert
+endfunction
+
+" Function: s:RemoveDelimiters(left, right, line) {{{2
+" this function is called to remove the first left comment delimiter and the
+" last right delimiter of the given line.
+"
+" The args left and right must be strings. If there is no right delimiter (as
+" is the case for e.g vim file comments) them the arg right should be ""
+"
+" Args:
+" -left: the left comment delimiter
+" -right: the right comment delimiter
+" -line: the line to remove the delimiters from
+function s:RemoveDelimiters(left, right, line)
+
+ let l:left = a:left
+ let l:right = a:right
+ let lenLeft = strlen(left)
+ let lenRight = strlen(right)
+
+ let delimsSpaced = (g:NERDSpaceDelims || g:NERDRemoveExtraSpaces)
+
+ let line = a:line
+
+ "look for the left delimiter, if we find it, remove it.
+ let leftIndx = s:FindDelimiterIndex(a:left, line)
+ if leftIndx != -1
+ let line = strpart(line, 0, leftIndx) . strpart(line, leftIndx+lenLeft)
+
+ "if the user has specified that there is a space after the left delim
+ "then check for the space and remove it if it is there
+ if delimsSpaced && strpart(line, leftIndx, s:lenSpaceStr) == s:spaceStr
+ let line = strpart(line, 0, leftIndx) . strpart(line, leftIndx+s:lenSpaceStr)
+ endif
+ endif
+
+ "look for the right delimiter, if we find it, remove it
+ let rightIndx = s:FindDelimiterIndex(a:right, line)
+ if rightIndx != -1
+ let line = strpart(line, 0, rightIndx) . strpart(line, rightIndx+lenRight)
+
+ "if the user has specified that there is a space before the right delim
+ "then check for the space and remove it if it is there
+ if delimsSpaced && strpart(line, rightIndx-s:lenSpaceStr, s:lenSpaceStr) == s:spaceStr && s:Multipart()
+ let line = strpart(line, 0, rightIndx-s:lenSpaceStr) . strpart(line, rightIndx)
+ endif
+ endif
+
+ return line
+endfunction
+
+" Function: s:UncommentLines(topLine, bottomLine) {{{2
+" This function uncomments the given lines
+"
+" Args:
+" topLine: the top line of the visual selection to uncomment
+" bottomLine: the bottom line of the visual selection to uncomment
+function s:UncommentLines(topLine, bottomLine)
+ "make local copies of a:firstline and a:lastline and, if need be, swap
+ "them around if the top line is below the bottom
+ let l:firstline = a:topLine
+ let l:lastline = a:bottomLine
+ if firstline > lastline
+ let firstline = lastline
+ let lastline = a:topLine
+ endif
+
+ "go thru each line uncommenting each line removing sexy comments
+ let currentLine = firstline
+ while currentLine <= lastline
+
+ "check the current line to see if it is part of a sexy comment
+ let sexyComBounds = s:FindBoundingLinesOfSexyCom(currentLine)
+ if !empty(sexyComBounds)
+
+ "we need to store the num lines in the buf before the comment is
+ "removed so we know how many lines were removed when the sexy com
+ "was removed
+ let numLinesBeforeSexyComRemoved = s:NumLinesInBuf()
+
+ call s:UncommentLinesSexy(sexyComBounds[0], sexyComBounds[1])
+
+ "move to the line after last line of the sexy comment
+ let numLinesAfterSexyComRemoved = s:NumLinesInBuf()
+ let numLinesRemoved = numLinesBeforeSexyComRemoved - numLinesAfterSexyComRemoved
+ let currentLine = sexyComBounds[1] - numLinesRemoved + 1
+ let lastline = lastline - numLinesRemoved
+
+ "no sexy com was detected so uncomment the line as normal
+ else
+ call s:UncommentLinesNormal(currentLine, currentLine)
+ let currentLine = currentLine + 1
+ endif
+ endwhile
+
+endfunction
+
+" Function: s:UncommentLinesSexy(topline, bottomline) {{{2
+" This function removes all the comment characters associated with the sexy
+" comment spanning the given lines
+" Args:
+" -topline/bottomline: the top/bottom lines of the sexy comment
+function s:UncommentLinesSexy(topline, bottomline)
+ let left = s:GetSexyComLeft(0,1)
+ let right = s:GetSexyComRight(0,1)
+
+
+ "check if it is even possible for sexy comments to exist with the
+ "available delimiters
+ if left == -1 || right == -1
+ throw 'NERDCommenter.Delimiters exception: cannot uncomment sexy comments with available delimiters.'
+ endif
+
+ let leftUnEsc = s:GetSexyComLeft(0,0)
+ let rightUnEsc = s:GetSexyComRight(0,0)
+
+ let sexyComMarker = s:GetSexyComMarker(0, 1)
+ let sexyComMarkerUnEsc = s:GetSexyComMarker(0, 0)
+
+ "the markerOffset is how far right we need to move the sexyComMarker to
+ "line it up with the end of the left delim
+ let markerOffset = strlen(leftUnEsc)-strlen(sexyComMarkerUnEsc)
+
+ " go thru the intermediate lines of the sexy comment and remove the
+ " sexy comment markers (eg the '*'s on the start of line in a c sexy
+ " comment)
+ let currentLine = a:topline+1
+ while currentLine < a:bottomline
+ let theLine = getline(currentLine)
+
+ " remove the sexy comment marker from the line. We also remove the
+ " space after it if there is one and if appropriate options are set
+ let sexyComMarkerIndx = stridx(theLine, sexyComMarkerUnEsc)
+ if strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc), s:lenSpaceStr) == s:spaceStr && g:NERDSpaceDelims
+ let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc)+s:lenSpaceStr)
+ else
+ let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc))
+ endif
+
+ let theLine = s:SwapOutterPlaceHoldersForMultiPartDelims(theLine)
+
+ let theLine = s:ConvertLeadingWhiteSpace(theLine)
+
+ " move onto the next line
+ call setline(currentLine, theLine)
+ let currentLine = currentLine + 1
+ endwhile
+
+ " gotta make a copy of a:bottomline cos we modify the position of the
+ " last line it if we remove the topline
+ let bottomline = a:bottomline
+
+ " get the first line so we can remove the left delim from it
+ let theLine = getline(a:topline)
+
+ " if the first line contains only the left delim then just delete it
+ if theLine =~ '^[ \t]*' . left . '[ \t]*$' && !g:NERDCompactSexyComs
+ call cursor(a:topline, 1)
+ normal! dd
+ let bottomline = bottomline - 1
+
+ " topline contains more than just the left delim
+ else
+
+ " remove the delim. If there is a space after it
+ " then remove this too if appropriate
+ let delimIndx = stridx(theLine, leftUnEsc)
+ if strpart(theLine, delimIndx+strlen(leftUnEsc), s:lenSpaceStr) == s:spaceStr && g:NERDSpaceDelims
+ let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(leftUnEsc)+s:lenSpaceStr)
+ else
+ let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(leftUnEsc))
+ endif
+ let theLine = s:SwapOutterPlaceHoldersForMultiPartDelims(theLine)
+ call setline(a:topline, theLine)
+ endif
+
+ " get the last line so we can remove the right delim
+ let theLine = getline(bottomline)
+
+ " if the bottomline contains only the right delim then just delete it
+ if theLine =~ '^[ \t]*' . right . '[ \t]*$'
+ call cursor(bottomline, 1)
+ normal! dd
+
+ " the last line contains more than the right delim
+ else
+ " remove the right delim. If there is a space after it and
+ " if the appropriate options are set then remove this too.
+ let delimIndx = s:LastIndexOfDelim(rightUnEsc, theLine)
+ if strpart(theLine, delimIndx+strlen(leftUnEsc), s:lenSpaceStr) == s:spaceStr && g:NERDSpaceDelims
+ let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(rightUnEsc)+s:lenSpaceStr)
+ else
+ let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(rightUnEsc))
+ endif
+
+ " if the last line also starts with a sexy comment marker then we
+ " remove this as well
+ if theLine =~ '^[ \t]*' . sexyComMarker
+
+ " remove the sexyComMarker. If there is a space after it then
+ " remove that too
+ let sexyComMarkerIndx = stridx(theLine, sexyComMarkerUnEsc)
+ if strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc), s:lenSpaceStr) == s:spaceStr && g:NERDSpaceDelims
+ let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset ) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc)+s:lenSpaceStr)
+ else
+ let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset ) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc))
+ endif
+ endif
+
+ let theLine = s:SwapOutterPlaceHoldersForMultiPartDelims(theLine)
+ call setline(bottomline, theLine)
+ endif
+endfunction
+
+" Function: s:UncommentLineNormal(line) {{{2
+" uncomments the given line and returns the result
+" Args:
+" -line: the line to uncomment
+function s:UncommentLineNormal(line)
+ let line = a:line
+
+ "get the comment status on the line so we know how it is commented
+ let lineCommentStatus = s:IsCommentedOuttermost(b:left, b:right, b:leftAlt, b:rightAlt, line)
+
+ "it is commented with b:left and b:right so remove these delims
+ if lineCommentStatus == 1
+ let line = s:RemoveDelimiters(b:left, b:right, line)
+
+ "it is commented with b:leftAlt and b:rightAlt so remove these delims
+ elseif lineCommentStatus == 2 && g:NERDRemoveAltComs
+ let line = s:RemoveDelimiters(b:leftAlt, b:rightAlt, line)
+
+ "it is not properly commented with any delims so we check if it has
+ "any random left or right delims on it and remove the outtermost ones
+ else
+ "get the positions of all delim types on the line
+ let indxLeft = s:FindDelimiterIndex(b:left, line)
+ let indxLeftAlt = s:FindDelimiterIndex(b:leftAlt, line)
+ let indxRight = s:FindDelimiterIndex(b:right, line)
+ let indxRightAlt = s:FindDelimiterIndex(b:rightAlt, line)
+
+ "remove the outter most left comment delim
+ if indxLeft != -1 && (indxLeft < indxLeftAlt || indxLeftAlt == -1)
+ let line = s:RemoveDelimiters(b:left, '', line)
+ elseif indxLeftAlt != -1
+ let line = s:RemoveDelimiters(b:leftAlt, '', line)
+ endif
+
+ "remove the outter most right comment delim
+ if indxRight != -1 && (indxRight < indxRightAlt || indxRightAlt == -1)
+ let line = s:RemoveDelimiters('', b:right, line)
+ elseif indxRightAlt != -1
+ let line = s:RemoveDelimiters('', b:rightAlt, line)
+ endif
+ endif
+
+
+ let indxLeft = s:FindDelimiterIndex(b:left, line)
+ let indxLeftAlt = s:FindDelimiterIndex(b:leftAlt, line)
+ let indxLeftPlace = s:FindDelimiterIndex(g:NERDLPlace, line)
+
+ let indxRightPlace = s:FindDelimiterIndex(g:NERDRPlace, line)
+ let indxRightAlt = s:FindDelimiterIndex(b:rightAlt, line)
+ let indxRightPlace = s:FindDelimiterIndex(g:NERDRPlace, line)
+
+ let right = b:right
+ let left = b:left
+ if !s:Multipart()
+ let right = b:rightAlt
+ let left = b:leftAlt
+ endif
+
+
+ "if there are place-holders on the line then we check to see if they are
+ "the outtermost delimiters on the line. If so then we replace them with
+ "real delimiters
+ if indxLeftPlace != -1
+ if (indxLeftPlace < indxLeft || indxLeft==-1) && (indxLeftPlace < indxLeftAlt || indxLeftAlt==-1)
+ let line = s:ReplaceDelims(g:NERDLPlace, g:NERDRPlace, left, right, line)
+ endif
+ elseif indxRightPlace != -1
+ if (indxRightPlace < indxLeft || indxLeft==-1) && (indxLeftPlace < indxLeftAlt || indxLeftAlt==-1)
+ let line = s:ReplaceDelims(g:NERDLPlace, g:NERDRPlace, left, right, line)
+ endif
+
+ endif
+
+ let line = s:ConvertLeadingWhiteSpace(line)
+
+ return line
+endfunction
+
+" Function: s:UncommentLinesNormal(topline, bottomline) {{{2
+" This function is called to uncomment lines that arent a sexy comment
+" Args:
+" -topline/bottomline: the top/bottom line numbers of the comment
+function s:UncommentLinesNormal(topline, bottomline)
+ let currentLine = a:topline
+ while currentLine <= a:bottomline
+ let line = getline(currentLine)
+ call setline(currentLine, s:UncommentLineNormal(line))
+ let currentLine = currentLine + 1
+ endwhile
+endfunction
+
+
+" Section: Other helper functions {{{1
+" ============================================================================
+
+" Function: s:AddLeftDelim(delim, theLine) {{{2
+" Args:
+function s:AddLeftDelim(delim, theLine)
+ return substitute(a:theLine, '^\([ \t]*\)', '\1' . a:delim, '')
+endfunction
+
+" Function: s:AddLeftDelimAligned(delim, theLine) {{{2
+" Args:
+function s:AddLeftDelimAligned(delim, theLine, alignIndx)
+
+ "if the line is not long enough then bung some extra spaces on the front
+ "so we can align the delim properly
+ let theLine = a:theLine
+ if strlen(theLine) < a:alignIndx
+ let theLine = strpart(s:spaces, 0, a:alignIndx - strlen(theLine))
+ endif
+
+ return strpart(theLine, 0, a:alignIndx) . a:delim . strpart(theLine, a:alignIndx)
+endfunction
+
+" Function: s:AddRightDelim(delim, theLine) {{{2
+" Args:
+function s:AddRightDelim(delim, theLine)
+ if a:delim == ''
+ return a:theLine
+ else
+ return substitute(a:theLine, '$', a:delim, '')
+ endif
+endfunction
+
+" Function: s:AddRightDelimAligned(delim, theLine, alignIndx) {{{2
+" Args:
+function s:AddRightDelimAligned(delim, theLine, alignIndx)
+ if a:delim == ""
+ return a:theLine
+ else
+
+ " when we align the right delim we are just adding spaces
+ " so we get a string containing the needed spaces (it
+ " could be empty)
+ let extraSpaces = ''
+ let extraSpaces = strpart(s:spaces, 0, a:alignIndx-strlen(a:theLine))
+
+ " add the right delim
+ return substitute(a:theLine, '$', extraSpaces . a:delim, '')
+ endif
+endfunction
+
+" Function: s:AltMultipart() {{{2
+" returns 1 if the alternative delims are multipart
+function s:AltMultipart()
+ return b:rightAlt != ''
+endfunction
+
+" Function: s:CanCommentLine(forceNested, line) {{{2
+"This function is used to determine whether the given line can be commented.
+"It returns 1 if it can be and 0 otherwise
+"
+" Args:
+" -forceNested: a flag indicating whether the caller wants comments to be nested
+" if the current line is already commented
+" -lineNum: the line num of the line to check for commentability
+function s:CanCommentLine(forceNested, lineNum)
+ let theLine = getline(a:lineNum)
+
+ " make sure we don't comment lines that are just spaces or tabs or empty.
+ if theLine =~ "^[ \t]*$"
+ return 0
+ endif
+
+ "if the line is part of a sexy comment then just flag it...
+ if s:IsInSexyComment(a:lineNum)
+ return 0
+ endif
+
+ let isCommented = s:IsCommentedNormOrSexy(a:lineNum)
+
+ "if the line isnt commented return true
+ if !isCommented
+ return 1
+ endif
+
+ "if the line is commented but nesting is allowed then return true
+ if a:forceNested && (!s:Multipart() || g:NERDUsePlaceHolders)
+ return 1
+ endif
+
+ return 0
+endfunction
+
+" Function: s:CanPlaceCursor(line, col) {{{2
+" returns 1 if the cursor can be placed exactly in the given position
+function s:CanPlaceCursor(line, col)
+ let c = col(".")
+ let l = line(".")
+ call cursor(a:line, a:col)
+ let success = (line(".") == a:line && col(".") == a:col)
+ call cursor(l,c)
+ return success
+endfunction
+
+" Function: s:CanSexyCommentLines(topline, bottomline) {{{2
+" Return: 1 if the given lines can be commented sexually, 0 otherwise
+function s:CanSexyCommentLines(topline, bottomline)
+ " see if the selected regions have any sexy comments
+ let currentLine = a:topline
+ while(currentLine <= a:bottomline)
+ if s:IsInSexyComment(currentLine)
+ return 0
+ endif
+ let currentLine = currentLine + 1
+ endwhile
+ return 1
+endfunction
+" Function: s:CanToggleCommentLine(forceNested, line) {{{2
+"This function is used to determine whether the given line can be toggle commented.
+"It returns 1 if it can be and 0 otherwise
+"
+" Args:
+" -lineNum: the line num of the line to check for commentability
+function s:CanToggleCommentLine(forceNested, lineNum)
+ let theLine = getline(a:lineNum)
+ if (s:IsCommentedFromStartOfLine(b:left, theLine) || s:IsCommentedFromStartOfLine(b:leftAlt, theLine)) && !a:forceNested
+ return 0
+ endif
+
+ " make sure we don't comment lines that are just spaces or tabs or empty.
+ if theLine =~ "^[ \t]*$"
+ return 0
+ endif
+
+ "if the line is part of a sexy comment then just flag it...
+ if s:IsInSexyComment(a:lineNum)
+ return 0
+ endif
+
+ return 1
+endfunction
+
+" Function: s:ConvertLeadingSpacesToTabs(line) {{{2
+" This function takes a line and converts all leading tabs on that line into
+" spaces
+"
+" Args:
+" -line: the line whose leading tabs will be converted
+function s:ConvertLeadingSpacesToTabs(line)
+ let toReturn = a:line
+ while toReturn =~ '^\t*' . s:TabSpace() . '\(.*\)$'
+ let toReturn = substitute(toReturn, '^\(\t*\)' . s:TabSpace() . '\(.*\)$' , '\1\t\2' , "")
+ endwhile
+
+ return toReturn
+endfunction
+
+
+" Function: s:ConvertLeadingTabsToSpaces(line) {{{2
+" This function takes a line and converts all leading spaces on that line into
+" tabs
+"
+" Args:
+" -line: the line whose leading spaces will be converted
+function s:ConvertLeadingTabsToSpaces(line)
+ let toReturn = a:line
+ while toReturn =~ '^\( *\)\t'
+ let toReturn = substitute(toReturn, '^\( *\)\t', '\1' . s:TabSpace() , "")
+ endwhile
+
+ return toReturn
+endfunction
+
+" Function: s:ConvertLeadingWhiteSpace(line) {{{2
+" Converts the leading white space to tabs/spaces depending on &ts
+"
+" Args:
+" -line: the line to convert
+function s:ConvertLeadingWhiteSpace(line)
+ let toReturn = a:line
+ while toReturn =~ '^ *\t'
+ let toReturn = substitute(toReturn, '^ *\zs\t\ze', s:TabSpace(), "g")
+ endwhile
+
+ if !&expandtab
+ let toReturn = s:ConvertLeadingSpacesToTabs(toReturn)
+ endif
+
+ return toReturn
+endfunction
+
+
+" Function: s:CountNonESCedOccurances(str, searchstr, escChar) {{{2
+" This function counts the number of substrings contained in another string.
+" These substrings are only counted if they are not escaped with escChar
+" Args:
+" -str: the string to look for searchstr in
+" -searchstr: the substring to search for in str
+" -escChar: the escape character which, when preceding an instance of
+" searchstr, will cause it not to be counted
+function s:CountNonESCedOccurances(str, searchstr, escChar)
+ "get the index of the first occurrence of searchstr
+ let indx = stridx(a:str, a:searchstr)
+
+ "if there is an instance of searchstr in str process it
+ if indx != -1
+ "get the remainder of str after this instance of searchstr is removed
+ let lensearchstr = strlen(a:searchstr)
+ let strLeft = strpart(a:str, indx+lensearchstr)
+
+ "if this instance of searchstr is not escaped, add one to the count
+ "and recurse. If it is escaped, just recurse
+ if !s:IsEscaped(a:str, indx, a:escChar)
+ return 1 + s:CountNonESCedOccurances(strLeft, a:searchstr, a:escChar)
+ else
+ return s:CountNonESCedOccurances(strLeft, a:searchstr, a:escChar)
+ endif
+ endif
+endfunction
+" Function: s:DoesBlockHaveDelim(delim, top, bottom) {{{2
+" Returns 1 if the given block of lines has a delimiter (a:delim) in it
+" Args:
+" -delim: the comment delimiter to check the block for
+" -top: the top line number of the block
+" -bottom: the bottom line number of the block
+function s:DoesBlockHaveDelim(delim, top, bottom)
+ let currentLine = a:top
+ while currentLine < a:bottom
+ let theline = getline(currentLine)
+ if s:FindDelimiterIndex(a:delim, theline) != -1
+ return 1
+ endif
+ let currentLine = currentLine + 1
+ endwhile
+ return 0
+endfunction
+
+" Function: s:DoesBlockHaveMultipartDelim(top, bottom) {{{2
+" Returns 1 if the given block has a >= 1 multipart delimiter in it
+" Args:
+" -top: the top line number of the block
+" -bottom: the bottom line number of the block
+function s:DoesBlockHaveMultipartDelim(top, bottom)
+ if s:HasMultipartDelims()
+ if s:Multipart()
+ return s:DoesBlockHaveDelim(b:left, a:top, a:bottom) || s:DoesBlockHaveDelim(b:right, a:top, a:bottom)
+ else
+ return s:DoesBlockHaveDelim(b:leftAlt, a:top, a:bottom) || s:DoesBlockHaveDelim(b:rightAlt, a:top, a:bottom)
+ endif
+ endif
+ return 0
+endfunction
+
+
+" Function: s:Esc(str) {{{2
+" Escapes all the tricky chars in the given string
+function s:Esc(str)
+ let charsToEsc = '*/\."&$+'
+ return escape(a:str, charsToEsc)
+endfunction
+
+" Function: s:FindDelimiterIndex(delimiter, line) {{{2
+" This function is used to get the string index of the input comment delimiter
+" on the input line. If no valid comment delimiter is found in the line then
+" -1 is returned
+" Args:
+" -delimiter: the delimiter we are looking to find the index of
+" -line: the line we are looking for delimiter on
+function s:FindDelimiterIndex(delimiter, line)
+
+ "make sure the delimiter isnt empty otherwise we go into an infinite loop.
+ if a:delimiter == ""
+ return -1
+ endif
+
+
+ let l:delimiter = a:delimiter
+ let lenDel = strlen(l:delimiter)
+
+ "get the index of the first occurrence of the delimiter
+ let delIndx = stridx(a:line, l:delimiter)
+
+ "keep looping thru the line till we either find a real comment delimiter
+ "or run off the EOL
+ while delIndx != -1
+
+ "if we are not off the EOL get the str before the possible delimiter
+ "in question and check if it really is a delimiter. If it is, return
+ "its position
+ if delIndx != -1
+ if s:IsDelimValid(l:delimiter, delIndx, a:line)
+ return delIndx
+ endif
+ endif
+
+ "we have not yet found a real comment delimiter so move past the
+ "current one we are lookin at
+ let restOfLine = strpart(a:line, delIndx + lenDel)
+ let distToNextDelim = stridx(restOfLine , l:delimiter)
+
+ "if distToNextDelim is -1 then there is no more potential delimiters
+ "on the line so set delIndx to -1. Otherwise, move along the line by
+ "distToNextDelim
+ if distToNextDelim == -1
+ let delIndx = -1
+ else
+ let delIndx = delIndx + lenDel + distToNextDelim
+ endif
+ endwhile
+
+ "there is no comment delimiter on this line
+ return -1
+endfunction
+
+" Function: s:FindBoundingLinesOfSexyCom(lineNum) {{{2
+" This function takes in a line number and tests whether this line number is
+" the top/bottom/middle line of a sexy comment. If it is then the top/bottom
+" lines of the sexy comment are returned
+" Args:
+" -lineNum: the line number that is to be tested whether it is the
+" top/bottom/middle line of a sexy com
+" Returns:
+" A string that has the top/bottom lines of the sexy comment encoded in it.
+" The format is 'topline,bottomline'. If a:lineNum turns out not to be the
+" top/bottom/middle of a sexy comment then -1 is returned
+function s:FindBoundingLinesOfSexyCom(lineNum)
+
+ "find which delimiters to look for as the start/end delims of the comment
+ let left = ''
+ let right = ''
+ if s:Multipart()
+ let left = s:GetLeft(0,0,1)
+ let right = s:GetRight(0,0,1)
+ elseif s:AltMultipart()
+ let left = s:GetLeft(1,0,1)
+ let right = s:GetRight(1,0,1)
+ else
+ return []
+ endif
+
+ let sexyComMarker = s:GetSexyComMarker(0, 1)
+
+ "initialise the top/bottom line numbers of the sexy comment to -1
+ let top = -1
+ let bottom = -1
+
+ let currentLine = a:lineNum
+ while top == -1 || bottom == -1
+ let theLine = getline(currentLine)
+
+ "check if the current line is the top of the sexy comment
+ if currentLine <= a:lineNum && theLine =~ '^[ \t]*' . left && theLine !~ '.*' . right && currentLine < s:NumLinesInBuf()
+ let top = currentLine
+ let currentLine = a:lineNum
+
+ "check if the current line is the bottom of the sexy comment
+ elseif theLine =~ '^[ \t]*' . right && theLine !~ '.*' . left && currentLine > 1
+ let bottom = currentLine
+
+ "the right delimiter is on the same line as the last sexyComMarker
+ elseif theLine =~ '^[ \t]*' . sexyComMarker . '.*' . right
+ let bottom = currentLine
+
+ "we have not found the top or bottom line so we assume currentLine is an
+ "intermediate line and look to prove otherwise
+ else
+
+ "if the line doesnt start with a sexyComMarker then it is not a sexy
+ "comment
+ if theLine !~ '^[ \t]*' . sexyComMarker
+ return []
+ endif
+
+ endif
+
+ "if top is -1 then we havent found the top yet so keep looking up
+ if top == -1
+ let currentLine = currentLine - 1
+ "if we have found the top line then go down looking for the bottom
+ else
+ let currentLine = currentLine + 1
+ endif
+
+ endwhile
+
+ return [top, bottom]
+endfunction
+
+
+" Function: s:GetLeft(alt, space, esc) {{{2
+" returns the left/left-alternative delimiter
+" Args:
+" -alt: specifies whether to get left or left-alternative delim
+" -space: specifies whether the delim should be spaced or not
+" (the space string will only be added if NERDSpaceDelims is set)
+" -esc: specifies whether the tricky chars in the delim should be ESCed
+function s:GetLeft(alt, space, esc)
+ let delim = b:left
+
+ if a:alt
+ if b:leftAlt == ''
+ return ''
+ else
+ let delim = b:leftAlt
+ endif
+ endif
+ if delim == ''
+ return ''
+ endif
+
+ if a:space && g:NERDSpaceDelims
+ let delim = delim . s:spaceStr
+ endif
+
+ if a:esc
+ let delim = s:Esc(delim)
+ endif
+
+ return delim
+endfunction
+
+" Function: s:GetRight(alt, space, esc) {{{2
+" returns the right/right-alternative delimiter
+" Args:
+" -alt: specifies whether to get right or right-alternative delim
+" -space: specifies whether the delim should be spaced or not
+" (the space string will only be added if NERDSpaceDelims is set)
+" -esc: specifies whether the tricky chars in the delim should be ESCed
+function s:GetRight(alt, space, esc)
+ let delim = b:right
+
+ if a:alt
+ if !s:AltMultipart()
+ return ''
+ else
+ let delim = b:rightAlt
+ endif
+ endif
+ if delim == ''
+ return ''
+ endif
+
+ if a:space && g:NERDSpaceDelims
+ let delim = s:spaceStr . delim
+ endif
+
+ if a:esc
+ let delim = s:Esc(delim)
+ endif
+
+ return delim
+endfunction
+
+
+" Function: s:GetSexyComMarker() {{{2
+" Returns the sexy comment marker for the current filetype.
+"
+" C style sexy comments are assumed if possible. If not then the sexy comment
+" marker is the last char of the delimiter pair that has both left and right
+" delims and has the longest left delim
+"
+" Args:
+" -space: specifies whether the marker is to have a space string after it
+" (the space string will only be added if NERDSpaceDelims is set)
+" -esc: specifies whether the tricky chars in the marker are to be ESCed
+function s:GetSexyComMarker(space, esc)
+ let sexyComMarker = b:sexyComMarker
+
+ "if there is no hardcoded marker then we find one
+ if sexyComMarker == ''
+
+ "if the filetype has c style comments then use standard c sexy
+ "comments
+ if s:HasCStyleComments()
+ let sexyComMarker = '*'
+ else
+ "find a comment marker by getting the longest available left delim
+ "(that has a corresponding right delim) and taking the last char
+ let lenLeft = strlen(b:left)
+ let lenLeftAlt = strlen(b:leftAlt)
+ let left = ''
+ let right = ''
+ if s:Multipart() && lenLeft >= lenLeftAlt
+ let left = b:left
+ elseif s:AltMultipart()
+ let left = b:leftAlt
+ else
+ return -1
+ endif
+
+ "get the last char of left
+ let sexyComMarker = strpart(left, strlen(left)-1)
+ endif
+ endif
+
+ if a:space && g:NERDSpaceDelims
+ let sexyComMarker = sexyComMarker . s:spaceStr
+ endif
+
+ if a:esc
+ let sexyComMarker = s:Esc(sexyComMarker)
+ endif
+
+ return sexyComMarker
+endfunction
+
+" Function: s:GetSexyComLeft(space, esc) {{{2
+" Returns the left delimiter for sexy comments for this filetype or -1 if
+" there is none. C style sexy comments are used if possible
+" Args:
+" -space: specifies if the delim has a space string on the end
+" (the space string will only be added if NERDSpaceDelims is set)
+" -esc: specifies whether the tricky chars in the string are ESCed
+function s:GetSexyComLeft(space, esc)
+ let lenLeft = strlen(b:left)
+ let lenLeftAlt = strlen(b:leftAlt)
+ let left = ''
+
+ "assume c style sexy comments if possible
+ if s:HasCStyleComments()
+ let left = '/*'
+ else
+ "grab the longest left delim that has a right
+ if s:Multipart() && lenLeft >= lenLeftAlt
+ let left = b:left
+ elseif s:AltMultipart()
+ let left = b:leftAlt
+ else
+ return -1
+ endif
+ endif
+
+ if a:space && g:NERDSpaceDelims
+ let left = left . s:spaceStr
+ endif
+
+ if a:esc
+ let left = s:Esc(left)
+ endif
+
+ return left
+endfunction
+
+" Function: s:GetSexyComRight(space, esc) {{{2
+" Returns the right delimiter for sexy comments for this filetype or -1 if
+" there is none. C style sexy comments are used if possible.
+" Args:
+" -space: specifies if the delim has a space string on the start
+" (the space string will only be added if NERDSpaceDelims
+" is specified for the current filetype)
+" -esc: specifies whether the tricky chars in the string are ESCed
+function s:GetSexyComRight(space, esc)
+ let lenLeft = strlen(b:left)
+ let lenLeftAlt = strlen(b:leftAlt)
+ let right = ''
+
+ "assume c style sexy comments if possible
+ if s:HasCStyleComments()
+ let right = '*/'
+ else
+ "grab the right delim that pairs with the longest left delim
+ if s:Multipart() && lenLeft >= lenLeftAlt
+ let right = b:right
+ elseif s:AltMultipart()
+ let right = b:rightAlt
+ else
+ return -1
+ endif
+ endif
+
+ if a:space && g:NERDSpaceDelims
+ let right = s:spaceStr . right
+ endif
+
+ if a:esc
+ let right = s:Esc(right)
+ endif
+
+ return right
+endfunction
+
+" Function: s:HasMultipartDelims() {{{2
+" Returns 1 iff the current filetype has at least one set of multipart delims
+function s:HasMultipartDelims()
+ return s:Multipart() || s:AltMultipart()
+endfunction
+
+" Function: s:HasLeadingTabs(...) {{{2
+" Returns 1 if any of the given strings have leading tabs
+function s:HasLeadingTabs(...)
+ for s in a:000
+ if s =~ '^\t.*'
+ return 1
+ end
+ endfor
+ return 0
+endfunction
+" Function: s:HasCStyleComments() {{{2
+" Returns 1 iff the current filetype has c style comment delimiters
+function s:HasCStyleComments()
+ return (b:left == '/*' && b:right == '*/') || (b:leftAlt == '/*' && b:rightAlt == '*/')
+endfunction
+
+" Function: s:IsCommentedNormOrSexy(lineNum) {{{2
+"This function is used to determine whether the given line is commented with
+"either set of delimiters or if it is part of a sexy comment
+"
+" Args:
+" -lineNum: the line number of the line to check
+function s:IsCommentedNormOrSexy(lineNum)
+ let theLine = getline(a:lineNum)
+
+ "if the line is commented normally return 1
+ if s:IsCommented(b:left, b:right, theLine) || s:IsCommented(b:leftAlt, b:rightAlt, theLine)
+ return 1
+ endif
+
+ "if the line is part of a sexy comment return 1
+ if s:IsInSexyComment(a:lineNum)
+ return 1
+ endif
+ return 0
+endfunction
+
+" Function: s:IsCommented(left, right, line) {{{2
+"This function is used to determine whether the given line is commented with
+"the given delimiters
+"
+" Args:
+" -line: the line that to check if commented
+" -left/right: the left and right delimiters to check for
+function s:IsCommented(left, right, line)
+ "if the line isnt commented return true
+ if s:FindDelimiterIndex(a:left, a:line) != -1 && (s:FindDelimiterIndex(a:right, a:line) != -1 || !s:Multipart())
+ return 1
+ endif
+ return 0
+endfunction
+
+" Function: s:IsCommentedFromStartOfLine(left, line) {{{2
+"This function is used to determine whether the given line is commented with
+"the given delimiters at the start of the line i.e the left delimiter is the
+"first thing on the line (apart from spaces\tabs)
+"
+" Args:
+" -line: the line that to check if commented
+" -left: the left delimiter to check for
+function s:IsCommentedFromStartOfLine(left, line)
+ let theLine = s:ConvertLeadingTabsToSpaces(a:line)
+ let numSpaces = strlen(substitute(theLine, '^\( *\).*$', '\1', ''))
+ let delimIndx = s:FindDelimiterIndex(a:left, theLine)
+ return delimIndx == numSpaces
+endfunction
+
+" Function: s:IsCommentedOuttermost(left, right, leftAlt, rightAlt, line) {{{2
+" Finds the type of the outtermost delims on the line
+"
+" Args:
+" -line: the line that to check if the outtermost comments on it are
+" left/right
+" -left/right: the left and right delimiters to check for
+" -leftAlt/rightAlt: the left and right alternative delimiters to check for
+"
+" Returns:
+" 0 if the line is not commented with either set of delims
+" 1 if the line is commented with the left/right delim set
+" 2 if the line is commented with the leftAlt/rightAlt delim set
+function s:IsCommentedOuttermost(left, right, leftAlt, rightAlt, line)
+ "get the first positions of the left delims and the last positions of the
+ "right delims
+ let indxLeft = s:FindDelimiterIndex(a:left, a:line)
+ let indxLeftAlt = s:FindDelimiterIndex(a:leftAlt, a:line)
+ let indxRight = s:LastIndexOfDelim(a:right, a:line)
+ let indxRightAlt = s:LastIndexOfDelim(a:rightAlt, a:line)
+
+ "check if the line has a left delim before a leftAlt delim
+ if (indxLeft <= indxLeftAlt || indxLeftAlt == -1) && indxLeft != -1
+ "check if the line has a right delim after any rightAlt delim
+ if (indxRight > indxRightAlt && indxRight > indxLeft) || !s:Multipart()
+ return 1
+ endif
+
+ "check if the line has a leftAlt delim before a left delim
+ elseif (indxLeftAlt <= indxLeft || indxLeft == -1) && indxLeftAlt != -1
+ "check if the line has a rightAlt delim after any right delim
+ if (indxRightAlt > indxRight && indxRightAlt > indxLeftAlt) || !s:AltMultipart()
+ return 2
+ endif
+ else
+ return 0
+ endif
+
+ return 0
+
+endfunction
+
+
+" Function: s:IsDelimValid(delimiter, delIndx, line) {{{2
+" This function is responsible for determining whether a given instance of a
+" comment delimiter is a real delimiter or not. For example, in java the
+" // string is a comment delimiter but in the line:
+" System.out.println("//");
+" it does not count as a comment delimiter. This function is responsible for
+" distinguishing between such cases. It does so by applying a set of
+" heuristics that are not fool proof but should work most of the time.
+"
+" Args:
+" -delimiter: the delimiter we are validating
+" -delIndx: the position of delimiter in line
+" -line: the line that delimiter occurs in
+"
+" Returns:
+" 0 if the given delimiter is not a real delimiter (as far as we can tell) ,
+" 1 otherwise
+function s:IsDelimValid(delimiter, delIndx, line)
+ "get the delimiter without the escchars
+ let l:delimiter = a:delimiter
+
+ "get the strings before and after the delimiter
+ let preComStr = strpart(a:line, 0, a:delIndx)
+ let postComStr = strpart(a:line, a:delIndx+strlen(delimiter))
+
+ "to check if the delimiter is real, make sure it isnt preceded by
+ "an odd number of quotes and followed by the same (which would indicate
+ "that it is part of a string and therefore is not a comment)
+ if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, '"', "\\")) && !s:IsNumEven(s:CountNonESCedOccurances(postComStr, '"', "\\"))
+ return 0
+ endif
+ if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, "'", "\\")) && !s:IsNumEven(s:CountNonESCedOccurances(postComStr, "'", "\\"))
+ return 0
+ endif
+ if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, "`", "\\")) && !s:IsNumEven(s:CountNonESCedOccurances(postComStr, "`", "\\"))
+ return 0
+ endif
+
+
+ "if the comment delimiter is escaped, assume it isnt a real delimiter
+ if s:IsEscaped(a:line, a:delIndx, "\\")
+ return 0
+ endif
+
+ "vim comments are so fuckin stupid!! Why the hell do they have comment
+ "delimiters that are used elsewhere in the syntax?!?! We need to check
+ "some conditions especially for vim
+ if &filetype == "vim"
+ if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, '"', "\\"))
+ return 0
+ endif
+
+ "if the delimiter is on the very first char of the line or is the
+ "first non-tab/space char on the line then it is a valid comment delimiter
+ if a:delIndx == 0 || a:line =~ "^[ \t]\\{" . a:delIndx . "\\}\".*$"
+ return 1
+ endif
+
+ let numLeftParen =s:CountNonESCedOccurances(preComStr, "(", "\\")
+ let numRightParen =s:CountNonESCedOccurances(preComStr, ")", "\\")
+
+ "if the quote is inside brackets then assume it isnt a comment
+ if numLeftParen > numRightParen
+ return 0
+ endif
+
+ "if the line has an even num of unescaped "'s then we can assume that
+ "any given " is not a comment delimiter
+ if s:IsNumEven(s:CountNonESCedOccurances(a:line, "\"", "\\"))
+ return 0
+ endif
+ endif
+
+ return 1
+
+endfunction
+
+" Function: s:IsNumEven(num) {{{2
+" A small function the returns 1 if the input number is even and 0 otherwise
+" Args:
+" -num: the number to check
+function s:IsNumEven(num)
+ return (a:num % 2) == 0
+endfunction
+
+" Function: s:IsEscaped(str, indx, escChar) {{{2
+" This function takes a string, an index into that string and an esc char and
+" returns 1 if the char at the index is escaped (i.e if it is preceded by an
+" odd number of esc chars)
+" Args:
+" -str: the string to check
+" -indx: the index into str that we want to check
+" -escChar: the escape char the char at indx may be ESCed with
+function s:IsEscaped(str, indx, escChar)
+ "initialise numEscChars to 0 and look at the char before indx
+ let numEscChars = 0
+ let curIndx = a:indx-1
+
+ "keep going back thru str until we either reach the start of the str or
+ "run out of esc chars
+ while curIndx >= 0 && strpart(a:str, curIndx, 1) == a:escChar
+
+ "we have found another esc char so add one to the count and move left
+ "one char
+ let numEscChars = numEscChars + 1
+ let curIndx = curIndx - 1
+
+ endwhile
+
+ "if there is an odd num of esc chars directly before the char at indx then
+ "the char at indx is escaped
+ return !s:IsNumEven(numEscChars)
+endfunction
+
+" Function: s:IsInSexyComment(line) {{{2
+" returns 1 if the given line number is part of a sexy comment
+function s:IsInSexyComment(line)
+ return !empty(s:FindBoundingLinesOfSexyCom(a:line))
+endfunction
+
+" Function: s:IsSexyComment(topline, bottomline) {{{2
+" This function takes in 2 line numbers and returns 1 if the lines between and
+" including the given line numbers are a sexy comment. It returns 0 otherwise.
+" Args:
+" -topline: the line that the possible sexy comment starts on
+" -bottomline: the line that the possible sexy comment stops on
+function s:IsSexyComment(topline, bottomline)
+
+ "get the delim set that would be used for a sexy comment
+ let left = ''
+ let right = ''
+ if s:Multipart()
+ let left = b:left
+ let right = b:right
+ elseif s:AltMultipart()
+ let left = b:leftAlt
+ let right = b:rightAlt
+ else
+ return 0
+ endif
+
+ "swap the top and bottom line numbers around if need be
+ let topline = a:topline
+ let bottomline = a:bottomline
+ if bottomline < topline
+ topline = bottomline
+ bottomline = a:topline
+ endif
+
+ "if there is < 2 lines in the comment it cannot be sexy
+ if (bottomline - topline) <= 0
+ return 0
+ endif
+
+ "if the top line doesnt begin with a left delim then the comment isnt sexy
+ if getline(a:topline) !~ '^[ \t]*' . left
+ return 0
+ endif
+
+ "if there is a right delim on the top line then this isnt a sexy comment
+ if s:FindDelimiterIndex(right, getline(a:topline)) != -1
+ return 0
+ endif
+
+ "if there is a left delim on the bottom line then this isnt a sexy comment
+ if s:FindDelimiterIndex(left, getline(a:bottomline)) != -1
+ return 0
+ endif
+
+ "if the bottom line doesnt begin with a right delim then the comment isnt
+ "sexy
+ if getline(a:bottomline) !~ '^.*' . right . '$'
+ return 0
+ endif
+
+ let sexyComMarker = s:GetSexyComMarker(0, 1)
+
+ "check each of the intermediate lines to make sure they start with a
+ "sexyComMarker
+ let currentLine = a:topline+1
+ while currentLine < a:bottomline
+ let theLine = getline(currentLine)
+
+ if theLine !~ '^[ \t]*' . sexyComMarker
+ return 0
+ endif
+
+ "if there is a right delim in an intermediate line then the block isnt
+ "a sexy comment
+ if s:FindDelimiterIndex(right, theLine) != -1
+ return 0
+ endif
+
+ let currentLine = currentLine + 1
+ endwhile
+
+ "we have not found anything to suggest that this isnt a sexy comment so
+ return 1
+
+endfunction
+
+" Function: s:LastIndexOfDelim(delim, str) {{{2
+" This function takes a string and a delimiter and returns the last index of
+" that delimiter in string
+" Args:
+" -delim: the delimiter to look for
+" -str: the string to look for delim in
+function s:LastIndexOfDelim(delim, str)
+ let delim = a:delim
+ let lenDelim = strlen(delim)
+
+ "set index to the first occurrence of delim. If there is no occurrence then
+ "bail
+ let indx = s:FindDelimiterIndex(delim, a:str)
+ if indx == -1
+ return -1
+ endif
+
+ "keep moving to the next instance of delim in str till there is none left
+ while 1
+
+ "search for the next delim after the previous one
+ let searchStr = strpart(a:str, indx+lenDelim)
+ let indx2 = s:FindDelimiterIndex(delim, searchStr)
+
+ "if we find a delim update indx to record the position of it, if we
+ "dont find another delim then indx is the last one so break out of
+ "this loop
+ if indx2 != -1
+ let indx = indx + indx2 + lenDelim
+ else
+ break
+ endif
+ endwhile
+
+ return indx
+
+endfunction
+
+" Function: s:LeftMostIndx(countCommentedLines, countEmptyLines, topline, bottomline) {{{2
+" This function takes in 2 line numbers and returns the index of the left most
+" char (that is not a space or a tab) on all of these lines.
+" Args:
+" -countCommentedLines: 1 if lines that are commented are to be checked as
+" well. 0 otherwise
+" -countEmptyLines: 1 if empty lines are to be counted in the search
+" -topline: the top line to be checked
+" -bottomline: the bottom line to be checked
+function s:LeftMostIndx(countCommentedLines, countEmptyLines, topline, bottomline)
+
+ " declare the left most index as an extreme value
+ let leftMostIndx = 1000
+
+ " go thru the block line by line updating leftMostIndx
+ let currentLine = a:topline
+ while currentLine <= a:bottomline
+
+ " get the next line and if it is allowed to be commented, or is not
+ " commented, check it
+ let theLine = getline(currentLine)
+ if a:countEmptyLines || theLine !~ '^[ \t]*$'
+ if a:countCommentedLines || (!s:IsCommented(b:left, b:right, theLine) && !s:IsCommented(b:leftAlt, b:rightAlt, theLine))
+ " convert spaces to tabs and get the number of leading spaces for
+ " this line and update leftMostIndx if need be
+ let theLine = s:ConvertLeadingTabsToSpaces(theLine)
+ let leadSpaceOfLine = strlen( substitute(theLine, '\(^[ \t]*\).*$','\1','') )
+ if leadSpaceOfLine < leftMostIndx
+ let leftMostIndx = leadSpaceOfLine
+ endif
+ endif
+ endif
+
+ " move on to the next line
+ let currentLine = currentLine + 1
+ endwhile
+
+ if leftMostIndx == 1000
+ return 0
+ else
+ return leftMostIndx
+ endif
+endfunction
+
+" Function: s:Multipart() {{{2
+" returns 1 if the current delims are multipart
+function s:Multipart()
+ return b:right != ''
+endfunction
+
+" Function: s:NerdEcho(msg, typeOfMsg) {{{2
+" Args:
+" -msg: the message to echo
+" -typeOfMsg: 0 = warning message
+" 1 = normal message
+function s:NerdEcho(msg, typeOfMsg)
+ if a:typeOfMsg == 0
+ echohl WarningMsg
+ echo 'NERDCommenter:' . a:msg
+ echohl None
+ elseif a:typeOfMsg == 1
+ echo 'NERDCommenter:' . a:msg
+ endif
+endfunction
+
+" Function: s:NumberOfLeadingTabs(s) {{{2
+" returns the number of leading tabs in the given string
+function s:NumberOfLeadingTabs(s)
+ return strlen(substitute(a:s, '^\(\t*\).*$', '\1', ""))
+endfunction
+
+" Function: s:NumLinesInBuf() {{{2
+" Returns the number of lines in the current buffer
+function s:NumLinesInBuf()
+ return line('$')
+endfunction
+
+" Function: s:ReplaceDelims(toReplace1, toReplace2, replacor1, replacor2, str) {{{2
+" This function takes in a string, 2 delimiters in that string and 2 strings
+" to replace these delimiters with.
+"
+" Args:
+" -toReplace1: the first delimiter to replace
+" -toReplace2: the second delimiter to replace
+" -replacor1: the string to replace toReplace1 with
+" -replacor2: the string to replace toReplace2 with
+" -str: the string that the delimiters to be replaced are in
+function s:ReplaceDelims(toReplace1, toReplace2, replacor1, replacor2, str)
+ let line = s:ReplaceLeftMostDelim(a:toReplace1, a:replacor1, a:str)
+ let line = s:ReplaceRightMostDelim(a:toReplace2, a:replacor2, line)
+ return line
+endfunction
+
+" Function: s:ReplaceLeftMostDelim(toReplace, replacor, str) {{{2
+" This function takes a string and a delimiter and replaces the left most
+" occurrence of this delimiter in the string with a given string
+"
+" Args:
+" -toReplace: the delimiter in str that is to be replaced
+" -replacor: the string to replace toReplace with
+" -str: the string that contains toReplace
+function s:ReplaceLeftMostDelim(toReplace, replacor, str)
+ let toReplace = a:toReplace
+ let replacor = a:replacor
+ "get the left most occurrence of toReplace
+ let indxToReplace = s:FindDelimiterIndex(toReplace, a:str)
+
+ "if there IS an occurrence of toReplace in str then replace it and return
+ "the resulting string
+ if indxToReplace != -1
+ let line = strpart(a:str, 0, indxToReplace) . replacor . strpart(a:str, indxToReplace+strlen(toReplace))
+ return line
+ endif
+
+ return a:str
+endfunction
+
+" Function: s:ReplaceRightMostDelim(toReplace, replacor, str) {{{2
+" This function takes a string and a delimiter and replaces the right most
+" occurrence of this delimiter in the string with a given string
+"
+" Args:
+" -toReplace: the delimiter in str that is to be replaced
+" -replacor: the string to replace toReplace with
+" -str: the string that contains toReplace
+"
+function s:ReplaceRightMostDelim(toReplace, replacor, str)
+ let toReplace = a:toReplace
+ let replacor = a:replacor
+ let lenToReplace = strlen(toReplace)
+
+ "get the index of the last delim in str
+ let indxToReplace = s:LastIndexOfDelim(toReplace, a:str)
+
+ "if there IS a delimiter in str, replace it and return the result
+ let line = a:str
+ if indxToReplace != -1
+ let line = strpart(a:str, 0, indxToReplace) . replacor . strpart(a:str, indxToReplace+strlen(toReplace))
+ endif
+ return line
+endfunction
+
+"FUNCTION: s:RestoreScreenState() {{{2
+"
+"Sets the screen state back to what it was when s:SaveScreenState was last
+"called.
+"
+function s:RestoreScreenState()
+ if !exists("t:NERDComOldTopLine") || !exists("t:NERDComOldPos")
+ throw 'NERDCommenter exception: cannot restore screen'
+ endif
+
+ call cursor(t:NERDComOldTopLine, 0)
+ normal! zt
+ call setpos(".", t:NERDComOldPos)
+endfunction
+
+" Function: s:RightMostIndx(countCommentedLines, countEmptyLines, topline, bottomline) {{{2
+" This function takes in 2 line numbers and returns the index of the right most
+" char on all of these lines.
+" Args:
+" -countCommentedLines: 1 if lines that are commented are to be checked as
+" well. 0 otherwise
+" -countEmptyLines: 1 if empty lines are to be counted in the search
+" -topline: the top line to be checked
+" -bottomline: the bottom line to be checked
+function s:RightMostIndx(countCommentedLines, countEmptyLines, topline, bottomline)
+ let rightMostIndx = -1
+
+ " go thru the block line by line updating rightMostIndx
+ let currentLine = a:topline
+ while currentLine <= a:bottomline
+
+ " get the next line and see if it is commentable, otherwise it doesnt
+ " count
+ let theLine = getline(currentLine)
+ if a:countEmptyLines || theLine !~ '^[ \t]*$'
+
+ if a:countCommentedLines || (!s:IsCommented(b:left, b:right, theLine) && !s:IsCommented(b:leftAlt, b:rightAlt, theLine))
+
+ " update rightMostIndx if need be
+ let theLine = s:ConvertLeadingTabsToSpaces(theLine)
+ let lineLen = strlen(theLine)
+ if lineLen > rightMostIndx
+ let rightMostIndx = lineLen
+ endif
+ endif
+ endif
+
+ " move on to the next line
+ let currentLine = currentLine + 1
+ endwhile
+
+ return rightMostIndx
+endfunction
+
+"FUNCTION: s:SaveScreenState() {{{2
+"Saves the current cursor position in the current buffer and the window
+"scroll position
+function s:SaveScreenState()
+ let t:NERDComOldPos = getpos(".")
+ let t:NERDComOldTopLine = line("w0")
+endfunction
+
+" Function: s:SwapOutterMultiPartDelimsForPlaceHolders(line) {{{2
+" This function takes a line and swaps the outter most multi-part delims for
+" place holders
+" Args:
+" -line: the line to swap the delims in
+"
+function s:SwapOutterMultiPartDelimsForPlaceHolders(line)
+ " find out if the line is commented using normal delims and/or
+ " alternate ones
+ let isCommented = s:IsCommented(b:left, b:right, a:line)
+ let isCommentedAlt = s:IsCommented(b:leftAlt, b:rightAlt, a:line)
+
+ let line2 = a:line
+
+ "if the line is commented and there is a right delimiter, replace
+ "the delims with place-holders
+ if isCommented && s:Multipart()
+ let line2 = s:ReplaceDelims(b:left, b:right, g:NERDLPlace, g:NERDRPlace, a:line)
+
+ "similarly if the line is commented with the alternative
+ "delimiters
+ elseif isCommentedAlt && s:AltMultipart()
+ let line2 = s:ReplaceDelims(b:leftAlt, b:rightAlt, g:NERDLPlace, g:NERDRPlace, a:line)
+ endif
+
+ return line2
+endfunction
+
+" Function: s:SwapOutterPlaceHoldersForMultiPartDelims(line) {{{2
+" This function takes a line and swaps the outtermost place holders for
+" multi-part delims
+" Args:
+" -line: the line to swap the delims in
+"
+function s:SwapOutterPlaceHoldersForMultiPartDelims(line)
+ let left = ''
+ let right = ''
+ if s:Multipart()
+ let left = b:left
+ let right = b:right
+ elseif s:AltMultipart()
+ let left = b:leftAlt
+ let right = b:rightAlt
+ endif
+
+ let line = s:ReplaceDelims(g:NERDLPlace, g:NERDRPlace, left, right, a:line)
+ return line
+endfunction
+" Function: s:TabbedCol(line, col) {{{2
+" Gets the col number for given line and existing col number. The new col
+" number is the col number when all leading spaces are converted to tabs
+" Args:
+" -line:the line to get the rel col for
+" -col: the abs col
+function s:TabbedCol(line, col)
+ let lineTruncated = strpart(a:line, 0, a:col)
+ let lineSpacesToTabs = substitute(lineTruncated, s:TabSpace(), '\t', 'g')
+ return strlen(lineSpacesToTabs)
+endfunction
+"FUNCTION: s:TabSpace() {{{2
+"returns a string of spaces equal in length to &tabstop
+function s:TabSpace()
+ let tabSpace = ""
+ let spacesPerTab = &tabstop
+ while spacesPerTab > 0
+ let tabSpace = tabSpace . " "
+ let spacesPerTab = spacesPerTab - 1
+ endwhile
+ return tabSpace
+endfunction
+
+" Function: s:UnEsc(str, escChar) {{{2
+" This function removes all the escape chars from a string
+" Args:
+" -str: the string to remove esc chars from
+" -escChar: the escape char to be removed
+function s:UnEsc(str, escChar)
+ return substitute(a:str, a:escChar, "", "g")
+endfunction
+
+" Function: s:UntabbedCol(line, col) {{{2
+" Takes a line and a col and returns the absolute column of col taking into
+" account that a tab is worth 3 or 4 (or whatever) spaces.
+" Args:
+" -line:the line to get the abs col for
+" -col: the col that doesnt take into account tabs
+function s:UntabbedCol(line, col)
+ let lineTruncated = strpart(a:line, 0, a:col)
+ let lineTabsToSpaces = substitute(lineTruncated, '\t', s:TabSpace(), 'g')
+ return strlen(lineTabsToSpaces)
+endfunction
+" Section: Comment mapping setup {{{1
+" ===========================================================================
+
+" switch to/from alternative delimiters
+nnoremap <plug>NERDCommenterAltDelims :call <SID>SwitchToAlternativeDelimiters(1)<cr>
+
+" comment out lines
+nnoremap <silent> <plug>NERDCommenterComment :call NERDComment(0, "norm")<cr>
+vnoremap <silent> <plug>NERDCommenterComment <ESC>:call NERDComment(1, "norm")<cr>
+
+" toggle comments
+nnoremap <silent> <plug>NERDCommenterToggle :call NERDComment(0, "toggle")<cr>
+vnoremap <silent> <plug>NERDCommenterToggle <ESC>:call NERDComment(1, "toggle")<cr>
+
+" minimal comments
+nnoremap <silent> <plug>NERDCommenterMinimal :call NERDComment(0, "minimal")<cr>
+vnoremap <silent> <plug>NERDCommenterMinimal <ESC>:call NERDComment(1, "minimal")<cr>
+
+" sexy comments
+nnoremap <silent> <plug>NERDCommenterSexy :call NERDComment(0, "sexy")<CR>
+vnoremap <silent> <plug>NERDCommenterSexy <ESC>:call NERDComment(1, "sexy")<CR>
+
+" invert comments
+nnoremap <silent> <plug>NERDCommenterInvert :call NERDComment(0, "invert")<CR>
+vnoremap <silent> <plug>NERDCommenterInvert <ESC>:call NERDComment(1, "invert")<CR>
+
+" yank then comment
+nmap <silent> <plug>NERDCommenterYank :call NERDComment(0, "yank")<CR>
+vmap <silent> <plug>NERDCommenterYank <ESC>:call NERDComment(1, "yank")<CR>
+
+" left aligned comments
+nnoremap <silent> <plug>NERDCommenterAlignLeft :call NERDComment(0, "alignLeft")<cr>
+vnoremap <silent> <plug>NERDCommenterAlignLeft <ESC>:call NERDComment(1, "alignLeft")<cr>
+
+" left and right aligned comments
+nnoremap <silent> <plug>NERDCommenterAlignBoth :call NERDComment(0, "alignBoth")<cr>
+vnoremap <silent> <plug>NERDCommenterAlignBoth <ESC>:call NERDComment(1, "alignBoth")<cr>
+
+" nested comments
+nnoremap <silent> <plug>NERDCommenterNest :call NERDComment(0, "nested")<cr>
+vnoremap <silent> <plug>NERDCommenterNest <ESC>:call NERDComment(1, "nested")<cr>
+
+" uncomment
+nnoremap <silent> <plug>NERDCommenterUncomment :call NERDComment(0, "uncomment")<cr>
+vnoremap <silent> <plug>NERDCommenterUncomment :call NERDComment(1, "uncomment")<cr>
+
+" comment till the end of the line
+nnoremap <silent> <plug>NERDCommenterToEOL :call NERDComment(0, "toEOL")<cr>
+
+" append comments
+nmap <silent> <plug>NERDCommenterAppend :call NERDComment(0, "append")<cr>
+
+" insert comments
+inoremap <silent> <plug>NERDCommenterInInsert <SPACE><BS><ESC>:call NERDComment(0, "insert")<CR>
+
+
+function! s:CreateMaps(target, combo)
+ if !hasmapto(a:target, 'n')
+ exec 'nmap ' . a:combo . ' ' . a:target
+ endif
+
+ if !hasmapto(a:target, 'v')
+ exec 'vmap ' . a:combo . ' ' . a:target
+ endif
+endfunction
+
+if g:NERDCreateDefaultMappings
+ call s:CreateMaps('<plug>NERDCommenterComment', ',cc')
+ call s:CreateMaps('<plug>NERDCommenterToggle', ',c<space>')
+ call s:CreateMaps('<plug>NERDCommenterMinimal', ',cm')
+ call s:CreateMaps('<plug>NERDCommenterSexy', ',cs')
+ call s:CreateMaps('<plug>NERDCommenterInvert', ',ci')
+ call s:CreateMaps('<plug>NERDCommenterYank', ',cy')
+ call s:CreateMaps('<plug>NERDCommenterAlignLeft', ',cl')
+ call s:CreateMaps('<plug>NERDCommenterAlignBoth', ',cb')
+ call s:CreateMaps('<plug>NERDCommenterNest', ',cn')
+ call s:CreateMaps('<plug>NERDCommenterUncomment', ',cu')
+ call s:CreateMaps('<plug>NERDCommenterToEOL', ',c$')
+ call s:CreateMaps('<plug>NERDCommenterAppend', ',cA')
+
+ if !hasmapto('<plug>NERDCommenterAltDelims', 'n')
+ nmap ,ca <plug>NERDCommenterAltDelims
+ endif
+endif
+
+
+
+" Section: Menu item setup {{{1
+" ===========================================================================
+"check if the user wants the menu to be displayed
+if g:NERDMenuMode != 0
+
+ let menuRoot = ""
+ if g:NERDMenuMode == 1
+ let menuRoot = 'comment'
+ elseif g:NERDMenuMode == 2
+ let menuRoot = '&comment'
+ elseif g:NERDMenuMode == 3
+ let menuRoot = '&Plugin.&comment'
+ endif
+
+ function! s:CreateMenuItems(target, desc, root)
+ exec 'nmenu <silent> ' . a:root . '.' . a:desc . ' ' . a:target
+ exec 'vmenu <silent> ' . a:root . '.' . a:desc . ' ' . a:target
+ endfunction
+ call s:CreateMenuItems("<plug>NERDCommenterComment", 'Comment', menuRoot)
+ call s:CreateMenuItems("<plug>NERDCommenterToggle", 'Toggle', menuRoot)
+ call s:CreateMenuItems('<plug>NERDCommenterMinimal', 'Minimal', menuRoot)
+ call s:CreateMenuItems('<plug>NERDCommenterNest', 'Nested', menuRoot)
+ exec 'nmenu <silent> '. menuRoot .'.To\ EOL <plug>NERDCommenterToEOL'
+ call s:CreateMenuItems('<plug>NERDCommenterInvert', 'Invert', menuRoot)
+ call s:CreateMenuItems('<plug>NERDCommenterSexy', 'Sexy', menuRoot)
+ call s:CreateMenuItems('<plug>NERDCommenterYank', 'Yank\ then\ comment', menuRoot)
+ exec 'nmenu <silent> '. menuRoot .'.Append <plug>NERDCommenterAppend'
+ exec 'menu <silent> '. menuRoot .'.-Sep- :'
+ call s:CreateMenuItems('<plug>NERDCommenterAlignLeft', 'Left\ aligned', menuRoot)
+ call s:CreateMenuItems('<plug>NERDCommenterAlignBoth', 'Left\ and\ right\ aligned', menuRoot)
+ exec 'menu <silent> '. menuRoot .'.-Sep2- :'
+ call s:CreateMenuItems('<plug>NERDCommenterUncomment', 'Uncomment', menuRoot)
+ exec 'nmenu <silent> '. menuRoot .'.Switch\ Delimiters <plug>NERDCommenterAltDelims'
+ exec 'imenu <silent> '. menuRoot .'.Insert\ Comment\ Here <plug>NERDCommenterInInsert'
+ exec 'menu <silent> '. menuRoot .'.-Sep3- :'
+ exec 'menu <silent>'. menuRoot .'.Help :help NERDCommenterContents<CR>'
+endif
+" vim: set foldmethod=marker :
diff --git a/files/.vim/plugin/NERD_tree.vim b/files/.vim/plugin/NERD_tree.vim
new file mode 100755
index 0000000..96a38eb
--- /dev/null
+++ b/files/.vim/plugin/NERD_tree.vim
@@ -0,0 +1,3559 @@
+" ============================================================================
+" File: NERD_tree.vim
+" Description: vim global plugin that provides a nice tree explorer
+" Maintainer: Martin Grenfell <martin_grenfell at msn dot com>
+" Last Change: 29 October, 2008
+" License: This program is free software. It comes without any warranty,
+" to the extent permitted by applicable law. You can redistribute
+" it and/or modify it under the terms of the Do What The Fuck You
+" Want To Public License, Version 2, as published by Sam Hocevar.
+" See http://sam.zoy.org/wtfpl/COPYING for more details.
+"
+" ============================================================================
+let s:NERD_tree_version = '2.14.2'
+
+" SECTION: Script init stuff {{{1
+"============================================================
+if exists("loaded_nerd_tree")
+ finish
+endif
+if v:version < 700
+ echoerr "NERDTree: this plugin requires vim >= 7. DOWNLOAD IT! You'll thank me later!"
+ finish
+endif
+let loaded_nerd_tree = 1
+
+"for line continuation - i.e dont want C in &cpo
+let s:old_cpo = &cpo
+set cpo&vim
+
+"Function: s:initVariable() function {{{2
+"This function is used to initialise a given variable to a given value. The
+"variable is only initialised if it does not exist prior
+"
+"Args:
+"var: the name of the var to be initialised
+"value: the value to initialise var to
+"
+"Returns:
+"1 if the var is set, 0 otherwise
+function! s:initVariable(var, value)
+ if !exists(a:var)
+ exec 'let ' . a:var . ' = ' . "'" . a:value . "'"
+ return 1
+ endif
+ return 0
+endfunction
+
+"SECTION: Init variable calls and other random constants {{{2
+call s:initVariable("g:NERDChristmasTree", 1)
+call s:initVariable("g:NERDTreeAutoCenter", 1)
+call s:initVariable("g:NERDTreeAutoCenterThreshold", 3)
+call s:initVariable("g:NERDTreeCaseSensitiveSort", 0)
+call s:initVariable("g:NERDTreeChDirMode", 0)
+if !exists("g:NERDTreeIgnore")
+ let g:NERDTreeIgnore = ['\~$']
+endif
+call s:initVariable("g:NERDTreeHighlightCursorline", 1)
+call s:initVariable("g:NERDTreeBookmarksFile", expand('$HOME') . '/.NERDTreeBookmarks')
+call s:initVariable("g:NERDTreeMouseMode", 1)
+call s:initVariable("g:NERDTreeNotificationThreshold", 100)
+call s:initVariable("g:NERDTreeQuitOnOpen", 0)
+call s:initVariable("g:NERDTreeShowBookmarks", 0)
+call s:initVariable("g:NERDTreeShowFiles", 1)
+call s:initVariable("g:NERDTreeShowHidden", 0)
+call s:initVariable("g:NERDTreeShowLineNumbers", 0)
+call s:initVariable("g:NERDTreeSortDirs", 1)
+
+if !exists("g:NERDTreeSortOrder")
+ let g:NERDTreeSortOrder = ['\/$', '*', '\.swp$', '\.bak$', '\~$']
+else
+ "if there isnt a * in the sort sequence then add one
+ if count(g:NERDTreeSortOrder, '*') < 1
+ call add(g:NERDTreeSortOrder, '*')
+ endif
+endif
+
+"we need to use this number many times for sorting... so we calculate it only
+"once here
+let s:NERDTreeSortStarIndex = index(g:NERDTreeSortOrder, '*')
+
+call s:initVariable("g:NERDTreeWinPos", "left")
+call s:initVariable("g:NERDTreeWinSize", 31)
+
+let s:running_windows = has("win16") || has("win32") || has("win64")
+
+"init the shell commands that will be used to copy nodes, and remove dir trees
+"
+"Note: the space after the command is important
+if s:running_windows
+ call s:initVariable("g:NERDTreeRemoveDirCmd", 'rmdir /s /q ')
+else
+ call s:initVariable("g:NERDTreeRemoveDirCmd", 'rm -rf ')
+ call s:initVariable("g:NERDTreeCopyCmd", 'cp -r ')
+endif
+
+
+"SECTION: Init variable calls for key mappings {{{2
+call s:initVariable("g:NERDTreeMapActivateNode", "o")
+call s:initVariable("g:NERDTreeMapChangeRoot", "C")
+call s:initVariable("g:NERDTreeMapChdir", "cd")
+call s:initVariable("g:NERDTreeMapCloseChildren", "X")
+call s:initVariable("g:NERDTreeMapCloseDir", "x")
+call s:initVariable("g:NERDTreeMapDeleteBookmark", "D")
+call s:initVariable("g:NERDTreeMapExecute", "!")
+call s:initVariable("g:NERDTreeMapFilesystemMenu", "m")
+call s:initVariable("g:NERDTreeMapHelp", "?")
+call s:initVariable("g:NERDTreeMapJumpFirstChild", "K")
+call s:initVariable("g:NERDTreeMapJumpLastChild", "J")
+call s:initVariable("g:NERDTreeMapJumpNextSibling", "<C-j>")
+call s:initVariable("g:NERDTreeMapJumpParent", "p")
+call s:initVariable("g:NERDTreeMapJumpPrevSibling", "<C-k>")
+call s:initVariable("g:NERDTreeMapJumpRoot", "P")
+call s:initVariable("g:NERDTreeMapOpenExpl", "e")
+call s:initVariable("g:NERDTreeMapOpenInTab", "t")
+call s:initVariable("g:NERDTreeMapOpenInTabSilent", "T")
+call s:initVariable("g:NERDTreeMapOpenRecursively", "O")
+call s:initVariable("g:NERDTreeMapOpenSplit", "<tab>")
+call s:initVariable("g:NERDTreeMapPreview", "g" . NERDTreeMapActivateNode)
+call s:initVariable("g:NERDTreeMapPreviewSplit", "g" . NERDTreeMapOpenSplit)
+call s:initVariable("g:NERDTreeMapQuit", "q")
+call s:initVariable("g:NERDTreeMapRefresh", "r")
+call s:initVariable("g:NERDTreeMapRefreshRoot", "R")
+call s:initVariable("g:NERDTreeMapToggleBookmarks", "B")
+call s:initVariable("g:NERDTreeMapToggleFiles", "F")
+call s:initVariable("g:NERDTreeMapToggleFilters", "f")
+call s:initVariable("g:NERDTreeMapToggleHidden", "H")
+call s:initVariable("g:NERDTreeMapUpdir", "u")
+call s:initVariable("g:NERDTreeMapUpdirKeepOpen", "U")
+
+"SECTION: Script level variable declaration{{{2
+let s:escape_chars = " \\`\|\"#%&,?()\*^<>"
+let s:NERDTreeWinName = '_NERD_tree_'
+
+let s:tree_wid = 2
+let s:tree_markup_reg = '^[ `|]*[\-+~]'
+let s:tree_up_dir_line = '.. (up a dir)'
+
+let s:os_slash = '/'
+if s:running_windows
+ let s:os_slash = '\'
+endif
+
+" SECTION: Commands {{{1
+"============================================================
+"init the command that users start the nerd tree with
+command! -n=? -complete=dir NERDTree :call s:initNerdTree('<args>')
+command! -n=? -complete=dir NERDTreeToggle :call s:toggle('<args>')
+command! -n=0 NERDTreeClose :call s:closeTreeIfOpen()
+command! -n=1 -complete=customlist,s:completeBookmarks NERDTreeFromBookmark call s:initNerdTree('<args>')
+" SECTION: Auto commands {{{1
+"============================================================
+"Save the cursor position whenever we close the nerd tree
+exec "autocmd BufWinLeave *". s:NERDTreeWinName ." call <SID>saveScreenState()"
+"cache bookmarks when vim loads
+autocmd VimEnter * call s:Bookmark.CacheBookmarks(0)
+
+"SECTION: Classes {{{1
+"============================================================
+"CLASS: Bookmark {{{2
+"============================================================
+let s:Bookmark = {}
+" FUNCTION: Bookmark.AddBookmark(name, path) {{{3
+" Class method to add a new bookmark to the list, if a previous bookmark exists
+" with the same name, just update the path for that bookmark
+function! s:Bookmark.AddBookmark(name, path)
+ for i in s:Bookmark.Bookmarks()
+ if i.name == a:name
+ let i.path = a:path
+ return
+ endif
+ endfor
+ call add(s:Bookmark.Bookmarks(), s:Bookmark.New(a:name, a:path))
+ call s:Bookmark.Sort()
+endfunction
+" Function: Bookmark.Bookmarks() {{{3
+" Class method to get all bookmarks. Lazily initializes the bookmarks global
+" variable
+function! s:Bookmark.Bookmarks()
+ if !exists("g:NERDTreeBookmarks")
+ let g:NERDTreeBookmarks = []
+ endif
+ return g:NERDTreeBookmarks
+endfunction
+" Function: Bookmark.BookmarkExistsFor(name) {{{3
+" class method that returns 1 if a bookmark with the given name is found, 0
+" otherwise
+function! s:Bookmark.BookmarkExistsFor(name)
+ try
+ call s:Bookmark.BookmarkFor(a:name)
+ return 1
+ catch /NERDTree.BookmarkNotFound/
+ return 0
+ endtry
+endfunction
+" Function: Bookmark.BookmarkFor(name) {{{3
+" Class method to get the bookmark that has the given name. {} is return if no
+" bookmark is found
+function! s:Bookmark.BookmarkFor(name)
+ for i in s:Bookmark.Bookmarks()
+ if i.name == a:name
+ return i
+ endif
+ endfor
+ throw "NERDTree.BookmarkNotFound exception: no bookmark found for name: \"". a:name .'"'
+endfunction
+" Function: Bookmark.BookmarkNames() {{{3
+" Class method to return an array of all bookmark names
+function! s:Bookmark.BookmarkNames()
+ let names = []
+ for i in s:Bookmark.Bookmarks()
+ call add(names, i.name)
+ endfor
+ return names
+endfunction
+" FUNCTION: Bookmark.CacheBookmarks(silent) {{{3
+" Class method to read all bookmarks from the bookmarks file intialize
+" bookmark objects for each one.
+"
+" Args:
+" silent - dont echo an error msg if invalid bookmarks are found
+function! s:Bookmark.CacheBookmarks(silent)
+ if filereadable(g:NERDTreeBookmarksFile)
+ let g:NERDTreeBookmarks = []
+ let g:NERDTreeInvalidBookmarks = []
+ let bookmarkStrings = readfile(g:NERDTreeBookmarksFile)
+ let invalidBookmarksFound = 0
+ for i in bookmarkStrings
+
+ "ignore blank lines
+ if i != ''
+
+ let name = substitute(i, '^\(.\{-}\) .*$', '\1', '')
+ let path = substitute(i, '^.\{-} \(.*\)$', '\1', '')
+
+ try
+ let bookmark = s:Bookmark.New(name, s:Path.New(path))
+ call add(g:NERDTreeBookmarks, bookmark)
+ catch /NERDTree.Path.InvalidArguments/
+ call add(g:NERDTreeInvalidBookmarks, i)
+ let invalidBookmarksFound += 1
+ endtry
+ endif
+ endfor
+ if invalidBookmarksFound
+ call s:Bookmark.Write()
+ if !a:silent
+ call s:echo(invalidBookmarksFound . " invalid bookmarks were read. See :help NERDTreeInvalidBookmarks for info.")
+ endif
+ endif
+ call s:Bookmark.Sort()
+ endif
+endfunction
+" FUNCTION: Bookmark.compareTo(otherbookmark) {{{3
+" Compare these two bookmarks for sorting purposes
+function! s:Bookmark.compareTo(otherbookmark)
+ return a:otherbookmark.name < self.name
+endfunction
+" FUNCTION: Bookmark.ClearAll() {{{3
+" Class method to delete all bookmarks.
+function! s:Bookmark.ClearAll()
+ for i in s:Bookmark.Bookmarks()
+ call i.delete()
+ endfor
+ call s:Bookmark.Write()
+endfunction
+" FUNCTION: Bookmark.delete() {{{3
+" Delete this bookmark. If the node for this bookmark is under the current
+" root, then recache bookmarks for its Path object
+function! s:Bookmark.delete()
+ let node = {}
+ try
+ let node = self.getNode(1)
+ catch /NERDTree.BookmarkedNodeNotFound/
+ endtry
+ call remove(s:Bookmark.Bookmarks(), index(s:Bookmark.Bookmarks(), self))
+ if !empty(node)
+ call node.path.cacheDisplayString()
+ endif
+ call s:Bookmark.Write()
+endfunction
+" FUNCTION: Bookmark.getNode(searchFromAbsoluteRoot) {{{3
+" Gets the treenode for this bookmark
+"
+" Args:
+" searchFromAbsoluteRoot: specifies whether we should search from the current
+" tree root, or the highest cached node
+function! s:Bookmark.getNode(searchFromAbsoluteRoot)
+ let searchRoot = a:searchFromAbsoluteRoot ? s:TreeDirNode.AbsoluteTreeRoot() : t:NERDTreeRoot
+ let targetNode = searchRoot.findNode(self.path)
+ if empty(targetNode)
+ throw "NERDTree.BookmarkedNodeNotFound no node was found for bookmark: " . self.name
+ endif
+ return targetNode
+endfunction
+" FUNCTION: Bookmark.GetNodeForName(name, searchFromAbsoluteRoot) {{{3
+" Class method that finds the bookmark with the given name and returns the
+" treenode for it.
+function! s:Bookmark.GetNodeForName(name, searchFromAbsoluteRoot)
+ let bookmark = s:Bookmark.BookmarkFor(a:name)
+ return bookmark.getNode(a:searchFromAbsoluteRoot)
+endfunction
+" Function: Bookmark.InvalidBookmarks() {{{3
+" Class method to get all invalid bookmark strings read from the bookmarks
+" file
+function! s:Bookmark.InvalidBookmarks()
+ if !exists("g:NERDTreeInvalidBookmarks")
+ let g:NERDTreeInvalidBookmarks = []
+ endif
+ return g:NERDTreeInvalidBookmarks
+endfunction
+" FUNCTION: Bookmark.mustExist() {{{3
+function! s:Bookmark.mustExist()
+ if !self.path.exists()
+ call s:Bookmark.CacheBookmarks(1)
+ throw "NERDTree.BookmarkPointsToInvalidLocation exception: the bookmark \"".
+ \ self.name ."\" points to a non existing location: \"". self.path.strForOS(0)
+ endif
+endfunction
+" FUNCTION: Bookmark.New(name, path) {{{3
+" Create a new bookmark object with the given name and path object
+function! s:Bookmark.New(name, path)
+ if a:name =~ ' '
+ throw "NERDTree.IllegalBookmarkName illegal name:" . a:name
+ endif
+
+ let newBookmark = copy(self)
+ let newBookmark.name = a:name
+ let newBookmark.path = a:path
+ return newBookmark
+endfunction
+" Function: Bookmark.setPath(path) {{{3
+" makes this bookmark point to the given path
+function! s:Bookmark.setPath(path)
+ let self.path = a:path
+endfunction
+" Function: Bookmark.Sort() {{{3
+" Class method that sorts all bookmarks
+function! s:Bookmark.Sort()
+ let CompareFunc = function("s:compareBookmarks")
+ call sort(s:Bookmark.Bookmarks(), CompareFunc)
+endfunction
+" Function: Bookmark.str() {{{3
+" Get the string that should be rendered in the view for this bookmark
+function! s:Bookmark.str()
+ let pathStrMaxLen = winwidth(s:getTreeWinNum()) - 4 - len(self.name)
+ if &nu
+ let pathStrMaxLen = pathStrMaxLen - &numberwidth
+ endif
+
+ let pathStr = self.path.strForOS(0)
+ if len(pathStr) > pathStrMaxLen
+ let pathStr = '<' . strpart(pathStr, len(pathStr) - pathStrMaxLen)
+ endif
+ return '>' . self.name . ' ' . pathStr
+endfunction
+" FUNCTION: Bookmark.toRoot() {{{3
+" Make the node for this bookmark the new tree root
+function! s:Bookmark.toRoot()
+ if self.validate()
+ try
+ let targetNode = self.getNode(1)
+ catch /NERDTree.BookmarkedNodeNotFound/
+ let targetNode = s:TreeFileNode.New(s:Bookmark.BookmarkFor(self.name).path)
+ endtry
+ call targetNode.makeRoot()
+ call s:renderView()
+ call s:putCursorOnNode(targetNode, 0, 0)
+ endif
+endfunction
+" FUNCTION: Bookmark.ToRoot(name) {{{3
+" Make the node for this bookmark the new tree root
+function! s:Bookmark.ToRoot(name)
+ let bookmark = s:Bookmark.BookmarkFor(a:name)
+ call bookmark.toRoot()
+endfunction
+
+
+"FUNCTION: Bookmark.validate() {{{3
+function! s:Bookmark.validate()
+ if self.path.exists()
+ return 1
+ else
+ call s:Bookmark.CacheBookmarks(1)
+ call s:renderView()
+ call s:echo(self.name . "now points to an invalid location. See :help NERDTreeInvalidBookmarks for info.")
+ return 0
+ endif
+endfunction
+
+" Function: Bookmark.Write() {{{3
+" Class method to write all bookmarks to the bookmarks file
+function! s:Bookmark.Write()
+ let bookmarkStrings = []
+ for i in s:Bookmark.Bookmarks()
+ call add(bookmarkStrings, i.name . ' ' . i.path.strForOS(0))
+ endfor
+
+ "add a blank line before the invalid ones
+ call add(bookmarkStrings, "")
+
+ for j in s:Bookmark.InvalidBookmarks()
+ call add(bookmarkStrings, j)
+ endfor
+ call writefile(bookmarkStrings, g:NERDTreeBookmarksFile)
+endfunction
+"CLASS: TreeFileNode {{{2
+"This class is the parent of the TreeDirNode class and constitures the
+"'Component' part of the composite design pattern between the treenode
+"classes.
+"============================================================
+let s:TreeFileNode = {}
+"FUNCTION: TreeFileNode.bookmark(name) {{{3
+"bookmark this node with a:name
+function! s:TreeFileNode.bookmark(name)
+ try
+ let oldMarkedNode = s:Bookmark.GetNodeForName(a:name, 1)
+ call oldMarkedNode.path.cacheDisplayString()
+ catch /NERDTree.Bookmark\(DoesntExist\|NotFound\)/
+ endtry
+
+ call s:Bookmark.AddBookmark(a:name, self.path)
+ call self.path.cacheDisplayString()
+ call s:Bookmark.Write()
+endfunction
+"FUNCTION: TreeFileNode.cacheParent() {{{3
+"initializes self.parent if it isnt already
+function! s:TreeFileNode.cacheParent()
+ if empty(self.parent)
+ let parentPath = self.path.getParent()
+ if parentPath.equals(self.path)
+ throw "NERDTree.CannotCacheParent exception: already at root"
+ endif
+ let self.parent = s:TreeFileNode.New(parentPath)
+ endif
+endfunction
+"FUNCTION: TreeFileNode.compareNodes {{{3
+"This is supposed to be a class level method but i cant figure out how to
+"get func refs to work from a dict..
+"
+"A class level method that compares two nodes
+"
+"Args:
+"n1, n2: the 2 nodes to compare
+function! s:compareNodes(n1, n2)
+ return a:n1.path.compareTo(a:n2.path)
+endfunction
+
+"FUNCTION: TreeFileNode.clearBoomarks() {{{3
+function! s:TreeFileNode.clearBoomarks()
+ for i in s:Bookmark.Bookmarks()
+ if i.path.equals(self.path)
+ call i.delete()
+ end
+ endfor
+ call self.path.cacheDisplayString()
+endfunction
+"FUNCTION: TreeFileNode.copy(dest) {{{3
+function! s:TreeFileNode.copy(dest)
+ call self.path.copy(a:dest)
+ let newPath = s:Path.New(a:dest)
+ let parent = t:NERDTreeRoot.findNode(newPath.getParent())
+ if !empty(parent)
+ call parent.refresh()
+ endif
+ return parent.findNode(newPath)
+endfunction
+
+"FUNCTION: TreeFileNode.delete {{{3
+"Removes this node from the tree and calls the Delete method for its path obj
+function! s:TreeFileNode.delete()
+ call self.path.delete()
+ call self.parent.removeChild(self)
+endfunction
+
+"FUNCTION: TreeFileNode.equals(treenode) {{{3
+"
+"Compares this treenode to the input treenode and returns 1 if they are the
+"same node.
+"
+"Use this method instead of == because sometimes when the treenodes contain
+"many children, vim seg faults when doing ==
+"
+"Args:
+"treenode: the other treenode to compare to
+function! s:TreeFileNode.equals(treenode)
+ return self.path.str(1) == a:treenode.path.str(1)
+endfunction
+
+"FUNCTION: TreeFileNode.findNode(path) {{{3
+"Returns self if this node.path.Equals the given path.
+"Returns {} if not equal.
+"
+"Args:
+"path: the path object to compare against
+function! s:TreeFileNode.findNode(path)
+ if a:path.equals(self.path)
+ return self
+ endif
+ return {}
+endfunction
+"FUNCTION: TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) {{{3
+"
+"Finds the next sibling for this node in the indicated direction. This sibling
+"must be a directory and may/may not have children as specified.
+"
+"Args:
+"direction: 0 if you want to find the previous sibling, 1 for the next sibling
+"
+"Return:
+"a treenode object or {} if no appropriate sibling could be found
+function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction)
+ "if we have no parent then we can have no siblings
+ if self.parent != {}
+ let nextSibling = self.findSibling(a:direction)
+
+ while nextSibling != {}
+ if nextSibling.path.isDirectory && nextSibling.hasVisibleChildren() && nextSibling.isOpen
+ return nextSibling
+ endif
+ let nextSibling = nextSibling.findSibling(a:direction)
+ endwhile
+ endif
+
+ return {}
+endfunction
+"FUNCTION: TreeFileNode.findSibling(direction) {{{3
+"
+"Finds the next sibling for this node in the indicated direction
+"
+"Args:
+"direction: 0 if you want to find the previous sibling, 1 for the next sibling
+"
+"Return:
+"a treenode object or {} if no sibling could be found
+function! s:TreeFileNode.findSibling(direction)
+ "if we have no parent then we can have no siblings
+ if self.parent != {}
+
+ "get the index of this node in its parents children
+ let siblingIndx = self.parent.getChildIndex(self.path)
+
+ if siblingIndx != -1
+ "move a long to the next potential sibling node
+ let siblingIndx = a:direction == 1 ? siblingIndx+1 : siblingIndx-1
+
+ "keep moving along to the next sibling till we find one that is valid
+ let numSiblings = self.parent.getChildCount()
+ while siblingIndx >= 0 && siblingIndx < numSiblings
+
+ "if the next node is not an ignored node (i.e. wont show up in the
+ "view) then return it
+ if self.parent.children[siblingIndx].path.ignore() == 0
+ return self.parent.children[siblingIndx]
+ endif
+
+ "go to next node
+ let siblingIndx = a:direction == 1 ? siblingIndx+1 : siblingIndx-1
+ endwhile
+ endif
+ endif
+
+ return {}
+endfunction
+
+"FUNCTION: TreeFileNode.isVisible() {{{3
+"returns 1 if this node should be visible according to the tree filters and
+"hidden file filters (and their on/off status)
+function! s:TreeFileNode.isVisible()
+ return !self.path.ignore()
+endfunction
+
+
+"FUNCTION: TreeFileNode.isRoot() {{{3
+"returns 1 if this node is t:NERDTreeRoot
+function! s:TreeFileNode.isRoot()
+ if !s:treeExistsForTab()
+ throw "NERDTree.TreeFileNode.IsRoot exception: No tree exists for the current tab"
+ endif
+ return self.equals(t:NERDTreeRoot)
+endfunction
+
+"FUNCTION: TreeFileNode.makeRoot() {{{3
+"Make this node the root of the tree
+function! s:TreeFileNode.makeRoot()
+ if self.path.isDirectory
+ let t:NERDTreeRoot = self
+ else
+ call self.cacheParent()
+ let t:NERDTreeRoot = self.parent
+ endif
+
+ call t:NERDTreeRoot.open()
+
+ "change dir to the dir of the new root if instructed to
+ if g:NERDTreeChDirMode == 2
+ exec "cd " . t:NERDTreeRoot.path.strForEditCmd()
+ endif
+endfunction
+"FUNCTION: TreeFileNode.New(path) {{{3
+"Returns a new TreeNode object with the given path and parent
+"
+"Args:
+"path: a path object representing the full filesystem path to the file/dir that the node represents
+function! s:TreeFileNode.New(path)
+ if a:path.isDirectory
+ return s:TreeDirNode.New(a:path)
+ else
+ let newTreeNode = {}
+ let newTreeNode = copy(self)
+ let newTreeNode.path = a:path
+ let newTreeNode.parent = {}
+ return newTreeNode
+ endif
+endfunction
+
+"FUNCTION: TreeFileNode.refresh() {{{3
+function! s:TreeFileNode.refresh()
+ call self.path.refresh()
+endfunction
+"FUNCTION: TreeFileNode.rename() {{{3
+"Calls the rename method for this nodes path obj
+function! s:TreeFileNode.rename(newName)
+ let newName = substitute(a:newName, '\(\\\|\/\)$', '', '')
+ call self.path.rename(newName)
+ call self.parent.removeChild(self)
+
+ let parentPath = self.path.getPathTrunk()
+ let newParent = t:NERDTreeRoot.findNode(parentPath)
+
+ if newParent != {}
+ call newParent.createChild(self.path, 1)
+ call newParent.refresh()
+ endif
+endfunction
+"FUNCTION: TreeFileNode.strDisplay() {{{3
+"
+"Returns a string that specifies how the node should be represented as a
+"string
+"
+"Return:
+"a string that can be used in the view to represent this node
+function! s:TreeFileNode.strDisplay()
+ return self.path.strDisplay()
+endfunction
+
+"CLASS: TreeDirNode {{{2
+"This class is a child of the TreeFileNode class and constitutes the
+"'Composite' part of the composite design pattern between the treenode
+"classes.
+"============================================================
+let s:TreeDirNode = copy(s:TreeFileNode)
+"FUNCTION: TreeDirNode.AbsoluteTreeRoot(){{{3
+"class method that returns the highest cached ancestor of the current root
+function! s:TreeDirNode.AbsoluteTreeRoot()
+ let currentNode = t:NERDTreeRoot
+ while currentNode.parent != {}
+ let currentNode = currentNode.parent
+ endwhile
+ return currentNode
+endfunction
+"FUNCTION: TreeDirNode.addChild(treenode, inOrder) {{{3
+"Adds the given treenode to the list of children for this node
+"
+"Args:
+"-treenode: the node to add
+"-inOrder: 1 if the new node should be inserted in sorted order
+function! s:TreeDirNode.addChild(treenode, inOrder)
+ call add(self.children, a:treenode)
+ let a:treenode.parent = self
+
+ if a:inOrder
+ call self.sortChildren()
+ endif
+endfunction
+
+"FUNCTION: TreeDirNode.close() {{{3
+"Closes this directory
+function! s:TreeDirNode.close()
+ let self.isOpen = 0
+endfunction
+
+"FUNCTION: TreeDirNode.closeChildren() {{{3
+"Closes all the child dir nodes of this node
+function! s:TreeDirNode.closeChildren()
+ for i in self.children
+ if i.path.isDirectory
+ call i.close()
+ call i.closeChildren()
+ endif
+ endfor
+endfunction
+
+"FUNCTION: TreeDirNode.createChild(path, inOrder) {{{3
+"Instantiates a new child node for this node with the given path. The new
+"nodes parent is set to this node.
+"
+"Args:
+"path: a Path object that this node will represent/contain
+"inOrder: 1 if the new node should be inserted in sorted order
+"
+"Returns:
+"the newly created node
+function! s:TreeDirNode.createChild(path, inOrder)
+ let newTreeNode = s:TreeFileNode.New(a:path)
+ call self.addChild(newTreeNode, a:inOrder)
+ return newTreeNode
+endfunction
+
+"FUNCTION: TreeDirNode.findNode(path) {{{3
+"Will find one of the children (recursively) that has the given path
+"
+"Args:
+"path: a path object
+unlet s:TreeDirNode.findNode
+function! s:TreeDirNode.findNode(path)
+ if a:path.equals(self.path)
+ return self
+ endif
+ if stridx(a:path.str(1), self.path.str(1), 0) == -1
+ return {}
+ endif
+
+ if self.path.isDirectory
+ for i in self.children
+ let retVal = i.findNode(a:path)
+ if retVal != {}
+ return retVal
+ endif
+ endfor
+ endif
+ return {}
+endfunction
+"FUNCTION: TreeDirNode.getChildCount() {{{3
+"Returns the number of children this node has
+function! s:TreeDirNode.getChildCount()
+ return len(self.children)
+endfunction
+
+"FUNCTION: TreeDirNode.getChild(path) {{{3
+"Returns child node of this node that has the given path or {} if no such node
+"exists.
+"
+"This function doesnt not recurse into child dir nodes
+"
+"Args:
+"path: a path object
+function! s:TreeDirNode.getChild(path)
+ if stridx(a:path.str(1), self.path.str(1), 0) == -1
+ return {}
+ endif
+
+ let index = self.getChildIndex(a:path)
+ if index == -1
+ return {}
+ else
+ return self.children[index]
+ endif
+
+endfunction
+
+"FUNCTION: TreeDirNode.getChildByIndex(indx, visible) {{{3
+"returns the child at the given index
+"Args:
+"indx: the index to get the child from
+"visible: 1 if only the visible children array should be used, 0 if all the
+"children should be searched.
+function! s:TreeDirNode.getChildByIndex(indx, visible)
+ let array_to_search = a:visible? self.getVisibleChildren() : self.children
+ if a:indx > len(array_to_search)
+ throw "NERDTree.TreeDirNode.InvalidArguments exception. Index is out of bounds."
+ endif
+ return array_to_search[a:indx]
+endfunction
+
+"FUNCTION: TreeDirNode.getChildIndex(path) {{{3
+"Returns the index of the child node of this node that has the given path or
+"-1 if no such node exists.
+"
+"This function doesnt not recurse into child dir nodes
+"
+"Args:
+"path: a path object
+function! s:TreeDirNode.getChildIndex(path)
+ if stridx(a:path.str(1), self.path.str(1), 0) == -1
+ return -1
+ endif
+
+ "do a binary search for the child
+ let a = 0
+ let z = self.getChildCount()
+ while a < z
+ let mid = (a+z)/2
+ let diff = a:path.compareTo(self.children[mid].path)
+
+ if diff == -1
+ let z = mid
+ elseif diff == 1
+ let a = mid+1
+ else
+ return mid
+ endif
+ endwhile
+ return -1
+endfunction
+
+"FUNCTION: TreeDirNode.getVisibleChildCount() {{{3
+"Returns the number of visible children this node has
+function! s:TreeDirNode.getVisibleChildCount()
+ return len(self.getVisibleChildren())
+endfunction
+
+"FUNCTION: TreeDirNode.getVisibleChildren() {{{3
+"Returns a list of children to display for this node, in the correct order
+"
+"Return:
+"an array of treenodes
+function! s:TreeDirNode.getVisibleChildren()
+ let toReturn = []
+ for i in self.children
+ if i.path.ignore() == 0
+ call add(toReturn, i)
+ endif
+ endfor
+ return toReturn
+endfunction
+
+"FUNCTION: TreeDirNode.hasVisibleChildren() {{{3
+"returns 1 if this node has any childre, 0 otherwise..
+function! s:TreeDirNode.hasVisibleChildren()
+ return self.getVisibleChildCount() != 0
+endfunction
+
+"FUNCTION: TreeDirNode._initChildren() {{{3
+"Removes all childen from this node and re-reads them
+"
+"Args:
+"silent: 1 if the function should not echo any "please wait" messages for
+"large directories
+"
+"Return: the number of child nodes read
+function! s:TreeDirNode._initChildren(silent)
+ "remove all the current child nodes
+ let self.children = []
+
+ "get an array of all the files in the nodes dir
+ let dir = self.path
+ let filesStr = globpath(dir.strForGlob(), '*') . "\n" . globpath(dir.strForGlob(), '.*')
+ let files = split(filesStr, "\n")
+
+ if !a:silent && len(files) > g:NERDTreeNotificationThreshold
+ call s:echo("Please wait, caching a large dir ...")
+ endif
+
+ let invalidFilesFound = 0
+ for i in files
+
+ "filter out the .. and . directories
+ "Note: we must match .. AND ../ cos sometimes the globpath returns
+ "../ for path with strange chars (eg $)
+ if i !~ '\.\.\/\?$' && i !~ '\.\/\?$'
+
+ "put the next file in a new node and attach it
+ try
+ let path = s:Path.New(i)
+ call self.createChild(path, 0)
+ catch /^NERDTree.Path.\(InvalidArguments\|InvalidFiletype\)/
+ let invalidFilesFound += 1
+ endtry
+ endif
+ endfor
+
+ call self.sortChildren()
+
+ if !a:silent && len(files) > g:NERDTreeNotificationThreshold
+ call s:echo("Please wait, caching a large dir ... DONE (". self.getChildCount() ." nodes cached).")
+ endif
+
+ if invalidFilesFound
+ call s:echoWarning(invalidFilesFound . " file(s) could not be loaded into the NERD tree")
+ endif
+ return self.getChildCount()
+endfunction
+"FUNCTION: TreeDirNode.New(path) {{{3
+"Returns a new TreeNode object with the given path and parent
+"
+"Args:
+"path: a path object representing the full filesystem path to the file/dir that the node represents
+unlet s:TreeDirNode.New
+function! s:TreeDirNode.New(path)
+ if a:path.isDirectory != 1
+ throw "NERDTree.TreeDirNode.InvalidArguments exception. A TreeDirNode object must be instantiated with a directory Path object."
+ endif
+
+ let newTreeNode = copy(self)
+ let newTreeNode.path = a:path
+
+ let newTreeNode.isOpen = 0
+ let newTreeNode.children = []
+
+ let newTreeNode.parent = {}
+
+ return newTreeNode
+endfunction
+"FUNCTION: TreeDirNode.open() {{{3
+"Reads in all this nodes children
+"
+"Return: the number of child nodes read
+function! s:TreeDirNode.open()
+ let self.isOpen = 1
+ if self.children == []
+ return self._initChildren(0)
+ else
+ return 0
+ endif
+endfunction
+
+"FUNCTION: TreeDirNode.openRecursively() {{{3
+"Opens this treenode and all of its children whose paths arent 'ignored'
+"because of the file filters.
+"
+"This method is actually a wrapper for the OpenRecursively2 method which does
+"the work.
+function! s:TreeDirNode.openRecursively()
+ call self._openRecursively2(1)
+endfunction
+
+"FUNCTION: TreeDirNode._openRecursively2() {{{3
+"Opens this all children of this treenode recursively if either:
+" *they arent filtered by file filters
+" *a:forceOpen is 1
+"
+"Args:
+"forceOpen: 1 if this node should be opened regardless of file filters
+function! s:TreeDirNode._openRecursively2(forceOpen)
+ if self.path.ignore() == 0 || a:forceOpen
+ let self.isOpen = 1
+ if self.children == []
+ call self._initChildren(1)
+ endif
+
+ for i in self.children
+ if i.path.isDirectory == 1
+ call i._openRecursively2(0)
+ endif
+ endfor
+ endif
+endfunction
+
+"FUNCTION: TreeDirNode.refresh() {{{3
+unlet s:TreeDirNode.refresh
+function! s:TreeDirNode.refresh()
+ call self.path.refresh()
+
+ "if this node was ever opened, refresh its children
+ if self.isOpen || !empty(self.children)
+ "go thru all the files/dirs under this node
+ let newChildNodes = []
+ let invalidFilesFound = 0
+ let dir = self.path
+ let filesStr = globpath(dir.strForGlob(), '*') . "\n" . globpath(dir.strForGlob(), '.*')
+ let files = split(filesStr, "\n")
+ for i in files
+ if i !~ '\.\.$' && i !~ '\.$'
+
+ try
+ "create a new path and see if it exists in this nodes children
+ let path = s:Path.New(i)
+ let newNode = self.getChild(path)
+ if newNode != {}
+ call newNode.refresh()
+ call add(newChildNodes, newNode)
+
+ "the node doesnt exist so create it
+ else
+ let newNode = s:TreeFileNode.New(path)
+ let newNode.parent = self
+ call add(newChildNodes, newNode)
+ endif
+
+
+ catch /^NERDTree.InvalidArguments/
+ let invalidFilesFound = 1
+ endtry
+ endif
+ endfor
+
+ "swap this nodes children out for the children we just read/refreshed
+ let self.children = newChildNodes
+ call self.sortChildren()
+
+ if invalidFilesFound
+ call s:echoWarning("some files could not be loaded into the NERD tree")
+ endif
+ endif
+endfunction
+
+"FUNCTION: TreeDirNode.removeChild(treenode) {{{3
+"
+"Removes the given treenode from this nodes set of children
+"
+"Args:
+"treenode: the node to remove
+"
+"Throws a NERDTree.TreeDirNode exception if the given treenode is not found
+function! s:TreeDirNode.removeChild(treenode)
+ for i in range(0, self.getChildCount()-1)
+ if self.children[i].equals(a:treenode)
+ call remove(self.children, i)
+ return
+ endif
+ endfor
+
+ throw "NERDTree.TreeDirNode exception: child node was not found"
+endfunction
+
+"FUNCTION: TreeDirNode.sortChildren() {{{3
+"
+"Sorts the children of this node according to alphabetical order and the
+"directory priority.
+"
+function! s:TreeDirNode.sortChildren()
+ let CompareFunc = function("s:compareNodes")
+ call sort(self.children, CompareFunc)
+endfunction
+
+"FUNCTION: TreeDirNode.toggleOpen() {{{3
+"Opens this directory if it is closed and vice versa
+function! s:TreeDirNode.toggleOpen()
+ if self.isOpen == 1
+ call self.close()
+ else
+ call self.open()
+ endif
+endfunction
+
+"FUNCTION: TreeDirNode.transplantChild(newNode) {{{3
+"Replaces the child of this with the given node (where the child node's full
+"path matches a:newNode's fullpath). The search for the matching node is
+"non-recursive
+"
+"Arg:
+"newNode: the node to graft into the tree
+function! s:TreeDirNode.transplantChild(newNode)
+ for i in range(0, self.getChildCount()-1)
+ if self.children[i].equals(a:newNode)
+ let self.children[i] = a:newNode
+ let a:newNode.parent = self
+ break
+ endif
+ endfor
+endfunction
+"============================================================
+"CLASS: Path {{{2
+"============================================================
+let s:Path = {}
+"FUNCTION: Path.bookmarkNames() {{{3
+function! s:Path.bookmarkNames()
+ if !exists("self._bookmarkNames")
+ call self.cacheDisplayString()
+ endif
+ return self._bookmarkNames
+endfunction
+"FUNCTION: Path.cacheDisplayString() {{{3
+function! s:Path.cacheDisplayString()
+ let self.cachedDisplayString = self.getLastPathComponent(1)
+
+ if self.isExecutable
+ let self.cachedDisplayString = self.cachedDisplayString . '*'
+ endif
+
+ let self._bookmarkNames = []
+ for i in s:Bookmark.Bookmarks()
+ if i.path.equals(self)
+ call add(self._bookmarkNames, i.name)
+ endif
+ endfor
+ if !empty(self._bookmarkNames)
+ let self.cachedDisplayString .= ' {' . join(self._bookmarkNames) . '}'
+ endif
+
+ if self.isSymLink
+ let self.cachedDisplayString .= ' -> ' . self.symLinkDest
+ endif
+
+ if self.isReadOnly
+ let self.cachedDisplayString .= ' [RO]'
+ endif
+endfunction
+"FUNCTION: Path.changeToDir() {{{3
+function! s:Path.changeToDir()
+ let dir = self.strForCd()
+ if self.isDirectory == 0
+ let dir = self.getPathTrunk().strForCd()
+ endif
+
+ try
+ execute "cd " . dir
+ call s:echo("CWD is now: " . getcwd())
+ catch
+ throw "NERDTree.Path.Change exception: cannot change to " . dir
+ endtry
+endfunction
+
+"FUNCTION: Path.compareTo() {{{3
+"
+"Compares this Path to the given path and returns 0 if they are equal, -1 if
+"this Path is "less than" the given path, or 1 if it is "greater".
+"
+"Args:
+"path: the path object to compare this to
+"
+"Return:
+"1, -1 or 0
+function! s:Path.compareTo(path)
+ let thisPath = self.getLastPathComponent(1)
+ let thatPath = a:path.getLastPathComponent(1)
+
+ "if the paths are the same then clearly we return 0
+ if thisPath == thatPath
+ return 0
+ endif
+
+ let thisSS = self.getSortOrderIndex()
+ let thatSS = a:path.getSortOrderIndex()
+
+ "compare the sort sequences, if they are different then the return
+ "value is easy
+ if thisSS < thatSS
+ return -1
+ elseif thisSS > thatSS
+ return 1
+ else
+ "if the sort sequences are the same then compare the paths
+ "alphabetically
+ let pathCompare = g:NERDTreeCaseSensitiveSort ? thisPath <# thatPath : thisPath <? thatPath
+ if pathCompare
+ return -1
+ else
+ return 1
+ endif
+ endif
+endfunction
+
+"FUNCTION: Path.Create(fullpath) {{{3
+"
+"Factory method.
+"
+"Creates a path object with the given path. The path is also created on the
+"filesystem. If the path already exists, a NERDTree.Path.Exists exception is
+"thrown. If any other errors occur, a NERDTree.Path exception is thrown.
+"
+"Args:
+"fullpath: the full filesystem path to the file/dir to create
+function! s:Path.Create(fullpath)
+ "bail if the a:fullpath already exists
+ if isdirectory(a:fullpath) || filereadable(a:fullpath)
+ throw "NERDTree.Path.Exists Exception: Directory Exists: '" . a:fullpath . "'"
+ endif
+
+ try
+
+ "if it ends with a slash, assume its a dir create it
+ if a:fullpath =~ '\(\\\|\/\)$'
+ "whack the trailing slash off the end if it exists
+ let fullpath = substitute(a:fullpath, '\(\\\|\/\)$', '', '')
+
+ call mkdir(fullpath, 'p')
+
+ "assume its a file and create
+ else
+ call writefile([], a:fullpath)
+ endif
+ catch /.*/
+ throw "NERDTree.Path Exception: Could not create path: '" . a:fullpath . "'"
+ endtry
+
+ return s:Path.New(a:fullpath)
+endfunction
+
+"FUNCTION: Path.copy(dest) {{{3
+"
+"Copies the file/dir represented by this Path to the given location
+"
+"Args:
+"dest: the location to copy this dir/file to
+function! s:Path.copy(dest)
+ if !s:Path.CopyingSupported()
+ throw "NERDTree.Path.CopyingNotSupported Exception: Copying is not supported on this OS"
+ endif
+
+ let dest = s:Path.WinToUnixPath(a:dest)
+
+ let cmd = g:NERDTreeCopyCmd . " " . self.strForOS(0) . " " . dest
+ let success = system(cmd)
+ if success != 0
+ throw "NERDTree.Path Exception: Could not copy ''". self.strForOS(0) ."'' to: '" . a:dest . "'"
+ endif
+endfunction
+
+"FUNCTION: Path.CopyingSupported() {{{3
+"
+"returns 1 if copying is supported for this OS
+function! s:Path.CopyingSupported()
+ return exists('g:NERDTreeCopyCmd')
+endfunction
+
+
+"FUNCTION: Path.copyingWillOverwrite(dest) {{{3
+"
+"returns 1 if copy this path to the given location will cause files to
+"overwritten
+"
+"Args:
+"dest: the location this path will be copied to
+function! s:Path.copyingWillOverwrite(dest)
+ if filereadable(a:dest)
+ return 1
+ endif
+
+ if isdirectory(a:dest)
+ let path = s:Path.JoinPathStrings(a:dest, self.getLastPathComponent(0))
+ if filereadable(path)
+ return 1
+ endif
+ endif
+endfunction
+
+"FUNCTION: Path.delete() {{{3
+"
+"Deletes the file represented by this path.
+"Deletion of directories is not supported
+"
+"Throws NERDTree.Path.Deletion exceptions
+function! s:Path.delete()
+ if self.isDirectory
+
+ let cmd = ""
+ if s:running_windows
+ "if we are runnnig windows then put quotes around the pathstring
+ let cmd = g:NERDTreeRemoveDirCmd . self.strForOS(1)
+ else
+ let cmd = g:NERDTreeRemoveDirCmd . self.strForOS(1)
+ endif
+ let success = system(cmd)
+
+ if v:shell_error != 0
+ throw "NERDTree.Path.Deletion Exception: Could not delete directory: '" . self.strForOS(0) . "'"
+ endif
+ else
+ let success = delete(self.strForOS(0))
+ if success != 0
+ throw "NERDTree.Path.Deletion Exception: Could not delete file: '" . self.str(0) . "'"
+ endif
+ endif
+
+ "delete all bookmarks for this path
+ for i in self.bookmarkNames()
+ let bookmark = s:Bookmark.BookmarkFor(i)
+ call bookmark.delete()
+ endfor
+endfunction
+
+"FUNCTION: Path.extractDriveLetter(fullpath) {{{3
+"
+"If running windows, cache the drive letter for this path
+function! s:Path.extractDriveLetter(fullpath)
+ if s:running_windows
+ let self.drive = substitute(a:fullpath, '\(^[a-zA-Z]:\).*', '\1', '')
+ else
+ let self.drive = ''
+ endif
+
+endfunction
+"FUNCTION: Path.exists() {{{3
+"return 1 if this path points to a location that is readable or is a directory
+function! s:Path.exists()
+ return filereadable(self.strForOS(0)) || isdirectory(self.strForOS(0))
+endfunction
+"FUNCTION: Path.getDir() {{{3
+"
+"Returns this path if it is a directory, else this paths parent.
+"
+"Return:
+"a Path object
+function! s:Path.getDir()
+ if self.isDirectory
+ return self
+ else
+ return self.getParent()
+ endif
+endfunction
+"FUNCTION: Path.getParent() {{{3
+"
+"Returns a new path object for this paths parent
+"
+"Return:
+"a new Path object
+function! s:Path.getParent()
+ let path = '/'. join(self.pathSegments[0:-2], '/')
+ return s:Path.New(path)
+endfunction
+"FUNCTION: Path.getLastPathComponent(dirSlash) {{{3
+"
+"Gets the last part of this path.
+"
+"Args:
+"dirSlash: if 1 then a trailing slash will be added to the returned value for
+"directory nodes.
+function! s:Path.getLastPathComponent(dirSlash)
+ if empty(self.pathSegments)
+ return ''
+ endif
+ let toReturn = self.pathSegments[-1]
+ if a:dirSlash && self.isDirectory
+ let toReturn = toReturn . '/'
+ endif
+ return toReturn
+endfunction
+
+"FUNCTION: Path.getPathTrunk() {{{3
+"Gets the path without the last segment on the end.
+function! s:Path.getPathTrunk()
+ return s:Path.New(self.strTrunk())
+endfunction
+
+"FUNCTION: Path.getSortOrderIndex() {{{3
+"returns the index of the pattern in g:NERDTreeSortOrder that this path matches
+function! s:Path.getSortOrderIndex()
+ let i = 0
+ while i < len(g:NERDTreeSortOrder)
+ if self.getLastPathComponent(1) =~ g:NERDTreeSortOrder[i]
+ return i
+ endif
+ let i = i + 1
+ endwhile
+ return s:NERDTreeSortStarIndex
+endfunction
+
+"FUNCTION: Path.ignore() {{{3
+"returns true if this path should be ignored
+function! s:Path.ignore()
+ let lastPathComponent = self.getLastPathComponent(0)
+
+ "filter out the user specified paths to ignore
+ if t:NERDTreeIgnoreEnabled
+ for i in g:NERDTreeIgnore
+ if lastPathComponent =~ i
+ return 1
+ endif
+ endfor
+ endif
+
+ "dont show hidden files unless instructed to
+ if t:NERDTreeShowHidden == 0 && lastPathComponent =~ '^\.'
+ return 1
+ endif
+
+ if t:NERDTreeShowFiles == 0 && self.isDirectory == 0
+ return 1
+ endif
+
+ return 0
+endfunction
+
+"FUNCTION: Path.JoinPathStrings(...) {{{3
+function! s:Path.JoinPathStrings(...)
+ let components = []
+ for i in a:000
+ let components = extend(components, split(i, '/'))
+ endfor
+ return '/' . join(components, '/')
+endfunction
+
+"FUNCTION: Path.equals() {{{3
+"
+"Determines whether 2 path objects are "equal".
+"They are equal if the paths they represent are the same
+"
+"Args:
+"path: the other path obj to compare this with
+function! s:Path.equals(path)
+ return self.str(0) == a:path.str(0)
+endfunction
+
+"FUNCTION: Path.New() {{{3
+"
+"The Constructor for the Path object
+"Throws NERDTree.Path.InvalidArguments exception.
+function! s:Path.New(fullpath)
+ let newPath = copy(self)
+
+ call newPath.readInfoFromDisk(a:fullpath)
+
+ let newPath.cachedDisplayString = ""
+
+ return newPath
+endfunction
+
+"FUNCTION: Path.readInfoFromDisk(fullpath) {{{3
+"
+"
+"Throws NERDTree.Path.InvalidArguments exception.
+function! s:Path.readInfoFromDisk(fullpath)
+ call self.extractDriveLetter(a:fullpath)
+
+ let fullpath = s:Path.WinToUnixPath(a:fullpath)
+
+ if getftype(fullpath) == "fifo"
+ throw "NERDTree.Path.InvalidFiletype Exception: Cant handle FIFO files: " . a:fullpath
+ endif
+
+ let self.pathSegments = split(fullpath, '/')
+
+
+ let self.isReadOnly = 0
+ if isdirectory(a:fullpath)
+ let self.isDirectory = 1
+ elseif filereadable(a:fullpath)
+ let self.isDirectory = 0
+ let self.isReadOnly = filewritable(a:fullpath) == 0
+ else
+ throw "NERDTree.Path.InvalidArguments Exception: Invalid path = " . a:fullpath
+ endif
+
+ let self.isExecutable = 0
+ if !self.isDirectory
+ let self.isExecutable = getfperm(a:fullpath) =~ 'x'
+ endif
+
+ "grab the last part of the path (minus the trailing slash)
+ let lastPathComponent = self.getLastPathComponent(0)
+
+ "get the path to the new node with the parent dir fully resolved
+ let hardPath = resolve(self.strTrunk()) . '/' . lastPathComponent
+
+ "if the last part of the path is a symlink then flag it as such
+ let self.isSymLink = (resolve(hardPath) != hardPath)
+ if self.isSymLink
+ let self.symLinkDest = resolve(fullpath)
+
+ "if the link is a dir then slap a / on the end of its dest
+ if isdirectory(self.symLinkDest)
+
+ "we always wanna treat MS windows shortcuts as files for
+ "simplicity
+ if hardPath !~ '\.lnk$'
+
+ let self.symLinkDest = self.symLinkDest . '/'
+ endif
+ endif
+ endif
+endfunction
+
+"FUNCTION: Path.refresh() {{{3
+function! s:Path.refresh()
+ call self.readInfoFromDisk(self.strForOS(0))
+ call self.cacheDisplayString()
+endfunction
+
+"FUNCTION: Path.rename() {{{3
+"
+"Renames this node on the filesystem
+function! s:Path.rename(newPath)
+ if a:newPath == ''
+ throw "NERDTree.Path.InvalidArguments exception. Invalid newPath for renaming = ". a:newPath
+ endif
+
+ let success = rename(self.strForOS(0), a:newPath)
+ if success != 0
+ throw "NERDTree.Path.Rename Exception: Could not rename: '" . self.strForOS(0) . "'" . 'to:' . a:newPath
+ endif
+ call self.readInfoFromDisk(a:newPath)
+
+ for i in self.bookmarkNames()
+ let b = s:Bookmark.BookmarkFor(i)
+ call b.setPath(copy(self))
+ endfor
+ call s:Bookmark.Write()
+endfunction
+
+"FUNCTION: Path.str(esc) {{{3
+"
+"Gets the actual string path that this obj represents.
+"
+"Args:
+"esc: if 1 then all the tricky chars in the returned string will be escaped
+function! s:Path.str(esc)
+ let toReturn = '/' . join(self.pathSegments, '/')
+ if self.isDirectory && toReturn != '/'
+ let toReturn = toReturn . '/'
+ endif
+
+ if a:esc
+ let toReturn = escape(toReturn, s:escape_chars)
+ endif
+ return toReturn
+endfunction
+
+"FUNCTION: Path.strAbs() {{{3
+"
+"Returns a string representing this path with all the symlinks resolved
+"
+"Return:
+"string
+function! s:Path.strAbs()
+ return resolve(self.str(1))
+endfunction
+
+"FUNCTION: Path.strForCd() {{{3
+"
+" returns a string that can be used with :cd
+"
+"Return:
+"a string that can be used in the view to represent this path
+function! s:Path.strForCd()
+ if s:running_windows
+ return self.strForOS(0)
+ else
+ return self.strForOS(1)
+ endif
+endfunction
+"FUNCTION: Path.strDisplay() {{{3
+"
+"Returns a string that specifies how the path should be represented as a
+"string
+"
+"Return:
+"a string that can be used in the view to represent this path
+function! s:Path.strDisplay()
+ if self.cachedDisplayString == ""
+ call self.cacheDisplayString()
+ endif
+
+ return self.cachedDisplayString
+endfunction
+
+"FUNCTION: Path.strForEditCmd() {{{3
+"
+"Return: the string for this path that is suitable to be used with the :edit
+"command
+function! s:Path.strForEditCmd()
+ if s:running_windows
+ return self.strForOS(0)
+ else
+ return self.str(1)
+ endif
+
+endfunction
+"FUNCTION: Path.strForGlob() {{{3
+function! s:Path.strForGlob()
+ let lead = s:os_slash
+
+ "if we are running windows then slap a drive letter on the front
+ if s:running_windows
+ let lead = self.drive . '\'
+ endif
+
+ let toReturn = lead . join(self.pathSegments, s:os_slash)
+
+ if !s:running_windows
+ let toReturn = escape(toReturn, s:escape_chars)
+ endif
+ return toReturn
+endfunction
+"FUNCTION: Path.strForOS(esc) {{{3
+"
+"Gets the string path for this path object that is appropriate for the OS.
+"EG, in windows c:\foo\bar
+" in *nix /foo/bar
+"
+"Args:
+"esc: if 1 then all the tricky chars in the returned string will be
+" escaped. If we are running windows then the str is double quoted instead.
+function! s:Path.strForOS(esc)
+ let lead = s:os_slash
+
+ "if we are running windows then slap a drive letter on the front
+ if s:running_windows
+ let lead = self.drive . '\'
+ endif
+
+ let toReturn = lead . join(self.pathSegments, s:os_slash)
+
+ if a:esc
+ if s:running_windows
+ let toReturn = '"' . toReturn . '"'
+ else
+ let toReturn = escape(toReturn, s:escape_chars)
+ endif
+ endif
+ return toReturn
+endfunction
+
+"FUNCTION: Path.strTrunk() {{{3
+"Gets the path without the last segment on the end.
+function! s:Path.strTrunk()
+ return self.drive . '/' . join(self.pathSegments[0:-2], '/')
+endfunction
+
+"FUNCTION: Path.WinToUnixPath(pathstr){{{3
+"Takes in a windows path and returns the unix equiv
+"
+"A class level method
+"
+"Args:
+"pathstr: the windows path to convert
+function! s:Path.WinToUnixPath(pathstr)
+ if !s:running_windows
+ return a:pathstr
+ endif
+
+ let toReturn = a:pathstr
+
+ "remove the x:\ of the front
+ let toReturn = substitute(toReturn, '^.*:\(\\\|/\)\?', '/', "")
+
+ "convert all \ chars to /
+ let toReturn = substitute(toReturn, '\', '/', "g")
+
+ return toReturn
+endfunction
+
+" SECTION: General Functions {{{1
+"============================================================
+"FUNCTION: s:bufInWindows(bnum){{{2
+"[[STOLEN FROM VTREEEXPLORER.VIM]]
+"Determine the number of windows open to this buffer number.
+"Care of Yegappan Lakshman. Thanks!
+"
+"Args:
+"bnum: the subject buffers buffer number
+function! s:bufInWindows(bnum)
+ let cnt = 0
+ let winnum = 1
+ while 1
+ let bufnum = winbufnr(winnum)
+ if bufnum < 0
+ break
+ endif
+ if bufnum == a:bnum
+ let cnt = cnt + 1
+ endif
+ let winnum = winnum + 1
+ endwhile
+
+ return cnt
+endfunction " >>>
+
+"FUNCTION: s:compareBookmarks(first, second) {{{2
+"Compares two bookmarks
+function! s:compareBookmarks(first, second)
+ return a:first.compareTo(a:second)
+endfunction
+
+" FUNCTION: s:completeBookmarks(A,L,P) {{{2
+" completion function for the bookmark commands
+function! s:completeBookmarks(A,L,P)
+ return filter(s:Bookmark.BookmarkNames(), 'v:val =~ "^' . a:A . '"')
+endfunction
+"FUNCTION: s:initNerdTree(name) {{{2
+"Initialise the nerd tree for this tab. The tree will start in either the
+"given directory, or the directory associated with the given bookmark
+"
+"Args:
+"name: the name of a bookmark or a directory
+function! s:initNerdTree(name)
+ let path = {}
+ if s:Bookmark.BookmarkExistsFor(a:name)
+ let path = s:Bookmark.BookmarkFor(a:name).path
+ else
+ let dir = a:name == '' ? expand('%:p:h') : a:name
+ let dir = resolve(dir)
+ try
+ let path = s:Path.New(dir)
+ catch /NERDTree.Path.InvalidArguments/
+ call s:echo("No bookmark or directory found for: " . a:name)
+ return
+ endtry
+ endif
+ if !path.isDirectory
+ let path = path.getParent()
+ endif
+
+ "if instructed to, then change the vim CWD to the dir the NERDTree is
+ "inited in
+ if g:NERDTreeChDirMode != 0
+ exec 'cd ' . path.strForCd()
+ endif
+
+ let t:treeShowHelp = 0
+ let t:NERDTreeIgnoreEnabled = 1
+ let t:NERDTreeShowFiles = g:NERDTreeShowFiles
+ let t:NERDTreeShowHidden = g:NERDTreeShowHidden
+ let t:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks
+
+ if s:treeExistsForTab()
+ if s:isTreeOpen()
+ call s:closeTree()
+ endif
+ unlet t:NERDTreeRoot
+ endif
+
+ let t:NERDTreeRoot = s:TreeDirNode.New(path)
+ call t:NERDTreeRoot.open()
+
+ call s:createTreeWin()
+ call s:renderView()
+ call s:putCursorOnNode(t:NERDTreeRoot, 0, 0)
+endfunction
+" Function: s:treeExistsForTab() {{{2
+" Returns 1 if a nerd tree root exists in the current tab
+function! s:treeExistsForTab()
+ return exists("t:NERDTreeRoot")
+endfunction
+" SECTION: Public Functions {{{1
+"============================================================
+"Returns the node that the cursor is currently on.
+"
+"If the cursor is not in the NERDTree window, it is temporarily put there.
+"
+"If no NERD tree window exists for the current tab, a NERDTree.NoTreeForTab
+"exception is thrown.
+"
+"If the cursor is not on a node then an empty dictionary {} is returned.
+function! NERDTreeGetCurrentNode()
+ if !s:treeExistsForTab() || !s:isTreeOpen()
+ throw "NERDTree.NoTreeForTab exception: there is no NERD tree open for the current tab"
+ endif
+
+ let winnr = winnr()
+ if winnr != s:getTreeWinNum()
+ call s:putCursorInTreeWin()
+ endif
+
+ let treenode = s:getSelectedNode()
+
+ if winnr != winnr()
+ wincmd w
+ endif
+
+ return treenode
+endfunction
+
+"Returns the path object for the current node.
+"
+"Subject to the same conditions as NERDTreeGetCurrentNode
+function! NERDTreeGetCurrentPath()
+ let node = NERDTreeGetCurrentNode()
+ if node != {}
+ return node.path
+ else
+ return {}
+ endif
+endfunction
+
+" SECTION: View Functions {{{1
+"============================================================
+"FUNCTION: s:centerView() {{{2
+"centers the nerd tree window around the cursor (provided the nerd tree
+"options permit)
+function! s:centerView()
+ if g:NERDTreeAutoCenter
+ let current_line = winline()
+ let lines_to_top = current_line
+ let lines_to_bottom = winheight(s:getTreeWinNum()) - current_line
+ if lines_to_top < g:NERDTreeAutoCenterThreshold || lines_to_bottom < g:NERDTreeAutoCenterThreshold
+ normal! zz
+ endif
+ endif
+endfunction
+"FUNCTION: s:closeTree() {{{2
+"Closes the NERD tree window
+function! s:closeTree()
+ if !s:isTreeOpen()
+ throw "NERDTree.view.closeTree exception: no NERDTree is open"
+ endif
+
+ if winnr("$") != 1
+ execute s:getTreeWinNum() . " wincmd w"
+ close
+ execute "wincmd p"
+ else
+ :q
+ endif
+endfunction
+
+"FUNCTION: s:closeTreeIfOpen() {{{2
+"Closes the NERD tree window if it is open
+function! s:closeTreeIfOpen()
+ if s:isTreeOpen()
+ call s:closeTree()
+ endif
+endfunction
+"FUNCTION: s:closeTreeIfQuitOnOpen() {{{2
+"Closes the NERD tree window if the close on open option is set
+function! s:closeTreeIfQuitOnOpen()
+ if g:NERDTreeQuitOnOpen
+ call s:closeTree()
+ endif
+endfunction
+"FUNCTION: s:createTreeWin() {{{2
+"Inits the NERD tree window. ie. opens it, sizes it, sets all the local
+"options etc
+function! s:createTreeWin()
+ "create the nerd tree window
+ let splitLocation = (g:NERDTreeWinPos == "top" || g:NERDTreeWinPos == "left") ? "topleft " : "botright "
+ let splitMode = s:shouldSplitVertically() ? "vertical " : ""
+ let splitSize = g:NERDTreeWinSize
+ let t:NERDTreeWinName = localtime() . s:NERDTreeWinName
+ let cmd = splitLocation . splitMode . splitSize . ' new ' . t:NERDTreeWinName
+ silent! execute cmd
+
+ setlocal winfixwidth
+
+ "throwaway buffer options
+ setlocal noswapfile
+ setlocal buftype=nofile
+ setlocal bufhidden=delete
+ setlocal nowrap
+ setlocal foldcolumn=0
+ setlocal nobuflisted
+ setlocal nospell
+ if g:NERDTreeShowLineNumbers
+ setlocal nu
+ else
+ setlocal nonu
+ endif
+
+ iabc <buffer>
+
+ if g:NERDTreeHighlightCursorline
+ setlocal cursorline
+ endif
+
+
+
+ call s:bindMappings()
+ setfiletype nerdtree
+ " syntax highlighting
+ if has("syntax") && exists("g:syntax_on") && !has("syntax_items")
+ call s:setupSyntaxHighlighting()
+ endif
+endfunction
+
+"FUNCTION: s:drawTree {{{2
+"Draws the given node recursively
+"
+"Args:
+"curNode: the node that is being rendered with this call
+"depth: the current depth in the tree for this call
+"drawText: 1 if we should actually draw the line for this node (if 0 then the
+"child nodes are rendered only)
+"vertMap: a binary array that indicates whether a vertical bar should be draw
+"for each depth in the tree
+"isLastChild:true if this curNode is the last child of its parent
+function! s:drawTree(curNode, depth, drawText, vertMap, isLastChild)
+ if a:drawText == 1
+
+ let treeParts = ''
+
+ "get all the leading spaces and vertical tree parts for this line
+ if a:depth > 1
+ for j in a:vertMap[0:-2]
+ if j == 1
+ let treeParts = treeParts . '| '
+ else
+ let treeParts = treeParts . ' '
+ endif
+ endfor
+ endif
+
+ "get the last vertical tree part for this line which will be different
+ "if this node is the last child of its parent
+ if a:isLastChild
+ let treeParts = treeParts . '`'
+ else
+ let treeParts = treeParts . '|'
+ endif
+
+
+ "smack the appropriate dir/file symbol on the line before the file/dir
+ "name itself
+ if a:curNode.path.isDirectory
+ if a:curNode.isOpen
+ let treeParts = treeParts . '~'
+ else
+ let treeParts = treeParts . '+'
+ endif
+ else
+ let treeParts = treeParts . '-'
+ endif
+ let line = treeParts . a:curNode.strDisplay()
+
+ call setline(line(".")+1, line)
+ call cursor(line(".")+1, col("."))
+ endif
+
+ "if the node is an open dir, draw its children
+ if a:curNode.path.isDirectory == 1 && a:curNode.isOpen == 1
+
+ let childNodesToDraw = a:curNode.getVisibleChildren()
+ if len(childNodesToDraw) > 0
+
+ "draw all the nodes children except the last
+ let lastIndx = len(childNodesToDraw)-1
+ if lastIndx > 0
+ for i in childNodesToDraw[0:lastIndx-1]
+ call s:drawTree(i, a:depth + 1, 1, add(copy(a:vertMap), 1), 0)
+ endfor
+ endif
+
+ "draw the last child, indicating that it IS the last
+ call s:drawTree(childNodesToDraw[lastIndx], a:depth + 1, 1, add(copy(a:vertMap), 0), 1)
+ endif
+ endif
+endfunction
+
+
+"FUNCTION: s:dumpHelp {{{2
+"prints out the quick help
+function! s:dumpHelp()
+ let old_h = @h
+ if t:treeShowHelp == 1
+ let @h= "\" NERD tree (" . s:NERD_tree_version . ") quickhelp~\n"
+ let @h=@h."\" ============================\n"
+ let @h=@h."\" File node mappings~\n"
+ let @h=@h."\" ". (g:NERDTreeMouseMode == 3 ? "single" : "double") ."-click,\n"
+ let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in prev window\n"
+ let @h=@h."\" ". g:NERDTreeMapPreview .": preview\n"
+ let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n"
+ let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n"
+ let @h=@h."\" middle-click,\n"
+ let @h=@h."\" ". g:NERDTreeMapOpenSplit .": open split\n"
+ let @h=@h."\" ". g:NERDTreeMapPreviewSplit .": preview split\n"
+ let @h=@h."\" ". g:NERDTreeMapExecute.": Execute file\n"
+
+ let @h=@h."\"\n\" ----------------------------\n"
+ let @h=@h."\" Directory node mappings~\n"
+ let @h=@h."\" ". (g:NERDTreeMouseMode == 1 ? "double" : "single") ."-click,\n"
+ let @h=@h."\" ". g:NERDTreeMapActivateNode .": open & close node\n"
+ let @h=@h."\" ". g:NERDTreeMapOpenRecursively .": recursively open node\n"
+ let @h=@h."\" ". g:NERDTreeMapCloseDir .": close parent of node\n"
+ let @h=@h."\" ". g:NERDTreeMapCloseChildren .": close all child nodes of\n"
+ let @h=@h."\" current node recursively\n"
+ let @h=@h."\" middle-click,\n"
+ let @h=@h."\" ". g:NERDTreeMapOpenExpl.": Open netrw for selected\n"
+ let @h=@h."\" node\n"
+
+ let @h=@h."\"\n\" ----------------------------\n"
+ let @h=@h."\" Bookmark table mappings~\n"
+ let @h=@h."\" double-click,\n"
+ let @h=@h."\" ". g:NERDTreeMapActivateNode .": open bookmark\n"
+ let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n"
+ let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n"
+ let @h=@h."\" ". g:NERDTreeMapDeleteBookmark .": delete bookmark\n"
+
+ let @h=@h."\"\n\" ----------------------------\n"
+ let @h=@h."\" Tree navigation mappings~\n"
+ let @h=@h."\" ". g:NERDTreeMapJumpRoot .": go to root\n"
+ let @h=@h."\" ". g:NERDTreeMapJumpParent .": go to parent\n"
+ let @h=@h."\" ". g:NERDTreeMapJumpFirstChild .": go to first child\n"
+ let @h=@h."\" ". g:NERDTreeMapJumpLastChild .": go to last child\n"
+ let @h=@h."\" ". g:NERDTreeMapJumpNextSibling .": go to next sibling\n"
+ let @h=@h."\" ". g:NERDTreeMapJumpPrevSibling .": go to prev sibling\n"
+
+ let @h=@h."\"\n\" ----------------------------\n"
+ let @h=@h."\" Filesystem mappings~\n"
+ let @h=@h."\" ". g:NERDTreeMapChangeRoot .": change tree root to the\n"
+ let @h=@h."\" selected dir\n"
+ let @h=@h."\" ". g:NERDTreeMapUpdir .": move tree root up a dir\n"
+ let @h=@h."\" ". g:NERDTreeMapUpdirKeepOpen .": move tree root up a dir\n"
+ let @h=@h."\" but leave old root open\n"
+ let @h=@h."\" ". g:NERDTreeMapRefresh .": refresh cursor dir\n"
+ let @h=@h."\" ". g:NERDTreeMapRefreshRoot .": refresh current root\n"
+ let @h=@h."\" ". g:NERDTreeMapFilesystemMenu .": Show filesystem menu\n"
+ let @h=@h."\" ". g:NERDTreeMapChdir .":change the CWD to the\n"
+ let @h=@h."\" selected dir\n"
+
+ let @h=@h."\"\n\" ----------------------------\n"
+ let @h=@h."\" Tree filtering mappings~\n"
+ let @h=@h."\" ". g:NERDTreeMapToggleHidden .": hidden files (" . (t:NERDTreeShowHidden ? "on" : "off") . ")\n"
+ let @h=@h."\" ". g:NERDTreeMapToggleFilters .": file filters (" . (t:NERDTreeIgnoreEnabled ? "on" : "off") . ")\n"
+ let @h=@h."\" ". g:NERDTreeMapToggleFiles .": files (" . (t:NERDTreeShowFiles ? "on" : "off") . ")\n"
+ let @h=@h."\" ". g:NERDTreeMapToggleBookmarks .": bookmarks (" . (t:NERDTreeShowBookmarks ? "on" : "off") . ")\n"
+
+ let @h=@h."\"\n\" ----------------------------\n"
+ let @h=@h."\" Other mappings~\n"
+ let @h=@h."\" ". g:NERDTreeMapQuit .": Close the NERDTree window\n"
+ let @h=@h."\" ". g:NERDTreeMapHelp .": toggle help\n"
+ let @h=@h."\"\n\" ----------------------------\n"
+ let @h=@h."\" Bookmark commands~\n"
+ let @h=@h."\" :Bookmark <name>\n"
+ let @h=@h."\" :BookmarkToRoot <name>\n"
+ let @h=@h."\" :RevealBookmark <name>\n"
+ let @h=@h."\" :OpenBookmark <name>\n"
+ let @h=@h."\" :ClearBookmarks [<names>]\n"
+ let @h=@h."\" :ClearAllBookmarks\n"
+ else
+ let @h="\" Press ". g:NERDTreeMapHelp ." for help\n"
+ endif
+
+ silent! put h
+
+ let @h = old_h
+endfunction
+"FUNCTION: s:echo {{{2
+"A wrapper for :echo. Appends 'NERDTree:' on the front of all messages
+"
+"Args:
+"msg: the message to echo
+function! s:echo(msg)
+ redraw
+ echomsg "NERDTree: " . a:msg
+endfunction
+"FUNCTION: s:echoWarning {{{2
+"Wrapper for s:echo, sets the message type to warningmsg for this message
+"Args:
+"msg: the message to echo
+function! s:echoWarning(msg)
+ echohl warningmsg
+ call s:echo(a:msg)
+ echohl normal
+endfunction
+"FUNCTION: s:echoError {{{2
+"Wrapper for s:echo, sets the message type to errormsg for this message
+"Args:
+"msg: the message to echo
+function! s:echoError(msg)
+ echohl errormsg
+ call s:echo(a:msg)
+ echohl normal
+endfunction
+"FUNCTION: s:findNodeLineNumber(treenode){{{2
+"Finds the line number for the given tree node
+"
+"Args:
+"treenode: the node to find the line no. for
+function! s:findNodeLineNumber(treenode)
+ "if the node is the root then return the root line no.
+ if a:treenode.isRoot()
+ return s:findRootNodeLineNumber()
+ endif
+
+ let totalLines = line("$")
+
+ "the path components we have matched so far
+ let pathcomponents = [substitute(t:NERDTreeRoot.path.str(0), '/ *$', '', '')]
+ "the index of the component we are searching for
+ let curPathComponent = 1
+
+ let fullpath = a:treenode.path.str(0)
+
+
+ let lnum = s:findRootNodeLineNumber()
+ while lnum > 0
+ let lnum = lnum + 1
+ "have we reached the bottom of the tree?
+ if lnum == totalLines+1
+ return -1
+ endif
+
+ let curLine = getline(lnum)
+
+ let indent = s:indentLevelFor(curLine)
+ if indent == curPathComponent
+ let curLine = s:stripMarkupFromLine(curLine, 1)
+
+ let curPath = join(pathcomponents, '/') . '/' . curLine
+ if stridx(fullpath, curPath, 0) == 0
+ if fullpath == curPath || strpart(fullpath, len(curPath)-1,1) == '/'
+ let curLine = substitute(curLine, '/ *$', '', '')
+ call add(pathcomponents, curLine)
+ let curPathComponent = curPathComponent + 1
+
+ if fullpath == curPath
+ return lnum
+ endif
+ endif
+ endif
+ endif
+ endwhile
+ return -1
+endfunction
+
+"FUNCTION: s:findRootNodeLineNumber(){{{2
+"Finds the line number of the root node
+function! s:findRootNodeLineNumber()
+ let rootLine = 1
+ while getline(rootLine) !~ '^/'
+ let rootLine = rootLine + 1
+ endwhile
+ return rootLine
+endfunction
+
+"FUNCTION: s:firstNormalWindow(){{{2
+"find the window number of the first normal window
+function! s:firstNormalWindow()
+ let i = 1
+ while i <= winnr("$")
+ let bnum = winbufnr(i)
+ if bnum != -1 && getbufvar(bnum, '&buftype') == ''
+ \ && !getwinvar(i, '&previewwindow')
+ return i
+ endif
+
+ let i += 1
+ endwhile
+ return -1
+endfunction
+"FUNCTION: s:getPath(ln) {{{2
+"Gets the full path to the node that is rendered on the given line number
+"
+"Args:
+"ln: the line number to get the path for
+"
+"Return:
+"A path if a node was selected, {} if nothing is selected.
+"If the 'up a dir' line was selected then the path to the parent of the
+"current root is returned
+function! s:getPath(ln)
+ let line = getline(a:ln)
+
+ "check to see if we have the root node
+ if line =~ '^\/'
+ return t:NERDTreeRoot.path
+ endif
+
+ " in case called from outside the tree
+ if line !~ '^ *[|`]' || line =~ '^$'
+ return {}
+ endif
+
+ if line == s:tree_up_dir_line
+ return t:NERDTreeRoot.path.getParent()
+ endif
+
+ let indent = s:indentLevelFor(line)
+
+ "remove the tree parts and the leading space
+ let curFile = s:stripMarkupFromLine(line, 0)
+
+ let wasdir = 0
+ if curFile =~ '/$'
+ let wasdir = 1
+ let curFile = substitute(curFile, '/\?$', '/', "")
+ endif
+
+
+ let dir = ""
+ let lnum = a:ln
+ while lnum > 0
+ let lnum = lnum - 1
+ let curLine = getline(lnum)
+ let curLineStripped = s:stripMarkupFromLine(curLine, 1)
+
+ "have we reached the top of the tree?
+ if curLine =~ '^/'
+ let dir = substitute (curLine, ' *$', "", "") . dir
+ break
+ endif
+ if curLineStripped =~ '/$'
+ let lpindent = s:indentLevelFor(curLine)
+ if lpindent < indent
+ let indent = indent - 1
+
+ let dir = substitute (curLineStripped,'^\\', "", "") . dir
+ continue
+ endif
+ endif
+ endwhile
+ let curFile = t:NERDTreeRoot.path.drive . dir . curFile
+ let toReturn = s:Path.New(curFile)
+ return toReturn
+endfunction
+
+"FUNCTION: s:getSelectedBookmark() {{{2
+"returns the bookmark the cursor is over in the bookmarks table or {}
+function! s:getSelectedBookmark()
+ let line = getline(".")
+ let name = substitute(line, '^>\(.\{-}\) .\+$', '\1', '')
+ if name != line
+ try
+ return s:Bookmark.BookmarkFor(name)
+ catch /NERDTree.BookmarkNotFound/
+ return {}
+ endtry
+ endif
+ return {}
+endfunction
+
+"FUNCTION: s:getSelectedDir() {{{2
+"Returns the current node if it is a dir node, or else returns the current
+"nodes parent
+function! s:getSelectedDir()
+ let currentDir = s:getSelectedNode()
+ if currentDir != {} && !currentDir.isRoot()
+ if currentDir.path.isDirectory == 0
+ let currentDir = currentDir.parent
+ endif
+ endif
+ return currentDir
+endfunction
+"FUNCTION: s:getSelectedNode() {{{2
+"gets the treenode that the cursor is currently over
+function! s:getSelectedNode()
+ try
+ let path = s:getPath(line("."))
+ if path == {}
+ return {}
+ endif
+ return t:NERDTreeRoot.findNode(path)
+ catch /^NERDTree/
+ return {}
+ endtry
+endfunction
+"FUNCTION: s:getTreeWinNum() {{{2
+"gets the nerd tree window number for this tab
+function! s:getTreeWinNum()
+ if exists("t:NERDTreeWinName")
+ return bufwinnr(t:NERDTreeWinName)
+ else
+ return -1
+ endif
+endfunction
+"FUNCTION: s:indentLevelFor(line) {{{2
+function! s:indentLevelFor(line)
+ return match(a:line, '[^ \-+~`|]') / s:tree_wid
+endfunction
+"FUNCTION: s:isTreeOpen() {{{2
+function! s:isTreeOpen()
+ return s:getTreeWinNum() != -1
+endfunction
+"FUNCTION: s:isWindowUsable(winnumber) {{{2
+"Returns 1 if opening a file from the tree in the given window requires it to
+"be split
+"
+"Args:
+"winnumber: the number of the window in question
+function! s:isWindowUsable(winnumber)
+ "gotta split if theres only one window (i.e. the NERD tree)
+ if winnr("$") == 1
+ return 0
+ endif
+
+ let oldwinnr = winnr()
+ exec a:winnumber . "wincmd p"
+ let specialWindow = getbufvar("%", '&buftype') != '' || getwinvar('%', '&previewwindow')
+ let modified = &modified
+ exec oldwinnr . "wincmd p"
+
+ "if its a special window e.g. quickfix or another explorer plugin then we
+ "have to split
+ if specialWindow
+ return 0
+ endif
+
+ if &hidden
+ return 1
+ endif
+
+ return !modified || s:bufInWindows(winbufnr(a:winnumber)) >= 2
+endfunction
+
+" FUNCTION: s:jumpToChild(direction) {{{2
+" Args:
+" direction: 0 if going to first child, 1 if going to last
+function! s:jumpToChild(direction)
+ let currentNode = s:getSelectedNode()
+ if currentNode == {} || currentNode.isRoot()
+ call s:echo("cannot jump to " . (a:direction ? "last" : "first") . " child")
+ return
+ end
+ let dirNode = currentNode.parent
+ let childNodes = dirNode.getVisibleChildren()
+
+ let targetNode = childNodes[0]
+ if a:direction
+ let targetNode = childNodes[len(childNodes) - 1]
+ endif
+
+ if targetNode.equals(currentNode)
+ let siblingDir = currentNode.parent.findOpenDirSiblingWithVisibleChildren(a:direction)
+ if siblingDir != {}
+ let indx = a:direction ? siblingDir.getVisibleChildCount()-1 : 0
+ let targetNode = siblingDir.getChildByIndex(indx, 1)
+ endif
+ endif
+
+ call s:putCursorOnNode(targetNode, 1, 0)
+
+ call s:centerView()
+endfunction
+
+
+"FUNCTION: s:openDirNodeSplit(treenode) {{{2
+"Open the file represented by the given node in a new window.
+"No action is taken for file nodes
+"
+"ARGS:
+"treenode: file node to open
+function! s:openDirNodeSplit(treenode)
+ if a:treenode.path.isDirectory == 1
+ call s:openNodeSplit(a:treenode)
+ endif
+endfunction
+
+" FUNCTION: s:openExplorerFor(treenode) {{{2
+" opens a netrw window for the given dir treenode
+function! s:openExplorerFor(treenode)
+ let oldwin = winnr()
+ wincmd p
+ if oldwin == winnr() || (&modified && s:bufInWindows(winbufnr(winnr())) < 2)
+ wincmd p
+ call s:openDirNodeSplit(a:treenode)
+ else
+ exec ("silent edit " . a:treenode.path.strForEditCmd())
+ endif
+endfunction
+"FUNCTION: s:openFileNode(treenode) {{{2
+"Open the file represented by the given node in the current window, splitting
+"the window if needed
+"
+"ARGS:
+"treenode: file node to open
+function! s:openFileNode(treenode)
+ call s:putCursorInTreeWin()
+
+ "if the file is already open in this tab then just stick the cursor in it
+ let winnr = bufwinnr('^' . a:treenode.path.strForOS(0) . '$')
+ if winnr != -1
+ exec winnr . "wincmd w"
+
+ else
+ if !s:isWindowUsable(winnr("#")) && s:firstNormalWindow() == -1
+ call s:openFileNodeSplit(a:treenode)
+ else
+ try
+ if !s:isWindowUsable(winnr("#"))
+ exec s:firstNormalWindow() . "wincmd w"
+ else
+ wincmd p
+ endif
+ exec ("edit " . a:treenode.path.strForEditCmd())
+ catch /^Vim\%((\a\+)\)\=:E37/
+ call s:putCursorInTreeWin()
+ call s:echo("Cannot open file, it is already open and modified")
+ catch /^Vim\%((\a\+)\)\=:/
+ echo v:exception
+ endtry
+ endif
+ endif
+endfunction
+
+"FUNCTION: s:openFileNodeSplit(treenode) {{{2
+"Open the file represented by the given node in a new window.
+"No action is taken for dir nodes
+"
+"ARGS:
+"treenode: file node to open
+function! s:openFileNodeSplit(treenode)
+ if a:treenode.path.isDirectory == 0
+ try
+ call s:openNodeSplit(a:treenode)
+ catch /^NERDTree.view.FileOpen/
+ call s:echo("Cannot open file, it is already open and modified" )
+ endtry
+ endif
+endfunction
+
+"FUNCTION: s:openNodeSplit(treenode) {{{2
+"Open the file/dir represented by the given node in a new window
+"
+"ARGS:
+"treenode: file node to open
+function! s:openNodeSplit(treenode)
+ call s:putCursorInTreeWin()
+
+ " Save the user's settings for splitbelow and splitright
+ let savesplitbelow=&splitbelow
+ let savesplitright=&splitright
+
+ " Figure out how to do the split based on the user's preferences.
+ " We want to split to the (left,right,top,bottom) of the explorer
+ " window, but we want to extract the screen real-estate from the
+ " window next to the explorer if possible.
+ "
+ " 'there' will be set to a command to move from the split window
+ " back to the explorer window
+ "
+ " 'back' will be set to a command to move from the explorer window
+ " back to the newly split window
+ "
+ " 'right' and 'below' will be set to the settings needed for
+ " splitbelow and splitright IF the explorer is the only window.
+ "
+ if s:shouldSplitVertically()
+ let there= g:NERDTreeWinPos == "left" ? "wincmd h" : "wincmd l"
+ let back = g:NERDTreeWinPos == "left" ? "wincmd l" : "wincmd h"
+ let right= g:NERDTreeWinPos == "left"
+ let below=0
+ else
+ let there= g:NERDTreeWinPos == "top" ? "wincmd k" : "wincmd j"
+ let back = g:NERDTreeWinPos == "top" ? "wincmd j" : "wincmd k"
+ let below= g:NERDTreeWinPos == "top"
+ let right=0
+ endif
+
+ " Attempt to go to adjacent window
+ exec(back)
+
+ let onlyOneWin = (winnr() == s:getTreeWinNum())
+
+ " If no adjacent window, set splitright and splitbelow appropriately
+ if onlyOneWin
+ let &splitright=right
+ let &splitbelow=below
+ else
+ " found adjacent window - invert split direction
+ let &splitright=!right
+ let &splitbelow=!below
+ endif
+
+ " Create a variable to use if splitting vertically
+ let splitMode = ""
+ if (onlyOneWin && s:shouldSplitVertically()) || (!onlyOneWin && !s:shouldSplitVertically())
+ let splitMode = "vertical"
+ endif
+
+ echomsg splitMode
+
+ " Open the new window
+ try
+ exec(splitMode." sp " . a:treenode.path.strForEditCmd())
+ catch /^Vim\%((\a\+)\)\=:E37/
+ call s:putCursorInTreeWin()
+ throw "NERDTree.view.FileOpen exception: ". a:treenode.path.str(0) ." is already open and modified."
+ catch /^Vim\%((\a\+)\)\=:/
+ "do nothing
+ endtry
+
+ "resize the tree window if no other window was open before
+ if onlyOneWin
+ let size = exists("t:NERDTreeOldWindowSize") ? t:NERDTreeOldWindowSize : g:NERDTreeWinSize
+ exec(there)
+ exec("silent ". splitMode ." resize ". size)
+ wincmd p
+ endif
+
+ " Restore splitmode settings
+ let &splitbelow=savesplitbelow
+ let &splitright=savesplitright
+endfunction
+
+"FUNCTION: s:promptToDelBuffer(bufnum, msg){{{2
+"prints out the given msg and, if the user responds by pushing 'y' then the
+"buffer with the given bufnum is deleted
+"
+"Args:
+"bufnum: the buffer that may be deleted
+"msg: a message that will be echoed to the user asking them if they wish to
+" del the buffer
+function! s:promptToDelBuffer(bufnum, msg)
+ echo a:msg
+ if nr2char(getchar()) == 'y'
+ exec "silent bdelete! " . a:bufnum
+ endif
+endfunction
+
+"FUNCTION: s:putCursorOnBookmarkTable(){{{2
+"Places the cursor at the top of the bookmarks table
+function! s:putCursorOnBookmarkTable()
+ if !t:NERDTreeShowBookmarks
+ throw "NERDTree.IllegalOperation exception: cant find bookmark table, bookmarks arent active"
+ endif
+
+ let rootNodeLine = s:findRootNodeLineNumber()
+
+ let line = 1
+ while getline(line) !~ '^>-\+Bookmarks-\+$'
+ let line = line + 1
+ if line >= rootNodeLine
+ throw "NERDTree.BookmarkTableNotFound exception: didnt find the bookmarks table"
+ endif
+ endwhile
+ call cursor(line, 0)
+endfunction
+
+"FUNCTION: s:putCursorOnNode(treenode, isJump, recurseUpward){{{2
+"Places the cursor on the line number representing the given node
+"
+"Args:
+"treenode: the node to put the cursor on
+"isJump: 1 if this cursor movement should be counted as a jump by vim
+"recurseUpward: try to put the cursor on the parent if the this node isnt
+"visible
+function! s:putCursorOnNode(treenode, isJump, recurseUpward)
+ let ln = s:findNodeLineNumber(a:treenode)
+ if ln != -1
+ if a:isJump
+ mark '
+ endif
+ call cursor(ln, col("."))
+ else
+ if a:recurseUpward
+ let node = a:treenode
+ while s:findNodeLineNumber(node) == -1 && node != {}
+ let node = node.parent
+ call node.open()
+ endwhile
+ call s:renderView()
+ call s:putCursorOnNode(a:treenode, a:isJump, 0)
+ endif
+ endif
+endfunction
+
+"FUNCTION: s:putCursorInTreeWin(){{{2
+"Places the cursor in the nerd tree window
+function! s:putCursorInTreeWin()
+ if !s:isTreeOpen()
+ throw "NERDTree.view.InvalidOperation Exception: No NERD tree window exists"
+ endif
+
+ exec s:getTreeWinNum() . "wincmd w"
+endfunction
+
+"FUNCTION: s:renderBookmarks {{{2
+function! s:renderBookmarks()
+
+ call setline(line(".")+1, ">----------Bookmarks----------")
+ call cursor(line(".")+1, col("."))
+
+ for i in s:Bookmark.Bookmarks()
+ call setline(line(".")+1, i.str())
+ call cursor(line(".")+1, col("."))
+ endfor
+
+ call setline(line(".")+1, '')
+ call cursor(line(".")+1, col("."))
+endfunction
+"FUNCTION: s:renderView {{{2
+"The entry function for rendering the tree. Renders the root then calls
+"s:drawTree to draw the children of the root
+"
+"Args:
+function! s:renderView()
+ execute s:getTreeWinNum() . "wincmd w"
+
+ setlocal modifiable
+
+ "remember the top line of the buffer and the current line so we can
+ "restore the view exactly how it was
+ let curLine = line(".")
+ let curCol = col(".")
+ let topLine = line("w0")
+
+ "delete all lines in the buffer (being careful not to clobber a register)
+ silent 1,$delete _
+
+ call s:dumpHelp()
+
+ "delete the blank line before the help and add one after it
+ call setline(line(".")+1, "")
+ call cursor(line(".")+1, col("."))
+
+ if t:NERDTreeShowBookmarks
+ call s:renderBookmarks()
+ endif
+
+ "add the 'up a dir' line
+ call setline(line(".")+1, s:tree_up_dir_line)
+ call cursor(line(".")+1, col("."))
+
+ "draw the header line
+ call setline(line(".")+1, t:NERDTreeRoot.path.str(0))
+ call cursor(line(".")+1, col("."))
+
+ "draw the tree
+ call s:drawTree(t:NERDTreeRoot, 0, 0, [], t:NERDTreeRoot.getChildCount() == 1)
+
+ "delete the blank line at the top of the buffer
+ silent 1,1delete _
+
+ "restore the view
+ let old_scrolloff=&scrolloff
+ let &scrolloff=0
+ call cursor(topLine, 1)
+ normal! zt
+ call cursor(curLine, curCol)
+ let &scrolloff = old_scrolloff
+
+ setlocal nomodifiable
+endfunction
+
+"FUNCTION: s:renderViewSavingPosition {{{2
+"Renders the tree and ensures the cursor stays on the current node or the
+"current nodes parent if it is no longer available upon re-rendering
+function! s:renderViewSavingPosition()
+ let currentNode = s:getSelectedNode()
+
+ "go up the tree till we find a node that will be visible or till we run
+ "out of nodes
+ while currentNode != {} && !currentNode.isVisible() && !currentNode.isRoot()
+ let currentNode = currentNode.parent
+ endwhile
+
+ call s:renderView()
+
+ if currentNode != {}
+ call s:putCursorOnNode(currentNode, 0, 0)
+ endif
+endfunction
+"FUNCTION: s:restoreScreenState() {{{2
+"
+"Sets the screen state back to what it was when s:saveScreenState was last
+"called.
+"
+"Assumes the cursor is in the NERDTree window
+function! s:restoreScreenState()
+ if !exists("t:NERDTreeOldTopLine") || !exists("t:NERDTreeOldPos") || !exists("t:NERDTreeOldWindowSize")
+ return
+ endif
+ exec("silent ". (s:shouldSplitVertically() ? "vertical" : "") ." resize ".t:NERDTreeOldWindowSize)
+
+ let old_scrolloff=&scrolloff
+ let &scrolloff=0
+ call cursor(t:NERDTreeOldTopLine, 0)
+ normal! zt
+ call setpos(".", t:NERDTreeOldPos)
+ let &scrolloff=old_scrolloff
+endfunction
+
+"FUNCTION: s:saveScreenState() {{{2
+"Saves the current cursor position in the current buffer and the window
+"scroll position
+function! s:saveScreenState()
+ let win = winnr()
+ try
+ call s:putCursorInTreeWin()
+ let t:NERDTreeOldPos = getpos(".")
+ let t:NERDTreeOldTopLine = line("w0")
+ let t:NERDTreeOldWindowSize = s:shouldSplitVertically() ? winwidth("") : winheight("")
+ exec win . "wincmd w"
+ catch /NERDTree.view.InvalidOperation/
+ endtry
+endfunction
+
+"FUNCTION: s:setupSyntaxHighlighting() {{{2
+function! s:setupSyntaxHighlighting()
+ "treeFlags are syntax items that should be invisible, but give clues as to
+ "how things should be highlighted
+ syn match treeFlag #\~#
+ syn match treeFlag #\[RO\]#
+
+ "highlighting for the .. (up dir) line at the top of the tree
+ execute "syn match treeUp #". s:tree_up_dir_line ."#"
+
+ "highlighting for the ~/+ symbols for the directory nodes
+ syn match treeClosable #\~\<#
+ syn match treeClosable #\~\.#
+ syn match treeOpenable #+\<#
+ syn match treeOpenable #+\.#he=e-1
+
+ "highlighting for the tree structural parts
+ syn match treePart #|#
+ syn match treePart #`#
+ syn match treePartFile #[|`]-#hs=s+1 contains=treePart
+
+ "quickhelp syntax elements
+ syn match treeHelpKey #" \{1,2\}[^ ]*:#hs=s+2,he=e-1
+ syn match treeHelpKey #" \{1,2\}[^ ]*,#hs=s+2,he=e-1
+ syn match treeHelpTitle #" .*\~#hs=s+2,he=e-1 contains=treeFlag
+ syn match treeToggleOn #".*(on)#hs=e-2,he=e-1 contains=treeHelpKey
+ syn match treeToggleOff #".*(off)#hs=e-3,he=e-1 contains=treeHelpKey
+ syn match treeHelpCommand #" :.\{-}\>#hs=s+3
+ syn match treeHelp #^".*# contains=treeHelpKey,treeHelpTitle,treeFlag,treeToggleOff,treeToggleOn,treeHelpCommand
+
+ "highlighting for readonly files
+ syn match treeRO #[\/0-9a-zA-Z]\+.*\[RO\]# contains=treeFlag,treeBookmark
+
+ "highlighting for sym links
+ syn match treeLink #[^-| `].* -> # contains=treeBookmark,treeOpenable,treeClosable,treeDirSlash
+
+ "highlighing for directory nodes and file nodes
+ syn match treeDirSlash #/#
+ syn match treeDir #[^-| `].*/# contains=treeLink,treeDirSlash,treeOpenable,treeClosable
+ syn match treeExecFile #[|`]-.*\*\($\| \)# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark
+ syn match treeFile #|-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile
+ syn match treeFile #`-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile
+ syn match treeCWD #^/.*$#
+
+ "highlighting for bookmarks
+ syn match treeBookmark # {.*}#hs=s+1
+
+ "highlighting for the bookmarks table
+ syn match treeBookmarksLeader #^>#
+ syn match treeBookmarksHeader #^>-\+Bookmarks-\+$# contains=treeBookmarksLeader
+ syn match treeBookmarkName #^>.\{-} #he=e-1 contains=treeBookmarksLeader
+ syn match treeBookmark #^>.*$# contains=treeBookmarksLeader,treeBookmarkName,treeBookmarksHeader
+
+ if g:NERDChristmasTree
+ hi def link treePart Special
+ hi def link treePartFile Type
+ hi def link treeFile Normal
+ hi def link treeExecFile Title
+ hi def link treeDirSlash Identifier
+ hi def link treeClosable Type
+ else
+ hi def link treePart Normal
+ hi def link treePartFile Normal
+ hi def link treeFile Normal
+ hi def link treeClosable Title
+ endif
+
+ hi def link treeBookmarksHeader statement
+ hi def link treeBookmarksLeader ignore
+ hi def link treeBookmarkName Identifier
+ hi def link treeBookmark normal
+
+ hi def link treeHelp String
+ hi def link treeHelpKey Identifier
+ hi def link treeHelpCommand Identifier
+ hi def link treeHelpTitle Macro
+ hi def link treeToggleOn Question
+ hi def link treeToggleOff WarningMsg
+
+ hi def link treeDir Directory
+ hi def link treeUp Directory
+ hi def link treeCWD Statement
+ hi def link treeLink Macro
+ hi def link treeOpenable Title
+ hi def link treeFlag ignore
+ hi def link treeRO WarningMsg
+ hi def link treeBookmark Statement
+
+ hi def link NERDTreeCurrentNode Search
+endfunction
+
+" Function: s:shouldSplitVertically() {{{2
+" Returns 1 if g:NERDTreeWinPos is 'left' or 'right'
+function! s:shouldSplitVertically()
+ return g:NERDTreeWinPos == 'left' || g:NERDTreeWinPos == 'right'
+endfunction
+"FUNCTION: s:stripMarkupFromLine(line, removeLeadingSpaces){{{2
+"returns the given line with all the tree parts stripped off
+"
+"Args:
+"line: the subject line
+"removeLeadingSpaces: 1 if leading spaces are to be removed (leading spaces =
+"any spaces before the actual text of the node)
+function! s:stripMarkupFromLine(line, removeLeadingSpaces)
+ let line = a:line
+ "remove the tree parts and the leading space
+ let line = substitute (line, s:tree_markup_reg,"","")
+
+ "strip off any read only flag
+ let line = substitute (line, ' \[RO\]', "","")
+
+ "strip off any bookmark flags
+ let line = substitute (line, ' {[^}]*}', "","")
+
+ "strip off any executable flags
+ let line = substitute (line, '*\ze\($\| \)', "","")
+
+ let wasdir = 0
+ if line =~ '/$'
+ let wasdir = 1
+ endif
+ let line = substitute (line,' -> .*',"","") " remove link to
+ if wasdir == 1
+ let line = substitute (line, '/\?$', '/', "")
+ endif
+
+ if a:removeLeadingSpaces
+ let line = substitute (line, '^ *', '', '')
+ endif
+
+ return line
+endfunction
+
+"FUNCTION: s:toggle(dir) {{{2
+"Toggles the NERD tree. I.e the NERD tree is open, it is closed, if it is
+"closed it is restored or initialized (if it doesnt exist)
+"
+"Args:
+"dir: the full path for the root node (is only used if the NERD tree is being
+"initialized.
+function! s:toggle(dir)
+ if s:treeExistsForTab()
+ if !s:isTreeOpen()
+ call s:createTreeWin()
+ call s:renderView()
+
+ call s:restoreScreenState()
+ else
+ call s:closeTree()
+ endif
+ else
+ call s:initNerdTree(a:dir)
+ endif
+endfunction
+"SECTION: Interface bindings {{{1
+"============================================================
+"FUNCTION: s:activateNode(forceKeepWindowOpen) {{{2
+"If the current node is a file, open it in the previous window (or a new one
+"if the previous is modified). If it is a directory then it is opened.
+"
+"args:
+"forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
+function! s:activateNode(forceKeepWindowOpen)
+ if getline(".") == s:tree_up_dir_line
+ return s:upDir(0)
+ endif
+
+ let treenode = s:getSelectedNode()
+ if treenode != {}
+ if treenode.path.isDirectory
+ call treenode.toggleOpen()
+ call s:renderView()
+ call s:putCursorOnNode(treenode, 0, 0)
+ else
+ call s:openFileNode(treenode)
+ if !a:forceKeepWindowOpen
+ call s:closeTreeIfQuitOnOpen()
+ end
+ endif
+ else
+ let bookmark = s:getSelectedBookmark()
+ if !empty(bookmark)
+ if bookmark.path.isDirectory
+ call bookmark.toRoot()
+ else
+ if bookmark.validate()
+ call s:openFileNode(s:TreeFileNode.New(bookmark.path))
+ endif
+ endif
+ endif
+ endif
+endfunction
+
+"FUNCTION: s:bindMappings() {{{2
+function! s:bindMappings()
+ " set up mappings and commands for this buffer
+ nnoremap <silent> <buffer> <middlerelease> :call <SID>handleMiddleMouse()<cr>
+ nnoremap <silent> <buffer> <leftrelease> <leftrelease>:call <SID>checkForActivate()<cr>
+ nnoremap <silent> <buffer> <2-leftmouse> :call <SID>activateNode(0)<cr>
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapActivateNode . " :call <SID>activateNode(0)<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenSplit ." :call <SID>openEntrySplit(0)<cr>"
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreview ." :call <SID>previewNode(0)<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreviewSplit ." :call <SID>previewNode(1)<cr>"
+
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapExecute ." :call <SID>executeNode()<cr>"
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenRecursively ." :call <SID>openNodeRecursively()<cr>"
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdirKeepOpen ." :call <SID>upDir(1)<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdir ." :call <SID>upDir(0)<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChangeRoot ." :call <SID>chRoot()<cr>"
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChdir ." :call <SID>chCwd()<cr>"
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapQuit ." :NERDTreeToggle<cr>"
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefreshRoot ." :call <SID>refreshRoot()<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefresh ." :call <SID>refreshCurrent()<cr>"
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapHelp ." :call <SID>displayHelp()<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleHidden ." :call <SID>toggleShowHidden()<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFilters ." :call <SID>toggleIgnoreFilter()<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFiles ." :call <SID>toggleShowFiles()<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleBookmarks ." :call <SID>toggleShowBookmarks()<cr>"
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseDir ." :call <SID>closeCurrentDir()<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseChildren ." :call <SID>closeChildren()<cr>"
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapFilesystemMenu ." :call <SID>showFileSystemMenu()<cr>"
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpParent ." :call <SID>jumpToParent()<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpNextSibling ." :call <SID>jumpToSibling(1)<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpPrevSibling ." :call <SID>jumpToSibling(0)<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpFirstChild ." :call <SID>jumpToFirstChild()<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpLastChild ." :call <SID>jumpToLastChild()<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpRoot ." :call <SID>jumpToRoot()<cr>"
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTab ." :call <SID>openInNewTab(0)<cr>"
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTabSilent ." :call <SID>openInNewTab(1)<cr>"
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenExpl ." :call <SID>openExplorer()<cr>"
+
+ exec "nnoremap <silent> <buffer> ". g:NERDTreeMapDeleteBookmark ." :call <SID>deleteBookmark()<cr>"
+
+ command! -buffer -nargs=1 Bookmark :call <SID>bookmarkNode('<args>')
+ command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 RevealBookmark :call <SID>revealBookmark('<args>')
+ command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 OpenBookmark :call <SID>openBookmark('<args>')
+ command! -buffer -complete=customlist,s:completeBookmarks -nargs=* ClearBookmarks call <SID>clearBookmarks('<args>')
+ command! -buffer -complete=customlist,s:completeBookmarks -nargs=+ BookmarkToRoot call s:Bookmark.ToRoot('<args>')
+ command! -buffer -nargs=0 ClearAllBookmarks call s:Bookmark.ClearAll() <bar> call <SID>renderView()
+ command! -buffer -nargs=0 ReadBookmarks call s:Bookmark.CacheBookmarks(0) <bar> call <SID>renderView()
+ command! -buffer -nargs=0 WriteBookmarks call s:Bookmark.Write()
+endfunction
+
+" FUNCTION: s:bookmarkNode(name) {{{2
+" Associate the current node with the given name
+function! s:bookmarkNode(name)
+ let currentNode = s:getSelectedNode()
+ if currentNode != {}
+ try
+ call currentNode.bookmark(a:name)
+ call s:renderView()
+ catch /NERDTree.IllegalBookmarkName/
+ call s:echo("bookmark names must not contain spaces")
+ endtry
+ else
+ call s:echo("select a node first")
+ endif
+endfunction
+"FUNCTION: s:checkForActivate() {{{2
+"Checks if the click should open the current node, if so then activate() is
+"called (directories are automatically opened if the symbol beside them is
+"clicked)
+function! s:checkForActivate()
+ let currentNode = s:getSelectedNode()
+ if currentNode != {}
+ let startToCur = strpart(getline(line(".")), 0, col("."))
+ let char = strpart(startToCur, strlen(startToCur)-1, 1)
+
+ "if they clicked a dir, check if they clicked on the + or ~ sign
+ "beside it
+ if currentNode.path.isDirectory
+ if startToCur =~ s:tree_markup_reg . '$' && char =~ '[+~]'
+ call s:activateNode(0)
+ return
+ endif
+ endif
+
+ if (g:NERDTreeMouseMode == 2 && currentNode.path.isDirectory) || g:NERDTreeMouseMode == 3
+ if char !~ s:tree_markup_reg && startToCur !~ '\/$'
+ call s:activateNode(0)
+ return
+ endif
+ endif
+ endif
+endfunction
+
+" FUNCTION: s:chCwd() {{{2
+function! s:chCwd()
+ let treenode = s:getSelectedNode()
+ if treenode == {}
+ call s:echo("Select a node first")
+ return
+ endif
+
+ try
+ call treenode.path.changeToDir()
+ catch /^NERDTree.Path.Change/
+ call s:echoWarning("could not change cwd")
+ endtry
+endfunction
+
+" FUNCTION: s:chRoot() {{{2
+" changes the current root to the selected one
+function! s:chRoot()
+ let treenode = s:getSelectedNode()
+ if treenode == {}
+ call s:echo("Select a node first")
+ return
+ endif
+
+ call treenode.makeRoot()
+ call s:renderView()
+ call s:putCursorOnNode(t:NERDTreeRoot, 0, 0)
+endfunction
+
+" FUNCTION: s:clearBookmarks(bookmarks) {{{2
+function! s:clearBookmarks(bookmarks)
+ if a:bookmarks == ''
+ let currentNode = s:getSelectedNode()
+ if currentNode != {}
+ call currentNode.clearBoomarks()
+ endif
+ else
+ for name in split(a:bookmarks, ' ')
+ let bookmark = s:Bookmark.BookmarkFor(name)
+ call bookmark.delete()
+ endfor
+ endif
+ call s:renderView()
+endfunction
+" FUNCTION: s:closeChildren() {{{2
+" closes all childnodes of the current node
+function! s:closeChildren()
+ let currentNode = s:getSelectedDir()
+ if currentNode == {}
+ call s:echo("Select a node first")
+ return
+ endif
+
+ call currentNode.closeChildren()
+ call s:renderView()
+ call s:putCursorOnNode(currentNode, 0, 0)
+endfunction
+" FUNCTION: s:closeCurrentDir() {{{2
+" closes the parent dir of the current node
+function! s:closeCurrentDir()
+ let treenode = s:getSelectedNode()
+ if treenode == {}
+ call s:echo("Select a node first")
+ return
+ endif
+
+ let parent = treenode.parent
+ if parent.isRoot()
+ call s:echo("cannot close tree root")
+ else
+ call treenode.parent.close()
+ call s:renderView()
+ call s:putCursorOnNode(treenode.parent, 0, 0)
+ endif
+endfunction
+
+" FUNCTION: s:copyNode() {{{2
+function! s:copyNode()
+ let currentNode = s:getSelectedNode()
+ if currentNode == {}
+ call s:echo("Put the cursor on a file node first")
+ return
+ endif
+
+ let newNodePath = input("Copy the current node\n" .
+ \ "==========================================================\n" .
+ \ "Enter the new path to copy the node to: \n" .
+ \ "", currentNode.path.str(0))
+
+ if newNodePath != ""
+ "strip trailing slash
+ let newNodePath = substitute(newNodePath, '\/$', '', '')
+
+ let confirmed = 1
+ if currentNode.path.copyingWillOverwrite(newNodePath)
+ call s:echo("\nWarning: copying may overwrite files! Continue? (yN)")
+ let choice = nr2char(getchar())
+ let confirmed = choice == 'y'
+ endif
+
+ if confirmed
+ try
+ let newNode = currentNode.copy(newNodePath)
+ call s:renderView()
+ call s:putCursorOnNode(newNode, 0, 0)
+ catch /^NERDTree/
+ call s:echoWarning("Could not copy node")
+ endtry
+ endif
+ else
+ call s:echo("Copy aborted.")
+ endif
+ redraw
+endfunction
+
+" FUNCTION: s:deleteBookmark() {{{2
+" if the cursor is on a bookmark, prompt to delete
+function! s:deleteBookmark()
+ let bookmark = s:getSelectedBookmark()
+ if bookmark == {}
+ call s:echo("Put the cursor on a bookmark")
+ return
+ endif
+
+ echo "Are you sure you wish to delete the bookmark:\n\"" . bookmark.name . "\" (yN):"
+
+ if nr2char(getchar()) == 'y'
+ try
+ call bookmark.delete()
+ call s:renderView()
+ redraw
+ catch /^NERDTree/
+ call s:echoWarning("Could not remove bookmark")
+ endtry
+ else
+ call s:echo("delete aborted" )
+ endif
+
+endfunction
+
+" FUNCTION: s:deleteNode() {{{2
+" if the current node is a file, pops up a dialog giving the user the option
+" to delete it
+function! s:deleteNode()
+ let currentNode = s:getSelectedNode()
+ if currentNode == {}
+ call s:echo("Put the cursor on a file node first")
+ return
+ endif
+
+ let confirmed = 0
+
+ if currentNode.path.isDirectory
+ let choice =input("Delete the current node\n" .
+ \ "==========================================================\n" .
+ \ "STOP! To delete this entire directory, type 'yes'\n" .
+ \ "" . currentNode.path.strForOS(0) . ": ")
+ let confirmed = choice == 'yes'
+ else
+ echo "Delete the current node\n" .
+ \ "==========================================================\n".
+ \ "Are you sure you wish to delete the node:\n" .
+ \ "" . currentNode.path.strForOS(0) . " (yN):"
+ let choice = nr2char(getchar())
+ let confirmed = choice == 'y'
+ endif
+
+
+ if confirmed
+ try
+ call currentNode.delete()
+ call s:renderView()
+
+ "if the node is open in a buffer, ask the user if they want to
+ "close that buffer
+ let bufnum = bufnr(currentNode.path.str(0))
+ if buflisted(bufnum)
+ let prompt = "\nNode deleted.\n\nThe file is open in buffer ". bufnum . (bufwinnr(bufnum) == -1 ? " (hidden)" : "") .". Delete this buffer? (yN)"
+ call s:promptToDelBuffer(bufnum, prompt)
+ endif
+
+ redraw
+ catch /^NERDTree/
+ call s:echoWarning("Could not remove node")
+ endtry
+ else
+ call s:echo("delete aborted" )
+ endif
+
+endfunction
+
+" FUNCTION: s:displayHelp() {{{2
+" toggles the help display
+function! s:displayHelp()
+ let t:treeShowHelp = t:treeShowHelp ? 0 : 1
+ call s:renderView()
+ call s:centerView()
+endfunction
+
+" FUNCTION: s:executeNode() {{{2
+function! s:executeNode()
+ let treenode = s:getSelectedNode()
+ if treenode == {} || treenode.path.isDirectory
+ call s:echo("Select an executable file node first" )
+ else
+ echo "NERDTree executor\n" .
+ \ "==========================================================\n".
+ \ "Complete the command to execute (add arguments etc): \n\n"
+ let cmd = treenode.path.strForOS(1)
+ let cmd = input(':!', cmd . ' ')
+
+ if cmd != ''
+ exec ':!' . cmd
+ else
+ call s:echo("command aborted")
+ endif
+ endif
+endfunction
+
+" FUNCTION: s:handleMiddleMouse() {{{2
+function! s:handleMiddleMouse()
+ let curNode = s:getSelectedNode()
+ if curNode == {}
+ call s:echo("Put the cursor on a node first" )
+ return
+ endif
+
+ if curNode.path.isDirectory
+ call s:openExplorer()
+ else
+ call s:openEntrySplit(0)
+ endif
+endfunction
+
+
+" FUNCTION: s:insertNewNode() {{{2
+" Adds a new node to the filesystem and then into the tree
+function! s:insertNewNode()
+ let curDirNode = s:getSelectedDir()
+ if curDirNode == {}
+ call s:echo("Put the cursor on a node first" )
+ return
+ endif
+
+ let newNodeName = input("Add a childnode\n".
+ \ "==========================================================\n".
+ \ "Enter the dir/file name to be created. Dirs end with a '/'\n" .
+ \ "", curDirNode.path.strForGlob() . s:os_slash)
+
+ if newNodeName == ''
+ call s:echo("Node Creation Aborted.")
+ return
+ endif
+
+ try
+ let newPath = s:Path.Create(newNodeName)
+ let parentNode = t:NERDTreeRoot.findNode(newPath.getPathTrunk())
+
+ let newTreeNode = s:TreeFileNode.New(newPath)
+ if parentNode.isOpen || !empty(parentNode.children)
+ call parentNode.addChild(newTreeNode, 1)
+ call s:renderView()
+ call s:putCursorOnNode(newTreeNode, 1, 0)
+ endif
+ catch /^NERDTree/
+ call s:echoWarning("Node Not Created.")
+ endtry
+endfunction
+
+" FUNCTION: s:jumpToFirstChild() {{{2
+" wrapper for the jump to child method
+function! s:jumpToFirstChild()
+ call s:jumpToChild(0)
+endfunction
+
+" FUNCTION: s:jumpToLastChild() {{{2
+" wrapper for the jump to child method
+function! s:jumpToLastChild()
+ call s:jumpToChild(1)
+endfunction
+
+" FUNCTION: s:jumpToParent() {{{2
+" moves the cursor to the parent of the current node
+function! s:jumpToParent()
+ let currentNode = s:getSelectedNode()
+ if !empty(currentNode)
+ if !empty(currentNode.parent)
+ call s:putCursorOnNode(currentNode.parent, 1, 0)
+ call s:centerView()
+ else
+ call s:echo("cannot jump to parent")
+ endif
+ else
+ call s:echo("put the cursor on a node first")
+ endif
+endfunction
+
+" FUNCTION: s:jumpToRoot() {{{2
+" moves the cursor to the root node
+function! s:jumpToRoot()
+ call s:putCursorOnNode(t:NERDTreeRoot, 1, 0)
+ call s:centerView()
+endfunction
+
+" FUNCTION: s:jumpToSibling() {{{2
+" moves the cursor to the sibling of the current node in the given direction
+"
+" Args:
+" forward: 1 if the cursor should move to the next sibling, 0 if it should
+" move back to the previous sibling
+function! s:jumpToSibling(forward)
+ let currentNode = s:getSelectedNode()
+ if !empty(currentNode)
+ let sibling = currentNode.findSibling(a:forward)
+
+ if !empty(sibling)
+ call s:putCursorOnNode(sibling, 1, 0)
+ call s:centerView()
+ endif
+ else
+ call s:echo("put the cursor on a node first")
+ endif
+endfunction
+
+" FUNCTION: s:openBookmark(name) {{{2
+" put the cursor on the given bookmark and, if its a file, open it
+function! s:openBookmark(name)
+ try
+ let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
+ call s:putCursorOnNode(targetNode, 0, 1)
+ redraw!
+ catch /NERDTree.BookmarkedNodeNotFound/
+ call s:echo("note - target node is not cached")
+ let bookmark = s:Bookmark.BookmarkFor(a:name)
+ let targetNode = s:TreeFileNode.New(bookmark.path)
+ endtry
+ if targetNode.path.isDirectory
+ call s:openExplorerFor(targetNode)
+ else
+ call s:openFileNode(targetNode)
+ endif
+endfunction
+" FUNCTION: s:openEntrySplit(forceKeepWindowOpen) {{{2
+"Opens the currently selected file from the explorer in a
+"new window
+"
+"args:
+"forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
+function! s:openEntrySplit(forceKeepWindowOpen)
+ let treenode = s:getSelectedNode()
+ if treenode != {}
+ call s:openFileNodeSplit(treenode)
+ if !a:forceKeepWindowOpen
+ call s:closeTreeIfQuitOnOpen()
+ endif
+ else
+ call s:echo("select a node first")
+ endif
+endfunction
+
+" FUNCTION: s:openExplorer() {{{2
+function! s:openExplorer()
+ let treenode = s:getSelectedDir()
+ if treenode != {}
+ call s:openExplorerFor(treenode)
+ else
+ call s:echo("select a node first")
+ endif
+endfunction
+
+" FUNCTION: s:openInNewTab(stayCurrentTab) {{{2
+" Opens the selected node or bookmark in a new tab
+" Args:
+" stayCurrentTab: if 1 then vim will stay in the current tab, if 0 then vim
+" will go to the tab where the new file is opened
+function! s:openInNewTab(stayCurrentTab)
+ let currentTab = tabpagenr()
+
+ let treenode = s:getSelectedNode()
+ if treenode != {}
+ if treenode.path.isDirectory
+ tabnew
+ call s:initNerdTree(treenode.path.strForOS(0))
+ else
+ exec "tabedit " . treenode.path.strForEditCmd()
+ endif
+ else
+ let bookmark = s:getSelectedBookmark()
+ if bookmark != {}
+ if bookmark.path.isDirectory
+ tabnew
+ call s:initNerdTree(bookmark.name)
+ else
+ exec "tabedit " . bookmark.path.strForEditCmd()
+ endif
+ endif
+ endif
+ if a:stayCurrentTab
+ exec "tabnext " . currentTab
+ endif
+endfunction
+
+" FUNCTION: s:openNodeRecursively() {{{2
+function! s:openNodeRecursively()
+ let treenode = s:getSelectedNode()
+ if treenode == {} || treenode.path.isDirectory == 0
+ call s:echo("Select a directory node first" )
+ else
+ call s:echo("Recursively opening node. Please wait...")
+ call treenode.openRecursively()
+ call s:renderView()
+ redraw
+ call s:echo("Recursively opening node. Please wait... DONE")
+ endif
+
+endfunction
+
+"FUNCTION: s:previewNode() {{{2
+function! s:previewNode(openNewWin)
+ if a:openNewWin
+ call s:openEntrySplit(1)
+ else
+ call s:activateNode(1)
+ end
+ call s:putCursorInTreeWin()
+endfunction
+
+" FUNCTION: s:revealBookmark(name) {{{2
+" put the cursor on the node associate with the given name
+function! s:revealBookmark(name)
+ try
+ let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
+ call s:putCursorOnNode(targetNode, 0, 1)
+ catch /NERDTree.BookmarkDoesntExist/
+ call s:echo("Bookmark isnt cached under the current root")
+ endtry
+endfunction
+" FUNCTION: s:refreshRoot() {{{2
+" Reloads the current root. All nodes below this will be lost and the root dir
+" will be reloaded.
+function! s:refreshRoot()
+ call s:echo("Refreshing the root node. This could take a while...")
+ call t:NERDTreeRoot.refresh()
+ call s:renderView()
+ redraw
+ call s:echo("Refreshing the root node. This could take a while... DONE")
+endfunction
+
+" FUNCTION: s:refreshCurrent() {{{2
+" refreshes the root for the current node
+function! s:refreshCurrent()
+ let treenode = s:getSelectedDir()
+ if treenode == {}
+ call s:echo("Refresh failed. Select a node first")
+ return
+ endif
+
+ call s:echo("Refreshing node. This could take a while...")
+ call treenode.refresh()
+ call s:renderView()
+ redraw
+ call s:echo("Refreshing node. This could take a while... DONE")
+endfunction
+" FUNCTION: s:renameCurrent() {{{2
+" allows the user to rename the current node
+function! s:renameCurrent()
+ let curNode = s:getSelectedNode()
+ if curNode == {}
+ call s:echo("Put the cursor on a node first" )
+ return
+ endif
+
+ let newNodePath = input("Rename the current node\n" .
+ \ "==========================================================\n" .
+ \ "Enter the new path for the node: \n" .
+ \ "", curNode.path.strForOS(0))
+
+ if newNodePath == ''
+ call s:echo("Node Renaming Aborted.")
+ return
+ endif
+
+ try
+ let bufnum = bufnr(curNode.path.str(0))
+
+ call curNode.rename(newNodePath)
+ call s:renderView()
+
+ "if the node is open in a buffer, ask the user if they want to
+ "close that buffer
+ if bufnum != -1
+ let prompt = "\nNode renamed.\n\nThe old file is open in buffer ". bufnum . (bufwinnr(bufnum) == -1 ? " (hidden)" : "") .". Delete this buffer? (yN)"
+ call s:promptToDelBuffer(bufnum, prompt)
+ endif
+
+ call s:putCursorOnNode(curNode, 1, 0)
+
+ redraw
+ catch /^NERDTree/
+ call s:echoWarning("Node Not Renamed.")
+ endtry
+endfunction
+
+" FUNCTION: s:showFileSystemMenu() {{{2
+function! s:showFileSystemMenu()
+ let curNode = s:getSelectedNode()
+ if curNode == {}
+ call s:echo("Put the cursor on a node first" )
+ return
+ endif
+
+
+ let prompt = "NERDTree Filesystem Menu\n" .
+ \ "==========================================================\n".
+ \ "Select the desired operation: \n" .
+ \ " (a)dd a childnode\n".
+ \ " (m)ove the current node\n".
+ \ " (d)elete the current node\n"
+ if s:Path.CopyingSupported()
+ let prompt = prompt . " (c)opy the current node\n\n"
+ else
+ let prompt = prompt . " \n"
+ endif
+
+ echo prompt
+
+ let choice = nr2char(getchar())
+
+ if choice ==? "a"
+ call s:insertNewNode()
+ elseif choice ==? "m"
+ call s:renameCurrent()
+ elseif choice ==? "d"
+ call s:deleteNode()
+ elseif choice ==? "c" && s:Path.CopyingSupported()
+ call s:copyNode()
+ endif
+endfunction
+
+" FUNCTION: s:toggleIgnoreFilter() {{{2
+" toggles the use of the NERDTreeIgnore option
+function! s:toggleIgnoreFilter()
+ let t:NERDTreeIgnoreEnabled = !t:NERDTreeIgnoreEnabled
+ call s:renderViewSavingPosition()
+ call s:centerView()
+endfunction
+
+" FUNCTION: s:toggleShowBookmarks() {{{2
+" toggles the display of bookmarks
+function! s:toggleShowBookmarks()
+ let t:NERDTreeShowBookmarks = !t:NERDTreeShowBookmarks
+ if t:NERDTreeShowBookmarks
+ call s:renderView()
+ call s:putCursorOnBookmarkTable()
+ else
+ call s:renderViewSavingPosition()
+ endif
+ call s:centerView()
+endfunction
+" FUNCTION: s:toggleShowFiles() {{{2
+" toggles the display of hidden files
+function! s:toggleShowFiles()
+ let t:NERDTreeShowFiles = !t:NERDTreeShowFiles
+ call s:renderViewSavingPosition()
+ call s:centerView()
+endfunction
+
+" FUNCTION: s:toggleShowHidden() {{{2
+" toggles the display of hidden files
+function! s:toggleShowHidden()
+ let t:NERDTreeShowHidden = !t:NERDTreeShowHidden
+ call s:renderViewSavingPosition()
+ call s:centerView()
+endfunction
+
+"FUNCTION: s:upDir(keepState) {{{2
+"moves the tree up a level
+"
+"Args:
+"keepState: 1 if the current root should be left open when the tree is
+"re-rendered
+function! s:upDir(keepState)
+ let cwd = t:NERDTreeRoot.path.str(0)
+ if cwd == "/" || cwd =~ '^[^/]..$'
+ call s:echo("already at top dir")
+ else
+ if !a:keepState
+ call t:NERDTreeRoot.close()
+ endif
+
+ let oldRoot = t:NERDTreeRoot
+
+ if empty(t:NERDTreeRoot.parent)
+ let path = t:NERDTreeRoot.path.getPathTrunk()
+ let newRoot = s:TreeDirNode.New(path)
+ call newRoot.open()
+ call newRoot.transplantChild(t:NERDTreeRoot)
+ let t:NERDTreeRoot = newRoot
+ else
+ let t:NERDTreeRoot = t:NERDTreeRoot.parent
+
+ endif
+
+ call s:renderView()
+ call s:putCursorOnNode(oldRoot, 0, 0)
+ endif
+endfunction
+
+
+"reset &cpo back to users setting
+let &cpo = s:old_cpo
+
+" vim: set sw=4 sts=4 et fdm=marker:
diff --git a/files/.vim/plugin/SyntaxFolds.vim b/files/.vim/plugin/SyntaxFolds.vim
new file mode 100755
index 0000000..27c622c
--- /dev/null
+++ b/files/.vim/plugin/SyntaxFolds.vim
@@ -0,0 +1,323 @@
+" ==============================================================================
+" File: syntaxFolds.vim
+" Author: Srinath Avadhanula
+" ( srinath@fastmail.fm )
+" Last Change: Sun Oct 27 01:00 AM 2002 PST
+" Description: Emulation of the syntax folding capability of vim using manual
+" folding
+"
+" This script provides an emulation of the syntax folding of vim using manual
+" folding. Just as in syntax folding, the folds are defined by regions. Each
+" region is specified by a call to FoldRegions() which accepts 4 parameters:
+"
+" call FoldRegions(startpat, endpat, startoff, endoff)
+"
+" startpat: a line matching this pattern defines the beginning of a fold.
+" endpat : a line matching this pattern defines the end of a fold.
+" startoff: this is the offset from the starting line at which folding will
+" actually start
+" endoff : like startoff, but gives the offset of the actual fold end from
+" the line satisfying endpat.
+" startoff and endoff are necessary when the folding region does
+" not have a specific end pattern corresponding to a start
+" pattern. for example in latex,
+" \begin{section}
+" defines the beginning of a section, but its not necessary to
+" have a corresponding
+" \end{section}
+" the section is assumed to end 1 line _before_ another section
+" starts.
+" startskip: a pattern which defines the beginning of a "skipped" region.
+"
+" For example, suppose we define a \itemize fold as follows:
+" startpat = '^\s*\\item',
+" endpat = '^\s*\\item\|^\s*\\end{\(enumerate\|itemize\|description\)}',
+" startoff = 0,
+" endoff = -1
+"
+" This defines a fold which starts with a line beginning with an
+" \item and ending one line before a line beginning with an
+" \item or \end{enumerate} etc.
+"
+" Then, as long as \item's are not nested things are fine.
+" However, once items begin to nest, the fold started by one
+" \item can end because of an \item in an \itemize
+" environment within this \item. i.e, the following can happen:
+"
+" \begin{itemize}
+" \item Some text <------- fold will start here
+" This item will contain a nested item
+" \begin{itemize} <----- fold will end here because next line contains \item...
+" \item Hello
+" \end{itemize} <----- ... instead of here.
+" \item Next item of the parent itemize
+" \end{itemize}
+"
+" Therefore, in order to completely define a folding item which
+" allows nesting, we need to also define a "skip" pattern.
+" startskip and end skip do that.
+" Leave '' when there is no nesting.
+" endskip: the pattern which defines the end of the "skip" pattern for
+" nested folds.
+"
+" Example:
+" 1. A syntax fold region for a latex section is
+" startpat = "\\section{"
+" endpat = "\\section{"
+" startoff = 0
+" endoff = -1
+" startskip = ''
+" endskip = ''
+" Note that the start and end patterns are thus the same and endoff has a
+" negative value to capture the effect of a section ending one line before
+" the next starts.
+" 2. A syntax fold region for the \itemize environment is:
+" startpat = '^\s*\\item',
+" endpat = '^\s*\\item\|^\s*\\end{\(enumerate\|itemize\|description\)}',
+" startoff = 0,
+" endoff = -1,
+" startskip = '^\s*\\begin{\(enumerate\|itemize\|description\)}',
+" endskip = '^\s*\\end{\(enumerate\|itemize\|description\)}'
+" Note the use of startskip and endskip to allow nesting.
+"
+"
+" Each time a call is made to FoldRegions(), all the regions (which might be
+" disjoint, but not nested) are folded up.
+" Nested folds can be created by successive calls to FoldRegions(). The first
+" call defines the region which is deepest in the folding. See MakeTexFolds()
+" for an idea of how this works for latex files.
+
+" Function: AddSyntaxFoldItem (start, end, startoff, endoff [, skipStart, skipEnd]) {{{
+function! AddSyntaxFoldItem(start, end, startoff, endoff, ...)
+ if a:0 > 0
+ let skipStart = a:1
+ let skipEnd = a:2
+ else
+ let skipStart = ''
+ let skipEnd = ''
+ end
+ if !exists('b:numFoldItems')
+ let b:numFoldItems = 0
+ end
+ let b:numFoldItems = b:numFoldItems + 1
+
+ exe 'let b:startPat_'.b:numFoldItems.' = a:start'
+ exe 'let b:endPat_'.b:numFoldItems.' = a:end'
+ exe 'let b:startOff_'.b:numFoldItems.' = a:startoff'
+ exe 'let b:endOff_'.b:numFoldItems.' = a:endoff'
+ exe 'let b:skipStartPat_'.b:numFoldItems.' = skipStart'
+ exe 'let b:skipEndPat_'.b:numFoldItems.' = skipEnd'
+endfunction
+
+
+" }}}
+" Function: MakeSyntaxFolds (force) {{{
+" Description: This function calls FoldRegions() several times with the
+" parameters specifying various regions resulting in a nested fold
+" structure for the file.
+function! MakeSyntaxFolds(force, ...)
+ if exists('b:doneFolding') && a:force == 0
+ return
+ end
+
+ let skipEndPattern = ''
+ if a:0 > 0
+ let line1 = a:1
+ let skipEndPattern = '\|'.a:2
+ else
+ let line1 = 1
+ let r = line('.')
+ let c = virtcol('.')
+
+ setlocal fdm=manual
+ normal! zE
+ end
+ if !exists('b:numFoldItems')
+ b:numFoldItems = 1000000
+ end
+
+ let i = 1
+
+ let maxline = line('.')
+
+ while exists('b:startPat_'.i) && i <= b:numFoldItems
+ exe 'let startPat = b:startPat_'.i
+ exe 'let endPat = b:endPat_'.i
+ exe 'let startOff = b:startOff_'.i
+ exe 'let endOff = b:endOff_'.i
+
+ let skipStart = ''
+ let skipEnd = ''
+ if exists('b:skipStartPat_'.i)
+ exe 'let skipStart = b:skipStartPat_'.i
+ exe 'let skipEnd = b:skipEndPat_'.i
+ end
+ exe line1
+ let lastLoc = line1
+
+ if skipStart != ''
+ call InitStack('BeginSkipArray')
+ call FoldRegionsWithSkip(startPat, endPat, startOff, endOff, skipStart, skipEnd, 1, line('$'))
+ " call PrintError('done folding ['.startPat.']')
+ else
+ call FoldRegionsWithNoSkip(startPat, endPat, startOff, endOff, 1, line('$'), '')
+ end
+
+ let i = i + 1
+ endwhile
+
+ exe maxline
+
+ if a:0 == 0
+ exe r
+ exe "normal! ".c."|"
+ if foldlevel(r) > 1
+ exe "normal! ".(foldlevel(r) - 1)."zo"
+ end
+ let b:doneFolding = 0
+ end
+endfunction
+
+
+" }}}
+" FoldRegionsWithSkip: folding things such as \item's which can be nested. {{{
+function! FoldRegionsWithSkip(startpat, endpat, startoff, endoff, startskip, endskip, line1, line2)
+ exe a:line1
+ " count the regions which have been skipped as we go along. do not want to
+ " create a fold which with a beginning or end line in one of the skipped
+ " regions.
+ let skippedRegions = ''
+
+ " start searching for either the starting pattern or the end pattern.
+ while search(a:startskip.'\|'.a:endskip, 'W')
+
+ if getline('.') =~ a:endskip
+
+ let lastBegin = Pop('BeginSkipArray')
+ " call PrintError('popping '.lastBegin.' from stack and folding till '.line('.'))
+ call FoldRegionsWithNoSkip(a:startpat, a:endpat, a:startoff, a:endoff, lastBegin, line('.'), skippedRegions)
+ let skippedRegions = skippedRegions.lastBegin.','.line('.').'|'
+
+
+ " if this is the beginning of a skip region, then, push this line as
+ " the beginning of a skipped region.
+ elseif getline('.') =~ a:startskip
+
+ " call PrintError('pushing '.line('.').' ['.getline('.').'] into stack')
+ call Push('BeginSkipArray', line('.'))
+
+ end
+ endwhile
+
+ " call PrintError('with skip starting at '.a:line1.' returning at line# '.line('.'))
+endfunction
+
+" }}}
+" FoldRegionsWithNoSkip: folding things such as \sections which do not nest. {{{
+function! FoldRegionsWithNoSkip(startpat, endpat, startoff, endoff, line1, line2, skippedRegions)
+ exe a:line1
+
+ " call PrintError('line1 = '.a:line1.', searching from '.line('.').'... for ['.a:startpat.'')
+ let lineBegin = s:MySearch(a:startpat, 'in')
+ " call PrintError('... and finding it at '.lineBegin)
+
+ while lineBegin <= a:line2
+ if IsInSkippedRegion(lineBegin, a:skippedRegions)
+ let lineBegin = s:MySearch(a:startpat, 'out')
+ " call PrintError(lineBegin.' is being skipped')
+ continue
+ end
+ let lineEnd = s:MySearch(a:endpat, 'out')
+ while IsInSkippedRegion(lineEnd, a:skippedRegions) && lineEnd <= a:line2
+ let lineEnd = s:MySearch(a:endpat, 'out')
+ endwhile
+ if lineEnd > a:line2
+ exe (lineBegin + a:startoff).','.a:line2.' fold'
+ break
+ else
+ " call PrintError ('for ['.a:startpat.'] '.(lineBegin + a:startoff).','.(lineEnd + a:endoff).' fold')
+ exe (lineBegin + a:startoff).','.(lineEnd + a:endoff).' fold'
+ end
+
+ " call PrintError('line1 = '.a:line1.', searching from '.line('.').'... for ['.a:startpat.'')
+ let lineBegin = s:MySearch(a:startpat, 'in')
+ " call PrintError('... and finding it at '.lineBegin)
+ endwhile
+
+ exe a:line2
+ return
+endfunction
+
+" }}}
+" InitStack: initialize a stack {{{
+function! InitStack(name)
+ exe 'let s:'.a:name.'_numElems = 0'
+endfunction
+" }}}
+" Push: push element into stack {{{
+function! Push(name, elem)
+ exe 'let numElems = s:'.a:name.'_numElems'
+ let numElems = numElems + 1
+ exe 'let s:'.a:name.'_Element_'.numElems.' = a:elem'
+ exe 'let s:'.a:name.'_numElems = numElems'
+endfunction
+" }}}
+" Pop: pops element off stack {{{
+function! Pop(name)
+ exe 'let numElems = s:'.a:name.'_numElems'
+ if numElems == 0
+ return ''
+ else
+ exe 'let ret = s:'.a:name.'_Element_'.numElems
+ let numElems = numElems - 1
+ exe 'let s:'.a:name.'_numElems = numElems'
+ return ret
+ end
+endfunction
+" }}}
+" MySearch: just like search(), but returns large number on failure {{{
+function! <SID>MySearch(pat, opt)
+ if a:opt == 'in'
+ if getline('.') =~ a:pat
+ let ret = line('.')
+ else
+ let ret = search(a:pat, 'W')
+ end
+ else
+ normal! $
+ let ret = search(a:pat, 'W')
+ end
+
+ if ret == 0
+ let ret = line('$') + 1
+ end
+ return ret
+endfunction
+" }}}
+" Function: IsInSkippedRegion (lnum, regions) {{{
+" Description: finds whether a given line number is within one of the regions
+" skipped.
+function! IsInSkippedRegion(lnum, regions)
+ let i = 1
+ let subset = s:Strntok(a:regions, '|', i)
+ while subset != ''
+ let n1 = s:Strntok(subset, ',', 1)
+ let n2 = s:Strntok(subset, ',', 2)
+ if a:lnum >= n1 && a:lnum <= n2
+ return 1
+ end
+
+ let subset = s:Strntok(a:regions, '|', i)
+ let i = i + 1
+ endwhile
+
+ return 0
+endfunction " }}}
+" Function: Strntok (string, tok, n) {{{
+" extract the n^th token from s seperated by tok.
+" example: Strntok('1,23,3', ',', 2) = 23
+fun! <SID>Strntok(s, tok, n)
+ return matchstr( a:s.a:tok[0], '\v(\zs([^'.a:tok.']*)\ze['.a:tok.']){'.a:n.'}')
+endfun " }}}
+
+" vim600:fdm=marker
diff --git a/files/.vim/plugin/filebrowser.vim b/files/.vim/plugin/filebrowser.vim
new file mode 100755
index 0000000..e9de049
--- /dev/null
+++ b/files/.vim/plugin/filebrowser.vim
@@ -0,0 +1,251 @@
+" filebrowser.vim: utility file for vim 6.2+
+"
+" Copyright: Srinath Avadhanula <srinath AT fastmail DOT fm>
+" Parts of this file are taken from explorer.vim which is a plugin file
+" distributed with vim under the Vim charityware license.
+" License: distributed under the Vim charityware license.
+"
+" Settings:
+" FB_CallBackFunction: the function name which gets called when the user
+" presses <cr> on a file-name in the file browser.
+" FB_AllowRegexp: A filename has to match this regexp to be displayed.
+" FB_RejectRegexp: If a filename matches this regexp, then its not displayed.
+" (Both these regexps are '' by default which means no filtering is
+" done).
+
+" line continuation used here.
+let s:save_cpo = &cpo
+set cpo&vim
+
+"======================================================================
+" Globally visible functions (API)
+"======================================================================
+" FB_OpenFileBrowser: opens a new buffer and displays the file list {{{
+" Description:
+function! FB_OpenFileBrowser(dir)
+ if !isdirectory(a:dir)
+ return
+ endif
+ if exists('s:FB_BufferNumber')
+ if bufwinnr(s:FB_BufferNumber) != -1
+ execute bufwinnr(s:FB_BufferNumber).' wincmd w'
+ return
+ endif
+ execute 'aboveleft split #'.s:FB_BufferNumber
+ else
+ aboveleft split __Choose_File__
+ let s:FB_BufferNumber = bufnr('%')
+ endif
+ call FB_DisplayFiles(a:dir)
+endfunction " }}}
+" FB_DisplayFiles: displays the files in a given directory {{{
+" Description:
+" Call this function only when the cursor is in a temporary buffer
+function! FB_DisplayFiles(dir)
+ if !isdirectory(a:dir)
+ return
+ endif
+ call s:FB_SetSilentSettings()
+ " make this a "scratch" buffer
+ call s:FB_SetScratchSettings()
+
+ let allowRegexp = s:FB_GetVar('FB_AllowRegexp', '')
+ let rejectRegexp = s:FB_GetVar('FB_RejectRegexp', '')
+
+ " change to the directory to make processing simpler.
+ execute "lcd ".a:dir
+ " delete everything in the buffer.
+ " IMPORTANT: we need to be in a scratch buffer
+ 0,$ d_
+
+ let allFilenames = glob('*')
+ let dispFiles = ""
+ let subDirs = "../\n"
+
+ let i = 1
+ while 1
+ let filename = s:FB_Strntok(allFilenames, "\n", i)
+ if filename == ''
+ break
+ endif
+ if isdirectory(filename)
+ let subDirs = subDirs.filename."/\n"
+ else
+ if allowRegexp != '' && filename !~ allowRegexp
+ elseif rejectRegexp != '' && filename =~ rejectRegexp
+ else
+ let dispFiles = dispFiles.filename."\n"
+ endif
+ endif
+ let i = i + 1
+ endwhile
+ 0put!=dispFiles
+ 0put!=subDirs
+ " delte the last empty line resulting from the put
+ $ d_
+
+ call s:FB_SetHighlighting()
+ call s:FB_DisplayHelp()
+ call s:FB_SetMaps()
+
+ " goto the first file/directory
+ 0
+ call search('^"=', 'w')
+ normal! j:<bs>
+
+ set nomodified nomodifiable
+
+ call s:FB_ResetSilentSettings()
+endfunction " }}}
+" FB_SetVar: sets script local variables from outside this script {{{
+" Description:
+function! FB_SetVar(varname, value)
+ let s:{a:varname} = a:value
+endfunction " }}}
+
+" FB_SetHighlighting: sets syntax highlighting for the buffer {{{
+" Description:
+" Origin: from explorer.vim in vim
+function! <SID>FB_SetHighlighting()
+ " Set up syntax highlighting
+ " Something wrong with the evaluation of the conditional though...
+ if has("syntax") && exists("g:syntax_on") && !has("syntax_items")
+ syn match browseSynopsis "^\"[ -].*"
+ syn match browseDirectory "[^\"].*/ "
+ syn match browseDirectory "[^\"].*/$"
+ syn match browseCurDir "^\"= .*$"
+ syn match browseSortBy "^\" Sorted by .*$" contains=browseSuffixInfo
+ syn match browseSuffixInfo "(.*)$" contained
+ syn match browseFilter "^\" Not Showing:.*$"
+ syn match browseFiletime "«\d\+$"
+
+ "hi def link browseSynopsis PreProc
+ hi def link browseSynopsis Special
+ hi def link browseDirectory Directory
+ hi def link browseCurDir Statement
+ hi def link browseSortBy String
+ hi def link browseSuffixInfo Type
+ hi def link browseFilter String
+ hi def link browseFiletime Ignore
+ hi def link browseSuffixes Type
+ endif
+endfunction " }}}
+" FB_SetMaps: sets buffer local maps {{{
+" Description:
+function! <SID>FB_SetMaps()
+ nnoremap <buffer> <silent> q :bdelete<cr>
+ nnoremap <buffer> <silent> C :call FB_DisplayFiles(getcwd())<CR>
+ nnoremap <buffer> <silent> <esc> :bdelete<cr>
+ nnoremap <buffer> <silent> <CR> :call <SID>FB_EditEntry()<CR>
+ nnoremap <buffer> <silent> ? :call <SID>FB_ToggleHelp()<CR>
+
+ " lock the user in this window
+ nnoremap <buffer> <C-w> <nop>
+endfunction " }}}
+" FB_SetSilentSettings: some settings which make things silent {{{
+" Description:
+" Origin: from explorer.vim distributed with vim.
+function! <SID>FB_SetSilentSettings()
+ let s:save_report=&report
+ let s:save_showcmd = &sc
+ set report=10000 noshowcmd
+endfunction
+" FB_ResetSilentSettings: reset settings set by FB_SetSilentSettings
+" Description:
+function! <SID>FB_ResetSilentSettings()
+ let &report=s:save_report
+ let &showcmd = s:save_showcmd
+endfunction " }}}
+" FB_SetScratchSettings: makes the present buffer a scratch buffer {{{
+" Description:
+function! <SID>FB_SetScratchSettings()
+ " Turn off the swapfile, set the buffer type so that it won't get
+ " written, and so that it will get deleted when it gets hidden.
+ setlocal noreadonly modifiable
+ setlocal noswapfile
+ setlocal buftype=nowrite
+ setlocal bufhidden=delete
+ " Don't wrap around long lines
+ setlocal nowrap
+endfunction
+
+" }}}
+" FB_ToggleHelp: toggles verbosity of help {{{
+" Description:
+function! <SID>FB_ToggleHelp()
+ let s:FB_VerboseHelp = 1 - s:FB_GetVar('FB_VerboseHelp', 0)
+
+ call FB_DisplayFiles('.')
+endfunction " }}}
+" FB_DisplayHelp: displays a helpful header {{{
+" Description:
+function! <SID>FB_DisplayHelp()
+ let verboseHelp = s:FB_GetVar('FB_VerboseHelp', 0)
+ if verboseHelp
+ let txt =
+ \ "\" <cr>: on file, choose the file and quit\n"
+ \ ."\" on dir, enter directory\n"
+ \ ."\" q/<esc>: quit without choosing\n"
+ \ ."\" C: change directory to getcwd()\n"
+ \ ."\" ?: toggle help verbosity\n"
+ \ ."\"= ".getcwd()
+ else
+ let txt = "\" ?: toggle help verbosity\n"
+ \ ."\"= ".getcwd()
+ endif
+ 0put!=txt
+endfunction " }}}
+
+" Handles various actions in the file-browser
+" FB_EditEntry: handles the user pressing <enter> on a line {{{
+" Description:
+function! <SID>FB_EditEntry()
+ let line = getline('.')
+
+ if isdirectory(line)
+ call FB_DisplayFiles(line)
+ endif
+
+ " If the user has a call back function defined on choosing a file, handle
+ " it.
+ let cbf = s:FB_GetVar('FB_CallBackFunction', '')
+ if cbf != '' && line !~ '^" ' && filereadable(line)
+ let fname = fnamemodify(line, ':p')
+ bdelete
+
+ let arguments = s:FB_GetVar('FB_CallBackFunctionArgs', '')
+ if arguments != ''
+ let arguments = ','.arguments
+ endif
+ call Tex_Debug('arguments = '.arguments, 'fb')
+ call Tex_Debug("call ".cbf."('".fname."'".arguments.')', 'fb')
+ exec "call ".cbf."('".fname."'".arguments.')'
+ endif
+endfunction " }}}
+
+" FB_Strntok (string, tok, n) {{{
+" extract the n^th token from s seperated by tok.
+" example: FB_Strntok('1,23,3', ',', 2) = 23
+fun! <SID>FB_Strntok(s, tok, n)
+ return matchstr( a:s.a:tok[0], '\v(\zs([^'.a:tok.']*)\ze['.a:tok.']){'.a:n.'}')
+endfun " }}}
+" FB_GetVar: gets the most local value of a variable {{{
+function! <SID>FB_GetVar(name, default)
+ if exists('s:'.a:name)
+ return s:{a:name}
+ elseif exists('w:'.a:name)
+ return w:{a:name}
+ elseif exists('b:'.a:name)
+ return b:{a:name}
+ elseif exists('g:'.a:name)
+ return g:{a:name}
+ else
+ return a:default
+ endif
+endfunction
+
+" }}}
+
+let &cpo = s:save_cpo
+
+" vim:fdm=marker:ff=unix:noet:ts=4:sw=4:nowrap
diff --git a/files/.vim/plugin/imaps.vim b/files/.vim/plugin/imaps.vim
new file mode 100755
index 0000000..d871aa1
--- /dev/null
+++ b/files/.vim/plugin/imaps.vim
@@ -0,0 +1,831 @@
+" File: imaps.vim
+" Authors: Srinath Avadhanula <srinath AT fastmail.fm>
+" Benji Fisher <benji AT member.AMS.org>
+"
+" WWW: http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/vim-latex/vimfiles/plugin/imaps.vim?only_with_tag=MAIN
+"
+" Description: insert mode template expander with cursor placement
+" while preserving filetype indentation.
+"
+" $Id: imaps.vim 997 2006-03-20 09:45:45Z srinathava $
+"
+" Documentation: {{{
+"
+" Motivation:
+" this script provides a way to generate insert mode mappings which do not
+" suffer from some of the problem of mappings and abbreviations while allowing
+" cursor placement after the expansion. It can alternatively be thought of as
+" a template expander.
+"
+" Consider an example. If you do
+"
+" imap lhs something
+"
+" then a mapping is set up. However, there will be the following problems:
+" 1. the 'ttimeout' option will generally limit how easily you can type the
+" lhs. if you type the left hand side too slowly, then the mapping will not
+" be activated.
+" 2. if you mistype one of the letters of the lhs, then the mapping is
+" deactivated as soon as you backspace to correct the mistake.
+"
+" If, in order to take care of the above problems, you do instead
+"
+" iab lhs something
+"
+" then the timeout problem is solved and so is the problem of mistyping.
+" however, abbreviations are only expanded after typing a non-word character.
+" which causes problems of cursor placement after the expansion and invariably
+" spurious spaces are inserted.
+"
+" Usage Example:
+" this script attempts to solve all these problems by providing an emulation
+" of imaps wchich does not suffer from its attendant problems. Because maps
+" are activated without having to press additional characters, therefore
+" cursor placement is possible. furthermore, file-type specific indentation is
+" preserved, because the rhs is expanded as if the rhs is typed in literally
+" by the user.
+"
+" The script already provides some default mappings. each "mapping" is of the
+" form:
+"
+" call IMAP (lhs, rhs, ft)
+"
+" Some characters in the RHS have special meaning which help in cursor
+" placement.
+"
+" Example One:
+"
+" call IMAP ("bit`", "\\begin{itemize}\<cr>\\item <++>\<cr>\\end{itemize}<++>", "tex")
+"
+" This effectively sets up the map for "bit`" whenever you edit a latex file.
+" When you type in this sequence of letters, the following text is inserted:
+"
+" \begin{itemize}
+" \item *
+" \end{itemize}<++>
+"
+" where * shows the cursor position. The cursor position after inserting the
+" text is decided by the position of the first "place-holder". Place holders
+" are special characters which decide cursor placement and movement. In the
+" example above, the place holder characters are <+ and +>. After you have typed
+" in the item, press <C-j> and you will be taken to the next set of <++>'s.
+" Therefore by placing the <++> characters appropriately, you can minimize the
+" use of movement keys.
+"
+" NOTE: Set g:Imap_UsePlaceHolders to 0 to disable placeholders altogether.
+" Set
+" g:Imap_PlaceHolderStart and g:Imap_PlaceHolderEnd
+" to something else if you want different place holder characters.
+" Also, b:Imap_PlaceHolderStart and b:Imap_PlaceHolderEnd override the values
+" of g:Imap_PlaceHolderStart and g:Imap_PlaceHolderEnd respectively. This is
+" useful for setting buffer specific place hoders.
+"
+" Example Two:
+" You can use the <C-r> command to insert dynamic elements such as dates.
+" call IMAP ('date`', "\<c-r>=strftime('%b %d %Y')\<cr>", '')
+"
+" sets up the map for date` to insert the current date.
+"
+"--------------------------------------%<--------------------------------------
+" Bonus: This script also provides a command Snip which puts tearoff strings,
+" '----%<----' above and below the visually selected range of lines. The
+" length of the string is chosen to be equal to the longest line in the range.
+" Recommended Usage:
+" '<,'>Snip
+"--------------------------------------%<--------------------------------------
+" }}}
+
+" line continuation used here.
+let s:save_cpo = &cpo
+set cpo&vim
+
+" ==============================================================================
+" Script Options / Variables
+" ==============================================================================
+" Options {{{
+if !exists('g:Imap_StickyPlaceHolders')
+ let g:Imap_StickyPlaceHolders = 1
+endif
+if !exists('g:Imap_DeleteEmptyPlaceHolders')
+ let g:Imap_DeleteEmptyPlaceHolders = 1
+endif
+" }}}
+" Variables {{{
+" s:LHS_{ft}_{char} will be generated automatically. It will look like
+" s:LHS_tex_o = 'fo\|foo\|boo' and contain all mapped sequences ending in "o".
+" s:Map_{ft}_{lhs} will be generated automatically. It will look like
+" s:Map_c_foo = 'for(<++>; <++>; <++>)', the mapping for "foo".
+"
+" }}}
+
+" ==============================================================================
+" functions for easy insert mode mappings.
+" ==============================================================================
+" IMAP: Adds a "fake" insert mode mapping. {{{
+" For example, doing
+" IMAP('abc', 'def' ft)
+" will mean that if the letters abc are pressed in insert mode, then
+" they will be replaced by def. If ft != '', then the "mapping" will be
+" specific to the files of type ft.
+"
+" Using IMAP has a few advantages over simply doing:
+" imap abc def
+" 1. with imap, if you begin typing abc, the cursor will not advance and
+" long as there is a possible completion, the letters a, b, c will be
+" displayed on on top of the other. using this function avoids that.
+" 2. with imap, if a backspace or arrow key is pressed before completing
+" the word, then the mapping is lost. this function allows movement.
+" (this ofcourse means that this function is only limited to
+" left-hand-sides which do not have movement keys or unprintable
+" characters)
+" It works by only mapping the last character of the left-hand side.
+" when this character is typed in, then a reverse lookup is done and if
+" the previous characters consititute the left hand side of the mapping,
+" the previously typed characters and erased and the right hand side is
+" inserted
+
+" IMAP: set up a filetype specific mapping.
+" Description:
+" "maps" the lhs to rhs in files of type 'ft'. If supplied with 2
+" additional arguments, then those are assumed to be the placeholder
+" characters in rhs. If unspecified, then the placeholder characters
+" are assumed to be '<+' and '+>' These placeholder characters in
+" a:rhs are replaced with the users setting of
+" [bg]:Imap_PlaceHolderStart and [bg]:Imap_PlaceHolderEnd settings.
+"
+function! IMAP(lhs, rhs, ft, ...)
+
+ " Find the place holders to save for IMAP_PutTextWithMovement() .
+ if a:0 < 2
+ let phs = '<+'
+ let phe = '+>'
+ else
+ let phs = a:1
+ let phe = a:2
+ endif
+
+ let hash = s:Hash(a:lhs)
+ let s:Map_{a:ft}_{hash} = a:rhs
+ let s:phs_{a:ft}_{hash} = phs
+ let s:phe_{a:ft}_{hash} = phe
+
+ " Add a:lhs to the list of left-hand sides that end with lastLHSChar:
+ let lastLHSChar = a:lhs[strlen(a:lhs)-1]
+ let hash = s:Hash(lastLHSChar)
+ if !exists("s:LHS_" . a:ft . "_" . hash)
+ let s:LHS_{a:ft}_{hash} = escape(a:lhs, '\')
+ else
+ let s:LHS_{a:ft}_{hash} = escape(a:lhs, '\') .'\|'. s:LHS_{a:ft}_{hash}
+ endif
+
+ " map only the last character of the left-hand side.
+ if lastLHSChar == ' '
+ let lastLHSChar = '<space>'
+ end
+ exe 'inoremap <silent>'
+ \ escape(lastLHSChar, '|')
+ \ '<C-r>=<SID>LookupCharacter("' .
+ \ escape(lastLHSChar, '\|"') .
+ \ '")<CR>'
+endfunction
+
+" }}}
+" IMAP_list: list the rhs and place holders corresponding to a:lhs {{{
+"
+" Added mainly for debugging purposes, but maybe worth keeping.
+function! IMAP_list(lhs)
+ let char = a:lhs[strlen(a:lhs)-1]
+ let charHash = s:Hash(char)
+ if exists("s:LHS_" . &ft ."_". charHash) && a:lhs =~ s:LHS_{&ft}_{charHash}
+ let ft = &ft
+ elseif exists("s:LHS__" . charHash) && a:lhs =~ s:LHS__{charHash}
+ let ft = ""
+ else
+ return ""
+ endif
+ let hash = s:Hash(a:lhs)
+ return "rhs = " . s:Map_{ft}_{hash} . " place holders = " .
+ \ s:phs_{ft}_{hash} . " and " . s:phe_{ft}_{hash}
+endfunction
+" }}}
+" LookupCharacter: inserts mapping corresponding to this character {{{
+"
+" This function extracts from s:LHS_{&ft}_{a:char} or s:LHS__{a:char}
+" the longest lhs matching the current text. Then it replaces lhs with the
+" corresponding rhs saved in s:Map_{ft}_{lhs} .
+" The place-holder variables are passed to IMAP_PutTextWithMovement() .
+function! s:LookupCharacter(char)
+ if IMAP_GetVal('Imap_FreezeImap', 0) == 1
+ return a:char
+ endif
+ let charHash = s:Hash(a:char)
+
+ " The line so far, including the character that triggered this function:
+ let text = strpart(getline("."), 0, col(".")-1) . a:char
+ " Prefer a local map to a global one, even if the local map is shorter.
+ " Is this what we want? Do we care?
+ " Use '\V' (very no-magic) so that only '\' is special, and it was already
+ " escaped when building up s:LHS_{&ft}_{charHash} .
+ if exists("s:LHS_" . &ft . "_" . charHash)
+ \ && text =~ "\\C\\V\\(" . s:LHS_{&ft}_{charHash} . "\\)\\$"
+ let ft = &ft
+ elseif exists("s:LHS__" . charHash)
+ \ && text =~ "\\C\\V\\(" . s:LHS__{charHash} . "\\)\\$"
+ let ft = ""
+ else
+ " If this is a character which could have been used to trigger an
+ " abbreviation, check if an abbreviation exists.
+ if a:char !~ '\k'
+ let lastword = matchstr(getline('.'), '\k\+$', '')
+ call IMAP_Debug('getting lastword = ['.lastword.']', 'imap')
+ if lastword != ''
+ " An extremeley wierd way to get around the fact that vim
+ " doesn't have the equivalent of the :mapcheck() function for
+ " abbreviations.
+ let _a = @a
+ exec "redir @a | silent! iab ".lastword." | redir END"
+ let abbreviationRHS = matchstr(@a."\n", "\n".'i\s\+'.lastword.'\s\+@\?\zs.*\ze'."\n")
+
+ call IMAP_Debug('getting abbreviationRHS = ['.abbreviationRHS.']', 'imap')
+
+ if @a =~ "No abbreviation found" || abbreviationRHS == ""
+ let @a = _a
+ return a:char
+ endif
+
+ let @a = _a
+ let abbreviationRHS = escape(abbreviationRHS, '\<"')
+ exec 'let abbreviationRHS = "'.abbreviationRHS.'"'
+
+ let lhs = lastword.a:char
+ let rhs = abbreviationRHS.a:char
+ let phs = IMAP_GetPlaceHolderStart()
+ let phe = IMAP_GetPlaceHolderEnd()
+ else
+ return a:char
+ endif
+ else
+ return a:char
+ endif
+ endif
+ " Find the longest left-hand side that matches the line so far.
+ " matchstr() returns the match that starts first. This automatically
+ " ensures that the longest LHS is used for the mapping.
+ if !exists('lhs') || !exists('rhs')
+ let lhs = matchstr(text, "\\C\\V\\(" . s:LHS_{ft}_{charHash} . "\\)\\$")
+ let hash = s:Hash(lhs)
+ let rhs = s:Map_{ft}_{hash}
+ let phs = s:phs_{ft}_{hash}
+ let phe = s:phe_{ft}_{hash}
+ endif
+
+ if strlen(lhs) == 0
+ return a:char
+ endif
+ " enough back-spaces to erase the left-hand side; -1 for the last
+ " character typed:
+ let bs = substitute(strpart(lhs, 1), ".", "\<bs>", "g")
+ return bs . IMAP_PutTextWithMovement(rhs, phs, phe)
+endfunction
+
+" }}}
+" IMAP_PutTextWithMovement: returns the string with movement appended {{{
+" Description:
+" If a:str contains "placeholders", then appends movement commands to
+" str in a way that the user moves to the first placeholder and enters
+" insert or select mode. If supplied with 2 additional arguments, then
+" they are assumed to be the placeholder specs. Otherwise, they are
+" assumed to be '<+' and '+>'. These placeholder chars are replaced
+" with the users settings of [bg]:Imap_PlaceHolderStart and
+" [bg]:Imap_PlaceHolderEnd.
+function! IMAP_PutTextWithMovement(str, ...)
+
+ " The placeholders used in the particular input string. These can be
+ " different from what the user wants to use.
+ if a:0 < 2
+ let phs = '<+'
+ let phe = '+>'
+ else
+ let phs = escape(a:1, '\')
+ let phe = escape(a:2, '\')
+ endif
+
+ let text = a:str
+
+ " The user's placeholder settings.
+ let phsUser = IMAP_GetPlaceHolderStart()
+ let pheUser = IMAP_GetPlaceHolderEnd()
+
+ " Problem: depending on the setting of the 'encoding' option, a character
+ " such as "\xab" may not match itself. We try to get around this by
+ " changing the encoding of all our strings. At the end, we have to
+ " convert text back.
+ let phsEnc = s:Iconv(phs, "encode")
+ let pheEnc = s:Iconv(phe, "encode")
+ let phsUserEnc = s:Iconv(phsUser, "encode")
+ let pheUserEnc = s:Iconv(pheUser, "encode")
+ let textEnc = s:Iconv(text, "encode")
+ if textEnc != text
+ let textEncoded = 1
+ else
+ let textEncoded = 0
+ endif
+
+ let pattern = '\V\(\.\{-}\)' .phs. '\(\.\{-}\)' .phe. '\(\.\*\)'
+ " If there are no placeholders, just return the text.
+ if textEnc !~ pattern
+ call IMAP_Debug('Not getting '.phs.' and '.phe.' in '.textEnc, 'imap')
+ return text
+ endif
+ " Break text up into "initial <+template+> final"; any piece may be empty.
+ let initialEnc = substitute(textEnc, pattern, '\1', '')
+ let templateEnc = substitute(textEnc, pattern, '\2', '')
+ let finalEnc = substitute(textEnc, pattern, '\3', '')
+
+ " If the user does not want to use placeholders, then remove all but the
+ " first placeholder.
+ " Otherwise, replace all occurences of the placeholders here with the
+ " user's choice of placeholder settings.
+ if exists('g:Imap_UsePlaceHolders') && !g:Imap_UsePlaceHolders
+ let finalEnc = substitute(finalEnc, '\V'.phs.'\.\{-}'.phe, '', 'g')
+ else
+ let finalEnc = substitute(finalEnc, '\V'.phs.'\(\.\{-}\)'.phe,
+ \ phsUserEnc.'\1'.pheUserEnc, 'g')
+ endif
+
+ " The substitutions are done, so convert back, if necessary.
+ if textEncoded
+ let initial = s:Iconv(initialEnc, "decode")
+ let template = s:Iconv(templateEnc, "decode")
+ let final = s:Iconv(finalEnc, "decode")
+ else
+ let initial = initialEnc
+ let template = templateEnc
+ let final = finalEnc
+ endif
+
+ " Build up the text to insert:
+ " 1. the initial text plus an extra character;
+ " 2. go to Normal mode with <C-\><C-N>, so it works even if 'insertmode'
+ " is set, and mark the position;
+ " 3. replace the extra character with tamplate and final;
+ " 4. back to Normal mode and restore the cursor position;
+ " 5. call IMAP_Jumpfunc().
+ let template = phsUser . template . pheUser
+ " Old trick: insert and delete a character to get the same behavior at
+ " start, middle, or end of line and on empty lines.
+ let text = initial . "X\<C-\>\<C-N>:call IMAP_Mark('set')\<CR>\"_s"
+ let text = text . template . final
+ let text = text . "\<C-\>\<C-N>:call IMAP_Mark('go')\<CR>"
+ let text = text . "i\<C-r>=IMAP_Jumpfunc('', 1)\<CR>"
+
+ call IMAP_Debug('IMAP_PutTextWithMovement: text = ['.text.']', 'imap')
+ return text
+endfunction
+
+" }}}
+" IMAP_Jumpfunc: takes user to next <+place-holder+> {{{
+" Author: Luc Hermitte
+" Arguments:
+" direction: flag for the search() function. If set to '', search forwards,
+" if 'b', then search backwards. See the {flags} argument of the
+" |search()| function for valid values.
+" inclusive: In vim, the search() function is 'exclusive', i.e we always goto
+" next cursor match even if there is a match starting from the
+" current cursor position. Setting this argument to 1 makes
+" IMAP_Jumpfunc() also respect a match at the current cursor
+" position. 'inclusive'ness is necessary for IMAP() because a
+" placeholder string can occur at the very beginning of a map which
+" we want to select.
+" We use a non-zero value only in special conditions. Most mappings
+" should use a zero value.
+function! IMAP_Jumpfunc(direction, inclusive)
+
+ " The user's placeholder settings.
+ let phsUser = IMAP_GetPlaceHolderStart()
+ let pheUser = IMAP_GetPlaceHolderEnd()
+
+ let searchString = ''
+ " If this is not an inclusive search or if it is inclusive, but the
+ " current cursor position does not contain a placeholder character, then
+ " search for the placeholder characters.
+ if !a:inclusive || strpart(getline('.'), col('.')-1) !~ '\V\^'.phsUser
+ let searchString = '\V'.phsUser.'\_.\{-}'.pheUser
+ endif
+
+ " If we didn't find any placeholders return quietly.
+ if searchString != '' && !search(searchString, a:direction)
+ return ''
+ endif
+
+ " Open any closed folds and make this part of the text visible.
+ silent! foldopen!
+
+ " Calculate if we have an empty placeholder or if it contains some
+ " description.
+ let template =
+ \ matchstr(strpart(getline('.'), col('.')-1),
+ \ '\V\^'.phsUser.'\zs\.\{-}\ze\('.pheUser.'\|\$\)')
+ let placeHolderEmpty = !strlen(template)
+
+ " If we are selecting in exclusive mode, then we need to move one step to
+ " the right
+ let extramove = ''
+ if &selection == 'exclusive'
+ let extramove = 'l'
+ endif
+
+ " Select till the end placeholder character.
+ let movement = "\<C-o>v/\\V".pheUser."/e\<CR>".extramove
+
+ " First remember what the search pattern was. s:RemoveLastHistoryItem will
+ " reset @/ to this pattern so we do not create new highlighting.
+ let g:Tex_LastSearchPattern = @/
+
+ " Now either goto insert mode or select mode.
+ if placeHolderEmpty && g:Imap_DeleteEmptyPlaceHolders
+ " delete the empty placeholder into the blackhole.
+ return movement."\"_c\<C-o>:".s:RemoveLastHistoryItem."\<CR>"
+ else
+ return movement."\<C-\>\<C-N>:".s:RemoveLastHistoryItem."\<CR>gv\<C-g>"
+ endif
+
+endfunction
+
+" }}}
+" Maps for IMAP_Jumpfunc {{{
+"
+" These mappings use <Plug> and thus provide for easy user customization. When
+" the user wants to map some other key to jump forward, he can do for
+" instance:
+" nmap ,f <plug>IMAP_JumpForward
+" etc.
+
+" jumping forward and back in insert mode.
+imap <silent> <Plug>IMAP_JumpForward <c-r>=IMAP_Jumpfunc('', 0)<CR>
+imap <silent> <Plug>IMAP_JumpBack <c-r>=IMAP_Jumpfunc('b', 0)<CR>
+
+" jumping in normal mode
+nmap <silent> <Plug>IMAP_JumpForward i<c-r>=IMAP_Jumpfunc('', 0)<CR>
+nmap <silent> <Plug>IMAP_JumpBack i<c-r>=IMAP_Jumpfunc('b', 0)<CR>
+
+" deleting the present selection and then jumping forward.
+vmap <silent> <Plug>IMAP_DeleteAndJumpForward "_<Del>i<c-r>=IMAP_Jumpfunc('', 0)<CR>
+vmap <silent> <Plug>IMAP_DeleteAndJumpBack "_<Del>i<c-r>=IMAP_Jumpfunc('b', 0)<CR>
+
+" jumping forward without deleting present selection.
+vmap <silent> <Plug>IMAP_JumpForward <C-\><C-N>i<c-r>=IMAP_Jumpfunc('', 0)<CR>
+vmap <silent> <Plug>IMAP_JumpBack <C-\><C-N>`<i<c-r>=IMAP_Jumpfunc('b', 0)<CR>
+
+" }}}
+" Default maps for IMAP_Jumpfunc {{{
+" map only if there is no mapping already. allows for user customization.
+" NOTE: Default mappings for jumping to the previous placeholder are not
+" provided. It is assumed that if the user will create such mappings
+" hself if e so desires.
+if !hasmapto('<Plug>IMAP_JumpForward', 'i')
+ imap <C-J> <Plug>IMAP_JumpForward
+endif
+if !hasmapto('<Plug>IMAP_JumpForward', 'n')
+ nmap <C-J> <Plug>IMAP_JumpForward
+endif
+if exists('g:Imap_StickyPlaceHolders') && g:Imap_StickyPlaceHolders
+ if !hasmapto('<Plug>IMAP_JumpForward', 'v')
+ vmap <C-J> <Plug>IMAP_JumpForward
+ endif
+else
+ if !hasmapto('<Plug>IMAP_DeleteAndJumpForward', 'v')
+ vmap <C-J> <Plug>IMAP_DeleteAndJumpForward
+ endif
+endif
+" }}}
+
+nmap <silent> <script> <plug><+SelectRegion+> `<v`>
+
+" ==============================================================================
+" enclosing selected region.
+" ==============================================================================
+" VEnclose: encloses the visually selected region with given arguments {{{
+" Description: allows for differing action based on visual line wise
+" selection or visual characterwise selection. preserves the
+" marks and search history.
+function! VEnclose(vstart, vend, VStart, VEnd)
+
+ " its characterwise if
+ " 1. characterwise selection and valid values for vstart and vend.
+ " OR
+ " 2. linewise selection and invalid values for VStart and VEnd
+ if (visualmode() == 'v' && (a:vstart != '' || a:vend != '')) || (a:VStart == '' && a:VEnd == '')
+
+ let newline = ""
+ let _r = @r
+
+ let normcmd = "normal! \<C-\>\<C-n>`<v`>\"_s"
+
+ exe "normal! \<C-\>\<C-n>`<v`>\"ry"
+ if @r =~ "\n$"
+ let newline = "\n"
+ let @r = substitute(@r, "\n$", '', '')
+ endif
+
+ " In exclusive selection, we need to select an extra character.
+ if &selection == 'exclusive'
+ let movement = 8
+ else
+ let movement = 7
+ endif
+ let normcmd = normcmd.
+ \ a:vstart."!!mark!!".a:vend.newline.
+ \ "\<C-\>\<C-N>?!!mark!!\<CR>v".movement."l\"_s\<C-r>r\<C-\>\<C-n>"
+
+ " this little if statement is because till very recently, vim used to
+ " report col("'>") > length of selected line when `> is $. on some
+ " systems it reports a -ve number.
+ if col("'>") < 0 || col("'>") > strlen(getline("'>"))
+ let lastcol = strlen(getline("'>"))
+ else
+ let lastcol = col("'>")
+ endif
+ if lastcol - col("'<") != 0
+ let len = lastcol - col("'<")
+ else
+ let len = ''
+ endif
+
+ " the next normal! is for restoring the marks.
+ let normcmd = normcmd."`<v".len."l\<C-\>\<C-N>"
+
+ " First remember what the search pattern was. s:RemoveLastHistoryItem
+ " will reset @/ to this pattern so we do not create new highlighting.
+ let g:Tex_LastSearchPattern = @/
+
+ silent! exe normcmd
+ " this is to restore the r register.
+ let @r = _r
+ " and finally, this is to restore the search history.
+ execute s:RemoveLastHistoryItem
+
+ else
+
+ exec 'normal! `<O'.a:VStart."\<C-\>\<C-n>"
+ exec 'normal! `>o'.a:VEnd."\<C-\>\<C-n>"
+ if &indentexpr != ''
+ silent! normal! `<kV`>j=
+ endif
+ silent! normal! `>
+ endif
+endfunction
+
+" }}}
+" ExecMap: adds the ability to correct an normal/visual mode mapping. {{{
+" Author: Hari Krishna Dara <hari_vim@yahoo.com>
+" Reads a normal mode mapping at the command line and executes it with the
+" given prefix. Press <BS> to correct and <Esc> to cancel.
+function! ExecMap(prefix, mode)
+ " Temporarily remove the mapping, otherwise it will interfere with the
+ " mapcheck call below:
+ let myMap = maparg(a:prefix, a:mode)
+ exec a:mode."unmap ".a:prefix
+
+ " Generate a line with spaces to clear the previous message.
+ let i = 1
+ let clearLine = "\r"
+ while i < &columns
+ let clearLine = clearLine . ' '
+ let i = i + 1
+ endwhile
+
+ let mapCmd = a:prefix
+ let foundMap = 0
+ let breakLoop = 0
+ echon "\rEnter Map: " . mapCmd
+ while !breakLoop
+ let char = getchar()
+ if char !~ '^\d\+$'
+ if char == "\<BS>"
+ let mapCmd = strpart(mapCmd, 0, strlen(mapCmd) - 1)
+ endif
+ else " It is the ascii code.
+ let char = nr2char(char)
+ if char == "\<Esc>"
+ let breakLoop = 1
+ else
+ let mapCmd = mapCmd . char
+ if maparg(mapCmd, a:mode) != ""
+ let foundMap = 1
+ let breakLoop = 1
+ elseif mapcheck(mapCmd, a:mode) == ""
+ let mapCmd = strpart(mapCmd, 0, strlen(mapCmd) - 1)
+ endif
+ endif
+ endif
+ echon clearLine
+ echon "\rEnter Map: " . mapCmd
+ endwhile
+ if foundMap
+ if a:mode == 'v'
+ " use a plug to select the region instead of using something like
+ " `<v`> to avoid problems caused by some of the characters in
+ " '`<v`>' being mapped.
+ let gotoc = "\<plug><+SelectRegion+>"
+ else
+ let gotoc = ''
+ endif
+ exec "normal ".gotoc.mapCmd
+ endif
+ exec a:mode.'noremap '.a:prefix.' '.myMap
+endfunction
+
+" }}}
+
+" ==============================================================================
+" helper functions
+" ==============================================================================
+" Strntok: extract the n^th token from a list {{{
+" example: Strntok('1,23,3', ',', 2) = 23
+fun! <SID>Strntok(s, tok, n)
+ return matchstr( a:s.a:tok[0], '\v(\zs([^'.a:tok.']*)\ze['.a:tok.']){'.a:n.'}')
+endfun
+
+" }}}
+" s:RemoveLastHistoryItem: removes last search item from search history {{{
+" Description: Execute this string to clean up the search history.
+let s:RemoveLastHistoryItem = ':call histdel("/", -1)|let @/=g:Tex_LastSearchPattern'
+
+" }}}
+" s:Hash: Return a version of a string that can be used as part of a variable" {{{
+" name.
+" Converts every non alphanumeric character into _{ascii}_ where {ascii} is
+" the ASCII code for that character...
+fun! s:Hash(text)
+ return substitute(a:text, '\([^[:alnum:]]\)',
+ \ '\="_".char2nr(submatch(1))."_"', 'g')
+endfun
+"" }}}
+" IMAP_GetPlaceHolderStart and IMAP_GetPlaceHolderEnd: "{{{
+" return the buffer local placeholder variables, or the global one, or the default.
+function! IMAP_GetPlaceHolderStart()
+ if exists("b:Imap_PlaceHolderStart") && strlen(b:Imap_PlaceHolderEnd)
+ return b:Imap_PlaceHolderStart
+ elseif exists("g:Imap_PlaceHolderStart") && strlen(g:Imap_PlaceHolderEnd)
+ return g:Imap_PlaceHolderStart
+ else
+ return "<+"
+endfun
+function! IMAP_GetPlaceHolderEnd()
+ if exists("b:Imap_PlaceHolderEnd") && strlen(b:Imap_PlaceHolderEnd)
+ return b:Imap_PlaceHolderEnd
+ elseif exists("g:Imap_PlaceHolderEnd") && strlen(g:Imap_PlaceHolderEnd)
+ return g:Imap_PlaceHolderEnd
+ else
+ return "+>"
+endfun
+" }}}
+" s:Iconv: a wrapper for iconv()" {{{
+" Problem: after
+" let text = "\xab"
+" (or using the raw 8-bit ASCII character in a file with 'fenc' set to
+" "latin1") if 'encoding' is set to utf-8, then text does not match itself:
+" echo text =~ text
+" returns 0.
+" Solution: When this happens, a re-encoded version of text does match text:
+" echo iconv(text, "latin1", "utf8") =~ text
+" returns 1. In this case, convert text to utf-8 with iconv().
+" TODO: Is it better to use &encoding instead of "utf8"? Internally, vim
+" uses utf-8, and can convert between latin1 and utf-8 even when compiled with
+" -iconv, so let's try using utf-8.
+" Arguments:
+" a:text = text to be encoded or decoded
+" a:mode = "encode" (latin1 to utf8) or "decode" (utf8 to latin1)
+" Caution: do not encode and then decode without checking whether the text
+" has changed, becuase of the :if clause in encoding!
+function! s:Iconv(text, mode)
+ if a:mode == "decode"
+ return iconv(a:text, "utf8", "latin1")
+ endif
+ if a:text =~ '\V\^' . escape(a:text, '\') . '\$'
+ return a:text
+ endif
+ let textEnc = iconv(a:text, "latin1", "utf8")
+ if textEnc !~ '\V\^' . escape(a:text, '\') . '\$'
+ call IMAP_Debug('Encoding problems with text '.a:text.' ', 'imap')
+ endif
+ return textEnc
+endfun
+"" }}}
+" IMAP_Debug: interface to Tex_Debug if available, otherwise emulate it {{{
+" Description:
+" Do not want a memory leak! Set this to zero so that imaps always
+" starts out in a non-debugging mode.
+if !exists('g:Imap_Debug')
+ let g:Imap_Debug = 0
+endif
+function! IMAP_Debug(string, pattern)
+ if !g:Imap_Debug
+ return
+ endif
+ if exists('*Tex_Debug')
+ call Tex_Debug(a:string, a:pattern)
+ else
+ if !exists('s:debug_'.a:pattern)
+ let s:debug_{a:pattern} = a:string
+ else
+ let s:debug_{a:pattern} = s:debug_{a:pattern}.a:string
+ endif
+ endif
+endfunction " }}}
+" IMAP_DebugClear: interface to Tex_DebugClear if avaialable, otherwise emulate it {{{
+" Description:
+function! IMAP_DebugClear(pattern)
+ if exists('*Tex_DebugClear')
+ call Tex_DebugClear(a:pattern)
+ else
+ let s:debug_{a:pattern} = ''
+ endif
+endfunction " }}}
+" IMAP_PrintDebug: interface to Tex_DebugPrint if avaialable, otherwise emulate it {{{
+" Description:
+function! IMAP_PrintDebug(pattern)
+ if exists('*Tex_PrintDebug')
+ call Tex_PrintDebug(a:pattern)
+ else
+ if exists('s:debug_'.a:pattern)
+ echo s:debug_{a:pattern}
+ endif
+ endif
+endfunction " }}}
+" IMAP_Mark: Save the cursor position (if a:action == 'set') in a" {{{
+" script-local variable; restore this position if a:action == 'go'.
+let s:Mark = "(0,0)"
+let s:initBlanks = ''
+function! IMAP_Mark(action)
+ if a:action == 'set'
+ let s:Mark = "(" . line(".") . "," . col(".") . ")"
+ let s:initBlanks = matchstr(getline('.'), '^\s*')
+ elseif a:action == 'go'
+ execute "call cursor" s:Mark
+ let blanksNow = matchstr(getline('.'), '^\s*')
+ if strlen(blanksNow) > strlen(s:initBlanks)
+ execute 'silent! normal! '.(strlen(blanksNow) - strlen(s:initBlanks)).'l'
+ elseif strlen(blanksNow) < strlen(s:initBlanks)
+ execute 'silent! normal! '.(strlen(s:initBlanks) - strlen(blanksNow)).'h'
+ endif
+ endif
+endfunction "" }}}
+" IMAP_GetVal: gets the value of a variable {{{
+" Description: first checks window local, then buffer local etc.
+function! IMAP_GetVal(name, ...)
+ if a:0 > 0
+ let default = a:1
+ else
+ let default = ''
+ endif
+ if exists('w:'.a:name)
+ return w:{a:name}
+ elseif exists('b:'.a:name)
+ return b:{a:name}
+ elseif exists('g:'.a:name)
+ return g:{a:name}
+ else
+ return default
+ endif
+endfunction " }}}
+
+" ==============================================================================
+" A bonus function: Snip()
+" ==============================================================================
+" Snip: puts a scissor string above and below block of text {{{
+" Desciption:
+"-------------------------------------%<-------------------------------------
+" this puts a the string "--------%<---------" above and below the visually
+" selected block of lines. the length of the 'tearoff' string depends on the
+" maximum string length in the selected range. this is an aesthetically more
+" pleasing alternative instead of hardcoding a length.
+"-------------------------------------%<-------------------------------------
+function! <SID>Snip() range
+ let i = a:firstline
+ let maxlen = -2
+ " find out the maximum virtual length of each line.
+ while i <= a:lastline
+ exe i
+ let length = virtcol('$')
+ let maxlen = (length > maxlen ? length : maxlen)
+ let i = i + 1
+ endwhile
+ let maxlen = (maxlen > &tw && &tw != 0 ? &tw : maxlen)
+ let half = maxlen/2
+ exe a:lastline
+ " put a string below
+ exe "norm! o\<esc>".(half - 1)."a-\<esc>A%<\<esc>".(half - 1)."a-"
+ " and above. its necessary to put the string below the block of lines
+ " first because that way the first line number doesnt change...
+ exe a:firstline
+ exe "norm! O\<esc>".(half - 1)."a-\<esc>A%<\<esc>".(half - 1)."a-"
+endfunction
+
+com! -nargs=0 -range Snip :<line1>,<line2>call <SID>Snip()
+" }}}
+
+let &cpo = s:save_cpo
+
+" vim:ft=vim:ts=4:sw=4:noet:fdm=marker:commentstring=\"\ %s:nowrap
diff --git a/files/.vim/plugin/libList.vim b/files/.vim/plugin/libList.vim
new file mode 100755
index 0000000..7d72c3e
--- /dev/null
+++ b/files/.vim/plugin/libList.vim
@@ -0,0 +1,249 @@
+" File: libList.vim
+" Last Change: 2001 Dec 10
+" Maintainer: Gontran BAERTS <gbcreation@free.fr>
+" Version: 0.1
+"
+" Please don't hesitate to correct my english :)
+" Send corrections to <gbcreation@free.fr>
+"
+"-----------------------------------------------------------------------------
+" Description: libList.vim is a set of functions to work with lists or one
+" level arrays.
+"
+"-----------------------------------------------------------------------------
+" To Enable: Normally, this file will reside in your plugins directory and be
+" automatically sourced.
+"
+"-----------------------------------------------------------------------------
+" Usage: Lists are strings variable with values separated by g:listSep
+" character (comma" by default). You may redefine g:listSep variable as you
+" wish.
+"
+" Here are available functions :
+"
+" - AddListItem( array, newItem, index ) :
+" Add item "newItem" to array "array" at "index" position
+" - GetListItem( array, index ) :
+" Return item at "index" position in array "array"
+" - GetListMatchItem( array, pattern ) :
+" Return item matching "pattern" in array "array"
+" - GetListCount( array ) :
+" Return the number of items in array "array"
+" - RemoveListItem( array, index ) :
+" Remove item at "index" position from array "array"
+" - ReplaceListItem( array, index, item ) :
+" Remove item at "index" position by "item" in array "array"
+" - ExchangeListItems( array, item1Index, item2Index ) :
+" Exchange item "item1Index" with item "item2Index" in array "array"
+" - QuickSortList( array, beg, end ) :
+" Return array "array" with items between "beg" and "end" sorted
+"
+" Example:
+" let mylist=""
+" echo GetListCount( mylist ) " --> 0
+" let mylist = AddListItem( mylist, "One", 0 ) " mylist == "One"
+" let mylist = AddListItem( mylist, "Three", 1 ) " mylist == "One,Three"
+" let mylist = AddListItem( mylist, "Two", 1 ) " mylist == "One,Two,Three"
+" echo GetListCount( mylist ) " --> 3
+" echo GetListItem( mylist, 2 ) " --> Three
+" echo GetListMatchItem( mylist, "w" ) " --> two
+" echo GetListMatchItem( mylist, "e" ) " --> One
+" let mylist = RemoveListItem( mylist, 2 ) " mylist == "One,Two"
+" echo GetListCount( mylist ) " --> 2
+" let mylist = ReplaceListItem( mylist, 0, "Three" ) " mylist == "Three,Two"
+" let mylist = ExchangeListItems( mylist, 0, 1 ) " mylist == "Two,Three"
+" let mylist = AddListItem( mylist, "One", 0 ) " mylist == "One,Two,Three"
+" let mylist = QuickSortList( mylist, 0, GetListCount(mylist)-1 )
+" " mylist == "One,Three,Two"
+"
+"-----------------------------------------------------------------------------
+" Updates:
+" in version 0.1
+" - First version
+
+" Has this already been loaded ?
+if exists("loaded_libList")
+ finish
+endif
+let loaded_libList=1
+
+"**
+" Separator:
+" You may change the separator character et any time.
+"**
+let g:listSep = ","
+
+"**
+"AddListItem:
+" Add new item at given position.
+" First item index is 0 (zero).
+"Parameters:
+" - array : Array/List (string of values) which receives the new item.
+" - newItem : String containing the item value to add.
+" - index : Integer indicating the position at which the new item is added.
+" It must be greater than or equals to 0 (zero).
+"Return:
+"String containing array values, including newItem.
+"**
+function AddListItem( array, newItem, index )
+ if a:index == 0
+ if a:array == ""
+ return a:newItem
+ endif
+ return a:newItem . g:listSep . a:array
+ endif
+ return substitute( a:array, '\(\%(^\|' . g:listSep . '\)[^' . g:listSep . ']\+\)\{' . a:index . '\}', '\0' . g:listSep . a:newItem , "" )
+endfunction
+
+"**
+"GetListItem:
+" Get item at given position.
+"Parameters:
+" - array : Array/List (string of values).
+" - index : Integer indicating the position of item to return.
+" It must be greater than or equals to 0 (zero).
+"Return:
+"String representing the item.
+"**
+function GetListItem( array, index )
+ if a:index == 0
+ return matchstr( a:array, '^[^' . g:listSep . ']\+' )
+ else
+ return matchstr( a:array, "[^" . g:listSep . "]\\+", matchend( a:array, '\(\%(^\|' . g:listSep . '\)[^' . g:listSep . ']\+\)\{' . a:index . '\}' . g:listSep ) )
+ endif
+endfunction
+
+"**
+"GetListMatchItem:
+" Get the first item matching given pattern.
+"Parameters:
+" - array : Array/List (string of values).
+" - pattern : Regular expression to match with items.
+" Avoid to use ^, $ and listSep characters in pattern, unless you
+" know what you do.
+"Return:
+"String representing the first item that matches the pattern.
+"**
+function GetListMatchItem( array, pattern )
+ return matchstr( a:array, '[^' . g:listSep . ']*' . a:pattern . '[^' . g:listSep . ']*' )
+endfunction
+
+"**
+"ReplaceListItem:
+" Replace item at given position by a new one.
+"Parameters:
+" - array : Array/List (string of values).
+" - index : Integer indicating the position of item to replace.
+" It must be greater than or equals to 0 (zero).
+" - item : String containing the new value of the replaced item.
+"Return:
+"String containing array values.
+"**
+function ReplaceListItem( array, index, item )
+ if a:index == 0
+ return substitute( a:array, '^[^' .g:listSep. ']\+', a:item, "" )
+ else
+ return substitute( a:array, '\(\%(\%(^\|' . g:listSep . '\)[^' . g:listSep . ']\+\)\{' . a:index . '\}\)' . g:listSep . '[^' . g:listSep . ']\+', '\1' . g:listSep . a:item , "" )
+ endif
+endfunction
+
+"**
+"RemoveListItem:
+" Remove item at given position.
+"Parameters:
+" - array : Array/List (string of values) from which remove an item.
+" - index : Integer indicating the position of item to remove.
+" It must be greater than or equals to 0 (zero).
+"Return:
+"String containing array values, except the removed one.
+"**
+function RemoveListItem( array, index )
+ if a:index == 0
+ return substitute( a:array, '^[^' .g:listSep. ']\+\(' . g:listSep . '\|$\)', "", "" )
+ else
+ return substitute( a:array, '\(\%(\%(^\|' . g:listSep . '\)[^' . g:listSep . ']\+\)\{' . a:index . '\}\)' . g:listSep . '[^' . g:listSep . ']\+', '\1', "" )
+ endif
+endfunction
+
+"**
+"ExchangeListItems:
+" Exchange item at position item1Index with item at position item2Index.
+"Parameters:
+" - array : Array/List (string of values).
+" - item1index : Integer indicating the position of the first item to exchange.
+" It must be greater than or equals to 0 (zero).
+" - item2index : Integer indicating the position of the second item to
+" exchange. It must be greater than or equals to 0 (zero).
+"Return:
+"String containing array values.
+"**
+function ExchangeListItems( array, item1Index, item2Index )
+ let item1 = GetListItem( a:array, a:item1Index )
+ let array = ReplaceListItem( a:array, a:item1Index, GetListItem( a:array, a:item2Index ) )
+ return ReplaceListItem( array, a:item2Index, item1 )
+endfunction
+
+"**
+"GetListCount:
+" Number of items in array.
+"Parameters:
+" - array : Array/List (string of values).
+"Return:
+"Integer representing the number of items in array.
+"Index of last item is GetListCount(array)-1.
+"**
+function GetListCount( array )
+ if a:array == "" | return 0 | endif
+ let pos = 0
+ let cnt = 0
+ while pos != -1
+ let pos = matchend( a:array, g:listSep, pos )
+ let cnt = cnt + 1
+ endwhile
+ return cnt
+endfunction
+
+"**
+"QuickSortList:
+" Sort array.
+"Parameters:
+" - array : Array/List (string of values).
+" - beg : Min index of the range of items to sort.
+" - end : Max index of the range of items to sort.
+"Return:
+"String containing array values with indicated range of items sorted.
+"**
+function QuickSortList( array, beg, end )
+ let array = a:array
+ let pivot = GetListItem( array, a:beg )
+ let l = a:beg
+ let r = a:end
+ while l < r
+ while GetListItem( array, r ) > pivot
+ let r = r - 1
+ endwhile
+ if l != r
+ let array = ReplaceListItem( array, l, GetListItem( array, r ) )
+ let array = ReplaceListItem( array, r, pivot )
+ let l = l + 1
+ endif
+
+ while GetListItem( array, l ) < pivot
+ let l = l + 1
+ endwhile
+ if l != r
+ let array = ReplaceListItem( array, r, GetListItem( array, l ) )
+ let array = ReplaceListItem( array, l, pivot )
+ let r = r - 1
+ endif
+ endwhile
+ if a:beg < l-1
+ let array = QuickSortList( array, a:beg, l-1 )
+ endif
+ if a:end > l+1
+ let array = QuickSortList( array, l+1, a:end )
+ endif
+ return array
+endfunction
+
+
diff --git a/files/.vim/plugin/luarefvim.vim b/files/.vim/plugin/luarefvim.vim
new file mode 100755
index 0000000..cb5664c
--- /dev/null
+++ b/files/.vim/plugin/luarefvim.vim
@@ -0,0 +1,30 @@
+" luarefvim plugin
+" This is somewhat based on CRefVim
+" Maintainer: Luis Carvalho <lexcarvalho@gmail.com>
+" Last Change: Jun, 3, 2005
+" Version: 0.2
+
+" initial setup: avoid loading more than once
+if exists("loaded_luarefvim")
+ finish
+endif
+let loaded_luarefvim = 1
+
+" mappings:
+vmap <silent> <unique> <Leader>lr y:call <SID>LookUp('<c-r>"')<CR>
+nmap <silent> <unique> <Leader>lr :call <SID>LookUp(expand("<cword>"))<CR>
+map <silent> <unique> <Leader>lc :help luaref<CR>
+
+function <SID>LookUp(str)
+ if a:str == "--" "comment?
+ silent! execute ":help lrv-comment"
+ elseif a:str == ""
+ silent! execute ":help luaref"
+ else
+ silent! execute ":help lrv-" . a:str
+ if v:errmsg != ""
+ echo "luarefvim: \`" . a:str . "\' not found"
+ endif
+ endif
+endfunction
+
diff --git a/files/.vim/plugin/matchit.vim b/files/.vim/plugin/matchit.vim
new file mode 100755
index 0000000..549c26c
--- /dev/null
+++ b/files/.vim/plugin/matchit.vim
@@ -0,0 +1,812 @@
+" matchit.vim: (global plugin) Extended "%" matching
+" Last Change: Fri Jan 25 10:00 AM 2008 EST
+" Maintainer: Benji Fisher PhD <benji@member.AMS.org>
+" Version: 1.13.2, for Vim 6.3+
+" URL: http://www.vim.org/script.php?script_id=39
+
+" Documentation:
+" The documentation is in a separate file, matchit.txt .
+
+" Credits:
+" Vim editor by Bram Moolenaar (Thanks, Bram!)
+" Original script and design by Raul Segura Acevedo
+" Support for comments by Douglas Potts
+" Support for back references and other improvements by Benji Fisher
+" Support for many languages by Johannes Zellner
+" Suggestions for improvement, bug reports, and support for additional
+" languages by Jordi-Albert Batalla, Neil Bird, Servatius Brandt, Mark
+" Collett, Stephen Wall, Dany St-Amant, Yuheng Xie, and Johannes Zellner.
+
+" Debugging:
+" If you'd like to try the built-in debugging commands...
+" :MatchDebug to activate debugging for the current buffer
+" This saves the values of several key script variables as buffer-local
+" variables. See the MatchDebug() function, below, for details.
+
+" TODO: I should think about multi-line patterns for b:match_words.
+" This would require an option: how many lines to scan (default 1).
+" This would be useful for Python, maybe also for *ML.
+" TODO: Maybe I should add a menu so that people will actually use some of
+" the features that I have implemented.
+" TODO: Eliminate the MultiMatch function. Add yet another argument to
+" Match_wrapper() instead.
+" TODO: Allow :let b:match_words = '\(\(foo\)\(bar\)\):\3\2:end\1'
+" TODO: Make backrefs safer by using '\V' (very no-magic).
+" TODO: Add a level of indirection, so that custom % scripts can use my
+" work but extend it.
+
+" allow user to prevent loading
+" and prevent duplicate loading
+if exists("loaded_matchit") || &cp
+ finish
+endif
+let loaded_matchit = 1
+let s:last_mps = ""
+let s:last_words = ":"
+
+let s:save_cpo = &cpo
+set cpo&vim
+
+nnoremap <silent> % :<C-U>call <SID>Match_wrapper('',1,'n') <CR>
+nnoremap <silent> g% :<C-U>call <SID>Match_wrapper('',0,'n') <CR>
+vnoremap <silent> % :<C-U>call <SID>Match_wrapper('',1,'v') <CR>m'gv``
+vnoremap <silent> g% :<C-U>call <SID>Match_wrapper('',0,'v') <CR>m'gv``
+onoremap <silent> % v:<C-U>call <SID>Match_wrapper('',1,'o') <CR>
+onoremap <silent> g% v:<C-U>call <SID>Match_wrapper('',0,'o') <CR>
+
+" Analogues of [{ and ]} using matching patterns:
+nnoremap <silent> [% :<C-U>call <SID>MultiMatch("bW", "n") <CR>
+nnoremap <silent> ]% :<C-U>call <SID>MultiMatch("W", "n") <CR>
+vmap [% <Esc>[%m'gv``
+vmap ]% <Esc>]%m'gv``
+" vnoremap <silent> [% :<C-U>call <SID>MultiMatch("bW", "v") <CR>m'gv``
+" vnoremap <silent> ]% :<C-U>call <SID>MultiMatch("W", "v") <CR>m'gv``
+onoremap <silent> [% v:<C-U>call <SID>MultiMatch("bW", "o") <CR>
+onoremap <silent> ]% v:<C-U>call <SID>MultiMatch("W", "o") <CR>
+
+" text object:
+vmap a% <Esc>[%v]%
+
+" Auto-complete mappings: (not yet "ready for prime time")
+" TODO Read :help write-plugin for the "right" way to let the user
+" specify a key binding.
+" let g:match_auto = '<C-]>'
+" let g:match_autoCR = '<C-CR>'
+" if exists("g:match_auto")
+" execute "inoremap " . g:match_auto . ' x<Esc>"=<SID>Autocomplete()<CR>Pls'
+" endif
+" if exists("g:match_autoCR")
+" execute "inoremap " . g:match_autoCR . ' <CR><C-R>=<SID>Autocomplete()<CR>'
+" endif
+" if exists("g:match_gthhoh")
+" execute "inoremap " . g:match_gthhoh . ' <C-O>:call <SID>Gthhoh()<CR>'
+" endif " gthhoh = "Get the heck out of here!"
+
+let s:notslash = '\\\@<!\%(\\\\\)*'
+
+function! s:Match_wrapper(word, forward, mode) range
+ " In s:CleanUp(), :execute "set" restore_options .
+ let restore_options = (&ic ? " " : " no") . "ignorecase"
+ if exists("b:match_ignorecase")
+ let &ignorecase = b:match_ignorecase
+ endif
+ let restore_options = " ve=" . &ve . restore_options
+ set ve=
+ " If this function was called from Visual mode, make sure that the cursor
+ " is at the correct end of the Visual range:
+ if a:mode == "v"
+ execute "normal! gv\<Esc>"
+ endif
+ " In s:CleanUp(), we may need to check whether the cursor moved forward.
+ let startline = line(".")
+ let startcol = col(".")
+ " Use default behavior if called with a count.
+ if v:count
+ exe "normal! " . v:count . "%"
+ return s:CleanUp(restore_options, a:mode, startline, startcol)
+ end
+
+ " First step: if not already done, set the script variables
+ " s:do_BR flag for whether there are backrefs
+ " s:pat parsed version of b:match_words
+ " s:all regexp based on s:pat and the default groups
+ "
+ if !exists("b:match_words") || b:match_words == ""
+ let match_words = ""
+ " Allow b:match_words = "GetVimMatchWords()" .
+ elseif b:match_words =~ ":"
+ let match_words = b:match_words
+ else
+ execute "let match_words =" b:match_words
+ endif
+" Thanks to Preben "Peppe" Guldberg and Bram Moolenaar for this suggestion!
+ if (match_words != s:last_words) || (&mps != s:last_mps) ||
+ \ exists("b:match_debug")
+ let s:last_words = match_words
+ let s:last_mps = &mps
+ " The next several lines were here before
+ " BF started messing with this script.
+ " quote the special chars in 'matchpairs', replace [,:] with \| and then
+ " append the builtin pairs (/*, */, #if, #ifdef, #else, #elif, #endif)
+ " let default = substitute(escape(&mps, '[$^.*~\\/?]'), '[,:]\+',
+ " \ '\\|', 'g').'\|\/\*\|\*\/\|#if\>\|#ifdef\>\|#else\>\|#elif\>\|#endif\>'
+ let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") .
+ \ '\/\*:\*\/,#if\%(def\)\=:#else\>:#elif\>:#endif\>'
+ " s:all = pattern with all the keywords
+ let match_words = match_words . (strlen(match_words) ? "," : "") . default
+ if match_words !~ s:notslash . '\\\d'
+ let s:do_BR = 0
+ let s:pat = match_words
+ else
+ let s:do_BR = 1
+ let s:pat = s:ParseWords(match_words)
+ endif
+ let s:all = substitute(s:pat, s:notslash . '\zs[,:]\+', '\\|', 'g')
+ let s:all = '\%(' . s:all . '\)'
+ " let s:all = '\%(' . substitute(s:all, '\\\ze[,:]', '', 'g') . '\)'
+ if exists("b:match_debug")
+ let b:match_pat = s:pat
+ endif
+ endif
+
+ " Second step: set the following local variables:
+ " matchline = line on which the cursor started
+ " curcol = number of characters before match
+ " prefix = regexp for start of line to start of match
+ " suffix = regexp for end of match to end of line
+ " Require match to end on or after the cursor and prefer it to
+ " start on or before the cursor.
+ let matchline = getline(startline)
+ if a:word != ''
+ " word given
+ if a:word !~ s:all
+ echohl WarningMsg|echo 'Missing rule for word:"'.a:word.'"'|echohl NONE
+ return s:CleanUp(restore_options, a:mode, startline, startcol)
+ endif
+ let matchline = a:word
+ let curcol = 0
+ let prefix = '^\%('
+ let suffix = '\)$'
+ " Now the case when "word" is not given
+ else " Find the match that ends on or after the cursor and set curcol.
+ let regexp = s:Wholematch(matchline, s:all, startcol-1)
+ let curcol = match(matchline, regexp)
+ " If there is no match, give up.
+ if curcol == -1
+ return s:CleanUp(restore_options, a:mode, startline, startcol)
+ endif
+ let endcol = matchend(matchline, regexp)
+ let suf = strlen(matchline) - endcol
+ let prefix = (curcol ? '^.*\%' . (curcol + 1) . 'c\%(' : '^\%(')
+ let suffix = (suf ? '\)\%' . (endcol + 1) . 'c.*$' : '\)$')
+ endif
+ if exists("b:match_debug")
+ let b:match_match = matchstr(matchline, regexp)
+ let b:match_col = curcol+1
+ endif
+
+ " Third step: Find the group and single word that match, and the original
+ " (backref) versions of these. Then, resolve the backrefs.
+ " Set the following local variable:
+ " group = colon-separated list of patterns, one of which matches
+ " = ini:mid:fin or ini:fin
+ "
+ " Reconstruct the version with unresolved backrefs.
+ let patBR = substitute(match_words.',',
+ \ s:notslash.'\zs[,:]*,[,:]*', ',', 'g')
+ let patBR = substitute(patBR, s:notslash.'\zs:\{2,}', ':', 'g')
+ " Now, set group and groupBR to the matching group: 'if:endif' or
+ " 'while:endwhile' or whatever. A bit of a kluge: s:Choose() returns
+ " group . "," . groupBR, and we pick it apart.
+ let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, patBR)
+ let i = matchend(group, s:notslash . ",")
+ let groupBR = strpart(group, i)
+ let group = strpart(group, 0, i-1)
+ " Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix
+ if s:do_BR " Do the hard part: resolve those backrefs!
+ let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline)
+ endif
+ if exists("b:match_debug")
+ let b:match_wholeBR = groupBR
+ let i = matchend(groupBR, s:notslash . ":")
+ let b:match_iniBR = strpart(groupBR, 0, i-1)
+ endif
+
+ " Fourth step: Set the arguments for searchpair().
+ let i = matchend(group, s:notslash . ":")
+ let j = matchend(group, '.*' . s:notslash . ":")
+ let ini = strpart(group, 0, i-1)
+ let mid = substitute(strpart(group, i,j-i-1), s:notslash.'\zs:', '\\|', 'g')
+ let fin = strpart(group, j)
+ "Un-escape the remaining , and : characters.
+ let ini = substitute(ini, s:notslash . '\zs\\\(:\|,\)', '\1', 'g')
+ let mid = substitute(mid, s:notslash . '\zs\\\(:\|,\)', '\1', 'g')
+ let fin = substitute(fin, s:notslash . '\zs\\\(:\|,\)', '\1', 'g')
+ " searchpair() requires that these patterns avoid \(\) groups.
+ let ini = substitute(ini, s:notslash . '\zs\\(', '\\%(', 'g')
+ let mid = substitute(mid, s:notslash . '\zs\\(', '\\%(', 'g')
+ let fin = substitute(fin, s:notslash . '\zs\\(', '\\%(', 'g')
+ " Set mid. This is optimized for readability, not micro-efficiency!
+ if a:forward && matchline =~ prefix . fin . suffix
+ \ || !a:forward && matchline =~ prefix . ini . suffix
+ let mid = ""
+ endif
+ " Set flag. This is optimized for readability, not micro-efficiency!
+ if a:forward && matchline =~ prefix . fin . suffix
+ \ || !a:forward && matchline !~ prefix . ini . suffix
+ let flag = "bW"
+ else
+ let flag = "W"
+ endif
+ " Set skip.
+ if exists("b:match_skip")
+ let skip = b:match_skip
+ elseif exists("b:match_comment") " backwards compatibility and testing!
+ let skip = "r:" . b:match_comment
+ else
+ let skip = 's:comment\|string'
+ endif
+ let skip = s:ParseSkip(skip)
+ if exists("b:match_debug")
+ let b:match_ini = ini
+ let b:match_tail = (strlen(mid) ? mid.'\|' : '') . fin
+ endif
+
+ " Fifth step: actually start moving the cursor and call searchpair().
+ " Later, :execute restore_cursor to get to the original screen.
+ let restore_cursor = virtcol(".") . "|"
+ normal! g0
+ let restore_cursor = line(".") . "G" . virtcol(".") . "|zs" . restore_cursor
+ normal! H
+ let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor
+ execute restore_cursor
+ call cursor(0, curcol + 1)
+ " normal! 0
+ " if curcol
+ " execute "normal!" . curcol . "l"
+ " endif
+ if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on"))
+ let skip = "0"
+ else
+ execute "if " . skip . "| let skip = '0' | endif"
+ endif
+ let sp_return = searchpair(ini, mid, fin, flag, skip)
+ let final_position = "call cursor(" . line(".") . "," . col(".") . ")"
+ " Restore cursor position and original screen.
+ execute restore_cursor
+ normal! m'
+ if sp_return > 0
+ execute final_position
+ endif
+ return s:CleanUp(restore_options, a:mode, startline, startcol, mid.'\|'.fin)
+endfun
+
+" Restore options and do some special handling for Operator-pending mode.
+" The optional argument is the tail of the matching group.
+fun! s:CleanUp(options, mode, startline, startcol, ...)
+ execute "set" a:options
+ " Open folds, if appropriate.
+ if a:mode != "o"
+ if &foldopen =~ "percent"
+ normal! zv
+ endif
+ " In Operator-pending mode, we want to include the whole match
+ " (for example, d%).
+ " This is only a problem if we end up moving in the forward direction.
+ elseif (a:startline < line(".")) ||
+ \ (a:startline == line(".") && a:startcol < col("."))
+ if a:0
+ " Check whether the match is a single character. If not, move to the
+ " end of the match.
+ let matchline = getline(".")
+ let currcol = col(".")
+ let regexp = s:Wholematch(matchline, a:1, currcol-1)
+ let endcol = matchend(matchline, regexp)
+ if endcol > currcol " This is NOT off by one!
+ execute "normal!" . (endcol - currcol) . "l"
+ endif
+ endif " a:0
+ endif " a:mode != "o" && etc.
+ return 0
+endfun
+
+" Example (simplified HTML patterns): if
+" a:groupBR = '<\(\k\+\)>:</\1>'
+" a:prefix = '^.\{3}\('
+" a:group = '<\(\k\+\)>:</\(\k\+\)>'
+" a:suffix = '\).\{2}$'
+" a:matchline = "123<tag>12" or "123</tag>12"
+" then extract "tag" from a:matchline and return "<tag>:</tag>" .
+fun! s:InsertRefs(groupBR, prefix, group, suffix, matchline)
+ if a:matchline !~ a:prefix .
+ \ substitute(a:group, s:notslash . '\zs:', '\\|', 'g') . a:suffix
+ return a:group
+ endif
+ let i = matchend(a:groupBR, s:notslash . ':')
+ let ini = strpart(a:groupBR, 0, i-1)
+ let tailBR = strpart(a:groupBR, i)
+ let word = s:Choose(a:group, a:matchline, ":", "", a:prefix, a:suffix,
+ \ a:groupBR)
+ let i = matchend(word, s:notslash . ":")
+ let wordBR = strpart(word, i)
+ let word = strpart(word, 0, i-1)
+ " Now, a:matchline =~ a:prefix . word . a:suffix
+ if wordBR != ini
+ let table = s:Resolve(ini, wordBR, "table")
+ else
+ " let table = "----------"
+ let table = ""
+ let d = 0
+ while d < 10
+ if tailBR =~ s:notslash . '\\' . d
+ " let table[d] = d
+ let table = table . d
+ else
+ let table = table . "-"
+ endif
+ let d = d + 1
+ endwhile
+ endif
+ let d = 9
+ while d
+ if table[d] != "-"
+ let backref = substitute(a:matchline, a:prefix.word.a:suffix,
+ \ '\'.table[d], "")
+ " Are there any other characters that should be escaped?
+ let backref = escape(backref, '*,:')
+ execute s:Ref(ini, d, "start", "len")
+ let ini = strpart(ini, 0, start) . backref . strpart(ini, start+len)
+ let tailBR = substitute(tailBR, s:notslash . '\zs\\' . d,
+ \ escape(backref, '\\&'), 'g')
+ endif
+ let d = d-1
+ endwhile
+ if exists("b:match_debug")
+ if s:do_BR
+ let b:match_table = table
+ let b:match_word = word
+ else
+ let b:match_table = ""
+ let b:match_word = ""
+ endif
+ endif
+ return ini . ":" . tailBR
+endfun
+
+" Input a comma-separated list of groups with backrefs, such as
+" a:groups = '\(foo\):end\1,\(bar\):end\1'
+" and return a comma-separated list of groups with backrefs replaced:
+" return '\(foo\):end\(foo\),\(bar\):end\(bar\)'
+fun! s:ParseWords(groups)
+ let groups = substitute(a:groups.",", s:notslash.'\zs[,:]*,[,:]*', ',', 'g')
+ let groups = substitute(groups, s:notslash . '\zs:\{2,}', ':', 'g')
+ let parsed = ""
+ while groups =~ '[^,:]'
+ let i = matchend(groups, s:notslash . ':')
+ let j = matchend(groups, s:notslash . ',')
+ let ini = strpart(groups, 0, i-1)
+ let tail = strpart(groups, i, j-i-1) . ":"
+ let groups = strpart(groups, j)
+ let parsed = parsed . ini
+ let i = matchend(tail, s:notslash . ':')
+ while i != -1
+ " In 'if:else:endif', ini='if' and word='else' and then word='endif'.
+ let word = strpart(tail, 0, i-1)
+ let tail = strpart(tail, i)
+ let i = matchend(tail, s:notslash . ':')
+ let parsed = parsed . ":" . s:Resolve(ini, word, "word")
+ endwhile " Now, tail has been used up.
+ let parsed = parsed . ","
+ endwhile " groups =~ '[^,:]'
+ let parsed = substitute(parsed, ',$', '', '')
+ return parsed
+endfun
+
+" TODO I think this can be simplified and/or made more efficient.
+" TODO What should I do if a:start is out of range?
+" Return a regexp that matches all of a:string, such that
+" matchstr(a:string, regexp) represents the match for a:pat that starts
+" as close to a:start as possible, before being preferred to after, and
+" ends after a:start .
+" Usage:
+" let regexp = s:Wholematch(getline("."), 'foo\|bar', col(".")-1)
+" let i = match(getline("."), regexp)
+" let j = matchend(getline("."), regexp)
+" let match = matchstr(getline("."), regexp)
+fun! s:Wholematch(string, pat, start)
+ let group = '\%(' . a:pat . '\)'
+ let prefix = (a:start ? '\(^.*\%<' . (a:start + 2) . 'c\)\zs' : '^')
+ let len = strlen(a:string)
+ let suffix = (a:start+1 < len ? '\(\%>'.(a:start+1).'c.*$\)\@=' : '$')
+ if a:string !~ prefix . group . suffix
+ let prefix = ''
+ endif
+ return prefix . group . suffix
+endfun
+
+" No extra arguments: s:Ref(string, d) will
+" find the d'th occurrence of '\(' and return it, along with everything up
+" to and including the matching '\)'.
+" One argument: s:Ref(string, d, "start") returns the index of the start
+" of the d'th '\(' and any other argument returns the length of the group.
+" Two arguments: s:Ref(string, d, "foo", "bar") returns a string to be
+" executed, having the effect of
+" :let foo = s:Ref(string, d, "start")
+" :let bar = s:Ref(string, d, "len")
+fun! s:Ref(string, d, ...)
+ let len = strlen(a:string)
+ if a:d == 0
+ let start = 0
+ else
+ let cnt = a:d
+ let match = a:string
+ while cnt
+ let cnt = cnt - 1
+ let index = matchend(match, s:notslash . '\\(')
+ if index == -1
+ return ""
+ endif
+ let match = strpart(match, index)
+ endwhile
+ let start = len - strlen(match)
+ if a:0 == 1 && a:1 == "start"
+ return start - 2
+ endif
+ let cnt = 1
+ while cnt
+ let index = matchend(match, s:notslash . '\\(\|\\)') - 1
+ if index == -2
+ return ""
+ endif
+ " Increment if an open, decrement if a ')':
+ let cnt = cnt + (match[index]=="(" ? 1 : -1) " ')'
+ " let cnt = stridx('0(', match[index]) + cnt
+ let match = strpart(match, index+1)
+ endwhile
+ let start = start - 2
+ let len = len - start - strlen(match)
+ endif
+ if a:0 == 1
+ return len
+ elseif a:0 == 2
+ return "let " . a:1 . "=" . start . "| let " . a:2 . "=" . len
+ else
+ return strpart(a:string, start, len)
+ endif
+endfun
+
+" Count the number of disjoint copies of pattern in string.
+" If the pattern is a literal string and contains no '0' or '1' characters
+" then s:Count(string, pattern, '0', '1') should be faster than
+" s:Count(string, pattern).
+fun! s:Count(string, pattern, ...)
+ let pat = escape(a:pattern, '\\')
+ if a:0 > 1
+ let foo = substitute(a:string, '[^'.a:pattern.']', "a:1", "g")
+ let foo = substitute(a:string, pat, a:2, "g")
+ let foo = substitute(foo, '[^' . a:2 . ']', "", "g")
+ return strlen(foo)
+ endif
+ let result = 0
+ let foo = a:string
+ let index = matchend(foo, pat)
+ while index != -1
+ let result = result + 1
+ let foo = strpart(foo, index)
+ let index = matchend(foo, pat)
+ endwhile
+ return result
+endfun
+
+" s:Resolve('\(a\)\(b\)', '\(c\)\2\1\1\2') should return table.word, where
+" word = '\(c\)\(b\)\(a\)\3\2' and table = '-32-------'. That is, the first
+" '\1' in target is replaced by '\(a\)' in word, table[1] = 3, and this
+" indicates that all other instances of '\1' in target are to be replaced
+" by '\3'. The hard part is dealing with nesting...
+" Note that ":" is an illegal character for source and target,
+" unless it is preceded by "\".
+fun! s:Resolve(source, target, output)
+ let word = a:target
+ let i = matchend(word, s:notslash . '\\\d') - 1
+ let table = "----------"
+ while i != -2 " There are back references to be replaced.
+ let d = word[i]
+ let backref = s:Ref(a:source, d)
+ " The idea is to replace '\d' with backref. Before we do this,
+ " replace any \(\) groups in backref with :1, :2, ... if they
+ " correspond to the first, second, ... group already inserted
+ " into backref. Later, replace :1 with \1 and so on. The group
+ " number w+b within backref corresponds to the group number
+ " s within a:source.
+ " w = number of '\(' in word before the current one
+ let w = s:Count(
+ \ substitute(strpart(word, 0, i-1), '\\\\', '', 'g'), '\(', '1')
+ let b = 1 " number of the current '\(' in backref
+ let s = d " number of the current '\(' in a:source
+ while b <= s:Count(substitute(backref, '\\\\', '', 'g'), '\(', '1')
+ \ && s < 10
+ if table[s] == "-"
+ if w + b < 10
+ " let table[s] = w + b
+ let table = strpart(table, 0, s) . (w+b) . strpart(table, s+1)
+ endif
+ let b = b + 1
+ let s = s + 1
+ else
+ execute s:Ref(backref, b, "start", "len")
+ let ref = strpart(backref, start, len)
+ let backref = strpart(backref, 0, start) . ":". table[s]
+ \ . strpart(backref, start+len)
+ let s = s + s:Count(substitute(ref, '\\\\', '', 'g'), '\(', '1')
+ endif
+ endwhile
+ let word = strpart(word, 0, i-1) . backref . strpart(word, i+1)
+ let i = matchend(word, s:notslash . '\\\d') - 1
+ endwhile
+ let word = substitute(word, s:notslash . '\zs:', '\\', 'g')
+ if a:output == "table"
+ return table
+ elseif a:output == "word"
+ return word
+ else
+ return table . word
+ endif
+endfun
+
+" Assume a:comma = ",". Then the format for a:patterns and a:1 is
+" a:patterns = "<pat1>,<pat2>,..."
+" a:1 = "<alt1>,<alt2>,..."
+" If <patn> is the first pattern that matches a:string then return <patn>
+" if no optional arguments are given; return <patn>,<altn> if a:1 is given.
+fun! s:Choose(patterns, string, comma, branch, prefix, suffix, ...)
+ let tail = (a:patterns =~ a:comma."$" ? a:patterns : a:patterns . a:comma)
+ let i = matchend(tail, s:notslash . a:comma)
+ if a:0
+ let alttail = (a:1 =~ a:comma."$" ? a:1 : a:1 . a:comma)
+ let j = matchend(alttail, s:notslash . a:comma)
+ endif
+ let current = strpart(tail, 0, i-1)
+ if a:branch == ""
+ let currpat = current
+ else
+ let currpat = substitute(current, s:notslash . a:branch, '\\|', 'g')
+ endif
+ while a:string !~ a:prefix . currpat . a:suffix
+ let tail = strpart(tail, i)
+ let i = matchend(tail, s:notslash . a:comma)
+ if i == -1
+ return -1
+ endif
+ let current = strpart(tail, 0, i-1)
+ if a:branch == ""
+ let currpat = current
+ else
+ let currpat = substitute(current, s:notslash . a:branch, '\\|', 'g')
+ endif
+ if a:0
+ let alttail = strpart(alttail, j)
+ let j = matchend(alttail, s:notslash . a:comma)
+ endif
+ endwhile
+ if a:0
+ let current = current . a:comma . strpart(alttail, 0, j-1)
+ endif
+ return current
+endfun
+
+" Call this function to turn on debugging information. Every time the main
+" script is run, buffer variables will be saved. These can be used directly
+" or viewed using the menu items below.
+if !exists(":MatchDebug")
+ command! -nargs=0 MatchDebug call s:Match_debug()
+endif
+
+fun! s:Match_debug()
+ let b:match_debug = 1 " Save debugging information.
+ " pat = all of b:match_words with backrefs parsed
+ amenu &Matchit.&pat :echo b:match_pat<CR>
+ " match = bit of text that is recognized as a match
+ amenu &Matchit.&match :echo b:match_match<CR>
+ " curcol = cursor column of the start of the matching text
+ amenu &Matchit.&curcol :echo b:match_col<CR>
+ " wholeBR = matching group, original version
+ amenu &Matchit.wh&oleBR :echo b:match_wholeBR<CR>
+ " iniBR = 'if' piece, original version
+ amenu &Matchit.ini&BR :echo b:match_iniBR<CR>
+ " ini = 'if' piece, with all backrefs resolved from match
+ amenu &Matchit.&ini :echo b:match_ini<CR>
+ " tail = 'else\|endif' piece, with all backrefs resolved from match
+ amenu &Matchit.&tail :echo b:match_tail<CR>
+ " fin = 'endif' piece, with all backrefs resolved from match
+ amenu &Matchit.&word :echo b:match_word<CR>
+ " '\'.d in ini refers to the same thing as '\'.table[d] in word.
+ amenu &Matchit.t&able :echo '0:' . b:match_table . ':9'<CR>
+endfun
+
+" Jump to the nearest unmatched "(" or "if" or "<tag>" if a:spflag == "bW"
+" or the nearest unmatched "</tag>" or "endif" or ")" if a:spflag == "W".
+" Return a "mark" for the original position, so that
+" let m = MultiMatch("bW", "n") ... execute m
+" will return to the original position. If there is a problem, do not
+" move the cursor and return "", unless a count is given, in which case
+" go up or down as many levels as possible and again return "".
+" TODO This relies on the same patterns as % matching. It might be a good
+" idea to give it its own matching patterns.
+fun! s:MultiMatch(spflag, mode)
+ if !exists("b:match_words") || b:match_words == ""
+ return ""
+ end
+ let restore_options = (&ic ? "" : "no") . "ignorecase"
+ if exists("b:match_ignorecase")
+ let &ignorecase = b:match_ignorecase
+ endif
+ let startline = line(".")
+ let startcol = col(".")
+
+ " First step: if not already done, set the script variables
+ " s:do_BR flag for whether there are backrefs
+ " s:pat parsed version of b:match_words
+ " s:all regexp based on s:pat and the default groups
+ " This part is copied and slightly modified from s:Match_wrapper().
+ let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") .
+ \ '\/\*:\*\/,#if\%(def\)\=:#else\>:#elif\>:#endif\>'
+ " Allow b:match_words = "GetVimMatchWords()" .
+ if b:match_words =~ ":"
+ let match_words = b:match_words
+ else
+ execute "let match_words =" b:match_words
+ endif
+ if (match_words != s:last_words) || (&mps != s:last_mps) ||
+ \ exists("b:match_debug")
+ let s:last_words = match_words
+ let s:last_mps = &mps
+ if match_words !~ s:notslash . '\\\d'
+ let s:do_BR = 0
+ let s:pat = match_words
+ else
+ let s:do_BR = 1
+ let s:pat = s:ParseWords(match_words)
+ endif
+ let s:all = '\%(' . substitute(s:pat . (strlen(s:pat)?",":"") . default,
+ \ '[,:]\+','\\|','g') . '\)'
+ if exists("b:match_debug")
+ let b:match_pat = s:pat
+ endif
+ endif
+
+ " Second step: figure out the patterns for searchpair()
+ " and save the screen, cursor position, and 'ignorecase'.
+ " - TODO: A lot of this is copied from s:Match_wrapper().
+ " - maybe even more functionality should be split off
+ " - into separate functions!
+ let cdefault = (s:pat =~ '[^,]$' ? "," : "") . default
+ let open = substitute(s:pat . cdefault,
+ \ s:notslash . '\zs:.\{-}' . s:notslash . ',', '\\),\\(', 'g')
+ let open = '\(' . substitute(open, s:notslash . '\zs:.*$', '\\)', '')
+ let close = substitute(s:pat . cdefault,
+ \ s:notslash . '\zs,.\{-}' . s:notslash . ':', '\\),\\(', 'g')
+ let close = substitute(close, '^.\{-}' . s:notslash . ':', '\\(', '') . '\)'
+ if exists("b:match_skip")
+ let skip = b:match_skip
+ elseif exists("b:match_comment") " backwards compatibility and testing!
+ let skip = "r:" . b:match_comment
+ else
+ let skip = 's:comment\|string'
+ endif
+ let skip = s:ParseSkip(skip)
+ " let restore_cursor = line(".") . "G" . virtcol(".") . "|"
+ " normal! H
+ " let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor
+ let restore_cursor = virtcol(".") . "|"
+ normal! g0
+ let restore_cursor = line(".") . "G" . virtcol(".") . "|zs" . restore_cursor
+ normal! H
+ let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor
+ execute restore_cursor
+
+ " Third step: call searchpair().
+ " Replace '\('--but not '\\('--with '\%(' and ',' with '\|'.
+ let openpat = substitute(open, '\(\\\@<!\(\\\\\)*\)\@<=\\(', '\\%(', 'g')
+ let openpat = substitute(openpat, ',', '\\|', 'g')
+ let closepat = substitute(close, '\(\\\@<!\(\\\\\)*\)\@<=\\(', '\\%(', 'g')
+ let closepat = substitute(closepat, ',', '\\|', 'g')
+ if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on"))
+ let skip = '0'
+ else
+ execute "if " . skip . "| let skip = '0' | endif"
+ endif
+ mark '
+ let level = v:count1
+ while level
+ if searchpair(openpat, '', closepat, a:spflag, skip) < 1
+ call s:CleanUp(restore_options, a:mode, startline, startcol)
+ return ""
+ endif
+ let level = level - 1
+ endwhile
+
+ " Restore options and return a string to restore the original position.
+ call s:CleanUp(restore_options, a:mode, startline, startcol)
+ return restore_cursor
+endfun
+
+" Search backwards for "if" or "while" or "<tag>" or ...
+" and return "endif" or "endwhile" or "</tag>" or ... .
+" For now, this uses b:match_words and the same script variables
+" as s:Match_wrapper() . Later, it may get its own patterns,
+" either from a buffer variable or passed as arguments.
+" fun! s:Autocomplete()
+" echo "autocomplete not yet implemented :-("
+" if !exists("b:match_words") || b:match_words == ""
+" return ""
+" end
+" let startpos = s:MultiMatch("bW")
+"
+" if startpos == ""
+" return ""
+" endif
+" " - TODO: figure out whether 'if' or '<tag>' matched, and construct
+" " - the appropriate closing.
+" let matchline = getline(".")
+" let curcol = col(".") - 1
+" " - TODO: Change the s:all argument if there is a new set of match pats.
+" let regexp = s:Wholematch(matchline, s:all, curcol)
+" let suf = strlen(matchline) - matchend(matchline, regexp)
+" let prefix = (curcol ? '^.\{' . curcol . '}\%(' : '^\%(')
+" let suffix = (suf ? '\).\{' . suf . '}$' : '\)$')
+" " Reconstruct the version with unresolved backrefs.
+" let patBR = substitute(b:match_words.',', '[,:]*,[,:]*', ',', 'g')
+" let patBR = substitute(patBR, ':\{2,}', ':', "g")
+" " Now, set group and groupBR to the matching group: 'if:endif' or
+" " 'while:endwhile' or whatever.
+" let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, patBR)
+" let i = matchend(group, s:notslash . ",")
+" let groupBR = strpart(group, i)
+" let group = strpart(group, 0, i-1)
+" " Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix
+" if s:do_BR
+" let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline)
+" endif
+" " let g:group = group
+"
+" " - TODO: Construct the closing from group.
+" let fake = "end" . expand("<cword>")
+" execute startpos
+" return fake
+" endfun
+
+" Close all open structures. "Get the heck out of here!"
+" fun! s:Gthhoh()
+" let close = s:Autocomplete()
+" while strlen(close)
+" put=close
+" let close = s:Autocomplete()
+" endwhile
+" endfun
+
+" Parse special strings as typical skip arguments for searchpair():
+" s:foo becomes (current syntax item) =~ foo
+" S:foo becomes (current syntax item) !~ foo
+" r:foo becomes (line before cursor) =~ foo
+" R:foo becomes (line before cursor) !~ foo
+fun! s:ParseSkip(str)
+ let skip = a:str
+ if skip[1] == ":"
+ if skip[0] == "s"
+ let skip = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '" .
+ \ strpart(skip,2) . "'"
+ elseif skip[0] == "S"
+ let skip = "synIDattr(synID(line('.'),col('.'),1),'name') !~? '" .
+ \ strpart(skip,2) . "'"
+ elseif skip[0] == "r"
+ let skip = "strpart(getline('.'),0,col('.'))=~'" . strpart(skip,2). "'"
+ elseif skip[0] == "R"
+ let skip = "strpart(getline('.'),0,col('.'))!~'" . strpart(skip,2). "'"
+ endif
+ endif
+ return skip
+endfun
+
+let &cpo = s:save_cpo
+
+" vim:sts=2:sw=2:
diff --git a/files/.vim/plugin/project.vim b/files/.vim/plugin/project.vim
new file mode 100755
index 0000000..47bd379
--- /dev/null
+++ b/files/.vim/plugin/project.vim
@@ -0,0 +1,1293 @@
+"=============================================================================
+" File: project.vim
+" Author: Aric Blumer (Aric.Blumer at aricvim@charter.net)
+" Last Change: Fri 13 Oct 2006 09:47:08 AM EDT
+" Version: 1.4.1
+"=============================================================================
+" See documentation in accompanying help file
+" You may use this code in whatever way you see fit.
+
+if exists('loaded_project') || &cp
+ finish
+endif
+let loaded_project=1
+
+function! s:Project(filename) " <<<
+ " Initialization <<<
+ if exists("g:proj_running")
+ if strlen(a:filename) != 0
+ call confirm('Project already loaded; ignoring filename "'.a:filename."\".\n".'See ":help project-invoking" for information about changing project files.', "&OK", 1)
+ endif
+ let filename=bufname(g:proj_running)
+ else
+ if strlen(a:filename) == 0
+ let filename ='~/.vimprojects' " Default project filename
+ else
+ let filename = a:filename
+ endif
+ endif
+ if !exists('g:proj_window_width')
+ let g:proj_window_width=24 " Default project window width
+ endif
+ if !exists('g:proj_window_increment')
+ let g:proj_window_increment=100 " Project Window width increment
+ endif
+ if !exists('g:proj_flags')
+ if has("win32") || has("mac")
+ let g:proj_flags='imst' " Project default flags for windows/mac
+ else
+ let g:proj_flags='imstb' " Project default flags for everything else
+ endif
+ endif
+ if !exists("g:proj_running") || (bufwinnr(g:proj_running) == -1) " Open the Project Window
+ exec 'silent vertical new '.filename
+ if match(g:proj_flags, '\CF') == -1 " We're floating
+ silent! wincmd H
+ exec 'vertical resize '.g:proj_window_width
+ endif
+ setlocal nomodeline
+ else
+ silent! 99wincmd h
+ if bufwinnr(g:proj_running) == -1
+ vertical split
+ let v:errmsg="nothing"
+ silent! bnext
+ if 'nothing' != v:errmsg
+ enew
+ endif
+ endif
+ return
+ endif
+ " Process the flags
+ let b:proj_cd_cmd='cd'
+ if match(g:proj_flags, '\Cl') != -1
+ let b:proj_cd_cmd = 'lcd'
+ endif
+
+ let b:proj_locate_command='silent! wincmd H'
+ let b:proj_resize_command='exec ''vertical resize ''.g:proj_window_width'
+ if match(g:proj_flags, '\CF') != -1 " Set the resize commands to nothing
+ let b:proj_locate_command=''
+ let b:proj_resize_command=''
+ endif
+
+ let g:proj_last_buffer = -1
+ ">>>
+ " ProjFoldText() <<<
+ " The foldtext function for displaying just the description.
+ function! ProjFoldText()
+ let line=substitute(getline(v:foldstart),'^[ \t#]*\([^=]*\).*', '\1', '')
+ let line=strpart(' ', 0, (v:foldlevel - 1)).substitute(line,'\s*{\+\s*', '', '')
+ return line
+ endfunction ">>>
+ " s:DoSetup() <<<
+ " Ensure everything is set up
+ function! s:DoSetup()
+ setlocal foldenable foldmethod=marker foldmarker={,} commentstring=%s foldcolumn=0 nonumber noswapfile shiftwidth=1
+ setlocal foldtext=ProjFoldText() nobuflisted nowrap
+ setlocal winwidth=1
+ if match(g:proj_flags, '\Cn') != -1
+ setlocal number
+ endif
+ endfunction ">>>
+ call s:DoSetup()
+ " Syntax Stuff <<<
+ if match(g:proj_flags, '\Cs')!=-1 && has('syntax') && exists('g:syntax_on') && !has('syntax_items')
+ syntax match projectDescriptionDir '^\s*.\{-}=\s*\(\\ \|\f\|:\|"\)\+' contains=projectDescription,projectWhiteError
+ syntax match projectDescription '\<.\{-}='he=e-1,me=e-1 contained nextgroup=projectDirectory contains=projectWhiteError
+ syntax match projectDescription '{\|}'
+ syntax match projectDirectory '=\(\\ \|\f\|:\)\+' contained
+ syntax match projectDirectory '=".\{-}"' contained
+ syntax match projectScriptinout '\<in\s*=\s*\(\\ \|\f\|:\|"\)\+' contains=projectDescription,projectWhiteError
+ syntax match projectScriptinout '\<out\s*=\s*\(\\ \|\f\|:\|"\)\+' contains=projectDescription,projectWhiteError
+ syntax match projectComment '#.*'
+ syntax match projectCD '\<CD\s*=\s*\(\\ \|\f\|:\|"\)\+' contains=projectDescription,projectWhiteError
+ syntax match projectFilterEntry '\<filter\s*=.*"' contains=projectWhiteError,projectFilterError,projectFilter,projectFilterRegexp
+ syntax match projectFilter '\<filter='he=e-1,me=e-1 contained nextgroup=projectFilterRegexp,projectFilterError,projectWhiteError
+ syntax match projectFlagsEntry '\<flags\s*=\( \|[^ ]*\)' contains=projectFlags,projectWhiteError
+ syntax match projectFlags '\<flags' contained nextgroup=projectFlagsValues,projectWhiteError
+ syntax match projectFlagsValues '=[^ ]* 'hs=s+1,me=e-1 contained contains=projectFlagsError
+ syntax match projectFlagsError '[^rtTsSwl= ]\+' contained
+ syntax match projectWhiteError '=\s\+'hs=s+1 contained
+ syntax match projectWhiteError '\s\+='he=e-1 contained
+ syntax match projectFilterError '=[^"]'hs=s+1 contained
+ syntax match projectFilterRegexp '=".*"'hs=s+1 contained
+ syntax match projectFoldText '^[^=]\+{'
+
+ highlight def link projectDescription Identifier
+ highlight def link projectScriptinout Identifier
+ highlight def link projectFoldText Identifier
+ highlight def link projectComment Comment
+ highlight def link projectFilter Identifier
+ highlight def link projectFlags Identifier
+ highlight def link projectDirectory Constant
+ highlight def link projectFilterRegexp String
+ highlight def link projectFlagsValues String
+ highlight def link projectWhiteError Error
+ highlight def link projectFlagsError Error
+ highlight def link projectFilterError Error
+ endif ">>>
+ " s:SortR(start, end) <<<
+ " Sort lines. SortR() is called recursively.
+ " from ":help eval-examples" by Robert Webb, slightly modified
+ function! s:SortR(start, end)
+ if (a:start >= a:end)
+ return
+ endif
+ let partition = a:start - 1
+ let middle = partition
+ let partStr = getline((a:start + a:end) / 2)
+ let i = a:start
+ while (i <= a:end)
+ let str = getline(i)
+ if str < partStr
+ let result = -1
+ elseif str > partStr
+ let result = 1
+ else
+ let result = 0
+ endif
+ if (result <= 0)
+ let partition = partition + 1
+ if (result == 0)
+ let middle = partition
+ endif
+ if (i != partition)
+ let str2 = getline(partition)
+ call setline(i, str2)
+ call setline(partition, str)
+ endif
+ endif
+ let i = i + 1
+ endwhile
+ if (middle != partition)
+ let str = getline(middle)
+ let str2 = getline(partition)
+ call setline(middle, str2)
+ call setline(partition, str)
+ endif
+ call s:SortR(a:start, partition - 1)
+ call s:SortR(partition + 1, a:end)
+ endfunc ">>>
+ " s:IsAbsolutePath(path) <<<
+ " Returns true if filename has an absolute path.
+ function! s:IsAbsolutePath(path)
+ if a:path =~ '^ftp:' || a:path =~ '^rcp:' || a:path =~ '^scp:' || a:path =~ '^http:'
+ return 2
+ endif
+ if a:path =~ '\$'
+ let path=expand(a:path) " Expand any environment variables that might be in the path
+ else
+ let path=a:path
+ endif
+ if path[0] == '/' || path[0] == '~' || path[0] == '\\' || path[1] == ':'
+ return 1
+ endif
+ return 0
+ endfunction " >>>
+ " s:DoSetupAndSplit() <<<
+ " Call DoSetup to ensure the settings are correct. Split to the next
+ " file.
+ function! s:DoSetupAndSplit()
+ call s:DoSetup() " Ensure that all the settings are right
+ let n = winnr() " Determine if there is a CTRL_W-p window
+ silent! wincmd p
+ if n == winnr()
+ silent! wincmd l
+ endif
+ if n == winnr()
+ " If n == winnr(), then there is no CTRL_W-p window
+ " So we have to create a new one
+ if bufnr('%') == g:proj_running
+ exec 'silent vertical new'
+ else
+ exec 'silent vertical split | silent! bnext'
+ endif
+ wincmd p " Go back to the Project Window and ensure it is the right width
+ exec b:proj_locate_command
+ exec b:proj_resize_command
+ wincmd p
+ endif
+ endfunction ">>>
+ " s:DoSetupAndSplit_au() <<<
+ " Same as above but ensure that the Project window is the current
+ " window. Only called from an autocommand
+ function! s:DoSetupAndSplit_au()
+ if winbufnr(0) != g:proj_running
+ return
+ endif
+ call s:DoSetup() " Ensure that all the settings are right
+ if winbufnr(2) == -1 " We're the only window right now.
+ exec 'silent vertical split | bnext'
+ if bufnr('%') == g:proj_running
+ enew
+ endif
+ if bufnr('%') == g:proj_last_buffer | bnext | bprev | bnext | endif
+ wincmd p " Go back to the Project Window and ensure it is the right width
+ exec b:proj_locate_command
+ exec b:proj_resize_command
+ elseif(winnr() != 1)
+ exec b:proj_locate_command
+ exec b:proj_resize_command
+ endif
+ endfunction
+ function! s:RecordPrevBuffer_au()
+ let g:proj_last_buffer = bufnr('%')
+ endfunction ">>>
+ " s:RecursivelyConstructDirectives(lineno) <<<
+ " Construct the inherited directives
+ function! s:RecursivelyConstructDirectives(lineno)
+ let lineno=s:FindFoldTop(a:lineno)
+ let foldlineno = lineno
+ let foldlev=foldlevel(lineno)
+ let parent_infoline = ''
+ if foldlev > 1
+ while foldlevel(lineno) >= foldlev " Go to parent fold
+ if lineno < 1
+ echoerr 'Some kind of fold error. Check your syntax.'
+ return
+ endif
+ let lineno = lineno - 1
+ endwhile
+ let parent_infoline = s:RecursivelyConstructDirectives(lineno)
+ endif
+ let parent_home = s:GetHome(parent_infoline, '')
+ let parent_c_d = s:GetCd(parent_infoline, parent_home)
+ let parent_scriptin = s:GetScriptin(parent_infoline, parent_home)
+ let parent_scriptout = s:GetScriptout(parent_infoline, parent_home)
+ let parent_filter = s:GetFilter(parent_infoline, '*')
+ let infoline = getline(foldlineno)
+ " Extract the home directory of this fold
+ let home=s:GetHome(infoline, parent_home)
+ if home != ''
+ if (foldlevel(foldlineno) == 1) && !s:IsAbsolutePath(home)
+ call confirm('Outermost Project Fold must have absolute path! Or perhaps the path does not exist.', "&OK", 1)
+ let home = '~' " Some 'reasonable' value
+ endif
+ endif
+ " Extract any CD information
+ let c_d = s:GetCd(infoline, home)
+ if c_d != ''
+ if (foldlevel(foldlineno) == 1) && !s:IsAbsolutePath(c_d)
+ call confirm('Outermost Project Fold must have absolute CD path! Or perhaps the path does not exist.', "&OK", 1)
+ let c_d = '.' " Some 'reasonable' value
+ endif
+ else
+ let c_d=parent_c_d
+ endif
+ " Extract scriptin
+ let scriptin = s:GetScriptin(infoline, home)
+ if scriptin == ''
+ let scriptin = parent_scriptin
+ endif
+ " Extract scriptout
+ let scriptout = s:GetScriptout(infoline, home)
+ if scriptout == ''
+ let scriptout = parent_scriptout
+ endif
+ " Extract filter
+ let filter = s:GetFilter(infoline, parent_filter)
+ if filter == '' | let filter = parent_filter | endif
+ return s:ConstructInfo(home, c_d, scriptin, scriptout, '', filter)
+ endfunction ">>>
+ " s:ConstructInfo(home, c_d, scriptin, scriptout, flags, filter) <<<
+ function! s:ConstructInfo(home, c_d, scriptin, scriptout, flags, filter)
+ let retval='Directory='.a:home
+ if a:c_d[0] != ''
+ let retval=retval.' CD='.a:c_d
+ endif
+ if a:scriptin[0] != ''
+ let retval=retval.' in='.a:scriptin
+ endif
+ if a:scriptout[0] != ''
+ let retval=retval.' out='.a:scriptout
+ endif
+ if a:filter[0] != ''
+ let retval=retval.' filter="'.a:filter.'"'
+ endif
+ return retval
+ endfunction ">>>
+ " s:OpenEntry(line, precmd, editcmd) <<<
+ " Get the filename under the cursor, and open a window with it.
+ function! s:OpenEntry(line, precmd, editcmd, dir)
+ silent exec a:precmd
+ if (a:editcmd[0] != '')
+ if a:dir
+ let fname='.'
+ else
+ if (foldlevel(a:line) == 0) && (a:editcmd[0] != '')
+ return 0 " If we're outside a fold, do nothing
+ endif
+ let fname=substitute(getline(a:line), '\s*#.*', '', '') " Get rid of comments and whitespace before comment
+ let fname=substitute(fname, '^\s*\(.*\)', '\1', '') " Get rid of leading whitespace
+ if strlen(fname) == 0
+ return 0 " The line is blank. Do nothing.
+ endif
+ endif
+ else
+ let fname='.'
+ endif
+ let infoline = s:RecursivelyConstructDirectives(a:line)
+ let retval=s:OpenEntry2(a:line, infoline, fname, a:editcmd)
+ call s:DisplayInfo()
+ return retval
+ endfunction
+ ">>>
+ " s:OpenEntry2(line, infoline, precmd, editcmd) <<<
+ " Get the filename under the cursor, and open a window with it.
+ function! s:OpenEntry2(line, infoline, fname, editcmd)
+ let fname=escape(a:fname, ' %#') " Thanks to Thomas Link for cluing me in on % and #
+ let home=s:GetHome(a:infoline, '').'/'
+ if home=='/'
+ echoerr 'Project structure error. Check your syntax.'
+ return
+ endif
+ "Save the cd command
+ let cd_cmd = b:proj_cd_cmd
+ if a:editcmd[0] != '' " If editcmd is '', then just set up the environment in the Project Window
+ call s:DoSetupAndSplit()
+ " If it is an absolute path, don't prepend home
+ if !s:IsAbsolutePath(fname)
+ let fname=home.fname
+ endif
+ if s:IsAbsolutePath(fname) == 2
+ exec a:editcmd.' '.fname
+ else
+ silent exec 'silent '.a:editcmd.' '.fname
+ endif
+ else " only happens in the Project File
+ exec 'au! BufEnter,BufLeave '.expand('%:p')
+ endif
+ " Extract any CD information
+ let c_d = s:GetCd(a:infoline, home)
+ if c_d != '' && (s:IsAbsolutePath(home) != 2)
+ if match(g:proj_flags, '\CL') != -1
+ call s:SetupAutoCommand(c_d)
+ endif
+ if !isdirectory(glob(c_d))
+ call confirm("From this fold's entry,\nCD=".'"'.c_d.'" is not a valid directory.', "&OK", 1)
+ else
+ silent exec cd_cmd.' '.c_d
+ endif
+ endif
+ " Extract any scriptin information
+ let scriptin = s:GetScriptin(a:infoline, home)
+ if scriptin != ''
+ if !filereadable(glob(scriptin))
+ call confirm('"'.scriptin.'" not found. Ignoring.', "&OK", 1)
+ else
+ call s:SetupScriptAutoCommand('BufEnter', scriptin)
+ exec 'source '.scriptin
+ endif
+ endif
+ let scriptout = s:GetScriptout(a:infoline, home)
+ if scriptout != ''
+ if !filereadable(glob(scriptout))
+ call confirm('"'.scriptout.'" not found. Ignoring.', "&OK", 1)
+ else
+ call s:SetupScriptAutoCommand('BufLeave', scriptout)
+ endif
+ endif
+ return 1
+ endfunction
+ ">>>
+ " s:DoFoldOrOpenEntry(cmd0, cmd1) <<<
+ " Used for double clicking. If the mouse is on a fold, open/close it. If
+ " not, try to open the file.
+ function! s:DoFoldOrOpenEntry(cmd0, cmd1)
+ if getline('.')=~'{\|}' || foldclosed('.') != -1
+ normal! za
+ else
+ call s:DoEnsurePlacementSize_au()
+ call s:OpenEntry(line('.'), a:cmd0, a:cmd1, 0)
+ if (match(g:proj_flags, '\Cc') != -1)
+ let g:proj_mywinnumber = winbufnr(0)
+ Project
+ hide
+ if(g:proj_mywinnumber != winbufnr(0))
+ wincmd p
+ endif
+ wincmd =
+ endif
+ endif
+ endfunction ">>>
+ " s:VimDirListing(filter, padding, separator, filevariable, filecount, dirvariable, dircount) <<<
+ function! s:VimDirListing(filter, padding, separator, filevariable, filecount, dirvariable, dircount)
+ let end = 0
+ let files=''
+ let filter = a:filter
+ " Chop up the filter
+ " Apparently glob() cannot take something like this: glob('*.c *.h')
+ let while_var = 1
+ while while_var
+ let end = stridx(filter, ' ')
+ if end == -1
+ let end = strlen(filter)
+ let while_var = 0
+ endif
+ let single=glob(strpart(filter, 0, end))
+ if strlen(single) != 0
+ let files = files.single."\010"
+ endif
+ let filter = strpart(filter, end + 1)
+ endwhile
+ " files now contains a list of everything in the directory. We need to
+ " weed out the directories.
+ let fnames=files
+ let {a:filevariable}=''
+ let {a:dirvariable}=''
+ let {a:filecount}=0
+ let {a:dircount}=0
+ while strlen(fnames) > 0
+ let fname = substitute(fnames, '\(\(\f\|[ :\[\]]\)*\).*', '\1', '')
+ let fnames = substitute(fnames, '\(\f\|[ :\[\]]\)*.\(.*\)', '\2', '')
+ if isdirectory(glob(fname))
+ let {a:dirvariable}={a:dirvariable}.a:padding.fname.a:separator
+ let {a:dircount}={a:dircount} + 1
+ else
+ let {a:filevariable}={a:filevariable}.a:padding.fname.a:separator
+ let {a:filecount}={a:filecount} + 1
+ endif
+ endwhile
+ endfunction ">>>
+ " s:GenerateEntry(recursive, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort) <<<
+ function! s:GenerateEntry(recursive, line, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort)
+ let line=a:line
+ if a:dir =~ '\\ '
+ let dir='"'.substitute(a:dir, '\\ ', ' ', 'g').'"'
+ else
+ let dir=a:dir
+ endif
+ let spaces=strpart(' ', 0, a:foldlev)
+ let c_d=(strlen(a:c_d) > 0) ? 'CD='.a:c_d.' ' : ''
+ let c_d=(strlen(a:filter_directive) > 0) ? c_d.'filter="'.a:filter_directive.'" ': c_d
+ call append(line, spaces.'}')
+ call append(line, spaces.a:name.'='.dir.' '.c_d.'{')
+ if a:recursive
+ exec 'cd '.a:absolute_dir
+ call s:VimDirListing("*", '', "\010", 'b:files', 'b:filecount', 'b:dirs', 'b:dircount')
+ cd -
+ let dirs=b:dirs
+ let dcount=b:dircount
+ unlet b:files b:filecount b:dirs b:dircount
+ while dcount > 0
+ let dname = substitute(dirs, '\(\( \|\f\|:\)*\).*', '\1', '')
+ let edname = escape(dname, ' ')
+ let dirs = substitute(dirs, '\( \|\f\|:\)*.\(.*\)', '\2', '')
+ let line=s:GenerateEntry(1, line + 1, dname, a:absolute_dir.'/'.edname, edname, '', '', a:filter, a:foldlev+1, a:sort)
+ let dcount=dcount-1
+ endwhile
+ endif
+ return line+1
+ endfunction " >>>
+ " s:DoEntryFromDir(line, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort) <<<
+ " Generate the fold from the directory hierarchy (if recursive), then
+ " fill it in with RefreshEntriesFromDir()
+ function! s:DoEntryFromDir(recursive, line, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort)
+ call s:GenerateEntry(a:recursive, a:line, a:name, escape(a:absolute_dir, ' '), escape(a:dir, ' '), escape(a:c_d, ' '), a:filter_directive, a:filter, a:foldlev, a:sort)
+ normal! j
+ call s:RefreshEntriesFromDir(1)
+ endfunction ">>>
+ " s:CreateEntriesFromDir(recursive) <<<
+ " Prompts user for information and then calls s:DoEntryFromDir()
+ function! s:CreateEntriesFromDir(recursive)
+ " Save a mark for the current cursor position
+ normal! mk
+ let line=line('.')
+ let name = inputdialog('Enter the Name of the Entry: ')
+ if strlen(name) == 0
+ return
+ endif
+ let foldlev=foldlevel(line)
+ if (foldclosed(line) != -1) || (getline(line) =~ '}')
+ let foldlev=foldlev - 1
+ endif
+ let absolute = (foldlev <= 0)?'Absolute ': ''
+ let home=''
+ let filter='*'
+ if (match(g:proj_flags, '\Cb') != -1) && has('browse')
+ " Note that browse() is inconsistent: On Win32 you can't select a
+ " directory, and it gives you a relative path.
+ let dir = browse(0, 'Enter the '.absolute.'Directory to Load: ', '', '')
+ let dir = fnamemodify(dir, ':p')
+ else
+ let dir = inputdialog('Enter the '.absolute.'Directory to Load: ', '')
+ endif
+ if (dir[strlen(dir)-1] == '/') || (dir[strlen(dir)-1] == '\\')
+ let dir=strpart(dir, 0, strlen(dir)-1) " Remove trailing / or \
+ endif
+ let dir = substitute(dir, '^\~', $HOME, 'g')
+ if (foldlev > 0)
+ let parent_directive=s:RecursivelyConstructDirectives(line)
+ let filter = s:GetFilter(parent_directive, '*')
+ let home=s:GetHome(parent_directive, '')
+ if home[strlen(home)-1] != '/' && home[strlen(home)-1] != '\\'
+ let home=home.'/'
+ endif
+ unlet parent_directive
+ if s:IsAbsolutePath(dir)
+ " It is not a relative path Try to make it relative
+ let hend=matchend(dir, '\C'.glob(home))
+ if hend != -1
+ let dir=strpart(dir, hend) " The directory can be a relative path
+ else
+ let home=""
+ endif
+ endif
+ endif
+ if strlen(home.dir) == 0
+ return
+ endif
+ if !isdirectory(home.dir)
+ if has("unix")
+ silent exec '!mkdir '.home.dir.' > /dev/null'
+ else
+ call confirm('"'.home.dir.'" is not a valid directory.', "&OK", 1)
+ return
+ endif
+ endif
+ let c_d = inputdialog('Enter the CD parameter: ', '')
+ let filter_directive = inputdialog('Enter the File Filter: ', '')
+ if strlen(filter_directive) != 0
+ let filter = filter_directive
+ endif
+ " If I'm on a closed fold, go to the bottom of it
+ if foldclosedend(line) != -1
+ let line = foldclosedend(line)
+ endif
+ let foldlev = foldlevel(line)
+ " If we're at the end of a fold . . .
+ if getline(line) =~ '}'
+ let foldlev = foldlev - 1 " . . . decrease the indentation by 1.
+ endif
+ " Do the work
+ call s:DoEntryFromDir(a:recursive, line, name, home.dir, dir, c_d, filter_directive, filter, foldlev, 0)
+ " Restore the cursor position
+ normal! `k
+ endfunction ">>>
+ " s:RefreshEntriesFromDir(recursive) <<<
+ " Finds metadata at the top of the fold, and then replaces all files
+ " with the contents of the directory. Works recursively if recursive is 1.
+ function! s:RefreshEntriesFromDir(recursive)
+ if foldlevel('.') == 0
+ echo 'Nothing to refresh.'
+ return
+ endif
+ " Open the fold.
+ if getline('.') =~ '}'
+ normal! zo[z
+ else
+ normal! zo]z[z
+ endif
+ let just_a_fold=0
+ let infoline = s:RecursivelyConstructDirectives(line('.'))
+ let immediate_infoline = getline('.')
+ if strlen(substitute(immediate_infoline, '[^=]*=\(\(\f\|:\|\\ \)*\).*', '\1', '')) == strlen(immediate_infoline)
+ let just_a_fold = 1
+ endif
+ " Extract the home directory of the fold
+ let home = s:GetHome(infoline, '')
+ if home == ''
+ " No Match. This means that this is just a label with no
+ " directory entry.
+ if a:recursive == 0
+ return " We're done--nothing to do
+ endif
+ " Mark that it is just a fold, so later we don't delete filenames
+ " that aren't there.
+ let just_a_fold = 1
+ endif
+ if just_a_fold == 0
+ " Extract the filter between quotes (we don't care what CD is).
+ let filter = s:GetFilter(infoline, '*')
+ " Extract the description (name) of the fold
+ let name = substitute(infoline, '^[#\t ]*\([^=]*\)=.*', '\1', '')
+ if strlen(name) == strlen(infoline)
+ return " If there's no name, we're done.
+ endif
+ if (home == '') || (name == '')
+ return
+ endif
+ " Extract the flags
+ let flags = s:GetFlags(immediate_infoline)
+ let sort = (match(g:proj_flags, '\CS') != -1)
+ if flags != ''
+ if match(flags, '\Cr') != -1
+ " If the flags do not contain r (refresh), then treat it just
+ " like a fold
+ let just_a_fold = 1
+ endif
+ if match(flags, '\CS') != -1
+ let sort = 1
+ endif
+ if match(flags, '\Cs') != -1
+ let sort = 0
+ endif
+ else
+ let flags=''
+ endif
+ endif
+ " Move to the first non-fold boundary line
+ normal! j
+ " Delete filenames until we reach the end of the fold
+ while getline('.') !~ '}'
+ if line('.') == line('$')
+ break
+ endif
+ if getline('.') !~ '{'
+ " We haven't reached a sub-fold, so delete what's there.
+ if (just_a_fold == 0) && (getline('.') !~ '^\s*#') && (getline('.') !~ '#.*pragma keep')
+ d _
+ else
+ " Skip lines only in a fold and comment lines
+ normal! j
+ endif
+ else
+ " We have reached a sub-fold. If we're doing recursive, then
+ " call this function again. If not, find the end of the fold.
+ if a:recursive == 1
+ call s:RefreshEntriesFromDir(1)
+ normal! ]zj
+ else
+ if foldclosed('.') == -1
+ normal! zc
+ endif
+ normal! j
+ endif
+ endif
+ endwhile
+ if just_a_fold == 0
+ " We're not just in a fold, and we have deleted all the filenames.
+ " Now it is time to regenerate what is in the directory.
+ if !isdirectory(glob(home))
+ call confirm('"'.home.'" is not a valid directory.', "&OK", 1)
+ else
+ let foldlev=foldlevel('.')
+ " T flag. Thanks Tomas Z.
+ if (match(flags, '\Ct') != -1) || ((match(g:proj_flags, '\CT') == -1) && (match(flags, '\CT') == -1))
+ " Go to the top of the fold (force other folds to the
+ " bottom)
+ normal! [z
+ normal! j
+ " Skip any comments
+ while getline('.') =~ '^\s*#'
+ normal! j
+ endwhile
+ endif
+ normal! k
+ let cwd=getcwd()
+ let spaces=strpart(' ', 0, foldlev)
+ exec 'cd '.home
+ if match(g:proj_flags, '\Ci') != -1
+ echon home."\r"
+ endif
+ call s:VimDirListing(filter, spaces, "\n", 'b:files', 'b:filecount', 'b:dirs', 'b:dircount')
+ if b:filecount > 0
+ normal! mk
+ silent! put =b:files
+ normal! `kj
+ if sort
+ call s:SortR(line('.'), line('.') + b:filecount - 1)
+ endif
+ else
+ normal! j
+ endif
+ unlet b:files b:filecount b:dirs b:dircount
+ exec 'cd '.cwd
+ endif
+ endif
+ " Go to the top of the refreshed fold.
+ normal! [z
+ endfunction ">>>
+ " s:MoveUp() <<<
+ " Moves the entity under the cursor up a line.
+ function! s:MoveUp()
+ let lineno=line('.')
+ if lineno == 1
+ return
+ endif
+ let fc=foldclosed('.')
+ let a_reg=@a
+ if lineno == line('$')
+ normal! "add"aP
+ else
+ normal! "addk"aP
+ endif
+ let @a=a_reg
+ if fc != -1
+ normal! zc
+ endif
+ endfunction ">>>
+ " s:MoveDown() <<<
+ " Moves the entity under the cursor down a line.
+ function! s:MoveDown()
+ let fc=foldclosed('.')
+ let a_reg=@a
+ normal! "add"ap
+ let @a=a_reg
+ if (fc != -1) && (foldclosed('.') == -1)
+ normal! zc
+ endif
+ endfunction " >>>
+ " s:DisplayInfo() <<<
+ " Displays filename and current working directory when i (info) is in
+ " the flags.
+ function! s:DisplayInfo()
+ if match(g:proj_flags, '\Ci') != -1
+ echo 'file: '.expand('%').', cwd: '.getcwd().', lines: '.line('$')
+ endif
+ endfunction ">>>
+ " s:SetupAutoCommand(cwd) <<<
+ " Sets up an autocommand to ensure that the cwd is set to the one
+ " desired for the fold regardless. :lcd only does this on a per-window
+ " basis, not a per-buffer basis.
+ function! s:SetupAutoCommand(cwd)
+ if !exists("b:proj_has_autocommand")
+ let b:proj_cwd_save = escape(getcwd(), ' ')
+ let b:proj_has_autocommand = 1
+ let bufname=escape(substitute(expand('%:p', 0), '\\', '/', 'g'), ' ')
+ exec 'au BufEnter '.bufname." let b:proj_cwd_save=escape(getcwd(), ' ') | cd ".a:cwd
+ exec 'au BufLeave '.bufname.' exec "cd ".b:proj_cwd_save'
+ exec 'au BufWipeout '.bufname.' au! * '.bufname
+ endif
+ endfunction ">>>
+ " s:SetupScriptAutoCommand(bufcmd, script) <<<
+ " Sets up an autocommand to run the scriptin script.
+ function! s:SetupScriptAutoCommand(bufcmd, script)
+ if !exists("b:proj_has_".a:bufcmd)
+ let b:proj_has_{a:bufcmd} = 1
+ exec 'au '.a:bufcmd.' '.escape(substitute(expand('%:p', 0), '\\', '/', 'g'), ' ').' source '.a:script
+ endif
+ endfunction " >>>
+ " s:DoEnsurePlacementSize_au() <<<
+ " Ensure that the Project window is on the left of the window and has
+ " the correct size. Only called from an autocommand
+ function! s:DoEnsurePlacementSize_au()
+ if (winbufnr(0) != g:proj_running) || (winnr() != 1)
+ if exists("g:proj_doinghelp")
+ if g:proj_doinghelp > 0
+ let g:proj_doinghelp = g:proj_doinghelp - 1
+ return
+ endif
+ unlet g:proj_doinghelp
+ return
+ endif
+ exec b:proj_locate_command
+ endif
+ exec b:proj_resize_command
+ endfunction ">>>
+ " s:Spawn(number) <<<
+ " Spawn an external command on the file
+ function! s:Spawn(number)
+ echo | if exists("g:proj_run".a:number)
+ let fname=getline('.')
+ if fname!~'{\|}'
+ let fname=substitute(fname, '\s*#.*', '', '')
+ let fname=substitute(fname, '^\s*\(.*\)\s*', '\1', '')
+ if fname == '' | return | endif
+ let parent_infoline = s:RecursivelyConstructDirectives(line('.'))
+ let home=expand(s:GetHome(parent_infoline, ''))
+ let c_d=expand(s:GetCd(parent_infoline, ''))
+ let command=substitute(g:proj_run{a:number}, '%%', "\010", 'g')
+ let command=substitute(command, '%f', escape(home.'/'.fname, '\'), 'g')
+ let command=substitute(command, '%F', substitute(escape(home.'/'.fname, '\'), ' ', '\\\\ ', 'g'), 'g')
+ let command=substitute(command, '%s', escape(home.'/'.fname, '\'), 'g')
+ let command=substitute(command, '%n', escape(fname, '\'), 'g')
+ let command=substitute(command, '%N', substitute(fname, ' ', '\\\\ ', 'g'), 'g')
+ let command=substitute(command, '%h', escape(home, '\'), 'g')
+ let command=substitute(command, '%H', substitute(escape(home, '\'), ' ', '\\\\ ', 'g'), 'g')
+ if c_d != ''
+ if c_d == home
+ let percent_r='.'
+ else
+ let percent_r=substitute(home, escape(c_d.'/', '\'), '', 'g')
+ endif
+ else
+ let percent_r=home
+ endif
+ let command=substitute(command, '%r', percent_r, 'g')
+ let command=substitute(command, '%R', substitute(percent_r, ' ', '\\\\ ', 'g'), 'g')
+ let command=substitute(command, '%d', escape(c_d, '\'), 'g')
+ let command=substitute(command, '%D', substitute(escape(c_d, '\'), ' ', '\\\\ ', 'g'), 'g')
+ let command=substitute(command, "\010", '%', 'g')
+ exec command
+ endif
+ endif
+ endfunction ">>>
+ " s:ListSpawn(varnamesegment) <<<
+ " List external commands
+ function! s:ListSpawn(varnamesegment)
+ let number = 1
+ while number < 10
+ if exists("g:proj_run".a:varnamesegment.number)
+ echohl LineNr | echo number.':' | echohl None | echon ' '.substitute(escape(g:proj_run{a:varnamesegment}{number}, '\'), "\n", '\\n', 'g')
+ else
+ echohl LineNr | echo number.':' | echohl None
+ endif
+ let number=number + 1
+ endwhile
+ endfunction ">>>
+ " s:FindFoldTop(line) <<<
+ " Return the line number of the directive line
+ function! s:FindFoldTop(line)
+ let lineno=a:line
+ if getline(lineno) =~ '}'
+ let lineno = lineno - 1
+ endif
+ while getline(lineno) !~ '{' && lineno > 1
+ if getline(lineno) =~ '}'
+ let lineno=s:FindFoldTop(lineno)
+ endif
+ let lineno = lineno - 1
+ endwhile
+ return lineno
+ endfunction ">>>
+ " s:FindFoldBottom(line) <<<
+ " Return the line number of the directive line
+ function! s:FindFoldBottom(line)
+ let lineno=a:line
+ if getline(lineno) =~ '{'
+ let lineno=lineno + 1
+ endif
+ while getline(lineno) !~ '}' && lineno < line('$')
+ if getline(lineno) =~ '{'
+ let lineno=s:FindFoldBottom(lineno)
+ endif
+ let lineno = lineno + 1
+ endwhile
+ return lineno
+ endfunction ">>>
+ " s:LoadAll(recurse, line) <<<
+ " Load all files in a project
+ function! s:LoadAll(recurse, line)
+ let b:loadcount=0
+ function! s:SpawnExec(infoline, fname, lineno, data)
+ if s:OpenEntry2(a:lineno, a:infoline, a:fname, 'e')
+ wincmd p
+ let b:loadcount=b:loadcount+1
+ echon b:loadcount."\r"
+ if getchar(0) != 0
+ let b:stop_everything=1
+ endif
+ endif
+ endfunction
+ call Project_ForEach(a:recurse, line('.'), "*<SID>SpawnExec", 0, '^\(.*l\)\@!')
+ delfunction s:SpawnExec
+ echon b:loadcount." Files Loaded\r"
+ unlet b:loadcount
+ if exists("b:stop_everything") | unlet b:stop_everything | endif
+ endfunction ">>>
+ " s:WipeAll(recurse, line) <<<
+ " Wipe all files in a project
+ function! s:WipeAll(recurse, line)
+ let b:wipecount=0
+ let b:totalcount=0
+ function! s:SpawnExec(home, c_d, fname, lineno, data)
+ let fname=escape(a:fname, ' ')
+ if s:IsAbsolutePath(fname)
+ let fname=fnamemodify(fname, ':n') " :n is coming, won't break anything now
+ else
+ let fname=fnamemodify(a:home.'/'.fname, ':n') " :n is coming, won't break anything now
+ endif
+ let b:totalcount=b:totalcount+1
+ let fname=substitute(fname, '^\~', $HOME, 'g')
+ if bufloaded(substitute(fname, '\\ ', ' ', 'g'))
+ if getbufvar(fname.'\>', '&modified') == 1
+ exec 'sb '.fname
+ wincmd L
+ w
+ wincmd p
+ endif
+ let b:wipecount=b:wipecount+1
+ exec 'bwipe! '.fname
+ endif
+ if b:totalcount % 5 == 0
+ echon b:wipecount.' of '.b:totalcount."\r"
+ redraw
+ endif
+ if getchar(0) != 0
+ let b:stop_everything=1
+ endif
+ endfunction
+ call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", 0, '^\(.*w\)\@!')
+ delfunction s:SpawnExec
+ echon b:wipecount.' of '.b:totalcount." Files Wiped\r"
+ unlet b:wipecount b:totalcount
+ if exists("b:stop_everything") | unlet b:stop_everything | endif
+ endfunction ">>>
+ " s:LoadAllSplit(recurse, line) <<<
+ " Load all files in a project using split windows.
+ " Contributed by A. Harrison
+ function! s:LoadAllSplit(recurse, line)
+ let b:loadcount=0
+ function! s:SpawnExec(infoline, fname, lineno, data)
+ let winNr = winnr() "get ProjectWindow number
+ if s:OpenEntry2(a:lineno, a:infoline, a:fname, 'sp')
+ exec winNr."wincmd w"
+ let b:loadcount=b:loadcount+1
+ echon b:loadcount."\r"
+ if getchar(0) != 0
+ let b:stop_everything=1
+ endif
+ endif
+ endfunction
+ call Project_ForEach(a:recurse, line('.'), "*<SID>SpawnExec", 0, '^\(.*l\)\@!')
+ delfunction s:SpawnExec
+ echon b:loadcount." Files Loaded\r"
+ unlet b:loadcount
+ if exists("b:stop_everything") | unlet b:stop_everything | endif
+ endfunction ">>>
+ " s:GrepAll(recurse, lineno, pattern) <<<
+ " Grep all files in a project, optionally recursively
+ function! s:GrepAll(recurse, lineno, pattern)
+ cunmap <buffer> help
+ let pattern=(a:pattern[0] == '')?input("GREP options and pattern: "):a:pattern
+ cnoremap <buffer> help let g:proj_doinghelp = 1<CR>:help
+ if pattern[0] == ''
+ return
+ endif
+ let b:escape_spaces=1
+ let fnames=Project_GetAllFnames(a:recurse, a:lineno, ' ')
+ unlet b:escape_spaces
+ cclose " Make sure grep window is closed
+ call s:DoSetupAndSplit()
+ if match(g:proj_flags, '\Cv') == -1
+ silent! exec 'silent! grep '.pattern.' '.fnames
+ if v:shell_error != 0
+ echo 'GREP error. Perhaps there are too many filenames.'
+ else
+ copen
+ endif
+ else
+ silent! exec 'silent! vimgrep '.pattern.' '.fnames
+ copen
+ endif
+ endfunction ">>>
+ " GetXXX Functions <<<
+ function! s:GetHome(info, parent_home)
+ " Thanks to Adam Montague for pointing out the need for @ in urls.
+ let home=substitute(a:info, '^[^=]*=\(\(\\ \|\f\|:\|@\)\+\).*', '\1', '')
+ if strlen(home) == strlen(a:info)
+ let home=substitute(a:info, '.\{-}"\(.\{-}\)".*', '\1', '')
+ if strlen(home) != strlen(a:info) | let home=escape(home, ' ') | endif
+ endif
+ if strlen(home) == strlen(a:info)
+ let home=a:parent_home
+ elseif home=='.'
+ let home=a:parent_home
+ elseif !s:IsAbsolutePath(home)
+ let home=a:parent_home.'/'.home
+ endif
+ return home
+ endfunction
+ function! s:GetFilter(info, parent_filter)
+ let filter = substitute(a:info, '.*\<filter="\([^"]*\).*', '\1', '')
+ if strlen(filter) == strlen(a:info) | let filter = a:parent_filter | endif
+ return filter
+ endfunction
+ function! s:GetCd(info, home)
+ let c_d=substitute(a:info, '.*\<CD=\(\(\\ \|\f\|:\)\+\).*', '\1', '')
+ if strlen(c_d) == strlen(a:info)
+ let c_d=substitute(a:info, '.*\<CD="\(.\{-}\)".*', '\1', '')
+ if strlen(c_d) != strlen(a:info) | let c_d=escape(c_d, ' ') | endif
+ endif
+ if strlen(c_d) == strlen(a:info)
+ let c_d=''
+ elseif c_d == '.'
+ let c_d = a:home
+ elseif !s:IsAbsolutePath(c_d)
+ let c_d = a:home.'/'.c_d
+ endif
+ return c_d
+ endfunction
+ function! s:GetScriptin(info, home)
+ let scriptin = substitute(a:info, '.*\<in=\(\(\\ \|\f\|:\)\+\).*', '\1', '')
+ if strlen(scriptin) == strlen(a:info)
+ let scriptin=substitute(a:info, '.*\<in="\(.\{-}\)".*', '\1', '')
+ if strlen(scriptin) != strlen(a:info) | let scriptin=escape(scriptin, ' ') | endif
+ endif
+ if strlen(scriptin) == strlen(a:info) | let scriptin='' | else
+ if !s:IsAbsolutePath(scriptin) | let scriptin=a:home.'/'.scriptin | endif | endif
+ return scriptin
+ endfunction
+ function! s:GetScriptout(info, home)
+ let scriptout = substitute(a:info, '.*\<out=\(\(\\ \|\f\|:\)\+\).*', '\1', '')
+ if strlen(scriptout) == strlen(a:info)
+ let scriptout=substitute(a:info, '.*\<out="\(.\{-}\)".*', '\1', '')
+ if strlen(scriptout) != strlen(a:info) | let scriptout=escape(scriptout, ' ') | endif
+ endif
+ if strlen(scriptout) == strlen(a:info) | let scriptout='' | else
+ if !s:IsAbsolutePath(scriptout) | let scriptout=a:home.'/'.scriptout | endif | endif
+ return scriptout
+ endfunction
+ function! s:GetFlags(info)
+ let flags=substitute(a:info, '.*\<flags=\([^ {]*\).*', '\1', '')
+ if (strlen(flags) == strlen(a:info))
+ let flags=''
+ endif
+ return flags
+ endfunction ">>>
+ " Project_GetAllFnames(recurse, lineno, separator) <<<
+ " Grep all files in a project, optionally recursively
+ function! Project_GetAllFnames(recurse, lineno, separator)
+ let b:fnamelist=''
+ function! s:SpawnExec(home, c_d, fname, lineno, data)
+ if exists('b:escape_spaces')
+ let fname=escape(a:fname, ' ')
+ else
+ let fname=a:fname
+ endif
+ if !s:IsAbsolutePath(a:fname)
+ let fname=a:home.'/'.fname
+ endif
+ let b:fnamelist=b:fnamelist.a:data.fname
+ endfunction
+ call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", a:separator, '')
+ delfunction s:SpawnExec
+ let retval=b:fnamelist
+ unlet b:fnamelist
+ return retval
+ endfunction ">>>
+ " Project_GetAllFnames(recurse, lineno, separator) <<<
+ " Grep all files in a project, optionally recursively
+ function! Project_GetFname(line)
+ if (foldlevel(a:line) == 0)
+ return ''
+ endif
+ let fname=substitute(getline(a:line), '\s*#.*', '', '') " Get rid of comments and whitespace before comment
+ let fname=substitute(fname, '^\s*\(.*\)', '\1', '') " Get rid of leading whitespace
+ if strlen(fname) == 0
+ return '' " The line is blank. Do nothing.
+ endif
+ if s:IsAbsolutePath(fname)
+ return fname
+ endif
+ let infoline = s:RecursivelyConstructDirectives(a:line)
+ return s:GetHome(infoline, '').'/'.fname
+ endfunction ">>>
+ " Project_ForEach(recurse, lineno, cmd, data, match) <<<
+ " Grep all files in a project, optionally recursively
+ function! Project_ForEach(recurse, lineno, cmd, data, match)
+ let info=s:RecursivelyConstructDirectives(a:lineno)
+ let lineno=s:FindFoldTop(a:lineno) + 1
+ let flags=s:GetFlags(getline(lineno - 1))
+ if (flags == '') || (a:match=='') || (match(flags, a:match) != -1)
+ call s:Project_ForEachR(a:recurse, lineno, info, a:cmd, a:data, a:match)
+ endif
+ endfunction
+ function! s:Project_ForEachR(recurse, lineno, info, cmd, data, match)
+ let home=s:GetHome(a:info, '')
+ let c_d=s:GetCd(a:info, home)
+ let scriptin = s:GetScriptin(a:info, home)
+ let scriptout = s:GetScriptout(a:info, home)
+ let filter = s:GetFilter(a:info, '')
+ let lineno = a:lineno
+ let curline=getline(lineno)
+ while (curline !~ '}') && (curline < line('$'))
+ if exists("b:stop_everything") && b:stop_everything | return 0 | endif
+ if curline =~ '{'
+ if a:recurse
+ let flags=s:GetFlags(curline)
+ if (flags == '') || (a:match=='') || (match(flags, a:match) != -1)
+ let this_home=s:GetHome(curline, home)
+ let this_cd=s:GetCd(curline, this_home)
+ if this_cd=='' | let this_cd=c_d | endif
+ let this_scriptin=s:GetScriptin(curline, this_home)
+ if this_scriptin == '' | let this_scriptin=scriptin | endif
+ let this_scriptout=s:GetScriptin(curline, this_home)
+ if this_scriptout == '' | let this_scriptout=scriptout | endif
+ let this_filter=s:GetFilter(curline, filter)
+ let lineno=s:Project_ForEachR(1, lineno+1,
+ \s:ConstructInfo(this_home, this_cd, this_scriptin, this_scriptout, flags, this_filter), a:cmd, a:data, a:match)
+ else
+ let lineno=s:FindFoldBottom(lineno)
+ endif
+ else
+ let lineno=s:FindFoldBottom(lineno)
+ endif
+ else
+ let fname=substitute(curline, '\s*#.*', '', '')
+ let fname=substitute(fname, '^\s*\(.*\)', '\1', '')
+ if (strlen(fname) != strlen(curline)) && (fname[0] != '')
+ if a:cmd[0] == '*'
+ call {strpart(a:cmd, 1)}(a:info, fname, lineno, a:data)
+ else
+ call {a:cmd}(home, c_d, fname, lineno, a:data)
+ endif
+ endif
+ endif
+ let lineno=lineno + 1
+ let curline=getline(lineno)
+ endwhile
+ return lineno
+ endfunction ">>>
+ " s:SpawnAll(recurse, number) <<<
+ " Spawn an external command on the files of a project
+ function! s:SpawnAll(recurse, number)
+ echo | if exists("g:proj_run_fold".a:number)
+ if g:proj_run_fold{a:number}[0] == '*'
+ function! s:SpawnExec(home, c_d, fname, lineno, data)
+ let command=substitute(strpart(g:proj_run_fold{a:data}, 1), '%s', escape(a:fname, ' \'), 'g')
+ let command=substitute(command, '%f', escape(a:fname, '\'), 'g')
+ let command=substitute(command, '%h', escape(a:home, '\'), 'g')
+ let command=substitute(command, '%d', escape(a:c_d, '\'), 'g')
+ let command=substitute(command, '%F', substitute(escape(a:fname, '\'), ' ', '\\\\ ', 'g'), 'g')
+ exec command
+ endfunction
+ call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", a:number, '.')
+ delfunction s:SpawnExec
+ else
+ let info=s:RecursivelyConstructDirectives(line('.'))
+ let home=s:GetHome(info, '')
+ let c_d=s:GetCd(info, '')
+ let b:escape_spaces=1
+ let fnames=Project_GetAllFnames(a:recurse, line('.'), ' ')
+ unlet b:escape_spaces
+ let command=substitute(g:proj_run_fold{a:number}, '%f', substitute(escape(fnames, '\'), '\\ ', ' ', 'g'), 'g')
+ let command=substitute(command, '%s', escape(fnames, '\'), 'g')
+ let command=substitute(command, '%h', escape(home, '\'), 'g')
+ let command=substitute(command, '%d', escape(c_d, '\'), 'g')
+ let command=substitute(command, '%F', escape(fnames, '\'), 'g')
+ exec command
+ if v:shell_error != 0
+ echo 'Shell error. Perhaps there are too many filenames.'
+ endif
+ endif
+ endif
+ endfunction ">>>
+ if !exists("g:proj_running")
+ " s:DoProjectOnly(void) <<<
+ " Make the file window the only one.
+ function! s:DoProjectOnly()
+ if winbufnr(0) != g:proj_running
+ let lzsave=&lz
+ set lz
+ only
+ Project
+ silent! wincmd p
+ let &lz=lzsave
+ unlet lzsave
+ endif
+ endfunction
+ " >>>
+
+ " Mappings <<<
+ nnoremap <buffer> <silent> <Return> \|:call <SID>DoFoldOrOpenEntry('', 'e')<CR>
+ nnoremap <buffer> <silent> <S-Return> \|:call <SID>DoFoldOrOpenEntry('', 'sp')<CR>
+ nnoremap <buffer> <silent> <C-Return> \|:call <SID>DoFoldOrOpenEntry('silent! only', 'e')<CR>
+ nnoremap <buffer> <silent> <LocalLeader>T \|:call <SID>DoFoldOrOpenEntry('', 'tabe')<CR>
+ nmap <buffer> <silent> <LocalLeader>s <S-Return>
+ nnoremap <buffer> <silent> <LocalLeader>S \|:call <SID>LoadAllSplit(0, line('.'))<CR>
+ nmap <buffer> <silent> <LocalLeader>o <C-Return>
+ nnoremap <buffer> <silent> <LocalLeader>i :echo <SID>RecursivelyConstructDirectives(line('.'))<CR>
+ nnoremap <buffer> <silent> <LocalLeader>I :echo Project_GetFname(line('.'))<CR>
+ nmap <buffer> <silent> <M-CR> <Return><C-W>p
+ nmap <buffer> <silent> <LocalLeader>v <M-CR>
+ nnoremap <buffer> <silent> <LocalLeader>l \|:call <SID>LoadAll(0, line('.'))<CR>
+ nnoremap <buffer> <silent> <LocalLeader>L \|:call <SID>LoadAll(1, line('.'))<CR>
+ nnoremap <buffer> <silent> <LocalLeader>w \|:call <SID>WipeAll(0, line('.'))<CR>
+ nnoremap <buffer> <silent> <LocalLeader>W \|:call <SID>WipeAll(1, line('.'))<CR>
+ nnoremap <buffer> <silent> <LocalLeader>W \|:call <SID>WipeAll(1, line('.'))<CR>
+ nnoremap <buffer> <silent> <LocalLeader>g \|:call <SID>GrepAll(0, line('.'), "")<CR>
+ nnoremap <buffer> <silent> <LocalLeader>G \|:call <SID>GrepAll(1, line('.'), "")<CR>
+ nnoremap <buffer> <silent> <2-LeftMouse> \|:call <SID>DoFoldOrOpenEntry('', 'e')<CR>
+ nnoremap <buffer> <silent> <S-2-LeftMouse> \|:call <SID>DoFoldOrOpenEntry('', 'sp')<CR>
+ nnoremap <buffer> <silent> <M-2-LeftMouse> <M-CR>
+ nnoremap <buffer> <silent> <S-LeftMouse> <LeftMouse>
+ nmap <buffer> <silent> <C-2-LeftMouse> <C-Return>
+ nnoremap <buffer> <silent> <C-LeftMouse> <LeftMouse>
+ nnoremap <buffer> <silent> <3-LeftMouse> <Nop>
+ nmap <buffer> <silent> <RightMouse> <space>
+ nmap <buffer> <silent> <2-RightMouse> <space>
+ nmap <buffer> <silent> <3-RightMouse> <space>
+ nmap <buffer> <silent> <4-RightMouse> <space>
+ nnoremap <buffer> <silent> <space> \|:silent exec 'vertical resize '.(match(g:proj_flags, '\Ct')!=-1 && winwidth('.') > g:proj_window_width?(g:proj_window_width):(winwidth('.') + g:proj_window_increment))<CR>
+ nnoremap <buffer> <silent> <C-Up> \|:silent call <SID>MoveUp()<CR>
+ nnoremap <buffer> <silent> <C-Down> \|:silent call <SID>MoveDown()<CR>
+ nmap <buffer> <silent> <LocalLeader><Up> <C-Up>
+ nmap <buffer> <silent> <LocalLeader><Down> <C-Down>
+ let k=1
+ while k < 10
+ exec 'nnoremap <buffer> <LocalLeader>'.k.' \|:call <SID>Spawn('.k.')<CR>'
+ exec 'nnoremap <buffer> <LocalLeader>f'.k.' \|:call <SID>SpawnAll(0, '.k.')<CR>'
+ exec 'nnoremap <buffer> <LocalLeader>F'.k.' \|:call <SID>SpawnAll(1, '.k.')<CR>'
+ let k=k+1
+ endwhile
+ nnoremap <buffer> <LocalLeader>0 \|:call <SID>ListSpawn("")<CR>
+ nnoremap <buffer> <LocalLeader>f0 \|:call <SID>ListSpawn("_fold")<CR>
+ nnoremap <buffer> <LocalLeader>F0 \|:call <SID>ListSpawn("_fold")<CR>
+ nnoremap <buffer> <silent> <LocalLeader>c :call <SID>CreateEntriesFromDir(0)<CR>
+ nnoremap <buffer> <silent> <LocalLeader>C :call <SID>CreateEntriesFromDir(1)<CR>
+ nnoremap <buffer> <silent> <LocalLeader>r :call <SID>RefreshEntriesFromDir(0)<CR>
+ nnoremap <buffer> <silent> <LocalLeader>R :call <SID>RefreshEntriesFromDir(1)<CR>
+ " For Windows users: same as \R
+ nnoremap <buffer> <silent> <F5> :call <SID>RefreshEntriesFromDir(1)<CR>
+ nnoremap <buffer> <silent> <LocalLeader>e :call <SID>OpenEntry(line('.'), '', '', 0)<CR>
+ nnoremap <buffer> <silent> <LocalLeader>E :call <SID>OpenEntry(line('.'), '', 'e', 1)<CR>
+ " The :help command stomps on the Project Window. Try to avoid that.
+ " This is not perfect, but it is alot better than without the mappings.
+ cnoremap <buffer> help let g:proj_doinghelp = 1<CR>:help
+ nnoremap <buffer> <F1> :let g:proj_doinghelp = 1<CR><F1>
+ " This is to avoid changing the buffer, but it is not fool-proof.
+ nnoremap <buffer> <silent> <C-^> <Nop>
+ "nnoremap <script> <Plug>ProjectOnly :let lzsave=&lz<CR>:set lz<CR><C-W>o:Project<CR>:silent! wincmd p<CR>:let &lz=lzsave<CR>:unlet lzsave<CR>
+ nnoremap <script> <Plug>ProjectOnly :call <SID>DoProjectOnly()<CR>
+ if match(g:proj_flags, '\Cm') != -1
+ if !hasmapto('<Plug>ProjectOnly')
+ nmap <silent> <unique> <C-W>o <Plug>ProjectOnly
+ nmap <silent> <unique> <C-W><C-O> <C-W>o
+ endif
+ endif " >>>
+ if filereadable(glob('~/.vimproject_mappings')) | source ~/.vimproject_mappings | endif
+ " Autocommands <<<
+ " Autocommands to clean up if we do a buffer wipe
+ " These don't work unless we substitute \ for / for Windows
+ let bufname=escape(substitute(expand('%:p', 0), '\\', '/', 'g'), ' ')
+ exec 'au BufWipeout '.bufname.' au! * '.bufname
+ exec 'au BufWipeout '.bufname.' unlet g:proj_running'
+ exec 'au BufWipeout '.bufname.' nunmap <C-W>o'
+ exec 'au BufWipeout '.bufname.' nunmap <C-W><C-O>'
+ " Autocommands to keep the window the specified size
+ exec 'au WinLeave '.bufname.' call s:DoEnsurePlacementSize_au()'
+ exec 'au BufEnter '.bufname.' call s:DoSetupAndSplit_au()'
+ au WinLeave * call s:RecordPrevBuffer_au()
+ " >>>
+ setlocal buflisted
+ let g:proj_running = bufnr(bufname.'\>')
+ if g:proj_running == -1
+ call confirm('Project/Vim error. Please Enter :Project again and report this bug.', "&OK", 1)
+ unlet g:proj_running
+ endif
+ setlocal nobuflisted
+ endif
+endfunction " >>>
+
+if exists(':Project') != 2
+ command -nargs=? -complete=file Project call <SID>Project('<args>')
+endif
+" Toggle Mapping
+if !exists("*<SID>DoToggleProject()") "<<<
+ function! s:DoToggleProject()
+ if !exists('g:proj_running') || bufwinnr(g:proj_running) == -1
+ Project
+ else
+ let g:proj_mywindow = winnr()
+ Project
+ hide
+ if(winnr() != g:proj_mywindow)
+ wincmd p
+ endif
+ unlet g:proj_mywindow
+ endif
+ endfunction
+endif ">>>
+nnoremap <script> <Plug>ToggleProject :call <SID>DoToggleProject()<CR>
+if exists('g:proj_flags') && (match(g:proj_flags, '\Cg') != -1)
+ if !hasmapto('<Plug>ToggleProject')
+ nmap <silent> <F12> <Plug>ToggleProject
+ endif
+endif
+
+finish
+
+" vim600: set foldmethod=marker foldmarker=<<<,>>> foldlevel=1:
diff --git a/files/.vim/plugin/rails.vim b/files/.vim/plugin/rails.vim
new file mode 100755
index 0000000..1ac6057
--- /dev/null
+++ b/files/.vim/plugin/rails.vim
@@ -0,0 +1,4666 @@
+" rails.vim - Detect a rails application
+" Author: Tim Pope <vimNOSPAM@tpope.info>
+" GetLatestVimScripts: 1567 1 :AutoInstall: rails.vim
+" URL: http://rails.vim.tpope.net/
+" $Id: rails.vim 239 2008-01-03 15:55:55Z tpope $
+
+" See doc/rails.txt for details. Grab it from the URL above if you don't have it
+" To access it from Vim, see :help add-local-help (hint: :helptags ~/.vim/doc)
+" Afterwards, you should be able to do :help rails
+
+" ============================================================================
+
+" Exit quickly when:
+" - this plugin was already loaded (or disabled)
+" - when 'compatible' is set
+if &cp || (exists("g:loaded_rails") && g:loaded_rails) && !(exists("g:rails_debug") && g:rails_debug)
+ finish
+endif
+let g:loaded_rails = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" Utility Functions {{{1
+
+function! s:sub(str,pat,rep)
+ return substitute(a:str,'\v\C'.a:pat,a:rep,'')
+endfunction
+
+function! s:gsub(str,pat,rep)
+ return substitute(a:str,'\v\C'.a:pat,a:rep,'g')
+endfunction
+
+function! s:string(str)
+ if exists("*string")
+ return string(a:str)
+ else
+ return "'" . s:gsub(a:str,"'","'.\"'\".'") . "'"
+ endif
+endfunction
+
+function! s:compact(ary)
+ return s:sub(s:sub(s:gsub(a:ary,'\n\n+','\n'),'\n$',''),'^\n','')
+endfunction
+
+function! s:escarg(p)
+ return s:gsub(a:p,'[ !%#]','\\&')
+endfunction
+
+function! s:esccmd(p)
+ return s:gsub(a:p,'[!%#]','\\&')
+endfunction
+
+function! s:ra()
+ " Rails root, escaped for use as single argument
+ return s:escarg(RailsRoot())
+endfunction
+
+function! s:rc()
+ " Rails root, escaped for use with a command (spaces not escaped)
+ return s:esccmd(RailsRoot())
+endfunction
+
+function! s:escvar(r)
+ let r = fnamemodify(a:r,':~')
+ let r = s:gsub(r,'\W','\="_".char2nr(submatch(0))."_"')
+ let r = s:gsub(r,'^\d','_&')
+ return r
+endfunction
+
+function! s:rv()
+ " Rails root, escaped to be a variable name
+ return s:escvar(RailsRoot())
+endfunction
+
+function! s:rquote(str)
+ " Imperfect but adequate for Ruby arguments
+ if a:str =~ '^[A-Za-z0-9_/.:-]\+$'
+ return a:str
+ elseif &shell =~? 'cmd'
+ return '"'.s:gsub(s:gsub(a:str,'\','\\'),'"','\\"').'"'
+ else
+ return "'".s:gsub(s:gsub(a:str,'\','\\'),"'","'\\\\''")."'"
+ endif
+endfunction
+
+function! s:sname()
+ return fnamemodify(s:file,':t:r')
+endfunction
+
+function! s:hasfile(file)
+ return filereadable(RailsRoot().'/'.a:file)
+endfunction
+
+function! s:rubyexestr(cmd)
+ if RailsRoot() =~ '://'
+ return "ruby ".a:cmd
+ else
+ return "ruby -C ".s:rquote(RailsRoot())." ".a:cmd
+ endif
+endfunction
+
+function! s:rubyexestrwithfork(cmd)
+ if s:getopt("ruby_fork_port","ab") && executable("ruby_fork_client")
+ return "ruby_fork_client -p ".s:getopt("ruby_fork_port","ab")." ".a:cmd
+ else
+ return s:rubyexestr(a:cmd)
+ endif
+endfunction
+
+function! s:rubyexebg(cmd)
+ let cmd = s:esccmd(s:rubyexestr(a:cmd))
+ if has("gui_win32")
+ if &shellcmdflag == "-c" && ($PATH . &shell) =~? 'cygwin'
+ silent exe "!cygstart -d ".s:rquote(RailsRoot())." ruby ".a:cmd
+ else
+ exe "!start ".cmd
+ endif
+ elseif exists("$STY") && !has("gui_running") && s:getopt("gnu_screen","abg") && executable("screen")
+ silent exe "!screen -ln -fn -t ".s:sub(s:sub(a:cmd,'\s.*',''),'^%(script|-rcommand)/','rails-').' '.cmd
+ else
+ exe "!".cmd
+ endif
+ return v:shell_error
+endfunction
+
+function! s:rubyexe(cmd,...)
+ if a:0
+ call s:rubyexebg(a:cmd)
+ else
+ exe "!".s:esccmd(s:rubyexestr(a:cmd))
+ endif
+ return v:shell_error
+endfunction
+
+function! s:rubyeval(ruby,...)
+ if a:0 > 0
+ let def = a:1
+ else
+ let def = ""
+ endif
+ if !executable("ruby")
+ return def
+ endif
+ let cmd = s:rubyexestr('-e '.s:rquote('begin; require %{rubygems}; rescue LoadError; end; begin; require %{active_support}; rescue LoadError; end; '.a:ruby))
+ "let g:rails_last_ruby_command = cmd
+ " If the shell is messed up, this command could cause an error message
+ silent! let results = system(cmd)
+ "let g:rails_last_ruby_result = results
+ if v:shell_error != 0 " results =~ '-e:\d' || results =~ 'ruby:.*(fatal)'
+ return def
+ else
+ return results
+ endif
+endfunction
+
+function! s:railseval(ruby)
+ if a:0 > 0
+ let def = a:1
+ else
+ let def = ""
+ endif
+ if !executable("ruby")
+ return def
+ endif
+ let args = "-r./config/boot -r ".s:rquote(RailsRoot()."/config/environment")." -e ".s:rquote(a:ruby)
+ let cmd = s:rubyexestrwithfork(args)
+ " If the shell is messed up, this command could cause an error message
+ silent! let results = system(cmd)
+ if v:shell_error != 0 " results =~ '-e:\d' || results =~ 'ruby:.*(fatal)'
+ return def
+ else
+ return results
+ endif
+endfunction
+
+function! s:endof(lnum)
+ if a:lnum == 0
+ return 0
+ endif
+ if &ft == "yaml" || expand("%:e") == "yml"
+ return -1
+ endif
+ let cline = getline(a:lnum)
+ let spc = matchstr(cline,'^\s*')
+ let endpat = '\<end\>'
+ if matchstr(getline(a:lnum+1),'^'.spc) && !matchstr(getline(a:lnum+1),'^'.spc.endpat) && matchstr(cline,endpat)
+ return a:lnum
+ endif
+ let endl = a:lnum
+ while endl <= line('$')
+ let endl = endl + 1
+ if getline(endl) =~ '^'.spc.endpat
+ return endl
+ elseif getline(endl) =~ '^=begin\>'
+ while getline(endl) !~ '^=end\>' && endl <= line('$')
+ let endl = endl + 1
+ endwhile
+ let endl = endl + 1
+ elseif getline(endl) !~ '^'.spc && getline(endl) !~ '^\s*\%(#.*\)\=$'
+ return 0
+ endif
+ endwhile
+ return 0
+endfunction
+
+function! s:lastmethodline(...)
+ if a:0
+ let line = a:1
+ else
+ let line = line(".")
+ endif
+ while line > 0 && getline(line) !~ &l:define
+ let line = line - 1
+ endwhile
+ let lend = s:endof(line)
+ if lend < 0 || lend >= (a:0 ? a:1 : line("."))
+ return line
+ else
+ return 0
+ endif
+endfunction
+
+function! s:lastmethod()
+ let line = s:lastmethodline()
+ if line
+ return s:sub(matchstr(getline(line),'\%('.&define.'\)\zs\h\%(\k\|[:.]\)*[?!=]\='),':$','')
+ else
+ return ""
+ endif
+endfunction
+
+function! s:lastrespondtoline(...)
+ let mline = s:lastmethodline()
+ if a:0
+ let line = a:1
+ else
+ let line = line(".")
+ endif
+ while line > mline && getline(line) !~ '\C^\s*respond_to\s*\%(\<do\)\s*|\zs\h\k*\ze|'
+ let line = line - 1
+ endwhile
+ let lend = s:endof(line)
+ if lend >= (a:0 ? a:1 : line("."))
+ return line
+ else
+ return -1
+ endif
+endfunction
+
+function! s:lastformat()
+ let rline = s:lastrespondtoline()
+ if rline
+ let variable = matchstr(getline(rline),'\C^\s*respond_to\s*\%(\<do\|{\)\s*|\zs\h\k*\ze|')
+ let line = line('.')
+ while line > rline
+ let match = matchstr(getline(line),'\C^\s*'.variable.'\s*\.\s*\zs\h\k*')
+ if match != ''
+ return match
+ endif
+ let line = line - 1
+ endwhile
+ endif
+ return ""
+endfunction
+
+function! s:format(...)
+ if RailsFileType() =~ '^view\>'
+ let format = fnamemodify(RailsFilePath(),':r:e')
+ else
+ let format = s:lastformat()
+ endif
+ if format == ''
+ if fnamemodify(RailsFilePath(),':e') == 'rhtml'
+ let format = 'html'
+ elseif fnamemodify(RailsFilePath(),':e') == 'rxml'
+ let format = 'xml'
+ elseif fnamemodify(RailsFilePath(),':e') == 'rjs'
+ let format = 'js'
+ elseif a:0
+ return a:1
+ endif
+ endif
+ return format
+endfunction
+
+function! s:viewspattern()
+ return '\%('.s:gsub(s:view_types,',','\\|').'\)'
+endfunction
+
+function! s:controller(...)
+ let t = RailsFileType()
+ let f = RailsFilePath()
+ let o = s:getopt("controller","lb")
+ if o != ""
+ return o
+ elseif f =~ '\<app/views/layouts/'
+ return s:sub(f,'.*<app/views/layouts/(.{-})\..*','\1')
+ elseif f =~ '\<app/views/'
+ return s:sub(f,'.*<app/views/(.{-})/\k+\.\k+%(\.\k+)=$','\1')
+ elseif f =~ '\<app/helpers/.*_helper\.rb$'
+ return s:sub(f,'.*<app/helpers/(.{-})_helper\.rb$','\1')
+ elseif f =~ '\<app/controllers/application\.rb$'
+ return "application"
+ elseif f =~ '\<app/controllers/.*_controller\.rb$'
+ return s:sub(f,'.*<app/controllers/(.{-})_controller\.rb$','\1')
+ elseif f =~ '\<app/apis/.*_api\.rb$'
+ return s:sub(f,'.*<app/apis/(.{-})_api\.rb$','\1')
+ elseif f =~ '\<test/functional/.*_controller_test\.rb$'
+ return s:sub(f,'.*<test/functional/(.{-})_controller_test\.rb$','\1')
+ elseif f =~ '\<spec/controllers/.*_controller_spec\.rb$'
+ return s:sub(f,'.*<spec/controllers/(.{-})_controller_spec\.rb$','\1')
+ elseif f =~ '\<spec/helpers/.*_helper_spec\.rb$'
+ return s:sub(f,'.*<spec/helpers/(.{-})_helper_spec\.rb$','\1')
+ elseif f =~ '\<spec/views/.*/\w\+_view_spec\.rb$'
+ return s:sub(f,'.*<spec/views/(.{-})/\w+_view_spec\.rb$','\1')
+ elseif f =~ '\<components/.*_controller\.rb$'
+ return s:sub(f,'.*<components/(.{-})_controller\.rb$','\1')
+ elseif f =~ '\<components/.*\.'.s:viewspattern().'$'
+ return s:sub(f,'.*<components/(.{-})/\k+\.\k+$','\1')
+ elseif f =~ '\<app/models/.*\.rb$' && t =~ '^model-mailer\>'
+ return s:sub(f,'.*<app/models/(.{-})\.rb$','\1')
+ elseif f =~ '\<public/stylesheets/.*\.css$'
+ return s:sub(f,'.*<public/stylesheets/(.{-})\.css$','\1')
+ elseif a:0 && a:1
+ return s:pluralize(s:model())
+ endif
+ return ""
+endfunction
+
+function! s:model(...)
+ let f = RailsFilePath()
+ let o = s:getopt("model","lb")
+ if o != ""
+ return o
+ elseif f =~ '\<app/models/.*_observer.rb$'
+ return s:sub(f,'.*<app/models/(.*)_observer\.rb$','\1')
+ elseif f =~ '\<app/models/.*\.rb$'
+ return s:sub(f,'.*<app/models/(.*)\.rb$','\1')
+ elseif f =~ '\<test/unit/.*_observer_test\.rb$'
+ return s:sub(f,'.*<test/unit/(.*)_observer_test\.rb$','\1')
+ elseif f =~ '\<test/unit/.*_test\.rb$'
+ return s:sub(f,'.*<test/unit/(.*)_test\.rb$','\1')
+ elseif f =~ '\<spec/models/.*_spec\.rb$'
+ return s:sub(f,'.*<spec/models/(.*)_spec\.rb$','\1')
+ elseif f =~ '\<\%(test\|spec\)/fixtures/.*\.\w*\~\=$'
+ return s:singularize(s:sub(f,'.*<%(test|spec)/fixtures/(.*)\.\w*\~=$','\1'))
+ elseif a:0 && a:1
+ return s:singularize(s:controller())
+ endif
+ return ""
+endfunction
+
+function! s:underscore(str)
+ let str = s:gsub(a:str,'::','/')
+ let str = s:gsub(str,'(\u+)(\u\l)','\1_\2')
+ let str = s:gsub(str,'(\l|\d)(\u)','\1_\2')
+ let str = s:gsub(str,'-','_')
+ let str = tolower(str)
+ return str
+endfunction
+
+function! s:camelize(str)
+ let str = s:gsub(a:str,'/(.)','::\u\1')
+ let str = s:gsub(str,'%([_-]|<)(.)','\u\1')
+ return str
+endfunction
+
+function! s:singularize(word)
+ " Probably not worth it to be as comprehensive as Rails but we can
+ " still hit the common cases.
+ let word = a:word
+ if word =~? '\.js$' || word == ''
+ return word
+ endif
+ let word = s:sub(word,'eople$','ersons')
+ let word = s:sub(word,'[aeio]@<!ies$','ys')
+ let word = s:sub(word,'xe[ns]$','xs')
+ let word = s:sub(word,'ves$','fs')
+ let word = s:sub(word,'ss%(es)=$','sss')
+ let word = s:sub(word,'s$','')
+ return word
+endfunction
+
+function! s:pluralize(word)
+ let word = a:word
+ if word == ''
+ return word
+ endif
+ let word = s:sub(word,'[aeio]@<!y$','ie')
+ let word = s:sub(word,'%([osxz]|[cs]h)$','&e')
+ let word = s:sub(word,'f@<!f$','ve')
+ let word = word."s"
+ let word = s:sub(word,'ersons$','eople')
+ return word
+endfunction
+
+function! s:usesubversion()
+ if !exists("b:rails_use_subversion")
+ let b:rails_use_subversion = s:getopt("subversion","abg") && (RailsRoot()!="") && isdirectory(RailsRoot()."/.svn")
+ endif
+ return b:rails_use_subversion
+endfunction
+
+function! s:environment()
+ if exists('$RAILS_ENV')
+ return $RAILS_ENV
+ else
+ return "development"
+ endif
+endfunction
+
+function! s:environments(...)
+ let e = s:getopt("environment","abg")
+ if e == ''
+ return "development\ntest\nproduction"
+ else
+ return s:gsub(e,'[:;,- ]',"\n")
+ endif
+endfunction
+
+function! s:warn(str)
+ echohl WarningMsg
+ echomsg a:str
+ echohl None
+ " Sometimes required to flush output
+ echo ""
+ let v:warningmsg = a:str
+endfunction
+
+function! s:error(str)
+ echohl ErrorMsg
+ echomsg a:str
+ echohl None
+ let v:errmsg = a:str
+endfunction
+
+function! s:debug(str)
+ if g:rails_debug
+ echohl Debug
+ echomsg a:str
+ echohl None
+ endif
+endfunction
+
+" }}}1
+" "Public" Interface {{{1
+
+" RailsRevision() and RailsRoot() the only official public functions
+
+function! RailsRevision()
+ return s:revision
+endfunction
+
+function! RailsRoot()
+ if exists("b:rails_root")
+ return b:rails_root
+ else
+ return ""
+ endif
+endfunction
+
+function! RailsFilePath()
+ if !exists("b:rails_root")
+ return ""
+ elseif exists("b:rails_file_path")
+ return b:rails_file_path
+ endif
+ let f = s:gsub(expand('%:p'),'\\ @!','/')
+ let f = s:sub(f,'/$','')
+ if s:gsub(b:rails_root,'\\ @!','/') == strpart(f,0,strlen(b:rails_root))
+ return strpart(f,strlen(b:rails_root)+1)
+ else
+ return f
+ endif
+endfunction
+
+function! RailsFile()
+ return RailsFilePath()
+endfunction
+
+function! RailsFileType()
+ if !exists("b:rails_root")
+ return ""
+ elseif exists("b:rails_file_type")
+ return b:rails_file_type
+ elseif exists("b:rails_cached_file_type")
+ return b:rails_cached_file_type
+ endif
+ let f = RailsFilePath()
+ let e = fnamemodify(RailsFilePath(),':e')
+ let r = ""
+ let top = getline(1)." ".getline(2)." ".getline(3)." ".getline(4)." ".getline(5).getline(6)." ".getline(7)." ".getline(8)." ".getline(9)." ".getline(10)
+ if f == ""
+ let r = f
+ elseif f =~ '_controller\.rb$' || f =~ '\<app/controllers/application\.rb$'
+ if top =~ '\<wsdl_service_name\>'
+ let r = "controller-api"
+ else
+ let r = "controller"
+ endif
+ elseif f =~ '_api\.rb'
+ let r = "api"
+ elseif f =~ '\<test/test_helper\.rb$'
+ let r = "test"
+ elseif f =~ '\<spec/spec_helper\.rb$'
+ let r = "spec"
+ elseif f =~ '_helper\.rb$'
+ let r = "helper"
+ elseif f =~ '\<app/models\>'
+ let class = matchstr(top,'\<Acti\w\w\u\w\+\%(::\h\w*\)\+\>')
+ if class == "ActiveResoure::Base"
+ let class = "ares"
+ let r = "model-ares"
+ elseif class != ''
+ "let class = s:sub(class,'::Base$','')
+ let class = tolower(s:gsub(class,'[^A-Z]',''))
+ let r = "model-".s:sub(class,'^amb>','mailer')
+ elseif f =~ '_mailer\.rb$'
+ let r = "model-mailer"
+ elseif top =~ '\<\%(validates_\w\+_of\|set_\%(table_name\|primary_key\)\|has_one\|has_many\|belongs_to\)\>'
+ let r = "model-arb"
+ else
+ let r = "model"
+ endif
+ elseif f =~ '\<app/views/layouts\>.*\.'
+ let r = "view-layout-" . e
+ elseif f =~ '\<\%(app/views\|components\)/.*/_\k\+\.\k\+\%(\.\k\+\)\=$'
+ let r = "view-partial-" . e
+ elseif f =~ '\<app/views\>.*\.' || f =~ '\<components/.*/.*\.'.s:viewspattern().'$'
+ let r = "view-" . e
+ elseif f =~ '\<test/unit/.*_test\.rb$'
+ let r = "test-unit"
+ elseif f =~ '\<test/functional/.*_test\.rb$'
+ let r = "test-functional"
+ elseif f =~ '\<test/integration/.*_test\.rb$'
+ let r = "test-integration"
+ elseif f =~ '\<spec/\w*s/.*_spec\.rb$'
+ let r = s:sub(f,'.*<spec/(\w*)s/.*','spec-\1')
+ elseif f =~ '\<\%(test\|spec\)/fixtures\>'
+ if e == "yml"
+ let r = "fixtures-yaml"
+ else
+ let r = "fixtures" . (e == "" ? "" : "-" . e)
+ endif
+ elseif f =~ '\<test/.*_test\.rb'
+ let r = "test"
+ elseif f =~ '\<spec/.*_spec\.rb'
+ let r = "spec"
+ elseif f =~ '\<db/migrate\>' || f=~ '\<db/schema\.rb$'
+ let r = "migration"
+ elseif f =~ '\<vendor/plugins/.*/recipes/.*\.rb$' || f =~ '\.rake$' || f =~ '\<\%(Rake\|Cap\)file$' || f =~ '\<config/deploy\.rb$'
+ let r = "task"
+ elseif f =~ '\<log/.*\.log$'
+ let r = "log"
+ elseif e == "css" || e == "js" || e == "html"
+ let r = e
+ elseif f =~ '\<config/routes\>.*\.rb$'
+ let r = "config-routes"
+ elseif f =~ '\<config/'
+ let r = "config"
+ endif
+ return r
+endfunction
+
+function! RailsType()
+ return RailsFileType()
+endfunction
+
+function! RailsEval(ruby,...)
+ if !exists("b:rails_root")
+ return a:0 ? a:1 : ""
+ elseif a:0
+ return s:railseval(a:ruby,a:1)
+ else
+ return s:railseval(a:ruby)
+ endif
+endfunction
+
+" }}}1
+" Configuration {{{
+
+function! s:SetOptDefault(opt,val)
+ if !exists("g:".a:opt)
+ let g:{a:opt} = a:val
+ endif
+endfunction
+
+function! s:InitConfig()
+ call s:SetOptDefault("rails_level",3)
+ call s:SetOptDefault("rails_statusline",1)
+ call s:SetOptDefault("rails_syntax",1)
+ call s:SetOptDefault("rails_mappings",1)
+ call s:SetOptDefault("rails_abbreviations",1)
+ call s:SetOptDefault("rails_expensive",1+0*(has("win32")||has("win32unix")))
+ call s:SetOptDefault("rails_dbext",g:rails_expensive)
+ call s:SetOptDefault("rails_subversion",0)
+ call s:SetOptDefault("rails_tabstop",0)
+ call s:SetOptDefault("rails_default_file","README")
+ call s:SetOptDefault("rails_default_database","")
+ call s:SetOptDefault("rails_root_url",'http://localhost:3000/')
+ call s:SetOptDefault("rails_modelines",1)
+ call s:SetOptDefault("rails_menu",1)
+ call s:SetOptDefault("rails_gnu_screen",1)
+ call s:SetOptDefault("rails_history_size",5)
+ call s:SetOptDefault("rails_debug",0)
+ call s:SetOptDefault("rails_generators","controller\nintegration_test\nmailer\nmigration\nmodel\nobserver\nplugin\nresource\nscaffold\nsession_migration")
+ call s:SetOptDefault("rails_rake_tasks","db:charset\ndb:collation\ndb:create\ndb:create:all\ndb:drop\ndb:drop:all\ndb:fixtures:identify\ndb:fixtures:load\ndb:migrate\ndb:reset\ndb:rollback\ndb:schema:dump\ndb:schema:load\ndb:sessions:clear\ndb:sessions:create\ndb:structure:dump\ndb:test:clone\ndb:test:clone_structure\ndb:test:prepare\ndb:test:purge\ndb:version\ndoc:app\ndoc:clobber_app\ndoc:clobber_plugins\ndoc:clobber_rails\ndoc:plugins\ndoc:rails\ndoc:reapp\ndoc:rerails\nlog:clear\nnotes\nnotes:fixme\nnotes:optimize\nnotes:todo\nrails:freeze:edge\nrails:freeze:gems\nrails:unfreeze\nrails:update\nrails:update:configs\nrails:update:javascripts\nrails:update:scripts\nroutes\nstats\ntest\ntest:functionals\ntest:integration\ntest:plugins\ntest:recent\ntest:uncommitted\ntest:units\ntmp:cache:clear\ntmp:clear\ntmp:create\ntmp:pids:clear\ntmp:sessions:clear\ntmp:sockets:clear")
+ if g:rails_dbext
+ if exists("g:loaded_dbext") && executable("sqlite3") && ! executable("sqlite")
+ " Since dbext can't find it by itself
+ call s:SetOptDefault("dbext_default_SQLITE_bin","sqlite3")
+ endif
+ endif
+endfunction
+
+" }}}1
+" Autocommand Functions {{{1
+
+function! s:QuickFixCmdPre()
+ if exists("b:rails_root")
+ if strpart(getcwd(),0,strlen(RailsRoot())) != RailsRoot()
+ let s:last_dir = getcwd()
+ echo "lchdir ".s:ra()
+ "exe "lchdir ".s:ra()
+ lchdir `=RailsRoot()`
+ endif
+ endif
+endfunction
+
+function! s:QuickFixCmdPost()
+ if exists("s:last_dir")
+ "exe "lchdir ".s:escarg(s:last_dir)
+ lchdir `=s:last_dir`
+ unlet s:last_dir
+ endif
+endfunction
+
+function! s:BufEnter()
+ if exists("b:rails_refresh") && b:rails_refresh
+ unlet! b:rails_root b:rails_use_subversion
+ let b:rails_refresh = 0
+ call s:Detect(expand("%:p"))
+ unlet! b:rails_refresh
+ elseif !exists("b:rails_root") && isdirectory(expand('%;p'))
+ " FIXME: This doesn't catch all directories
+ call s:Detect(expand('%:p'))
+ endif
+ if exists("b:rails_root")
+ if exists("+completefunc") && &completefunc == 'syntaxcomplete#Complete'
+ if exists("g:loaded_syntax_completion")
+ " Ugly but necessary, until we have our own completion
+ unlet g:loaded_syntax_completion
+ silent! delfunction syntaxcomplete#Complete
+ endif
+ endif
+ call s:BufDatabase(-1)
+ call s:menuBufEnter()
+ endif
+endfunction
+
+function! s:BufLeave()
+ call s:menuBufLeave()
+endfunction
+
+" }}}1
+" Commands {{{1
+
+function! s:BufCommands()
+ call s:BufFinderCommands() " Provides Rcommand!
+ call s:BufNavCommands()
+ call s:BufScriptWrappers()
+ Rcommand! -buffer -bar -nargs=? -bang -complete=custom,s:RakeComplete Rake :call s:Rake(<bang>0,<q-args>)
+ Rcommand! -buffer -bar -nargs=? -bang -complete=custom,s:PreviewComplete Rpreview :call s:Preview(<bang>0,<q-args>)
+ Rcommand! -buffer -bar -nargs=? -bang -complete=custom,s:environments Rlog :call s:Log(<bang>0,<q-args>)
+ Rcommand! -buffer -bar -nargs=* -bang -complete=custom,s:SetComplete Rset :call s:Set(<bang>0,<f-args>)
+ command! -buffer -bar -nargs=0 Rtags :call s:Tags(<bang>0)
+ " Embedding all this logic directly into the command makes the error
+ " messages more concise.
+ command! -buffer -bar -nargs=? -bang Rdoc :
+ \ if <bang>0 || <q-args> =~ "^\\([:'-]\\|g:\\)" | call s:prephelp() |
+ \ if <q-args> =~ '^-\=$' | help rails |
+ \ elseif <q-args> =~ '^g:' | help <args> |
+ \ elseif <q-args> =~ '^-' | help rails<args> |
+ \ else | help rails-<args> | endif |
+ \ else | call s:Doc(<bang>0,<q-args>) | endif
+ command! -buffer -bar -nargs=0 -bang Rrefresh :if <bang>0|unlet! g:loaded_rails|source `=s:file`|endif|call s:Refresh(<bang>0)
+ if exists(":Project")
+ command! -buffer -bar -nargs=? -bang Rproject :call s:Project(<bang>0,<q-args>)
+ endif
+ if exists("g:loaded_dbext")
+ Rcommand! -buffer -bar -nargs=? -bang -complete=custom,s:environments Rdbext :call s:BufDatabase(2,<q-args>,<bang>0)
+ endif
+ let ext = expand("%:e")
+ if ext =~ s:viewspattern()
+ " TODO: complete controller names with trailing slashes here
+ Rcommand! -buffer -bar -nargs=? -range -complete=custom,s:controllerList Rextract :<line1>,<line2>call s:Extract(<bang>0,<f-args>)
+ command! -buffer -bar -nargs=? -range Rpartial :call s:warn("Warning: :Rpartial has been deprecated in favor of :Rextract") | <line1>,<line2>Rextract<bang> <args>
+ endif
+ if RailsFilePath() =~ '\<db/migrate/.*\.rb$'
+ command! -buffer -bar Rinvert :call s:Invert(<bang>0)
+ endif
+endfunction
+
+function! s:Doc(bang, string)
+ if a:string != ""
+ if exists("g:rails_search_url")
+ let query = substitute(a:string,'[^A-Za-z0-9_.~-]','\="%".printf("%02X",char2nr(submatch(0)))','g')
+ let url = printf(g:rails_search_url, query)
+ else
+ return s:error("specify a g:rails_search_url with %s for a query placeholder")
+ endif
+ elseif isdirectory(RailsRoot()."/doc/api/classes")
+ let url = RailsRoot()."/doc/api/index.html"
+ elseif s:getpidfor("0.0.0.0","8808") > 0
+ let url = "http://localhost:8808"
+ else
+ let url = "http://api.rubyonrails.org"
+ endif
+ call s:initOpenURL()
+ if exists(":OpenURL")
+ exe "OpenURL ".s:escarg(url)
+ else
+ return s:error("No :OpenURL command found")
+ endif
+endfunction
+
+function! s:Log(bang,arg)
+ if a:arg == ""
+ let lf = "log/".s:environment().".log"
+ else
+ let lf = "log/".a:arg.".log"
+ endif
+ let size = getfsize(RailsRoot()."/".lf)
+ if size >= 1048576
+ call s:warn("Log file is ".((size+512)/1024)."KB. Consider :Rake log:clear")
+ endif
+ if a:bang
+ exe "cgetfile ".lf
+ clast
+ else
+ if exists(":Tail")
+ " TODO: check if :Tail works with `=`
+ exe "Tail ".s:ra().'/'.lf
+ else
+ "exe "pedit ".s:ra().'/'.lf
+ pedit `=RailsRoot().'/'.lf`
+ endif
+ endif
+endfunction
+
+function! s:NewApp(bang,...)
+ if a:0 == 0
+ if a:bang
+ echo "rails.vim revision ".s:revision
+ else
+ !rails
+ endif
+ return
+ endif
+ let dir = ""
+ if a:1 !~ '^-'
+ let dir = a:1
+ elseif a:{a:0} =~ '[\/]'
+ let dir = a:{a:0}
+ else
+ let dir = a:1
+ endif
+ let str = ""
+ let c = 1
+ while c <= a:0
+ let str = str . " " . s:rquote(expand(a:{c}))
+ let c = c + 1
+ endwhile
+ "let str = s:sub(str,'^ ','')
+ let dir = expand(dir)
+ if isdirectory(fnamemodify(dir,':h')."/.svn") && g:rails_subversion
+ let append = " -c"
+ else
+ let append = ""
+ endif
+ if g:rails_default_database != "" && str !~ '-d \|--database='
+ let append = append." -d ".g:rails_default_database
+ endif
+ if a:bang
+ let append = append." --force"
+ endif
+ exe "!rails".append.str
+ if filereadable(dir."/".g:rails_default_file)
+ "exe "edit ".s:escarg(dir)."/".g:rails_default_file
+ edit `=dir.'/'.g:rails_default_file`
+ endif
+endfunction
+
+function! s:Tags(bang)
+ if exists("g:Tlist_Ctags_Cmd")
+ let cmd = g:Tlist_Ctags_Cmd
+ elseif executable("exuberant-ctags")
+ let cmd = "exuberant-ctags"
+ elseif executable("ctags-exuberant")
+ let cmd = "ctags-exuberant"
+ elseif executable("ctags")
+ let cmd = "ctags"
+ elseif executable("ctags.exe")
+ let cmd = "ctags.exe"
+ else
+ return s:error("ctags not found")
+ endif
+ exe "!".cmd." -R ".s:ra()
+endfunction
+
+function! s:Refresh(bang)
+ " What else?
+ if a:bang
+ unlet! s:rails_helper_methods
+ endif
+ if exists("g:rubycomplete_rails") && g:rubycomplete_rails && has("ruby")
+ silent! ruby ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
+ silent! ruby Dependencies.clear if defined?(Dependencies)
+ if a:bang
+ silent! ruby ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
+ endif
+ endif
+ call s:cacheclear()
+ call s:BufLeave()
+ if a:bang && s:cacheworks()
+ let s:cache = {}
+ endif
+ let i = 1
+ let max = bufnr('$')
+ while i <= max
+ let rr = getbufvar(i,"rails_root")
+ if rr != ""
+ unlet! s:user_classes_{s:escvar(rr)}
+ unlet! s:dbext_type_{s:escvar(rr)}
+ call setbufvar(i,"rails_refresh",1)
+ endif
+ let i = i + 1
+ endwhile
+ call s:BufEnter()
+endfunction
+
+" }}}1
+" Rake {{{1
+
+" Depends: s:efm, s:rubyexestrwithfork, s:sub, s:lastmethodline, s:getopt, s;rquote, s:QuickFixCmdPre, ...
+
+let s:efm_backtrace='%D(in\ %f),'
+ \.'%\\s%#from\ %f:%l:%m,'
+ \.'%\\s#{RAILS_ROOT}/%f:%l:\ %#%m,'
+ \.'%\\s%#[%f:%l:\ %#%m,'
+ \.'%\\s%#%f:%l:\ %#%m'
+
+function! s:makewithruby(arg,...)
+ if &efm == s:efm
+ if a:0 ? a:1 : 1
+ setlocal efm=\%-E-e:%.%#,\%+E%f:%l:\ parse\ error,%W%f:%l:\ warning:\ %m,%E%f:%l:in\ %*[^:]:\ %m,%E%f:%l:\ %m,%-C%\tfrom\ %f:%l:in\ %.%#,%-Z%\tfrom\ %f:%l,%-Z%p^,%-G%.%#
+ endif
+ endif
+ let old_make = &makeprg
+ let &l:makeprg = s:rubyexestrwithfork(a:arg)
+ make
+ let &l:makeprg = old_make
+endfunction
+
+function! s:Rake(bang,arg)
+ let oldefm = &efm
+ if a:bang
+ let &efm = s:efm_backtrace
+ "errorformat=%*[^"]"%f"%*\D%l: %m,"%f"%*\D%l: %m,%-G%f:%l: (Each undeclared identifier is reported only once,%-G%f:%l: for each function it appears in.),%f:%l:%c:%m,%f(%l):%m,%f:%l:%m,"%f"\, line %l%*\D%c%*[^ ] %m,%D%*\a[%*\d]: Entering directory `%f',%X%*\a[%*\d]: Leaving directory `%f',%D%*\a: Entering directory `%
+ endif
+ let t = RailsFileType()
+ let arg = a:arg
+ if &filetype == "ruby" && arg == '' && g:rails_modelines
+ let lnum = s:lastmethodline()
+ let str = getline(lnum)."\n".getline(lnum+1)."\n".getline(lnum+2)."\n"
+ let pat = '\s\+\zs.\{-\}\ze\%(\n\|\s\s\|#{\@!\|$\)'
+ let mat = matchstr(str,'#\s*rake'.pat)
+ let mat = s:sub(mat,'\s+$','')
+ if mat != ""
+ let arg = mat
+ endif
+ endif
+ if arg == ''
+ let opt = s:getopt('task','bl')
+ if opt != ''
+ let arg = opt
+ endif
+ endif
+ let withrubyargs = '-r ./config/boot -r '.s:rquote(RailsRoot().'/config/environment').' -e "puts \%((in \#{Dir.getwd}))" '
+ if arg =~# '^\%(stats\|routes\|notes\|db:\%(charset\|collation\|version\)\)\%(:\|$\)'
+ " So you can see the output even with an inadequate redirect
+ call s:QuickFixCmdPre()
+ exe "!".&makeprg." ".arg
+ call s:QuickFixCmdPost()
+ elseif arg =~ '^preview\>'
+ exe 'R'.s:gsub(arg,':','/')
+ elseif arg =~ '^runner:'
+ let arg = s:sub(arg,'^runner:','')
+ let root = matchstr(arg,'%\%(:\w\)*')
+ let file = expand(root).matchstr(arg,'%\%(:\w\)*\zs.*')
+ if file =~ '[@#].*$'
+ let extra = " -- -n ".matchstr(file,'[@#]\zs.*')
+ let file = s:sub(file,'[@#].*','')
+ else
+ let extra = ''
+ endif
+ if s:hasfile(file) || s:hasfile(file.'.rb')
+ call s:makewithruby(withrubyargs.'-r"'.file.'"'.extra,file !~# '_\%(spec\|test\)\%(\.rb\)\=$')
+ else
+ call s:makewithruby(withrubyargs.'-e '.s:esccmd(s:rquote(arg)))
+ endif
+ elseif arg == 'run' || arg == 'runner'
+ call s:makewithruby(withrubyargs.'-r"'.RailsFilePath().'"',RailsFilePath() !~# '_\%(spec\|test\)\%(\.rb\)\=$')
+ elseif arg =~ '^run:'
+ let arg = s:sub(arg,'^run:','')
+ let arg = s:sub(arg,'^%:h',expand('%:h'))
+ let arg = s:sub(arg,'^%(\%|$|[@#]@=)',expand('%'))
+ let arg = s:sub(arg,'[@#](\w+)$',' -- -n\1')
+ call s:makewithruby(withrubyargs.'-r'.arg,arg !~# '_\%(spec\|test\)\.rb$')
+ elseif arg != ''
+ exe 'make '.arg
+ elseif t =~ '^task\>'
+ let lnum = s:lastmethodline()
+ let line = getline(lnum)
+ " We can't grab the namespace so only run tasks at the start of the line
+ if line =~ '^\%(task\|file\)\>'
+ exe 'make '.s:lastmethod()
+ else
+ make
+ endif
+ elseif t =~ '^spec\>'
+ if RailsFilePath() =~# '\<test/test_helper\.rb$'
+ make spec SPEC_OPTS=
+ else
+ make spec SPEC="%:p" SPEC_OPTS=
+ endif
+ elseif t =~ '^test\>'
+ let meth = s:lastmethod()
+ if meth =~ '^test_'
+ let call = " -n".meth.""
+ else
+ let call = ""
+ endif
+ if t =~ '^test-\%(unit\|functional\|integration\)$'
+ exe "make ".s:sub(s:gsub(t,'-',':'),'unit$|functional$','&s')." TEST=\"%:p\"".s:sub(call,'^ ',' TESTOPTS=')
+ elseif RailsFilePath() =~# '\<test/test_helper\.rb$'
+ make test
+ else
+ call s:makewithruby('-e "puts \%((in \#{Dir.getwd}))" -r"%:p" -- '.call,0)
+ endif
+ elseif t=~ '^\%(db-\)\=migration\>' && RailsFilePath() !~# '\<db/schema\.rb$'
+ let ver = matchstr(RailsFilePath(),'\<db/migrate/0*\zs\d*\ze_')
+ if ver != ""
+ exe "make db:migrate VERSION=".ver
+ else
+ make db:migrate
+ endif
+ elseif t=~ '^model\>'
+ make test:units TEST="%:p:r:s?[\/]app[\/]models[\/]?/test/unit/?_test.rb"
+ elseif t=~ '^api\>'
+ make test:units TEST="%:p:r:s?[\/]app[\/]apis[\/]?/test/functional/?_test.rb"
+ elseif t=~ '^\<\%(controller\|helper\|view\)\>'
+ if RailsFilePath() =~ '\<app/' && s:controller() !~# '^\%(application\)\=$'
+ exe 'make test:functionals TEST="'.s:ra().'/test/functional/'.s:controller().'_controller_test.rb"'
+ else
+ make test:functionals
+ endif
+ else
+ make
+ endif
+ if oldefm != ''
+ let &efm = oldefm
+ endif
+endfunction
+
+function! s:RakeComplete(A,L,P)
+ return g:rails_rake_tasks
+endfunction
+
+" }}}1
+" Preview {{{1
+
+" Depends: s:getopt, s:sub, s:controller, s:lastmethod, s:Detect
+" Provides: s:initOpenURL
+
+function! s:initOpenURL()
+ if !exists(":OpenURL")
+ if has("gui_mac")
+ command -bar -nargs=1 OpenURL :!open <args>
+ elseif has("gui_win32")
+ command -bar -nargs=1 OpenURL :!start cmd /cstart /b <args>
+ elseif executable("sensible-browser")
+ command -bar -nargs=1 OpenURL :!sensible-browser <args>
+ endif
+ endif
+endfunction
+
+" This returns the URI with a trailing newline if it is found
+function! s:scanlineforuri(lnum)
+ let line = getline(a:lnum)
+ let url = matchstr(line,"\\v\\C%(%(GET|PUT|POST|DELETE)\\s+|\w+:/)/[^ \n\r\t<>\"]*[^] .,;\n\r\t<>\":]")
+ if url =~ '\C^\u\+\s\+'
+ let method = matchstr(url,'^\u\+')
+ let url = matchstr(url,'\s\+\zs.*')
+ if method !=? "GET"
+ if url =~ '?'
+ let url = url.'&'
+ else
+ let url = url.'?'
+ endif
+ let url = url.'_method='.tolower(method)
+ endif
+ endif
+ if url != ""
+ return s:sub(url,'^/','') . "\n"
+ else
+ return ""
+ endif
+endfunction
+
+function! s:defaultpreview()
+ let ret = ''
+ if s:getopt('preview','l') != ''
+ let uri = s:getopt('preview','l')
+ elseif s:controller() != '' && s:controller() != 'application' && RailsFilePath() !~ '^public/'
+ if RailsFileType() =~ '^controller\>'
+ let start = s:lastmethodline() - 1
+ if start + 1
+ while getline(start) =~ '^\s*\%(#.*\)\=$'
+ let ret = s:scanlineforuri(start).ret
+ let start = start - 1
+ endwhile
+ let ret = ret.s:controller().'/'.s:lastmethod().'/'
+ else
+ let ret = ret.s:controller().'/'
+ endif
+ elseif s:getopt('preview','b') != ''
+ let ret = s:getopt('preview','b')
+ elseif RailsFileType() =~ '^view\%(-partial\|-layout\)\@!'
+ let ret = ret.s:controller().'/'.expand('%:t:r:r').'/'
+ endif
+ elseif s:getopt('preview','b') != ''
+ let uri = s:getopt('preview','b')
+ elseif RailsFilePath() =~ '^public/'
+ let ret = s:sub(RailsFilePath(),'^public/','')
+ elseif s:getopt('preview','ag') != ''
+ let ret = s:getopt('preview','ag')
+ endif
+ return ret
+endfunction
+
+function! s:Preview(bang,arg)
+ let root = s:getopt("root_url")
+ if root == ''
+ let root = s:getopt("url")
+ endif
+ let root = s:sub(root,'/$','')
+ if a:arg =~ '://'
+ let uri = a:arg
+ elseif a:arg != ''
+ let uri = root.'/'.s:sub(a:arg,'^/','')
+ else
+ let uri = matchstr(s:defaultpreview(),'.\{-\}\%(\n\@=\|$\)')
+ let uri = root.'/'.s:sub(s:sub(uri,'^/',''),'/$','')
+ endif
+ call s:initOpenURL()
+ if exists(':OpenURL') && !a:bang
+ exe 'OpenURL '.uri
+ else
+ " Work around bug where URLs ending in / get handled as FTP
+ let url = uri.(uri =~ '/$' ? '?' : '')
+ silent exe 'pedit '.url
+ wincmd w
+ if &filetype == ''
+ if uri =~ '\.css$'
+ setlocal filetype=css
+ elseif uri =~ '\.js$'
+ setlocal filetype=javascript
+ elseif getline(1) =~ '^\s*<'
+ setlocal filetype=xhtml
+ endif
+ endif
+ call s:Detect(RailsRoot())
+ map <buffer> <silent> q :bwipe<CR>
+ wincmd p
+ if !a:bang
+ call s:warn("Define a :OpenURL command to use a browser")
+ endif
+ endif
+endfunction
+
+function! s:PreviewComplete(A,L,P)
+ return s:defaultpreview()
+endfunction
+
+" }}}1
+" Script Wrappers {{{1
+
+" Depends: s:rquote, s:rubyexebg, s:rubyexe, s:rubyexestrwithfork, s:sub, s:getopt, s:usesubversion, s:user_classes_..., ..., s:pluginList, ...
+
+function! s:BufScriptWrappers()
+ Rcommand! -buffer -bar -nargs=+ -complete=custom,s:ScriptComplete Rscript :call s:Script(<bang>0,<f-args>)
+ Rcommand! -buffer -bar -nargs=* -complete=custom,s:ConsoleComplete Rconsole :call s:Console(<bang>0,'console',<f-args>)
+ "Rcommand! -buffer -bar -nargs=* Rbreakpointer :call s:Console(<bang>0,'breakpointer',<f-args>)
+ Rcommand! -buffer -bar -nargs=* -complete=custom,s:GenerateComplete Rgenerate :call s:Generate(<bang>0,<f-args>)
+ Rcommand! -buffer -bar -nargs=* -complete=custom,s:DestroyComplete Rdestroy :call s:Destroy(<bang>0,<f-args>)
+ Rcommand! -buffer -bar -nargs=? -bang -complete=custom,s:ServerComplete Rserver :call s:Server(<bang>0,<q-args>)
+ Rcommand! -buffer -bang -nargs=1 -range=0 -complete=custom,s:RubyComplete Rrunner :call s:Runner(<bang>0 ? -2 : (<count>==<line2>?<count>:-1),<f-args>)
+ Rcommand! -buffer -nargs=1 -range=0 -complete=custom,s:RubyComplete Rp :call s:Runner(<count>==<line2>?<count>:-1,'p begin '.<f-args>.' end')
+ Rcommand! -buffer -nargs=1 -range=0 -complete=custom,s:RubyComplete Rpp :call s:Runner(<count>==<line2>?<count>:-1,'require %{pp}; pp begin '.<f-args>.' end')
+ Rcommand! -buffer -nargs=1 -range=0 -complete=custom,s:RubyComplete Ry :call s:Runner(<count>==<line2>?<count>:-1,'y begin '.<f-args>.' end')
+endfunction
+
+function! s:Script(bang,cmd,...)
+ let str = ""
+ let c = 1
+ while c <= a:0
+ let str = str . " " . s:rquote(a:{c})
+ let c = c + 1
+ endwhile
+ if a:bang
+ call s:rubyexebg(s:rquote("script/".a:cmd).str)
+ else
+ call s:rubyexe(s:rquote("script/".a:cmd).str)
+ endif
+endfunction
+
+function! s:Runner(count,args)
+ if a:count == -2
+ call s:Script(a:bang,"runner",a:args)
+ else
+ let str = s:rubyexestrwithfork('-r./config/boot -e "require '."'commands/runner'".'" '.s:rquote(a:args))
+ let res = s:sub(system(str),'\n$','')
+ if a:count < 0
+ echo res
+ else
+ exe a:count.'put =res'
+ endif
+ endif
+endfunction
+
+function! s:Console(bang,cmd,...)
+ let str = ""
+ let c = 1
+ while c <= a:0
+ let str = str . " " . s:rquote(a:{c})
+ let c = c + 1
+ endwhile
+ call s:rubyexebg(s:rquote("script/".a:cmd).str)
+endfunction
+
+function! s:getpidfor(bind,port)
+ if has("win32") || has("win64")
+ let netstat = system("netstat -anop tcp")
+ let pid = matchstr(netstat,'\<'.a:bind.':'.a:port.'\>.\{-\}LISTENING\s\+\zs\d\+')
+ elseif executable('lsof')
+ let pid = system("lsof -i 4tcp@".a:bind.':'.a:port."|grep LISTEN|awk '{print $2}'")
+ let pid = s:sub(pid,'\n','')
+ else
+ let pid = ""
+ endif
+ return pid
+endfunction
+
+function! s:Server(bang,arg)
+ let port = matchstr(a:arg,'\%(-p\|--port=\=\)\s*\zs\d\+')
+ if port == ''
+ let port = "3000"
+ endif
+ " TODO: Extract bind argument
+ let bind = "0.0.0.0"
+ if a:bang && executable("ruby")
+ let pid = s:getpidfor(bind,port)
+ if pid =~ '^\d\+$'
+ echo "Killing server with pid ".pid
+ if !has("win32")
+ call system("ruby -e 'Process.kill(:TERM,".pid.")'")
+ sleep 100m
+ endif
+ call system("ruby -e 'Process.kill(9,".pid.")'")
+ sleep 100m
+ endif
+ if a:arg == "-"
+ return
+ endif
+ endif
+ if has("win32") || has("win64") || (exists("$STY") && !has("gui_running") && s:getopt("gnu_screen","abg") && executable("screen"))
+ call s:rubyexebg(s:rquote("script/server")." ".a:arg)
+ else
+ "--daemon would be more descriptive but lighttpd does not support it
+ call s:rubyexe(s:rquote("script/server")." ".a:arg." -d")
+ endif
+ call s:setopt('a:root_url','http://'.(bind=='0.0.0.0'?'localhost': bind).':'.port.'/')
+endfunction
+
+function! s:Destroy(bang,...)
+ if a:0 == 0
+ call s:rubyexe("script/destroy")
+ return
+ elseif a:0 == 1
+ call s:rubyexe("script/destroy ".s:rquote(a:1))
+ return
+ endif
+ let str = ""
+ let c = 1
+ while c <= a:0
+ let str = str . " " . s:rquote(a:{c})
+ let c = c + 1
+ endwhile
+ call s:rubyexe(s:rquote("script/destroy").str.(s:usesubversion()?' -c':''))
+ unlet! s:user_classes_{s:rv()}
+endfunction
+
+function! s:Generate(bang,...)
+ if a:0 == 0
+ call s:rubyexe("script/generate")
+ return
+ elseif a:0 == 1
+ call s:rubyexe("script/generate ".s:rquote(a:1))
+ return
+ endif
+ let target = s:rquote(a:1)
+ let str = ""
+ let c = 2
+ while c <= a:0
+ let str = str . " " . s:rquote(a:{c})
+ let c = c + 1
+ endwhile
+ if str !~ '-p\>' && str !~ '--pretend\>'
+ let execstr = s:rubyexestr('-r./config/boot -e "require '."'commands/generate'".'" -- '.target." -p -f".str)
+ let res = system(execstr)
+ let file = matchstr(res,'\s\+\%(create\|force\)\s\+\zs\f\+\.rb\ze\n')
+ if file == ""
+ let file = matchstr(res,'\s\+\%(exists\)\s\+\zs\f\+\.rb\ze\n')
+ endif
+ "echo file
+ else
+ let file = ""
+ endif
+ if !s:rubyexe("script/generate ".target.(s:usesubversion()?' -c':'').str) && file != ""
+ unlet! s:user_classes_{s:rv()}
+ "exe "edit ".s:ra()."/".file
+ edit `=RailsRoot().'/'.file`
+ endif
+endfunction
+
+function! s:ScriptComplete(ArgLead,CmdLine,P)
+ let cmd = s:sub(a:CmdLine,'^\u\w*\s+','')
+ let P = a:P - strlen(a:CmdLine)+strlen(cmd)
+ if cmd !~ '^[ A-Za-z0-9_=-]*$'
+ " You're on your own, bud
+ return ""
+ elseif cmd =~ '^\w*$'
+ return "about\nconsole\ndestroy\ngenerate\nperformance/benchmarker\nperformance/profiler\nplugin\nproccess/reaper\nprocess/spawner\nrunner\nserver"
+ elseif cmd =~ '^\%(plugin\)\s\+'.a:ArgLead.'$'
+ return "discover\nlist\ninstall\nupdate\nremove\nsource\nunsource\nsources"
+ elseif cmd =~ '\%(plugin\)\s\+\%(install\|remove\)\s\+'.a:ArgLead.'$' || cmd =~ '\%(generate\|destroy\)\s\+plugin\s\+'.a:ArgLead.'$'
+ return s:pluginList(a:ArgLead,a:CmdLine,a:P)
+ elseif cmd =~ '^\%(generate\|destroy\)\s\+'.a:ArgLead.'$'
+ return g:rails_generators
+ elseif cmd =~ '^\%(generate\|destroy\)\s\+\w\+\s\+'.a:ArgLead.'$'
+ let target = matchstr(cmd,'^\w\+\s\+\zs\w\+\ze\s\+')
+ let pattern = "" " TODO
+ if target =~# '^\%(\w*_\)\=controller$'
+ return s:sub(s:controllerList(pattern,"",""),'^application\n=','')
+ elseif target =~# '^\%(\w*_\)\=model$' || target =~# '^scaffold\%(_resource\)\=$' || target == 'mailer'
+ return s:modelList(pattern,"","")
+ elseif target == 'migration' || target == 'session_migration'
+ return s:migrationList(pattern,"","")
+ elseif target == 'integration_test'
+ return s:integrationtestList(pattern,"","")
+ elseif target == 'observer'
+ " script/generate observer is in Edge Rails
+ let observers = s:observerList(pattern,"","")
+ let models = s:modelList(pattern,"","")
+ if cmd =~ '^destroy\>'
+ let models = ""
+ endif
+ while strlen(models) > 0
+ let tmp = matchstr(models."\n",'.\{-\}\ze\n')
+ let models = s:sub(models,'.{-}%(\n|$)','')
+ if stridx("\n".observers."\n","\n".tmp."\n") == -1 && tmp !~'_observer$'
+ let observers = observers."\n".tmp
+ endif
+ endwhile
+ return s:sub(observers,'^\n','')
+ elseif target == 'web_service'
+ return s:apiList(pattern,"","")
+ else
+ return ""
+ endif
+ elseif cmd =~ '^\%(generate\|destroy\)\s\+scaffold\s\+\w\+\s\+'.a:ArgLead.'$'
+ return s:sub(s:controllerList("","",""),'^application\n=','')
+ elseif cmd =~ '^\%(console\)\s\+\(--\=\w\+\s\+\)\='.a:ArgLead."$"
+ return s:environments()."\n-s\n--sandbox"
+ elseif cmd =~ '^\%(server\)\s\+.*-e\s\+'.a:ArgLead."$"
+ return s:environments()
+ elseif cmd =~ '^\%(server\)\s\+'
+ return "-p\n-b\n-e\n-m\n-d\n-u\n-c\n-h\n--port=\n--binding=\n--environment=\n--mime-types=\n--daemon\n--debugger\n--charset=\n--help\n"
+ endif
+ return ""
+" return s:relglob(RailsRoot()."/script/",a:ArgLead."*")
+endfunction
+
+function! s:CustomComplete(A,L,P,cmd)
+ let L = "Rscript ".a:cmd." ".s:sub(a:L,'^\h\w*\s+','')
+ let P = a:P - strlen(a:L) + strlen(L)
+ return s:ScriptComplete(a:A,L,P)
+endfunction
+
+function! s:ServerComplete(A,L,P)
+ return s:CustomComplete(a:A,a:L,a:P,"server")
+endfunction
+
+function! s:ConsoleComplete(A,L,P)
+ return s:CustomComplete(a:A,a:L,a:P,"console")
+endfunction
+
+function! s:GenerateComplete(A,L,P)
+ return s:CustomComplete(a:A,a:L,a:P,"generate")
+endfunction
+
+function! s:DestroyComplete(A,L,P)
+ return s:CustomComplete(a:A,a:L,a:P,"destroy")
+endfunction
+
+function! s:RubyComplete(A,L,R)
+ return s:gsub(RailsUserClasses(),' ','\n')."\nActiveRecord::Base"
+endfunction
+
+" }}}1
+" Navigation {{{1
+
+function! s:BufNavCommands()
+ " TODO: completion
+ "silent exe "command! -bar -buffer -nargs=? Rcd :cd ".s:ra()."/<args>"
+ "silent exe "command! -bar -buffer -nargs=? Rlcd :lcd ".s:ra()."/<args>"
+ command! -buffer -bar -nargs=? Rcd :cd `=RailsRoot().'/'.<q-args>`
+ command! -buffer -bar -nargs=? Rlcd :lcd `=RailsRoot().'/'.<q-args>`
+ " Vim 6.2 chokes on script local completion functions (e.g., s:FindList).
+ " :Rcommand! is a thin wrapper arround :command! which works around this
+ Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList Rfind :call s:Find(<bang>0,<count>,'' ,<f-args>)
+ Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList REfind :call s:Find(<bang>0,<count>,'E',<f-args>)
+ Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList RSfind :call s:Find(<bang>0,<count>,'S',<f-args>)
+ Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList RVfind :call s:Find(<bang>0,<count>,'V',<f-args>)
+ Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList RTfind :call s:Find(<bang>0,<count>,'T',<f-args>)
+ Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList Rsfind :<count>RSfind<bang> <args>
+ Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList Rtabfind :<count>RTfind<bang> <args>
+ Rcommand! -buffer -bar -nargs=* -bang -complete=custom,s:EditList Redit :call s:Edit(<bang>0,<count>,'' ,<f-args>)
+ Rcommand! -buffer -bar -nargs=* -bang -complete=custom,s:EditList REedit :call s:Edit(<bang>0,<count>,'E',<f-args>)
+ Rcommand! -buffer -bar -nargs=* -bang -complete=custom,s:EditList RSedit :call s:Edit(<bang>0,<count>,'S',<f-args>)
+ Rcommand! -buffer -bar -nargs=* -bang -complete=custom,s:EditList RVedit :call s:Edit(<bang>0,<count>,'V',<f-args>)
+ Rcommand! -buffer -bar -nargs=* -bang -complete=custom,s:EditList RTedit :call s:Edit(<bang>0,<count>,'T',<f-args>)
+ command! -buffer -bar -nargs=0 A :call s:Alternate(<bang>0,"")
+ command! -buffer -bar -nargs=0 AE :call s:Alternate(<bang>0,"E")
+ command! -buffer -bar -nargs=0 AS :call s:Alternate(<bang>0,"S")
+ command! -buffer -bar -nargs=0 AV :call s:Alternate(<bang>0,"V")
+ command! -buffer -bar -nargs=0 AT :call s:Alternate(<bang>0,"T")
+ command! -buffer -bar -nargs=0 AN :call s:Related(<bang>0,"")
+ command! -buffer -bar -nargs=0 R :call s:Related(<bang>0,"")
+ command! -buffer -bar -nargs=0 RE :call s:Related(<bang>0,"E")
+ command! -buffer -bar -nargs=0 RS :call s:Related(<bang>0,"S")
+ command! -buffer -bar -nargs=0 RV :call s:Related(<bang>0,"V")
+ command! -buffer -bar -nargs=0 RT :call s:Related(<bang>0,"T")
+ "command! -buffer -bar -nargs=0 RN :call s:Alternate(<bang>0,"")
+endfunction
+
+function! s:djump(def)
+ let def = s:sub(a:def,'^[@#]','')
+ if def != ''
+ let ext = matchstr(def,'\.\zs.*')
+ let def = matchstr(def,'[^.]*')
+ let v:errmsg = ''
+ silent! exe "djump ".def
+ if ext != '' && (v:errmsg == '' || v:errmsg =~ '^E387')
+ let rpat = '\C^\s*respond_to\s*\%(\<do\|{\)\s*|\zs\h\k*\ze|'
+ let end = s:endof(line('.'))
+ let rline = search(rpat,'',end)
+ if rline > 0
+ "call cursor(rline,1)
+ let variable = matchstr(getline(rline),rpat)
+ let success = search('\C^\s*'.variable.'\s*\.\s*\zs'.ext.'\>','',end)
+ if !success
+ silent! exe "djump ".def
+ endif
+ endif
+ endif
+ endif
+endfunction
+
+function! s:Find(bang,count,arg,...)
+ let cmd = a:arg . (a:bang ? '!' : '')
+ let str = ""
+ if a:0
+ let i = 1
+ while i < a:0
+ let str = str . s:escarg(a:{i}) . " "
+ let i = i + 1
+ endwhile
+ let file = a:{i}
+ let tail = matchstr(file,'[@#].*$')
+ if tail != ""
+ let file = s:sub(file,'[@#].*$','')
+ endif
+ if file != ""
+ let file = s:RailsIncludefind(file)
+ endif
+ else
+ let file = s:RailsFind()
+ let tail = ""
+ endif
+ if file =~ '^\%(app\|config\|db\|public\|spec\|test\|vendor\)/.*\.' || !a:0 || 1
+ call s:findedit((a:count==1?'' : a:count).cmd,file.tail,str)
+ else
+ " Old way
+ let fcmd = (a:count==1?'' : a:count).s:findcmdfor(cmd)
+ let fcmd = s:sub(fcmd,'(\d+)vert ','vert \1')
+ if file != ""
+ exe fcmd.' '.str.s:escarg(file)
+ endif
+ call s:djump(tail)
+ endif
+endfunction
+
+function! s:Edit(bang,count,arg,...)
+ let cmd = a:arg . (a:bang ? '!' : '')
+ if a:0
+ let str = ""
+ let i = 1
+ while i < a:0
+ "let str = str . s:escarg(a:{i}) . " "
+ let str = str . "`=a:".i."` "
+ let i = i + 1
+ endwhile
+ let file = a:{i}
+ call s:findedit(s:editcmdfor(cmd),file,str)
+ else
+ exe s:editcmdfor(cmd)
+ endif
+endfunction
+
+function! s:FindList(ArgLead, CmdLine, CursorPos)
+ if exists("*UserFileComplete") " genutils.vim
+ return UserFileComplete(s:RailsIncludefind(a:ArgLead), a:CmdLine, a:CursorPos, 1, &path)
+ else
+ return ""
+ endif
+endfunction
+
+function! s:EditList(ArgLead, CmdLine, CursorPos)
+ return s:relglob("",a:ArgLead."*[^~]")
+endfunction
+
+function! RailsIncludeexpr()
+ " Is this foolproof?
+ if mode() =~ '[iR]' || expand("<cfile>") != v:fname
+ return s:RailsIncludefind(v:fname)
+ else
+ return s:RailsIncludefind(v:fname,1)
+ endif
+endfunction
+
+function! s:linepeak()
+ let line = getline(line("."))
+ let line = s:sub(line,'^(.{'.col(".").'}).*','\1')
+ let line = s:sub(line,'([:"'."'".']|\%[qQ]=[[({<])=\f*$','')
+ return line
+endfunction
+
+function! s:matchcursor(pat)
+ let line = getline(".")
+ let lastend = 0
+ while lastend >= 0
+ let beg = match(line,'\C'.a:pat,lastend)
+ let end = matchend(line,'\C'.a:pat,lastend)
+ if beg < col(".") && end >= col(".")
+ return matchstr(line,'\C'.a:pat,lastend)
+ endif
+ let lastend = end
+ endwhile
+ return ""
+endfunction
+
+function! s:findit(pat,repl)
+ let res = s:matchcursor(a:pat)
+ if res != ""
+ return substitute(res,'\C'.a:pat,a:repl,'')
+ else
+ return ""
+ endif
+endfunction
+
+function! s:findamethod(func,repl)
+ return s:findit('\s*\<\%('.a:func.'\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>.\=',a:repl)
+endfunction
+
+function! s:findasymbol(sym,repl)
+ return s:findit('\s*:\%('.a:sym.'\)\s*=>\s*(\=\s*[@:'."'".'"]\(\f\+\)\>.\=',a:repl)
+endfunction
+
+function! s:findfromview(func,repl)
+ return s:findit('\s*\%(<%\)\==\=\s*\<\%('.a:func.'\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>['."'".'"]\=\s*\%(%>\s*\)\=',a:repl)
+endfunction
+
+function! s:RailsFind()
+ " UGH
+ let format = s:format('html')
+ let res = s:findit('\v\s*<require\s*\(=\s*File.dirname\(__FILE__\)\s*\+\s*[:'."'".'"](\f+)>.=',expand('%:h').'/\1')
+ if res != ""|return res.(fnamemodify(res,':e') == '' ? '.rb' : '')|endif
+ let res = s:findit('\v<File.dirname\(__FILE__\)\s*\+\s*[:'."'".'"](\f+)>['."'".'"]=',expand('%:h').'\1')
+ if res != ""|return res|endif
+ let res = s:findamethod('require','\1')
+ if res != ""|return res.(fnamemodify(res,':e') == '' ? '.rb' : '')|endif
+ let res = s:findamethod('belongs_to\|has_one\|composed_of\|validates_associated\|scaffold','app/models/\1.rb')
+ if res != ""|return res|endif
+ let res = s:singularize(s:findamethod('has_many\|has_and_belongs_to_many','app/models/\1'))
+ if res != ""|return res.".rb"|endif
+ let res = s:singularize(s:findamethod('create_table\|drop_table\|add_column\|rename_column\|remove_column\|add_index','app/models/\1'))
+ if res != ""|return res.".rb"|endif
+ let res = s:singularize(s:findasymbol('through','app/models/\1'))
+ if res != ""|return res.".rb"|endif
+ let res = s:findamethod('fixtures','fixtures/\1')
+ if res != ""
+ return RailsFilePath() =~ '\<spec/' ? 'spec/'.res : res
+ endif
+ let res = s:findamethod('map\.resources','app/controllers/\1_controller.rb')
+ if res != ""|return res|endif
+ let res = s:findamethod('layout','app/views/layouts/\1')
+ if res != ""|return res|endif
+ let res = s:findasymbol('layout','app/views/layouts/\1')
+ if res != ""|return res|endif
+ let res = s:findamethod('helper','app/helpers/\1_helper.rb')
+ if res != ""|return res|endif
+ let res = s:findasymbol('controller','app/controllers/\1_controller.rb')
+ if res != ""|return res|endif
+ let res = s:findasymbol('action','\1')
+ if res != ""|return res|endif
+ let res = s:sub(s:sub(s:findasymbol('partial','\1'),'^/',''),'\k+$','_&')
+ if res != ""|return res."\n".s:findview(res)|endif
+ let res = s:sub(s:sub(s:findfromview('render\s*(\=\s*:partial\s\+=>\s*','\1'),'^/',''),'\k+$','_&')
+ if res != ""|return res."\n".s:findview(res)|endif
+ let res = s:findamethod('render\s*:\%(template\|action\)\s\+=>\s*','\1.'.format.'\n\1')
+ if res != ""|return res|endif
+ let res = s:findamethod('redirect_to\s*(\=\s*:action\s\+=>\s*','\1')
+ if res != ""|return res|endif
+ let res = s:findfromview('stylesheet_link_tag','public/stylesheets/\1.css')
+ if res != ""|return res|endif
+ let res = s:sub(s:findfromview('javascript_include_tag','public/javascripts/\1.js'),'/defaults>','/application')
+ if res != ""|return res|endif
+ if RailsFileType() =~ '^controller\>'
+ let res = s:findit('\s*\<def\s\+\(\k\+\)\>(\=',s:sub(s:sub(RailsFilePath(),'/controllers/','/views/'),'_controller\.rb$','').'/\1')
+ if res != ""|return res|endif
+ endif
+ let isf_keep = &isfname
+ set isfname=@,48-57,/,-,_,: ",\",'
+ " TODO: grab visual selection in visual mode
+ let cfile = expand("<cfile>")
+ let res = s:RailsIncludefind(cfile,1)
+ let &isfname = isf_keep
+ return res
+endfunction
+
+function! s:initnamedroutes()
+ if s:cacheneeds("named_routes")
+ let exec = "ActionController::Routing::Routes.named_routes.each {|n,r| puts %{#{n} app/controllers/#{r.requirements[:controller]}_controller.rb##{r.requirements[:action]}}}"
+ let string = s:railseval(exec)
+ let routes = {}
+ let list = split(string,"\n")
+ let i = 0
+ " If we use for, Vim 6.2 dumbly treats endfor like endfunction
+ while i < len(list)
+ let route = split(list[i]," ")
+ let name = route[0]
+ let routes[name] = route[1]
+ let i = i + 1
+ endwhile
+ call s:cacheset("named_routes",routes)
+ endif
+endfunction
+
+function! s:namedroutefile(route)
+ call s:initnamedroutes()
+ if s:cachehas("named_routes") && has_key(s:cache("named_routes"),a:route)
+ return s:cache("named_routes")[a:route]
+ endif
+ return ""
+endfunction
+
+function! RailsNamedRoutes()
+ call s:initnamedroutes()
+ if s:cachehas("named_routes")
+ return keys(s:cache("named_routes"))
+ else
+ " Dead code
+ if s:cacheneeds("route_names")
+ let lines = readfile(RailsRoot()."/config/routes.rb")
+ let plurals = map(filter(copy(lines),'v:val =~# "^ map\\.resources\\s\\+:\\w"'),'matchstr(v:val,"^ map\\.resources\\=\\s\\+:\\zs\\w\\+")')
+ let singulars = map(copy(plurals),'s:singularize(v:val)')
+ let extras = map(copy(singulars),'"new_".v:val')+map(copy(singulars),'"edit_".v:val')
+ let all = plurals + singulars + extras
+ let named = map(filter(copy(lines),'v:val =~# "^ map\\.\\%(connect\\>\\|resources\\=\\>\\)\\@!\\w\\+"'),'matchstr(v:val,"^ map\\.\\zs\\w\\+")')
+ call s:cacheset("route_names",named+all+map(copy(all),'"formatted_".v:val'))
+ endif
+ if s:cachehas("route_names")
+ return s:cache("route_names")
+ endif
+ endif
+endfunction
+
+function! s:RailsIncludefind(str,...)
+ if a:str == "ApplicationController"
+ return "app/controllers/application.rb"
+ elseif a:str == "Test::Unit::TestCase"
+ return "test/unit/testcase.rb"
+ elseif a:str == "<%="
+ " Probably a silly idea
+ return "action_view.rb"
+ endif
+ let str = a:str
+ if a:0 == 1
+ " Get the text before the filename under the cursor.
+ " We'll cheat and peak at this in a bit
+ let line = s:linepeak()
+ let line = s:sub(line,'([:"'."'".']|\%[qQ]=[[({<])=\f*$','')
+ else
+ let line = ""
+ endif
+ let str = s:sub(str,'^\s*','')
+ let str = s:sub(str,'\s*$','')
+ let str = s:sub(str,'^[:@]','')
+ "let str = s:sub(str,"\\([\"']\\)\\(.*\\)\\1",'\2')
+ let str = s:sub(str,':0x\x+$','') " For #<Object:0x...> style output
+ let str = s:gsub(str,"[\"']",'')
+ if line =~ '\<\(require\|load\)\s*(\s*$'
+ return str
+ endif
+ let str = s:underscore(str)
+ let fpat = '\(\s*\%("\f*"\|:\f*\|'."'\\f*'".'\)\s*,\s*\)*'
+ if a:str =~ '\u'
+ " Classes should always be in .rb files
+ let str = str . '.rb'
+ elseif line =~ ':partial\s*=>\s*'
+ let str = s:sub(str,'([^/]+)$','_\1')
+ let str = s:findview(str)
+ elseif line =~ '\<layout\s*(\=\s*' || line =~ ':layout\s*=>\s*'
+ let str = s:findview(s:sub(str,'^/=','layouts/'))
+ elseif line =~ ':controller\s*=>\s*'
+ let str = 'app/controllers/'.str.'_controller.rb'
+ elseif line =~ '\<helper\s*(\=\s*'
+ let str = 'app/helpers/'.str.'_helper.rb'
+ elseif line =~ '\<fixtures\s*(\='.fpat
+ if RailsFilePath() =~# '\<spec/'
+ let str = s:sub(str,'^/@!','spec/fixtures/')
+ else
+ let str = s:sub(str,'^/@!','test/fixtures/')
+ endif
+ elseif line =~ '\<stylesheet_\(link_tag\|path\)\s*(\='.fpat
+ let str = s:sub(str,'^/@!','/stylesheets/')
+ let str = 'public'.s:sub(str,'^[^.]*$','&.css')
+ elseif line =~ '\<javascript_\(include_tag\|path\)\s*(\='.fpat
+ if str == "defaults"
+ let str = "application"
+ endif
+ let str = s:sub(str,'^/@!','/javascripts/')
+ let str = 'public'.s:sub(str,'^[^.]*$','&.js')
+ elseif line =~ '\<\(has_one\|belongs_to\)\s*(\=\s*'
+ let str = 'app/models/'.str.'.rb'
+ elseif line =~ '\<has_\(and_belongs_to_\)\=many\s*(\=\s*'
+ let str = 'app/models/'.s:singularize(str).'.rb'
+ elseif line =~ '\<def\s\+' && expand("%:t") =~ '_controller\.rb'
+ let str = s:sub(s:sub(RailsFilePath(),'/controllers/','/views/'),'_controller\.rb$','/'.str)
+ "let str = s:sub(expand("%:p"),'.*[\/]app[\/]controllers[\/](.{-})_controller.rb','views/\1').'/'.str
+ " FIXME: support nested extensions
+ let vt = s:view_types.","
+ while vt != ""
+ let t = matchstr(vt,'[^,]*')
+ let vt = s:sub(vt,'[^,]*,','')
+ if filereadable(str.".".t)
+ let str = str.".".t
+ break
+ endif
+ endwhile
+ elseif str =~ '_\%(path\|url\)$'
+ " REST helpers
+ let str = s:sub(str,'_%(path|url)$','')
+ let str = s:sub(str,'^hash_for_','')
+ let file = s:namedroutefile(str)
+ if file == ""
+ let str = s:sub(str,'^formatted_','')
+ if str =~ '^\%(new\|edit\)_'
+ let str = 'app/controllers/'.s:sub(s:pluralize(str),'^(new|edit)_(.*)','\2_controller.rb#\1')
+ elseif str == s:singularize(str)
+ " If the word can't be singularized, it's probably a link to the show
+ " method. We should verify by checking for an argument, but that's
+ " difficult the way things here are currently structured.
+ let str = 'app/controllers/'.s:pluralize(str).'_controller.rb#show'
+ else
+ let str = 'app/controllers/'.str.'_controller.rb#index'
+ endif
+ else
+ let str = file
+ endif
+ elseif str !~ '/'
+ " If we made it this far, we'll risk making it singular.
+ let str = s:singularize(str)
+ let str = s:sub(str,'_id$','')
+ endif
+ if str =~ '^/' && !filereadable(str)
+ let str = s:sub(str,'^/','')
+ endif
+ if str =~ '^lib/' && !filereadable(str)
+ let str = s:sub(str,'^lib/','')
+ endif
+ return str
+endfunction
+
+" }}}1
+" File Finders {{{1
+
+function! s:addfilecmds(type)
+ let l = s:sub(a:type,'^.','\l&')
+ let cmds = 'ESVT '
+ let cmd = ''
+ while cmds != ''
+ let cplt = " -complete=custom,".s:sid.l."List"
+ exe "command! -buffer -bar -nargs=*".cplt." R".cmd.l." :call s:".l.'Edit(<bang>0,"'.cmd.'",<f-args>)'
+ let cmd = strpart(cmds,0,1)
+ let cmds = strpart(cmds,1)
+ endwhile
+endfunction
+
+function! s:BufFinderCommands()
+ command! -buffer -bar -bang -nargs=+ Rcommand :call s:Command(<bang>0,<f-args>)
+ call s:addfilecmds("model")
+ call s:addfilecmds("view")
+ call s:addfilecmds("controller")
+ call s:addfilecmds("migration")
+ call s:addfilecmds("observer")
+ call s:addfilecmds("helper")
+ call s:addfilecmds("api")
+ call s:addfilecmds("layout")
+ call s:addfilecmds("fixtures")
+ call s:addfilecmds("unittest")
+ call s:addfilecmds("functionaltest")
+ call s:addfilecmds("integrationtest")
+ call s:addfilecmds("stylesheet")
+ call s:addfilecmds("javascript")
+ call s:addfilecmds("task")
+ call s:addfilecmds("lib")
+ call s:addfilecmds("plugin")
+endfunction
+
+function! s:autocamelize(files,test)
+ if a:test =~# '^\u'
+ return s:camelize(a:files)
+ else
+ return a:files
+ endif
+endfunction
+
+function! RailsUserClasses()
+ if !exists("b:rails_root")
+ return ""
+ elseif s:getopt('classes','ab') != ''
+ return s:getopt('classes','ab')
+ endif
+ let var = "user_classes_".s:rv()
+ if !exists("s:".var)
+ let s:{var} = s:sub(s:sub(s:gsub(s:camelize(
+ \ s:relglob("app/models/","**/*",".rb") . "\n" .
+ \ s:sub(s:relglob("app/controllers/","**/*",".rb"),'<application>','&_controller') . "\n" .
+ \ s:relglob("app/helpers/","**/*",".rb") . "\n" .
+ \ s:relglob("lib/","**/*",".rb") . "\n" .
+ \ ""),'\n+',' '),'^\s+',''),'\s+$','')
+ endif
+ return s:{var}
+endfunction
+
+function! s:relglob(path,glob,...)
+ " How could such a simple operation be so complicated?
+ if exists("+shellslash") && ! &shellslash
+ let old_ss = &shellslash
+ let &shellslash = 1
+ endif
+ if a:path =~ '[\/]$'
+ let path = a:path
+ else
+ let path = a:path . ''
+ endif
+ if path !~ '^/' && path !~ '^\w:' && RailsRoot() != ''
+ let path = RailsRoot() . '/' . path
+ endif
+ let suffix = a:0 ? a:1 : ''
+ let badres = glob(path.a:glob.suffix)."\n"
+ if v:version <= 602
+ " Nasty Vim bug in version 6.2
+ let badres = glob(path.a:glob.suffix)."\n"
+ endif
+ let goodres = ""
+ let striplen = strlen(path)
+ let stripend = strlen(suffix)
+ while strlen(badres) > 0
+ let idx = stridx(badres,"\n")
+ "if idx == -1
+ "let idx = strlen(badres)
+ "endif
+ let tmp = strpart(badres,0,idx)
+ let badres = strpart(badres,idx+1)
+ let goodres = goodres.strpart(tmp,striplen,strlen(tmp)-striplen-stripend)
+ if suffix == '' && isdirectory(tmp) && goodres !~ '/$'
+ let goodres = goodres."/"
+ endif
+ let goodres = goodres."\n"
+ endwhile
+ "let goodres = s:gsub("\n".goodres,'\n.{-}\~\n','\n')
+ if exists("old_ss")
+ let &shellslash = old_ss
+ endif
+ return s:compact(goodres)
+endfunction
+
+if v:version <= 602
+ " Yet another Vim 6.2 limitation
+ let s:recurse = "*"
+else
+ let s:recurse = "**/*"
+endif
+
+function! s:helperList(A,L,P)
+ return s:autocamelize(s:relglob("app/helpers/",s:recurse,"_helper.rb"),a:A)
+endfunction
+
+function! s:controllerList(A,L,P)
+ let con = s:gsub(s:relglob("app/controllers/",s:recurse,".rb"),'_controller>','')
+ return s:autocamelize(con,a:A)
+endfunction
+
+function! s:viewList(A,L,P)
+ let c = s:controller(1)
+ let top = s:relglob("app/views/",a:A."*[^~]")
+ if c != ''
+ let local = s:relglob("app/views/".c."/",a:A."*.*[^~]")
+ if local != ''
+ return local."\n".top
+ endif
+ endif
+ return top
+endfunction
+
+function! s:layoutList(A,L,P)
+ return s:relglob("app/views/layouts/","*")
+endfunction
+
+function! s:stylesheetList(A,L,P)
+ return s:relglob("public/stylesheets/",s:recurse,".css")
+endfunction
+
+function! s:javascriptList(A,L,P)
+ return s:relglob("public/javascripts/",s:recurse,".js")
+endfunction
+
+function! s:modelList(A,L,P)
+ let models = s:relglob("app/models/",s:recurse,".rb")."\n"
+ " . matches everything, and no good way to exclude newline. Lame.
+ let models = s:gsub(models,'[ -~]*_observer\n',"")
+ let models = s:compact(models)
+ return s:autocamelize(models,a:A)
+endfunction
+
+function! s:observerList(A,L,P)
+ return s:autocamelize(s:relglob("app/models/",s:recurse,"_observer.rb"),a:A)
+endfunction
+
+function! s:fixturesList(A,L,P)
+ return s:compact(s:relglob("test/fixtures/",s:recurse)."\n".s:relglob("spec/fixtures/",s:recurse))
+endfunction
+
+function! s:migrationList(A,L,P)
+ return s:autocamelize(s:relglob("db/migrate/???_",a:A."*",".rb"),a:A)
+endfunction
+
+function! s:apiList(A,L,P)
+ return s:autocamelize(s:relglob("app/apis/",s:recurse,"_api.rb"),a:A)
+endfunction
+
+function! s:unittestList(A,L,P)
+ return s:autocamelize(s:relglob("test/unit/",s:recurse,"_test.rb"),a:A)
+endfunction
+
+function! s:functionaltestList(A,L,P)
+ return s:autocamelize(s:relglob("test/functional/",s:recurse,"_test.rb"),a:A)
+endfunction
+
+function! s:integrationtestList(A,L,P)
+ return s:autocamelize(s:relglob("test/integration/",s:recurse,"_test.rb"),a:A)
+endfunction
+
+function! s:pluginList(A,L,P)
+ if a:A =~ '/'
+ return s:relglob('vendor/plugins/',matchstr(a:A,'.\{-\}/').'**/*')
+ else
+ return s:relglob('vendor/plugins/',"*","/init.rb")
+ endif
+endfunction
+
+" Task files, not actual rake tasks
+function! s:taskList(A,L,P)
+ let top = s:relglob("lib/tasks/",s:recurse,".rake")
+ if RailsFilePath() =~ '\<vendor/plugins/.'
+ let path = s:sub(RailsFilePath(),'<vendor/plugins/[^/]*/\zs.*','tasks/')
+ return s:relglob(path,s:recurse,".rake") . "\n" . top
+ else
+ return top
+ endif
+endfunction
+
+function! s:libList(A,L,P)
+ let all = s:relglob('lib/',s:recurse,".rb")
+ if RailsFilePath() =~ '\<vendor/plugins/.'
+ let path = s:sub(RailsFilePath(),'<vendor/plugins/[^/]*/\zs.*','lib/')
+ let all = s:relglob(path,s:recurse,".rb") . "\n" . all
+ endif
+ return s:autocamelize(all,a:A)
+endfunction
+
+function! s:Command(bang,...)
+ if a:bang
+ let str = ""
+ let i = 0
+ while i < a:0
+ let i = i + 1
+ if a:{i} =~# '^-complete=custom,s:' && v:version <= 602
+ let str = str . " " . s:sub(a:{i},',s:',','.s:sid)
+ else
+ let str = str . " " . a:{i}
+ endif
+ endwhile
+ exe "command!".str
+ return
+ endif
+ let suffix = ".rb"
+ let filter = "**/*"
+ let prefix = ""
+ let default = ""
+ let name = ""
+ let i = 0
+ while i < a:0
+ let i = i + 1
+ let arg = a:{i}
+ if arg =~# '^-suffix='
+ let suffix = matchstr(arg,'-suffix=\zs.*')
+ elseif arg =~# '^-default='
+ let default = matchstr(arg,'-default=\zs.*')
+ elseif arg =~# '^-\%(glob\|filter\)='
+ let filter = matchstr(arg,'-\w*=\zs.*')
+ elseif arg !~# '^-'
+ " A literal '\n'. For evaluation below
+ if name == ""
+ let name = arg
+ else
+ let prefix = prefix."\\n".s:sub(arg,'/=$','/')
+ endif
+ endif
+ endwhile
+ let prefix = s:sub(prefix,'^\\n','')
+ if name !~ '^[A-Za-z]\+$'
+ return s:error("E182: Invalid command name")
+ endif
+ let cmds = 'ESVT '
+ let cmd = ''
+ while cmds != ''
+ exe 'command! -buffer -bar -bang -nargs=* -complete=custom,'.s:sid.'CommandList R'.cmd.name." :call s:CommandEdit(<bang>0,'".cmd."','".name."',\"".prefix."\",".s:string(suffix).",".s:string(filter).",".s:string(default).",<f-args>)"
+ let cmd = strpart(cmds,0,1)
+ let cmds = strpart(cmds,1)
+ endwhile
+endfunction
+
+function! s:CommandList(A,L,P)
+ let cmd = matchstr(a:L,'\CR[A-Z]\=\w\+')
+ exe cmd." &"
+ let lp = s:last_prefix . "\n"
+ let res = ""
+ while lp != ""
+ let p = matchstr(lp,'.\{-\}\ze\n')
+ let lp = s:sub(lp,'.{-}\n','')
+ let res = res . s:relglob(p,s:last_filter,s:last_suffix)."\n"
+ endwhile
+ let res = s:compact(res)
+ if s:last_camelize
+ return s:autocamelize(res,a:A)
+ else
+ return res
+ endif
+endfunction
+
+function! s:CommandEdit(bang,cmd,name,prefix,suffix,filter,default,...)
+ if a:0 && a:1 == "&"
+ let s:last_prefix = a:prefix
+ let s:last_suffix = a:suffix
+ let s:last_filter = a:filter
+ let s:last_camelize = (a:suffix =~# '\.rb$')
+ else
+ if a:default == "both()"
+ if s:model() != ""
+ let default = s:model()
+ else
+ let default = s:controller()
+ endif
+ elseif a:default == "model()"
+ let default = s:model(1)
+ elseif a:default == "controller()"
+ let default = s:controller(1)
+ else
+ let default = a:default
+ endif
+ call s:EditSimpleRb(a:bang,a:cmd,a:name,a:0 ? a:1 : default,a:prefix,a:suffix)
+ endif
+endfunction
+
+function! s:EditSimpleRb(bang,cmd,name,target,prefix,suffix)
+ let cmd = s:findcmdfor(a:cmd.(a:bang?'!':''))
+ if a:target == ""
+ " Good idea to emulate error numbers like this?
+ return s:error("E471: Argument required") " : R',a:name)
+ "else
+ "let g:target = a:target
+ endif
+ let f = s:underscore(a:target)
+ let jump = matchstr(f,'[@#].*')
+ let f = s:sub(f,'[@#].*','')
+ if f == '.'
+ let f = s:sub(f,'\.$','')
+ else
+ let f = f.a:suffix.jump
+ if a:suffix !~ '\.'
+ "let f = f.".rb"
+ endif
+ endif
+ let f = s:gsub(a:prefix,'\n',f.'\n').f
+ return s:findedit(cmd,f)
+endfunction
+
+function! s:migrationfor(file)
+ let tryagain = 0
+ let arg = a:file
+ if arg =~ '^\d$'
+ let glob = '00'.arg.'_*.rb'
+ elseif arg =~ '^\d\d$'
+ let glob = '0'.arg.'_*.rb'
+ elseif arg =~ '^\d\d\d$'
+ let glob = ''.arg.'_*.rb'
+ elseif arg == ''
+ if s:model(1) != ''
+ let glob = '*_'.s:pluralize(s:model(1)).'.rb'
+ let tryagain = 1
+ else
+ let glob = '*.rb'
+ endif
+ else
+ let glob = '*'.arg.'*rb'
+ endif
+ let migr = s:sub(glob(RailsRoot().'/db/migrate/'.glob),'.*\n','')
+ if migr == '' && tryagain
+ let migr = s:sub(glob(RailsRoot().'/db/migrate/*.rb'),'.*\n','')
+ endif
+ if strpart(migr,0,strlen(RailsRoot())) == RailsRoot()
+ let migr = strpart(migr,1+strlen(RailsRoot()))
+ endif
+ return migr
+endfunction
+
+function! s:migrationEdit(bang,cmd,...)
+ let cmd = s:findcmdfor(a:cmd.(a:bang?'!':''))
+ let arg = a:0 ? a:1 : ''
+ let migr = arg == "." ? "db/migrate" : s:migrationfor(arg)
+ if migr != ''
+ call s:findedit(cmd,migr)
+ else
+ return s:error("Migration not found".(arg=='' ? '' : ': '.arg))
+ endif
+endfunction
+
+function! s:fixturesEdit(bang,cmd,...)
+ if a:0
+ let c = s:underscore(a:1)
+ else
+ let c = s:pluralize(s:model(1))
+ endif
+ if c == ""
+ return s:error("E471: Argument required")
+ endif
+ let e = fnamemodify(c,':e')
+ let e = e == '' ? e : '.'.e
+ let c = fnamemodify(c,':r')
+ let file = 'test/fixtures/'.c.e
+ if file =~ '\.\w\+$' && !s:hasfile("spec/fixtures/".c.e)
+ call s:edit(a:cmd.(a:bang?'!':''),file)
+ else
+ call s:findedit(a:cmd.(a:bang?'!':''),file."\nspec/fixtures/".c.e)
+ endif
+endfunction
+
+function! s:modelEdit(bang,cmd,...)
+ call s:EditSimpleRb(a:bang,a:cmd,"model",a:0? a:1 : s:model(1),"app/models/",".rb")
+endfunction
+
+function! s:observerEdit(bang,cmd,...)
+ call s:EditSimpleRb(a:bang,a:cmd,"observer",a:0? a:1 : s:model(1),"app/models/","_observer.rb")
+endfunction
+
+function! s:viewEdit(bang,cmd,...)
+ if a:0
+ let view = a:1
+ elseif RailsFileType() == 'controller'
+ let view = s:lastmethod()
+ else
+ let view = ''
+ endif
+ if view == ''
+ return s:error("No view name given")
+ elseif view == '.'
+ return s:edit(a:cmd.(a:bang?'!':''),'app/views')
+ elseif view !~ '/' && s:controller(1) != ''
+ let view = s:controller(1) . '/' . view
+ endif
+ if view !~ '/'
+ return s:error("Cannot find view without controller")
+ endif
+ let file = "app/views/".view
+ let found = s:findview(view)
+ if found != ''
+ call s:edit(a:cmd.(a:bang?'!':''),found)
+ elseif file =~ '\.\w\+\.\w\+$' || file =~ '\.'.s:viewspattern().'$'
+ call s:edit(a:cmd.(a:bang?'!':''),file)
+ elseif file =~ '\.\w\+$'
+ call s:findedit(a:cmd.(a:bang?'!':''),file)
+ else
+ let format = s:format('html')
+ if glob(RailsRoot().'/'.file.'.'.format.'.*[^~]') != ''
+ let file = file . '.' . format
+ endif
+ call s:findedit(a:cmd.(a:bang?'!':''),file)
+ endif
+endfunction
+
+function! s:findview(name)
+ " TODO: full support of nested extensions
+ let c = a:name
+ let pre = "app/views/"
+ let file = ""
+ if c !~ '/'
+ let controller = s:controller(1)
+ if controller != ''
+ let c = controller.'/'.c
+ endif
+ endif
+ if c =~ '\.\w\+\.\w\+$' || c =~ '\.'.s:viewspattern().'$'
+ return pre.c
+ elseif s:hasfile(pre.c.".rhtml")
+ let file = pre.c.".rhtml"
+ elseif s:hasfile(pre.c.".rxml")
+ let file = pre.c.".rxml"
+ else
+ let format = "." . s:format('html')
+ let vt = s:view_types.","
+ while 1
+ while vt != ""
+ let t = matchstr(vt,'[^,]*')
+ let vt = s:sub(vt,'[^,]*,','')
+ if s:hasfile(pre.c.format.".".t)
+ let file = pre.c.format.".".t
+ break
+ endif
+ endwhile
+ if format == '' || file != ''
+ break
+ else
+ let format = ''
+ endif
+ endwhile
+ endif
+ return file
+endfunction
+
+function! s:findlayout(name)
+ return s:findview("layouts/".a:name)
+endfunction
+
+function! s:layoutEdit(bang,cmd,...)
+ if a:0
+ let c = s:underscore(a:1)
+ else
+ let c = s:controller(1)
+ endif
+ if c == ""
+ let c = "application"
+ endif
+ let file = s:findlayout(c)
+ if file == ""
+ let file = s:findlayout("application")
+ endif
+ if file == ""
+ let file = "app/views/layouts/application.rhtml"
+ endif
+ call s:edit(a:cmd.(a:bang?'!':''),s:sub(file,'^/',''))
+endfunction
+
+function! s:controllerEdit(bang,cmd,...)
+ let suffix = '.rb'
+ if a:0 == 0
+ let controller = s:controller(1)
+ if RailsFileType() =~ '^view\%(-layout\|-partial\)\@!'
+ let suffix = suffix.'#'.expand('%:t:r')
+ endif
+ else
+ let controller = a:1
+ endif
+ if s:hasfile("app/controllers/".controller."_controller.rb") || !s:hasfile("app/controllers/".controller.".rb")
+ let suffix = "_controller".suffix
+ endif
+ return s:EditSimpleRb(a:bang,a:cmd,"controller",controller,"app/controllers/",suffix)
+endfunction
+
+function! s:helperEdit(bang,cmd,...)
+ return s:EditSimpleRb(a:bang,a:cmd,"helper",a:0? a:1 : s:controller(1),"app/helpers/","_helper.rb")
+endfunction
+
+function! s:apiEdit(bang,cmd,...)
+ return s:EditSimpleRb(a:bang,a:cmd,"api",a:0 ? a:1 : s:controller(1),"app/apis/","_api.rb")
+endfunction
+
+function! s:stylesheetEdit(bang,cmd,...)
+ return s:EditSimpleRb(a:bang,a:cmd,"stylesheet",a:0? a:1 : s:controller(1),"public/stylesheets/",".css")
+endfunction
+
+function! s:javascriptEdit(bang,cmd,...)
+ return s:EditSimpleRb(a:bang,a:cmd,"javascript",a:0? a:1 : "application","public/javascripts/",".js")
+endfunction
+
+function! s:unittestEdit(bang,cmd,...)
+ let f = a:0 ? a:1 : s:model(1)
+ if !a:0 && RailsFileType() =~ '^model-aro\>' && f != '' && f !~ '_observer$'
+ if s:hasfile("test/unit/".f."_observer.rb") || !s:hasfile("test/unit/".f.".rb")
+ let f = f . "_observer"
+ endif
+ endif
+ return s:EditSimpleRb(a:bang,a:cmd,"unittest",f,"test/unit/","_test.rb")
+endfunction
+
+function! s:functionaltestEdit(bang,cmd,...)
+ if a:0
+ let f = a:1
+ else
+ let f = s:controller()
+ endif
+ if f != '' && !s:hasfile("test/functional/".f."_test.rb")
+ if s:hasfile("test/functional/".f."_controller_test.rb")
+ let f = f . "_controller"
+ elseif s:hasfile("test/functional/".f."_api_test.rb")
+ let f = f . "_api"
+ endif
+ endif
+ return s:EditSimpleRb(a:bang,a:cmd,"functionaltest",f,"test/functional/","_test.rb")
+endfunction
+
+function! s:integrationtestEdit(bang,cmd,...)
+ if a:0
+ let f = a:1
+ elseif s:model() != ''
+ let f = s:model()
+ else
+ let f = s:controller()
+ endif
+ return s:EditSimpleRb(a:bang,a:cmd,"integrationtest",f,"test/integration/","_test.rb")
+endfunction
+
+function! s:pluginEdit(bang,cmd,...)
+ let cmd = s:findcmdfor(a:cmd.(a:bang?'!':''))
+ let plugin = ""
+ let extra = ""
+ if RailsFilePath() =~ '\<vendor/plugins/.'
+ let plugin = matchstr(RailsFilePath(),'\<vendor/plugins/\zs[^/]*\ze')
+ let extra = "vendor/plugins/" . plugin . "/\n"
+ endif
+ if a:0
+ if a:1 =~ '^[^/.]*/\=$' && s:hasfile("vendor/plugins/".a:1."/init.rb")
+ return s:EditSimpleRb(a:bang,a:cmd,"plugin",s:sub(a:1,'/$',''),"vendor/plugins/","/init.rb")
+ elseif plugin == ""
+ call s:edit(cmd,"vendor/plugins/".s:sub(a:1,'\.$',''))
+ elseif a:1 == "."
+ call s:findedit(cmd,"vendor/plugins/".plugin)
+ elseif isdirectory(RailsRoot()."/vendor/plugins/".matchstr(a:1,'^[^/]*'))
+ call s:edit(cmd,"vendor/plugins/".a:1)
+ else
+ call s:findedit(cmd,"vendor/plugins/".a:1."\nvendor/plugins/".plugin."/".a:1)
+ endif
+ else
+ return s:EditSimpleRb(a:bang,a:cmd,"plugin",plugin,"vendor/plugins/","/init.rb")
+ endif
+endfunction
+
+function! s:taskEdit(bang,cmd,...)
+ let plugin = ""
+ let extra = ""
+ if RailsFilePath() =~ '\<vendor/plugins/.'
+ let plugin = matchstr(RailsFilePath(),'\<vendor/plugins/[^/]*')
+ let extra = plugin."/tasks/\n"
+ endif
+ if a:0
+ call s:EditSimpleRb(a:bang,a:cmd,"task",a:1,extra."lib/tasks/",".rake")
+ else
+ call s:findedit((a:bang ? "!" : ""),(plugin != "" ? plugin."/Rakefile\n" : "")."Rakefile")
+ endif
+endfunction
+
+function! s:libEdit(bang,cmd,...)
+ let extra = ""
+ if RailsFilePath() =~ '\<vendor/plugins/.'
+ let extra = s:sub(RailsFilePath(),'<vendor/plugins/[^/]*/\zs.*','lib/')."\n"
+ endif
+ if a:0
+ call s:EditSimpleRb(a:bang,a:cmd,"task",a:0? a:1 : "",extra."lib/",".rb")
+ else
+ " Easter egg
+ call s:EditSimpleRb(a:bang,a:cmd,"task","environment","config/",".rb")
+ endif
+endfunction
+
+" }}}1
+" Alternate/Related {{{1
+
+function! s:findcmdfor(cmd)
+ let bang = ''
+ if a:cmd =~ '\!$'
+ let bang = '!'
+ let cmd = s:sub(a:cmd,'\!$','')
+ else
+ let cmd = a:cmd
+ endif
+ if cmd =~ '^\d'
+ let num = matchstr(cmd,'^\d\+')
+ let cmd = s:sub(cmd,'^\d+','')
+ else
+ let num = ''
+ endif
+ if cmd == '' || cmd == 'E' || cmd == 'F'
+ return num.'find'.bang
+ elseif cmd == 'S'
+ return num.'sfind'.bang
+ elseif cmd == 'V'
+ return 'vert '.num.'sfind'.bang
+ elseif cmd == 'T'
+ return num.'tabfind'.bang
+ else
+ return num.cmd.bang
+ endif
+endfunction
+
+function! s:editcmdfor(cmd)
+ let cmd = s:findcmdfor(a:cmd)
+ let cmd = s:sub(cmd,'<sfind>','split')
+ let cmd = s:sub(cmd,'find>','edit')
+ return cmd
+endfunction
+
+function! s:try(cmd) abort
+ if !exists(":try")
+ " I've seen at least one weird setup without :try
+ exe a:cmd
+ else
+ try
+ exe a:cmd
+ catch
+ call s:error(s:sub(v:exception,'^.{-}:\zeE',''))
+ return 0
+ endtry
+ endif
+ return 1
+endfunction
+
+function! s:findedit(cmd,file,...) abort
+ let cmd = s:findcmdfor(a:cmd)
+ if a:file =~ '\n'
+ let filelist = a:file . "\n"
+ let file = ''
+ while file == '' && filelist != ''
+ let maybe = matchstr(filelist,'^.\{-\}\ze\n')
+ let filelist = s:sub(filelist,'^.{-}\n','')
+ if s:hasfile(s:sub(maybe,'[@#].*',''))
+ let file = maybe
+ endif
+ endwhile
+ if file == ''
+ let file = matchstr(a:file."\n",'^.\{-\}\ze\n')
+ endif
+ else
+ let file = a:file
+ endif
+ if file =~ '[@#]'
+ let djump = matchstr(file,'[@#]\zs.*')
+ let file = matchstr(file,'.\{-\}\ze[@#]')
+ else
+ let djump = ''
+ endif
+ if file == ''
+ let testcmd = "edit"
+ elseif RailsRoot() =~ '://' || cmd =~ 'edit' || cmd =~ 'split'
+ if file !~ '^/' && file !~ '^\w:' && file !~ '://'
+ let file = s:ra().'/'.file
+ endif
+ let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').file
+ elseif isdirectory(RailsRoot().'/'.file)
+ let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').s:ra().'/'.file
+ exe testcmd
+ return
+ else
+ let testcmd = cmd.' '.(a:0 ? a:1 . ' ' : '').file
+ endif
+ if s:try(testcmd)
+ " Shorten the file name (I don't fully understand how Vim decides when to
+ " use a relative/absolute path for the file name, so lets blindly force it
+ " to be as short as possible)
+ "silent! file %:~:.
+ "silent! lcd .
+ call s:djump(djump)
+ endif
+endfunction
+
+function! s:edit(cmd,file,...)
+ let cmd = s:editcmdfor(a:cmd)
+ let cmd = cmd.' '.(a:0 ? a:1 . ' ' : '')
+ let file = a:file
+ if file !~ '^/' && file !~ '^\w:' && file !~ '://'
+ "let file = s:ra().'/'.file
+ exe cmd."`=RailsRoot().'/'.file`"
+ else
+ exe cmd.file
+ endif
+ "exe cmd.file
+endfunction
+
+function! s:Alternate(bang,cmd)
+ let cmd = a:cmd.(a:bang?"!":"")
+ let file = s:AlternateFile()
+ if file != ""
+ call s:findedit(cmd,file)
+ else
+ call s:warn("No alternate file is defined")
+ endif
+endfunction
+
+function! s:AlternateFile()
+ let f = RailsFilePath()
+ let t = RailsFileType()
+ let altopt = s:getopt("alternate","bl")
+ if altopt != ""
+ return altopt
+ elseif f =~ '\<config/environments/'
+ return "config/environment.rb"
+ elseif f == 'README'
+ return "config/database.yml"
+ elseif f =~ '\<config/database\.yml$' | return "config/routes.rb"
+ elseif f =~ '\<config/routes\.rb$' | return "config/environment.rb"
+ elseif f =~ '\<config/environment\.rb$' | return "config/database.yml"
+ elseif f =~ '\<db/migrate/\d\d\d_'
+ let num = matchstr(f,'\<db/migrate/0*\zs\d\+\ze_')-1
+ return num ? s:migrationfor(num) : "db/schema.rb"
+ elseif f =~ '\<application\.js$'
+ return "app/helpers/application_helper.rb"
+ elseif t =~ '^js\>'
+ return "public/javascripts/application.js"
+ elseif f =~ '\<db/schema\.rb$'
+ return s:migrationfor("")
+ elseif t =~ '^view\>'
+ if t =~ '\<layout\>'
+ let dest = fnamemodify(f,':r:s?/layouts\>??').'/layout.'.fnamemodify(f,':e')
+ else
+ let dest = f
+ endif
+ " Go to the (r)spec, helper, controller, or (mailer) model
+ let spec = fnamemodify(dest,':r:s?\<app/?spec/?')."_view_spec.rb"
+ let helper = fnamemodify(dest,':h:s?/views/?/helpers/?')."_helper.rb"
+ let controller = fnamemodify(dest,':h:s?/views/?/controllers/?')."_controller.rb"
+ let model = fnamemodify(dest,':h:s?/views/?/models/?').".rb"
+ if s:hasfile(spec)
+ return spec
+ elseif s:hasfile(helper)
+ return helper
+ elseif s:hasfile(controller)
+ let jumpto = expand("%:t:r")
+ return controller.'#'.jumpto
+ elseif s:hasfile(model)
+ return model
+ else
+ return helper
+ endif
+ elseif t =~ '^controller-api\>'
+ let api = s:sub(s:sub(f,'/controllers/','/apis/'),'_controller\.rb$','_api.rb')
+ return api
+ elseif t =~ '^helper\>'
+ let controller = s:sub(s:sub(f,'/helpers/','/controllers/'),'_helper\.rb$','_controller.rb')
+ let controller = s:sub(controller,'application_controller','application')
+ let spec = s:sub(s:sub(f,'<app/','spec/'),'\.rb$','_spec.rb')
+ if s:hasfile(spec)
+ return spec
+ else
+ return controller
+ endif
+ elseif t =~ '\<fixtures\>' && f =~ '\<spec/'
+ let file = s:singularize(expand("%:t:r")).'_spec.rb'
+ return file
+ elseif t =~ '\<fixtures\>'
+ let file = s:singularize(expand("%:t:r")).'_test.rb' " .expand('%:e')
+ return file
+ elseif f == ''
+ call s:warn("No filename present")
+ elseif f =~ '\<test/unit/routing_test\.rb$'
+ return 'config/routes.rb'
+ elseif t=~ '^spec-view\>'
+ return s:sub(s:sub(f,'<spec/','app/'),'_view_spec\.rb$','')
+ elseif fnamemodify(f,":e") == "rb"
+ let file = fnamemodify(f,":r")
+ if file =~ '_\%(test\|spec\)$'
+ let file = s:sub(file,'_%(test|spec)$','.rb')
+ else
+ let file = file.'_test.rb'
+ endif
+ if t =~ '^model\>'
+ return s:sub(file,'app/models/','test/unit/')."\n".s:sub(s:sub(file,'_test\.rb$','_spec.rb'),'app/models/','spec/models/')
+ elseif t =~ '^controller\>'
+ "return s:sub(file,'app/controllers/','test/functional/')
+ return s:sub(file,'<app/controllers/','test/functional/')."\n".s:sub(s:sub(file,'_test\.rb$','_spec.rb'),'app/controllers/','spec/controllers/')
+ elseif t =~ '^test-unit\>'
+ return s:sub(file,'test/unit/','app/models/')
+ elseif t =~ '^test-functional\>'
+ if file =~ '_api\.rb'
+ return s:sub(file,'test/functional/','app/apis/')
+ elseif file =~ '_controller\.rb'
+ return s:sub(file,'test/functional/','app/controllers/')
+ else
+ return s:sub(file,'test/functional/','')
+ endif
+ elseif t =~ '^spec\>'
+ return s:sub(file,'<spec/','app/')
+ elseif file =~ '\<vendor/.*/lib/'
+ return s:sub(file,'<vendor/.{-}/\zslib/','test/')
+ elseif file =~ '\<vendor/.*/test/'
+ return s:sub(file,'<vendor/.{-}/\zstest/','lib/')
+ else
+ return fnamemodify(file,":t")
+ endif
+ else
+ return ""
+ endif
+endfunction
+
+function! s:Related(bang,cmd)
+ let cmd = a:cmd.(a:bang?"!":"")
+ let file = s:RelatedFile()
+ if file != ""
+ call s:findedit(cmd,file)
+ else
+ call s:warn("No related file is defined")
+ endif
+endfunction
+
+function! s:RelatedFile()
+ let f = RailsFilePath()
+ let t = RailsFileType()
+ let lastmethod = s:lastmethod()
+ if s:getopt("related","l") != ""
+ return s:getopt("related","l")
+ elseif t =~ '^\%(controller\|model-mailer\)\>' && lastmethod != ""
+ let root = s:sub(s:sub(s:sub(f,'/application\.rb$','/shared_controller.rb'),'/%(controllers|models)/','/views/'),'%(_controller)=\.rb$','/'.lastmethod)
+ let format = s:format('html')
+ if glob(RailsRoot().'/'.root.'.'.format.'.*[^~]') != ''
+ return root . '.' . format
+ else
+ return root
+ endif
+ elseif s:getopt("related","b") != ""
+ return s:getopt("related","b")
+ elseif f =~ '\<config/environments/'
+ return "config/database.yml#". expand("%:t:r")
+ elseif f == 'README'
+ return "config/database.yml"
+ elseif f =~ '\<config/database\.yml$'
+ let lm = s:lastmethod()
+ if lm != ""
+ return "config/environments/".lm.".rb\nconfig/environment.rb"
+ else
+ return "config/environment.rb"
+ endif
+ elseif f =~ '\<config/routes\.rb$' | return "config/database.yml"
+ elseif f =~ '\<config/environment\.rb$' | return "config/routes.rb"
+ elseif f =~ '\<db/migrate/\d\d\d_'
+ let num = matchstr(f,'\<db/migrate/0*\zs\d\+\ze_')+1
+ let migr = s:migrationfor(num)
+ return migr == '' ? "db/schema.rb" : migr
+ elseif t =~ '^test\>' && f =~ '\<test/\w\+/'
+ let target = s:sub(f,'.*<test/\w+/','test/mocks/test/')
+ let target = s:sub(target,'_test\.rb$','.rb')
+ return target
+ elseif f =~ '\<application\.js$'
+ return "app/helpers/application_helper.rb"
+ elseif t =~ '^js\>'
+ return "public/javascripts/application.js"
+ elseif t =~ '^view-layout\>'
+ return s:sub(s:sub(s:sub(f,'/views/','/controllers/'),'/layouts/(\k+)\..*$','/\1_controller.rb'),'<application_controller\.rb$','application.rb')
+ "elseif t=~ '^view-partial\>'
+ "call s:warn("No related file is defined")
+ elseif t =~ '^view\>'
+ let controller = s:sub(s:sub(f,'/views/','/controllers/'),'/(\k+%(\.\k+)=)\..*$','_controller.rb#\1')
+ let model = s:sub(s:sub(f,'/views/','/models/'),'/(\k+)\..*$','.rb#\1')
+ if filereadable(s:sub(controller,'#.{-}$',''))
+ return controller
+ elseif filereadable(s:sub(model,'#.{-}$','')) || model =~ '_mailer\.rb#'
+ return model
+ else
+ return controller
+ endif
+ elseif t =~ '^controller-api\>'
+ return s:sub(s:sub(f,'/controllers/','/apis/'),'_controller\.rb$','_api.rb')
+ elseif t =~ '^controller\>'
+ return s:sub(s:sub(f,'/controllers/','/helpers/'),'%(_controller)=\.rb$','_helper.rb')
+ elseif t=~ '^helper\>'
+ return s:sub(s:sub(f,'/helpers/','/views/layouts/'),'%(_helper)=\.rb$','')
+ elseif t =~ '^model-arb\>'
+ "call s:migrationEdit(0,cmd,'create_'.s:pluralize(expand('%:t:r')))
+ return s:migrationfor('create_'.s:pluralize(expand('%:t:r')))
+ elseif t =~ '^model-aro\>'
+ return s:sub(f,'_observer\.rb$','.rb')
+ elseif t =~ '^api\>'
+ return s:sub(s:sub(f,'/apis/','/controllers/'),'_api\.rb$','_controller.rb')
+ elseif f =~ '\<db/schema\.rb$'
+ return s:migrationfor(1)
+ else
+ "call s:warn("No related file is defined")
+ return ""
+ endif
+endfunction
+
+" }}}1
+" Partial Extraction {{{1
+
+" Depends: s:error, s:sub, s:viewspattern, s:Detect, s:warn
+
+function! s:Extract(bang,...) range abort
+ if a:0 == 0 || a:0 > 1
+ return s:error("Incorrect number of arguments")
+ endif
+ if a:1 =~ '[^a-z0-9_/.]'
+ return s:error("Invalid partial name")
+ endif
+ let ext = expand("%:e")
+ let file = a:1
+ let first = a:firstline
+ let last = a:lastline
+ let range = first.",".last
+ if RailsFileType() =~ '^view-layout\>'
+ if RailsFilePath() =~ '\<app/views/layouts/application\>'
+ let curdir = 'app/views/shared'
+ if file !~ '/'
+ let file = "shared/" .file
+ endif
+ else
+ let curdir = s:sub(RailsFilePath(),'.*<app/views/layouts/(.*)%(\.\w*)$','app/views/\1')
+ endif
+ else
+ let curdir = fnamemodify(RailsFilePath(),':h')
+ endif
+ let curdir = RailsRoot()."/".curdir
+ let dir = fnamemodify(file,":h")
+ let fname = fnamemodify(file,":t")
+ if fnamemodify(fname,":e") == ""
+ let name = fname
+ let fname = fname.".".matchstr(expand("%:t"),'\.\zs.*')
+ elseif fnamemodify(fname,":e") !~ '^'.s:viewspattern().'$'
+ let name = fnamemodify(fname,":r")
+ let fname = fname.".".ext
+ else
+ let name = fnamemodify(fname,":r:r")
+ endif
+ let var = "@".name
+ let collection = ""
+ if dir =~ '^/'
+ let out = (RailsRoot()).dir."/_".fname
+ elseif dir == ""
+ let out = (curdir)."/_".fname
+ elseif isdirectory(curdir."/".dir)
+ let out = (curdir)."/".dir."/_".fname
+ else
+ let out = (RailsRoot())."/app/views/".dir."/_".fname
+ endif
+ if filereadable(out)
+ let partial_warn = 1
+ "echoerr "Partial exists"
+ "return
+ endif
+ if bufnr(out) > 0
+ if bufloaded(out)
+ return s:error("Partial already open in buffer ".bufnr(out))
+ else
+ exe "bwipeout ".bufnr(out)
+ endif
+ endif
+ " No tabs, they'll just complicate things
+ if ext =~? '^\%(rhtml\|erb\|dryml\)$'
+ let erub1 = '\<\%\s*'
+ let erub2 = '\s*-=\%\>'
+ else
+ let erub1 = ''
+ let erub2 = ''
+ endif
+ let spaces = matchstr(getline(first),"^ *")
+ if getline(last+1) =~ '\v^\s*'.erub1.'end'.erub2.'\s*$'
+ let fspaces = matchstr(getline(last+1),"^ *")
+ if getline(first-1) =~ '\v^'.fspaces.erub1.'for\s+(\k+)\s+in\s+([^ %>]+)'.erub2.'\s*$'
+ let collection = s:sub(getline(first-1),'^'.fspaces.erub1.'for\s+(\k+)\s+in\s+([^ >]+)'.erub2.'\s*$','\1>\2')
+ elseif getline(first-1) =~ '\v^'.fspaces.erub1.'([^ %>]+)\.each\s+do\s+\|\s*(\k+)\s*\|'.erub2.'\s*$'
+ let collection = s:sub(getline(first-1),'^'.fspaces.erub1.'([^ %>]+)\.each\s+do\s+\|\s*(\k+)\s*\|'.erub2.'\s*$','\2>\1')
+ endif
+ if collection != ''
+ let var = matchstr(collection,'^\k\+')
+ let collection = s:sub(collection,'^\k+\>','')
+ let first = first - 1
+ let last = last + 1
+ endif
+ else
+ let fspaces = spaces
+ endif
+ "silent exe range."write ".out
+ let renderstr = "render :partial => '".fnamemodify(file,":r:r")."'"
+ if collection != ""
+ let renderstr = renderstr.", :collection => ".collection
+ elseif "@".name != var
+ let renderstr = renderstr.", :object => ".var
+ endif
+ if ext =~? '^\%(rhtml\|erb\|dryml\)$'
+ let renderstr = "<%= ".renderstr." %>"
+ elseif ext == "rxml" || ext == "builder"
+ let renderstr = "xml << ".s:sub(renderstr,"render ","render(").")"
+ elseif ext == "rjs"
+ let renderstr = "page << ".s:sub(renderstr,"render ","render(").")"
+ elseif ext == "haml"
+ let renderstr = "= ".renderstr
+ endif
+ let buf = @@
+ silent exe range."yank"
+ let partial = @@
+ let @@ = buf
+ let ai = &ai
+ let &ai = 0
+ silent exe "norm! :".first.",".last."change\<CR>".fspaces.renderstr."\<CR>.\<CR>"
+ let &ai = ai
+ if renderstr =~ '<%'
+ norm ^6w
+ else
+ norm ^5w
+ endif
+ let ft = &ft
+ if &hidden
+ enew
+ else
+ new
+ endif
+ let shortout = fnamemodify(out,':~:.')
+ "exe "silent file ".s:escarg(shortout)
+ silent file `=shortout`
+ let &ft = ft
+ let @@ = partial
+ silent put
+ 0delete
+ let @@ = buf
+ if spaces != ""
+ silent! exe '%substitute/^'.spaces.'//'
+ endif
+ silent! exe '%substitute?\%(\w\|[@:"'."'".'-]\)\@<!'.var.'\>?'.name.'?g'
+ 1
+ call s:Detect(out)
+ if exists("l:partial_warn")
+ call s:warn("Warning: partial exists!")
+ endif
+endfunction
+
+" }}}1
+" Migration Inversion {{{1
+
+" Depends: s:sub, s:endof, s:gsub, s:error
+
+function! s:mkeep(str)
+ " Things to keep (like comments) from a migration statement
+ return matchstr(a:str,' #[^{].*')
+endfunction
+
+function! s:mextargs(str,num)
+ if a:str =~ '^\s*\w\+\s*('
+ return s:sub(matchstr(a:str,'^\s*\w\+\s*\zs(\%([^,)]\+[,)]\)\{,'.a:num.'\}'),',$',')')
+ else
+ return s:sub(s:sub(matchstr(a:str,'\w\+\>\zs\s*\%([^,){ ]*[, ]*\)\{,'.a:num.'\}'),'[, ]*$',''),'^\s+',' ')
+ endif
+endfunction
+
+function! s:migspc(line)
+ return matchstr(a:line,'^\s*')
+endfunction
+
+function! s:invertrange(beg,end)
+ let str = ""
+ let lnum = a:beg
+ while lnum <= a:end
+ let line = getline(lnum)
+ let add = ""
+ if line == ''
+ let add = ' '
+ elseif line =~ '^\s*\(#[^{].*\)\=$'
+ let add = line
+ elseif line =~ '\<create_table\>'
+ let add = s:migspc(line)."drop_table".s:mextargs(line,1).s:mkeep(line)
+ let lnum = s:endof(lnum)
+ elseif line =~ '\<drop_table\>'
+ let add = s:sub(line,'<drop_table>\s*\(=\s*([^,){ ]*).*','create_table \1 do |t|'."\n".matchstr(line,'^\s*').'end').s:mkeep(line)
+ elseif line =~ '\<add_column\>'
+ let add = s:migspc(line).'remove_column'.s:mextargs(line,2).s:mkeep(line)
+ elseif line =~ '\<remove_column\>'
+ let add = s:sub(line,'<remove_column>','add_column')
+ elseif line =~ '\<add_index\>'
+ let add = s:migspc(line).'remove_index'.s:mextargs(line,1)
+ let mat = matchstr(line,':name\s*=>\s*\zs[^ ,)]*')
+ if mat != ''
+ let add = s:sub(add,'\)=$',', :name => '.mat.'&')
+ else
+ let mat = matchstr(line,'\<add_index\>[^,]*,\s*\zs\%(\[[^]]*\]\|[:"'."'".']\w*["'."'".']\=\)')
+ if mat != ''
+ let add = s:sub(add,'\)=$',', :column => '.mat.'&')
+ endif
+ endif
+ let add = add.s:mkeep(line)
+ elseif line =~ '\<remove_index\>'
+ let add = s:sub(s:sub(line,'<remove_index','add_index'),':column\s*=>\s*','')
+ elseif line =~ '\<rename_\%(table\|column\)\>'
+ let add = s:sub(line,'<rename_%(table\s*\(=\s*|column\s*\(=\s*[^,]*,\s*)\zs([^,]*)(,\s*)([^,]*)','\3\2\1')
+ elseif line =~ '\<change_column\>'
+ let add = s:migspc(line).'change_column'.s:mextargs(line,2).s:mkeep(line)
+ elseif line =~ '\<change_column_default\>'
+ let add = s:migspc(line).'change_column_default'.s:mextargs(line,2).s:mkeep(line)
+ elseif line =~ '\.update_all(\(["'."'".']\).*\1)$' || line =~ '\.update_all \(["'."'".']\).*\1$'
+ " .update_all('a = b') => .update_all('b = a')
+ let pre = matchstr(line,'^.*\.update_all[( ][}'."'".'"]')
+ let post = matchstr(line,'["'."'".'])\=$')
+ let mat = strpart(line,strlen(pre),strlen(line)-strlen(pre)-strlen(post))
+ let mat = s:gsub(','.mat.',','%(,\s*)@<=([^ ,=]{-})(\s*\=\s*)([^,=]{-})%(\s*,)@=','\3\2\1')
+ let add = pre.s:sub(s:sub(mat,'^,',''),',$','').post
+ elseif line =~ '^s\*\%(if\|unless\|while\|until\|for\)\>'
+ let lnum = s:endof(lnum)
+ endif
+ if lnum == 0
+ return -1
+ endif
+ if add == ""
+ let add = s:sub(line,'^\s*\zs.*','raise ActiveRecord::IrreversableMigration')
+ elseif add == " "
+ let add = ""
+ endif
+ let str = add."\n".str
+ let lnum = lnum + 1
+ endwhile
+ let str = s:gsub(str,'(\s*raise ActiveRecord::IrreversableMigration\n)+','\1')
+ return str
+endfunction
+
+function! s:Invert(bang)
+ let err = "Could not parse method"
+ let src = "up"
+ let dst = "down"
+ let beg = search('\%('.&l:define.'\).*'.src.'\>',"w")
+ let end = s:endof(beg)
+ if beg + 1 == end
+ let src = "down"
+ let dst = "up"
+ let beg = search('\%('.&l:define.'\).*'.src.'\>',"w")
+ let end = s:endof(beg)
+ endif
+ if !beg || !end
+ return s:error(err)
+ endif
+ let str = s:invertrange(beg+1,end-1)
+ if str == -1
+ return s:error(err)
+ endif
+ let beg = search('\%('.&l:define.'\).*'.dst.'\>',"w")
+ let end = s:endof(beg)
+ if !beg || !end
+ return s:error(err)
+ endif
+ if beg + 1 < end
+ exe (beg+1).",".(end-1)."delete _"
+ endif
+ if str != ""
+ let reg_keep = @"
+ let @" = str
+ exe beg."put"
+ exe 1+beg
+ let @" = reg_keep
+ endif
+endfunction
+
+" }}}1
+" Cache {{{1
+
+function! s:cacheworks()
+ if v:version < 700
+ return 0
+ endif
+ if !exists("s:cache")
+ let s:cache = {}
+ endif
+ if !has_key(s:cache,RailsRoot())
+ let s:cache[RailsRoot()] = {}
+ endif
+ return 1
+endfunction
+
+function! s:cacheclear(...)
+ if RailsRoot() == "" | return "" | endif
+ if !s:cacheworks() | return "" | endif
+ if a:0 == 1
+ if s:cachehas(a:1)
+ unlet! s:cache[RailsRoot()][a:1]
+ endif
+ else
+ let s:cache[RailsRoot()] = {}
+ endif
+endfunction
+
+function! s:cache(...)
+ if !s:cacheworks() | return "" | endif
+ if a:0 == 1
+ return s:cache[RailsRoot()][a:1]
+ else
+ return s:cache[RailsRoot()]
+ endif
+endfunction
+
+"function! RailsCache(...)
+ "if !s:cacheworks() | return "" | endif
+ "if a:0 == 1
+ "if s:cachehas(a:1)
+ "return s:cache(a:1)
+ "else
+ "return ""
+ "endif
+ "else
+ "return s:cache()
+ "endif
+"endfunction
+
+function! s:cachehas(key)
+ if !s:cacheworks() | return "" | endif
+ return has_key(s:cache(),a:key)
+endfunction
+
+function! s:cacheneeds(key)
+ if !s:cacheworks() | return "" | endif
+ return !has_key(s:cache(),a:key)
+endfunction
+
+function! s:cacheset(key,value)
+ if !s:cacheworks() | return "" | endif
+ let s:cache[RailsRoot()][a:key] = a:value
+endfunction
+
+" }}}1
+" Syntax {{{1
+
+" Depends: s:rubyeval, s:gsub, cache functions
+
+function! s:helpermethods()
+ let s:rails_helper_methods = ""
+ \."atom_feed auto_discovery_link_tag auto_link "
+ \."benchmark button_to button_to_function "
+ \."cache capture cdata_section check_box check_box_tag collection_select concat content_for content_tag content_tag_for country_options_for_select country_select cycle "
+ \."date_select datetime_select debug define_javascript_functions distance_of_time_in_words distance_of_time_in_words_to_now div_for dom_class dom_id draggable_element draggable_element_js drop_receiving_element drop_receiving_element_js "
+ \."error_message_on error_messages_for escape_javascript escape_once evaluate_remote_response excerpt "
+ \."field_set_tag fields_for file_field file_field_tag form form_for form_remote_for form_remote_tag form_tag "
+ \."hidden_field hidden_field_tag highlight "
+ \."image_path image_submit_tag image_tag input "
+ \."javascript_cdata_section javascript_include_tag javascript_path javascript_tag "
+ \."label link_to link_to_function link_to_if link_to_remote link_to_unless link_to_unless_current "
+ \."mail_to markdown "
+ \."number_to_currency number_to_human_size number_to_percentage number_to_phone number_with_delimiter number_with_precision "
+ \."observe_field observe_form option_groups_from_collection_for_select options_for_select options_from_collection_for_select "
+ \."partial_path password_field password_field_tag path_to_image path_to_javascript path_to_stylesheet periodically_call_remote pluralize "
+ \."radio_button radio_button_tag remote_form_for remote_function reset_cycle "
+ \."sanitize sanitize_css select select_date select_datetime select_day select_hour select_minute select_month select_second select_tag select_time select_year simple_format sortable_element sortable_element_js strip_links strip_tags stylesheet_link_tag stylesheet_path submit_tag submit_to_remote "
+ \."tag text_area text_area_tag text_field text_field_tag textilize textilize_without_paragraph time_ago_in_words time_select time_zone_options_for_select time_zone_select truncate "
+ \."update_page update_page_tag url_for "
+ \."visual_effect "
+ \."word_wrap"
+
+ " The list of helper methods used to be derived automatically. Let's keep
+ " this code around in case it's needed again.
+ if !exists("s:rails_helper_methods")
+ if g:rails_expensive
+ let s:rails_helper_methods = ""
+ if has("ruby")
+ " && (has("win32") || has("win32unix"))
+ ruby begin; require 'rubygems'; rescue LoadError; end
+ if exists("g:rubycomplete_rails") && g:rubycomplete_rails
+ ruby begin; require VIM::evaluate('RailsRoot()')+'/config/environment'; rescue Exception; end
+ else
+ ruby begin; require 'active_support'; require 'action_controller'; require 'action_view'; rescue LoadError; end
+ end
+ ruby begin; h = ActionView::Helpers.constants.grep(/Helper$/).collect {|c|ActionView::Helpers.const_get c}.collect {|c| c.public_instance_methods(false)}.collect {|es| es.reject {|e| e =~ /_with(out)?_deprecation$/ || es.include?("#{e}_without_deprecation")}}.flatten.sort.uniq.reject {|m| m =~ /[=?!]$/}; VIM::command('let s:rails_helper_methods = "%s"' % h.join(" ")); rescue Exception; end
+ endif
+ if s:rails_helper_methods == ""
+ let s:rails_helper_methods = s:rubyeval('require %{action_controller}; require %{action_view}; h = ActionView::Helpers.constants.grep(/Helper$/).collect {|c|ActionView::Helpers.const_get c}.collect {|c| c.public_instance_methods(false)}.collect {|es| es.reject {|e| e =~ /_with(out)?_deprecation$/ || es.include?(%{#{e}_without_deprecation})}}.flatten.sort.uniq.reject {|m| m =~ /[=?!]$/}; puts h.join(%{ })',"link_to")
+ endif
+ else
+ let s:rails_helper_methods = "link_to"
+ endif
+ endif
+ "let g:rails_helper_methods = s:rails_helper_methods
+ return s:rails_helper_methods
+endfunction
+
+function! s:BufSyntax()
+ if (!exists("g:rails_syntax") || g:rails_syntax)
+ let t = RailsFileType()
+ let s:prototype_functions = "$ $$ $A $F $H $R $w"
+ " From the Prototype bundle for TextMate
+ let s:prototype_classes = "Prototype Class Abstract Try PeriodicalExecuter Enumerable Hash ObjectRange Element Ajax Responders Base Request Updater PeriodicalUpdater Toggle Insertion Before Top Bottom After ClassNames Form Serializers TimedObserver Observer EventObserver Event Position Effect Effect2 Transitions ScopedQueue Queues DefaultOptions Parallel Opacity Move MoveBy Scale Highlight ScrollTo Fade Appear Puff BlindUp BlindDown SwitchOff DropOut Shake SlideDown SlideUp Squish Grow Shrink Pulsate Fold"
+
+ let rails_helper_methods = '+\.\@<!\<\('.s:gsub(s:helpermethods(),'\s+','\\|').'\)\>+'
+ let classes = s:gsub(RailsUserClasses(),'::',' ')
+ if &syntax == 'ruby'
+ if classes != ''
+ exe "syn keyword rubyRailsUserClass ".classes." containedin=rubyClassDeclaration,rubyModuleDeclaration,rubyClass,rubyModule"
+ endif
+ if t == ''
+ syn keyword rubyRailsMethod params request response session headers cookies flash
+ endif
+ if t =~ '^api\>'
+ syn keyword rubyRailsAPIMethod api_method inflect_names
+ endif
+ if t =~ '^model$' || t =~ '^model-arb\>'
+ syn keyword rubyRailsARMethod acts_as_list acts_as_nested_set acts_as_tree composed_of serialize
+ syn keyword rubyRailsARAssociationMethod belongs_to has_one has_many has_and_belongs_to_many
+ "syn match rubyRailsARCallbackMethod '\<\(before\|after\)_\(create\|destroy\|save\|update\|validation\|validation_on_create\|validation_on_update\)\>'
+ syn keyword rubyRailsARCallbackMethod before_create before_destroy before_save before_update before_validation before_validation_on_create before_validation_on_update
+ syn keyword rubyRailsARCallbackMethod after_create after_destroy after_save after_update after_validation after_validation_on_create after_validation_on_update
+ syn keyword rubyRailsARClassMethod attr_accessible attr_protected establish_connection set_inheritance_column set_locking_column set_primary_key set_sequence_name set_table_name
+ "syn keyword rubyRailsARCallbackMethod after_find after_initialize
+ syn keyword rubyRailsARValidationMethod validate validate_on_create validate_on_update validates_acceptance_of validates_associated validates_confirmation_of validates_each validates_exclusion_of validates_format_of validates_inclusion_of validates_length_of validates_numericality_of validates_presence_of validates_size_of validates_uniqueness_of
+ syn keyword rubyRailsMethod logger
+ endif
+ if t =~ '^model-aro\>'
+ syn keyword rubyRailsARMethod observe
+ endif
+ if t =~ '^model-mailer\>'
+ syn keyword rubyRailsMethod logger
+ " Misnomer but who cares
+ syn keyword rubyRailsControllerMethod helper helper_attr helper_method
+ endif
+ if t =~ '^controller\>' || t =~ '^view\>' || t=~ '^helper\>'
+ syn keyword rubyRailsMethod params request response session headers cookies flash
+ syn match rubyRailsError '[@:]\@<!@\%(params\|request\|response\|session\|headers\|cookies\|flash\)\>'
+ syn match rubyRailsError '\<\%(render_partial\|puts\)\>'
+ syn keyword rubyRailsRenderMethod render
+ syn keyword rubyRailsMethod logger
+ endif
+ if t =~ '^helper\>' || t=~ '^view\>'
+ "exe "syn match rubyRailsHelperMethod ".rails_helper_methods
+ exe "syn keyword rubyRailsHelperMethod ".s:sub(s:helpermethods(),'<select\s+','')
+ syn match rubyRailsHelperMethod '\<select\>\%(\s*{\|\s*do\>\|\s*(\=\s*&\)\@!'
+ syn match rubyRailsViewMethod '\.\@<!\<\(h\|html_escape\|u\|url_encode\|controller\)\>'
+ if t =~ '\<partial\>'
+ syn keyword rubyRailsMethod local_assigns
+ endif
+ "syn keyword rubyRailsDeprecatedMethod start_form_tag end_form_tag link_to_image human_size update_element_function
+ elseif t =~ '^controller\>'
+ syn keyword rubyRailsControllerMethod helper helper_attr helper_method filter layout url_for serialize exempt_from_layout filter_parameter_logging hide_action cache_sweeper
+ syn match rubyRailsDeprecatedMethod '\<render_\%(action\|text\|file\|template\|nothing\|without_layout\)\>'
+ syn keyword rubyRailsRenderMethod render_to_string redirect_to head
+ syn match rubyRailsRenderMethod '\<respond_to\>?\@!'
+ syn keyword rubyRailsFilterMethod before_filter append_before_filter prepend_before_filter after_filter append_after_filter prepend_after_filter around_filter append_around_filter prepend_around_filter skip_before_filter skip_after_filter
+ syn keyword rubyRailsFilterMethod verify
+ endif
+ if t =~ '^\%(db-\)\=\%(migration\|schema\)\>'
+ syn keyword rubyRailsMigrationMethod create_table drop_table rename_table add_column rename_column change_column change_column_default remove_column add_index remove_index
+ endif
+ if t =~ '^test\>'
+ if s:cacheneeds("user_asserts") && filereadable(RailsRoot()."/test/test_helper.rb")
+ call s:cacheset("user_asserts",map(filter(readfile(RailsRoot()."/test/test_helper.rb"),'v:val =~ "^ def assert_"'),'matchstr(v:val,"^ def \\zsassert_\\w\\+")'))
+ endif
+ if s:cachehas("user_asserts") && !empty(s:cache("user_asserts"))
+ exe "syn keyword rubyRailsUserMethod ".join(s:cache("user_asserts"))
+ endif
+ syn keyword rubyRailsTestMethod add_assertion assert assert_block assert_equal assert_in_delta assert_instance_of assert_kind_of assert_match assert_nil assert_no_match assert_not_equal assert_not_nil assert_not_same assert_nothing_raised assert_nothing_thrown assert_operator assert_raise assert_respond_to assert_same assert_send assert_throws assert_recognizes assert_generates assert_routing flunk fixtures fixture_path use_transactional_fixtures use_instantiated_fixtures assert_difference assert_no_difference
+ if t !~ '^test-unit\>'
+ syn match rubyRailsTestControllerMethod '\.\@<!\<\%(get\|post\|put\|delete\|head\|process\|assigns\)\>'
+ syn keyword rubyRailsTestControllerMethod assert_response assert_redirected_to assert_template assert_recognizes assert_generates assert_routing assert_dom_equal assert_dom_not_equal assert_valid assert_select assert_select_rjs assert_select_encoded assert_select_email
+ endif
+ elseif t=~ '^spec\>'
+ syn keyword rubyRailsTestMethod describe context it specify it_should_behave_like before after fixtures controller_name helper_name
+ syn keyword rubyRailsTestMethod violated pending
+ if t !~ '^spec-model\>'
+ syn match rubyRailsTestControllerMethod '\.\@<!\<\%(get\|post\|put\|delete\|head\|process\|assigns\)\>'
+ syn keyword rubyRailsMethod params request response session flash
+ endif
+ endif
+ if t =~ '^task\>'
+ syn match rubyRailsRakeMethod '^\s*\zs\%(task\|file\|namespace\|desc\|before\|after\|on\)\>\%(\s*=\)\@!'
+ endif
+ if t =~ '^model-awss\>'
+ syn keyword rubyRailsMethod member
+ endif
+ if t =~ '^config-routes\>'
+ syn match rubyRailsMethod '\.\zs\%(connect\|resources\=\|root\|named_route\|namespace\)\>'
+ endif
+ syn keyword rubyRailsMethod debugger
+ syn keyword rubyRailsMethod alias_attribute alias_method_chain attr_accessor_with_default attr_internal attr_internal_accessor attr_internal_reader attr_internal_writer delegate mattr_accessor mattr_reader mattr_writer
+ syn keyword rubyRailsMethod cattr_accessor cattr_reader cattr_writer class_inheritable_accessor class_inheritable_array class_inheritable_array_writer class_inheritable_hash class_inheritable_hash_writer class_inheritable_option class_inheritable_reader class_inheritable_writer inheritable_attributes read_inheritable_attribute reset_inheritable_attributes write_inheritable_array write_inheritable_attribute write_inheritable_hash
+ syn keyword rubyRailsInclude require_dependency gem
+
+ syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:order\s*=>\s*\)\@<="+ skip=+\\\\\|\\"+ end=+"+ contains=@rubyStringSpecial,railsOrderSpecial
+ syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:order\s*=>\s*\)\@<='+ skip=+\\\\\|\\'+ end=+'+ contains=@rubyStringSpecial,railsOrderSpecial
+ syn match railsOrderSpecial +\c\<\%(DE\|A\)SC\>+ contained
+ syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:conditions\s*=>\s*\[\s*\)\@<="+ skip=+\\\\\|\\"+ end=+"+ contains=@rubyStringSpecial,railsConditionsSpecial
+ syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:conditions\s*=>\s*\[\s*\)\@<='+ skip=+\\\\\|\\'+ end=+'+ contains=@rubyStringSpecial,railsConditionsSpecial
+ syn match railsConditionsSpecial +?\|:\h\w*+ contained
+ syn cluster rubyNotTop add=railsOrderSpecial,railsConditionsSpecial
+
+ " XHTML highlighting inside %Q<>
+ unlet! b:current_syntax
+ let removenorend = !exists("g:html_no_rendering")
+ let g:html_no_rendering = 1
+ syn include @htmlTop syntax/xhtml.vim
+ if removenorend
+ unlet! g:html_no_rendering
+ endif
+ let b:current_syntax = "ruby"
+ " Restore syn sync, as best we can
+ if !exists("g:ruby_minlines")
+ let g:ruby_minlines = 50
+ endif
+ syn sync fromstart
+ exe "syn sync minlines=" . g:ruby_minlines
+ syn case match
+ syn region rubyString matchgroup=rubyStringDelimiter start=+%Q\=<+ end=+>+ contains=@htmlTop,@rubyStringSpecial
+ "syn region rubyString matchgroup=rubyStringDelimiter start=+%q<+ end=+>+ contains=@htmlTop
+ syn cluster htmlArgCluster add=@rubyStringSpecial
+ syn cluster htmlPreProc add=@rubyStringSpecial
+
+ elseif &syntax == "eruby" || &syntax == "haml" " && t =~ '^view\>'
+ syn case match
+ if classes != ''
+ exe "syn keyword erubyRailsUserClass ".classes." contained containedin=@erubyRailsRegions"
+ endif
+ if &syntax == "haml"
+ syn cluster erubyRailsRegions contains=hamlRubyCodeIncluded,hamlRubyCode,hamlRubyHash,rubyInterpolation
+ else
+ syn cluster erubyRailsRegions contains=erubyOneLiner,erubyBlock,erubyExpression,rubyInterpolation
+ endif
+ syn match rubyRailsError '[@:]\@<!@\%(params\|request\|response\|session\|headers\|cookies\|flash\)\>' contained containedin=@erubyRailsRegions,@rubyTop
+ "exe "syn match erubyRailsHelperMethod ".rails_helper_methods." contained containedin=@erubyRailsRegions"
+ exe "syn keyword erubyRailsHelperMethod ".s:sub(s:helpermethods(),'<select\s+','')." contained containedin=@erubyRailsRegions"
+ syn match erubyRailsHelperMethod '\<select\>\%(\s*{\|\s*do\>\|\s*(\=\s*&\)\@!' contained containedin=@erubyRailsRegions
+ syn keyword erubyRailsMethod debugger logger contained containedin=@erubyRailsRegions
+ syn keyword erubyRailsMethod params request response session headers cookies flash contained containedin=@erubyRailsRegions
+ syn match erubyRailsViewMethod '\.\@<!\<\(h\|html_escape\|u\|url_encode\|controller\)\>' contained containedin=@erubyRailsRegions
+ if t =~ '\<partial\>'
+ syn keyword erubyRailsMethod local_assigns contained containedin=@erubyRailsRegions
+ endif
+ syn keyword erubyRailsRenderMethod render contained containedin=@erubyRailsRegions
+ syn match rubyRailsError '[^@:]\@<!@\%(params\|request\|response\|session\|headers\|cookies\|flash\)\>' contained containedin=@erubyRailsRegions
+ syn match rubyRailsError '\<\%(render_partial\|puts\)\>' contained containedin=@erubyRailsRegions
+ syn case match
+ set isk+=$
+ exe "syn keyword javascriptRailsClass contained ".s:prototype_classes
+ exe "syn keyword javascriptRailsFunction contained ".s:prototype_functions
+ syn cluster htmlJavaScript add=javascriptRailsClass,javascriptRailsFunction
+ elseif &syntax == "yaml"
+ syn case match
+ " Modeled after syntax/eruby.vim
+ unlet! b:current_syntax
+ let g:main_syntax = 'eruby'
+ syn include @rubyTop syntax/ruby.vim
+ unlet g:main_syntax
+ syn cluster yamlRailsRegions contains=yamlRailsOneLiner,yamlRailsBlock,yamlRailsExpression
+ syn region yamlRailsOneLiner matchgroup=yamlRailsDelimiter start="^%%\@!" end="$" contains=@rubyRailsTop containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment keepend oneline
+ syn region yamlRailsBlock matchgroup=yamlRailsDelimiter start="<%%\@!" end="%>" contains=@rubyTop containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment
+ syn region yamlRailsExpression matchgroup=yamlRailsDelimiter start="<%=" end="%>" contains=@rubyTop containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment
+ syn region yamlRailsComment matchgroup=yamlRailsDelimiter start="<%#" end="%>" contains=rubyTodo,@Spell containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment keepend
+ syn match yamlRailsMethod '\.\@<!\<\(h\|html_escape\|u\|url_encode\)\>' contained containedin=@yamlRailsRegions
+ if classes != ''
+ exe "syn keyword yamlRailsUserClass ".classes." contained containedin=@yamlRailsRegions"
+ endif
+ let b:current_syntax = "yaml"
+ elseif &syntax == "html"
+ syn case match
+ set isk+=$
+ exe "syn keyword javascriptRailsClass contained ".s:prototype_classes
+ exe "syn keyword javascriptRailsFunction contained ".s:prototype_functions
+ syn cluster htmlJavaScript add=javascriptRailsClass,javascriptRailsFunction
+ elseif &syntax == "javascript"
+ " The syntax file included with Vim incorrectly sets syn case ignore.
+ syn case match
+ set isk+=$
+ exe "syn keyword javascriptRailsClass ".s:prototype_classes
+ exe "syn keyword javascriptRailsFunction ".s:prototype_functions
+
+ endif
+ endif
+ call s:HiDefaults()
+endfunction
+
+function! s:HiDefaults()
+ hi def link rubyRailsAPIMethod rubyRailsMethod
+ hi def link rubyRailsARAssociationMethod rubyRailsARMethod
+ hi def link rubyRailsARCallbackMethod rubyRailsARMethod
+ hi def link rubyRailsARClassMethod rubyRailsARMethod
+ hi def link rubyRailsARValidationMethod rubyRailsARMethod
+ hi def link rubyRailsARMethod rubyRailsMethod
+ hi def link rubyRailsRenderMethod rubyRailsMethod
+ hi def link rubyRailsHelperMethod rubyRailsMethod
+ hi def link rubyRailsViewMethod rubyRailsMethod
+ hi def link rubyRailsMigrationMethod rubyRailsMethod
+ hi def link rubyRailsControllerMethod rubyRailsMethod
+ hi def link rubyRailsDeprecatedMethod rubyRailsError
+ hi def link rubyRailsFilterMethod rubyRailsMethod
+ hi def link rubyRailsTestControllerMethod rubyRailsTestMethod
+ hi def link rubyRailsTestMethod rubyRailsMethod
+ hi def link rubyRailsRakeMethod rubyRailsMethod
+ hi def link rubyRailsMethod railsMethod
+ hi def link rubyRailsError rubyError
+ hi def link rubyRailsInclude rubyInclude
+ hi def link rubyRailsUserClass railsUserClass
+ hi def link rubyRailsUserMethod railsUserMethod
+ hi def link erubyRailsHelperMethod erubyRailsMethod
+ hi def link erubyRailsViewMethod erubyRailsMethod
+ hi def link erubyRailsRenderMethod erubyRailsMethod
+ hi def link erubyRailsMethod railsMethod
+ hi def link erubyRailsUserMethod railsUserMethod
+ hi def link railsUserMethod railsMethod
+ hi def link erubyRailsUserClass railsUserClass
+ hi def link yamlRailsDelimiter Delimiter
+ hi def link yamlRailsMethod railsMethod
+ hi def link yamlRailsComment Comment
+ hi def link yamlRailsUserClass railsUserClass
+ hi def link yamlRailsUserMethod railsUserMethod
+ hi def link javascriptRailsFunction railsMethod
+ hi def link javascriptRailsClass railsClass
+ hi def link railsUserClass railsClass
+ hi def link railsMethod Function
+ hi def link railsClass Type
+ hi def link railsOrderSpecial railsStringSpecial
+ hi def link railsConditionsSpecial railsStringSpecial
+ hi def link railsStringSpecial Identifier
+endfunction
+
+function! s:RailslogSyntax()
+ syn match railslogRender '^\s*\<\%(Processing\|Rendering\|Rendered\|Redirected\|Completed\)\>'
+ syn match railslogComment '^\s*# .*'
+ syn match railslogModel '^\s*\u\%(\w\|:\)* \%(Load\%( Including Associations\| IDs For Limited Eager Loading\)\=\|Columns\|Count\|Update\|Destroy\|Delete all\)\>' skipwhite nextgroup=railslogModelNum
+ syn match railslogModel '^\s*SQL\>' skipwhite nextgroup=railslogModelNum
+ syn region railslogModelNum start='(' end=')' contains=railslogNumber contained skipwhite nextgroup=railslogSQL
+ syn match railslogSQL '\u.*$' contained
+ " Destroy generates multiline SQL, ugh
+ syn match railslogSQL '^ \%(FROM\|WHERE\|ON\|AND\|OR\|ORDER\) .*$'
+ syn match railslogNumber '\<\d\+\>%'
+ syn match railslogNumber '[ (]\@<=\<\d\+\.\d\+\>'
+ syn region railslogString start='"' skip='\\"' end='"' oneline contained
+ syn region railslogHash start='{' end='}' oneline contains=railslogHash,railslogString
+ syn match railslogIP '\<\d\{1,3\}\%(\.\d\{1,3}\)\{3\}\>'
+ syn match railslogTimestamp '\<\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\>'
+ syn match railslogSessionID '\<\x\{32\}\>'
+ syn match railslogIdentifier '^\s*\%(Session ID\|Parameters\)\ze:'
+ syn match railslogSuccess '\<2\d\d \u[A-Za-z0-9 ]*\>'
+ syn match railslogRedirect '\<3\d\d \u[A-Za-z0-9 ]*\>'
+ syn match railslogError '\<[45]\d\d \u[A-Za-z0-9 ]*\>'
+ syn match railslogError '^DEPRECATION WARNING\>'
+ syn keyword railslogHTTP OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT
+ syn region railslogStackTrace start=":\d\+:in `\w\+'$" end="^\s*$" keepend fold
+ hi def link railslogComment Comment
+ hi def link railslogRender Keyword
+ hi def link railslogModel Type
+ hi def link railslogSQL PreProc
+ hi def link railslogNumber Number
+ hi def link railslogString String
+ hi def link railslogSessionID Constant
+ hi def link railslogIdentifier Identifier
+ hi def link railslogRedirect railslogSuccess
+ hi def link railslogSuccess Special
+ hi def link railslogError Error
+ hi def link railslogHTTP Special
+endfunction
+
+" }}}1
+" Statusline {{{1
+
+" Depends: nothing!
+" Provides: s:BufInitStatusline
+
+function! s:addtostatus(letter,status)
+ let status = a:status
+ if status !~ 'Rails' && g:rails_statusline
+ let status=substitute(status,'\C%'.tolower(a:letter),'%'.tolower(a:letter).'%{RailsStatusline()}','')
+ if status !~ 'Rails'
+ let status=substitute(status,'\C%'.toupper(a:letter),'%'.toupper(a:letter).'%{RailsSTATUSLINE()}','')
+ endif
+ endif
+ return status
+endfunction
+
+function! s:BufInitStatusline()
+ if g:rails_statusline
+ if &l:statusline == ''
+ let &l:statusline = &g:statusline
+ endif
+ if &l:statusline == ''
+ let &l:statusline='%<%f %h%m%r%='
+ if &ruler
+ let &l:statusline = &l:statusline . '%-16( %l,%c-%v %)%P'
+ endif
+ endif
+ let &l:statusline = s:InjectIntoStatusline(&l:statusline)
+ endif
+endfunction
+
+function! s:InitStatusline()
+ if g:rails_statusline
+ if &g:statusline == ''
+ let &g:statusline='%<%f %h%m%r%='
+ if &ruler
+ let &g:statusline = &g:statusline . '%-16( %l,%c-%v %)%P'
+ endif
+ endif
+ let &g:statusline = s:InjectIntoStatusline(&g:statusline)
+ endif
+endfunction
+
+function! s:InjectIntoStatusline(status)
+ let status = a:status
+ if status !~ 'Rails'
+ let status = s:addtostatus('y',status)
+ let status = s:addtostatus('r',status)
+ let status = s:addtostatus('m',status)
+ let status = s:addtostatus('w',status)
+ let status = s:addtostatus('h',status)
+ if status !~ 'Rails'
+ let status=substitute(status,'%=','%{RailsStatusline()}%=','')
+ endif
+ if status !~ 'Rails' && status != ''
+ let status=status.'%{RailsStatusline()}'
+ endif
+ endif
+ return status
+endfunction
+
+function! RailsStatusline()
+ if exists("b:rails_root")
+ let t = RailsFileType()
+ if t != ""
+ return "[Rails-".t."]"
+ else
+ return "[Rails]"
+ endif
+ else
+ return ""
+ endif
+endfunction
+
+function! RailsSTATUSLINE()
+ if exists("b:rails_root")
+ let t = RailsFileType()
+ if t != ""
+ return ",RAILS-".toupper(t)
+ else
+ return ",RAILS"
+ endif
+ else
+ return ""
+ endif
+endfunction
+
+" }}}1
+" Mappings {{{1
+
+" Depends: nothing!
+" Exports: s:BufMappings
+
+function! s:BufMappings()
+ map <buffer> <silent> <Plug>RailsAlternate :A<CR>
+ map <buffer> <silent> <Plug>RailsRelated :R<CR>
+ map <buffer> <silent> <Plug>RailsFind :REfind<CR>
+ map <buffer> <silent> <Plug>RailsSplitFind :RSfind<CR>
+ map <buffer> <silent> <Plug>RailsVSplitFind :RVfind<CR>
+ map <buffer> <silent> <Plug>RailsTabFind :RTfind<CR>
+ if g:rails_mappings
+ if !hasmapto("<Plug>RailsFind")
+ nmap <buffer> gf <Plug>RailsFind
+ endif
+ if !hasmapto("<Plug>RailsSplitFind")
+ nmap <buffer> <C-W>f <Plug>RailsSplitFind
+ endif
+ if !hasmapto("<Plug>RailsTabFind")
+ nmap <buffer> <C-W>gf <Plug>RailsTabFind
+ endif
+ if !hasmapto("<Plug>RailsAlternate")
+ nmap <buffer> [f <Plug>RailsAlternate
+ endif
+ if !hasmapto("<Plug>RailsRelated")
+ nmap <buffer> ]f <Plug>RailsRelated
+ endif
+ if exists("$CREAM")
+ imap <buffer> <C-CR> <C-O><Plug>RailsFind
+ " Are these a good idea?
+ imap <buffer> <M-[> <C-O><Plug>RailsAlternate
+ imap <buffer> <M-]> <C-O><Plug>RailsRelated
+ endif
+ endif
+ " SelectBuf you're a dirty hack
+ let v:errmsg = ""
+endfunction
+
+" }}}1
+" Menus {{{1
+
+" Depends: s:gsub, s:sub, s:error
+" Provides: s:prephelp
+
+function! s:CreateMenus() abort
+ if exists("g:rails_installed_menu") && g:rails_installed_menu != ""
+ exe "aunmenu ".s:gsub(g:rails_installed_menu,'\&','')
+ unlet g:rails_installed_menu
+ endif
+ if has("menu") && (exists("g:did_install_default_menus") || exists("$CREAM")) && g:rails_menu
+ if g:rails_menu > 1
+ let g:rails_installed_menu = '&Rails'
+ else
+ let g:rails_installed_menu = '&Plugin.&Rails'
+ endif
+ if exists("$CREAM")
+ let menucmd = '87anoremenu <script> '
+ exe menucmd.g:rails_installed_menu.'.-PSep- :'
+ exe menucmd.g:rails_installed_menu.'.&Related\ file\ :R\ /\ Alt+] :R<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Alternate\ file\ :A\ /\ Alt+[ :A<CR>'
+ exe menucmd.g:rails_installed_menu.'.&File\ under\ cursor\ Ctrl+Enter :Rfind<CR>'
+ else
+ let menucmd = 'anoremenu <script> '
+ exe menucmd.g:rails_installed_menu.'.-PSep- :'
+ "exe menucmd.g:rails_installed_menu.'.&Related\ file\ :R :R<CR>'
+ "exe menucmd.g:rails_installed_menu.'.&Alternate\ file\ :A :A<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Related\ file\ :R\ /\ ]f :R<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Alternate\ file\ :A\ /\ [f :A<CR>'
+ exe menucmd.g:rails_installed_menu.'.&File\ under\ cursor\ gf :Rfind<CR>'
+ endif
+ exe menucmd.g:rails_installed_menu.'.&Other\ files.Application\ &Controller :find app/controllers/application.rb<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Other\ files.Application\ &Helper :find app/helpers/application_helper.rb<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Other\ files.Application\ &Javascript :find public/javascripts/application.js<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Other\ files.Application\ &Layout :Rlayout application<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Other\ files.Application\ &README :find doc/README_FOR_APP<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Other\ files.&Environment :find config/environment.rb<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Other\ files.&Database\ Configuration :find config/database.yml<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Other\ files.Database\ &Schema :call <SID>findschema()<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Other\ files.R&outes :find config/routes.rb<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Other\ files.&Test\ Helper :find test/test_helper.rb<CR>'
+ exe menucmd.g:rails_installed_menu.'.-FSep- :'
+ exe menucmd.g:rails_installed_menu.'.Ra&ke\ :Rake :Rake<CR>'
+ let tasks = g:rails_rake_tasks
+ while tasks != ''
+ let task = matchstr(tasks,'.\{-\}\ze\%(\n\|$\)')
+ let tasks = s:sub(tasks,'.{-}%(\n|$)','')
+ exe menucmd.g:rails_installed_menu.'.Rake\ &tasks\ :Rake.'.s:sub(s:sub(task,'^[^:]*$','&:all'),':','.').' :Rake '.task.'<CR>'
+ endwhile
+ let tasks = g:rails_generators
+ while tasks != ''
+ let task = matchstr(tasks,'.\{-\}\ze\%(\n\|$\)')
+ let tasks = s:sub(tasks,'.{-}%(\n|$)','')
+ exe menucmd.'<silent> '.g:rails_installed_menu.'.&Generate\ :Rgen.'.s:gsub(task,'_','\\ ').' :call <SID>menuprompt("Rgenerate '.task.'","Arguments for script/generate '.task.': ")<CR>'
+ exe menucmd.'<silent> '.g:rails_installed_menu.'.&Destroy\ :Rdestroy.'.s:gsub(task,'_','\\ ').' :call <SID>menuprompt("Rdestroy '.task.'","Arguments for script/destroy '.task.': ")<CR>'
+ endwhile
+ exe menucmd.g:rails_installed_menu.'.&Server\ :Rserver.&Start\ :Rserver :Rserver<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Server\ :Rserver.&Force\ start\ :Rserver! :Rserver!<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Server\ :Rserver.&Kill\ :Rserver!\ - :Rserver! -<CR>'
+ exe menucmd.'<silent> '.g:rails_installed_menu.'.&Evaluate\ Ruby\.\.\.\ :Rp :call <SID>menuprompt("Rp","Code to execute and output: ")<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Console\ :Rconsole :Rconsole<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Preview\ :Rpreview :Rpreview<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Log\ file\ :Rlog :Rlog<CR>'
+ exe s:sub(menucmd,'anoremenu','vnoremenu').' <silent> '.g:rails_installed_menu.'.E&xtract\ as\ partial\ :Rextract :call <SID>menuprompt("'."'".'<,'."'".'>Rextract","Partial name (e.g., template or /controller/template): ")<CR>'
+ exe menucmd.g:rails_installed_menu.'.&Migration\ writer\ :Rinvert :Rinvert<CR>'
+ exe menucmd.' '.g:rails_installed_menu.'.-HSep- :'
+ exe menucmd.'<silent> '.g:rails_installed_menu.'.&Help\ :help\ rails :call <SID>prephelp()<Bar>help rails<CR>'
+ exe menucmd.'<silent> '.g:rails_installed_menu.'.Abo&ut\ :call <SID>prephelp()<Bar>help rails-about<CR>'
+ let g:rails_did_menus = 1
+ call s:ProjectMenu()
+ call s:menuBufLeave()
+ if exists("b:rails_root")
+ call s:menuBufEnter()
+ endif
+ endif
+endfunction
+
+function! s:ProjectMenu()
+ if exists("g:rails_did_menus") && g:rails_history_size > 0
+ if !exists("g:RAILS_HISTORY")
+ let g:RAILS_HISTORY = ""
+ endif
+ let history = g:RAILS_HISTORY
+ let menu = s:gsub(g:rails_installed_menu,'\&','')
+ silent! exe "aunmenu <script> ".menu.".Projects"
+ let dots = s:gsub(menu,'[^.]','')
+ exe 'anoremenu <script> <silent> '.(exists("$CREAM") ? '87' : '').dots.'.100 '.menu.'.Pro&jects.&New\.\.\.\ :Rails :call <SID>menuprompt("Rails","New application path and additional arguments: ")<CR>'
+ exe 'anoremenu <script> '.menu.'.Pro&jects.-FSep- :'
+ while history =~ '\n'
+ let proj = matchstr(history,'^.\{-\}\ze\n')
+ let history = s:sub(history,'^.{-}\n','')
+ exe 'anoremenu <script> '.menu.'.Pro&jects.'.s:gsub(proj,'[.\\ ]','\\&').' :e '.s:gsub(proj."/".g:rails_default_file,'[ !%#]','\\&')."<CR>"
+ endwhile
+ endif
+endfunction
+
+function! s:menuBufEnter()
+ if exists("g:rails_installed_menu") && g:rails_installed_menu != ""
+ let menu = s:gsub(g:rails_installed_menu,'\&','')
+ exe 'amenu enable '.menu.'.*'
+ if RailsFileType() !~ '^view\>'
+ exe 'vmenu disable '.menu.'.Extract\ as\ partial'
+ endif
+ if RailsFileType() !~ '^\%(db-\)\=migration$' || RailsFilePath() =~ '\<db/schema\.rb$'
+ exe 'amenu disable '.menu.'.Migration\ writer'
+ endif
+ call s:ProjectMenu()
+ endif
+endfunction
+
+function! s:menuBufLeave()
+ if exists("g:rails_installed_menu") && g:rails_installed_menu != ""
+ let menu = s:gsub(g:rails_installed_menu,'\&','')
+ exe 'amenu disable '.menu.'.*'
+ exe 'amenu enable '.menu.'.Help\ '
+ exe 'amenu enable '.menu.'.About\ '
+ exe 'amenu enable '.menu.'.Projects'
+ endif
+endfunction
+
+function! s:menuprompt(vimcmd,prompt)
+ let res = inputdialog(a:prompt,'','!!!')
+ if res == '!!!'
+ return ""
+ endif
+ exe a:vimcmd." ".res
+endfunction
+
+function! s:prephelp()
+ let fn = fnamemodify(s:file,':h:h').'/doc/'
+ if filereadable(fn.'rails.txt')
+ if !filereadable(fn.'tags') || getftime(fn.'tags') <= getftime(fn.'rails.txt')
+ "silent! exe 'helptags '.s:escarg(fn)
+ silent! helptags `=fn`
+ endif
+ endif
+endfunction
+
+function! s:findschema()
+ let env = exists('$RAILS_ENV') ? $RAILS_ENV : return "development"
+ if filereadable(RailsRoot()."/db/schema.rb")
+ "exe "edit ".s:ra()."/db/schema.rb"
+ edit `=RailsRoot().'/db/schema.rb'`
+ elseif filereadable(RailsRoot().'/db/'.env.'_structure.sql')
+ "exe "edit ".s:ra()."/db/".env."_structure.sql"
+ edit `=RailsRoot().'/db/'.env.'_structure.sql'`
+ else
+ return s:error("Schema not found: try :Rake db:schema:dump")
+ endif
+endfunction
+
+" }}}1
+" Project {{{
+
+" Depends: s:gsub, s:escarg, s:warn, s:sub, s:relglob
+
+function! s:Project(bang,arg)
+ let rr = RailsRoot()
+ exe "Project ".a:arg
+ let line = search('^[^ =]*="'.s:gsub(rr,'[\/]','[\\/]').'"')
+ let projname = s:gsub(fnamemodify(rr,':t'),'\=','-') " .'_on_rails'
+ if line && a:bang
+ let projname = matchstr(getline('.'),'^[^=]*')
+ " Most of this would be unnecessary if the project.vim author had just put
+ " the newlines AFTER each project rather than before. Ugh.
+ norm zR0"_d%
+ if line('.') > 2
+ delete _
+ endif
+ if line('.') != line('$')
+ .-2
+ endif
+ let line = 0
+ elseif !line
+ $
+ endif
+ if !line
+ if line('.') > 1
+ append
+
+.
+ endif
+ let line = line('.')+1
+ call s:NewProject(projname,rr,a:bang)
+ endif
+ normal! zMzo
+ if search("^ app=app {","W",line+10)
+ normal! zo
+ exe line
+ endif
+ normal! 0zt
+endfunction
+
+function! s:NewProject(proj,rr,fancy)
+ let line = line('.')+1
+ let template = s:NewProjectTemplate(a:proj,a:rr,a:fancy)
+ silent put =template
+ exe line
+ " Ugh. how else can I force detecting folds?
+ setlocal foldmethod=manual
+ norm! $%
+ silent exe "doautocmd User ".s:escarg(a:rr)."/Rproject"
+ let newline = line('.')
+ exe line
+ norm! $%
+ if line('.') != newline
+ call s:warn("Warning: Rproject autocommand failed to leave cursor at end of project")
+ endif
+ exe line
+ setlocal foldmethod=marker
+ setlocal nomodified
+ " FIXME: make undo stop here
+ if !exists("g:maplocalleader")
+ silent! normal \R
+ else " Needs to be tested
+ exe 'silent! normal '.g:maplocalleader.'R'
+ endif
+endfunction
+
+function! s:NewProjectTemplate(proj,rr,fancy)
+ let str = a:proj.'="'.a:rr."\" CD=. filter=\"*\" {\n"
+ let str = str." app=app {\n"
+ if isdirectory(a:rr.'/app/apis')
+ let str = str." apis=apis {\n }\n"
+ endif
+ let str = str." controllers=controllers filter=\"**\" {\n }\n"
+ let str = str." helpers=helpers filter=\"**\" {\n }\n"
+ let str = str." models=models filter=\"**\" {\n }\n"
+ if a:fancy
+ let str = str." views=views {\n"
+ let views = s:relglob(a:rr.'/app/views/','*')."\n"
+ while views != ''
+ let dir = matchstr(views,'^.\{-\}\ze\n')
+ let views = s:sub(views,'^.{-}\n','')
+ let str = str." ".dir."=".dir.' filter="**" {'."\n }\n"
+ endwhile
+ let str = str." }\n"
+ else
+ let str = str." views=views filter=\"**\" {\n }\n"
+ endif
+ let str = str . " }\n"
+ let str = str . " config=config {\n environments=environments {\n }\n }\n"
+ let str = str . " db=db {\n"
+ if isdirectory(a:rr.'/db/migrate')
+ let str = str . " migrate=migrate {\n }\n"
+ endif
+ let str = str . " }\n"
+ let str = str . " lib=lib filter=\"* */**/*.rb \" {\n tasks=tasks filter=\"**/*.rake\" {\n }\n }\n"
+ let str = str . " public=public {\n images=images {\n }\n javascripts=javascripts {\n }\n stylesheets=stylesheets {\n }\n }\n"
+ if isdirectory(a:rr.'/spec')
+ let str = str . " spec=spec {\n"
+ let str = str . " controllers=controllers filter=\"**\" {\n }\n"
+ let str = str . " fixtures=fixtures filter=\"**\" {\n }\n"
+ let str = str . " helpers=helpers filter=\"**\" {\n }\n"
+ let str = str . " models=models filter=\"**\" {\n }\n"
+ let str = str . " views=views filter=\"**\" {\n }\n }\n"
+ endif
+ let str = str . " test=test {\n"
+ if isdirectory(a:rr.'/test/fixtures')
+ let str = str . " fixtures=fixtures filter=\"**\" {\n }\n"
+ endif
+ if isdirectory(a:rr.'/test/functional')
+ let str = str . " functional=functional filter=\"**\" {\n }\n"
+ endif
+ if isdirectory(a:rr.'/test/integration')
+ let str = str . " integration=integration filter=\"**\" {\n }\n"
+ endif
+ let str = str . " mocks=mocks filter=\"**\" {\n }\n"
+ if isdirectory(a:rr.'/test/unit')
+ let str = str . " unit=unit filter=\"**\" {\n }\n"
+ endif
+ let str = str . " }\n}\n"
+ "if exists("*RailsProcessProject")
+ "let str = call RailsProcessProject(a:rr,str)
+ "endif
+ return str
+endfunction
+
+" }}}1
+" Database {{{1
+
+" Depends: s:environment, s:rubyeval, s:rv, reloadability
+
+function! s:extractdbvar(str,arg)
+ return matchstr("\n".a:str."\n",'\n'.a:arg.'=\zs.\{-\}\ze\n')
+endfunction
+
+function! s:BufDatabase(...)
+ if exists("s:lock_database")
+ return
+ endif
+ let s:lock_database = 1
+ let rv = s:rv()
+ if (a:0 && a:1 > 1)
+ unlet! s:dbext_type_{rv}
+ endif
+ if (a:0 > 1 && a:2 != '')
+ let env = a:2
+ else
+ let env = s:environment()
+ endif
+ " Crude caching mechanism
+ if !exists("s:dbext_type_".rv)
+ if exists("g:loaded_dbext") && (g:rails_dbext + (a:0 ? a:1 : 0)) > 0 && filereadable(RailsRoot()."/config/database.yml")
+ " Ideally we would filter this through ERB but that could be insecure.
+ " It might be possible to make use of taint checking.
+ let out = ""
+ if has("ruby")
+ ruby require 'yaml'; VIM::command('let out = %s' % File.open(VIM::evaluate("RailsRoot()")+"/config/database.yml") {|f| y = YAML::load(f); e = y[VIM::evaluate("env")]; i=0; e=y[e] while e.respond_to?(:to_str) && (i+=1)<16; e.map {|k,v| "#{k}=#{v}\n" if v}.compact.join }.inspect) rescue nil
+ endif
+ if out == ""
+ let cmdb = 'require %{yaml}; File.open(%q{'.RailsRoot().'/config/database.yml}) {|f| y = YAML::load(f); e = y[%{'
+ let cmde = '}]; i=0; e=y[e] while e.respond_to?(:to_str) && (i+=1)<16; e.each{|k,v|puts k+%{=}+v if v}}'
+ if a:0 ? a:1 : g:rails_expensive
+ let out = s:rubyeval(cmdb.env.cmde,'')
+ else
+ unlet! s:lock_database
+ return
+ endif
+ endif
+ let adapter = s:extractdbvar(out,'adapter')
+ let s:dbext_bin_{rv} = ''
+ let s:dbext_integratedlogin_{rv} = ''
+ if adapter == 'postgresql'
+ let adapter = 'pgsql'
+ elseif adapter == 'sqlite3'
+ let adapter = 'sqlite'
+ " Does not appear to work
+ let s:dbext_bin = 'sqlite3'
+ elseif adapter == 'sqlserver'
+ let adapter = 'sqlsrv'
+ elseif adapter == 'sybase'
+ let adapter = 'asa'
+ elseif adapter == 'oci'
+ let adapter = 'ora'
+ endif
+ let s:dbext_type_{rv} = toupper(adapter)
+ let s:dbext_user_{rv} = s:extractdbvar(out,'username')
+ let s:dbext_passwd_{rv} = s:extractdbvar(out,'password')
+ if s:dbext_passwd_{rv} == '' && adapter == 'mysql'
+ " Hack to override password from .my.cnf
+ let s:dbext_extra_{rv} = ' --password='
+ else
+ let s:dbext_extra_{rv} = ''
+ endif
+ let s:dbext_dbname_{rv} = s:extractdbvar(out,'database')
+ if s:dbext_dbname_{rv} != '' && s:dbext_dbname_{rv} !~ '^:' && adapter =~? '^sqlite'
+ let s:dbext_dbname_{rv} = RailsRoot().'/'.s:dbext_dbname_{rv}
+ endif
+ let s:dbext_profile_{rv} = ''
+ let s:dbext_host_{rv} = s:extractdbvar(out,'host')
+ let s:dbext_port_{rv} = s:extractdbvar(out,'port')
+ let s:dbext_dsnname_{rv} = s:extractdbvar(out,'dsn')
+ if s:dbext_host_{rv} =~? '^\cDBI:'
+ if s:dbext_host_{rv} =~? '\c\<Trusted[_ ]Connection\s*=\s*yes\>'
+ let s:dbext_integratedlogin_{rv} = 1
+ endif
+ let s:dbext_host_{rv} = matchstr(s:dbext_host_{rv},'\c\<\%(Server\|Data Source\)\s*=\s*\zs[^;]*')
+ endif
+ endif
+ endif
+ if exists("s:dbext_type_".rv)
+ silent! let b:dbext_type = s:dbext_type_{rv}
+ silent! let b:dbext_profile = s:dbext_profile_{rv}
+ silent! let b:dbext_bin = s:dbext_bin_{rv}
+ silent! let b:dbext_user = s:dbext_user_{rv}
+ silent! let b:dbext_passwd = s:dbext_passwd_{rv}
+ silent! let b:dbext_dbname = s:dbext_dbname_{rv}
+ silent! let b:dbext_host = s:dbext_host_{rv}
+ silent! let b:dbext_port = s:dbext_port_{rv}
+ silent! let b:dbext_dsnname = s:dbext_dsnname_{rv}
+ silent! let b:dbext_extra = s:dbext_extra_{rv}
+ silent! let b:dbext_integratedlogin = s:dbext_integratedlogin_{rv}
+ if b:dbext_type == 'PGSQL'
+ let $PGPASSWORD = b:dbext_passwd
+ elseif exists('$PGPASSWORD')
+ let $PGPASSWORD = ''
+ endif
+ endif
+ if a:0 >= 3 && a:3 && exists(":Create")
+ if exists("b:dbext_dbname") && exists("b:dbext_type") && b:dbext_type !~? 'sqlite'
+ let db = b:dbext_dbname
+ if b:dbext_type == 'PGSQL'
+ " I don't always have a default database for a user so using the
+ " default user's database is a better choice for my setup. It
+ " probably won't work for everyone but nothing will.
+ let b:dbext_dbname = 'postgres'
+ else
+ let b:dbext_dbname = ''
+ endif
+ exe "Create database ".db
+ let b:dbext_dbname = db
+ endif
+ endif
+ unlet! s:lock_database
+endfunction
+
+" }}}1
+" Abbreviations {{{1
+
+" Depends: s:sub, s:gsub, s:string, s:linepeak, s:error
+
+function! s:selectiveexpand(pat,good,default,...)
+ if a:0 > 0
+ let nd = a:1
+ else
+ let nd = ""
+ endif
+ let c = nr2char(getchar(0))
+ let good = a:good
+ if c == "" " ^]
+ return s:sub(good.(a:0 ? " ".a:1 : ''),'\s+$','')
+ elseif c == "\t"
+ return good.(a:0 ? " ".a:1 : '')
+ elseif c =~ a:pat
+ return good.c.(a:0 ? a:1 : '')
+ else
+ return a:default.c
+ endif
+endfunction
+
+function! s:TheMagicC()
+ let l = s:linepeak()
+ if l =~ '\<find\s*\((\|:first,\|:all,\)' || l =~ '\<paginate\>'
+ return s:selectiveexpand('..',':conditions => ',':c')
+ elseif l =~ '\<render\s*(\=\s*:partial\s\*=>\s*'
+ return s:selectiveexpand('..',':collection => ',':c')
+ elseif RailsFileType() =~ '^model\>'
+ return s:selectiveexpand('..',':conditions => ',':c')
+ else
+ return s:selectiveexpand('..',':controller => ',':c')
+ endif
+endfunction
+
+function! s:AddSelectiveExpand(abbr,pat,expn,...)
+ let expn = s:gsub(s:gsub(a:expn ,'[\"|]','\\&'),'\<','\\<Lt>')
+ let expn2 = s:gsub(s:gsub(a:0 ? a:1 : '','[\"|]','\\&'),'\<','\\<Lt>')
+ if a:0
+ exe "inoreabbrev <buffer> <silent> ".a:abbr." <C-R>=<SID>selectiveexpand(".s:string(a:pat).",\"".expn."\",".s:string(a:abbr).",\"".expn2."\")<CR>"
+ else
+ exe "inoreabbrev <buffer> <silent> ".a:abbr." <C-R>=<SID>selectiveexpand(".s:string(a:pat).",\"".expn."\",".s:string(a:abbr).")<CR>"
+ endif
+endfunction
+
+function! s:AddTabExpand(abbr,expn)
+ call s:AddSelectiveExpand(a:abbr,'..',a:expn)
+endfunction
+
+function! s:AddBracketExpand(abbr,expn)
+ call s:AddSelectiveExpand(a:abbr,'[[.]',a:expn)
+endfunction
+
+function! s:AddColonExpand(abbr,expn)
+ call s:AddSelectiveExpand(a:abbr,':',a:expn)
+endfunction
+
+function! s:AddParenExpand(abbr,expn,...)
+ if a:0
+ call s:AddSelectiveExpand(a:abbr,'(',a:expn,a:1)
+ else
+ call s:AddSelectiveExpand(a:abbr,'(',a:expn,'')
+ endif
+endfunction
+
+function! s:BufAbbreviations()
+ command! -buffer -bar -nargs=* -bang Rabbrev :call s:Abbrev(<bang>0,<f-args>)
+ " Some of these were cherry picked from the TextMate snippets
+ if g:rails_abbreviations
+ let t = RailsFileType()
+ " Limit to the right filetypes. But error on the liberal side
+ if t =~ '^\(controller\|view\|helper\|test-functional\|test-integration\)\>'
+ Rabbrev pa[ params
+ Rabbrev rq[ request
+ Rabbrev rs[ response
+ Rabbrev se[ session
+ Rabbrev hd[ headers
+ Rabbrev co[ cookies
+ Rabbrev fl[ flash
+ Rabbrev rr( render
+ Rabbrev ra( render :action\ =>\
+ Rabbrev rc( render :controller\ =>\
+ Rabbrev rf( render :file\ =>\
+ Rabbrev ri( render :inline\ =>\
+ Rabbrev rj( render :json\ =>\
+ Rabbrev rl( render :layout\ =>\
+ Rabbrev rp( render :partial\ =>\
+ Rabbrev rt( render :text\ =>\
+ Rabbrev rx( render :xml\ =>\
+ endif
+ if t =~ '^\%(view\|helper\)\>'
+ Rabbrev dotiw distance_of_time_in_words
+ Rabbrev taiw time_ago_in_words
+ endif
+ if t =~ '^controller\>'
+ "call s:AddSelectiveExpand('rn','[,\r]','render :nothing => true')
+ "let b:rails_abbreviations = b:rails_abbreviations . "rn\trender :nothing => true\n"
+ Rabbrev re( redirect_to
+ Rabbrev rea( redirect_to :action\ =>\
+ Rabbrev rec( redirect_to :controller\ =>\
+ Rabbrev rst( respond_to
+ endif
+ if t =~ '^model-arb\>' || t =~ '^model$'
+ Rabbrev bt( belongs_to
+ Rabbrev ho( has_one
+ Rabbrev hm( has_many
+ Rabbrev habtm( has_and_belongs_to_many
+ Rabbrev co( composed_of
+ Rabbrev va( validates_associated
+ Rabbrev vb( validates_acceptance_of
+ Rabbrev vc( validates_confirmation_of
+ Rabbrev ve( validates_exclusion_of
+ Rabbrev vf( validates_format_of
+ Rabbrev vi( validates_inclusion_of
+ Rabbrev vl( validates_length_of
+ Rabbrev vn( validates_numericality_of
+ Rabbrev vp( validates_presence_of
+ Rabbrev vu( validates_uniqueness_of
+ endif
+ if t =~ '^\%(db-\)\=\%(migration\|schema\)\>'
+ Rabbrev mac( add_column
+ Rabbrev mrnc( rename_column
+ Rabbrev mrc( remove_column
+ Rabbrev mct( create_table
+ "Rabbrev mct create_table\ :\ do\ <Bar>t<Bar><CR>end<Esc>k$6hi
+ Rabbrev mrnt( rename_table
+ Rabbrev mdt( drop_table
+ Rabbrev mcc( t.column
+ endif
+ if t =~ '^test\>'
+ "Rabbrev ae( assert_equal
+ Rabbrev ase( assert_equal
+ "Rabbrev ako( assert_kind_of
+ Rabbrev asko( assert_kind_of
+ "Rabbrev ann( assert_not_nil
+ Rabbrev asnn( assert_not_nil
+ "Rabbrev ar( assert_raise
+ Rabbrev asr( assert_raise
+ "Rabbrev are( assert_response
+ Rabbrev asre( assert_response
+ Rabbrev art( assert_redirected_to
+ endif
+ Rabbrev :a :action\ =>\
+ inoreabbrev <buffer> <silent> :c <C-R>=<SID>TheMagicC()<CR>
+ " Lie a little
+ if t =~ '^view\>'
+ let b:rails_abbreviations = b:rails_abbreviations . ":c\t:collection => \n"
+ elseif s:controller() != ''
+ let b:rails_abbreviations = b:rails_abbreviations . ":c\t:controller => \n"
+ else
+ let b:rails_abbreviations = b:rails_abbreviations . ":c\t:conditions => \n"
+ endif
+ Rabbrev :i :id\ =>\
+ Rabbrev :o :object\ =>\
+ Rabbrev :p :partial\ =>\
+ Rabbrev logd( logger.debug
+ Rabbrev logi( logger.info
+ Rabbrev logw( logger.warn
+ Rabbrev loge( logger.error
+ Rabbrev logf( logger.fatal
+ Rabbrev fi( find
+ Rabbrev AR:: ActiveRecord
+ Rabbrev AV:: ActionView
+ Rabbrev AC:: ActionController
+ Rabbrev AS:: ActiveSupport
+ Rabbrev AM:: ActionMailer
+ Rabbrev AE:: ActiveResource
+ Rabbrev AWS:: ActionWebService
+ endif
+endfunction
+
+function! s:Abbrev(bang,...) abort
+ if !exists("b:rails_abbreviations")
+ let b:rails_abbreviations = "\n"
+ endif
+ if a:0 > 3 || (a:bang && (a:0 != 1))
+ return s:error("Rabbrev: invalid arguments")
+ endif
+ if a:bang
+ return s:unabbrev(a:1)
+ endif
+ if a:0 == 0
+ echo s:sub(b:rails_abbreviations,'^\n','')
+ return
+ endif
+ let lhs = a:1
+ if a:0 > 3 || a:0 < 2
+ return s:error("Rabbrev: invalid arguments")
+ endif
+ let rhs = a:2
+ silent! call s:unabbrev(lhs)
+ if lhs =~ '($'
+ let b:rails_abbreviations = b:rails_abbreviations . lhs . "\t" . rhs . "" . (a:0 > 2 ? "\t".a:3 : ""). "\n"
+ let llhs = s:sub(lhs,'\($','')
+ if a:0 > 2
+ call s:AddParenExpand(llhs,rhs,a:3)
+ else
+ call s:AddParenExpand(llhs,rhs)
+ endif
+ return
+ endif
+ if a:0 > 2
+ return s:error("Rabbrev: invalid arguments")
+ endif
+ if lhs =~ ':$'
+ let llhs = s:sub(lhs,':=:$','')
+ call s:AddColonExpand(llhs,rhs)
+ elseif lhs =~ '\[$'
+ let llhs = s:sub(lhs,'\[$','')
+ call s:AddBracketExpand(llhs,rhs)
+ elseif lhs =~ '\w$'
+ call s:AddTabExpand(lhs,rhs)
+ else
+ return s:error("Rabbrev: unimplemented")
+ endif
+ let b:rails_abbreviations = b:rails_abbreviations . lhs . "\t" . rhs . "\n"
+endfunction
+
+function! s:unabbrev(abbr)
+ let abbr = s:sub(a:abbr,'%(::|\(|\[)$','')
+ let pat = s:sub(abbr,'\\','\\\\')
+ if !exists("b:rails_abbreviations")
+ let b:rails_abbreviations = "\n"
+ endif
+ let b:rails_abbreviations = substitute(b:rails_abbreviations,'\V\C\n'.pat.'\(\t\|::\t\|(\t\|[\t\)\.\{-\}\n','\n','')
+ exe "iunabbrev <buffer> ".abbr
+endfunction
+
+" }}}1
+" Tab Hacks {{{1
+
+" Depends: nothing!
+
+function! s:tabstop()
+ if !exists("b:rails_root")
+ return 0
+ elseif &filetype !~ '^\%(ruby\|'.s:gsub(s:view_types,',','\\|').'\|html\|css\|sass\|yaml\|javascript\)$'
+ return 0
+ elseif exists("b:rails_tabstop")
+ return b:rails_tabstop
+ elseif exists("g:rails_tabstop")
+ return g:rails_tabstop
+ endif
+endfunction
+
+function! s:breaktabs()
+ let ts = s:tabstop()
+ if ts
+ if exists("s:retab_in_process")
+ unlet s:retab_in_process
+ let line = line('.')
+ lockmarks silent! undo
+ lockmarks exe line
+ else
+ let &l:tabstop = 2
+ setlocal noexpandtab
+ let mod = &l:modifiable
+ setlocal modifiable
+ let line = line('.')
+ " FIXME: when I say g/^\s/, only apply to those lines
+ lockmarks g/^\s/retab!
+ lockmarks exe line
+ let &l:modifiable = mod
+ endif
+ let &l:tabstop = ts
+ let &l:softtabstop = ts
+ let &l:shiftwidth = ts
+ endif
+endfunction
+
+function! s:fixtabs()
+ let ts = s:tabstop()
+ if ts && ! &l:expandtab && !exists("s:retab_in_process")
+ let s:retab_in_process = 1
+ let &l:tabstop = 2
+ setlocal expandtab
+ let line = line('.')
+ lockmarks retab
+ lockmarks exe line
+ let &l:tabstop = ts
+ endif
+endfunction
+
+" }}}1
+" Settings {{{1
+
+" Depends: s:error, s:sub, s:sname, s:escvar, s:lastmethod, s:environment, s:gsub, s:lastmethodlib, s:gsub
+
+function! s:Set(bang,...)
+ let c = 1
+ let defscope = ''
+ while c <= a:0
+ let arg = a:{c}
+ let c = c + 1
+ if arg =~? '^<[abgl]\=>$'
+ let defscope = (matchstr(arg,'<\zs.*\ze>'))
+ elseif arg !~ '='
+ if defscope != '' && arg !~ '^\w:'
+ let arg = defscope.':'.opt
+ endif
+ let val = s:getopt(arg)
+ if val == '' && s:opts() !~ '\<'.arg.'\n'
+ call s:error("No such rails.vim option: ".arg)
+ else
+ echo arg."=".val
+ endif
+ else
+ let opt = matchstr(arg,'[^=]*')
+ let val = s:sub(arg,'^[^=]*\=','')
+ if defscope != '' && opt !~ '^\w:'
+ let opt = defscope.':'.opt
+ endif
+ call s:setopt(opt,val)
+ endif
+ endwhile
+endfunction
+
+function! s:getopt(opt,...)
+ let opt = a:opt
+ if a:0
+ let scope = a:1
+ elseif opt =~ '^[abgl]:'
+ let scope = tolower(matchstr(opt,'^\w'))
+ let opt = s:sub(opt,'^\w:','')
+ else
+ let scope = 'abgl'
+ endif
+ if scope =~ 'l' && &filetype != 'ruby'
+ let scope = s:sub(scope,'l','b')
+ endif
+ if scope =~ 'l'
+ call s:LocalModelines()
+ endif
+ let opt = s:sub(opt,'<%(rake|rake_task|rake_target)$','task')
+ " Get buffer option
+ if scope =~ 'l' && exists("b:_".s:sname()."_".s:escvar(s:lastmethod())."_".opt)
+ return b:_{s:sname()}_{s:escvar(s:lastmethod())}_{opt}
+ elseif exists("b:".s:sname()."_".opt) && (scope =~ 'b' || (scope =~ 'l' && s:lastmethod() == ''))
+ return b:{s:sname()}_{opt}
+ elseif scope =~ 'a' && exists("s:_".s:rv()."_".s:environment()."_".opt)
+ return s:_{s:rv()}_{s:environment()}_{opt}
+ elseif scope =~ 'g' && exists("g:".s:sname()."_".opt)
+ return g:{s:sname()}_{opt}
+ else
+ return ""
+ endif
+endfunction
+
+function! s:setopt(opt,val)
+ if a:opt =~? '[abgl]:'
+ let scope = matchstr(a:opt,'^\w')
+ let opt = s:sub(a:opt,'^\w:','')
+ else
+ let scope = ''
+ let opt = a:opt
+ endif
+ let opt = s:sub(opt,'<%(rake|rake_task|rake_target)$','task')
+ let defscope = matchstr(s:opts(),'\n\zs\w\ze:'.opt,'\n')
+ if defscope == ''
+ let defscope = 'a'
+ endif
+ if scope == ''
+ let scope = defscope
+ endif
+ if &filetype == 'ruby' && (scope == 'B' || scope == 'l')
+ let scope = 'b'
+ endif
+ if opt =~ '\W'
+ return s:error("Invalid option ".a:opt)
+ elseif scope =~? 'a'
+ let s:_{s:rv()}_{s:environment()}_{opt} = a:val
+ elseif scope == 'B' && defscope == 'l'
+ let b:_{s:sname()}_{s:escvar('')}_{opt} = a:val
+ elseif scope =~? 'b'
+ let b:{s:sname()}_{opt} = a:val
+ elseif scope =~? 'g'
+ let g:{s:sname()}_{opt} = a:val
+ elseif scope =~? 'l'
+ let b:_{s:sname()}_{s:escvar(s:lastmethod())}_{opt} = a:val
+ else
+ return s:error("Invalid scope for ".a:opt)
+ endif
+endfunction
+
+function! s:opts()
+ return "\nb:alternate\nb:controller\na:gnu_screen\nb:model\nl:preview\nb:task\nl:related\na:root_url\na:ruby_fork_port\n"
+endfunction
+
+function! s:SetComplete(A,L,P)
+ if a:A =~ '='
+ let opt = matchstr(a:A,'[^=]*')
+ return opt."=".s:getopt(opt)
+ else
+ let extra = matchstr(a:A,'^[abgl]:')
+ let opts = s:gsub(s:sub(s:gsub(s:opts(),'\n\w:','\n'.extra),'^\n',''),'\n','=\n')
+ return opts
+ endif
+ return ""
+endfunction
+
+function! s:BufModelines()
+ if !g:rails_modelines
+ return
+ endif
+ let lines = getline("$")."\n".getline(line("$")-1)."\n".getline(1)."\n".getline(2)."\n".getline(3)."\n"
+ let pat = '\s\+\zs.\{-\}\ze\%(\n\|\s\s\|#{\@!\|%>\|-->\|$\)'
+ let cnt = 1
+ let mat = matchstr(lines,'\C\<Rset'.pat)
+ let matend = matchend(lines,'\C\<Rset'.pat)
+ while mat != "" && cnt < 10
+ let mat = s:sub(mat,'\s+$','')
+ let mat = s:gsub(mat,'\|','\\|')
+ if mat != ''
+ silent! exe "Rset <B> ".mat
+ endif
+ let mat = matchstr(lines,'\C\<Rset'.pat,matend)
+ let matend = matchend(lines,'\C\<Rset'.pat,matend)
+ let cnt = cnt + 1
+ endwhile
+endfunction
+
+function! s:LocalModelines()
+ if !g:rails_modelines
+ return
+ endif
+ let lbeg = s:lastmethodline()
+ let lend = s:endof(lbeg)
+ if lbeg == 0 || lend == 0
+ return
+ endif
+ let lines = "\n"
+ let lnum = lbeg
+ while lnum < lend && lnum < lbeg + 5
+ let lines = lines . getline(lnum) . "\n"
+ let lnum = lnum + 1
+ endwhile
+ let pat = '\s\+\zs.\{-\}\ze\%(\n\|\s\s\|#{\@!\|%>\|-->\|$\)'
+ let cnt = 1
+ let mat = matchstr(lines,'\C\<rset'.pat)
+ let matend = matchend(lines,'\C\<rset'.pat)
+ while mat != "" && cnt < 10
+ let mat = s:sub(mat,'\s+$','')
+ let mat = s:gsub(mat,'\|','\\|')
+ if mat != ''
+ silent! exe "Rset <l> ".mat
+ endif
+ let mat = matchstr(lines,'\C\<rset'.pat,matend)
+ let matend = matchend(lines,'\C\<rset'.pat,matend)
+ let cnt = cnt + 1
+ endwhile
+endfunction
+
+" }}}1
+" Detection {{{1
+
+function! s:Detect(filename)
+ let fn = substitute(fnamemodify(a:filename,":p"),'\c^file://','','')
+ if fn =~ '[\/]config[\/]environment\.rb$'
+ return s:BufInit(strpart(fn,0,strlen(fn)-22))
+ endif
+ if isdirectory(fn)
+ let fn = fnamemodify(fn,":s?[\/]$??")
+ else
+ let fn = fnamemodify(fn,':s?\(.*\)[\/][^\/]*$?\1?')
+ endif
+ let ofn = ""
+ let nfn = fn
+ while nfn != ofn && nfn != ""
+ if exists("s:_".s:escvar(nfn))
+ return s:BufInit(nfn)
+ endif
+ let ofn = nfn
+ let nfn = fnamemodify(nfn,':h')
+ endwhile
+ let ofn = ""
+ while fn != ofn
+ if filereadable(fn . "/config/environment.rb")
+ return s:BufInit(fn)
+ endif
+ let ofn = fn
+ let fn = fnamemodify(ofn,':s?\(.*\)[\/]\(app\|config\|db\|doc\|lib\|log\|public\|script\|spec\|test\|tmp\|vendor\)\($\|[\/].*$\)?\1?')
+ endwhile
+ return 0
+endfunction
+
+function! s:scrub(collection,item)
+ " Removes item from a newline separated collection
+ let col = "\n" . a:collection
+ let idx = stridx(col,"\n".a:item."\n")
+ let cnt = 0
+ while idx != -1 && cnt < 100
+ let col = strpart(col,0,idx).strpart(col,idx+strlen(a:item)+1)
+ let idx = stridx(col,"\n".a:item."\n")
+ let cnt = cnt + 1
+ endwhile
+ return strpart(col,1)
+endfunction
+
+function! s:callback(file)
+ if RailsRoot() != ""
+ let var = "callback_".s:rv()."_".s:escvar(a:file)
+ if !exists("s:".var) || exists("b:rails_refresh")
+ let s:{var} = s:hasfile(a:file)
+ endif
+ if s:{var}
+ if exists(":sandbox")
+ sandbox source `=RailsRoot().'/'.a:file`
+ elseif g:rails_modelines
+ source `=RailsRoot().'/'.a:file`
+ endif
+ endif
+ endif
+endfunction
+
+function! s:BufInit(path)
+ let cpo_save = &cpo
+ set cpo&vim
+ let firsttime = !(exists("b:rails_root") && b:rails_root == a:path)
+ let b:rails_root = a:path
+ let s:_{s:rv()} = 1
+ " Apparently RailsFileType() can be slow if the underlying file system is
+ " slow (even though it doesn't really do anything IO related). This caching
+ " is a temporary hack; if it doesn't cause problems it should probably be
+ " refactored.
+ unlet! b:rails_cached_file_type
+ let b:rails_cached_file_type = RailsFileType()
+ if g:rails_history_size > 0
+ if !exists("g:RAILS_HISTORY")
+ let g:RAILS_HISTORY = ""
+ endif
+ let path = a:path
+ let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,path)
+ if has("win32")
+ let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,s:gsub(path,'\\','/'))
+ endif
+ let path = fnamemodify(path,':p:~:h')
+ let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,path)
+ if has("win32")
+ let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,s:gsub(path,'\\','/'))
+ endif
+ let g:RAILS_HISTORY = path."\n".g:RAILS_HISTORY
+ let g:RAILS_HISTORY = s:sub(g:RAILS_HISTORY,'%(.{-}\n){,'.g:rails_history_size.'}\zs.*','')
+ endif
+ call s:callback("config/syntax.vim")
+ if &ft == "mason"
+ setlocal filetype=eruby
+ elseif &ft =~ '^\%(conf\|ruby\)\=$' && expand("%:e") =~ '^\%(rjs\|rxml\|builder\|rake\|mab\)$'
+ setlocal filetype=ruby
+ elseif &ft =~ '^\%(conf\|ruby\)\=$' && expand("%:t") =~ '^\%(Rake\|Cap\)file$'
+ setlocal filetype=ruby
+ elseif &ft =~ '^\%(liquid\)\=$' && expand("%:e") == "liquid"
+ setlocal filetype=liquid
+ elseif &ft =~ '^\%(haml\|x\=html\)\=$' && expand("%:e") == "haml"
+ setlocal filetype=haml
+ elseif &ft =~ '^\%(sass\|conf\)\=$' && expand("%:e") == "sass"
+ setlocal filetype=sass
+ elseif &ft =~ '^\%(dryml\)\=$' && expand("%:e") == "dryml"
+ setlocal filetype=dryml
+ elseif (&ft == "" || v:version < 701) && expand("%:e") =~ '^\%(rhtml\|erb\)$'
+ setlocal filetype=eruby
+ elseif (&ft == "" || v:version < 700) && expand("%:e") == 'yml'
+ setlocal filetype=yaml
+ elseif firsttime
+ " Activate custom syntax
+ let &syntax = &syntax
+ endif
+ if firsttime
+ call s:BufInitStatusline()
+ endif
+ if expand("%:e") == "log"
+ setlocal modifiable filetype=railslog
+ silent! %s/\%(\e\[[0-9;]*m\|\r$\)//g
+ setlocal readonly nomodifiable noswapfile autoread foldmethod=syntax
+ nnoremap <buffer> <silent> R :checktime<CR>
+ nnoremap <buffer> <silent> G :checktime<Bar>$<CR>
+ nnoremap <buffer> <silent> q :bwipe<CR>
+ $
+ endif
+ call s:BufSettings()
+ call s:BufCommands()
+ call s:BufAbbreviations()
+ call s:BufDatabase()
+ " snippetsEmu.vim
+ if exists('g:loaded_snippet')
+ silent! runtime! ftplugin/rails_snippets.vim
+ " filetype snippets need to come last for higher priority
+ exe "silent! runtime! ftplugin/".&filetype."_snippets.vim"
+ endif
+ let t = RailsFileType()
+ let t = "-".t
+ let f = '/'.RailsFilePath()
+ if f =~ '[ !#$%\,]'
+ let f = ''
+ endif
+ runtime! macros/rails.vim
+ silent doautocmd User Rails
+ if t != '-'
+ exe "silent doautocmd User Rails".s:gsub(t,'-','.')
+ endif
+ if f != ''
+ exe "silent doautocmd User Rails".f
+ endif
+ call s:callback("config/rails.vim")
+ call s:BufModelines()
+ call s:BufMappings()
+ "unlet! b:rails_cached_file_type
+ let &cpo = cpo_save
+ return b:rails_root
+endfunction
+
+function! s:SetBasePath()
+ let rp = s:gsub(RailsRoot(),'[ ,]','\\&')
+ let t = RailsFileType()
+ let oldpath = s:sub(&l:path,'^\.,','')
+ if stridx(oldpath,rp) == 2
+ let oldpath = ''
+ endif
+ let &l:path = '.,'.rp.",".rp."/app/controllers,".rp."/app,".rp."/app/models,".rp."/app/helpers,".rp."/config,".rp."/lib,".rp."/vendor,".rp."/vendor/plugins/*/lib,".rp."/test/unit,".rp."/test/functional,".rp."/test/integration,".rp."/app/apis,".rp."/app/services,".rp."/test,"."/vendor/plugins/*/test,".rp."/vendor/rails/*/lib,".rp."/vendor/rails/*/test,".rp."/spec,".rp."/spec/*,"
+ if s:controller() != ''
+ let &l:path = &l:path . rp . '/app/views/' . s:controller() . ',' . rp . '/app/views,' . rp . '/public,'
+ endif
+ if t =~ '^log\>'
+ let &l:path = &l:path . rp . '/app/views,'
+ endif
+ if &l:path =~ '://'
+ let &l:path = ".,"
+ endif
+ let &l:path = &l:path . oldpath
+endfunction
+
+function! s:BufSettings()
+ if !exists('b:rails_root')
+ return ''
+ endif
+ call s:SetBasePath()
+ let rp = s:gsub(RailsRoot(),'[ ,]','\\&')
+ let &errorformat=s:efm
+ setlocal makeprg=rake
+ if stridx(&tags,rp) == -1
+ let &l:tags = &tags . "," . rp . "/tags," . rp . "/.tags"
+ endif
+ if has("gui_win32") || has("gui_running")
+ let code = '*.rb;*.rake;Rakefile'
+ let templates = '*.'.s:gsub(s:view_types,',',';*.')
+ let fixtures = '*.yml;*.csv'
+ let statics = '*.html;*.css;*.js;*.xml;*.xsd;*.sql;.htaccess;README;README_FOR_APP'
+ let b:browsefilter = ""
+ \."All Rails Files\t".code.';'.templates.';'.fixtures.';'.statics."\n"
+ \."Source Code (*.rb, *.rake)\t".code."\n"
+ \."Templates (*.rhtml, *.rxml, *.rjs)\t".templates."\n"
+ \."Fixtures (*.yml, *.csv)\t".fixtures."\n"
+ \."Static Files (*.html, *.css, *.js)\t".statics."\n"
+ \."All Files (*.*)\t*.*\n"
+ endif
+ setlocal includeexpr=RailsIncludeexpr()
+ let &l:suffixesadd=".rb,.".s:gsub(s:view_types,',',',.').",.css,.js,.yml,.csv,.rake,.sql,.html,.xml"
+ if &ft =~ '^\%(e\=ruby\|[yh]aml\|javascript\|css\|sass\)$'
+ setlocal sw=2 sts=2 et
+ "set include=\\<\\zsAct\\f*::Base\\ze\\>\\\|^\\s*\\(require\\\|load\\)\\s\\+['\"]\\zs\\f\\+\\ze
+ if exists('+completefunc')
+ if &completefunc == ''
+ set completefunc=syntaxcomplete#Complete
+ endif
+ endif
+ endif
+ if &filetype == "ruby"
+ let &l:suffixesadd=".rb,.".s:gsub(s:view_types,',',',.').",.yml,.csv,.rake,s.rb"
+ if expand('%:e') == 'rake'
+ setlocal define=^\\s*def\\s\\+\\(self\\.\\)\\=\\\|^\\s*\\%(task\\\|file\\)\\s\\+[:'\"]
+ else
+ setlocal define=^\\s*def\\s\\+\\(self\\.\\)\\=
+ endif
+ " This really belongs in after/ftplugin/ruby.vim but we'll be nice
+ if exists("g:loaded_surround") && !exists("b:surround_101")
+ let b:surround_5 = "\r\nend"
+ let b:surround_69 = "\1expr: \1\rend"
+ let b:surround_101 = "\r\nend"
+ endif
+ elseif &filetype == 'yaml' || expand('%:e') == 'yml'
+ setlocal define=^\\%(\\h\\k*:\\)\\@=
+ let &l:suffixesadd=".yml,.csv,.rb,.".s:gsub(s:view_types,',',',.').",.rake,s.rb"
+ elseif &filetype == "eruby"
+ let &l:suffixesadd=".".s:gsub(s:view_types,',',',.').",.rb,.css,.js,.html,.yml,.csv"
+ if exists("g:loaded_allml")
+ " allml is available on vim.org.
+ let b:allml_stylesheet_link_tag = "<%= stylesheet_link_tag '\r' %>"
+ let b:allml_javascript_include_tag = "<%= javascript_include_tag '\r' %>"
+ let b:allml_doctype_index = 10
+ endif
+ endif
+ if &filetype == "eruby" || &filetype == "yaml"
+ " surround.vim
+ if exists("g:loaded_surround")
+ " The idea behind the || part here is that one can normally define the
+ " surrounding to omit the hyphen (since standard ERuby does not use it)
+ " but have it added in Rails ERuby files. Unfortunately, this makes it
+ " difficult if you really don't want a hyphen in Rails ERuby files. If
+ " this is your desire, you will need to accomplish it via a rails.vim
+ " autocommand.
+ if !exists("b:surround_45") || b:surround_45 == "<% \r %>" " -
+ let b:surround_45 = "<% \r -%>"
+ endif
+ if !exists("b:surround_61") " =
+ let b:surround_61 = "<%= \r %>"
+ endif
+ if !exists("b:surround_35") " #
+ let b:surround_35 = "<%# \r %>"
+ endif
+ if !exists("b:surround_101") || b:surround_101 == "<% \r %>\n<% end %>" "e
+ let b:surround_5 = "<% \r -%>\n<% end -%>"
+ let b:surround_69 = "<% \1expr: \1 -%>\r<% end -%>"
+ let b:surround_101 = "<% \r -%>\n<% end -%>"
+ endif
+ endif
+ endif
+endfunction
+
+" }}}1
+" Initialization {{{1
+
+function! s:InitPlugin()
+ call s:InitConfig()
+ "call s:InitStatusline()
+ if has("autocmd")
+
+ augroup railsPluginDetect
+ autocmd!
+ autocmd BufNewFile,BufRead * call s:Detect(expand("<afile>:p"))
+ autocmd VimEnter * if expand("<amatch>") == "" && !exists("b:rails_root") | call s:Detect(getcwd()) | call s:BufEnter() | endif
+ autocmd BufEnter * call s:BufEnter()
+ autocmd BufLeave * call s:BufLeave()
+ " g:RAILS_HISTORY hasn't been set when s:InitPlugin() is called.
+ autocmd VimEnter * call s:ProjectMenu()
+ autocmd BufWritePost */config/database.yml unlet! s:dbext_type_{s:rv()} " Force reload
+ autocmd BufWritePost */test/test_helper.rb call s:cacheclear("user_asserts")
+ autocmd BufWritePost */config/routes.rb call s:cacheclear("named_routes")
+ autocmd FileType railslog call s:RailslogSyntax()
+ autocmd FileType * if exists("b:rails_root") | call s:BufSettings() | endif
+ autocmd FileType netrw call s:Detect(expand("<afile>:p")) | call s:BufEnter()
+ autocmd Syntax ruby,eruby,yaml,haml,javascript,railslog if exists("b:rails_root") | call s:BufSyntax() | endif
+ silent! autocmd QuickFixCmdPre make* call s:QuickFixCmdPre()
+ silent! autocmd QuickFixCmdPost make* call s:QuickFixCmdPost()
+ augroup END
+ augroup railsPluginTabstop
+ autocmd!
+ autocmd BufWritePost,BufReadPost * call s:breaktabs()
+ autocmd BufWritePre * call s:fixtabs()
+ augroup END
+
+ endif
+ let s:view_types = 'rhtml,erb,rxml,builder,rjs,mab,liquid,haml,dryml'
+ " Current directory
+ let s:efm='%D(in\ %f),'
+ " Failure and Error headers, start a multiline message
+ let s:efm=s:efm
+ \.'%A\ %\\+%\\d%\\+)\ Failure:,'
+ \.'%A\ %\\+%\\d%\\+)\ Error:,'
+ \.'%+A'."'".'%.%#'."'".'\ FAILED,'
+ " Exclusions
+ let s:efm=s:efm
+ \.'%C%.%#(eval)%.%#,'
+ \.'%C-e:%.%#,'
+ \.'%C%.%#/lib/gems/%\\d.%\\d/gems/%.%#,'
+ \.'%C%.%#/lib/ruby/%\\d.%\\d/%.%#,'
+ \.'%C%.%#/vendor/rails/%.%#,'
+ " Specific to template errors
+ let s:efm=s:efm
+ \.'%C\ %\\+On\ line\ #%l\ of\ %f,'
+ \.'%CActionView::TemplateError:\ compile\ error,'
+ " stack backtrace is in brackets. if multiple lines, it starts on a new line.
+ let s:efm=s:efm
+ \.'%Ctest_%.%#(%.%#):%#,'
+ \.'%C%.%#\ [%f:%l]:,'
+ \.'%C\ \ \ \ [%f:%l:%.%#,'
+ \.'%C\ \ \ \ %f:%l:%.%#,'
+ \.'%C\ \ \ \ \ %f:%l:%.%#]:,'
+ \.'%C\ \ \ \ \ %f:%l:%.%#,'
+ " Catch all
+ let s:efm=s:efm
+ \.'%Z%f:%l:\ %#%m,'
+ \.'%Z%f:%l:,'
+ \.'%C%m,'
+ " Syntax errors in the test itself
+ let s:efm=s:efm
+ \.'%.%#.rb:%\\d%\\+:in\ `load'."'".':\ %f:%l:\ syntax\ error\\\, %m,'
+ \.'%.%#.rb:%\\d%\\+:in\ `load'."'".':\ %f:%l:\ %m,'
+ " And required files
+ let s:efm=s:efm
+ \.'%.%#:in\ `require'."'".':in\ `require'."'".':\ %f:%l:\ syntax\ error\\\, %m,'
+ \.'%.%#:in\ `require'."'".':in\ `require'."'".':\ %f:%l:\ %m,'
+ " Exclusions
+ let s:efm=s:efm
+ \.'%-G%.%#/lib/gems/%\\d.%\\d/gems/%.%#,'
+ \.'%-G%.%#/lib/ruby/%\\d.%\\d/%.%#,'
+ \.'%-G%.%#/vendor/rails/%.%#,'
+ \.'%-G%.%#%\\d%\\d:%\\d%\\d:%\\d%\\d%.%#,'
+ " Final catch all for one line errors
+ let s:efm=s:efm
+ \.'%-G%\\s%#from\ %.%#,'
+ \.'%f:%l:\ %#%m,'
+ " Drop everything else
+ let s:efm=s:efm
+ \.'%-G%.%#'
+ " OLD
+ let s:efm_old=''
+ \.'%Z%f:%l:\ syntax\ error\\,\ %m,'
+ \.'%Z\ %#,'
+ \.'%Z%p^,'
+ \.'%CActionView::TemplateError:\ %f:%l:in\ `%.%#'."'".':\ %m,'
+ \.'%CActionView::TemplateError:\ You\ have\ a\ %m!,'
+ \.'%CNoMethodError:\ You\ have\ a\ %m!,'
+ \.'%CActionView::TemplateError:\ %m,'
+ \.'%CThe\ error\ occured\ while\ %m,'
+ \.'ActionView::TemplateError\ (%m)\ on\ line\ #%l\ of\ %f:,'
+ \.'%AActionView::TemplateError\ (compile\ error,'
+ " from
+ command! -bar -bang -nargs=* -complete=dir Rails :call s:NewApp(<bang>0,<f-args>)
+ call s:CreateMenus()
+ map <SID>xx <SID>xx
+ let s:sid = s:sub(maparg("<SID>xx"),'xx$','')
+ unmap <SID>xx
+ " Apparently, the nesting level within Vim when the Ruby interface is
+ " initialized determines how much stack space Ruby gets. In previous
+ " versions of rails.vim, sporadic stack overflows occured when omnicomplete
+ " was used. This was apparently due to rails.vim having first initialized
+ " ruby deep in a nested function call.
+ silent! ruby nil
+endfunction
+
+" }}}1
+
+let s:file = expand('<sfile>:p')
+let s:revision = ' $Id: rails.vim 239 2008-01-03 15:55:55Z tpope $ '
+let s:revision = s:sub(s:revision,'^ [$]Id:.{-}(<[0-9a-f]+>).*[$] $','\1')
+call s:InitPlugin()
+
+let &cpo = s:cpo_save
+
+" vim:set sw=2 sts=2:
diff --git a/files/.vim/plugin/remoteOpen.vim b/files/.vim/plugin/remoteOpen.vim
new file mode 100755
index 0000000..cb550ff
--- /dev/null
+++ b/files/.vim/plugin/remoteOpen.vim
@@ -0,0 +1,163 @@
+" File: remoteOpen.vim
+" Author: Srinath Avadhanula <srinath AT fastmail DOT fm>
+" $Id: remoteOpen.vim 997 2006-03-20 09:45:45Z srinathava $
+"
+" Description:
+" Often times, an external program needs to open a file in gvim from the
+" command line. However, it will not know if the file is already opened in a
+" previous vim session. It is not sufficient to simply specify
+"
+" gvim --remote-silent <filename>
+"
+" because this simply opens up <filename> in the first remote gvim session it
+" sees. This script provides a command RemoteOpen which is meant to be used
+" from the command line as follows:
+"
+" gvim -c ":RemoteOpen +<lnum> <filename>"
+"
+" where <lnum> is the line-number you wish <filename> to open to. What will
+" happen is that a new gvim will start up and enquire from all previous
+" sessions if <filename> is already open in any of them. If it is, then it
+" will edit the file in that session and bring it to the foreground and itself
+" quit. Otherwise, it will not quit and instead open up the file for editing
+" at <lnum>.
+"
+" This was mainly created to be used with Yap (the dvi previewer in miktex),
+" so you can specify the program for "inverse search" as specified above.
+" This ensures that the inverse search uses the correct gvim each time.
+"
+" Ofcourse, this requires vim with +clientserver. If not, then RemoteOpen just
+" opens in the present session.
+
+" Enclose <args> in single quotes so it can be passed as a function argument.
+com -nargs=1 RemoteOpen :call RemoteOpen('<args>')
+com -nargs=? RemoteInsert :call RemoteInsert('<args>')
+
+" RemoteOpen: open a file remotely (if possible) {{{
+" Description: checks all open vim windows to see if this file has been opened
+" anywhere and if so, opens it there instead of in this session.
+function! RemoteOpen(arglist)
+
+ " First construct line number and filename from argument. a:arglist is of
+ " the form:
+ " +10 c:\path\to\file
+ " or just
+ " c:\path\to\file
+ if a:arglist =~ '^\s*+\d\+'
+ let linenum = matchstr(a:arglist, '^\s*+\zs\d\+\ze')
+ let filename = matchstr(a:arglist, '^\s*+\d\+\s*\zs.*\ze')
+ else
+ let linenum = 1
+ let filename = matchstr(a:arglist, '^\s*\zs.*\ze')
+ endif
+ let filename = escape(filename, ' ')
+ call Tex_Debug("linenum = ".linenum.', filename = '.filename, "ropen")
+
+ " If there is no clientserver functionality, then just open in the present
+ " session and return
+ if !has('clientserver')
+ call Tex_Debug("-clientserver, opening locally and returning", "ropen")
+ exec "e ".filename
+ exec linenum
+ normal! zv
+ return
+ endif
+
+ " Otherwise, loop through all available servers
+ let servers = serverlist()
+ " If there are no servers, open file locally.
+ if servers == ''
+ call Tex_Debug("no open servers, opening locally", "ropen")
+ exec "e ".filename
+ exec linenum
+ let g:Remote_Server = 1
+ normal! zv
+ return
+ endif
+
+ let i = 1
+ let server = s:Strntok(servers, "\n", i)
+ let targetServer = v:servername
+
+ while server != ''
+ " Find out if there was any server which was used by remoteOpen before
+ " this. If a new gvim session was ever started via remoteOpen, then
+ " g:Remote_Server will be set.
+ if remote_expr(server, 'exists("g:Remote_Server")')
+ let targetServer = server
+ endif
+
+ " Ask each server if that file is being edited by them.
+ let bufnum = remote_expr(server, "bufnr('".filename."')")
+ " If it is...
+ if bufnum != -1
+ " ask the server to edit that file and come to the foreground.
+ " set a variable g:Remote_Server to indicate that this server
+ " session has at least one file opened via RemoteOpen
+ let targetServer = server
+ break
+ end
+
+ let i = i + 1
+ let server = s:Strntok(servers, "\n", i)
+ endwhile
+
+ " If none of the servers have the file open, then open this file in the
+ " first server. This has the advantage if yap tries to make vim open
+ " multiple vims, then at least they will all be opened by the same gvim
+ " server.
+ call remote_send(targetServer,
+ \ "\<C-\>\<C-n>".
+ \ ":let g:Remote_Server = 1\<CR>".
+ \ ":drop ".filename."\<CR>".
+ \ ":".linenum."\<CR>zv"
+ \ )
+ call remote_foreground(targetServer)
+ " quit this vim session
+ if v:servername != targetServer
+ q
+ endif
+endfunction " }}}
+" RemoteInsert: inserts a \cite'ation remotely (if possible) {{{
+" Description:
+function! RemoteInsert(...)
+
+ let citation = matchstr(argv(0), "\\[InsText('.cite{\\zs.\\{-}\\ze}');\\]")
+ if citation == ""
+ q
+ endif
+
+ " Otherwise, loop through all available servers
+ let servers = serverlist()
+
+ let i = 1
+ let server = s:Strntok(servers, "\n", i)
+ let targetServer = v:servername
+
+ while server != ''
+ if remote_expr(server, 'exists("g:Remote_WaitingForCite")')
+ call remote_send(server, citation . "\<CR>")
+ call remote_foreground(server)
+ if v:servername != server
+ q
+ else
+ return
+ endif
+ endif
+
+ let i = i + 1
+ let server = s:Strntok(servers, "\n", i)
+ endwhile
+
+ q
+
+endfunction " }}}
+" Strntok: extract the n^th token from a list {{{
+" example: Strntok('1,23,3', ',', 2) = 23
+fun! <SID>Strntok(s, tok, n)
+ return matchstr( a:s.a:tok[0], '\v(\zs([^'.a:tok.']*)\ze['.a:tok.']){'.a:n.'}')
+endfun
+
+" }}}
+
+" vim:ft=vim:ts=4:sw=4:noet:fdm=marker:commentstring=\"\ %s:nowrap
diff --git a/files/.vim/plugin/snipMate.vim b/files/.vim/plugin/snipMate.vim
new file mode 100755
index 0000000..f79fe1d
--- /dev/null
+++ b/files/.vim/plugin/snipMate.vim
@@ -0,0 +1,190 @@
+" File: snipMate.vim
+" Author: Michael Sanders
+" Version: 0.79
+" Description: snipMate.vim implements some of TextMate's snippets features in
+" Vim. A snippet is a piece of often-typed text that you can
+" insert into your document using a trigger word followed by a "<tab>".
+"
+" For more help see snipMate.txt; you can do this by using:
+" :helptags ~/.vim/doc
+" :h snipMate.txt
+
+if exists('loaded_snips') || &cp || version < 700
+ finish
+endif
+let loaded_snips = 1
+if !exists('snips_author') | let snips_author = 'Me' | endif
+
+au BufRead,BufNewFile *.snippets\= set ft=snippet
+au FileType snippet setl noet fdm=indent
+
+let s:snippets = {} | let s:multi_snips = {}
+
+if !exists('snippets_dir')
+ let snippets_dir = substitute(globpath(&rtp, 'snippets/'), "\n", ',', 'g')
+endif
+
+fun! MakeSnip(scope, trigger, content, ...)
+ let multisnip = a:0 && a:1 != ''
+ let var = multisnip ? 's:multi_snips' : 's:snippets'
+ if !has_key({var}, a:scope) | let {var}[a:scope] = {} | endif
+ if !has_key({var}[a:scope], a:trigger)
+ let {var}[a:scope][a:trigger] = multisnip ? [[a:1, a:content]] : a:content
+ elseif multisnip | let {var}[a:scope][a:trigger] += [[a:1, a:content]]
+ else
+ echom 'Warning in snipMate.vim: Snippet '.a:trigger.' is already defined.'
+ \ .' See :h multi_snip for help on snippets with multiple matches.'
+ endif
+endf
+
+fun! ExtractSnips(dir, ft)
+ for path in split(globpath(a:dir, '*'), "\n")
+ if isdirectory(path)
+ let pathname = fnamemodify(path, ':t')
+ for snipFile in split(globpath(path, '*.snippet'), "\n")
+ call s:ProcessFile(snipFile, a:ft, pathname)
+ endfor
+ elseif fnamemodify(path, ':e') == 'snippet'
+ call s:ProcessFile(path, a:ft)
+ endif
+ endfor
+endf
+
+" Processes a single-snippet file; optionally add the name of the parent
+" directory for a snippet with multiple matches.
+fun s:ProcessFile(file, ft, ...)
+ let keyword = fnamemodify(a:file, ':t:r')
+ if keyword == '' | return | endif
+ try
+ let text = join(readfile(a:file), "\n")
+ catch /E484/
+ echom "Error in snipMate.vim: couldn't read file: ".a:file
+ endtry
+ return a:0 ? MakeSnip(a:ft, a:1, text, keyword)
+ \ : MakeSnip(a:ft, keyword, text)
+endf
+
+fun! ExtractSnipsFile(file, ft)
+ if !filereadable(a:file) | return | endif
+ let text = readfile(a:file)
+ let inSnip = 0
+ for line in text + ["\n"]
+ if inSnip && (line == '' || strpart(line, 0, 1) == "\t")
+ let content .= strpart(line, 1)."\n"
+ continue
+ elseif inSnip
+ call MakeSnip(a:ft, trigger, content[:-2], name)
+ let inSnip = 0
+ endif
+
+ if stridx(line, 'snippet') == 0
+ let inSnip = 1
+ let trigger = strpart(line, 8)
+ let name = ''
+ let space = stridx(trigger, ' ') + 1
+ if space " Process multi snip
+ let name = strpart(trigger, space)
+ let trigger = strpart(trigger, 0, space - 1)
+ endif
+ let content = ''
+ endif
+ endfor
+endf
+
+fun! ResetSnippets()
+ let s:snippets = {} | let s:multi_snips = {} | let g:did_ft = {}
+endf
+
+let g:did_ft = {}
+fun! GetSnippets(dir, filetypes)
+ for ft in split(a:filetypes, '\.')
+ if has_key(g:did_ft, ft) | continue | endif
+ call s:DefineSnips(a:dir, ft, ft)
+ if ft == 'objc' || ft == 'cpp' || ft == 'cs'
+ call s:DefineSnips(a:dir, 'c', ft)
+ elseif ft == 'xhtml'
+ call s:DefineSnips(a:dir, 'html', 'xhtml')
+ endif
+ let g:did_ft[ft] = 1
+ endfor
+endf
+
+" Define "aliasft" snippets for the filetype "realft".
+fun s:DefineSnips(dir, aliasft, realft)
+ for path in split(globpath(a:dir, a:aliasft.'/')."\n".
+ \ globpath(a:dir, a:aliasft.'-*/'), "\n")
+ call ExtractSnips(path, a:realft)
+ endfor
+ for path in split(globpath(a:dir, a:aliasft.'.snippets')."\n".
+ \ globpath(a:dir, a:aliasft.'-*.snippets'), "\n")
+ call ExtractSnipsFile(path, a:realft)
+ endfor
+endf
+
+fun! TriggerSnippet()
+ if exists('g:SuperTabMappingForward')
+ if g:SuperTabMappingForward == "<tab>"
+ let SuperTabKey = "\<c-n>"
+ elseif g:SuperTabMappingBackward == "<tab>"
+ let SuperTabKey = "\<c-p>"
+ endif
+ endif
+
+ if pumvisible() " Update snippet if completion is used, or deal with supertab
+ if exists('SuperTabKey')
+ call feedkeys(SuperTabKey) | return ''
+ endif
+ call feedkeys("\<esc>a", 'n') " Close completion menu
+ call feedkeys("\<tab>") | return ''
+ endif
+
+ if exists('g:snipPos') | return snipMate#jumpTabStop() | endif
+
+ let word = matchstr(getline('.'), '\S\+\%'.col('.').'c')
+ for scope in [bufnr('%')] + split(&ft, '\.') + ['_']
+ let [trigger, snippet] = s:GetSnippet(word, scope)
+ " If word is a trigger for a snippet, delete the trigger & expand
+ " the snippet.
+ if snippet != ''
+ let col = col('.') - len(trigger)
+ sil exe 's/\V'.escape(trigger, '/').'\%#//'
+ return snipMate#expandSnip(snippet, col)
+ endif
+ endfor
+
+ if exists('SuperTabKey')
+ call feedkeys(SuperTabKey)
+ return ''
+ endif
+ return "\<tab>"
+endf
+
+" Check if word under cursor is snippet trigger; if it isn't, try checking if
+" the text after non-word characters is (e.g. check for "foo" in "bar.foo")
+fun s:GetSnippet(word, scope)
+ let word = a:word | let snippet = ''
+ while snippet == ''
+ if exists('s:snippets["'.a:scope.'"]["'.escape(word, '\"').'"]')
+ let snippet = s:snippets[a:scope][word]
+ elseif exists('s:multi_snips["'.a:scope.'"]["'.escape(word, '\"').'"]')
+ let snippet = s:ChooseSnippet(a:scope, word)
+ else
+ if match(word, '\W') == -1 | break | endif
+ let word = substitute(word, '.\{-}\W', '', '')
+ endif
+ endw
+ return [word, snippet]
+endf
+
+fun s:ChooseSnippet(scope, trigger)
+ let snippet = []
+ let i = 1
+ for snip in s:multi_snips[a:scope][a:trigger]
+ let snippet += [i.'. '.snip[0]]
+ let i += 1
+ endfor
+ if i == 2 | return s:multi_snips[a:scope][a:trigger][0][1] | endif
+ let num = inputlist(snippet) - 1
+ return num == -1 ? '' : s:multi_snips[a:scope][a:trigger][num][1]
+endf
+" vim:noet:sw=4:ts=4:ft=vim
diff --git a/files/.vim/plugin/sqlplus.vim b/files/.vim/plugin/sqlplus.vim
new file mode 100755
index 0000000..4360fb4
--- /dev/null
+++ b/files/.vim/plugin/sqlplus.vim
@@ -0,0 +1,257 @@
+" sqlplus.vim
+" author: Jamis Buck (jgb3@email.byu.edu)
+" version: 1.2.3
+"
+" This file contains routines that may be used to execute SQL queries and describe
+" tables from within VIM. It depends on SQL*Plus. You must have $ORACLE_HOME
+" $ORACLE_SID set in your environment, although you can explicitly set the
+" database name to use with the :DB <db-name> command.
+"
+" In command mode:
+" <F8>: execute the SELECT query under your cursor. The query must begin with
+" the "select" keyword and end with a ";"
+" <Leader><F8>: prompt for an SQL command/query to execute.
+" <F9>: treat the identifier under the cursor as a table name, and do a 'describe'
+" on it.
+" <F10>: prompt for a table to describe.
+" <F11>: set the current SQL*Plus username and password
+" <Leader>sb: open an empty buffer in a new window to enter SQL commands in
+" <Leader>ss: execute the (one-line) query on the current line
+" <Leader>se: execute the query under the cursor (as <F8>)
+" <Leader>st: describe the table under the cursor (as <F9>)
+" <Leader>sc: open the user's common SQL buffer (g:sqlplus_common_buffer) in a
+" new window.
+"
+" :Select <...> -- execute the given Select query.
+" :Update <...> -- execute the given Update command.
+" :Delete <...> -- execute the given Delete command
+" :DB <db-name> -- set the database name to <db-name>
+" :SQL <...> -- open a blank SQL buffer in a new window, or if a filename is
+" specified, open the given file in a new window.
+"
+" In visual mode:
+" <F8>: execute the selected query
+"
+" If queries contain bind variables, you will be prompted to give a value for
+" each one. if the value is a string, you must explicitly put quotes around it.
+" If the query contains an INTO clause, it is removed before executing.
+"
+" You will be prompted for your user-name and password the first time you access
+" one of these functions during a session. After that, your user-id and password
+" will be remembered until the session ends.
+"
+" The results of the query/command are displayed in a separate window.
+"
+" You can specify the values of the following global variables in your .vimrc
+" file, to alter the behavior of this plugin:
+"
+" g:sqlplus_userid -- the user-id to log in to the database as. If this
+" is specified, g:sqlplus_passwd must be given as well, which is the
+" password to use. Default: ""
+" g:sqlplus_path -- the path the the SQL*Plus executable, including any
+" command line options. Default: $ORACLE_HOME . "/bin/sqlplus -s"
+" g:sqlplus_common_commands -- any SQL*Plus commands that should be
+" executed every time SQL*Plus is invoked.
+" Default: "set pagesize 10000\nset wrap off\nset linesize 9999\n"
+" g:sqlplus_common_buffer -- the name of a file that will contain
+" common SQL queries and expressions, that may be opened via the
+" <Leader>sc command.
+" g:sqlplus_db -- the name of the database to connect to. This variable
+" may also be modified via the :DB command.
+"
+" ------------------------------------------------------------------------------
+" Thanks to:
+" Matt Kunze (kunzem@optimiz.com) for getting this script to work under
+" Windows
+" ------------------------------------------------------------------------------
+
+
+" Global variables (may be set in ~/.vimrc) {{{1
+if !exists( "g:sqlplus_userid" )
+ let g:sqlplus_userid = ""
+ let g:sqlplus_passwd = ""
+endif
+if !exists( "g:sqlplus_path" )
+ let g:sqlplus_path = $ORACLE_HOME . "/bin/sqlplus -s "
+endif
+if !exists( "g:sqlplus_common_commands" )
+ let g:sqlplus_common_commands = "set pagesize 10000\nset wrap off\nset linesize 9999\n"
+endif
+if !exists( "g:sqlplus_common_buffer" )
+ let g:sqlplus_common_buffer = "~/.vim_sql"
+endif
+if !exists( "g:sqlplus_db" )
+ let g:sqlplus_db = $ORACLE_SID
+endif
+"}}}
+
+function! AE_getSQLPlusUIDandPasswd( force ) "{{{1
+ if g:sqlplus_userid == "" || a:force != 0
+ if g:sqlplus_userid == ""
+ if has("win32")
+ let l:userid = ''
+ else
+ let l:userid = substitute( system( "whoami" ), "\n", "", "g" )
+ endif
+ else
+ let l:userid = g:sqlplus_userid
+ endif
+ let g:sqlplus_userid = input( "Please enter your SQL*Plus user-id: ", l:userid )
+ let g:sqlplus_passwd = inputsecret( "Please enter your SQL*Plus password: " )
+ let g:sqlplus_db = input( "Please enter your database name: ", g:sqlplus_db )
+ endif
+endfunction "}}}
+
+function! AE_configureOutputWindow() "{{{1
+ set ts=8 buftype=nofile nowrap sidescroll=5 listchars+=precedes:<,extends:>
+ normal $G
+ while getline(".") == ""
+ normal dd
+ endwhile
+ normal 1G
+ let l:newheight = line("$")
+ if l:newheight < winheight(0)
+ exe "resize " . l:newheight
+ endif
+endfunction "}}}
+
+function! AE_configureSqlBuffer() "{{{1
+ set syn=sql
+endfunction "}}}
+
+function! AE_describeTable( tableName ) "{{{1
+ let l:cmd = "prompt DESCRIBING TABLE '" . a:tableName . "'\ndesc " . a:tableName
+ call AE_execQuery( l:cmd )
+endfunction "}}}
+
+function! AE_describeTableUnderCursor() "{{{1
+ normal viw"zy
+ call AE_describeTable( @z )
+endfunction "}}}
+
+function! AE_describeTablePrompt() "{{{1
+ let l:tablename = input( "Please enter the name of the table to describe: " )
+ call AE_describeTable( l:tablename )
+endfunction "}}}
+
+function! AE_execQuery( sql_query ) "{{{1
+ call AE_getSQLPlusUIDandPasswd( 0 )
+ new
+ let l:tmpfile = tempname() . ".sql"
+ let l:oldo = @o
+ let @o="i" . g:sqlplus_common_commands . a:sql_query
+ let l:pos = match( @o, ";$" )
+ if l:pos < 0
+ let @o=@o . ";"
+ endif
+ let @o=@o . "\n"
+ normal @o
+ let @o=l:oldo
+ exe "silent write " . l:tmpfile
+ close
+ new
+ let l:cmd = g:sqlplus_path . g:sqlplus_userid . "/" . g:sqlplus_passwd . "@" . g:sqlplus_db
+ let l:cmd = l:cmd . " @" . l:tmpfile
+ exe "1,$!" . l:cmd
+ call AE_configureOutputWindow()
+ call delete( l:tmpfile )
+endfunction "}}}
+
+function! AE_promptQuery() "{{{1
+ let l:sqlquery = input( "SQL Query: " )
+ call AE_execQuery( l:sqlquery )
+endfunction "}}}
+
+function! AE_resetPassword() "{{{1
+ let g:sqlplus_userid = ""
+ let g:sqlplus_passwd = ""
+endfunction "}}}
+
+function! AE_execLiteralQuery( sql_query ) "{{{1
+ let l:query = substitute( a:sql_query, '\c\<INTO\>.*\<FROM\>', 'FROM', 'g' )
+
+ let l:idx = stridx( l:query, "\n" )
+ while l:idx >= 0
+ let l:query = strpart( l:query, 0, l:idx ) . " " . strpart( l:query, l:idx+1 )
+ let l:idx = stridx( l:query, "\n" )
+ endwhile
+
+ let l:var = matchstr( l:query, ':\h\w*' )
+ while l:var > ""
+ let l:var_val = input( "Enter value for " . strpart( l:var, 1 ) . ": " )
+ let l:query = substitute( l:query, l:var . '\>', l:var_val, 'g' )
+ let l:var = matchstr( l:query, ':\h\w*' )
+ endwhile
+
+ call AE_execQuery( l:query )
+endfunction "}}}
+
+function! AE_execQueryUnderCursor() "{{{1
+ exe "silent norm! ?\\c[^.]*\\<\\(select\\|update\\|delete\\)\\>\nv/;\nh\"zy"
+ noh
+ call AE_execLiteralQuery( @z )
+endfunction "}}}
+
+function! AE_execWholeScript() "{{{1
+ "exe "silent norm! :%y z"
+ exe "%y z"
+ call AE_getSQLPlusUIDandPasswd( 0 )
+ new
+ let l:tmpfile = tempname() . ".sql"
+ let @z="i" . @z . "\n"
+ normal @z
+ exe "silent write " . l:tmpfile
+ close
+ new
+ let l:cmd = g:sqlplus_path . g:sqlplus_userid . "/" . g:sqlplus_passwd . "@" . g:sqlplus_db
+ let l:cmd = l:cmd . " @" . l:tmpfile
+ exe "1,$!" . l:cmd
+ call AE_configureOutputWindow()
+ call delete( l:tmpfile )
+endfunction "}}}
+
+
+function! AE_openSqlBuffer( fname ) "{{{1
+ exe "new " . a:fname
+ call AE_configureSqlBuffer()
+endfunction "}}}
+
+function! AE_openEmptySqlBuffer() "{{{1
+ call AE_openSqlBuffer( "" )
+endfunction "}}}
+
+
+" command-mode mappings {{{1
+map <Leader>sb :call AE_openEmptySqlBuffer()<CR>
+map <Leader>ss "zyy:call AE_execLiteralQuery( @z )<CR>
+map <Leader>se :call AE_execQueryUnderCursor()<CR>
+map <Leader>st :call AE_describeTableUnderCursor()<CR>
+exe "map <Leader>sc :call AE_openSqlBuffer( \"" . g:sqlplus_common_buffer . "\" )<CR>"
+
+map <F7> :call AE_execWholeScript()<CR>
+map <F8> :call AE_execQueryUnderCursor()<CR>
+map <Leader><F8> :call AE_promptQuery()<CR>
+map <F9> :call AE_describeTableUnderCursor()<CR>
+map <F10> :call AE_describeTablePrompt()<CR>
+map <F11> :call AE_getSQLPlusUIDandPasswd(1)<CR>
+"}}}
+
+" visual mode mappings {{{1
+vmap <F8> "zy:call AE_execLiteralQuery( @z )<CR>
+"}}}
+
+" commands {{{1
+command! -nargs=+ Select :call AE_execQuery( "select <a>" )
+command! -nargs=+ Update :call AE_execQuery( "update <a>" )
+command! -nargs=+ Delete :call AE_execQuery( "delete <a>" )
+command! -nargs=1 DB :let g:sqlplus_db="<args>"
+command! -nargs=? SQL :call AE_openSqlBuffer( "<args>" )
+
+:menu Oracle.Execute\ whole\ script<Tab>F7 :call AE_execWholeScript()<CR>
+:menu Oracle.Execute\ query\ under\ cursor<Tab>F8 :call AE_execQueryUnderCursor()<CR>
+:menu Oracle.Prompt\ for\ query<Tab>\\F8 :call AE_promptQuery()<CR>
+:menu Oracle.Describe\ table\ under\ cursor<Tab>F09 :call AE_describeTableUnderCursor()<CR>
+:menu Oracle.Prompt\ for\ table\ to\ describe<Tab>F10 :call AE_describeTablePrompt()<CR>
+:menu Oracle.Change\ connect\ information<Tab>F11 :call AE_getSQLPlusUIDandPasswd(1)<CR>
+
+"}}}
diff --git a/files/.vim/plugin/taglist.vim b/files/.vim/plugin/taglist.vim
new file mode 100755
index 0000000..59901f6
--- /dev/null
+++ b/files/.vim/plugin/taglist.vim
@@ -0,0 +1,4546 @@
+" File: taglist.vim
+" Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com)
+" Version: 4.5
+" Last Modified: September 21, 2007
+" Copyright: Copyright (C) 2002-2007 Yegappan Lakshmanan
+" 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.
+"
+" The "Tag List" plugin is a source code browser plugin for Vim and provides
+" an overview of the structure of the programming language files and allows
+" you to efficiently browse through source code files for different
+" programming languages. You can visit the taglist plugin home page for more
+" information:
+"
+" http://vim-taglist.sourceforge.net
+"
+" You can subscribe to the taglist mailing list to post your questions
+" or suggestions for improvement or to report bugs. Visit the following
+" page for subscribing to the mailing list:
+"
+" http://groups.yahoo.com/group/taglist/
+"
+" For more information about using this plugin, after installing the
+" taglist plugin, use the ":help taglist" command.
+"
+" Installation
+" ------------
+" 1. Download the taglist.zip file and unzip the files to the $HOME/.vim
+" or the $HOME/vimfiles or the $VIM/vimfiles directory. This should
+" unzip the following two files (the directory structure should be
+" preserved):
+"
+" plugin/taglist.vim - main taglist plugin file
+" doc/taglist.txt - documentation (help) file
+"
+" Refer to the 'add-plugin', 'add-global-plugin' and 'runtimepath'
+" Vim help pages for more details about installing Vim plugins.
+" 2. Change to the $HOME/.vim/doc or $HOME/vimfiles/doc or
+" $VIM/vimfiles/doc directory, start Vim and run the ":helptags ."
+" command to process the taglist help file.
+" 3. If the exuberant ctags utility is not present in your PATH, then set the
+" Tlist_Ctags_Cmd variable to point to the location of the exuberant ctags
+" utility (not to the directory) in the .vimrc file.
+" 4. If you are running a terminal/console version of Vim and the
+" terminal doesn't support changing the window width then set the
+" 'Tlist_Inc_Winwidth' variable to 0 in the .vimrc file.
+" 5. Restart Vim.
+" 6. You can now use the ":TlistToggle" command to open/close the taglist
+" window. You can use the ":help taglist" command to get more
+" information about using the taglist plugin.
+"
+" ****************** Do not modify after this line ************************
+
+" Line continuation used here
+let s:cpo_save = &cpo
+set cpo&vim
+
+if !exists('loaded_taglist')
+ " First time loading the taglist plugin
+ "
+ " To speed up the loading of Vim, the taglist plugin uses autoload
+ " mechanism to load the taglist functions.
+ " Only define the configuration variables, user commands and some
+ " auto-commands and finish sourcing the file
+
+ " The taglist plugin requires the built-in Vim system() function. If this
+ " function is not available, then don't load the plugin.
+ if !exists('*system')
+ echomsg 'Taglist: Vim system() built-in function is not available. ' .
+ \ 'Plugin is not loaded.'
+ let loaded_taglist = 'no'
+ let &cpo = s:cpo_save
+ finish
+ endif
+
+ " Location of the exuberant ctags tool
+ if !exists('Tlist_Ctags_Cmd')
+ if executable('exuberant-ctags')
+ " On Debian Linux, exuberant ctags is installed
+ " as exuberant-ctags
+ let Tlist_Ctags_Cmd = 'exuberant-ctags'
+ elseif executable('exctags')
+ " On Free-BSD, exuberant ctags is installed as exctags
+ let Tlist_Ctags_Cmd = 'exctags'
+ elseif executable('ctags')
+ let Tlist_Ctags_Cmd = 'ctags'
+ elseif executable('ctags.exe')
+ let Tlist_Ctags_Cmd = 'ctags.exe'
+ elseif executable('tags')
+ let Tlist_Ctags_Cmd = 'tags'
+ else
+ echomsg 'Taglist: Exuberant ctags (http://ctags.sf.net) ' .
+ \ 'not found in PATH. Plugin is not loaded.'
+ " Skip loading the plugin
+ let loaded_taglist = 'no'
+ let &cpo = s:cpo_save
+ finish
+ endif
+ endif
+
+
+ " Automatically open the taglist window on Vim startup
+ if !exists('Tlist_Auto_Open')
+ let Tlist_Auto_Open = 0
+ endif
+
+ " When the taglist window is toggle opened, move the cursor to the
+ " taglist window
+ if !exists('Tlist_GainFocus_On_ToggleOpen')
+ let Tlist_GainFocus_On_ToggleOpen = 0
+ endif
+
+ " Process files even when the taglist window is not open
+ if !exists('Tlist_Process_File_Always')
+ let Tlist_Process_File_Always = 0
+ endif
+
+ if !exists('Tlist_Show_Menu')
+ let Tlist_Show_Menu = 0
+ endif
+
+ " Tag listing sort type - 'name' or 'order'
+ if !exists('Tlist_Sort_Type')
+ let Tlist_Sort_Type = 'order'
+ endif
+
+ " Tag listing window split (horizontal/vertical) control
+ if !exists('Tlist_Use_Horiz_Window')
+ let Tlist_Use_Horiz_Window = 0
+ endif
+
+ " Open the vertically split taglist window on the left or on the right
+ " side. This setting is relevant only if Tlist_Use_Horiz_Window is set to
+ " zero (i.e. only for vertically split windows)
+ if !exists('Tlist_Use_Right_Window')
+ let Tlist_Use_Right_Window = 0
+ endif
+
+ " Increase Vim window width to display vertically split taglist window.
+ " For MS-Windows version of Vim running in a MS-DOS window, this must be
+ " set to 0 otherwise the system may hang due to a Vim limitation.
+ if !exists('Tlist_Inc_Winwidth')
+ if (has('win16') || has('win95')) && !has('gui_running')
+ let Tlist_Inc_Winwidth = 0
+ else
+ let Tlist_Inc_Winwidth = 1
+ endif
+ endif
+
+ " Vertically split taglist window width setting
+ if !exists('Tlist_WinWidth')
+ let Tlist_WinWidth = 30
+ endif
+
+ " Horizontally split taglist window height setting
+ if !exists('Tlist_WinHeight')
+ let Tlist_WinHeight = 10
+ endif
+
+ " Display tag prototypes or tag names in the taglist window
+ if !exists('Tlist_Display_Prototype')
+ let Tlist_Display_Prototype = 0
+ endif
+
+ " Display tag scopes in the taglist window
+ if !exists('Tlist_Display_Tag_Scope')
+ let Tlist_Display_Tag_Scope = 1
+ endif
+
+ " Use single left mouse click to jump to a tag. By default this is disabled.
+ " Only double click using the mouse will be processed.
+ if !exists('Tlist_Use_SingleClick')
+ let Tlist_Use_SingleClick = 0
+ endif
+
+ " Control whether additional help is displayed as part of the taglist or
+ " not. Also, controls whether empty lines are used to separate the tag
+ " tree.
+ if !exists('Tlist_Compact_Format')
+ let Tlist_Compact_Format = 0
+ endif
+
+ " Exit Vim if only the taglist window is currently open. By default, this is
+ " set to zero.
+ if !exists('Tlist_Exit_OnlyWindow')
+ let Tlist_Exit_OnlyWindow = 0
+ endif
+
+ " Automatically close the folds for the non-active files in the taglist
+ " window
+ if !exists('Tlist_File_Fold_Auto_Close')
+ let Tlist_File_Fold_Auto_Close = 0
+ endif
+
+ " Close the taglist window when a tag is selected
+ if !exists('Tlist_Close_On_Select')
+ let Tlist_Close_On_Select = 0
+ endif
+
+ " Automatically update the taglist window to display tags for newly
+ " edited files
+ if !exists('Tlist_Auto_Update')
+ let Tlist_Auto_Update = 1
+ endif
+
+ " Automatically highlight the current tag
+ if !exists('Tlist_Auto_Highlight_Tag')
+ let Tlist_Auto_Highlight_Tag = 1
+ endif
+
+ " Automatically highlight the current tag on entering a buffer
+ if !exists('Tlist_Highlight_Tag_On_BufEnter')
+ let Tlist_Highlight_Tag_On_BufEnter = 1
+ endif
+
+ " Enable fold column to display the folding for the tag tree
+ if !exists('Tlist_Enable_Fold_Column')
+ let Tlist_Enable_Fold_Column = 1
+ endif
+
+ " Display the tags for only one file in the taglist window
+ if !exists('Tlist_Show_One_File')
+ let Tlist_Show_One_File = 0
+ endif
+
+ if !exists('Tlist_Max_Submenu_Items')
+ let Tlist_Max_Submenu_Items = 20
+ endif
+
+ if !exists('Tlist_Max_Tag_Length')
+ let Tlist_Max_Tag_Length = 10
+ endif
+
+ " Do not change the name of the taglist title variable. The winmanager
+ " plugin relies on this name to determine the title for the taglist
+ " plugin.
+ let TagList_title = "__Tag_List__"
+
+ " Taglist debug messages
+ let s:tlist_msg = ''
+
+ " Define the taglist autocommand to automatically open the taglist window
+ " on Vim startup
+ if g:Tlist_Auto_Open
+ autocmd VimEnter * nested call s:Tlist_Window_Check_Auto_Open()
+ endif
+
+ " Refresh the taglist
+ if g:Tlist_Process_File_Always
+ autocmd BufEnter * call s:Tlist_Refresh()
+ endif
+
+ if g:Tlist_Show_Menu
+ autocmd GUIEnter * call s:Tlist_Menu_Init()
+ endif
+
+ " When the taglist buffer is created when loading a Vim session file,
+ " the taglist buffer needs to be initialized. The BufFilePost event
+ " is used to handle this case.
+ autocmd BufFilePost __Tag_List__ call s:Tlist_Vim_Session_Load()
+
+ " Define the user commands to manage the taglist window
+ command! -nargs=0 -bar TlistToggle call s:Tlist_Window_Toggle()
+ command! -nargs=0 -bar TlistOpen call s:Tlist_Window_Open()
+ " For backwards compatiblity define the Tlist command
+ command! -nargs=0 -bar Tlist TlistToggle
+ command! -nargs=+ -complete=file TlistAddFiles
+ \ call s:Tlist_Add_Files(<f-args>)
+ command! -nargs=+ -complete=dir TlistAddFilesRecursive
+ \ call s:Tlist_Add_Files_Recursive(<f-args>)
+ command! -nargs=0 -bar TlistClose call s:Tlist_Window_Close()
+ command! -nargs=0 -bar TlistUpdate call s:Tlist_Update_Current_File()
+ command! -nargs=0 -bar TlistHighlightTag call s:Tlist_Window_Highlight_Tag(
+ \ fnamemodify(bufname('%'), ':p'), line('.'), 2, 1)
+ " For backwards compatiblity define the TlistSync command
+ command! -nargs=0 -bar TlistSync TlistHighlightTag
+ command! -nargs=* -complete=buffer TlistShowPrototype
+ \ echo Tlist_Get_Tag_Prototype_By_Line(<f-args>)
+ command! -nargs=* -complete=buffer TlistShowTag
+ \ echo Tlist_Get_Tagname_By_Line(<f-args>)
+ command! -nargs=* -complete=file TlistSessionLoad
+ \ call s:Tlist_Session_Load(<q-args>)
+ command! -nargs=* -complete=file TlistSessionSave
+ \ call s:Tlist_Session_Save(<q-args>)
+ command! -bar TlistLock let Tlist_Auto_Update=0
+ command! -bar TlistUnlock let Tlist_Auto_Update=1
+
+ " Commands for enabling/disabling debug and to display debug messages
+ command! -nargs=? -complete=file -bar TlistDebug
+ \ call s:Tlist_Debug_Enable(<q-args>)
+ command! -nargs=0 -bar TlistUndebug call s:Tlist_Debug_Disable()
+ command! -nargs=0 -bar TlistMessages call s:Tlist_Debug_Show()
+
+ " Define autocommands to autoload the taglist plugin when needed.
+
+ " Trick to get the current script ID
+ map <SID>xx <SID>xx
+ let s:tlist_sid = substitute(maparg('<SID>xx'), '<SNR>\(\d\+_\)xx$',
+ \ '\1', '')
+ unmap <SID>xx
+
+ exe 'autocmd FuncUndefined *' . s:tlist_sid . 'Tlist_* source ' .
+ \ escape(expand('<sfile>'), ' ')
+ exe 'autocmd FuncUndefined *' . s:tlist_sid . 'Tlist_Window_* source ' .
+ \ escape(expand('<sfile>'), ' ')
+ exe 'autocmd FuncUndefined *' . s:tlist_sid . 'Tlist_Menu_* source ' .
+ \ escape(expand('<sfile>'), ' ')
+ exe 'autocmd FuncUndefined Tlist_* source ' .
+ \ escape(expand('<sfile>'), ' ')
+ exe 'autocmd FuncUndefined TagList_* source ' .
+ \ escape(expand('<sfile>'), ' ')
+
+ let loaded_taglist = 'fast_load_done'
+
+ if g:Tlist_Show_Menu && has('gui_running')
+ call s:Tlist_Menu_Init()
+ endif
+
+ " restore 'cpo'
+ let &cpo = s:cpo_save
+ finish
+endif
+
+if !exists('s:tlist_sid')
+ " Two or more versions of taglist plugin are installed. Don't
+ " load this version of the plugin.
+ finish
+endif
+
+unlet! s:tlist_sid
+
+if loaded_taglist != 'fast_load_done'
+ " restore 'cpo'
+ let &cpo = s:cpo_save
+ finish
+endif
+
+" Taglist plugin functionality is available
+let loaded_taglist = 'available'
+
+"------------------- end of user configurable options --------------------
+
+" Default language specific settings for supported file types and tag types
+"
+" Variable name format:
+"
+" s:tlist_def_{vim_ftype}_settings
+"
+" vim_ftype - Filetype detected by Vim
+"
+" Value format:
+"
+" <ctags_ftype>;<flag>:<name>;<flag>:<name>;...
+"
+" ctags_ftype - File type supported by exuberant ctags
+" flag - Flag supported by exuberant ctags to generate a tag type
+" name - Name of the tag type used in the taglist window to display the
+" tags of this type
+"
+
+" assembly language
+let s:tlist_def_asm_settings = 'asm;d:define;l:label;m:macro;t:type'
+
+" aspperl language
+let s:tlist_def_aspperl_settings = 'asp;f:function;s:sub;v:variable'
+
+" aspvbs language
+let s:tlist_def_aspvbs_settings = 'asp;f:function;s:sub;v:variable'
+
+" awk language
+let s:tlist_def_awk_settings = 'awk;f:function'
+
+" beta language
+let s:tlist_def_beta_settings = 'beta;f:fragment;s:slot;v:pattern'
+
+" c language
+let s:tlist_def_c_settings = 'c;d:macro;g:enum;s:struct;u:union;t:typedef;' .
+ \ 'v:variable;f:function'
+
+" c++ language
+let s:tlist_def_cpp_settings = 'c++;n:namespace;v:variable;d:macro;t:typedef;' .
+ \ 'c:class;g:enum;s:struct;u:union;f:function'
+
+" c# language
+let s:tlist_def_cs_settings = 'c#;d:macro;t:typedef;n:namespace;c:class;' .
+ \ 'E:event;g:enum;s:struct;i:interface;' .
+ \ 'p:properties;m:method'
+
+" cobol language
+let s:tlist_def_cobol_settings = 'cobol;d:data;f:file;g:group;p:paragraph;' .
+ \ 'P:program;s:section'
+
+" eiffel language
+let s:tlist_def_eiffel_settings = 'eiffel;c:class;f:feature'
+
+" erlang language
+let s:tlist_def_erlang_settings = 'erlang;d:macro;r:record;m:module;f:function'
+
+" expect (same as tcl) language
+let s:tlist_def_expect_settings = 'tcl;c:class;f:method;p:procedure'
+
+" fortran language
+let s:tlist_def_fortran_settings = 'fortran;p:program;b:block data;' .
+ \ 'c:common;e:entry;i:interface;k:type;l:label;m:module;' .
+ \ 'n:namelist;t:derived;v:variable;f:function;s:subroutine'
+
+" HTML language
+let s:tlist_def_html_settings = 'html;a:anchor;f:javascript function'
+
+" java language
+let s:tlist_def_java_settings = 'java;p:package;c:class;i:interface;' .
+ \ 'f:field;m:method'
+
+" javascript language
+let s:tlist_def_javascript_settings = 'javascript;f:function'
+
+" lisp language
+let s:tlist_def_lisp_settings = 'lisp;f:function'
+
+" lua language
+let s:tlist_def_lua_settings = 'lua;f:function'
+
+" makefiles
+let s:tlist_def_make_settings = 'make;m:macro'
+
+" pascal language
+let s:tlist_def_pascal_settings = 'pascal;f:function;p:procedure'
+
+" perl language
+let s:tlist_def_perl_settings = 'perl;c:constant;l:label;p:package;s:subroutine'
+
+" php language
+let s:tlist_def_php_settings = 'php;c:class;d:constant;v:variable;f:function'
+
+" python language
+let s:tlist_def_python_settings = 'python;c:class;m:member;f:function'
+
+" rexx language
+let s:tlist_def_rexx_settings = 'rexx;s:subroutine'
+
+" ruby language
+let s:tlist_def_ruby_settings = 'ruby;c:class;f:method;F:function;' .
+ \ 'm:singleton method'
+
+" scheme language
+let s:tlist_def_scheme_settings = 'scheme;s:set;f:function'
+
+" shell language
+let s:tlist_def_sh_settings = 'sh;f:function'
+
+" C shell language
+let s:tlist_def_csh_settings = 'sh;f:function'
+
+" Z shell language
+let s:tlist_def_zsh_settings = 'sh;f:function'
+
+" slang language
+let s:tlist_def_slang_settings = 'slang;n:namespace;f:function'
+
+" sml language
+let s:tlist_def_sml_settings = 'sml;e:exception;c:functor;s:signature;' .
+ \ 'r:structure;t:type;v:value;f:function'
+
+" sql language
+let s:tlist_def_sql_settings = 'sql;c:cursor;F:field;P:package;r:record;' .
+ \ 's:subtype;t:table;T:trigger;v:variable;f:function;p:procedure'
+
+" tcl language
+let s:tlist_def_tcl_settings = 'tcl;c:class;f:method;m:method;p:procedure'
+
+" vera language
+let s:tlist_def_vera_settings = 'vera;c:class;d:macro;e:enumerator;' .
+ \ 'f:function;g:enum;m:member;p:program;' .
+ \ 'P:prototype;t:task;T:typedef;v:variable;' .
+ \ 'x:externvar'
+
+"verilog language
+let s:tlist_def_verilog_settings = 'verilog;m:module;c:constant;P:parameter;' .
+ \ 'e:event;r:register;t:task;w:write;p:port;v:variable;f:function'
+
+" vim language
+let s:tlist_def_vim_settings = 'vim;a:autocmds;v:variable;f:function'
+
+" yacc language
+let s:tlist_def_yacc_settings = 'yacc;l:label'
+
+"------------------- end of language specific options --------------------
+
+" Vim window size is changed by the taglist plugin or not
+let s:tlist_winsize_chgd = -1
+" Taglist window is maximized or not
+let s:tlist_win_maximized = 0
+" Name of files in the taglist
+let s:tlist_file_names=''
+" Number of files in the taglist
+let s:tlist_file_count = 0
+" Number of filetypes supported by taglist
+let s:tlist_ftype_count = 0
+" Is taglist part of other plugins like winmanager or cream?
+let s:tlist_app_name = "none"
+" Are we displaying brief help text
+let s:tlist_brief_help = 1
+" List of files removed on user request
+let s:tlist_removed_flist = ""
+" Index of current file displayed in the taglist window
+let s:tlist_cur_file_idx = -1
+" Taglist menu is empty or not
+let s:tlist_menu_empty = 1
+
+" An autocommand is used to refresh the taglist window when entering any
+" buffer. We don't want to refresh the taglist window if we are entering the
+" file window from one of the taglist functions. The 'Tlist_Skip_Refresh'
+" variable is used to skip the refresh of the taglist window and is set
+" and cleared appropriately.
+let s:Tlist_Skip_Refresh = 0
+
+" Tlist_Window_Display_Help()
+function! s:Tlist_Window_Display_Help()
+ if s:tlist_app_name == "winmanager"
+ " To handle a bug in the winmanager plugin, add a space at the
+ " last line
+ call setline('$', ' ')
+ endif
+
+ if s:tlist_brief_help
+ " Add the brief help
+ call append(0, '" Press <F1> to display help text')
+ else
+ " Add the extensive help
+ call append(0, '" <enter> : Jump to tag definition')
+ call append(1, '" o : Jump to tag definition in new window')
+ call append(2, '" p : Preview the tag definition')
+ call append(3, '" <space> : Display tag prototype')
+ call append(4, '" u : Update tag list')
+ call append(5, '" s : Select sort field')
+ call append(6, '" d : Remove file from taglist')
+ call append(7, '" x : Zoom-out/Zoom-in taglist window')
+ call append(8, '" + : Open a fold')
+ call append(9, '" - : Close a fold')
+ call append(10, '" * : Open all folds')
+ call append(11, '" = : Close all folds')
+ call append(12, '" [[ : Move to the start of previous file')
+ call append(13, '" ]] : Move to the start of next file')
+ call append(14, '" q : Close the taglist window')
+ call append(15, '" <F1> : Remove help text')
+ endif
+endfunction
+
+" Tlist_Window_Toggle_Help_Text()
+" Toggle taglist plugin help text between the full version and the brief
+" version
+function! s:Tlist_Window_Toggle_Help_Text()
+ if g:Tlist_Compact_Format
+ " In compact display mode, do not display help
+ return
+ endif
+
+ " Include the empty line displayed after the help text
+ let brief_help_size = 1
+ let full_help_size = 16
+
+ setlocal modifiable
+
+ " Set report option to a huge value to prevent informational messages
+ " while deleting the lines
+ let old_report = &report
+ set report=99999
+
+ " Remove the currently highlighted tag. Otherwise, the help text
+ " might be highlighted by mistake
+ match none
+
+ " Toggle between brief and full help text
+ if s:tlist_brief_help
+ let s:tlist_brief_help = 0
+
+ " Remove the previous help
+ exe '1,' . brief_help_size . ' delete _'
+
+ " Adjust the start/end line numbers for the files
+ call s:Tlist_Window_Update_Line_Offsets(0, 1, full_help_size - brief_help_size)
+ else
+ let s:tlist_brief_help = 1
+
+ " Remove the previous help
+ exe '1,' . full_help_size . ' delete _'
+
+ " Adjust the start/end line numbers for the files
+ call s:Tlist_Window_Update_Line_Offsets(0, 0, full_help_size - brief_help_size)
+ endif
+
+ call s:Tlist_Window_Display_Help()
+
+ " Restore the report option
+ let &report = old_report
+
+ setlocal nomodifiable
+endfunction
+
+" Taglist debug support
+let s:tlist_debug = 0
+
+" File for storing the debug messages
+let s:tlist_debug_file = ''
+
+" Tlist_Debug_Enable
+" Enable logging of taglist debug messages.
+function! s:Tlist_Debug_Enable(...)
+ let s:tlist_debug = 1
+
+ " Check whether a valid file name is supplied.
+ if a:1 != ''
+ let s:tlist_debug_file = fnamemodify(a:1, ':p')
+
+ " Empty the log file
+ exe 'redir! > ' . s:tlist_debug_file
+ redir END
+
+ " Check whether the log file is present/created
+ if !filewritable(s:tlist_debug_file)
+ call s:Tlist_Warning_Msg('Taglist: Unable to create log file '
+ \ . s:tlist_debug_file)
+ let s:tlist_debug_file = ''
+ endif
+ endif
+endfunction
+
+" Tlist_Debug_Disable
+" Disable logging of taglist debug messages.
+function! s:Tlist_Debug_Disable(...)
+ let s:tlist_debug = 0
+ let s:tlist_debug_file = ''
+endfunction
+
+" Tlist_Debug_Show
+" Display the taglist debug messages in a new window
+function! s:Tlist_Debug_Show()
+ if s:tlist_msg == ''
+ call s:Tlist_Warning_Msg('Taglist: No debug messages')
+ return
+ endif
+
+ " Open a new window to display the taglist debug messages
+ new taglist_debug.txt
+ " Delete all the lines (if the buffer already exists)
+ silent! %delete _
+ " Add the messages
+ silent! put =s:tlist_msg
+ " Move the cursor to the first line
+ normal! gg
+endfunction
+
+" Tlist_Log_Msg
+" Log the supplied debug message along with the time
+function! s:Tlist_Log_Msg(msg)
+ if s:tlist_debug
+ if s:tlist_debug_file != ''
+ exe 'redir >> ' . s:tlist_debug_file
+ silent echon strftime('%H:%M:%S') . ': ' . a:msg . "\n"
+ redir END
+ else
+ " Log the message into a variable
+ " Retain only the last 3000 characters
+ let len = strlen(s:tlist_msg)
+ if len > 3000
+ let s:tlist_msg = strpart(s:tlist_msg, len - 3000)
+ endif
+ let s:tlist_msg = s:tlist_msg . strftime('%H:%M:%S') . ': ' .
+ \ a:msg . "\n"
+ endif
+ endif
+endfunction
+
+" Tlist_Warning_Msg()
+" Display a message using WarningMsg highlight group
+function! s:Tlist_Warning_Msg(msg)
+ echohl WarningMsg
+ echomsg a:msg
+ echohl None
+endfunction
+
+" Last returned file index for file name lookup.
+" Used to speed up file lookup
+let s:tlist_file_name_idx_cache = -1
+
+" Tlist_Get_File_Index()
+" Return the index of the specified filename
+function! s:Tlist_Get_File_Index(fname)
+ if s:tlist_file_count == 0 || a:fname == ''
+ return -1
+ endif
+
+ " If the new filename is same as the last accessed filename, then
+ " return that index
+ if s:tlist_file_name_idx_cache != -1 &&
+ \ s:tlist_file_name_idx_cache < s:tlist_file_count
+ if s:tlist_{s:tlist_file_name_idx_cache}_filename == a:fname
+ " Same as the last accessed file
+ return s:tlist_file_name_idx_cache
+ endif
+ endif
+
+ " First, check whether the filename is present
+ let s_fname = a:fname . "\n"
+ let i = stridx(s:tlist_file_names, s_fname)
+ if i == -1
+ let s:tlist_file_name_idx_cache = -1
+ return -1
+ endif
+
+ " Second, compute the file name index
+ let nl_txt = substitute(strpart(s:tlist_file_names, 0, i), "[^\n]", '', 'g')
+ let s:tlist_file_name_idx_cache = strlen(nl_txt)
+ return s:tlist_file_name_idx_cache
+endfunction
+
+" Last returned file index for line number lookup.
+" Used to speed up file lookup
+let s:tlist_file_lnum_idx_cache = -1
+
+" Tlist_Window_Get_File_Index_By_Linenum()
+" Return the index of the filename present in the specified line number
+" Line number refers to the line number in the taglist window
+function! s:Tlist_Window_Get_File_Index_By_Linenum(lnum)
+ call s:Tlist_Log_Msg('Tlist_Window_Get_File_Index_By_Linenum (' . a:lnum . ')')
+
+ " First try to see whether the new line number is within the range
+ " of the last returned file
+ if s:tlist_file_lnum_idx_cache != -1 &&
+ \ s:tlist_file_lnum_idx_cache < s:tlist_file_count
+ if a:lnum >= s:tlist_{s:tlist_file_lnum_idx_cache}_start &&
+ \ a:lnum <= s:tlist_{s:tlist_file_lnum_idx_cache}_end
+ return s:tlist_file_lnum_idx_cache
+ endif
+ endif
+
+ let fidx = -1
+
+ if g:Tlist_Show_One_File
+ " Displaying only one file in the taglist window. Check whether
+ " the line is within the tags displayed for that file
+ if s:tlist_cur_file_idx != -1
+ if a:lnum >= s:tlist_{s:tlist_cur_file_idx}_start
+ \ && a:lnum <= s:tlist_{s:tlist_cur_file_idx}_end
+ let fidx = s:tlist_cur_file_idx
+ endif
+
+ endif
+ else
+ " Do a binary search in the taglist
+ let left = 0
+ let right = s:tlist_file_count - 1
+
+ while left < right
+ let mid = (left + right) / 2
+
+ if a:lnum >= s:tlist_{mid}_start && a:lnum <= s:tlist_{mid}_end
+ let s:tlist_file_lnum_idx_cache = mid
+ return mid
+ endif
+
+ if a:lnum < s:tlist_{mid}_start
+ let right = mid - 1
+ else
+ let left = mid + 1
+ endif
+ endwhile
+
+ if left >= 0 && left < s:tlist_file_count
+ \ && a:lnum >= s:tlist_{left}_start
+ \ && a:lnum <= s:tlist_{left}_end
+ let fidx = left
+ endif
+ endif
+
+ let s:tlist_file_lnum_idx_cache = fidx
+
+ return fidx
+endfunction
+
+" Tlist_Exe_Cmd_No_Acmds
+" Execute the specified Ex command after disabling autocommands
+function! s:Tlist_Exe_Cmd_No_Acmds(cmd)
+ let old_eventignore = &eventignore
+ set eventignore=all
+ exe a:cmd
+ let &eventignore = old_eventignore
+endfunction
+
+" Tlist_Skip_File()
+" Check whether tag listing is supported for the specified file
+function! s:Tlist_Skip_File(filename, ftype)
+ " Skip buffers with no names and buffers with filetype not set
+ if a:filename == '' || a:ftype == ''
+ return 1
+ endif
+
+ " Skip files which are not supported by exuberant ctags
+ " First check whether default settings for this filetype are available.
+ " If it is not available, then check whether user specified settings are
+ " available. If both are not available, then don't list the tags for this
+ " filetype
+ let var = 's:tlist_def_' . a:ftype . '_settings'
+ if !exists(var)
+ let var = 'g:tlist_' . a:ftype . '_settings'
+ if !exists(var)
+ return 1
+ endif
+ endif
+
+ " Skip files which are not readable or files which are not yet stored
+ " to the disk
+ if !filereadable(a:filename)
+ return 1
+ endif
+
+ return 0
+endfunction
+
+" Tlist_User_Removed_File
+" Returns 1 if a file is removed by a user from the taglist
+function! s:Tlist_User_Removed_File(filename)
+ return stridx(s:tlist_removed_flist, a:filename . "\n") != -1
+endfunction
+
+" Tlist_Update_Remove_List
+" Update the list of user removed files from the taglist
+" add == 1, add the file to the removed list
+" add == 0, delete the file from the removed list
+function! s:Tlist_Update_Remove_List(filename, add)
+ if a:add
+ let s:tlist_removed_flist = s:tlist_removed_flist . a:filename . "\n"
+ else
+ let idx = stridx(s:tlist_removed_flist, a:filename . "\n")
+ let text_before = strpart(s:tlist_removed_flist, 0, idx)
+ let rem_text = strpart(s:tlist_removed_flist, idx)
+ let next_idx = stridx(rem_text, "\n")
+ let text_after = strpart(rem_text, next_idx + 1)
+
+ let s:tlist_removed_flist = text_before . text_after
+ endif
+endfunction
+
+" Tlist_FileType_Init
+" Initialize the ctags arguments and tag variable for the specified
+" file type
+function! s:Tlist_FileType_Init(ftype)
+ call s:Tlist_Log_Msg('Tlist_FileType_Init (' . a:ftype . ')')
+ " If the user didn't specify any settings, then use the default
+ " ctags args. Otherwise, use the settings specified by the user
+ let var = 'g:tlist_' . a:ftype . '_settings'
+ if exists(var)
+ " User specified ctags arguments
+ let settings = {var} . ';'
+ else
+ " Default ctags arguments
+ let var = 's:tlist_def_' . a:ftype . '_settings'
+ if !exists(var)
+ " No default settings for this file type. This filetype is
+ " not supported
+ return 0
+ endif
+ let settings = s:tlist_def_{a:ftype}_settings . ';'
+ endif
+
+ let msg = 'Taglist: Invalid ctags option setting - ' . settings
+
+ " Format of the option that specifies the filetype and ctags arugments:
+ "
+ " <language_name>;flag1:name1;flag2:name2;flag3:name3
+ "
+
+ " Extract the file type to pass to ctags. This may be different from the
+ " file type detected by Vim
+ let pos = stridx(settings, ';')
+ if pos == -1
+ call s:Tlist_Warning_Msg(msg)
+ return 0
+ endif
+ let ctags_ftype = strpart(settings, 0, pos)
+ if ctags_ftype == ''
+ call s:Tlist_Warning_Msg(msg)
+ return 0
+ endif
+ " Make sure a valid filetype is supplied. If the user didn't specify a
+ " valid filetype, then the ctags option settings may be treated as the
+ " filetype
+ if ctags_ftype =~ ':'
+ call s:Tlist_Warning_Msg(msg)
+ return 0
+ endif
+
+ " Remove the file type from settings
+ let settings = strpart(settings, pos + 1)
+ if settings == ''
+ call s:Tlist_Warning_Msg(msg)
+ return 0
+ endif
+
+ " Process all the specified ctags flags. The format is
+ " flag1:name1;flag2:name2;flag3:name3
+ let ctags_flags = ''
+ let cnt = 0
+ while settings != ''
+ " Extract the flag
+ let pos = stridx(settings, ':')
+ if pos == -1
+ call s:Tlist_Warning_Msg(msg)
+ return 0
+ endif
+ let flag = strpart(settings, 0, pos)
+ if flag == ''
+ call s:Tlist_Warning_Msg(msg)
+ return 0
+ endif
+ " Remove the flag from settings
+ let settings = strpart(settings, pos + 1)
+
+ " Extract the tag type name
+ let pos = stridx(settings, ';')
+ if pos == -1
+ call s:Tlist_Warning_Msg(msg)
+ return 0
+ endif
+ let name = strpart(settings, 0, pos)
+ if name == ''
+ call s:Tlist_Warning_Msg(msg)
+ return 0
+ endif
+ let settings = strpart(settings, pos + 1)
+
+ let cnt = cnt + 1
+
+ let s:tlist_{a:ftype}_{cnt}_name = flag
+ let s:tlist_{a:ftype}_{cnt}_fullname = name
+ let ctags_flags = ctags_flags . flag
+ endwhile
+
+ let s:tlist_{a:ftype}_ctags_args = '--language-force=' . ctags_ftype .
+ \ ' --' . ctags_ftype . '-types=' . ctags_flags
+ let s:tlist_{a:ftype}_count = cnt
+ let s:tlist_{a:ftype}_ctags_flags = ctags_flags
+
+ " Save the filetype name
+ let s:tlist_ftype_{s:tlist_ftype_count}_name = a:ftype
+ let s:tlist_ftype_count = s:tlist_ftype_count + 1
+
+ return 1
+endfunction
+
+" Tlist_Detect_Filetype
+" Determine the filetype for the specified file using the filetypedetect
+" autocmd.
+function! s:Tlist_Detect_Filetype(fname)
+ " Ignore the filetype autocommands
+ let old_eventignore = &eventignore
+ set eventignore=FileType
+
+ " Save the 'filetype', as this will be changed temporarily
+ let old_filetype = &filetype
+
+ " Run the filetypedetect group of autocommands to determine
+ " the filetype
+ exe 'doautocmd filetypedetect BufRead ' . a:fname
+
+ " Save the detected filetype
+ let ftype = &filetype
+
+ " Restore the previous state
+ let &filetype = old_filetype
+ let &eventignore = old_eventignore
+
+ return ftype
+endfunction
+
+" Tlist_Get_Buffer_Filetype
+" Get the filetype for the specified buffer
+function! s:Tlist_Get_Buffer_Filetype(bnum)
+ let buf_ft = getbufvar(a:bnum, '&filetype')
+
+ if bufloaded(a:bnum)
+ " For loaded buffers, the 'filetype' is already determined
+ return buf_ft
+ endif
+
+ " For unloaded buffers, if the 'filetype' option is set, return it
+ if buf_ft != ''
+ return buf_ft
+ endif
+
+ " Skip non-existent buffers
+ if !bufexists(a:bnum)
+ return ''
+ endif
+
+ " For buffers whose filetype is not yet determined, try to determine
+ " the filetype
+ let bname = bufname(a:bnum)
+
+ return s:Tlist_Detect_Filetype(bname)
+endfunction
+
+" Tlist_Discard_TagInfo
+" Discard the stored tag information for a file
+function! s:Tlist_Discard_TagInfo(fidx)
+ call s:Tlist_Log_Msg('Tlist_Discard_TagInfo (' .
+ \ s:tlist_{a:fidx}_filename . ')')
+ let ftype = s:tlist_{a:fidx}_filetype
+
+ " Discard information about the tags defined in the file
+ let i = 1
+ while i <= s:tlist_{a:fidx}_tag_count
+ let fidx_i = 's:tlist_' . a:fidx . '_' . i
+ unlet! {fidx_i}_tag
+ unlet! {fidx_i}_tag_name
+ unlet! {fidx_i}_tag_type
+ unlet! {fidx_i}_ttype_idx
+ unlet! {fidx_i}_tag_proto
+ unlet! {fidx_i}_tag_searchpat
+ unlet! {fidx_i}_tag_linenum
+ let i = i + 1
+ endwhile
+
+ let s:tlist_{a:fidx}_tag_count = 0
+
+ " Discard information about tag type groups
+ let i = 1
+ while i <= s:tlist_{ftype}_count
+ let ttype = s:tlist_{ftype}_{i}_name
+ if s:tlist_{a:fidx}_{ttype} != ''
+ let fidx_ttype = 's:tlist_' . a:fidx . '_' . ttype
+ let {fidx_ttype} = ''
+ let {fidx_ttype}_offset = 0
+ let cnt = {fidx_ttype}_count
+ let {fidx_ttype}_count = 0
+ let j = 1
+ while j <= cnt
+ unlet! {fidx_ttype}_{j}
+ let j = j + 1
+ endwhile
+ endif
+ let i = i + 1
+ endwhile
+
+ " Discard the stored menu command also
+ let s:tlist_{a:fidx}_menu_cmd = ''
+endfunction
+
+" Tlist_Window_Update_Line_Offsets
+" Update the line offsets for tags for files starting from start_idx
+" and displayed in the taglist window by the specified offset
+function! s:Tlist_Window_Update_Line_Offsets(start_idx, increment, offset)
+ let i = a:start_idx
+
+ while i < s:tlist_file_count
+ if s:tlist_{i}_visible
+ " Update the start/end line number only if the file is visible
+ if a:increment
+ let s:tlist_{i}_start = s:tlist_{i}_start + a:offset
+ let s:tlist_{i}_end = s:tlist_{i}_end + a:offset
+ else
+ let s:tlist_{i}_start = s:tlist_{i}_start - a:offset
+ let s:tlist_{i}_end = s:tlist_{i}_end - a:offset
+ endif
+ endif
+ let i = i + 1
+ endwhile
+endfunction
+
+" Tlist_Discard_FileInfo
+" Discard the stored information for a file
+function! s:Tlist_Discard_FileInfo(fidx)
+ call s:Tlist_Log_Msg('Tlist_Discard_FileInfo (' .
+ \ s:tlist_{a:fidx}_filename . ')')
+ call s:Tlist_Discard_TagInfo(a:fidx)
+
+ let ftype = s:tlist_{a:fidx}_filetype
+
+ let i = 1
+ while i <= s:tlist_{ftype}_count
+ let ttype = s:tlist_{ftype}_{i}_name
+ unlet! s:tlist_{a:fidx}_{ttype}
+ unlet! s:tlist_{a:fidx}_{ttype}_offset
+ unlet! s:tlist_{a:fidx}_{ttype}_count
+ let i = i + 1
+ endwhile
+
+ unlet! s:tlist_{a:fidx}_filename
+ unlet! s:tlist_{a:fidx}_sort_type
+ unlet! s:tlist_{a:fidx}_filetype
+ unlet! s:tlist_{a:fidx}_mtime
+ unlet! s:tlist_{a:fidx}_start
+ unlet! s:tlist_{a:fidx}_end
+ unlet! s:tlist_{a:fidx}_valid
+ unlet! s:tlist_{a:fidx}_visible
+ unlet! s:tlist_{a:fidx}_tag_count
+ unlet! s:tlist_{a:fidx}_menu_cmd
+endfunction
+
+" Tlist_Window_Remove_File_From_Display
+" Remove the specified file from display
+function! s:Tlist_Window_Remove_File_From_Display(fidx)
+ call s:Tlist_Log_Msg('Tlist_Window_Remove_File_From_Display (' .
+ \ s:tlist_{a:fidx}_filename . ')')
+ " If the file is not visible then no need to remove it
+ if !s:tlist_{a:fidx}_visible
+ return
+ endif
+
+ " Remove the tags displayed for the specified file from the window
+ let start = s:tlist_{a:fidx}_start
+ " Include the empty line after the last line also
+ if g:Tlist_Compact_Format
+ let end = s:tlist_{a:fidx}_end
+ else
+ let end = s:tlist_{a:fidx}_end + 1
+ endif
+
+ setlocal modifiable
+ exe 'silent! ' . start . ',' . end . 'delete _'
+ setlocal nomodifiable
+
+ " Correct the start and end line offsets for all the files following
+ " this file, as the tags for this file are removed
+ call s:Tlist_Window_Update_Line_Offsets(a:fidx + 1, 0, end - start + 1)
+endfunction
+
+" Tlist_Remove_File
+" Remove the file under the cursor or the specified file index
+" user_request - User requested to remove the file from taglist
+function! s:Tlist_Remove_File(file_idx, user_request)
+ let fidx = a:file_idx
+
+ if fidx == -1
+ let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.'))
+ if fidx == -1
+ return
+ endif
+ endif
+ call s:Tlist_Log_Msg('Tlist_Remove_File (' .
+ \ s:tlist_{fidx}_filename . ', ' . a:user_request . ')')
+
+ let save_winnr = winnr()
+ let winnum = bufwinnr(g:TagList_title)
+ if winnum != -1
+ " Taglist window is open, remove the file from display
+
+ if save_winnr != winnum
+ let old_eventignore = &eventignore
+ set eventignore=all
+ exe winnum . 'wincmd w'
+ endif
+
+ call s:Tlist_Window_Remove_File_From_Display(fidx)
+
+ if save_winnr != winnum
+ exe save_winnr . 'wincmd w'
+ let &eventignore = old_eventignore
+ endif
+ endif
+
+ let fname = s:tlist_{fidx}_filename
+
+ if a:user_request
+ " As the user requested to remove the file from taglist,
+ " add it to the removed list
+ call s:Tlist_Update_Remove_List(fname, 1)
+ endif
+
+ " Remove the file name from the taglist list of filenames
+ let idx = stridx(s:tlist_file_names, fname . "\n")
+ let text_before = strpart(s:tlist_file_names, 0, idx)
+ let rem_text = strpart(s:tlist_file_names, idx)
+ let next_idx = stridx(rem_text, "\n")
+ let text_after = strpart(rem_text, next_idx + 1)
+ let s:tlist_file_names = text_before . text_after
+
+ call s:Tlist_Discard_FileInfo(fidx)
+
+ " Shift all the file variables by one index
+ let i = fidx + 1
+
+ while i < s:tlist_file_count
+ let j = i - 1
+
+ let s:tlist_{j}_filename = s:tlist_{i}_filename
+ let s:tlist_{j}_sort_type = s:tlist_{i}_sort_type
+ let s:tlist_{j}_filetype = s:tlist_{i}_filetype
+ let s:tlist_{j}_mtime = s:tlist_{i}_mtime
+ let s:tlist_{j}_start = s:tlist_{i}_start
+ let s:tlist_{j}_end = s:tlist_{i}_end
+ let s:tlist_{j}_valid = s:tlist_{i}_valid
+ let s:tlist_{j}_visible = s:tlist_{i}_visible
+ let s:tlist_{j}_tag_count = s:tlist_{i}_tag_count
+ let s:tlist_{j}_menu_cmd = s:tlist_{i}_menu_cmd
+
+ let k = 1
+ while k <= s:tlist_{j}_tag_count
+ let s:tlist_{j}_{k}_tag = s:tlist_{i}_{k}_tag
+ let s:tlist_{j}_{k}_tag_name = s:tlist_{i}_{k}_tag_name
+ let s:tlist_{j}_{k}_tag_type = s:Tlist_Get_Tag_Type_By_Tag(i, k)
+ let s:tlist_{j}_{k}_ttype_idx = s:tlist_{i}_{k}_ttype_idx
+ let s:tlist_{j}_{k}_tag_proto = s:Tlist_Get_Tag_Prototype(i, k)
+ let s:tlist_{j}_{k}_tag_searchpat = s:Tlist_Get_Tag_SearchPat(i, k)
+ let s:tlist_{j}_{k}_tag_linenum = s:Tlist_Get_Tag_Linenum(i, k)
+ let k = k + 1
+ endwhile
+
+ let ftype = s:tlist_{i}_filetype
+
+ let k = 1
+ while k <= s:tlist_{ftype}_count
+ let ttype = s:tlist_{ftype}_{k}_name
+ let s:tlist_{j}_{ttype} = s:tlist_{i}_{ttype}
+ let s:tlist_{j}_{ttype}_offset = s:tlist_{i}_{ttype}_offset
+ let s:tlist_{j}_{ttype}_count = s:tlist_{i}_{ttype}_count
+ if s:tlist_{j}_{ttype} != ''
+ let l = 1
+ while l <= s:tlist_{j}_{ttype}_count
+ let s:tlist_{j}_{ttype}_{l} = s:tlist_{i}_{ttype}_{l}
+ let l = l + 1
+ endwhile
+ endif
+ let k = k + 1
+ endwhile
+
+ " As the file and tag information is copied to the new index,
+ " discard the previous information
+ call s:Tlist_Discard_FileInfo(i)
+
+ let i = i + 1
+ endwhile
+
+ " Reduce the number of files displayed
+ let s:tlist_file_count = s:tlist_file_count - 1
+
+ if g:Tlist_Show_One_File
+ " If the tags for only one file is displayed and if we just
+ " now removed that file, then invalidate the current file idx
+ if s:tlist_cur_file_idx == fidx
+ let s:tlist_cur_file_idx = -1
+ endif
+ endif
+endfunction
+
+" Tlist_Window_Goto_Window
+" Goto the taglist window
+function! s:Tlist_Window_Goto_Window()
+ let winnum = bufwinnr(g:TagList_title)
+ if winnum != -1
+ if winnr() != winnum
+ call s:Tlist_Exe_Cmd_No_Acmds(winnum . 'wincmd w')
+ endif
+ endif
+endfunction
+
+" Tlist_Window_Create
+" Create a new taglist window. If it is already open, jump to it
+function! s:Tlist_Window_Create()
+ call s:Tlist_Log_Msg('Tlist_Window_Create()')
+ " If the window is open, jump to it
+ let winnum = bufwinnr(g:TagList_title)
+ if winnum != -1
+ " Jump to the existing window
+ if winnr() != winnum
+ exe winnum . 'wincmd w'
+ endif
+ return
+ endif
+
+ " If used with winmanager don't open windows. Winmanager will handle
+ " the window/buffer management
+ if s:tlist_app_name == "winmanager"
+ return
+ endif
+
+ " Create a new window. If user prefers a horizontal window, then open
+ " a horizontally split window. Otherwise open a vertically split
+ " window
+ if g:Tlist_Use_Horiz_Window
+ " Open a horizontally split window
+ let win_dir = 'botright'
+ " Horizontal window height
+ let win_size = g:Tlist_WinHeight
+ else
+ if s:tlist_winsize_chgd == -1
+ " Open a vertically split window. Increase the window size, if
+ " needed, to accomodate the new window
+ if g:Tlist_Inc_Winwidth &&
+ \ &columns < (80 + g:Tlist_WinWidth)
+ " Save the original window position
+ let s:tlist_pre_winx = getwinposx()
+ let s:tlist_pre_winy = getwinposy()
+
+ " one extra column is needed to include the vertical split
+ let &columns= &columns + g:Tlist_WinWidth + 1
+
+ let s:tlist_winsize_chgd = 1
+ else
+ let s:tlist_winsize_chgd = 0
+ endif
+ endif
+
+ if g:Tlist_Use_Right_Window
+ " Open the window at the rightmost place
+ let win_dir = 'botright vertical'
+ else
+ " Open the window at the leftmost place
+ let win_dir = 'topleft vertical'
+ endif
+ let win_size = g:Tlist_WinWidth
+ endif
+
+ " If the tag listing temporary buffer already exists, then reuse it.
+ " Otherwise create a new buffer
+ let bufnum = bufnr(g:TagList_title)
+ if bufnum == -1
+ " Create a new buffer
+ let wcmd = g:TagList_title
+ else
+ " Edit the existing buffer
+ let wcmd = '+buffer' . bufnum
+ endif
+
+ " Create the taglist window
+ exe 'silent! ' . win_dir . ' ' . win_size . 'split ' . wcmd
+
+ " Save the new window position
+ let s:tlist_winx = getwinposx()
+ let s:tlist_winy = getwinposy()
+
+ " Initialize the taglist window
+ call s:Tlist_Window_Init()
+endfunction
+
+" Tlist_Window_Zoom
+" Zoom (maximize/minimize) the taglist window
+function! s:Tlist_Window_Zoom()
+ if s:tlist_win_maximized
+ " Restore the window back to the previous size
+ if g:Tlist_Use_Horiz_Window
+ exe 'resize ' . g:Tlist_WinHeight
+ else
+ exe 'vert resize ' . g:Tlist_WinWidth
+ endif
+ let s:tlist_win_maximized = 0
+ else
+ " Set the window size to the maximum possible without closing other
+ " windows
+ if g:Tlist_Use_Horiz_Window
+ resize
+ else
+ vert resize
+ endif
+ let s:tlist_win_maximized = 1
+ endif
+endfunction
+
+" Tlist_Ballon_Expr
+" When the mouse cursor is over a tag in the taglist window, display the
+" tag prototype (balloon)
+function! Tlist_Ballon_Expr()
+ " Get the file index
+ let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(v:beval_lnum)
+ if fidx == -1
+ return ''
+ endif
+
+ " Get the tag output line for the current tag
+ let tidx = s:Tlist_Window_Get_Tag_Index(fidx, v:beval_lnum)
+ if tidx == 0
+ return ''
+ endif
+
+ " Get the tag search pattern and display it
+ return s:Tlist_Get_Tag_Prototype(fidx, tidx)
+endfunction
+
+" Tlist_Window_Check_Width
+" Check the width of the taglist window. For horizontally split windows, the
+" 'winfixheight' option is used to fix the height of the window. For
+" vertically split windows, Vim doesn't support the 'winfixwidth' option. So
+" need to handle window width changes from this function.
+function! s:Tlist_Window_Check_Width()
+ let tlist_winnr = bufwinnr(g:TagList_title)
+ if tlist_winnr == -1
+ return
+ endif
+
+ let width = winwidth(tlist_winnr)
+ if width != g:Tlist_WinWidth
+ call s:Tlist_Log_Msg("Tlist_Window_Check_Width: Changing window " .
+ \ "width from " . width . " to " . g:Tlist_WinWidth)
+ let save_winnr = winnr()
+ if save_winnr != tlist_winnr
+ call s:Tlist_Exe_Cmd_No_Acmds(tlist_winnr . 'wincmd w')
+ endif
+ exe 'vert resize ' . g:Tlist_WinWidth
+ if save_winnr != tlist_winnr
+ call s:Tlist_Exe_Cmd_No_Acmds('wincmd p')
+ endif
+ endif
+endfunction
+
+" Tlist_Window_Exit_Only_Window
+" If the 'Tlist_Exit_OnlyWindow' option is set, then exit Vim if only the
+" taglist window is present.
+function! s:Tlist_Window_Exit_Only_Window()
+ " Before quitting Vim, delete the taglist buffer so that
+ " the '0 mark is correctly set to the previous buffer.
+ if v:version < 700
+ if winbufnr(2) == -1
+ bdelete
+ quit
+ endif
+ else
+ if winbufnr(2) == -1
+ if tabpagenr('$') == 1
+ " Only one tag page is present
+ bdelete
+ quit
+ else
+ " More than one tab page is present. Close only the current
+ " tab page
+ close
+ endif
+ endif
+ endif
+endfunction
+
+" Tlist_Window_Init
+" Set the default options for the taglist window
+function! s:Tlist_Window_Init()
+ call s:Tlist_Log_Msg('Tlist_Window_Init()')
+
+ " The 'readonly' option should not be set for the taglist buffer.
+ " If Vim is started as "view/gview" or if the ":view" command is
+ " used, then the 'readonly' option is set for all the buffers.
+ " Unset it for the taglist buffer
+ setlocal noreadonly
+
+ " Set the taglist buffer filetype to taglist
+ setlocal filetype=taglist
+
+ " Define taglist window element highlighting
+ syntax match TagListComment '^" .*'
+ syntax match TagListFileName '^[^" ].*$'
+ syntax match TagListTitle '^ \S.*$'
+ syntax match TagListTagScope '\s\[.\{-\}\]$'
+
+ " Define the highlighting only if colors are supported
+ if has('gui_running') || &t_Co > 2
+ " Colors to highlight various taglist window elements
+ " If user defined highlighting group exists, then use them.
+ " Otherwise, use default highlight groups.
+ if hlexists('MyTagListTagName')
+ highlight link TagListTagName MyTagListTagName
+ else
+ highlight default link TagListTagName Search
+ endif
+ " Colors to highlight comments and titles
+ if hlexists('MyTagListComment')
+ highlight link TagListComment MyTagListComment
+ else
+ highlight clear TagListComment
+ highlight default link TagListComment Comment
+ endif
+ if hlexists('MyTagListTitle')
+ highlight link TagListTitle MyTagListTitle
+ else
+ highlight clear TagListTitle
+ highlight default link TagListTitle Title
+ endif
+ if hlexists('MyTagListFileName')
+ highlight link TagListFileName MyTagListFileName
+ else
+ highlight clear TagListFileName
+ highlight default TagListFileName guibg=Grey ctermbg=darkgray
+ \ guifg=white ctermfg=white
+ endif
+ if hlexists('MyTagListTagScope')
+ highlight link TagListTagScope MyTagListTagScope
+ else
+ highlight clear TagListTagScope
+ highlight default link TagListTagScope Identifier
+ endif
+ else
+ highlight default TagListTagName term=reverse cterm=reverse
+ endif
+
+ " Folding related settings
+ setlocal foldenable
+ setlocal foldminlines=0
+ setlocal foldmethod=manual
+ setlocal foldlevel=9999
+ if g:Tlist_Enable_Fold_Column
+ setlocal foldcolumn=3
+ else
+ setlocal foldcolumn=0
+ endif
+ setlocal foldtext=v:folddashes.getline(v:foldstart)
+
+ if s:tlist_app_name != "winmanager"
+ " Mark buffer as scratch
+ silent! setlocal buftype=nofile
+ if s:tlist_app_name == "none"
+ silent! setlocal bufhidden=delete
+ endif
+ silent! setlocal noswapfile
+ " Due to a bug in Vim 6.0, the winbufnr() function fails for unlisted
+ " buffers. So if the taglist buffer is unlisted, multiple taglist
+ " windows will be opened. This bug is fixed in Vim 6.1 and above
+ if v:version >= 601
+ silent! setlocal nobuflisted
+ endif
+ endif
+
+ silent! setlocal nowrap
+
+ " If the 'number' option is set in the source window, it will affect the
+ " taglist window. So forcefully disable 'number' option for the taglist
+ " window
+ silent! setlocal nonumber
+
+ " Use fixed height when horizontally split window is used
+ if g:Tlist_Use_Horiz_Window
+ if v:version >= 602
+ set winfixheight
+ endif
+ endif
+ if !g:Tlist_Use_Horiz_Window && v:version >= 700
+ set winfixwidth
+ endif
+
+ " Setup balloon evaluation to display tag prototype
+ if v:version >= 700 && has('balloon_eval')
+ setlocal balloonexpr=Tlist_Ballon_Expr()
+ set ballooneval
+ endif
+
+ " Setup the cpoptions properly for the maps to work
+ let old_cpoptions = &cpoptions
+ set cpoptions&vim
+
+ " Create buffer local mappings for jumping to the tags and sorting the list
+ nnoremap <buffer> <silent> <CR>
+ \ :call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR>
+ nnoremap <buffer> <silent> o
+ \ :call <SID>Tlist_Window_Jump_To_Tag('newwin')<CR>
+ nnoremap <buffer> <silent> p
+ \ :call <SID>Tlist_Window_Jump_To_Tag('preview')<CR>
+ nnoremap <buffer> <silent> P
+ \ :call <SID>Tlist_Window_Jump_To_Tag('prevwin')<CR>
+ if v:version >= 700
+ nnoremap <buffer> <silent> t
+ \ :call <SID>Tlist_Window_Jump_To_Tag('checktab')<CR>
+ nnoremap <buffer> <silent> <C-t>
+ \ :call <SID>Tlist_Window_Jump_To_Tag('newtab')<CR>
+ endif
+ nnoremap <buffer> <silent> <2-LeftMouse>
+ \ :call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR>
+ nnoremap <buffer> <silent> s
+ \ :call <SID>Tlist_Change_Sort('cmd', 'toggle', '')<CR>
+ nnoremap <buffer> <silent> + :silent! foldopen<CR>
+ nnoremap <buffer> <silent> - :silent! foldclose<CR>
+ nnoremap <buffer> <silent> * :silent! %foldopen!<CR>
+ nnoremap <buffer> <silent> = :silent! %foldclose<CR>
+ nnoremap <buffer> <silent> <kPlus> :silent! foldopen<CR>
+ nnoremap <buffer> <silent> <kMinus> :silent! foldclose<CR>
+ nnoremap <buffer> <silent> <kMultiply> :silent! %foldopen!<CR>
+ nnoremap <buffer> <silent> <Space> :call <SID>Tlist_Window_Show_Info()<CR>
+ nnoremap <buffer> <silent> u :call <SID>Tlist_Window_Update_File()<CR>
+ nnoremap <buffer> <silent> d :call <SID>Tlist_Remove_File(-1, 1)<CR>
+ nnoremap <buffer> <silent> x :call <SID>Tlist_Window_Zoom()<CR>
+ nnoremap <buffer> <silent> [[ :call <SID>Tlist_Window_Move_To_File(-1)<CR>
+ nnoremap <buffer> <silent> <BS> :call <SID>Tlist_Window_Move_To_File(-1)<CR>
+ nnoremap <buffer> <silent> ]] :call <SID>Tlist_Window_Move_To_File(1)<CR>
+ nnoremap <buffer> <silent> <Tab> :call <SID>Tlist_Window_Move_To_File(1)<CR>
+ nnoremap <buffer> <silent> <F1> :call <SID>Tlist_Window_Toggle_Help_Text()<CR>
+ nnoremap <buffer> <silent> q :close<CR>
+
+ " Insert mode mappings
+ inoremap <buffer> <silent> <CR>
+ \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR>
+ " Windows needs return
+ inoremap <buffer> <silent> <Return>
+ \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR>
+ inoremap <buffer> <silent> o
+ \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('newwin')<CR>
+ inoremap <buffer> <silent> p
+ \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('preview')<CR>
+ inoremap <buffer> <silent> P
+ \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('prevwin')<CR>
+ if v:version >= 700
+ inoremap <buffer> <silent> t
+ \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('checktab')<CR>
+ inoremap <buffer> <silent> <C-t>
+ \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('newtab')<CR>
+ endif
+ inoremap <buffer> <silent> <2-LeftMouse>
+ \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR>
+ inoremap <buffer> <silent> s
+ \ <C-o>:call <SID>Tlist_Change_Sort('cmd', 'toggle', '')<CR>
+ inoremap <buffer> <silent> + <C-o>:silent! foldopen<CR>
+ inoremap <buffer> <silent> - <C-o>:silent! foldclose<CR>
+ inoremap <buffer> <silent> * <C-o>:silent! %foldopen!<CR>
+ inoremap <buffer> <silent> = <C-o>:silent! %foldclose<CR>
+ inoremap <buffer> <silent> <kPlus> <C-o>:silent! foldopen<CR>
+ inoremap <buffer> <silent> <kMinus> <C-o>:silent! foldclose<CR>
+ inoremap <buffer> <silent> <kMultiply> <C-o>:silent! %foldopen!<CR>
+ inoremap <buffer> <silent> <Space> <C-o>:call
+ \ <SID>Tlist_Window_Show_Info()<CR>
+ inoremap <buffer> <silent> u
+ \ <C-o>:call <SID>Tlist_Window_Update_File()<CR>
+ inoremap <buffer> <silent> d <C-o>:call <SID>Tlist_Remove_File(-1, 1)<CR>
+ inoremap <buffer> <silent> x <C-o>:call <SID>Tlist_Window_Zoom()<CR>
+ inoremap <buffer> <silent> [[ <C-o>:call <SID>Tlist_Window_Move_To_File(-1)<CR>
+ inoremap <buffer> <silent> <BS> <C-o>:call <SID>Tlist_Window_Move_To_File(-1)<CR>
+ inoremap <buffer> <silent> ]] <C-o>:call <SID>Tlist_Window_Move_To_File(1)<CR>
+ inoremap <buffer> <silent> <Tab> <C-o>:call <SID>Tlist_Window_Move_To_File(1)<CR>
+ inoremap <buffer> <silent> <F1> <C-o>:call <SID>Tlist_Window_Toggle_Help_Text()<CR>
+ inoremap <buffer> <silent> q <C-o>:close<CR>
+
+ " Map single left mouse click if the user wants this functionality
+ if g:Tlist_Use_SingleClick == 1
+ " Contributed by Bindu Wavell
+ " attempt to perform single click mapping, it would be much
+ " nicer if we could nnoremap <buffer> ... however vim does
+ " not fire the <buffer> <leftmouse> when you use the mouse
+ " to enter a buffer.
+ let clickmap = ':if bufname("%") =~ "__Tag_List__" <bar> ' .
+ \ 'call <SID>Tlist_Window_Jump_To_Tag("useopen") ' .
+ \ '<bar> endif <CR>'
+ if maparg('<leftmouse>', 'n') == ''
+ " no mapping for leftmouse
+ exe ':nnoremap <silent> <leftmouse> <leftmouse>' . clickmap
+ else
+ " we have a mapping
+ let mapcmd = ':nnoremap <silent> <leftmouse> <leftmouse>'
+ let mapcmd = mapcmd . substitute(substitute(
+ \ maparg('<leftmouse>', 'n'), '|', '<bar>', 'g'),
+ \ '\c^<leftmouse>', '', '')
+ let mapcmd = mapcmd . clickmap
+ exe mapcmd
+ endif
+ endif
+
+ " Define the taglist autocommands
+ augroup TagListAutoCmds
+ autocmd!
+ " Display the tag prototype for the tag under the cursor.
+ autocmd CursorHold __Tag_List__ call s:Tlist_Window_Show_Info()
+ " Highlight the current tag periodically
+ autocmd CursorHold * silent call s:Tlist_Window_Highlight_Tag(
+ \ fnamemodify(bufname('%'), ':p'), line('.'), 1, 0)
+
+ " Adjust the Vim window width when taglist window is closed
+ autocmd BufUnload __Tag_List__ call s:Tlist_Post_Close_Cleanup()
+ " Close the fold for this buffer when leaving the buffer
+ if g:Tlist_File_Fold_Auto_Close
+ autocmd BufEnter * silent
+ \ call s:Tlist_Window_Open_File_Fold(expand('<abuf>'))
+ endif
+ " Exit Vim itself if only the taglist window is present (optional)
+ if g:Tlist_Exit_OnlyWindow
+ autocmd BufEnter __Tag_List__ nested
+ \ call s:Tlist_Window_Exit_Only_Window()
+ endif
+ if s:tlist_app_name != "winmanager" &&
+ \ !g:Tlist_Process_File_Always &&
+ \ (!has('gui_running') || !g:Tlist_Show_Menu)
+ " Auto refresh the taglist window
+ autocmd BufEnter * call s:Tlist_Refresh()
+ endif
+
+ if !g:Tlist_Use_Horiz_Window
+ if v:version < 700
+ autocmd WinEnter * call s:Tlist_Window_Check_Width()
+ endif
+ endif
+ if v:version >= 700
+ autocmd TabEnter * silent call s:Tlist_Refresh_Folds()
+ endif
+ augroup end
+
+ " Restore the previous cpoptions settings
+ let &cpoptions = old_cpoptions
+endfunction
+
+" Tlist_Window_Refresh
+" Display the tags for all the files in the taglist window
+function! s:Tlist_Window_Refresh()
+ call s:Tlist_Log_Msg('Tlist_Window_Refresh()')
+ " Set report option to a huge value to prevent informational messages
+ " while deleting the lines
+ let old_report = &report
+ set report=99999
+
+ " Mark the buffer as modifiable
+ setlocal modifiable
+
+ " Delete the contents of the buffer to the black-hole register
+ silent! %delete _
+
+ " As we have cleared the taglist window, mark all the files
+ " as not visible
+ let i = 0
+ while i < s:tlist_file_count
+ let s:tlist_{i}_visible = 0
+ let i = i + 1
+ endwhile
+
+ if g:Tlist_Compact_Format == 0
+ " Display help in non-compact mode
+ call s:Tlist_Window_Display_Help()
+ endif
+
+ " Mark the buffer as not modifiable
+ setlocal nomodifiable
+
+ " Restore the report option
+ let &report = old_report
+
+ " If the tags for only one file should be displayed in the taglist
+ " window, then no need to add the tags here. The bufenter autocommand
+ " will add the tags for that file.
+ if g:Tlist_Show_One_File
+ return
+ endif
+
+ " List all the tags for the previously processed files
+ " Do this only if taglist is configured to display tags for more than
+ " one file. Otherwise, when Tlist_Show_One_File is configured,
+ " tags for the wrong file will be displayed.
+ let i = 0
+ while i < s:tlist_file_count
+ call s:Tlist_Window_Refresh_File(s:tlist_{i}_filename,
+ \ s:tlist_{i}_filetype)
+ let i = i + 1
+ endwhile
+
+ if g:Tlist_Auto_Update
+ " Add and list the tags for all buffers in the Vim buffer list
+ let i = 1
+ let last_bufnum = bufnr('$')
+ while i <= last_bufnum
+ if buflisted(i)
+ let fname = fnamemodify(bufname(i), ':p')
+ let ftype = s:Tlist_Get_Buffer_Filetype(i)
+ " If the file doesn't support tag listing, skip it
+ if !s:Tlist_Skip_File(fname, ftype)
+ call s:Tlist_Window_Refresh_File(fname, ftype)
+ endif
+ endif
+ let i = i + 1
+ endwhile
+ endif
+
+ " If Tlist_File_Fold_Auto_Close option is set, then close all the folds
+ if g:Tlist_File_Fold_Auto_Close
+ " Close all the folds
+ silent! %foldclose
+ endif
+
+ " Move the cursor to the top of the taglist window
+ normal! gg
+endfunction
+
+" Tlist_Post_Close_Cleanup()
+" Close the taglist window and adjust the Vim window width
+function! s:Tlist_Post_Close_Cleanup()
+ call s:Tlist_Log_Msg('Tlist_Post_Close_Cleanup()')
+ " Mark all the files as not visible
+ let i = 0
+ while i < s:tlist_file_count
+ let s:tlist_{i}_visible = 0
+ let i = i + 1
+ endwhile
+
+ " Remove the taglist autocommands
+ silent! autocmd! TagListAutoCmds
+
+ " Clear all the highlights
+ match none
+
+ silent! syntax clear TagListTitle
+ silent! syntax clear TagListComment
+ silent! syntax clear TagListTagScope
+
+ " Remove the left mouse click mapping if it was setup initially
+ if g:Tlist_Use_SingleClick
+ if hasmapto('<LeftMouse>')
+ nunmap <LeftMouse>
+ endif
+ endif
+
+ if s:tlist_app_name != "winmanager"
+ if g:Tlist_Use_Horiz_Window || g:Tlist_Inc_Winwidth == 0 ||
+ \ s:tlist_winsize_chgd != 1 ||
+ \ &columns < (80 + g:Tlist_WinWidth)
+ " No need to adjust window width if using horizontally split taglist
+ " window or if columns is less than 101 or if the user chose not to
+ " adjust the window width
+ else
+ " If the user didn't manually move the window, then restore the window
+ " position to the pre-taglist position
+ if s:tlist_pre_winx != -1 && s:tlist_pre_winy != -1 &&
+ \ getwinposx() == s:tlist_winx &&
+ \ getwinposy() == s:tlist_winy
+ exe 'winpos ' . s:tlist_pre_winx . ' ' . s:tlist_pre_winy
+ endif
+
+ " Adjust the Vim window width
+ let &columns= &columns - (g:Tlist_WinWidth + 1)
+ endif
+ endif
+
+ let s:tlist_winsize_chgd = -1
+
+ " Reset taglist state variables
+ if s:tlist_app_name == "winmanager"
+ let s:tlist_app_name = "none"
+ endif
+ let s:tlist_window_initialized = 0
+endfunction
+
+" Tlist_Window_Refresh_File()
+" List the tags defined in the specified file in a Vim window
+function! s:Tlist_Window_Refresh_File(filename, ftype)
+ call s:Tlist_Log_Msg('Tlist_Window_Refresh_File (' . a:filename . ')')
+ " First check whether the file already exists
+ let fidx = s:Tlist_Get_File_Index(a:filename)
+ if fidx != -1
+ let file_listed = 1
+ else
+ let file_listed = 0
+ endif
+
+ if !file_listed
+ " Check whether this file is removed based on user request
+ " If it is, then don't display the tags for this file
+ if s:Tlist_User_Removed_File(a:filename)
+ return
+ endif
+ endif
+
+ if file_listed && s:tlist_{fidx}_visible
+ " Check whether the file tags are currently valid
+ if s:tlist_{fidx}_valid
+ " Goto the first line in the file
+ exe s:tlist_{fidx}_start
+
+ " If the line is inside a fold, open the fold
+ if foldclosed('.') != -1
+ exe "silent! " . s:tlist_{fidx}_start . "," .
+ \ s:tlist_{fidx}_end . "foldopen!"
+ endif
+ return
+ endif
+
+ " Discard and remove the tags for this file from display
+ call s:Tlist_Discard_TagInfo(fidx)
+ call s:Tlist_Window_Remove_File_From_Display(fidx)
+ endif
+
+ " Process and generate a list of tags defined in the file
+ if !file_listed || !s:tlist_{fidx}_valid
+ let ret_fidx = s:Tlist_Process_File(a:filename, a:ftype)
+ if ret_fidx == -1
+ return
+ endif
+ let fidx = ret_fidx
+ endif
+
+ " Set report option to a huge value to prevent informational messages
+ " while adding lines to the taglist window
+ let old_report = &report
+ set report=99999
+
+ if g:Tlist_Show_One_File
+ " Remove the previous file
+ if s:tlist_cur_file_idx != -1
+ call s:Tlist_Window_Remove_File_From_Display(s:tlist_cur_file_idx)
+ let s:tlist_{s:tlist_cur_file_idx}_visible = 0
+ let s:tlist_{s:tlist_cur_file_idx}_start = 0
+ let s:tlist_{s:tlist_cur_file_idx}_end = 0
+ endif
+ let s:tlist_cur_file_idx = fidx
+ endif
+
+ " Mark the buffer as modifiable
+ setlocal modifiable
+
+ " Add new files to the end of the window. For existing files, add them at
+ " the same line where they were previously present. If the file is not
+ " visible, then add it at the end
+ if s:tlist_{fidx}_start == 0 || !s:tlist_{fidx}_visible
+ if g:Tlist_Compact_Format
+ let s:tlist_{fidx}_start = line('$')
+ else
+ let s:tlist_{fidx}_start = line('$') + 1
+ endif
+ endif
+
+ let s:tlist_{fidx}_visible = 1
+
+ " Goto the line where this file should be placed
+ if g:Tlist_Compact_Format
+ exe s:tlist_{fidx}_start
+ else
+ exe s:tlist_{fidx}_start - 1
+ endif
+
+ let txt = fnamemodify(s:tlist_{fidx}_filename, ':t') . ' (' .
+ \ fnamemodify(s:tlist_{fidx}_filename, ':p:h') . ')'
+ if g:Tlist_Compact_Format == 0
+ silent! put =txt
+ else
+ silent! put! =txt
+ " Move to the next line
+ exe line('.') + 1
+ endif
+ let file_start = s:tlist_{fidx}_start
+
+ " Add the tag names grouped by tag type to the buffer with a title
+ let i = 1
+ let ttype_cnt = s:tlist_{a:ftype}_count
+ while i <= ttype_cnt
+ let ttype = s:tlist_{a:ftype}_{i}_name
+ " Add the tag type only if there are tags for that type
+ let fidx_ttype = 's:tlist_' . fidx . '_' . ttype
+ let ttype_txt = {fidx_ttype}
+ if ttype_txt != ''
+ let txt = ' ' . s:tlist_{a:ftype}_{i}_fullname
+ if g:Tlist_Compact_Format == 0
+ let ttype_start_lnum = line('.') + 1
+ silent! put =txt
+ else
+ let ttype_start_lnum = line('.')
+ silent! put! =txt
+ endif
+ silent! put =ttype_txt
+
+ let {fidx_ttype}_offset = ttype_start_lnum - file_start
+
+ " create a fold for this tag type
+ let fold_start = ttype_start_lnum
+ let fold_end = fold_start + {fidx_ttype}_count
+ exe fold_start . ',' . fold_end . 'fold'
+
+ " Adjust the cursor position
+ if g:Tlist_Compact_Format == 0
+ exe ttype_start_lnum + {fidx_ttype}_count
+ else
+ exe ttype_start_lnum + {fidx_ttype}_count + 1
+ endif
+
+ if g:Tlist_Compact_Format == 0
+ " Separate the tag types by a empty line
+ silent! put =''
+ endif
+ endif
+ let i = i + 1
+ endwhile
+
+ if s:tlist_{fidx}_tag_count == 0
+ if g:Tlist_Compact_Format == 0
+ silent! put =''
+ endif
+ endif
+
+ let s:tlist_{fidx}_end = line('.') - 1
+
+ " Create a fold for the entire file
+ exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'fold'
+ exe 'silent! ' . s:tlist_{fidx}_start . ',' .
+ \ s:tlist_{fidx}_end . 'foldopen!'
+
+ " Goto the starting line for this file,
+ exe s:tlist_{fidx}_start
+
+ if s:tlist_app_name == "winmanager"
+ " To handle a bug in the winmanager plugin, add a space at the
+ " last line
+ call setline('$', ' ')
+ endif
+
+ " Mark the buffer as not modifiable
+ setlocal nomodifiable
+
+ " Restore the report option
+ let &report = old_report
+
+ " Update the start and end line numbers for all the files following this
+ " file
+ let start = s:tlist_{fidx}_start
+ " include the empty line after the last line
+ if g:Tlist_Compact_Format
+ let end = s:tlist_{fidx}_end
+ else
+ let end = s:tlist_{fidx}_end + 1
+ endif
+ call s:Tlist_Window_Update_Line_Offsets(fidx + 1, 1, end - start + 1)
+
+ " Now that we have updated the taglist window, update the tags
+ " menu (if present)
+ if g:Tlist_Show_Menu
+ call s:Tlist_Menu_Update_File(1)
+ endif
+endfunction
+
+" Tlist_Init_File
+" Initialize the variables for a new file
+function! s:Tlist_Init_File(filename, ftype)
+ call s:Tlist_Log_Msg('Tlist_Init_File (' . a:filename . ')')
+ " Add new files at the end of the list
+ let fidx = s:tlist_file_count
+ let s:tlist_file_count = s:tlist_file_count + 1
+ " Add the new file name to the taglist list of file names
+ let s:tlist_file_names = s:tlist_file_names . a:filename . "\n"
+
+ " Initialize the file variables
+ let s:tlist_{fidx}_filename = a:filename
+ let s:tlist_{fidx}_sort_type = g:Tlist_Sort_Type
+ let s:tlist_{fidx}_filetype = a:ftype
+ let s:tlist_{fidx}_mtime = -1
+ let s:tlist_{fidx}_start = 0
+ let s:tlist_{fidx}_end = 0
+ let s:tlist_{fidx}_valid = 0
+ let s:tlist_{fidx}_visible = 0
+ let s:tlist_{fidx}_tag_count = 0
+ let s:tlist_{fidx}_menu_cmd = ''
+
+ " Initialize the tag type variables
+ let i = 1
+ while i <= s:tlist_{a:ftype}_count
+ let ttype = s:tlist_{a:ftype}_{i}_name
+ let s:tlist_{fidx}_{ttype} = ''
+ let s:tlist_{fidx}_{ttype}_offset = 0
+ let s:tlist_{fidx}_{ttype}_count = 0
+ let i = i + 1
+ endwhile
+
+ return fidx
+endfunction
+
+" Tlist_Get_Tag_Type_By_Tag
+" Return the tag type for the specified tag index
+function! s:Tlist_Get_Tag_Type_By_Tag(fidx, tidx)
+ let ttype_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_type'
+
+ " Already parsed and have the tag name
+ if exists(ttype_var)
+ return {ttype_var}
+ endif
+
+ let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag
+ let {ttype_var} = s:Tlist_Extract_Tagtype(tag_line)
+
+ return {ttype_var}
+endfunction
+
+" Tlist_Get_Tag_Prototype
+function! s:Tlist_Get_Tag_Prototype(fidx, tidx)
+ let tproto_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_proto'
+
+ " Already parsed and have the tag prototype
+ if exists(tproto_var)
+ return {tproto_var}
+ endif
+
+ " Parse and extract the tag prototype
+ let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag
+ let start = stridx(tag_line, '/^') + 2
+ let end = stridx(tag_line, '/;"' . "\t")
+ if tag_line[end - 1] == '$'
+ let end = end -1
+ endif
+ let tag_proto = strpart(tag_line, start, end - start)
+ let {tproto_var} = substitute(tag_proto, '\s*', '', '')
+
+ return {tproto_var}
+endfunction
+
+" Tlist_Get_Tag_SearchPat
+function! s:Tlist_Get_Tag_SearchPat(fidx, tidx)
+ let tpat_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_searchpat'
+
+ " Already parsed and have the tag search pattern
+ if exists(tpat_var)
+ return {tpat_var}
+ endif
+
+ " Parse and extract the tag search pattern
+ let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag
+ let start = stridx(tag_line, '/^') + 2
+ let end = stridx(tag_line, '/;"' . "\t")
+ if tag_line[end - 1] == '$'
+ let end = end -1
+ endif
+ let {tpat_var} = '\V\^' . strpart(tag_line, start, end - start) .
+ \ (tag_line[end] == '$' ? '\$' : '')
+
+ return {tpat_var}
+endfunction
+
+" Tlist_Get_Tag_Linenum
+" Return the tag line number, given the tag index
+function! s:Tlist_Get_Tag_Linenum(fidx, tidx)
+ let tline_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_linenum'
+
+ " Already parsed and have the tag line number
+ if exists(tline_var)
+ return {tline_var}
+ endif
+
+ " Parse and extract the tag line number
+ let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag
+ let start = strridx(tag_line, 'line:') + 5
+ let end = strridx(tag_line, "\t")
+ if end < start
+ let {tline_var} = strpart(tag_line, start) + 0
+ else
+ let {tline_var} = strpart(tag_line, start, end - start) + 0
+ endif
+
+ return {tline_var}
+endfunction
+
+" Tlist_Parse_Tagline
+" Parse a tag line from the ctags output. Separate the tag output based on the
+" tag type and store it in the tag type variable.
+" The format of each line in the ctags output is:
+"
+" tag_name<TAB>file_name<TAB>ex_cmd;"<TAB>extension_fields
+"
+function! s:Tlist_Parse_Tagline(tag_line)
+ if a:tag_line == ''
+ " Skip empty lines
+ return
+ endif
+
+ " Extract the tag type
+ let ttype = s:Tlist_Extract_Tagtype(a:tag_line)
+
+ " Make sure the tag type is a valid and supported one
+ if ttype == '' || stridx(s:ctags_flags, ttype) == -1
+ " Line is not in proper tags format or Tag type is not supported
+ return
+ endif
+
+ " Update the total tag count
+ let s:tidx = s:tidx + 1
+
+ " The following variables are used to optimize this code. Vim is slow in
+ " using curly brace names. To reduce the amount of processing needed, the
+ " curly brace variables are pre-processed here
+ let fidx_tidx = 's:tlist_' . s:fidx . '_' . s:tidx
+ let fidx_ttype = 's:tlist_' . s:fidx . '_' . ttype
+
+ " Update the count of this tag type
+ let ttype_idx = {fidx_ttype}_count + 1
+ let {fidx_ttype}_count = ttype_idx
+
+ " Store the ctags output for this tag
+ let {fidx_tidx}_tag = a:tag_line
+
+ " Store the tag index and the tag type index (back pointers)
+ let {fidx_ttype}_{ttype_idx} = s:tidx
+ let {fidx_tidx}_ttype_idx = ttype_idx
+
+ " Extract the tag name
+ let tag_name = strpart(a:tag_line, 0, stridx(a:tag_line, "\t"))
+
+ " Extract the tag scope/prototype
+ if g:Tlist_Display_Prototype
+ let ttxt = ' ' . s:Tlist_Get_Tag_Prototype(s:fidx, s:tidx)
+ else
+ let ttxt = ' ' . tag_name
+
+ " Add the tag scope, if it is available and is configured. Tag
+ " scope is the last field after the 'line:<num>\t' field
+ if g:Tlist_Display_Tag_Scope
+ let tag_scope = s:Tlist_Extract_Tag_Scope(a:tag_line)
+ if tag_scope != ''
+ let ttxt = ttxt . ' [' . tag_scope . ']'
+ endif
+ endif
+ endif
+
+ " Add this tag to the tag type variable
+ let {fidx_ttype} = {fidx_ttype} . ttxt . "\n"
+
+ " Save the tag name
+ let {fidx_tidx}_tag_name = tag_name
+endfunction
+
+" Tlist_Process_File
+" Get the list of tags defined in the specified file and store them
+" in Vim variables. Returns the file index where the tags are stored.
+function! s:Tlist_Process_File(filename, ftype)
+ call s:Tlist_Log_Msg('Tlist_Process_File (' . a:filename . ', ' .
+ \ a:ftype . ')')
+ " Check whether this file is supported
+ if s:Tlist_Skip_File(a:filename, a:ftype)
+ return -1
+ endif
+
+ " If the tag types for this filetype are not yet created, then create
+ " them now
+ let var = 's:tlist_' . a:ftype . '_count'
+ if !exists(var)
+ if s:Tlist_FileType_Init(a:ftype) == 0
+ return -1
+ endif
+ endif
+
+ " If this file is already processed, then use the cached values
+ let fidx = s:Tlist_Get_File_Index(a:filename)
+ if fidx == -1
+ " First time, this file is loaded
+ let fidx = s:Tlist_Init_File(a:filename, a:ftype)
+ else
+ " File was previously processed. Discard the tag information
+ call s:Tlist_Discard_TagInfo(fidx)
+ endif
+
+ let s:tlist_{fidx}_valid = 1
+
+ " Exuberant ctags arguments to generate a tag list
+ let ctags_args = ' -f - --format=2 --excmd=pattern --fields=nks '
+
+ " Form the ctags argument depending on the sort type
+ if s:tlist_{fidx}_sort_type == 'name'
+ let ctags_args = ctags_args . '--sort=yes'
+ else
+ let ctags_args = ctags_args . '--sort=no'
+ endif
+
+ " Add the filetype specific arguments
+ let ctags_args = ctags_args . ' ' . s:tlist_{a:ftype}_ctags_args
+
+ " Ctags command to produce output with regexp for locating the tags
+ let ctags_cmd = g:Tlist_Ctags_Cmd . ctags_args
+ let ctags_cmd = ctags_cmd . ' "' . a:filename . '"'
+
+ if &shellxquote == '"'
+ " Double-quotes within double-quotes will not work in the
+ " command-line.If the 'shellxquote' option is set to double-quotes,
+ " then escape the double-quotes in the ctags command-line.
+ let ctags_cmd = escape(ctags_cmd, '"')
+ endif
+
+ " In Windows 95, if not using cygwin, disable the 'shellslash'
+ " option. Otherwise, this will cause problems when running the
+ " ctags command.
+ if has('win95') && !has('win32unix')
+ let old_shellslash = &shellslash
+ set noshellslash
+ endif
+
+ if has('win32') && !has('win32unix') && !has('win95')
+ \ && (&shell =~ 'cmd.exe')
+ " Windows does not correctly deal with commands that have more than 1
+ " set of double quotes. It will strip them all resulting in:
+ " 'C:\Program' is not recognized as an internal or external command
+ " operable program or batch file. To work around this, place the
+ " command inside a batch file and call the batch file.
+ " Do this only on Win2K, WinXP and above.
+ " Contributed by: David Fishburn.
+ let s:taglist_tempfile = fnamemodify(tempname(), ':h') .
+ \ '\taglist.cmd'
+ exe 'redir! > ' . s:taglist_tempfile
+ silent echo ctags_cmd
+ redir END
+
+ call s:Tlist_Log_Msg('Cmd inside batch file: ' . ctags_cmd)
+ let ctags_cmd = '"' . s:taglist_tempfile . '"'
+ endif
+
+ call s:Tlist_Log_Msg('Cmd: ' . ctags_cmd)
+
+ " Run ctags and get the tag list
+ let cmd_output = system(ctags_cmd)
+
+ " Restore the value of the 'shellslash' option.
+ if has('win95') && !has('win32unix')
+ let &shellslash = old_shellslash
+ endif
+
+ if exists('s:taglist_tempfile')
+ " Delete the temporary cmd file created on MS-Windows
+ call delete(s:taglist_tempfile)
+ endif
+
+ " Handle errors
+ if v:shell_error
+ let msg = "Taglist: Failed to generate tags for " . a:filename
+ call s:Tlist_Warning_Msg(msg)
+ if cmd_output != ''
+ call s:Tlist_Warning_Msg(cmd_output)
+ endif
+ return fidx
+ endif
+
+ " Store the modification time for the file
+ let s:tlist_{fidx}_mtime = getftime(a:filename)
+
+ " No tags for current file
+ if cmd_output == ''
+ call s:Tlist_Log_Msg('No tags defined in ' . a:filename)
+ return fidx
+ endif
+
+ call s:Tlist_Log_Msg('Generated tags information for ' . a:filename)
+
+ if v:version > 601
+ " The following script local variables are used by the
+ " Tlist_Parse_Tagline() function.
+ let s:ctags_flags = s:tlist_{a:ftype}_ctags_flags
+ let s:fidx = fidx
+ let s:tidx = 0
+
+ " Process the ctags output one line at a time. The substitute()
+ " command is used to parse the tag lines instead of using the
+ " matchstr()/stridx()/strpart() functions for performance reason
+ call substitute(cmd_output, "\\([^\n]\\+\\)\n",
+ \ '\=s:Tlist_Parse_Tagline(submatch(1))', 'g')
+
+ " Save the number of tags for this file
+ let s:tlist_{fidx}_tag_count = s:tidx
+
+ " The following script local variables are no longer needed
+ unlet! s:ctags_flags
+ unlet! s:tidx
+ unlet! s:fidx
+ else
+ " Due to a bug in Vim earlier than version 6.1,
+ " we cannot use substitute() to parse the ctags output.
+ " Instead the slow str*() functions are used
+ let ctags_flags = s:tlist_{a:ftype}_ctags_flags
+ let tidx = 0
+
+ while cmd_output != ''
+ " Extract one line at a time
+ let idx = stridx(cmd_output, "\n")
+ let one_line = strpart(cmd_output, 0, idx)
+ " Remove the line from the tags output
+ let cmd_output = strpart(cmd_output, idx + 1)
+
+ if one_line == ''
+ " Line is not in proper tags format
+ continue
+ endif
+
+ " Extract the tag type
+ let ttype = s:Tlist_Extract_Tagtype(one_line)
+
+ " Make sure the tag type is a valid and supported one
+ if ttype == '' || stridx(ctags_flags, ttype) == -1
+ " Line is not in proper tags format or Tag type is not
+ " supported
+ continue
+ endif
+
+ " Update the total tag count
+ let tidx = tidx + 1
+
+ " The following variables are used to optimize this code. Vim is
+ " slow in using curly brace names. To reduce the amount of
+ " processing needed, the curly brace variables are pre-processed
+ " here
+ let fidx_tidx = 's:tlist_' . fidx . '_' . tidx
+ let fidx_ttype = 's:tlist_' . fidx . '_' . ttype
+
+ " Update the count of this tag type
+ let ttype_idx = {fidx_ttype}_count + 1
+ let {fidx_ttype}_count = ttype_idx
+
+ " Store the ctags output for this tag
+ let {fidx_tidx}_tag = one_line
+
+ " Store the tag index and the tag type index (back pointers)
+ let {fidx_ttype}_{ttype_idx} = tidx
+ let {fidx_tidx}_ttype_idx = ttype_idx
+
+ " Extract the tag name
+ let tag_name = strpart(one_line, 0, stridx(one_line, "\t"))
+
+ " Extract the tag scope/prototype
+ if g:Tlist_Display_Prototype
+ let ttxt = ' ' . s:Tlist_Get_Tag_Prototype(fidx, tidx)
+ else
+ let ttxt = ' ' . tag_name
+
+ " Add the tag scope, if it is available and is configured. Tag
+ " scope is the last field after the 'line:<num>\t' field
+ if g:Tlist_Display_Tag_Scope
+ let tag_scope = s:Tlist_Extract_Tag_Scope(one_line)
+ if tag_scope != ''
+ let ttxt = ttxt . ' [' . tag_scope . ']'
+ endif
+ endif
+ endif
+
+ " Add this tag to the tag type variable
+ let {fidx_ttype} = {fidx_ttype} . ttxt . "\n"
+
+ " Save the tag name
+ let {fidx_tidx}_tag_name = tag_name
+ endwhile
+
+ " Save the number of tags for this file
+ let s:tlist_{fidx}_tag_count = tidx
+ endif
+
+ call s:Tlist_Log_Msg('Processed ' . s:tlist_{fidx}_tag_count .
+ \ ' tags in ' . a:filename)
+
+ return fidx
+endfunction
+
+" Tlist_Update_File
+" Update the tags for a file (if needed)
+function! Tlist_Update_File(filename, ftype)
+ call s:Tlist_Log_Msg('Tlist_Update_File (' . a:filename . ')')
+ " If the file doesn't support tag listing, skip it
+ if s:Tlist_Skip_File(a:filename, a:ftype)
+ return
+ endif
+
+ " Convert the file name to a full path
+ let fname = fnamemodify(a:filename, ':p')
+
+ " First check whether the file already exists
+ let fidx = s:Tlist_Get_File_Index(fname)
+
+ if fidx != -1 && s:tlist_{fidx}_valid
+ " File exists and the tags are valid
+ " Check whether the file was modified after the last tags update
+ " If it is modified, then update the tags
+ if s:tlist_{fidx}_mtime == getftime(fname)
+ return
+ endif
+ else
+ " If the tags were removed previously based on a user request,
+ " as we are going to update the tags (based on the user request),
+ " remove the filename from the deleted list
+ call s:Tlist_Update_Remove_List(fname, 0)
+ endif
+
+ " If the taglist window is opened, update it
+ let winnum = bufwinnr(g:TagList_title)
+ if winnum == -1
+ " Taglist window is not present. Just update the taglist
+ " and return
+ call s:Tlist_Process_File(fname, a:ftype)
+ else
+ if g:Tlist_Show_One_File && s:tlist_cur_file_idx != -1
+ " If tags for only one file are displayed and we are not
+ " updating the tags for that file, then no need to
+ " refresh the taglist window. Otherwise, the taglist
+ " window should be updated.
+ if s:tlist_{s:tlist_cur_file_idx}_filename != fname
+ call s:Tlist_Process_File(fname, a:ftype)
+ return
+ endif
+ endif
+
+ " Save the current window number
+ let save_winnr = winnr()
+
+ " Goto the taglist window
+ call s:Tlist_Window_Goto_Window()
+
+ " Save the cursor position
+ let save_line = line('.')
+ let save_col = col('.')
+
+ " Update the taglist window
+ call s:Tlist_Window_Refresh_File(fname, a:ftype)
+
+ " Restore the cursor position
+ if v:version >= 601
+ call cursor(save_line, save_col)
+ else
+ exe save_line
+ exe 'normal! ' . save_col . '|'
+ endif
+
+ if winnr() != save_winnr
+ " Go back to the original window
+ call s:Tlist_Exe_Cmd_No_Acmds(save_winnr . 'wincmd w')
+ endif
+ endif
+
+ " Update the taglist menu
+ if g:Tlist_Show_Menu
+ call s:Tlist_Menu_Update_File(1)
+ endif
+endfunction
+
+" Tlist_Window_Close
+" Close the taglist window
+function! s:Tlist_Window_Close()
+ call s:Tlist_Log_Msg('Tlist_Window_Close()')
+ " Make sure the taglist window exists
+ let winnum = bufwinnr(g:TagList_title)
+ if winnum == -1
+ call s:Tlist_Warning_Msg('Error: Taglist window is not open')
+ return
+ endif
+
+ if winnr() == winnum
+ " Already in the taglist window. Close it and return
+ if winbufnr(2) != -1
+ " If a window other than the taglist window is open,
+ " then only close the taglist window.
+ close
+ endif
+ else
+ " Goto the taglist window, close it and then come back to the
+ " original window
+ let curbufnr = bufnr('%')
+ exe winnum . 'wincmd w'
+ 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
+endfunction
+
+" Tlist_Window_Mark_File_Window
+" Mark the current window as the file window to use when jumping to a tag.
+" Only if the current window is a non-plugin, non-preview and non-taglist
+" window
+function! s:Tlist_Window_Mark_File_Window()
+ if getbufvar('%', '&buftype') == '' && !&previewwindow
+ let w:tlist_file_window = "yes"
+ endif
+endfunction
+
+" Tlist_Window_Open
+" Open and refresh the taglist window
+function! s:Tlist_Window_Open()
+ call s:Tlist_Log_Msg('Tlist_Window_Open()')
+ " If the window is open, jump to it
+ let winnum = bufwinnr(g:TagList_title)
+ if winnum != -1
+ " Jump to the existing window
+ if winnr() != winnum
+ exe winnum . 'wincmd w'
+ endif
+ return
+ endif
+
+ if s:tlist_app_name == "winmanager"
+ " Taglist plugin is no longer part of the winmanager app
+ let s:tlist_app_name = "none"
+ endif
+
+ " Get the filename and filetype for the specified buffer
+ let curbuf_name = fnamemodify(bufname('%'), ':p')
+ let curbuf_ftype = s:Tlist_Get_Buffer_Filetype('%')
+ let cur_lnum = line('.')
+
+ " Mark the current window as the desired window to open a file when a tag
+ " is selected.
+ call s:Tlist_Window_Mark_File_Window()
+
+ " Open the taglist window
+ call s:Tlist_Window_Create()
+
+ call s:Tlist_Window_Refresh()
+
+ if g:Tlist_Show_One_File
+ " Add only the current buffer and file
+ "
+ " If the file doesn't support tag listing, skip it
+ if !s:Tlist_Skip_File(curbuf_name, curbuf_ftype)
+ call s:Tlist_Window_Refresh_File(curbuf_name, curbuf_ftype)
+ endif
+ endif
+
+ if g:Tlist_File_Fold_Auto_Close
+ " Open the fold for the current file, as all the folds in
+ " the taglist window are closed
+ let fidx = s:Tlist_Get_File_Index(curbuf_name)
+ if fidx != -1
+ exe "silent! " . s:tlist_{fidx}_start . "," .
+ \ s:tlist_{fidx}_end . "foldopen!"
+ endif
+ endif
+
+ " Highlight the current tag
+ call s:Tlist_Window_Highlight_Tag(curbuf_name, cur_lnum, 1, 1)
+endfunction
+
+" Tlist_Window_Toggle()
+" Open or close a taglist window
+function! s:Tlist_Window_Toggle()
+ call s:Tlist_Log_Msg('Tlist_Window_Toggle()')
+ " If taglist window is open then close it.
+ let winnum = bufwinnr(g:TagList_title)
+ if winnum != -1
+ call s:Tlist_Window_Close()
+ return
+ endif
+
+ call s:Tlist_Window_Open()
+
+ " Go back to the original window, if Tlist_GainFocus_On_ToggleOpen is not
+ " set
+ if !g:Tlist_GainFocus_On_ToggleOpen
+ call s:Tlist_Exe_Cmd_No_Acmds('wincmd p')
+ endif
+
+ " Update the taglist menu
+ if g:Tlist_Show_Menu
+ call s:Tlist_Menu_Update_File(0)
+ endif
+endfunction
+
+" Tlist_Process_Filelist
+" Process multiple files. Each filename is separated by "\n"
+" Returns the number of processed files
+function! s:Tlist_Process_Filelist(file_names)
+ let flist = a:file_names
+
+ " Enable lazy screen updates
+ let old_lazyredraw = &lazyredraw
+ set lazyredraw
+
+ " Keep track of the number of processed files
+ let fcnt = 0
+
+ " Process one file at a time
+ while flist != ''
+ let nl_idx = stridx(flist, "\n")
+ let one_file = strpart(flist, 0, nl_idx)
+
+ " Remove the filename from the list
+ let flist = strpart(flist, nl_idx + 1)
+
+ if one_file == ''
+ continue
+ endif
+
+ " Skip directories
+ if isdirectory(one_file)
+ continue
+ endif
+
+ let ftype = s:Tlist_Detect_Filetype(one_file)
+
+ echon "\r "
+ echon "\rProcessing tags for " . fnamemodify(one_file, ':p:t')
+
+ let fcnt = fcnt + 1
+
+ call Tlist_Update_File(one_file, ftype)
+ endwhile
+
+ " Clear the displayed informational messages
+ echon "\r "
+
+ " Restore the previous state
+ let &lazyredraw = old_lazyredraw
+
+ return fcnt
+endfunction
+
+" Tlist_Process_Dir
+" Process the files in a directory matching the specified pattern
+function! s:Tlist_Process_Dir(dir_name, pat)
+ let flist = glob(a:dir_name . '/' . a:pat) . "\n"
+
+ let fcnt = s:Tlist_Process_Filelist(flist)
+
+ let len = strlen(a:dir_name)
+ if a:dir_name[len - 1] == '\' || a:dir_name[len - 1] == '/'
+ let glob_expr = a:dir_name . '*'
+ else
+ let glob_expr = a:dir_name . '/*'
+ endif
+ let all_files = glob(glob_expr) . "\n"
+
+ while all_files != ''
+ let nl_idx = stridx(all_files, "\n")
+ let one_file = strpart(all_files, 0, nl_idx)
+
+ let all_files = strpart(all_files, nl_idx + 1)
+ if one_file == ''
+ continue
+ endif
+
+ " Skip non-directory names
+ if !isdirectory(one_file)
+ continue
+ endif
+
+ echon "\r "
+ echon "\rProcessing files in directory " . fnamemodify(one_file, ':t')
+ let fcnt = fcnt + s:Tlist_Process_Dir(one_file, a:pat)
+ endwhile
+
+ return fcnt
+endfunction
+
+" Tlist_Add_Files_Recursive
+" Add files recursively from a directory
+function! s:Tlist_Add_Files_Recursive(dir, ...)
+ let dir_name = fnamemodify(a:dir, ':p')
+ if !isdirectory(dir_name)
+ call s:Tlist_Warning_Msg('Error: ' . dir_name . ' is not a directory')
+ return
+ endif
+
+ if a:0 == 1
+ " User specified file pattern
+ let pat = a:1
+ else
+ " Default file pattern
+ let pat = '*'
+ endif
+
+ echon "\r "
+ echon "\rProcessing files in directory " . fnamemodify(dir_name, ':t')
+ let fcnt = s:Tlist_Process_Dir(dir_name, pat)
+
+ echon "\rAdded " . fcnt . " files to the taglist"
+endfunction
+
+" Tlist_Add_Files
+" Add the specified list of files to the taglist
+function! s:Tlist_Add_Files(...)
+ let flist = ''
+ let i = 1
+
+ " Get all the files matching the file patterns supplied as argument
+ while i <= a:0
+ let flist = flist . glob(a:{i}) . "\n"
+ let i = i + 1
+ endwhile
+
+ if flist == ''
+ call s:Tlist_Warning_Msg('Error: No matching files are found')
+ return
+ endif
+
+ let fcnt = s:Tlist_Process_Filelist(flist)
+ echon "\rAdded " . fcnt . " files to the taglist"
+endfunction
+
+" Tlist_Extract_Tagtype
+" Extract the tag type from the tag text
+function! s:Tlist_Extract_Tagtype(tag_line)
+ " The tag type is after the tag prototype field. The prototype field
+ " ends with the /;"\t string. We add 4 at the end to skip the characters
+ " in this special string..
+ let start = strridx(a:tag_line, '/;"' . "\t") + 4
+ let end = strridx(a:tag_line, 'line:') - 1
+ let ttype = strpart(a:tag_line, start, end - start)
+
+ return ttype
+endfunction
+
+" Tlist_Extract_Tag_Scope
+" Extract the tag scope from the tag text
+function! s:Tlist_Extract_Tag_Scope(tag_line)
+ let start = strridx(a:tag_line, 'line:')
+ let end = strridx(a:tag_line, "\t")
+ if end <= start
+ return ''
+ endif
+
+ let tag_scope = strpart(a:tag_line, end + 1)
+ let tag_scope = strpart(tag_scope, stridx(tag_scope, ':') + 1)
+
+ return tag_scope
+endfunction
+
+" Tlist_Refresh()
+" Refresh the taglist
+function! s:Tlist_Refresh()
+ call s:Tlist_Log_Msg('Tlist_Refresh (Skip_Refresh = ' .
+ \ s:Tlist_Skip_Refresh . ', ' . bufname('%') . ')')
+ " If we are entering the buffer from one of the taglist functions, then
+ " no need to refresh the taglist window again.
+ if s:Tlist_Skip_Refresh
+ " We still need to update the taglist menu
+ if g:Tlist_Show_Menu
+ call s:Tlist_Menu_Update_File(0)
+ endif
+ return
+ endif
+
+ " If part of the winmanager plugin and not configured to process
+ " tags always and not configured to display the tags menu, then return
+ if (s:tlist_app_name == 'winmanager') && !g:Tlist_Process_File_Always
+ \ && !g:Tlist_Show_Menu
+ return
+ endif
+
+ " Skip buffers with 'buftype' set to nofile, nowrite, quickfix or help
+ if &buftype != ''
+ return
+ endif
+
+ let filename = fnamemodify(bufname('%'), ':p')
+ let ftype = s:Tlist_Get_Buffer_Filetype('%')
+
+ " If the file doesn't support tag listing, skip it
+ if s:Tlist_Skip_File(filename, ftype)
+ return
+ endif
+
+ let tlist_win = bufwinnr(g:TagList_title)
+
+ " If the taglist window is not opened and not configured to process
+ " tags always and not displaying the tags menu, then return
+ if tlist_win == -1 && !g:Tlist_Process_File_Always && !g:Tlist_Show_Menu
+ return
+ endif
+
+ let fidx = s:Tlist_Get_File_Index(filename)
+ if fidx == -1
+ " Check whether this file is removed based on user request
+ " If it is, then don't display the tags for this file
+ if s:Tlist_User_Removed_File(filename)
+ return
+ endif
+
+ " If the taglist should not be auto updated, then return
+ if !g:Tlist_Auto_Update
+ return
+ endif
+ endif
+
+ let cur_lnum = line('.')
+
+ if fidx == -1
+ " Update the tags for the file
+ let fidx = s:Tlist_Process_File(filename, ftype)
+ else
+ let mtime = getftime(filename)
+ if s:tlist_{fidx}_mtime != mtime
+ " Invalidate the tags listed for this file
+ let s:tlist_{fidx}_valid = 0
+
+ " Update the taglist and the window
+ call Tlist_Update_File(filename, ftype)
+
+ " Store the new file modification time
+ let s:tlist_{fidx}_mtime = mtime
+ endif
+ endif
+
+ " Update the taglist window
+ if tlist_win != -1
+ " Disable screen updates
+ let old_lazyredraw = &lazyredraw
+ set nolazyredraw
+
+ " Save the current window number
+ let save_winnr = winnr()
+
+ " Goto the taglist window
+ call s:Tlist_Window_Goto_Window()
+
+ if !g:Tlist_Auto_Highlight_Tag || !g:Tlist_Highlight_Tag_On_BufEnter
+ " Save the cursor position
+ let save_line = line('.')
+ let save_col = col('.')
+ endif
+
+ " Update the taglist window
+ call s:Tlist_Window_Refresh_File(filename, ftype)
+
+ " Open the fold for the file
+ exe "silent! " . s:tlist_{fidx}_start . "," .
+ \ s:tlist_{fidx}_end . "foldopen!"
+
+ if g:Tlist_Highlight_Tag_On_BufEnter && g:Tlist_Auto_Highlight_Tag
+ if g:Tlist_Show_One_File && s:tlist_cur_file_idx != fidx
+ " If displaying tags for only one file in the taglist
+ " window and about to display the tags for a new file,
+ " then center the current tag line for the new file
+ let center_tag_line = 1
+ else
+ let center_tag_line = 0
+ endif
+
+ " Highlight the current tag
+ call s:Tlist_Window_Highlight_Tag(filename, cur_lnum, 1, center_tag_line)
+ else
+ " Restore the cursor position
+ if v:version >= 601
+ call cursor(save_line, save_col)
+ else
+ exe save_line
+ exe 'normal! ' . save_col . '|'
+ endif
+ endif
+
+ " Jump back to the original window
+ if save_winnr != winnr()
+ call s:Tlist_Exe_Cmd_No_Acmds(save_winnr . 'wincmd w')
+ endif
+
+ " Restore screen updates
+ let &lazyredraw = old_lazyredraw
+ endif
+
+ " Update the taglist menu
+ if g:Tlist_Show_Menu
+ call s:Tlist_Menu_Update_File(0)
+ endif
+endfunction
+
+" Tlist_Change_Sort()
+" Change the sort order of the tag listing
+" caller == 'cmd', command used in the taglist window
+" caller == 'menu', taglist menu
+" action == 'toggle', toggle sort from name to order and vice versa
+" action == 'set', set the sort order to sort_type
+function! s:Tlist_Change_Sort(caller, action, sort_type)
+ call s:Tlist_Log_Msg('Tlist_Change_Sort (caller = ' . a:caller .
+ \ ', action = ' . a:action . ', sort_type = ' . a:sort_type . ')')
+ if a:caller == 'cmd'
+ let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.'))
+ if fidx == -1
+ return
+ endif
+
+ " Remove the previous highlighting
+ match none
+ elseif a:caller == 'menu'
+ let fidx = s:Tlist_Get_File_Index(fnamemodify(bufname('%'), ':p'))
+ if fidx == -1
+ return
+ endif
+ endif
+
+ if a:action == 'toggle'
+ let sort_type = s:tlist_{fidx}_sort_type
+
+ " Toggle the sort order from 'name' to 'order' and vice versa
+ if sort_type == 'name'
+ let s:tlist_{fidx}_sort_type = 'order'
+ else
+ let s:tlist_{fidx}_sort_type = 'name'
+ endif
+ else
+ let s:tlist_{fidx}_sort_type = a:sort_type
+ endif
+
+ " Invalidate the tags listed for this file
+ let s:tlist_{fidx}_valid = 0
+
+ if a:caller == 'cmd'
+ " Save the current line for later restoration
+ let curline = '\V\^' . getline('.') . '\$'
+
+ call s:Tlist_Window_Refresh_File(s:tlist_{fidx}_filename,
+ \ s:tlist_{fidx}_filetype)
+
+ exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'foldopen!'
+
+ " Go back to the cursor line before the tag list is sorted
+ call search(curline, 'w')
+
+ call s:Tlist_Menu_Update_File(1)
+ else
+ call s:Tlist_Menu_Remove_File()
+
+ call s:Tlist_Refresh()
+ endif
+endfunction
+
+" Tlist_Update_Current_File()
+" Update taglist for the current buffer by regenerating the tag list
+" Contributed by WEN Guopeng.
+function! s:Tlist_Update_Current_File()
+ call s:Tlist_Log_Msg('Tlist_Update_Current_File()')
+ if winnr() == bufwinnr(g:TagList_title)
+ " In the taglist window. Update the current file
+ call s:Tlist_Window_Update_File()
+ else
+ " Not in the taglist window. Update the current buffer
+ let filename = fnamemodify(bufname('%'), ':p')
+ let fidx = s:Tlist_Get_File_Index(filename)
+ if fidx != -1
+ let s:tlist_{fidx}_valid = 0
+ endif
+ let ft = s:Tlist_Get_Buffer_Filetype('%')
+ call Tlist_Update_File(filename, ft)
+ endif
+endfunction
+
+" Tlist_Window_Update_File()
+" Update the tags displayed in the taglist window
+function! s:Tlist_Window_Update_File()
+ call s:Tlist_Log_Msg('Tlist_Window_Update_File()')
+ let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.'))
+ if fidx == -1
+ return
+ endif
+
+ " Remove the previous highlighting
+ match none
+
+ " Save the current line for later restoration
+ let curline = '\V\^' . getline('.') . '\$'
+
+ let s:tlist_{fidx}_valid = 0
+
+ " Update the taglist window
+ call s:Tlist_Window_Refresh_File(s:tlist_{fidx}_filename,
+ \ s:tlist_{fidx}_filetype)
+
+ exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'foldopen!'
+
+ " Go back to the tag line before the list is updated
+ call search(curline, 'w')
+endfunction
+
+" Tlist_Window_Get_Tag_Type_By_Linenum()
+" Return the tag type index for the specified line in the taglist window
+function! s:Tlist_Window_Get_Tag_Type_By_Linenum(fidx, lnum)
+ let ftype = s:tlist_{a:fidx}_filetype
+
+ " Determine to which tag type the current line number belongs to using the
+ " tag type start line number and the number of tags in a tag type
+ let i = 1
+ while i <= s:tlist_{ftype}_count
+ let ttype = s:tlist_{ftype}_{i}_name
+ let start_lnum =
+ \ s:tlist_{a:fidx}_start + s:tlist_{a:fidx}_{ttype}_offset
+ let end = start_lnum + s:tlist_{a:fidx}_{ttype}_count
+ if a:lnum >= start_lnum && a:lnum <= end
+ break
+ endif
+ let i = i + 1
+ endwhile
+
+ " Current line doesn't belong to any of the displayed tag types
+ if i > s:tlist_{ftype}_count
+ return ''
+ endif
+
+ return ttype
+endfunction
+
+" Tlist_Window_Get_Tag_Index()
+" Return the tag index for the specified line in the taglist window
+function! s:Tlist_Window_Get_Tag_Index(fidx, lnum)
+ let ttype = s:Tlist_Window_Get_Tag_Type_By_Linenum(a:fidx, a:lnum)
+
+ " Current line doesn't belong to any of the displayed tag types
+ if ttype == ''
+ return 0
+ endif
+
+ " Compute the index into the displayed tags for the tag type
+ let ttype_lnum = s:tlist_{a:fidx}_start + s:tlist_{a:fidx}_{ttype}_offset
+ let tidx = a:lnum - ttype_lnum
+ if tidx == 0
+ return 0
+ endif
+
+ " Get the corresponding tag line and return it
+ return s:tlist_{a:fidx}_{ttype}_{tidx}
+endfunction
+
+" Tlist_Window_Highlight_Line
+" Highlight the current line
+function! s:Tlist_Window_Highlight_Line()
+ " Clear previously selected name
+ match none
+
+ " Highlight the current line
+ if g:Tlist_Display_Prototype == 0
+ let pat = '/\%' . line('.') . 'l\s\+\zs.*/'
+ else
+ let pat = '/\%' . line('.') . 'l.*/'
+ endif
+
+ exe 'match TagListTagName ' . pat
+endfunction
+
+" Tlist_Window_Open_File
+" Open the specified file in either a new window or an existing window
+" and place the cursor at the specified tag pattern
+function! s:Tlist_Window_Open_File(win_ctrl, filename, tagpat)
+ call s:Tlist_Log_Msg('Tlist_Window_Open_File (' . a:filename . ',' .
+ \ a:win_ctrl . ')')
+ let prev_Tlist_Skip_Refresh = s:Tlist_Skip_Refresh
+ let s:Tlist_Skip_Refresh = 1
+
+ if s:tlist_app_name == "winmanager"
+ " Let the winmanager edit the file
+ call WinManagerFileEdit(a:filename, a:win_ctrl == 'newwin')
+ else
+
+ if a:win_ctrl == 'newtab'
+ " Create a new tab
+ exe 'tabnew ' . escape(a:filename, ' ')
+ " Open the taglist window in the new tab
+ call s:Tlist_Window_Open()
+ endif
+
+ if a:win_ctrl == 'checktab'
+ " Check whether the file is present in any of the tabs.
+ " If the file is present in the current tab, then use the
+ " current tab.
+ if bufwinnr(a:filename) != -1
+ let file_present_in_tab = 1
+ let i = tabpagenr()
+ else
+ let i = 1
+ let bnum = bufnr(a:filename)
+ let file_present_in_tab = 0
+ while i <= tabpagenr('$')
+ if index(tabpagebuflist(i), bnum) != -1
+ let file_present_in_tab = 1
+ break
+ endif
+ let i += 1
+ endwhile
+ endif
+
+ if file_present_in_tab
+ " Goto the tab containing the file
+ exe 'tabnext ' . i
+ else
+ " Open a new tab
+ exe 'tabnew ' . escape(a:filename, ' ')
+
+ " Open the taglist window
+ call s:Tlist_Window_Open()
+ endif
+ endif
+
+ let winnum = -1
+ if a:win_ctrl == 'prevwin'
+ " Open the file in the previous window, if it is usable
+ let cur_win = winnr()
+ wincmd p
+ if &buftype == '' && !&previewwindow
+ exe "edit " . escape(a:filename, ' ')
+ let winnum = winnr()
+ else
+ " Previous window is not usable
+ exe cur_win . 'wincmd w'
+ endif
+ endif
+
+ " Goto the window containing the file. If the window is not there, open a
+ " new window
+ if winnum == -1
+ let winnum = bufwinnr(a:filename)
+ endif
+
+ if winnum == -1
+ " Locate the previously used window for opening a file
+ let fwin_num = 0
+ let first_usable_win = 0
+
+ let i = 1
+ let bnum = winbufnr(i)
+ while bnum != -1
+ if getwinvar(i, 'tlist_file_window') == 'yes'
+ let fwin_num = i
+ break
+ endif
+ if first_usable_win == 0 &&
+ \ getbufvar(bnum, '&buftype') == '' &&
+ \ !getwinvar(i, '&previewwindow')
+ " First non-taglist, non-plugin and non-preview window
+ let first_usable_win = i
+ endif
+ let i = i + 1
+ let bnum = winbufnr(i)
+ endwhile
+
+ " If a previously used window is not found, then use the first
+ " non-taglist window
+ if fwin_num == 0
+ let fwin_num = first_usable_win
+ endif
+
+ if fwin_num != 0
+ " Jump to the file window
+ exe fwin_num . "wincmd w"
+
+ " If the user asked to jump to the tag in a new window, then split
+ " the existing window into two.
+ if a:win_ctrl == 'newwin'
+ split
+ endif
+ exe "edit " . escape(a:filename, ' ')
+ else
+ " Open a new window
+ if g:Tlist_Use_Horiz_Window
+ exe 'leftabove split ' . escape(a:filename, ' ')
+ else
+ if winbufnr(2) == -1
+ " Only the taglist window is present
+ if g:Tlist_Use_Right_Window
+ exe 'leftabove vertical split ' .
+ \ escape(a:filename, ' ')
+ else
+ exe 'rightbelow vertical split ' .
+ \ escape(a:filename, ' ')
+ endif
+
+ " Go to the taglist window to change the window size to
+ " the user configured value
+ call s:Tlist_Exe_Cmd_No_Acmds('wincmd p')
+ if g:Tlist_Use_Horiz_Window
+ exe 'resize ' . g:Tlist_WinHeight
+ else
+ exe 'vertical resize ' . g:Tlist_WinWidth
+ endif
+ " Go back to the file window
+ call s:Tlist_Exe_Cmd_No_Acmds('wincmd p')
+ else
+ " A plugin or help window is also present
+ wincmd w
+ exe 'leftabove split ' . escape(a:filename, ' ')
+ endif
+ endif
+ endif
+ " Mark the window, so that it can be reused.
+ call s:Tlist_Window_Mark_File_Window()
+ else
+ if v:version >= 700
+ " If the file is opened in more than one window, then check
+ " whether the last accessed window has the selected file.
+ " If it does, then use that window.
+ let lastwin_bufnum = winbufnr(winnr('#'))
+ if bufnr(a:filename) == lastwin_bufnum
+ let winnum = winnr('#')
+ endif
+ endif
+ exe winnum . 'wincmd w'
+
+ " If the user asked to jump to the tag in a new window, then split the
+ " existing window into two.
+ if a:win_ctrl == 'newwin'
+ split
+ endif
+ endif
+ endif
+
+ " Jump to the tag
+ if a:tagpat != ''
+ " Add the current cursor position to the jump list, so that user can
+ " jump back using the ' and ` marks.
+ mark '
+ silent call search(a:tagpat, 'w')
+
+ " Bring the line to the middle of the window
+ normal! z.
+
+ " If the line is inside a fold, open the fold
+ if foldclosed('.') != -1
+ .foldopen
+ endif
+ endif
+
+ " If the user selects to preview the tag then jump back to the
+ " taglist window
+ if a:win_ctrl == 'preview'
+ " Go back to the taglist window
+ let winnum = bufwinnr(g:TagList_title)
+ exe winnum . 'wincmd w'
+ else
+ " If the user has selected to close the taglist window, when a
+ " tag is selected, close the taglist window
+ if g:Tlist_Close_On_Select
+ call s:Tlist_Window_Goto_Window()
+ close
+
+ " Go back to the window displaying the selected file
+ let wnum = bufwinnr(a:filename)
+ if wnum != -1 && wnum != winnr()
+ call s:Tlist_Exe_Cmd_No_Acmds(wnum . 'wincmd w')
+ endif
+ endif
+ endif
+
+ let s:Tlist_Skip_Refresh = prev_Tlist_Skip_Refresh
+endfunction
+
+" Tlist_Window_Jump_To_Tag()
+" Jump to the location of the current tag
+" win_ctrl == useopen - Reuse the existing file window
+" win_ctrl == newwin - Open a new window
+" win_ctrl == preview - Preview the tag
+" win_ctrl == prevwin - Open in previous window
+" win_ctrl == newtab - Open in new tab
+function! s:Tlist_Window_Jump_To_Tag(win_ctrl)
+ call s:Tlist_Log_Msg('Tlist_Window_Jump_To_Tag(' . a:win_ctrl . ')')
+ " Do not process comment lines and empty lines
+ let curline = getline('.')
+ if curline =~ '^\s*$' || curline[0] == '"'
+ return
+ endif
+
+ " If inside a closed fold, then use the first line of the fold
+ " and jump to the file.
+ let lnum = foldclosed('.')
+ if lnum == -1
+ " Jump to the selected tag or file
+ let lnum = line('.')
+ else
+ " Open the closed fold
+ .foldopen!
+ endif
+
+ let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(lnum)
+ if fidx == -1
+ return
+ endif
+
+ " Get the tag output for the current tag
+ let tidx = s:Tlist_Window_Get_Tag_Index(fidx, lnum)
+ if tidx != 0
+ let tagpat = s:Tlist_Get_Tag_SearchPat(fidx, tidx)
+
+ " Highlight the tagline
+ call s:Tlist_Window_Highlight_Line()
+ else
+ " Selected a line which is not a tag name. Just edit the file
+ let tagpat = ''
+ endif
+
+ call s:Tlist_Window_Open_File(a:win_ctrl, s:tlist_{fidx}_filename, tagpat)
+endfunction
+
+" Tlist_Window_Show_Info()
+" Display information about the entry under the cursor
+function! s:Tlist_Window_Show_Info()
+ call s:Tlist_Log_Msg('Tlist_Window_Show_Info()')
+
+ " Clear the previously displayed line
+ echo
+
+ " Do not process comment lines and empty lines
+ let curline = getline('.')
+ if curline =~ '^\s*$' || curline[0] == '"'
+ return
+ endif
+
+ " If inside a fold, then don't display the prototype
+ if foldclosed('.') != -1
+ return
+ endif
+
+ let lnum = line('.')
+
+ " Get the file index
+ let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(lnum)
+ if fidx == -1
+ return
+ endif
+
+ if lnum == s:tlist_{fidx}_start
+ " Cursor is on a file name
+ let fname = s:tlist_{fidx}_filename
+ if strlen(fname) > 50
+ let fname = fnamemodify(fname, ':t')
+ endif
+ echo fname . ', Filetype=' . s:tlist_{fidx}_filetype .
+ \ ', Tag count=' . s:tlist_{fidx}_tag_count
+ return
+ endif
+
+ " Get the tag output line for the current tag
+ let tidx = s:Tlist_Window_Get_Tag_Index(fidx, lnum)
+ if tidx == 0
+ " Cursor is on a tag type
+ let ttype = s:Tlist_Window_Get_Tag_Type_By_Linenum(fidx, lnum)
+ if ttype == ''
+ return
+ endif
+
+ let ttype_name = ''
+
+ let ftype = s:tlist_{fidx}_filetype
+ let i = 1
+ while i <= s:tlist_{ftype}_count
+ if ttype == s:tlist_{ftype}_{i}_name
+ let ttype_name = s:tlist_{ftype}_{i}_fullname
+ break
+ endif
+ let i = i + 1
+ endwhile
+
+ echo 'Tag type=' . ttype_name .
+ \ ', Tag count=' . s:tlist_{fidx}_{ttype}_count
+ return
+ endif
+
+ " Get the tag search pattern and display it
+ echo s:Tlist_Get_Tag_Prototype(fidx, tidx)
+endfunction
+
+" Tlist_Find_Nearest_Tag_Idx
+" Find the tag idx nearest to the supplied line number
+" Returns -1, if a tag couldn't be found for the specified line number
+function! s:Tlist_Find_Nearest_Tag_Idx(fidx, linenum)
+ let sort_type = s:tlist_{a:fidx}_sort_type
+
+ let left = 1
+ let right = s:tlist_{a:fidx}_tag_count
+
+ if sort_type == 'order'
+ " Tags sorted by order, use a binary search.
+ " The idea behind this function is taken from the ctags.vim script (by
+ " Alexey Marinichev) available at the Vim online website.
+
+ " If the current line is the less than the first tag, then no need to
+ " search
+ let first_lnum = s:Tlist_Get_Tag_Linenum(a:fidx, 1)
+
+ if a:linenum < first_lnum
+ return -1
+ endif
+
+ while left < right
+ let middle = (right + left + 1) / 2
+ let middle_lnum = s:Tlist_Get_Tag_Linenum(a:fidx, middle)
+
+ if middle_lnum == a:linenum
+ let left = middle
+ break
+ endif
+
+ if middle_lnum > a:linenum
+ let right = middle - 1
+ else
+ let left = middle
+ endif
+ endwhile
+ else
+ " Tags sorted by name, use a linear search. (contributed by Dave
+ " Eggum).
+ " Look for a tag with a line number less than or equal to the supplied
+ " line number. If multiple tags are found, then use the tag with the
+ " line number closest to the supplied line number. IOW, use the tag
+ " with the highest line number.
+ let closest_lnum = 0
+ let final_left = 0
+ while left <= right
+ let lnum = s:Tlist_Get_Tag_Linenum(a:fidx, left)
+
+ if lnum < a:linenum && lnum > closest_lnum
+ let closest_lnum = lnum
+ let final_left = left
+ elseif lnum == a:linenum
+ let closest_lnum = lnum
+ let final_left = left
+ break
+ else
+ let left = left + 1
+ endif
+ endwhile
+ if closest_lnum == 0
+ return -1
+ endif
+ if left >= right
+ let left = final_left
+ endif
+ endif
+
+ return left
+endfunction
+
+" Tlist_Window_Highlight_Tag()
+" Highlight the current tag
+" cntx == 1, Called by the taglist plugin itself
+" cntx == 2, Forced by the user through the TlistHighlightTag command
+" center = 1, move the tag line to the center of the taglist window
+function! s:Tlist_Window_Highlight_Tag(filename, cur_lnum, cntx, center)
+ " Highlight the current tag only if the user configured the
+ " taglist plugin to do so or if the user explictly invoked the
+ " command to highlight the current tag.
+ if !g:Tlist_Auto_Highlight_Tag && a:cntx == 1
+ return
+ endif
+
+ if a:filename == ''
+ return
+ endif
+
+ " Make sure the taglist window is present
+ let winnum = bufwinnr(g:TagList_title)
+ if winnum == -1
+ call s:Tlist_Warning_Msg('Error: Taglist window is not open')
+ return
+ endif
+
+ let fidx = s:Tlist_Get_File_Index(a:filename)
+ if fidx == -1
+ return
+ endif
+
+ " If the file is currently not displayed in the taglist window, then retrn
+ if !s:tlist_{fidx}_visible
+ return
+ endif
+
+ " If there are no tags for this file, then no need to proceed further
+ if s:tlist_{fidx}_tag_count == 0
+ return
+ endif
+
+ " Ignore all autocommands
+ let old_ei = &eventignore
+ set eventignore=all
+
+ " Save the original window number
+ let org_winnr = winnr()
+
+ if org_winnr == winnum
+ let in_taglist_window = 1
+ else
+ let in_taglist_window = 0
+ endif
+
+ " Go to the taglist window
+ if !in_taglist_window
+ exe winnum . 'wincmd w'
+ endif
+
+ " Clear previously selected name
+ match none
+
+ let tidx = s:Tlist_Find_Nearest_Tag_Idx(fidx, a:cur_lnum)
+ if tidx == -1
+ " Make sure the current tag line is visible in the taglist window.
+ " Calling the winline() function makes the line visible. Don't know
+ " of a better way to achieve this.
+ let lnum = line('.')
+
+ if lnum < s:tlist_{fidx}_start || lnum > s:tlist_{fidx}_end
+ " Move the cursor to the beginning of the file
+ exe s:tlist_{fidx}_start
+ endif
+
+ if foldclosed('.') != -1
+ .foldopen
+ endif
+
+ call winline()
+
+ if !in_taglist_window
+ exe org_winnr . 'wincmd w'
+ endif
+
+ " Restore the autocommands
+ let &eventignore = old_ei
+ return
+ endif
+
+ " Extract the tag type
+ let ttype = s:Tlist_Get_Tag_Type_By_Tag(fidx, tidx)
+
+ " Compute the line number
+ " Start of file + Start of tag type + offset
+ let lnum = s:tlist_{fidx}_start + s:tlist_{fidx}_{ttype}_offset +
+ \ s:tlist_{fidx}_{tidx}_ttype_idx
+
+ " Goto the line containing the tag
+ exe lnum
+
+ " Open the fold
+ if foldclosed('.') != -1
+ .foldopen
+ endif
+
+ if a:center
+ " Move the tag line to the center of the taglist window
+ normal! z.
+ else
+ " Make sure the current tag line is visible in the taglist window.
+ " Calling the winline() function makes the line visible. Don't know
+ " of a better way to achieve this.
+ call winline()
+ endif
+
+ " Highlight the tag name
+ call s:Tlist_Window_Highlight_Line()
+
+ " Go back to the original window
+ if !in_taglist_window
+ exe org_winnr . 'wincmd w'
+ endif
+
+ " Restore the autocommands
+ let &eventignore = old_ei
+ return
+endfunction
+
+" Tlist_Get_Tag_Prototype_By_Line
+" Get the prototype for the tag on or before the specified line number in the
+" current buffer
+function! Tlist_Get_Tag_Prototype_By_Line(...)
+ if a:0 == 0
+ " Arguments are not supplied. Use the current buffer name
+ " and line number
+ let filename = bufname('%')
+ let linenr = line('.')
+ elseif a:0 == 2
+ " Filename and line number are specified
+ let filename = a:1
+ let linenr = a:2
+ if linenr !~ '\d\+'
+ " Invalid line number
+ return ""
+ endif
+ else
+ " Sufficient arguments are not supplied
+ let msg = 'Usage: Tlist_Get_Tag_Prototype_By_Line <filename> ' .
+ \ '<line_number>'
+ call s:Tlist_Warning_Msg(msg)
+ return ""
+ endif
+
+ " Expand the file to a fully qualified name
+ let filename = fnamemodify(filename, ':p')
+ if filename == ''
+ return ""
+ endif
+
+ let fidx = s:Tlist_Get_File_Index(filename)
+ if fidx == -1
+ return ""
+ endif
+
+ " If there are no tags for this file, then no need to proceed further
+ if s:tlist_{fidx}_tag_count == 0
+ return ""
+ endif
+
+ " Get the tag text using the line number
+ let tidx = s:Tlist_Find_Nearest_Tag_Idx(fidx, linenr)
+ if tidx == -1
+ return ""
+ endif
+
+ return s:Tlist_Get_Tag_Prototype(fidx, tidx)
+endfunction
+
+" Tlist_Get_Tagname_By_Line
+" Get the tag name on or before the specified line number in the
+" current buffer
+function! Tlist_Get_Tagname_By_Line(...)
+ if a:0 == 0
+ " Arguments are not supplied. Use the current buffer name
+ " and line number
+ let filename = bufname('%')
+ let linenr = line('.')
+ elseif a:0 == 2
+ " Filename and line number are specified
+ let filename = a:1
+ let linenr = a:2
+ if linenr !~ '\d\+'
+ " Invalid line number
+ return ""
+ endif
+ else
+ " Sufficient arguments are not supplied
+ let msg = 'Usage: Tlist_Get_Tagname_By_Line <filename> <line_number>'
+ call s:Tlist_Warning_Msg(msg)
+ return ""
+ endif
+
+ " Make sure the current file has a name
+ let filename = fnamemodify(filename, ':p')
+ if filename == ''
+ return ""
+ endif
+
+ let fidx = s:Tlist_Get_File_Index(filename)
+ if fidx == -1
+ return ""
+ endif
+
+ " If there are no tags for this file, then no need to proceed further
+ if s:tlist_{fidx}_tag_count == 0
+ return ""
+ endif
+
+ " Get the tag name using the line number
+ let tidx = s:Tlist_Find_Nearest_Tag_Idx(fidx, linenr)
+ if tidx == -1
+ return ""
+ endif
+
+ return s:tlist_{fidx}_{tidx}_tag_name
+endfunction
+
+" Tlist_Window_Move_To_File
+" Move the cursor to the beginning of the current file or the next file
+" or the previous file in the taglist window
+" dir == -1, move to start of current or previous function
+" dir == 1, move to start of next function
+function! s:Tlist_Window_Move_To_File(dir)
+ if foldlevel('.') == 0
+ " Cursor is on a non-folded line (it is not in any of the files)
+ " Move it to a folded line
+ if a:dir == -1
+ normal! zk
+ else
+ " While moving down to the start of the next fold,
+ " no need to do go to the start of the next file.
+ normal! zj
+ return
+ endif
+ endif
+
+ let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.'))
+ if fidx == -1
+ return
+ endif
+
+ let cur_lnum = line('.')
+
+ if a:dir == -1
+ if cur_lnum > s:tlist_{fidx}_start
+ " Move to the beginning of the current file
+ exe s:tlist_{fidx}_start
+ return
+ endif
+
+ if fidx != 0
+ " Move to the beginning of the previous file
+ let fidx = fidx - 1
+ else
+ " Cursor is at the first file, wrap around to the last file
+ let fidx = s:tlist_file_count - 1
+ endif
+
+ exe s:tlist_{fidx}_start
+ return
+ else
+ " Move to the beginning of the next file
+ let fidx = fidx + 1
+
+ if fidx >= s:tlist_file_count
+ " Cursor is at the last file, wrap around to the first file
+ let fidx = 0
+ endif
+
+ if s:tlist_{fidx}_start != 0
+ exe s:tlist_{fidx}_start
+ endif
+ return
+ endif
+endfunction
+
+" Tlist_Session_Load
+" Load a taglist session (information about all the displayed files
+" and the tags) from the specified file
+function! s:Tlist_Session_Load(...)
+ if a:0 == 0 || a:1 == ''
+ call s:Tlist_Warning_Msg('Usage: TlistSessionLoad <filename>')
+ return
+ endif
+
+ let sessionfile = a:1
+
+ if !filereadable(sessionfile)
+ let msg = 'Taglist: Error - Unable to open file ' . sessionfile
+ call s:Tlist_Warning_Msg(msg)
+ return
+ endif
+
+ " Mark the current window as the file window
+ call s:Tlist_Window_Mark_File_Window()
+
+ " Source the session file
+ exe 'source ' . sessionfile
+
+ let new_file_count = g:tlist_file_count
+ unlet! g:tlist_file_count
+
+ let i = 0
+ while i < new_file_count
+ let ftype = g:tlist_{i}_filetype
+ unlet! g:tlist_{i}_filetype
+
+ if !exists('s:tlist_' . ftype . '_count')
+ if s:Tlist_FileType_Init(ftype) == 0
+ let i = i + 1
+ continue
+ endif
+ endif
+
+ let fname = g:tlist_{i}_filename
+ unlet! g:tlist_{i}_filename
+
+ let fidx = s:Tlist_Get_File_Index(fname)
+ if fidx != -1
+ let s:tlist_{fidx}_visible = 0
+ let i = i + 1
+ continue
+ else
+ " As we are loading the tags from the session file, if this
+ " file was previously deleted by the user, now we need to
+ " add it back. So remove the file from the deleted list.
+ call s:Tlist_Update_Remove_List(fname, 0)
+ endif
+
+ let fidx = s:Tlist_Init_File(fname, ftype)
+
+ let s:tlist_{fidx}_filename = fname
+
+ let s:tlist_{fidx}_sort_type = g:tlist_{i}_sort_type
+ unlet! g:tlist_{i}_sort_type
+
+ let s:tlist_{fidx}_filetype = ftype
+ let s:tlist_{fidx}_mtime = getftime(fname)
+
+ let s:tlist_{fidx}_start = 0
+ let s:tlist_{fidx}_end = 0
+
+ let s:tlist_{fidx}_valid = 1
+
+ let s:tlist_{fidx}_tag_count = g:tlist_{i}_tag_count
+ unlet! g:tlist_{i}_tag_count
+
+ let j = 1
+ while j <= s:tlist_{fidx}_tag_count
+ let s:tlist_{fidx}_{j}_tag = g:tlist_{i}_{j}_tag
+ let s:tlist_{fidx}_{j}_tag_name = g:tlist_{i}_{j}_tag_name
+ let s:tlist_{fidx}_{j}_ttype_idx = g:tlist_{i}_{j}_ttype_idx
+ unlet! g:tlist_{i}_{j}_tag
+ unlet! g:tlist_{i}_{j}_tag_name
+ unlet! g:tlist_{i}_{j}_ttype_idx
+ let j = j + 1
+ endwhile
+
+ let j = 1
+ while j <= s:tlist_{ftype}_count
+ let ttype = s:tlist_{ftype}_{j}_name
+
+ if exists('g:tlist_' . i . '_' . ttype)
+ let s:tlist_{fidx}_{ttype} = g:tlist_{i}_{ttype}
+ unlet! g:tlist_{i}_{ttype}
+ let s:tlist_{fidx}_{ttype}_offset = 0
+ let s:tlist_{fidx}_{ttype}_count = g:tlist_{i}_{ttype}_count
+ unlet! g:tlist_{i}_{ttype}_count
+
+ let k = 1
+ while k <= s:tlist_{fidx}_{ttype}_count
+ let s:tlist_{fidx}_{ttype}_{k} = g:tlist_{i}_{ttype}_{k}
+ unlet! g:tlist_{i}_{ttype}_{k}
+ let k = k + 1
+ endwhile
+ else
+ let s:tlist_{fidx}_{ttype} = ''
+ let s:tlist_{fidx}_{ttype}_offset = 0
+ let s:tlist_{fidx}_{ttype}_count = 0
+ endif
+
+ let j = j + 1
+ endwhile
+
+ let i = i + 1
+ endwhile
+
+ " If the taglist window is open, then update it
+ let winnum = bufwinnr(g:TagList_title)
+ if winnum != -1
+ let save_winnr = winnr()
+
+ " Goto the taglist window
+ call s:Tlist_Window_Goto_Window()
+
+ " Refresh the taglist window
+ call s:Tlist_Window_Refresh()
+
+ " Go back to the original window
+ if save_winnr != winnr()
+ call s:Tlist_Exe_Cmd_No_Acmds('wincmd p')
+ endif
+ endif
+endfunction
+
+" Tlist_Session_Save
+" Save a taglist session (information about all the displayed files
+" and the tags) into the specified file
+function! s:Tlist_Session_Save(...)
+ if a:0 == 0 || a:1 == ''
+ call s:Tlist_Warning_Msg('Usage: TlistSessionSave <filename>')
+ return
+ endif
+
+ let sessionfile = a:1
+
+ if s:tlist_file_count == 0
+ " There is nothing to save
+ call s:Tlist_Warning_Msg('Warning: Taglist is empty. Nothing to save.')
+ return
+ endif
+
+ if filereadable(sessionfile)
+ let ans = input('Do you want to overwrite ' . sessionfile . ' (Y/N)?')
+ if ans !=? 'y'
+ return
+ endif
+
+ echo "\n"
+ endif
+
+ let old_verbose = &verbose
+ set verbose&vim
+
+ exe 'redir! > ' . sessionfile
+
+ silent! echo '" Taglist session file. This file is auto-generated.'
+ silent! echo '" File information'
+ silent! echo 'let tlist_file_count = ' . s:tlist_file_count
+
+ let i = 0
+
+ while i < s:tlist_file_count
+ " Store information about the file
+ silent! echo 'let tlist_' . i . "_filename = '" .
+ \ s:tlist_{i}_filename . "'"
+ silent! echo 'let tlist_' . i . '_sort_type = "' .
+ \ s:tlist_{i}_sort_type . '"'
+ silent! echo 'let tlist_' . i . '_filetype = "' .
+ \ s:tlist_{i}_filetype . '"'
+ silent! echo 'let tlist_' . i . '_tag_count = ' .
+ \ s:tlist_{i}_tag_count
+ " Store information about all the tags
+ let j = 1
+ while j <= s:tlist_{i}_tag_count
+ let txt = escape(s:tlist_{i}_{j}_tag, '"\\')
+ silent! echo 'let tlist_' . i . '_' . j . '_tag = "' . txt . '"'
+ silent! echo 'let tlist_' . i . '_' . j . '_tag_name = "' .
+ \ s:tlist_{i}_{j}_tag_name . '"'
+ silent! echo 'let tlist_' . i . '_' . j . '_ttype_idx' . ' = ' .
+ \ s:tlist_{i}_{j}_ttype_idx
+ let j = j + 1
+ endwhile
+
+ " Store information about all the tags grouped by their type
+ let ftype = s:tlist_{i}_filetype
+ let j = 1
+ while j <= s:tlist_{ftype}_count
+ let ttype = s:tlist_{ftype}_{j}_name
+ if s:tlist_{i}_{ttype}_count != 0
+ let txt = escape(s:tlist_{i}_{ttype}, '"\')
+ let txt = substitute(txt, "\n", "\\\\n", 'g')
+ silent! echo 'let tlist_' . i . '_' . ttype . ' = "' .
+ \ txt . '"'
+ silent! echo 'let tlist_' . i . '_' . ttype . '_count = ' .
+ \ s:tlist_{i}_{ttype}_count
+ let k = 1
+ while k <= s:tlist_{i}_{ttype}_count
+ silent! echo 'let tlist_' . i . '_' . ttype . '_' . k .
+ \ ' = ' . s:tlist_{i}_{ttype}_{k}
+ let k = k + 1
+ endwhile
+ endif
+ let j = j + 1
+ endwhile
+
+ silent! echo
+
+ let i = i + 1
+ endwhile
+
+ redir END
+
+ let &verbose = old_verbose
+endfunction
+
+" Tlist_Buffer_Removed
+" A buffer is removed from the Vim buffer list. Remove the tags defined
+" for that file
+function! s:Tlist_Buffer_Removed(filename)
+ call s:Tlist_Log_Msg('Tlist_Buffer_Removed (' . a:filename . ')')
+
+ " Make sure a valid filename is supplied
+ if a:filename == ''
+ return
+ endif
+
+ " Get tag list index of the specified file
+ let fidx = s:Tlist_Get_File_Index(a:filename)
+ if fidx == -1
+ " File not present in the taglist
+ return
+ endif
+
+ " Remove the file from the list
+ call s:Tlist_Remove_File(fidx, 0)
+endfunction
+
+" When a buffer is deleted, remove the file from the taglist
+autocmd BufDelete * silent call s:Tlist_Buffer_Removed(expand('<afile>:p'))
+
+" Tlist_Window_Open_File_Fold
+" Open the fold for the specified file and close the fold for all the
+" other files
+function! s:Tlist_Window_Open_File_Fold(acmd_bufnr)
+ call s:Tlist_Log_Msg('Tlist_Window_Open_File_Fold (' . a:acmd_bufnr . ')')
+
+ " Make sure the taglist window is present
+ let winnum = bufwinnr(g:TagList_title)
+ if winnum == -1
+ call s:Tlist_Warning_Msg('Taglist: Error - Taglist window is not open')
+ return
+ endif
+
+ " Save the original window number
+ let org_winnr = winnr()
+ if org_winnr == winnum
+ let in_taglist_window = 1
+ else
+ let in_taglist_window = 0
+ endif
+
+ if in_taglist_window
+ " When entering the taglist window, no need to update the folds
+ return
+ endif
+
+ " Go to the taglist window
+ if !in_taglist_window
+ call s:Tlist_Exe_Cmd_No_Acmds(winnum . 'wincmd w')
+ endif
+
+ " Close all the folds
+ silent! %foldclose
+
+ " Get tag list index of the specified file
+ let fname = fnamemodify(bufname(a:acmd_bufnr + 0), ':p')
+ if filereadable(fname)
+ let fidx = s:Tlist_Get_File_Index(fname)
+ if fidx != -1
+ " Open the fold for the file
+ exe "silent! " . s:tlist_{fidx}_start . "," .
+ \ s:tlist_{fidx}_end . "foldopen"
+ endif
+ endif
+
+ " Go back to the original window
+ if !in_taglist_window
+ call s:Tlist_Exe_Cmd_No_Acmds(org_winnr . 'wincmd w')
+ endif
+endfunction
+
+" Tlist_Window_Check_Auto_Open
+" Open the taglist window automatically on Vim startup.
+" Open the window only when files present in any of the Vim windows support
+" tags.
+function! s:Tlist_Window_Check_Auto_Open()
+ let open_window = 0
+
+ let i = 1
+ let buf_num = winbufnr(i)
+ while buf_num != -1
+ let filename = fnamemodify(bufname(buf_num), ':p')
+ let ft = s:Tlist_Get_Buffer_Filetype(buf_num)
+ if !s:Tlist_Skip_File(filename, ft)
+ let open_window = 1
+ break
+ endif
+ let i = i + 1
+ let buf_num = winbufnr(i)
+ endwhile
+
+ if open_window
+ call s:Tlist_Window_Toggle()
+ endif
+endfunction
+
+" Tlist_Refresh_Folds
+" Remove and create the folds for all the files displayed in the taglist
+" window. Used after entering a tab. If this is not done, then the folds
+" are not properly created for taglist windows displayed in multiple tabs.
+function! s:Tlist_Refresh_Folds()
+ let winnum = bufwinnr(g:TagList_title)
+ if winnum == -1
+ return
+ endif
+
+ let save_wnum = winnr()
+ exe winnum . 'wincmd w'
+
+ " First remove all the existing folds
+ normal! zE
+
+ " Create the folds for each in the tag list
+ let fidx = 0
+ while fidx < s:tlist_file_count
+ let ftype = s:tlist_{fidx}_filetype
+
+ " Create the folds for each tag type in a file
+ let j = 1
+ while j <= s:tlist_{ftype}_count
+ let ttype = s:tlist_{ftype}_{j}_name
+ if s:tlist_{fidx}_{ttype}_count
+ let s = s:tlist_{fidx}_start + s:tlist_{fidx}_{ttype}_offset
+ let e = s + s:tlist_{fidx}_{ttype}_count
+ exe s . ',' . e . 'fold'
+ endif
+ let j = j + 1
+ endwhile
+
+ exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'fold'
+ exe 'silent! ' . s:tlist_{fidx}_start . ',' .
+ \ s:tlist_{fidx}_end . 'foldopen!'
+ let fidx = fidx + 1
+ endwhile
+
+ exe save_wnum . 'wincmd w'
+endfunction
+
+function! s:Tlist_Menu_Add_Base_Menu()
+ call s:Tlist_Log_Msg('Adding the base menu')
+
+ " Add the menu
+ anoremenu <silent> T&ags.Refresh\ menu :call <SID>Tlist_Menu_Refresh()<CR>
+ anoremenu <silent> T&ags.Sort\ menu\ by.Name
+ \ :call <SID>Tlist_Change_Sort('menu', 'set', 'name')<CR>
+ anoremenu <silent> T&ags.Sort\ menu\ by.Order
+ \ :call <SID>Tlist_Change_Sort('menu', 'set', 'order')<CR>
+ anoremenu T&ags.-SEP1- :
+
+ if &mousemodel =~ 'popup'
+ anoremenu <silent> PopUp.T&ags.Refresh\ menu
+ \ :call <SID>Tlist_Menu_Refresh()<CR>
+ anoremenu <silent> PopUp.T&ags.Sort\ menu\ by.Name
+ \ :call <SID>Tlist_Change_Sort('menu', 'set', 'name')<CR>
+ anoremenu <silent> PopUp.T&ags.Sort\ menu\ by.Order
+ \ :call <SID>Tlist_Change_Sort('menu', 'set', 'order')<CR>
+ anoremenu PopUp.T&ags.-SEP1- :
+ endif
+endfunction
+
+let s:menu_char_prefix =
+ \ '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+
+" Tlist_Menu_Get_Tag_Type_Cmd
+" Get the menu command for the specified tag type
+" fidx - File type index
+" ftype - File Type
+" add_ttype_name - To add or not to add the tag type name to the menu entries
+" ttype_idx - Tag type index
+function! s:Tlist_Menu_Get_Tag_Type_Cmd(fidx, ftype, add_ttype_name, ttype_idx)
+ " Curly brace variable name optimization
+ let ftype_ttype_idx = a:ftype . '_' . a:ttype_idx
+
+ let ttype = s:tlist_{ftype_ttype_idx}_name
+ if a:add_ttype_name
+ " If the tag type name contains space characters, escape it. This
+ " will be used to create the menu entries.
+ let ttype_fullname = escape(s:tlist_{ftype_ttype_idx}_fullname, ' ')
+ endif
+
+ " Curly brace variable name optimization
+ let fidx_ttype = a:fidx . '_' . ttype
+
+ " Number of tag entries for this tag type
+ let tcnt = s:tlist_{fidx_ttype}_count
+ if tcnt == 0 " No entries for this tag type
+ return ''
+ endif
+
+ let mcmd = ''
+
+ " Create the menu items for the tags.
+ " Depending on the number of tags of this type, split the menu into
+ " multiple sub-menus, if needed.
+ if tcnt > g:Tlist_Max_Submenu_Items
+ let j = 1
+ while j <= tcnt
+ let final_index = j + g:Tlist_Max_Submenu_Items - 1
+ if final_index > tcnt
+ let final_index = tcnt
+ endif
+
+ " Extract the first and last tag name and form the
+ " sub-menu name
+ let tidx = s:tlist_{fidx_ttype}_{j}
+ let first_tag = s:tlist_{a:fidx}_{tidx}_tag_name
+
+ let tidx = s:tlist_{fidx_ttype}_{final_index}
+ let last_tag = s:tlist_{a:fidx}_{tidx}_tag_name
+
+ " Truncate the names, if they are greater than the
+ " max length
+ let first_tag = strpart(first_tag, 0, g:Tlist_Max_Tag_Length)
+ let last_tag = strpart(last_tag, 0, g:Tlist_Max_Tag_Length)
+
+ " Form the menu command prefix
+ let m_prefix = 'anoremenu <silent> T\&ags.'
+ if a:add_ttype_name
+ let m_prefix = m_prefix . ttype_fullname . '.'
+ endif
+ let m_prefix = m_prefix . first_tag . '\.\.\.' . last_tag . '.'
+
+ " Character prefix used to number the menu items (hotkey)
+ let m_prefix_idx = 0
+
+ while j <= final_index
+ let tidx = s:tlist_{fidx_ttype}_{j}
+
+ let tname = s:tlist_{a:fidx}_{tidx}_tag_name
+
+ let mcmd = mcmd . m_prefix . '\&' .
+ \ s:menu_char_prefix[m_prefix_idx] . '\.' .
+ \ tname . ' :call <SID>Tlist_Menu_Jump_To_Tag(' .
+ \ tidx . ')<CR>|'
+
+ let m_prefix_idx = m_prefix_idx + 1
+ let j = j + 1
+ endwhile
+ endwhile
+ else
+ " Character prefix used to number the menu items (hotkey)
+ let m_prefix_idx = 0
+
+ let m_prefix = 'anoremenu <silent> T\&ags.'
+ if a:add_ttype_name
+ let m_prefix = m_prefix . ttype_fullname . '.'
+ endif
+ let j = 1
+ while j <= tcnt
+ let tidx = s:tlist_{fidx_ttype}_{j}
+
+ let tname = s:tlist_{a:fidx}_{tidx}_tag_name
+
+ let mcmd = mcmd . m_prefix . '\&' .
+ \ s:menu_char_prefix[m_prefix_idx] . '\.' .
+ \ tname . ' :call <SID>Tlist_Menu_Jump_To_Tag(' . tidx
+ \ . ')<CR>|'
+
+ let m_prefix_idx = m_prefix_idx + 1
+ let j = j + 1
+ endwhile
+ endif
+
+ return mcmd
+endfunction
+
+" Update the taglist menu with the tags for the specified file
+function! s:Tlist_Menu_File_Refresh(fidx)
+ call s:Tlist_Log_Msg('Refreshing the tag menu for ' . s:tlist_{a:fidx}_filename)
+ " The 'B' flag is needed in the 'cpoptions' option
+ let old_cpoptions = &cpoptions
+ set cpoptions&vim
+
+ exe s:tlist_{a:fidx}_menu_cmd
+
+ " Update the popup menu (if enabled)
+ if &mousemodel =~ 'popup'
+ let cmd = substitute(s:tlist_{a:fidx}_menu_cmd, ' T\\&ags\.',
+ \ ' PopUp.T\\\&ags.', "g")
+ exe cmd
+ endif
+
+ " The taglist menu is not empty now
+ let s:tlist_menu_empty = 0
+
+ " Restore the 'cpoptions' settings
+ let &cpoptions = old_cpoptions
+endfunction
+
+" Tlist_Menu_Update_File
+" Add the taglist menu
+function! s:Tlist_Menu_Update_File(clear_menu)
+ if !has('gui_running')
+ " Not running in GUI mode
+ return
+ endif
+
+ call s:Tlist_Log_Msg('Updating the tag menu, clear_menu = ' . a:clear_menu)
+
+ " Remove the tags menu
+ if a:clear_menu
+ call s:Tlist_Menu_Remove_File()
+
+ endif
+
+ " Skip buffers with 'buftype' set to nofile, nowrite, quickfix or help
+ if &buftype != ''
+ return
+ endif
+
+ let filename = fnamemodify(bufname('%'), ':p')
+ let ftype = s:Tlist_Get_Buffer_Filetype('%')
+
+ " If the file doesn't support tag listing, skip it
+ if s:Tlist_Skip_File(filename, ftype)
+ return
+ endif
+
+ let fidx = s:Tlist_Get_File_Index(filename)
+ if fidx == -1 || !s:tlist_{fidx}_valid
+ " Check whether this file is removed based on user request
+ " If it is, then don't display the tags for this file
+ if s:Tlist_User_Removed_File(filename)
+ return
+ endif
+
+ " Process the tags for the file
+ let fidx = s:Tlist_Process_File(filename, ftype)
+ if fidx == -1
+ return
+ endif
+ endif
+
+ let fname = escape(fnamemodify(bufname('%'), ':t'), '.')
+ if fname != ''
+ exe 'anoremenu T&ags.' . fname . ' <Nop>'
+ anoremenu T&ags.-SEP2- :
+ endif
+
+ if !s:tlist_{fidx}_tag_count
+ return
+ endif
+
+ if s:tlist_{fidx}_menu_cmd != ''
+ " Update the menu with the cached command
+ call s:Tlist_Menu_File_Refresh(fidx)
+
+ return
+ endif
+
+ " We are going to add entries to the tags menu, so the menu won't be
+ " empty
+ let s:tlist_menu_empty = 0
+
+ let cmd = ''
+
+ " Determine whether the tag type name needs to be added to the menu
+ " If more than one tag type is present in the taglisting for a file,
+ " then the tag type name needs to be present
+ let add_ttype_name = -1
+ let i = 1
+ while i <= s:tlist_{ftype}_count && add_ttype_name < 1
+ let ttype = s:tlist_{ftype}_{i}_name
+ if s:tlist_{fidx}_{ttype}_count
+ let add_ttype_name = add_ttype_name + 1
+ endif
+ let i = i + 1
+ endwhile
+
+ " Process the tags by the tag type and get the menu command
+ let i = 1
+ while i <= s:tlist_{ftype}_count
+ let mcmd = s:Tlist_Menu_Get_Tag_Type_Cmd(fidx, ftype, add_ttype_name, i)
+ if mcmd != ''
+ let cmd = cmd . mcmd
+ endif
+
+ let i = i + 1
+ endwhile
+
+ " Cache the menu command for reuse
+ let s:tlist_{fidx}_menu_cmd = cmd
+
+ " Update the menu
+ call s:Tlist_Menu_File_Refresh(fidx)
+endfunction
+
+" Tlist_Menu_Remove_File
+" Remove the tags displayed in the tags menu
+function! s:Tlist_Menu_Remove_File()
+ if !has('gui_running') || s:tlist_menu_empty
+ return
+ endif
+
+ call s:Tlist_Log_Msg('Removing the tags menu for a file')
+
+ " Cleanup the Tags menu
+ silent! unmenu T&ags
+ if &mousemodel =~ 'popup'
+ silent! unmenu PopUp.T&ags
+ endif
+
+ " Add a dummy menu item to retain teared off menu
+ noremenu T&ags.Dummy l
+
+ silent! unmenu! T&ags
+ if &mousemodel =~ 'popup'
+ silent! unmenu! PopUp.T&ags
+ endif
+
+ call s:Tlist_Menu_Add_Base_Menu()
+
+ " Remove the dummy menu item
+ unmenu T&ags.Dummy
+
+ let s:tlist_menu_empty = 1
+endfunction
+
+" Tlist_Menu_Refresh
+" Refresh the taglist menu
+function! s:Tlist_Menu_Refresh()
+ call s:Tlist_Log_Msg('Refreshing the tags menu')
+ let fidx = s:Tlist_Get_File_Index(fnamemodify(bufname('%'), ':p'))
+ if fidx != -1
+ " Invalidate the cached menu command
+ let s:tlist_{fidx}_menu_cmd = ''
+ endif
+
+ " Update the taglist, menu and window
+ call s:Tlist_Update_Current_File()
+endfunction
+
+" Tlist_Menu_Jump_To_Tag
+" Jump to the selected tag
+function! s:Tlist_Menu_Jump_To_Tag(tidx)
+ let fidx = s:Tlist_Get_File_Index(fnamemodify(bufname('%'), ':p'))
+ if fidx == -1
+ return
+ endif
+
+ let tagpat = s:Tlist_Get_Tag_SearchPat(fidx, a:tidx)
+ if tagpat == ''
+ return
+ endif
+
+ " Add the current cursor position to the jump list, so that user can
+ " jump back using the ' and ` marks.
+ mark '
+
+ silent call search(tagpat, 'w')
+
+ " Bring the line to the middle of the window
+ normal! z.
+
+ " If the line is inside a fold, open the fold
+ if foldclosed('.') != -1
+ .foldopen
+ endif
+endfunction
+
+" Tlist_Menu_Init
+" Initialize the taglist menu
+function! s:Tlist_Menu_Init()
+ call s:Tlist_Menu_Add_Base_Menu()
+
+ " Automatically add the tags defined in the current file to the menu
+ augroup TagListMenuCmds
+ autocmd!
+
+ if !g:Tlist_Process_File_Always
+ autocmd BufEnter * call s:Tlist_Refresh()
+ endif
+ autocmd BufLeave * call s:Tlist_Menu_Remove_File()
+ augroup end
+
+ call s:Tlist_Menu_Update_File(0)
+endfunction
+
+" Tlist_Vim_Session_Load
+" Initialize the taglist window/buffer, which is created when loading
+" a Vim session file.
+function! s:Tlist_Vim_Session_Load()
+ call s:Tlist_Log_Msg('Tlist_Vim_Session_Load')
+
+ " Initialize the taglist window
+ call s:Tlist_Window_Init()
+
+ " Refresh the taglist window
+ call s:Tlist_Window_Refresh()
+endfunction
+
+" Tlist_Set_App
+" Set the name of the external plugin/application to which taglist
+" belongs.
+" Taglist plugin is part of another plugin like cream or winmanager.
+function! Tlist_Set_App(name)
+ if a:name == ""
+ return
+ endif
+
+ let s:tlist_app_name = a:name
+endfunction
+
+" Winmanager integration
+
+" Initialization required for integration with winmanager
+function! TagList_Start()
+ " If current buffer is not taglist buffer, then don't proceed
+ if bufname('%') != '__Tag_List__'
+ return
+ endif
+
+ call Tlist_Set_App('winmanager')
+
+ " Get the current filename from the winmanager plugin
+ let bufnum = WinManagerGetLastEditedFile()
+ if bufnum != -1
+ let filename = fnamemodify(bufname(bufnum), ':p')
+ let ftype = s:Tlist_Get_Buffer_Filetype(bufnum)
+ endif
+
+ " Initialize the taglist window, if it is not already initialized
+ if !exists('s:tlist_window_initialized') || !s:tlist_window_initialized
+ call s:Tlist_Window_Init()
+ call s:Tlist_Window_Refresh()
+ let s:tlist_window_initialized = 1
+ endif
+
+ " Update the taglist window
+ if bufnum != -1
+ if !s:Tlist_Skip_File(filename, ftype) && g:Tlist_Auto_Update
+ call s:Tlist_Window_Refresh_File(filename, ftype)
+ endif
+ endif
+endfunction
+
+function! TagList_IsValid()
+ return 0
+endfunction
+
+function! TagList_WrapUp()
+ return 0
+endfunction
+
+" restore 'cpo'
+let &cpo = s:cpo_save
+unlet s:cpo_save
+