diff options
| author | Ben Beltran <ben@freshout.us> | 2012-10-08 11:44:10 -0500 |
|---|---|---|
| committer | Ben Beltran <ben@freshout.us> | 2012-10-08 11:44:10 -0500 |
| commit | 0d23b6e515a01a5782532351821ebfa11f3d6cf2 (patch) | |
| tree | aa395b7e50ccb533d6b48b809016ac06f2d5bc8c /vim/plugin | |
| parent | a91731eac872b7837c2821341db5888702125cef (diff) | |
Add vim again :)
Diffstat (limited to 'vim/plugin')
31 files changed, 20735 insertions, 0 deletions
diff --git a/vim/plugin/.DS_Store b/vim/plugin/.DS_Store Binary files differnew file mode 100644 index 0000000..5008ddf --- /dev/null +++ b/vim/plugin/.DS_Store diff --git a/vim/plugin/31-create-scala.vim b/vim/plugin/31-create-scala.vim new file mode 100644 index 0000000..e4ecb44 --- /dev/null +++ b/vim/plugin/31-create-scala.vim @@ -0,0 +1,55 @@ +" Vim plugin that generates new Scala source file when you type +" vim nonexistent.scala. +" Scripts tries to detect package name from the directory path, e. g. +" .../src/main/scala/com/mycompany/myapp/app.scala gets header +" package com.mycompany.myapp +" +" Author : Stepan Koltsov <yozh@mx1.ru> + +function! MakeScalaFile() + if exists("b:template_used") && b:template_used + return + endif + + let b:template_used = 1 + + let filename = expand("<afile>:p") + let x = substitute(filename, "\.scala$", "", "") + + let p = substitute(x, "/[^/]*$", "", "") + let p = substitute(p, "/", ".", "g") + let p = substitute(p, ".*\.src$", "@", "") " unnamed package + let p = substitute(p, ".*\.src\.", "!", "") + let p = substitute(p, "^!main\.scala\.", "!", "") " + let p = substitute(p, "^!.*\.ru\.", "!ru.", "") + let p = substitute(p, "^!.*\.org\.", "!org.", "") + let p = substitute(p, "^!.*\.com\.", "!com.", "") + + " ! marks that we found package name. + if match(p, "^!") == 0 + let p = substitute(p, "^!", "", "") + else + " Don't know package name. + let p = "@" + endif + + let class = substitute(x, ".*/", "", "") + + if p != "@" + call append("0", "package " . p) + endif + + "norm G + "call append(".", "class " . class . " {") + + "norm G + "call append(".", "} /// end of " . class) + + call append(".", "// vim: set ts=4 sw=4 et:") + call append(".", "") + +endfunction + +au BufNewFile *.scala call MakeScalaFile() + +" vim: set ts=4 sw=4 et: diff --git a/vim/plugin/AlignMapsPlugin.vim b/vim/plugin/AlignMapsPlugin.vim new file mode 100644 index 0000000..eed0293 --- /dev/null +++ b/vim/plugin/AlignMapsPlugin.vim @@ -0,0 +1,242 @@ +" AlignMapsPlugin: Alignment maps based upon <Align.vim> and <AlignMaps.vim> +" Maintainer: Dr. Charles E. Campbell, Jr. <NdrOchipS@PcampbellAfamily.Mbiz> +" Date: Mar 03, 2009 +" +" NOTE: the code herein needs vim 6.0 or later +" needs <Align.vim> v6 or later +" needs <cecutil.vim> v5 or later +" Copyright: Copyright (C) 1999-2008 Charles E. Campbell, Jr. {{{1 +" 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, +" AlignMaps.vim is provided *as is* and comes with no warranty +" of any kind, either expressed or implied. By using this +" plugin, you agree that in no event will the copyright +" holder be liable for any damages resulting from the use +" of this software. +" +" Usage: {{{1 +" Use 'a to mark beginning of to-be-aligned region, Alternative: use V +" move cursor to end of region, and execute map. (linewise visual mode) to +" The maps also set up marks 'y and 'z, and retain mark region, execute same +" 'a at the beginning of region. map. Uses 'a, 'y, and 'z. +" +" The start/end wrappers save and restore marks 'y and 'z. +" +" Although the comments indicate the maps use a leading backslash, +" actually they use <Leader> (:he mapleader), so the user can +" specify that the maps start how he or she prefers. +" +" Note: these maps all use <Align.vim>. +" +" Romans 1:20 For the invisible things of Him since the creation of the {{{1 +" world are clearly seen, being perceived through the things that are +" made, even His everlasting power and divinity; that they may be +" without excuse. + +" --------------------------------------------------------------------- +" Load Once: {{{1 +if &cp || exists("g:loaded_AlignMapsPlugin") + finish +endif +let s:keepcpo = &cpo +let g:loaded_AlignMapsPlugin = "v41" +set cpo&vim + +" ===================================================================== +" Maps: {{{1 + +" --------------------------------------------------------------------- +" WS: wrapper start map (internal) {{{2 +" Produces a blank line above and below, marks with 'y and 'z +if !hasmapto('<Plug>WrapperStart') + map <unique> <SID>WS <Plug>AlignMapsWrapperStart +endif +nmap <silent> <script> <Plug>AlignMapsWrapperStart :set lz<CR>:call AlignMaps#WrapperStart(0)<CR> +vmap <silent> <script> <Plug>AlignMapsWrapperStart :<c-u>set lz<CR>:call AlignMaps#WrapperStart(1)<CR> + +" --------------------------------------------------------------------- +" WE: wrapper end (internal) {{{2 +" Removes guard lines, restores marks y and z, and restores search pattern +if !hasmapto('<Plug>WrapperEnd') + nmap <unique> <SID>WE <Plug>AlignMapsWrapperEnd +endif +nmap <silent> <script> <Plug>AlignMapsWrapperEnd :call AlignMaps#WrapperEnd()<CR>:set nolz<CR> + +" --------------------------------------------------------------------- +" Complex C-code alignment maps: {{{2 +if !hasmapto('<Plug>AM_a?') |map <unique> <Leader>a? <Plug>AM_a?|endif +if !hasmapto('<Plug>AM_a,') |map <unique> <Leader>a, <Plug>AM_a,|endif +if !hasmapto('<Plug>AM_a<') |map <unique> <Leader>a< <Plug>AM_a<|endif +if !hasmapto('<Plug>AM_a=') |map <unique> <Leader>a= <Plug>AM_a=|endif +if !hasmapto('<Plug>AM_a(') |map <unique> <Leader>a( <Plug>AM_a(|endif +if !hasmapto('<Plug>AM_abox') |map <unique> <Leader>abox <Plug>AM_abox|endif +if !hasmapto('<Plug>AM_acom') |map <unique> <Leader>acom <Plug>AM_acom|endif +if !hasmapto('<Plug>AM_adcom')|map <unique> <Leader>adcom <Plug>AM_adcom|endif +if !hasmapto('<Plug>AM_aocom')|map <unique> <Leader>aocom <Plug>AM_aocom|endif +if !hasmapto('<Plug>AM_ascom')|map <unique> <Leader>ascom <Plug>AM_ascom|endif +if !hasmapto('<Plug>AM_adec') |map <unique> <Leader>adec <Plug>AM_adec|endif +if !hasmapto('<Plug>AM_adef') |map <unique> <Leader>adef <Plug>AM_adef|endif +if !hasmapto('<Plug>AM_afnc') |map <unique> <Leader>afnc <Plug>AM_afnc|endif +if !hasmapto('<Plug>AM_afnc') |map <unique> <Leader>afnc <Plug>AM_afnc|endif +if !hasmapto('<Plug>AM_aunum')|map <unique> <Leader>aunum <Plug>AM_aenum|endif +if !hasmapto('<Plug>AM_aenum')|map <unique> <Leader>aenum <Plug>AM_aunum|endif +if exists("g:alignmaps_euronumber") && !exists("g:alignmaps_usanumber") + if !hasmapto('<Plug>AM_anum')|map <unique> <Leader>anum <Plug>AM_aenum|endif +else + if !hasmapto('<Plug>AM_anum')|map <unique> <Leader>anum <Plug>AM_aunum|endif +endif + +map <silent> <script> <Plug>AM_a? <SID>WS:AlignCtrl mIp1P1lC ? : : : : <CR>:'a,.Align<CR>:'a,'z-1s/\(\s\+\)? /?\1/e<CR><SID>WE +map <silent> <script> <Plug>AM_a, <SID>WS:'y,'zs/\(\S\)\s\+/\1 /ge<CR>'yjma'zk:call AlignMaps#CharJoiner(",")<cr>:silent 'y,'zg/,/call AlignMaps#FixMultiDec()<CR>'z:exe "norm \<Plug>AM_adec"<cr><SID>WE +map <silent> <script> <Plug>AM_a< <SID>WS:AlignCtrl mIp1P1=l << >><CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_a( <SID>WS:AlignCtrl mIp0P1=l<CR>:'a,.Align [(,]<CR>:sil 'y+1,'z-1s/\(\s\+\),/,\1/ge<CR><SID>WE +map <silent> <script> <Plug>AM_a= <SID>WS:AlignCtrl mIp1P1=l<CR>:AlignCtrl g :=<CR>:'a,'zAlign :\==<CR><SID>WE +map <silent> <script> <Plug>AM_abox <SID>WS:let g:alignmaps_iws=substitute(getline("'a"),'^\(\s*\).*$','\1','e')<CR>:'a,'z-1s/^\s\+//e<CR>:'a,'z-1s/^.*$/@&@/<CR>:AlignCtrl m=p01P0w @<CR>:'a,.Align<CR>:'a,'z-1s/@/ * /<CR>:'a,'z-1s/@$/*/<CR>'aYP:s/./*/g<CR>0r/'zkYp:s/./*/g<CR>0r A/<Esc>:exe "'a-1,'z-1s/^/".g:alignmaps_iws."/e"<CR><SID>WE +map <silent> <script> <Plug>AM_acom <SID>WS:'a,.s/\/[*/]\/\=/@&@/e<CR>:'a,.s/\*\//@&/e<CR>:'y,'zs/^\( *\) @/\1@/e<CR>'zk:call AlignMaps#StdAlign(2)<CR>:'y,'zs/^\(\s*\) @/\1/e<CR>:'y,'zs/ @//eg<CR><SID>WE +map <silent> <script> <Plug>AM_adcom <SID>WS:'a,.v/^\s*\/[/*]/s/\/[*/]\*\=/@&@/e<CR>:'a,.v/^\s*\/[/*]/s/\*\//@&/e<CR>:'y,'zv/^\s*\/[/*]/s/^\( *\) @/\1@/e<CR>'zk:call AlignMaps#StdAlign(3)<cr>:'y,'zv/^\s*\/[/*]/s/^\(\s*\) @/\1/e<CR>:'y,'zs/ @//eg<CR><SID>WE +map <silent> <script> <Plug>AM_aocom <SID>WS:AlignPush<CR>:AlignCtrl g /[*/]<CR>:exe "norm \<Plug>AM_acom"<cr>:AlignPop<CR><SID>WE +map <silent> <script> <Plug>AM_ascom <SID>WS:'a,.s/\/[*/]/@&@/e<CR>:'a,.s/\*\//@&/e<CR>:silent! 'a,.g/^\s*@\/[*/]/s/@//ge<CR>:AlignCtrl v ^\s*\/[*/]<CR>:AlignCtrl g \/[*/]<CR>'zk:call AlignMaps#StdAlign(2)<cr>:'y,'zs/^\(\s*\) @/\1/e<CR>:'y,'zs/ @//eg<CR><SID>WE +map <silent> <script> <Plug>AM_adec <SID>WS:'a,'zs/\([^ \t/(]\)\([*&]\)/\1 \2/e<CR>:'y,'zv/^\//s/\([^ \t]\)\s\+/\1 /ge<CR>:'y,'zv/^\s*[*/]/s/\([^/][*&]\)\s\+/\1/ge<CR>:'y,'zv/^\s*[*/]/s/^\(\s*\%(\K\k*\s\+\%([a-zA-Z_*(&]\)\@=\)\+\)\([*(&]*\)\s*\([a-zA-Z0-9_()]\+\)\s*\(\(\[.\{-}]\)*\)\s*\(=\)\=\s*\(.\{-}\)\=\s*;/\1@\2#@\3\4@\6@\7;@/e<CR>:'y,'zv/^\s*[*/]/s/\*\/\s*$/@*\//e<CR>:'y,'zv/^\s*[*/]/s/^\s\+\*/@@@@@* /e<CR>:'y,'zv/^\s*[*/]/s/^@@@@@\*\(.*[^*/]\)$/&@*/e<CR>'yjma'zk:AlignCtrl v ^\s*[*/#]<CR>:call AlignMaps#StdAlign(1)<cr>:'y,'zv/^\s*[*/]/s/@ //ge<CR>:'y,'zv/^\s*[*/]/s/\(\s*\);/;\1/e<CR>:'y,'zv/^#/s/# //e<CR>:'y,'zv/^\s\+[*/#]/s/\([^/*]\)\(\*\+\)\( \+\)/\1\3\2/e<CR>:'y,'zv/^\s\+[*/#]/s/\((\+\)\( \+\)\*/\2\1*/e<CR>:'y,'zv/^\s\+[*/#]/s/^\(\s\+\) \*/\1*/e<CR>:'y,'zv/^\s\+[*/#]/s/[ \t@]*$//e<CR>:'y,'zs/^[*]/ */e<CR><SID>WE +map <silent> <script> <Plug>AM_adef <SID>WS:AlignPush<CR>:AlignCtrl v ^\s*\(\/\*\<bar>\/\/\)<CR>:'a,.v/^\s*\(\/\*\<bar>\/\/\)/s/^\(\s*\)#\(\s\)*define\s*\(\I[a-zA-Z_0-9(),]*\)\s*\(.\{-}\)\($\<Bar>\/\*\)/#\1\2define @\3@\4@\5/e<CR>:'a,.v/^\s*\(\/\*\<bar>\/\/\)/s/\($\<Bar>\*\/\)/@&/e<CR>'zk:call AlignMaps#StdAlign(1)<cr>'yjma'zk:'a,.v/^\s*\(\/\*\<bar>\/\/\)/s/ @//g<CR><SID>WE +map <silent> <script> <Plug>AM_afnc :<c-u>set lz<CR>:silent call AlignMaps#Afnc()<CR>:set nolz<CR> +map <silent> <script> <Plug>AM_aunum <SID>WS:'a,'zs/\%([0-9.]\)\s\+\zs\([-+.]\=\d\)/@\1/ge<CR>:'a,'zs/\(\(^\|\s\)\d\+\)\(\s\+\)@/\1@\3@/ge<CR>:'a,'zs/\.@/\.0@/ge<CR>:AlignCtrl wmp0P0r<CR>:'a,'zAlign [.@]<CR>:'a,'zs/@/ /ge<CR>:'a,'zs/\(\.\)\(\s\+\)\([0-9.,eE+]\+\)/\1\3\2/ge<CR>:'a,'zs/\([eE]\)\(\s\+\)\([0-9+\-+]\+\)/\1\3\2/ge<CR><SID>WE +map <silent> <script> <Plug>AM_aenum <SID>WS:'a,'zs/\%([0-9.]\)\s\+\([-+]\=\d\)/\1@\2/ge<CR>:'a,'zs/\.@/\.0@/ge<CR>:AlignCtrl wmp0P0r<CR>:'a,'zAlign [,@]<CR>:'a,'zs/@/ /ge<CR>:'a,'zs/\(,\)\(\s\+\)\([-0-9.,eE+]\+\)/\1\3\2/ge<CR>:'a,'zs/\([eE]\)\(\s\+\)\([0-9+\-+]\+\)/\1\3\2/ge<CR><SID>WE + +" --------------------------------------------------------------------- +" html table alignment {{{2 +if !hasmapto('<Plug>AM_Htd')|map <unique> <Leader>Htd <Plug>AM_Htd|endif +map <silent> <script> <Plug>AM_Htd <SID>WS:'y,'zs%<[tT][rR]><[tT][dD][^>]\{-}>\<Bar></[tT][dD]><[tT][dD][^>]\{-}>\<Bar></[tT][dD]></[tT][rR]>%@&@%g<CR>'yjma'zk:AlignCtrl m=Ilp1P0 @<CR>:'a,.Align<CR>:'y,'zs/ @/@/<CR>:'y,'zs/@ <[tT][rR]>/<[tT][rR]>/ge<CR>:'y,'zs/@//ge<CR><SID>WE + +" --------------------------------------------------------------------- +" character-based right-justified alignment maps {{{2 +if !hasmapto('<Plug>AM_T|')|map <unique> <Leader>T| <Plug>AM_T||endif +if !hasmapto('<Plug>AM_T#') |map <unique> <Leader>T# <Plug>AM_T#|endif +if !hasmapto('<Plug>AM_T,') |map <unique> <Leader>T, <Plug>AM_T,o|endif +if !hasmapto('<Plug>AM_Ts,') |map <unique> <Leader>Ts, <Plug>AM_Ts,|endif +if !hasmapto('<Plug>AM_T:') |map <unique> <Leader>T: <Plug>AM_T:|endif +if !hasmapto('<Plug>AM_T;') |map <unique> <Leader>T; <Plug>AM_T;|endif +if !hasmapto('<Plug>AM_T<') |map <unique> <Leader>T< <Plug>AM_T<|endif +if !hasmapto('<Plug>AM_T=') |map <unique> <Leader>T= <Plug>AM_T=|endif +if !hasmapto('<Plug>AM_T?') |map <unique> <Leader>T? <Plug>AM_T?|endif +if !hasmapto('<Plug>AM_T@') |map <unique> <Leader>T@ <Plug>AM_T@|endif +if !hasmapto('<Plug>AM_Tab') |map <unique> <Leader>Tab <Plug>AM_Tab|endif +if !hasmapto('<Plug>AM_Tsp') |map <unique> <Leader>Tsp <Plug>AM_Tsp|endif +if !hasmapto('<Plug>AM_T~') |map <unique> <Leader>T~ <Plug>AM_T~|endif + +map <silent> <script> <Plug>AM_T| <SID>WS:AlignCtrl mIp0P0=r <Bar><CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_T# <SID>WS:AlignCtrl mIp0P0=r #<CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_T, <SID>WS:AlignCtrl mIp0P1=r ,<CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_Ts, <SID>WS:AlignCtrl mIp0P1=r ,<CR>:'a,.Align<CR>:'a,.s/\(\s*\),/,\1/ge<CR><SID>WE +map <silent> <script> <Plug>AM_T: <SID>WS:AlignCtrl mIp1P1=r :<CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_T; <SID>WS:AlignCtrl mIp0P0=r ;<CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_T< <SID>WS:AlignCtrl mIp0P0=r <<CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_T= <SID>WS:'a,'z-1s/\s\+\([*/+\-%<Bar>&\~^]\==\)/ \1/e<CR>:'a,'z-1s@ \+\([*/+\-%<Bar>&\~^]\)=@\1=@ge<CR>:'a,'z-1s/; */;@/e<CR>:'a,'z-1s/==/\="\<Char-0x0f>\<Char-0x0f>"/ge<CR>:'a,'z-1s/!=/\x="!\<Char-0x0f>"/ge<CR>:AlignCtrl mIp1P1=r = @<CR>:AlignCtrl g =<CR>:'a,'z-1Align<CR>:'a,'z-1s/; *@/;/e<CR>:'a,'z-1s/; *$/;/e<CR>:'a,'z-1s@\([*/+\-%<Bar>&\~^]\)\( \+\)=@\2\1=@ge<CR>:'a,'z-1s/\( \+\);/;\1/ge<CR>:'a,'z-1s/\xff/=/ge<CR><SID>WE:exe "norm <Plug>acom" +map <silent> <script> <Plug>AM_T? <SID>WS:AlignCtrl mIp0P0=r ?<CR>:'a,.Align<CR>:'y,'zs/ \( *\);/;\1/ge<CR><SID>WE +map <silent> <script> <Plug>AM_T@ <SID>WS:AlignCtrl mIp0P0=r @<CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_Tab <SID>WS:'a,.s/^\(\t*\)\(.*\)/\=submatch(1).escape(substitute(submatch(2),'\t','@','g'),'\')/<CR>:AlignCtrl mI=r @<CR>:'a,.Align<CR>:'y+1,'z-1s/@/ /g<CR><SID>WE +map <silent> <script> <Plug>AM_Tsp <SID>WS:'a,.s/^\(\s*\)\(.*\)/\=submatch(1).escape(substitute(submatch(2),'\s\+','@','g'),'\')/<CR>:AlignCtrl mI=r @<CR>:'a,.Align<CR>:'y+1,'z-1s/@/ /g<CR><SID>WE +map <silent> <script> <Plug>AM_T~ <SID>WS:AlignCtrl mIp0P0=r ~<CR>:'a,.Align<CR>:'y,'zs/ \( *\);/;\1/ge<CR><SID>WE + +" --------------------------------------------------------------------- +" character-based left-justified alignment maps {{{2 +if !hasmapto('<Plug>AM_t|') |map <unique> <Leader>t| <Plug>AM_t||endif +if !hasmapto('<Plug>AM_t#') |map <unique> <Leader>t# <Plug>AM_t#|endif +if !hasmapto('<Plug>AM_t,') |map <unique> <Leader>t, <Plug>AM_t,|endif +if !hasmapto('<Plug>AM_t:') |map <unique> <Leader>t: <Plug>AM_t:|endif +if !hasmapto('<Plug>AM_t;') |map <unique> <Leader>t; <Plug>AM_t;|endif +if !hasmapto('<Plug>AM_t<') |map <unique> <Leader>t< <Plug>AM_t<|endif +if !hasmapto('<Plug>AM_t=') |map <unique> <Leader>t= <Plug>AM_t=|endif +if !hasmapto('<Plug>AM_ts,') |map <unique> <Leader>ts, <Plug>AM_ts,|endif +if !hasmapto('<Plug>AM_ts:') |map <unique> <Leader>ts: <Plug>AM_ts:|endif +if !hasmapto('<Plug>AM_ts;') |map <unique> <Leader>ts; <Plug>AM_ts;|endif +if !hasmapto('<Plug>AM_ts<') |map <unique> <Leader>ts< <Plug>AM_ts<|endif +if !hasmapto('<Plug>AM_ts=') |map <unique> <Leader>ts= <Plug>AM_ts=|endif +if !hasmapto('<Plug>AM_w=') |map <unique> <Leader>w= <Plug>AM_w=|endif +if !hasmapto('<Plug>AM_t?') |map <unique> <Leader>t? <Plug>AM_t?|endif +if !hasmapto('<Plug>AM_t~') |map <unique> <Leader>t~ <Plug>AM_t~|endif +if !hasmapto('<Plug>AM_t@') |map <unique> <Leader>t@ <Plug>AM_t@|endif +if !hasmapto('<Plug>AM_m=') |map <unique> <Leader>m= <Plug>AM_m=|endif +if !hasmapto('<Plug>AM_tab') |map <unique> <Leader>tab <Plug>AM_tab|endif +if !hasmapto('<Plug>AM_tml') |map <unique> <Leader>tml <Plug>AM_tml|endif +if !hasmapto('<Plug>AM_tsp') |map <unique> <Leader>tsp <Plug>AM_tsp|endif +if !hasmapto('<Plug>AM_tsq') |map <unique> <Leader>tsq <Plug>AM_tsq|endif +if !hasmapto('<Plug>AM_tt') |map <unique> <Leader>tt <Plug>AM_tt|endif + +map <silent> <script> <Plug>AM_t| <SID>WS:AlignCtrl mIp0P0=l <Bar><CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_t# <SID>WS:AlignCtrl mIp0P0=l #<CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_t, <SID>WS:AlignCtrl mIp0P1=l ,<CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_t: <SID>WS:AlignCtrl mIp1P1=l :<CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_t; <SID>WS:AlignCtrl mIp0P1=l ;<CR>:'a,.Align<CR>:sil 'y,'zs/\( *\);/;\1/ge<CR><SID>WE +map <silent> <script> <Plug>AM_t< <SID>WS:AlignCtrl mIp0P0=l <<CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_t= <SID>WS:call AlignMaps#Equals()<CR><SID>WE +map <silent> <script> <Plug>AM_ts, <SID>WS:AlignCtrl mIp0P1=l #<CR>:'a,.Align<CR>:sil 'y+1,'z-1s/\(\s*\)#/,\1/ge<CR><SID>WE +map <silent> <script> <Plug>AM_ts, <SID>WS:AlignCtrl mIp0P1=l ,<CR>:'a,.Align<CR>:sil 'y+1,'z-1s/\(\s*\),/,\1/ge<CR><SID>WE +map <silent> <script> <Plug>AM_ts: <SID>WS:AlignCtrl mIp1P1=l :<CR>:'a,.Align<CR>:sil 'y+1,'z-1s/\(\s*\):/:\1/ge<CR><SID>WE +map <silent> <script> <Plug>AM_ts; <SID>WS:AlignCtrl mIp1P1=l ;<CR>:'a,.Align<CR>:sil 'y+1,'z-1s/\(\s*\);/;\1/ge<CR><SID>WE +map <silent> <script> <Plug>AM_ts< <SID>WS:AlignCtrl mIp1P1=l <<CR>:'a,.Align<CR>:sil 'y+1,'z-1s/\(\s*\)</<\1/ge<CR><SID>WE +map <silent> <script> <Plug>AM_ts= <SID>WS:AlignCtrl mIp1P1=l =<CR>:'a,.Align<CR>:sil 'y+1,'z-1s/\(\s*\)=/=\1/ge<CR><SID>WE +map <silent> <script> <Plug>AM_w= <SID>WS:'a,'zg/=/s/\s\+\([*/+\-%<Bar>&\~^]\==\)/ \1/e<CR>:'a,'zg/=/s@ \+\([*/+\-%<Bar>&\~^]\)=@\1=@ge<CR>:'a,'zg/=/s/==/\="\<Char-0x0f>\<Char-0x0f>"/ge<CR>:'a,'zg/=/s/!=/\="!\<Char-0x0f>"/ge<CR>'zk:AlignCtrl mWp1P1=l =<CR>:AlignCtrl g =<CR>:'a,'z-1g/=/Align<CR>:'a,'z-1g/=/s@\([*/+\-%<Bar>&\~^!=]\)\( \+\)=@\2\1=@ge<CR>:'a,'z-1g/=/s/\( \+\);/;\1/ge<CR>:'a,'z-1v/^\s*\/[*/]/s/\/[*/]/@&@/e<CR>:'a,'z-1v/^\s*\/[*/]/s/\*\//@&/e<CR>'zk:call AlignMaps#StdAlign(1)<cr>:'y,'zs/^\(\s*\) @/\1/e<CR>:'a,'z-1g/=/s/\xff/=/ge<CR>:'y,'zg/=/s/ @//eg<CR><SID>WE +map <silent> <script> <Plug>AM_t? <SID>WS:AlignCtrl mIp0P0=l ?<CR>:'a,.Align<CR>:.,'zs/ \( *\);/;\1/ge<CR><SID>WE +map <silent> <script> <Plug>AM_t~ <SID>WS:AlignCtrl mIp0P0=l ~<CR>:'a,.Align<CR>:'y,'zs/ \( *\);/;\1/ge<CR><SID>WE +map <silent> <script> <Plug>AM_t@ <SID>WS::call AlignMaps#StdAlign(1)<cr>:<SID>WE +map <silent> <script> <Plug>AM_m= <SID>WS:'a,'zs/\s\+\([*/+\-%<Bar>&\~^]\==\)/ \1/e<CR>:'a,'zs@ \+\([*/+\-%<Bar>&\~^]\)=@\1=@ge<CR>:'a,'zs/==/\="\<Char-0x0f>\<Char-0x0f>"/ge<CR>:'a,'zs/!=/\="!\<Char-0x0f>"/ge<CR>'zk:AlignCtrl mIp1P1=l =<CR>:AlignCtrl g =<CR>:'a,'z-1Align<CR>:'a,'z-1s@\([*/+\-%<Bar>&\~^!=]\)\( \+\)=@\2\1=@ge<CR>:'a,'z-1s/\( \+\);/;\1/ge<CR>:'a,'z-s/%\ze[^=]/ @%@ /e<CR>'zk:call AlignMaps#StdAlign(1)<cr>:'y,'zs/^\(\s*\) @/\1/e<CR>:'a,'z-1s/\xff/=/ge<CR>:'y,'zs/ @//eg<CR><SID>WE +map <silent> <script> <Plug>AM_tab <SID>WS:'a,.s/^\(\t*\)\(.*\)$/\=submatch(1).escape(substitute(submatch(2),'\t',"\<Char-0x0f>",'g'),'\')/<CR>:if &ts == 1<bar>exe "AlignCtrl mI=lp0P0 \<Char-0x0f>"<bar>else<bar>exe "AlignCtrl mI=l \<Char-0x0f>"<bar>endif<CR>:'a,.Align<CR>:exe "'y+1,'z-1s/\<Char-0x0f>/".((&ts == 1)? '\t' : ' ')."/g"<CR><SID>WE +map <silent> <script> <Plug>AM_tml <SID>WS:AlignCtrl mWp1P0=l \\\@<!\\\s*$<CR>:'a,.Align<CR><SID>WE +map <silent> <script> <Plug>AM_tsp <SID>WS:'a,.s/^\(\s*\)\(.*\)/\=submatch(1).escape(substitute(submatch(2),'\s\+','@','g'),'\')/<CR>:AlignCtrl mI=lp0P0 @<CR>:'a,.Align<CR>:'y+1,'z-1s/@/ /g<CR><SID>WE +map <silent> <script> <Plug>AM_tsq <SID>WS:'a,.AlignReplaceQuotedSpaces<CR>:'a,.s/^\(\s*\)\(.*\)/\=submatch(1).substitute(submatch(2),'\s\+','@','g')/<CR>:AlignCtrl mIp0P0=l @<CR>:'a,.Align<CR>:'y+1,'z-1s/[%@]/ /g<CR><SID>WE +map <silent> <script> <Plug>AM_tt <SID>WS:AlignCtrl mIp1P1=l \\\@<!& \\\\<CR>:'a,.Align<CR><SID>WE + +" ===================================================================== +" Menu Support: {{{1 +" ma ..move.. use menu +" v V or ctrl-v ..move.. use menu +if has("menu") && has("gui_running") && &go =~ 'm' && !exists("s:firstmenu") + let s:firstmenu= 1 + if !exists("g:DrChipTopLvlMenu") + let g:DrChipTopLvlMenu= "DrChip." + endif + if g:DrChipTopLvlMenu != "" + let s:mapleader = exists("g:mapleader")? g:mapleader : '\' + let s:emapleader= escape(s:mapleader,'\ ') + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.<<\ and\ >><tab>'.s:emapleader.'a< '.s:mapleader.'a<' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Assignment\ =<tab>'.s:emapleader.'t= '.s:mapleader.'t=' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Assignment\ :=<tab>'.s:emapleader.'a= '.s:mapleader.'a=' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Backslashes<tab>'.s:emapleader.'tml '.s:mapleader.'tml' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Breakup\ Comma\ Declarations<tab>'.s:emapleader.'a, '.s:mapleader.'a,' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.C\ Comment\ Box<tab>'.s:emapleader.'abox '.s:mapleader.'abox' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Commas<tab>'.s:emapleader.'t, '.s:mapleader.'t,' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Commas<tab>'.s:emapleader.'ts, '.s:mapleader.'ts,' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Commas\ With\ Strings<tab>'.s:emapleader.'tsq '.s:mapleader.'tsq' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Comments<tab>'.s:emapleader.'acom '.s:mapleader.'acom' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Comments\ Only<tab>'.s:emapleader.'aocom '.s:mapleader.'aocom' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Declaration\ Comments<tab>'.s:emapleader.'adcom '.s:mapleader.'adcom' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Declarations<tab>'.s:emapleader.'adec '.s:mapleader.'adec' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Definitions<tab>'.s:emapleader.'adef '.s:mapleader.'adef' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Function\ Header<tab>'.s:emapleader.'afnc '.s:mapleader.'afnc' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Html\ Tables<tab>'.s:emapleader.'Htd '.s:mapleader.'Htd' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.(\.\.\.)?\.\.\.\ :\ \.\.\.<tab>'.s:emapleader.'a? '.s:mapleader.'a?' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Numbers<tab>'.s:emapleader.'anum '.s:mapleader.'anum' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Numbers\ (American-Style)<tab>'.s:emapleader.'aunum <Leader>aunum '.s:mapleader.'aunum <Leader>aunum' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Numbers\ (Euro-Style)<tab>'.s:emapleader.'aenum '.s:mapleader.'aenum' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Spaces\ (Left\ Justified)<tab>'.s:emapleader.'tsp '.s:mapleader.'tsp' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Spaces\ (Right\ Justified)<tab>'.s:emapleader.'Tsp '.s:mapleader.'Tsp' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Statements\ With\ Percent\ Style\ Comments<tab>'.s:emapleader.'m= '.s:mapleader.'m=' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Symbol\ <<tab>'.s:emapleader.'t< '.s:mapleader.'t<' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Symbol\ \|<tab>'.s:emapleader.'t\| '.s:mapleader.'t|' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Symbol\ @<tab>'.s:emapleader.'t@ '.s:mapleader.'t@' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Symbol\ #<tab>'.s:emapleader.'t# '.s:mapleader.'t#' + exe 'menu '.g:DrChipTopLvlMenu.'AlignMaps.Tabs<tab>'.s:emapleader.'tab '.s:mapleader.'tab' + unlet s:mapleader + unlet s:emapleader + endif +endif + +" ===================================================================== +" Restore: {{{1 +let &cpo= s:keepcpo +unlet s:keepcpo + +" ============================================================================== +" Modelines: {{{1 +" vim: ts=4 nowrap fdm=marker diff --git a/vim/plugin/AlignPlugin.vim b/vim/plugin/AlignPlugin.vim new file mode 100644 index 0000000..727fe7e --- /dev/null +++ b/vim/plugin/AlignPlugin.vim @@ -0,0 +1,41 @@ +" AlignPlugin: tool to align multiple fields based on one or more separators +" Author: Charles E. Campbell, Jr. +" Date: Nov 02, 2008 +" GetLatestVimScripts: 294 1 :AutoInstall: Align.vim +" GetLatestVimScripts: 1066 1 :AutoInstall: cecutil.vim +" Copyright: Copyright (C) 1999-2007 Charles E. Campbell, Jr. {{{1 +" 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, +" Align.vim is provided *as is* and comes with no warranty +" of any kind, either expressed or implied. By using this +" plugin, you agree that in no event will the copyright +" holder be liable for any damages resulting from the use +" of this software. +" +" Romans 1:16,17a : For I am not ashamed of the gospel of Christ, for it is {{{1 +" the power of God for salvation for everyone who believes; for the Jew first, +" and also for the Greek. For in it is revealed God's righteousness from +" faith to faith. +" --------------------------------------------------------------------- +" Load Once: {{{1 +if &cp || exists("g:loaded_AlignPlugin") + finish +endif +let g:loaded_AlignPlugin = "v35" +let s:keepcpo = &cpo +set cpo&vim + +" --------------------------------------------------------------------- +" Public Interface: {{{1 +com! -bang -range -nargs=* Align <line1>,<line2>call Align#Align(<bang>0,<q-args>) +com! -range -nargs=0 AlignReplaceQuotedSpaces <line1>,<line2>call Align#AlignReplaceQuotedSpaces() +com! -nargs=* AlignCtrl call Align#AlignCtrl(<q-args>) +com! -nargs=0 AlignPush call Align#AlignPush() +com! -nargs=0 AlignPop call Align#AlignPop() + +" --------------------------------------------------------------------- +" Restore: {{{1 +let &cpo= s:keepcpo +unlet s:keepcpo +" vim: ts=4 fdm=marker diff --git a/vim/plugin/EasyMotion.vim b/vim/plugin/EasyMotion.vim new file mode 100755 index 0000000..fff29dc --- /dev/null +++ b/vim/plugin/EasyMotion.vim @@ -0,0 +1,73 @@ +" EasyMotion - Vim motions on speed! +" +" Author: Kim Silkebækken <kim.silkebaekken+vim@gmail.com> +" Source repository: https://github.com/Lokaltog/vim-easymotion + +" Script initialization {{{ + if exists('g:EasyMotion_loaded') || &compatible || version < 702 + finish + endif + + let g:EasyMotion_loaded = 1 +" }}} +" Default configuration {{{ + " Default options {{{ + call EasyMotion#InitOptions({ + \ 'leader_key' : '<Leader><Leader>' + \ , 'keys' : 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + \ , 'do_shade' : 1 + \ , 'do_mapping' : 1 + \ , 'grouping' : 1 + \ + \ , 'hl_group_target' : 'EasyMotionTarget' + \ , 'hl_group_shade' : 'EasyMotionShade' + \ }) + " }}} + " Default highlighting {{{ + let s:target_hl_defaults = { + \ 'gui' : ['NONE', '#ff0000' , 'bold'] + \ , 'cterm256': ['NONE', '196' , 'bold'] + \ , 'cterm' : ['NONE', 'red' , 'bold'] + \ } + + let s:shade_hl_defaults = { + \ 'gui' : ['NONE', '#777777' , 'NONE'] + \ , 'cterm256': ['NONE', '242' , 'NONE'] + \ , 'cterm' : ['NONE', 'grey' , 'NONE'] + \ } + + call EasyMotion#InitHL(g:EasyMotion_hl_group_target, s:target_hl_defaults) + call EasyMotion#InitHL(g:EasyMotion_hl_group_shade, s:shade_hl_defaults) + + " Reset highlighting after loading a new color scheme {{{ + augroup EasyMotionInitHL + autocmd! + + autocmd ColorScheme * call EasyMotion#InitHL(g:EasyMotion_hl_group_target, s:target_hl_defaults) + autocmd ColorScheme * call EasyMotion#InitHL(g:EasyMotion_hl_group_shade, s:shade_hl_defaults) + augroup end + " }}} + " }}} + " Default key mapping {{{ + call EasyMotion#InitMappings({ + \ 'f' : { 'name': 'F' , 'dir': 0 } + \ , 'F' : { 'name': 'F' , 'dir': 1 } + \ , 't' : { 'name': 'T' , 'dir': 0 } + \ , 'T' : { 'name': 'T' , 'dir': 1 } + \ , 'w' : { 'name': 'WB' , 'dir': 0 } + \ , 'W' : { 'name': 'WBW', 'dir': 0 } + \ , 'b' : { 'name': 'WB' , 'dir': 1 } + \ , 'B' : { 'name': 'WBW', 'dir': 1 } + \ , 'e' : { 'name': 'E' , 'dir': 0 } + \ , 'E' : { 'name': 'EW' , 'dir': 0 } + \ , 'ge': { 'name': 'E' , 'dir': 1 } + \ , 'gE': { 'name': 'EW' , 'dir': 1 } + \ , 'j' : { 'name': 'JK' , 'dir': 0 } + \ , 'k' : { 'name': 'JK' , 'dir': 1 } + \ , 'n' : { 'name': 'Search' , 'dir': 0 } + \ , 'N' : { 'name': 'Search' , 'dir': 1 } + \ }) + " }}} +" }}} + +" vim: fdm=marker:noet:ts=4:sw=4:sts=4 diff --git a/vim/plugin/NERD_commenter.vim b/vim/plugin/NERD_commenter.vim new file mode 100644 index 0000000..ef88dd4 --- /dev/null +++ b/vim/plugin/NERD_commenter.vim @@ -0,0 +1,2790 @@ +" ============================================================================ +" File: NERD_commenter.vim +" Description: vim global plugin that provides easy code commenting +" Maintainer: Martin Grenfell <martin.grenfell at gmail dot com> +" Version: 2.3.0 +" Last Change: 08th December, 2010 +" 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 + +" 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:NERDSpaceDelims", 0) +call s:InitVariable("g:NERDDelimiterRequests", 1) + +let s:NERDFileNameEscape="[]#*$%'\" ?`!&();<>\\" +"vf ;;dA:hcs"'A {j^f(lyi(k$p0f{a A }0f{a 'left':jdd^ + +let s:delimiterMap = { + \ 'aap': { 'left': '#' }, + \ 'abc': { 'left': '%' }, + \ 'acedb': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'actionscript': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'ada': { 'left': '--', 'leftAlt': '-- ' }, + \ 'ahdl': { 'left': '--' }, + \ 'ahk': { 'left': ';', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'amiga': { 'left': ';' }, + \ 'aml': { 'left': '/*' }, + \ 'ampl': { 'left': '#' }, + \ 'apache': { 'left': '#' }, + \ 'apachestyle': { 'left': '#' }, + \ 'asciidoc': { 'left': '//' }, + \ 'applescript': { 'left': '--', 'leftAlt': '(*', 'rightAlt': '*)' }, + \ 'asm68k': { 'left': ';' }, + \ 'asm': { 'left': ';', 'leftAlt': '#' }, + \ 'asn': { 'left': '--' }, + \ 'aspvbs': { 'left': '''' }, + \ 'asterisk': { 'left': ';' }, + \ 'asy': { 'left': '//' }, + \ 'atlas': { 'left': 'C', 'right': '$' }, + \ 'autohotkey': { 'left': ';' }, + \ 'autoit': { 'left': ';' }, + \ 'ave': { 'left': "'" }, + \ 'awk': { 'left': '#' }, + \ 'basic': { 'left': "'", 'leftAlt': 'REM ' }, + \ 'bbx': { 'left': '%' }, + \ 'bc': { 'left': '#' }, + \ 'bib': { 'left': '%' }, + \ 'bindzone': { 'left': ';' }, + \ 'bst': { 'left': '%' }, + \ 'btm': { 'left': '::' }, + \ 'caos': { 'left': '*' }, + \ 'calibre': { 'left': '//' }, + \ 'catalog': { 'left': '--', 'right': '--' }, + \ 'c': { 'left': '/*','right': '*/', 'leftAlt': '//' }, + \ 'cfg': { 'left': '#' }, + \ 'cg': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'ch': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'cl': { 'left': '#' }, + \ 'clean': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'clipper': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'clojure': { 'left': ';' }, + \ 'cmake': { 'left': '#' }, + \ 'conkyrc': { 'left': '#' }, + \ 'cpp': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'crontab': { 'left': '#' }, + \ 'cs': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'csp': { 'left': '--' }, + \ 'cterm': { 'left': '*' }, + \ 'cucumber': { 'left': '#' }, + \ 'cvs': { 'left': 'CVS:' }, + \ 'd': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'dcl': { 'left': '$!' }, + \ 'dakota': { 'left': '#' }, + \ 'debcontrol': { 'left': '#' }, + \ 'debsources': { 'left': '#' }, + \ 'def': { 'left': ';' }, + \ 'desktop': { 'left': '#' }, + \ 'dhcpd': { 'left': '#' }, + \ 'diff': { 'left': '#' }, + \ 'django': { 'left': '<!--','right': '-->', 'leftAlt': '{#', 'rightAlt': '#}' }, + \ 'docbk': { 'left': '<!--', 'right': '-->' }, + \ 'dns': { 'left': ';' }, + \ 'dosbatch': { 'left': 'REM ', 'leftAlt': '::' }, + \ 'dosini': { 'left': ';' }, + \ 'dot': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'dracula': { 'left': ';' }, + \ 'dsl': { 'left': ';' }, + \ 'dtml': { 'left': '<dtml-comment>', 'right': '</dtml-comment>' }, + \ 'dylan': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'ebuild': { 'left': '#' }, + \ 'ecd': { 'left': '#' }, + \ 'eclass': { 'left': '#' }, + \ 'eiffel': { 'left': '--' }, + \ 'elf': { 'left': "'" }, + \ 'elmfilt': { 'left': '#' }, + \ 'erlang': { 'left': '%' }, + \ 'eruby': { 'left': '<%#', 'right': '%>', 'leftAlt': '<!--', 'rightAlt': '-->' }, + \ 'expect': { 'left': '#' }, + \ 'exports': { 'left': '#' }, + \ 'factor': { 'left': '! ', 'leftAlt': '!# ' }, + \ 'fgl': { 'left': '#' }, + \ 'focexec': { 'left': '-*' }, + \ 'form': { 'left': '*' }, + \ 'foxpro': { 'left': '*' }, + \ 'fstab': { 'left': '#' }, + \ 'fvwm': { 'left': '#' }, + \ 'fx': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'gams': { 'left': '*' }, + \ 'gdb': { 'left': '#' }, + \ 'gdmo': { 'left': '--' }, + \ 'geek': { 'left': 'GEEK_COMMENT:' }, + \ 'genshi': { 'left': '<!--','right': '-->', 'leftAlt': '{#', 'rightAlt': '#}' }, + \ 'gentoo-conf-d': { 'left': '#' }, + \ 'gentoo-env-d': { 'left': '#' }, + \ 'gentoo-init-d': { 'left': '#' }, + \ 'gentoo-make-conf': { 'left': '#' }, + \ 'gentoo-package-keywords': { 'left': '#' }, + \ 'gentoo-package-mask': { 'left': '#' }, + \ 'gentoo-package-use': { 'left': '#' }, + \ 'gitcommit': { 'left': '#' }, + \ 'gitconfig': { 'left': ';' }, + \ 'gitrebase': { 'left': '#' }, + \ 'gnuplot': { 'left': '#' }, + \ 'groovy': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'gsp': { 'left': '<%--', 'right': '--%>' }, + \ 'gtkrc': { 'left': '#' }, + \ 'haskell': { 'left': '{-','right': '-}', 'leftAlt': '--' }, + \ 'hb': { 'left': '#' }, + \ 'h': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'haml': { 'left': '-#', 'leftAlt': '/' }, + \ 'hercules': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'hog': { 'left': '#' }, + \ 'hostsaccess': { 'left': '#' }, + \ 'htmlcheetah': { 'left': '##' }, + \ 'htmldjango': { 'left': '<!--','right': '-->', 'leftAlt': '{#', 'rightAlt': '#}' }, + \ 'htmlos': { 'left': '#', 'right': '/#' }, + \ 'ia64': { 'left': '#' }, + \ 'icon': { 'left': '#' }, + \ 'idlang': { 'left': ';' }, + \ 'idl': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'inform': { 'left': '!' }, + \ 'inittab': { 'left': '#' }, + \ 'ishd': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'iss': { 'left': ';' }, + \ 'ist': { 'left': '%' }, + \ 'java': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'javacc': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'javascript': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'javascript.jquery': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'jess': { 'left': ';' }, + \ 'jgraph': { 'left': '(*', 'right': '*)' }, + \ 'jproperties': { 'left': '#' }, + \ 'jsp': { 'left': '<%--', 'right': '--%>' }, + \ 'kix': { 'left': ';' }, + \ 'kscript': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'lace': { 'left': '--' }, + \ 'ldif': { 'left': '#' }, + \ 'lilo': { 'left': '#' }, + \ 'lilypond': { 'left': '%' }, + \ 'liquid': { 'left': '{%', 'right': '%}' }, + \ 'lisp': { 'left': ';', 'leftAlt': '#|', 'rightAlt': '|#' }, + \ 'llvm': { 'left': ';' }, + \ 'lotos': { 'left': '(*', 'right': '*)' }, + \ 'lout': { 'left': '#' }, + \ 'lprolog': { 'left': '%' }, + \ 'lscript': { 'left': "'" }, + \ 'lss': { 'left': '#' }, + \ 'lua': { 'left': '--', 'leftAlt': '--[[', 'rightAlt': ']]' }, + \ 'lynx': { 'left': '#' }, + \ 'lytex': { 'left': '%' }, + \ 'mail': { 'left': '> ' }, + \ 'mako': { 'left': '##' }, + \ 'man': { 'left': '."' }, + \ 'map': { 'left': '%' }, + \ 'maple': { 'left': '#' }, + \ 'markdown': { 'left': '<!--', 'right': '-->' }, + \ 'masm': { 'left': ';' }, + \ 'mason': { 'left': '<% #', 'right': '%>' }, + \ 'master': { 'left': '$' }, + \ 'matlab': { 'left': '%' }, + \ 'mel': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'mib': { 'left': '--' }, + \ 'mkd': { 'left': '>' }, + \ 'mma': { 'left': '(*', 'right': '*)' }, + \ 'model': { 'left': '$', 'right': '$' }, + \ 'moduala.': { 'left': '(*', 'right': '*)' }, + \ 'modula2': { 'left': '(*', 'right': '*)' }, + \ 'modula3': { 'left': '(*', 'right': '*)' }, + \ 'monk': { 'left': ';' }, + \ 'mush': { 'left': '#' }, + \ 'named': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'nasm': { 'left': ';' }, + \ 'nastran': { 'left': '$' }, + \ 'natural': { 'left': '/*' }, + \ 'ncf': { 'left': ';' }, + \ 'newlisp': { 'left': ';' }, + \ 'nroff': { 'left': '\"' }, + \ 'nsis': { 'left': '#' }, + \ 'ntp': { 'left': '#' }, + \ 'objc': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'objcpp': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'objj': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'ocaml': { 'left': '(*', 'right': '*)' }, + \ 'occam': { 'left': '--' }, + \ 'omlet': { 'left': '(*', 'right': '*)' }, + \ 'omnimark': { 'left': ';' }, + \ 'openroad': { 'left': '//' }, + \ 'opl': { 'left': "REM" }, + \ 'ora': { 'left': '#' }, + \ 'ox': { 'left': '//' }, + \ 'pascal': { 'left': '{','right': '}', 'leftAlt': '(*', 'rightAlt': '*)' }, + \ 'patran': { 'left': '$', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'pcap': { 'left': '#' }, + \ 'pccts': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'pdf': { 'left': '%' }, + \ 'pfmain': { 'left': '//' }, + \ 'php': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'pic': { 'left': ';' }, + \ 'pike': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'pilrc': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'pine': { 'left': '#' }, + \ 'plm': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'plsql': { 'left': '--', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'po': { 'left': '#' }, + \ 'postscr': { 'left': '%' }, + \ 'pov': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'povini': { 'left': ';' }, + \ 'ppd': { 'left': '%' }, + \ 'ppwiz': { 'left': ';;' }, + \ 'processing': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'prolog': { 'left': '%', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'ps1': { 'left': '#' }, + \ 'psf': { 'left': '#' }, + \ 'ptcap': { 'left': '#' }, + \ 'python': { 'left': '#' }, + \ 'radiance': { 'left': '#' }, + \ 'ratpoison': { 'left': '#' }, + \ 'r': { 'left': '#' }, + \ 'rc': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'rebol': { 'left': ';' }, + \ 'registry': { 'left': ';' }, + \ 'remind': { 'left': '#' }, + \ 'resolv': { 'left': '#' }, + \ 'rgb': { 'left': '!' }, + \ 'rib': { 'left': '#' }, + \ 'robots': { 'left': '#' }, + \ 'sa': { 'left': '--' }, + \ 'samba': { 'left': ';', 'leftAlt': '#' }, + \ 'sass': { 'left': '//', 'leftAlt': '/*' }, + \ 'sather': { 'left': '--' }, + \ 'scala': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'scilab': { 'left': '//' }, + \ 'scsh': { 'left': ';' }, + \ 'sed': { 'left': '#' }, + \ 'sgmldecl': { 'left': '--', 'right': '--' }, + \ 'sgmllnx': { 'left': '<!--', 'right': '-->' }, + \ 'sicad': { 'left': '*' }, + \ 'simula': { 'left': '%', 'leftAlt': '--' }, + \ 'sinda': { 'left': '$' }, + \ 'skill': { 'left': ';' }, + \ 'slang': { 'left': '%' }, + \ 'slice': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'slrnrc': { 'left': '%' }, + \ 'sm': { 'left': '#' }, + \ 'smarty': { 'left': '{*', 'right': '*}' }, + \ 'smil': { 'left': '<!', 'right': '>' }, + \ 'smith': { 'left': ';' }, + \ 'sml': { 'left': '(*', 'right': '*)' }, + \ 'snnsnet': { 'left': '#' }, + \ 'snnspat': { 'left': '#' }, + \ 'snnsres': { 'left': '#' }, + \ 'snobol4': { 'left': '*' }, + \ 'spec': { 'left': '#' }, + \ 'specman': { 'left': '//' }, + \ 'spectre': { 'left': '//', 'leftAlt': '*' }, + \ 'spice': { 'left': '$' }, + \ 'sql': { 'left': '--' }, + \ 'sqlforms': { 'left': '--' }, + \ 'sqlj': { 'left': '--' }, + \ 'sqr': { 'left': '!' }, + \ 'squid': { 'left': '#' }, + \ 'st': { 'left': '"' }, + \ 'stp': { 'left': '--' }, + \ 'systemverilog': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'tads': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'tags': { 'left': ';' }, + \ 'tak': { 'left': '$' }, + \ 'tasm': { 'left': ';' }, + \ 'tcl': { 'left': '#' }, + \ 'texinfo': { 'left': "@c " }, + \ 'texmf': { 'left': '%' }, + \ 'tf': { 'left': ';' }, + \ 'tidy': { 'left': '#' }, + \ 'tli': { 'left': '#' }, + \ 'tmux': { 'left': '#' }, + \ 'trasys': { 'left': "$" }, + \ 'tsalt': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'tsscl': { 'left': '#' }, + \ 'tssgm': { 'left': "comment = '", 'right': "'" }, + \ 'txt2tags': { 'left': '%' }, + \ 'uc': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'uil': { 'left': '!' }, + \ 'vb': { 'left': "'" }, + \ 'velocity': { 'left': "##", 'right': "", 'leftAlt': '#*', 'rightAlt': '*#' }, + \ 'vera': { 'left': '/*','right': '*/', 'leftAlt': '//' }, + \ 'verilog': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'verilog_systemverilog': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'vgrindefs': { 'left': '#' }, + \ 'vhdl': { 'left': '--' }, + \ 'vimperator': { 'left': '"' }, + \ 'virata': { 'left': '%' }, + \ 'vrml': { 'left': '#' }, + \ 'vsejcl': { 'left': '/*' }, + \ 'webmacro': { 'left': '##' }, + \ 'wget': { 'left': '#' }, + \ 'Wikipedia': { 'left': '<!--', 'right': '-->' }, + \ 'winbatch': { 'left': ';' }, + \ 'wml': { 'left': '#' }, + \ 'wvdial': { 'left': ';' }, + \ 'xdefaults': { 'left': '!' }, + \ 'xkb': { 'left': '//' }, + \ 'xmath': { 'left': '#' }, + \ 'xpm2': { 'left': '!' }, + \ 'xquery': { 'left': '(:', 'right': ':)' }, + \ 'z8a': { 'left': ';' } + \ } + +" 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. +" +" 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) + let b:NERDSexyComMarker = '' + + if has_key(s:delimiterMap, a:filetype) + let b:NERDCommenterDelims = s:delimiterMap[a:filetype] + for i in ['left', 'leftAlt', 'right', 'rightAlt'] + if !has_key(b:NERDCommenterDelims, i) + let b:NERDCommenterDelims[i] = '' + endif + endfor + else + let b:NERDCommenterDelims = s:CreateDelimMapFromCms() + endif + +endfunction + +function s:CreateDelimMapFromCms() + return { + \ 'left': substitute(&commentstring, '\([^ \t]*\)\s*%s.*', '\1', ''), + \ 'right': substitute(&commentstring, '.*%s\s*\(.*\)', '\1', 'g'), + \ 'leftAlt': '', + \ 'rightAlt': '' } +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:NERDCommenterDelims['leftAlt'] == '' && b:NERDCommenterDelims['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 = s:Left() + let tempRight = s:Right() + + "swap current delimiters for alternative + let b:NERDCommenterDelims['left'] = b:NERDCommenterDelims['leftAlt'] + let b:NERDCommenterDelims['right'] = b:NERDCommenterDelims['rightAlt'] + + "set the previously current delimiters to be the new alternative ones + let b:NERDCommenterDelims['leftAlt'] = tempLeft + let b:NERDCommenterDelims['rightAlt'] = tempRight + + "tell the user what comment delimiters they are now using + if a:printMsgs + call s:NerdEcho("Now using " . s:Left() . " " . s:Right() . " 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:Left({'space': 1}) + let right = s:Right({'space': 1}) + + " 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:Left({'space': 1}) + let rightSpaced = s:Right({'space': 1}) + + "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(s:Left(), theLine) + let lastRightDelim = s:LastIndexOfDelim(s:Right(), theLine) + + if firstLeftDelim != -1 && lastRightDelim != -1 + let searchStr = strpart(theLine, 0, lastRightDelim) + let searchStr = strpart(searchStr, firstLeftDelim+strlen(s:Left())) + + "replace the outter most delims in searchStr with + "place-holders + let theLineWithPlaceHolders = s:ReplaceDelims(s:Left(), s:Right(), g:NERDLPlace, g:NERDRPlace, searchStr) + + "add the right delimiter onto the line + let theLine = strpart(theLine, 0, firstLeftDelim+strlen(s: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:Left({'space': 1})) + + " 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(s:Left(), s:Right(), theLine) || s:IsCommented(s:Left({'alt': 1}), s:Right({'alt': 1}), 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:Left({'space': 1}), theLine, leftAlignIndx) + else + let theLine = s:AddLeftDelim(s:Left({'space': 1}), theLine) + endif + if a:align == "both" + let theLine = s:AddRightDelimAligned(s:Right({'space': 1}), theLine, rightAlignIndx) + else + let theLine = s:AddRightDelim(s:Right({'space': 1}), 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: Place holders 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' + let theLine = repeat(' ', leftAlignIndx) . left + + " Make sure tabs are respected + if !&expandtab + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:topline, theLine) + + " 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' + let theLine = repeat(' ', leftAlignIndx) . repeat(' ', strlen(left)-strlen(sexyComMarker)) . right + + " Make sure tabs are respected + if !&expandtab + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:bottomline+2, theLine) + + 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 = repeat(' ', leftAlignIndx) . repeat(' ', 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:Left({'space': 1}), theLine) + let theLine = s:AddRightDelim(s:Right({'space': 1}), 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(s: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(s:Left(), theLine) || s:IsCommentedFromStartOfLine(s:Left({'alt': 1}), 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 = sexyComBounds[1] - (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 !exists("g:did_load_ftplugin") || g:did_load_ftplugin != 1 + call s:NerdEcho("filetype plugins should be enabled. See :help NERDComInstallation and :help :filetype-plugin-on", 0) + endif + + 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(s:Left(), theLine) || s:IsCommentedFromStartOfLine(s:Left({'alt': 1}), 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:Left({'space': 1}) + let right = s:Right({'space': 1}) + + 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(s:Left(), s:Right(), s:Left({'alt': 1}), s:Right({'alt': 1}), line) + + "it is commented with s:Left() and s:Right() so remove these delims + if lineCommentStatus == 1 + let line = s:RemoveDelimiters(s:Left(), s:Right(), line) + + "it is commented with s:Left({'alt': 1}) and s:Right({'alt': 1}) so remove these delims + elseif lineCommentStatus == 2 && g:NERDRemoveAltComs + let line = s:RemoveDelimiters(s:Left({'alt': 1}), s:Right({'alt': 1}), 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(s:Left(), line) + let indxLeftAlt = s:FindDelimiterIndex(s:Left({'alt': 1}), line) + let indxRight = s:FindDelimiterIndex(s:Right(), line) + let indxRightAlt = s:FindDelimiterIndex(s:Right({'alt': 1}), line) + + "remove the outter most left comment delim + if indxLeft != -1 && (indxLeft < indxLeftAlt || indxLeftAlt == -1) + let line = s:RemoveDelimiters(s:Left(), '', line) + elseif indxLeftAlt != -1 + let line = s:RemoveDelimiters(s:Left({'alt': 1}), '', line) + endif + + "remove the outter most right comment delim + if indxRight != -1 && (indxRight < indxRightAlt || indxRightAlt == -1) + let line = s:RemoveDelimiters('', s:Right(), line) + elseif indxRightAlt != -1 + let line = s:RemoveDelimiters('', s:Right({'alt': 1}), line) + endif + endif + + + let indxLeft = s:FindDelimiterIndex(s:Left(), line) + let indxLeftAlt = s:FindDelimiterIndex(s:Left({'alt': 1}), line) + let indxLeftPlace = s:FindDelimiterIndex(g:NERDLPlace, line) + + let indxRightPlace = s:FindDelimiterIndex(g:NERDRPlace, line) + let indxRightAlt = s:FindDelimiterIndex(s:Right({'alt': 1}), line) + let indxRightPlace = s:FindDelimiterIndex(g:NERDRPlace, line) + + let right = s:Right() + let left = s:Left() + if !s:Multipart() + let right = s:Right({'alt': 1}) + let left = s:Left({'alt': 1}) + 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 = repeat(' ', 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 = repeat(' ', 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:NERDCommenterDelims['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(s:Left(), theLine) || s:IsCommentedFromStartOfLine(s:Left({'alt': 1}), 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(s:Left(), a:top, a:bottom) || s:DoesBlockHaveDelim(s:Right(), a:top, a:bottom) + else + return s:DoesBlockHaveDelim(s:Left({'alt': 1}), a:top, a:bottom) || s:DoesBlockHaveDelim(s:Right({'alt': 1}), 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:Left({'esc': 1}) + let right = s:Right({'esc': 1}) + elseif s:AltMultipart() + let left = s:Left({'alt': 1, 'esc': 1}) + let right = s:Right({'alt': 1, 'esc': 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: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:NERDSexyComMarker + + "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(s:Left()) + let lenLeftAlt = strlen(s:Left({'alt': 1})) + let left = '' + let right = '' + if s:Multipart() && lenLeft >= lenLeftAlt + let left = s:Left() + elseif s:AltMultipart() + let left = s:Left({'alt': 1}) + 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(s:Left()) + let lenLeftAlt = strlen(s:Left({'alt': 1})) + 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 = s:Left() + elseif s:AltMultipart() + let left = s:Left({'alt': 1}) + 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(s:Left()) + let lenLeftAlt = strlen(s:Left({'alt': 1})) + 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 = s:Right() + elseif s:AltMultipart() + let right = s:Right({'alt': 1}) + 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 (s:Left() == '/*' && s:Right() == '*/') || (s:Left({'alt': 1}) == '/*' && s:Right({'alt': 1}) == '*/') +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(s:Left(), s:Right(), theLine) || s:IsCommented(s:Left({'alt': 1}), s:Right({'alt': 1}), 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 = s:Left() + let right = s:Right() + elseif s:AltMultipart() + let left = s:Left({'alt': 1}) + let right = s:Right({'alt': 1}) + 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:Left(...) {{{2 +" returns left delimiter data +function s:Left(...) + let params = a:0 ? a:1 : {} + + let delim = has_key(params, 'alt') ? b:NERDCommenterDelims['leftAlt'] : b:NERDCommenterDelims['left'] + + if delim == '' + return '' + endif + + if has_key(params, 'space') && g:NERDSpaceDelims + let delim = delim . s:spaceStr + endif + + if has_key(params, 'esc') + let delim = s:Esc(delim) + endif + + return delim +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(s:Left(), s:Right(), theLine) && !s:IsCommented(s:Left({'alt': 1}), s:Right({'alt': 1}), 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 s: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 + echom 'NERDCommenter:' . a:msg + echohl None + elseif a:typeOfMsg == 1 + echom '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:Right(...) {{{2 +" returns right delimiter data +function s:Right(...) + let params = a:0 ? a:1 : {} + + let delim = has_key(params, 'alt') ? b:NERDCommenterDelims['rightAlt'] : b:NERDCommenterDelims['right'] + + if delim == '' + return '' + endif + + if has_key(params, 'space') && g:NERDSpaceDelims + let delim = s:spaceStr . delim + endif + + if has_key(params, 'esc') + let delim = s:Esc(delim) + endif + + return delim +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(s:Left(), s:Right(), theLine) && !s:IsCommented(s:Left({'alt': 1}), s:Right({'alt': 1}), 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(s:Left(), s:Right(), a:line) + let isCommentedAlt = s:IsCommented(s:Left({'alt': 1}), s:Right({'alt': 1}), 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(s:Left(), s: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(s:Left({'alt': 1}), s:Right({'alt': 1}), 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 = s:Left() + let right = s:Right() + elseif s:AltMultipart() + let left = s:Left({'alt': 1}) + let right = s:Right({'alt': 1}) + 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', '<leader>cc') + call s:CreateMaps('<plug>NERDCommenterToggle', '<leader>c<space>') + call s:CreateMaps('<plug>NERDCommenterMinimal', '<leader>cm') + call s:CreateMaps('<plug>NERDCommenterSexy', '<leader>cs') + call s:CreateMaps('<plug>NERDCommenterInvert', '<leader>ci') + call s:CreateMaps('<plug>NERDCommenterYank', '<leader>cy') + call s:CreateMaps('<plug>NERDCommenterAlignLeft', '<leader>cl') + call s:CreateMaps('<plug>NERDCommenterAlignBoth', '<leader>cb') + call s:CreateMaps('<plug>NERDCommenterNest', '<leader>cn') + call s:CreateMaps('<plug>NERDCommenterUncomment', '<leader>cu') + call s:CreateMaps('<plug>NERDCommenterToEOL', '<leader>c$') + call s:CreateMaps('<plug>NERDCommenterAppend', '<leader>cA') + + if !hasmapto('<plug>NERDCommenterAltDelims', 'n') + nmap <leader>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/vim/plugin/NERD_tree.vim b/vim/plugin/NERD_tree.vim new file mode 100644 index 0000000..669fd6c --- /dev/null +++ b/vim/plugin/NERD_tree.vim @@ -0,0 +1,4049 @@ +" ============================================================================ +" File: NERD_tree.vim +" Description: vim global plugin that provides a nice tree explorer +" Maintainer: Martin Grenfell <martin.grenfell at gmail dot com> +" Last Change: 1 December, 2009 +" 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 = '4.1.0' + +" 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 . ' = ' . "'" . substitute(a:value, "'", "''", "g") . "'" + 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:NERDTreeBookmarksFile", expand('$HOME') . '/.NERDTreeBookmarks') +call s:initVariable("g:NERDTreeHighlightCursorline", 1) +call s:initVariable("g:NERDTreeHijackNetrw", 1) +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, '*') + +if !exists('g:NERDTreeStatusline') + + "the exists() crap here is a hack to stop vim spazzing out when + "loading a session that was created with an open nerd tree. It spazzes + "because it doesnt store b:NERDTreeRoot (its a b: var, and its a hash) + let g:NERDTreeStatusline = "%{exists('b:NERDTreeRoot')?b:NERDTreeRoot.path.str():''}" + +endif +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:NERDTreeMapMenu", "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", "i") +call s:initVariable("g:NERDTreeMapOpenVSplit", "s") +call s:initVariable("g:NERDTreeMapPreview", "g" . NERDTreeMapActivateNode) +call s:initVariable("g:NERDTreeMapPreviewSplit", "g" . NERDTreeMapOpenSplit) +call s:initVariable("g:NERDTreeMapPreviewVSplit", "g" . NERDTreeMapOpenVSplit) +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", "I") +call s:initVariable("g:NERDTreeMapToggleZoom", "A") +call s:initVariable("g:NERDTreeMapUpdir", "u") +call s:initVariable("g:NERDTreeMapUpdirKeepOpen", "U") + +"SECTION: Script level variable declaration{{{2 +if s:running_windows + let s:escape_chars = " `\|\"#%&,?()\*^<>" +else + let s:escape_chars = " \\`\|\"#%&,?()\*^<>" +endif +let s:NERDTreeBufName = 'NERD_tree_' + +let s:tree_wid = 2 +let s:tree_markup_reg = '^[ `|▼▶]*[\-+~ ]*' +let s:tree_up_dir_line = '.. (up a dir)' + +"the number to add to the nerd tree buffer name to make the buf name unique +let s:next_buffer_number = 1 + +" SECTION: Commands {{{1 +"============================================================ +"init the command that users start the nerd tree with +command! -n=? -complete=dir -bar NERDTree :call s:initNerdTree('<args>') +command! -n=? -complete=dir -bar NERDTreeToggle :call s:toggle('<args>') +command! -n=0 -bar NERDTreeClose :call s:closeTreeIfOpen() +command! -n=1 -complete=customlist,s:completeBookmarks -bar NERDTreeFromBookmark call s:initNerdTree('<args>') +command! -n=0 -bar NERDTreeMirror call s:initNerdTreeMirror() +command! -n=0 -bar NERDTreeFind call s:findAndRevealPath() +" SECTION: Auto commands {{{1 +"============================================================ +augroup NERDTree + "Save the cursor position whenever we close the nerd tree + exec "autocmd BufWinLeave ". s:NERDTreeBufName ."* call <SID>saveScreenState()" + + "disallow insert mode in the NERDTree + exec "autocmd BufEnter ". s:NERDTreeBufName ."* stopinsert" + "cache bookmarks when vim loads + autocmd VimEnter * call s:Bookmark.CacheBookmarks(0) + + "load all nerdtree plugins after vim starts + autocmd VimEnter * runtime! nerdtree_plugin/**/*.vim +augroup END + +if g:NERDTreeHijackNetrw + augroup NERDTreeHijackNetrw + autocmd VimEnter * silent! autocmd! FileExplorer + au BufEnter,VimEnter * call s:checkForBrowse(expand("<amatch>")) + augroup END +endif + +"SECTION: Classes {{{1 +"============================================================ +"CLASS: Bookmark {{{2 +"============================================================ +let s:Bookmark = {} +" FUNCTION: Bookmark.activate() {{{3 +function! s:Bookmark.activate() + if self.path.isDirectory + call self.toRoot() + else + if self.validate() + let n = s:TreeFileNode.New(self.path) + call n.open() + call s:closeTreeIfQuitOnOpen() + endif + endif +endfunction +" 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.BookmarkNotFoundError/ + 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.BookmarkNotFoundError: 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.InvalidArgumentsError/ + 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.BookmarkedNodeNotFoundError/ + 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() : b:NERDTreeRoot + let targetNode = searchRoot.findNode(self.path) + if empty(targetNode) + throw "NERDTree.BookmarkedNodeNotFoundError: 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.GetSelected() {{{3 +" returns the Bookmark the cursor is over, or {} +function! s:Bookmark.GetSelected() + let line = getline(".") + let name = substitute(line, '^>\(.\{-}\) .\+$', '\1', '') + if name != line + try + return s:Bookmark.BookmarkFor(name) + catch /^NERDTree.BookmarkNotFoundError/ + return {} + endtry + endif + return {} +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.BookmarkPointsToInvalidLocationError: the bookmark \"". + \ self.name ."\" points to a non existing location: \"". self.path.str() + 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.IllegalBookmarkNameError: illegal name:" . a:name + endif + + let newBookmark = copy(self) + let newBookmark.name = a:name + let newBookmark.path = a:path + return newBookmark +endfunction +" FUNCTION: Bookmark.openInNewTab(options) {{{3 +" Create a new bookmark object with the given name and path object +function! s:Bookmark.openInNewTab(options) + let currentTab = tabpagenr() + if self.path.isDirectory + tabnew + call s:initNerdTree(self.name) + else + exec "tabedit " . bookmark.path.str({'format': 'Edit'}) + endif + + if has_key(a:options, 'stayInCurrentTab') + exec "tabnext " . currentTab + endif +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.str({'format': 'UI'}) + 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.BookmarkedNodeNotFoundError/ + let targetNode = s:TreeFileNode.New(s:Bookmark.BookmarkFor(self.name).path) + endtry + call targetNode.makeRoot() + call s:renderView() + call targetNode.putCursorHere(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.str()) + 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: KeyMap {{{2 +"============================================================ +let s:KeyMap = {} +"FUNCTION: KeyMap.All() {{{3 +function! s:KeyMap.All() + if !exists("s:keyMaps") + let s:keyMaps = [] + endif + return s:keyMaps +endfunction + +"FUNCTION: KeyMap.BindAll() {{{3 +function! s:KeyMap.BindAll() + for i in s:KeyMap.All() + call i.bind() + endfor +endfunction + +"FUNCTION: KeyMap.bind() {{{3 +function! s:KeyMap.bind() + exec "nnoremap <silent> <buffer> ". self.key ." :call ". self.callback ."()<cr>" +endfunction + +"FUNCTION: KeyMap.Create(options) {{{3 +function! s:KeyMap.Create(options) + let newKeyMap = copy(self) + let newKeyMap.key = a:options['key'] + let newKeyMap.quickhelpText = a:options['quickhelpText'] + let newKeyMap.callback = a:options['callback'] + call add(s:KeyMap.All(), newKeyMap) +endfunction +"CLASS: MenuController {{{2 +"============================================================ +let s:MenuController = {} +"FUNCTION: MenuController.New(menuItems) {{{3 +"create a new menu controller that operates on the given menu items +function! s:MenuController.New(menuItems) + let newMenuController = copy(self) + if a:menuItems[0].isSeparator() + let newMenuController.menuItems = a:menuItems[1:-1] + else + let newMenuController.menuItems = a:menuItems + endif + return newMenuController +endfunction + +"FUNCTION: MenuController.showMenu() {{{3 +"start the main loop of the menu and get the user to choose/execute a menu +"item +function! s:MenuController.showMenu() + call self._saveOptions() + + try + let self.selection = 0 + + let done = 0 + while !done + redraw! + call self._echoPrompt() + let key = nr2char(getchar()) + let done = self._handleKeypress(key) + endwhile + finally + call self._restoreOptions() + endtry + + if self.selection != -1 + let m = self._current() + call m.execute() + endif +endfunction + +"FUNCTION: MenuController._echoPrompt() {{{3 +function! s:MenuController._echoPrompt() + echo "NERDTree Menu. Use j/k/enter and the shortcuts indicated" + echo "==========================================================" + + for i in range(0, len(self.menuItems)-1) + if self.selection == i + echo "> " . self.menuItems[i].text + else + echo " " . self.menuItems[i].text + endif + endfor +endfunction + +"FUNCTION: MenuController._current(key) {{{3 +"get the MenuItem that is curently selected +function! s:MenuController._current() + return self.menuItems[self.selection] +endfunction + +"FUNCTION: MenuController._handleKeypress(key) {{{3 +"change the selection (if appropriate) and return 1 if the user has made +"their choice, 0 otherwise +function! s:MenuController._handleKeypress(key) + if a:key == 'j' + call self._cursorDown() + elseif a:key == 'k' + call self._cursorUp() + elseif a:key == nr2char(27) "escape + let self.selection = -1 + return 1 + elseif a:key == "\r" || a:key == "\n" "enter and ctrl-j + return 1 + else + let index = self._nextIndexFor(a:key) + if index != -1 + let self.selection = index + if len(self._allIndexesFor(a:key)) == 1 + return 1 + endif + endif + endif + + return 0 +endfunction + +"FUNCTION: MenuController._allIndexesFor(shortcut) {{{3 +"get indexes to all menu items with the given shortcut +function! s:MenuController._allIndexesFor(shortcut) + let toReturn = [] + + for i in range(0, len(self.menuItems)-1) + if self.menuItems[i].shortcut == a:shortcut + call add(toReturn, i) + endif + endfor + + return toReturn +endfunction + +"FUNCTION: MenuController._nextIndexFor(shortcut) {{{3 +"get the index to the next menu item with the given shortcut, starts from the +"current cursor location and wraps around to the top again if need be +function! s:MenuController._nextIndexFor(shortcut) + for i in range(self.selection+1, len(self.menuItems)-1) + if self.menuItems[i].shortcut == a:shortcut + return i + endif + endfor + + for i in range(0, self.selection) + if self.menuItems[i].shortcut == a:shortcut + return i + endif + endfor + + return -1 +endfunction + +"FUNCTION: MenuController._setCmdheight() {{{3 +"sets &cmdheight to whatever is needed to display the menu +function! s:MenuController._setCmdheight() + let &cmdheight = len(self.menuItems) + 3 +endfunction + +"FUNCTION: MenuController._saveOptions() {{{3 +"set any vim options that are required to make the menu work (saving their old +"values) +function! s:MenuController._saveOptions() + let self._oldLazyredraw = &lazyredraw + let self._oldCmdheight = &cmdheight + set nolazyredraw + call self._setCmdheight() +endfunction + +"FUNCTION: MenuController._restoreOptions() {{{3 +"restore the options we saved in _saveOptions() +function! s:MenuController._restoreOptions() + let &cmdheight = self._oldCmdheight + let &lazyredraw = self._oldLazyredraw +endfunction + +"FUNCTION: MenuController._cursorDown() {{{3 +"move the cursor to the next menu item, skipping separators +function! s:MenuController._cursorDown() + let done = 0 + while !done + if self.selection < len(self.menuItems)-1 + let self.selection += 1 + else + let self.selection = 0 + endif + + if !self._current().isSeparator() + let done = 1 + endif + endwhile +endfunction + +"FUNCTION: MenuController._cursorUp() {{{3 +"move the cursor to the previous menu item, skipping separators +function! s:MenuController._cursorUp() + let done = 0 + while !done + if self.selection > 0 + let self.selection -= 1 + else + let self.selection = len(self.menuItems)-1 + endif + + if !self._current().isSeparator() + let done = 1 + endif + endwhile +endfunction + +"CLASS: MenuItem {{{2 +"============================================================ +let s:MenuItem = {} +"FUNCTION: MenuItem.All() {{{3 +"get all top level menu items +function! s:MenuItem.All() + if !exists("s:menuItems") + let s:menuItems = [] + endif + return s:menuItems +endfunction + +"FUNCTION: MenuItem.AllEnabled() {{{3 +"get all top level menu items that are currently enabled +function! s:MenuItem.AllEnabled() + let toReturn = [] + for i in s:MenuItem.All() + if i.enabled() + call add(toReturn, i) + endif + endfor + return toReturn +endfunction + +"FUNCTION: MenuItem.Create(options) {{{3 +"make a new menu item and add it to the global list +function! s:MenuItem.Create(options) + let newMenuItem = copy(self) + + let newMenuItem.text = a:options['text'] + let newMenuItem.shortcut = a:options['shortcut'] + let newMenuItem.children = [] + + let newMenuItem.isActiveCallback = -1 + if has_key(a:options, 'isActiveCallback') + let newMenuItem.isActiveCallback = a:options['isActiveCallback'] + endif + + let newMenuItem.callback = -1 + if has_key(a:options, 'callback') + let newMenuItem.callback = a:options['callback'] + endif + + if has_key(a:options, 'parent') + call add(a:options['parent'].children, newMenuItem) + else + call add(s:MenuItem.All(), newMenuItem) + endif + + return newMenuItem +endfunction + +"FUNCTION: MenuItem.CreateSeparator(options) {{{3 +"make a new separator menu item and add it to the global list +function! s:MenuItem.CreateSeparator(options) + let standard_options = { 'text': '--------------------', + \ 'shortcut': -1, + \ 'callback': -1 } + let options = extend(a:options, standard_options, "force") + + return s:MenuItem.Create(options) +endfunction + +"FUNCTION: MenuItem.CreateSubmenu(options) {{{3 +"make a new submenu and add it to global list +function! s:MenuItem.CreateSubmenu(options) + let standard_options = { 'callback': -1 } + let options = extend(a:options, standard_options, "force") + + return s:MenuItem.Create(options) +endfunction + +"FUNCTION: MenuItem.enabled() {{{3 +"return 1 if this menu item should be displayed +" +"delegates off to the isActiveCallback, and defaults to 1 if no callback was +"specified +function! s:MenuItem.enabled() + if self.isActiveCallback != -1 + return {self.isActiveCallback}() + endif + return 1 +endfunction + +"FUNCTION: MenuItem.execute() {{{3 +"perform the action behind this menu item, if this menuitem has children then +"display a new menu for them, otherwise deletegate off to the menuitem's +"callback +function! s:MenuItem.execute() + if len(self.children) + let mc = s:MenuController.New(self.children) + call mc.showMenu() + else + if self.callback != -1 + call {self.callback}() + endif + endif +endfunction + +"FUNCTION: MenuItem.isSeparator() {{{3 +"return 1 if this menuitem is a separator +function! s:MenuItem.isSeparator() + return self.callback == -1 && self.children == [] +endfunction + +"FUNCTION: MenuItem.isSubmenu() {{{3 +"return 1 if this menuitem is a submenu +function! s:MenuItem.isSubmenu() + return self.callback == -1 && !empty(self.children) +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.activate(forceKeepWinOpen) {{{3 +function! s:TreeFileNode.activate(forceKeepWinOpen) + call self.open() + if !a:forceKeepWinOpen + call s:closeTreeIfQuitOnOpen() + end +endfunction +"FUNCTION: TreeFileNode.bookmark(name) {{{3 +"bookmark this node with a:name +function! s:TreeFileNode.bookmark(name) + + "if a bookmark exists with the same name and the node is cached then save + "it so we can update its display string + let oldMarkedNode = {} + try + let oldMarkedNode = s:Bookmark.GetNodeForName(a:name, 1) + catch /^NERDTree.BookmarkNotFoundError/ + catch /^NERDTree.BookmarkedNodeNotFoundError/ + endtry + + call s:Bookmark.AddBookmark(a:name, self.path) + call self.path.cacheDisplayString() + call s:Bookmark.Write() + + if !empty(oldMarkedNode) + call oldMarkedNode.path.cacheDisplayString() + endif +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.CannotCacheParentError: 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 = b: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.displayString() {{{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.displayString() + return self.path.displayString() +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() ==# a:treenode.path.str() +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.getLineNum(){{{3 +"returns the line number this node is rendered on, or -1 if it isnt rendered +function! s:TreeFileNode.getLineNum() + "if the node is the root then return the root line no. + if self.isRoot() + return s:TreeFileNode.GetRootLineNum() + endif + + let totalLines = line("$") + + "the path components we have matched so far + let pathcomponents = [substitute(b:NERDTreeRoot.path.str({'format': 'UI'}), '/ *$', '', '')] + "the index of the component we are searching for + let curPathComponent = 1 + + let fullpath = self.path.str({'format': 'UI'}) + + + let lnum = s:TreeFileNode.GetRootLineNum() + 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: TreeFileNode.GetRootForTab(){{{3 +"get the root node for this tab +function! s:TreeFileNode.GetRootForTab() + if s:treeExistsForTab() + return getbufvar(t:NERDTreeBufName, 'NERDTreeRoot') + end + return {} +endfunction +"FUNCTION: TreeFileNode.GetRootLineNum(){{{3 +"gets the line number of the root node +function! s:TreeFileNode.GetRootLineNum() + let rootLine = 1 + while getline(rootLine) !~ '^\(/\|<\)' + let rootLine = rootLine + 1 + endwhile + return rootLine +endfunction + +"FUNCTION: TreeFileNode.GetSelected() {{{3 +"gets the treenode that the cursor is currently over +function! s:TreeFileNode.GetSelected() + try + let path = s:getPath(line(".")) + if path ==# {} + return {} + endif + return b:NERDTreeRoot.findNode(path) + catch /NERDTree/ + return {} + endtry +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 b:NERDTreeRoot +function! s:TreeFileNode.isRoot() + if !s:treeExistsForBuf() + throw "NERDTree.NoTreeError: No tree exists for the current buffer" + endif + + return self.equals(b:NERDTreeRoot) +endfunction + +"FUNCTION: TreeFileNode.makeRoot() {{{3 +"Make this node the root of the tree +function! s:TreeFileNode.makeRoot() + if self.path.isDirectory + let b:NERDTreeRoot = self + else + call self.cacheParent() + let b:NERDTreeRoot = self.parent + endif + + call b:NERDTreeRoot.open() + + "change dir to the dir of the new root if instructed to + if g:NERDTreeChDirMode ==# 2 + exec "cd " . b:NERDTreeRoot.path.str({'format': 'Edit'}) + 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 = copy(self) + let newTreeNode.path = a:path + let newTreeNode.parent = {} + return newTreeNode + endif +endfunction + +"FUNCTION: TreeFileNode.open() {{{3 +"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:TreeFileNode.open() + if b:NERDTreeType ==# "secondary" + exec 'edit ' . self.path.str({'format': 'Edit'}) + return + endif + + "if the file is already open in this tab then just stick the cursor in it + let winnr = bufwinnr('^' . self.path.str() . '$') + if winnr != -1 + call s:exec(winnr . "wincmd w") + + else + if !s:isWindowUsable(winnr("#")) && s:firstUsableWindow() ==# -1 + call self.openSplit() + else + try + if !s:isWindowUsable(winnr("#")) + call s:exec(s:firstUsableWindow() . "wincmd w") + else + call s:exec('wincmd p') + endif + exec ("edit " . self.path.str({'format': 'Edit'})) + catch /^Vim\%((\a\+)\)\=:E37/ + call s:putCursorInTreeWin() + throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self.path.str() ." is already open and modified." + catch /^Vim\%((\a\+)\)\=:/ + echo v:exception + endtry + endif + endif +endfunction +"FUNCTION: TreeFileNode.openSplit() {{{3 +"Open this node in a new window +function! s:TreeFileNode.openSplit() + + if b:NERDTreeType ==# "secondary" + exec "split " . self.path.str({'format': 'Edit'}) + return + endif + + " Save the user's settings for splitbelow and splitright + let savesplitbelow=&splitbelow + let savesplitright=&splitright + + " '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. + " + 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 + + " Attempt to go to adjacent window + call s:exec(back) + + let onlyOneWin = (winnr("$") ==# 1) + + " 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 + + let splitMode = onlyOneWin ? "vertical" : "" + + " Open the new window + try + exec(splitMode." sp " . self.path.str({'format': 'Edit'})) + catch /^Vim\%((\a\+)\)\=:E37/ + call s:putCursorInTreeWin() + throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self.path.str() ." 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("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize + call s:exec(there) + exec("silent ". splitMode ." resize ". size) + call s:exec('wincmd p') + endif + + " Restore splitmode settings + let &splitbelow=savesplitbelow + let &splitright=savesplitright +endfunction +"FUNCTION: TreeFileNode.openVSplit() {{{3 +"Open this node in a new vertical window +function! s:TreeFileNode.openVSplit() + if b:NERDTreeType ==# "secondary" + exec "vnew " . self.path.str({'format': 'Edit'}) + return + endif + + let winwidth = winwidth(".") + if winnr("$")==#1 + let winwidth = g:NERDTreeWinSize + endif + + call s:exec("wincmd p") + exec "vnew " . self.path.str({'format': 'Edit'}) + + "resize the nerd tree back to the original size + call s:putCursorInTreeWin() + exec("silent vertical resize ". winwidth) + call s:exec('wincmd p') +endfunction +"FUNCTION: TreeFileNode.openInNewTab(options) {{{3 +function! s:TreeFileNode.openInNewTab(options) + let currentTab = tabpagenr() + + if !has_key(a:options, 'keepTreeOpen') + call s:closeTreeIfQuitOnOpen() + endif + + exec "tabedit " . self.path.str({'format': 'Edit'}) + + if has_key(a:options, 'stayInCurrentTab') && a:options['stayInCurrentTab'] + exec "tabnext " . currentTab + endif + +endfunction +"FUNCTION: TreeFileNode.putCursorHere(isJump, recurseUpward){{{3 +"Places the cursor on the line number this node is rendered on +" +"Args: +"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:TreeFileNode.putCursorHere(isJump, recurseUpward) + let ln = self.getLineNum() + if ln != -1 + if a:isJump + mark ' + endif + call cursor(ln, col(".")) + else + if a:recurseUpward + let node = self + while node != {} && node.getLineNum() ==# -1 + let node = node.parent + call node.open() + endwhile + call s:renderView() + call node.putCursorHere(a:isJump, 0) + endif + 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.getParent() + let newParent = b:NERDTreeRoot.findNode(parentPath) + + if newParent != {} + call newParent.createChild(self.path, 1) + call newParent.refresh() + endif +endfunction +"FUNCTION: TreeFileNode.renderToString {{{3 +"returns a string representation for this tree to be rendered in the view +function! s:TreeFileNode.renderToString() + return self._renderToString(0, 0, [], self.getChildCount() ==# 1) +endfunction + + +"Args: +"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:TreeFileNode._renderToString(depth, drawText, vertMap, isLastChild) + let output = "" + 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] + let treeParts = treeParts . ' ' + endfor + endif + + if self.path.isDirectory + if self.isOpen + let treeParts = treeParts . '▼ ' + else + let treeParts = treeParts . '▶ ' + endif + else + let treeParts = treeParts . '' + endif + + let line = treeParts . self.displayString() + + let output = output . line . "\n" + endif + + "if the node is an open dir, draw its children + if self.path.isDirectory ==# 1 && self.isOpen ==# 1 + + let childNodesToDraw = self.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] + let output = output . i._renderToString(a:depth + 1, 1, add(copy(a:vertMap), 1), 0) + endfor + endif + + "draw the last child, indicating that it IS the last + let output = output . childNodesToDraw[lastIndx]._renderToString(a:depth + 1, 1, add(copy(a:vertMap), 0), 1) + endif + endif + + return output +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 = b:NERDTreeRoot + while currentNode.parent != {} + let currentNode = currentNode.parent + endwhile + return currentNode +endfunction +"FUNCTION: TreeDirNode.activate(forceKeepWinOpen) {{{3 +unlet s:TreeDirNode.activate +function! s:TreeDirNode.activate(forceKeepWinOpen) + call self.toggleOpen() + call s:renderView() + call self.putCursorHere(0, 0) +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(), self.path.str(), 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(), self.path.str(), 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.InvalidArgumentsError: 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(), self.path.str(), 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.GetSelected() {{{3 +"Returns the current node if it is a dir node, or else returns the current +"nodes parent +unlet s:TreeDirNode.GetSelected +function! s:TreeDirNode.GetSelected() + let currentDir = s:TreeFileNode.GetSelected() + if currentDir != {} && !currentDir.isRoot() + if currentDir.path.isDirectory ==# 0 + let currentDir = currentDir.parent + endif + endif + return currentDir +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 globDir = dir.str({'format': 'Glob'}) + let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*') + 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.\(InvalidArguments\|InvalidFiletype\)Error/ + 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.InvalidArgumentsError: 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 +unlet s:TreeDirNode.open +function! s:TreeDirNode.open() + let self.isOpen = 1 + if self.children ==# [] + return self._initChildren(0) + else + return 0 + endif +endfunction + +" FUNCTION: TreeDirNode.openExplorer() {{{3 +" opens an explorer window for this node in the previous window (could be a +" nerd tree or a netrw) +function! s:TreeDirNode.openExplorer() + let oldwin = winnr() + call s:exec('wincmd p') + if oldwin ==# winnr() || (&modified && s:bufInWindows(winbufnr(winnr())) < 2) + call s:exec('wincmd p') + call self.openSplit() + else + exec ("silent edit " . self.path.str({'format': 'Edit'})) + endif +endfunction +"FUNCTION: TreeDirNode.openInNewTab(options) {{{3 +unlet s:TreeDirNode.openInNewTab +function! s:TreeDirNode.openInNewTab(options) + let currentTab = tabpagenr() + + if !has_key(a:options, 'keepTreeOpen') || !a:options['keepTreeOpen'] + call s:closeTreeIfQuitOnOpen() + endif + + tabnew + call s:initNerdTree(self.path.str()) + + if has_key(a:options, 'stayInCurrentTab') && a:options['stayInCurrentTab'] + exec "tabnext " . currentTab + 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 globDir = dir.str({'format': 'Glob'}) + let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*') + let files = split(filesStr, "\n") + 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 !~ '\/\.\/\?$' + + 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.InvalidArgumentsError/ + 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.reveal(path) {{{3 +"reveal the given path, i.e. cache and open all treenodes needed to display it +"in the UI +function! s:TreeDirNode.reveal(path) + if !a:path.isUnder(self.path) + throw "NERDTree.InvalidArgumentsError: " . a:path.str() . " should be under " . self.path.str() + endif + + call self.open() + + if self.path.equals(a:path.getParent()) + let n = self.findNode(a:path) + call s:renderView() + call n.putCursorHere(1,0) + return + endif + + let p = a:path + while !p.getParent().equals(self.path) + let p = p.getParent() + endwhile + + let n = self.findNode(p) + call n.reveal(a:path) +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.ChildNotFoundError 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.ChildNotFoundError: 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.AbsolutePathFor(str) {{{3 +function! s:Path.AbsolutePathFor(str) + let prependCWD = 0 + if s:running_windows + let prependCWD = a:str !~ '^.:\(\\\|\/\)' + else + let prependCWD = a:str !~ '^/' + endif + + let toReturn = a:str + if prependCWD + let toReturn = getcwd() . s:Path.Slash() . a:str + endif + + return toReturn +endfunction +"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.str({'format': 'Cd'}) + if self.isDirectory ==# 0 + let dir = self.getParent().str({'format': 'Cd'}) + endif + + try + execute "cd " . dir + call s:echo("CWD is now: " . getcwd()) + catch + throw "NERDTree.PathChangeError: cannot change CWD 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.CreatePathError: 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.CreatePathError: 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.CopyingNotSupportedError: Copying is not supported on this OS" + endif + + let dest = s:Path.WinToUnixPath(a:dest) + + let cmd = g:NERDTreeCopyCmd . " " . self.str() . " " . dest + let success = system(cmd) + if success != 0 + throw "NERDTree.CopyError: Could not copy ''". self.str() ."'' 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 = g:NERDTreeRemoveDirCmd . self.str({'escape': 1}) + let success = system(cmd) + + if v:shell_error != 0 + throw "NERDTree.PathDeletionError: Could not delete directory: '" . self.str() . "'" + endif + else + let success = delete(self.str()) + if success != 0 + throw "NERDTree.PathDeletionError: Could not delete file: '" . self.str() . "'" + 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.displayString() {{{3 +" +"Returns a string that specifies how the path should be represented as a +"string +function! s:Path.displayString() + if self.cachedDisplayString ==# "" + call self.cacheDisplayString() + endif + + return self.cachedDisplayString +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() + let p = self.str() + return filereadable(p) || isdirectory(p) +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() + if s:running_windows + let path = self.drive . '\' . join(self.pathSegments[0:-2], '\') + else + let path = '/'. join(self.pathSegments[0:-2], '/') + endif + + 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.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 b:NERDTreeIgnoreEnabled + for i in g:NERDTreeIgnore + if lastPathComponent =~ i + return 1 + endif + endfor + endif + + "dont show hidden files unless instructed to + if b:NERDTreeShowHidden ==# 0 && lastPathComponent =~ '^\.' + return 1 + endif + + if b:NERDTreeShowFiles ==# 0 && self.isDirectory ==# 0 + return 1 + endif + + return 0 +endfunction + +"FUNCTION: Path.isUnder(path) {{{3 +"return 1 if this path is somewhere under the given path in the filesystem. +" +"a:path should be a dir +function! s:Path.isUnder(path) + if a:path.isDirectory == 0 + return 0 + endif + + let this = self.str() + let that = a:path.str() + return stridx(this, that . s:Path.Slash()) == 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() ==# a:path.str() +endfunction + +"FUNCTION: Path.New() {{{3 +"The Constructor for the Path object +function! s:Path.New(path) + let newPath = copy(self) + + call newPath.readInfoFromDisk(s:Path.AbsolutePathFor(a:path)) + + let newPath.cachedDisplayString = "" + + return newPath +endfunction + +"FUNCTION: Path.Slash() {{{3 +"return the slash to use for the current OS +function! s:Path.Slash() + return s:running_windows ? '\' : '/' +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.InvalidFiletypeError: 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.InvalidArgumentsError: 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.str()) + call self.cacheDisplayString() +endfunction + +"FUNCTION: Path.rename() {{{3 +" +"Renames this node on the filesystem +function! s:Path.rename(newPath) + if a:newPath ==# '' + throw "NERDTree.InvalidArgumentsError: Invalid newPath for renaming = ". a:newPath + endif + + let success = rename(self.str(), a:newPath) + if success != 0 + throw "NERDTree.PathRenameError: Could not rename: '" . self.str() . "'" . '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() {{{3 +" +"Returns a string representation of this Path +" +"Takes an optional dictionary param to specify how the output should be +"formatted. +" +"The dict may have the following keys: +" 'format' +" 'escape' +" 'truncateTo' +" +"The 'format' key may have a value of: +" 'Cd' - a string to be used with the :cd command +" 'Edit' - a string to be used with :e :sp :new :tabedit etc +" 'UI' - a string used in the NERD tree UI +" +"The 'escape' key, if specified will cause the output to be escaped with +"shellescape() +" +"The 'truncateTo' key causes the resulting string to be truncated to the value +"'truncateTo' maps to. A '<' char will be prepended. +function! s:Path.str(...) + let options = a:0 ? a:1 : {} + let toReturn = "" + + if has_key(options, 'format') + let format = options['format'] + if has_key(self, '_strFor' . format) + exec 'let toReturn = self._strFor' . format . '()' + else + raise 'NERDTree.UnknownFormatError: unknown format "'. format .'"' + endif + else + let toReturn = self._str() + endif + + if has_key(options, 'escape') && options['escape'] + let toReturn = shellescape(toReturn) + endif + + if has_key(options, 'truncateTo') + let limit = options['truncateTo'] + if len(toReturn) > limit + let toReturn = "<" . strpart(toReturn, len(toReturn) - limit + 1) + endif + endif + + return toReturn +endfunction + +"FUNCTION: Path._strForUI() {{{3 +function! s:Path._strForUI() + let toReturn = '/' . join(self.pathSegments, '/') + if self.isDirectory && toReturn != '/' + let toReturn = toReturn . '/' + endif + return toReturn +endfunction + +"FUNCTION: Path._strForCd() {{{3 +" +" returns a string that can be used with :cd +function! s:Path._strForCd() + return escape(self.str(), s:escape_chars) +endfunction +"FUNCTION: Path._strForEdit() {{{3 +" +"Return: the string for this path that is suitable to be used with the :edit +"command +function! s:Path._strForEdit() + let p = self.str({'format': 'UI'}) + let cwd = getcwd() + + if s:running_windows + let p = tolower(self.str()) + let cwd = tolower(getcwd()) + endif + + let p = escape(p, s:escape_chars) + + let cwd = cwd . s:Path.Slash() + + "return a relative path if we can + if stridx(p, cwd) ==# 0 + let p = strpart(p, strlen(cwd)) + endif + + if p ==# '' + let p = '.' + endif + + return p + +endfunction +"FUNCTION: Path._strForGlob() {{{3 +function! s:Path._strForGlob() + let lead = s:Path.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:Path.Slash()) + + if !s:running_windows + let toReturn = escape(toReturn, s:escape_chars) + endif + return toReturn +endfunction +"FUNCTION: Path._str() {{{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 +function! s:Path._str() + let lead = s:Path.Slash() + + "if we are running windows then slap a drive letter on the front + if s:running_windows + let lead = self.drive . '\' + endif + + return lead . join(self.pathSegments, s:Path.Slash()) +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:checkForBrowse(dir) {{{2 +"inits a secondary nerd tree in the current buffer if appropriate +function! s:checkForBrowse(dir) + if a:dir != '' && isdirectory(a:dir) + call s:initNerdTreeInPlace(a:dir) + endif +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:exec(cmd) {{{2 +" same as :exec cmd but eventignore=all is set for the duration +function! s:exec(cmd) + let old_ei = &ei + set ei=all + exec a:cmd + let &ei = old_ei +endfunction +" FUNCTION: s:findAndRevealPath() {{{2 +function! s:findAndRevealPath() + try + let p = s:Path.New(expand("%:p")) + catch /^NERDTree.InvalidArgumentsError/ + call s:echo("no file for the current buffer") + return + endtry + + if !s:treeExistsForTab() + call s:initNerdTree(p.getParent().str()) + else + if !p.isUnder(s:TreeFileNode.GetRootForTab().path) + call s:initNerdTree(p.getParent().str()) + else + if !s:isTreeOpen() + call s:toggle("") + endif + endif + endif + call s:putCursorInTreeWin() + call b:NERDTreeRoot.reveal(p) +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 ==# '' ? getcwd() : a:name + + "hack to get an absolute path if a relative path is given + if dir =~ '^\.' + let dir = getcwd() . s:Path.Slash() . dir + endif + let dir = resolve(dir) + + try + let path = s:Path.New(dir) + catch /^NERDTree.InvalidArgumentsError/ + 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 + call path.changeToDir() + endif + + if s:treeExistsForTab() + if s:isTreeOpen() + call s:closeTree() + endif + unlet t:NERDTreeBufName + endif + + let newRoot = s:TreeDirNode.New(path) + call newRoot.open() + + call s:createTreeWin() + let b:treeShowHelp = 0 + let b:NERDTreeIgnoreEnabled = 1 + let b:NERDTreeShowFiles = g:NERDTreeShowFiles + let b:NERDTreeShowHidden = g:NERDTreeShowHidden + let b:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks + let b:NERDTreeRoot = newRoot + + let b:NERDTreeType = "primary" + + call s:renderView() + call b:NERDTreeRoot.putCursorHere(0, 0) +endfunction + +"FUNCTION: s:initNerdTreeInPlace(dir) {{{2 +function! s:initNerdTreeInPlace(dir) + try + let path = s:Path.New(a:dir) + catch /^NERDTree.InvalidArgumentsError/ + call s:echo("Invalid directory name:" . a:name) + return + endtry + + "we want the directory buffer to disappear when we do the :edit below + setlocal bufhidden=wipe + + let previousBuf = expand("#") + + "we need a unique name for each secondary tree buffer to ensure they are + "all independent + exec "silent edit " . s:nextBufferName() + + let b:NERDTreePreviousBuf = bufnr(previousBuf) + + let b:NERDTreeRoot = s:TreeDirNode.New(path) + call b:NERDTreeRoot.open() + + "throwaway buffer options + setlocal noswapfile + setlocal buftype=nofile + setlocal bufhidden=hide + 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:setupStatusline() + + let b:treeShowHelp = 0 + let b:NERDTreeIgnoreEnabled = 1 + let b:NERDTreeShowFiles = g:NERDTreeShowFiles + let b:NERDTreeShowHidden = g:NERDTreeShowHidden + let b:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks + + let b:NERDTreeType = "secondary" + + call s:bindMappings() + setfiletype nerdtree + " syntax highlighting + if has("syntax") && exists("g:syntax_on") + call s:setupSyntaxHighlighting() + endif + + call s:renderView() +endfunction +" FUNCTION: s:initNerdTreeMirror() {{{2 +function! s:initNerdTreeMirror() + + "get the names off all the nerd tree buffers + let treeBufNames = [] + for i in range(1, tabpagenr("$")) + let nextName = s:tabpagevar(i, 'NERDTreeBufName') + if nextName != -1 && (!exists("t:NERDTreeBufName") || nextName != t:NERDTreeBufName) + call add(treeBufNames, nextName) + endif + endfor + let treeBufNames = s:unique(treeBufNames) + + "map the option names (that the user will be prompted with) to the nerd + "tree buffer names + let options = {} + let i = 0 + while i < len(treeBufNames) + let bufName = treeBufNames[i] + let treeRoot = getbufvar(bufName, "NERDTreeRoot") + let options[i+1 . '. ' . treeRoot.path.str() . ' (buf name: ' . bufName . ')'] = bufName + let i = i + 1 + endwhile + + "work out which tree to mirror, if there is more than 1 then ask the user + let bufferName = '' + if len(keys(options)) > 1 + let choices = ["Choose a tree to mirror"] + let choices = extend(choices, sort(keys(options))) + let choice = inputlist(choices) + if choice < 1 || choice > len(options) || choice ==# '' + return + endif + + let bufferName = options[sort(keys(options))[choice-1]] + elseif len(keys(options)) ==# 1 + let bufferName = values(options)[0] + else + call s:echo("No trees to mirror") + return + endif + + if s:treeExistsForTab() && s:isTreeOpen() + call s:closeTree() + endif + + let t:NERDTreeBufName = bufferName + call s:createTreeWin() + exec 'buffer ' . bufferName + if !&hidden + call s:renderView() + endif +endfunction +" FUNCTION: s:nextBufferName() {{{2 +" returns the buffer name for the next nerd tree +function! s:nextBufferName() + let name = s:NERDTreeBufName . s:next_buffer_number + let s:next_buffer_number += 1 + return name +endfunction +" FUNCTION: s:tabpagevar(tabnr, var) {{{2 +function! s:tabpagevar(tabnr, var) + let currentTab = tabpagenr() + let old_ei = &ei + set ei=all + + exec "tabnext " . a:tabnr + let v = -1 + if exists('t:' . a:var) + exec 'let v = t:' . a:var + endif + exec "tabnext " . currentTab + + let &ei = old_ei + + return v +endfunction +" Function: s:treeExistsForBuffer() {{{2 +" Returns 1 if a nerd tree root exists in the current buffer +function! s:treeExistsForBuf() + return exists("b:NERDTreeRoot") +endfunction +" Function: s:treeExistsForTab() {{{2 +" Returns 1 if a nerd tree root exists in the current tab +function! s:treeExistsForTab() + return exists("t:NERDTreeBufName") +endfunction +" Function: s:unique(list) {{{2 +" returns a:list without duplicates +function! s:unique(list) + let uniqlist = [] + for elem in a:list + if index(uniqlist, elem) ==# -1 + let uniqlist += [elem] + endif + endfor + return uniqlist +endfunction +" SECTION: Public API {{{1 +"============================================================ +let g:NERDTreePath = s:Path +let g:NERDTreeDirNode = s:TreeDirNode +let g:NERDTreeFileNode = s:TreeFileNode +let g:NERDTreeBookmark = s:Bookmark + +function! NERDTreeAddMenuItem(options) + call s:MenuItem.Create(a:options) +endfunction + +function! NERDTreeAddMenuSeparator(...) + let opts = a:0 ? a:1 : {} + call s:MenuItem.CreateSeparator(opts) +endfunction + +function! NERDTreeAddSubmenu(options) + return s:MenuItem.Create(a:options) +endfunction + +function! NERDTreeAddKeyMap(options) + call s:KeyMap.Create(a:options) +endfunction + +function! NERDTreeRender() + call s:renderView() +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 primary NERD tree window for this tab +function! s:closeTree() + if !s:isTreeOpen() + throw "NERDTree.NoTreeFoundError: no NERDTree is open" + endif + + if winnr("$") != 1 + if winnr() == s:getTreeWinNum() + wincmd p + let bufnr = bufnr("") + wincmd p + else + let bufnr = bufnr("") + endif + + call s:exec(s:getTreeWinNum() . " wincmd w") + close + call s:exec(bufwinnr(bufnr) . " wincmd w") + else + close + 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 && s:isTreeOpen() + 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 ==# "left" ? "topleft " : "botright " + let splitSize = g:NERDTreeWinSize + + if !exists('t:NERDTreeBufName') + let t:NERDTreeBufName = s:nextBufferName() + silent! exec splitLocation . 'vertical ' . splitSize . ' new' + silent! exec "edit " . t:NERDTreeBufName + else + silent! exec splitLocation . 'vertical ' . splitSize . ' split' + silent! exec "buffer " . t:NERDTreeBufName + endif + + setlocal winfixwidth + + "throwaway buffer options + setlocal noswapfile + setlocal buftype=nofile + 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:setupStatusline() + + call s:bindMappings() + setfiletype nerdtree + " syntax highlighting + if has("syntax") && exists("g:syntax_on") + call s:setupSyntaxHighlighting() + endif +endfunction + +"FUNCTION: s:dumpHelp {{{2 +"prints out the quick help +function! s:dumpHelp() + let old_h = @h + if b: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."\" <CR>,\n" + if b:NERDTreeType ==# "primary" + let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in prev window\n" + else + let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in current window\n" + endif + if b:NERDTreeType ==# "primary" + let @h=@h."\" ". g:NERDTreeMapPreview .": preview\n" + endif + 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:NERDTreeMapOpenVSplit .": open vsplit\n" + let @h=@h."\" ". g:NERDTreeMapPreviewVSplit .": preview vsplit\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.": explore selected dir\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:NERDTreeMapMenu .": Show 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 (" . (b:NERDTreeShowHidden ? "on" : "off") . ")\n" + let @h=@h."\" ". g:NERDTreeMapToggleFilters .": file filters (" . (b:NERDTreeIgnoreEnabled ? "on" : "off") . ")\n" + let @h=@h."\" ". g:NERDTreeMapToggleFiles .": files (" . (b:NERDTreeShowFiles ? "on" : "off") . ")\n" + let @h=@h."\" ". g:NERDTreeMapToggleBookmarks .": bookmarks (" . (b:NERDTreeShowBookmarks ? "on" : "off") . ")\n" + + "add quickhelp entries for each custom key map + if len(s:KeyMap.All()) + let @h=@h."\"\n\" ----------------------------\n" + let @h=@h."\" Custom mappings~\n" + for i in s:KeyMap.All() + let @h=@h."\" ". i.key .": ". i.quickhelpText ."\n" + endfor + endif + + 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:NERDTreeMapToggleZoom .": Zoom (maximize-minimize)\n" + let @h=@h."\" 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:firstUsableWindow(){{{2 +"find the window number of the first normal window +function! s:firstUsableWindow() + let i = 1 + while i <= winnr("$") + let bnum = winbufnr(i) + if bnum != -1 && getbufvar(bnum, '&buftype') ==# '' + \ && !getwinvar(i, '&previewwindow') + \ && (!getbufvar(bnum, '&modified') || &hidden) + 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) + + let rootLine = s:TreeFileNode.GetRootLineNum() + + "check to see if we have the root node + if a:ln == rootLine + return b:NERDTreeRoot.path + endif + + " in case called from outside the tree + "if line !~ '^ *[|`▶▼ ]' || line =~ '^$' + "return {} + "endif + + if line ==# s:tree_up_dir_line + return b: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 lnum == rootLine + let dir = b:NERDTreeRoot.path.str({'format': 'UI'}) . 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 = b:NERDTreeRoot.path.drive . dir . curFile + let toReturn = s:Path.New(curFile) + return toReturn +endfunction + +"FUNCTION: s:getTreeWinNum() {{{2 +"gets the nerd tree window number for this tab +function! s:getTreeWinNum() + if exists("t:NERDTreeBufName") + return bufwinnr(t:NERDTreeBufName) + 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 0 if opening a file from the tree in the given window requires it to +"be split, 1 otherwise +" +"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() + call s:exec(a:winnumber . "wincmd p") + let specialWindow = getbufvar("%", '&buftype') != '' || getwinvar('%', '&previewwindow') + let modified = &modified + call s: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:TreeFileNode.GetSelected() + 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 targetNode.putCursorHere(1, 0) + + call s:centerView() +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 !b:NERDTreeShowBookmarks + throw "NERDTree.IllegalOperationError: cant find bookmark table, bookmarks arent active" + endif + + let rootNodeLine = s:TreeFileNode.GetRootLineNum() + + let line = 1 + while getline(line) !~ '^>-\+Bookmarks-\+$' + let line = line + 1 + if line >= rootNodeLine + throw "NERDTree.BookmarkTableNotFoundError: didnt find the bookmarks table" + endif + endwhile + call cursor(line, 0) +endfunction + +"FUNCTION: s:putCursorInTreeWin(){{{2 +"Places the cursor in the nerd tree window +function! s:putCursorInTreeWin() + if !s:isTreeOpen() + throw "NERDTree.InvalidOperationError: cant put cursor in NERD tree window, no window exists" + endif + + call s: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 +function! s:renderView() + 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 b: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 + let header = b:NERDTreeRoot.path.str({'format': 'UI', 'truncateTo': winwidth(0)}) + call setline(line(".")+1, header) + call cursor(line(".")+1, col(".")) + + "draw the tree + let old_o = @o + let @o = b:NERDTreeRoot.renderToString() + silent put o + let @o = old_o + + "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:TreeFileNode.GetSelected() + + "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 currentNode.putCursorHere(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("b:NERDTreeOldTopLine") || !exists("b:NERDTreeOldPos") || !exists("b:NERDTreeOldWindowSize") + return + endif + exec("silent vertical resize ".b:NERDTreeOldWindowSize) + + let old_scrolloff=&scrolloff + let &scrolloff=0 + call cursor(b:NERDTreeOldTopLine, 0) + normal! zt + call setpos(".", b: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 b:NERDTreeOldPos = getpos(".") + let b:NERDTreeOldTopLine = line("w0") + let b:NERDTreeOldWindowSize = winwidth("") + call s:exec(win . "wincmd w") + catch /^NERDTree.InvalidOperationError/ + endtry +endfunction + +"FUNCTION: s:setupStatusline() {{{2 +function! s:setupStatusline() + if g:NERDTreeStatusline != -1 + let &l:statusline = g:NERDTreeStatusline + endif +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 #.*\[RO\]#hs=s+2 contains=treeFlag,treeBookmark,treePart,treePartFile + + "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: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() + if !&hidden + call s:renderView() + endif + 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:TreeFileNode.GetSelected() + if treenode != {} + call treenode.activate(a:forceKeepWindowOpen) + else + let bookmark = s:Bookmark.GetSelected() + if !empty(bookmark) + call bookmark.activate() + 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,0)<cr>" + exec "nnoremap <silent> <buffer> <cr> :call <SID>activateNode(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:NERDTreeMapOpenVSplit ." :call <SID>openEntrySplit(1,0)<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreviewVSplit ." :call <SID>previewNode(2)<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 ." :call <SID>closeTreeWindow()<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:NERDTreeMapToggleZoom ." :call <SID>toggleZoom()<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:NERDTreeMapMenu ." :call <SID>showMenu()<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>" + + "bind all the user custom maps + call s:KeyMap.BindAll() + + 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:TreeFileNode.GetSelected() + if currentNode != {} + try + call currentNode.bookmark(a:name) + call s:renderView() + catch /^NERDTree.IllegalBookmarkNameError/ + 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:TreeFileNode.GetSelected() + if currentNode != {} + call s:activateNode(0) + return + endif +endfunction + +" FUNCTION: s:chCwd() {{{2 +function! s:chCwd() + let treenode = s:TreeFileNode.GetSelected() + if treenode ==# {} + call s:echo("Select a node first") + return + endif + + try + call treenode.path.changeToDir() + catch /^NERDTree.PathChangeError/ + 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:TreeFileNode.GetSelected() + if treenode ==# {} + call s:echo("Select a node first") + return + endif + + call treenode.makeRoot() + call s:renderView() + call b:NERDTreeRoot.putCursorHere(0, 0) +endfunction + +" FUNCTION: s:clearBookmarks(bookmarks) {{{2 +function! s:clearBookmarks(bookmarks) + if a:bookmarks ==# '' + let currentNode = s:TreeFileNode.GetSelected() + 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:TreeDirNode.GetSelected() + if currentNode ==# {} + call s:echo("Select a node first") + return + endif + + call currentNode.closeChildren() + call s:renderView() + call currentNode.putCursorHere(0, 0) +endfunction +" FUNCTION: s:closeCurrentDir() {{{2 +" closes the parent dir of the current node +function! s:closeCurrentDir() + let treenode = s:TreeFileNode.GetSelected() + if treenode ==# {} + call s:echo("Select a node first") + return + endif + + let parent = treenode.parent + if parent ==# {} || parent.isRoot() + call s:echo("cannot close tree root") + else + call treenode.parent.close() + call s:renderView() + call treenode.parent.putCursorHere(0, 0) + endif +endfunction +" FUNCTION: s:closeTreeWindow() {{{2 +" close the tree window +function! s:closeTreeWindow() + if b:NERDTreeType ==# "secondary" && b:NERDTreePreviousBuf != -1 + exec "buffer " . b:NERDTreePreviousBuf + else + if winnr("$") > 1 + call s:closeTree() + else + call s:echo("Cannot close last window") + endif + endif +endfunction +" FUNCTION: s:deleteBookmark() {{{2 +" if the cursor is on a bookmark, prompt to delete +function! s:deleteBookmark() + let bookmark = s:Bookmark.GetSelected() + 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:displayHelp() {{{2 +" toggles the help display +function! s:displayHelp() + let b:treeShowHelp = b:treeShowHelp ? 0 : 1 + call s:renderView() + call s:centerView() +endfunction + +" FUNCTION: s:handleMiddleMouse() {{{2 +function! s:handleMiddleMouse() + let curNode = s:TreeFileNode.GetSelected() + 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,0) + endif +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:TreeFileNode.GetSelected() + if !empty(currentNode) + if !empty(currentNode.parent) + call currentNode.parent.putCursorHere(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 b:NERDTreeRoot.putCursorHere(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:TreeFileNode.GetSelected() + if !empty(currentNode) + let sibling = currentNode.findSibling(a:forward) + + if !empty(sibling) + call sibling.putCursorHere(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 targetNode.putCursorHere(0, 1) + redraw! + catch /^NERDTree.BookmarkedNodeNotFoundError/ + 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 targetNode.openExplorer() + else + call targetNode.open() + endif +endfunction +" FUNCTION: s:openEntrySplit(vertical, 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(vertical, forceKeepWindowOpen) + let treenode = s:TreeFileNode.GetSelected() + if treenode != {} + if a:vertical + call treenode.openVSplit() + else + call treenode.openSplit() + endif + 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:TreeDirNode.GetSelected() + if treenode != {} + call treenode.openExplorer() + 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 target = s:TreeFileNode.GetSelected() + if target == {} + let target = s:Bookmark.GetSelected() + endif + + if target != {} + call target.openInNewTab({'stayInCurrentTab': a:stayCurrentTab}) + endif +endfunction + +" FUNCTION: s:openNodeRecursively() {{{2 +function! s:openNodeRecursively() + let treenode = s:TreeFileNode.GetSelected() + 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 +"Args: +" openNewWin: if 0, use the previous window, if 1 open in new split, if 2 +" open in a vsplit +function! s:previewNode(openNewWin) + let currentBuf = bufnr("") + if a:openNewWin > 0 + call s:openEntrySplit(a:openNewWin ==# 2,1) + else + call s:activateNode(1) + end + call s:exec(bufwinnr(currentBuf) . "wincmd w") +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 targetNode.putCursorHere(0, 1) + catch /^NERDTree.BookmarkNotFoundError/ + 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 b: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:TreeDirNode.GetSelected() + 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:showMenu() {{{2 +function! s:showMenu() + let curNode = s:TreeFileNode.GetSelected() + if curNode ==# {} + call s:echo("Put the cursor on a node first" ) + return + endif + + let mc = s:MenuController.New(s:MenuItem.AllEnabled()) + call mc.showMenu() +endfunction + +" FUNCTION: s:toggleIgnoreFilter() {{{2 +" toggles the use of the NERDTreeIgnore option +function! s:toggleIgnoreFilter() + let b:NERDTreeIgnoreEnabled = !b:NERDTreeIgnoreEnabled + call s:renderViewSavingPosition() + call s:centerView() +endfunction + +" FUNCTION: s:toggleShowBookmarks() {{{2 +" toggles the display of bookmarks +function! s:toggleShowBookmarks() + let b:NERDTreeShowBookmarks = !b:NERDTreeShowBookmarks + if b: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 b:NERDTreeShowFiles = !b:NERDTreeShowFiles + call s:renderViewSavingPosition() + call s:centerView() +endfunction + +" FUNCTION: s:toggleShowHidden() {{{2 +" toggles the display of hidden files +function! s:toggleShowHidden() + let b:NERDTreeShowHidden = !b:NERDTreeShowHidden + call s:renderViewSavingPosition() + call s:centerView() +endfunction + +" FUNCTION: s:toggleZoom() {{2 +" zoom (maximize/minimize) the NERDTree window +function! s:toggleZoom() + if exists("b:NERDTreeZoomed") && b:NERDTreeZoomed + let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize + exec "silent vertical resize ". size + let b:NERDTreeZoomed = 0 + else + exec "vertical resize" + let b:NERDTreeZoomed = 1 + endif +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 = b:NERDTreeRoot.path.str({'format': 'UI'}) + if cwd ==# "/" || cwd =~ '^[^/]..$' + call s:echo("already at top dir") + else + if !a:keepState + call b:NERDTreeRoot.close() + endif + + let oldRoot = b:NERDTreeRoot + + if empty(b:NERDTreeRoot.parent) + let path = b:NERDTreeRoot.path.getParent() + let newRoot = s:TreeDirNode.New(path) + call newRoot.open() + call newRoot.transplantChild(b:NERDTreeRoot) + let b:NERDTreeRoot = newRoot + else + let b:NERDTreeRoot = b:NERDTreeRoot.parent + endif + + if g:NERDTreeChDirMode ==# 2 + call b:NERDTreeRoot.path.changeToDir() + endif + + call s:renderView() + call oldRoot.putCursorHere(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/vim/plugin/SearchComplete.vim b/vim/plugin/SearchComplete.vim new file mode 100644 index 0000000..8e950c6 --- /dev/null +++ b/vim/plugin/SearchComplete.vim @@ -0,0 +1,100 @@ +" SearchComplete.vim +" Author: Chris Russell +" Version: 1.1 +" License: GPL v2.0 +" +" Description: +" This script defineds functions and key mappings for Tab completion in +" searches. +" +" Help: +" This script catches the <Tab> character when using the '/' search +" command. Pressing Tab will expand the current partial word to the +" next matching word starting with the partial word. +" +" If you want to match a tab, use the '\t' pattern. +" +" Installation: +" Simply drop this file into your $HOME/.vim/plugin directory. +" +" Changelog: +" 2002-11-08 v1.1 +" Convert to unix eol +" 2002-11-05 v1.0 +" Initial release +" +" TODO: +" + + +"-------------------------------------------------- +" Avoid multiple sourcing +"-------------------------------------------------- +if exists( "loaded_search_complete" ) + finish +endif +let loaded_search_complete = 1 + + +"-------------------------------------------------- +" Key mappings +"-------------------------------------------------- +noremap / :call SearchCompleteStart()<CR>/ + + +"-------------------------------------------------- +" Set mappings for search complete +"-------------------------------------------------- +function! SearchCompleteStart() + cnoremap <Tab> <C-C>:call SearchComplete()<CR>/<C-R>s + cnoremap <silent> <CR> <CR>:call SearchCompleteStop()<CR> + cnoremap <silent> <Esc> <C-C>:call SearchCompleteStop()<CR> +endfunction + +"-------------------------------------------------- +" Tab completion in / search +"-------------------------------------------------- +function! SearchComplete() + " get current cursor position + let l:loc = col( "." ) - 1 + " get partial search and delete + let l:search = histget( '/', -1 ) + call histdel( '/', -1 ) + " check if new search + if l:search == @s + " get root search string + let l:search = b:searchcomplete + " increase number of autocompletes + let b:searchcompletedepth = b:searchcompletedepth . "\<C-N>" + else + " one autocomplete + let b:searchcompletedepth = "\<C-N>" + endif + " store origional search parameter + let b:searchcomplete = l:search + " set paste option to disable indent options + let l:paste = &paste + setlocal paste + " on a temporary line put search string and use autocomplete + execute "normal! A\n" . l:search . b:searchcompletedepth + " get autocomplete result + let @s = getline( line( "." ) ) + " undo and return to first char + execute "normal! u0" + " return to cursor position + if l:loc > 0 + execute "normal! ". l:loc . "l" + endif + " reset paste option + let &paste = l:paste +endfunction + +"-------------------------------------------------- +" Remove search complete mappings +"-------------------------------------------------- +function! SearchCompleteStop() + cunmap <Tab> + cunmap <CR> + cunmap <Esc> +endfunction + diff --git a/vim/plugin/ZoomWinPlugin.vim b/vim/plugin/ZoomWinPlugin.vim new file mode 100644 index 0000000..3f24a7d --- /dev/null +++ b/vim/plugin/ZoomWinPlugin.vim @@ -0,0 +1,49 @@ +" ZoomWin: Brief-like ability to zoom into/out-of a window +" Author: Charles Campbell +" original version by Ron Aaron +" Date: Jan 16, 2009 +" Version: 23e ASTRO-ONLY +" History: see :help zoomwin-history {{{1 +" GetLatestVimScripts: 508 1 :AutoInstall: ZoomWin.vim + +" --------------------------------------------------------------------- +" Load Once: {{{1 +if &cp || exists("g:loaded_ZoomWinPlugin") + finish +endif +if v:version < 702 + echohl WarningMsg + echo "***warning*** this version of ZoomWin needs vim 7.2" + echohl Normal + finish +endif +let s:keepcpo = &cpo +let g:loaded_ZoomWinPlugin = "v23" +set cpo&vim +"DechoTabOn + +" --------------------------------------------------------------------- +" Public Interface: {{{1 +if !hasmapto("<Plug>ZoomWin") + nmap <unique> <c-w>o <Plug>ZoomWin +endif +nnoremap <silent> <script> <Plug>ZoomWin :set lz<CR>:silent call ZoomWin#ZoomWin()<CR>:set nolz<CR> +com! ZoomWin :set lz|silent call ZoomWin#ZoomWin()|set nolz + +au VimLeave * call ZoomWin#CleanupSessionFile() + +" --------------------------------------------------------------------- +" ZoomWin: toggles between a single-window and a multi-window layout {{{1 +" The original version was by Ron Aaron. +" This function provides compatibility with previous versions. +fun! ZoomWin() + call ZoomWin#ZoomWin() +endfun + +" --------------------------------------------------------------------- +" Restore: {{{1 +let &cpo= s:keepcpo +unlet s:keepcpo +" --------------------------------------------------------------------- +" Modelines: {{{1 +" vim: ts=4 fdm=marker diff --git a/vim/plugin/ack.vim b/vim/plugin/ack.vim new file mode 100644 index 0000000..8cc0d45 --- /dev/null +++ b/vim/plugin/ack.vim @@ -0,0 +1,79 @@ +" NOTE: You must, of course, install the ack script +" in your path. +" On Debian / Ubuntu: +" sudo apt-get install ack-grep +" On your vimrc: +" let g:ackprg="ack-grep -H --nocolor --nogroup --column" +" +" With MacPorts: +" sudo port install p5-app-ack + +" Location of the ack utility +if !exists("g:ackprg") + let g:ackprg="ack -H --nocolor --nogroup --column" +endif + +function! s:Ack(cmd, args) + redraw + echo "Searching ..." + + " If no pattern is provided, search for the word under the cursor + if empty(a:args) + let l:grepargs = expand("<cword>") + else + let l:grepargs = a:args + end + + " Format, used to manage column jump + if a:cmd =~# '-g$' + let g:ackformat="%f" + else + let g:ackformat="%f:%l:%c:%m" + end + + let grepprg_bak=&grepprg + let grepformat_bak=&grepformat + try + let &grepprg=g:ackprg + let &grepformat=g:ackformat + silent execute a:cmd . " " . l:grepargs + finally + let &grepprg=grepprg_bak + let &grepformat=grepformat_bak + endtry + + if a:cmd =~# '^l' + botright lopen + else + botright copen + endif + + " TODO: Document this! + exec "nnoremap <silent> <buffer> q :ccl<CR>" + exec "nnoremap <silent> <buffer> t <C-W><CR><C-W>T" + exec "nnoremap <silent> <buffer> T <C-W><CR><C-W>TgT<C-W><C-W>" + exec "nnoremap <silent> <buffer> o <CR>" + exec "nnoremap <silent> <buffer> go <CR><C-W><C-W>" + + " If highlighting is on, highlight the search keyword. + if exists("g:ackhighlight") + let @/=a:args + set hlsearch + end + + redraw! +endfunction + +function! s:AckFromSearch(cmd, args) + let search = getreg('/') + " translate vim regular expression to perl regular expression. + let search = substitute(search,'\(\\<\|\\>\)','\\b','g') + call s:Ack(a:cmd, '"' . search .'" '. a:args) +endfunction + +command! -bang -nargs=* -complete=file Ack call s:Ack('grep<bang>',<q-args>) +command! -bang -nargs=* -complete=file AckAdd call s:Ack('grepadd<bang>', <q-args>) +command! -bang -nargs=* -complete=file AckFromSearch call s:AckFromSearch('grep<bang>', <q-args>) +command! -bang -nargs=* -complete=file LAck call s:Ack('lgrep<bang>', <q-args>) +command! -bang -nargs=* -complete=file LAckAdd call s:Ack('lgrepadd<bang>', <q-args>) +command! -bang -nargs=* -complete=file AckFile call s:Ack('grep<bang> -g', <q-args>) diff --git a/vim/plugin/cecutil.vim b/vim/plugin/cecutil.vim new file mode 100644 index 0000000..0bf3434 --- /dev/null +++ b/vim/plugin/cecutil.vim @@ -0,0 +1,510 @@ +" cecutil.vim : save/restore window position +" save/restore mark position +" save/restore selected user maps +" Author: Charles E. Campbell, Jr. +" Version: 18b ASTRO-ONLY +" Date: Aug 27, 2008 +" +" Saving Restoring Destroying Marks: {{{1 +" call SaveMark(markname) let savemark= SaveMark(markname) +" call RestoreMark(markname) call RestoreMark(savemark) +" call DestroyMark(markname) +" commands: SM RM DM +" +" Saving Restoring Destroying Window Position: {{{1 +" call SaveWinPosn() let winposn= SaveWinPosn() +" call RestoreWinPosn() call RestoreWinPosn(winposn) +" \swp : save current window/buffer's position +" \rwp : restore current window/buffer's previous position +" commands: SWP RWP +" +" Saving And Restoring User Maps: {{{1 +" call SaveUserMaps(mapmode,maplead,mapchx,suffix) +" call RestoreUserMaps(suffix) +" +" GetLatestVimScripts: 1066 1 :AutoInstall: cecutil.vim +" +" You believe that God is one. You do well. The demons also {{{1 +" believe, and shudder. But do you want to know, vain man, that +" faith apart from works is dead? (James 2:19,20 WEB) + +" --------------------------------------------------------------------- +" Load Once: {{{1 +if &cp || exists("g:loaded_cecutil") + finish +endif +let g:loaded_cecutil = "v18b" +let s:keepcpo = &cpo +set cpo&vim +"DechoTabOn + +" ======================= +" Public Interface: {{{1 +" ======================= + +" --------------------------------------------------------------------- +" Map Interface: {{{2 +if !hasmapto('<Plug>SaveWinPosn') + map <unique> <Leader>swp <Plug>SaveWinPosn +endif +if !hasmapto('<Plug>RestoreWinPosn') + map <unique> <Leader>rwp <Plug>RestoreWinPosn +endif +nmap <silent> <Plug>SaveWinPosn :call SaveWinPosn()<CR> +nmap <silent> <Plug>RestoreWinPosn :call RestoreWinPosn()<CR> + +" --------------------------------------------------------------------- +" Command Interface: {{{2 +com! -bar -nargs=0 SWP call SaveWinPosn() +com! -bar -nargs=0 RWP call RestoreWinPosn() +com! -bar -nargs=1 SM call SaveMark(<q-args>) +com! -bar -nargs=1 RM call RestoreMark(<q-args>) +com! -bar -nargs=1 DM call DestroyMark(<q-args>) + +if v:version < 630 + let s:modifier= "sil " +else + let s:modifier= "sil keepj " +endif + +" =============== +" Functions: {{{1 +" =============== + +" --------------------------------------------------------------------- +" SaveWinPosn: {{{2 +" let winposn= SaveWinPosn() will save window position in winposn variable +" call SaveWinPosn() will save window position in b:cecutil_winposn{b:cecutil_iwinposn} +" let winposn= SaveWinPosn(0) will *only* save window position in winposn variable (no stacking done) +fun! SaveWinPosn(...) +" call Dfunc("SaveWinPosn() a:0=".a:0) + if line(".") == 1 && getline(1) == "" +" call Dfunc("SaveWinPosn : empty buffer") + return "" + endif + let so_keep = &l:so + let siso_keep = &siso + let ss_keep = &l:ss + setlocal so=0 siso=0 ss=0 + + let swline = line(".") + let swcol = col(".") + let swwline = winline() - 1 + let swwcol = virtcol(".") - wincol() + let savedposn = "call GoWinbufnr(".winbufnr(0).")|silent ".swline + let savedposn = savedposn."|".s:modifier."norm! 0z\<cr>" + if swwline > 0 + let savedposn= savedposn.":".s:modifier."norm! ".swwline."\<c-y>\<cr>" + endif + if swwcol > 0 + let savedposn= savedposn.":".s:modifier."norm! 0".swwcol."zl\<cr>" + endif + let savedposn = savedposn.":".s:modifier."call cursor(".swline.",".swcol.")\<cr>" + + " save window position in + " b:cecutil_winposn_{iwinposn} (stack) + " only when SaveWinPosn() is used + if a:0 == 0 + if !exists("b:cecutil_iwinposn") + let b:cecutil_iwinposn= 1 + else + let b:cecutil_iwinposn= b:cecutil_iwinposn + 1 + endif +" call Decho("saving posn to SWP stack") + let b:cecutil_winposn{b:cecutil_iwinposn}= savedposn + endif + + let &l:so = so_keep + let &siso = siso_keep + let &l:ss = ss_keep + +" if exists("b:cecutil_iwinposn") " Decho +" call Decho("b:cecutil_winpos{".b:cecutil_iwinposn."}[".b:cecutil_winposn{b:cecutil_iwinposn}."]") +" else " Decho +" call Decho("b:cecutil_iwinposn doesn't exist") +" endif " Decho +" call Dret("SaveWinPosn [".savedposn."]") + return savedposn +endfun + +" --------------------------------------------------------------------- +" RestoreWinPosn: {{{2 +" call RestoreWinPosn() +" call RestoreWinPosn(winposn) +fun! RestoreWinPosn(...) +" call Dfunc("RestoreWinPosn() a:0=".a:0) +" call Decho("getline(1)<".getline(1).">") +" call Decho("line(.)=".line(".")) + if line(".") == 1 && getline(1) == "" +" call Dfunc("RestoreWinPosn : empty buffer") + return "" + endif + let so_keep = &l:so + let siso_keep = &l:siso + let ss_keep = &l:ss + setlocal so=0 siso=0 ss=0 + + if a:0 == 0 || a:1 == "" + " use saved window position in b:cecutil_winposn{b:cecutil_iwinposn} if it exists + if exists("b:cecutil_iwinposn") && exists("b:cecutil_winposn{b:cecutil_iwinposn}") +" call Decho("using stack b:cecutil_winposn{".b:cecutil_iwinposn."}<".b:cecutil_winposn{b:cecutil_iwinposn}.">") + try + exe "silent! ".b:cecutil_winposn{b:cecutil_iwinposn} + catch /^Vim\%((\a\+)\)\=:E749/ + " ignore empty buffer error messages + endtry + " normally drop top-of-stack by one + " but while new top-of-stack doesn't exist + " drop top-of-stack index by one again + if b:cecutil_iwinposn >= 1 + unlet b:cecutil_winposn{b:cecutil_iwinposn} + let b:cecutil_iwinposn= b:cecutil_iwinposn - 1 + while b:cecutil_iwinposn >= 1 && !exists("b:cecutil_winposn{b:cecutil_iwinposn}") + let b:cecutil_iwinposn= b:cecutil_iwinposn - 1 + endwhile + if b:cecutil_iwinposn < 1 + unlet b:cecutil_iwinposn + endif + endif + else + echohl WarningMsg + echomsg "***warning*** need to SaveWinPosn first!" + echohl None + endif + + else " handle input argument +" call Decho("using input a:1<".a:1.">") + " use window position passed to this function + exe "silent ".a:1 + " remove a:1 pattern from b:cecutil_winposn{b:cecutil_iwinposn} stack + if exists("b:cecutil_iwinposn") + let jwinposn= b:cecutil_iwinposn + while jwinposn >= 1 " search for a:1 in iwinposn..1 + if exists("b:cecutil_winposn{jwinposn}") " if it exists + if a:1 == b:cecutil_winposn{jwinposn} " and the pattern matches + unlet b:cecutil_winposn{jwinposn} " unlet it + if jwinposn == b:cecutil_iwinposn " if at top-of-stack + let b:cecutil_iwinposn= b:cecutil_iwinposn - 1 " drop stacktop by one + endif + endif + endif + let jwinposn= jwinposn - 1 + endwhile + endif + endif + + " Seems to be something odd: vertical motions after RWP + " cause jump to first column. The following fixes that. + " Note: was using wincol()>1, but with signs, a cursor + " at column 1 yields wincol()==3. Beeping ensued. + if virtcol('.') > 1 + silent norm! hl + elseif virtcol(".") < virtcol("$") + silent norm! lh + endif + + let &l:so = so_keep + let &l:siso = siso_keep + let &l:ss = ss_keep + +" call Dret("RestoreWinPosn") +endfun + +" --------------------------------------------------------------------- +" GoWinbufnr: go to window holding given buffer (by number) {{{2 +" Prefers current window; if its buffer number doesn't match, +" then will try from topleft to bottom right +fun! GoWinbufnr(bufnum) +" call Dfunc("GoWinbufnr(".a:bufnum.")") + if winbufnr(0) == a:bufnum +" call Dret("GoWinbufnr : winbufnr(0)==a:bufnum") + return + endif + winc t + let first=1 + while winbufnr(0) != a:bufnum && (first || winnr() != 1) + winc w + let first= 0 + endwhile +" call Dret("GoWinbufnr") +endfun + +" --------------------------------------------------------------------- +" SaveMark: sets up a string saving a mark position. {{{2 +" For example, SaveMark("a") +" Also sets up a global variable, g:savemark_{markname} +fun! SaveMark(markname) +" call Dfunc("SaveMark(markname<".a:markname.">)") + let markname= a:markname + if strpart(markname,0,1) !~ '\a' + let markname= strpart(markname,1,1) + endif +" call Decho("markname=".markname) + + let lzkeep = &lz + set lz + + if 1 <= line("'".markname) && line("'".markname) <= line("$") + let winposn = SaveWinPosn(0) + exe s:modifier."norm! `".markname + let savemark = SaveWinPosn(0) + let g:savemark_{markname} = savemark + let savemark = markname.savemark + call RestoreWinPosn(winposn) + else + let g:savemark_{markname} = "" + let savemark = "" + endif + + let &lz= lzkeep + +" call Dret("SaveMark : savemark<".savemark.">") + return savemark +endfun + +" --------------------------------------------------------------------- +" RestoreMark: {{{2 +" call RestoreMark("a") -or- call RestoreMark(savemark) +fun! RestoreMark(markname) +" call Dfunc("RestoreMark(markname<".a:markname.">)") + + if strlen(a:markname) <= 0 +" call Dret("RestoreMark : no such mark") + return + endif + let markname= strpart(a:markname,0,1) + if markname !~ '\a' + " handles 'a -> a styles + let markname= strpart(a:markname,1,1) + endif +" call Decho("markname=".markname." strlen(a:markname)=".strlen(a:markname)) + + let lzkeep = &lz + set lz + let winposn = SaveWinPosn(0) + + if strlen(a:markname) <= 2 + if exists("g:savemark_{markname}") && strlen(g:savemark_{markname}) != 0 + " use global variable g:savemark_{markname} +" call Decho("use savemark list") + call RestoreWinPosn(g:savemark_{markname}) + exe "norm! m".markname + endif + else + " markname is a savemark command (string) +" call Decho("use savemark command") + let markcmd= strpart(a:markname,1) + call RestoreWinPosn(markcmd) + exe "norm! m".markname + endif + + call RestoreWinPosn(winposn) + let &lz = lzkeep + +" call Dret("RestoreMark") +endfun + +" --------------------------------------------------------------------- +" DestroyMark: {{{2 +" call DestroyMark("a") -- destroys mark +fun! DestroyMark(markname) +" call Dfunc("DestroyMark(markname<".a:markname.">)") + + " save options and set to standard values + let reportkeep= &report + let lzkeep = &lz + set lz report=10000 + + let markname= strpart(a:markname,0,1) + if markname !~ '\a' + " handles 'a -> a styles + let markname= strpart(a:markname,1,1) + endif +" call Decho("markname=".markname) + + let curmod = &mod + let winposn = SaveWinPosn(0) + 1 + let lineone = getline(".") + exe "k".markname + d + put! =lineone + let &mod = curmod + call RestoreWinPosn(winposn) + + " restore options to user settings + let &report = reportkeep + let &lz = lzkeep + +" call Dret("DestroyMark") +endfun + +" --------------------------------------------------------------------- +" QArgSplitter: to avoid \ processing by <f-args>, <q-args> is needed. {{{2 +" However, <q-args> doesn't split at all, so this one returns a list +" with splits at all whitespace (only!), plus a leading length-of-list. +" The resulting list: qarglist[0] corresponds to a:0 +" qarglist[i] corresponds to a:{i} +fun! QArgSplitter(qarg) +" call Dfunc("QArgSplitter(qarg<".a:qarg.">)") + let qarglist = split(a:qarg) + let qarglistlen = len(qarglist) + let qarglist = insert(qarglist,qarglistlen) +" call Dret("QArgSplitter ".string(qarglist)) + return qarglist +endfun + +" --------------------------------------------------------------------- +" ListWinPosn: {{{2 +"fun! ListWinPosn() " Decho +" if !exists("b:cecutil_iwinposn") || b:cecutil_iwinposn == 0 " Decho +" call Decho("nothing on SWP stack") " Decho +" else " Decho +" let jwinposn= b:cecutil_iwinposn " Decho +" while jwinposn >= 1 " Decho +" if exists("b:cecutil_winposn{jwinposn}") " Decho +" call Decho("winposn{".jwinposn."}<".b:cecutil_winposn{jwinposn}.">") " Decho +" else " Decho +" call Decho("winposn{".jwinposn."} -- doesn't exist") " Decho +" endif " Decho +" let jwinposn= jwinposn - 1 " Decho +" endwhile " Decho +" endif " Decho +"endfun " Decho +"com! -nargs=0 LWP call ListWinPosn() " Decho + +" --------------------------------------------------------------------- +" SaveUserMaps: this function sets up a script-variable (s:restoremap) {{{2 +" which can be used to restore user maps later with +" call RestoreUserMaps() +" +" mapmode - see :help maparg for details (n v o i c l "") +" ex. "n" = Normal +" The letters "b" and "u" are optional prefixes; +" The "u" means that the map will also be unmapped +" The "b" means that the map has a <buffer> qualifier +" ex. "un" = Normal + unmapping +" ex. "bn" = Normal + <buffer> +" ex. "bun" = Normal + <buffer> + unmapping +" ex. "ubn" = Normal + <buffer> + unmapping +" maplead - see mapchx +" mapchx - "<something>" handled as a single map item. +" ex. "<left>" +" - "string" a string of single letters which are actually +" multiple two-letter maps (using the maplead: +" maplead . each_character_in_string) +" ex. maplead="\" and mapchx="abc" saves user mappings for +" \a, \b, and \c +" Of course, if maplead is "", then for mapchx="abc", +" mappings for a, b, and c are saved. +" - :something handled as a single map item, w/o the ":" +" ex. mapchx= ":abc" will save a mapping for "abc" +" suffix - a string unique to your plugin +" ex. suffix= "DrawIt" +fun! SaveUserMaps(mapmode,maplead,mapchx,suffix) +" call Dfunc("SaveUserMaps(mapmode<".a:mapmode."> maplead<".a:maplead."> mapchx<".a:mapchx."> suffix<".a:suffix.">)") + + if !exists("s:restoremap_{a:suffix}") + " initialize restoremap_suffix to null string + let s:restoremap_{a:suffix}= "" + endif + + " set up dounmap: if 1, then save and unmap (a:mapmode leads with a "u") + " if 0, save only + let mapmode = a:mapmode + let dounmap = 0 + let dobuffer = "" + while mapmode =~ '^[bu]' + if mapmode =~ '^u' + let dounmap= 1 + let mapmode= strpart(a:mapmode,1) + elseif mapmode =~ '^b' + let dobuffer= "<buffer> " + let mapmode= strpart(a:mapmode,1) + endif + endwhile +" call Decho("dounmap=".dounmap." dobuffer<".dobuffer.">") + + " save single map :...something... + if strpart(a:mapchx,0,1) == ':' +" call Decho("save single map :...something...") + let amap= strpart(a:mapchx,1) + if amap == "|" || amap == "\<c-v>" + let amap= "\<c-v>".amap + endif + let amap = a:maplead.amap + let s:restoremap_{a:suffix} = s:restoremap_{a:suffix}."|:silent! ".mapmode."unmap ".dobuffer.amap + if maparg(amap,mapmode) != "" + let maprhs = substitute(maparg(amap,mapmode),'|','<bar>','ge') + let s:restoremap_{a:suffix} = s:restoremap_{a:suffix}."|:".mapmode."map ".dobuffer.amap." ".maprhs + endif + if dounmap + exe "silent! ".mapmode."unmap ".dobuffer.amap + endif + + " save single map <something> + elseif strpart(a:mapchx,0,1) == '<' +" call Decho("save single map <something>") + let amap = a:mapchx + if amap == "|" || amap == "\<c-v>" + let amap= "\<c-v>".amap +" call Decho("amap[[".amap."]]") + endif + let s:restoremap_{a:suffix} = s:restoremap_{a:suffix}."|silent! ".mapmode."unmap ".dobuffer.amap + if maparg(a:mapchx,mapmode) != "" + let maprhs = substitute(maparg(amap,mapmode),'|','<bar>','ge') + let s:restoremap_{a:suffix} = s:restoremap_{a:suffix}."|".mapmode."map ".amap." ".dobuffer.maprhs + endif + if dounmap + exe "silent! ".mapmode."unmap ".dobuffer.amap + endif + + " save multiple maps + else +" call Decho("save multiple maps") + let i= 1 + while i <= strlen(a:mapchx) + let amap= a:maplead.strpart(a:mapchx,i-1,1) + if amap == "|" || amap == "\<c-v>" + let amap= "\<c-v>".amap + endif + let s:restoremap_{a:suffix} = s:restoremap_{a:suffix}."|silent! ".mapmode."unmap ".dobuffer.amap + if maparg(amap,mapmode) != "" + let maprhs = substitute(maparg(amap,mapmode),'|','<bar>','ge') + let s:restoremap_{a:suffix} = s:restoremap_{a:suffix}."|".mapmode."map ".amap." ".dobuffer.maprhs + endif + if dounmap + exe "silent! ".mapmode."unmap ".dobuffer.amap + endif + let i= i + 1 + endwhile + endif +" call Dret("SaveUserMaps : restoremap_".a:suffix.": ".s:restoremap_{a:suffix}) +endfun + +" --------------------------------------------------------------------- +" RestoreUserMaps: {{{2 +" Used to restore user maps saved by SaveUserMaps() +fun! RestoreUserMaps(suffix) +" call Dfunc("RestoreUserMaps(suffix<".a:suffix.">)") + if exists("s:restoremap_{a:suffix}") + let s:restoremap_{a:suffix}= substitute(s:restoremap_{a:suffix},'|\s*$','','e') + if s:restoremap_{a:suffix} != "" +" call Decho("exe ".s:restoremap_{a:suffix}) + exe "silent! ".s:restoremap_{a:suffix} + endif + unlet s:restoremap_{a:suffix} + endif +" call Dret("RestoreUserMaps") +endfun + +" ============== +" Restore: {{{1 +" ============== +let &cpo= s:keepcpo +unlet s:keepcpo + +" ================ +" Modelines: {{{1 +" ================ +" vim: ts=4 fdm=marker diff --git a/vim/plugin/color_sample_pack.vim b/vim/plugin/color_sample_pack.vim new file mode 100644 index 0000000..00e8af2 --- /dev/null +++ b/vim/plugin/color_sample_pack.vim @@ -0,0 +1,164 @@ +" Maintainer: Robert Melton ( iam -at- robertmelton -dot- com) +" Last Change: 2010 Jan 20th + +" default schemes +amenu T&hemes.D&efault.Blue :colo blue<CR> +amenu T&hemes.D&efault.DarkBlue :colo darkblue<CR> +amenu T&hemes.D&efault.Default :colo default<CR> +amenu T&hemes.D&efault.Delek :colo delek<CR> +amenu T&hemes.D&efault.Desert :colo desert<CR> +amenu T&hemes.D&efault.ElfLord :colo elflord<CR> +amenu T&hemes.D&efault.Evening :colo evening<CR> +amenu T&hemes.D&efault.Koehler :colo koehler<CR> +amenu T&hemes.D&efault.Morning :colo morning<CR> +amenu T&hemes.D&efault.Murphy :colo murphy<CR> +amenu T&hemes.D&efault.Pablo :colo pablo<CR> +amenu T&hemes.D&efault.PeachPuff :colo peachpuff<CR> +amenu T&hemes.D&efault.Ron :colo ron<CR> +amenu T&hemes.D&efault.Shine :colo shine<CR> +amenu T&hemes.D&efault.Torte :colo torte<CR> +amenu T&hemes.-s1- : + +" 37 new themes +amenu T&hemes.&New.&Dark.Adaryn :colo adaryn<CR> +amenu T&hemes.&New.&Dark.Adrian :colo adrian<CR> +amenu T&hemes.&New.&Dark.Anotherdark :colo anotherdark<CR> +amenu T&hemes.&New.&Dark.BlackSea :colo blacksea<CR> +amenu T&hemes.&New.&Dark.Colorer :colo colorer<CR> +amenu T&hemes.&New.&Dark.Darkbone :colo darkbone<CR> +amenu T&hemes.&New.&Dark.DarkZ :colo darkz<CR> +amenu T&hemes.&New.&Dark.Herald :colo herald<CR> +amenu T&hemes.&New.&Dark.Jammy :colo jammy<CR> +amenu T&hemes.&New.&Dark.Kellys :colo kellys<CR> +amenu T&hemes.&New.&Dark.Lettuce :colo lettuce<CR> +amenu T&hemes.&New.&Dark.Maroloccio :colo maroloccio<CR> +amenu T&hemes.&New.&Dark.Molokai :colo molokai<CR> +amenu T&hemes.&New.&Dark.Mustang :colo mustang<CR> +amenu T&hemes.&New.&Dark.TIRBlack :colo tir_black<CR> +amenu T&hemes.&New.&Dark.Twilight :colo twilight<CR> +amenu T&hemes.&New.&Dark.Two2Tango :colo two2tango<CR> +amenu T&hemes.&New.&Dark.Wuye :colo wuye<CR> +amenu T&hemes.&New.&Dark.Zmrok :colo zmrok<CR> +amenu T&hemes.&New.&Light.BClear :colo bclear<CR> +amenu T&hemes.&New.&Light.Satori :colo satori<CR> +amenu T&hemes.&New.&Light.Silent :colo silent<CR> +amenu T&hemes.&New.&Light.SoSo :colo soso<CR> +amenu T&hemes.&New.&Light.SummerFruit256 :colo summerfruit256<CR> +amenu T&hemes.&New.&Light.TAqua :colo taqua<CR> +amenu T&hemes.&New.&Light.TCSoft :colo tcsoft<CR> +amenu T&hemes.&New.&Light.VYLight :colo vylight<CR> +amenu T&hemes.&New.&Other.Aqua :colo aqua<CR> +amenu T&hemes.&New.&Other.Clarity :colo clarity<CR> +amenu T&hemes.&New.&Other.CleanPHP :colo cleanphp<CR> +amenu T&hemes.&New.&Other.Denim :colo denim<CR> +amenu T&hemes.&New.&Other.Guardian :colo guardian<CR> +amenu T&hemes.&New.&Other.Moss :colo moss<CR> +amenu T&hemes.&New.&Other.Nightshimmer :colo nightshimmer<CR> +amenu T&hemes.&New.&Other.NoQuarter :colo no_quarter<CR> +amenu T&hemes.&New.&Other.RobinHood :colo robinhood<CR> +amenu T&hemes.&New.&Other.SoftBlue :colo softblue<CR> +amenu T&hemes.&New.&Other.Wood :colo wood<CR> + +" 30 removed themes +amenu T&hemes.De&precated.&Dark.DwBlue :colo dw_blue<CR> +amenu T&hemes.De&precated.&Dark.DwCyan :colo dw_cyan<CR> +amenu T&hemes.De&precated.&Dark.DwGreen :colo dw_green<CR> +amenu T&hemes.De&precated.&Dark.DwOrange :colo dw_orange<CR> +amenu T&hemes.De&precated.&Dark.DwPurple :colo dw_purple<CR> +amenu T&hemes.De&precated.&Dark.DwRed :colo dw_red<CR> +amenu T&hemes.De&precated.&Dark.DwYellow :colo dw_yellow<CR> +amenu T&hemes.De&precated.&Dark.Fruity :colo fruity<CR> +amenu T&hemes.De&precated.&Dark.Leo :colo leo<CR> +amenu T&hemes.De&precated.&Dark.Matrix :colo matrix<CR> +amenu T&hemes.De&precated.&Dark.Metacosm :colo metacosm<CR> +amenu T&hemes.De&precated.&Dark.Northland :colo northland<CR> +amenu T&hemes.De&precated.&Dark.Railscasts2 :colo railscasts2<CR> +amenu T&hemes.De&precated.&Dark.Synic :colo synic<CR> +amenu T&hemes.De&precated.&Dark.Wombat256 :colo wombat256<CR> +amenu T&hemes.De&precated.&Dark.Xoria256 :colo xoria256<CR> +amenu T&hemes.De&precated.&Light.Autumn2 :colo autumn2<CR> +amenu T&hemes.De&precated.&Light.Buttercream :colo buttercream<CR> +amenu T&hemes.De&precated.&Light.Fine_blue :colo fine_blue<CR> +amenu T&hemes.De&precated.&Light.Impact :colo impact<CR> +amenu T&hemes.De&precated.&Light.Oceanlight :colo oceanlight<CR> +amenu T&hemes.De&precated.&Light.Print_bw :colo print_bw<CR> +amenu T&hemes.De&precated.&Light.Pyte :colo pyte<CR> +amenu T&hemes.De&precated.&Light.Spring :colo spring<CR> +amenu T&hemes.De&precated.&Light.Winter :colo winter<CR> +amenu T&hemes.De&precated.&Other.Astronaut :colo astronaut<CR> +amenu T&hemes.De&precated.&Other.Bluegreen :colo bluegreen<CR> +amenu T&hemes.De&precated.&Other.Navajo :colo navajo<CR> +amenu T&hemes.De&precated.&Other.Olive :colo olive<CR> +amenu T&hemes.De&precated.&Other.Tabula :colo tabula<CR> +amenu T&hemes.De&precated.&Other.Xemacs :colo xemacs<CR> + +" Themepack Themes +amenu T&hemes.&Dark.Asu1dark :colo asu1dark<CR> +amenu T&hemes.&Dark.Brookstream :colo brookstream<CR> +amenu T&hemes.&Dark.Calmar256-dark :colo calmar256-dark<CR> +amenu T&hemes.&Dark.Camo :colo camo<CR> +amenu T&hemes.&Dark.Candy :colo candy<CR> +amenu T&hemes.&Dark.Candycode :colo candycode<CR> +amenu T&hemes.&Dark.Dante :colo dante<CR> +amenu T&hemes.&Dark.Darkspectrum :colo darkspectrum<CR> +amenu T&hemes.&Dark.Desert256 :colo desert256<CR> +amenu T&hemes.&Dark.DesertEx :colo desertEx<CR> +amenu T&hemes.&Dark.Dusk :colo dusk<CR> +amenu T&hemes.&Dark.Earendel :colo earendel<CR> +amenu T&hemes.&Dark.Ekvoli :colo ekvoli<CR> +amenu T&hemes.&Dark.Fnaqevan :colo fnaqevan<CR> +amenu T&hemes.&Dark.Freya :colo freya<CR> +amenu T&hemes.&Dark.Golden :colo golden<CR> +amenu T&hemes.&Dark.Inkpot :colo inkpot<CR> +amenu T&hemes.&Dark.Jellybeans :colo jellybeans<CR> +amenu T&hemes.&Dark.Lucius :colo lucius<CR> +amenu T&hemes.&Dark.Manxome :colo manxome<CR> +amenu T&hemes.&Dark.Moria :colo moria<CR> +amenu T&hemes.&Dark.Motus :colo motus<CR> +amenu T&hemes.&Dark.Neon :colo neon<CR> +amenu T&hemes.&Dark.Neverness :colo neverness<CR> +amenu T&hemes.&Dark.Oceanblack :colo oceanblack<CR> +amenu T&hemes.&Dark.Railscasts :colo railscasts<CR> +amenu T&hemes.&Dark.Rdark :colo rdark<CR> +amenu T&hemes.&Dark.Relaxedgreen :colo relaxedgreen<CR> +amenu T&hemes.&Dark.Rootwater :colo rootwater<CR> +amenu T&hemes.&Dark.Tango :colo tango<CR> +amenu T&hemes.&Dark.Tango2 :colo tango2<CR> +amenu T&hemes.&Dark.Vibrantink :colo vibrantink<CR> +amenu T&hemes.&Dark.Vividchalk :colo vividchalk<CR> +amenu T&hemes.&Dark.Wombat :colo wombat<CR> +amenu T&hemes.&Dark.Zenburn :colo zenburn<CR> + +amenu T&hemes.&Light.Autumn :colo autumn<CR> +amenu T&hemes.&Light.Autumnleaf :colo autumnleaf<CR> +amenu T&hemes.&Light.Baycomb :colo baycomb<CR> +amenu T&hemes.&Light.Biogoo :colo biogoo<CR> +amenu T&hemes.&Light.Calmar256-light :colo calmar256-light<CR> +amenu T&hemes.&Light.Chela_light :colo chela_light<CR> +amenu T&hemes.&Light.Dawn :colo dawn<CR> +amenu T&hemes.&Light.Eclipse :colo eclipse<CR> +amenu T&hemes.&Light.Fog :colo fog<CR> +amenu T&hemes.&Light.Fruit :colo fruit<CR> +amenu T&hemes.&Light.Habilight :colo habilight<CR> +amenu T&hemes.&Light.Ironman :colo ironman<CR> +amenu T&hemes.&Light.Martin_krischik :colo martin_krischik<CR> +amenu T&hemes.&Light.Nuvola :colo nuvola<CR> +amenu T&hemes.&Light.PapayaWhip :colo PapayaWhip<CR> +amenu T&hemes.&Light.Sienna :colo sienna<CR> +amenu T&hemes.&Light.Simpleandfriendly :colo simpleandfriendly<CR> +amenu T&hemes.&Light.Tolerable :colo tolerable<CR> +amenu T&hemes.&Light.Vc :colo vc<CR> + +amenu T&hemes.&Other.Aiseered :colo aiseered<CR> +amenu T&hemes.&Other.Borland :colo borland<CR> +amenu T&hemes.&Other.Breeze :colo breeze<CR> +amenu T&hemes.&Other.Chocolateliquor :colo chocolateliquor<CR> +amenu T&hemes.&Other.Darkblue2 :colo darkblue2<CR> +amenu T&hemes.&Other.Darkslategray :colo darkslategray<CR> +amenu T&hemes.&Other.Marklar :colo marklar<CR> +amenu T&hemes.&Other.Navajo-night :colo navajo-night<CR> +amenu T&hemes.&Other.Night :colo night<CR> +amenu T&hemes.&Other.Oceandeep :colo oceandeep<CR> +amenu T&hemes.&Other.Peaksea :colo peaksea<CR> +amenu T&hemes.&Other.Sea :colo sea<CR> +amenu T&hemes.&Other.Settlemyer :colo settlemyer<CR> diff --git a/vim/plugin/conque_term.vim b/vim/plugin/conque_term.vim new file mode 100644 index 0000000..07f96e0 --- /dev/null +++ b/vim/plugin/conque_term.vim @@ -0,0 +1,93 @@ +" FILE: plugin/conque_term.vim {{{ +" AUTHOR: Nico Raffo <nicoraffo@gmail.com> +" MODIFIED: 2010-05-27 +" VERSION: 1.1, for Vim 7.0 +" LICENSE: +" Conque - pty interaction in Vim +" Copyright (C) 2009-2010 Nico Raffo +" +" MIT License +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to deal +" in the Software without restriction, including without limitation the rights +" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +" copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +" THE SOFTWARE. +" }}} + +" See docs/conque_term.txt for help or type :help conque_term + +if exists('g:ConqueTerm_Loaded') || v:version < 700 + finish +endif + +" ********************************************************************************************************** +" **** CONFIG ********************************************************************************************** +" ********************************************************************************************************** + +" Choose key mapping to leave insert mode {{{ +" If you choose something other than '<Esc>', then <Esc> will be sent to terminal +" Using a different key will usually fix Alt/Meta key issues +if !exists('g:ConqueTerm_EscKey') + let g:ConqueTerm_EscKey = '<Esc>' +endif " }}} + +" Enable color. {{{ +" If your apps use a lot of color it will slow down the shell. +if !exists('g:ConqueTerm_Color') + let g:ConqueTerm_Color = 1 +endif " }}} + +" TERM environment setting {{{ +if !exists('g:ConqueTerm_TERM') + let g:ConqueTerm_TERM = 'vt100' +endif " }}} + +" Syntax for your buffer {{{ +if !exists('g:ConqueTerm_Syntax') + let g:ConqueTerm_Syntax = 'conque_term' +endif " }}} + +" Keep on updating the shell window after you've switched to another buffer {{{ +if !exists('g:ConqueTerm_ReadUnfocused') + let g:ConqueTerm_ReadUnfocused = 0 +endif " }}} + +" Use this regular expression to highlight prompt {{{ +if !exists('g:ConqueTerm_PromptRegex') + let g:ConqueTerm_PromptRegex = '^\w\+@[0-9A-Za-z_.-]\+:[0-9A-Za-z_./\~,:-]\+\$' +endif " }}} + +" Allow user to use <C-w> keys to switch window in insert mode. {{{ +if !exists('g:ConqueTerm_CWInsert') + let g:ConqueTerm_CWInsert = 0 +endif " }}} + +" ********************************************************************************************************** +" **** Startup ********************************************************************************************* +" ********************************************************************************************************** + +" Startup {{{ + +let g:ConqueTerm_Loaded = 1 +let g:ConqueTerm_Idx = 1 + +command! -nargs=+ -complete=shellcmd ConqueTerm call conque_term#open(<q-args>) +command! -nargs=+ -complete=shellcmd ConqueTermSplit call conque_term#open(<q-args>, ['belowright split']) +command! -nargs=+ -complete=shellcmd ConqueTermVSplit call conque_term#open(<q-args>, ['belowright vsplit']) +command! -nargs=+ -complete=shellcmd ConqueTermTab call conque_term#open(<q-args>, ['tabnew']) + +" }}} + diff --git a/vim/plugin/delimitMate.vim b/vim/plugin/delimitMate.vim new file mode 100644 index 0000000..95ebf19 --- /dev/null +++ b/vim/plugin/delimitMate.vim @@ -0,0 +1,434 @@ +" File: plugin/delimitMate.vim +" Version: 2.6 +" Modified: 2011-01-14 +" Description: This plugin provides auto-completion for quotes, parens, etc. +" Maintainer: Israel Chauca F. <israelchauca@gmail.com> +" Manual: Read ":help delimitMate". +" ============================================================================ + +" Initialization: {{{ + +if exists("g:loaded_delimitMate") || &cp + " User doesn't want this plugin or compatible is set, let's get out! + finish +endif +let g:loaded_delimitMate = 1 + +if exists("s:loaded_delimitMate") && !exists("g:delimitMate_testing") + " Don't define the functions if they already exist: just do the work + " (unless we are testing): + call s:DelimitMateDo() + finish +endif + +if v:version < 700 + echoerr "delimitMate: this plugin requires vim >= 7!" + finish +endif + +let s:loaded_delimitMate = 1 +let delimitMate_version = "2.6" + +function! s:option_init(name, default) "{{{ + let b = exists("b:delimitMate_" . a:name) + let g = exists("g:delimitMate_" . a:name) + let prefix = "_l_delimitMate_" + + if !b && !g + let sufix = a:default + elseif !b && g + exec "let sufix = g:delimitMate_" . a:name + else + exec "let sufix = b:delimitMate_" . a:name + endif + if exists("b:" . prefix . a:name) + exec "unlockvar! b:" . prefix . a:name + endif + exec "let b:" . prefix . a:name . " = " . string(sufix) + exec "lockvar! b:" . prefix . a:name +endfunction "}}} + +function! s:init() "{{{ +" Initialize variables: + + " autoclose + call s:option_init("autoclose", 1) + + " matchpairs + call s:option_init("matchpairs", string(&matchpairs)[1:-2]) + call s:option_init("matchpairs_list", split(b:_l_delimitMate_matchpairs, ',')) + call s:option_init("left_delims", split(b:_l_delimitMate_matchpairs, ':.,\=')) + call s:option_init("right_delims", split(b:_l_delimitMate_matchpairs, ',\=.:')) + + " quotes + call s:option_init("quotes", "\" ' `") + call s:option_init("quotes_list", split(b:_l_delimitMate_quotes)) + + " nesting_quotes + call s:option_init("nesting_quotes", []) + + " excluded_regions + call s:option_init("excluded_regions", "Comment") + call s:option_init("excluded_regions_list", split(b:_l_delimitMate_excluded_regions, ',\s*')) + let enabled = len(b:_l_delimitMate_excluded_regions_list) > 0 + call s:option_init("excluded_regions_enabled", enabled) + + " excluded filetypes + call s:option_init("excluded_ft", "") + + " expand_space + if exists("b:delimitMate_expand_space") && type(b:delimitMate_expand_space) == type("") + echom "b:delimitMate_expand_space is '".b:delimitMate_expand_space."' but it must be either 1 or 0!" + echom "Read :help 'delimitMate_expand_space' for more details." + unlet b:delimitMate_expand_space + let b:delimitMate_expand_space = 1 + endif + if exists("g:delimitMate_expand_space") && type(g:delimitMate_expand_space) == type("") + echom "delimitMate_expand_space is '".g:delimitMate_expand_space."' but it must be either 1 or 0!" + echom "Read :help 'delimitMate_expand_space' for more details." + unlet g:delimitMate_expand_space + let g:delimitMate_expand_space = 1 + endif + call s:option_init("expand_space", 0) + + " expand_cr + if exists("b:delimitMate_expand_cr") && type(b:delimitMate_expand_cr) == type("") + echom "b:delimitMate_expand_cr is '".b:delimitMate_expand_cr."' but it must be either 1 or 0!" + echom "Read :help 'delimitMate_expand_cr' for more details." + unlet b:delimitMate_expand_cr + let b:delimitMate_expand_cr = 1 + endif + if exists("g:delimitMate_expand_cr") && type(g:delimitMate_expand_cr) == type("") + echom "delimitMate_expand_cr is '".g:delimitMate_expand_cr."' but it must be either 1 or 0!" + echom "Read :help 'delimitMate_expand_cr' for more details." + unlet g:delimitMate_expand_cr + let g:delimitMate_expand_cr = 1 + endif + if ((&backspace !~ 'eol' || &backspace !~ 'start') && &backspace != 2) && + \ ((exists('b:delimitMate_expand_cr') && b:delimitMate_expand_cr == 1) || + \ (exists('g:delimitMate_expand_cr') && g:delimitMate_expand_cr == 1)) + echom "delimitMate: There seems to be some incompatibility with your settings that may interfer with the expansion of <CR>. See :help 'delimitMate_expand_cr' for details." + endif + call s:option_init("expand_cr", 0) + + " smart_matchpairs + call s:option_init("smart_matchpairs", '^\%(\w\|\!\|£\|\$\|_\|["'']\s*\S\)') + + " smart_quotes + call s:option_init("smart_quotes", 1) + + " apostrophes + call s:option_init("apostrophes", "") + call s:option_init("apostrophes_list", split(b:_l_delimitMate_apostrophes, ":\s*")) + + " tab2exit + call s:option_init("tab2exit", 1) + + " balance_matchpairs + call s:option_init("balance_matchpairs", 0) + + let b:_l_delimitMate_buffer = [] + +endfunction "}}} Init() + +"}}} + +" Functions: {{{ + +function! s:Map() "{{{ + " Set mappings: + try + let save_cpo = &cpo + let save_keymap = &keymap + let save_iminsert = &iminsert + let save_imsearch = &imsearch + set keymap= + set cpo&vim + if b:_l_delimitMate_autoclose + call s:AutoClose() + else + call s:NoAutoClose() + endif + call s:ExtraMappings() + finally + let &cpo = save_cpo + let &keymap = save_keymap + let &iminsert = save_iminsert + let &imsearch = save_imsearch + endtry + + let b:delimitMate_enabled = 1 + +endfunction "}}} Map() + +function! s:Unmap() " {{{ + let imaps = + \ b:_l_delimitMate_right_delims + + \ b:_l_delimitMate_left_delims + + \ b:_l_delimitMate_quotes_list + + \ b:_l_delimitMate_apostrophes_list + + \ ['<BS>', '<S-BS>', '<Del>', '<CR>', '<Space>', '<S-Tab>', '<Esc>'] + + \ ['<Up>', '<Down>', '<Left>', '<Right>', '<LeftMouse>', '<RightMouse>'] + + \ ['<Home>', '<End>', '<PageUp>', '<PageDown>', '<S-Down>', '<S-Up>', '<C-G>g'] + + for map in imaps + if maparg(map, "i") =~? 'delimitMate' + if map == '|' + let map = '<Bar>' + endif + exec 'silent! iunmap <buffer> ' . map + endif + endfor + + if !has('gui_running') + silent! iunmap <C-[>OC + endif + + let b:delimitMate_enabled = 0 +endfunction " }}} s:Unmap() + +function! s:TestMappingsDo() "{{{ + %d + if !exists("g:delimitMate_testing") + silent call delimitMate#TestMappings() + else + let temp_varsDM = [b:_l_delimitMate_expand_space, b:_l_delimitMate_expand_cr, b:_l_delimitMate_autoclose] + for i in [0,1] + let b:delimitMate_expand_space = i + let b:delimitMate_expand_cr = i + for a in [0,1] + let b:delimitMate_autoclose = a + call s:init() + call s:Unmap() + call s:Map() + call delimitMate#TestMappings() + call append(line('$'),'') + endfor + endfor + let b:delimitMate_expand_space = temp_varsDM[0] + let b:delimitMate_expand_cr = temp_varsDM[1] + let b:delimitMate_autoclose = temp_varsDM[2] + unlet temp_varsDM + endif + normal gg + g/\%^$/d +endfunction "}}} + +function! s:DelimitMateDo(...) "{{{ + + " First, remove all magic, if needed: + if exists("b:delimitMate_enabled") && b:delimitMate_enabled == 1 + call s:Unmap() + endif + + " Check if this file type is excluded: + if exists("g:delimitMate_excluded_ft") && + \ index(split(g:delimitMate_excluded_ft, ','), &filetype, 0, 1) >= 0 + + " Finish here: + return 1 + endif + + " Check if user tried to disable using b:loaded_delimitMate + if exists("b:loaded_delimitMate") + return 1 + endif + + " Initialize settings: + call s:init() + + " Now, add magic: + call s:Map() + + if a:0 > 0 + echo "delimitMate has been reset." + endif +endfunction "}}} + +function! s:DelimitMateSwitch() "{{{ + if exists("b:delimitMate_enabled") && b:delimitMate_enabled + call s:Unmap() + echo "delimitMate has been disabled." + else + call s:Unmap() + call s:init() + call s:Map() + echo "delimitMate has been enabled." + endif +endfunction "}}} + +function! s:Finish() " {{{ + if exists('g:delimitMate_loaded') + return delimitMate#Finish(1) + endif + return '' +endfunction " }}} + +function! s:FlushBuffer() " {{{ + if exists('g:delimitMate_loaded') + return delimitMate#FlushBuffer() + endif + return '' +endfunction " }}} + +"}}} + +" Mappers: {{{ +function! s:NoAutoClose() "{{{ + " inoremap <buffer> ) <C-R>=delimitMate#SkipDelim('\)')<CR> + for delim in b:_l_delimitMate_right_delims + b:_l_delimitMate_quotes_list + if delim == '|' + let delim = '<Bar>' + endif + exec 'inoremap <silent> <Plug>delimitMate' . delim . ' <C-R>=delimitMate#SkipDelim("' . escape(delim,'"') . '")<CR>' + exec 'silent! imap <unique> <buffer> '.delim.' <Plug>delimitMate'.delim + endfor +endfunction "}}} + +function! s:AutoClose() "{{{ + " Add matching pair and jump to the midle: + " inoremap <silent> <buffer> ( ()<Left> + let i = 0 + while i < len(b:_l_delimitMate_matchpairs_list) + let ld = b:_l_delimitMate_left_delims[i] == '|' ? '<bar>' : b:_l_delimitMate_left_delims[i] + let rd = b:_l_delimitMate_right_delims[i] == '|' ? '<bar>' : b:_l_delimitMate_right_delims[i] + exec 'inoremap <silent> <Plug>delimitMate' . ld . ' ' . ld . '<C-R>=delimitMate#ParenDelim("' . escape(rd, '|') . '")<CR>' + exec 'silent! imap <unique> <buffer> '.ld.' <Plug>delimitMate'.ld + let i += 1 + endwhile + + " Exit from inside the matching pair: + for delim in b:_l_delimitMate_right_delims + exec 'inoremap <silent> <Plug>delimitMate' . delim . ' <C-R>=delimitMate#JumpOut("\' . delim . '")<CR>' + exec 'silent! imap <unique> <buffer> ' . delim . ' <Plug>delimitMate'. delim + endfor + + " Add matching quote and jump to the midle, or exit if inside a pair of matching quotes: + " inoremap <silent> <buffer> " <C-R>=delimitMate#QuoteDelim("\"")<CR> + for delim in b:_l_delimitMate_quotes_list + if delim == '|' + let delim = '<Bar>' + endif + exec 'inoremap <silent> <Plug>delimitMate' . delim . ' <C-R>=delimitMate#QuoteDelim("\' . delim . '")<CR>' + exec 'silent! imap <unique> <buffer> ' . delim . ' <Plug>delimitMate' . delim + endfor + + " Try to fix the use of apostrophes (kept for backward compatibility): + " inoremap <silent> <buffer> n't n't + for map in b:_l_delimitMate_apostrophes_list + exec "inoremap <silent> " . map . " " . map + exec 'silent! imap <unique> <buffer> ' . map . ' <Plug>delimitMate' . map + endfor +endfunction "}}} + +function! s:ExtraMappings() "{{{ + " If pair is empty, delete both delimiters: + inoremap <silent> <Plug>delimitMateBS <C-R>=delimitMate#BS()<CR> + if !hasmapto('<Plug>delimitMateBS','i') + silent! imap <unique> <buffer> <BS> <Plug>delimitMateBS + endif + " If pair is empty, delete closing delimiter: + inoremap <silent> <expr> <Plug>delimitMateS-BS delimitMate#WithinEmptyPair() ? "\<C-R>=delimitMate#Del()\<CR>" : "\<S-BS>" + if !hasmapto('<Plug>delimitMateS-BS','i') + silent! imap <unique> <buffer> <S-BS> <Plug>delimitMateS-BS + endif + " Expand return if inside an empty pair: + inoremap <silent> <Plug>delimitMateCR <C-R>=delimitMate#ExpandReturn()<CR> + if b:_l_delimitMate_expand_cr != 0 && !hasmapto('<Plug>delimitMateCR', 'i') + silent! imap <unique> <buffer> <CR> <Plug>delimitMateCR + endif + " Expand space if inside an empty pair: + inoremap <silent> <Plug>delimitMateSpace <C-R>=delimitMate#ExpandSpace()<CR> + if b:_l_delimitMate_expand_space != 0 && !hasmapto('<Plug>delimitMateSpace', 'i') + silent! imap <unique> <buffer> <Space> <Plug>delimitMateSpace + endif + " Jump over any delimiter: + inoremap <silent> <Plug>delimitMateS-Tab <C-R>=delimitMate#JumpAny("\<S-Tab>")<CR> + if b:_l_delimitMate_tab2exit && !hasmapto('<Plug>delimitMateS-Tab', 'i') + silent! imap <unique> <buffer> <S-Tab> <Plug>delimitMateS-Tab + endif + " Change char buffer on Del: + inoremap <silent> <Plug>delimitMateDel <C-R>=delimitMate#Del()<CR> + if !hasmapto('<Plug>delimitMateDel', 'i') + silent! imap <unique> <buffer> <Del> <Plug>delimitMateDel + endif + " Flush the char buffer on movement keystrokes or when leaving insert mode: + for map in ['Esc', 'Left', 'Right', 'Home', 'End'] + exec 'inoremap <silent> <Plug>delimitMate'.map.' <C-R>=<SID>Finish()<CR><'.map.'>' + if !hasmapto('<Plug>delimitMate'.map, 'i') + exec 'silent! imap <unique> <buffer> <'.map.'> <Plug>delimitMate'.map + endif + endfor + " Except when pop-up menu is active: + for map in ['Up', 'Down', 'PageUp', 'PageDown', 'S-Down', 'S-Up'] + exec 'inoremap <silent> <expr> <Plug>delimitMate'.map.' pumvisible() ? "\<'.map.'>" : "\<C-R>=\<SID>Finish()\<CR>\<'.map.'>"' + if !hasmapto('<Plug>delimitMate'.map, 'i') + exec 'silent! imap <unique> <buffer> <'.map.'> <Plug>delimitMate'.map + endif + endfor + " Avoid ambiguous mappings: + for map in ['LeftMouse', 'RightMouse'] + exec 'inoremap <silent> <Plug>delimitMateM'.map.' <C-R>=delimitMate#Finish(1)<CR><'.map.'>' + if !hasmapto('<Plug>delimitMate'.map, 'i') + exec 'silent! imap <unique> <buffer> <'.map.'> <Plug>delimitMateM'.map + endif + endfor + + " Jump over next delimiters + inoremap <buffer> <Plug>delimitMateJumpMany <C-R>=len(b:_l_delimitMate_buffer) ? delimitMate#Finish(0) : delimitMate#JumpMany()<CR> + if !hasmapto('<Plug>delimitMateJumpMany') + imap <silent> <buffer> <C-G>g <Plug>delimitMateJumpMany + endif + + " The following simply creates an ambiguous mapping so vim fully processes + " the escape sequence for terminal keys, see 'ttimeout' for a rough + " explanation, this just forces it to work + if !has('gui_running') + imap <silent> <C-[>OC <RIGHT> + endif +endfunction "}}} + +"}}} + +" Commands: {{{ + +call s:DelimitMateDo() + +" Let me refresh without re-loading the buffer: +command! -bar DelimitMateReload call s:DelimitMateDo(1) + +" Quick test: +command! -bar DelimitMateTest silent call s:TestMappingsDo() + +" Switch On/Off: +command! -bar DelimitMateSwitch call s:DelimitMateSwitch() +"}}} + +" Autocommands: {{{ + +augroup delimitMate + au! + " Run on file type change. + "autocmd VimEnter * autocmd FileType * call <SID>DelimitMateDo() + autocmd FileType * call <SID>DelimitMateDo() + + " Run on new buffers. + autocmd BufNewFile,BufRead,BufEnter * + \ if !exists('b:delimitMate_was_here') | + \ call <SID>DelimitMateDo() | + \ let b:delimitMate_was_here = 1 | + \ endif + + " Flush the char buffer: + autocmd InsertEnter * call <SID>FlushBuffer() + autocmd BufEnter * + \ if mode() == 'i' | + \ call <SID>FlushBuffer() | + \ endif + +augroup END + +"}}} + +" GetLatestVimScripts: 2754 1 :AutoInstall: delimitMate.vim +" vim:foldmethod=marker:foldcolumn=4 diff --git a/vim/plugin/endwise.vim b/vim/plugin/endwise.vim new file mode 100644 index 0000000..dca90ff --- /dev/null +++ b/vim/plugin/endwise.vim @@ -0,0 +1,134 @@ +" endwise.vim - EndWise +" Author: Tim Pope <http://tpo.pe/> +" Version: 1.0 +" License: Same as Vim itself. See :help license +" GetLatestVimScripts: 2386 1 :AutoInstall: endwise.vim + +if exists("g:loaded_endwise") || &cp + finish +endif +let g:loaded_endwise = 1 + +augroup endwise " {{{1 + autocmd! + autocmd FileType lua + \ let b:endwise_addition = '\=submatch(0)=="{" ? "}" : "end"' | + \ let b:endwise_words = 'function,do,then' | + \ let b:endwise_pattern = '^\s*\zs\%(function\|do\|then\)\>\%(.*[^.:@$]\<end\>\)\@!\|\<then\|do\ze\%(\s*|.*|\)\=\s*$' | + \ let b:endwise_syngroups = 'luaFunction,luaStatement,luaCond' + autocmd FileType ruby + \ let b:endwise_addition = '\=submatch(0)=="{" ? "}" : "end"' | + \ let b:endwise_words = 'module,class,def,if,unless,case,while,until,begin,do' | + \ let b:endwise_pattern = '^\s*\zs\%(module\|class\|def\|if\|unless\|case\|while\|until\|for\|\|begin\)\>\%(.*[^.:@$]\<end\>\)\@!\|\<do\ze\%(\s*|.*|\)\=\s*$' | + \ let b:endwise_syngroups = 'rubyModule,rubyClass,rubyDefine,rubyControl,rubyConditional,rubyRepeat' + autocmd FileType sh,zsh + \ let b:endwise_addition = '\=submatch(0)=="if" ? "fi" : submatch(0)=="case" ? "esac" : "done"' | + \ let b:endwise_words = 'if,until,case,do' | + \ let b:endwise_pattern = '\%(^\s*\zs\%(if\|case\)\>\ze\|\zs\<do\ze$\|^\s*\zsdo\s*\ze$\)' | + \ let b:endwise_syngroups = 'shConditional,shLoop,shIf,shFor,shRepeat,shCaseEsac,zshConditional,zshRepeat,zshDelimiter' + autocmd FileType vb,vbnet,aspvbs + \ let b:endwise_addition = 'End &' | + \ let b:endwise_words = 'Function,Sub,Class,Module,Enum,Namespace' | + \ let b:endwise_pattern = '\%(\<End\>.*\)\@<!\<&\>' | + \ let b:endwise_syngroups = 'vbStatement,vbnetStorage,vbnetProcedure,vbnet.*Words,AspVBSStatement' + autocmd FileType vim + \ let b:endwise_addition = 'end&' | + \ let b:endwise_words = 'fu\%[nction],wh\%[ile],if,for,try' | + \ let b:endwise_syngroups = 'vimFuncKey,vimNotFunc,vimCommand' +augroup END " }}}1 + +" Maps {{{1 + +if maparg("<Plug>DiscretionaryEnd") == "" + inoremap <silent> <SID>DiscretionaryEnd <C-R>=<SID>crend(0)<CR> + inoremap <silent> <SID>AlwaysEnd <C-R>=<SID>crend(1)<CR> + imap <script> <Plug>DiscretionaryEnd <SID>DiscretionaryEnd + imap <script> <Plug>AlwaysEnd <SID>AlwaysEnd +endif +if maparg('<CR>','i') =~# '<C-R>=.*crend(.)<CR>\|<\%(Plug\|SID\)>.*End' + " Already mapped +elseif maparg('<CR>','i') =~ '<CR>' + exe "imap <script> <C-X><CR> ".maparg('<CR>','i')."<SID>AlwaysEnd" + exe "imap <script> <CR> ".maparg('<CR>','i')."<SID>DiscretionaryEnd" +elseif maparg('<CR>','i') =~ '<Plug>delimitMateCR' + exe "imap <C-X><CR> ".maparg('<CR>', 'i')."<Plug>AlwaysEnd" + exe "imap <CR> ".maparg('<CR>', 'i')."<Plug>DiscretionaryEnd" +else + imap <C-X><CR> <CR><Plug>AlwaysEnd + imap <CR> <CR><Plug>DiscretionaryEnd +endif + +if maparg('<M-o>','i') == '' + inoremap <M-o> <C-O>o +endif + +" }}}1 + +" Code {{{1 + +function! s:mysearchpair(beginpat,endpat,synpat) + let g:endwise_syntaxes = "" + let s:lastline = line('.') + call s:synname() + let line = searchpair(a:beginpat,'',a:endpat,'Wn','<SID>synname() !~# "^'.substitute(a:synpat,'\\','\\\\','g').'$"',line('.')+50) + return line +endfunction + +function! s:crend(always) + let n = "" + if !exists("b:endwise_addition") || !exists("b:endwise_words") || !exists("b:endwise_syngroups") + return n + end + let synpat = '\%('.substitute(b:endwise_syngroups,',','\\|','g').'\)' + let wordchoice = '\%('.substitute(b:endwise_words,',','\\|','g').'\)' + if exists("b:endwise_pattern") + let beginpat = substitute(b:endwise_pattern,'&',substitute(wordchoice,'\\','\\&','g'),'g') + else + let beginpat = '\<'.wordchoice.'\>' + endif + let lnum = line('.') - 1 + let space = matchstr(getline(lnum),'^\s*') + let col = match(getline(lnum),beginpat) + 1 + let word = matchstr(getline(lnum),beginpat) + let endword = substitute(word,'.*',b:endwise_addition,'') + let y = n.endword."\<C-O>O" + let endpat = '\<'.endword.'\>' + if a:always + return y + elseif col <= 0 || synIDattr(synID(lnum,col,1),'name') !~ '^'.synpat.'$' + return n + elseif getline('.') !~ '^\s*#\=$' + return n + endif + let line = s:mysearchpair(beginpat,endpat,synpat) + " even is false if no end was found, or if the end found was less + " indented than the current line + let even = strlen(matchstr(getline(line),'^\s*')) >= strlen(space) + if line == 0 + let even = 0 + endif + if !even && line == line('.') + 1 + return y + endif + if even + return n + endif + return y +endfunction + +function! s:synname() + " Checking this helps to force things to stay in sync + while s:lastline < line('.') + let s = synIDattr(synID(s:lastline,indent(s:lastline)+1,1),'name') + let s:lastline = nextnonblank(s:lastline + 1) + endwhile + + let s = synIDattr(synID(line('.'),col('.'),1),'name') + let g:endwise_syntaxes = g:endwise_syntaxes . line('.').','.col('.')."=".s."\n" + let s:lastline = line('.') + return s +endfunction + +" }}}1 + +" vim:set sw=2 sts=2: diff --git a/vim/plugin/fugitive.vim b/vim/plugin/fugitive.vim new file mode 100644 index 0000000..17d52fc --- /dev/null +++ b/vim/plugin/fugitive.vim @@ -0,0 +1,2220 @@ +" fugitive.vim - A Git wrapper so awesome, it should be illegal +" Maintainer: Tim Pope <http://tpo.pe/> +" Version: 1.2 +" GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim + +if exists('g:loaded_fugitive') || &cp + finish +endif +let g:loaded_fugitive = 1 + +if !exists('g:fugitive_git_executable') + let g:fugitive_git_executable = 'git' +endif + +" Utility {{{1 + +function! s:function(name) abort + return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),'')) +endfunction + +function! s:sub(str,pat,rep) abort + return substitute(a:str,'\v\C'.a:pat,a:rep,'') +endfunction + +function! s:gsub(str,pat,rep) abort + return substitute(a:str,'\v\C'.a:pat,a:rep,'g') +endfunction + +function! s:shellesc(arg) abort + if a:arg =~ '^[A-Za-z0-9_/.-]\+$' + return a:arg + elseif &shell =~# 'cmd' && a:arg !~# '"' + return '"'.a:arg.'"' + else + return shellescape(a:arg) + endif +endfunction + +function! s:fnameescape(file) abort + if exists('*fnameescape') + return fnameescape(a:file) + else + return escape(a:file," \t\n*?[{`$\\%#'\"|!<") + endif +endfunction + +function! s:throw(string) abort + let v:errmsg = 'fugitive: '.a:string + throw v:errmsg +endfunction + +function! s:warn(str) + echohl WarningMsg + echomsg a:str + echohl None + let v:warningmsg = a:str +endfunction + +function! s:shellslash(path) + if exists('+shellslash') && !&shellslash + return s:gsub(a:path,'\\','/') + else + return a:path + endif +endfunction + +function! s:recall() + let rev = s:buffer().rev() + if rev ==# ':' + return matchstr(getline('.'),'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (new commits)\)\=$\|^\d\{6} \x\{40\} \d\t\zs.*') + endif + return rev +endfunction + +function! s:add_methods(namespace, method_names) abort + for name in a:method_names + let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name) + endfor +endfunction + +let s:commands = [] +function! s:command(definition) abort + let s:commands += [a:definition] +endfunction + +function! s:define_commands() + for command in s:commands + exe 'command! -buffer '.command + endfor +endfunction + +function! s:compatibility_check() + if exists('b:git_dir') && exists('*GitBranchInfoCheckGitDir') && !exists('g:fugitive_did_compatibility_warning') + let g:fugitive_did_compatibility_warning = 1 + call s:warn("See http://github.com/tpope/vim-fugitive/issues#issue/1 for why you should remove git-branch-info.vim") + endif +endfunction + +augroup fugitive_utility + autocmd! + autocmd User Fugitive call s:define_commands() + autocmd VimEnter * call s:compatibility_check() +augroup END + +let s:abstract_prototype = {} + +" }}}1 +" Initialization {{{1 + +function! s:ExtractGitDir(path) abort + let path = s:shellslash(a:path) + if path =~? '^fugitive://.*//' + return matchstr(path,'fugitive://\zs.\{-\}\ze//') + endif + let fn = fnamemodify(path,':s?[\/]$??') + let ofn = "" + let nfn = fn + while fn != ofn + if filereadable(fn . '/.git/HEAD') + return s:sub(simplify(fnamemodify(fn . '/.git',':p')),'\W$','') + elseif fn =~ '\.git$' && filereadable(fn . '/HEAD') + return s:sub(simplify(fnamemodify(fn,':p')),'\W$','') + endif + let ofn = fn + let fn = fnamemodify(ofn,':h') + endwhile + return '' +endfunction + +function! s:Detect(path) + if exists('b:git_dir') && b:git_dir ==# '' + unlet b:git_dir + endif + if !exists('b:git_dir') + let dir = s:ExtractGitDir(a:path) + if dir != '' + let b:git_dir = dir + endif + endif + if exists('b:git_dir') + silent doautocmd User Fugitive + cnoremap <expr> <buffer> <C-R><C-G> <SID>recall() + let buffer = fugitive#buffer() + if expand('%:p') =~# '//' + call buffer.setvar('&path',s:sub(buffer.getvar('&path'),'^\.%(,|$)','')) + endif + if stridx(buffer.getvar('&tags'),escape(b:git_dir.'/tags',', ')) == -1 + call buffer.setvar('&tags',escape(b:git_dir.'/tags',', ').','.buffer.getvar('&tags')) + if &filetype != '' + call buffer.setvar('&tags',escape(b:git_dir.'/'.&filetype.'.tags',', ').','.buffer.getvar('&tags')) + endif + endif + endif +endfunction + +augroup fugitive + autocmd! + autocmd BufNewFile,BufReadPost * call s:Detect(expand('<amatch>:p')) + autocmd FileType netrw call s:Detect(expand('<afile>:p')) + autocmd VimEnter * if expand('<amatch>')==''|call s:Detect(getcwd())|endif + autocmd BufWinLeave * execute getwinvar(+winnr(), 'fugitive_leave') +augroup END + +" }}}1 +" Repository {{{1 + +let s:repo_prototype = {} +let s:repos = {} + +function! s:repo(...) abort + let dir = a:0 ? a:1 : (exists('b:git_dir') && b:git_dir !=# '' ? b:git_dir : s:ExtractGitDir(expand('%:p'))) + if dir !=# '' + if has_key(s:repos,dir) + let repo = get(s:repos,dir) + else + let repo = {'git_dir': dir} + let s:repos[dir] = repo + endif + return extend(extend(repo,s:repo_prototype,'keep'),s:abstract_prototype,'keep') + endif + call s:throw('not a git repository: '.expand('%:p')) +endfunction + +function! s:repo_dir(...) dict abort + return join([self.git_dir]+a:000,'/') +endfunction + +function! s:repo_tree(...) dict abort + if !self.bare() + let dir = fnamemodify(self.git_dir,':h') + return join([dir]+a:000,'/') + endif + call s:throw('no work tree') +endfunction + +function! s:repo_bare() dict abort + return self.dir() !~# '/\.git$' +endfunction + +function! s:repo_translate(spec) dict abort + if a:spec ==# '.' || a:spec ==# '/.' + return self.bare() ? self.dir() : self.tree() + elseif a:spec =~# '^/' + return fnamemodify(self.dir(),':h').a:spec + elseif a:spec =~# '^:[0-3]:' + return 'fugitive://'.self.dir().'//'.a:spec[1].'/'.a:spec[3:-1] + elseif a:spec ==# ':' + if $GIT_INDEX_FILE =~# '/[^/]*index[^/]*\.lock$' && fnamemodify($GIT_INDEX_FILE,':p')[0:strlen(s:repo().dir())] ==# s:repo().dir('') && filereadable($GIT_INDEX_FILE) + return fnamemodify($GIT_INDEX_FILE,':p') + else + return self.dir('index') + endif + elseif a:spec =~# '^:/' + let ref = self.rev_parse(matchstr(a:spec,'.[^:]*')) + return 'fugitive://'.self.dir().'//'.ref + elseif a:spec =~# '^:' + return 'fugitive://'.self.dir().'//0/'.a:spec[1:-1] + elseif a:spec =~# 'HEAD\|^refs/' && a:spec !~ ':' && filereadable(self.dir(a:spec)) + return self.dir(a:spec) + elseif filereadable(s:repo().dir('refs/'.a:spec)) + return self.dir('refs/'.a:spec) + elseif filereadable(s:repo().dir('refs/tags/'.a:spec)) + return self.dir('refs/tags/'.a:spec) + elseif filereadable(s:repo().dir('refs/heads/'.a:spec)) + return self.dir('refs/heads/'.a:spec) + elseif filereadable(s:repo().dir('refs/remotes/'.a:spec)) + return self.dir('refs/remotes/'.a:spec) + elseif filereadable(s:repo().dir('refs/remotes/'.a:spec.'/HEAD')) + return self.dir('refs/remotes/'.a:spec,'/HEAD') + else + try + let ref = self.rev_parse(matchstr(a:spec,'[^:]*')) + let path = s:sub(matchstr(a:spec,':.*'),'^:','/') + return 'fugitive://'.self.dir().'//'.ref.path + catch /^fugitive:/ + return self.tree(a:spec) + endtry + endif +endfunction + +call s:add_methods('repo',['dir','tree','bare','translate']) + +function! s:repo_git_command(...) dict abort + let git = g:fugitive_git_executable . ' --git-dir='.s:shellesc(self.git_dir) + return git.join(map(copy(a:000),'" ".s:shellesc(v:val)'),'') +endfunction + +function! s:repo_git_chomp(...) dict abort + return s:sub(system(call(self.git_command,a:000,self)),'\n$','') +endfunction + +function! s:repo_git_chomp_in_tree(...) dict abort + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd.'`=s:repo().tree()`' + return call(s:repo().git_chomp, a:000, s:repo()) + finally + execute cd.'`=dir`' + endtry +endfunction + +function! s:repo_rev_parse(rev) dict abort + let hash = self.git_chomp('rev-parse','--verify',a:rev) + if hash =~ '\<\x\{40\}$' + return matchstr(hash,'\<\x\{40\}$') + endif + call s:throw('rev-parse '.a:rev.': '.hash) +endfunction + +call s:add_methods('repo',['git_command','git_chomp','git_chomp_in_tree','rev_parse']) + +function! s:repo_dirglob(base) dict abort + let base = s:sub(a:base,'^/','') + let matches = split(glob(self.tree(s:gsub(base,'/','*&').'*/')),"\n") + call map(matches,'v:val[ strlen(self.tree())+(a:base !~ "^/") : -1 ]') + return matches +endfunction + +function! s:repo_superglob(base) dict abort + if a:base =~# '^/' || a:base !~# ':' + let results = [] + if a:base !~# '^/' + let heads = ["HEAD","ORIG_HEAD","FETCH_HEAD","MERGE_HEAD"] + let heads += sort(split(s:repo().git_chomp("rev-parse","--symbolic","--branches","--tags","--remotes"),"\n")) + call filter(heads,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base') + let results += heads + endif + if !self.bare() + let base = s:sub(a:base,'^/','') + let matches = split(glob(self.tree(s:gsub(base,'/','*&').'*')),"\n") + call map(matches,'s:shellslash(v:val)') + call map(matches,'v:val !~ "/$" && isdirectory(v:val) ? v:val."/" : v:val') + call map(matches,'v:val[ strlen(self.tree())+(a:base !~ "^/") : -1 ]') + let results += matches + endif + return results + + elseif a:base =~# '^:' + let entries = split(self.git_chomp('ls-files','--stage'),"\n") + call map(entries,'s:sub(v:val,".*(\\d)\\t(.*)",":\\1:\\2")') + if a:base !~# '^:[0-3]\%(:\|$\)' + call filter(entries,'v:val[1] == "0"') + call map(entries,'v:val[2:-1]') + endif + call filter(entries,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base') + return entries + + else + let tree = matchstr(a:base,'.*[:/]') + let entries = split(self.git_chomp('ls-tree',tree),"\n") + call map(entries,'s:sub(v:val,"^04.*\\zs$","/")') + call map(entries,'tree.s:sub(v:val,".*\t","")') + return filter(entries,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base') + endif +endfunction + +call s:add_methods('repo',['dirglob','superglob']) + +function! s:repo_config(conf) dict abort + return matchstr(system(s:repo().git_command('config').' '.a:conf),"[^\r\n]*") +endfun + +function! s:repo_user() dict abort + let username = s:repo().config('user.name') + let useremail = s:repo().config('user.email') + return username.' <'.useremail.'>' +endfun + +function! s:repo_aliases() dict abort + if !has_key(self,'_aliases') + let self._aliases = {} + for line in split(self.git_chomp('config','--get-regexp','^alias[.]'),"\n") + let self._aliases[matchstr(line,'\.\zs\S\+')] = matchstr(line,' \zs.*') + endfor + endif + return self._aliases +endfunction + +call s:add_methods('repo',['config', 'user', 'aliases']) + +function! s:repo_keywordprg() dict abort + let args = ' --git-dir='.escape(self.dir(),"\\\"' ").' show' + if has('gui_running') && !has('win32') + return g:fugitive_git_executable . ' --no-pager' . args + else + return g:fugitive_git_executable . args + endif +endfunction + +call s:add_methods('repo',['keywordprg']) + +" }}}1 +" Buffer {{{1 + +let s:buffer_prototype = {} + +function! s:buffer(...) abort + let buffer = {'#': bufnr(a:0 ? a:1 : '%')} + call extend(extend(buffer,s:buffer_prototype,'keep'),s:abstract_prototype,'keep') + if buffer.getvar('git_dir') !=# '' + return buffer + endif + call s:throw('not a git repository: '.expand('%:p')) +endfunction + +function! fugitive#buffer(...) abort + return s:buffer(a:0 ? a:1 : '%') +endfunction + +function! s:buffer_getvar(var) dict abort + return getbufvar(self['#'],a:var) +endfunction + +function! s:buffer_setvar(var,value) dict abort + return setbufvar(self['#'],a:var,a:value) +endfunction + +function! s:buffer_getline(lnum) dict abort + return getbufline(self['#'],a:lnum)[0] +endfunction + +function! s:buffer_repo() dict abort + return s:repo(self.getvar('git_dir')) +endfunction + +function! s:buffer_type(...) dict abort + if self.getvar('fugitive_type') != '' + let type = self.getvar('fugitive_type') + elseif fnamemodify(self.spec(),':p') =~# '.\git/refs/\|\.git/\w*HEAD$' + let type = 'head' + elseif self.getline(1) =~ '^tree \x\{40\}$' && self.getline(2) == '' + let type = 'tree' + elseif self.getline(1) =~ '^\d\{6\} \w\{4\} \x\{40\}\>\t' + let type = 'tree' + elseif self.getline(1) =~ '^\d\{6\} \x\{40\}\> \d\t' + let type = 'index' + elseif isdirectory(self.spec()) + let type = 'directory' + elseif self.spec() == '' + let type = 'null' + else + let type = 'file' + endif + if a:0 + return !empty(filter(copy(a:000),'v:val ==# type')) + else + return type + endif +endfunction + +if has('win32') + + function! s:buffer_spec() dict abort + let bufname = bufname(self['#']) + let retval = '' + for i in split(bufname,'[^:]\zs\\') + let retval = fnamemodify((retval==''?'':retval.'\').i,':.') + endfor + return s:shellslash(fnamemodify(retval,':p')) + endfunction + +else + + function! s:buffer_spec() dict abort + let bufname = bufname(self['#']) + return s:shellslash(bufname == '' ? '' : fnamemodify(bufname,':p')) + endfunction + +endif + +function! s:buffer_name() dict abort + return self.spec() +endfunction + +function! s:buffer_commit() dict abort + return matchstr(self.spec(),'^fugitive://.\{-\}//\zs\w*') +endfunction + +function! s:buffer_path(...) dict abort + let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*') + if rev != '' + let rev = s:sub(rev,'\w*','') + else + let rev = self.spec()[strlen(self.repo().tree()) : -1] + endif + return s:sub(s:sub(rev,'.\zs/$',''),'^/',a:0 ? a:1 : '') +endfunction + +function! s:buffer_rev() dict abort + let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*') + if rev =~ '^\x/' + return ':'.rev[0].':'.rev[2:-1] + elseif rev =~ '.' + return s:sub(rev,'/',':') + elseif self.spec() =~ '\.git/index$' + return ':' + elseif self.spec() =~ '\.git/refs/\|\.git/.*HEAD$' + return self.spec()[strlen(self.repo().dir())+1 : -1] + else + return self.path() + endif +endfunction + +function! s:buffer_sha1() dict abort + if self.spec() =~ '^fugitive://' || self.spec() =~ '\.git/refs/\|\.git/.*HEAD$' + return self.repo().rev_parse(self.rev()) + else + return '' + endif +endfunction + +function! s:buffer_expand(rev) dict abort + if a:rev =~# '^:[0-3]$' + let file = a:rev.self.path(':') + elseif a:rev =~# '^[-:]/$' + let file = '/'.self.path() + elseif a:rev =~# '^-' + let file = 'HEAD^{}'.a:rev[1:-1].self.path(':') + elseif a:rev =~# '^@{' + let file = 'HEAD'.a:rev.self.path(':') + elseif a:rev =~# '^[~^]' + let commit = s:sub(self.commit(),'^\d=$','HEAD') + let file = commit.a:rev.self.path(':') + else + let file = a:rev + endif + return s:sub(s:sub(file,'\%$',self.path()),'\.\@<=/$','') +endfunction + +function! s:buffer_containing_commit() dict abort + if self.commit() =~# '^\d$' + return ':' + elseif self.commit() =~# '.' + return self.commit() + else + return 'HEAD' + endif +endfunction + +call s:add_methods('buffer',['getvar','setvar','getline','repo','type','spec','name','commit','path','rev','sha1','expand','containing_commit']) + +" }}}1 +" Git {{{1 + +call s:command("-bang -nargs=? -complete=customlist,s:GitComplete Git :execute s:Git(<bang>0,<q-args>)") + +function! s:ExecuteInTree(cmd) abort + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd.'`=s:repo().tree()`' + execute a:cmd + finally + execute cd.'`=dir`' + endtry +endfunction + +function! s:Git(bang,cmd) abort + if a:bang + return s:Edit('edit',1,a:cmd) + endif + let git = s:repo().git_command() + if has('gui_running') && !has('win32') + let git .= ' --no-pager' + endif + let cmd = matchstr(a:cmd,'\v\C.{-}%($|\\@<!%(\\\\)*\|)@=') + call s:ExecuteInTree('!'.git.' '.cmd) + call fugitive#reload_status() + return matchstr(a:cmd,'\v\C\\@<!%(\\\\)*\|\zs.*') +endfunction + +function! s:GitComplete(A,L,P) abort + if !exists('s:exec_path') + let s:exec_path = s:sub(system(g:fugitive_git_executable.' --exec-path'),'\n$','') + endif + let cmds = map(split(glob(s:exec_path.'/git-*'),"\n"),'s:sub(v:val[strlen(s:exec_path)+5 : -1],"\\.exe$","")') + if a:L =~ ' [[:alnum:]-]\+ ' + return s:repo().superglob(a:A) + elseif a:A == '' + return sort(cmds+keys(s:repo().aliases())) + else + return filter(sort(cmds+keys(s:repo().aliases())),'v:val[0:strlen(a:A)-1] ==# a:A') + endif +endfunction + +" }}}1 +" Gcd, Glcd {{{1 + +function! s:DirComplete(A,L,P) abort + let matches = s:repo().dirglob(a:A) + return matches +endfunction + +call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Gcd :cd<bang> `=s:repo().bare() ? s:repo().dir(<q-args>) : s:repo().tree(<q-args>)`") +call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Glcd :lcd<bang> `=s:repo().bare() ? s:repo().dir(<q-args>) : s:repo().tree(<q-args>)`") + +" }}}1 +" Gstatus {{{1 + +call s:command("-bar Gstatus :execute s:Status()") + +function! s:Status() abort + try + Gpedit : + wincmd P + nnoremap <buffer> <silent> q :<C-U>bdelete<CR> + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry + return '' +endfunction + +function! fugitive#reload_status() abort + let mytab = tabpagenr() + for tab in [mytab] + range(1,tabpagenr('$')) + for winnr in range(1,tabpagewinnr(tab,'$')) + if getbufvar(tabpagebuflist(tab)[winnr-1],'fugitive_type') ==# 'index' + execute 'tabnext '.tab + if winnr != winnr() + execute winnr.'wincmd w' + let restorewinnr = 1 + endif + try + if !&modified + call s:BufReadIndex() + endif + finally + if exists('restorewinnr') + wincmd p + endif + execute 'tabnext '.mytab + endtry + endif + endfor + endfor +endfunction + +function! s:StageReloadSeek(target,lnum1,lnum2) + let jump = a:target + let f = matchstr(getline(a:lnum1-1),'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*') + if f !=# '' | let jump = f | endif + let f = matchstr(getline(a:lnum2+1),'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*') + if f !=# '' | let jump = f | endif + silent! edit! + 1 + redraw + call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.jump.'\%( (new commits)\)\=\$','W') +endfunction + +function! s:StageDiff(diff) abort + let section = getline(search('^# .*:$','bcnW')) + let line = getline('.') + let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (new commits)\)\=$') + if filename ==# '' && section ==# '# Changes to be committed:' + return 'Git diff --cached' + elseif filename ==# '' + return 'Git diff' + elseif line =~# '^#\trenamed:' && filename =~# ' -> ' + let [old, new] = split(filename,' -> ') + execute 'Gedit '.s:fnameescape(':0:'.new) + return a:diff.' HEAD:'.s:fnameescape(old) + elseif section ==# '# Changes to be committed:' + execute 'Gedit '.s:fnameescape(':0:'.filename) + return a:diff.' -' + else + execute 'Gedit '.s:fnameescape('/'.filename) + return a:diff + endif +endfunction + +function! s:StageDiffEdit() abort + let section = getline(search('^# .*:$','bcnW')) + let line = getline('.') + let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (new commits)\)\=$') + let arg = (filename ==# '' ? '.' : filename) + if section ==# '# Changes to be committed:' + return 'Git! diff --cached '.s:shellesc(arg) + elseif section ==# '# Untracked files:' + let repo = s:repo() + call repo.git_chomp_in_tree('add','--intent-to-add',arg) + if arg ==# '.' + silent! edit! + 1 + if !search('^# Change\%(d but not updated\|s not staged for commit\):$','W') + call search('^# Change','W') + endif + else + call s:StageReloadSeek(arg,line('.'),line('.')) + endif + return '' + else + return 'Git! diff '.s:shellesc(arg) + endif +endfunction + +function! s:StageToggle(lnum1,lnum2) abort + try + let output = '' + for lnum in range(a:lnum1,a:lnum2) + let line = getline(lnum) + let repo = s:repo() + if line ==# '# Changes to be committed:' + call repo.git_chomp_in_tree('reset','-q') + silent! edit! + 1 + if !search('^# Untracked files:$','W') + call search('^# Change','W') + endif + return '' + elseif line =~# '^# Change\%(d but not updated\|s not staged for commit\):$' + call repo.git_chomp_in_tree('add','-u') + silent! edit! + 1 + if !search('^# Untracked files:$','W') + call search('^# Change','W') + endif + return '' + elseif line ==# '# Untracked files:' + call repo.git_chomp_in_tree('add','.') + silent! edit! + 1 + call search('^# Change','W') + return '' + endif + let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (\a\+ [[:alpha:], ]\+)\)\=$') + if filename ==# '' + continue + endif + if !exists('first_filename') + let first_filename = filename + endif + execute lnum + let section = getline(search('^# .*:$','bnW')) + if line =~# '^#\trenamed:' && filename =~ ' -> ' + let cmd = ['mv','--'] + reverse(split(filename,' -> ')) + let filename = cmd[-1] + elseif section =~? ' to be ' + let cmd = ['reset','-q','--',filename] + elseif line =~# '^#\tdeleted:' + let cmd = ['rm','--',filename] + else + let cmd = ['add','--',filename] + endif + let output .= call(repo.git_chomp_in_tree,cmd,s:repo())."\n" + endfor + if exists('first_filename') + call s:StageReloadSeek(first_filename,a:lnum1,a:lnum2) + endif + echo s:sub(s:gsub(output,'\n+','\n'),'\n$','') + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry + return 'checktime' +endfunction + +function! s:StagePatch(lnum1,lnum2) abort + let add = [] + let reset = [] + + for lnum in range(a:lnum1,a:lnum2) + let line = getline(lnum) + if line ==# '# Changes to be committed:' + return 'Git reset --patch' + elseif line =~# '^# Change\%(d but not updated\|s not staged for commit\):$' + return 'Git add --patch' + endif + let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (new commits)\)\=$') + if filename ==# '' + continue + endif + if !exists('first_filename') + let first_filename = filename + endif + execute lnum + let section = getline(search('^# .*:$','bnW')) + if line =~# '^#\trenamed:' && filename =~ ' -> ' + let reset += [split(filename,' -> ')[1]] + elseif section =~? ' to be ' + let reset += [filename] + elseif line !~# '^#\tdeleted:' + let add += [filename] + endif + endfor + try + if !empty(add) + execute "Git add --patch -- ".join(map(add,'s:shellesc(v:val)')) + endif + if !empty(reset) + execute "Git reset --patch -- ".join(map(add,'s:shellesc(v:val)')) + endif + if exists('first_filename') + silent! edit! + 1 + redraw + call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.first_filename.'\%( (new commits)\)\=\$','W') + endif + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry + return 'checktime' +endfunction + +" }}}1 +" Gcommit {{{1 + +call s:command("-nargs=? -complete=customlist,s:CommitComplete Gcommit :execute s:Commit(<q-args>)") + +function! s:Commit(args) abort + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + let msgfile = s:repo().dir('COMMIT_EDITMSG') + let outfile = tempname() + let errorfile = tempname() + try + execute cd.'`=s:repo().tree()`' + if &shell =~# 'cmd' + let command = '' + let old_editor = $GIT_EDITOR + let $GIT_EDITOR = 'false' + else + let command = 'env GIT_EDITOR=false ' + endif + let command .= s:repo().git_command('commit').' '.a:args + if &shell =~# 'csh' + silent execute '!('.command.' > '.outfile.') >& '.errorfile + elseif a:args =~# '\%(^\| \)--interactive\>' + execute '!'.command.' 2> '.errorfile + else + silent execute '!'.command.' > '.outfile.' 2> '.errorfile + endif + if !has('gui_running') + redraw! + endif + if !v:shell_error + if filereadable(outfile) + for line in readfile(outfile) + echo line + endfor + endif + return '' + else + let errors = readfile(errorfile) + let error = get(errors,-2,get(errors,-1,'!')) + if error =~# '\<false''\=\.$' + let args = a:args + let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-[se]|--edit|--interactive)%($| )','') + let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-F|--file|-m|--message)%(\s+|\=)%(''[^'']*''|"%(\\.|[^"])*"|\\.|\S)*','') + let args = s:gsub(args,'%(^| )@<=[%#]%(:\w)*','\=expand(submatch(0))') + let args = '-F '.s:shellesc(msgfile).' '.args + if args !~# '\%(^\| \)--cleanup\>' + let args = '--cleanup=strip '.args + endif + if bufname('%') == '' && line('$') == 1 && getline(1) == '' && !&mod + keepalt edit `=msgfile` + elseif s:buffer().type() ==# 'index' + keepalt edit `=msgfile` + execute (search('^#','n')+1).'wincmd+' + setlocal nopreviewwindow + else + keepalt split `=msgfile` + endif + let b:fugitive_commit_arguments = args + setlocal bufhidden=delete filetype=gitcommit + return '1' + elseif error ==# '!' + return s:Status() + else + call s:throw(error) + endif + endif + catch /^fugitive:/ + return 'echoerr v:errmsg' + finally + if exists('old_editor') + let $GIT_EDITOR = old_editor + endif + call delete(outfile) + call delete(errorfile) + execute cd.'`=dir`' + call fugitive#reload_status() + endtry +endfunction + +function! s:CommitComplete(A,L,P) abort + if a:A =~ '^-' || type(a:A) == type(0) " a:A is 0 on :Gcommit -<Tab> + let args = ['-C', '-F', '-a', '-c', '-e', '-i', '-m', '-n', '-o', '-q', '-s', '-t', '-u', '-v', '--all', '--allow-empty', '--amend', '--author=', '--cleanup=', '--dry-run', '--edit', '--file=', '--include', '--interactive', '--message=', '--no-verify', '--only', '--quiet', '--reedit-message=', '--reuse-message=', '--signoff', '--template=', '--untracked-files', '--verbose'] + return filter(args,'v:val[0 : strlen(a:A)-1] ==# a:A') + else + return s:repo().superglob(a:A) + endif +endfunction + +function! s:FinishCommit() + let args = getbufvar(+expand('<abuf>'),'fugitive_commit_arguments') + if !empty(args) + call setbufvar(+expand('<abuf>'),'fugitive_commit_arguments','') + return s:Commit(args) + endif + return '' +endfunction + +augroup fugitive_commit + autocmd! + autocmd VimLeavePre,BufDelete *.git/COMMIT_EDITMSG execute s:sub(s:FinishCommit(), '^echoerr (.*)', 'echohl ErrorMsg|echo \1|echohl NONE') +augroup END + +" }}}1 +" Ggrep, Glog {{{1 + +if !exists('g:fugitive_summary_format') + let g:fugitive_summary_format = '%s' +endif + +call s:command("-bang -nargs=? -complete=customlist,s:EditComplete Ggrep :execute s:Grep(<bang>0,<q-args>)") +call s:command("-bar -bang -nargs=* -complete=customlist,s:EditComplete Glog :execute s:Log('grep<bang>',<f-args>)") + +function! s:Grep(bang,arg) abort + let grepprg = &grepprg + let grepformat = &grepformat + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd.'`=s:repo().tree()`' + let &grepprg = s:repo().git_command('--no-pager', 'grep', '-n') + let &grepformat = '%f:%l:%m' + exe 'grep! '.escape(matchstr(a:arg,'\v\C.{-}%($|[''" ]\@=\|)@='),'|') + let list = getqflist() + for entry in list + if bufname(entry.bufnr) =~ ':' + let entry.filename = s:repo().translate(bufname(entry.bufnr)) + unlet! entry.bufnr + elseif a:arg =~# '\%(^\| \)--cached\>' + let entry.filename = s:repo().translate(':0:'.bufname(entry.bufnr)) + unlet! entry.bufnr + endif + endfor + call setqflist(list,'r') + if !a:bang && !empty(list) + return 'cfirst'.matchstr(a:arg,'\v\C[''" ]\zs\|.*') + else + return matchstr(a:arg,'\v\C[''" ]\|\zs.*') + endif + finally + let &grepprg = grepprg + let &grepformat = grepformat + execute cd.'`=dir`' + endtry +endfunction + +function! s:Log(cmd,...) + let path = s:buffer().path('/') + if path =~# '^/\.git\%(/\|$\)' || index(a:000,'--') != -1 + let path = '' + endif + let cmd = ['--no-pager', 'log', '--no-color'] + let cmd += [escape('--pretty=format:fugitive://'.s:repo().dir().'//%H'.path.'::'.g:fugitive_summary_format,'%')] + if empty(filter(a:000[0 : index(a:000,'--')],'v:val !~# "^-"')) + if s:buffer().commit() =~# '\x\{40\}' + let cmd += [s:buffer().commit()] + elseif s:buffer().path() =~# '^\.git/refs/\|^\.git/.*HEAD$' + let cmd += [s:buffer().path()[5:-1]] + endif + end + let cmd += map(copy(a:000),'s:sub(v:val,"^\\%(%(:\\w)*)","\\=fnamemodify(s:buffer().path(),submatch(1))")') + if path =~# '/.' + let cmd += ['--',path[1:-1]] + endif + let grepformat = &grepformat + let grepprg = &grepprg + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd.'`=s:repo().tree()`' + let &grepprg = call(s:repo().git_command,cmd,s:repo()) + let &grepformat = '%f::%m' + exe a:cmd + finally + let &grepformat = grepformat + let &grepprg = grepprg + execute cd.'`=dir`' + endtry +endfunction + +" }}}1 +" Gedit, Gpedit, Gsplit, Gvsplit, Gtabedit, Gread {{{1 + +function! s:Edit(cmd,bang,...) abort + if a:cmd !~# 'read' + if &previewwindow && getbufvar('','fugitive_type') ==# 'index' + wincmd p + if &diff + let mywinnr = winnr() + for winnr in range(winnr('$'),1,-1) + if winnr != mywinnr && getwinvar(winnr,'&diff') + execute winnr.'wincmd w' + close + wincmd p + endif + endfor + endif + endif + endif + + if a:bang + let args = s:gsub(a:0 ? a:1 : '', '\\@<!%(\\\\)*\zs[%#]', '\=s:buffer().expand(submatch(0))') + if a:cmd =~# 'read' + let git = s:repo().git_command() + let last = line('$') + silent call s:ExecuteInTree((a:cmd ==# 'read' ? '$read' : a:cmd).'!'.git.' --no-pager '.args) + if a:cmd ==# 'read' + silent execute '1,'.last.'delete_' + endif + call fugitive#reload_status() + diffupdate + return 'redraw|echo '.string(':!'.git.' '.args) + else + let temp = tempname() + let s:temp_files[temp] = s:repo().dir() + silent execute a:cmd.' '.temp + if a:cmd =~# 'pedit' + wincmd P + endif + let echo = s:Edit('read',1,args) + silent write! + setlocal buftype=nowrite nomodified filetype=git foldmarker=<<<<<<<,>>>>>>> + if getline(1) !~# '^diff ' + setlocal readonly nomodifiable + endif + if a:cmd =~# 'pedit' + wincmd p + endif + return echo + endif + return '' + endif + + if a:0 && a:1 == '' + return '' + elseif a:0 + let file = s:buffer().expand(a:1) + elseif expand('%') ==# '' + let file = ':' + elseif s:buffer().commit() ==# '' && s:buffer().path('/') !~# '^/.git\>' + let file = s:buffer().path(':') + else + let file = s:buffer().path('/') + endif + try + let file = s:repo().translate(file) + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry + if a:cmd ==# 'read' + return 'silent %delete_|read '.s:fnameescape(file).'|silent 1delete_|diffupdate|'.line('.') + else + return a:cmd.' '.s:fnameescape(file) + endif +endfunction + +function! s:EditComplete(A,L,P) abort + return s:repo().superglob(a:A) +endfunction + +function! s:EditRunComplete(A,L,P) abort + if a:L =~# '^\w\+!' + return s:GitComplete(a:A,a:L,a:P) + else + return s:repo().superglob(a:A) + endif +endfunction + +call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Ge :execute s:Edit('edit<bang>',0,<f-args>)") +call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gedit :execute s:Edit('edit<bang>',0,<f-args>)") +call s:command("-bar -bang -nargs=? -complete=customlist,s:EditRunComplete Gpedit :execute s:Edit('pedit',<bang>0,<f-args>)") +call s:command("-bar -bang -nargs=? -complete=customlist,s:EditRunComplete Gsplit :execute s:Edit('split',<bang>0,<f-args>)") +call s:command("-bar -bang -nargs=? -complete=customlist,s:EditRunComplete Gvsplit :execute s:Edit('vsplit',<bang>0,<f-args>)") +call s:command("-bar -bang -nargs=? -complete=customlist,s:EditRunComplete Gtabedit :execute s:Edit('tabedit',<bang>0,<f-args>)") +call s:command("-bar -bang -nargs=? -count -complete=customlist,s:EditRunComplete Gread :execute s:Edit((!<count> && <line1> ? '' : <count>).'read',<bang>0,<f-args>)") + +" }}}1 +" Gwrite, Gwq {{{1 + +call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gwrite :execute s:Write(<bang>0,<f-args>)") +call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gw :execute s:Write(<bang>0,<f-args>)") +call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gwq :execute s:Wq(<bang>0,<f-args>)") + +function! s:Write(force,...) abort + if exists('b:fugitive_commit_arguments') + return 'write|bdelete' + elseif expand('%:t') == 'COMMIT_EDITMSG' && $GIT_INDEX_FILE != '' + return 'wq' + elseif s:buffer().type() == 'index' + return 'Gcommit' + elseif s:buffer().path() ==# '' && getline(4) =~# '^+++ ' + let filename = getline(4)[6:-1] + setlocal buftype= + silent write + setlocal buftype=nowrite + if matchstr(getline(2),'index [[:xdigit:]]\+\.\.\zs[[:xdigit:]]\{7\}') ==# s:repo().rev_parse(':0:'.filename)[0:6] + let err = s:repo().git_chomp('apply','--cached','--reverse',s:buffer().spec()) + else + let err = s:repo().git_chomp('apply','--cached',s:buffer().spec()) + endif + if err !=# '' + let v:errmsg = split(err,"\n")[0] + return 'echoerr v:errmsg' + elseif a:force + return 'bdelete' + else + return 'Gedit '.fnameescape(filename) + endif + endif + let mytab = tabpagenr() + let mybufnr = bufnr('') + let path = a:0 ? a:1 : s:buffer().path() + if path =~# '^:\d\>' + return 'write'.(a:force ? '! ' : ' ').s:fnameescape(s:repo().translate(s:buffer().expand(path))) + endif + let always_permitted = (s:buffer().path() ==# path && s:buffer().commit() =~# '^0\=$') + if !always_permitted && !a:force && s:repo().git_chomp_in_tree('diff','--name-status','HEAD','--',path) . s:repo().git_chomp_in_tree('ls-files','--others','--',path) !=# '' + let v:errmsg = 'fugitive: file has uncommitted changes (use ! to override)' + return 'echoerr v:errmsg' + endif + let file = s:repo().translate(path) + let treebufnr = 0 + for nr in range(1,bufnr('$')) + if fnamemodify(bufname(nr),':p') ==# file + let treebufnr = nr + endif + endfor + + if treebufnr > 0 && treebufnr != bufnr('') + let temp = tempname() + silent execute '%write '.temp + for tab in [mytab] + range(1,tabpagenr('$')) + for winnr in range(1,tabpagewinnr(tab,'$')) + if tabpagebuflist(tab)[winnr-1] == treebufnr + execute 'tabnext '.tab + if winnr != winnr() + execute winnr.'wincmd w' + let restorewinnr = 1 + endif + try + let lnum = line('.') + let last = line('$') + silent execute '$read '.temp + silent execute '1,'.last.'delete_' + silent write! + silent execute lnum + let did = 1 + finally + if exists('restorewinnr') + wincmd p + endif + execute 'tabnext '.mytab + endtry + endif + endfor + endfor + if !exists('did') + call writefile(readfile(temp,'b'),file,'b') + endif + else + execute 'write! '.s:fnameescape(s:repo().translate(path)) + endif + + if a:force + let error = s:repo().git_chomp_in_tree('add', '--force', file) + else + let error = s:repo().git_chomp_in_tree('add', file) + endif + if v:shell_error + let v:errmsg = 'fugitive: '.error + return 'echoerr v:errmsg' + endif + if s:buffer().path() ==# path && s:buffer().commit() =~# '^\d$' + set nomodified + endif + + let one = s:repo().translate(':1:'.path) + let two = s:repo().translate(':2:'.path) + let three = s:repo().translate(':3:'.path) + for nr in range(1,bufnr('$')) + if bufloaded(nr) && !getbufvar(nr,'&modified') && (bufname(nr) == one || bufname(nr) == two || bufname(nr) == three) + execute nr.'bdelete' + endif + endfor + + unlet! restorewinnr + let zero = s:repo().translate(':0:'.path) + for tab in range(1,tabpagenr('$')) + for winnr in range(1,tabpagewinnr(tab,'$')) + let bufnr = tabpagebuflist(tab)[winnr-1] + let bufname = bufname(bufnr) + if bufname ==# zero && bufnr != mybufnr + execute 'tabnext '.tab + if winnr != winnr() + execute winnr.'wincmd w' + let restorewinnr = 1 + endif + try + let lnum = line('.') + let last = line('$') + silent $read `=file` + silent execute '1,'.last.'delete_' + silent execute lnum + set nomodified + diffupdate + finally + if exists('restorewinnr') + wincmd p + endif + execute 'tabnext '.mytab + endtry + break + endif + endfor + endfor + call fugitive#reload_status() + return 'checktime' +endfunction + +function! s:Wq(force,...) abort + let bang = a:force ? '!' : '' + if exists('b:fugitive_commit_arguments') + return 'wq'.bang + endif + let result = call(s:function('s:Write'),[a:force]+a:000) + if result =~# '^\%(write\|wq\|echoerr\)' + return s:sub(result,'^write','wq') + else + return result.'|quit'.bang + endif +endfunction + +" }}}1 +" Gdiff {{{1 + +call s:command("-bang -bar -nargs=? -complete=customlist,s:EditComplete Gdiff :execute s:Diff(<bang>0,<f-args>)") +call s:command("-bar -nargs=? -complete=customlist,s:EditComplete Gvdiff :execute s:Diff(0,<f-args>)") +call s:command("-bar -nargs=? -complete=customlist,s:EditComplete Gsdiff :execute s:Diff(1,<f-args>)") + +augroup fugitive_diff + autocmd! + autocmd BufWinLeave * if s:diff_window_count() == 2 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | call s:diffoff_all(getbufvar(+expand('<abuf>'), 'git_dir')) | endif + autocmd BufWinEnter * if s:diff_window_count() == 1 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | call s:diffoff() | endif +augroup END + +function! s:diff_window_count() + let c = 0 + for nr in range(1,winnr('$')) + let c += getwinvar(nr,'&diff') + endfor + return c +endfunction + +function! s:diffthis() + if !&diff + let w:fugitive_diff_restore = 'setlocal nodiff noscrollbind' + let w:fugitive_diff_restore .= ' scrollopt=' . &l:scrollopt + let w:fugitive_diff_restore .= &l:wrap ? ' wrap' : ' nowrap' + let w:fugitive_diff_restore .= ' foldmethod=' . &l:foldmethod + let w:fugitive_diff_restore .= ' foldcolumn=' . &l:foldcolumn + diffthis + endif +endfunction + +function! s:diffoff() + if exists('w:fugitive_diff_restore') + execute w:fugitive_diff_restore + unlet w:fugitive_diff_restore + else + diffoff + endif +endfunction + +function! s:diffoff_all(dir) + for nr in range(1,winnr('$')) + if getwinvar(nr,'&diff') + if nr != winnr() + execute nr.'wincmd w' + let restorewinnr = 1 + endif + if exists('b:git_dir') && b:git_dir ==# a:dir + call s:diffoff() + endif + if exists('restorewinnr') + wincmd p + endif + endif + endfor +endfunction + +function! s:buffer_compare_age(commit) dict abort + let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5} + let my_score = get(scores,':'.self.commit(),0) + let their_score = get(scores,':'.a:commit,0) + if my_score || their_score + return my_score < their_score ? -1 : my_score != their_score + elseif self.commit() ==# a:commit + return 0 + endif + let base = self.repo().git_chomp('merge-base',self.commit(),a:commit) + if base ==# self.commit() + return -1 + elseif base ==# a:commit + return 1 + endif + let my_time = +self.repo().git_chomp('log','--max-count=1','--pretty=format:%at',self.commit()) + let their_time = +self.repo().git_chomp('log','--max-count=1','--pretty=format:%at',a:commit) + return my_time < their_time ? -1 : my_time != their_time +endfunction + +call s:add_methods('buffer',['compare_age']) + +function! s:Diff(bang,...) abort + let split = a:bang ? 'split' : 'vsplit' + if exists(':DiffGitCached') + return 'DiffGitCached' + elseif (!a:0 || a:1 == ':') && s:buffer().commit() =~# '^[0-1]\=$' && s:repo().git_chomp_in_tree('ls-files', '--unmerged', '--', s:buffer().path()) !=# '' + let nr = bufnr('') + execute 'leftabove '.split.' `=fugitive#buffer().repo().translate(s:buffer().expand('':2''))`' + execute 'nnoremap <buffer> <silent> dp :diffput '.nr.'<Bar>diffupdate<CR>' + call s:diffthis() + wincmd p + execute 'rightbelow '.split.' `=fugitive#buffer().repo().translate(s:buffer().expand('':3''))`' + execute 'nnoremap <buffer> <silent> dp :diffput '.nr.'<Bar>diffupdate<CR>' + call s:diffthis() + wincmd p + call s:diffthis() + return '' + elseif a:0 + if a:1 ==# '' + return '' + elseif a:1 ==# '/' + let file = s:buffer().path('/') + elseif a:1 ==# ':' + let file = s:buffer().path(':0:') + elseif a:1 =~# '^:/.' + try + let file = s:repo().rev_parse(a:1).s:buffer().path(':') + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry + else + let file = s:buffer().expand(a:1) + endif + if file !~# ':' && file !~# '^/' && s:repo().git_chomp('cat-file','-t',file) =~# '^\%(tag\|commit\)$' + let file = file.s:buffer().path(':') + endif + else + let file = s:buffer().path(s:buffer().commit() == '' ? ':0:' : '/') + endif + try + let spec = s:repo().translate(file) + let commit = matchstr(spec,'\C[^:/]//\zs\x\+') + if s:buffer().compare_age(commit) < 0 + execute 'rightbelow '.split.' `=spec`' + else + execute 'leftabove '.split.' `=spec`' + endif + call s:diffthis() + wincmd p + call s:diffthis() + return '' + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry +endfunction + +" }}}1 +" Gmove, Gremove {{{1 + +function! s:Move(force,destination) + if a:destination =~# '^/' + let destination = a:destination[1:-1] + else + let destination = fnamemodify(s:sub(a:destination,'[%#]%(:\w)*','\=expand(submatch(0))'),':p') + if destination[0:strlen(s:repo().tree())] ==# s:repo().tree('') + let destination = destination[strlen(s:repo().tree('')):-1] + endif + endif + if isdirectory(s:buffer().name()) + " Work around Vim parser idiosyncrasy + let discarded = s:buffer().setvar('&swapfile',0) + endif + let message = call(s:repo().git_chomp_in_tree,['mv']+(a:force ? ['-f'] : [])+['--', s:buffer().path(), destination], s:repo()) + if v:shell_error + let v:errmsg = 'fugitive: '.message + return 'echoerr v:errmsg' + endif + let destination = s:repo().tree(destination) + if isdirectory(destination) + let destination = fnamemodify(s:sub(destination,'/$','').'/'.expand('%:t'),':.') + endif + call fugitive#reload_status() + if s:buffer().commit() == '' + if isdirectory(destination) + return 'edit '.s:fnameescape(destination) + else + return 'saveas! '.s:fnameescape(destination) + endif + else + return 'file '.s:fnameescape(s:repo().translate(':0:'.destination) + endif +endfunction + +function! s:MoveComplete(A,L,P) + if a:A =~ '^/' + return s:repo().superglob(a:A) + else + let matches = split(glob(a:A.'*'),"\n") + call map(matches,'v:val !~ "/$" && isdirectory(v:val) ? v:val."/" : v:val') + return matches + endif +endfunction + +function! s:Remove(force) + if s:buffer().commit() ==# '' + let cmd = ['rm'] + elseif s:buffer().commit() ==# '0' + let cmd = ['rm','--cached'] + else + let v:errmsg = 'fugitive: rm not supported here' + return 'echoerr v:errmsg' + endif + if a:force + let cmd += ['--force'] + endif + let message = call(s:repo().git_chomp_in_tree,cmd+['--',s:buffer().path()],s:repo()) + if v:shell_error + let v:errmsg = 'fugitive: '.s:sub(message,'error:.*\zs\n\(.*-f.*',' (add ! to force)') + return 'echoerr '.string(v:errmsg) + else + call fugitive#reload_status() + return 'bdelete'.(a:force ? '!' : '') + endif +endfunction + +augroup fugitive_remove + autocmd! + autocmd User Fugitive if s:buffer().commit() =~# '^0\=$' | + \ exe "command! -buffer -bar -bang -nargs=1 -complete=customlist,s:MoveComplete Gmove :execute s:Move(<bang>0,<q-args>)" | + \ exe "command! -buffer -bar -bang Gremove :execute s:Remove(<bang>0)" | + \ endif +augroup END + +" }}}1 +" Gblame {{{1 + +augroup fugitive_blame + autocmd! + autocmd BufReadPost *.fugitiveblame setfiletype fugitiveblame + autocmd FileType fugitiveblame setlocal nomodeline | if exists('b:git_dir') | let &l:keywordprg = s:repo().keywordprg() | endif + autocmd Syntax fugitiveblame call s:BlameSyntax() + autocmd User Fugitive if s:buffer().type('file', 'blob') | exe "command! -buffer -bar -bang -range=0 -nargs=* Gblame :execute s:Blame(<bang>0,<line1>,<line2>,<count>,[<f-args>])" | endif +augroup END + +function! s:Blame(bang,line1,line2,count,args) abort + try + if s:buffer().path() == '' + call s:throw('file or blob required') + endif + if filter(copy(a:args),'v:val !~# "^\\%(--root\|--show-name\\|-\\=\\%([ltwfs]\\|[MC]\\d*\\)\\+\\)$"') != [] + call s:throw('unsupported option') + endif + call map(a:args,'s:sub(v:val,"^\\ze[^-]","-")') + let cmd = ['--no-pager', 'blame', '--show-number'] + a:args + if s:buffer().commit() =~# '\D\|..' + let cmd += [s:buffer().commit()] + else + let cmd += ['--contents', '-'] + endif + let basecmd = escape(call(s:repo().git_command,cmd+['--',s:buffer().path()],s:repo()),'!') + try + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + if !s:repo().bare() + let dir = getcwd() + execute cd.'`=s:repo().tree()`' + endif + if a:count + execute 'write !'.substitute(basecmd,' blame ',' blame -L '.a:line1.','.a:line2.' ','g') + else + let error = tempname() + let temp = error.'.fugitiveblame' + if &shell =~# 'csh' + silent! execute '%write !('.basecmd.' > '.temp.') >& '.error + else + silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error + endif + if exists('l:dir') + execute cd.'`=dir`' + unlet dir + endif + if v:shell_error + call s:throw(join(readfile(error),"\n")) + endif + let bufnr = bufnr('') + let restore = 'call setwinvar(bufwinnr('.bufnr.'),"&scrollbind",0)' + if &l:wrap + let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&wrap",1)' + endif + if &l:foldenable + let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&foldenable",1)' + endif + let winnr = winnr() + windo set noscrollbind + exe winnr.'wincmd w' + setlocal scrollbind nowrap nofoldenable + let top = line('w0') + &scrolloff + let current = line('.') + let s:temp_files[temp] = s:repo().dir() + exe 'leftabove vsplit '.temp + let b:fugitive_blamed_bufnr = bufnr + let w:fugitive_leave = restore + let b:fugitive_blame_arguments = join(a:args,' ') + execute top + normal! zt + execute current + execute "vertical resize ".(match(getline('.'),'\s\+\d\+)')+1) + setlocal nomodified nomodifiable nonumber scrollbind nowrap foldcolumn=0 nofoldenable filetype=fugitiveblame + if exists('+relativenumber') + setlocal norelativenumber + endif + nnoremap <buffer> <silent> <CR> :<C-U>exe <SID>BlameJump('')<CR> + nnoremap <buffer> <silent> P :<C-U>exe <SID>BlameJump('^'.v:count1)<CR> + nnoremap <buffer> <silent> ~ :<C-U>exe <SID>BlameJump('~'.v:count1)<CR> + nnoremap <buffer> <silent> o :<C-U>exe <SID>Edit((&splitbelow ? "botright" : "topleft")." split", 0, matchstr(getline('.'),'\x\+'))<CR> + nnoremap <buffer> <silent> O :<C-U>exe <SID>Edit("tabedit", 0, matchstr(getline('.'),'\x\+'))<CR> + syncbind + endif + finally + if exists('l:dir') + execute cd.'`=dir`' + endif + endtry + return '' + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry +endfunction + +function! s:BlameJump(suffix) abort + let commit = matchstr(getline('.'),'^\^\=\zs\x\+') + if commit =~# '^0\+$' + let commit = ':0' + endif + let lnum = matchstr(getline('.'),'\d\+\ze\s\+[([:digit:]]') + let path = matchstr(getline('.'),'^\^\=\zs\x\+\s\+\zs.\{-\}\ze\s*\d\+ ') + if path ==# '' + let path = s:buffer(b:fugitive_blamed_bufnr).path() + endif + let args = b:fugitive_blame_arguments + let offset = line('.') - line('w0') + let bufnr = bufnr('%') + let winnr = bufwinnr(b:fugitive_blamed_bufnr) + if winnr > 0 + exe winnr.'wincmd w' + endif + execute s:Edit('edit', 0, commit.a:suffix.':'.path) + if winnr > 0 + exe bufnr.'bdelete' + endif + execute 'Gblame '.args + execute lnum + let delta = line('.') - line('w0') - offset + if delta > 0 + execute 'norm! 'delta."\<C-E>" + elseif delta < 0 + execute 'norm! '(-delta)."\<C-Y>" + endif + syncbind + return '' +endfunction + +function! s:BlameSyntax() abort + let b:current_syntax = 'fugitiveblame' + syn match FugitiveblameBoundary "^\^" + syn match FugitiveblameBlank "^\s\+\s\@=" nextgroup=FugitiveblameAnnotation,fugitiveblameOriginalFile,FugitiveblameOriginalLineNumber skipwhite + syn match FugitiveblameHash "\%(^\^\=\)\@<=\x\{7,40\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite + syn match FugitiveblameUncommitted "\%(^\^\=\)\@<=0\{7,40\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite + syn region FugitiveblameAnnotation matchgroup=FugitiveblameDelimiter start="(" end="\%( \d\+\)\@<=)" contained keepend oneline + syn match FugitiveblameTime "[0-9:/+-][0-9:/+ -]*[0-9:/+-]\%( \+\d\+)\)\@=" contained containedin=FugitiveblameAnnotation + syn match FugitiveblameLineNumber " \@<=\d\+)\@=" contained containedin=FugitiveblameAnnotation + syn match FugitiveblameOriginalFile " \%(\f\+\D\@<=\|\D\@=\f\+\)\%(\%(\s\+\d\+\)\=\s\%((\|\s*\d\+)\)\)\@=" contained nextgroup=FugitiveblameOriginalLineNumber,FugitiveblameAnnotation skipwhite + syn match FugitiveblameOriginalLineNumber " \@<=\d\+\%(\s(\)\@=" contained nextgroup=FugitiveblameAnnotation skipwhite + syn match FugitiveblameOriginalLineNumber " \@<=\d\+\%(\s\+\d\+)\)\@=" contained nextgroup=FugitiveblameShort skipwhite + syn match FugitiveblameShort "\d\+)" contained contains=FugitiveblameLineNumber + syn match FugitiveblameNotCommittedYet "(\@<=Not Committed Yet\>" contained containedin=FugitiveblameAnnotation + hi def link FugitiveblameBoundary Keyword + hi def link FugitiveblameHash Identifier + hi def link FugitiveblameUncommitted Function + hi def link FugitiveblameTime PreProc + hi def link FugitiveblameLineNumber Number + hi def link FugitiveblameOriginalFile String + hi def link FugitiveblameOriginalLineNumber Float + hi def link FugitiveblameShort FugitiveblameDelimiter + hi def link FugitiveblameDelimiter Delimiter + hi def link FugitiveblameNotCommittedYet Comment +endfunction + +" }}}1 +" Gbrowse {{{1 + +call s:command("-bar -bang -count=0 -nargs=? -complete=customlist,s:EditComplete Gbrowse :execute s:Browse(<bang>0,<line1>,<count>,<f-args>)") + +function! s:Browse(bang,line1,count,...) abort + try + let rev = a:0 ? substitute(a:1,'@[[:alnum:]_-]*\%(://.\{-\}\)\=$','','') : '' + if rev ==# '' + let expanded = s:buffer().rev() + elseif rev ==# ':' + let expanded = s:buffer().path('/') + else + let expanded = s:buffer().expand(rev) + endif + let full = s:repo().translate(expanded) + let commit = '' + if full =~# '^fugitive://' + let commit = matchstr(full,'://.*//\zs\w\+') + let path = matchstr(full,'://.*//\w\+\zs/.*') + if commit =~ '..' + let type = s:repo().git_chomp('cat-file','-t',commit.s:sub(path,'^/',':')) + else + let type = 'blob' + endif + let path = path[1:-1] + elseif s:repo().bare() + let path = '.git/' . full[strlen(s:repo().dir())+1:-1] + let type = '' + else + let path = full[strlen(s:repo().tree())+1:-1] + if path =~# '^\.git/' + let type = '' + elseif isdirectory(full) + let type = 'tree' + else + let type = 'blob' + endif + endif + if path =~# '^\.git/.*HEAD' && filereadable(s:repo().dir(path[5:-1])) + let body = readfile(s:repo().dir(path[5:-1]))[0] + if body =~# '^\x\{40\}$' + let commit = body + let type = 'commit' + let path = '' + elseif body =~# '^ref: refs/' + let path = '.git/' . matchstr(body,'ref: \zs.*') + endif + endif + + if a:0 && a:1 =~# '@[[:alnum:]_-]*\%(://.\{-\}\)\=$' + let remote = matchstr(a:1,'@\zs[[:alnum:]_-]\+\%(://.\{-\}\)\=$') + elseif path =~# '^\.git/refs/remotes/.' + let remote = matchstr(path,'^\.git/refs/remotes/\zs[^/]\+') + else + let remote = 'origin' + let branch = matchstr(rev,'^[[:alnum:]/._-]\+\ze[:^~@]') + if branch ==# '' && path =~# '^\.git/refs/\w\+/' + let branch = s:sub(path,'^\.git/refs/\w+/','') + endif + if filereadable(s:repo().dir('refs/remotes/'.branch)) + let remote = matchstr(branch,'[^/]\+') + let rev = rev[strlen(remote)+1:-1] + else + if branch ==# '' + let branch = matchstr(s:repo().head_ref(),'\<refs/heads/\zs.*') + endif + if branch != '' + let remote = s:repo().git_chomp('config','branch.'.branch.'.remote') + if remote =~# '^\.\=$' + let remote = 'origin' + elseif rev[0:strlen(branch)-1] ==# branch && rev[strlen(branch)] =~# '[:^~@]' + let rev = s:repo().git_chomp('config','branch.'.branch.'.merge')[11:-1] . rev[strlen(branch):-1] + endif + endif + endif + endif + + let raw = s:repo().git_chomp('config','remote.'.remote.'.url') + if raw ==# '' + let raw = remote + endif + + let url = s:github_url(s:repo(),raw,rev,commit,path,type,a:line1,a:count) + if url == '' + let url = s:instaweb_url(s:repo(),rev,commit,path,type,a:count ? a:line1 : 0) + endif + + if url == '' + call s:throw("Instaweb failed to start and '".remote."' is not a GitHub remote") + endif + + if a:bang + let @* = url + return 'echomsg '.string(url) + else + return 'echomsg '.string(url).'|call fugitive#buffer().repo().git_chomp("web--browse",'.string(url).')' + endif + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry +endfunction + +function! s:github_url(repo,url,rev,commit,path,type,line1,line2) abort + let path = a:path + let repo_path = matchstr(a:url,'^\%(https\=://\|git://\|git@\)github\.com[/:]\zs.\{-\}\ze\%(\.git\)\=$') + if repo_path ==# '' + return '' + endif + let root = 'https://github.com/' . repo_path + if path =~# '^\.git/refs/heads/' + let branch = a:repo.git_chomp('config','branch.'.path[16:-1].'.merge')[11:-1] + if branch ==# '' + return root . '/commits/' . path[16:-1] + else + return root . '/commits/' . branch + endif + elseif path =~# '^\.git/refs/.' + return root . '/commits/' . matchstr(path,'[^/]\+$') + elseif path =~# '.git/\%(config$\|hooks\>\)' + return root . '/admin' + elseif path =~# '^\.git\>' + return root + endif + if a:rev =~# '^[[:alnum:]._-]\+:' + let commit = matchstr(a:rev,'^[^:]*') + elseif a:commit =~# '^\d\=$' + let local = matchstr(a:repo.head_ref(),'\<refs/heads/\zs.*') + let commit = a:repo.git_chomp('config','branch.'.local.'.merge')[11:-1] + if commit ==# '' + let commit = local + endif + else + let commit = a:commit + endif + if a:type == 'tree' + let url = s:sub(root . '/tree/' . commit . '/' . path,'/$','') + elseif a:type == 'blob' + let url = root . '/blob/' . commit . '/' . path + if a:line2 && a:line1 == a:line2 + let url .= '#L' . a:line1 + elseif a:line2 + let url .= '#L' . a:line1 . '-' . a:line2 + endif + elseif a:type == 'tag' + let commit = matchstr(getline(3),'^tag \zs.*') + let url = root . '/tree/' . commit + else + let url = root . '/commit/' . commit + endif + return url +endfunction + +function! s:instaweb_url(repo,rev,commit,path,type,...) abort + let output = a:repo.git_chomp('instaweb','-b','unknown') + if output =~# 'http://' + let root = matchstr(output,'http://.*').'/?p='.fnamemodify(a:repo.dir(),':t') + else + return '' + endif + if a:path =~# '^\.git/refs/.' + return root . ';a=shortlog;h=' . matchstr(a:path,'^\.git/\zs.*') + elseif a:path =~# '^\.git\>' + return root + endif + let url = root + if a:commit =~# '^\x\{40\}$' + if a:type ==# 'commit' + let url .= ';a=commit' + endif + let url .= ';h=' . a:repo.rev_parse(a:commit . (a:path == '' ? '' : ':' . a:path)) + else + if a:type ==# 'blob' + let tmp = tempname() + silent execute 'write !'.a:repo.git_command('hash-object','-w','--stdin').' > '.tmp + let url .= ';h=' . readfile(tmp)[0] + else + try + let url .= ';h=' . a:repo.rev_parse((a:commit == '' ? 'HEAD' : ':' . a:commit) . ':' . a:path) + catch /^fugitive:/ + call s:throw('fugitive: cannot browse uncommitted file') + endtry + endif + let root .= ';hb=' . matchstr(a:repo.head_ref(),'[^ ]\+$') + endif + if a:path !=# '' + let url .= ';f=' . a:path + endif + if a:0 && a:1 + let url .= '#l' . a:1 + endif + return url +endfunction + +" }}}1 +" File access {{{1 + +function! s:ReplaceCmd(cmd,...) abort + let fn = bufname('') + let tmp = tempname() + let prefix = '' + try + if a:0 && a:1 != '' + if &shell =~# 'cmd' + let old_index = $GIT_INDEX_FILE + let $GIT_INDEX_FILE = a:1 + else + let prefix = 'env GIT_INDEX_FILE='.s:shellesc(a:1).' ' + endif + endif + if &shell =~# 'cmd' + call system('cmd /c "'.prefix.a:cmd.' > '.tmp.'"') + else + call system(' ('.prefix.a:cmd.' > '.tmp.') ') + endif + finally + if exists('old_index') + let $GIT_INDEX_FILE = old_index + endif + endtry + silent exe 'keepalt file '.tmp + silent edit! + silent exe 'keepalt file '.s:fnameescape(fn) + call delete(tmp) + if bufname('$') == tmp + silent execute 'bwipeout '.bufnr('$') + endif + silent exe 'doau BufReadPost '.s:fnameescape(fn) +endfunction + +function! s:BufReadIndex() + if !exists('b:fugitive_display_format') + let b:fugitive_display_format = filereadable(expand('%').'.lock') + endif + let b:fugitive_display_format = b:fugitive_display_format % 2 + let b:fugitive_type = 'index' + try + let b:git_dir = s:repo().dir() + setlocal noro ma + if fnamemodify($GIT_INDEX_FILE !=# '' ? $GIT_INDEX_FILE : b:git_dir . '/index', ':p') ==# expand('%:p') + let index = '' + else + let index = expand('%:p') + endif + if b:fugitive_display_format + call s:ReplaceCmd(s:repo().git_command('ls-files','--stage'),index) + set ft=git nospell + else + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd.'`=s:repo().tree()`' + call s:ReplaceCmd(s:repo().git_command('status'),index) + finally + execute cd.'`=dir`' + endtry + set ft=gitcommit + endif + setlocal ro noma nomod nomodeline bufhidden=wipe + call s:JumpInit() + nunmap <buffer> P + nunmap <buffer> ~ + nnoremap <buffer> <silent> <C-N> :call search('^#\t.*','W')<Bar>.<CR> + nnoremap <buffer> <silent> <C-P> :call search('^#\t.*','Wbe')<Bar>.<CR> + nnoremap <buffer> <silent> - :<C-U>execute <SID>StageToggle(line('.'),line('.')+v:count1-1)<CR> + xnoremap <buffer> <silent> - :<C-U>execute <SID>StageToggle(line("'<"),line("'>"))<CR> + nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += 1<Bar>exe <SID>BufReadIndex()<CR> + nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= 1<Bar>exe <SID>BufReadIndex()<CR> + nnoremap <buffer> <silent> C :<C-U>Gcommit<CR> + nnoremap <buffer> <silent> cA :<C-U>Gcommit --amend --reuse-message=HEAD<CR> + nnoremap <buffer> <silent> ca :<C-U>Gcommit --amend<CR> + nnoremap <buffer> <silent> cc :<C-U>Gcommit<CR> + nnoremap <buffer> <silent> D :<C-U>execute <SID>StageDiff('Gvdiff')<CR> + nnoremap <buffer> <silent> dd :<C-U>execute <SID>StageDiff('Gvdiff')<CR> + nnoremap <buffer> <silent> dh :<C-U>execute <SID>StageDiff('Gsdiff')<CR> + nnoremap <buffer> <silent> ds :<C-U>execute <SID>StageDiff('Gsdiff')<CR> + nnoremap <buffer> <silent> dp :<C-U>execute <SID>StageDiffEdit()<CR> + nnoremap <buffer> <silent> dv :<C-U>execute <SID>StageDiff('Gvdiff')<CR> + nnoremap <buffer> <silent> p :<C-U>execute <SID>StagePatch(line('.'),line('.')+v:count1-1)<CR> + xnoremap <buffer> <silent> p :<C-U>execute <SID>StagePatch(line("'<"),line("'>"))<CR> + nnoremap <buffer> <silent> q :<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<CR> + nnoremap <buffer> <silent> R :<C-U>edit<CR> + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry +endfunction + +function! s:FileRead() + try + let repo = s:repo(s:ExtractGitDir(expand('<amatch>'))) + let path = s:sub(s:sub(matchstr(expand('<amatch>'),'fugitive://.\{-\}//\zs.*'),'/',':'),'^\d:',':&') + let hash = repo.rev_parse(path) + if path =~ '^:' + let type = 'blob' + else + let type = repo.git_chomp('cat-file','-t',hash) + endif + " TODO: use count, if possible + return "read !".escape(repo.git_command('cat-file',type,hash),'%#\') + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry +endfunction + +function! s:BufReadIndexFile() + try + let b:fugitive_type = 'blob' + let b:git_dir = s:repo().dir() + call s:ReplaceCmd(s:repo().git_command('cat-file','blob',s:buffer().sha1())) + if &bufhidden ==# '' + setlocal bufhidden=delete + endif + return '' + catch /^fugitive: rev-parse/ + silent exe 'doau BufNewFile '.s:fnameescape(bufname('')) + return '' + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry +endfunction + +function! s:BufWriteIndexFile() + let tmp = tempname() + try + let path = matchstr(expand('<amatch>'),'//\d/\zs.*') + let stage = matchstr(expand('<amatch>'),'//\zs\d') + silent execute 'write !'.s:repo().git_command('hash-object','-w','--stdin').' > '.tmp + let sha1 = readfile(tmp)[0] + let old_mode = matchstr(s:repo().git_chomp('ls-files','--stage',path),'^\d\+') + if old_mode == '' + let old_mode = executable(s:repo().tree(path)) ? '100755' : '100644' + endif + let info = old_mode.' '.sha1.' '.stage."\t".path + call writefile([info],tmp) + if has('win32') + let error = system('type '.tmp.'|'.s:repo().git_command('update-index','--index-info')) + else + let error = system(s:repo().git_command('update-index','--index-info').' < '.tmp) + endif + if v:shell_error == 0 + setlocal nomodified + silent execute 'doautocmd BufWritePost '.s:fnameescape(expand('%:p')) + call fugitive#reload_status() + return '' + else + return 'echoerr '.string('fugitive: '.error) + endif + finally + call delete(tmp) + endtry +endfunction + +function! s:BufReadObject() + try + setlocal noro ma + let b:git_dir = s:repo().dir() + let hash = s:buffer().sha1() + if !exists("b:fugitive_type") + let b:fugitive_type = s:repo().git_chomp('cat-file','-t',hash) + endif + if b:fugitive_type !~# '^\%(tag\|commit\|tree\|blob\)$' + return "echoerr 'fugitive: unrecognized git type'" + endif + let firstline = getline('.') + if !exists('b:fugitive_display_format') && b:fugitive_type != 'blob' + let b:fugitive_display_format = +getbufvar('#','fugitive_display_format') + endif + + let pos = getpos('.') + silent %delete + setlocal endofline + + if b:fugitive_type == 'tree' + let b:fugitive_display_format = b:fugitive_display_format % 2 + if b:fugitive_display_format + call s:ReplaceCmd(s:repo().git_command('ls-tree',hash)) + else + call s:ReplaceCmd(s:repo().git_command('show','--no-color',hash)) + endif + elseif b:fugitive_type == 'tag' + let b:fugitive_display_format = b:fugitive_display_format % 2 + if b:fugitive_display_format + call s:ReplaceCmd(s:repo().git_command('cat-file',b:fugitive_type,hash)) + else + call s:ReplaceCmd(s:repo().git_command('cat-file','-p',hash)) + endif + elseif b:fugitive_type == 'commit' + let b:fugitive_display_format = b:fugitive_display_format % 2 + if b:fugitive_display_format + call s:ReplaceCmd(s:repo().git_command('cat-file',b:fugitive_type,hash)) + else + call s:ReplaceCmd(s:repo().git_command('show','--no-color','--pretty=format:tree %T%nparent %P%nauthor %an <%ae> %ad%ncommitter %cn <%ce> %cd%nencoding %e%n%n%s%n%n%b',hash)) + call search('^parent ') + if getline('.') ==# 'parent ' + silent delete_ + else + silent s/\%(^parent\)\@<! /\rparent /ge + endif + if search('^encoding \%(<unknown>\)\=$','W',line('.')+3) + silent delete_ + end + 1 + endif + elseif b:fugitive_type ==# 'blob' + call s:ReplaceCmd(s:repo().git_command('cat-file',b:fugitive_type,hash)) + endif + call setpos('.',pos) + setlocal ro noma nomod nomodeline + if &bufhidden ==# '' + setlocal bufhidden=delete + endif + if b:fugitive_type !=# 'blob' + set filetype=git + nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += v:count1<Bar>exe <SID>BufReadObject()<CR> + nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= v:count1<Bar>exe <SID>BufReadObject()<CR> + else + call s:JumpInit() + endif + + return '' + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry +endfunction + +augroup fugitive_files + autocmd! + autocmd BufReadCmd *.git/index exe s:BufReadIndex() + autocmd BufReadCmd *.git/*index*.lock exe s:BufReadIndex() + autocmd FileReadCmd fugitive://**//[0-3]/** exe s:FileRead() + autocmd BufReadCmd fugitive://**//[0-3]/** exe s:BufReadIndexFile() + autocmd BufWriteCmd fugitive://**//[0-3]/** exe s:BufWriteIndexFile() + autocmd BufReadCmd fugitive://**//[0-9a-f][0-9a-f]* exe s:BufReadObject() + autocmd FileReadCmd fugitive://**//[0-9a-f][0-9a-f]* exe s:FileRead() + autocmd FileType git call s:JumpInit() +augroup END + +" }}}1 +" Temp files {{{1 + +let s:temp_files = {} + +augroup fugitive_temp + autocmd! + autocmd BufNewFile,BufReadPost * + \ if has_key(s:temp_files,expand('<amatch>:p')) | + \ let b:git_dir = s:temp_files[expand('<amatch>:p')] | + \ let b:git_type = 'temp' | + \ call s:Detect(expand('<amatch>:p')) | + \ setlocal bufhidden=delete | + \ nnoremap <buffer> <silent> q :<C-U>bdelete<CR> | + \ endif +augroup END + +" }}}1 +" Go to file {{{1 + +function! s:JumpInit() abort + nnoremap <buffer> <silent> <CR> :<C-U>exe <SID>GF("edit")<CR> + if !&modifiable + nnoremap <buffer> <silent> o :<C-U>exe <SID>GF("split")<CR> + nnoremap <buffer> <silent> O :<C-U>exe <SID>GF("tabedit")<CR> + nnoremap <buffer> <silent> P :<C-U>exe <SID>Edit('edit',0,<SID>buffer().commit().'^'.v:count1.<SID>buffer().path(':'))<CR> + nnoremap <buffer> <silent> ~ :<C-U>exe <SID>Edit('edit',0,<SID>buffer().commit().'~'.v:count1.<SID>buffer().path(':'))<CR> + nnoremap <buffer> <silent> C :<C-U>exe <SID>Edit('edit',0,<SID>buffer().containing_commit())<CR> + nnoremap <buffer> <silent> cc :<C-U>exe <SID>Edit('edit',0,<SID>buffer().containing_commit())<CR> + nnoremap <buffer> <silent> co :<C-U>exe <SID>Edit('split',0,<SID>buffer().containing_commit())<CR> + nnoremap <buffer> <silent> cO :<C-U>exe <SID>Edit('tabedit',0,<SID>buffer().containing_commit())<CR> + nnoremap <buffer> <silent> cp :<C-U>exe <SID>Edit('pedit',0,<SID>buffer().containing_commit())<CR> + endif +endfunction + +function! s:GF(mode) abort + try + let buffer = s:buffer() + let myhash = buffer.sha1() + if myhash ==# '' && getline(1) =~# '^\%(commit\|tag\) \w' + let myhash = matchstr(getline(1),'^\w\+ \zs\S\+') + endif + + if buffer.type('tree') + let showtree = (getline(1) =~# '^tree ' && getline(2) == "") + if showtree && line('.') == 1 + return "" + elseif showtree && line('.') > 2 + return s:Edit(a:mode,0,buffer.commit().':'.s:buffer().path().(buffer.path() =~# '^$\|/$' ? '' : '/').s:sub(getline('.'),'/$','')) + elseif getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40\}\t' + return s:Edit(a:mode,0,buffer.commit().':'.s:buffer().path().(buffer.path() =~# '^$\|/$' ? '' : '/').s:sub(matchstr(getline('.'),'\t\zs.*'),'/$','')) + endif + + elseif buffer.type('blob') + let ref = expand("<cfile>") + try + let sha1 = buffer.repo().rev_parse(ref) + catch /^fugitive:/ + endtry + if exists('sha1') + return s:Edit(a:mode,0,ref) + endif + + else + + " Index + if getline('.') =~# '^\d\{6\} \x\{40\} \d\t' + let ref = matchstr(getline('.'),'\x\{40\}') + let file = ':'.s:sub(matchstr(getline('.'),'\d\t.*'),'\t',':') + return s:Edit(a:mode,0,file) + + elseif getline('.') =~# '^#\trenamed:.* -> ' + let file = '/'.matchstr(getline('.'),' -> \zs.*') + return s:Edit(a:mode,0,file) + elseif getline('.') =~# '^#\t[[:alpha:] ]\+: *.' + let file = '/'.matchstr(getline('.'),': *\zs.\{-\}\ze\%( (new commits)\)\=$') + return s:Edit(a:mode,0,file) + elseif getline('.') =~# '^#\t.' + let file = '/'.matchstr(getline('.'),'#\t\zs.*') + return s:Edit(a:mode,0,file) + elseif getline('.') =~# ': needs merge$' + let file = '/'.matchstr(getline('.'),'.*\ze: needs merge$') + return s:Edit(a:mode,0,file).'|Gdiff' + + elseif getline('.') ==# '# Not currently on any branch.' + return s:Edit(a:mode,0,'HEAD') + elseif getline('.') =~# '^# On branch ' + let file = 'refs/heads/'.getline('.')[12:] + return s:Edit(a:mode,0,file) + elseif getline('.') =~# "^# Your branch .*'" + let file = matchstr(getline('.'),"'\\zs\\S\\+\\ze'") + return s:Edit(a:mode,0,file) + endif + + let showtree = (getline(1) =~# '^tree ' && getline(2) == "") + + if getline('.') =~# '^ref: ' + let ref = strpart(getline('.'),5) + + elseif getline('.') =~# '^commit \x\{40\}\>' + let ref = matchstr(getline('.'),'\x\{40\}') + return s:Edit(a:mode,0,ref) + + elseif getline('.') =~# '^parent \x\{40\}\>' + let ref = matchstr(getline('.'),'\x\{40\}') + let line = line('.') + let parent = 0 + while getline(line) =~# '^parent ' + let parent += 1 + let line -= 1 + endwhile + return s:Edit(a:mode,0,ref) + + elseif getline('.') =~ '^tree \x\{40\}$' + let ref = matchstr(getline('.'),'\x\{40\}') + if s:repo().rev_parse(myhash.':') == ref + let ref = myhash.':' + endif + return s:Edit(a:mode,0,ref) + + elseif getline('.') =~# '^object \x\{40\}$' && getline(line('.')+1) =~ '^type \%(commit\|tree\|blob\)$' + let ref = matchstr(getline('.'),'\x\{40\}') + let type = matchstr(getline(line('.')+1),'type \zs.*') + + elseif getline('.') =~# '^\l\{3,8\} '.myhash.'$' + return '' + + elseif getline('.') =~# '^\l\{3,8\} \x\{40\}\>' + let ref = matchstr(getline('.'),'\x\{40\}') + echoerr "warning: unknown context ".matchstr(getline('.'),'^\l*') + + elseif getline('.') =~# '^[+-]\{3\} [ab/]' + let ref = getline('.')[4:] + + elseif getline('.') =~# '^rename from ' + let ref = 'a/'.getline('.')[12:] + elseif getline('.') =~# '^rename to ' + let ref = 'b/'.getline('.')[10:] + + elseif getline('.') =~# '^diff --git \%(a/.*\|/dev/null\) \%(b/.*\|/dev/null\)' + let dref = matchstr(getline('.'),'\Cdiff --git \zs\%(a/.*\|/dev/null\)\ze \%(b/.*\|/dev/null\)') + let ref = matchstr(getline('.'),'\Cdiff --git \%(a/.*\|/dev/null\) \zs\%(b/.*\|/dev/null\)') + let dcmd = 'Gdiff' + + elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# '^diff --git \%(a/.*\|/dev/null\) \%(b/.*\|/dev/null\)' + let line = getline(line('.')-1) + let dref = matchstr(line,'\Cdiff --git \zs\%(a/.*\|/dev/null\)\ze \%(b/.*\|/dev/null\)') + let ref = matchstr(line,'\Cdiff --git \%(a/.*\|/dev/null\) \zs\%(b/.*\|/dev/null\)') + let dcmd = 'Gdiff!' + + elseif line('$') == 1 && getline('.') =~ '^\x\{40\}$' + let ref = getline('.') + else + let ref = '' + endif + + if myhash ==# '' + let ref = s:sub(ref,'^a/','HEAD:') + let ref = s:sub(ref,'^b/',':0:') + if exists('dref') + let dref = s:sub(dref,'^a/','HEAD:') + endif + else + let ref = s:sub(ref,'^a/',myhash.'^:') + let ref = s:sub(ref,'^b/',myhash.':') + if exists('dref') + let dref = s:sub(dref,'^a/',myhash.'^:') + endif + endif + + if ref ==# '/dev/null' + " Empty blob + let ref = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391' + endif + + if exists('dref') + return s:Edit(a:mode,0,ref) . '|'.dcmd.' '.s:fnameescape(dref) + elseif ref != "" + return s:Edit(a:mode,0,ref) + endif + + endif + return '' + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry +endfunction + +" }}}1 +" Statusline {{{1 + +function! s:repo_head_ref() dict abort + return readfile(s:repo().dir('HEAD'))[0] +endfunction + +call s:add_methods('repo',['head_ref']) + +function! fugitive#statusline(...) + if !exists('b:git_dir') + return '' + endif + let status = '' + if s:buffer().commit() != '' + let status .= ':' . s:buffer().commit()[0:7] + endif + let head = s:repo().head_ref() + if head =~# '^ref: ' + let status .= s:sub(head,'^ref: %(refs/%(heads/|remotes/|tags/)=)=','(').')' + elseif head =~# '^\x\{40\}$' + let status .= '('.head[0:7].')' + endif + if &statusline =~# '%[MRHWY]' && &statusline !~# '%[mrhwy]' + return ',GIT'.status + else + return '[Git'.status.']' + endif +endfunction + +" }}}1 + +" vim:set ft=vim ts=8 sw=2 sts=2: diff --git a/vim/plugin/gist.vim b/vim/plugin/gist.vim new file mode 100644 index 0000000..9081d00 --- /dev/null +++ b/vim/plugin/gist.vim @@ -0,0 +1,826 @@ +"============================================================================= +" File: gist.vim +" Author: Yasuhiro Matsumoto <mattn.jp@gmail.com> +" Last Change: 20-Aug-2011. +" Version: 5.0 +" WebPage: http://github.com/mattn/gist-vim +" License: BSD +" Usage: +" +" :Gist +" post current buffer to gist, using default privicy option +" (see g:gist_private) +" +" :'<,'>Gist +" post selected text to gist., using default privicy option +" This applies to all permutations listed below (except multi) +" (see g:gist_private) +" +" :Gist -p +" create a private gist +" +" :Gist -P +" create a public gist +" (only relevant if you've set gists to be private by default) +" +" :Gist -P +" post whole text to gist as public +" This is only relevant if you've set gists to be private by default +" :Gist -a +" create a gist anonymously +" +" :Gist -m +" create a gist with all open buffers +" +" :Gist -e +" edit the gist. (you need to have opend the gist buffer first) +" you can update the gist with :w command on gist buffer +" +" :Gist -d +" delete the gist. (you need to have opend the gist buffer first) +" password authentication is needed +" +" :Gist -f +" fork the gist. (you need to have opend the gist buffer first) +" password authentication is needed +" +" :Gist -e foo.js +" edit the gist with name 'foo.js'. (you need to have opend the gist buffer first) +" +" :Gist XXXXX +" get gist XXXXX +" +" :Gist -c XXXXX +" get gist XXXXX and add to clipboard +" +" :Gist -l +" list your public gists +" +" :Gist -l mattn +" list gists from mattn +" +" :Gist -la +" list all your (public and private) gists +" +" Tips: +" * if set g:gist_clip_command, gist.vim will copy the gist code +" with option '-c'. +" +" # mac +" let g:gist_clip_command = 'pbcopy' +" +" # linux +" let g:gist_clip_command = 'xclip -selection clipboard' +" +" # others(cygwin?) +" let g:gist_clip_command = 'putclip' +" +" * if you want to detect filetype from gist's filename... +" +" # detect filetype if vim failed auto-detection. +" let g:gist_detect_filetype = 1 +" +" # detect filetype always. +" let g:gist_detect_filetype = 2 +" +" * if you want to open browser after the post... +" +" let g:gist_open_browser_after_post = 1 +" +" * if you want to change the browser... +" +" let g:gist_browser_command = 'w3m %URL%' +" +" or +" +" let g:gist_browser_command = 'opera %URL% &' +" +" on windows, should work with original setting. +" +" * if you want to show your private gists with ':Gist -l' +" +" let g:gist_show_privates = 1 +" +" * if don't you want to copy URL of the post... +" +" let g:gist_put_url_to_clipboard_after_post = 0 +" +" or if you want to copy URL and add linefeed at the last of URL, +" +" let g:gist_put_url_to_clipboard_after_post = 2 +" +" default value is 1. +" +" Thanks: +" MATSUU Takuto: +" removed carriage return +" gist_browser_command enhancement +" edit support +" +" GetLatestVimScripts: 2423 1 :AutoInstall: gist.vim +" script type: plugin + +if &cp || (exists('g:loaded_gist_vim') && g:loaded_gist_vim) + finish +endif +let g:loaded_gist_vim = 1 + +if (!exists('g:github_user') || !exists('g:github_token')) && !executable('git') + echohl ErrorMsg | echomsg "Gist: require 'git' command" | echohl None + finish +endif + +if !executable('curl') + echohl ErrorMsg | echomsg "Gist: require 'curl' command" | echohl None + finish +endif + +if !exists('g:gist_open_browser_after_post') + let g:gist_open_browser_after_post = 0 +endif + +if !exists('g:gist_put_url_to_clipboard_after_post') + let g:gist_put_url_to_clipboard_after_post = 1 +endif + +if !exists('g:gist_curl_options') + let g:gist_curl_options = "" +endif + +if !exists('g:gist_browser_command') + if has('win32') || has('win64') + let g:gist_browser_command = "!start rundll32 url.dll,FileProtocolHandler %URL%" + elseif has('mac') + let g:gist_browser_command = "open %URL%" + elseif executable('xdg-open') + let g:gist_browser_command = "xdg-open %URL%" + else + let g:gist_browser_command = "firefox %URL% &" + endif +endif + +if !exists('g:gist_detect_filetype') + let g:gist_detect_filetype = 0 +endif + +if !exists('g:gist_private') + let g:gist_private = 0 +endif + +if !exists('g:gist_show_privates') + let g:gist_show_privates = 0 +endif + +if !exists('g:gist_cookie_dir') + let g:gist_cookie_dir = substitute(expand('<sfile>:p:h'), '[/\\]plugin$', '', '').'/cookies' +endif + +function! s:nr2hex(nr) + let n = a:nr + let r = "" + while n + let r = '0123456789ABCDEF'[n % 16] . r + let n = n / 16 + endwhile + return r +endfunction + +function! s:encodeURIComponent(instr) + let instr = iconv(a:instr, &enc, "utf-8") + let len = strlen(instr) + let i = 0 + let outstr = '' + while i < len + let ch = instr[i] + if ch =~# '[0-9A-Za-z-._~!''()*]' + let outstr = outstr . ch + elseif ch == ' ' + let outstr = outstr . '+' + else + let outstr = outstr . '%' . substitute('0' . s:nr2hex(char2nr(ch)), '^.*\(..\)$', '\1', '') + endif + let i = i + 1 + endwhile + return outstr +endfunction + +" Note: A colon in the file name has side effects on Windows due to NTFS Alternate Data Streams; avoid it. +let s:bufprefix = 'gist' . (has('unix') ? ':' : '_') +function! s:GistList(user, token, gistls, page) + if a:gistls == '-all' + let url = 'https://gist.github.com/gists' + elseif g:gist_show_privates && a:gistls == a:user + let url = 'https://gist.github.com/mine' + else + let url = 'https://gist.github.com/'.a:gistls + endif + let winnum = bufwinnr(bufnr(s:bufprefix.a:gistls)) + if winnum != -1 + if winnum != bufwinnr('%') + exe winnum 'wincmd w' + endif + setlocal modifiable + else + exec 'silent split' s:bufprefix.a:gistls + endif + if a:page > 1 + let oldlines = getline(0, line('$')) + let url = url . '?page=' . a:page + endif + + setlocal foldmethod=manual + let oldlines = [] + if g:gist_show_privates + echon 'Login to gist... ' + silent %d _ + let res = s:GistGetPage(url, a:user, '', '-L') + silent put =res.content + else + silent %d _ + exec 'silent r! curl -s' g:gist_curl_options url + endif + + 1delete _ + silent! %s/>/>\r/g + silent! %s/</\r</g + silent! %g/<pre/,/<\/pre/join! + silent! %g/<span class="date"/,/<\/span/join + silent! %g/^<span class="date"/s/> */>/g + silent! %v/^\(gist:\|<pre>\|<span class="date">\)/d _ + silent! %s/<div[^>]*>/\r /g + silent! %s/<\/pre>/\r/g + silent! %g/^gist:/,/<span class="date"/join + silent! %s/<[^>]\+>//g + silent! %s/\r//g + silent! %s/ / /g + silent! %s/"/"/g + silent! %s/&/\&/g + silent! %s/>/>/g + silent! %s/</</g + silent! %s/&#\(\d\d\);/\=nr2char(submatch(1))/g + silent! %g/^gist: /s/ //g + + call append(0, oldlines) + $put='more...' + + let b:user = a:user + let b:token = a:token + let b:gistls = a:gistls + let b:page = a:page + setlocal buftype=nofile bufhidden=hide noswapfile + setlocal nomodified + syntax match SpecialKey /^gist:/he=e-1 + nnoremap <silent> <buffer> <cr> :call <SID>GistListAction()<cr> + + cal cursor(1+len(oldlines),1) + setlocal foldmethod=expr + setlocal foldexpr=getline(v:lnum)=~'^\\(gist:\\\|more\\)'?'>1':'=' + setlocal foldtext=getline(v:foldstart) +endfunction + +function! s:GistGetFileName(gistid) + let url = 'https://gist.github.com/'.a:gistid + let res = system('curl -s '.g:gist_curl_options.' '.url) + let res = substitute(res, '^.*<a href="/raw/[^"]\+/\([^"]\+\)".*$', '\1', '') + if res =~ '/' + return '' + else + return res + endif +endfunction + +function! s:GistDetectFiletype(gistid) + let url = 'https://gist.github.com/'.a:gistid + let mx = '^.*<div class=".\{-}type-\([^"]\+\)">.*$' + let res = system('curl -s '.g:gist_curl_options.' '.url) + let res = substitute(matchstr(res, mx), mx, '\1', '') + let res = substitute(res, '.*\(\.[^\.]\+\)$', '\1', '') + let res = substitute(res, '-', '', 'g') + " TODO: more filetype detection that is specified in html. + if res == 'bat' | let res = 'dosbatch' | endif + if res == 'as' | let res = 'actionscript' | endif + if res == 'bash' | let res = 'sh' | endif + if res == 'cl' | let res = 'lisp' | endif + if res == 'rb' | let res = 'ruby' | endif + if res == 'viml' | let res = 'vim' | endif + if res == 'plain' || res == 'text' | let res = '' | endif + + if res =~ '^\.' + silent! exec "doau BufRead *".res + else + silent! exec "setlocal ft=".tolower(res) + endif +endfunction + +function! s:GistWrite(fname) + if substitute(a:fname, '\\', '/', 'g') == expand("%:p:gs@\\@/@") + Gist -e + else + exe "w".(v:cmdbang ? "!" : "") fnameescape(v:cmdarg) fnameescape(a:fname) + silent! exe "file" fnameescape(a:fname) + silent! au! BufWriteCmd <buffer> + endif +endfunction + +function! s:GistGet(user, token, gistid, clipboard) + let url = 'https://raw.github.com/gist/'.a:gistid + let winnum = bufwinnr(bufnr(s:bufprefix.a:gistid)) + if winnum != -1 + if winnum != bufwinnr('%') + exe winnum 'wincmd w' + endif + setlocal modifiable + else + exec 'silent split' s:bufprefix.a:gistid + endif + filetype detect + silent %d _ + exec 'silent 0r! curl -s' g:gist_curl_options url + $delete _ + setlocal buftype=acwrite bufhidden=delete noswapfile + setlocal nomodified + doau StdinReadPost <buffer> + if (&ft == '' && g:gist_detect_filetype == 1) || g:gist_detect_filetype == 2 + call s:GistDetectFiletype(a:gistid) + endif + if a:clipboard + if exists('g:gist_clip_command') + exec 'silent w !'.g:gist_clip_command + else + %yank + + endif + endif + 1 + au! BufWriteCmd <buffer> call s:GistWrite(expand("<amatch>")) +endfunction + +function! s:GistListAction() + let line = getline('.') + let mx = '^gist:\(\w\+\).*' + if line =~# mx + let gistid = substitute(line, mx, '\1', '') + call s:GistGet(g:github_user, g:github_token, gistid, 0) + return + endif + if line =~# '^more\.\.\.$' + delete + call s:GistList(b:user, b:token, b:gistls, b:page+1) + return + endif +endfunction + +function! s:GistUpdate(user, token, content, gistid, gistnm) + if len(a:gistnm) == 0 + let name = s:GistGetFileName(a:gistid) + else + let name = a:gistnm + endif + let namemx = '^[^.]\+\(.\+\)$' + let ext = '' + if name =~ namemx + let ext = substitute(name, namemx, '\1', '') + endif + let query = [ + \ '_method=put', + \ 'file_ext[gistfile1%s]=%s', + \ 'file_name[gistfile1%s]=%s', + \ 'file_contents[gistfile1%s]=%s', + \ 'login=%s', + \ 'token=%s', + \ ] + let squery = printf(join(query, '&'), + \ s:encodeURIComponent(ext), s:encodeURIComponent(ext), + \ s:encodeURIComponent(ext), s:encodeURIComponent(name), + \ s:encodeURIComponent(ext), s:encodeURIComponent(a:content), + \ s:encodeURIComponent(a:user), + \ s:encodeURIComponent(a:token)) + unlet query + + let file = tempname() + call writefile([squery], file) + echon 'Updating it to gist... ' + let quote = &shellxquote == '"' ? "'" : '"' + let url = 'https://gist.github.com/gists/'.a:gistid + let res = system('curl -i '.g:gist_curl_options.' -d @'.quote.file.quote.' '.url) + call delete(file) + let headers = split(res, '\(\r\?\n\|\r\n\?\)') + let location = matchstr(headers, '^Location: ') + let location = substitute(location, '^[^:]\+: ', '', '') + if len(location) > 0 && location =~ '^\(http\|https\):\/\/gist\.github\.com\/' + setlocal nomodified + redraw + echo 'Done: '.location + else + let message = matchstr(headers, '^Status: ') + let message = substitute(message, '^[^:]\+: [0-9]\+ ', '', '') + echohl ErrorMsg | echomsg 'Edit failed: '.message | echohl None + endif + return location +endfunction + +function! s:GistGetPage(url, user, param, opt) + if !isdirectory(g:gist_cookie_dir) + call mkdir(g:gist_cookie_dir, 'p') + endif + let cookie_file = g:gist_cookie_dir.'/github' + + if len(a:url) == 0 + call delete(cookie_file) + return + endif + + let quote = &shellxquote == '"' ? "'" : '"' + if !filereadable(cookie_file) + let password = inputsecret('Password:') + if len(password) == 0 + echo 'Canceled' + return + endif + let url = 'https://gist.github.com/login?return_to=gist' + let res = system('curl -L -s '.g:gist_curl_options.' -c '.quote.cookie_file.quote.' '.quote.url.quote) + let token = substitute(res, '^.* name="authenticity_token" type="hidden" value="\([^"]\+\)".*$', '\1', '') + + let query = [ + \ 'authenticity_token=%s', + \ 'login=%s', + \ 'password=%s', + \ 'return_to=gist', + \ 'commit=Log+in', + \ ] + let squery = printf(join(query, '&'), + \ s:encodeURIComponent(token), + \ s:encodeURIComponent(a:user), + \ s:encodeURIComponent(password)) + unlet query + + let file = tempname() + let command = 'curl -s '.g:gist_curl_options.' -i' + let command .= ' -b '.quote.cookie_file.quote + let command .= ' -c '.quote.cookie_file.quote + let command .= ' '.quote.'https://gist.github.com/session'.quote + let command .= ' -d @' . quote.file.quote + call writefile([squery], file) + let res = system(command) + call delete(file) + let res = matchstr(split(res, '\(\r\?\n\|\r\n\?\)'), '^Location: ') + let res = substitute(res, '^[^:]\+: ', '', '') + if len(res) == 0 + call delete(cookie_file) + return '' + endif + endif + let command = 'curl -s '.g:gist_curl_options.' -i '.a:opt + if len(a:param) + let command .= ' -d '.quote.a:param.quote + endif + let command .= ' -b '.quote.cookie_file.quote + let command .= ' '.quote.a:url.quote + let res = iconv(system(command), "utf-8", &encoding) + let pos = stridx(res, "\r\n\r\n") + if pos != -1 + let content = res[pos+4:] + else + let pos = stridx(res, "\n\n") + let content = res[pos+2:] + endif + return { + \ "header" : split(res[0:pos], '\r\?\n'), + \ "content" : content + \} +endfunction + +function! s:GistDelete(user, token, gistid) + echon 'Deleting gist... ' + let res = s:GistGetPage('https://gist.github.com/'.a:gistid, a:user, '', '') + if (!len(res)) + echohl ErrorMsg | echomsg 'Wrong password? no response received from github trying to delete ' . a:gistid | echohl None + return + endif + let mx = '^.* name="authenticity_token" type="hidden" value="\([^"]\+\)".*$' + let token = substitute(matchstr(res.content, mx), mx, '\1', '') + if len(token) > 0 + let res = s:GistGetPage('https://gist.github.com/delete/'.a:gistid, a:user, '_method=delete&authenticity_token='.token, '') + if len(res.content) > 0 + redraw + echo 'Done: ' + else + let message = matchstr(res.header, '^Status: ') + let message = substitute(message, '^[^:]\+: [0-9]\+ ', '', '') + echohl ErrorMsg | echomsg 'Delete failed: '.message | echohl None + endif + else + echohl ErrorMsg | echomsg 'Delete failed' | echohl None + endif +endfunction + + +" GistPost function: +" Post new gist to github +" +" if there is an embedded gist url or gist id in your file, +" it will just update it. +" -- by c9s +" +" embedded gist url format: +" +" Gist: https://gist.github.com/123123 +" +" embedded gist id format: +" +" GistID: 123123 +" +function! s:GistPost(user, token, content, private) + + " find GistID: in content, then we should just update + for l in split(a:content, "\n") + if l =~ '\<GistID:' + let gistid = matchstr(l, 'GistID:\s*[0-9a-z]\+') + + if strlen(gistid) == 0 + echohl WarningMsg | echo "GistID error" | echohl None + return + endif + echo "Found GistID: " . gistid + + cal s:GistUpdate(a:user, a:token, a:content, gistid, '') + return + elseif l =~ '\<Gist:' + let gistid = matchstr(l, 'Gist:\s*https://gist.github.com/[0-9a-z]\+') + + if strlen(gistid) == 0 + echohl WarningMsg | echo "GistID error" | echohl None + return + endif + echo "Found GistID: " . gistid + + cal s:GistUpdate(a:user, a:token, a:content, gistid, '') + return + endif + endfor + + let ext = expand('%:e') + let ext = len(ext) ? '.'.ext : '' + let name = expand('%:t') + + let query = [ + \ 'file_ext[gistfile1]=%s', + \ 'file_name[gistfile1]=%s', + \ 'file_contents[gistfile1]=%s', + \ ] + + if len(a:user) > 0 && len(a:token) > 0 + call add(query, 'login=%s') + call add(query, 'token=%s') + else + call add(query, '%.0s%.0s') + endif + + if a:private + call add(query, 'action_button=private') + endif + let squery = printf(join(query, '&'), + \ s:encodeURIComponent(ext), + \ s:encodeURIComponent(name), + \ s:encodeURIComponent(a:content), + \ s:encodeURIComponent(a:user), + \ s:encodeURIComponent(a:token)) + unlet query + + let file = tempname() + call writefile([squery], file) + echon 'Posting it to gist... ' + let quote = &shellxquote == '"' ? "'" : '"' + let url = 'https://gist.github.com/gists' + let res = system('curl -i '.g:gist_curl_options.' -d @'.quote.file.quote.' '.url) + call delete(file) + let headers = split(res, '\(\r\?\n\|\r\n\?\)') + let location = matchstr(headers, '^Location: ') + let location = substitute(location, '^[^:]\+: ', '', '') + if len(location) > 0 && location =~ '^\(http\|https\):\/\/gist\.github\.com\/' + redraw + echo 'Done: '.location + else + let message = matchstr(headers, '^Status: ') + let message = substitute(message, '^[^:]\+: [0-9]\+ ', '', '') + echohl ErrorMsg | echomsg 'Post failed: '.message | echohl None + endif + return location +endfunction + +function! s:GistPostBuffers(user, token, private) + let bufnrs = range(1, bufnr("$")) + let bn = bufnr('%') + let query = [] + if len(a:user) > 0 && len(a:token) > 0 + call add(query, 'login=%s') + call add(query, 'token=%s') + else + call add(query, '%.0s%.0s') + endif + if a:private + call add(query, 'action_button=private') + endif + let squery = printf(join(query, "&"), + \ s:encodeURIComponent(a:user), + \ s:encodeURIComponent(a:token)) . '&' + + let query = [ + \ 'file_ext[gistfile]=%s', + \ 'file_name[gistfile]=%s', + \ 'file_contents[gistfile]=%s', + \ ] + let format = join(query, "&") . '&' + + let index = 1 + for bufnr in bufnrs + if !bufexists(bufnr) || buflisted(bufnr) == 0 + continue + endif + echo "Creating gist content".index."... " + silent! exec "buffer!" bufnr + let content = join(getline(1, line('$')), "\n") + let ext = expand('%:e') + let ext = len(ext) ? '.'.ext : '' + let name = expand('%:t') + let squery .= printf(substitute(format, 'gistfile', 'gistfile'.index, 'g'), + \ s:encodeURIComponent(ext), + \ s:encodeURIComponent(name), + \ s:encodeURIComponent(content)) + let index = index + 1 + endfor + silent! exec "buffer!" bn + + let file = tempname() + call writefile([squery], file) + echo "Posting it to gist... " + let quote = &shellxquote == '"' ? "'" : '"' + let url = 'https://gist.github.com/gists' + let res = system('curl -i '.g:gist_curl_options.' -d @'.quote.file.quote.' '.url) + call delete(file) + let res = matchstr(split(res, '\(\r\?\n\|\r\n\?\)'), '^Location: ') + let res = substitute(res, '^.*: ', '', '') + if len(res) > 0 && res =~ '^\(http\|https\):\/\/gist\.github\.com\/' + redraw + echo 'Done: '.res + else + echohl ErrorMsg | echomsg 'Post failed' | echohl None + endif + return res +endfunction + +function! Gist(line1, line2, ...) + if !exists('g:github_user') + let g:github_user = substitute(system('git config --global github.user'), "\n", '', '') + if strlen(g:github_user) == 0 + let g:github_user = $GITHUB_USER + end + endif + if !exists('g:github_token') + let g:github_token = substitute(system('git config --global github.token'), "\n", '', '') + if strlen(g:github_token) == 0 + let g:github_token = $GITHUB_TOKEN + end + endif + if strlen(g:github_user) == 0 || strlen(g:github_token) == 0 + echohl ErrorMsg + echomsg "You have no setting for github." + echohl WarningMsg + echo "git config --global github.user your-name" + echo "git config --global github.token your-token" + echo "or set g:github_user and g:github_token in your vimrc" + echo "or set shell env vars GITHUB_USER and GITHUB_TOKEN" + echohl None + return 0 + end + + let bufname = bufname("%") + let user = g:github_user + let token = g:github_token + let gistid = '' + let gistls = '' + let gistnm = '' + let private = g:gist_private + let multibuffer = 0 + let clipboard = 0 + let deletepost = 0 + let editpost = 0 + let listmx = '^\(-l\|--list\)\s*\([^\s]\+\)\?$' + let bufnamemx = '^' . s:bufprefix .'\([0-9a-f]\+\)$' + + let args = (a:0 > 0) ? split(a:1, ' ') : [] + for arg in args + if arg =~ '^\(-la\|--listall\)$\C' + let gistls = '-all' + elseif arg =~ '^\(-l\|--list\)$\C' + if g:gist_show_privates + let gistls = 'mine' + else + let gistls = g:github_user + endif + elseif arg == '--abandon\C' + call s:GistGetPage('', '', '', '') + return + elseif arg =~ '^\(-m\|--multibuffer\)$\C' + let multibuffer = 1 + elseif arg =~ '^\(-p\|--private\)$\C' + let private = 1 + elseif arg =~ '^\(-P\|--public\)$\C' + let private = 0 + elseif arg =~ '^\(-a\|--anonymous\)$\C' + let user = '' + let token = '' + elseif arg =~ '^\(-c\|--clipboard\)$\C' + let clipboard = 1 + elseif arg =~ '^\(-d\|--delete\)$\C' && bufname =~ bufnamemx + let deletepost = 1 + let gistid = substitute(bufname, bufnamemx, '\1', '') + elseif arg =~ '^\(-e\|--edit\)$\C' && bufname =~ bufnamemx + let editpost = 1 + let gistid = substitute(bufname, bufnamemx, '\1', '') + elseif arg =~ '^\(-f\|--fork\)$\C' && bufname =~ bufnamemx + let gistid = substitute(bufname, bufnamemx, '\1', '') + let res = s:GistGetPage("https://gist.github.com/fork/".gistid, g:github_user, '', '') + let loc = filter(res.header, 'v:val =~ "^Location:"')[0] + let loc = substitute(loc, '^[^:]\+: ', '', '') + let mx = '^https://gist.github.com/\([0-9a-z]\+\)$' + if loc =~ mx + let gistid = substitute(loc, mx, '\1', '') + else + echohl ErrorMsg | echomsg 'Fork failed' | echohl None + return + endif + elseif arg !~ '^-' && len(gistnm) == 0 + if editpost == 1 || deletepost == 1 + let gistnm = arg + elseif len(gistls) > 0 && arg != '^\w\+$\C' + let gistls = arg + elseif arg =~ '^[0-9a-z]\+$\C' + let gistid = arg + else + echohl ErrorMsg | echomsg 'Invalid arguments' | echohl None + unlet args + return 0 + endif + elseif len(arg) > 0 + echohl ErrorMsg | echomsg 'Invalid arguments' | echohl None + unlet args + return 0 + endif + endfor + unlet args + "echo "gistid=".gistid + "echo "gistls=".gistls + "echo "gistnm=".gistnm + "echo "private=".private + "echo "clipboard=".clipboard + "echo "editpost=".editpost + "echo "deletepost=".deletepost + + if len(gistls) > 0 + call s:GistList(user, token, gistls, 1) + elseif len(gistid) > 0 && editpost == 0 && deletepost == 0 + call s:GistGet(user, token, gistid, clipboard) + else + let url = '' + if multibuffer == 1 + let url = s:GistPostBuffers(user, token, private) + else + let content = join(getline(a:line1, a:line2), "\n") + if editpost == 1 + let url = s:GistUpdate(user, token, content, gistid, gistnm) + elseif deletepost == 1 + call s:GistDelete(user, token, gistid) + else + let url = s:GistPost(user, token, content, private) + endif + endif + if len(url) > 0 + if g:gist_open_browser_after_post + let cmd = substitute(g:gist_browser_command, '%URL%', url, 'g') + if cmd =~ '^!' + silent! exec cmd + elseif cmd =~ '^:[A-Z]' + exec cmd + else + call system(cmd) + endif + endif + if g:gist_put_url_to_clipboard_after_post > 0 + if g:gist_put_url_to_clipboard_after_post == 2 + let url = url . "\n" + endif + if exists('g:gist_clip_command') + call system(g:gist_clip_command, url) + elseif has('unix') && !has('xterm_clipboard') + let @" = url + else + let @+ = url + endif + endif + endif + endif + return 1 +endfunction + +command! -nargs=? -range=% Gist :call Gist(<line1>, <line2>, <f-args>) +" vim:set et: diff --git a/vim/plugin/indent-object.vim b/vim/plugin/indent-object.vim new file mode 100644 index 0000000..afb8edd --- /dev/null +++ b/vim/plugin/indent-object.vim @@ -0,0 +1,224 @@ +"-------------------------------------------------------------------------------- +" +" Copyright (c) 2010 Michael Smith <msmith@msmith.id.au> +" +" http://github.com/michaeljsmith/vim-indent-object +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to +" deal in the Software without restriction, including without limitation the +" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +" sell copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +" IN THE SOFTWARE. +" +"-------------------------------------------------------------------------------- + +" Mappings excluding line below. +onoremap <silent>ai :<C-u>cal <Sid>HandleTextObjectMapping(0, 0, 0, [line("."), line("."), col("."), col(".")])<CR> +onoremap <silent>ii :<C-u>cal <Sid>HandleTextObjectMapping(1, 0, 0, [line("."), line("."), col("."), col(".")])<CR> +vnoremap <silent>ai :<C-u>cal <Sid>HandleTextObjectMapping(0, 0, 1, [line("'<"), line("'>"), col("'<"), col("'>")])<CR><Esc>gv +vnoremap <silent>ii :<C-u>cal <Sid>HandleTextObjectMapping(1, 0, 1, [line("'<"), line("'>"), col("'<"), col("'>")])<CR><Esc>gv + +" Mappings including line below. +onoremap <silent>aI :<C-u>cal <Sid>HandleTextObjectMapping(0, 1, 0, [line("."), line("."), col("."), col(".")])<CR> +onoremap <silent>iI :<C-u>cal <Sid>HandleTextObjectMapping(1, 1, 0, [line("."), line("."), col("."), col(".")])<CR> +vnoremap <silent>aI :<C-u>cal <Sid>HandleTextObjectMapping(0, 1, 1, [line("'<"), line("'>"), col("'<"), col("'>")])<CR><Esc>gv +vnoremap <silent>iI :<C-u>cal <Sid>HandleTextObjectMapping(1, 1, 1, [line("'<"), line("'>"), col("'<"), col("'>")])<CR><Esc>gv + +let s:l0 = -1 +let s:l1 = -1 +let s:c0 = -1 +let s:c1 = -1 + +function! <Sid>TextObject(inner, incbelow, vis, range, count) + + " Record the current state of the visual region. + let vismode = "V" + + " Detect if this is a completely new visual selction session. + let new_vis = 0 + let new_vis = new_vis || s:l0 != a:range[0] + let new_vis = new_vis || s:l1 != a:range[1] + let new_vis = new_vis || s:c0 != a:range[2] + let new_vis = new_vis || s:c1 != a:range[3] + + let s:l0 = a:range[0] + let s:l1 = a:range[1] + let s:c0 = a:range[2] + let s:c1 = a:range[3] + + " Repeatedly increase the scope of the selection. + let itr_cnt = 0 + let cnt = a:count + while cnt > 0 + + " Look for the minimum indentation in the current visual region. + let l = s:l0 + let idnt_invalid = 1000 + let idnt = idnt_invalid + while l <= s:l1 + if !(getline(l) =~ "^\\s*$") + let idnt = min([idnt, indent(l)]) + endif + let l += 1 + endwhile + + " Keep track of where the range should be expanded to. + let l_1 = s:l0 + let l_1o = l_1 + let l2 = s:l1 + let l2o = l2 + + " If we are highlighting only blank lines, we may not have found a + " valid indent. In this case we need to look for the next and previous + " non blank lines and check which of those has the largest indent. + if idnt == idnt_invalid + let idnt = 0 + let pnb = prevnonblank(s:l0) + if pnb + let idnt = max([idnt, indent(pnb)]) + let l_1 = pnb + endif + let nnb = nextnonblank(s:l0) + if nnb + let idnt = max([idnt, indent(nnb)]) + endif + + " If we are in whitespace at the beginning of a block, skip over + " it when we are selecting the range. Similarly, if we are in + " whitespace at the end, ignore it. + if idnt > indent(pnb) + let l_1 = nnb + endif + if idnt > indent(nnb) + let l2 = pnb + endif + endif + + " Search backward for the first line with less indent than the target + " indent (skipping blank lines). + let blnk = getline(l_1) =~ "^\\s*$" + while l_1 > 0 && ((idnt == 0 && !blnk) || (idnt != 0 && (blnk || indent(l_1) >= idnt))) + if !blnk || !a:inner + let l_1o = l_1 + endif + let l_1 -= 1 + let blnk = getline(l_1) =~ "^\\s*$" + endwhile + + " Search forward for the first line with more indent than the target + " indent (skipping blank lines). + let line_cnt = line("$") + let blnk = getline(l2) =~ "^\\s*$" + while l2 <= line_cnt && ((idnt == 0 && !blnk) || (idnt != 0 && (blnk || indent(l2) >= idnt))) + if !blnk || !a:inner + let l2o = l2 + endif + let l2 += 1 + let blnk = getline(l2) =~ "^\\s*$" + endwhile + + " Determine which of these extensions to include. Include neither if + " we are selecting an 'inner' object. Exclude the bottom unless are + " told to include it. + let idnt2 = max([indent(l_1), indent(l2)]) + if indent(l_1) < idnt2 || a:inner + let l_1 = l_1o + endif + if indent(l2) < idnt2 || a:inner || !a:incbelow + let l2 = l2o + endif + let l_1 = max([l_1, 1]) + let l2 = min([l2, line("$")]) + + " Extend the columns to the start and end. + " If inner is selected, set the final cursor pos to the start + " of the text in the line. + let c_1 = 1 + if a:inner + let c_1 = match(getline(l_1), "\\c\\S") + 1 + endif + let c2 = len(getline(l2)) + if !a:inner + let c2 += 1 + endif + + " Make sure there's no change if we haven't really made a + " significant change in linewise mode - this makes sure that + " we can iteratively increase selection in linewise mode. + if itr_cnt == 0 && vismode ==# 'V' && s:l0 == l_1 && s:l1 == l2 + let c_1 = s:c0 + let c2 = s:c1 + endif + + " Check whether the visual region has changed. + let chg = 0 + let chg = chg || s:l0 != l_1 + let chg = chg || s:l1 != l2 + let chg = chg || s:c0 != c_1 + let chg = chg || s:c1 != c2 + + if vismode ==# 'V' && new_vis + let chg = 1 + endif + + " Update the vars. + let s:l0 = l_1 + let s:l1 = l2 + let s:c0 = c_1 + let s:c1 = c2 + + " If there was no change, then don't decrement the count (it didn't + " count because it didn't do anything). + if chg + let cnt = cnt - 1 + else + " Since this didn't work, push the selection back one char. This + " will have the effect of getting the enclosing block. Do it at + " the beginning rather than the end - the beginning is very likely + " to be only one indentation level different. + if s:l0 == 0 + return + endif + let s:l0 -= 1 + let s:c0 = len(getline(s:l0)) + endif + + let itr_cnt += 1 + + endwhile + + " Apply the range we have found. Make sure to use the current visual mode. + call cursor(s:l0, s:c0) + exe "normal! " . vismode + call cursor(s:l1, s:c1) + normal! o + + " Update these static variables - we need to keep these up-to-date between + " invocations because it's the only way we can detect whether it's a new + " visual mode. We need to know if it's a new visual mode because otherwise + " if there's a single line block in visual line mode and we select it with + " "V", we can't tell whether it's already been selected using Vii. + exe "normal! \<Esc>" + let s:l0 = line("'<") + let s:l1 = line("'>") + let s:c0 = col("'<") + let s:c1 = col("'>") + normal gv + +endfunction + +function! <Sid>HandleTextObjectMapping(inner, incbelow, vis, range) + call <Sid>TextObject(a:inner, a:incbelow, a:vis, a:range, v:count1) +endfunction diff --git a/vim/plugin/rails.vim b/vim/plugin/rails.vim new file mode 100644 index 0000000..2dd17f7 --- /dev/null +++ b/vim/plugin/rails.vim @@ -0,0 +1,339 @@ +" rails.vim - Detect a rails application +" Author: Tim Pope <http://tpo.pe/> +" GetLatestVimScripts: 1567 1 :AutoInstall: rails.vim + +" Install this file as plugin/rails.vim. 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 + +if exists('g:loaded_rails') || &cp || v:version < 700 + finish +endif +let g:loaded_rails = 1 + +" Utility Functions {{{1 + +function! s:error(str) + echohl ErrorMsg + echomsg a:str + echohl None + let v:errmsg = a:str +endfunction + +function! s:autoload(...) + if !exists("g:autoloaded_rails") && v:version >= 700 + runtime! autoload/rails.vim + endif + if exists("g:autoloaded_rails") + if a:0 + exe a:1 + endif + return 1 + endif + if !exists("g:rails_no_autoload_warning") + let g:rails_no_autoload_warning = 1 + if v:version >= 700 + call s:error("Disabling rails.vim: autoload/rails.vim is missing") + else + call s:error("Disabling rails.vim: Vim version 7 or higher required") + endif + endif + return "" +endfunction + +" }}}1 +" Configuration {{{ + +function! s:SetOptDefault(opt,val) + if !exists("g:".a:opt) + let g:{a:opt} = a:val + endif +endfunction + +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_ctags_arguments","--languages=-javascript") +call s:SetOptDefault("rails_default_file","README") +call s:SetOptDefault("rails_root_url",'http://localhost:3000/') +call s:SetOptDefault("rails_modelines",0) +call s:SetOptDefault("rails_menu",0) +call s:SetOptDefault("rails_gnu_screen",1) +call s:SetOptDefault("rails_history_size",5) +call s:SetOptDefault("rails_generators","controller\ngenerator\nhelper\nintegration_test\nmailer\nmetal\nmigration\nmodel\nobserver\nperformance_test\nplugin\nresource\nscaffold\nscaffold_controller\nsession_migration\nstylesheets") +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 + +" }}}1 +" Detection {{{1 + +function! s:escvar(r) + let r = fnamemodify(a:r,':~') + let r = substitute(r,'\W','\="_".char2nr(submatch(0))."_"','g') + let r = substitute(r,'^\d','_&','') + return r +endfunction + +function! s:Detect(filename) + let fn = substitute(fnamemodify(a:filename,":p"),'\c^file://','','') + let sep = matchstr(fn,'^[^\\/]\{3,\}\zs[\\/]') + if sep != "" + let fn = getcwd().sep.fn + endif + 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\|features\|lib\|log\|public\|script\|spec\|stories\|test\|tmp\|vendor\)\($\|[\/].*$\)?\1?') + endwhile + return 0 +endfunction + +function! s:BufInit(path) + let s:_{s:escvar(a:path)} = 1 + if s:autoload() + return RailsBufInit(a:path) + endif +endfunction + +" }}}1 +" Initialization {{{1 + +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()) | endif | if exists("b:rails_root") | silent doau User BufEnterRails | endif + autocmd FileType netrw if !exists("b:rails_root") | call s:Detect(expand("<afile>:p")) | endif | if exists("b:rails_root") | silent doau User BufEnterRails | endif + autocmd BufEnter * if exists("b:rails_root")|silent doau User BufEnterRails|endif + autocmd BufLeave * if exists("b:rails_root")|silent doau User BufLeaveRails|endif + autocmd Syntax railslog if s:autoload()|call rails#log_syntax()|endif +augroup END + +command! -bar -bang -nargs=* -complete=dir Rails :if s:autoload()|call rails#new_app_command(<bang>0,<f-args>)|endif + +" }}}1 +" abolish.vim support {{{1 + +function! s:function(name) + return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),'')) +endfunction + +augroup railsPluginAbolish + autocmd! + autocmd VimEnter * call s:abolish_setup() +augroup END + +function! s:abolish_setup() + if exists('g:Abolish') && has_key(g:Abolish,'Coercions') + if !has_key(g:Abolish.Coercions,'l') + let g:Abolish.Coercions.l = s:function('s:abolish_l') + endif + if !has_key(g:Abolish.Coercions,'t') + let g:Abolish.Coercions.t = s:function('s:abolish_t') + endif + endif +endfunction + +function! s:abolish_l(word) + let singular = rails#singularize(a:word) + return a:word ==? singular ? rails#pluralize(a:word) : singular +endfunction + +function! s:abolish_t(word) + if a:word =~# '\u' + return rails#pluralize(rails#underscore(a:word)) + else + return rails#singularize(rails#camelize(a:word)) + endif +endfunction + +" }}}1 +" Menus {{{1 + +if !(g:rails_menu && has("menu")) + finish +endif + +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:menucmd(priority) + return 'anoremenu <script> '.(exists("$CREAM") ? 87 : '').s:gsub(g:rails_installed_menu,'[^.]','').'.'.a:priority.' ' +endfunction + +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 + let dots = s:gsub(g:rails_installed_menu,'[^.]','') + let menucmd = s:menucmd(200) + if exists("$CREAM") + 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 + exe menucmd.g:rails_installed_menu.'.-PSep- :' + 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 :Rcontroller application<CR>' + exe menucmd.g:rails_installed_menu.'.&Other\ files.Application\ &Helper :Rhelper application<CR>' + exe menucmd.g:rails_installed_menu.'.&Other\ files.Application\ &Javascript :Rjavascript application<CR>' + exe menucmd.g:rails_installed_menu.'.&Other\ files.Application\ &Layout :Rlayout application<CR>' + exe menucmd.g:rails_installed_menu.'.&Other\ files.Application\ &README :R doc/README_FOR_APP<CR>' + exe menucmd.g:rails_installed_menu.'.&Other\ files.&Environment :Renvironment<CR>' + exe menucmd.g:rails_installed_menu.'.&Other\ files.&Database\ Configuration :R config/database.yml<CR>' + exe menucmd.g:rails_installed_menu.'.&Other\ files.Database\ &Schema :Rmigration 0<CR>' + exe menucmd.g:rails_installed_menu.'.&Other\ files.R&outes :Rinitializer<CR>' + exe menucmd.g:rails_installed_menu.'.&Other\ files.&Test\ Helper :Rintegrationtest<CR>' + exe menucmd.g:rails_installed_menu.'.-FSep- :' + exe menucmd.g:rails_installed_menu.'.Ra&ke\ :Rake :Rake<CR>' + let menucmd = substitute(menucmd,'200 $','500 ','') + 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 substitute(menucmd,'<script>','<script> <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\ :Rscript :Rscript console<CR>' + exe menucmd.g:rails_installed_menu.'.&Preview\ :Rpreview :Rpreview<CR>' + exe menucmd.g:rails_installed_menu.'.&Log\ file\ :Rlog :Rlog<CR>' + exe substitute(s:sub(menucmd,'anoremenu','vnoremenu'),'<script>','<script> <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 substitute(menucmd,'<script>','<script> <silent>','').g:rails_installed_menu.'.&Help\ :help\ rails :if <SID>autoload()<Bar>exe RailsHelpCommand("")<Bar>endif<CR>' + exe substitute(menucmd,'<script>','<script> <silent>','').g:rails_installed_menu.'.Abo&ut\ :if <SID>autoload()<Bar>exe RailsHelpCommand("about")<Bar>endif<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() + silent! exe 'aunmenu '.menu.'.Rake\ tasks' + silent! exe 'aunmenu '.menu.'.Generate' + silent! exe 'aunmenu '.menu.'.Destroy' + if rails#app().cache.needs('rake_tasks') || empty(rails#app().rake_tasks()) + exe substitute(s:menucmd(300),'<script>','<script> <silent>','').g:rails_installed_menu.'.Rake\ &tasks\ :Rake.Fill\ this\ menu :call rails#app().rake_tasks()<Bar>call <SID>menuBufLeave()<Bar>call <SID>menuBufEnter()<CR>' + else + let i = 0 + while i < len(rails#app().rake_tasks()) + let task = rails#app().rake_tasks()[i] + exe s:menucmd(300).g:rails_installed_menu.'.Rake\ &tasks\ :Rake.'.s:sub(task,':',':.').' :Rake '.task.'<CR>' + let i += 1 + endwhile + endif + let i = 0 + let menucmd = substitute(s:menucmd(400),'<script>','<script> <silent>','').g:rails_installed_menu + while i < len(rails#app().generators()) + let generator = rails#app().generators()[i] + exe menucmd.'.&Generate\ :Rgen.'.s:gsub(generator,'_','\\ ').' :call <SID>menuprompt("Rgenerate '.generator.'","Arguments for script/generate '.generator.': ")<CR>' + exe menucmd.'.&Destroy\ :Rdestroy.'.s:gsub(generator,'_','\\ ').' :call <SID>menuprompt("Rdestroy '.generator.'","Arguments for script/destroy '.generator.': ")<CR>' + let i += 1 + endwhile + 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' + silent! exe 'aunmenu '.menu.'.Rake\ tasks' + silent! exe 'aunmenu '.menu.'.Generate' + silent! exe 'aunmenu '.menu.'.Destroy' + exe s:menucmd(300).g:rails_installed_menu.'.Rake\ tasks\ :Rake.-TSep- :' + exe s:menucmd(400).g:rails_installed_menu.'.&Generate\ :Rgen.-GSep- :' + exe s:menucmd(400).g:rails_installed_menu.'.&Destroy\ :Rdestroy.-DSep- :' + endif +endfunction + +function! s:menuprompt(vimcmd,prompt) + let res = inputdialog(a:prompt,'','!!!') + if res == '!!!' + return "" + endif + exe a:vimcmd." ".res +endfunction + +call s:CreateMenus() + +augroup railsPluginMenu + autocmd! + autocmd User BufEnterRails call s:menuBufEnter() + autocmd User BufLeaveRails call s:menuBufLeave() + " g:RAILS_HISTORY hasn't been set when s:InitPlugin() is called. + autocmd VimEnter * call s:ProjectMenu() +augroup END + +" }}}1 +" vim:set sw=2 sts=2: diff --git a/vim/plugin/searchfold_0.9.vim b/vim/plugin/searchfold_0.9.vim new file mode 100644 index 0000000..1fb9ba8 --- /dev/null +++ b/vim/plugin/searchfold_0.9.vim @@ -0,0 +1,319 @@ +" Vim global plugin -- create folds based on last search pattern +" General: {{{1 +" File: searchfold.vim +" Created: 2008 Jan 19 +" Last Change: 2011 May 24 +" Rev Days: 18 +" Author: Andy Wokula <anwoku@yahoo.de> +" Credits: Antonio Colombo's f.vim (Vimscript #318, 2005 May 10) +" Vim Version: Vim 7.0 +" Version: 0.9 + +" Description: +" Provide mappings to fold away lines not matching the last search pattern +" and to restore old fold settings afterwards. Uses manual fold method, +" which allows for nested folds to hide or show more context. Doesn't +" preserve the user's manual folds. + +" Usage: +" <Leader>z fold away lines not matching the last search pattern. +" +" With [count], change the initial foldlevel to ([count] minus +" one). The setting will be stored in g:searchfold_foldlevel +" and will be reused when [count] is omitted. +" +" <Leader>iz fold away lines that do match the last search pattern +" (inverse folding), also with [count]. +" +" <Leader>Z restore the previous fold settings. +" +" Minor Extra: If already in restored state, show a dialog to +" revert all involved local fold options to the global +" defaults. The "(s)how" just prints info. + +" Customization: +" :let g:searchfold_maxdepth = 7 +" (number) +" maximum fold depth +" +" :let g:searchfold_usestep = 1 +" (boolean) +" Controls how folds are organized: If 1 (default), each "zr" +" (after "\z") unfolds the same amount of lines above and +" below a match. If 0, only one more line is unfolded above a +" match. This applies for next "\z" or "\iz". +" +" :let g:searchfold_postZ_do_zv = 1 +" (boolean) +" If 1, execute "zv" (view cursor line) after <Leader>Z. +" +" :let g:searchfold_foldlevel = 0 +" (number) +" Initial 'foldlevel' to set for <Leader>z and <Leader>iz. +" +" :let g:searchfold_do_maps = 1 +" (boolean) +" Whether to map the default keys or not. +" +" Hint: For boolean options, 1 actually means any non-zero number. + +" Related: Vimscript #158 (foldutil.vim) ... still to be checked out +" http://www.noah.org/wiki/Vim#Folding +" Vimscript #2302 (foldsearch.vim) +" Vimscript #578 (allfold.tar.gz) +" +" Changes: +" v0.9 redraw removed, plug map renamed, comments (usestep ...) +" v0.8 added inverse folding (<Leader>iz), g:searchfold_foldlevel, +" count for <Leader>z, <Plug> mappings, disabled F(), (fixes) +" v0.7 b:searchfold fallback, s:foldtext check +" v0.6 (after v0.4) added customization vars (usestep, maxdepth, Zpost) +" reverting global fold settings adds to cmd-history +" v0.4 decreasing fold step always 1 +" maxdepth 7 (before: 6) +" v0.3 (after v0.1) added a modified F() from f.vim +" functions now with return values +" v0.2 (skipped) + +" Init Folklore: {{{1 +if exists("loaded_searchfold") + finish +endif +let loaded_searchfold = 1 + +if v:version<700 + echo "Searchfold: you need at least Vim 7.0" + finish +endif + +" Customization: {{{1 +if !exists("g:searchfold_maxdepth") + let g:searchfold_maxdepth = 7 +endif +if !exists("g:searchfold_usestep") + let g:searchfold_usestep = 1 +endif +if !exists("g:searchfold_postZ_do_zv") + let g:searchfold_postZ_do_zv = 1 +endif +if !exists("g:searchfold_foldlevel") + let g:searchfold_foldlevel = 0 +endif +if !exists("g:searchfold_do_maps") + let g:searchfold_do_maps = 1 +endif + +" s:variables {{{1 +let s:foldtext = "(v:folddashes.'').((v:foldend)-(v:foldstart)+(1))" +" use unique notation of 'foldtext' to identify active searchfold in a +" window + +func! s:FoldNested(from, to) " {{{1 + " create one fold from line a:from to line a:to, with more nested folds + " return 1 if folds were created + " return 0 if from > to + let nlines = a:to - a:from + if nlines < 0 + return 0 + elseif nlines < 3 + " range of 1 line possible + exec a:from.",".a:to. "fold" + return 1 + endif + + " calc folds, start with most outer fold + " - range of inner folds at least 2 lines (from<to) + " - limit nesting (depth) + " - snap folds at start and end of file + " - at greater "depth" (here depth->0), don't create folds with few + " lines only (check to-from>step) + if g:searchfold_maxdepth < 1 || g:searchfold_maxdepth > 12 + let g:searchfold_maxdepth = 7 + endif + let depth = g:searchfold_maxdepth + let step = 1 " decstep:'' + let step1 = 1 " (const) decstep:'1' + let from = a:from + let to = a:to + " let decstep = exists("g:searchfold_usestep") && g:searchfold_usestep ? "" : "1" + let decstep = g:searchfold_usestep ? "" : "1" + let foldranges = [] + let lined = line("$") + while depth>0 && from<to && to-from>step + call insert(foldranges, from.",".to) + let from += from>1 ? step : 0 + " let to -= to<lined ? 1 : 0 + let to -= to<lined ? step{decstep} : 0 + let step += step " arbitrary + let depth -= 1 + endwhile + + " create folds, start with most inner fold + for range in foldranges + exec range. "fold" + endfor + + return 1 +endfunc + +func! s:CreateFolds(inverse) " {{{1 + " create search folds for the whole buffer based on last search pattern + let sav_cur = getpos(".") + + let matches = [] " list of lnums + if !a:inverse + global//call add(matches, line(".")) + else + vglobal//call add(matches, line(".")) + endif + + let nmatches = len(matches) + if nmatches > 0 + call s:FoldNested(1, matches[0]-1) + let imax = nmatches - 1 + let i = 0 + while i < imax + if matches[i]+1 < matches[i+1] + call s:FoldNested(matches[i]+1, matches[i+1]-1) + endif + let i += 1 + endwhile + call s:FoldNested(matches[imax]+1, line("$")) + endif + + let &l:foldlevel = g:searchfold_foldlevel + call cursor(sav_cur[1:]) + + return nmatches +endfunc + +func! <sid>SearchFoldEnable(inverse) "{{{1 + " return number of matches + if !search("", "n") + " last search pattern not found, do nothing + return 0 + endif + if (!exists("w:searchfold") || w:searchfold.bufnr != bufnr("")) + \ && &fdt != s:foldtext + " remember settings + let w:searchfold = { "bufnr": bufnr(""), + \ "fdm": &fdm, + \ "fdl": &fdl, + \ "fdt": &fdt, + \ "fen": &fen, + \ "fml": &fml } + " else: do not remember settings if already enabled + endif + setlocal foldmethod=manual + setlocal foldlevel=0 + let &l:foldtext=s:foldtext + setlocal foldenable + setlocal foldminlines=0 + normal! zE + if exists("w:searchfold") + let b:searchfold = w:searchfold + endif + return s:CreateFolds(a:inverse) +endfunc +func! SearchFoldRestore() "{{{1 + " turn off + if exists("w:searchfold") && w:searchfold.bufnr == bufnr("") + " restore settings; var has the right settings if exists, but + " doesn't survive window split or win close/restore + let &l:fdm = w:searchfold.fdm + let &l:fdl = w:searchfold.fdl + let &l:fdt = w:searchfold.fdt + let &l:fen = w:searchfold.fen + let &l:fml = w:searchfold.fml + if &fdm == "manual" + " remove all search folds (old folds are lost anyway): + normal! zE + endif + unlet w:searchfold + elseif exists("b:searchfold") && &fdt == s:foldtext + " fallback only, may have wrong settings if overwritten + let &l:fdm = b:searchfold.fdm + let &l:fdl = b:searchfold.fdl + let &l:fdt = b:searchfold.fdt + let &l:fen = b:searchfold.fen + let &l:fml = b:searchfold.fml + if &fdm == "manual" + normal! zE + endif + else + let choice = input("Revert to global fold settings? (y/[n]/(s)how):")[0] + let setargs = 'fdm< fdl< fdt< fen< fml<' + if choice == "y" + let cmd = 'setlocal '. setargs + echo ':'. cmd + exec cmd + " call histadd(':', cmd) + elseif choice == "s" + let setargs = tr(setargs, "<","?") + let cmd = 'setglobal '. setargs + echo ':'. cmd + exec cmd + let cmd = 'setlocal '. setargs + echo ':'. cmd + exec cmd + endif + return + endif + if g:searchfold_postZ_do_zv + normal! zv + endif +endfunc + +"" func! F() range "{{{1 +" " commented out 2010 Jun 01 +" " range arg: ignore range given by accident +" let pat = input("Which regexp? ", @/) +" if pat == "" +" if exists("w:searchfold") +" call SearchFoldRestore() +" endif +" return +" endif +" let @/ = pat +" call histadd("search", @/) +" call SearchFold() +" endfunc + +" :call F() only for backwards compatibility + +func! SearchFold(...) "{{{1 + let inverse = a:0>=1 && a:1 + if v:count >= 1 + let g:searchfold_foldlevel = v:count - 1 + endif + let nmatches = <sid>SearchFoldEnable(inverse) + " at most one match per line counted + if nmatches == 0 + echohl ErrorMsg + echomsg "Searchfold: Pattern not found:" @/ + echohl none + elseif nmatches == line("$") + echomsg "Searchfold: Pattern found in every line:" @/ + elseif nmatches == 1 + echo "Searchfold: 1 line found" + else + echo "Searchfold:" nmatches "lines found" + endif + " 2011 Feb 06 commented out: + " let &hls = &hls + " redraw +endfunc + +" Mappings: {{{1 +nn <silent> <Plug>SearchFoldNormal :<C-U>call SearchFold(0)<CR> +nn <silent> <Plug>SearchFoldInverse :<C-U>call SearchFold(1)<CR> +nn <silent> <Plug>SearchFoldRestore :<C-U>call SearchFoldRestore()<CR> + +if g:searchfold_do_maps + nmap <Leader>z <Plug>SearchFoldNormal + nmap <Leader>iz <Plug>SearchFoldInverse + nmap <Leader>Z <Plug>SearchFoldRestore +endif + +" Modeline: {{{1 +" vim:set fdm=marker ts=8 sts=4 sw=4 noet: diff --git a/vim/plugin/showmarks.vim b/vim/plugin/showmarks.vim new file mode 100644 index 0000000..c6931c2 --- /dev/null +++ b/vim/plugin/showmarks.vim @@ -0,0 +1,507 @@ +" ============================================================================== +" Name: ShowMarks +" Description: Visually displays the location of marks. +" Authors: Anthony Kruize <trandor@labyrinth.net.au> +" Michael Geddes <michaelrgeddes@optushome.com.au> +" Version: 2.2 +" Modified: 17 August 2004 +" License: Released into the public domain. +" ChangeLog: See :help showmarks-changelog +" +" Usage: Copy this file into the plugins directory so it will be +" automatically sourced. +" +" Default keymappings are: +" <Leader>mt - Toggles ShowMarks on and off. +" <Leader>mo - Turns ShowMarks on, and displays marks. +" <Leader>mh - Clears a mark. +" <Leader>ma - Clears all marks. +" <Leader>mm - Places the next available mark. +" +" Hiding a mark doesn't actually remove it, it simply moves it +" to line 1 and hides it visually. +" +" Configuration: *********************************************************** +" * PLEASE read the included help file(showmarks.txt) for a * +" * more thorough explanation of how to use ShowMarks. * +" *********************************************************** +" The following options can be used to customize the behavior +" of ShowMarks. Simply include them in your vimrc file with +" the desired settings. +" +" showmarks_enable (Default: 1) +" Defines whether ShowMarks is enabled by default. +" Example: let g:showmarks_enable=0 +" showmarks_include (Default: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.'`^<>[]{}()\"") +" Defines all marks, in precedence order (only the highest +" precence will show on lines having more than one mark). +" Can be buffer-specific (set b:showmarks_include) +" showmarks_ignore_type (Default: "hq") +" Defines the buffer types to be ignored. +" Valid types are: +" h - Help p - preview +" q - quickfix r - readonly +" m - non-modifiable +" showmarks_textlower (Default: ">") +" Defines how the mark is to be displayed. +" A maximum of two characters can be displayed. To include +" the mark in the text use a tab(\t) character. A single +" character will display as the mark with the character +" suffixed (same as "\t<character>") +" Examples: +" To display the mark with a > suffixed: +" let g:showmarks_textlower="\t>" +" or +" let g:showmarks_textlower=">" +" To display the mark with a ( prefixed: +" let g:showmarks_textlower="(\t" +" To display two > characters: +" let g:showmarks_textlower=">>" +" showmarks_textupper (Default: ">") +" Same as above but for the marks A-Z. +" Example: let g:showmarks_textupper="**" +" showmarks_textother (Default: ">") +" Same as above but for all other marks. +" Example: let g:showmarks_textother="--" +" showmarks_hlline_lower (Default: 0) +" showmarks_hlline_upper (Default: 0) +" showmarks_hlline_other (Default: 0) +" Defines whether the entire line for a particular mark +" should be highlighted. +" Example: let g:showmarks_hlline_lower=1 +" +" Setting Highlighting Colours +" ShowMarks uses the following highlighting groups: +" ShowMarksHLl - For marks a-z +" ShowMarksHLu - For marks A-Z +" ShowMarksHLo - For all other marks +" ShowMarksHLm - For multiple marks on the same line. +" (Highest precendece mark is shown) +" +" By default they are set to a bold blue on light blue. +" Defining a highlight for each of these groups will +" override the default highlighting. +" See the VIM help for more information about highlighting. +" ============================================================================== + +" Check if we should continue loading +if exists( "loaded_showmarks" ) + finish +endif +let loaded_showmarks = 1 + +" Bail if Vim isn't compiled with signs support. +if has( "signs" ) == 0 + echohl ErrorMsg + echo "ShowMarks requires Vim to have +signs support." + echohl None + finish +endif + +" Options: Set up some nice defaults +if !exists('g:showmarks_enable' ) | let g:showmarks_enable = 1 | endif +if !exists('g:showmarks_textlower' ) | let g:showmarks_textlower = ">" | endif +if !exists('g:showmarks_textupper' ) | let g:showmarks_textupper = ">" | endif +if !exists('g:showmarks_textother' ) | let g:showmarks_textother = ">" | endif +if !exists('g:showmarks_ignore_type' ) | let g:showmarks_ignore_type = "hq" | endif +if !exists('g:showmarks_ignore_name' ) | let g:showmarks_ignore_name = "" | endif +if !exists('g:showmarks_hlline_lower') | let g:showmarks_hlline_lower = "0" | endif +if !exists('g:showmarks_hlline_upper') | let g:showmarks_hlline_upper = "0" | endif +if !exists('g:showmarks_hlline_other') | let g:showmarks_hlline_other = "0" | endif + +" This is the default, and used in ShowMarksSetup to set up info for any +" possible mark (not just those specified in the possibly user-supplied list +" of marks to show -- it can be changed on-the-fly). +let s:all_marks = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.'`^<>[]{}()\"" + +" Commands +com! -nargs=0 ShowMarksToggle :call <sid>ShowMarksToggle() +com! -nargs=0 ShowMarksOn :call <sid>ShowMarksOn() +com! -nargs=0 ShowMarksClearMark :call <sid>ShowMarksClearMark() +com! -nargs=0 ShowMarksClearAll :call <sid>ShowMarksClearAll() +com! -nargs=0 ShowMarksPlaceMark :call <sid>ShowMarksPlaceMark() + +" Mappings (NOTE: Leave the '|'s immediately following the '<cr>' so the mapping does not contain any trailing spaces!) +if !hasmapto( '<Plug>ShowmarksShowMarksToggle' ) | map <silent> <unique> <leader>mt :ShowMarksToggle<cr>| endif +if !hasmapto( '<Plug>ShowmarksShowMarksOn' ) | map <silent> <unique> <leader>mo :ShowMarksOn<cr>| endif +if !hasmapto( '<Plug>ShowmarksClearMark' ) | map <silent> <unique> <leader>mh :ShowMarksClearMark<cr>| endif +if !hasmapto( '<Plug>ShowmarksClearAll' ) | map <silent> <unique> <leader>ma :ShowMarksClearAll<cr>| endif +if !hasmapto( '<Plug>ShowmarksPlaceMark' ) | map <silent> <unique> <leader>mm :ShowMarksPlaceMark<cr>| endif +noremap <unique> <script> \sm m +noremap <silent> m :exe 'norm \sm'.nr2char(getchar())<bar>call <sid>ShowMarks()<CR> + +" AutoCommands: Only if ShowMarks is enabled +if g:showmarks_enable == 1 + aug ShowMarks + au! + autocmd CursorHold * call s:ShowMarks() + aug END +endif + +" Highlighting: Setup some nice colours to show the mark positions. +hi default ShowMarksHLl ctermfg=darkblue ctermbg=blue cterm=bold guifg=blue guibg=lightblue gui=bold +hi default ShowMarksHLu ctermfg=darkblue ctermbg=blue cterm=bold guifg=blue guibg=lightblue gui=bold +hi default ShowMarksHLo ctermfg=darkblue ctermbg=blue cterm=bold guifg=blue guibg=lightblue gui=bold +hi default ShowMarksHLm ctermfg=darkblue ctermbg=blue cterm=bold guifg=blue guibg=lightblue gui=bold + +" Function: IncludeMarks() +" Description: This function returns the list of marks (in priority order) to +" show in this buffer. Each buffer, if not already set, inherits the global +" setting; if the global include marks have not been set; that is set to the +" default value. +fun! s:IncludeMarks() + if exists('b:showmarks_include') && exists('b:showmarks_previous_include') && b:showmarks_include != b:showmarks_previous_include + " The user changed the marks to include; hide all marks; change the + " included mark list, then show all marks. Prevent infinite + " recursion during this switch. + if exists('s:use_previous_include') + " Recursive call from ShowMarksHideAll() + return b:showmarks_previous_include + elseif exists('s:use_new_include') + " Recursive call from ShowMarks() + return b:showmarks_include + else + let s:use_previous_include = 1 + call <sid>ShowMarksHideAll() + unlet s:use_previous_include + let s:use_new_include = 1 + call <sid>ShowMarks() + unlet s:use_new_include + endif + endif + + if !exists('g:showmarks_include') + let g:showmarks_include = s:all_marks + endif + if !exists('b:showmarks_include') + let b:showmarks_include = g:showmarks_include + endif + + " Save this include setting so we can detect if it was changed. + let b:showmarks_previous_include = b:showmarks_include + + return b:showmarks_include +endf + +" Function: NameOfMark() +" Paramaters: mark - Specifies the mark to find the name of. +" Description: Convert marks that cannot be used as part of a variable name to +" something that can be. i.e. We cannot use [ as a variable-name suffix (as +" in 'placed_['; this function will return something like 63, so the variable +" will be something like 'placed_63'). +" 10 is added to the mark's index to avoid colliding with the numeric marks +" 0-9 (since a non-word mark could be listed in showmarks_include in the +" first 10 characters if the user overrides the default). +" Returns: The name of the requested mark. +fun! s:NameOfMark(mark) + let name = a:mark + if a:mark =~# '\W' + let name = stridx(s:all_marks, a:mark) + 10 + endif + return name +endf + +" Function: VerifyText() +" Paramaters: which - Specifies the variable to verify. +" Description: Verify the validity of a showmarks_text{upper,lower,other} setup variable. +" Default to ">" if it is found to be invalid. +fun! s:VerifyText(which) + if strlen(g:showmarks_text{a:which}) == 0 || strlen(g:showmarks_text{a:which}) > 2 + echohl ErrorMsg + echo "ShowMarks: text".a:which." must contain only 1 or 2 characters." + echohl None + let g:showmarks_text{a:which}=">" + endif +endf + +" Function: ShowMarksSetup() +" Description: This function sets up the sign definitions for each mark. +" It uses the showmarks_textlower, showmarks_textupper and showmarks_textother +" variables to determine how to draw the mark. +fun! s:ShowMarksSetup() + " Make sure the textlower, textupper, and textother options are valid. + call s:VerifyText('lower') + call s:VerifyText('upper') + call s:VerifyText('other') + + let n = 0 + let s:maxmarks = strlen(s:all_marks) + while n < s:maxmarks + let c = strpart(s:all_marks, n, 1) + let nm = s:NameOfMark(c) + let text = '>'.c + let lhltext = '' + if c =~# '[a-z]' + if strlen(g:showmarks_textlower) == 1 + let text=c.g:showmarks_textlower + elseif strlen(g:showmarks_textlower) == 2 + let t1 = strpart(g:showmarks_textlower,0,1) + let t2 = strpart(g:showmarks_textlower,1,1) + if t1 == "\t" + let text=c.t2 + elseif t2 == "\t" + let text=t1.c + else + let text=g:showmarks_textlower + endif + endif + let s:ShowMarksDLink{nm} = 'ShowMarksHLl' + if g:showmarks_hlline_lower == 1 + let lhltext = 'linehl='.s:ShowMarksDLink{nm}.nm + endif + elseif c =~# '[A-Z]' + if strlen(g:showmarks_textupper) == 1 + let text=c.g:showmarks_textupper + elseif strlen(g:showmarks_textupper) == 2 + let t1 = strpart(g:showmarks_textupper,0,1) + let t2 = strpart(g:showmarks_textupper,1,1) + if t1 == "\t" + let text=c.t2 + elseif t2 == "\t" + let text=t1.c + else + let text=g:showmarks_textupper + endif + endif + let s:ShowMarksDLink{nm} = 'ShowMarksHLu' + if g:showmarks_hlline_upper == 1 + let lhltext = 'linehl='.s:ShowMarksDLink{nm}.nm + endif + else " Other signs, like ', ., etc. + if strlen(g:showmarks_textother) == 1 + let text=c.g:showmarks_textother + elseif strlen(g:showmarks_textother) == 2 + let t1 = strpart(g:showmarks_textother,0,1) + let t2 = strpart(g:showmarks_textother,1,1) + if t1 == "\t" + let text=c.t2 + elseif t2 == "\t" + let text=t1.c + else + let text=g:showmarks_textother + endif + endif + let s:ShowMarksDLink{nm} = 'ShowMarksHLo' + if g:showmarks_hlline_other == 1 + let lhltext = 'linehl='.s:ShowMarksDLink{nm}.nm + endif + endif + + " Define the sign with a unique highlight which will be linked when placed. + exe 'sign define ShowMark'.nm.' '.lhltext.' text='.text.' texthl='.s:ShowMarksDLink{nm}.nm + let b:ShowMarksLink{nm} = '' + let n = n + 1 + endw +endf + +" Set things up +call s:ShowMarksSetup() + +" Function: ShowMarksOn +" Description: Enable showmarks, and show them now. +fun! s:ShowMarksOn() + if g:showmarks_enable == 0 + call <sid>ShowMarksToggle() + else + call <sid>ShowMarks() + endif +endf + +" Function: ShowMarksToggle() +" Description: This function toggles whether marks are displayed or not. +fun! s:ShowMarksToggle() + if g:showmarks_enable == 0 + let g:showmarks_enable = 1 + call <sid>ShowMarks() + aug ShowMarks + au! + autocmd CursorHold * call s:ShowMarks() + aug END + else + let g:showmarks_enable = 0 + call <sid>ShowMarksHideAll() + aug ShowMarks + au! + autocmd BufEnter * call s:ShowMarksHideAll() + aug END + endif +endf + +" Function: ShowMarks() +" Description: This function runs through all the marks and displays or +" removes signs as appropriate. It is called on the CursorHold autocommand. +" We use the marked_{ln} variables (containing a timestamp) to track what marks +" we've shown (placed) in this call to ShowMarks; to only actually place the +" first mark on any particular line -- this forces only the first mark +" (according to the order of showmarks_include) to be shown (i.e., letters +" take precedence over marks like paragraph and sentence.) +fun! s:ShowMarks() + if g:showmarks_enable == 0 + return + endif + + if ((match(g:showmarks_ignore_type, "[Hh]") > -1) && (&buftype == "help" )) + \ || ((match(g:showmarks_ignore_type, "[Qq]") > -1) && (&buftype == "quickfix")) + \ || ((match(g:showmarks_ignore_type, "[Pp]") > -1) && (&pvw == 1 )) + \ || ((match(g:showmarks_ignore_type, "[Rr]") > -1) && (&readonly == 1 )) + \ || ((match(g:showmarks_ignore_type, "[Mm]") > -1) && (&modifiable == 0 )) + return + endif + + let n = 0 + let s:maxmarks = strlen(s:IncludeMarks()) + while n < s:maxmarks + let c = strpart(s:IncludeMarks(), n, 1) + let nm = s:NameOfMark(c) + let id = n + (s:maxmarks * winbufnr(0)) + let ln = line("'".c) + + if ln == 0 && (exists('b:placed_'.nm) && b:placed_{nm} != ln) + exe 'sign unplace '.id.' buffer='.winbufnr(0) + elseif ln > 1 || c !~ '[a-zA-Z]' + " Have we already placed a mark here in this call to ShowMarks? + if exists('mark_at'.ln) + " Already placed a mark, set the highlight to multiple + if c =~# '[a-zA-Z]' && b:ShowMarksLink{mark_at{ln}} != 'ShowMarksHLm' + let b:ShowMarksLink{mark_at{ln}} = 'ShowMarksHLm' + exe 'hi link '.s:ShowMarksDLink{mark_at{ln}}.mark_at{ln}.' '.b:ShowMarksLink{mark_at{ln}} + endif + else + if !exists('b:ShowMarksLink'.nm) || b:ShowMarksLink{nm} != s:ShowMarksDLink{nm} + let b:ShowMarksLink{nm} = s:ShowMarksDLink{nm} + exe 'hi link '.s:ShowMarksDLink{nm}.nm.' '.b:ShowMarksLink{nm} + endif + let mark_at{ln} = nm + if !exists('b:placed_'.nm) || b:placed_{nm} != ln + exe 'sign unplace '.id.' buffer='.winbufnr(0) + exe 'sign place '.id.' name=ShowMark'.nm.' line='.ln.' buffer='.winbufnr(0) + let b:placed_{nm} = ln + endif + endif + endif + let n = n + 1 + endw +endf + +" Function: ShowMarksClearMark() +" Description: This function hides the mark at the current line. +" It simply moves the mark to line 1 and removes the sign. +" Only marks a-z and A-Z are supported. +fun! s:ShowMarksClearMark() + let ln = line(".") + let n = 0 + let s:maxmarks = strlen(s:IncludeMarks()) + while n < s:maxmarks + let c = strpart(s:IncludeMarks(), n, 1) + if c =~# '[a-zA-Z]' && ln == line("'".c) + let nm = s:NameOfMark(c) + let id = n + (s:maxmarks * winbufnr(0)) + exe 'sign unplace '.id.' buffer='.winbufnr(0) + exe '1 mark '.c + let b:placed_{nm} = 1 + endif + let n = n + 1 + endw +endf + +" Function: ShowMarksClearAll() +" Description: This function clears all marks in the buffer. +" It simply moves the marks to line 1 and removes the signs. +" Only marks a-z and A-Z are supported. +fun! s:ShowMarksClearAll() + let n = 0 + let s:maxmarks = strlen(s:IncludeMarks()) + while n < s:maxmarks + let c = strpart(s:IncludeMarks(), n, 1) + if c =~# '[a-zA-Z]' + let nm = s:NameOfMark(c) + let id = n + (s:maxmarks * winbufnr(0)) + exe 'sign unplace '.id.' buffer='.winbufnr(0) + exe '1 mark '.c + let b:placed_{nm} = 1 + endif + let n = n + 1 + endw +endf + +" Function: ShowMarksHideAll() +" Description: This function hides all marks in the buffer. +" It simply removes the signs. +fun! s:ShowMarksHideAll() + let n = 0 + let s:maxmarks = strlen(s:IncludeMarks()) + while n < s:maxmarks + let c = strpart(s:IncludeMarks(), n, 1) + let nm = s:NameOfMark(c) + if exists('b:placed_'.nm) + let id = n + (s:maxmarks * winbufnr(0)) + exe 'sign unplace '.id.' buffer='.winbufnr(0) + unlet b:placed_{nm} + endif + let n = n + 1 + endw +endf + +" Function: ShowMarksPlaceMark() +" Description: This function will place the next unplaced mark (in priority +" order) to the current location. The idea here is to automate the placement +" of marks so the user doesn't have to remember which marks are placed or not. +" Hidden marks are considered to be unplaced. +" Only marks a-z are supported. +fun! s:ShowMarksPlaceMark() + " Find the first, next, and last [a-z] mark in showmarks_include (i.e. + " priority order), so we know where to "wrap". + let first_alpha_mark = -1 + let last_alpha_mark = -1 + let next_mark = -1 + + if !exists('b:previous_auto_mark') + let b:previous_auto_mark = -1 + endif + + " Find the next unused [a-z] mark (in priority order); if they're all + " used, find the next one after the previously auto-assigned mark. + let n = 0 + let s:maxmarks = strlen(s:IncludeMarks()) + while n < s:maxmarks + let c = strpart(s:IncludeMarks(), n, 1) + if c =~# '[a-z]' + if line("'".c) <= 1 + " Found an unused [a-z] mark; we're done. + let next_mark = n + break + endif + + if first_alpha_mark < 0 + let first_alpha_mark = n + endif + let last_alpha_mark = n + if n > b:previous_auto_mark && next_mark == -1 + let next_mark = n + endif + endif + let n = n + 1 + endw + + if next_mark == -1 && (b:previous_auto_mark == -1 || b:previous_auto_mark == last_alpha_mark) + " Didn't find an unused mark, and haven't placed any auto-chosen marks yet, + " or the previously placed auto-chosen mark was the last alpha mark -- + " use the first alpha mark this time. + let next_mark = first_alpha_mark + endif + + if (next_mark == -1) + echohl WarningMsg + echo 'No marks in [a-z] included! (No "next mark" to choose from)' + echohl None + return + endif + + let c = strpart(s:IncludeMarks(), next_mark, 1) + let b:previous_auto_mark = next_mark + exe 'mark '.c + call <sid>ShowMarks() +endf + +" ----------------------------------------------------------------------------- +" vim:ts=4:sw=4:noet diff --git a/vim/plugin/snipMate.vim b/vim/plugin/snipMate.vim new file mode 100644 index 0000000..ef03b12 --- /dev/null +++ b/vim/plugin/snipMate.vim @@ -0,0 +1,271 @@ +" File: snipMate.vim +" Author: Michael Sanders +" Version: 0.84 +" 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[0] == "\t" || line == '') + let content .= strpart(line, 1)."\n" + continue + elseif inSnip + call MakeSnip(a:ft, trigger, content[:-2], name) + let inSnip = 0 + endif + + if line[:6] == 'snippet' + 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 + +" Reset snippets for filetype. +fun! ResetSnippets(ft) + let ft = a:ft == '' ? '_' : a:ft + for dict in [s:snippets, s:multi_snips, g:did_ft] + if has_key(dict, ft) + unlet dict[ft] + endif + endfor +endf + +" Reset snippets for all filetypes. +fun! ResetAllSnippets() + let s:snippets = {} | let s:multi_snips = {} | let g:did_ft = {} +endf + +" Reload snippets for filetype. +fun! ReloadSnippets(ft) + let ft = a:ft == '' ? '_' : a:ft + call ResetSnippets(ft) + call GetSnippets(g:snippets_dir, ft) +endf + +" Reload snippets for all filetypes. +fun! ReloadAllSnippets() + for ft in keys(g:did_ft) + call ReloadSnippets(ft) + endfor +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(0) | 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 + +fun! BackwardsSnippet() + if exists('g:snipPos') | return snipMate#jumpTabStop(1) | endif + + if exists('g:SuperTabMappingForward') + if g:SuperTabMappingBackward == "<s-tab>" + let SuperTabKey = "\<c-p>" + elseif g:SuperTabMappingForward == "<s-tab>" + let SuperTabKey = "\<c-n>" + endif + endif + if exists('SuperTabKey') + call feedkeys(SuperTabKey) + return '' + endif + return "\<s-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) + if snippet == '' | break | endif + else + if match(word, '\W') == -1 | break | endif + let word = substitute(word, '.\{-}\W', '', '') + endif + endw + if word == '' && a:word != '.' && stridx(a:word, '.') != -1 + let [word, snippet] = s:GetSnippet('.', a:scope) + endif + 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 + +fun! ShowAvailableSnips() + let line = getline('.') + let col = col('.') + let word = matchstr(getline('.'), '\S\+\%'.col.'c') + let words = [word] + if stridx(word, '.') + let words += split(word, '\.', 1) + endif + let matchlen = 0 + let matches = [] + for scope in [bufnr('%')] + split(&ft, '\.') + ['_'] + let triggers = has_key(s:snippets, scope) ? keys(s:snippets[scope]) : [] + if has_key(s:multi_snips, scope) + let triggers += keys(s:multi_snips[scope]) + endif + for trigger in triggers + for word in words + if word == '' + let matches += [trigger] " Show all matches if word is empty + elseif trigger =~ '^'.word + let matches += [trigger] + let len = len(word) + if len > matchlen | let matchlen = len | endif + endif + endfor + endfor + endfor + + " This is to avoid a bug with Vim when using complete(col - matchlen, matches) + " (Issue#46 on the Google Code snipMate issue tracker). + call setline(line('.'), substitute(line, repeat('.', matchlen).'\%'.col.'c', '', '')) + call complete(col, matches) + return '' +endf +" vim:noet:sw=4:ts=4:ft=vim diff --git a/vim/plugin/supertab.vim b/vim/plugin/supertab.vim new file mode 100644 index 0000000..67f8c5c --- /dev/null +++ b/vim/plugin/supertab.vim @@ -0,0 +1,737 @@ +" Author: +" Original: Gergely Kontra <kgergely@mcl.hu> +" Current: Eric Van Dewoestine <ervandew@gmail.com> (as of version 0.4) +" Please direct all correspondence to Eric. +" Version: 1.6 +" GetLatestVimScripts: 1643 1 :AutoInstall: supertab.vim +" +" Description: {{{ +" Use your tab key to do all your completion in insert mode! +" You can cycle forward and backward with the <Tab> and <S-Tab> keys +" Note: you must press <Tab> once to be able to cycle back +" +" http://www.vim.org/scripts/script.php?script_id=1643 +" }}} +" +" License: {{{ +" Copyright (c) 2002 - 2011 +" All rights reserved. +" +" Redistribution and use of this software in source and binary forms, with +" or without modification, are permitted provided that the following +" conditions are met: +" +" * Redistributions of source code must retain the above +" copyright notice, this list of conditions and the +" following disclaimer. +" +" * Redistributions in binary form must reproduce the above +" copyright notice, this list of conditions and the +" following disclaimer in the documentation and/or other +" materials provided with the distribution. +" +" * Neither the name of Gergely Kontra or Eric Van Dewoestine nor the names +" of its contributors may be used to endorse or promote products derived +" from this software without specific prior written permission of Gergely +" Kontra or Eric Van Dewoestine. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +" IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +" THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +" CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +" EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +" PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +" }}} +" +" Testing Info: {{{ +" Running vim + supertab with the absolute bar minimum settings: +" $ vim -u NONE -U NONE -c "set nocp | runtime plugin/supertab.vim" +" }}} + +if v:version < 700 + finish +endif + +if exists('complType') " Integration with other completion functions. + finish +endif + +let s:save_cpo=&cpo +set cpo&vim + +" Global Variables {{{ + + if !exists("g:SuperTabDefaultCompletionType") + let g:SuperTabDefaultCompletionType = "<c-p>" + endif + + if !exists("g:SuperTabContextDefaultCompletionType") + let g:SuperTabContextDefaultCompletionType = "<c-p>" + endif + + if !exists("g:SuperTabCompletionContexts") + let g:SuperTabCompletionContexts = ['s:ContextText'] + endif + + if !exists("g:SuperTabRetainCompletionDuration") + let g:SuperTabRetainCompletionDuration = 'insert' + endif + + if !exists("g:SuperTabNoCompleteBefore") + " retain backwards compatability + if exists("g:SuperTabMidWordCompletion") && !g:SuperTabMidWordCompletion + let g:SuperTabNoCompleteBefore = ['\k'] + else + let g:SuperTabNoCompleteBefore = [] + endif + endif + + if !exists("g:SuperTabNoCompleteAfter") + " retain backwards compatability + if exists("g:SuperTabLeadingSpaceCompletion") && g:SuperTabLeadingSpaceCompletion + let g:SuperTabNoCompleteAfter = [] + else + let g:SuperTabNoCompleteAfter = ['\s'] + endif + endif + + if !exists("g:SuperTabMappingForward") + let g:SuperTabMappingForward = '<tab>' + endif + if !exists("g:SuperTabMappingBackward") + let g:SuperTabMappingBackward = '<s-tab>' + endif + + if !exists("g:SuperTabMappingTabLiteral") + let g:SuperTabMappingTabLiteral = '<c-tab>' + endif + + if !exists("g:SuperTabLongestEnhanced") + let g:SuperTabLongestEnhanced = 0 + endif + + if !exists("g:SuperTabLongestHighlight") + let g:SuperTabLongestHighlight = 0 + endif + + if !exists("g:SuperTabCrMapping") + let g:SuperTabCrMapping = 1 + endif + +" }}} + +" Script Variables {{{ + + " construct the help text. + let s:tabHelp = + \ "Hit <CR> or CTRL-] on the completion type you wish to switch to.\n" . + \ "Use :help ins-completion for more information.\n" . + \ "\n" . + \ "|<c-n>| - Keywords in 'complete' searching down.\n" . + \ "|<c-p>| - Keywords in 'complete' searching up (SuperTab default).\n" . + \ "|<c-x><c-l>| - Whole lines.\n" . + \ "|<c-x><c-n>| - Keywords in current file.\n" . + \ "|<c-x><c-k>| - Keywords in 'dictionary'.\n" . + \ "|<c-x><c-t>| - Keywords in 'thesaurus', thesaurus-style.\n" . + \ "|<c-x><c-i>| - Keywords in the current and included files.\n" . + \ "|<c-x><c-]>| - Tags.\n" . + \ "|<c-x><c-f>| - File names.\n" . + \ "|<c-x><c-d>| - Definitions or macros.\n" . + \ "|<c-x><c-v>| - Vim command-line.\n" . + \ "|<c-x><c-u>| - User defined completion.\n" . + \ "|<c-x><c-o>| - Omni completion.\n" . + \ "|<c-x>s| - Spelling suggestions." + + " set the available completion types and modes. + let s:types = + \ "\<c-e>\<c-y>\<c-l>\<c-n>\<c-k>\<c-t>\<c-i>\<c-]>" . + \ "\<c-f>\<c-d>\<c-v>\<c-n>\<c-p>\<c-u>\<c-o>\<c-n>\<c-p>s" + let s:modes = '/^E/^Y/^L/^N/^K/^T/^I/^]/^F/^D/^V/^P/^U/^O/s' + let s:types = s:types . "np" + let s:modes = s:modes . '/n/p' + +" }}} + +" SuperTabSetDefaultCompletionType(type) {{{ +" Globally available function that users can use to set the default +" completion type for the current buffer, like in an ftplugin. +function! SuperTabSetDefaultCompletionType(type) + " init hack for <c-x><c-v> workaround. + let b:complCommandLine = 0 + + let b:SuperTabDefaultCompletionType = a:type + + " set the current completion type to the default + call SuperTabSetCompletionType(b:SuperTabDefaultCompletionType) +endfunction " }}} + +" SuperTabSetCompletionType(type) {{{ +" Globally available function that users can use to create mappings to quickly +" switch completion modes. Useful when a user wants to restore the default or +" switch to another mode without having to kick off a completion of that type +" or use SuperTabHelp. Note, this function only changes the current +" completion type, not the default, meaning that the default will still be +" restored once the configured retension duration has been met (see +" g:SuperTabRetainCompletionDuration). To change the default for the current +" buffer, use SuperTabDefaultCompletionType(type) instead. Example mapping to +" restore SuperTab default: +" nmap <F6> :call SetSuperTabCompletionType("<c-p>")<cr> +function! SuperTabSetCompletionType(type) + call s:InitBuffer() + exec "let b:complType = \"" . escape(a:type, '<') . "\"" +endfunction " }}} + +" SuperTabAlternateCompletion(type) {{{ +" Function which can be mapped to a key to kick off an alternate completion +" other than the default. For instance, if you have 'context' as the default +" and want to map ctrl+space to issue keyword completion. +" Note: due to the way vim expands ctrl characters in mappings, you cannot +" create the alternate mapping like so: +" imap <c-space> <c-r>=SuperTabAlternateCompletion("<c-p>")<cr> +" instead, you have to use \<lt> to prevent vim from expanding the key +" when creating the mapping. +" gvim: +" imap <c-space> <c-r>=SuperTabAlternateCompletion("\<lt>c-p>")<cr> +" console: +" imap <nul> <c-r>=SuperTabAlternateCompletion("\<lt>c-p>")<cr> +function! SuperTabAlternateCompletion(type) + call SuperTabSetCompletionType(a:type) + " end any current completion before attempting to start the new one. + " use feedkeys to prevent possible remapping of <c-e> from causing issues. + "call feedkeys("\<c-e>", 'n') + " ^ since we can't detect completion mode vs regular insert mode, we force + " vim into keyword completion mode and end that mode to prevent the regular + " insert behavior of <c-e> from occurring. + call feedkeys("\<c-x>\<c-p>\<c-e>", 'n') + call feedkeys(b:complType, 'n') + return '' +endfunction " }}} + +" SuperTabLongestHighlight(dir) {{{ +" When longest highlight is enabled, this function is used to do the actual +" selection of the completion popup entry. +function! SuperTabLongestHighlight(dir) + if !pumvisible() + return '' + endif + return a:dir == -1 ? "\<up>" : "\<down>" +endfunction " }}} + +" s:Init {{{ +" Global initilization when supertab is loaded. +function! s:Init() + " Setup mechanism to restore original completion type upon leaving insert + " mode if configured to do so + if g:SuperTabRetainCompletionDuration == 'insert' + augroup supertab_retain + autocmd! + autocmd InsertLeave * call s:SetDefaultCompletionType() + augroup END + endif +endfunction " }}} + +" s:InitBuffer {{{ +" Per buffer initilization. +function! s:InitBuffer() + if exists('b:SuperTabNoCompleteBefore') + return + endif + + let b:complReset = 0 + let b:complTypeManual = !exists('b:complTypeManual') ? '' : b:complTypeManual + let b:complTypeContext = '' + + " init hack for <c-x><c-v> workaround. + let b:complCommandLine = 0 + + if !exists('b:SuperTabNoCompleteBefore') + let b:SuperTabNoCompleteBefore = g:SuperTabNoCompleteBefore + endif + if !exists('b:SuperTabNoCompleteAfter') + let b:SuperTabNoCompleteAfter = g:SuperTabNoCompleteAfter + endif + + let b:SuperTabDefaultCompletionType = g:SuperTabDefaultCompletionType + + " set the current completion type to the default + call SuperTabSetCompletionType(b:SuperTabDefaultCompletionType) +endfunction " }}} + +" s:ManualCompletionEnter() {{{ +" Handles manual entrance into completion mode. +function! s:ManualCompletionEnter() + if &smd + echo '' | echohl ModeMsg | echo '-- ^X++ mode (' . s:modes . ')' | echohl None + endif + let complType = nr2char(getchar()) + if stridx(s:types, complType) != -1 + if stridx("\<c-e>\<c-y>", complType) != -1 " no memory, just scroll... + return "\<c-x>" . complType + elseif stridx('np', complType) != -1 + let complType = nr2char(char2nr(complType) - 96) + else + let complType = "\<c-x>" . complType + endif + + let b:complTypeManual = complType + + if index(['insert', 'session'], g:SuperTabRetainCompletionDuration) != -1 + let b:complType = complType + endif + + " Hack to workaround bug when invoking command line completion via <c-r>= + if complType == "\<c-x>\<c-v>" + return s:CommandLineCompletion() + endif + + " optionally enable enhanced longest completion + if g:SuperTabLongestEnhanced && &completeopt =~ 'longest' + call s:EnableLongestEnhancement() + endif + + if g:SuperTabLongestHighlight && + \ &completeopt =~ 'longest' && + \ &completeopt =~ 'menu' && + \ !pumvisible() + let dir = (complType == "\<c-x>\<c-p>") ? -1 : 1 + call feedkeys("\<c-r>=SuperTabLongestHighlight(" . dir . ")\<cr>", 'n') + endif + + return complType + endif + + echohl "Unknown mode" + return complType +endfunction " }}} + +" s:SetCompletionType() {{{ +" Sets the completion type based on what the user has chosen from the help +" buffer. +function! s:SetCompletionType() + let chosen = substitute(getline('.'), '.*|\(.*\)|.*', '\1', '') + if chosen != getline('.') + let winnr = b:winnr + close + exec winnr . 'winc w' + call SuperTabSetCompletionType(chosen) + endif +endfunction " }}} + +" s:SetDefaultCompletionType() {{{ +function! s:SetDefaultCompletionType() + if exists('b:SuperTabDefaultCompletionType') && + \ (!exists('b:complCommandLine') || !b:complCommandLine) + call SuperTabSetCompletionType(b:SuperTabDefaultCompletionType) + endif +endfunction " }}} + +" s:SuperTab(command) {{{ +" Used to perform proper cycle navigation as the user requests the next or +" previous entry in a completion list, and determines whether or not to simply +" retain the normal usage of <tab> based on the cursor position. +function! s:SuperTab(command) + if exists('b:SuperTabDisabled') && b:SuperTabDisabled + return "\<tab>" + endif + + call s:InitBuffer() + + if s:WillComplete() + " optionally enable enhanced longest completion + if g:SuperTabLongestEnhanced && &completeopt =~ 'longest' + call s:EnableLongestEnhancement() + endif + + if !pumvisible() + let b:complTypeManual = '' + endif + + " exception: if in <c-p> mode, then <c-n> should move up the list, and + " <c-p> down the list. + if a:command == 'p' && !b:complReset && + \ (b:complType == "\<c-p>" || + \ (b:complType == 'context' && + \ b:complTypeManual == '' && + \ b:complTypeContext == "\<c-p>")) + return "\<c-n>" + + elseif a:command == 'p' && !b:complReset && + \ (b:complType == "\<c-n>" || + \ (b:complType == 'context' && + \ b:complTypeManual == '' && + \ b:complTypeContext == "\<c-n>")) + return "\<c-p>" + + " already in completion mode and not resetting for longest enhancement, so + " just scroll to next/previous + elseif pumvisible() && !b:complReset + let type = b:complType == 'context' ? b:complTypeContext : b:complType + if a:command == 'n' + return type == "\<c-p>" ? "\<c-p>" : "\<c-n>" + endif + return type == "\<c-p>" ? "\<c-n>" : "\<c-p>" + endif + + " handle 'context' completion. + if b:complType == 'context' + let complType = s:ContextCompletion() + if complType == '' + exec "let complType = \"" . + \ escape(g:SuperTabContextDefaultCompletionType, '<') . "\"" + endif + let b:complTypeContext = complType + + " Hack to workaround bug when invoking command line completion via <c-r>= + elseif b:complType == "\<c-x>\<c-v>" + let complType = s:CommandLineCompletion() + else + let complType = b:complType + endif + + " highlight first result if longest enabled + if g:SuperTabLongestHighlight && + \ &completeopt =~ 'longest' && + \ &completeopt =~ 'menu' && + \ (!pumvisible() || b:complReset) + let dir = (complType == "\<c-p>") ? -1 : 1 + call feedkeys("\<c-r>=SuperTabLongestHighlight(" . dir . ")\<cr>", 'n') + endif + + if b:complReset + let b:complReset = 0 + " not an accurate condition for everyone, but better than sending <c-e> + " at the wrong time. + if pumvisible() + return "\<c-e>" . complType + endif + endif + + return complType + endif + + return "\<tab>" +endfunction " }}} + +" s:SuperTabHelp() {{{ +" Opens a help window where the user can choose a completion type to enter. +function! s:SuperTabHelp() + let winnr = winnr() + if bufwinnr("SuperTabHelp") == -1 + botright split SuperTabHelp + + setlocal noswapfile + setlocal buftype=nowrite + setlocal bufhidden=delete + + let saved = @" + let @" = s:tabHelp + silent put + call cursor(1, 1) + silent 1,delete + call cursor(4, 1) + let @" = saved + exec "resize " . line('$') + + syntax match Special "|.\{-}|" + + setlocal readonly + setlocal nomodifiable + + nmap <silent> <buffer> <cr> :call <SID>SetCompletionType()<cr> + nmap <silent> <buffer> <c-]> :call <SID>SetCompletionType()<cr> + else + exec bufwinnr("SuperTabHelp") . "winc w" + endif + let b:winnr = winnr +endfunction " }}} + +" s:WillComplete() {{{ +" Determines if completion should be kicked off at the current location. +function! s:WillComplete() + if pumvisible() + return 1 + endif + + let line = getline('.') + let cnum = col('.') + + " Start of line. + if line =~ '^\s*\%' . cnum . 'c' + return 0 + endif + + " honor SuperTabNoCompleteAfter + let pre = line[:cnum - 2] + for pattern in b:SuperTabNoCompleteAfter + if pre =~ pattern . '$' + return 0 + endif + endfor + + " honor SuperTabNoCompleteBefore + " Within a word, but user does not have mid word completion enabled. + let post = line[cnum - 1:] + for pattern in b:SuperTabNoCompleteBefore + if post =~ '^' . pattern + return 0 + endif + endfor + + return 1 +endfunction " }}} + +" s:EnableLongestEnhancement() {{{ +function! s:EnableLongestEnhancement() + augroup supertab_reset + autocmd! + autocmd InsertLeave,CursorMovedI <buffer> + \ call s:ReleaseKeyPresses() | autocmd! supertab_reset + augroup END + call s:CaptureKeyPresses() +endfunction " }}} + +" s:CompletionReset(char) {{{ +function! s:CompletionReset(char) + let b:complReset = 1 + return a:char +endfunction " }}} + +" s:CaptureKeyPresses() {{{ +function! s:CaptureKeyPresses() + if !exists('b:capturing') || !b:capturing + let b:capturing = 1 + " save any previous mappings + " TODO: capture additional info provided by vim 7.3.032 and up. + let b:captured = { + \ '<bs>': maparg('<bs>', 'i'), + \ '<c-h>': maparg('<c-h>', 'i'), + \ } + " TODO: use &keyword to get an accurate list of chars to map + for c in split('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_', '.\zs') + exec 'imap <buffer> ' . c . ' <c-r>=<SID>CompletionReset("' . c . '")<cr>' + endfor + imap <buffer> <bs> <c-r>=<SID>CompletionReset("\<lt>bs>")<cr> + imap <buffer> <c-h> <c-r>=<SID>CompletionReset("\<lt>c-h>")<cr> + endif +endfunction " }}} + +" s:ReleaseKeyPresses() {{{ +function! s:ReleaseKeyPresses() + if exists('b:capturing') && b:capturing + let b:capturing = 0 + for c in split('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_', '.\zs') + exec 'iunmap <buffer> ' . c + endfor + + iunmap <buffer> <bs> + iunmap <buffer> <c-h> + + " restore any previous mappings + for [key, rhs] in items(b:captured) + if rhs != '' + let args = substitute(rhs, '.*\(".\{-}"\).*', '\1', '') + if args != rhs + let args = substitute(args, '<', '<lt>', 'g') + let expr = substitute(rhs, '\(.*\)".\{-}"\(.*\)', '\1%s\2', '') + let rhs = printf(expr, args) + endif + exec printf("imap <silent> %s %s", key, rhs) + endif + endfor + unlet b:captured + + if mode() == 'i' && &completeopt =~ 'menu' + " force full exit from completion mode (don't exit insert mode since + " that will break repeating with '.') + call feedkeys("\<space>\<bs>", 'n') + endif + endif +endfunction " }}} + +" s:CommandLineCompletion() {{{ +" Hack needed to account for apparent bug in vim command line mode completion +" when invoked via <c-r>= +function! s:CommandLineCompletion() + " This hack will trigger InsertLeave which will then invoke + " s:SetDefaultCompletionType. To prevent default completion from being + " restored prematurely, set an internal flag for s:SetDefaultCompletionType + " to check for. + let b:complCommandLine = 1 + return "\<c-\>\<c-o>:call feedkeys('\<c-x>\<c-v>\<c-v>', 'n') | " . + \ "let b:complCommandLine = 0\<cr>" +endfunction " }}} + +" s:ContextCompletion() {{{ +function! s:ContextCompletion() + let contexts = exists('b:SuperTabCompletionContexts') ? + \ b:SuperTabCompletionContexts : g:SuperTabCompletionContexts + + for context in contexts + try + let Context = function(context) + let complType = Context() + unlet Context + if type(complType) == 1 && complType != '' + return complType + endif + catch /E700/ + echohl Error + echom 'supertab: no context function "' . context . '" found.' + echohl None + endtry + endfor + return '' +endfunction " }}} + +" s:ContextDiscover() {{{ +function! s:ContextDiscover() + let discovery = exists('g:SuperTabContextDiscoverDiscovery') ? + \ g:SuperTabContextDiscoverDiscovery : [] + + " loop through discovery list to find the default + if !empty(discovery) + for pair in discovery + let var = substitute(pair, '\(.*\):.*', '\1', '') + let type = substitute(pair, '.*:\(.*\)', '\1', '') + exec 'let value = ' . var + if value !~ '^\s*$' && value != '0' + exec "let complType = \"" . escape(type, '<') . "\"" + return complType + endif + endfor + endif +endfunction " }}} + +" s:ContextText() {{{ +function! s:ContextText() + let exclusions = exists('g:SuperTabContextTextFileTypeExclusions') ? + \ g:SuperTabContextTextFileTypeExclusions : [] + + if index(exclusions, &ft) == -1 + let curline = getline('.') + let cnum = col('.') + let synname = synIDattr(synID(line('.'), cnum - 1, 1), 'name') + if curline =~ '.*/\w*\%' . cnum . 'c' || + \ ((has('win32') || has('win64')) && curline =~ '.*\\\w*\%' . cnum . 'c') + return "\<c-x>\<c-f>" + + elseif curline =~ '.*\(\w\|[\])]\)\(\.\|::\|->\)\w*\%' . cnum . 'c' && + \ synname !~ '\(String\|Comment\)' + let omniPrecedence = exists('g:SuperTabContextTextOmniPrecedence') ? + \ g:SuperTabContextTextOmniPrecedence : ['&completefunc', '&omnifunc'] + + for omniFunc in omniPrecedence + if omniFunc !~ '^&' + let omniFunc = '&' . omniFunc + endif + if getbufvar(bufnr('%'), omniFunc) != '' + return omniFunc == '&omnifunc' ? "\<c-x>\<c-o>" : "\<c-x>\<c-u>" + endif + endfor + endif + endif +endfunction " }}} + +" s:ExpandMap(map) {{{ +function! s:ExpandMap(map) + let map = a:map + if map =~ '<Plug>' + let plug = substitute(map, '.\{-}\(<Plug>\w\+\).*', '\1', '') + let plug_map = maparg(plug, 'i') + let map = substitute(map, '.\{-}\(<Plug>\w\+\).*', plug_map, '') + endif + return map +endfunction " }}} + +" Key Mappings {{{ + " map a regular tab to ctrl-tab (note: doesn't work in console vim) + exec 'inoremap ' . g:SuperTabMappingTabLiteral . ' <tab>' + + imap <c-x> <c-r>=<SID>ManualCompletionEnter()<cr> + + imap <script> <Plug>SuperTabForward <c-r>=<SID>SuperTab('n')<cr> + imap <script> <Plug>SuperTabBackward <c-r>=<SID>SuperTab('p')<cr> + + exec 'imap ' . g:SuperTabMappingForward . ' <Plug>SuperTabForward' + exec 'imap ' . g:SuperTabMappingBackward . ' <Plug>SuperTabBackward' + + " After hitting <Tab>, hitting it once more will go to next match + " (because in XIM mode <c-n> and <c-p> mappings are ignored) + " and wont start a brand new completion + " The side effect, that in the beginning of line <c-n> and <c-p> inserts a + " <Tab>, but I hope it may not be a problem... + let ctrl_n = maparg('<c-n>', 'i') + if ctrl_n != '' + let ctrl_n = substitute(ctrl_n, '<', '<lt>', 'g') + exec 'imap <c-n> <c-r>=<SID>ForwardBack("n", "' . ctrl_n . '")<cr>' + else + imap <c-n> <Plug>SuperTabForward + endif + let ctrl_p = maparg('<c-p>', 'i') + if ctrl_p != '' + let ctrl_p = substitute(ctrl_p, '<', '<lt>', 'g') + exec 'imap <c-p> <c-r>=<SID>ForwardBack("p", "' . ctrl_p . '")<cr>' + else + imap <c-p> <Plug>SuperTabBackward + endif + function! s:ForwardBack(command, map) + exec "let map = \"" . escape(a:map, '<') . "\"" + return pumvisible() ? s:SuperTab(a:command) : map + endfunction + + if g:SuperTabCrMapping + if maparg('<CR>','i') =~ '<CR>' + let map = maparg('<cr>', 'i') + let cr = (map =~? '\(^\|[^)]\)<cr>') + let map = s:ExpandMap(map) + exec "inoremap <script> <cr> <c-r>=<SID>SelectCompletion(" . cr . ")<cr>" . map + else + inoremap <cr> <c-r>=<SID>SelectCompletion(1)<cr> + endif + function! s:SelectCompletion(cr) + " selecting a completion + if pumvisible() + " ugly hack to let other <cr> mappings for other plugins cooperate + " with supertab + let b:supertab_pumwasvisible = 1 + return "\<c-y>" + endif + + " only needed when chained with other mappings and one of them will + " issue a <cr>. + if exists('b:supertab_pumwasvisible') && !a:cr + unlet b:supertab_pumwasvisible + return '' + endif + + " not so pleasant hack to keep <cr> working for abbreviations + let word = substitute(getline('.'), '^.*\s\+\(.*\%' . col('.') . 'c\).*', '\1', '') + if maparg(word, 'i', 1) != '' + call feedkeys("\<c-]>", 't') + call feedkeys("\<cr>", 'n') + return '' + endif + + " only return a cr if nothing else is mapped to it since we don't want + " to duplicate a cr returned by another mapping. + return a:cr ? "\<cr>" : "" + endfunction + endif +" }}} + +" Command Mappings {{{ + if !exists(":SuperTabHelp") + command SuperTabHelp :call <SID>SuperTabHelp() + endif +" }}} + +call s:Init() + +let &cpo = s:save_cpo + +" vim:ft=vim:fdm=marker diff --git a/vim/plugin/surround.vim b/vim/plugin/surround.vim new file mode 100644 index 0000000..ea28c02 --- /dev/null +++ b/vim/plugin/surround.vim @@ -0,0 +1,625 @@ +" surround.vim - Surroundings +" Author: Tim Pope <vimNOSPAM@tpope.org> +" Version: 1.90 +" GetLatestVimScripts: 1697 1 :AutoInstall: surround.vim +" +" See surround.txt for help. This can be accessed by doing +" +" :helptags ~/.vim/doc +" :help surround +" +" Licensed under the same terms as Vim itself. + +" ============================================================================ + +" Exit quickly when: +" - this plugin was already loaded or disabled +" - when 'compatible' is set +if (exists("g:loaded_surround") && g:loaded_surround) || &cp + finish +endif +let g:loaded_surround = 1 + +let s:cpo_save = &cpo +set cpo&vim + +" Input functions {{{1 + +function! s:getchar() + let c = getchar() + if c =~ '^\d\+$' + let c = nr2char(c) + endif + return c +endfunction + +function! s:inputtarget() + let c = s:getchar() + while c =~ '^\d\+$' + let c = c . s:getchar() + endwhile + if c == " " + let c = c . s:getchar() + endif + if c =~ "\<Esc>\|\<C-C>\|\0" + return "" + else + return c + endif +endfunction + +function! s:inputreplacement() + "echo '-- SURROUND --' + let c = s:getchar() + if c == " " + let c = c . s:getchar() + endif + if c =~ "\<Esc>" || c =~ "\<C-C>" + return "" + else + return c + endif +endfunction + +function! s:beep() + exe "norm! \<Esc>" + return "" +endfunction + +function! s:redraw() + redraw + return "" +endfunction + +" }}}1 + +" Wrapping functions {{{1 + +function! s:extractbefore(str) + if a:str =~ '\r' + return matchstr(a:str,'.*\ze\r') + else + return matchstr(a:str,'.*\ze\n') + endif +endfunction + +function! s:extractafter(str) + if a:str =~ '\r' + return matchstr(a:str,'\r\zs.*') + else + return matchstr(a:str,'\n\zs.*') + endif +endfunction + +function! s:repeat(str,count) + let cnt = a:count + let str = "" + while cnt > 0 + let str = str . a:str + let cnt = cnt - 1 + endwhile + return str +endfunction + +function! s:fixindent(str,spc) + let str = substitute(a:str,'\t',s:repeat(' ',&sw),'g') + let spc = substitute(a:spc,'\t',s:repeat(' ',&sw),'g') + let str = substitute(str,'\(\n\|\%^\).\@=','\1'.spc,'g') + if ! &et + let str = substitute(str,'\s\{'.&ts.'\}',"\t",'g') + endif + return str +endfunction + +function! s:process(string) + let i = 0 + while i < 7 + let i = i + 1 + let repl_{i} = '' + let m = matchstr(a:string,nr2char(i).'.\{-\}\ze'.nr2char(i)) + if m != '' + let m = substitute(strpart(m,1),'\r.*','','') + let repl_{i} = input(substitute(m,':\s*$','','').': ') + endif + endwhile + let s = "" + let i = 0 + while i < strlen(a:string) + let char = strpart(a:string,i,1) + if char2nr(char) < 8 + let next = stridx(a:string,char,i+1) + if next == -1 + let s = s . char + else + let insertion = repl_{char2nr(char)} + let subs = strpart(a:string,i+1,next-i-1) + let subs = matchstr(subs,'\r.*') + while subs =~ '^\r.*\r' + let sub = matchstr(subs,"^\r\\zs[^\r]*\r[^\r]*") + let subs = strpart(subs,strlen(sub)+1) + let r = stridx(sub,"\r") + let insertion = substitute(insertion,strpart(sub,0,r),strpart(sub,r+1),'') + endwhile + let s = s . insertion + let i = next + endif + else + let s = s . char + endif + let i = i + 1 + endwhile + return s +endfunction + +function! s:wrap(string,char,type,...) + let keeper = a:string + let newchar = a:char + let type = a:type + let linemode = type ==# 'V' ? 1 : 0 + let special = a:0 ? a:1 : 0 + let before = "" + let after = "" + if type ==# "V" + let initspaces = matchstr(keeper,'\%^\s*') + else + let initspaces = matchstr(getline('.'),'\%^\s*') + endif + " Duplicate b's are just placeholders (removed) + let pairs = "b()B{}r[]a<>" + let extraspace = "" + if newchar =~ '^ ' + let newchar = strpart(newchar,1) + let extraspace = ' ' + endif + let idx = stridx(pairs,newchar) + if newchar == ' ' + let before = '' + let after = '' + elseif exists("b:surround_".char2nr(newchar)) + let all = s:process(b:surround_{char2nr(newchar)}) + let before = s:extractbefore(all) + let after = s:extractafter(all) + elseif exists("g:surround_".char2nr(newchar)) + let all = s:process(g:surround_{char2nr(newchar)}) + let before = s:extractbefore(all) + let after = s:extractafter(all) + elseif newchar ==# "p" + let before = "\n" + let after = "\n\n" + elseif newchar =~# "[tT\<C-T><,]" + let dounmapp = 0 + let dounmapb = 0 + if !maparg(">","c") + let dounmapb= 1 + " Hide from AsNeeded + exe "cn"."oremap > <CR>" + endif + let default = "" + if newchar ==# "T" + if !exists("s:lastdel") + let s:lastdel = "" + endif + let default = matchstr(s:lastdel,'<\zs.\{-\}\ze>') + endif + let tag = input("<",default) + echo "<".substitute(tag,'>*$','>','') + if dounmapb + silent! cunmap > + endif + if tag != "" + let tag = substitute(tag,'>*$','','') + let before = '<'.tag.'>' + if tag =~ '/$' + let after = '' + else + let after = '</'.substitute(tag,' .*','','').'>' + endif + if newchar == "\<C-T>" || newchar == "," + if type ==# "v" || type ==# "V" + let before = before . "\n\t" + endif + if type ==# "v" + let after = "\n". after + endif + endif + endif + elseif newchar ==# 'l' || newchar == '\' + " LaTeX + let env = input('\begin{') + let env = '{' . env + let env = env . s:closematch(env) + echo '\begin'.env + if env != "" + let before = '\begin'.env + let after = '\end'.matchstr(env,'[^}]*').'}' + endif + "if type ==# 'v' || type ==# 'V' + "let before = before ."\n\t" + "endif + "if type ==# 'v' + "let after = "\n".initspaces.after + "endif + elseif newchar ==# 'f' || newchar ==# 'F' + let fnc = input('function: ') + if fnc != "" + let before = substitute(fnc,'($','','').'(' + let after = ')' + if newchar ==# 'F' + let before = before . ' ' + let after = ' ' . after + endif + endif + elseif idx >= 0 + let spc = (idx % 3) == 1 ? " " : "" + let idx = idx / 3 * 3 + let before = strpart(pairs,idx+1,1) . spc + let after = spc . strpart(pairs,idx+2,1) + elseif newchar == "\<C-[>" || newchar == "\<C-]>" + let before = "{\n\t" + let after = "\n}" + elseif newchar !~ '\a' + let before = newchar + let after = newchar + else + let before = '' + let after = '' + endif + "let before = substitute(before,'\n','\n'.initspaces,'g') + let after = substitute(after ,'\n','\n'.initspaces,'g') + "let after = substitute(after,"\n\\s*\<C-U>\\s*",'\n','g') + if type ==# 'V' || (special && type ==# "v") + let before = substitute(before,' \+$','','') + let after = substitute(after ,'^ \+','','') + if after !~ '^\n' + let after = initspaces.after + endif + if keeper !~ '\n$' && after !~ '^\n' + let keeper = keeper . "\n" + elseif keeper =~ '\n$' && after =~ '^\n' + let after = strpart(after,1) + endif + if before !~ '\n\s*$' + let before = before . "\n" + if special + let before = before . "\t" + endif + endif + endif + if type ==# 'V' + let before = initspaces.before + endif + if before =~ '\n\s*\%$' + if type ==# 'v' + let keeper = initspaces.keeper + endif + let padding = matchstr(before,'\n\zs\s\+\%$') + let before = substitute(before,'\n\s\+\%$','\n','') + let keeper = s:fixindent(keeper,padding) + endif + if type ==# 'V' + let keeper = before.keeper.after + elseif type =~ "^\<C-V>" + " Really we should be iterating over the buffer + let repl = substitute(before,'[\\~]','\\&','g').'\1'.substitute(after,'[\\~]','\\&','g') + let repl = substitute(repl,'\n',' ','g') + let keeper = substitute(keeper."\n",'\(.\{-\}\)\(\n\)',repl.'\n','g') + let keeper = substitute(keeper,'\n\%$','','') + else + let keeper = before.extraspace.keeper.extraspace.after + endif + return keeper +endfunction + +function! s:wrapreg(reg,char,...) + let orig = getreg(a:reg) + let type = substitute(getregtype(a:reg),'\d\+$','','') + let special = a:0 ? a:1 : 0 + let new = s:wrap(orig,a:char,type,special) + call setreg(a:reg,new,type) +endfunction +" }}}1 + +function! s:insert(...) " {{{1 + " Optional argument causes the result to appear on 3 lines, not 1 + "call inputsave() + let linemode = a:0 ? a:1 : 0 + let char = s:inputreplacement() + while char == "\<CR>" || char == "\<C-S>" + " TODO: use total count for additional blank lines + let linemode = linemode + 1 + let char = s:inputreplacement() + endwhile + "call inputrestore() + if char == "" + return "" + endif + "call inputsave() + let cb_save = &clipboard + set clipboard-=unnamed + let reg_save = @@ + call setreg('"',"\r",'v') + call s:wrapreg('"',char,linemode) + " If line mode is used and the surrounding consists solely of a suffix, + " remove the initial newline. This fits a use case of mine but is a + " little inconsistent. Is there anyone that would prefer the simpler + " behavior of just inserting the newline? + if linemode && match(getreg('"'),'^\n\s*\zs.*') == 0 + call setreg('"',matchstr(getreg('"'),'^\n\s*\zs.*'),getregtype('"')) + endif + " This can be used to append a placeholder to the end + if exists("g:surround_insert_tail") + call setreg('"',g:surround_insert_tail,"a".getregtype('"')) + endif + "if linemode + "call setreg('"',substitute(getreg('"'),'^\s\+','',''),'c') + "endif + if col('.') >= col('$') + norm! ""p + else + norm! ""P + endif + if linemode + call s:reindent() + endif + norm! `] + call search('\r','bW') + let @@ = reg_save + let &clipboard = cb_save + return "\<Del>" +endfunction " }}}1 + +function! s:reindent() " {{{1 + if exists("b:surround_indent") ? b:surround_indent : (exists("g:surround_indent") && g:surround_indent) + silent norm! '[='] + endif +endfunction " }}}1 + +function! s:dosurround(...) " {{{1 + let scount = v:count1 + let char = (a:0 ? a:1 : s:inputtarget()) + let spc = "" + if char =~ '^\d\+' + let scount = scount * matchstr(char,'^\d\+') + let char = substitute(char,'^\d\+','','') + endif + if char =~ '^ ' + let char = strpart(char,1) + let spc = 1 + endif + if char == 'a' + let char = '>' + endif + if char == 'r' + let char = ']' + endif + let newchar = "" + if a:0 > 1 + let newchar = a:2 + if newchar == "\<Esc>" || newchar == "\<C-C>" || newchar == "" + return s:beep() + endif + endif + let cb_save = &clipboard + set clipboard-=unnamed + let append = "" + let original = getreg('"') + let otype = getregtype('"') + call setreg('"',"") + let strcount = (scount == 1 ? "" : scount) + if char == '/' + exe 'norm! '.strcount.'[/d'.strcount.']/' + else + exe 'norm! d'.strcount.'i'.char + endif + let keeper = getreg('"') + let okeeper = keeper " for reindent below + if keeper == "" + call setreg('"',original,otype) + let &clipboard = cb_save + return "" + endif + let oldline = getline('.') + let oldlnum = line('.') + if char ==# "p" + call setreg('"','','V') + elseif char ==# "s" || char ==# "w" || char ==# "W" + " Do nothing + call setreg('"','') + elseif char =~ "[\"'`]" + exe "norm! i \<Esc>d2i".char + call setreg('"',substitute(getreg('"'),' ','','')) + elseif char == '/' + norm! "_x + call setreg('"','/**/',"c") + let keeper = substitute(substitute(keeper,'^/\*\s\=','',''),'\s\=\*$','','') + else + " One character backwards + call search('.','bW') + exe "norm! da".char + endif + let removed = getreg('"') + let rem2 = substitute(removed,'\n.*','','') + let oldhead = strpart(oldline,0,strlen(oldline)-strlen(rem2)) + let oldtail = strpart(oldline, strlen(oldline)-strlen(rem2)) + let regtype = getregtype('"') + if char =~# '[\[({<T]' || spc + let keeper = substitute(keeper,'^\s\+','','') + let keeper = substitute(keeper,'\s\+$','','') + endif + if col("']") == col("$") && col('.') + 1 == col('$') + if oldhead =~# '^\s*$' && a:0 < 2 + let keeper = substitute(keeper,'\%^\n'.oldhead.'\(\s*.\{-\}\)\n\s*\%$','\1','') + endif + let pcmd = "p" + else + let pcmd = "P" + endif + if line('.') < oldlnum && regtype ==# "V" + let pcmd = "p" + endif + call setreg('"',keeper,regtype) + if newchar != "" + call s:wrapreg('"',newchar) + endif + silent exe 'norm! ""'.pcmd.'`[' + if removed =~ '\n' || okeeper =~ '\n' || getreg('"') =~ '\n' + call s:reindent() + endif + if getline('.') =~ '^\s\+$' && keeper =~ '^\s*\n' + silent norm! cc + endif + call setreg('"',removed,regtype) + let s:lastdel = removed + let &clipboard = cb_save + if newchar == "" + silent! call repeat#set("\<Plug>Dsurround".char,scount) + else + silent! call repeat#set("\<Plug>Csurround".char.newchar,scount) + endif +endfunction " }}}1 + +function! s:changesurround() " {{{1 + let a = s:inputtarget() + if a == "" + return s:beep() + endif + let b = s:inputreplacement() + if b == "" + return s:beep() + endif + call s:dosurround(a,b) +endfunction " }}}1 + +function! s:opfunc(type,...) " {{{1 + let char = s:inputreplacement() + if char == "" + return s:beep() + endif + let reg = '"' + let sel_save = &selection + let &selection = "inclusive" + let cb_save = &clipboard + set clipboard-=unnamed + let reg_save = getreg(reg) + let reg_type = getregtype(reg) + "call setreg(reg,"\n","c") + let type = a:type + if a:type == "char" + silent exe 'norm! v`[o`]"'.reg.'y' + let type = 'v' + elseif a:type == "line" + silent exe 'norm! `[V`]"'.reg.'y' + let type = 'V' + elseif a:type ==# "v" || a:type ==# "V" || a:type ==# "\<C-V>" + let ve = &virtualedit + if !(a:0 && a:1) + set virtualedit= + endif + silent exe 'norm! gv"'.reg.'y' + let &virtualedit = ve + elseif a:type =~ '^\d\+$' + let type = 'v' + silent exe 'norm! ^v'.a:type.'$h"'.reg.'y' + if mode() ==# 'v' + norm! v + return s:beep() + endif + else + let &selection = sel_save + let &clipboard = cb_save + return s:beep() + endif + let keeper = getreg(reg) + if type ==# "v" && a:type !=# "v" + let append = matchstr(keeper,'\_s\@<!\s*$') + let keeper = substitute(keeper,'\_s\@<!\s*$','','') + endif + call setreg(reg,keeper,type) + call s:wrapreg(reg,char,a:0 && a:1) + if type ==# "v" && a:type !=# "v" && append != "" + call setreg(reg,append,"ac") + endif + silent exe 'norm! gv'.(reg == '"' ? '' : '"' . reg).'p`[' + if type ==# 'V' || (getreg(reg) =~ '\n' && type ==# 'v') + call s:reindent() + endif + call setreg(reg,reg_save,reg_type) + let &selection = sel_save + let &clipboard = cb_save + if a:type =~ '^\d\+$' + silent! call repeat#set("\<Plug>Y".(a:0 && a:1 ? "S" : "s")."surround".char,a:type) + endif +endfunction + +function! s:opfunc2(arg) + call s:opfunc(a:arg,1) +endfunction " }}}1 + +function! s:closematch(str) " {{{1 + " Close an open (, {, [, or < on the command line. + let tail = matchstr(a:str,'.[^\[\](){}<>]*$') + if tail =~ '^\[.\+' + return "]" + elseif tail =~ '^(.\+' + return ")" + elseif tail =~ '^{.\+' + return "}" + elseif tail =~ '^<.+' + return ">" + else + return "" + endif +endfunction " }}}1 + +nnoremap <silent> <Plug>Dsurround :<C-U>call <SID>dosurround(<SID>inputtarget())<CR> +nnoremap <silent> <Plug>Csurround :<C-U>call <SID>changesurround()<CR> +nnoremap <silent> <Plug>Yssurround :<C-U>call <SID>opfunc(v:count1)<CR> +nnoremap <silent> <Plug>YSsurround :<C-U>call <SID>opfunc2(v:count1)<CR> +" <C-U> discards the numerical argument but there's not much we can do with it +nnoremap <silent> <Plug>Ysurround :<C-U>set opfunc=<SID>opfunc<CR>g@ +nnoremap <silent> <Plug>YSurround :<C-U>set opfunc=<SID>opfunc2<CR>g@ +vnoremap <silent> <Plug>Vsurround :<C-U>call <SID>opfunc(visualmode())<CR> +vnoremap <silent> <Plug>VSurround :<C-U>call <SID>opfunc(visualmode(),visualmode() ==# 'V' ? 1 : 0)<CR> +vnoremap <silent> <Plug>VgSurround :<C-U>call <SID>opfunc(visualmode(),visualmode() ==# 'V' ? 0 : 1)<CR> +inoremap <silent> <Plug>Isurround <C-R>=<SID>insert()<CR> +inoremap <silent> <Plug>ISurround <C-R>=<SID>insert(1)<CR> + +if !exists("g:surround_no_mappings") || ! g:surround_no_mappings + nmap ds <Plug>Dsurround + nmap cs <Plug>Csurround + nmap ys <Plug>Ysurround + nmap yS <Plug>YSurround + nmap yss <Plug>Yssurround + nmap ySs <Plug>YSsurround + nmap ySS <Plug>YSsurround + if !hasmapto("<Plug>Vsurround","v") && !hasmapto("<Plug>VSurround","v") + if exists(":xmap") + xmap s <Plug>Vsurround + else + vmap s <Plug>Vsurround + endif + endif + if !hasmapto("<Plug>VSurround","v") + if exists(":xmap") + xmap S <Plug>VSurround + else + vmap S <Plug>VSurround + endif + endif + if exists(":xmap") + xmap gS <Plug>VgSurround + else + vmap gS <Plug>VgSurround + endif + if !hasmapto("<Plug>Isurround","i") && "" == mapcheck("<C-S>","i") + imap <C-S> <Plug>Isurround + endif + imap <C-G>s <Plug>Isurround + imap <C-G>S <Plug>ISurround + "Implemented internally instead + "imap <C-S><C-S> <Plug>ISurround +endif + +let &cpo = s:cpo_save + +" vim:set ft=vim sw=2 sts=2 et: diff --git a/vim/plugin/syntastic.vim b/vim/plugin/syntastic.vim new file mode 100644 index 0000000..0eb657a --- /dev/null +++ b/vim/plugin/syntastic.vim @@ -0,0 +1,614 @@ +"============================================================================ +"File: syntastic.vim +"Description: vim plugin for on the fly syntax checking +"Maintainer: Martin Grenfell <martin.grenfell at gmail dot com> +"Version: 2.3.0 +"Last Change: 16 Feb, 2012 +"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. +" +"============================================================================ + +if exists("g:loaded_syntastic_plugin") + finish +endif +let g:loaded_syntastic_plugin = 1 + +let s:running_windows = has("win16") || has("win32") || has("win64") + +if !s:running_windows + let s:uname = system('uname') +endif + +if !exists("g:syntastic_enable_signs") + let g:syntastic_enable_signs = 1 +endif +if !has('signs') + let g:syntastic_enable_signs = 0 +endif + +if !exists("g:syntastic_enable_balloons") + let g:syntastic_enable_balloons = 1 +endif +if !has('balloon_eval') + let g:syntastic_enable_balloons = 0 +endif + +if !exists("g:syntastic_enable_highlighting") + let g:syntastic_enable_highlighting = 1 +endif + +if !exists("g:syntastic_echo_current_error") + let g:syntastic_echo_current_error = 1 +endif + +if !exists("g:syntastic_auto_loc_list") + let g:syntastic_auto_loc_list = 2 +endif + +if !exists("g:syntastic_auto_jump") + let syntastic_auto_jump=0 +endif + +if !exists("g:syntastic_quiet_warnings") + let g:syntastic_quiet_warnings = 0 +endif + +if !exists("g:syntastic_stl_format") + let g:syntastic_stl_format = '[Syntax: line:%F (%t)]' +endif + +if !exists("g:syntastic_mode_map") + let g:syntastic_mode_map = {} +endif + +if !has_key(g:syntastic_mode_map, "mode") + let g:syntastic_mode_map['mode'] = 'active' +endif + +if !has_key(g:syntastic_mode_map, "active_filetypes") + let g:syntastic_mode_map['active_filetypes'] = [] +endif + +if !has_key(g:syntastic_mode_map, "passive_filetypes") + let g:syntastic_mode_map['passive_filetypes'] = [] +endif + +if !exists("g:syntastic_check_on_open") + let g:syntastic_check_on_open = 0 +endif + +if !exists("g:syntastic_loc_list_height") + let g:syntastic_loc_list_height = 10 +endif + +command! SyntasticToggleMode call s:ToggleMode() +command! SyntasticCheck call s:UpdateErrors(0) <bar> redraw! +command! Errors call s:ShowLocList() + +highlight link SyntasticError SpellBad +highlight link SyntasticWarning SpellCap + +augroup syntastic + if g:syntastic_echo_current_error + autocmd cursormoved * call s:EchoCurrentError() + endif + + autocmd BufReadPost * if g:syntastic_check_on_open | call s:UpdateErrors(1) | endif + autocmd BufWritePost * call s:UpdateErrors(1) + + autocmd BufWinEnter * if empty(&bt) | call s:AutoToggleLocList() | endif + autocmd BufWinLeave * if empty(&bt) | lclose | endif +augroup END + + +"refresh and redraw all the error info for this buf when saving or reading +function! s:UpdateErrors(auto_invoked) + if !empty(&buftype) + return + endif + + if !a:auto_invoked || s:ModeMapAllowsAutoChecking() + call s:CacheErrors() + end + + if s:BufHasErrorsOrWarningsToDisplay() + call setloclist(0, s:LocList()) + endif + + if g:syntastic_enable_balloons + call s:RefreshBalloons() + endif + + if g:syntastic_enable_signs + call s:RefreshSigns() + endif + + if g:syntastic_auto_jump && s:BufHasErrorsOrWarningsToDisplay() + silent! ll + endif + + call s:AutoToggleLocList() +endfunction + +"automatically open/close the location list window depending on the users +"config and buffer error state +function! s:AutoToggleLocList() + if s:BufHasErrorsOrWarningsToDisplay() + if g:syntastic_auto_loc_list == 1 + call s:ShowLocList() + endif + else + if g:syntastic_auto_loc_list > 0 + + "TODO: this will close the loc list window if one was opened by + "something other than syntastic + lclose + endif + endif +endfunction + +"lazy init the loc list for the current buffer +function! s:LocList() + if !exists("b:syntastic_loclist") + let b:syntastic_loclist = [] + endif + return b:syntastic_loclist +endfunction + +"clear the loc list for the buffer +function! s:ClearLocList() + let b:syntastic_loclist = [] +endfunction + +"detect and cache all syntax errors in this buffer +" +"depends on a function called SyntaxCheckers_{&ft}_GetLocList() existing +"elsewhere +function! s:CacheErrors() + call s:ClearLocList() + + if filereadable(expand("%")) + + "sub - for _ in filetypes otherwise we cant name syntax checker + "functions legally for filetypes like "gentoo-metadata" + let fts = substitute(&ft, '-', '_', 'g') + for ft in split(fts, '\.') + if s:Checkable(ft) + let errors = SyntaxCheckers_{ft}_GetLocList() + "make errors have type "E" by default + call SyntasticAddToErrors(errors, {'type': 'E'}) + call extend(s:LocList(), errors) + endif + endfor + endif +endfunction + +"toggle the g:syntastic_mode_map['mode'] +function! s:ToggleMode() + if g:syntastic_mode_map['mode'] == "active" + let g:syntastic_mode_map['mode'] = "passive" + else + let g:syntastic_mode_map['mode'] = "active" + endif + + call s:ClearLocList() + call s:UpdateErrors(1) + + echo "Syntastic: " . g:syntastic_mode_map['mode'] . " mode enabled" +endfunction + +"check the current filetypes against g:syntastic_mode_map to determine whether +"active mode syntax checking should be done +function! s:ModeMapAllowsAutoChecking() + let fts = split(&ft, '\.') + + if g:syntastic_mode_map['mode'] == 'passive' + "check at least one filetype is active + let actives = g:syntastic_mode_map["active_filetypes"] + return !empty(filter(fts, 'index(actives, v:val) != -1')) + else + "check no filetypes are passive + let passives = g:syntastic_mode_map["passive_filetypes"] + return empty(filter(fts, 'index(passives, v:val) != -1')) + endif +endfunction + +"return true if there are cached errors/warnings for this buf +function! s:BufHasErrorsOrWarnings() + return !empty(s:LocList()) +endfunction + +"return true if there are cached errors for this buf +function! s:BufHasErrors() + return len(s:ErrorsForType('E')) > 0 +endfunction + +function! s:BufHasErrorsOrWarningsToDisplay() + return s:BufHasErrors() || (!g:syntastic_quiet_warnings && s:BufHasErrorsOrWarnings()) +endfunction + +function! s:ErrorsForType(type) + return s:FilterLocList({'type': a:type}) +endfunction + +function! s:Errors() + return s:ErrorsForType("E") +endfunction + +function! s:Warnings() + return s:ErrorsForType("W") +endfunction + +"Filter a loc list (defaults to s:LocList()) by a:filters +"e.g. +" s:FilterLocList({'bufnr': 10, 'type': 'e'}) +" +"would return all errors in s:LocList() for buffer 10. +" +"Note that all comparisons are done with ==? +function! s:FilterLocList(filters, ...) + let llist = a:0 ? a:1 : s:LocList() + + let rv = deepcopy(llist) + for error in llist + for key in keys(a:filters) + let rhs = a:filters[key] + if type(rhs) == 1 "string + let rhs = '"' . rhs . '"' + endif + + call filter(rv, "v:val['".key."'] ==? " . rhs) + endfor + endfor + return rv +endfunction + +if g:syntastic_enable_signs + "define the signs used to display syntax and style errors/warns + sign define SyntasticError text=>> texthl=error + sign define SyntasticWarning text=>> texthl=todo + sign define SyntasticStyleError text=S> texthl=error + sign define SyntasticStyleWarning text=S> texthl=todo +endif + +"start counting sign ids at 5000, start here to hopefully avoid conflicting +"with any other code that places signs (not sure if this precaution is +"actually needed) +let s:first_sign_id = 5000 +let s:next_sign_id = s:first_sign_id + +"place signs by all syntax errs in the buffer +function! s:SignErrors() + if s:BufHasErrorsOrWarningsToDisplay() + + let errors = s:FilterLocList({'bufnr': bufnr('')}) + for i in errors + let sign_severity = 'Error' + let sign_subtype = '' + if has_key(i,'subtype') + let sign_subtype = i['subtype'] + endif + if i['type'] ==? 'w' + let sign_severity = 'Warning' + endif + let sign_type = 'Syntastic' . sign_subtype . sign_severity + + if !s:WarningMasksError(i, errors) + exec "sign place ". s:next_sign_id ." line=". i['lnum'] ." name=". sign_type ." file=". expand("%:p") + call add(s:BufSignIds(), s:next_sign_id) + let s:next_sign_id += 1 + endif + endfor + endif +endfunction + +"return true if the given error item is a warning that, if signed, would +"potentially mask an error if displayed at the same time +function! s:WarningMasksError(error, llist) + if a:error['type'] !=? 'w' + return 0 + endif + + return len(s:FilterLocList({ 'type': "E", 'lnum': a:error['lnum'] }, a:llist)) > 0 +endfunction + +"remove the signs with the given ids from this buffer +function! s:RemoveSigns(ids) + for i in a:ids + exec "sign unplace " . i + call remove(s:BufSignIds(), index(s:BufSignIds(), i)) + endfor +endfunction + +"get all the ids of the SyntaxError signs in the buffer +function! s:BufSignIds() + if !exists("b:syntastic_sign_ids") + let b:syntastic_sign_ids = [] + endif + return b:syntastic_sign_ids +endfunction + +"update the error signs +function! s:RefreshSigns() + let old_signs = copy(s:BufSignIds()) + call s:SignErrors() + call s:RemoveSigns(old_signs) + let s:first_sign_id = s:next_sign_id +endfunction + +"display the cached errors for this buf in the location list +function! s:ShowLocList() + if !empty(s:LocList()) + let num = winnr() + exec "lopen " . g:syntastic_loc_list_height + if num != winnr() + wincmd p + endif + endif +endfunction + +"remove all error highlights from the window +function! s:ClearErrorHighlights() + for match in getmatches() + if stridx(match['group'], 'Syntastic') == 0 + call matchdelete(match['id']) + endif + endfor +endfunction + +"check if a syntax checker exists for the given filetype - and attempt to +"load one +function! s:Checkable(ft) + if !exists("g:loaded_" . a:ft . "_syntax_checker") + exec "runtime syntax_checkers/" . a:ft . ".vim" + endif + + return exists("*SyntaxCheckers_". a:ft ."_GetLocList") +endfunction + +"set up error ballons for the current set of errors +function! s:RefreshBalloons() + let b:syntastic_balloons = {} + if s:BufHasErrorsOrWarningsToDisplay() + for i in s:LocList() + let b:syntastic_balloons[i['lnum']] = i['text'] + endfor + set beval bexpr=SyntasticErrorBalloonExpr() + endif +endfunction + +"print as much of a:msg as possible without "Press Enter" prompt appearing +function! s:WideMsg(msg) + let old_ruler = &ruler + let old_showcmd = &showcmd + + let msg = strpart(a:msg, 0, winwidth(0)-1) + + "This is here because it is possible for some error messages to begin with + "\n which will cause a "press enter" prompt. I have noticed this in the + "javascript:jshint checker and have been unable to figure out why it + "happens + let msg = substitute(msg, "\n", "", "g") + + set noruler noshowcmd + redraw + + echo msg + + let &ruler=old_ruler + let &showcmd=old_showcmd +endfunction + +"echo out the first error we find for the current line in the cmd window +function! s:EchoCurrentError() + "If we have an error or warning at the current line, show it + let errors = s:FilterLocList({'lnum': line("."), "type": 'e'}) + let warnings = s:FilterLocList({'lnum': line("."), "type": 'w'}) + + let b:syntastic_echoing_error = len(errors) || len(warnings) + if len(errors) + return s:WideMsg(errors[0]['text']) + endif + if len(warnings) + return s:WideMsg(warnings[0]['text']) + endif + + "Otherwise, clear the status line + if b:syntastic_echoing_error + echo + let b:syntastic_echoing_error = 0 + endif +endfunction + +"load the chosen checker for the current filetype - useful for filetypes like +"javascript that have more than one syntax checker +function! s:LoadChecker(checker) + exec "runtime syntax_checkers/" . &ft . "/" . a:checker . ".vim" +endfunction + +"return a string representing the state of buffer according to +"g:syntastic_stl_format +" +"return '' if no errors are cached for the buffer +function! SyntasticStatuslineFlag() + if s:BufHasErrorsOrWarningsToDisplay() + let errors = s:Errors() + let warnings = s:Warnings() + + let output = g:syntastic_stl_format + + "hide stuff wrapped in %E(...) unless there are errors + let output = substitute(output, '\C%E{\([^}]*\)}', len(errors) ? '\1' : '' , 'g') + + "hide stuff wrapped in %W(...) unless there are warnings + let output = substitute(output, '\C%W{\([^}]*\)}', len(warnings) ? '\1' : '' , 'g') + + "hide stuff wrapped in %B(...) unless there are both errors and warnings + let output = substitute(output, '\C%B{\([^}]*\)}', (len(warnings) && len(errors)) ? '\1' : '' , 'g') + + "sub in the total errors/warnings/both + let output = substitute(output, '\C%w', len(warnings), 'g') + let output = substitute(output, '\C%e', len(errors), 'g') + let output = substitute(output, '\C%t', len(s:LocList()), 'g') + + "first error/warning line num + let output = substitute(output, '\C%F', s:LocList()[0]['lnum'], 'g') + + "first error line num + let output = substitute(output, '\C%fe', len(errors) ? errors[0]['lnum'] : '', 'g') + + "first warning line num + let output = substitute(output, '\C%fw', len(warnings) ? warnings[0]['lnum'] : '', 'g') + + return output + else + return '' + endif +endfunction + +"A wrapper for the :lmake command. Sets up the make environment according to +"the options given, runs make, resets the environment, returns the location +"list +" +"a:options can contain the following keys: +" 'makeprg' +" 'errorformat' +" +"The corresponding options are set for the duration of the function call. They +"are set with :let, so dont escape spaces. +" +"a:options may also contain: +" 'defaults' - a dict containing default values for the returned errors +" 'subtype' - all errors will be assigned the given subtype +function! SyntasticMake(options) + let old_loclist = getloclist(0) + let old_makeprg = &makeprg + let old_shellpipe = &shellpipe + let old_shell = &shell + let old_errorformat = &errorformat + + if !s:running_windows && (s:uname !~ "FreeBSD") + "this is a hack to stop the screen needing to be ':redraw'n when + "when :lmake is run. Otherwise the screen flickers annoyingly + let &shellpipe='&>' + let &shell = '/bin/bash' + endif + + if has_key(a:options, 'makeprg') + let &makeprg = a:options['makeprg'] + endif + + if has_key(a:options, 'errorformat') + let &errorformat = a:options['errorformat'] + endif + + silent lmake! + let errors = getloclist(0) + + call setloclist(0, old_loclist) + let &makeprg = old_makeprg + let &errorformat = old_errorformat + let &shellpipe=old_shellpipe + let &shell=old_shell + + if !s:running_windows && s:uname =~ "FreeBSD" + redraw! + endif + + if has_key(a:options, 'defaults') + call SyntasticAddToErrors(errors, a:options['defaults']) + endif + + " Add subtype info if present. + if has_key(a:options, 'subtype') + call SyntasticAddToErrors(errors, {'subtype': a:options['subtype']}) + endif + + return errors +endfunction + +"get the error balloon for the current mouse position +function! SyntasticErrorBalloonExpr() + if !exists('b:syntastic_balloons') + return '' + endif + return get(b:syntastic_balloons, v:beval_lnum, '') +endfunction + +"highlight the list of errors (a:errors) using matchadd() +" +"a:termfunc is provided to highlight errors that do not have a 'col' key (and +"hence cant be done automatically). This function must take one arg (an error +"item) and return a regex to match that item in the buffer. +" +"an optional boolean third argument can be provided to force a:termfunc to be +"used regardless of whether a 'col' key is present for the error +function! SyntasticHighlightErrors(errors, termfunc, ...) + if !g:syntastic_enable_highlighting + return + endif + + call s:ClearErrorHighlights() + + let force_callback = a:0 && a:1 + for item in a:errors + let group = item['type'] == 'E' ? 'SyntasticError' : 'SyntasticWarning' + if item['col'] && !force_callback + let lastcol = col([item['lnum'], '$']) + let lcol = min([lastcol, item['col']]) + call matchadd(group, '\%'.item['lnum'].'l\%'.lcol.'c') + else + let term = a:termfunc(item) + if len(term) > 0 + call matchadd(group, '\%' . item['lnum'] . 'l' . term) + endif + endif + endfor +endfunction + +"take a list of errors and add default values to them from a:options +function! SyntasticAddToErrors(errors, options) + for i in range(0, len(a:errors)-1) + for key in keys(a:options) + if !has_key(a:errors[i], key) || empty(a:errors[i][key]) + let a:errors[i][key] = a:options[key] + endif + endfor + endfor + return a:errors +endfunction + +"take a list of syntax checkers for the current filetype and load the right +"one based on the global settings and checker executable availabity +" +"a:checkers should be a list of syntax checker names. These names are assumed +"to be the names of the vim syntax checker files that should be sourced, as +"well as the names of the actual syntax checker executables. The checkers +"should be listed in order of default preference. +" +"if a option called 'g:syntastic_[filetype]_checker' exists then attempt to +"load the checker that it points to +function! SyntasticLoadChecker(checkers) + let opt_name = "g:syntastic_" . &ft . "_checker" + + if exists(opt_name) + let opt_val = {opt_name} + if index(a:checkers, opt_val) != -1 && executable(opt_val) + call s:LoadChecker(opt_val) + else + echoerr &ft . " syntax not supported or not installed." + endif + else + for checker in a:checkers + if executable(checker) + return s:LoadChecker(checker) + endif + endfor + endif +endfunction + +" vim: set et sts=4 sw=4: diff --git a/vim/plugin/tagbar.vim b/vim/plugin/tagbar.vim new file mode 100644 index 0000000..a078a18 --- /dev/null +++ b/vim/plugin/tagbar.vim @@ -0,0 +1,118 @@ +" ============================================================================ +" File: tagbar.vim +" Description: List the current file's tags in a sidebar, ordered by class etc +" Author: Jan Larres <jan@majutsushi.net> +" Licence: Vim licence +" Website: http://majutsushi.github.com/tagbar/ +" Version: 2.3 +" Note: This plugin was heavily inspired by the 'Taglist' plugin by +" Yegappan Lakshmanan and uses a small amount of code from it. +" +" Original taglist copyright notice: +" Permission is hereby granted to use and distribute this code, +" with or without modifications, provided that this copyright +" notice is copied with it. Like anything else that's free, +" taglist.vim is provided *as is* and comes with no warranty of +" any kind, either expressed or implied. In no event will the +" copyright holder be liable for any damamges resulting from the +" use of this software. +" ============================================================================ + +scriptencoding utf-8 + +if &cp || exists('g:loaded_tagbar') + finish +endif + +" Basic init {{{1 + +if v:version < 700 + echohl WarningMsg + echomsg 'Tagbar: Vim version is too old, Tagbar requires at least 7.0' + echohl None + finish +endif + +if v:version == 700 && !has('patch167') + echohl WarningMsg + echomsg 'Tagbar: Vim versions lower than 7.0.167 have a bug' + \ 'that prevents this version of Tagbar from working.' + \ 'Please use the alternate version posted on the website.' + echohl None + finish +endif + +if !exists('g:tagbar_left') + let g:tagbar_left = 0 +endif + +if !exists('g:tagbar_width') + let g:tagbar_width = 40 +endif + +if !exists('g:tagbar_autoclose') + let g:tagbar_autoclose = 0 +endif + +if !exists('g:tagbar_autofocus') + let g:tagbar_autofocus = 0 +endif + +if !exists('g:tagbar_sort') + let g:tagbar_sort = 1 +endif + +if !exists('g:tagbar_compact') + let g:tagbar_compact = 0 +endif + +if !exists('g:tagbar_expand') + let g:tagbar_expand = 0 +endif + +if !exists('g:tagbar_singleclick') + let g:tagbar_singleclick = 0 +endif + +if !exists('g:tagbar_foldlevel') + let g:tagbar_foldlevel = 99 +endif + +if !exists('g:tagbar_iconchars') + if has('multi_byte') && has('unix') && &encoding == 'utf-8' && + \ (empty(&termencoding) || &termencoding == 'utf-8') + let g:tagbar_iconchars = ['▶', '▼'] + else + let g:tagbar_iconchars = ['+', '-'] + endif +endif + +if !exists('g:tagbar_autoshowtag') + let g:tagbar_autoshowtag = 0 +endif + +if !exists('g:tagbar_updateonsave_maxlines') + let g:tagbar_updateonsave_maxlines = 5000 +endif + +if !exists('g:tagbar_systemenc') + let g:tagbar_systemenc = &encoding +endif + +augroup TagbarSession + autocmd! + autocmd SessionLoadPost * nested call tagbar#RestoreSession() +augroup END + +" Commands {{{1 +command! -nargs=0 TagbarToggle call tagbar#ToggleWindow() +command! -nargs=? TagbarOpen call tagbar#OpenWindow(<f-args>) +command! -nargs=0 TagbarOpenAutoClose call tagbar#OpenWindow('fc') +command! -nargs=0 TagbarClose call tagbar#CloseWindow() +command! -nargs=1 TagbarSetFoldlevel call tagbar#SetFoldLevel(<args>) +command! -nargs=0 TagbarShowTag call tagbar#OpenParents() +command! -nargs=? TagbarDebug call tagbar#StartDebug(<f-args>) +command! -nargs=0 TagbarDebugEnd call tagbar#StopDebug() + +" Modeline {{{1 +" vim: ts=8 sw=4 sts=4 et foldenable foldmethod=marker foldcolumn=1 diff --git a/vim/plugin/taglist.vim b/vim/plugin/taglist.vim new file mode 100644 index 0000000..59901f6 --- /dev/null +++ b/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 + diff --git a/vim/plugin/unimpaired.vim b/vim/plugin/unimpaired.vim new file mode 100644 index 0000000..60b6c02 --- /dev/null +++ b/vim/plugin/unimpaired.vim @@ -0,0 +1,315 @@ +" unimpaired.vim - Pairs of handy bracket mappings +" Maintainer: Tim Pope <http://tpo.pe/> +" Version: 1.1 +" GetLatestVimScripts: 1590 1 :AutoInstall: unimpaired.vim + +if exists("g:loaded_unimpaired") || &cp || v:version < 700 + finish +endif +let g:loaded_unimpaired = 1 + +" Next and previous {{{1 + +function! s:MapNextFamily(map,cmd) + let map = '<Plug>unimpaired'.toupper(a:map) + let end = ' ".(v:count ? v:count : "")<CR>' + execute 'nmap <silent> '.map.'Previous :<C-U>exe "'.a:cmd.'previous'.end + execute 'nmap <silent> '.map.'Next :<C-U>exe "'.a:cmd.'next'.end + execute 'nmap <silent> '.map.'First :<C-U>exe "'.a:cmd.'first'.end + execute 'nmap <silent> '.map.'Last :<C-U>exe "'.a:cmd.'last'.end + execute 'nmap <silent> ['. a:map .' '.map.'Previous' + execute 'nmap <silent> ]'. a:map .' '.map.'Next' + execute 'nmap <silent> ['.toupper(a:map).' '.map.'First' + execute 'nmap <silent> ]'.toupper(a:map).' '.map.'Last' +endfunction + +call s:MapNextFamily('a','') +call s:MapNextFamily('b','b') +call s:MapNextFamily('l','l') +call s:MapNextFamily('q','c') +call s:MapNextFamily('t','t') + +function! s:entries(path) + let path = substitute(a:path,'[\\/]$','','') + let files = split(glob(path."/.*"),"\n") + let files += split(glob(path."/*"),"\n") + call map(files,'substitute(v:val,"[\\/]$","","")') + call filter(files,'v:val !~# "[\\\\/]\\.\\.\\=$"') + call filter(files,'v:val[-4:-1] !=# ".swp" && v:val[-1:-1] !=# "~"') + return files +endfunction + +function! s:FileByOffset(num) + let file = expand('%:p') + let num = a:num + while num + let files = s:entries(fnamemodify(file,':h')) + if a:num < 0 + call reverse(sort(filter(files,'v:val < file'))) + else + call sort(filter(files,'v:val > file')) + endif + let temp = get(files,0,'') + if temp == '' + let file = fnamemodify(file,':h') + else + let file = temp + while isdirectory(file) + let files = s:entries(file) + if files == [] + " TODO: walk back up the tree and continue + break + endif + let file = files[num > 0 ? 0 : -1] + endwhile + let num += num > 0 ? -1 : 1 + endif + endwhile + return file +endfunction + +nnoremap <silent> <Plug>unimpairedONext :<C-U>edit `=<SID>FileByOffset(v:count1)`<CR> +nnoremap <silent> <Plug>unimpairedOPrevious :<C-U>edit `=<SID>FileByOffset(-v:count1)`<CR> + +nmap ]o <Plug>unimpairedONext +nmap [o <Plug>unimpairedOPrevious + +nmap [, :call search('^[<=>]\{7\}','bW')<CR> +nmap ], :call search('^[<=>]\{7\}','W')<CR> +omap [, V:call search('^[<=>]\{7\}','bW')<CR> +omap ], V:call search('^[<=>]\{7\}','W')<CR> +xmap [, :<C-U>exe 'norm! gv'<Bar>call search('^[<=>]\{7\}','bW')<CR> +xmap ], :<C-U>exe 'norm! gv'<Bar>call search('^[<=>]\{7\}','W')<CR> +nmap [< :call search('^<<<<<<<','bW')<CR> +nmap [= :call search('^=======','bW')<CR> +nmap [> :call search('^>>>>>>>','bW')<CR> +nmap ]< :call search('^<<<<<<<','W')<CR> +nmap ]= :call search('^=======','W')<CR> +nmap ]> :call search('^>>>>>>>','W')<CR> +xmap [< :<C-U>exe 'norm! gv'<Bar>call search('^<<<<<<<','bW')<CR> +xmap [= :<C-U>exe 'norm! gv'<Bar>call search('^=======','bW')<CR> +xmap [> :<C-U>exe 'norm! gv'<Bar>call search('^>>>>>>>','bW')<CR> +xmap ]< :<C-U>exe 'norm! gv'<Bar>call search('^<<<<<<<','W')<CR> +xmap ]= :<C-U>exe 'norm! gv'<Bar>call search('^=======','W')<CR> +xmap ]> :<C-U>exe 'norm! gv'<Bar>call search('^>>>>>>>','W')<CR> +omap [< V:call search('^<<<<<<<','bW')<CR> +omap [= V:call search('^=======','bW')<CR> +omap [> V:call search('^>>>>>>>','bW')<CR> +omap ]< V:call search('^<<<<<<<','W')<CR> +omap ]= V:call search('^=======','W')<CR> +omap ]> V:call search('^>>>>>>>','W')<CR> + +" }}}1 +" Line operations {{{1 + +function! s:BlankUp(count) abort + put!=repeat(nr2char(10), a:count) + ']+1 + silent! call repeat#set("\<Plug>unimpairedBlankUp", a:count) +endfunction + +function! s:BlankDown(count) abort + put =repeat(nr2char(10), a:count) + '[-1 + silent! call repeat#set("\<Plug>unimpairedBlankDown", a:count) +endfunction + +nnoremap <silent> <Plug>unimpairedBlankUp :<C-U>call <SID>BlankUp(v:count1)<CR> +nnoremap <silent> <Plug>unimpairedBlankDown :<C-U>call <SID>BlankDown(v:count1)<CR> + +nmap [<Space> <Plug>unimpairedBlankUp +nmap ]<Space> <Plug>unimpairedBlankDown + +function! s:Move(cmd, count, map) abort + normal! m` + exe 'move'.a:cmd.a:count + norm! `` + call repeat#set("\<Plug>unimpairedMove".a:map, a:count) +endfunction + +nnoremap <silent> <Plug>unimpairedMoveUp :<C-U>call <SID>Move('--',v:count1,'Up')<CR> +nnoremap <silent> <Plug>unimpairedMoveDown :<C-U>call <SID>Move('+',v:count1,'Down')<CR> +xnoremap <silent> <Plug>unimpairedMoveUp :<C-U>exe 'normal! m`'<Bar>exe '''<,''>move--'.v:count1<CR>`` +xnoremap <silent> <Plug>unimpairedMoveDown :<C-U>exe 'normal! m`'<Bar>exe '''<,''>move''>+'.v:count1<CR>`` + +nmap [e <Plug>unimpairedMoveUp +nmap ]e <Plug>unimpairedMoveDown +xmap [e <Plug>unimpairedMoveUp +xmap ]e <Plug>unimpairedMoveDown + +" }}}1 +" Encoding and decoding {{{1 + +function! s:StringEncode(str) + let map = {"\n": 'n', "\r": 'r', "\t": 't', "\b": 'b', "\f": '\f', '"': '"', '\': '\'} + return substitute(a:str,"[\001-\033\\\\\"]",'\="\\".get(map,submatch(0),printf("%03o",char2nr(submatch(0))))','g') +endfunction + +function! s:StringDecode(str) + let map = {'n': "\n", 'r': "\r", 't': "\t", 'b': "\b", 'f': "\f", 'e': "\e", 'a': "\001", 'v': "\013"} + let str = a:str + if str =~ '^\s*".\{-\}\\\@<!\%(\\\\\)*"\s*\n\=$' + let str = substitute(substitute(str,'^\s*\zs"','',''),'"\ze\s*\n\=$','','') + endif + let str = substitute(str,'\\n\%(\n$\)\=','\n','g') + return substitute(str,'\\\(\o\{1,3\}\|x\x\{1,2\}\|u\x\{1,4\}\|.\)','\=get(map,submatch(1),submatch(1) =~? "^[0-9xu]" ? nr2char("0".substitute(submatch(1),"^[Uu]","x","")) : submatch(1))','g') +endfunction + +function! s:UrlEncode(str) + return substitute(a:str,'[^A-Za-z0-9_.~-]','\="%".printf("%02X",char2nr(submatch(0)))','g') +endfunction + +function! s:UrlDecode(str) + let str = substitute(substitute(substitute(a:str,'%0[Aa]\n$','%0A',''),'%0[Aa]','\n','g'),'+',' ','g') + return substitute(str,'%\(\x\x\)','\=nr2char("0x".submatch(1))','g') +endfunction + +" HTML entities {{{2 + +let g:unimpaired_html_entities = { + \ 'nbsp': 160, 'iexcl': 161, 'cent': 162, 'pound': 163, + \ 'curren': 164, 'yen': 165, 'brvbar': 166, 'sect': 167, + \ 'uml': 168, 'copy': 169, 'ordf': 170, 'laquo': 171, + \ 'not': 172, 'shy': 173, 'reg': 174, 'macr': 175, + \ 'deg': 176, 'plusmn': 177, 'sup2': 178, 'sup3': 179, + \ 'acute': 180, 'micro': 181, 'para': 182, 'middot': 183, + \ 'cedil': 184, 'sup1': 185, 'ordm': 186, 'raquo': 187, + \ 'frac14': 188, 'frac12': 189, 'frac34': 190, 'iquest': 191, + \ 'Agrave': 192, 'Aacute': 193, 'Acirc': 194, 'Atilde': 195, + \ 'Auml': 196, 'Aring': 197, 'AElig': 198, 'Ccedil': 199, + \ 'Egrave': 200, 'Eacute': 201, 'Ecirc': 202, 'Euml': 203, + \ 'Igrave': 204, 'Iacute': 205, 'Icirc': 206, 'Iuml': 207, + \ 'ETH': 208, 'Ntilde': 209, 'Ograve': 210, 'Oacute': 211, + \ 'Ocirc': 212, 'Otilde': 213, 'Ouml': 214, 'times': 215, + \ 'Oslash': 216, 'Ugrave': 217, 'Uacute': 218, 'Ucirc': 219, + \ 'Uuml': 220, 'Yacute': 221, 'THORN': 222, 'szlig': 223, + \ 'agrave': 224, 'aacute': 225, 'acirc': 226, 'atilde': 227, + \ 'auml': 228, 'aring': 229, 'aelig': 230, 'ccedil': 231, + \ 'egrave': 232, 'eacute': 233, 'ecirc': 234, 'euml': 235, + \ 'igrave': 236, 'iacute': 237, 'icirc': 238, 'iuml': 239, + \ 'eth': 240, 'ntilde': 241, 'ograve': 242, 'oacute': 243, + \ 'ocirc': 244, 'otilde': 245, 'ouml': 246, 'divide': 247, + \ 'oslash': 248, 'ugrave': 249, 'uacute': 250, 'ucirc': 251, + \ 'uuml': 252, 'yacute': 253, 'thorn': 254, 'yuml': 255, + \ 'OElig': 338, 'oelig': 339, 'Scaron': 352, 'scaron': 353, + \ 'Yuml': 376, 'circ': 710, 'tilde': 732, 'ensp': 8194, + \ 'emsp': 8195, 'thinsp': 8201, 'zwnj': 8204, 'zwj': 8205, + \ 'lrm': 8206, 'rlm': 8207, 'ndash': 8211, 'mdash': 8212, + \ 'lsquo': 8216, 'rsquo': 8217, 'sbquo': 8218, 'ldquo': 8220, + \ 'rdquo': 8221, 'bdquo': 8222, 'dagger': 8224, 'Dagger': 8225, + \ 'permil': 8240, 'lsaquo': 8249, 'rsaquo': 8250, 'euro': 8364, + \ 'fnof': 402, 'Alpha': 913, 'Beta': 914, 'Gamma': 915, + \ 'Delta': 916, 'Epsilon': 917, 'Zeta': 918, 'Eta': 919, + \ 'Theta': 920, 'Iota': 921, 'Kappa': 922, 'Lambda': 923, + \ 'Mu': 924, 'Nu': 925, 'Xi': 926, 'Omicron': 927, + \ 'Pi': 928, 'Rho': 929, 'Sigma': 931, 'Tau': 932, + \ 'Upsilon': 933, 'Phi': 934, 'Chi': 935, 'Psi': 936, + \ 'Omega': 937, 'alpha': 945, 'beta': 946, 'gamma': 947, + \ 'delta': 948, 'epsilon': 949, 'zeta': 950, 'eta': 951, + \ 'theta': 952, 'iota': 953, 'kappa': 954, 'lambda': 955, + \ 'mu': 956, 'nu': 957, 'xi': 958, 'omicron': 959, + \ 'pi': 960, 'rho': 961, 'sigmaf': 962, 'sigma': 963, + \ 'tau': 964, 'upsilon': 965, 'phi': 966, 'chi': 967, + \ 'psi': 968, 'omega': 969, 'thetasym': 977, 'upsih': 978, + \ 'piv': 982, 'bull': 8226, 'hellip': 8230, 'prime': 8242, + \ 'Prime': 8243, 'oline': 8254, 'frasl': 8260, 'weierp': 8472, + \ 'image': 8465, 'real': 8476, 'trade': 8482, 'alefsym': 8501, + \ 'larr': 8592, 'uarr': 8593, 'rarr': 8594, 'darr': 8595, + \ 'harr': 8596, 'crarr': 8629, 'lArr': 8656, 'uArr': 8657, + \ 'rArr': 8658, 'dArr': 8659, 'hArr': 8660, 'forall': 8704, + \ 'part': 8706, 'exist': 8707, 'empty': 8709, 'nabla': 8711, + \ 'isin': 8712, 'notin': 8713, 'ni': 8715, 'prod': 8719, + \ 'sum': 8721, 'minus': 8722, 'lowast': 8727, 'radic': 8730, + \ 'prop': 8733, 'infin': 8734, 'ang': 8736, 'and': 8743, + \ 'or': 8744, 'cap': 8745, 'cup': 8746, 'int': 8747, + \ 'there4': 8756, 'sim': 8764, 'cong': 8773, 'asymp': 8776, + \ 'ne': 8800, 'equiv': 8801, 'le': 8804, 'ge': 8805, + \ 'sub': 8834, 'sup': 8835, 'nsub': 8836, 'sube': 8838, + \ 'supe': 8839, 'oplus': 8853, 'otimes': 8855, 'perp': 8869, + \ 'sdot': 8901, 'lceil': 8968, 'rceil': 8969, 'lfloor': 8970, + \ 'rfloor': 8971, 'lang': 9001, 'rang': 9002, 'loz': 9674, + \ 'spades': 9824, 'clubs': 9827, 'hearts': 9829, 'diams': 9830, + \ 'apos': 39} + +" }}}2 + +function! s:XmlEncode(str) + let str = a:str + let str = substitute(str,'&','\&','g') + let str = substitute(str,'<','\<','g') + let str = substitute(str,'>','\>','g') + let str = substitute(str,'"','\"','g') + return str +endfunction + +function! s:XmlEntityDecode(str) + let str = substitute(a:str,'\c&#\%(0*38\|x0*26\);','&','g') + let str = substitute(str,'\c&#\(\d\+\);','\=nr2char(submatch(1))','g') + let str = substitute(str,'\c&#\(x\x\+\);','\=nr2char("0".submatch(1))','g') + let str = substitute(str,'\c'',"'",'g') + let str = substitute(str,'\c"','"','g') + let str = substitute(str,'\c>','>','g') + let str = substitute(str,'\c<','<','g') + let str = substitute(str,'\C&\(\%(amp;\)\@!\w*\);','\=nr2char(get(g:unimpaired_html_entities,submatch(1),63))','g') + return substitute(str,'\c&','\&','g') +endfunction + +function! s:XmlDecode(str) + let str = substitute(a:str,'<\%([[:alnum:]-]\+=\%("[^"]*"\|''[^'']*''\)\|.\)\{-\}>','','g') + return s:XmlEntityDecode(str) +endfunction + +function! s:Transform(algorithm,type) + let sel_save = &selection + let cb_save = &clipboard + set selection=inclusive clipboard-=unnamed clipboard-=unnamedplus + let reg_save = @@ + if a:type =~ '^\d\+$' + silent exe 'norm! ^v'.a:type.'$hy' + elseif a:type =~ '^.$' + silent exe "normal! `<" . a:type . "`>y" + elseif a:type == 'line' + silent exe "normal! '[V']y" + elseif a:type == 'block' + silent exe "normal! `[\<C-V>`]y" + else + silent exe "normal! `[v`]y" + endif + let @@ = s:{a:algorithm}(@@) + norm! gvp + let @@ = reg_save + let &selection = sel_save + let &clipboard = cb_save + if a:type =~ '^\d\+$' + silent! call repeat#set("\<Plug>unimpairedLine".a:algorithm,a:type) + endif +endfunction + +function! s:TransformOpfunc(type) + return s:Transform(s:encode_algorithm, a:type) +endfunction + +function! s:TransformSetup(algorithm) + let s:encode_algorithm = a:algorithm + let &opfunc = matchstr(expand('<sfile>'), '<SNR>\d\+_').'TransformOpfunc' +endfunction + +function! s:MapTransform(algorithm, key) + exe 'nnoremap <silent> <Plug>unimpaired' .a:algorithm.' :<C-U>call <SID>TransformSetup("'.a:algorithm.'")<CR>g@' + exe 'xnoremap <silent> <Plug>unimpaired' .a:algorithm.' :<C-U>call <SID>Transform("'.a:algorithm.'",visualmode())<CR>' + exe 'nnoremap <silent> <Plug>unimpairedLine'.a:algorithm.' :<C-U>call <SID>Transform("'.a:algorithm.'",v:count1)<CR>' + exe 'nmap '.a:key.' <Plug>unimpaired'.a:algorithm + exe 'xmap '.a:key.' <Plug>unimpaired'.a:algorithm + exe 'nmap '.a:key.a:key[strlen(a:key)-1].' <Plug>unimpairedLine'.a:algorithm +endfunction + +call s:MapTransform('StringEncode','[y') +call s:MapTransform('StringDecode',']y') +call s:MapTransform('UrlEncode','[u') +call s:MapTransform('UrlDecode',']u') +call s:MapTransform('XmlEncode','[x') +call s:MapTransform('XmlDecode',']x') + +" }}}1 + +" vim:set sw=2 sts=2: diff --git a/vim/plugin/vim-rspec.rb b/vim/plugin/vim-rspec.rb new file mode 100644 index 0000000..4d7f4c3 --- /dev/null +++ b/vim/plugin/vim-rspec.rb @@ -0,0 +1,32 @@ +require "rubygems" +require "hpricot" + +doc = Hpricot(STDIN.read) +h1 = (doc/"h1") +classes = {"spec passed"=>"+","spec failed"=>"-","spec not_implemented"=>"#"} + +puts "* #{h1.inner_html}" + +stats = (doc/"script").select {|script| script.innerHTML =~ /duration|totals/ }.map {|script| script.inner_html.scan(/".*"/).first.gsub(/<\/?strong>/,"") } +stats.each do |stat| + puts "* #{stat.gsub(/\"/,'')}" +end +puts "* Parsed with Hpricot (http://wiki.github.com/why/hpricot)" +puts " " + +(doc/"div[@class='example_group']").each do |example| + puts "[#{(example/"dl/dt").inner_html}]" + (example/"dd").each do |dd| + txt = (dd/"span:first").inner_html + puts "#{classes[dd[:class]]} #{txt}" + next if dd[:class]!="spec failed" + failure = (dd/"div[@class='failure']") + msg = (failure/"div[@class='message']/pre").inner_html + back = (failure/"div[@class='backtrace']/pre").inner_html + ruby = (failure/"pre[@class='ruby']/code").inner_html.scan(/(<span class="linenum">)(\d+)(<\/span>)([^<]+)/).map {|elem| " "+elem[1]+": "+elem[3].chomp+"\n"}.join + puts " #{msg}" + puts " #{back}" + puts ruby + end + puts " " +end diff --git a/vim/plugin/vim-rspec.vim b/vim/plugin/vim-rspec.vim new file mode 100644 index 0000000..edc7145 --- /dev/null +++ b/vim/plugin/vim-rspec.vim @@ -0,0 +1,174 @@ +" +" Vim Rspec +" Last change: March 5 2009 +" Version> 0.0.5 +" Maintainer: Eustáquio 'TaQ' Rangel +" License: GPL +" URL: git://github.com/taq/vim-rspec +" +" Script to run the spec command inside Vim +" To install, unpack the files on your ~/.vim directory and source it +" +" The following options can be set/overridden in your .vimrc +" * g:RspecXSLPath :: Path to xsl file +" * g:RspecRBFilePath :: Path to vim-rspec.rb +" * g:RspecBin :: Rspec binary command (in rspec 2 this is 'rspec') +" * g:RspecOpts :: Opts to send to rspec call + +let s:xsltproc_cmd = "" +let s:grep_cmd = "" +let s:hpricot_cmd = "" +let s:xslt = 0 +let s:hpricot = 0 +let s:helper_dir = expand("<sfile>:h") + +function! s:find_xslt() + return system("xsltproc --version | head -n1") +endfunction + +function! s:find_grep() + return system("grep --version | head -n1") +endfunction + +function! s:find_hpricot() + return system("gem search -i hpricot") +endfunction + +function! s:error_msg(msg) + echohl ErrorMsg + echo a:msg + echohl None +endfunction + +function! s:notice_msg(msg) + echohl MoreMsg + echo a:msg + echohl None +endfunction + +function! s:fetch(varname, default) + if exists("g:".a:varname) + return eval("g:".a:varname) + else + return a:default + endif +endfunction + +function! s:RunSpecMain(type) + if len(s:xsltproc_cmd)<1 + let s:xsltproc_cmd = s:find_xslt() + let s:xslt = match(s:xsltproc_cmd,'\d')>=0 + end + if len(s:hpricot_cmd)<1 + let s:hpricot_cmd = s:find_hpricot() + let s:hpricot = match(s:hpricot_cmd,'true')>=0 + end + if !s:hpricot && !s:xslt + call s:error_msg("You need the hpricot gem or xsltproc to run this script.") + return + end + if len(s:grep_cmd)<1 + let s:grep_cmd = s:find_grep() + if match(s:grep_cmd,'\d')<0 + call s:error_msg("You need grep to run this script.") + return + end + end + let l:bufn = bufname("%") + + " find the installed rspec command + let l:default_cmd = "" + if executable("spec")==1 + let l:default_cmd = "spec" + elseif executable("rspec")==1 + let l:default_cmd = "rspec" + end + + " filters + let l:xsl = s:fetch("RspecXSLPath", s:helper_dir."/vim-rspec.xsl") + let l:rubys = s:fetch("RspecRBPath", s:helper_dir."/vim-rspec.rb") + + " hpricot gets the priority + let l:type = s:hpricot ? "hpricot" : "xsltproc" + let l:filter = s:hpricot ? "ruby ".l:rubys : "xsltproc --novalid --html ".l:xsl." - " + + " run just the current file + if a:type=="file" + if match(l:bufn,'_spec.rb')>=0 + call s:notice_msg("Running spec on the current file with ".l:type." ...") + let l:spec_bin = s:fetch("RspecBin",l:default_cmd) + let l:spec_opts = s:fetch("RspecOpts", "") + let l:spec = l:spec_bin . " " . l:spec_opts . " -f h " . l:bufn + else + call s:error_msg("Seems ".l:bufn." is not a *_spec.rb file") + return + end + else + let l:dir = expand("%:p:h") + if isdirectory(l:dir."/spec")>0 + call s:notice_msg("Running spec on the spec directory with ".l:type." ...") + else + " try to find a spec directory on the current path + let l:tokens = split(l:dir,"/") + let l:dir = "" + for l:item in l:tokens + call remove(l:tokens,-1) + let l:path = "/".join(l:tokens,"/")."/spec" + if isdirectory(l:path) + let l:dir = l:path + break + end + endfor + if len(l:dir)>0 + call s:notice_msg("Running spec with ".l:type." on the spec directory found (".l:dir.") ...") + else + call s:error_msg("No ".l:dir."/spec directory found") + return + end + end + if isdirectory(l:dir)<0 + call s:error_msg("Could not find the ".l:dir." directory.") + return + end + let l:spec = s:fetch("RspecBin", "spec") . s:fetch("RspecOpts", "") + let l:spec = l:spec . " -f h " . l:dir . " -p **/*_spec.rb" + end + + " run the spec command + let s:cmd = l:spec." | ".l:filter." 2> /dev/null | grep \"^[-\+\[\\#\\* ]\"" + echo + + " put the result on a new buffer + silent exec "new" + setl buftype=nofile + silent exec "r! ".s:cmd + setl syntax=vim-rspec + silent exec "nnoremap <buffer> <cr> :call <SID>TryToOpen()<cr>" + silent exec "nnoremap <buffer> q :q<CR>" + setl foldmethod=expr + setl foldexpr=getline(v:lnum)=~'^\+' + setl foldtext=\"+--\ \".string(v:foldend-v:foldstart+1).\"\ passed\ \" + call cursor(1,1) +endfunction + +function! s:TryToOpen() + let l:line = getline(".") + if match(l:line,'^ [\/\.]')<0 + call s:error_msg("No file found.") + return + end + let l:tokens = split(l:line,":") + silent exec "sp ".substitute(l:tokens[0],'/^\s\+',"","") + call cursor(l:tokens[1],1) +endfunction + +function! RunSpec() + call s:RunSpecMain("file") +endfunction + +function! RunSpecs() + call s:RunSpecMain("dir") +endfunction + +command! RunSpec call RunSpec() +command! RunSpecs call RunSpecs() diff --git a/vim/plugin/vim-rspec.xsl b/vim/plugin/vim-rspec.xsl new file mode 100644 index 0000000..432dc51 --- /dev/null +++ b/vim/plugin/vim-rspec.xsl @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> +<xsl:output method="text"/> + +<xsl:template match="/"> + <xsl:text>* Rspec Results </xsl:text> + <xsl:text>* Parsed with xsltproc (http://www.xmlsoft.org/XSLT/xsltproc2.html) </xsl:text> + <xsl:text> </xsl:text> + <xsl:apply-templates select="html/body/div[@class='rspec-report']/div[@class='results']"/> +</xsl:template> + +<xsl:template match="div[@class='rspec-report']"> + <xsl:apply-templates/> +</xsl:template> + +<xsl:template match="div[@class='example_group']"> + <xsl:text>[</xsl:text><xsl:value-of select="dl/dt"/><xsl:text>]</xsl:text> + <xsl:text> </xsl:text> + <xsl:apply-templates select="dl/dd"/> + <xsl:text> </xsl:text> +</xsl:template> + +<xsl:template match="dd[@class='spec passed']"> + <xsl:text>+ </xsl:text> + <xsl:value-of select="span"/> + <xsl:text> </xsl:text> +</xsl:template> + +<xsl:template match="dd[@class='spec failed']"> + <xsl:text>- </xsl:text> + <xsl:value-of select="span"/> + <xsl:text> </xsl:text> + <xsl:apply-templates select="div"/> +</xsl:template> + +<xsl:template match="dd[@class='spec not_implemented']"> + <xsl:text># </xsl:text> + <xsl:value-of select="span"/> + <xsl:text> </xsl:text> +</xsl:template> + +<xsl:template match="dd[@class='spec failed']/div[@class='failure']"> + <xsl:text> </xsl:text><xsl:value-of select="div[@class='message']/pre"/> + <xsl:text> </xsl:text> + <xsl:text> </xsl:text><xsl:value-of select="div[@class='backtrace']/pre"/> + <xsl:text> </xsl:text> + <xsl:apply-templates select="pre[@class='ruby']/code"/> +</xsl:template> + +<xsl:template match="code"> + <xsl:value-of select="text()"/> + <xsl:text> </xsl:text> +</xsl:template> + +</xsl:stylesheet> |