aboutsummaryrefslogtreecommitdiff
path: root/vim/autoload
diff options
context:
space:
mode:
authorBen Beltran <ben@freshout.us>2012-10-08 11:44:10 -0500
committerBen Beltran <ben@freshout.us>2012-10-08 11:44:10 -0500
commit0d23b6e515a01a5782532351821ebfa11f3d6cf2 (patch)
treeaa395b7e50ccb533d6b48b809016ac06f2d5bc8c /vim/autoload
parenta91731eac872b7837c2821341db5888702125cef (diff)
Add vim again :)
Diffstat (limited to 'vim/autoload')
-rw-r--r--vim/autoload/.DS_Storebin0 -> 6148 bytes
-rw-r--r--vim/autoload/Align.vim1029
-rw-r--r--vim/autoload/AlignMaps.vim330
-rwxr-xr-xvim/autoload/EasyMotion.vim573
-rw-r--r--vim/autoload/ZoomWin.vim380
-rw-r--r--vim/autoload/conque_term.vim1590
-rw-r--r--vim/autoload/delimitMate.vim586
-rw-r--r--vim/autoload/pathogen.vim250
-rw-r--r--vim/autoload/rails.vim4560
-rw-r--r--vim/autoload/snipMate.vim435
-rw-r--r--vim/autoload/syntastic.vim24
-rw-r--r--vim/autoload/syntastic/c.vim171
-rw-r--r--vim/autoload/tagbar.vim3099
-rw-r--r--vim/autoload/togglebg.vim55
14 files changed, 13082 insertions, 0 deletions
diff --git a/vim/autoload/.DS_Store b/vim/autoload/.DS_Store
new file mode 100644
index 0000000..5008ddf
--- /dev/null
+++ b/vim/autoload/.DS_Store
Binary files differ
diff --git a/vim/autoload/Align.vim b/vim/autoload/Align.vim
new file mode 100644
index 0000000..bce3542
--- /dev/null
+++ b/vim/autoload/Align.vim
@@ -0,0 +1,1029 @@
+" Align: tool to align multiple fields based on one or more separators
+" Author: Charles E. Campbell, Jr.
+" Date: Mar 03, 2009
+" Version: 35
+" 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 exists("g:loaded_Align") || &cp
+ finish
+endif
+let g:loaded_Align = "v35"
+if v:version < 700
+ echohl WarningMsg
+ echo "***warning*** this version of Align needs vim 7.0"
+ echohl Normal
+ finish
+endif
+let s:keepcpo= &cpo
+set cpo&vim
+"DechoTabOn
+
+" ---------------------------------------------------------------------
+" Debugging Support: {{{1
+"if !exists("g:loaded_Decho") | runtime plugin/Decho.vim | endif
+
+" ---------------------------------------------------------------------
+" Options: {{{1
+if !exists("g:Align_xstrlen")
+ if &enc == "latin1" || $LANG == "en_US.UTF-8" || !has("multi_byte")
+ let g:Align_xstrlen= 0
+ else
+ let g:Align_xstrlen= 1
+ endif
+endif
+
+" ---------------------------------------------------------------------
+" Align#AlignCtrl: enter alignment patterns here {{{1
+"
+" Styles = all alignment-break patterns are equivalent
+" C cycle through alignment-break pattern(s)
+" l left-justified alignment
+" r right-justified alignment
+" c center alignment
+" - skip separator, treat as part of field
+" : treat rest of line as field
+" + repeat previous [lrc] style
+" < left justify separators
+" > right justify separators
+" | center separators
+"
+" Builds = s:AlignPat s:AlignCtrl s:AlignPatQty
+" C s:AlignPat s:AlignCtrl s:AlignPatQty
+" p s:AlignPrePad
+" P s:AlignPostPad
+" w s:AlignLeadKeep
+" W s:AlignLeadKeep
+" I s:AlignLeadKeep
+" l s:AlignStyle
+" r s:AlignStyle
+" - s:AlignStyle
+" + s:AlignStyle
+" : s:AlignStyle
+" c s:AlignStyle
+" g s:AlignGPat
+" v s:AlignVPat
+" < s:AlignSep
+" > s:AlignSep
+" | s:AlignSep
+fun! Align#AlignCtrl(...)
+
+" call Dfunc("AlignCtrl(...) a:0=".a:0)
+
+ " save options that will be changed
+ let keep_search = @/
+ let keep_ic = &ic
+
+ " turn ignorecase off
+ set noic
+
+ " clear visual mode so that old visual-mode selections don't
+ " get applied to new invocations of Align().
+ if v:version < 602
+ if !exists("s:Align_gavemsg")
+ let s:Align_gavemsg= 1
+ echomsg "Align needs at least Vim version 6.2 to clear visual-mode selection"
+ endif
+ elseif exists("s:dovisclear")
+" call Decho("clearing visual mode a:0=".a:0." a:1<".a:1.">")
+ let clearvmode= visualmode(1)
+ endif
+
+ " set up a list akin to an argument list
+ if a:0 > 0
+ let A= s:QArgSplitter(a:1)
+ else
+ let A=[0]
+ endif
+
+ if A[0] > 0
+ let style = A[1]
+
+ " Check for bad separator patterns (zero-length matches)
+ " (but zero-length patterns for g/v is ok)
+ if style !~# '[gv]'
+ let ipat= 2
+ while ipat <= A[0]
+ if "" =~ A[ipat]
+ echoerr "AlignCtrl: separator<".A[ipat]."> matches zero-length string"
+ let &ic= keep_ic
+" call Dret("AlignCtrl")
+ return
+ endif
+ let ipat= ipat + 1
+ endwhile
+ endif
+ endif
+
+" call Decho("AlignCtrl() A[0]=".A[0])
+ if !exists("s:AlignStyle")
+ let s:AlignStyle= "l"
+ endif
+ if !exists("s:AlignPrePad")
+ let s:AlignPrePad= 0
+ endif
+ if !exists("s:AlignPostPad")
+ let s:AlignPostPad= 0
+ endif
+ if !exists("s:AlignLeadKeep")
+ let s:AlignLeadKeep= 'w'
+ endif
+
+ if A[0] == 0
+ " ----------------------
+ " List current selection
+ " ----------------------
+ if !exists("s:AlignPatQty")
+ let s:AlignPatQty= 0
+ endif
+ echo "AlignCtrl<".s:AlignCtrl."> qty=".s:AlignPatQty." AlignStyle<".s:AlignStyle."> Padding<".s:AlignPrePad."|".s:AlignPostPad."> LeadingWS=".s:AlignLeadKeep." AlignSep=".s:AlignSep
+" call Decho("AlignCtrl<".s:AlignCtrl."> qty=".s:AlignPatQty." AlignStyle<".s:AlignStyle."> Padding<".s:AlignPrePad."|".s:AlignPostPad."> LeadingWS=".s:AlignLeadKeep." AlignSep=".s:AlignSep)
+ if exists("s:AlignGPat") && !exists("s:AlignVPat")
+ echo "AlignGPat<".s:AlignGPat.">"
+ elseif !exists("s:AlignGPat") && exists("s:AlignVPat")
+ echo "AlignVPat<".s:AlignVPat.">"
+ elseif exists("s:AlignGPat") && exists("s:AlignVPat")
+ echo "AlignGPat<".s:AlignGPat."> AlignVPat<".s:AlignVPat.">"
+ endif
+ let ipat= 1
+ while ipat <= s:AlignPatQty
+ echo "Pat".ipat."<".s:AlignPat_{ipat}.">"
+" call Decho("Pat".ipat."<".s:AlignPat_{ipat}.">")
+ let ipat= ipat + 1
+ endwhile
+
+ else
+ " ----------------------------------
+ " Process alignment control settings
+ " ----------------------------------
+" call Decho("process the alignctrl settings")
+" call Decho("style<".style.">")
+
+ if style ==? "default"
+ " Default: preserve initial leading whitespace, left-justified,
+ " alignment on '=', one space padding on both sides
+ if exists("s:AlignCtrlStackQty")
+ " clear AlignCtrl stack
+ while s:AlignCtrlStackQty > 0
+ call Align#AlignPop()
+ endwhile
+ unlet s:AlignCtrlStackQty
+ endif
+ " Set AlignCtrl to its default value
+ call Align#AlignCtrl("Ilp1P1=<",'=')
+ call Align#AlignCtrl("g")
+ call Align#AlignCtrl("v")
+ let s:dovisclear = 1
+ let &ic = keep_ic
+ let @/ = keep_search
+" call Dret("AlignCtrl")
+ return
+ endif
+
+ if style =~# 'm'
+ " map support: Do an AlignPush now and the next call to Align()
+ " will do an AlignPop at exit
+" call Decho("style case m: do AlignPush")
+ call Align#AlignPush()
+ let s:DoAlignPop= 1
+ endif
+
+ " = : record a list of alignment patterns that are equivalent
+ if style =~# "="
+" call Decho("style case =: record list of equiv alignment patterns")
+ let s:AlignCtrl = '='
+ if A[0] >= 2
+ let s:AlignPatQty= 1
+ let s:AlignPat_1 = A[2]
+ let ipat = 3
+ while ipat <= A[0]
+ let s:AlignPat_1 = s:AlignPat_1.'\|'.A[ipat]
+ let ipat = ipat + 1
+ endwhile
+ let s:AlignPat_1= '\('.s:AlignPat_1.'\)'
+" call Decho("AlignCtrl<".s:AlignCtrl."> AlignPat<".s:AlignPat_1.">")
+ endif
+
+ "c : cycle through alignment pattern(s)
+ elseif style =~# 'C'
+" call Decho("style case C: cycle through alignment pattern(s)")
+ let s:AlignCtrl = 'C'
+ if A[0] >= 2
+ let s:AlignPatQty= A[0] - 1
+ let ipat = 1
+ while ipat < A[0]
+ let s:AlignPat_{ipat}= A[ipat+1]
+" call Decho("AlignCtrl<".s:AlignCtrl."> AlignQty=".s:AlignPatQty." AlignPat_".ipat."<".s:AlignPat_{ipat}.">")
+ let ipat= ipat + 1
+ endwhile
+ endif
+ endif
+
+ if style =~# 'p'
+ let s:AlignPrePad= substitute(style,'^.*p\(\d\+\).*$','\1','')
+" call Decho("style case p".s:AlignPrePad.": pre-separator padding")
+ if s:AlignPrePad == ""
+ echoerr "AlignCtrl: 'p' needs to be followed by a numeric argument'
+ let @/ = keep_search
+ let &ic= keep_ic
+" call Dret("AlignCtrl")
+ return
+ endif
+ endif
+
+ if style =~# 'P'
+ let s:AlignPostPad= substitute(style,'^.*P\(\d\+\).*$','\1','')
+" call Decho("style case P".s:AlignPostPad.": post-separator padding")
+ if s:AlignPostPad == ""
+ echoerr "AlignCtrl: 'P' needs to be followed by a numeric argument'
+ let @/ = keep_search
+ let &ic= keep_ic
+" call Dret("AlignCtrl")
+ return
+ endif
+ endif
+
+ if style =~# 'w'
+" call Decho("style case w: ignore leading whitespace")
+ let s:AlignLeadKeep= 'w'
+ elseif style =~# 'W'
+" call Decho("style case w: keep leading whitespace")
+ let s:AlignLeadKeep= 'W'
+ elseif style =~# 'I'
+" call Decho("style case w: retain initial leading whitespace")
+ let s:AlignLeadKeep= 'I'
+ endif
+
+ if style =~# 'g'
+ " first list item is a "g" selector pattern
+" call Decho("style case g: global selector pattern")
+ if A[0] < 2
+ if exists("s:AlignGPat")
+ unlet s:AlignGPat
+" call Decho("unlet s:AlignGPat")
+ endif
+ else
+ let s:AlignGPat= A[2]
+" call Decho("s:AlignGPat<".s:AlignGPat.">")
+ endif
+ elseif style =~# 'v'
+ " first list item is a "v" selector pattern
+" call Decho("style case v: global selector anti-pattern")
+ if A[0] < 2
+ if exists("s:AlignVPat")
+ unlet s:AlignVPat
+" call Decho("unlet s:AlignVPat")
+ endif
+ else
+ let s:AlignVPat= A[2]
+" call Decho("s:AlignVPat<".s:AlignVPat.">")
+ endif
+ endif
+
+ "[-lrc+:] : set up s:AlignStyle
+ if style =~# '[-lrc+:]'
+" call Decho("style case [-lrc+:]: field justification")
+ let s:AlignStyle= substitute(style,'[^-lrc:+]','','g')
+" call Decho("AlignStyle<".s:AlignStyle.">")
+ endif
+
+ "[<>|] : set up s:AlignSep
+ if style =~# '[<>|]'
+" call Decho("style case [-lrc+:]: separator justification")
+ let s:AlignSep= substitute(style,'[^<>|]','','g')
+" call Decho("AlignSep ".s:AlignSep)
+ endif
+ endif
+
+ " sanity
+ if !exists("s:AlignCtrl")
+ let s:AlignCtrl= '='
+ endif
+
+ " restore search and options
+ let @/ = keep_search
+ let &ic= keep_ic
+
+" call Dret("AlignCtrl ".s:AlignCtrl.'p'.s:AlignPrePad.'P'.s:AlignPostPad.s:AlignLeadKeep.s:AlignStyle)
+ return s:AlignCtrl.'p'.s:AlignPrePad.'P'.s:AlignPostPad.s:AlignLeadKeep.s:AlignStyle
+endfun
+
+" ---------------------------------------------------------------------
+" s:MakeSpace: returns a string with spacecnt blanks {{{1
+fun! s:MakeSpace(spacecnt)
+" call Dfunc("MakeSpace(spacecnt=".a:spacecnt.")")
+ let str = ""
+ let spacecnt = a:spacecnt
+ while spacecnt > 0
+ let str = str . " "
+ let spacecnt = spacecnt - 1
+ endwhile
+" call Dret("MakeSpace <".str.">")
+ return str
+endfun
+
+" ---------------------------------------------------------------------
+" Align#Align: align selected text based on alignment pattern(s) {{{1
+fun! Align#Align(hasctrl,...) range
+" call Dfunc("Align#Align(hasctrl=".a:hasctrl.",...) a:0=".a:0)
+
+ " sanity checks
+ if string(a:hasctrl) != "0" && string(a:hasctrl) != "1"
+ echohl Error|echo 'usage: Align#Align(hasctrl<'.a:hasctrl.'> (should be 0 or 1),"separator(s)" (you have '.a:0.') )'|echohl None
+" call Dret("Align#Align")
+ return
+ endif
+ if exists("s:AlignStyle") && s:AlignStyle == ":"
+ echohl Error |echo '(Align#Align) your AlignStyle is ":", which implies "do-no-alignment"!'|echohl None
+" call Dret("Align#Align")
+ return
+ endif
+
+ " set up a list akin to an argument list
+ if a:0 > 0
+ let A= s:QArgSplitter(a:1)
+ else
+ let A=[0]
+ endif
+
+ " if :Align! was used, then the first argument is (should be!) an AlignCtrl string
+ " Note that any alignment control set this way will be temporary.
+ let hasctrl= a:hasctrl
+" call Decho("hasctrl=".hasctrl)
+ if a:hasctrl && A[0] >= 1
+" call Decho("Align! : using A[1]<".A[1]."> for AlignCtrl")
+ if A[1] =~ '[gv]'
+ let hasctrl= hasctrl + 1
+ call Align#AlignCtrl('m')
+ call Align#AlignCtrl(A[1],A[2])
+" call Decho("Align! : also using A[2]<".A[2]."> for AlignCtrl")
+ elseif A[1] !~ 'm'
+ call Align#AlignCtrl(A[1]."m")
+ else
+ call Align#AlignCtrl(A[1])
+ endif
+ endif
+
+ " Check for bad separator patterns (zero-length matches)
+ let ipat= 1 + hasctrl
+ while ipat <= A[0]
+ if "" =~ A[ipat]
+ echoerr "Align: separator<".A[ipat]."> matches zero-length string"
+" call Dret("Align#Align")
+ return
+ endif
+ let ipat= ipat + 1
+ endwhile
+
+ " record current search pattern for subsequent restoration
+ let keep_search= @/
+ let keep_ic = &ic
+ let keep_report= &report
+ set noic report=10000
+
+ if A[0] > hasctrl
+ " Align will accept a list of separator regexps
+" call Decho("A[0]=".A[0].": accepting list of separator regexp")
+
+ if s:AlignCtrl =~# "="
+ "= : consider all separators to be equivalent
+" call Decho("AlignCtrl: record list of equivalent alignment patterns")
+ let s:AlignCtrl = '='
+ let s:AlignPat_1 = A[1 + hasctrl]
+ let s:AlignPatQty= 1
+ let ipat = 2 + hasctrl
+ while ipat <= A[0]
+ let s:AlignPat_1 = s:AlignPat_1.'\|'.A[ipat]
+ let ipat = ipat + 1
+ endwhile
+ let s:AlignPat_1= '\('.s:AlignPat_1.'\)'
+" call Decho("AlignCtrl<".s:AlignCtrl."> AlignPat<".s:AlignPat_1.">")
+
+ elseif s:AlignCtrl =~# 'C'
+ "c : cycle through alignment pattern(s)
+" call Decho("AlignCtrl: cycle through alignment pattern(s)")
+ let s:AlignCtrl = 'C'
+ let s:AlignPatQty= A[0] - hasctrl
+ let ipat = 1
+ while ipat <= s:AlignPatQty
+ let s:AlignPat_{ipat}= A[(ipat + hasctrl)]
+" call Decho("AlignCtrl<".s:AlignCtrl."> AlignQty=".s:AlignPatQty." AlignPat_".ipat."<".s:AlignPat_{ipat}.">")
+ let ipat= ipat + 1
+ endwhile
+ endif
+ endif
+
+ " Initialize so that begline<endline and begcol<endcol.
+ " Ragged right: check if the column associated with '< or '>
+ " is greater than the line's string length -> ragged right.
+ " Have to be careful about visualmode() -- it returns the last visual
+ " mode used whether or not it was used currently.
+ let begcol = virtcol("'<")-1
+ let endcol = virtcol("'>")-1
+ if begcol > endcol
+ let begcol = virtcol("'>")-1
+ let endcol = virtcol("'<")-1
+ endif
+" call Decho("begcol=".begcol." endcol=".endcol)
+ let begline = a:firstline
+ let endline = a:lastline
+ if begline > endline
+ let begline = a:lastline
+ let endline = a:firstline
+ endif
+" call Decho("begline=".begline." endline=".endline)
+ let fieldcnt = 0
+ if (begline == line("'>") && endline == line("'<")) || (begline == line("'<") && endline == line("'>"))
+ let vmode= visualmode()
+" call Decho("vmode=".vmode)
+ if vmode == "\<c-v>"
+ if exists("g:Align_xstrlen") && g:Align_xstrlen
+ let ragged = ( col("'>") > s:Strlen(getline("'>")) || col("'<") > s:Strlen(getline("'<")) )
+ else
+ let ragged = ( col("'>") > strlen(getline("'>")) || col("'<") > strlen(getline("'<")) )
+ endif
+ else
+ let ragged= 1
+ endif
+ else
+ let ragged= 1
+ endif
+ if ragged
+ let begcol= 0
+ endif
+" call Decho("lines[".begline.",".endline."] col[".begcol.",".endcol."] ragged=".ragged." AlignCtrl<".s:AlignCtrl.">")
+
+ " Keep user options
+ let etkeep = &l:et
+ let pastekeep= &l:paste
+ setlocal et paste
+
+ " convert selected range of lines to use spaces instead of tabs
+ " but if first line's initial white spaces are to be retained
+ " then use 'em
+ if begcol <= 0 && s:AlignLeadKeep == 'I'
+ " retain first leading whitespace for all subsequent lines
+ let bgntxt= substitute(getline(begline),'^\(\s*\).\{-}$','\1','')
+" call Decho("retaining 1st leading whitespace: bgntxt<".bgntxt.">")
+ set noet
+ endif
+ exe begline.",".endline."ret"
+
+ " Execute two passes
+ " First pass: collect alignment data (max field sizes)
+ " Second pass: perform alignment
+ let pass= 1
+ while pass <= 2
+" call Decho(" ")
+" call Decho("---- Pass ".pass.": ----")
+
+ let line= begline
+ while line <= endline
+ " Process each line
+ let txt = getline(line)
+" call Decho(" ")
+" call Decho("Pass".pass.": Line ".line." <".txt.">")
+
+ " AlignGPat support: allows a selector pattern (akin to g/selector/cmd )
+ if exists("s:AlignGPat")
+" call Decho("Pass".pass.": AlignGPat<".s:AlignGPat.">")
+ if match(txt,s:AlignGPat) == -1
+" call Decho("Pass".pass.": skipping")
+ let line= line + 1
+ continue
+ endif
+ endif
+
+ " AlignVPat support: allows a selector pattern (akin to v/selector/cmd )
+ if exists("s:AlignVPat")
+" call Decho("Pass".pass.": AlignVPat<".s:AlignVPat.">")
+ if match(txt,s:AlignVPat) != -1
+" call Decho("Pass".pass.": skipping")
+ let line= line + 1
+ continue
+ endif
+ endif
+
+ " Always skip blank lines
+ if match(txt,'^\s*$') != -1
+" call Decho("Pass".pass.": skipping")
+ let line= line + 1
+ continue
+ endif
+
+ " Extract visual-block selected text (init bgntxt, endtxt)
+ if exists("g:Align_xstrlen") && g:Align_xstrlen
+ let txtlen= s:Strlen(txt)
+ else
+ let txtlen= strlen(txt)
+ endif
+ if begcol > 0
+ " Record text to left of selected area
+ let bgntxt= strpart(txt,0,begcol)
+" call Decho("Pass".pass.": record text to left: bgntxt<".bgntxt.">")
+ elseif s:AlignLeadKeep == 'W'
+ let bgntxt= substitute(txt,'^\(\s*\).\{-}$','\1','')
+" call Decho("Pass".pass.": retaining all leading ws: bgntxt<".bgntxt.">")
+ elseif s:AlignLeadKeep == 'w' || !exists("bgntxt")
+ " No beginning text
+ let bgntxt= ""
+" call Decho("Pass".pass.": no beginning text")
+ endif
+ if ragged
+ let endtxt= ""
+ else
+ " Elide any text lying outside selected columnar region
+ let endtxt= strpart(txt,endcol+1,txtlen-endcol)
+ let txt = strpart(txt,begcol,endcol-begcol+1)
+ endif
+" call Decho(" ")
+" call Decho("Pass".pass.": bgntxt<".bgntxt.">")
+" call Decho("Pass".pass.": txt<". txt .">")
+" call Decho("Pass".pass.": endtxt<".endtxt.">")
+ if !exists("s:AlignPat_{1}")
+ echohl Error|echo "no separators specified!"|echohl None
+" call Dret("Align#Align")
+ return
+ endif
+
+ " Initialize for both passes
+ let seppat = s:AlignPat_{1}
+ let ifield = 1
+ let ipat = 1
+ let bgnfield = 0
+ let endfield = 0
+ let alignstyle = s:AlignStyle
+ let doend = 1
+ let newtxt = ""
+ let alignprepad = s:AlignPrePad
+ let alignpostpad= s:AlignPostPad
+ let alignsep = s:AlignSep
+ let alignophold = " "
+ let alignop = "l"
+" call Decho("Pass".pass.": initial alignstyle<".alignstyle."> seppat<".seppat.">")
+
+ " Process each field on the line
+ while doend > 0
+
+ " C-style: cycle through pattern(s)
+ if s:AlignCtrl == 'C' && doend == 1
+ let seppat = s:AlignPat_{ipat}
+" call Decho("Pass".pass.": processing field: AlignCtrl=".s:AlignCtrl." ipat=".ipat." seppat<".seppat.">")
+ let ipat = ipat + 1
+ if ipat > s:AlignPatQty
+ let ipat = 1
+ endif
+ endif
+
+ " cyclic alignment/justification operator handling
+ let alignophold = alignop
+ let alignop = strpart(alignstyle,0,1)
+ if alignop == '+' || doend == 2
+ let alignop= alignophold
+ else
+ let alignstyle = strpart(alignstyle,1).strpart(alignstyle,0,1)
+ let alignopnxt = strpart(alignstyle,0,1)
+ if alignop == ':'
+ let seppat = '$'
+ let doend = 2
+" call Decho("Pass".pass.": alignop<:> case: setting seppat<$> doend==2")
+ endif
+ endif
+
+ " cylic separator alignment specification handling
+ let alignsepop= strpart(alignsep,0,1)
+ let alignsep = strpart(alignsep,1).alignsepop
+
+ " mark end-of-field and the subsequent end-of-separator.
+ " Extend field if alignop is '-'
+ let endfield = match(txt,seppat,bgnfield)
+ let sepfield = matchend(txt,seppat,bgnfield)
+ let skipfield= sepfield
+" call Decho("Pass".pass.": endfield=match(txt<".txt.">,seppat<".seppat.">,bgnfield=".bgnfield.")=".endfield)
+ while alignop == '-' && endfield != -1
+ let endfield = match(txt,seppat,skipfield)
+ let sepfield = matchend(txt,seppat,skipfield)
+ let skipfield = sepfield
+ let alignop = strpart(alignstyle,0,1)
+ let alignstyle= strpart(alignstyle,1).strpart(alignstyle,0,1)
+" call Decho("Pass".pass.": extend field: endfield<".strpart(txt,bgnfield,endfield-bgnfield)."> alignop<".alignop."> alignstyle<".alignstyle.">")
+ endwhile
+ let seplen= sepfield - endfield
+" call Decho("Pass".pass.": seplen=[sepfield=".sepfield."] - [endfield=".endfield."]=".seplen)
+
+ if endfield != -1
+ if pass == 1
+ " ---------------------------------------------------------------------
+ " Pass 1: Update FieldSize to max
+" call Decho("Pass".pass.": before lead/trail remove: field<".strpart(txt,bgnfield,endfield-bgnfield).">")
+ let field = substitute(strpart(txt,bgnfield,endfield-bgnfield),'^\s*\(.\{-}\)\s*$','\1','')
+ if s:AlignLeadKeep == 'W'
+ let field = bgntxt.field
+ let bgntxt= ""
+ endif
+ if exists("g:Align_xstrlen") && g:Align_xstrlen
+ let fieldlen = s:Strlen(field)
+ else
+ let fieldlen = strlen(field)
+ endif
+ let sFieldSize = "FieldSize_".ifield
+ if !exists(sFieldSize)
+ let FieldSize_{ifield}= fieldlen
+" call Decho("Pass".pass.": set FieldSize_{".ifield."}=".FieldSize_{ifield}." <".field.">")
+ elseif fieldlen > FieldSize_{ifield}
+ let FieldSize_{ifield}= fieldlen
+" call Decho("Pass".pass.": oset FieldSize_{".ifield."}=".FieldSize_{ifield}." <".field.">")
+ endif
+ let sSepSize= "SepSize_".ifield
+ if !exists(sSepSize)
+ let SepSize_{ifield}= seplen
+" call Decho(" set SepSize_{".ifield."}=".SepSize_{ifield}." <".field.">")
+ elseif seplen > SepSize_{ifield}
+ let SepSize_{ifield}= seplen
+" call Decho("Pass".pass.": oset SepSize_{".ifield."}=".SepSize_{ifield}." <".field.">")
+ endif
+
+ else
+ " ---------------------------------------------------------------------
+ " Pass 2: Perform Alignment
+ let prepad = strpart(alignprepad,0,1)
+ let postpad = strpart(alignpostpad,0,1)
+ let alignprepad = strpart(alignprepad,1).strpart(alignprepad,0,1)
+ let alignpostpad = strpart(alignpostpad,1).strpart(alignpostpad,0,1)
+ let field = substitute(strpart(txt,bgnfield,endfield-bgnfield),'^\s*\(.\{-}\)\s*$','\1','')
+ if s:AlignLeadKeep == 'W'
+ let field = bgntxt.field
+ let bgntxt= ""
+ endif
+ if doend == 2
+ let prepad = 0
+ let postpad= 0
+ endif
+ if exists("g:Align_xstrlen") && g:Align_xstrlen
+ let fieldlen = s:Strlen(field)
+ else
+ let fieldlen = strlen(field)
+ endif
+ let sep = s:MakeSpace(prepad).strpart(txt,endfield,sepfield-endfield).s:MakeSpace(postpad)
+ if seplen < SepSize_{ifield}
+ if alignsepop == "<"
+ " left-justify separators
+ let sep = sep.s:MakeSpace(SepSize_{ifield}-seplen)
+ elseif alignsepop == ">"
+ " right-justify separators
+ let sep = s:MakeSpace(SepSize_{ifield}-seplen).sep
+ else
+ " center-justify separators
+ let sepleft = (SepSize_{ifield} - seplen)/2
+ let sepright = SepSize_{ifield} - seplen - sepleft
+ let sep = s:MakeSpace(sepleft).sep.s:MakeSpace(sepright)
+ endif
+ endif
+ let spaces = FieldSize_{ifield} - fieldlen
+" call Decho("Pass".pass.": Field #".ifield."<".field."> spaces=".spaces." be[".bgnfield.",".endfield."] pad=".prepad.','.postpad." FS_".ifield."<".FieldSize_{ifield}."> sep<".sep."> ragged=".ragged." doend=".doend." alignop<".alignop.">")
+
+ " Perform alignment according to alignment style justification
+ if spaces > 0
+ if alignop == 'c'
+ " center the field
+ let spaceleft = spaces/2
+ let spaceright= FieldSize_{ifield} - spaceleft - fieldlen
+ let newtxt = newtxt.s:MakeSpace(spaceleft).field.s:MakeSpace(spaceright).sep
+ elseif alignop == 'r'
+ " right justify the field
+ let newtxt= newtxt.s:MakeSpace(spaces).field.sep
+ elseif ragged && doend == 2
+ " left justify rightmost field (no trailing blanks needed)
+ let newtxt= newtxt.field
+ else
+ " left justfiy the field
+ let newtxt= newtxt.field.s:MakeSpace(spaces).sep
+ endif
+ elseif ragged && doend == 2
+ " field at maximum field size and no trailing blanks needed
+ let newtxt= newtxt.field
+ else
+ " field is at maximum field size already
+ let newtxt= newtxt.field.sep
+ endif
+" call Decho("Pass".pass.": newtxt<".newtxt.">")
+ endif " pass 1/2
+
+ " bgnfield indexes to end of separator at right of current field
+ " Update field counter
+ let bgnfield= sepfield
+ let ifield = ifield + 1
+ if doend == 2
+ let doend= 0
+ endif
+ " handle end-of-text as end-of-field
+ elseif doend == 1
+ let seppat = '$'
+ let doend = 2
+ else
+ let doend = 0
+ endif " endfield != -1
+ endwhile " doend loop (as well as regularly separated fields)
+
+ if pass == 2
+ " Write altered line to buffer
+" call Decho("Pass".pass.": bgntxt<".bgntxt."> line=".line)
+" call Decho("Pass".pass.": newtxt<".newtxt.">")
+" call Decho("Pass".pass.": endtxt<".endtxt.">")
+ call setline(line,bgntxt.newtxt.endtxt)
+ endif
+
+ let line = line + 1
+ endwhile " line loop
+
+ let pass= pass + 1
+ endwhile " pass loop
+" call Decho("end of two pass loop")
+
+ " Restore user options
+ let &l:et = etkeep
+ let &l:paste = pastekeep
+
+ if exists("s:DoAlignPop")
+ " AlignCtrl Map support
+ call Align#AlignPop()
+ unlet s:DoAlignPop
+ endif
+
+ " restore current search pattern
+ let @/ = keep_search
+ let &ic = keep_ic
+ let &report = keep_report
+
+" call Dret("Align#Align")
+ return
+endfun
+
+" ---------------------------------------------------------------------
+" Align#AlignPush: this command/function pushes an alignment control string onto a stack {{{1
+fun! Align#AlignPush()
+" call Dfunc("AlignPush()")
+
+ " initialize the stack
+ if !exists("s:AlignCtrlStackQty")
+ let s:AlignCtrlStackQty= 1
+ else
+ let s:AlignCtrlStackQty= s:AlignCtrlStackQty + 1
+ endif
+
+ " construct an AlignCtrlStack entry
+ if !exists("s:AlignSep")
+ let s:AlignSep= ''
+ endif
+ let s:AlignCtrlStack_{s:AlignCtrlStackQty}= s:AlignCtrl.'p'.s:AlignPrePad.'P'.s:AlignPostPad.s:AlignLeadKeep.s:AlignStyle.s:AlignSep
+" call Decho("AlignPush: AlignCtrlStack_".s:AlignCtrlStackQty."<".s:AlignCtrlStack_{s:AlignCtrlStackQty}.">")
+
+ " push [GV] patterns onto their own stack
+ if exists("s:AlignGPat")
+ let s:AlignGPat_{s:AlignCtrlStackQty}= s:AlignGPat
+ else
+ let s:AlignGPat_{s:AlignCtrlStackQty}= ""
+ endif
+ if exists("s:AlignVPat")
+ let s:AlignVPat_{s:AlignCtrlStackQty}= s:AlignVPat
+ else
+ let s:AlignVPat_{s:AlignCtrlStackQty}= ""
+ endif
+
+" call Dret("AlignPush")
+endfun
+
+" ---------------------------------------------------------------------
+" Align#AlignPop: this command/function pops an alignment pattern from a stack {{{1
+" and into the AlignCtrl variables.
+fun! Align#AlignPop()
+" call Dfunc("Align#AlignPop()")
+
+ " sanity checks
+ if !exists("s:AlignCtrlStackQty")
+ echoerr "AlignPush needs to be used prior to AlignPop"
+" call Dret("Align#AlignPop <> : AlignPush needs to have been called first")
+ return ""
+ endif
+ if s:AlignCtrlStackQty <= 0
+ unlet s:AlignCtrlStackQty
+ echoerr "AlignPush needs to be used prior to AlignPop"
+" call Dret("Align#AlignPop <> : AlignPop needs to have been called first")
+ return ""
+ endif
+
+ " pop top of AlignCtrlStack and pass value to AlignCtrl
+ let retval=s:AlignCtrlStack_{s:AlignCtrlStackQty}
+ unlet s:AlignCtrlStack_{s:AlignCtrlStackQty}
+ call Align#AlignCtrl(retval)
+
+ " pop G pattern stack
+ if s:AlignGPat_{s:AlignCtrlStackQty} != ""
+ call Align#AlignCtrl('g',s:AlignGPat_{s:AlignCtrlStackQty})
+ else
+ call Align#AlignCtrl('g')
+ endif
+ unlet s:AlignGPat_{s:AlignCtrlStackQty}
+
+ " pop V pattern stack
+ if s:AlignVPat_{s:AlignCtrlStackQty} != ""
+ call Align#AlignCtrl('v',s:AlignVPat_{s:AlignCtrlStackQty})
+ else
+ call Align#AlignCtrl('v')
+ endif
+
+ unlet s:AlignVPat_{s:AlignCtrlStackQty}
+ let s:AlignCtrlStackQty= s:AlignCtrlStackQty - 1
+
+" call Dret("Align#AlignPop <".retval."> : AlignCtrlStackQty=".s:AlignCtrlStackQty)
+ return retval
+endfun
+
+" ---------------------------------------------------------------------
+" Align#AlignReplaceQuotedSpaces: {{{1
+fun! Align#AlignReplaceQuotedSpaces()
+" call Dfunc("AlignReplaceQuotedSpaces()")
+
+ let l:line = getline(line("."))
+ if exists("g:Align_xstrlen") && g:Align_xstrlen
+ let l:linelen = s:Strlen(l:line)
+ else
+ let l:linelen = strlen(l:line)
+ endif
+ let l:startingPos = 0
+ let l:startQuotePos = 0
+ let l:endQuotePos = 0
+ let l:spacePos = 0
+ let l:quoteRe = '\\\@<!"'
+
+" "call Decho("in replace spaces. line=" . line('.'))
+ while (1)
+ let l:startQuotePos = match(l:line, l:quoteRe, l:startingPos)
+ if (l:startQuotePos < 0)
+" "call Decho("No more quotes to the end of line")
+ break
+ endif
+ let l:endQuotePos = match(l:line, l:quoteRe, l:startQuotePos + 1)
+ if (l:endQuotePos < 0)
+" "call Decho("Mismatched quotes")
+ break
+ endif
+ let l:spaceReplaceRe = '^.\{' . (l:startQuotePos + 1) . '}.\{-}\zs\s\ze.*.\{' . (linelen - l:endQuotePos) . '}$'
+" "call Decho('spaceReplaceRe="' . l:spaceReplaceRe . '"')
+ let l:newStr = substitute(l:line, l:spaceReplaceRe, '%', '')
+ while (l:newStr != l:line)
+" "call Decho('newstr="' . l:newStr . '"')
+ let l:line = l:newStr
+ let l:newStr = substitute(l:line, l:spaceReplaceRe, '%', '')
+ endwhile
+ let l:startingPos = l:endQuotePos + 1
+ endwhile
+ call setline(line('.'), l:line)
+
+" call Dret("AlignReplaceQuotedSpaces")
+endfun
+
+" ---------------------------------------------------------------------
+" s:QArgSplitter: to avoid \ processing by <f-args>, <q-args> is needed. {{{1
+" However, <q-args> doesn't split at all, so this function returns a list
+" of arguments which has been:
+" * split at whitespace
+" * unless inside "..."s. One may escape characters with a backslash inside double quotes.
+" along with a leading length-of-list.
+"
+" Examples: %Align "\"" will align on "s
+" %Align " " will align on spaces
+"
+" The resulting list: qarglist[0] corresponds to a:0
+" qarglist[i] corresponds to a:{i}
+fun! s:QArgSplitter(qarg)
+" call Dfunc("s:QArgSplitter(qarg<".a:qarg.">)")
+
+ if a:qarg =~ '".*"'
+ " handle "..." args, which may include whitespace
+ let qarglist = []
+ let args = a:qarg
+" call Decho("handle quoted arguments: args<".args.">")
+ while args != ""
+ let iarg = 0
+ let arglen = strlen(args)
+" call Decho("args[".iarg."]<".args[iarg]."> arglen=".arglen)
+ " find index to first not-escaped '"'
+ while args[iarg] != '"' && iarg < arglen
+ if args[iarg] == '\'
+ let args= strpart(args,1)
+ endif
+ let iarg= iarg + 1
+ endwhile
+" call Decho("args<".args."> iarg=".iarg." arglen=".arglen)
+
+ if iarg > 0
+ " handle left of quote or remaining section
+" call Decho("handle left of quote or remaining section")
+ if args[iarg] == '"'
+ let qarglist= qarglist + split(strpart(args,0,iarg-1))
+ else
+ let qarglist= qarglist + split(strpart(args,0,iarg))
+ endif
+ let args = strpart(args,iarg)
+ let arglen = strlen(args)
+
+ elseif iarg < arglen && args[0] == '"'
+ " handle "quoted" section
+" call Decho("handle quoted section")
+ let iarg= 1
+ while args[iarg] != '"' && iarg < arglen
+ if args[iarg] == '\'
+ let args= strpart(args,1)
+ endif
+ let iarg= iarg + 1
+ endwhile
+" call Decho("args<".args."> iarg=".iarg." arglen=".arglen)
+ if args[iarg] == '"'
+ call add(qarglist,strpart(args,1,iarg-1))
+ let args= strpart(args,iarg+1)
+ else
+ let qarglist = qarglist + split(args)
+ let args = ""
+ endif
+ endif
+" call Decho("qarglist".string(qarglist)." iarg=".iarg." args<".args.">")
+ endwhile
+
+ else
+ " split at all whitespace
+ let qarglist= split(a:qarg)
+ endif
+
+ let qarglistlen= len(qarglist)
+ let qarglist = insert(qarglist,qarglistlen)
+" call Dret("s:QArgSplitter ".string(qarglist))
+ return qarglist
+endfun
+
+" ---------------------------------------------------------------------
+" s:Strlen: this function returns the length of a string, even if its {{{1
+" using two-byte etc characters.
+" Currently, its only used if g:Align_xstrlen is set to a
+" nonzero value. Solution from Nicolai Weibull, vim docs
+" (:help strlen()), Tony Mechelynck, and my own invention.
+fun! s:Strlen(x)
+" call Dfunc("s:Strlen(x<".a:x.">")
+ if g:Align_xstrlen == 1
+ " number of codepoints (Latin a + combining circumflex is two codepoints)
+ " (comment from TM, solution from NW)
+ let ret= strlen(substitute(a:x,'.','c','g'))
+
+ elseif g:Align_xstrlen == 2
+ " number of spacing codepoints (Latin a + combining circumflex is one spacing
+ " codepoint; a hard tab is one; wide and narrow CJK are one each; etc.)
+ " (comment from TM, solution from TM)
+ let ret=strlen(substitute(a:x, '.\Z', 'x', 'g'))
+
+ elseif g:Align_xstrlen == 3
+ " virtual length (counting, for instance, tabs as anything between 1 and
+ " 'tabstop', wide CJK as 2 rather than 1, Arabic alif as zero when immediately
+ " preceded by lam, one otherwise, etc.)
+ " (comment from TM, solution from me)
+ let modkeep= &l:mod
+ exe "norm! o\<esc>"
+ call setline(line("."),a:x)
+ let ret= virtcol("$") - 1
+ d
+ let &l:mod= modkeep
+
+ else
+ " at least give a decent default
+ ret= strlen(a:x)
+ endif
+" call Dret("s:Strlen ".ret)
+ return ret
+endfun
+
+" ---------------------------------------------------------------------
+" Set up default values: {{{1
+"call Decho("-- Begin AlignCtrl Initialization --")
+call Align#AlignCtrl("default")
+"call Decho("-- End AlignCtrl Initialization --")
+
+" ---------------------------------------------------------------------
+" Restore: {{{1
+let &cpo= s:keepcpo
+unlet s:keepcpo
+" vim: ts=4 fdm=marker
diff --git a/vim/autoload/AlignMaps.vim b/vim/autoload/AlignMaps.vim
new file mode 100644
index 0000000..ace2de8
--- /dev/null
+++ b/vim/autoload/AlignMaps.vim
@@ -0,0 +1,330 @@
+" AlignMaps.vim : support functions for AlignMaps
+" Author: Charles E. Campbell, Jr.
+" Date: Mar 03, 2009
+" Version: 41
+" ---------------------------------------------------------------------
+" Load Once: {{{1
+if &cp || exists("g:loaded_AlignMaps")
+ finish
+endif
+let g:loaded_AlignMaps= "v41"
+let s:keepcpo = &cpo
+set cpo&vim
+
+" =====================================================================
+" Functions: {{{1
+
+" ---------------------------------------------------------------------
+" AlignMaps#WrapperStart: {{{2
+fun! AlignMaps#WrapperStart(vis) range
+" call Dfunc("AlignMaps#WrapperStart(vis=".a:vis.")")
+
+ if a:vis
+ norm! '<ma'>
+ endif
+
+ if line("'y") == 0 || line("'z") == 0 || !exists("s:alignmaps_wrapcnt") || s:alignmaps_wrapcnt <= 0
+" call Decho("wrapper initialization")
+ let s:alignmaps_wrapcnt = 1
+ let s:alignmaps_keepgd = &gdefault
+ let s:alignmaps_keepsearch = @/
+ let s:alignmaps_keepch = &ch
+ let s:alignmaps_keepmy = SaveMark("'y")
+ let s:alignmaps_keepmz = SaveMark("'z")
+ let s:alignmaps_posn = SaveWinPosn(0)
+ " set up fencepost blank lines
+ put =''
+ norm! mz'a
+ put! =''
+ ky
+ let s:alignmaps_zline = line("'z")
+ exe "'y,'zs/@/\177/ge"
+ else
+" call Decho("embedded wrapper")
+ let s:alignmaps_wrapcnt = s:alignmaps_wrapcnt + 1
+ norm! 'yjma'zk
+ endif
+
+ " change some settings to align-standard values
+ set nogd
+ set ch=2
+ AlignPush
+ norm! 'zk
+" call Dret("AlignMaps#WrapperStart : alignmaps_wrapcnt=".s:alignmaps_wrapcnt." my=".line("'y")." mz=".line("'z"))
+endfun
+
+" ---------------------------------------------------------------------
+" AlignMaps#WrapperEnd: {{{2
+fun! AlignMaps#WrapperEnd() range
+" call Dfunc("AlignMaps#WrapperEnd() alignmaps_wrapcnt=".s:alignmaps_wrapcnt." my=".line("'y")." mz=".line("'z"))
+
+ " remove trailing white space introduced by whatever in the modification zone
+ 'y,'zs/ \+$//e
+
+ " restore AlignCtrl settings
+ AlignPop
+
+ let s:alignmaps_wrapcnt= s:alignmaps_wrapcnt - 1
+ if s:alignmaps_wrapcnt <= 0
+ " initial wrapper ending
+ exe "'y,'zs/\177/@/ge"
+
+ " if the 'z line hasn't moved, then go ahead and restore window position
+ let zstationary= s:alignmaps_zline == line("'z")
+
+ " remove fencepost blank lines.
+ " restore 'a
+ norm! 'yjmakdd'zdd
+
+ " restore original 'y, 'z, and window positioning
+ call RestoreMark(s:alignmaps_keepmy)
+ call RestoreMark(s:alignmaps_keepmz)
+ if zstationary > 0
+ call RestoreWinPosn(s:alignmaps_posn)
+" call Decho("restored window positioning")
+ endif
+
+ " restoration of options
+ let &gd= s:alignmaps_keepgd
+ let &ch= s:alignmaps_keepch
+ let @/ = s:alignmaps_keepsearch
+
+ " remove script variables
+ unlet s:alignmaps_keepch
+ unlet s:alignmaps_keepsearch
+ unlet s:alignmaps_keepmy
+ unlet s:alignmaps_keepmz
+ unlet s:alignmaps_keepgd
+ unlet s:alignmaps_posn
+ endif
+
+" call Dret("AlignMaps#WrapperEnd : alignmaps_wrapcnt=".s:alignmaps_wrapcnt." my=".line("'y")." mz=".line("'z"))
+endfun
+
+" ---------------------------------------------------------------------
+" AlignMaps#StdAlign: some semi-standard align calls {{{2
+fun! AlignMaps#StdAlign(mode) range
+" call Dfunc("AlignMaps#StdAlign(mode=".a:mode.")")
+ if a:mode == 1
+ " align on @
+" call Decho("align on @")
+ AlignCtrl mIp1P1=l @
+ 'a,.Align
+ elseif a:mode == 2
+ " align on @, retaining all initial white space on each line
+" call Decho("align on @, retaining all initial white space on each line")
+ AlignCtrl mWp1P1=l @
+ 'a,.Align
+ elseif a:mode == 3
+ " like mode 2, but ignore /* */-style comments
+" call Decho("like mode 2, but ignore /* */-style comments")
+ AlignCtrl v ^\s*/[/*]
+ AlignCtrl mWp1P1=l @
+ 'a,.Align
+ else
+ echoerr "(AlignMaps) AlignMaps#StdAlign doesn't support mode#".a:mode
+ endif
+" call Dret("AlignMaps#StdAlign")
+endfun
+
+" ---------------------------------------------------------------------
+" AlignMaps#CharJoiner: joins lines which end in the given character (spaces {{{2
+" at end are ignored)
+fun! AlignMaps#CharJoiner(chr)
+" call Dfunc("AlignMaps#CharJoiner(chr=".a:chr.")")
+ let aline = line("'a")
+ let rep = line(".") - aline
+ while rep > 0
+ norm! 'a
+ while match(getline(aline),a:chr . "\s*$") != -1 && rep >= 0
+ " while = at end-of-line, delete it and join with next
+ norm! 'a$
+ j!
+ let rep = rep - 1
+ endwhile
+ " update rep(eat) count
+ let rep = rep - 1
+ if rep <= 0
+ " terminate loop if at end-of-block
+ break
+ endif
+ " prepare for next line
+ norm! jma
+ let aline = line("'a")
+ endwhile
+" call Dret("AlignMaps#CharJoiner")
+endfun
+
+" ---------------------------------------------------------------------
+" AlignMaps#Equals: supports \t= and \T= {{{2
+fun! AlignMaps#Equals() range
+" call Dfunc("AlignMaps#Equals()")
+ 'a,'zs/\s\+\([*/+\-%|&\~^]\==\)/ \1/e
+ 'a,'zs@ \+\([*/+\-%|&\~^]\)=@\1=@ge
+ 'a,'zs/==/\="\<Char-0x0f>\<Char-0x0f>"/ge
+ 'a,'zs/\([!<>:]\)=/\=submatch(1)."\<Char-0x0f>"/ge
+ norm g'zk
+ AlignCtrl mIp1P1=l =
+ AlignCtrl g =
+ 'a,'z-1Align
+ 'a,'z-1s@\([*/+\-%|&\~^!=]\)\( \+\)=@\2\1=@ge
+ 'a,'z-1s/\( \+\);/;\1/ge
+ if &ft == "c" || &ft == "cpp"
+" call Decho("exception for ".&ft)
+ 'a,'z-1v/^\s*\/[*/]/s/\/[*/]/@&@/e
+ 'a,'z-1v/^\s*\/[*/]/s/\*\//@&/e
+ if exists("g:mapleader")
+ exe "norm 'zk"
+ call AlignMaps#StdAlign(1)
+ else
+ exe "norm 'zk"
+ call AlignMaps#StdAlign(1)
+ endif
+ 'y,'zs/^\(\s*\) @/\1/e
+ endif
+ 'a,'z-1s/\%x0f/=/ge
+ 'y,'zs/ @//eg
+" call Dret("AlignMaps#Equals")
+endfun
+
+" ---------------------------------------------------------------------
+" AlignMaps#Afnc: useful for splitting one-line function beginnings {{{2
+" into one line per argument format
+fun! AlignMaps#Afnc()
+" call Dfunc("AlignMaps#Afnc()")
+
+ " keep display quiet
+ let chkeep = &ch
+ let gdkeep = &gd
+ let vekeep = &ve
+ set ch=2 nogd ve=
+
+ " will use marks y,z ; save current values
+ let mykeep = SaveMark("'y")
+ let mzkeep = SaveMark("'z")
+
+ " Find beginning of function -- be careful to skip over comments
+ let cmmntid = synIDtrans(hlID("Comment"))
+ let stringid = synIDtrans(hlID("String"))
+ exe "norm! ]]"
+ while search(")","bW") != 0
+" call Decho("line=".line(".")." col=".col("."))
+ let parenid= synIDtrans(synID(line("."),col("."),1))
+ if parenid != cmmntid && parenid != stringid
+ break
+ endif
+ endwhile
+ norm! %my
+ s/(\s*\(\S\)/(\r \1/e
+ exe "norm! `y%"
+ s/)\s*\(\/[*/]\)/)\r\1/e
+ exe "norm! `y%mz"
+ 'y,'zs/\s\+$//e
+ 'y,'zs/^\s\+//e
+ 'y+1,'zs/^/ /
+
+ " insert newline after every comma only one parenthesis deep
+ sil! exe "norm! `y\<right>h"
+ let parens = 1
+ let cmmnt = 0
+ let cmmntline= -1
+ while parens >= 1
+" call Decho("parens=".parens." @a=".@a)
+ exe 'norm! ma "ay`a '
+ if @a == "("
+ let parens= parens + 1
+ elseif @a == ")"
+ let parens= parens - 1
+
+ " comment bypass: /* ... */ or //...
+ elseif cmmnt == 0 && @a == '/'
+ let cmmnt= 1
+ elseif cmmnt == 1
+ if @a == '/'
+ let cmmnt = 2 " //...
+ let cmmntline= line(".")
+ elseif @a == '*'
+ let cmmnt= 3 " /*...
+ else
+ let cmmnt= 0
+ endif
+ elseif cmmnt == 2 && line(".") != cmmntline
+ let cmmnt = 0
+ let cmmntline= -1
+ elseif cmmnt == 3 && @a == '*'
+ let cmmnt= 4
+ elseif cmmnt == 4
+ if @a == '/'
+ let cmmnt= 0 " ...*/
+ elseif @a != '*'
+ let cmmnt= 3
+ endif
+
+ elseif @a == "," && parens == 1 && cmmnt == 0
+ exe "norm! i\<CR>\<Esc>"
+ endif
+ endwhile
+ norm! `y%mz%
+ sil! 'y,'zg/^\s*$/d
+
+ " perform substitutes to mark fields for Align
+ sil! 'y+1,'zv/^\//s/^\s\+\(\S\)/ \1/e
+ sil! 'y+1,'zv/^\//s/\(\S\)\s\+/\1 /eg
+ sil! 'y+1,'zv/^\//s/\* \+/*/ge
+ sil! 'y+1,'zv/^\//s/\w\zs\s*\*/ */ge
+ " func
+ " ws <- declaration -> <-ptr -> <-var-> <-[array][] -> <-glop-> <-end->
+ sil! 'y+1,'zv/^\//s/^\s*\(\(\K\k*\s*\)\+\)\s\+\([(*]*\)\s*\(\K\k*\)\s*\(\(\[.\{-}]\)*\)\s*\(.\{-}\)\=\s*\([,)]\)\s*$/ \1@#\3@\4\5@\7\8/e
+ sil! 'y+1,'z+1g/^\s*\/[*/]/norm! kJ
+ sil! 'y+1,'z+1s%/[*/]%@&@%ge
+ sil! 'y+1,'z+1s%*/%@&%ge
+ AlignCtrl mIp0P0=l @
+ sil! 'y+1,'zAlign
+ sil! 'y,'zs%@\(/[*/]\)@%\t\1 %e
+ sil! 'y,'zs%@\*/% */%e
+ sil! 'y,'zs/@\([,)]\)/\1/
+ sil! 'y,'zs/@/ /
+ AlignCtrl mIlrp0P0= # @
+ sil! 'y+1,'zAlign
+ sil! 'y+1,'zs/#/ /
+ sil! 'y+1,'zs/@//
+ sil! 'y+1,'zs/\(\s\+\)\([,)]\)/\2\1/e
+
+ " Restore
+ call RestoreMark(mykeep)
+ call RestoreMark(mzkeep)
+ let &ch= chkeep
+ let &gd= gdkeep
+ let &ve= vekeep
+
+" call Dret("AlignMaps#Afnc")
+endfun
+
+" ---------------------------------------------------------------------
+" AlignMaps#FixMultiDec: converts a type arg,arg,arg; line to multiple lines {{{2
+fun! AlignMaps#FixMultiDec()
+" call Dfunc("AlignMaps#FixMultiDec()")
+
+ " save register x
+ let xkeep = @x
+ let curline = getline(".")
+" call Decho("curline<".curline.">")
+
+ " Get the type. I'm assuming one type per line (ie. int x; double y; on one line will not be handled properly)
+ let @x=substitute(curline,'^\(\s*[a-zA-Z_ \t][a-zA-Z0-9_ \t]*\)\s\+[(*]*\h.*$','\1','')
+" call Decho("@x<".@x.">")
+
+ " transform line
+ exe 's/,/;\r'.@x.' /ge'
+
+ "restore register x
+ let @x= xkeep
+
+" call Dret("AlignMaps#FixMultiDec : my=".line("'y")." mz=".line("'z"))
+endfun
+
+" ---------------------------------------------------------------------
+" Restore: {{{1
+let &cpo= s:keepcpo
+unlet s:keepcpo
+" vim: ts=4 fdm=marker
diff --git a/vim/autoload/EasyMotion.vim b/vim/autoload/EasyMotion.vim
new file mode 100755
index 0000000..7c79dd8
--- /dev/null
+++ b/vim/autoload/EasyMotion.vim
@@ -0,0 +1,573 @@
+" EasyMotion - Vim motions on speed!
+"
+" Author: Kim Silkebækken <kim.silkebaekken+vim@gmail.com>
+" Source repository: https://github.com/Lokaltog/vim-easymotion
+
+" Default configuration functions {{{
+ function! EasyMotion#InitOptions(options) " {{{
+ for [key, value] in items(a:options)
+ if ! exists('g:EasyMotion_' . key)
+ exec 'let g:EasyMotion_' . key . ' = ' . string(value)
+ endif
+ endfor
+ endfunction " }}}
+ function! EasyMotion#InitHL(group, colors) " {{{
+ let group_default = a:group . 'Default'
+
+ " Prepare highlighting variables
+ let guihl = printf('guibg=%s guifg=%s gui=%s', a:colors.gui[0], a:colors.gui[1], a:colors.gui[2])
+ if !exists('g:CSApprox_loaded')
+ let ctermhl = &t_Co == 256
+ \ ? printf('ctermbg=%s ctermfg=%s cterm=%s', a:colors.cterm256[0], a:colors.cterm256[1], a:colors.cterm256[2])
+ \ : printf('ctermbg=%s ctermfg=%s cterm=%s', a:colors.cterm[0], a:colors.cterm[1], a:colors.cterm[2])
+ else
+ let ctermhl = ''
+ endif
+
+ " Create default highlighting group
+ execute printf('hi default %s %s %s', group_default, guihl, ctermhl)
+
+ " Check if the hl group exists
+ if hlexists(a:group)
+ redir => hlstatus | exec 'silent hi ' . a:group | redir END
+
+ " Return if the group isn't cleared
+ if hlstatus !~ 'cleared'
+ return
+ endif
+ endif
+
+ " No colors are defined for this group, link to defaults
+ execute printf('hi default link %s %s', a:group, group_default)
+ endfunction " }}}
+ function! EasyMotion#InitMappings(motions) "{{{
+ for motion in keys(a:motions)
+ call EasyMotion#InitOptions({ 'mapping_' . motion : g:EasyMotion_leader_key . motion })
+ endfor
+
+ if g:EasyMotion_do_mapping
+ for [motion, fn] in items(a:motions)
+ if empty(g:EasyMotion_mapping_{motion})
+ continue
+ endif
+
+ silent exec 'nnoremap <silent> ' . g:EasyMotion_mapping_{motion} . ' :call EasyMotion#' . fn.name . '(0, ' . fn.dir . ')<CR>'
+ silent exec 'onoremap <silent> ' . g:EasyMotion_mapping_{motion} . ' :call EasyMotion#' . fn.name . '(0, ' . fn.dir . ')<CR>'
+ silent exec 'vnoremap <silent> ' . g:EasyMotion_mapping_{motion} . ' :<C-U>call EasyMotion#' . fn.name . '(1, ' . fn.dir . ')<CR>'
+ endfor
+ endif
+ endfunction "}}}
+" }}}
+" Motion functions {{{
+ function! EasyMotion#F(visualmode, direction) " {{{
+ let char = s:GetSearchChar(a:visualmode)
+
+ if empty(char)
+ return
+ endif
+
+ let re = '\C' . escape(char, '.$^~')
+
+ call s:EasyMotion(re, a:direction, a:visualmode ? visualmode() : '', mode(1))
+ endfunction " }}}
+ function! EasyMotion#T(visualmode, direction) " {{{
+ let char = s:GetSearchChar(a:visualmode)
+
+ if empty(char)
+ return
+ endif
+
+ if a:direction == 1
+ let re = '\C' . escape(char, '.$^~') . '\zs.'
+ else
+ let re = '\C.' . escape(char, '.$^~')
+ endif
+
+ call s:EasyMotion(re, a:direction, a:visualmode ? visualmode() : '', mode(1))
+ endfunction " }}}
+ function! EasyMotion#WB(visualmode, direction) " {{{
+ call s:EasyMotion('\(\<.\|^$\)', a:direction, a:visualmode ? visualmode() : '', '')
+ endfunction " }}}
+ function! EasyMotion#WBW(visualmode, direction) " {{{
+ call s:EasyMotion('\(\(^\|\s\)\@<=\S\|^$\)', a:direction, a:visualmode ? visualmode() : '', '')
+ endfunction " }}}
+ function! EasyMotion#E(visualmode, direction) " {{{
+ call s:EasyMotion('\(.\>\|^$\)', a:direction, a:visualmode ? visualmode() : '', mode(1))
+ endfunction " }}}
+ function! EasyMotion#EW(visualmode, direction) " {{{
+ call s:EasyMotion('\(\S\(\s\|$\)\|^$\)', a:direction, a:visualmode ? visualmode() : '', mode(1))
+ endfunction " }}}
+ function! EasyMotion#JK(visualmode, direction) " {{{
+ call s:EasyMotion('^\(\w\|\s*\zs\|$\)', a:direction, a:visualmode ? visualmode() : '', '')
+ endfunction " }}}
+ function! EasyMotion#Search(visualmode, direction) " {{{
+ call s:EasyMotion(@/, a:direction, a:visualmode ? visualmode() : '', '')
+ endfunction " }}}
+" }}}
+" Helper functions {{{
+ function! s:Message(message) " {{{
+ echo 'EasyMotion: ' . a:message
+ endfunction " }}}
+ function! s:Prompt(message) " {{{
+ echohl Question
+ echo a:message . ': '
+ echohl None
+ endfunction " }}}
+ function! s:VarReset(var, ...) " {{{
+ if ! exists('s:var_reset')
+ let s:var_reset = {}
+ endif
+
+ let buf = bufname("")
+
+ if a:0 == 0 && has_key(s:var_reset, a:var)
+ " Reset var to original value
+ call setbufvar(buf, a:var, s:var_reset[a:var])
+ elseif a:0 == 1
+ let new_value = a:0 == 1 ? a:1 : ''
+
+ " Store original value
+ let s:var_reset[a:var] = getbufvar(buf, a:var)
+
+ " Set new var value
+ call setbufvar(buf, a:var, new_value)
+ endif
+ endfunction " }}}
+ function! s:SetLines(lines, key) " {{{
+ try
+ " Try to join changes with previous undo block
+ undojoin
+ catch
+ endtry
+
+ for [line_num, line] in a:lines
+ call setline(line_num, line[a:key])
+ endfor
+ endfunction " }}}
+ function! s:GetChar() " {{{
+ let char = getchar()
+
+ if char == 27
+ " Escape key pressed
+ redraw
+
+ call s:Message('Cancelled')
+
+ return ''
+ endif
+
+ return nr2char(char)
+ endfunction " }}}
+ function! s:GetSearchChar(visualmode) " {{{
+ call s:Prompt('Search for character')
+
+ let char = s:GetChar()
+
+ " Check that we have an input char
+ if empty(char)
+ " Restore selection
+ if ! empty(a:visualmode)
+ silent exec 'normal! gv'
+ endif
+
+ return ''
+ endif
+
+ return char
+ endfunction " }}}
+" }}}
+" Grouping algorithms {{{
+ let s:grouping_algorithms = {
+ \ 1: 'SCTree'
+ \ , 2: 'Original'
+ \ }
+ " Single-key/closest target priority tree {{{
+ " This algorithm tries to assign one-key jumps to all the targets closest to the cursor.
+ " It works recursively and will work correctly with as few keys as two.
+ function! s:GroupingAlgorithmSCTree(targets, keys)
+ " Prepare variables for working
+ let targets_len = len(a:targets)
+ let keys_len = len(a:keys)
+
+ let groups = {}
+
+ let keys = reverse(copy(a:keys))
+
+ " Semi-recursively count targets {{{
+ " We need to know exactly how many child nodes (targets) this branch will have
+ " in order to pass the correct amount of targets to the recursive function.
+
+ " Prepare sorted target count list {{{
+ " This is horrible, I know. But dicts aren't sorted in vim, so we need to
+ " work around that. That is done by having one sorted list with key counts,
+ " and a dict which connects the key with the keys_count list.
+
+ let keys_count = []
+ let keys_count_keys = {}
+
+ let i = 0
+ for key in keys
+ call add(keys_count, 0)
+
+ let keys_count_keys[key] = i
+
+ let i += 1
+ endfor
+ " }}}
+
+ let targets_left = targets_len
+ let level = 0
+ let i = 0
+
+ while targets_left > 0
+ " Calculate the amount of child nodes based on the current level
+ let childs_len = (level == 0 ? 1 : (keys_len - 1) )
+
+ for key in keys
+ " Add child node count to the keys_count array
+ let keys_count[keys_count_keys[key]] += childs_len
+
+ " Subtract the child node count
+ let targets_left -= childs_len
+
+ if targets_left <= 0
+ " Subtract the targets left if we added too many too
+ " many child nodes to the key count
+ let keys_count[keys_count_keys[key]] += targets_left
+
+ break
+ endif
+
+ let i += 1
+ endfor
+
+ let level += 1
+ endwhile
+ " }}}
+ " Create group tree {{{
+ let i = 0
+ let key = 0
+
+ call reverse(keys_count)
+
+ for key_count in keys_count
+ if key_count > 1
+ " We need to create a subgroup
+ " Recurse one level deeper
+ let groups[a:keys[key]] = s:GroupingAlgorithmSCTree(a:targets[i : i + key_count - 1], a:keys)
+ elseif key_count == 1
+ " Assign single target key
+ let groups[a:keys[key]] = a:targets[i]
+ else
+ " No target
+ continue
+ endif
+
+ let key += 1
+ let i += key_count
+ endfor
+ " }}}
+
+ " Finally!
+ return groups
+ endfunction
+ " }}}
+ " Original {{{
+ function! s:GroupingAlgorithmOriginal(targets, keys)
+ " Split targets into groups (1 level)
+ let targets_len = len(a:targets)
+ let keys_len = len(a:keys)
+
+ let groups = {}
+
+ let i = 0
+ let root_group = 0
+ try
+ while root_group < targets_len
+ let groups[a:keys[root_group]] = {}
+
+ for key in a:keys
+ let groups[a:keys[root_group]][key] = a:targets[i]
+
+ let i += 1
+ endfor
+
+ let root_group += 1
+ endwhile
+ catch | endtry
+
+ " Flatten the group array
+ if len(groups) == 1
+ let groups = groups[a:keys[0]]
+ endif
+
+ return groups
+ endfunction
+ " }}}
+ " Coord/key dictionary creation {{{
+ function! s:CreateCoordKeyDict(groups, ...)
+ " Dict structure:
+ " 1,2 : a
+ " 2,3 : b
+ let sort_list = []
+ let coord_keys = {}
+ let group_key = a:0 == 1 ? a:1 : ''
+
+ for [key, item] in items(a:groups)
+ let key = ( ! empty(group_key) ? group_key : key)
+
+ if type(item) == 3
+ " Destination coords
+
+ " The key needs to be zero-padded in order to
+ " sort correctly
+ let dict_key = printf('%05d,%05d', item[0], item[1])
+ let coord_keys[dict_key] = key
+
+ " We need a sorting list to loop correctly in
+ " PromptUser, dicts are unsorted
+ call add(sort_list, dict_key)
+ else
+ " Item is a dict (has children)
+ let coord_key_dict = s:CreateCoordKeyDict(item, key)
+
+ " Make sure to extend both the sort list and the
+ " coord key dict
+ call extend(sort_list, coord_key_dict[0])
+ call extend(coord_keys, coord_key_dict[1])
+ endif
+
+ unlet item
+ endfor
+
+ return [sort_list, coord_keys]
+ endfunction
+ " }}}
+" }}}
+" Core functions {{{
+ function! s:PromptUser(groups) "{{{
+ " If only one possible match, jump directly to it {{{
+ let group_values = values(a:groups)
+
+ if len(group_values) == 1
+ redraw
+
+ return group_values[0]
+ endif
+ " }}}
+ " Prepare marker lines {{{
+ let lines = {}
+ let hl_coords = []
+ let coord_key_dict = s:CreateCoordKeyDict(a:groups)
+
+ for dict_key in sort(coord_key_dict[0])
+ let target_key = coord_key_dict[1][dict_key]
+ let [line_num, col_num] = split(dict_key, ',')
+
+ let line_num = str2nr(line_num)
+ let col_num = str2nr(col_num)
+
+ " Add original line and marker line
+ if ! has_key(lines, line_num)
+ let current_line = getline(line_num)
+
+ let lines[line_num] = { 'orig': current_line, 'marker': current_line, 'mb_compensation': 0 }
+ endif
+
+ " Compensate for byte difference between marker
+ " character and target character
+ "
+ " This has to be done in order to match the correct
+ " column; \%c matches the byte column and not display
+ " column.
+ let target_char_len = strlen(matchstr(lines[line_num]['marker'], '\%' . col_num . 'c.'))
+ let target_key_len = strlen(target_key)
+
+ " Solve multibyte issues by matching the byte column
+ " number instead of the visual column
+ let col_num -= lines[line_num]['mb_compensation']
+
+ if strlen(lines[line_num]['marker']) > 0
+ " Substitute marker character if line length > 0
+ let lines[line_num]['marker'] = substitute(lines[line_num]['marker'], '\%' . col_num . 'c.', target_key, '')
+ else
+ " Set the line to the marker character if the line is empty
+ let lines[line_num]['marker'] = target_key
+ endif
+
+ " Add highlighting coordinates
+ call add(hl_coords, '\%' . line_num . 'l\%' . col_num . 'c')
+
+ " Add marker/target lenght difference for multibyte
+ " compensation
+ let lines[line_num]['mb_compensation'] += (target_char_len - target_key_len)
+ endfor
+
+ let lines_items = items(lines)
+ " }}}
+ " Highlight targets {{{
+ let target_hl_id = matchadd(g:EasyMotion_hl_group_target, join(hl_coords, '\|'), 1)
+ " }}}
+
+ try
+ " Set lines with markers
+ call s:SetLines(lines_items, 'marker')
+
+ redraw
+
+ " Get target character {{{
+ call s:Prompt('Target key')
+
+ let char = s:GetChar()
+ " }}}
+ finally
+ " Restore original lines
+ call s:SetLines(lines_items, 'orig')
+
+ " Un-highlight targets {{{
+ if exists('target_hl_id')
+ call matchdelete(target_hl_id)
+ endif
+ " }}}
+
+ redraw
+ endtry
+
+ " Check if we have an input char {{{
+ if empty(char)
+ throw 'Cancelled'
+ endif
+ " }}}
+ " Check if the input char is valid {{{
+ if ! has_key(a:groups, char)
+ throw 'Invalid target'
+ endif
+ " }}}
+
+ let target = a:groups[char]
+
+ if type(target) == 3
+ " Return target coordinates
+ return target
+ else
+ " Prompt for new target character
+ return s:PromptUser(target)
+ endif
+ endfunction "}}}
+ function! s:EasyMotion(regexp, direction, visualmode, mode) " {{{
+ let orig_pos = [line('.'), col('.')]
+ let targets = []
+
+ try
+ " Reset properties {{{
+ call s:VarReset('&scrolloff', 0)
+ call s:VarReset('&modified', 0)
+ call s:VarReset('&modifiable', 1)
+ call s:VarReset('&readonly', 0)
+ call s:VarReset('&spell', 0)
+ call s:VarReset('&virtualedit', '')
+ " }}}
+ " Find motion targets {{{
+ let search_direction = (a:direction == 1 ? 'b' : '')
+ let search_stopline = line(a:direction == 1 ? 'w0' : 'w$')
+
+ while 1
+ let pos = searchpos(a:regexp, search_direction, search_stopline)
+
+ " Reached end of search range
+ if pos == [0, 0]
+ break
+ endif
+
+ " Skip folded lines
+ if foldclosed(pos[0]) != -1
+ continue
+ endif
+
+ call add(targets, pos)
+ endwhile
+
+ let targets_len = len(targets)
+ if targets_len == 0
+ throw 'No matches'
+ endif
+ " }}}
+
+ let GroupingFn = function('s:GroupingAlgorithm' . s:grouping_algorithms[g:EasyMotion_grouping])
+ let groups = GroupingFn(targets, split(g:EasyMotion_keys, '\zs'))
+
+ " Shade inactive source {{{
+ if g:EasyMotion_do_shade
+ let shade_hl_pos = '\%' . orig_pos[0] . 'l\%'. orig_pos[1] .'c'
+
+ if a:direction == 1
+ " Backward
+ let shade_hl_re = '\%'. line('w0') .'l\_.*' . shade_hl_pos
+ else
+ " Forward
+ let shade_hl_re = shade_hl_pos . '\_.*\%'. line('w$') .'l'
+ endif
+
+ let shade_hl_id = matchadd(g:EasyMotion_hl_group_shade, shade_hl_re, 0)
+ endif
+ " }}}
+
+ " Prompt user for target group/character
+ let coords = s:PromptUser(groups)
+
+ " Update selection {{{
+ if ! empty(a:visualmode)
+ keepjumps call cursor(orig_pos[0], orig_pos[1])
+
+ exec 'normal! ' . a:visualmode
+ endif
+ " }}}
+ " Handle operator-pending mode {{{
+ if a:mode == 'no'
+ " This mode requires that we eat one more
+ " character to the right if we're using
+ " a forward motion
+ if a:direction != 1
+ let coords[1] += 1
+ endif
+ endif
+ " }}}
+
+ " Update cursor position
+ call cursor(orig_pos[0], orig_pos[1])
+ mark '
+ call cursor(coords[0], coords[1])
+
+ call s:Message('Jumping to [' . coords[0] . ', ' . coords[1] . ']')
+ catch
+ redraw
+
+ " Show exception message
+ call s:Message(v:exception)
+
+ " Restore original cursor position/selection {{{
+ if ! empty(a:visualmode)
+ silent exec 'normal! gv'
+ else
+ keepjumps call cursor(orig_pos[0], orig_pos[1])
+ endif
+ " }}}
+ finally
+ " Restore properties {{{
+ call s:VarReset('&scrolloff')
+ call s:VarReset('&modified')
+ call s:VarReset('&modifiable')
+ call s:VarReset('&readonly')
+ call s:VarReset('&spell')
+ call s:VarReset('&virtualedit')
+ " }}}
+ " Remove shading {{{
+ if g:EasyMotion_do_shade && exists('shade_hl_id')
+ call matchdelete(shade_hl_id)
+ endif
+ " }}}
+ endtry
+ endfunction " }}}
+" }}}
+
+" vim: fdm=marker:noet:ts=4:sw=4:sts=4
diff --git a/vim/autoload/ZoomWin.vim b/vim/autoload/ZoomWin.vim
new file mode 100644
index 0000000..d2d993f
--- /dev/null
+++ b/vim/autoload/ZoomWin.vim
@@ -0,0 +1,380 @@
+" ZoomWin: Brief-like ability to zoom into/out-of a window
+" Author: Charles Campbell
+" original version by Ron Aaron
+" Date: Jan 26, 2009
+" Version: 23
+" History: see :help zoomwin-history {{{1
+" GetLatestVimScripts: 508 1 :AutoInstall: ZoomWin.vim
+
+" ---------------------------------------------------------------------
+" Load Once: {{{1
+if &cp || exists("g:loaded_ZoomWin")
+ 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_ZoomWin = "v23"
+set cpo&vim
+"DechoTabOn
+
+" =====================================================================
+" Functions: {{{1
+
+" ---------------------------------------------------------------------
+" ZoomWin#ZoomWin: toggles between a single-window and a multi-window layout {{{2
+" The original version was by Ron Aaron.
+fun! ZoomWin#ZoomWin()
+" let g:decho_hide= 1 "Decho
+" call Dfunc("ZoomWin#ZoomWin() winbufnr(2)=".winbufnr(2))
+
+ " if the vim doesn't have +mksession, only a partial zoom is available {{{3
+ if !has("mksession")
+ if !exists("s:partialzoom")
+ echomsg "missing the +mksession feature; only a partial zoom is available"
+ let s:partialzoom= 0
+ endif
+ if v:version < 630
+ echoerr "***sorry*** you need an updated vim, preferably with +mksession"
+ elseif s:partialzoom
+ " partial zoom out
+ let s:partialzoom= 0
+ exe s:winrestore
+ else
+ " partial zoom in
+ let s:partialzoom= 1
+ let s:winrestore = winrestcmd()
+ res
+ endif
+" call Dret("ZoomWin#ZoomWin : partialzoom=".s:partialzoom)
+ return
+ endif
+
+ " Close certain windows {{{3
+ call s:ZoomWinPreserve(0)
+
+ " save options. Force window minimum height/width to be >= 1 {{{3
+ let keep_hidden = &hidden
+ let keep_write = &write
+
+ if v:version < 603
+ if &wmh == 0 || &wmw == 0
+ let keep_wmh = &wmh
+ let keep_wmw = &wmw
+ silent! set wmh=1 wmw=1
+ endif
+ endif
+ set hidden write
+
+ if winbufnr(2) == -1
+ " there's only one window - restore to multiple-windows mode {{{3
+" call Decho("there's only one window - restore to multiple windows")
+
+ if exists("s:sessionfile") && filereadable(s:sessionfile)
+ " save position in current one-window-only
+" call Decho("save position in current one-window-only in sponly")
+ let sponly = s:SavePosn(0)
+ let s:origline = line(".")
+ let s:origcol = virtcol(".")
+
+ " source session file to restore window layout
+ let ei_keep= &ei
+ set ei=all
+ exe 'silent! so '.fnameescape(s:sessionfile)
+" Decho("@@<".@@.">")
+ let v:this_session= s:sesskeep
+
+ if exists("s:savedposn1")
+ " restore windows' positioning and buffers
+" call Decho("restore windows, positions, buffers")
+ windo call s:RestorePosn(s:savedposn{winnr()})|unlet s:savedposn{winnr()}
+ call s:GotoWinNum(s:winkeep)
+ unlet s:winkeep
+ endif
+
+ if line(".") != s:origline || virtcol(".") != s:origcol
+ " If the cursor hasn't moved from the original position,
+ " then let the position remain what it was in the original
+ " multi-window layout.
+" call Decho("restore position using sponly")
+ call s:RestorePosn(sponly)
+ endif
+
+ " delete session file and variable holding its name
+" call Decho("delete session file")
+ call delete(s:sessionfile)
+ unlet s:sessionfile
+ let &ei=ei_keep
+ endif
+
+ else " there's more than one window - go to only-one-window mode {{{3
+" call Decho("there's multiple windows - goto one-window-only")
+
+ let s:winkeep = winnr()
+ let s:sesskeep = v:this_session
+
+ " doesn't work with the command line window (normal mode q:)
+ if &bt == "nofile" && expand("%") == (v:version < 702 ? 'command-line' : '[Command Line]')
+ echoerr "***error*** ZoomWin#ZoomWin doesn't work with the ".expand("%")." window"
+" call Dret("ZoomWin#ZoomWin : ".expand('%')." window error")
+ return
+ endif
+" call Decho("1: @@<".@@.">")
+
+ " disable all events (autocmds)
+" call Decho("disable events")
+ let ei_keep= &ei
+ set ei=all
+" call Decho("2: @@<".@@.">")
+
+ " save window positioning commands
+" call Decho("save window positioning commands")
+ windo let s:savedposn{winnr()}= s:SavePosn(1)
+ call s:GotoWinNum(s:winkeep)
+
+ " set up name of session file
+" call Decho("3: @@<".@@.">")
+ let s:sessionfile= tempname()
+" call Decho("4: @@<".@@.">")
+
+ " save session
+" call Decho("save session")
+ let ssop_keep = &ssop
+ let &ssop = 'blank,help,winsize,folds,globals,localoptions,options'
+" call Decho("5: @@<".@@.">")
+ exe 'mksession! '.fnameescape(s:sessionfile)
+" call Decho("6: @@<".@@.">")
+ let keepyy= @@
+ let keepy0= @0
+ let keepy1= @1
+ let keepy2= @2
+ let keepy3= @3
+ let keepy4= @4
+ let keepy5= @5
+ let keepy6= @6
+ let keepy7= @7
+ let keepy8= @8
+ let keepy9= @9
+ set lz ei=all bh=
+ if v:version >= 700
+ try
+ exe "keepalt keepmarks new! ".fnameescape(s:sessionfile)
+ catch /^Vim\%((\a\+)\)\=:E/
+ echoerr "Too many windows"
+ silent! call delete(s:sessionfile)
+ unlet s:sessionfile
+" call Dret("ZoomWin#ZoomWin : too many windows")
+ return
+ endtry
+ silent! keepjumps keepmarks v/wincmd\|split\|resize/d
+ keepalt w!
+ keepalt bw!
+ else
+ exe "new! ".fnameescape(s:sessionfile)
+ v/wincmd\|split\|resize/d
+ w!
+ bw!
+ endif
+ let @@= keepyy
+ let @0= keepy0
+ let @1= keepy1
+ let @2= keepy2
+ let @3= keepy3
+ let @4= keepy4
+ let @5= keepy5
+ let @6= keepy6
+ let @7= keepy7
+ let @8= keepy8
+ let @9= keepy9
+ call histdel('search', -1)
+ let @/ = histget('search', -1)
+" call Decho("7: @@<".@@.">")
+
+ " restore user's session options and restore event handling
+" call Decho("restore user session options and event handling")
+ set nolz
+ let &ssop = ssop_keep
+ silent! only!
+" call Decho("8: @@<".@@.">")
+ let &ei = ei_keep
+ echomsg expand("%")
+" call Decho("9: @@<".@@.">")
+ endif
+
+ " restore user option settings {{{3
+" call Decho("restore user option settings")
+ let &hidden= keep_hidden
+ let &write = keep_write
+ if v:version < 603
+ if exists("keep_wmw")
+ let &wmh= keep_wmh
+ let &wmw= keep_wmw
+ endif
+ endif
+
+ " Re-open certain windows {{{3
+ call s:ZoomWinPreserve(1)
+
+" call Dret("ZoomWin#ZoomWin")
+endfun
+
+" ---------------------------------------------------------------------
+" SavePosn: this function sets up a savedposn variable that {{{2
+" has the commands necessary to restore the view
+" of the current window.
+fun! s:SavePosn(savewinhoriz)
+" call Dfunc("SavePosn(savewinhoriz=".a:savewinhoriz.") file<".expand("%").">")
+ let swline = line(".")
+ if swline == 1 && getline(1) == ""
+ " empty buffer
+ let savedposn= "silent b ".winbufnr(0)
+" call Dret("SavePosn savedposn<".savedposn.">")
+ return savedposn
+ endif
+ let swcol = col(".")
+ let swwline = winline()-1
+ let swwcol = virtcol(".") - wincol()
+ let savedposn = "silent b ".winbufnr(0)."|".swline."|silent norm! z\<cr>"
+ if swwline > 0
+ let savedposn= savedposn.":silent norm! ".swwline."\<c-y>\<cr>:silent norm! zs\<cr>"
+ endif
+ let savedposn= savedposn.":silent call cursor(".swline.",".swcol.")\<cr>"
+
+ if a:savewinhoriz
+ if swwcol > 0
+ let savedposn= savedposn.":silent norm! ".swwcol."zl\<cr>"
+ endif
+
+ " handle certain special settings for the multi-window savedposn call
+ " bufhidden buftype buflisted
+ let settings= ""
+ if &bh != ""
+ let settings="bh=".&bh
+ setlocal bh=hide
+ endif
+ if !&bl
+ let settings= settings." nobl"
+ setlocal bl
+ endif
+ if &bt != ""
+ let settings= settings." bt=".&bt
+ setlocal bt=
+ endif
+ if settings != ""
+ let savedposn= savedposn.":setlocal ".settings."\<cr>"
+ endif
+
+ endif
+" call Dret("SavePosn savedposn<".savedposn.">")
+ return savedposn
+endfun
+
+" ---------------------------------------------------------------------
+" s:RestorePosn: this function restores noname and scratch windows {{{2
+fun! s:RestorePosn(savedposn)
+" call Dfunc("RestorePosn(savedposn<".a:savedposn.">) file<".expand("%").">")
+ if &scb
+ setlocal noscb
+ exe a:savedposn
+ setlocal scb
+ else
+ exe a:savedposn
+ endif
+" call Dret("RestorePosn")
+endfun
+
+" ---------------------------------------------------------------------
+" CleanupSessionFile: if you exit Vim before cleaning up the {{{2
+" supposed-to-be temporary session file
+fun! ZoomWin#CleanupSessionFile()
+" call Dfunc("ZoomWin#CleanupSessionFile()")
+ if exists("s:sessionfile") && filereadable(s:sessionfile)
+" call Decho("sessionfile exists and is readable; deleting it")
+ silent! call delete(s:sessionfile)
+ unlet s:sessionfile
+ endif
+" call Dret("ZoomWin#CleanupSessionFile")
+endfun
+
+" ---------------------------------------------------------------------
+" GotoWinNum: this function puts cursor into specified window {{{2
+fun! s:GotoWinNum(winnum)
+" call Dfunc("GotoWinNum(winnum=".a:winnum.") winnr=".winnr())
+ if a:winnum != winnr()
+ exe a:winnum."wincmd w"
+ endif
+" call Dret("GotoWinNum")
+endfun
+
+
+" ---------------------------------------------------------------------
+" ZoomWinPreserve: This function, largely written by David Fishburn, {{{2
+" allows ZoomWin to "preserve" certain windows:
+"
+" TagList, by Yegappan Lakshmanan
+" http://vim.sourceforge.net/scripts/script.php?script_id=273
+"
+" WinManager, by Srinath Avadhanula
+" http://vim.sourceforge.net/scripts/script.php?script_id=95
+"
+" It does so by closing the associated window upon entry to ZoomWin
+" and re-opening it upon exit by using commands provided by the
+" utilities themselves.
+fun! s:ZoomWinPreserve(open)
+" call Dfunc("ZoomWinPreserve(open=".a:open.")")
+
+ if a:open == 0
+
+ " Close Taglist
+ if exists('g:zoomwin_preserve_taglist') && exists('g:loaded_taglist')
+ " If taglist window is open then close it.
+ let s:taglist_winnum = bufwinnr(g:TagList_title)
+ if s:taglist_winnum != -1
+ " Close the window
+ exec "silent! Tlist"
+ endif
+ endif
+
+ " Close Winmanager
+ if exists('g:zoomwin_preserve_winmanager') && exists('g:loaded_winmanager')
+ " If the winmanager window is open then close it.
+ let s:is_winmgr_vis = IsWinManagerVisible()
+ if s:is_winmgr_vis == 1
+ exec "WMClose"
+ endif
+ endif
+
+ else
+
+ " Re-open Taglist
+ if exists('g:zoomwin_preserve_taglist') && exists('g:loaded_taglist')
+ " If taglist window was open, open it again
+ if s:taglist_winnum != -1
+ exec "silent! Tlist"
+ endif
+ endif
+
+ " Re-Open Winmanager
+ if exists('g:zoomwin_preserve_winmanager') && exists('g:loaded_winmanager')
+ " If the winmanager window is open then close it.
+ if s:is_winmgr_vis == 1
+ exec "WManager"
+ endif
+ endif
+ endif
+
+" call Dret("ZoomWinPreserve")
+endfun
+
+" =====================================================================
+" Restore: {{{1
+let &cpo= s:keepcpo
+unlet s:keepcpo
+
+" ---------------------------------------------------------------------
+" Modelines: {{{1
+" vim: ts=4 fdm=marker
diff --git a/vim/autoload/conque_term.vim b/vim/autoload/conque_term.vim
new file mode 100644
index 0000000..2780c7d
--- /dev/null
+++ b/vim/autoload/conque_term.vim
@@ -0,0 +1,1590 @@
+" 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.
+" }}}
+
+
+" **********************************************************************************************************
+" **** VIM FUNCTIONS ***************************************************************************************
+" **********************************************************************************************************
+
+" launch conque
+function! conque_term#open(...) "{{{
+ let command = get(a:000, 0, '')
+ let hooks = get(a:000, 1, [])
+
+ " bare minimum validation
+ if has('python') != 1
+ echohl WarningMsg | echomsg "Conque requires the Python interface to be installed" | echohl None
+ return 0
+ endif
+ if empty(command)
+ echohl WarningMsg | echomsg "No command found" | echohl None
+ return 0
+ else
+ let l:cargs = split(command, '\s')
+ if !executable(l:cargs[0])
+ echohl WarningMsg | echomsg "Not an executable: " . l:cargs[0] | echohl None
+ return 0
+ endif
+ endif
+
+ " set buffer window options
+ let g:ConqueTerm_BufName = substitute(command, ' ', '\\ ', 'g') . "\\ -\\ " . g:ConqueTerm_Idx
+ call conque_term#set_buffer_settings(command, hooks)
+ let b:ConqueTerm_Var = 'ConqueTerm_' . g:ConqueTerm_Idx
+ let g:ConqueTerm_Var = 'ConqueTerm_' . g:ConqueTerm_Idx
+ let g:ConqueTerm_Idx += 1
+
+ " open command
+ try
+ let l:config = '{"color":' . string(g:ConqueTerm_Color) . ',"TERM":"' . g:ConqueTerm_TERM . '"}'
+ execute 'python ' . b:ConqueTerm_Var . ' = Conque()'
+ execute "python " . b:ConqueTerm_Var . ".open('" . conque_term#python_escape(command) . "', " . l:config . ")"
+ catch
+ echohl WarningMsg | echomsg "Unable to open command: " . command | echohl None
+ return 0
+ endtry
+
+ " set buffer mappings and auto commands
+ call conque_term#set_mappings('start')
+
+ startinsert!
+ return 1
+endfunction "}}}
+
+" set buffer options
+function! conque_term#set_buffer_settings(command, pre_hooks) "{{{
+
+ " optional hooks to execute, e.g. 'split'
+ for h in a:pre_hooks
+ sil exe h
+ endfor
+ sil exe "edit " . g:ConqueTerm_BufName
+
+ " buffer settings
+ setlocal nocompatible " conque won't work in compatible mode
+ setlocal nopaste " conque won't work in paste mode
+ setlocal buftype=nofile " this buffer is not a file, you can't save it
+ setlocal nonumber " hide line numbers
+ setlocal foldcolumn=0 " reasonable left margin
+ setlocal nowrap " default to no wrap (esp with MySQL)
+ setlocal noswapfile " don't bother creating a .swp file
+ setlocal updatetime=50 " trigger cursorhold event after 50ms / XXX - global
+ setlocal scrolloff=0 " don't use buffer lines. it makes the 'clear' command not work as expected
+ setlocal sidescrolloff=0 " don't use buffer lines. it makes the 'clear' command not work as expected
+ setlocal sidescroll=1 " don't use buffer lines. it makes the 'clear' command not work as expected
+ setlocal foldmethod=manual " don't fold on {{{}}} and stuff
+ setlocal bufhidden=hide " when buffer is no longer displayed, don't wipe it out
+ setfiletype conque_term " useful
+ sil exe "setlocal syntax=" . g:ConqueTerm_Syntax
+
+endfunction " }}}
+
+" set key mappings and auto commands
+function! conque_term#set_mappings(action) "{{{
+
+ " set action
+ if a:action == 'toggle'
+ if exists('b:conque_on') && b:conque_on == 1
+ let l:action = 'stop'
+ echohl WarningMsg | echomsg "Terminal is paused" | echohl None
+ else
+ let l:action = 'start'
+ echohl WarningMsg | echomsg "Terminal is resumed" | echohl None
+ endif
+ else
+ let l:action = a:action
+ endif
+
+ " if mappings are being removed, add 'un'
+ let map_modifier = 'nore'
+ if l:action == 'stop'
+ let map_modifier = 'un'
+ endif
+
+ " remove all auto commands
+ if l:action == 'stop'
+ execute 'autocmd! ' . b:ConqueTerm_Var
+
+ else
+ execute 'augroup ' . b:ConqueTerm_Var
+
+ " handle unexpected closing of shell, passes HUP to parent and all child processes
+ execute 'autocmd ' . b:ConqueTerm_Var . ' BufUnload <buffer> python ' . b:ConqueTerm_Var . '.proc.signal(1)'
+
+ " check for resized/scrolled buffer when entering buffer
+ execute 'autocmd ' . b:ConqueTerm_Var . ' BufEnter <buffer> python ' . b:ConqueTerm_Var . '.update_window_size()'
+ execute 'autocmd ' . b:ConqueTerm_Var . ' VimResized python ' . b:ConqueTerm_Var . '.update_window_size()'
+
+ " set/reset updatetime on entering/exiting buffer
+ autocmd BufEnter <buffer> set updatetime=50
+ autocmd BufLeave <buffer> set updatetime=2000
+
+ " check for resized/scrolled buffer when entering insert mode
+ " XXX - messed up since we enter insert mode at each updatetime
+ "execute 'autocmd InsertEnter <buffer> python ' . b:ConqueTerm_Var . '.screen.align()'
+
+ " read more output when this isn't the current buffer
+ if g:ConqueTerm_ReadUnfocused == 1
+ execute 'autocmd ' . b:ConqueTerm_Var . ' CursorHold * call conque_term#read_all()'
+ endif
+
+ " poll for more output
+ sil execute 'autocmd ' . b:ConqueTerm_Var . ' CursorHoldI <buffer> python ' . b:ConqueTerm_Var . '.auto_read()'
+ endif
+
+ " use F22 key to get more input
+ if l:action == 'start'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <expr> <F22> "\<left>\<right>"'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <expr> <F23> "\<right>\<left>"'
+ else
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <expr> <F22>'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <expr> <F23>'
+ endif
+
+ " map ASCII 1-31
+ for c in range(1, 31)
+ " <Esc>
+ if c == 27
+ continue
+ endif
+ if l:action == 'start'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <C-' . nr2char(64 + c) . '> <C-o>:python ' . b:ConqueTerm_Var . '.write(chr(' . c . '))<CR>'
+ else
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <C-' . nr2char(64 + c) . '>'
+ endif
+ endfor
+ if l:action == 'start'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> <C-c> <C-o>:python ' . b:ConqueTerm_Var . '.write(chr(3))<CR>'
+ else
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> <C-c>'
+ endif
+
+ " leave insert mode
+ if !exists('g:ConqueTerm_EscKey') || g:ConqueTerm_EscKey == '<Esc>'
+ " use <Esc><Esc> to send <Esc> to terminal
+ if l:action == 'start'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Esc><Esc> <C-o>:python ' . b:ConqueTerm_Var . '.write(chr(27))<CR>'
+ else
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Esc><Esc>'
+ endif
+ else
+ " use <Esc> to send <Esc> to terminal
+ if l:action == 'start'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> ' . g:ConqueTerm_EscKey . ' <Esc>'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Esc> <C-o>:python ' . b:ConqueTerm_Var . '.write(chr(27))<CR>'
+ else
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> ' . g:ConqueTerm_EscKey
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Esc>'
+ endif
+ endif
+
+ " Map <C-w> in insert mode
+ if exists('g:ConqueTerm_CWInsert') && g:ConqueTerm_CWInsert == 1
+ inoremap <silent> <buffer> <C-w>j <Esc><C-w>j
+ inoremap <silent> <buffer> <C-w>k <Esc><C-w>k
+ inoremap <silent> <buffer> <C-w>h <Esc><C-w>h
+ inoremap <silent> <buffer> <C-w>l <Esc><C-w>l
+ inoremap <silent> <buffer> <C-w>w <Esc><C-w>w
+ endif
+
+ " map ASCII 33-127
+ for i in range(33, 127)
+ " <Bar>
+ if i == 124
+ if l:action == 'start'
+ sil exe "i" . map_modifier . "map <silent> <buffer> <Bar> <C-o>:python " . b:ConqueTerm_Var . ".write(chr(124))<CR>"
+ else
+ sil exe "i" . map_modifier . "map <silent> <buffer> <Bar>"
+ endif
+ continue
+ endif
+ if l:action == 'start'
+ sil exe "i" . map_modifier . "map <silent> <buffer> " . nr2char(i) . " <C-o>:python " . b:ConqueTerm_Var . ".write(chr(" . i . "))<CR>"
+ else
+ sil exe "i" . map_modifier . "map <silent> <buffer> " . nr2char(i)
+ endif
+ endfor
+
+ " map ASCII 128-255
+ for i in range(128, 255)
+ if l:action == 'start'
+ sil exe "i" . map_modifier . "map <silent> <buffer> " . nr2char(i) . " <C-o>:python " . b:ConqueTerm_Var . ".write('" . nr2char(i) . "')<CR>"
+ else
+ sil exe "i" . map_modifier . "map <silent> <buffer> " . nr2char(i)
+ endif
+ endfor
+
+ " Special cases
+ if l:action == 'start'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <BS> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u0008")<CR>'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Space> <C-o>:python ' . b:ConqueTerm_Var . '.write(" ")<CR>'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Up> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u001b[A")<CR>'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Down> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u001b[B")<CR>'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Right> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u001b[C")<CR>'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Left> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u001b[D")<CR>'
+ else
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <BS>'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Space>'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Up>'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Down>'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Right>'
+ sil exe 'i' . map_modifier . 'map <silent> <buffer> <Left>'
+ endif
+
+ " send selected text into conque
+ if l:action == 'start'
+ sil exe 'v' . map_modifier . 'map <silent> <F9> :<C-u>call conque_term#send_selected(visualmode())<CR>'
+ else
+ sil exe 'v' . map_modifier . 'map <silent> <F9>'
+ endif
+
+ " remap paste keys
+ if l:action == 'start'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> p :python ' . b:ConqueTerm_Var . '.write(vim.eval("@@"))<CR>a'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> P :python ' . b:ConqueTerm_Var . '.write(vim.eval("@@"))<CR>a'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> ]p :python ' . b:ConqueTerm_Var . '.write(vim.eval("@@"))<CR>a'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> [p :python ' . b:ConqueTerm_Var . '.write(vim.eval("@@"))<CR>a'
+ else
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> p'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> P'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> ]p'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> [p'
+ endif
+ if has('gui_running')
+ if l:action == 'start'
+ sil exe 'i' . map_modifier . 'map <buffer> <S-Insert> <Esc>:<C-u>python ' . b:ConqueTerm_Var . ".write(vim.eval('@+'))<CR>a"
+ else
+ sil exe 'i' . map_modifier . 'map <buffer> <S-Insert>'
+ endif
+ endif
+
+ " disable other normal mode keys which insert text
+ if l:action == 'start'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> r :echo "Replace mode disabled in shell."<CR>'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> R :echo "Replace mode disabled in shell."<CR>'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> c :echo "Change mode disabled in shell."<CR>'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> C :echo "Change mode disabled in shell."<CR>'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> s :echo "Change mode disabled in shell."<CR>'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> S :echo "Change mode disabled in shell."<CR>'
+ else
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> r'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> R'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> c'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> C'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> s'
+ sil exe 'n' . map_modifier . 'map <silent> <buffer> S'
+ endif
+
+ " set conque as on or off
+ if l:action == 'start'
+ let b:conque_on = 1
+ else
+ let b:conque_on = 0
+ endif
+
+ " map command to start stop the shell
+ if a:action == 'start'
+ nnoremap <F5> :<C-u>call conque_term#set_mappings('toggle')<CR>
+ endif
+
+endfunction " }}}
+
+
+" send selected text from another buffer
+function! conque_term#send_selected(type) "{{{
+ let reg_save = @@
+
+ " save user's sb settings
+ let sb_save = &switchbuf
+ set switchbuf=usetab
+
+ " yank current selection
+ sil exe "normal! `<" . a:type . "`>y"
+
+ " format yanked text
+ let @@ = substitute(@@, '^[\r\n]*', '', '')
+ let @@ = substitute(@@, '[\r\n]*$', '', '')
+
+ " execute yanked text
+ sil exe ":sb " . g:ConqueTerm_BufName
+ sil exe 'python ' . g:ConqueTerm_Var . '.paste_selection()'
+
+ " reset original values
+ let @@ = reg_save
+ sil exe 'set switchbuf=' . sb_save
+
+ " scroll buffer left
+ startinsert!
+ normal 0zH
+endfunction "}}}
+
+" read from all known conque buffers
+function! conque_term#read_all() "{{{
+ " don't run this if we're in a conque buffer
+ if exists('b:ConqueTerm_Var')
+ return
+ endif
+
+ try
+ for i in range(1, g:ConqueTerm_Idx - 1)
+ execute 'python ConqueTerm_' . string(i) . '.read(1)'
+ endfor
+ catch
+ " probably a deleted buffer
+ endtry
+
+ " restart updatetime
+ call feedkeys("f\e")
+endfunction "}}}
+
+" util function to add enough \s to pass a string to python
+function! conque_term#python_escape(input) "{{{
+ let l:cleaned = a:input
+ let l:cleaned = substitute(l:cleaned, '\\', '\\\\', 'g')
+ let l:cleaned = substitute(l:cleaned, '\n', '\\n', 'g')
+ let l:cleaned = substitute(l:cleaned, '\r', '\\r', 'g')
+ let l:cleaned = substitute(l:cleaned, "'", "\\\\'", 'g')
+ return l:cleaned
+endfunction "}}}
+
+" **********************************************************************************************************
+" **** PYTHON **********************************************************************************************
+" **********************************************************************************************************
+
+if has('python')
+
+python << EOF
+
+import vim, re, time, math
+
+# CONFIG CONSTANTS {{{
+
+CONQUE_CTL = {
+ 7:'bel', # bell
+ 8:'bs', # backspace
+ 9:'tab', # tab
+ 10:'nl', # new line
+ 13:'cr' # carriage return
+}
+# 11 : 'vt', # vertical tab
+# 12 : 'ff', # form feed
+# 14 : 'so', # shift out
+# 15 : 'si' # shift in
+
+# Escape sequences
+CONQUE_ESCAPE = {
+ 'm':'font',
+ 'J':'clear_screen',
+ 'K':'clear_line',
+ '@':'add_spaces',
+ 'A':'cursor_up',
+ 'B':'cursor_down',
+ 'C':'cursor_right',
+ 'D':'cursor_left',
+ 'G':'cursor_to_column',
+ 'H':'cursor',
+ 'P':'delete_chars',
+ 'f':'cursor',
+ 'g':'tab_clear',
+ 'r':'set_coords',
+ 'h':'set',
+ 'l':'reset'
+}
+# 'L':'insert_lines',
+# 'M':'delete_lines',
+# 'd':'cusor_vpos',
+
+# Alternate escape sequences, no [
+CONQUE_ESCAPE_PLAIN = {
+ 'D':'scroll_up',
+ 'E':'next_line',
+ 'H':'set_tab',
+ 'M':'scroll_down'
+}
+# 'N':'single_shift_2',
+# 'O':'single_shift_3',
+# '=':'alternate_keypad',
+# '>':'numeric_keypad',
+# '7':'save_cursor',
+# '8':'restore_cursor',
+
+# Uber alternate escape sequences, with # or ?
+CONQUE_ESCAPE_QUESTION = {
+ '1h':'new_line_mode',
+ '3h':'132_cols',
+ '4h':'smooth_scrolling',
+ '5h':'reverse_video',
+ '6h':'relative_origin',
+ '7h':'set_auto_wrap',
+ '8h':'set_auto_repeat',
+ '9h':'set_interlacing_mode',
+ '1l':'set_cursor_key',
+ '2l':'set_vt52',
+ '3l':'80_cols',
+ '4l':'set_jump_scrolling',
+ '5l':'normal_video',
+ '6l':'absolute_origin',
+ '7l':'reset_auto_wrap',
+ '8l':'reset_auto_repeat',
+ '9l':'reset_interlacing_mode'
+}
+
+CONQUE_ESCAPE_HASH = {
+ '8':'screen_alignment_test'
+}
+# '3':'double_height_top',
+# '4':'double_height_bottom',
+# '5':'single_height_single_width',
+# '6':'single_height_double_width',
+
+# Font codes {{{
+CONQUE_FONT = {
+ 0: {'description':'Normal (default)', 'attributes': {'cterm':'NONE','ctermfg':'NONE','ctermbg':'NONE','gui':'NONE','guifg':'NONE','guibg':'NONE'}, 'normal':True},
+ 1: {'description':'Bold', 'attributes': {'cterm':'BOLD','gui':'BOLD'}, 'normal':False},
+ 4: {'description':'Underlined', 'attributes': {'cterm':'UNDERLINE','gui':'UNDERLINE'}, 'normal':False},
+ 5: {'description':'Blink (appears as Bold)', 'attributes': {'cterm':'BOLD','gui':'BOLD'}, 'normal':False},
+ 7: {'description':'Inverse', 'attributes': {'cterm':'REVERSE','gui':'REVERSE'}, 'normal':False},
+ 8: {'description':'Invisible (hidden)', 'attributes': {'ctermfg':'0','ctermbg':'0','guifg':'#000000','guibg':'#000000'}, 'normal':False},
+ 22: {'description':'Normal (neither bold nor faint)', 'attributes': {'cterm':'NONE','gui':'NONE'}, 'normal':True},
+ 24: {'description':'Not underlined', 'attributes': {'cterm':'NONE','gui':'NONE'}, 'normal':True},
+ 25: {'description':'Steady (not blinking)', 'attributes': {'cterm':'NONE','gui':'NONE'}, 'normal':True},
+ 27: {'description':'Positive (not inverse)', 'attributes': {'cterm':'NONE','gui':'NONE'}, 'normal':True},
+ 28: {'description':'Visible (not hidden)', 'attributes': {'ctermfg':'NONE','ctermbg':'NONE','guifg':'NONE','guibg':'NONE'}, 'normal':True},
+ 30: {'description':'Set foreground color to Black', 'attributes': {'ctermfg':'16','guifg':'#000000'}, 'normal':False},
+ 31: {'description':'Set foreground color to Red', 'attributes': {'ctermfg':'1','guifg':'#ff0000'}, 'normal':False},
+ 32: {'description':'Set foreground color to Green', 'attributes': {'ctermfg':'2','guifg':'#00ff00'}, 'normal':False},
+ 33: {'description':'Set foreground color to Yellow', 'attributes': {'ctermfg':'3','guifg':'#ffff00'}, 'normal':False},
+ 34: {'description':'Set foreground color to Blue', 'attributes': {'ctermfg':'4','guifg':'#0000ff'}, 'normal':False},
+ 35: {'description':'Set foreground color to Magenta', 'attributes': {'ctermfg':'5','guifg':'#990099'}, 'normal':False},
+ 36: {'description':'Set foreground color to Cyan', 'attributes': {'ctermfg':'6','guifg':'#009999'}, 'normal':False},
+ 37: {'description':'Set foreground color to White', 'attributes': {'ctermfg':'7','guifg':'#ffffff'}, 'normal':False},
+ 39: {'description':'Set foreground color to default (original)', 'attributes': {'ctermfg':'NONE','guifg':'NONE'}, 'normal':True},
+ 40: {'description':'Set background color to Black', 'attributes': {'ctermbg':'16','guibg':'#000000'}, 'normal':False},
+ 41: {'description':'Set background color to Red', 'attributes': {'ctermbg':'1','guibg':'#ff0000'}, 'normal':False},
+ 42: {'description':'Set background color to Green', 'attributes': {'ctermbg':'2','guibg':'#00ff00'}, 'normal':False},
+ 43: {'description':'Set background color to Yellow', 'attributes': {'ctermbg':'3','guibg':'#ffff00'}, 'normal':False},
+ 44: {'description':'Set background color to Blue', 'attributes': {'ctermbg':'4','guibg':'#0000ff'}, 'normal':False},
+ 45: {'description':'Set background color to Magenta', 'attributes': {'ctermbg':'5','guibg':'#990099'}, 'normal':False},
+ 46: {'description':'Set background color to Cyan', 'attributes': {'ctermbg':'6','guibg':'#009999'}, 'normal':False},
+ 47: {'description':'Set background color to White', 'attributes': {'ctermbg':'7','guibg':'#ffffff'}, 'normal':False},
+ 49: {'description':'Set background color to default (original).', 'attributes': {'ctermbg':'NONE','guibg':'NONE'}, 'normal':True},
+ 90: {'description':'Set foreground color to Black', 'attributes': {'ctermfg':'8','guifg':'#000000'}, 'normal':False},
+ 91: {'description':'Set foreground color to Red', 'attributes': {'ctermfg':'9','guifg':'#ff0000'}, 'normal':False},
+ 92: {'description':'Set foreground color to Green', 'attributes': {'ctermfg':'10','guifg':'#00ff00'}, 'normal':False},
+ 93: {'description':'Set foreground color to Yellow', 'attributes': {'ctermfg':'11','guifg':'#ffff00'}, 'normal':False},
+ 94: {'description':'Set foreground color to Blue', 'attributes': {'ctermfg':'12','guifg':'#0000ff'}, 'normal':False},
+ 95: {'description':'Set foreground color to Magenta', 'attributes': {'ctermfg':'13','guifg':'#990099'}, 'normal':False},
+ 96: {'description':'Set foreground color to Cyan', 'attributes': {'ctermfg':'14','guifg':'#009999'}, 'normal':False},
+ 97: {'description':'Set foreground color to White', 'attributes': {'ctermfg':'15','guifg':'#ffffff'}, 'normal':False},
+ 100: {'description':'Set background color to Black', 'attributes': {'ctermbg':'8','guibg':'#000000'}, 'normal':False},
+ 101: {'description':'Set background color to Red', 'attributes': {'ctermbg':'9','guibg':'#ff0000'}, 'normal':False},
+ 102: {'description':'Set background color to Green', 'attributes': {'ctermbg':'10','guibg':'#00ff00'}, 'normal':False},
+ 103: {'description':'Set background color to Yellow', 'attributes': {'ctermbg':'11','guibg':'#ffff00'}, 'normal':False},
+ 104: {'description':'Set background color to Blue', 'attributes': {'ctermbg':'12','guibg':'#0000ff'}, 'normal':False},
+ 105: {'description':'Set background color to Magenta', 'attributes': {'ctermbg':'13','guibg':'#990099'}, 'normal':False},
+ 106: {'description':'Set background color to Cyan', 'attributes': {'ctermbg':'14','guibg':'#009999'}, 'normal':False},
+ 107: {'description':'Set background color to White', 'attributes': {'ctermbg':'15','guibg':'#ffffff'}, 'normal':False}
+}
+# }}}
+
+# regular expression matching (almost) all control sequences
+CONQUE_SEQ_REGEX = re.compile(ur"(\u001b\[?\??#?[0-9;]*[a-zA-Z@]|\u001b\][0-9];.*?\u0007|[\u0007-\u000f])", re.UNICODE)
+CONQUE_SEQ_REGEX_CTL = re.compile(ur"^[\u0007-\u000f]$", re.UNICODE)
+CONQUE_SEQ_REGEX_CSI = re.compile(ur"^\u001b\[", re.UNICODE)
+CONQUE_SEQ_REGEX_TITLE = re.compile(ur"^\u001b\]", re.UNICODE)
+CONQUE_SEQ_REGEX_HASH = re.compile(ur"^\u001b#", re.UNICODE)
+CONQUE_SEQ_REGEX_ESC = re.compile(ur"^\u001b", re.UNICODE)
+
+# match table output
+CONQUE_TABLE_OUTPUT = re.compile("^\s*\|\s.*\s\|\s*$|^\s*\+[=+-]+\+\s*$")
+
+# }}}
+
+###################################################################################################
+class Conque:
+
+ # CLASS PROPERTIES {{{
+
+ # screen object
+ screen = None
+
+ # subprocess object
+ proc = None
+
+ # terminal dimensions and scrolling region
+ columns = 80 # same as $COLUMNS
+ lines = 24 # same as $LINES
+ working_columns = 80 # can be changed by CSI ? 3 l/h
+ working_lines = 24 # can be changed by CSI r
+
+ # top/bottom of the scroll region
+ top = 1 # relative to top of screen
+ bottom = 24 # relative to top of screen
+
+ # cursor position
+ l = 1 # current cursor line
+ c = 1 # current cursor column
+
+ # autowrap mode
+ autowrap = True
+
+ # absolute coordinate mode
+ absolute_coords = True
+
+ # tabstop positions
+ tabstops = []
+
+ # enable colors
+ enable_colors = True
+
+ # color changes
+ color_changes = {}
+
+ # color history
+ color_history = {}
+
+ # don't wrap table output
+ unwrap_tables = True
+
+ # wrap CUF/CUB around line breaks
+ wrap_cursor = False
+
+ # }}}
+
+ # constructor
+ def __init__(self): # {{{
+ self.screen = ConqueScreen()
+ # }}}
+
+ # start program and initialize this instance
+ def open(self, command, options): # {{{
+
+ # int vars
+ self.columns = vim.current.window.width
+ self.lines = vim.current.window.height
+ self.working_columns = vim.current.window.width
+ self.working_lines = vim.current.window.height
+ self.bottom = vim.current.window.height
+
+ # init color
+ self.enable_colors = options['color']
+
+ # init tabstops
+ self.init_tabstops()
+
+ # open command
+ self.proc = ConqueSubprocess()
+ self.proc.open(command, { 'TERM' : options['TERM'], 'CONQUE' : '1', 'LINES' : str(self.lines), 'COLUMNS' : str(self.columns)})
+ # }}}
+
+ # write to pty
+ def write(self, input): # {{{
+
+
+ # check if window size has changed
+ self.update_window_size()
+
+ # write and read
+ self.proc.write(input)
+ self.read(1)
+ # }}}
+
+ # read from pty, and update buffer
+ def read(self, timeout = 1): # {{{
+ # read from subprocess
+ output = self.proc.read(timeout)
+ # and strip null chars
+ output = output.replace(chr(0), '')
+
+ if output == '':
+ return
+
+
+
+
+
+ chunks = CONQUE_SEQ_REGEX.split(output)
+
+
+
+
+
+ # don't go through all the csi regex if length is one (no matches)
+ if len(chunks) == 1:
+
+ self.plain_text(chunks[0])
+
+ else:
+ for s in chunks:
+ if s == '':
+ continue
+
+
+
+
+
+
+ # Check for control character match {{{
+ if CONQUE_SEQ_REGEX_CTL.match(s[0]):
+
+ nr = ord(s[0])
+ if nr in CONQUE_CTL:
+ getattr(self, 'ctl_' + CONQUE_CTL[nr])()
+ else:
+
+ pass
+ # }}}
+
+ # check for escape sequence match {{{
+ elif CONQUE_SEQ_REGEX_CSI.match(s):
+
+ if s[-1] in CONQUE_ESCAPE:
+ csi = self.parse_csi(s[2:])
+
+ getattr(self, 'csi_' + CONQUE_ESCAPE[s[-1]])(csi)
+ else:
+
+ pass
+ # }}}
+
+ # check for title match {{{
+ elif CONQUE_SEQ_REGEX_TITLE.match(s):
+
+ self.change_title(s[2], s[4:-1])
+ # }}}
+
+ # check for hash match {{{
+ elif CONQUE_SEQ_REGEX_HASH.match(s):
+
+ if s[-1] in CONQUE_ESCAPE_HASH:
+ getattr(self, 'hash_' + CONQUE_ESCAPE_HASH[s[-1]])()
+ else:
+
+ pass
+ # }}}
+
+ # check for other escape match {{{
+ elif CONQUE_SEQ_REGEX_ESC.match(s):
+
+ if s[-1] in CONQUE_ESCAPE_PLAIN:
+ getattr(self, 'esc_' + CONQUE_ESCAPE_PLAIN[s[-1]])()
+ else:
+
+ pass
+ # }}}
+
+ # else process plain text {{{
+ else:
+ self.plain_text(s)
+ # }}}
+
+ # set cursor position
+ self.screen.set_cursor(self.l, self.c)
+
+ vim.command('redraw')
+
+
+ # }}}
+
+ # for polling
+ def auto_read(self): # {{{
+ self.read(1)
+ if self.c == 1:
+ vim.command('call feedkeys("\<F23>", "t")')
+ else:
+ vim.command('call feedkeys("\<F22>", "t")')
+ self.screen.set_cursor(self.l, self.c)
+ # }}}
+
+ ###############################################################################################
+ # Plain text # {{{
+
+ def plain_text(self, input):
+
+ current_line = self.screen[self.l]
+
+ if len(current_line) < self.working_columns:
+ current_line = current_line + ' ' * (self.c - len(current_line))
+
+ # if line is wider than screen
+ if self.c + len(input) - 1 > self.working_columns:
+ # Table formatting hack
+ if self.unwrap_tables and CONQUE_TABLE_OUTPUT.match(input):
+ self.screen[self.l] = current_line[ : self.c - 1] + input + current_line[ self.c + len(input) - 1 : ]
+ self.apply_color(self.c, self.c + len(input))
+ self.c += len(input)
+ return
+
+ diff = self.c + len(input) - self.working_columns - 1
+ # if autowrap is enabled
+ if self.autowrap:
+ self.screen[self.l] = current_line[ : self.c - 1] + input[ : -1 * diff ]
+ self.apply_color(self.c, self.working_columns)
+ self.ctl_nl()
+ self.ctl_cr()
+ remaining = input[ -1 * diff : ]
+
+ self.plain_text(remaining)
+ else:
+ self.screen[self.l] = current_line[ : self.c - 1] + input[ : -1 * diff - 1 ] + input[-1]
+ self.apply_color(self.c, self.working_columns)
+ self.c = self.working_columns
+
+ # no autowrap
+ else:
+ self.screen[self.l] = current_line[ : self.c - 1] + input + current_line[ self.c + len(input) - 1 : ]
+ self.apply_color(self.c, self.c + len(input))
+ self.c += len(input)
+
+ def apply_color(self, start, end):
+
+
+ # stop here if coloration is disabled
+ if not self.enable_colors:
+ return
+
+ real_line = self.screen.get_real_line(self.l)
+
+ # check for previous overlapping coloration
+
+ to_del = []
+ if self.color_history.has_key(real_line):
+ for i in range(len(self.color_history[real_line])):
+ syn = self.color_history[real_line][i]
+
+ if syn['start'] >= start and syn['start'] < end:
+
+ vim.command('syn clear ' + syn['name'])
+ to_del.append(i)
+ # outside
+ if syn['end'] > end:
+
+ self.exec_highlight(real_line, end, syn['end'], syn['highlight'])
+ elif syn['end'] > start and syn['end'] <= end:
+
+ vim.command('syn clear ' + syn['name'])
+ to_del.append(i)
+ # outside
+ if syn['start'] < start:
+
+ self.exec_highlight(real_line, syn['start'], start, syn['highlight'])
+
+ if len(to_del) > 0:
+ to_del.reverse()
+ for di in to_del:
+ del self.color_history[real_line][di]
+
+ # if there are no new colors
+ if len(self.color_changes) == 0:
+ return
+
+ highlight = ''
+ for attr in self.color_changes.keys():
+ highlight = highlight + ' ' + attr + '=' + self.color_changes[attr]
+
+ # execute the highlight
+ self.exec_highlight(real_line, start, end, highlight)
+
+ def exec_highlight(self, real_line, start, end, highlight):
+ unique_key = str(self.proc.pid)
+
+ syntax_name = 'EscapeSequenceAt_' + unique_key + '_' + str(self.l) + '_' + str(start) + '_' + str(len(self.color_history) + 1)
+ syntax_options = ' contains=ALLBUT,ConqueString,MySQLString,MySQLKeyword oneline'
+ syntax_region = 'syntax match ' + syntax_name + ' /\%' + str(real_line) + 'l\%>' + str(start - 1) + 'c.*\%<' + str(end + 1) + 'c/' + syntax_options
+ syntax_highlight = 'highlight ' + syntax_name + highlight
+
+ vim.command(syntax_region)
+ vim.command(syntax_highlight)
+
+ # add syntax name to history
+ if not self.color_history.has_key(real_line):
+ self.color_history[real_line] = []
+
+ self.color_history[real_line].append({'name':syntax_name, 'start':start, 'end':end, 'highlight':highlight})
+
+ # }}}
+
+ ###############################################################################################
+ # Control functions {{{
+
+ def ctl_nl(self):
+ # if we're in a scrolling region, scroll instead of moving cursor down
+ if self.lines != self.working_lines and self.l == self.bottom:
+ del self.screen[self.top]
+ self.screen.insert(self.bottom, '')
+ elif self.l == self.bottom:
+ self.screen.append('')
+ else:
+ self.l += 1
+
+ self.color_changes = {}
+
+ def ctl_cr(self):
+ self.c = 1
+
+ self.color_changes = {}
+
+ def ctl_bs(self):
+ if self.c > 1:
+ self.c += -1
+
+ def ctl_bel(self):
+ print 'BELL'
+
+ def ctl_tab(self):
+ # default tabstop location
+ ts = self.working_columns
+
+ # check set tabstops
+ for i in range(self.c, len(self.tabstops)):
+ if self.tabstops[i]:
+ ts = i + 1
+ break
+
+
+
+ self.c = ts
+
+ # }}}
+
+ ###############################################################################################
+ # CSI functions {{{
+
+ def csi_font(self, csi): # {{{
+ if not self.enable_colors:
+ return
+
+ # defaults to 0
+ if len(csi['vals']) == 0:
+ csi['vals'] = [0]
+
+ # 256 xterm color foreground
+ if len(csi['vals']) == 3 and csi['vals'][0] == 38 and csi['vals'][1] == 5:
+ self.color_changes['ctermfg'] = str(csi['vals'][2])
+ self.color_changes['guifg'] = '#' + self.xterm_to_rgb(csi['vals'][2])
+
+ # 256 xterm color background
+ elif len(csi['vals']) == 3 and csi['vals'][0] == 48 and csi['vals'][1] == 5:
+ self.color_changes['ctermbg'] = str(csi['vals'][2])
+ self.color_changes['guibg'] = '#' + self.xterm_to_rgb(csi['vals'][2])
+
+ # 16 colors
+ else:
+ for val in csi['vals']:
+ if CONQUE_FONT.has_key(val):
+
+ # ignore starting normal colors
+ if CONQUE_FONT[val]['normal'] and len(self.color_changes) == 0:
+
+ continue
+ # clear color changes
+ elif CONQUE_FONT[val]['normal']:
+
+ self.color_changes = {}
+ # save these color attributes for next plain_text() call
+ else:
+
+ for attr in CONQUE_FONT[val]['attributes'].keys():
+ if self.color_changes.has_key(attr) and (attr == 'cterm' or attr == 'gui'):
+ self.color_changes[attr] += ',' + CONQUE_FONT[val]['attributes'][attr]
+ else:
+ self.color_changes[attr] = CONQUE_FONT[val]['attributes'][attr]
+ # }}}
+
+ def csi_clear_line(self, csi): # {{{
+
+
+ # this escape defaults to 0
+ if len(csi['vals']) == 0:
+ csi['val'] = 0
+
+
+
+
+ # 0 means cursor right
+ if csi['val'] == 0:
+ self.screen[self.l] = self.screen[self.l][0 : self.c - 1]
+
+ # 1 means cursor left
+ elif csi['val'] == 1:
+ self.screen[self.l] = ' ' * (self.c) + self.screen[self.l][self.c : ]
+
+ # clear entire line
+ elif csi['val'] == 2:
+ self.screen[self.l] = ''
+
+ # clear colors
+ if csi['val'] == 2 or (csi['val'] == 0 and self.c == 1):
+ real_line = self.screen.get_real_line(self.l)
+ if self.color_history.has_key(real_line):
+ for syn in self.color_history[real_line]:
+ vim.command('syn clear ' + syn['name'])
+
+
+
+ # }}}
+
+ def csi_cursor_right(self, csi): # {{{
+ # we use 1 even if escape explicitly specifies 0
+ if csi['val'] == 0:
+ csi['val'] = 1
+
+
+
+
+ if self.wrap_cursor and self.c + csi['val'] > self.working_columns:
+ self.l += int(math.floor( (self.c + csi['val']) / self.working_columns ))
+ self.c = (self.c + csi['val']) % self.working_columns
+ return
+
+ self.c = self.bound(self.c + csi['val'], 1, self.working_columns)
+ # }}}
+
+ def csi_cursor_left(self, csi): # {{{
+ # we use 1 even if escape explicitly specifies 0
+ if csi['val'] == 0:
+ csi['val'] = 1
+
+ if self.wrap_cursor and csi['val'] >= self.c:
+ self.l += int(math.floor( (self.c - csi['val']) / self.working_columns ))
+ self.c = self.working_columns - (csi['val'] - self.c) % self.working_columns
+ return
+
+ self.c = self.bound(self.c - csi['val'], 1, self.working_columns)
+ # }}}
+
+ def csi_cursor_to_column(self, csi): # {{{
+ self.c = self.bound(csi['val'], 1, self.working_columns)
+ # }}}
+
+ def csi_cursor_up(self, csi): # {{{
+ self.l = self.bound(self.l - csi['val'], self.top, self.bottom)
+
+ self.color_changes = {}
+ # }}}
+
+ def csi_cursor_down(self, csi): # {{{
+ self.l = self.bound(self.l + csi['val'], self.top, self.bottom)
+
+ self.color_changes = {}
+ # }}}
+
+ def csi_clear_screen(self, csi): # {{{
+ # default to 0
+ if len(csi['vals']) == 0:
+ csi['val'] = 0
+
+ # 2 == clear entire screen
+ if csi['val'] == 2:
+ self.l = 1
+ self.c = 1
+ self.screen.clear()
+
+ # 0 == clear down
+ elif csi['val'] == 0:
+ for l in range(self.bound(self.l + 1, 1, self.lines), self.lines + 1):
+ self.screen[l] = ''
+
+ # clear end of current line
+ self.csi_clear_line(self.parse_csi('K'))
+
+ # 1 == clear up
+ elif csi['val'] == 1:
+ for l in range(1, self.bound(self.l, 1, self.lines + 1)):
+ self.screen[l] = ''
+
+ # clear beginning of current line
+ self.csi_clear_line(self.parse_csi('1K'))
+
+ # clear coloration
+ if csi['val'] == 2 or csi['val'] == 0:
+ real_line = self.screen.get_real_line(self.l)
+ for line in self.color_history.keys():
+ if line >= real_line:
+ for syn in self.color_history[line]:
+ vim.command('syn clear ' + syn['name'])
+
+ self.color_changes = {}
+ # }}}
+
+ def csi_delete_chars(self, csi): # {{{
+ self.screen[self.l] = self.screen[self.l][ : self.c ] + self.screen[self.l][ self.c + csi['val'] : ]
+ # }}}
+
+ def csi_add_spaces(self, csi): # {{{
+ self.screen[self.l] = self.screen[self.l][ : self.c - 1] + ' ' * csi['val'] + self.screen[self.l][self.c : ]
+ # }}}
+
+ def csi_cursor(self, csi): # {{{
+ if len(csi['vals']) == 2:
+ new_line = csi['vals'][0]
+ new_col = csi['vals'][1]
+ else:
+ new_line = 1
+ new_col = 1
+
+ if self.absolute_coords:
+ self.l = self.bound(new_line, 1, self.lines)
+ else:
+ self.l = self.bound(self.top + new_line - 1, self.top, self.bottom)
+
+ self.c = self.bound(new_col, 1, self.working_columns)
+ if self.c > len(self.screen[self.l]):
+ self.screen[self.l] = self.screen[self.l] + ' ' * (self.c - len(self.screen[self.l]))
+
+ # }}}
+
+ def csi_set_coords(self, csi): # {{{
+ if len(csi['vals']) == 2:
+ new_start = csi['vals'][0]
+ new_end = csi['vals'][1]
+ else:
+ new_start = 1
+ new_end = vim.current.window.height
+
+ self.top = new_start
+ self.bottom = new_end
+ self.working_lines = new_end - new_start + 1
+
+ # if cursor is outside scrolling region, reset it
+ if self.l < self.top:
+ self.l = self.top
+ elif self.l > self.bottom:
+ self.l = self.bottom
+
+ self.color_changes = {}
+ # }}}
+
+ def csi_tab_clear(self, csi): # {{{
+ # this escape defaults to 0
+ if len(csi['vals']) == 0:
+ csi['val'] = 0
+
+
+
+ if csi['val'] == 0:
+ self.tabstops[self.c - 1] = False
+ elif csi['val'] == 3:
+ for i in range(0, self.columns + 1):
+ self.tabstops[i] = False
+ # }}}
+
+ def csi_set(self, csi): # {{{
+ # 132 cols
+ if csi['val'] == 3:
+ self.csi_clear_screen(self.parse_csi('2J'))
+ self.working_columns = 132
+
+ # relative_origin
+ elif csi['val'] == 6:
+ self.absolute_coords = False
+
+ # set auto wrap
+ elif csi['val'] == 7:
+ self.autowrap = True
+
+
+ self.color_changes = {}
+ # }}}
+
+ def csi_reset(self, csi): # {{{
+ # 80 cols
+ if csi['val'] == 3:
+ self.csi_clear_screen(self.parse_csi('2J'))
+ self.working_columns = 80
+
+ # absolute origin
+ elif csi['val'] == 6:
+ self.absolute_coords = True
+
+ # reset auto wrap
+ elif csi['val'] == 7:
+ self.autowrap = False
+
+
+ self.color_changes = {}
+ # }}}
+
+ # }}}
+
+ ###############################################################################################
+ # ESC functions {{{
+
+ def esc_scroll_up(self): # {{{
+ self.ctl_nl()
+
+ self.color_changes = {}
+ # }}}
+
+ def esc_next_line(self): # {{{
+ self.ctl_nl()
+ self.c = 1
+ # }}}
+
+ def esc_set_tab(self): # {{{
+
+ if self.c <= len(self.tabstops):
+ self.tabstops[self.c - 1] = True
+ # }}}
+
+ def esc_scroll_down(self): # {{{
+ if self.l == self.top:
+ del self.screen[self.bottom]
+ self.screen.insert(self.top, '')
+ else:
+ self.l += -1
+
+ self.color_changes = {}
+ # }}}
+
+ # }}}
+
+ ###############################################################################################
+ # HASH functions {{{
+
+ def hash_screen_alignment_test(self): # {{{
+ self.csi_clear_screen(self.parse_csi('2J'))
+ self.working_lines = self.lines
+ for l in range(1, self.lines + 1):
+ self.screen[l] = 'E' * self.working_columns
+ # }}}
+
+ # }}}
+
+ ###############################################################################################
+ # Random stuff {{{
+
+ def change_title(self, key, val):
+
+
+ if key == '0' or key == '2':
+
+ vim.command('setlocal statusline=' + re.escape(val))
+
+ def paste(self):
+ self.write(vim.eval('@@'))
+ self.read(50)
+
+ def paste_selection(self):
+ self.write(vim.eval('@@'))
+
+ def update_window_size(self):
+ # resize if needed
+ if vim.current.window.width != self.columns or vim.current.window.height != self.lines:
+
+ # reset all window size attributes to default
+ self.columns = vim.current.window.width
+ self.lines = vim.current.window.height
+ self.working_columns = vim.current.window.width
+ self.working_lines = vim.current.window.height
+ self.bottom = vim.current.window.height
+
+ # reset screen object attributes
+ self.l = self.screen.reset_size(self.l)
+
+ # reset tabstops
+ self.init_tabstops()
+
+
+
+ # signal process that screen size has changed
+ self.proc.window_resize(self.lines, self.columns)
+
+ def init_tabstops(self):
+ for i in range(0, self.columns + 1):
+ if i % 8 == 0:
+ self.tabstops.append(True)
+ else:
+ self.tabstops.append(False)
+
+ # }}}
+
+ ###############################################################################################
+ # Utility {{{
+
+ def parse_csi(self, s): # {{{
+ attr = { 'key' : s[-1], 'flag' : '', 'val' : 1, 'vals' : [] }
+
+ if len(s) == 1:
+ return attr
+
+ full = s[0:-1]
+
+ if full[0] == '?':
+ full = full[1:]
+ attr['flag'] = '?'
+
+ if full != '':
+ vals = full.split(';')
+ for val in vals:
+
+ val = re.sub("\D", "", val)
+
+ if val != '':
+ attr['vals'].append(int(val))
+
+ if len(attr['vals']) == 1:
+ attr['val'] = int(attr['vals'][0])
+
+ return attr
+ # }}}
+
+ def bound(self, val, min, max): # {{{
+ if val > max:
+ return max
+
+ if val < min:
+ return min
+
+ return val
+ # }}}
+
+ def xterm_to_rgb(self, color_code): # {{{
+ if color_code < 16:
+ ascii_colors = ['000000', 'CD0000', '00CD00', 'CDCD00', '0000EE', 'CD00CD', '00CDCD', 'E5E5E5',
+ '7F7F7F', 'FF0000', '00FF00', 'FFFF00', '5C5CFF', 'FF00FF', '00FFFF', 'FFFFFF']
+ return ascii_colors[color_code]
+
+ elif color_code < 232:
+ cc = int(color_code) - 16
+
+ p1 = "%02x" % (math.floor(cc / 36) * (255/5))
+ p2 = "%02x" % (math.floor((cc % 36) / 6) * (255/5))
+ p3 = "%02x" % (math.floor(cc % 6) * (255/5))
+
+ return p1 + p2 + p3
+ else:
+ grey_tone = "%02x" % math.floor((255/24) * (color_code - 232))
+ return grey_tone + grey_tone + grey_tone
+ # }}}
+
+ # }}}
+
+
+import os, signal, pty, tty, select, fcntl, termios, struct
+
+###################################################################################################
+class ConqueSubprocess:
+
+ # process id
+ pid = 0
+
+ # stdout+stderr file descriptor
+ fd = None
+
+ # constructor
+ def __init__(self): # {{{
+ self.pid = 0
+ # }}}
+
+ # create the pty or whatever (whatever == windows)
+ def open(self, command, env = {}): # {{{
+ command_arr = command.split()
+ executable = command_arr[0]
+ args = command_arr
+
+ try:
+ self.pid, self.fd = pty.fork()
+
+ except:
+ pass
+
+
+ # child proc, replace with command after altering terminal attributes
+ if self.pid == 0:
+
+ # set requested environment variables
+ for k in env.keys():
+ os.environ[k] = env[k]
+
+ # set some attributes
+ try:
+ attrs = tty.tcgetattr(1)
+ attrs[0] = attrs[0] ^ tty.IGNBRK
+ attrs[0] = attrs[0] | tty.BRKINT | tty.IXANY | tty.IMAXBEL
+ attrs[2] = attrs[2] | tty.HUPCL
+ attrs[3] = attrs[3] | tty.ICANON | tty.ECHO | tty.ISIG | tty.ECHOKE
+ attrs[6][tty.VMIN] = 1
+ attrs[6][tty.VTIME] = 0
+ tty.tcsetattr(1, tty.TCSANOW, attrs)
+ except:
+ pass
+
+ os.execvp(executable, args)
+
+ # else master, do nothing
+ else:
+ pass
+
+ # }}}
+
+ # read from pty
+ # XXX - select.poll() doesn't work in OS X!!!!!!!
+ def read(self, timeout = 1): # {{{
+
+ output = ''
+ read_timeout = float(timeout) / 1000
+
+ try:
+ # what, no do/while?
+ while 1:
+ s_read, s_write, s_error = select.select( [ self.fd ], [], [], read_timeout)
+
+ lines = ''
+ for s_fd in s_read:
+ try:
+ lines = os.read( self.fd, 32 )
+ except:
+ pass
+ output = output + lines
+
+ if lines == '':
+ break
+ except:
+ pass
+
+ return output
+ # }}}
+
+ # I guess this one's not bad
+ def write(self, input): # {{{
+ try:
+ os.write(self.fd, input)
+ except:
+ pass
+ # }}}
+
+ # signal process
+ def signal(self, signum): # {{{
+ try:
+ os.kill(self.pid, signum)
+ except:
+ pass
+ # }}}
+
+ # get process status
+ def get_status(self): #{{{
+
+ p_status = True
+
+ try:
+ if os.waitpid( self.pid, os.WNOHANG )[0]:
+ p_status = False
+ except:
+ p_status = False
+
+ return p_status
+
+ # }}}
+
+ # update window size in kernel, then send SIGWINCH to fg process
+ def window_resize(self, lines, columns): # {{{
+ try:
+ fcntl.ioctl(self.fd, termios.TIOCSWINSZ, struct.pack("HHHH", lines, columns, 0, 0))
+ os.kill(self.pid, signal.SIGWINCH)
+ except:
+ pass
+
+ # }}}
+
+
+###################################################################################################
+# ConqueScreen is an extention of the vim.current.buffer object
+# It restricts the working indices of the buffer object to the scroll region which pty is expecting
+# It also uses 1-based indexes, to match escape sequence commands
+#
+# E.g.:
+# s = ConqueScreen()
+# ...
+# s[5] = 'Set 5th line in terminal to this line'
+# s.append('Add new line to terminal')
+# s[5] = 'Since previous append() command scrolled the terminal down, this is a different line than first cb[5] call'
+#
+
+import vim
+
+class ConqueScreen(object):
+
+ # CLASS PROPERTIES {{{
+
+ # the buffer
+ buffer = None
+
+ # screen and scrolling regions
+ screen_top = 1
+
+ # screen width
+ screen_width = 80
+ screen_height = 80
+
+ # }}}
+
+ def __init__(self): # {{{
+ self.buffer = vim.current.buffer
+
+ self.screen_top = 1
+ self.screen_width = vim.current.window.width
+ self.screen_height = vim.current.window.height
+ # }}}
+
+ ###############################################################################################
+ # List overload {{{
+ def __len__(self): # {{{
+ return len(self.buffer)
+ # }}}
+
+ def __getitem__(self, key): # {{{
+ real_line = self.get_real_idx(key)
+
+ # if line is past buffer end, add lines to buffer
+ if real_line >= len(self.buffer):
+ for i in range(len(self.buffer), real_line + 1):
+ self.append(' ' * self.screen_width)
+
+ return self.buffer[ real_line ]
+ # }}}
+
+ def __setitem__(self, key, value): # {{{
+ real_line = self.get_real_idx(key)
+
+ # if line is past end of screen, append
+ if real_line == len(self.buffer):
+ self.buffer.append(value)
+ else:
+ self.buffer[ real_line ] = value
+ # }}}
+
+ def __delitem__(self, key): # {{{
+ del self.buffer[ self.screen_top + key - 2 ]
+ # }}}
+
+ def append(self, value): # {{{
+ if len(self.buffer) > self.screen_top + self.screen_height - 1:
+ self.buffer[len(self.buffer) - 1] = value
+ else:
+ self.buffer.append(value)
+
+ if len(self.buffer) > self.screen_top + self.screen_height - 1:
+ self.screen_top += 1
+ if vim.current.buffer.number == self.buffer.number:
+ vim.command('normal G')
+ # }}}
+
+ def insert(self, line, value): # {{{
+
+ l = self.screen_top + line - 2
+ self.buffer[l:l] = [ value ]
+
+ # }}}
+ # }}}
+
+ ###############################################################################################
+ # Util {{{
+ def get_top(self): # {{{
+ return self.screen_top
+ # }}}
+
+ def get_real_idx(self, line): # {{{
+ return (self.screen_top + line - 2)
+ # }}}
+
+ def get_real_line(self, line): # {{{
+ return (self.screen_top + line - 1)
+ # }}}
+
+ def set_screen_width(self, width): # {{{
+ self.screen_width = width
+ # }}}
+
+ # }}}
+
+ ###############################################################################################
+ def clear(self): # {{{
+ self.buffer.append(' ')
+ vim.command('normal Gzt')
+ self.screen_top = len(self.buffer)
+ # }}}
+
+ def set_cursor(self, line, column): # {{{
+ # figure out line
+ real_line = self.screen_top + line - 1
+ if real_line > len(self.buffer):
+ for l in range(len(self.buffer) - 1, real_line):
+ self.buffer.append('')
+
+ # figure out column
+ real_column = column
+ if len(self.buffer[real_line - 1]) < real_column:
+ self.buffer[real_line - 1] = self.buffer[real_line - 1] + ' ' * (real_column - len(self.buffer[real_line - 1]))
+
+ # python version is occasionally grumpy
+ try:
+ vim.current.window.cursor = (real_line, real_column - 1)
+ except:
+ vim.command('call cursor(' + str(real_line) + ', ' + str(real_column) + ')')
+ # }}}
+
+ def reset_size(self, line): # {{{
+
+
+
+
+ # save cursor line number
+ real_line = self.screen_top + line
+
+ # reset screen size
+ self.screen_width = vim.current.window.width
+ self.screen_height = vim.current.window.height
+ self.screen_top = len(self.buffer) - vim.current.window.height + 1
+ if self.screen_top < 1:
+ self.screen_top = 1
+
+
+ # align bottom of buffer to bottom of screen
+ vim.command('normal ' + str(self.screen_height) + 'kG')
+
+ # return new relative line number
+ return (real_line - self.screen_top)
+ # }}}
+
+ def scroll_to_bottom(self): # {{{
+ vim.current.window.cursor = (len(self.buffer) - 1, 1)
+ # }}}
+
+ def align(self): # {{{
+ # align bottom of buffer to bottom of screen
+ vim.command('normal ' + str(self.screen_height) + 'kG')
+ # }}}
+
+
+EOF
+
+endif
+
diff --git a/vim/autoload/delimitMate.vim b/vim/autoload/delimitMate.vim
new file mode 100644
index 0000000..bd10975
--- /dev/null
+++ b/vim/autoload/delimitMate.vim
@@ -0,0 +1,586 @@
+" File: autoload/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".
+" ============================================================================
+
+" Utilities {{{
+
+let delimitMate_loaded = 1
+
+function! delimitMate#ShouldJump() "{{{
+ " Returns 1 if the next character is a closing delimiter.
+ let col = col('.')
+ let lcol = col('$')
+ let char = getline('.')[col - 1]
+
+ " Closing delimiter on the right.
+ for cdel in b:_l_delimitMate_right_delims + b:_l_delimitMate_quotes_list
+ if char == cdel
+ return 1
+ endif
+ endfor
+
+ " Closing delimiter with space expansion.
+ let nchar = getline('.')[col]
+ if b:_l_delimitMate_expand_space && char == " "
+ for cdel in b:_l_delimitMate_right_delims + b:_l_delimitMate_quotes_list
+ if nchar == cdel
+ return 1
+ endif
+ endfor
+ endif
+
+ " Closing delimiter with CR expansion.
+ let uchar = getline(line('.') + 1)[0]
+ if b:_l_delimitMate_expand_cr && char == ""
+ for cdel in b:_l_delimitMate_right_delims + b:_l_delimitMate_quotes_list
+ if uchar == cdel
+ return 1
+ endif
+ endfor
+ endif
+
+ return 0
+endfunction "}}}
+
+function! delimitMate#IsEmptyPair(str) "{{{
+ for pair in b:_l_delimitMate_matchpairs_list
+ if a:str == join( split( pair, ':' ),'' )
+ return 1
+ endif
+ endfor
+ for quote in b:_l_delimitMate_quotes_list
+ if a:str == quote . quote
+ return 1
+ endif
+ endfor
+ return 0
+endfunction "}}}
+
+function! delimitMate#IsCRExpansion() " {{{
+ let nchar = getline(line('.')-1)[-1:]
+ let schar = getline(line('.')+1)[:0]
+ let isEmpty = getline('.') == ""
+ if index(b:_l_delimitMate_left_delims, nchar) > -1 &&
+ \ index(b:_l_delimitMate_left_delims, nchar) == index(b:_l_delimitMate_right_delims, schar) &&
+ \ isEmpty
+ return 1
+ elseif index(b:_l_delimitMate_quotes_list, nchar) > -1 &&
+ \ index(b:_l_delimitMate_quotes_list, nchar) == index(b:_l_delimitMate_quotes_list, schar) &&
+ \ isEmpty
+ return 1
+ else
+ return 0
+ endif
+endfunction " }}} delimitMate#IsCRExpansion()
+
+function! delimitMate#IsSpaceExpansion() " {{{
+ let line = getline('.')
+ let col = col('.')-2
+ if col > 0
+ let pchar = line[col - 1]
+ let nchar = line[col + 2]
+ let isSpaces = (line[col] == line[col+1] && line[col] == " ")
+
+ if index(b:_l_delimitMate_left_delims, pchar) > -1 &&
+ \ index(b:_l_delimitMate_left_delims, pchar) == index(b:_l_delimitMate_right_delims, nchar) &&
+ \ isSpaces
+ return 1
+ elseif index(b:_l_delimitMate_quotes_list, pchar) > -1 &&
+ \ index(b:_l_delimitMate_quotes_list, pchar) == index(b:_l_delimitMate_quotes_list, nchar) &&
+ \ isSpaces
+ return 1
+ endif
+ endif
+ return 0
+endfunction " }}} IsSpaceExpansion()
+
+function! delimitMate#WithinEmptyPair() "{{{
+ let cur = strpart( getline('.'), col('.')-2, 2 )
+ return delimitMate#IsEmptyPair( cur )
+endfunction "}}}
+
+function! delimitMate#WriteBefore(str) "{{{
+ let len = len(a:str)
+ let line = getline('.')
+ let col = col('.')-2
+ if col < 0
+ call setline('.',line[(col+len+1):])
+ else
+ call setline('.',line[:(col)].line[(col+len+1):])
+ endif
+ return a:str
+endfunction " }}}
+
+function! delimitMate#WriteAfter(str) "{{{
+ let len = len(a:str)
+ let line = getline('.')
+ let col = col('.')-2
+ if (col) < 0
+ call setline('.',a:str.line)
+ else
+ call setline('.',line[:(col)].a:str.line[(col+len):])
+ endif
+ return ''
+endfunction " }}}
+
+function! delimitMate#GetSyntaxRegion(line, col) "{{{
+ return synIDattr(synIDtrans(synID(a:line, a:col, 1)), 'name')
+endfunction " }}}
+
+function! delimitMate#GetCurrentSyntaxRegion() "{{{
+ let col = col('.')
+ if col == col('$')
+ let col = col - 1
+ endif
+ return delimitMate#GetSyntaxRegion(line('.'), col)
+endfunction " }}}
+
+function! delimitMate#GetCurrentSyntaxRegionIf(char) "{{{
+ let col = col('.')
+ let origin_line = getline('.')
+ let changed_line = strpart(origin_line, 0, col - 1) . a:char . strpart(origin_line, col - 1)
+ call setline('.', changed_line)
+ let region = delimitMate#GetSyntaxRegion(line('.'), col)
+ call setline('.', origin_line)
+ return region
+endfunction "}}}
+
+function! delimitMate#IsForbidden(char) "{{{
+ if b:_l_delimitMate_excluded_regions_enabled == 0
+ return 0
+ endif
+ "let result = index(b:_l_delimitMate_excluded_regions_list, delimitMate#GetCurrentSyntaxRegion()) >= 0
+ if index(b:_l_delimitMate_excluded_regions_list, delimitMate#GetCurrentSyntaxRegion()) >= 0
+ "echom "Forbidden 1!"
+ return 1
+ endif
+ let region = delimitMate#GetCurrentSyntaxRegionIf(a:char)
+ "let result = index(b:_l_delimitMate_excluded_regions_list, region) >= 0
+ "return result || region == 'Comment'
+ "echom "Forbidden 2!"
+ return index(b:_l_delimitMate_excluded_regions_list, region) >= 0
+endfunction "}}}
+
+function! delimitMate#FlushBuffer() " {{{
+ let b:_l_delimitMate_buffer = []
+ return ''
+endfunction " }}}
+
+function! delimitMate#BalancedParens(char) "{{{
+ " Returns:
+ " = 0 => Parens balanced.
+ " > 0 => More opening parens.
+ " < 0 => More closing parens.
+
+ let line = getline('.')
+ let col = col('.') - 2
+ let col = col >= 0 ? col : 0
+ let list = split(line, '\zs')
+ let left = b:_l_delimitMate_left_delims[index(b:_l_delimitMate_right_delims, a:char)]
+ let right = a:char
+ let opening = 0
+ let closing = 0
+
+ " If the cursor is not at the beginning, count what's behind it.
+ if col > 0
+ " Find the first opening paren:
+ let start = index(list, left)
+ " Must be before cursor:
+ let start = start < col ? start : col - 1
+ " Now count from the first opening until the cursor, this will prevent
+ " extra closing parens from being counted.
+ let opening = count(list[start : col - 1], left)
+ let closing = count(list[start : col - 1], right)
+ " I don't care if there are more closing parens than opening parens.
+ let closing = closing > opening ? opening : closing
+ endif
+
+ " Evaluate parens from the cursor to the end:
+ let opening += count(list[col :], left)
+ let closing += count(list[col :], right)
+
+ "echom "–––––––––"
+ "echom line
+ "echom col
+ ""echom left.":".a:char
+ "echom string(list)
+ "echom string(list[start : col - 1]) . " : " . string(list[col :])
+ "echom opening . " - " . closing . " = " . (opening - closing)
+
+ " Return the found balance:
+ return opening - closing
+endfunction "}}}
+
+function! delimitMate#RmBuffer(num) " {{{
+ if len(b:_l_delimitMate_buffer) > 0
+ call remove(b:_l_delimitMate_buffer, 0, (a:num-1))
+ endif
+ return ""
+endfunction " }}}
+
+" }}}
+
+" Doers {{{
+function! delimitMate#SkipDelim(char) "{{{
+ if delimitMate#IsForbidden(a:char)
+ return a:char
+ endif
+ let col = col('.') - 1
+ let line = getline('.')
+ if col > 0
+ let cur = line[col]
+ let pre = line[col-1]
+ else
+ let cur = line[col]
+ let pre = ""
+ endif
+ if pre == "\\"
+ " Escaped character
+ return a:char
+ elseif cur == a:char
+ " Exit pair
+ "return delimitMate#WriteBefore(a:char)
+ return a:char . delimitMate#Del()
+ elseif delimitMate#IsEmptyPair( pre . a:char )
+ " Add closing delimiter and jump back to the middle.
+ call insert(b:_l_delimitMate_buffer, a:char)
+ return delimitMate#WriteAfter(a:char)
+ else
+ " Nothing special here, return the same character.
+ return a:char
+ endif
+endfunction "}}}
+
+function! delimitMate#ParenDelim(char) " {{{
+ if delimitMate#IsForbidden(a:char)
+ return ''
+ endif
+ " Try to balance matchpairs
+ if b:_l_delimitMate_balance_matchpairs &&
+ \ delimitMate#BalancedParens(a:char) <= 0
+ return ''
+ endif
+ let line = getline('.')
+ let col = col('.')-2
+ let left = b:_l_delimitMate_left_delims[index(b:_l_delimitMate_right_delims,a:char)]
+ let smart_matchpairs = substitute(b:_l_delimitMate_smart_matchpairs, '\\!', left, 'g')
+ let smart_matchpairs = substitute(smart_matchpairs, '\\#', a:char, 'g')
+ "echom left.':'.smart_matchpairs . ':' . matchstr(line[col+1], smart_matchpairs)
+ if b:_l_delimitMate_smart_matchpairs != '' &&
+ \ line[col+1:] =~ smart_matchpairs
+ return ''
+ elseif (col) < 0
+ call setline('.',a:char.line)
+ call insert(b:_l_delimitMate_buffer, a:char)
+ else
+ "echom string(col).':'.line[:(col)].'|'.line[(col+1):]
+ call setline('.',line[:(col)].a:char.line[(col+1):])
+ call insert(b:_l_delimitMate_buffer, a:char)
+ endif
+ return ''
+endfunction " }}}
+
+function! delimitMate#QuoteDelim(char) "{{{
+ if delimitMate#IsForbidden(a:char)
+ return a:char
+ endif
+ let line = getline('.')
+ let col = col('.') - 2
+ if line[col] == "\\"
+ " Seems like a escaped character, insert one quotation mark.
+ return a:char
+ elseif line[col + 1] == a:char &&
+ \ index(b:_l_delimitMate_nesting_quotes, a:char) < 0
+ " Get out of the string.
+ return a:char . delimitMate#Del()
+ elseif (line[col] =~ '\w' && a:char == "'") ||
+ \ (b:_l_delimitMate_smart_quotes &&
+ \ (line[col] =~ '\w' ||
+ \ line[col + 1] =~ '\w'))
+ " Seems like an apostrophe or a smart quote case, insert a single quote.
+ return a:char
+ elseif (line[col] == a:char && line[col + 1 ] != a:char) && b:_l_delimitMate_smart_quotes
+ " Seems like we have an unbalanced quote, insert one quotation mark and jump to the middle.
+ call insert(b:_l_delimitMate_buffer, a:char)
+ return delimitMate#WriteAfter(a:char)
+ else
+ " Insert a pair and jump to the middle.
+ call insert(b:_l_delimitMate_buffer, a:char)
+ call delimitMate#WriteAfter(a:char)
+ return a:char
+ endif
+endfunction "}}}
+
+function! delimitMate#JumpOut(char) "{{{
+ if delimitMate#IsForbidden(a:char)
+ return a:char
+ endif
+ let line = getline('.')
+ let col = col('.')-2
+ if line[col+1] == a:char
+ return a:char . delimitMate#Del()
+ else
+ return a:char
+ endif
+endfunction " }}}
+
+function! delimitMate#JumpAny(key) " {{{
+ if delimitMate#IsForbidden('')
+ return a:key
+ endif
+ if !delimitMate#ShouldJump()
+ return a:key
+ endif
+ " Let's get the character on the right.
+ let char = getline('.')[col('.')-1]
+ if char == " "
+ " Space expansion.
+ "let char = char . getline('.')[col('.')] . delimitMate#Del()
+ return char . getline('.')[col('.')] . delimitMate#Del() . delimitMate#Del()
+ "call delimitMate#RmBuffer(1)
+ elseif char == ""
+ " CR expansion.
+ "let char = "\<CR>" . getline(line('.') + 1)[0] . "\<Del>"
+ let b:_l_delimitMate_buffer = []
+ return "\<CR>" . getline(line('.') + 1)[0] . "\<Del>"
+ else
+ "call delimitMate#RmBuffer(1)
+ return char . delimitMate#Del()
+ endif
+endfunction " delimitMate#JumpAny() }}}
+
+function! delimitMate#JumpMany() " {{{
+ let line = getline('.')[col('.') - 1 : ]
+ let len = len(line)
+ let rights = ""
+ let found = 0
+ let i = 0
+ while i < len
+ let char = line[i]
+ if index(b:_l_delimitMate_quotes_list, char) >= 0 ||
+ \ index(b:_l_delimitMate_right_delims, char) >= 0
+ let rights .= "\<Right>"
+ let found = 1
+ elseif found == 0
+ let rights .= "\<Right>"
+ else
+ break
+ endif
+ let i += 1
+ endwhile
+ if found == 1
+ return rights
+ else
+ return ''
+ endif
+endfunction " delimitMate#JumpMany() }}}
+
+function! delimitMate#ExpandReturn() "{{{
+ if delimitMate#IsForbidden("")
+ return "\<CR>"
+ endif
+ if delimitMate#WithinEmptyPair()
+ " Expand:
+ call delimitMate#FlushBuffer()
+ "return "\<Esc>a\<CR>x\<CR>\<Esc>k$\"_xa"
+ return "\<CR>\<UP>\<Esc>o"
+ else
+ return "\<CR>"
+ endif
+endfunction "}}}
+
+function! delimitMate#ExpandSpace() "{{{
+ if delimitMate#IsForbidden("\<Space>")
+ return "\<Space>"
+ endif
+ if delimitMate#WithinEmptyPair()
+ " Expand:
+ call insert(b:_l_delimitMate_buffer, 's')
+ return delimitMate#WriteAfter(' ') . "\<Space>"
+ else
+ return "\<Space>"
+ endif
+endfunction "}}}
+
+function! delimitMate#BS() " {{{
+ if delimitMate#IsForbidden("")
+ return "\<BS>"
+ endif
+ if delimitMate#WithinEmptyPair()
+ "call delimitMate#RmBuffer(1)
+ return "\<BS>" . delimitMate#Del()
+" return "\<Right>\<BS>\<BS>"
+ elseif delimitMate#IsSpaceExpansion()
+ "call delimitMate#RmBuffer(1)
+ return "\<BS>" . delimitMate#Del()
+ elseif delimitMate#IsCRExpansion()
+ return "\<BS>\<Del>"
+ else
+ return "\<BS>"
+ endif
+endfunction " }}} delimitMate#BS()
+
+function! delimitMate#Del() " {{{
+ if len(b:_l_delimitMate_buffer) > 0
+ let line = getline('.')
+ let col = col('.') - 2
+ call delimitMate#RmBuffer(1)
+ call setline('.', line[:col] . line[col+2:])
+ return ''
+ else
+ return "\<Del>"
+ endif
+endfunction " }}}
+
+function! delimitMate#Finish(move_back) " {{{
+ let len = len(b:_l_delimitMate_buffer)
+ if len > 0
+ let buffer = join(b:_l_delimitMate_buffer, '')
+ let len2 = len(buffer)
+ " Reset buffer:
+ let b:_l_delimitMate_buffer = []
+ let line = getline('.')
+ let col = col('.') -2
+ "echom 'col: ' . col . '-' . line[:col] . "|" . line[col+len+1:] . '%' . buffer
+ if col < 0
+ call setline('.', line[col+len2+1:])
+ else
+ call setline('.', line[:col] . line[col+len2+1:])
+ endif
+ let i = 1
+ let lefts = ""
+ while i <= len && a:move_back
+ let lefts = lefts . "\<Left>"
+ let i += 1
+ endwhile
+ return substitute(buffer, "s", "\<Space>", 'g') . lefts
+ endif
+ return ''
+endfunction " }}}
+
+" }}}
+
+" Tools: {{{
+function! delimitMate#TestMappings() "{{{
+ let options = sort(keys(delimitMate#OptionsList()))
+ let optoutput = ['delimitMate Report', '==================', '', '* Options: ( ) default, (g) global, (b) buffer','']
+ for option in options
+ exec 'call add(optoutput, ''('.(exists('b:delimitMate_'.option) ? 'b' : exists('g:delimitMate_'.option) ? 'g' : ' ').') delimitMate_''.option.'' = ''.string(b:_l_delimitMate_'.option.'))'
+ endfor
+ call append(line('$'), optoutput + ['--------------------',''])
+
+ " Check if mappings were set. {{{
+ let imaps = b:_l_delimitMate_right_delims
+ let imaps = imaps + ( b:_l_delimitMate_autoclose ? b:_l_delimitMate_left_delims : [] )
+ let imaps = imaps +
+ \ b:_l_delimitMate_quotes_list +
+ \ b:_l_delimitMate_apostrophes_list +
+ \ ['<BS>', '<S-BS>', '<Del>', '<S-Tab>', '<Esc>'] +
+ \ ['<Up>', '<Down>', '<Left>', '<Right>', '<LeftMouse>', '<RightMouse>'] +
+ \ ['<Home>', '<End>', '<PageUp>', '<PageDown>', '<S-Down>', '<S-Up>', '<C-G>g']
+ let imaps = imaps + ( b:_l_delimitMate_expand_cr ? ['<CR>'] : [] )
+ let imaps = imaps + ( b:_l_delimitMate_expand_space ? ['<Space>'] : [] )
+
+ let vmaps =
+ \ b:_l_delimitMate_right_delims +
+ \ b:_l_delimitMate_left_delims +
+ \ b:_l_delimitMate_quotes_list
+
+ let ibroken = []
+ for map in imaps
+ if maparg(map, "i") !~? 'delimitMate'
+ let output = ''
+ if map == '|'
+ let map = '<Bar>'
+ endif
+ redir => output | execute "verbose imap ".map | redir END
+ let ibroken = ibroken + [map.": is not set:"] + split(output, '\n')
+ endif
+ endfor
+
+ unlet! output
+ if ibroken == []
+ let output = ['* Mappings:', '', 'All mappings were set-up.', '--------------------', '', '']
+ else
+ let output = ['* Mappings:', ''] + ibroken + ['--------------------', '']
+ endif
+ call append('$', output+['* Showcase:', ''])
+ " }}}
+ if b:_l_delimitMate_autoclose
+ " {{{
+ for i in range(len(b:_l_delimitMate_left_delims))
+ exec "normal Go0\<C-D>Open: " . b:_l_delimitMate_left_delims[i]. "|"
+ exec "normal o0\<C-D>Delete: " . b:_l_delimitMate_left_delims[i] . "\<BS>|"
+ exec "normal o0\<C-D>Exit: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . "|"
+ if b:_l_delimitMate_expand_space == 1
+ exec "normal o0\<C-D>Space: " . b:_l_delimitMate_left_delims[i] . " |"
+ exec "normal o0\<C-D>Delete space: " . b:_l_delimitMate_left_delims[i] . " \<BS>|"
+ endif
+ if b:_l_delimitMate_expand_cr == 1
+ exec "normal o0\<C-D>Car return: " . b:_l_delimitMate_left_delims[i] . "\<CR>|"
+ exec "normal Go0\<C-D>Delete car return: " . b:_l_delimitMate_left_delims[i] . "\<CR>0\<C-D>\<BS>|"
+ endif
+ call append(line('$'), '')
+ endfor
+ for i in range(len(b:_l_delimitMate_quotes_list))
+ exec "normal Go0\<C-D>Open: " . b:_l_delimitMate_quotes_list[i] . "|"
+ exec "normal o0\<C-D>Delete: " . b:_l_delimitMate_quotes_list[i] . "\<BS>|"
+ exec "normal o0\<C-D>Exit: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . "|"
+ if b:_l_delimitMate_expand_space == 1
+ exec "normal o0\<C-D>Space: " . b:_l_delimitMate_quotes_list[i] . " |"
+ exec "normal o0\<C-D>Delete space: " . b:_l_delimitMate_quotes_list[i] . " \<BS>|"
+ endif
+ if b:_l_delimitMate_expand_cr == 1
+ exec "normal o0\<C-D>Car return: " . b:_l_delimitMate_quotes_list[i] . "\<CR>|"
+ exec "normal Go0\<C-D>Delete car return: " . b:_l_delimitMate_quotes_list[i] . "\<CR>\<BS>|"
+ endif
+ call append(line('$'), '')
+ endfor
+ "}}}
+ else
+ "{{{
+ for i in range(len(b:_l_delimitMate_left_delims))
+ exec "normal GoOpen & close: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . "|"
+ exec "normal oDelete: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . "\<BS>|"
+ exec "normal oExit: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . b:_l_delimitMate_right_delims[i] . "|"
+ if b:_l_delimitMate_expand_space == 1
+ exec "normal oSpace: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . " |"
+ exec "normal oDelete space: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . " \<BS>|"
+ endif
+ if b:_l_delimitMate_expand_cr == 1
+ exec "normal oCar return: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . "\<CR>|"
+ exec "normal GoDelete car return: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . "\<CR>\<BS>|"
+ endif
+ call append(line('$'), '')
+ endfor
+ for i in range(len(b:_l_delimitMate_quotes_list))
+ exec "normal GoOpen & close: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . "|"
+ exec "normal oDelete: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . "\<BS>|"
+ exec "normal oExit: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . "|"
+ if b:_l_delimitMate_expand_space == 1
+ exec "normal oSpace: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . " |"
+ exec "normal oDelete space: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . " \<BS>|"
+ endif
+ if b:_l_delimitMate_expand_cr == 1
+ exec "normal oCar return: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . "\<CR>|"
+ exec "normal GoDelete car return: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . "\<CR>\<BS>|"
+ endif
+ call append(line('$'), '')
+ endfor
+ endif "}}}
+ redir => setoptions | set | filetype | redir END
+ call append(line('$'), split(setoptions,"\n")
+ \ + ['--------------------'])
+ setlocal nowrap
+endfunction "}}}
+
+function! delimitMate#OptionsList() "{{{
+ return {'autoclose' : 1,'matchpairs': &matchpairs, 'quotes' : '" '' `', 'nesting_quotes' : [], 'expand_cr' : 0, 'expand_space' : 0, 'smart_quotes' : 1, 'smart_matchpairs' : '\w', 'balance_matchpairs' : 0, 'excluded_regions' : 'Comment', 'excluded_ft' : '', 'apostrophes' : ''}
+endfunction " delimitMate#OptionsList }}}
+"}}}
+
+" vim:foldmethod=marker:foldcolumn=4
diff --git a/vim/autoload/pathogen.vim b/vim/autoload/pathogen.vim
new file mode 100644
index 0000000..4071992
--- /dev/null
+++ b/vim/autoload/pathogen.vim
@@ -0,0 +1,250 @@
+" pathogen.vim - path option manipulation
+" Maintainer: Tim Pope <http://tpo.pe/>
+" Version: 2.0
+
+" Install in ~/.vim/autoload (or ~\vimfiles\autoload).
+"
+" For management of individually installed plugins in ~/.vim/bundle (or
+" ~\vimfiles\bundle), adding `call pathogen#infect()` to your .vimrc
+" prior to `filetype plugin indent on` is the only other setup necessary.
+"
+" The API is documented inline below. For maximum ease of reading,
+" :set foldmethod=marker
+
+if exists("g:loaded_pathogen") || &cp
+ finish
+endif
+let g:loaded_pathogen = 1
+
+" Point of entry for basic default usage. Give a directory name to invoke
+" pathogen#runtime_append_all_bundles() (defaults to "bundle"), or a full path
+" to invoke pathogen#runtime_prepend_subdirectories(). Afterwards,
+" pathogen#cycle_filetype() is invoked.
+function! pathogen#infect(...) abort " {{{1
+ let source_path = a:0 ? a:1 : 'bundle'
+ if source_path =~# '[\\/]'
+ call pathogen#runtime_prepend_subdirectories(source_path)
+ else
+ call pathogen#runtime_append_all_bundles(source_path)
+ endif
+ call pathogen#cycle_filetype()
+endfunction " }}}1
+
+" Split a path into a list.
+function! pathogen#split(path) abort " {{{1
+ if type(a:path) == type([]) | return a:path | endif
+ let split = split(a:path,'\\\@<!\%(\\\\\)*\zs,')
+ return map(split,'substitute(v:val,''\\\([\\,]\)'',''\1'',"g")')
+endfunction " }}}1
+
+" Convert a list to a path.
+function! pathogen#join(...) abort " {{{1
+ if type(a:1) == type(1) && a:1
+ let i = 1
+ let space = ' '
+ else
+ let i = 0
+ let space = ''
+ endif
+ let path = ""
+ while i < a:0
+ if type(a:000[i]) == type([])
+ let list = a:000[i]
+ let j = 0
+ while j < len(list)
+ let escaped = substitute(list[j],'[,'.space.']\|\\[\,'.space.']\@=','\\&','g')
+ let path .= ',' . escaped
+ let j += 1
+ endwhile
+ else
+ let path .= "," . a:000[i]
+ endif
+ let i += 1
+ endwhile
+ return substitute(path,'^,','','')
+endfunction " }}}1
+
+" Convert a list to a path with escaped spaces for 'path', 'tag', etc.
+function! pathogen#legacyjoin(...) abort " {{{1
+ return call('pathogen#join',[1] + a:000)
+endfunction " }}}1
+
+" Remove duplicates from a list.
+function! pathogen#uniq(list) abort " {{{1
+ let i = 0
+ let seen = {}
+ while i < len(a:list)
+ if (a:list[i] ==# '' && exists('empty')) || has_key(seen,a:list[i])
+ call remove(a:list,i)
+ elseif a:list[i] ==# ''
+ let i += 1
+ let empty = 1
+ else
+ let seen[a:list[i]] = 1
+ let i += 1
+ endif
+ endwhile
+ return a:list
+endfunction " }}}1
+
+" \ on Windows unless shellslash is set, / everywhere else.
+function! pathogen#separator() abort " {{{1
+ return !exists("+shellslash") || &shellslash ? '/' : '\'
+endfunction " }}}1
+
+" Convenience wrapper around glob() which returns a list.
+function! pathogen#glob(pattern) abort " {{{1
+ let files = split(glob(a:pattern),"\n")
+ return map(files,'substitute(v:val,"[".pathogen#separator()."/]$","","")')
+endfunction "}}}1
+
+" Like pathogen#glob(), only limit the results to directories.
+function! pathogen#glob_directories(pattern) abort " {{{1
+ return filter(pathogen#glob(a:pattern),'isdirectory(v:val)')
+endfunction "}}}1
+
+" Turn filetype detection off and back on again if it was already enabled.
+function! pathogen#cycle_filetype() " {{{1
+ if exists('g:did_load_filetypes')
+ filetype off
+ filetype on
+ endif
+endfunction " }}}1
+
+" Checks if a bundle is 'disabled'. A bundle is considered 'disabled' if
+" its 'basename()' is included in g:pathogen_disabled[]' or ends in a tilde.
+function! pathogen#is_disabled(path) " {{{1
+ if a:path =~# '\~$'
+ return 1
+ elseif !exists("g:pathogen_disabled")
+ return 0
+ endif
+ let sep = pathogen#separator()
+ return index(g:pathogen_disabled, strpart(a:path, strridx(a:path, sep)+1)) != -1
+endfunction "}}}1
+
+" Prepend all subdirectories of path to the rtp, and append all 'after'
+" directories in those subdirectories.
+function! pathogen#runtime_prepend_subdirectories(path) " {{{1
+ let sep = pathogen#separator()
+ let before = filter(pathogen#glob_directories(a:path.sep."*"), '!pathogen#is_disabled(v:val)')
+ let after = filter(pathogen#glob_directories(a:path.sep."*".sep."after"), '!pathogen#is_disabled(v:val[0:-7])')
+ let rtp = pathogen#split(&rtp)
+ let path = expand(a:path)
+ call filter(rtp,'v:val[0:strlen(path)-1] !=# path')
+ let &rtp = pathogen#join(pathogen#uniq(before + rtp + after))
+ return &rtp
+endfunction " }}}1
+
+" For each directory in rtp, check for a subdirectory named dir. If it
+" exists, add all subdirectories of that subdirectory to the rtp, immediately
+" after the original directory. If no argument is given, 'bundle' is used.
+" Repeated calls with the same arguments are ignored.
+function! pathogen#runtime_append_all_bundles(...) " {{{1
+ let sep = pathogen#separator()
+ let name = a:0 ? a:1 : 'bundle'
+ if "\n".s:done_bundles =~# "\\M\n".name."\n"
+ return ""
+ endif
+ let s:done_bundles .= name . "\n"
+ let list = []
+ for dir in pathogen#split(&rtp)
+ if dir =~# '\<after$'
+ let list += filter(pathogen#glob_directories(substitute(dir,'after$',name,'').sep.'*[^~]'.sep.'after'), '!pathogen#is_disabled(v:val[0:-7])') + [dir]
+ else
+ let list += [dir] + filter(pathogen#glob_directories(dir.sep.name.sep.'*[^~]'), '!pathogen#is_disabled(v:val)')
+ endif
+ endfor
+ let &rtp = pathogen#join(pathogen#uniq(list))
+ return 1
+endfunction
+
+let s:done_bundles = ''
+" }}}1
+
+" Invoke :helptags on all non-$VIM doc directories in runtimepath.
+function! pathogen#helptags() " {{{1
+ let sep = pathogen#separator()
+ for dir in pathogen#split(&rtp)
+ if (dir.sep)[0 : strlen($VIMRUNTIME)] !=# $VIMRUNTIME.sep && filewritable(dir.sep.'doc') == 2 && !empty(filter(split(glob(dir.sep.'doc'.sep.'*'),"\n>"),'!isdirectory(v:val)')) && (!filereadable(dir.sep.'doc'.sep.'tags') || filewritable(dir.sep.'doc'.sep.'tags'))
+ helptags `=dir.'/doc'`
+ endif
+ endfor
+endfunction " }}}1
+
+command! -bar Helptags :call pathogen#helptags()
+
+" Like findfile(), but hardcoded to use the runtimepath.
+function! pathogen#runtime_findfile(file,count) "{{{1
+ let rtp = pathogen#join(1,pathogen#split(&rtp))
+ let file = findfile(a:file,rtp,a:count)
+ if file ==# ''
+ return ''
+ else
+ return fnamemodify(file,':p')
+ endif
+endfunction " }}}1
+
+" Backport of fnameescape().
+function! pathogen#fnameescape(string) " {{{1
+ if exists('*fnameescape')
+ return fnameescape(a:string)
+ elseif a:string ==# '-'
+ return '\-'
+ else
+ return substitute(escape(a:string," \t\n*?[{`$\\%#'\"|!<"),'^[+>]','\\&','')
+ endif
+endfunction " }}}1
+
+function! s:find(count,cmd,file,lcd) " {{{1
+ let rtp = pathogen#join(1,pathogen#split(&runtimepath))
+ let file = pathogen#runtime_findfile(a:file,a:count)
+ if file ==# ''
+ return "echoerr 'E345: Can''t find file \"".a:file."\" in runtimepath'"
+ elseif a:lcd
+ let path = file[0:-strlen(a:file)-2]
+ execute 'lcd `=path`'
+ return a:cmd.' '.pathogen#fnameescape(a:file)
+ else
+ return a:cmd.' '.pathogen#fnameescape(file)
+ endif
+endfunction " }}}1
+
+function! s:Findcomplete(A,L,P) " {{{1
+ let sep = pathogen#separator()
+ let cheats = {
+ \'a': 'autoload',
+ \'d': 'doc',
+ \'f': 'ftplugin',
+ \'i': 'indent',
+ \'p': 'plugin',
+ \'s': 'syntax'}
+ if a:A =~# '^\w[\\/]' && has_key(cheats,a:A[0])
+ let request = cheats[a:A[0]].a:A[1:-1]
+ else
+ let request = a:A
+ endif
+ let pattern = substitute(request,'/\|\'.sep,'*'.sep,'g').'*'
+ let found = {}
+ for path in pathogen#split(&runtimepath)
+ let path = expand(path, ':p')
+ let matches = split(glob(path.sep.pattern),"\n")
+ call map(matches,'isdirectory(v:val) ? v:val.sep : v:val')
+ call map(matches,'expand(v:val, ":p")[strlen(path)+1:-1]')
+ for match in matches
+ let found[match] = 1
+ endfor
+ endfor
+ return sort(keys(found))
+endfunction " }}}1
+
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Ve :execute s:find(<count>,'edit<bang>',<q-args>,0)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vedit :execute s:find(<count>,'edit<bang>',<q-args>,0)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vopen :execute s:find(<count>,'edit<bang>',<q-args>,1)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vsplit :execute s:find(<count>,'split',<q-args>,<bang>1)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vvsplit :execute s:find(<count>,'vsplit',<q-args>,<bang>1)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vtabedit :execute s:find(<count>,'tabedit',<q-args>,<bang>1)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vpedit :execute s:find(<count>,'pedit',<q-args>,<bang>1)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vread :execute s:find(<count>,'read',<q-args>,<bang>1)
+
+" vim:set ft=vim ts=8 sw=2 sts=2:
diff --git a/vim/autoload/rails.vim b/vim/autoload/rails.vim
new file mode 100644
index 0000000..ca36075
--- /dev/null
+++ b/vim/autoload/rails.vim
@@ -0,0 +1,4560 @@
+" autoload/rails.vim
+" Author: Tim Pope <http://tpo.pe/>
+
+" Install this file as autoload/rails.vim.
+
+if exists('g:autoloaded_rails') || &cp
+ finish
+endif
+let g:autoloaded_rails = '4.4'
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" Utility Functions {{{1
+
+let s:app_prototype = {}
+let s:file_prototype = {}
+let s:buffer_prototype = {}
+let s:readable_prototype = {}
+
+function! s:add_methods(namespace, method_names)
+ for name in a:method_names
+ let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name)
+ endfor
+endfunction
+
+function! s:function(name)
+ return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
+endfunction
+
+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:startswith(string,prefix)
+ return strpart(a:string, 0, strlen(a:prefix)) ==# a:prefix
+endfunction
+
+function! s:compact(ary)
+ return s:sub(s:sub(s:gsub(a:ary,'\n\n+','\n'),'\n$',''),'^\n','')
+endfunction
+
+function! s:uniq(list)
+ let seen = {}
+ let i = 0
+ while i < len(a:list)
+ if has_key(seen,a:list[i])
+ call remove(a:list, i)
+ else
+ let seen[a:list[i]] = 1
+ let i += 1
+ endif
+ endwhile
+ return a:list
+endfunction
+
+function! s:scrub(collection,item)
+ " Removes item from a newline separated collection
+ let col = "\n" . a:collection
+ let idx = stridx(col,"\n".a:item."\n")
+ let cnt = 0
+ while idx != -1 && cnt < 100
+ let col = strpart(col,0,idx).strpart(col,idx+strlen(a:item)+1)
+ let idx = stridx(col,"\n".a:item."\n")
+ let cnt += 1
+ endwhile
+ return strpart(col,1)
+endfunction
+
+function! s:escarg(p)
+ return s:gsub(a:p,'[ !%#]','\\&')
+endfunction
+
+function! s:esccmd(p)
+ return s:gsub(a:p,'[!%#]','\\&')
+endfunction
+
+function! s:rquote(str)
+ " Imperfect but adequate for Ruby arguments
+ if a:str =~ '^[A-Za-z0-9_/.:-]\+$'
+ return a:str
+ elseif &shell =~? 'cmd'
+ return '"'.s:gsub(s:gsub(a:str,'\','\\'),'"','\\"').'"'
+ else
+ return "'".s:gsub(s:gsub(a:str,'\','\\'),"'","'\\\\''")."'"
+ endif
+endfunction
+
+function! s:sname()
+ return fnamemodify(s:file,':t:r')
+endfunction
+
+function! s:pop_command()
+ if exists("s:command_stack") && len(s:command_stack) > 0
+ exe remove(s:command_stack,-1)
+ endif
+endfunction
+
+function! s:push_chdir(...)
+ if !exists("s:command_stack") | let s:command_stack = [] | endif
+ if exists("b:rails_root") && (a:0 ? getcwd() !=# rails#app().path() : !s:startswith(getcwd(), rails#app().path()))
+ let chdir = exists("*haslocaldir") && haslocaldir() ? "lchdir " : "chdir "
+ call add(s:command_stack,chdir.s:escarg(getcwd()))
+ exe chdir.s:escarg(rails#app().path())
+ else
+ call add(s:command_stack,"")
+ endif
+endfunction
+
+function! s:app_path(...) dict
+ return join([self.root]+a:000,'/')
+endfunction
+
+function! s:app_has_file(file) dict
+ return filereadable(self.path(a:file))
+endfunction
+
+function! s:app_find_file(name, ...) dict abort
+ let trim = strlen(self.path())+1
+ if a:0
+ let path = s:pathjoin(map(s:pathsplit(a:1),'self.path(v:val)'))
+ else
+ let path = s:pathjoin([self.path()])
+ endif
+ let suffixesadd = s:pathjoin(get(a:000,1,&suffixesadd))
+ let default = get(a:000,2,'')
+ let oldsuffixesadd = &l:suffixesadd
+ try
+ let &suffixesadd = suffixesadd
+ " Versions before 7.1.256 returned directories from findfile
+ if type(default) == type(0) && (v:version < 702 || default == -1)
+ let all = findfile(a:name,path,-1)
+ if v:version < 702
+ call filter(all,'!isdirectory(v:val)')
+ endif
+ call map(all,'s:gsub(strpart(fnamemodify(v:val,":p"),trim),"\\\\","/")')
+ return default < 0 ? all : get(all,default-1,'')
+ elseif type(default) == type(0)
+ let found = findfile(a:name,path,default)
+ else
+ let i = 1
+ let found = findfile(a:name,path)
+ while v:version < 702 && found != "" && isdirectory(found)
+ let i += 1
+ let found = findfile(a:name,path,i)
+ endwhile
+ endif
+ return found == "" ? default : s:gsub(strpart(fnamemodify(found,':p'),trim),'\\','/')
+ finally
+ let &l:suffixesadd = oldsuffixesadd
+ endtry
+endfunction
+
+call s:add_methods('app',['path','has_file','find_file'])
+
+" Split a path into a list. From pathogen.vim
+function! s:pathsplit(path) abort
+ if type(a:path) == type([]) | return copy(a:path) | endif
+ let split = split(a:path,'\\\@<!\%(\\\\\)*\zs,')
+ return map(split,'substitute(v:val,''\\\([\\, ]\)'',''\1'',"g")')
+endfunction
+
+" Convert a list to a path. From pathogen.vim
+function! s:pathjoin(...) abort
+ let i = 0
+ let path = ""
+ while i < a:0
+ if type(a:000[i]) == type([])
+ let list = a:000[i]
+ let j = 0
+ while j < len(list)
+ let escaped = substitute(list[j],'[\\, ]','\\&','g')
+ if exists("+shellslash") && !&shellslash
+ let escaped = substitute(escaped,'^\(\w:\\\)\\','\1','')
+ endif
+ let path .= ',' . escaped
+ let j += 1
+ endwhile
+ else
+ let path .= "," . a:000[i]
+ endif
+ let i += 1
+ endwhile
+ return substitute(path,'^,','','')
+endfunction
+
+function! s:readable_end_of(lnum) dict abort
+ if a:lnum == 0
+ return 0
+ endif
+ if self.name() =~# '\.yml$'
+ return -1
+ endif
+ let cline = self.getline(a:lnum)
+ let spc = matchstr(cline,'^\s*')
+ let endpat = '\<end\>'
+ if matchstr(self.getline(a:lnum+1),'^'.spc) && !matchstr(self.getline(a:lnum+1),'^'.spc.endpat) && matchstr(cline,endpat)
+ return a:lnum
+ endif
+ let endl = a:lnum
+ while endl <= self.line_count()
+ let endl += 1
+ if self.getline(endl) =~ '^'.spc.endpat
+ return endl
+ elseif self.getline(endl) =~ '^=begin\>'
+ while self.getline(endl) !~ '^=end\>' && endl <= self.line_count()
+ let endl += 1
+ endwhile
+ let endl += 1
+ elseif self.getline(endl) !~ '^'.spc && self.getline(endl) !~ '^\s*\%(#.*\)\=$'
+ return 0
+ endif
+ endwhile
+ return 0
+endfunction
+
+function! s:endof(lnum)
+ return rails#buffer().end_of(a:lnum)
+endfunction
+
+function! s:readable_last_opening_line(start,pattern,limit) dict abort
+ let line = a:start
+ while line > a:limit && self.getline(line) !~ a:pattern
+ let line -= 1
+ endwhile
+ let lend = self.end_of(line)
+ if line > a:limit && (lend < 0 || lend >= a:start)
+ return line
+ else
+ return -1
+ endif
+endfunction
+
+function! s:lastopeningline(pattern,limit,start)
+ return rails#buffer().last_opening_line(a:start,a:pattern,a:limit)
+endfunction
+
+function! s:readable_define_pattern() dict abort
+ if self.name() =~ '\.yml$'
+ return '^\%(\h\k*:\)\@='
+ endif
+ let define = '^\s*def\s\+\(self\.\)\='
+ if self.name() =~# '\.rake$'
+ let define .= "\\\|^\\s*\\%(task\\\|file\\)\\s\\+[:'\"]"
+ endif
+ if self.name() =~# '/schema\.rb$'
+ let define .= "\\\|^\\s*create_table\\s\\+[:'\"]"
+ endif
+ if self.type_name('test')
+ let define .= '\|^\s*test\s*[''"]'
+ endif
+ return define
+endfunction
+
+function! s:readable_last_method_line(start) dict abort
+ return self.last_opening_line(a:start,self.define_pattern(),0)
+endfunction
+
+function! s:lastmethodline(start)
+ return rails#buffer().last_method_line(a:start)
+endfunction
+
+function! s:readable_last_method(start) dict abort
+ let lnum = self.last_method_line(a:start)
+ let line = self.getline(lnum)
+ if line =~# '^\s*test\s*\([''"]\).*\1'
+ let string = matchstr(line,'^\s*\w\+\s*\([''"]\)\zs.*\ze\1')
+ return 'test_'.s:gsub(string,' +','_')
+ elseif lnum
+ return s:sub(matchstr(line,'\%('.self.define_pattern().'\)\zs\h\%(\k\|[:.]\)*[?!=]\='),':$','')
+ else
+ return ""
+ endif
+endfunction
+
+function! s:lastmethod(...)
+ return rails#buffer().last_method(a:0 ? a:1 : line("."))
+endfunction
+
+function! s:readable_last_format(start) dict abort
+ if self.type_name('view')
+ let format = fnamemodify(self.path(),':r:e')
+ if format == ''
+ return get({'rhtml': 'html', 'rxml': 'xml', 'rjs': 'js', 'haml': 'html'},fnamemodify(self.path(),':e'),'')
+ else
+ return format
+ endif
+ endif
+ let rline = self.last_opening_line(a:start,'\C^\s*\%(mail\>.*\|respond_to\)\s*\%(\<do\|{\)\s*|\zs\h\k*\ze|',self.last_method_line(a:start))
+ if rline
+ let variable = matchstr(self.getline(rline),'\C^\s*\%(mail\>.*\|respond_to\)\s*\%(\<do\|{\)\s*|\zs\h\k*\ze|')
+ let line = a:start
+ while line > rline
+ let match = matchstr(self.getline(line),'\C^\s*'.variable.'\s*\.\s*\zs\h\k*')
+ if match != ''
+ return match
+ endif
+ let line -= 1
+ endwhile
+ endif
+ return ""
+endfunction
+
+function! s:lastformat(start)
+ return rails#buffer().last_format(a:start)
+endfunction
+
+function! s:format(...)
+ let format = rails#buffer().last_format(a:0 > 1 ? a:2 : line("."))
+ return format ==# '' && a:0 ? a:1 : format
+endfunction
+
+call s:add_methods('readable',['end_of','last_opening_line','last_method_line','last_method','last_format','define_pattern'])
+
+let s:view_types = split('rhtml,erb,rxml,builder,rjs,mab,liquid,haml,dryml,mn,slim',',')
+
+function! s:viewspattern()
+ return '\%('.join(s:view_types,'\|').'\)'
+endfunction
+
+function! s:controller(...)
+ return rails#buffer().controller_name(a:0 ? a:1 : 0)
+endfunction
+
+function! s:readable_controller_name(...) dict abort
+ let f = self.name()
+ if has_key(self,'getvar') && self.getvar('rails_controller') != ''
+ return self.getvar('rails_controller')
+ elseif f =~ '\<app/views/layouts/'
+ return s:sub(f,'.*<app/views/layouts/(.{-})\..*','\1')
+ elseif f =~ '\<app/views/'
+ return s:sub(f,'.*<app/views/(.{-})/\k+\.\k+%(\.\k+)=$','\1')
+ elseif f =~ '\<app/helpers/.*_helper\.rb$'
+ return s:sub(f,'.*<app/helpers/(.{-})_helper\.rb$','\1')
+ elseif f =~ '\<app/controllers/.*\.rb$'
+ return s:sub(f,'.*<app/controllers/(.{-})%(_controller)=\.rb$','\1')
+ elseif f =~ '\<app/mailers/.*\.rb$'
+ return s:sub(f,'.*<app/mailers/(.{-})\.rb$','\1')
+ elseif f =~ '\<app/apis/.*_api\.rb$'
+ return s:sub(f,'.*<app/apis/(.{-})_api\.rb$','\1')
+ elseif f =~ '\<test/functional/.*_test\.rb$'
+ return s:sub(f,'.*<test/functional/(.{-})%(_controller)=_test\.rb$','\1')
+ elseif f =~ '\<test/unit/helpers/.*_helper_test\.rb$'
+ return s:sub(f,'.*<test/unit/helpers/(.{-})_helper_test\.rb$','\1')
+ elseif f =~ '\<spec/controllers/.*_spec\.rb$'
+ return s:sub(f,'.*<spec/controllers/(.{-})%(_controller)=_spec\.rb$','\1')
+ elseif f =~ '\<spec/helpers/.*_helper_spec\.rb$'
+ return s:sub(f,'.*<spec/helpers/(.{-})_helper_spec\.rb$','\1')
+ elseif f =~ '\<spec/views/.*/\w\+_view_spec\.rb$'
+ return s:sub(f,'.*<spec/views/(.{-})/\w+_view_spec\.rb$','\1')
+ elseif f =~ '\<components/.*_controller\.rb$'
+ return s:sub(f,'.*<components/(.{-})_controller\.rb$','\1')
+ elseif f =~ '\<components/.*\.'.s:viewspattern().'$'
+ return s:sub(f,'.*<components/(.{-})/\k+\.\k+$','\1')
+ elseif f =~ '\<app/models/.*\.rb$' && self.type_name('mailer')
+ return s:sub(f,'.*<app/models/(.{-})\.rb$','\1')
+ elseif f =~ '\<\%(public\|app/assets\)/stylesheets/.*\.css\%(\.\w\+\)\=$'
+ return s:sub(f,'.*<%(public|app/assets)/stylesheets/(.{-})\.css%(\.\w+)=$','\1')
+ elseif f =~ '\<\%(public\|app/assets\)/javascripts/.*\.js\%(\.\w\+\)\=$'
+ return s:sub(f,'.*<%(public|app/assets)/javascripts/(.{-})\.js%(\.\w+)=$','\1')
+ elseif a:0 && a:1
+ return rails#pluralize(self.model_name())
+ endif
+ return ""
+endfunction
+
+function! s:model(...)
+ return rails#buffer().model_name(a:0 ? a:1 : 0)
+endfunction
+
+function! s:readable_model_name(...) dict abort
+ let f = self.name()
+ if has_key(self,'getvar') && self.getvar('rails_model') != ''
+ return self.getvar('rails_model')
+ elseif f =~ '\<app/models/.*_observer.rb$'
+ return s:sub(f,'.*<app/models/(.*)_observer\.rb$','\1')
+ elseif f =~ '\<app/models/.*\.rb$'
+ return s:sub(f,'.*<app/models/(.*)\.rb$','\1')
+ elseif f =~ '\<test/unit/.*_observer_test\.rb$'
+ return s:sub(f,'.*<test/unit/(.*)_observer_test\.rb$','\1')
+ elseif f =~ '\<test/unit/.*_test\.rb$'
+ return s:sub(f,'.*<test/unit/(.*)_test\.rb$','\1')
+ elseif f =~ '\<spec/models/.*_spec\.rb$'
+ return s:sub(f,'.*<spec/models/(.*)_spec\.rb$','\1')
+ elseif f =~ '\<\%(test\|spec\)/fixtures/.*\.\w*\~\=$'
+ return rails#singularize(s:sub(f,'.*<%(test|spec)/fixtures/(.*)\.\w*\~=$','\1'))
+ elseif f =~ '\<\%(test\|spec\)/blueprints/.*\.rb$'
+ return s:sub(f,'.*<%(test|spec)/blueprints/(.{-})%(_blueprint)=\.rb$','\1')
+ elseif f =~ '\<\%(test\|spec\)/exemplars/.*_exemplar\.rb$'
+ return s:sub(f,'.*<%(test|spec)/exemplars/(.*)_exemplar\.rb$','\1')
+ elseif f =~ '\<\%(test/\|spec/\)\=factories/.*\.rb$'
+ return s:sub(f,'.*<%(test/|spec/)=factories/(.{-})%(_factory)=\.rb$','\1')
+ elseif f =~ '\<\%(test/\|spec/\)\=fabricators/.*\.rb$'
+ return s:sub(f,'.*<%(test/|spec/)=fabricators/(.{-})%(_fabricator)=\.rb$','\1')
+ elseif a:0 && a:1
+ return rails#singularize(self.controller_name())
+ endif
+ return ""
+endfunction
+
+call s:add_methods('readable',['controller_name','model_name'])
+
+function! s:readfile(path,...)
+ let nr = bufnr('^'.a:path.'$')
+ if nr < 0 && exists('+shellslash') && ! &shellslash
+ let nr = bufnr('^'.s:gsub(a:path,'/','\\').'$')
+ endif
+ if bufloaded(nr)
+ return getbufline(nr,1,a:0 ? a:1 : '$')
+ elseif !filereadable(a:path)
+ return []
+ elseif a:0
+ return readfile(a:path,'',a:1)
+ else
+ return readfile(a:path)
+ endif
+endfunction
+
+function! s:file_lines() dict abort
+ let ftime = getftime(self.path)
+ if ftime > get(self,last_lines_ftime,0)
+ let self.last_lines = readfile(self.path())
+ let self.last_lines_ftime = ftime
+ endif
+ return get(self,'last_lines',[])
+endfunction
+
+function! s:file_getline(lnum,...) dict abort
+ if a:0
+ return self.lines[lnum-1 : a:1-1]
+ else
+ return self.lines[lnum-1]
+ endif
+endfunction
+
+function! s:buffer_lines() dict abort
+ return self.getline(1,'$')
+endfunction
+
+function! s:buffer_getline(...) dict abort
+ if a:0 == 1
+ return get(call('getbufline',[self.number()]+a:000),0,'')
+ else
+ return call('getbufline',[self.number()]+a:000)
+ endif
+endfunction
+
+function! s:readable_line_count() dict abort
+ return len(self.lines())
+endfunction
+
+function! s:environment()
+ if exists('$RAILS_ENV')
+ return $RAILS_ENV
+ else
+ return "development"
+ endif
+endfunction
+
+function! s:Complete_environments(...)
+ return s:completion_filter(rails#app().environments(),a:0 ? a:1 : "")
+endfunction
+
+function! s:warn(str)
+ echohl WarningMsg
+ echomsg a:str
+ echohl None
+ " Sometimes required to flush output
+ echo ""
+ let v:warningmsg = a:str
+endfunction
+
+function! s:error(str)
+ echohl ErrorMsg
+ echomsg a:str
+ echohl None
+ let v:errmsg = a:str
+endfunction
+
+function! s:debug(str)
+ if exists("g:rails_debug") && g:rails_debug
+ echohl Debug
+ echomsg a:str
+ echohl None
+ endif
+endfunction
+
+function! s:buffer_getvar(varname) dict abort
+ return getbufvar(self.number(),a:varname)
+endfunction
+
+function! s:buffer_setvar(varname, val) dict abort
+ return setbufvar(self.number(),a:varname,a:val)
+endfunction
+
+call s:add_methods('buffer',['getvar','setvar'])
+
+" }}}1
+" "Public" Interface {{{1
+
+" RailsRoot() is the only official public function
+
+function! rails#underscore(str)
+ let str = s:gsub(a:str,'::','/')
+ let str = s:gsub(str,'(\u+)(\u\l)','\1_\2')
+ let str = s:gsub(str,'(\l|\d)(\u)','\1_\2')
+ let str = tolower(str)
+ return str
+endfunction
+
+function! rails#camelize(str)
+ let str = s:gsub(a:str,'/(.=)','::\u\1')
+ let str = s:gsub(str,'%([_-]|<)(.)','\u\1')
+ return str
+endfunction
+
+function! rails#singularize(word)
+ " Probably not worth it to be as comprehensive as Rails but we can
+ " still hit the common cases.
+ let word = a:word
+ if word =~? '\.js$' || word == ''
+ return word
+ endif
+ let word = s:sub(word,'eople$','ersons')
+ let word = s:sub(word,'%([Mm]ov|[aeio])@<!ies$','ys')
+ let word = s:sub(word,'xe[ns]$','xs')
+ let word = s:sub(word,'ves$','fs')
+ let word = s:sub(word,'ss%(es)=$','sss')
+ let word = s:sub(word,'s$','')
+ let word = s:sub(word,'%([nrt]ch|tatus|lias)\zse$','')
+ let word = s:sub(word,'%(nd|rt)\zsice$','ex')
+ return word
+endfunction
+
+function! rails#pluralize(word)
+ let word = a:word
+ if word == ''
+ return word
+ endif
+ let word = s:sub(word,'[aeio]@<!y$','ie')
+ let word = s:sub(word,'%(nd|rt)@<=ex$','ice')
+ let word = s:sub(word,'%([osxz]|[cs]h)$','&e')
+ let word = s:sub(word,'f@<!f$','ve')
+ let word .= 's'
+ let word = s:sub(word,'ersons$','eople')
+ return word
+endfunction
+
+function! rails#app(...)
+ let root = a:0 ? a:1 : RailsRoot()
+ " TODO: populate dynamically
+ " TODO: normalize path
+ return get(s:apps,root,0)
+endfunction
+
+function! rails#buffer(...)
+ return extend(extend({'#': bufnr(a:0 ? a:1 : '%')},s:buffer_prototype,'keep'),s:readable_prototype,'keep')
+ endif
+endfunction
+
+function! s:buffer_app() dict abort
+ if self.getvar('rails_root') != ''
+ return rails#app(self.getvar('rails_root'))
+ else
+ return 0
+ endif
+endfunction
+
+function! s:readable_app() dict abort
+ return self._app
+endfunction
+
+function! RailsRevision()
+ return 1000*matchstr(g:autoloaded_rails,'^\d\+')+matchstr(g:autoloaded_rails,'[1-9]\d*$')
+endfunction
+
+function! RailsRoot()
+ if exists("b:rails_root")
+ return b:rails_root
+ else
+ return ""
+ endif
+endfunction
+
+function! s:app_file(name)
+ return extend(extend({'_app': self, '_name': a:name}, s:file_prototype,'keep'),s:readable_prototype,'keep')
+endfunction
+
+function! s:file_path() dict abort
+ return self.app().path(self._name)
+endfunction
+
+function! s:file_name() dict abort
+ return self._name
+endfunction
+
+function! s:buffer_number() dict abort
+ return self['#']
+endfunction
+
+function! s:buffer_path() dict abort
+ return s:gsub(fnamemodify(bufname(self.number()),':p'),'\\ @!','/')
+endfunction
+
+function! s:buffer_name() dict abort
+ let app = self.app()
+ let f = s:gsub(resolve(fnamemodify(bufname(self.number()),':p')),'\\ @!','/')
+ let f = s:sub(f,'/$','')
+ let sep = matchstr(f,'^[^\\/]\{3,\}\zs[\\/]')
+ if sep != ""
+ let f = getcwd().sep.f
+ endif
+ if s:startswith(tolower(f),s:gsub(tolower(app.path()),'\\ @!','/')) || f == ""
+ return strpart(f,strlen(app.path())+1)
+ else
+ if !exists("s:path_warn")
+ let s:path_warn = 1
+ call s:warn("File ".f." does not appear to be under the Rails root ".self.app().path().". Please report to the rails.vim author!")
+ endif
+ return f
+ endif
+endfunction
+
+function! RailsFilePath()
+ if !exists("b:rails_root")
+ return ""
+ else
+ return rails#buffer().name()
+ endif
+endfunction
+
+function! RailsFile()
+ return RailsFilePath()
+endfunction
+
+function! RailsFileType()
+ if !exists("b:rails_root")
+ return ""
+ else
+ return rails#buffer().type_name()
+ end
+endfunction
+
+function! s:readable_calculate_file_type() dict abort
+ let f = self.name()
+ let e = fnamemodify(f,':e')
+ let r = "-"
+ let full_path = self.path()
+ let nr = bufnr('^'.full_path.'$')
+ if nr < 0 && exists('+shellslash') && ! &shellslash
+ let nr = bufnr('^'.s:gsub(full_path,'/','\\').'$')
+ endif
+ if f == ""
+ let r = f
+ elseif nr > 0 && getbufvar(nr,'rails_file_type') != ''
+ return getbufvar(nr,'rails_file_type')
+ elseif f =~ '_controller\.rb$' || f =~ '\<app/controllers/.*\.rb$'
+ if join(s:readfile(full_path,50),"\n") =~ '\<wsdl_service_name\>'
+ let r = "controller-api"
+ else
+ let r = "controller"
+ endif
+ elseif f =~ '_api\.rb'
+ let r = "api"
+ elseif f =~ '\<test/test_helper\.rb$'
+ let r = "test"
+ elseif f =~ '\<spec/spec_helper\.rb$'
+ let r = "spec"
+ elseif f =~ '_helper\.rb$'
+ let r = "helper"
+ elseif f =~ '\<app/metal/.*\.rb$'
+ let r = "metal"
+ elseif f =~ '\<app/mailers/.*\.rb'
+ let r = "mailer"
+ elseif f =~ '\<app/models/'
+ let top = join(s:readfile(full_path,50),"\n")
+ let class = matchstr(top,'\<Acti\w\w\u\w\+\%(::\h\w*\)\+\>')
+ if class == "ActiveResource::Base"
+ let class = "ares"
+ let r = "model-ares"
+ elseif class == 'ActionMailer::Base'
+ let r = "mailer"
+ elseif class != ''
+ let class = tolower(s:gsub(class,'[^A-Z]',''))
+ let r = "model-".class
+ elseif f =~ '_mailer\.rb$'
+ let r = "mailer"
+ elseif top =~ '\<\%(validates_\w\+_of\|set_\%(table_name\|primary_key\)\|has_one\|has_many\|belongs_to\)\>'
+ let r = "model-arb"
+ else
+ let r = "model"
+ endif
+ elseif f =~ '\<app/views/layouts\>.*\.'
+ let r = "view-layout-" . e
+ elseif f =~ '\<\%(app/views\|components\)/.*/_\k\+\.\k\+\%(\.\k\+\)\=$'
+ let r = "view-partial-" . e
+ elseif f =~ '\<app/views\>.*\.' || f =~ '\<components/.*/.*\.'.s:viewspattern().'$'
+ let r = "view-" . e
+ elseif f =~ '\<test/unit/.*_test\.rb$'
+ let r = "test-unit"
+ elseif f =~ '\<test/functional/.*_test\.rb$'
+ let r = "test-functional"
+ elseif f =~ '\<test/integration/.*_test\.rb$'
+ let r = "test-integration"
+ elseif f =~ '\<spec/lib/.*_spec\.rb$'
+ let r = 'spec-lib'
+ elseif f =~ '\<lib/.*\.rb$'
+ let r = 'lib'
+ elseif f =~ '\<spec/\w*s/.*_spec\.rb$'
+ let r = s:sub(f,'.*<spec/(\w*)s/.*','spec-\1')
+ elseif f =~ '\<features/.*\.feature$'
+ let r = 'cucumber-feature'
+ elseif f =~ '\<features/step_definitions/.*_steps\.rb$'
+ let r = 'cucumber-steps'
+ elseif f =~ '\<features/.*\.rb$'
+ let r = 'cucumber'
+ elseif f =~ '\<\%(test\|spec\)/fixtures\>'
+ if e == "yml"
+ let r = "fixtures-yaml"
+ else
+ let r = "fixtures" . (e == "" ? "" : "-" . e)
+ endif
+ elseif f =~ '\<test/.*_test\.rb'
+ let r = "test"
+ elseif f =~ '\<spec/.*_spec\.rb'
+ let r = "spec"
+ elseif f =~ '\<spec/support/.*\.rb'
+ let r = "spec"
+ elseif f =~ '\<db/migrate\>'
+ let r = "db-migration"
+ elseif f=~ '\<db/schema\.rb$'
+ let r = "db-schema"
+ elseif f =~ '\<vendor/plugins/.*/recipes/.*\.rb$' || f =~ '\.rake$' || f =~ '\<\%(Rake\|Cap\)file$' || f =~ '\<config/deploy\.rb$'
+ let r = "task"
+ elseif f =~ '\<log/.*\.log$'
+ let r = "log"
+ elseif e == "css" || e =~ "s[ac]ss" || e == "less"
+ let r = "stylesheet-".e
+ elseif e == "js"
+ let r = "javascript"
+ elseif e == "coffee"
+ let r = "javascript-coffee"
+ elseif e == "html"
+ let r = e
+ elseif f =~ '\<config/routes\>.*\.rb$'
+ let r = "config-routes"
+ elseif f =~ '\<config/'
+ let r = "config"
+ endif
+ return r
+endfunction
+
+function! s:buffer_type_name(...) dict abort
+ let type = getbufvar(self.number(),'rails_cached_file_type')
+ if type == ''
+ let type = self.calculate_file_type()
+ endif
+ return call('s:match_type',[type == '-' ? '' : type] + a:000)
+endfunction
+
+function! s:readable_type_name() dict abort
+ let type = self.calculate_file_type()
+ return call('s:match_type',[type == '-' ? '' : type] + a:000)
+endfunction
+
+function! s:match_type(type,...)
+ if a:0
+ return !empty(filter(copy(a:000),'a:type =~# "^".v:val."\\%(-\\|$\\)"'))
+ else
+ return a:type
+ endif
+endfunction
+
+function! s:app_environments() dict
+ if self.cache.needs('environments')
+ call self.cache.set('environments',self.relglob('config/environments/','**/*','.rb'))
+ endif
+ return copy(self.cache.get('environments'))
+endfunction
+
+function! s:app_default_locale() dict abort
+ if self.cache.needs('default_locale')
+ let candidates = map(filter(s:readfile(self.path('config/environment.rb')),'v:val =~ "^ *config.i18n.default_locale = :[\"'']\\=[A-Za-z-]\\+[\"'']\\= *$"'),'matchstr(v:val,"[A-Za-z-]\\+[\"'']\\= *$")')
+ call self.cache.set('default_locale',get(candidates,0,'en'))
+ endif
+ return self.cache.get('default_locale')
+endfunction
+
+function! s:app_has(feature) dict
+ let map = {
+ \'test': 'test/',
+ \'spec': 'spec/',
+ \'cucumber': 'features/',
+ \'sass': 'public/stylesheets/sass/',
+ \'lesscss': 'app/stylesheets/',
+ \'coffee': 'app/scripts/'}
+ if self.cache.needs('features')
+ call self.cache.set('features',{})
+ endif
+ let features = self.cache.get('features')
+ if !has_key(features,a:feature)
+ let path = get(map,a:feature,a:feature.'/')
+ let features[a:feature] = isdirectory(rails#app().path(path))
+ endif
+ return features[a:feature]
+endfunction
+
+" Returns the subset of ['test', 'spec', 'cucumber'] present on the app.
+function! s:app_test_suites() dict
+ return filter(['test','spec','cucumber'],'self.has(v:val)')
+endfunction
+
+call s:add_methods('app',['default_locale','environments','file','has','test_suites'])
+call s:add_methods('file',['path','name','lines','getline'])
+call s:add_methods('buffer',['app','number','path','name','lines','getline','type_name'])
+call s:add_methods('readable',['app','calculate_file_type','type_name','line_count'])
+
+" }}}1
+" Ruby Execution {{{1
+
+function! s:app_ruby_shell_command(cmd) dict abort
+ if self.path() =~ '://'
+ return "ruby ".a:cmd
+ else
+ return "ruby -C ".s:rquote(self.path())." ".a:cmd
+ endif
+endfunction
+
+function! s:app_script_shell_command(cmd) dict abort
+ if self.has_file('script/rails') && a:cmd !~# '^rails\>'
+ let cmd = 'script/rails '.a:cmd
+ else
+ let cmd = 'script/'.a:cmd
+ endif
+ return self.ruby_shell_command(cmd)
+endfunction
+
+function! s:app_background_script_command(cmd) dict abort
+ let cmd = s:esccmd(self.script_shell_command(a:cmd))
+ if has_key(self,'options') && has_key(self.options,'gnu_screen')
+ let screen = self.options.gnu_screen
+ else
+ let screen = g:rails_gnu_screen
+ endif
+ if has("gui_win32")
+ if &shellcmdflag == "-c" && ($PATH . &shell) =~? 'cygwin'
+ silent exe "!cygstart -d ".s:rquote(self.path())." ruby ".a:cmd
+ else
+ exe "!start ".cmd
+ endif
+ elseif exists("$STY") && !has("gui_running") && screen && executable("screen")
+ silent exe "!screen -ln -fn -t ".s:sub(s:sub(a:cmd,'\s.*',''),'^%(script|-rcommand)/','rails-').' '.cmd
+ elseif exists("$TMUX") && !has("gui_running") && screen && executable("tmux")
+ silent exe '!tmux new-window -d -n "'.s:sub(s:sub(a:cmd,'\s.*',''),'^%(script|-rcommand)/','rails-').'" "'.cmd.'"'
+ else
+ exe "!".cmd
+ endif
+ return v:shell_error
+endfunction
+
+function! s:app_execute_script_command(cmd) dict abort
+ exe '!'.s:esccmd(self.script_shell_command(a:cmd))
+ return v:shell_error
+endfunction
+
+function! s:app_lightweight_ruby_eval(ruby,...) dict abort
+ let def = a:0 ? a:1 : ""
+ if !executable("ruby")
+ return def
+ endif
+ let args = '-e '.s:rquote('begin; require %{rubygems}; rescue LoadError; end; begin; require %{active_support}; rescue LoadError; end; '.a:ruby)
+ let cmd = self.ruby_shell_command(args)
+ " If the shell is messed up, this command could cause an error message
+ silent! let results = system(cmd)
+ return v:shell_error == 0 ? results : def
+endfunction
+
+function! s:app_eval(ruby,...) dict abort
+ let def = a:0 ? a:1 : ""
+ if !executable("ruby")
+ return def
+ endif
+ let args = "-r./config/boot -r ".s:rquote(self.path("config/environment"))." -e ".s:rquote(a:ruby)
+ let cmd = self.ruby_shell_command(args)
+ " If the shell is messed up, this command could cause an error message
+ silent! let results = system(cmd)
+ return v:shell_error == 0 ? results : def
+endfunction
+
+call s:add_methods('app', ['ruby_shell_command','script_shell_command','execute_script_command','background_script_command','lightweight_ruby_eval','eval'])
+
+" }}}1
+" Commands {{{1
+
+function! s:prephelp()
+ let fn = fnamemodify(s:file,':h:h').'/doc/'
+ if filereadable(fn.'rails.txt')
+ if !filereadable(fn.'tags') || getftime(fn.'tags') <= getftime(fn.'rails.txt')
+ silent! helptags `=fn`
+ endif
+ endif
+endfunction
+
+function! RailsHelpCommand(...)
+ call s:prephelp()
+ let topic = a:0 ? a:1 : ""
+ if topic == "" || topic == "-"
+ return "help rails"
+ elseif topic =~ '^g:'
+ return "help ".topic
+ elseif topic =~ '^-'
+ return "help rails".topic
+ else
+ return "help rails-".topic
+ endif
+endfunction
+
+function! s:BufCommands()
+ call s:BufFinderCommands()
+ call s:BufNavCommands()
+ call s:BufScriptWrappers()
+ command! -buffer -bar -nargs=? -bang -count -complete=customlist,s:Complete_rake Rake :call s:Rake(<bang>0,!<count> && <line1> ? -1 : <count>,<q-args>)
+ command! -buffer -bar -nargs=? -bang -range -complete=customlist,s:Complete_preview Rpreview :call s:Preview(<bang>0,<line1>,<q-args>)
+ command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_environments Rlog :call s:Log(<bang>0,<q-args>)
+ command! -buffer -bar -nargs=* -bang -complete=customlist,s:Complete_set Rset :call s:Set(<bang>0,<f-args>)
+ command! -buffer -bar -nargs=0 Rtags :call rails#app().tags_command()
+ " Embedding all this logic directly into the command makes the error
+ " messages more concise.
+ command! -buffer -bar -nargs=? -bang Rdoc :
+ \ if <bang>0 || <q-args> =~ "^\\([:'-]\\|g:\\)" |
+ \ exe RailsHelpCommand(<q-args>) |
+ \ else | call s:Doc(<bang>0,<q-args>) | endif
+ command! -buffer -bar -nargs=0 -bang Rrefresh :if <bang>0|unlet! g:autoloaded_rails|source `=s:file`|endif|call s:Refresh(<bang>0)
+ if exists(":NERDTree")
+ command! -buffer -bar -nargs=? -complete=customlist,s:Complete_cd Rtree :NERDTree `=rails#app().path(<f-args>)`
+ endif
+ if exists("g:loaded_dbext")
+ command! -buffer -bar -nargs=? -complete=customlist,s:Complete_environments Rdbext :call s:BufDatabase(2,<q-args>)|let b:dbext_buffer_defaulted = 1
+ endif
+ let ext = expand("%:e")
+ if ext =~ s:viewspattern()
+ " TODO: complete controller names with trailing slashes here
+ command! -buffer -bar -bang -nargs=? -range -complete=customlist,s:controllerList Rextract :<line1>,<line2>call s:Extract(<bang>0,<f-args>)
+ endif
+ if RailsFilePath() =~ '\<db/migrate/.*\.rb$'
+ command! -buffer -bar Rinvert :call s:Invert(<bang>0)
+ endif
+endfunction
+
+function! s:Doc(bang, string)
+ if a:string != ""
+ if exists("g:rails_search_url")
+ let query = substitute(a:string,'[^A-Za-z0-9_.~-]','\="%".printf("%02X",char2nr(submatch(0)))','g')
+ let url = printf(g:rails_search_url, query)
+ else
+ return s:error("specify a g:rails_search_url with %s for a query placeholder")
+ endif
+ elseif isdirectory(rails#app().path("doc/api/classes"))
+ let url = rails#app().path("/doc/api/index.html")
+ elseif s:getpidfor("0.0.0.0","8808") > 0
+ let url = "http://localhost:8808"
+ else
+ let url = "http://api.rubyonrails.org"
+ endif
+ call s:initOpenURL()
+ if exists(":OpenURL")
+ exe "OpenURL ".s:escarg(url)
+ else
+ return s:error("No :OpenURL command found")
+ endif
+endfunction
+
+function! s:Log(bang,arg)
+ if a:arg == ""
+ let lf = "log/".s:environment().".log"
+ else
+ let lf = "log/".a:arg.".log"
+ endif
+ let size = getfsize(rails#app().path(lf))
+ if size >= 1048576
+ call s:warn("Log file is ".((size+512)/1024)."KB. Consider :Rake log:clear")
+ endif
+ if a:bang
+ exe "cgetfile ".lf
+ clast
+ else
+ if exists(":Tail")
+ Tail `=rails#app().path(lf)`
+ else
+ pedit `=rails#app().path(lf)`
+ endif
+ endif
+endfunction
+
+function! rails#new_app_command(bang,...)
+ if a:0 == 0
+ let msg = "rails.vim ".g:autoloaded_rails
+ if a:bang && exists('b:rails_root') && rails#buffer().type_name() == ''
+ echo msg." (Rails)"
+ elseif a:bang && exists('b:rails_root')
+ echo msg." (Rails-".rails#buffer().type_name().")"
+ elseif a:bang
+ echo msg
+ else
+ !rails
+ endif
+ return
+ endif
+ let args = map(copy(a:000),'expand(v:val)')
+ if a:bang
+ let args = ['--force'] + args
+ endif
+ exe '!rails '.join(map(copy(args),'s:rquote(v:val)'),' ')
+ for dir in args
+ if dir !~# '^-' && filereadable(dir.'/'.g:rails_default_file)
+ edit `=dir.'/'.g:rails_default_file`
+ return
+ endif
+ endfor
+endfunction
+
+function! s:app_tags_command() dict
+ if exists("g:Tlist_Ctags_Cmd")
+ let cmd = g:Tlist_Ctags_Cmd
+ elseif executable("exuberant-ctags")
+ let cmd = "exuberant-ctags"
+ elseif executable("ctags-exuberant")
+ let cmd = "ctags-exuberant"
+ elseif executable("ctags")
+ let cmd = "ctags"
+ elseif executable("ctags.exe")
+ let cmd = "ctags.exe"
+ else
+ return s:error("ctags not found")
+ endif
+ exe '!'.cmd.' -f '.s:escarg(self.path("tmp/tags")).' -R --langmap="ruby:+.rake.builder.rjs" '.g:rails_ctags_arguments.' '.s:escarg(self.path())
+endfunction
+
+call s:add_methods('app',['tags_command'])
+
+function! s:Refresh(bang)
+ if exists("g:rubycomplete_rails") && g:rubycomplete_rails && has("ruby") && exists('g:rubycomplete_completions')
+ silent! ruby ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
+ silent! ruby if defined?(ActiveSupport::Dependencies); ActiveSupport::Dependencies.clear; elsif defined?(Dependencies); Dependencies.clear; end
+ if a:bang
+ silent! ruby ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
+ endif
+ endif
+ call rails#app().cache.clear()
+ silent doautocmd User BufLeaveRails
+ if a:bang
+ for key in keys(s:apps)
+ if type(s:apps[key]) == type({})
+ call s:apps[key].cache.clear()
+ endif
+ call extend(s:apps[key],filter(copy(s:app_prototype),'type(v:val) == type(function("tr"))'),'force')
+ endfor
+ endif
+ let i = 1
+ let max = bufnr('$')
+ while i <= max
+ let rr = getbufvar(i,"rails_root")
+ if rr != ""
+ call setbufvar(i,"rails_refresh",1)
+ endif
+ let i += 1
+ endwhile
+ silent doautocmd User BufEnterRails
+endfunction
+
+function! s:RefreshBuffer()
+ if exists("b:rails_refresh") && b:rails_refresh
+ let oldroot = b:rails_root
+ unlet! b:rails_root
+ let b:rails_refresh = 0
+ call RailsBufInit(oldroot)
+ unlet! b:rails_refresh
+ endif
+endfunction
+
+" }}}1
+" Rake {{{1
+
+function! s:app_rake_tasks() dict
+ if self.cache.needs('rake_tasks')
+ call s:push_chdir()
+ try
+ let lines = split(system("rake -T"),"\n")
+ finally
+ call s:pop_command()
+ endtry
+ if v:shell_error != 0
+ return []
+ endif
+ call map(lines,'matchstr(v:val,"^rake\\s\\+\\zs\\S*")')
+ call filter(lines,'v:val != ""')
+ call self.cache.set('rake_tasks',lines)
+ endif
+ return self.cache.get('rake_tasks')
+endfunction
+
+call s:add_methods('app', ['rake_tasks'])
+
+let s:efm_backtrace='%D(in\ %f),'
+ \.'%\\s%#from\ %f:%l:%m,'
+ \.'%\\s%#from\ %f:%l:,'
+ \.'%\\s#{RAILS_ROOT}/%f:%l:\ %#%m,'
+ \.'%\\s%##\ %f:%l:%m,'
+ \.'%\\s%##\ %f:%l,'
+ \.'%\\s%#[%f:%l:\ %#%m,'
+ \.'%\\s%#%f:%l:\ %#%m,'
+ \.'%\\s%#%f:%l:,'
+ \.'%m\ [%f:%l]:'
+
+function! s:makewithruby(arg,bang,...)
+ let old_make = &makeprg
+ try
+ let &l:makeprg = rails#app().ruby_shell_command(a:arg)
+ exe 'make'.(a:bang ? '!' : '')
+ if !a:bang
+ cwindow
+ endif
+ finally
+ let &l:makeprg = old_make
+ endtry
+endfunction
+
+function! s:Rake(bang,lnum,arg)
+ let self = rails#app()
+ let lnum = a:lnum < 0 ? 0 : a:lnum
+ let old_makeprg = &l:makeprg
+ let old_errorformat = &l:errorformat
+ try
+ if exists('b:bundler_root') && b:bundler_root ==# rails#app().path()
+ let &l:makeprg = 'bundle exec rake'
+ else
+ let &l:makeprg = 'rake'
+ endif
+ let &l:errorformat = s:efm_backtrace
+ let arg = a:arg
+ if &filetype == "ruby" && arg == '' && g:rails_modelines
+ let mnum = s:lastmethodline(lnum)
+ let str = getline(mnum)."\n".getline(mnum+1)."\n".getline(mnum+2)."\n"
+ let pat = '\s\+\zs.\{-\}\ze\%(\n\|\s\s\|#{\@!\|$\)'
+ let mat = matchstr(str,'#\s*rake'.pat)
+ let mat = s:sub(mat,'\s+$','')
+ if mat != ""
+ let arg = mat
+ endif
+ endif
+ if arg == ''
+ let opt = s:getopt('task','bl')
+ if opt != ''
+ let arg = opt
+ else
+ let arg = rails#buffer().default_rake_task(lnum)
+ endif
+ endif
+ if !has_key(self,'options') | let self.options = {} | endif
+ if arg == '-'
+ let arg = get(self.options,'last_rake_task','')
+ endif
+ let self.options['last_rake_task'] = arg
+ let withrubyargs = '-r ./config/boot -r '.s:rquote(self.path('config/environment')).' -e "puts \%((in \#{Dir.getwd}))" '
+ if arg =~# '^notes\>'
+ let &l:errorformat = '%-P%f:,\ \ *\ [%*[\ ]%l]\ [%t%*[^]]] %m,\ \ *\ [%*[\ ]%l] %m,%-Q'
+ " %D to chdir is apparently incompatible with %P multiline messages
+ call s:push_chdir(1)
+ exe 'make! '.arg
+ call s:pop_command()
+ if !a:bang
+ cwindow
+ endif
+ elseif arg =~# '^\%(stats\|routes\|secret\|time:zones\|db:\%(charset\|collation\|fixtures:identify\>.*\|migrate:status\|version\)\)\%([: ]\|$\)'
+ let &l:errorformat = '%D(in\ %f),%+G%.%#'
+ exe 'make! '.arg
+ if !a:bang
+ copen
+ endif
+ elseif arg =~ '^preview\>'
+ exe (lnum == 0 ? '' : lnum).'R'.s:gsub(arg,':','/')
+ elseif arg =~ '^runner:'
+ let arg = s:sub(arg,'^runner:','')
+ let root = matchstr(arg,'%\%(:\w\)*')
+ let file = expand(root).matchstr(arg,'%\%(:\w\)*\zs.*')
+ if file =~ '#.*$'
+ let extra = " -- -n ".matchstr(file,'#\zs.*')
+ let file = s:sub(file,'#.*','')
+ else
+ let extra = ''
+ endif
+ if self.has_file(file) || self.has_file(file.'.rb')
+ call s:makewithruby(withrubyargs.'-r"'.file.'"'.extra,a:bang,file !~# '_\%(spec\|test\)\%(\.rb\)\=$')
+ else
+ call s:makewithruby(withrubyargs.'-e '.s:esccmd(s:rquote(arg)),a:bang)
+ endif
+ elseif arg == 'run' || arg == 'runner'
+ call s:makewithruby(withrubyargs.'-r"'.RailsFilePath().'"',a:bang,RailsFilePath() !~# '_\%(spec\|test\)\%(\.rb\)\=$')
+ elseif arg =~ '^run:'
+ let arg = s:sub(arg,'^run:','')
+ let arg = s:sub(arg,'^\%:h',expand('%:h'))
+ let arg = s:sub(arg,'^%(\%|$|#@=)',expand('%'))
+ let arg = s:sub(arg,'#(\w+[?!=]=)$',' -- -n\1')
+ call s:makewithruby(withrubyargs.'-r'.arg,a:bang,arg !~# '_\%(spec\|test\)\.rb$')
+ else
+ exe 'make! '.arg
+ if !a:bang
+ cwindow
+ endif
+ endif
+ finally
+ let &l:errorformat = old_errorformat
+ let &l:makeprg = old_makeprg
+ endtry
+endfunction
+
+function! s:readable_default_rake_task(lnum) dict abort
+ let app = self.app()
+ let lnum = a:lnum < 0 ? 0 : a:lnum
+ if self.getvar('&buftype') == 'quickfix'
+ return '-'
+ elseif self.getline(lnum) =~# '# rake '
+ return matchstr(self.getline(lnum),'\C# rake \zs.*')
+ elseif self.getline(self.last_method_line(lnum)-1) =~# '# rake '
+ return matchstr(self.getline(self.last_method_line(lnum)-1),'\C# rake \zs.*')
+ elseif self.getline(self.last_method_line(lnum)) =~# '# rake '
+ return matchstr(self.getline(self.last_method_line(lnum)),'\C# rake \zs.*')
+ elseif self.getline(1) =~# '# rake ' && !lnum
+ return matchstr(self.getline(1),'\C# rake \zs.*')
+ elseif self.type_name('config-routes')
+ return 'routes'
+ elseif self.type_name('fixtures-yaml') && lnum
+ return "db:fixtures:identify LABEL=".self.last_method(lnum)
+ elseif self.type_name('fixtures') && lnum == 0
+ return "db:fixtures:load FIXTURES=".s:sub(fnamemodify(self.name(),':r'),'^.{-}/fixtures/','')
+ elseif self.type_name('task')
+ let mnum = self.last_method_line(lnum)
+ let line = getline(mnum)
+ " We can't grab the namespace so only run tasks at the start of the line
+ if line =~# '^\%(task\|file\)\>'
+ return self.last_method(a:lnum)
+ else
+ return matchstr(self.getline(1),'\C# rake \zs.*')
+ endif
+ elseif self.type_name('spec')
+ if self.name() =~# '\<spec/spec_helper\.rb$'
+ return 'spec'
+ elseif lnum > 0
+ return 'spec SPEC="'.self.path().'":'.lnum
+ else
+ return 'spec SPEC="'.self.path().'"'
+ endif
+ elseif self.type_name('test')
+ let meth = self.last_method(lnum)
+ if meth =~ '^test_'
+ let call = " -n".meth.""
+ else
+ let call = ""
+ endif
+ if self.type_name('test-unit','test-functional','test-integration')
+ return s:sub(s:gsub(self.type_name(),'-',':'),'unit$|functional$','&s').' TEST="'.self.path().'"'.s:sub(call,'^ ',' TESTOPTS=')
+ elseif self.name() =~# '\<test/test_helper\.rb$'
+ return 'test'
+ else
+ return 'test:recent TEST="'.self.path().'"'.s:sub(call,'^ ',' TESTOPTS=')
+ endif
+ elseif self.type_name('db-migration')
+ let ver = matchstr(self.name(),'\<db/migrate/0*\zs\d*\ze_')
+ if ver != ""
+ let method = self.last_method(lnum)
+ if method == "down" || lnum == 1
+ return "db:migrate:down VERSION=".ver
+ elseif method == "up" || lnum == line('$')
+ return "db:migrate:up VERSION=".ver
+ elseif lnum > 0
+ return "db:migrate:down db:migrate:up VERSION=".ver
+ else
+ return "db:migrate VERSION=".ver
+ endif
+ else
+ return 'db:migrate'
+ endif
+ elseif self.name() =~# '\<db/seeds\.rb$'
+ return 'db:seed'
+ elseif self.type_name('controller') && lnum
+ let lm = self.last_method(lnum)
+ if lm != ''
+ " rake routes doesn't support ACTION... yet...
+ return 'routes CONTROLLER='.self.controller_name().' ACTION='.lm
+ else
+ return 'routes CONTROLLER='.self.controller_name()
+ endif
+ elseif app.has('spec') && self.name() =~# '^app/.*\.\w\+$' && app.has_file(s:sub(self.name(),'^app/(.*)\.\w\+$','spec/\1_spec.rb'))
+ return 'spec SPEC="'.fnamemodify(s:sub(self.name(),'<app/','spec/'),':p:r').'_spec.rb"'
+ elseif app.has('spec') && self.name() =~# '^app/.*\.\w\+$' && app.has_file(s:sub(self.name(),'^app/(.*)$','spec/\1_spec.rb'))
+ return 'spec SPEC="'.fnamemodify(s:sub(self.name(),'<app/','spec/'),':p').'_spec.rb"'
+ elseif self.type_name('model')
+ return 'test:units TEST="'.fnamemodify(s:sub(self.name(),'<app/models/','test/unit/'),':p:r').'_test.rb"'
+ elseif self.type_name('api','mailer')
+ return 'test:units TEST="'.fnamemodify(s:sub(self.name(),'<app/%(apis|mailers|models)/','test/functional/'),':p:r').'_test.rb"'
+ elseif self.type_name('helper')
+ return 'test:units TEST="'.fnamemodify(s:sub(self.name(),'<app/','test/unit/'),':p:r').'_test.rb"'
+ elseif self.type_name('controller','helper','view')
+ if self.name() =~ '\<app/' && s:controller() !~# '^\%(application\)\=$'
+ return 'test:functionals TEST="'.s:escarg(app.path('test/functional/'.s:controller().'_controller_test.rb')).'"'
+ else
+ return 'test:functionals'
+ endif
+ elseif self.type_name('cucumber-feature')
+ if lnum > 0
+ return 'cucumber FEATURE="'.self.path().'":'.lnum
+ else
+ return 'cucumber FEATURE="'.self.path().'"'
+ endif
+ elseif self.type_name('cucumber')
+ return 'cucumber'
+ else
+ return ''
+ endif
+endfunction
+
+function! s:Complete_rake(A,L,P)
+ return s:completion_filter(rails#app().rake_tasks(),a:A)
+endfunction
+
+call s:add_methods('readable',['default_rake_task'])
+
+" }}}1
+" Preview {{{1
+
+function! s:initOpenURL()
+ if !exists(":OpenURL")
+ if has("gui_mac") || has("gui_macvim") || exists("$SECURITYSESSIONID")
+ command -bar -nargs=1 OpenURL :!open <args>
+ elseif has("gui_win32")
+ command -bar -nargs=1 OpenURL :!start cmd /cstart /b <args>
+ elseif executable("sensible-browser")
+ command -bar -nargs=1 OpenURL :!sensible-browser <args>
+ endif
+ endif
+endfunction
+
+function! s:scanlineforuris(line)
+ let url = matchstr(a:line,"\\v\\C%(%(GET|PUT|POST|DELETE)\\s+|\\w+://[^/]*)/[^ \n\r\t<>\"]*[^] .,;\n\r\t<>\":]")
+ if url =~ '\C^\u\+\s\+'
+ let method = matchstr(url,'^\u\+')
+ let url = matchstr(url,'\s\+\zs.*')
+ if method !=? "GET"
+ let url .= (url =~ '?' ? '&' : '?') . '_method='.tolower(method)
+ endif
+ endif
+ if url != ""
+ return [url]
+ else
+ return []
+ endif
+endfunction
+
+function! s:readable_preview_urls(lnum) dict abort
+ let urls = []
+ let start = self.last_method_line(a:lnum) - 1
+ while start > 0 && self.getline(start) =~ '^\s*\%(\%(-\=\|<%\)#.*\)\=$'
+ let urls = s:scanlineforuris(self.getline(start)) + urls
+ let start -= 1
+ endwhile
+ let start = 1
+ while start < self.line_count() && self.getline(start) =~ '^\s*\%(\%(-\=\|<%\)#.*\)\=$'
+ let urls += s:scanlineforuris(self.getline(start))
+ let start += 1
+ endwhile
+ if has_key(self,'getvar') && self.getvar('rails_preview') != ''
+ let url += [self.getvar('rails_preview')]
+ end
+ if self.name() =~ '^public/stylesheets/sass/'
+ let urls = urls + [s:sub(s:sub(self.name(),'^public/stylesheets/sass/','/stylesheets/'),'\.s[ac]ss$','.css')]
+ elseif self.name() =~ '^public/'
+ let urls = urls + [s:sub(self.name(),'^public','')]
+ elseif self.name() =~ '^app/assets/stylesheets/'
+ let urls = urls + ['/assets/application.css']
+ elseif self.name() =~ '^app/assets/javascripts/'
+ let urls = urls + ['/assets/application.js']
+ elseif self.name() =~ '^app/stylesheets/'
+ let urls = urls + [s:sub(s:sub(self.name(),'^app/stylesheets/','/stylesheets/'),'\.less$','.css')]
+ elseif self.name() =~ '^app/scripts/'
+ let urls = urls + [s:sub(s:sub(self.name(),'^app/scripts/','/javascripts/'),'\.coffee$','.js')]
+ elseif self.controller_name() != '' && self.controller_name() != 'application'
+ if self.type_name('controller') && self.last_method(a:lnum) != ''
+ let urls += ['/'.self.controller_name().'/'.self.last_method(a:lnum).'/']
+ elseif self.type_name('controller','view-layout','view-partial')
+ let urls += ['/'.self.controller_name().'/']
+ elseif self.type_name('view')
+ let urls += ['/'.s:controller().'/'.fnamemodify(self.name(),':t:r:r').'/']
+ endif
+ endif
+ return urls
+endfunction
+
+call s:add_methods('readable',['preview_urls'])
+
+function! s:Preview(bang,lnum,arg)
+ let root = s:getopt("root_url")
+ if root == ''
+ let root = s:getopt("url")
+ endif
+ let root = s:sub(root,'/$','')
+ if a:arg =~ '://'
+ let uri = a:arg
+ elseif a:arg != ''
+ let uri = root.'/'.s:sub(a:arg,'^/','')
+ else
+ let uri = get(rails#buffer().preview_urls(a:lnum),0,'')
+ let uri = root.'/'.s:sub(s:sub(uri,'^/',''),'/$','')
+ endif
+ call s:initOpenURL()
+ if exists(':OpenURL') && !a:bang
+ exe 'OpenURL '.uri
+ else
+ " Work around bug where URLs ending in / get handled as FTP
+ let url = uri.(uri =~ '/$' ? '?' : '')
+ silent exe 'pedit '.url
+ wincmd w
+ if &filetype == ''
+ if uri =~ '\.css$'
+ setlocal filetype=css
+ elseif uri =~ '\.js$'
+ setlocal filetype=javascript
+ elseif getline(1) =~ '^\s*<'
+ setlocal filetype=xhtml
+ endif
+ endif
+ call RailsBufInit(rails#app().path())
+ map <buffer> <silent> q :bwipe<CR>
+ wincmd p
+ if !a:bang
+ call s:warn("Define a :OpenURL command to use a browser")
+ endif
+ endif
+endfunction
+
+function! s:Complete_preview(A,L,P)
+ return rails#buffer().preview_urls(a:L =~ '^\d' ? matchstr(a:L,'^\d\+') : line('.'))
+endfunction
+
+" }}}1
+" Script Wrappers {{{1
+
+function! s:BufScriptWrappers()
+ command! -buffer -bar -nargs=* -complete=customlist,s:Complete_script Rscript :call rails#app().script_command(<bang>0,<f-args>)
+ command! -buffer -bar -nargs=* -complete=customlist,s:Complete_generate Rgenerate :call rails#app().generate_command(<bang>0,<f-args>)
+ command! -buffer -bar -nargs=* -complete=customlist,s:Complete_destroy Rdestroy :call rails#app().destroy_command(<bang>0,<f-args>)
+ command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_server Rserver :call rails#app().server_command(<bang>0,<q-args>)
+ command! -buffer -bang -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Rrunner :call rails#app().runner_command(<bang>0 ? -2 : (<count>==<line2>?<count>:-1),<f-args>)
+ command! -buffer -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Rp :call rails#app().runner_command(<count>==<line2>?<count>:-1,'p begin '.<f-args>.' end')
+ command! -buffer -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Rpp :call rails#app().runner_command(<count>==<line2>?<count>:-1,'require %{pp}; pp begin '.<f-args>.' end')
+ command! -buffer -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Ry :call rails#app().runner_command(<count>==<line2>?<count>:-1,'y begin '.<f-args>.' end')
+endfunction
+
+function! s:app_generators() dict
+ if self.cache.needs('generators')
+ let generators = self.relglob("vendor/plugins/","*/generators/*")
+ let generators += self.relglob("","lib/generators/*")
+ call filter(generators,'v:val =~ "/$"')
+ let generators += split(glob(expand("~/.rails/generators")."/*"),"\n")
+ call map(generators,'s:sub(v:val,"^.*[\\\\/]generators[\\\\/]\\ze.","")')
+ call map(generators,'s:sub(v:val,"[\\\\/]$","")')
+ call self.cache.set('generators',generators)
+ endif
+ return sort(split(g:rails_generators,"\n") + self.cache.get('generators'))
+endfunction
+
+function! s:app_script_command(bang,...) dict
+ let str = ""
+ let cmd = a:0 ? a:1 : "console"
+ let c = 2
+ while c <= a:0
+ let str .= " " . s:rquote(a:{c})
+ let c += 1
+ endwhile
+ if cmd ==# "plugin"
+ call self.cache.clear('generators')
+ endif
+ if a:bang || cmd =~# 'console'
+ return self.background_script_command(cmd.str)
+ else
+ return self.execute_script_command(cmd.str)
+ endif
+endfunction
+
+function! s:app_runner_command(count,args) dict
+ if a:count == -2
+ return self.script_command(a:bang,"runner",a:args)
+ else
+ let str = self.ruby_shell_command('-r./config/boot -e "require '."'commands/runner'".'" '.s:rquote(a:args))
+ let res = s:sub(system(str),'\n$','')
+ if a:count < 0
+ echo res
+ else
+ exe a:count.'put =res'
+ endif
+ endif
+endfunction
+
+function! s:getpidfor(bind,port)
+ if has("win32") || has("win64")
+ let netstat = system("netstat -anop tcp")
+ let pid = matchstr(netstat,'\<'.a:bind.':'.a:port.'\>.\{-\}LISTENING\s\+\zs\d\+')
+ elseif executable('lsof')
+ let pid = system("lsof -i 4tcp@".a:bind.':'.a:port."|grep LISTEN|awk '{print $2}'")
+ let pid = s:sub(pid,'\n','')
+ else
+ let pid = ""
+ endif
+ return pid
+endfunction
+
+function! s:app_server_command(bang,arg) dict
+ let port = matchstr(a:arg,'\%(-p\|--port=\=\)\s*\zs\d\+')
+ if port == ''
+ let port = "3000"
+ endif
+ " TODO: Extract bind argument
+ let bind = "0.0.0.0"
+ if a:bang && executable("ruby")
+ let pid = s:getpidfor(bind,port)
+ if pid =~ '^\d\+$'
+ echo "Killing server with pid ".pid
+ if !has("win32")
+ call system("ruby -e 'Process.kill(:TERM,".pid.")'")
+ sleep 100m
+ endif
+ call system("ruby -e 'Process.kill(9,".pid.")'")
+ sleep 100m
+ endif
+ if a:arg == "-"
+ return
+ endif
+ endif
+ if has_key(self,'options') && has_key(self.options,'gnu_screen')
+ let screen = self.options.gnu_screen
+ else
+ let screen = g:rails_gnu_screen
+ endif
+ if has("win32") || has("win64") || (exists("$STY") && !has("gui_running") && screen && executable("screen")) || (exists("$TMUX") && !has("gui_running") && screen && executable("tmux"))
+ call self.background_script_command('server '.a:arg)
+ else
+ " --daemon would be more descriptive but lighttpd does not support it
+ call self.execute_script_command('server '.a:arg." -d")
+ endif
+ call s:setopt('a:root_url','http://'.(bind=='0.0.0.0'?'localhost': bind).':'.port.'/')
+endfunction
+
+function! s:app_destroy_command(bang,...) dict
+ if a:0 == 0
+ return self.execute_script_command('destroy')
+ elseif a:0 == 1
+ return self.execute_script_command('destroy '.s:rquote(a:1))
+ endif
+ let str = ""
+ let c = 1
+ while c <= a:0
+ let str .= " " . s:rquote(a:{c})
+ let c += 1
+ endwhile
+ call self.execute_script_command('destroy'.str)
+ call self.cache.clear('user_classes')
+endfunction
+
+function! s:app_generate_command(bang,...) dict
+ if a:0 == 0
+ return self.execute_script_command('generate')
+ elseif a:0 == 1
+ return self.execute_script_command('generate '.s:rquote(a:1))
+ endif
+ let cmd = join(map(copy(a:000),'s:rquote(v:val)'),' ')
+ if cmd !~ '-p\>' && cmd !~ '--pretend\>'
+ let execstr = self.script_shell_command('generate '.cmd.' -p -f')
+ let res = system(execstr)
+ let g:res = res
+ let junk = '\%(\e\[[0-9;]*m\)\='
+ let file = matchstr(res,junk.'\s\+\%(create\|force\)'.junk.'\s\+\zs\f\+\.rb\ze\n')
+ if file == ""
+ let file = matchstr(res,junk.'\s\+\%(identical\)'.junk.'\s\+\zs\f\+\.rb\ze\n')
+ endif
+ else
+ let file = ""
+ endif
+ if !self.execute_script_command('generate '.cmd) && file != ''
+ call self.cache.clear('user_classes')
+ call self.cache.clear('features')
+ if file =~ '^db/migrate/\d\d\d\d'
+ let file = get(self.relglob('',s:sub(file,'\d+','[0-9]*[0-9]')),-1,file)
+ endif
+ edit `=self.path(file)`
+ endif
+endfunction
+
+call s:add_methods('app', ['generators','script_command','runner_command','server_command','destroy_command','generate_command'])
+
+function! s:Complete_script(ArgLead,CmdLine,P)
+ let cmd = s:sub(a:CmdLine,'^\u\w*\s+','')
+ if cmd !~ '^[ A-Za-z0-9_=:-]*$'
+ return []
+ elseif cmd =~# '^\w*$'
+ return s:completion_filter(rails#app().relglob("script/","**/*"),a:ArgLead)
+ elseif cmd =~# '^\%(plugin\)\s\+'.a:ArgLead.'$'
+ return s:completion_filter(["discover","list","install","update","remove","source","unsource","sources"],a:ArgLead)
+ elseif cmd =~# '\%(plugin\)\s\+\%(install\|remove\)\s\+'.a:ArgLead.'$' || cmd =~ '\%(generate\|destroy\)\s\+plugin\s\+'.a:ArgLead.'$'
+ return s:pluginList(a:ArgLead,a:CmdLine,a:P)
+ elseif cmd =~# '^\%(generate\|destroy\)\s\+'.a:ArgLead.'$'
+ return s:completion_filter(rails#app().generators(),a:ArgLead)
+ elseif cmd =~# '^\%(generate\|destroy\)\s\+\w\+\s\+'.a:ArgLead.'$'
+ let target = matchstr(cmd,'^\w\+\s\+\%(\w\+:\)\=\zs\w\+\ze\s\+')
+ if target =~# '^\w*controller$'
+ return filter(s:controllerList(a:ArgLead,"",""),'v:val !=# "application"')
+ elseif target ==# 'generator'
+ return s:completion_filter(map(rails#app().relglob('lib/generators/','*'),'s:sub(v:val,"/$","")'))
+ elseif target ==# 'helper'
+ return s:helperList(a:ArgLead,"","")
+ elseif target ==# 'integration_test' || target ==# 'integration_spec' || target ==# 'feature'
+ return s:integrationtestList(a:ArgLead,"","")
+ elseif target ==# 'metal'
+ return s:metalList(a:ArgLead,"","")
+ elseif target ==# 'migration' || target ==# 'session_migration'
+ return s:migrationList(a:ArgLead,"","")
+ elseif target =~# '^\w*\%(model\|resource\)$' || target =~# '\w*scaffold\%(_controller\)\=$' || target ==# 'mailer'
+ return s:modelList(a:ArgLead,"","")
+ elseif target ==# 'observer'
+ let observers = s:observerList("","","")
+ let models = s:modelList("","","")
+ if cmd =~# '^destroy\>'
+ let models = []
+ endif
+ call filter(models,'index(observers,v:val) < 0')
+ return s:completion_filter(observers + models,a:ArgLead)
+ else
+ return []
+ endif
+ elseif cmd =~# '^\%(generate\|destroy\)\s\+scaffold\s\+\w\+\s\+'.a:ArgLead.'$'
+ return filter(s:controllerList(a:ArgLead,"",""),'v:val !=# "application"')
+ return s:completion_filter(rails#app().environments())
+ elseif cmd =~# '^\%(console\)\s\+\(--\=\w\+\s\+\)\='.a:ArgLead."$"
+ return s:completion_filter(rails#app().environments()+["-s","--sandbox"],a:ArgLead)
+ elseif cmd =~# '^\%(server\)\s\+.*-e\s\+'.a:ArgLead."$"
+ return s:completion_filter(rails#app().environments(),a:ArgLead)
+ elseif cmd =~# '^\%(server\)\s\+'
+ if a:ArgLead =~# '^--environment='
+ return s:completion_filter(map(copy(rails#app().environments()),'"--environment=".v:val'),a:ArgLead)
+ else
+ return filter(["-p","-b","-e","-m","-d","-u","-c","-h","--port=","--binding=","--environment=","--mime-types=","--daemon","--debugger","--charset=","--help"],'s:startswith(v:val,a:ArgLead)')
+ endif
+ endif
+ return ""
+endfunction
+
+function! s:CustomComplete(A,L,P,cmd)
+ let L = "Rscript ".a:cmd." ".s:sub(a:L,'^\h\w*\s+','')
+ let P = a:P - strlen(a:L) + strlen(L)
+ return s:Complete_script(a:A,L,P)
+endfunction
+
+function! s:Complete_server(A,L,P)
+ return s:CustomComplete(a:A,a:L,a:P,"server")
+endfunction
+
+function! s:Complete_console(A,L,P)
+ return s:CustomComplete(a:A,a:L,a:P,"console")
+endfunction
+
+function! s:Complete_generate(A,L,P)
+ return s:CustomComplete(a:A,a:L,a:P,"generate")
+endfunction
+
+function! s:Complete_destroy(A,L,P)
+ return s:CustomComplete(a:A,a:L,a:P,"destroy")
+endfunction
+
+function! s:Complete_ruby(A,L,P)
+ return s:completion_filter(rails#app().user_classes()+["ActiveRecord::Base"],a:A)
+endfunction
+
+" }}}1
+" Navigation {{{1
+
+function! s:BufNavCommands()
+ command! -buffer -bar -nargs=? -complete=customlist,s:Complete_cd Rcd :cd `=rails#app().path(<q-args>)`
+ command! -buffer -bar -nargs=? -complete=customlist,s:Complete_cd Rlcd :lcd `=rails#app().path(<q-args>)`
+ command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find Rfind :call s:warn( 'Rfind has been deprecated in favor of :1R or :find' )|call s:Find(<count>,'<bang>' ,<f-args>)
+ command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find REfind :call s:warn('REfind has been deprecated in favor of :1RE or :find')|call s:Find(<count>,'E<bang>',<f-args>)
+ command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find RSfind :call s:warn('RSfind has been deprecated in favor of :1RS or :find')|call s:Find(<count>,'S<bang>',<f-args>)
+ command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find RVfind :call s:warn('RVfind has been deprecated in favor of :1RV or :find')|call s:Find(<count>,'V<bang>',<f-args>)
+ command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find RTfind :call s:warn('RTfind has been deprecated in favor of :1RT or :find')|call s:Find(<count>,'T<bang>',<f-args>)
+ command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find Rsfind :call s:warn('Rsfind has been deprecated in favor of :1RS or :sfind')|<count>RSfind<bang> <args>
+ command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find Rtabfind :call s:warn('Rtabfind has been deprecated in favor of :1RT or :tabfind')|<count>RTfind<bang> <args>
+ command! -buffer -bar -nargs=* -bang -complete=customlist,s:Complete_edit Redit :call s:warn( 'Redit has been deprecated in favor of :R')|call s:Edit(<count>,'<bang>' ,<f-args>)
+ command! -buffer -bar -nargs=* -bang -complete=customlist,s:Complete_edit REedit :call s:warn('REedit has been deprecated in favor of :RE')|call s:Edit(<count>,'E<bang>',<f-args>)
+ command! -buffer -bar -nargs=* -bang -complete=customlist,s:Complete_edit RSedit :call s:warn('RSedit has been deprecated in favor of :RS')|call s:Edit(<count>,'S<bang>',<f-args>)
+ command! -buffer -bar -nargs=* -bang -complete=customlist,s:Complete_edit RVedit :call s:warn('RVedit has been deprecated in favor of :RV')|call s:Edit(<count>,'V<bang>',<f-args>)
+ command! -buffer -bar -nargs=* -bang -complete=customlist,s:Complete_edit RTedit :call s:warn('RTedit has been deprecated in favor of :RT')|call s:Edit(<count>,'T<bang>',<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_edit RDedit :call s:warn('RDedit has been deprecated in favor of :RD')|call s:Edit(<count>,'<line1>D<bang>',<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related A :call s:Alternate('<bang>', <line1>,<line2>,<count>,<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AE :call s:Alternate('E<bang>',<line1>,<line2>,<count>,<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AS :call s:Alternate('S<bang>',<line1>,<line2>,<count>,<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AV :call s:Alternate('V<bang>',<line1>,<line2>,<count>,<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AT :call s:Alternate('T<bang>',<line1>,<line2>,<count>,<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AD :call s:Alternate('D<bang>',<line1>,<line2>,<count>,<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AN :call s:Related('<bang>' ,<line1>,<line2>,<count>,<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related R :call s:Related('<bang>' ,<line1>,<line2>,<count>,<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RE :call s:Related('E<bang>',<line1>,<line2>,<count>,<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RS :call s:Related('S<bang>',<line1>,<line2>,<count>,<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RV :call s:Related('V<bang>',<line1>,<line2>,<count>,<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RT :call s:Related('T<bang>',<line1>,<line2>,<count>,<f-args>)
+ command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RD :call s:Related('D<bang>',<line1>,<line2>,<count>,<f-args>)
+endfunction
+
+function! s:djump(def)
+ let def = s:sub(a:def,'^[#:]','')
+ if def =~ '^\d\+$'
+ exe def
+ elseif def =~ '^!'
+ if expand('%') !~ '://' && !isdirectory(expand('%:p:h'))
+ call mkdir(expand('%:p:h'),'p')
+ endif
+ elseif def != ''
+ let ext = matchstr(def,'\.\zs.*')
+ let def = matchstr(def,'[^.]*')
+ let v:errmsg = ''
+ silent! exe "djump ".def
+ if ext != '' && (v:errmsg == '' || v:errmsg =~ '^E387')
+ let rpat = '\C^\s*\%(mail\>.*\|respond_to\)\s*\%(\<do\|{\)\s*|\zs\h\k*\ze|'
+ let end = s:endof(line('.'))
+ let rline = search(rpat,'',end)
+ if rline > 0
+ let variable = matchstr(getline(rline),rpat)
+ let success = search('\C^\s*'.variable.'\s*\.\s*\zs'.ext.'\>','',end)
+ if !success
+ silent! exe "djump ".def
+ endif
+ endif
+ endif
+ endif
+endfunction
+
+function! s:Find(count,cmd,...)
+ let str = ""
+ if a:0
+ let i = 1
+ while i < a:0
+ let str .= s:escarg(a:{i}) . " "
+ let i += 1
+ endwhile
+ let file = a:{i}
+ let tail = matchstr(file,'[#!].*$\|:\d*\%(:in\>.*\)\=$')
+ if tail != ""
+ let file = s:sub(file,'[#!].*$|:\d*%(:in>.*)=$','')
+ endif
+ if file != ""
+ let file = s:RailsIncludefind(file)
+ endif
+ else
+ let file = s:RailsFind()
+ let tail = ""
+ endif
+ call s:findedit((a:count==1?'' : a:count).a:cmd,file.tail,str)
+endfunction
+
+function! s:Edit(count,cmd,...)
+ if a:0
+ let str = ""
+ let i = 1
+ while i < a:0
+ let str .= "`=a:".i."` "
+ let i += 1
+ endwhile
+ let file = a:{i}
+ call s:findedit(s:editcmdfor(a:cmd),file,str)
+ else
+ exe s:editcmdfor(a:cmd)
+ endif
+endfunction
+
+function! s:fuzzyglob(arg)
+ return s:gsub(s:gsub(a:arg,'[^/.]','[&]*'),'%(/|^)\.@!|\.','&*')
+endfunction
+
+function! s:Complete_find(ArgLead, CmdLine, CursorPos)
+ let paths = s:pathsplit(&l:path)
+ let seen = {}
+ for path in paths
+ if s:startswith(path,rails#app().path()) && path !~ '[][*]'
+ let path = path[strlen(rails#app().path()) + 1 : ]
+ for file in rails#app().relglob(path == '' ? '' : path.'/',s:fuzzyglob(rails#underscore(a:ArgLead)), a:ArgLead =~# '\u' ? '.rb' : '')
+ let seen[file] = 1
+ endfor
+ endif
+ endfor
+ return s:autocamelize(sort(keys(seen)),a:ArgLead)
+endfunction
+
+function! s:Complete_edit(ArgLead, CmdLine, CursorPos)
+ return s:completion_filter(rails#app().relglob("",s:fuzzyglob(a:ArgLead)),a:ArgLead)
+endfunction
+
+function! s:Complete_cd(ArgLead, CmdLine, CursorPos)
+ let all = rails#app().relglob("",a:ArgLead."*")
+ call filter(all,'v:val =~ "/$"')
+ return filter(all,'s:startswith(v:val,a:ArgLead)')
+endfunction
+
+function! RailsIncludeexpr()
+ " Is this foolproof?
+ if mode() =~ '[iR]' || expand("<cfile>") != v:fname
+ return s:RailsIncludefind(v:fname)
+ else
+ return s:RailsIncludefind(v:fname,1)
+ endif
+endfunction
+
+function! s:linepeak()
+ let line = getline(line("."))
+ let line = s:sub(line,'^(.{'.col(".").'}).*','\1')
+ let line = s:sub(line,'([:"'."'".']|\%[qQ]=[[({<])=\f*$','')
+ return line
+endfunction
+
+function! s:matchcursor(pat)
+ let line = getline(".")
+ let lastend = 0
+ while lastend >= 0
+ let beg = match(line,'\C'.a:pat,lastend)
+ let end = matchend(line,'\C'.a:pat,lastend)
+ if beg < col(".") && end >= col(".")
+ return matchstr(line,'\C'.a:pat,lastend)
+ endif
+ let lastend = end
+ endwhile
+ return ""
+endfunction
+
+function! s:findit(pat,repl)
+ let res = s:matchcursor(a:pat)
+ if res != ""
+ return substitute(res,'\C'.a:pat,a:repl,'')
+ else
+ return ""
+ endif
+endfunction
+
+function! s:findamethod(func,repl)
+ return s:findit('\s*\<\%('.a:func.'\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>.\=',a:repl)
+endfunction
+
+function! s:findasymbol(sym,repl)
+ return s:findit('\s*\%(:\%('.a:sym.'\)\s*=>\|\<'.a:sym.':\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>.\=',a:repl)
+endfunction
+
+function! s:findfromview(func,repl)
+ " ( ) ( ) ( \1 ) ( )
+ return s:findit('\s*\%(<%\)\==\=\s*\<\%('.a:func.'\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>['."'".'"]\=\s*\%(%>\s*\)\=',a:repl)
+endfunction
+
+function! s:RailsFind()
+ if filereadable(expand("<cfile>"))
+ return expand("<cfile>")
+ endif
+
+ " UGH
+ let buffer = rails#buffer()
+ let format = s:format('html')
+
+ let res = s:findit('\v\s*<require\s*\(=\s*File.dirname\(__FILE__\)\s*\+\s*[:'."'".'"](\f+)>.=',expand('%:h').'/\1')
+ if res != ""|return res.(fnamemodify(res,':e') == '' ? '.rb' : '')|endif
+
+ let res = s:findit('\v<File.dirname\(__FILE__\)\s*\+\s*[:'."'".'"](\f+)>['."'".'"]=',expand('%:h').'\1')
+ if res != ""|return res|endif
+
+ let res = rails#underscore(s:findit('\v\s*<%(include|extend)\(=\s*<([[:alnum:]_:]+)>','\1'))
+ if res != ""|return res.".rb"|endif
+
+ let res = s:findamethod('require','\1')
+ if res != ""|return res.(fnamemodify(res,':e') == '' ? '.rb' : '')|endif
+
+ let res = s:findamethod('belongs_to\|has_one\|composed_of\|validates_associated\|scaffold','app/models/\1.rb')
+ if res != ""|return res|endif
+
+ let res = rails#singularize(s:findamethod('has_many\|has_and_belongs_to_many','app/models/\1'))
+ if res != ""|return res.".rb"|endif
+
+ let res = rails#singularize(s:findamethod('create_table\|change_table\|drop_table\|add_column\|rename_column\|remove_column\|add_index','app/models/\1'))
+ if res != ""|return res.".rb"|endif
+
+ let res = rails#singularize(s:findasymbol('through','app/models/\1'))
+ if res != ""|return res.".rb"|endif
+
+ let res = s:findamethod('fixtures','fixtures/\1')
+ if res != ""
+ return RailsFilePath() =~ '\<spec/' ? 'spec/'.res : res
+ endif
+
+ let res = s:findamethod('\%(\w\+\.\)\=resources','app/controllers/\1_controller.rb')
+ if res != ""|return res|endif
+
+ let res = s:findamethod('\%(\w\+\.\)\=resource','app/controllers/\1')
+ if res != ""|return rails#pluralize(res)."_controller.rb"|endif
+
+ let res = s:findasymbol('to','app/controllers/\1')
+ if res =~ '#'|return s:sub(res,'#','_controller.rb#')|endif
+
+ let res = s:findamethod('root\s*\%(:to\s*=>\|\<to:\)\s*','app/controllers/\1')
+ if res =~ '#'|return s:sub(res,'#','_controller.rb#')|endif
+
+ let res = s:findamethod('\%(match\|get\|put\|post\|delete\|redirect\)\s*(\=\s*[:''"][^''"]*[''"]\=\s*\%(\%(,\s*:to\s*\)\==>\|,\s*to:\)\s*','app/controllers/\1')
+ if res =~ '#'|return s:sub(res,'#','_controller.rb#')|endif
+
+ let res = s:findamethod('layout','\=s:findlayout(submatch(1))')
+ if res != ""|return res|endif
+
+ let res = s:findasymbol('layout','\=s:findlayout(submatch(1))')
+ if res != ""|return res|endif
+
+ let res = s:findamethod('helper','app/helpers/\1_helper.rb')
+ if res != ""|return res|endif
+
+ let res = s:findasymbol('controller','app/controllers/\1_controller.rb')
+ if res != ""|return res|endif
+
+ let res = s:findasymbol('action','\1')
+ if res != ""|return res|endif
+
+ let res = s:findasymbol('template','app/views/\1')
+ if res != ""|return res|endif
+
+ let res = s:sub(s:sub(s:findasymbol('partial','\1'),'^/',''),'[^/]+$','_&')
+ if res != ""|return res."\n".s:findview(res)|endif
+
+ let res = s:sub(s:sub(s:findfromview('render\s*(\=\s*\%(:partial\s\+=>\|partial:\)\s*','\1'),'^/',''),'[^/]+$','_&')
+ if res != ""|return res."\n".s:findview(res)|endif
+
+ let res = s:findamethod('render\>\s*\%(:\%(template\|action\)\s\+=>\|template:\|action:\)\s*','\1.'.format.'\n\1')
+ if res != ""|return res|endif
+
+ let res = s:sub(s:findfromview('render','\1'),'^/','')
+ if buffer.type_name('view') | let res = s:sub(res,'[^/]+$','_&') | endif
+ if res != ""|return res."\n".s:findview(res)|endif
+
+ let res = s:findamethod('redirect_to\s*(\=\s*\%\(:action\s\+=>\|\<action:\)\s*','\1')
+ if res != ""|return res|endif
+
+ let res = s:findfromview('stylesheet_link_tag','public/stylesheets/\1')
+ if res != '' && fnamemodify(res, ':e') == '' " Append the default extension iff the filename doesn't already contains an extension
+ let res .= '.css'
+ end
+ if res != ""|return res|endif
+
+ let res = s:sub(s:findfromview('javascript_include_tag','public/javascripts/\1'),'/defaults>','/application')
+ if res != '' && fnamemodify(res, ':e') == '' " Append the default extension iff the filename doesn't already contains an extension
+ let res .= '.js'
+ end
+ if res != ""|return res|endif
+
+ if buffer.type_name('controller')
+ let contr = s:controller()
+ let view = s:findit('\s*\<def\s\+\(\k\+\)\>(\=','/\1')
+ let res = s:findview(contr.'/'.view)
+ if res != ""|return res|endif
+ endif
+
+ let old_isfname = &isfname
+ try
+ set isfname=@,48-57,/,-,_,:,#
+ " TODO: grab visual selection in visual mode
+ let cfile = expand("<cfile>")
+ finally
+ let &isfname = old_isfname
+ endtry
+ let res = s:RailsIncludefind(cfile,1)
+ return res
+endfunction
+
+function! s:app_named_route_file(route) dict
+ call self.route_names()
+ if self.cache.has("named_routes") && has_key(self.cache.get("named_routes"),a:route)
+ return self.cache.get("named_routes")[a:route]
+ endif
+ return ""
+endfunction
+
+function! s:app_route_names() dict
+ if self.cache.needs("named_routes")
+ let exec = "ActionController::Routing::Routes.named_routes.each {|n,r| puts %{#{n} app/controllers/#{r.requirements[:controller]}_controller.rb##{r.requirements[:action]}}}"
+ let string = self.eval(exec)
+ let routes = {}
+ for line in split(string,"\n")
+ let route = split(line," ")
+ let name = route[0]
+ let routes[name] = route[1]
+ endfor
+ call self.cache.set("named_routes",routes)
+ endif
+
+ return keys(self.cache.get("named_routes"))
+endfunction
+
+call s:add_methods('app', ['route_names','named_route_file'])
+
+function! RailsNamedRoutes()
+ return rails#app().route_names()
+endfunction
+
+function! s:RailsIncludefind(str,...)
+ if a:str ==# "ApplicationController"
+ return "application_controller.rb\napp/controllers/application.rb"
+ elseif a:str ==# "Test::Unit::TestCase"
+ return "test/unit/testcase.rb"
+ endif
+ let str = a:str
+ if a:0 == 1
+ " Get the text before the filename under the cursor.
+ " We'll cheat and peak at this in a bit
+ let line = s:linepeak()
+ let line = s:sub(line,'([:"'."'".']|\%[qQ]=[[({<])=\f*$','')
+ else
+ let line = ""
+ endif
+ let str = s:sub(str,'^\s*','')
+ let str = s:sub(str,'\s*$','')
+ let str = s:sub(str,'^:=[:@]','')
+ let str = s:sub(str,':0x\x+$','') " For #<Object:0x...> style output
+ let str = s:gsub(str,"[\"']",'')
+ if line =~# '\<\(require\|load\)\s*(\s*$'
+ return str
+ elseif str =~# '^\l\w*#\w\+$'
+ return 'app/controllers/'.s:sub(str,'#','_controller.rb#')
+ endif
+ let str = rails#underscore(str)
+ let fpat = '\(\s*\%("\f*"\|:\f*\|'."'\\f*'".'\)\s*,\s*\)*'
+ if a:str =~# '\u'
+ " Classes should always be in .rb files
+ let str .= '.rb'
+ elseif line =~# ':partial\s*=>\s*'
+ let str = s:sub(str,'[^/]+$','_&')
+ let str = s:findview(str)
+ elseif line =~# '\<layout\s*(\=\s*' || line =~# ':layout\s*=>\s*'
+ let str = s:findview(s:sub(str,'^/=','layouts/'))
+ elseif line =~# ':controller\s*=>\s*'
+ let str = 'app/controllers/'.str.'_controller.rb'
+ elseif line =~# '\<helper\s*(\=\s*'
+ let str = 'app/helpers/'.str.'_helper.rb'
+ elseif line =~# '\<fixtures\s*(\='.fpat
+ if RailsFilePath() =~# '\<spec/'
+ let str = s:sub(str,'^/@!','spec/fixtures/')
+ else
+ let str = s:sub(str,'^/@!','test/fixtures/')
+ endif
+ elseif line =~# '\<stylesheet_\(link_tag\|path\)\s*(\='.fpat
+ let str = s:sub(str,'^/@!','/stylesheets/')
+ if str != '' && fnamemodify(str, ':e') == ''
+ let str .= '.css'
+ endif
+ elseif line =~# '\<javascript_\(include_tag\|path\)\s*(\='.fpat
+ if str ==# "defaults"
+ let str = "application"
+ endif
+ let str = s:sub(str,'^/@!','/javascripts/')
+ if str != '' && fnamemodify(str, ':e') == ''
+ let str .= '.js'
+ endif
+ elseif line =~# '\<\(has_one\|belongs_to\)\s*(\=\s*'
+ let str = 'app/models/'.str.'.rb'
+ elseif line =~# '\<has_\(and_belongs_to_\)\=many\s*(\=\s*'
+ let str = 'app/models/'.rails#singularize(str).'.rb'
+ elseif line =~# '\<def\s\+' && expand("%:t") =~# '_controller\.rb'
+ let str = s:findview(str)
+ elseif str =~# '_\%(path\|url\)$' || (line =~# ':as\s*=>\s*$' && rails#buffer().type_name('config-routes'))
+ if line !~# ':as\s*=>\s*$'
+ let str = s:sub(str,'_%(path|url)$','')
+ let str = s:sub(str,'^hash_for_','')
+ endif
+ let file = rails#app().named_route_file(str)
+ if file == ""
+ let str = s:sub(str,'^formatted_','')
+ if str =~# '^\%(new\|edit\)_'
+ let str = 'app/controllers/'.s:sub(rails#pluralize(str),'^(new|edit)_(.*)','\2_controller.rb#\1')
+ elseif str ==# rails#singularize(str)
+ " If the word can't be singularized, it's probably a link to the show
+ " method. We should verify by checking for an argument, but that's
+ " difficult the way things here are currently structured.
+ let str = 'app/controllers/'.rails#pluralize(str).'_controller.rb#show'
+ else
+ let str = 'app/controllers/'.str.'_controller.rb#index'
+ endif
+ else
+ let str = file
+ endif
+ elseif str !~ '/'
+ " If we made it this far, we'll risk making it singular.
+ let str = rails#singularize(str)
+ let str = s:sub(str,'_id$','')
+ endif
+ if str =~ '^/' && !filereadable(str)
+ let str = s:sub(str,'^/','')
+ endif
+ if str =~# '^lib/' && !filereadable(str)
+ let str = s:sub(str,'^lib/','')
+ endif
+ return str
+endfunction
+
+" }}}1
+" File Finders {{{1
+
+function! s:addfilecmds(type)
+ let l = s:sub(a:type,'^.','\l&')
+ let cmds = 'ESVTD '
+ let cmd = ''
+ while cmds != ''
+ let cplt = " -complete=customlist,".s:sid.l."List"
+ exe "command! -buffer -bar ".(cmd == 'D' ? '-range=0 ' : '')."-nargs=*".cplt." R".cmd.l." :call s:".l.'Edit("'.(cmd == 'D' ? '<line1>' : '').cmd.'<bang>",<f-args>)'
+ let cmd = strpart(cmds,0,1)
+ let cmds = strpart(cmds,1)
+ endwhile
+endfunction
+
+function! s:BufFinderCommands()
+ command! -buffer -bar -nargs=+ Rnavcommand :call s:Navcommand(<bang>0,<f-args>)
+ call s:addfilecmds("metal")
+ call s:addfilecmds("model")
+ call s:addfilecmds("view")
+ call s:addfilecmds("controller")
+ call s:addfilecmds("mailer")
+ call s:addfilecmds("migration")
+ call s:addfilecmds("observer")
+ call s:addfilecmds("helper")
+ call s:addfilecmds("layout")
+ call s:addfilecmds("fixtures")
+ call s:addfilecmds("locale")
+ if rails#app().has('test') || rails#app().has('spec')
+ call s:addfilecmds("unittest")
+ call s:addfilecmds("functionaltest")
+ endif
+ if rails#app().has('test') || rails#app().has('spec') || rails#app().has('cucumber')
+ call s:addfilecmds("integrationtest")
+ endif
+ if rails#app().has('spec')
+ call s:addfilecmds("spec")
+ endif
+ call s:addfilecmds("stylesheet")
+ call s:addfilecmds("javascript")
+ call s:addfilecmds("plugin")
+ call s:addfilecmds("task")
+ call s:addfilecmds("lib")
+ call s:addfilecmds("environment")
+ call s:addfilecmds("initializer")
+endfunction
+
+function! s:completion_filter(results,A)
+ let results = sort(type(a:results) == type("") ? split(a:results,"\n") : copy(a:results))
+ call filter(results,'v:val !~# "\\~$"')
+ let filtered = filter(copy(results),'s:startswith(v:val,a:A)')
+ if !empty(filtered) | return filtered | endif
+ let regex = s:gsub(a:A,'[^/]','[&].*')
+ let filtered = filter(copy(results),'v:val =~# "^".regex')
+ if !empty(filtered) | return filtered | endif
+ let regex = s:gsub(a:A,'.','[&].*')
+ let filtered = filter(copy(results),'v:val =~# regex')
+ return filtered
+endfunction
+
+function! s:autocamelize(files,test)
+ if a:test =~# '^\u'
+ return s:completion_filter(map(copy(a:files),'rails#camelize(v:val)'),a:test)
+ else
+ return s:completion_filter(a:files,a:test)
+ endif
+endfunction
+
+function! s:app_relglob(path,glob,...) dict
+ if exists("+shellslash") && ! &shellslash
+ let old_ss = &shellslash
+ let &shellslash = 1
+ endif
+ let path = a:path
+ if path !~ '^/' && path !~ '^\w:'
+ let path = self.path(path)
+ endif
+ let suffix = a:0 ? a:1 : ''
+ let full_paths = split(glob(path.a:glob.suffix),"\n")
+ let relative_paths = []
+ for entry in full_paths
+ if suffix == '' && isdirectory(entry) && entry !~ '/$'
+ let entry .= '/'
+ endif
+ let relative_paths += [entry[strlen(path) : -strlen(suffix)-1]]
+ endfor
+ if exists("old_ss")
+ let &shellslash = old_ss
+ endif
+ return relative_paths
+endfunction
+
+call s:add_methods('app', ['relglob'])
+
+function! s:relglob(...)
+ return join(call(rails#app().relglob,a:000,rails#app()),"\n")
+endfunction
+
+function! s:helperList(A,L,P)
+ return s:autocamelize(rails#app().relglob("app/helpers/","**/*","_helper.rb"),a:A)
+endfunction
+
+function! s:controllerList(A,L,P)
+ let con = rails#app().relglob("app/controllers/","**/*",".rb")
+ call map(con,'s:sub(v:val,"_controller$","")')
+ return s:autocamelize(con,a:A)
+endfunction
+
+function! s:mailerList(A,L,P)
+ return s:autocamelize(rails#app().relglob("app/mailers/","**/*",".rb"),a:A)
+endfunction
+
+function! s:viewList(A,L,P)
+ let c = s:controller(1)
+ let top = rails#app().relglob("app/views/",s:fuzzyglob(a:A))
+ call filter(top,'v:val !~# "\\~$"')
+ if c != '' && a:A !~ '/'
+ let local = rails#app().relglob("app/views/".c."/","*.*[^~]")
+ return s:completion_filter(local+top,a:A)
+ endif
+ return s:completion_filter(top,a:A)
+endfunction
+
+function! s:layoutList(A,L,P)
+ return s:completion_filter(rails#app().relglob("app/views/layouts/","*"),a:A)
+endfunction
+
+function! s:stylesheetList(A,L,P)
+ let list = rails#app().relglob('app/assets/stylesheets/','**/*.*','')
+ call map(list,'s:sub(v:val,"\\..*$","")')
+ let list += rails#app().relglob('public/stylesheets/','**/*','.css')
+ if rails#app().has('sass')
+ call extend(list,rails#app().relglob('public/stylesheets/sass/','**/*','.s?ss'))
+ call s:uniq(list)
+ endif
+ return s:completion_filter(list,a:A)
+endfunction
+
+function! s:javascriptList(A,L,P)
+ let list = rails#app().relglob('app/assets/javascripts/','**/*.*','')
+ call map(list,'s:sub(v:val,"\\..*$","")')
+ let list += rails#app().relglob("public/javascripts/","**/*",".js")
+ return s:completion_filter(list,a:A)
+endfunction
+
+function! s:metalList(A,L,P)
+ return s:autocamelize(rails#app().relglob("app/metal/","**/*",".rb"),a:A)
+endfunction
+
+function! s:modelList(A,L,P)
+ let models = rails#app().relglob("app/models/","**/*",".rb")
+ call filter(models,'v:val !~# "_observer$"')
+ return s:autocamelize(models,a:A)
+endfunction
+
+function! s:observerList(A,L,P)
+ return s:autocamelize(rails#app().relglob("app/models/","**/*","_observer.rb"),a:A)
+endfunction
+
+function! s:fixturesList(A,L,P)
+ return s:completion_filter(rails#app().relglob("test/fixtures/","**/*")+rails#app().relglob("spec/fixtures/","**/*"),a:A)
+endfunction
+
+function! s:localeList(A,L,P)
+ return s:completion_filter(rails#app().relglob("config/locales/","**/*"),a:A)
+endfunction
+
+function! s:migrationList(A,L,P)
+ if a:A =~ '^\d'
+ let migrations = rails#app().relglob("db/migrate/",a:A."[0-9_]*",".rb")
+ return map(migrations,'matchstr(v:val,"^[0-9]*")')
+ else
+ let migrations = rails#app().relglob("db/migrate/","[0-9]*[0-9]_*",".rb")
+ call map(migrations,'s:sub(v:val,"^[0-9]*_","")')
+ return s:autocamelize(migrations,a:A)
+ endif
+endfunction
+
+function! s:unittestList(A,L,P)
+ let found = []
+ if rails#app().has('test')
+ let found += rails#app().relglob("test/unit/","**/*","_test.rb")
+ endif
+ if rails#app().has('spec')
+ let found += rails#app().relglob("spec/models/","**/*","_spec.rb")
+ endif
+ return s:autocamelize(found,a:A)
+endfunction
+
+function! s:functionaltestList(A,L,P)
+ let found = []
+ if rails#app().has('test')
+ let found += rails#app().relglob("test/functional/","**/*","_test.rb")
+ endif
+ if rails#app().has('spec')
+ let found += rails#app().relglob("spec/controllers/","**/*","_spec.rb")
+ let found += rails#app().relglob("spec/mailers/","**/*","_spec.rb")
+ endif
+ return s:autocamelize(found,a:A)
+endfunction
+
+function! s:integrationtestList(A,L,P)
+ if a:A =~# '^\u'
+ return s:autocamelize(rails#app().relglob("test/integration/","**/*","_test.rb"),a:A)
+ endif
+ let found = []
+ if rails#app().has('test')
+ let found += rails#app().relglob("test/integration/","**/*","_test.rb")
+ endif
+ if rails#app().has('spec')
+ let found += rails#app().relglob("spec/requests/","**/*","_spec.rb")
+ let found += rails#app().relglob("spec/integration/","**/*","_spec.rb")
+ endif
+ if rails#app().has('cucumber')
+ let found += rails#app().relglob("features/","**/*",".feature")
+ endif
+ return s:completion_filter(found,a:A)
+endfunction
+
+function! s:specList(A,L,P)
+ return s:completion_filter(rails#app().relglob("spec/","**/*","_spec.rb"),a:A)
+endfunction
+
+function! s:pluginList(A,L,P)
+ if a:A =~ '/'
+ return s:completion_filter(rails#app().relglob('vendor/plugins/',matchstr(a:A,'.\{-\}/').'**/*'),a:A)
+ else
+ return s:completion_filter(rails#app().relglob('vendor/plugins/',"*","/init.rb"),a:A)
+ endif
+endfunction
+
+" Task files, not actual rake tasks
+function! s:taskList(A,L,P)
+ let all = rails#app().relglob("lib/tasks/","**/*",".rake")
+ if RailsFilePath() =~ '\<vendor/plugins/.'
+ let path = s:sub(RailsFilePath(),'<vendor/plugins/[^/]*/\zs.*','')
+ let all = rails#app().relglob(path."tasks/","**/*",".rake")+rails#app().relglob(path."lib/tasks/","**/*",".rake")+all
+ endif
+ return s:autocamelize(all,a:A)
+endfunction
+
+function! s:libList(A,L,P)
+ let all = rails#app().relglob('lib/',"**/*",".rb")
+ if RailsFilePath() =~ '\<vendor/plugins/.'
+ let path = s:sub(RailsFilePath(),'<vendor/plugins/[^/]*/\zs.*','lib/')
+ let all = rails#app().relglob(path,"**/*",".rb") + all
+ endif
+ return s:autocamelize(all,a:A)
+endfunction
+
+function! s:environmentList(A,L,P)
+ return s:completion_filter(rails#app().relglob("config/environments/","**/*",".rb"),a:A)
+endfunction
+
+function! s:initializerList(A,L,P)
+ return s:completion_filter(rails#app().relglob("config/initializers/","**/*",".rb"),a:A)
+endfunction
+
+function! s:Navcommand(bang,...)
+ let suffix = ".rb"
+ let filter = "**/*"
+ let prefix = ""
+ let default = ""
+ let name = ""
+ let i = 0
+ while i < a:0
+ let i += 1
+ let arg = a:{i}
+ if arg =~# '^-suffix='
+ let suffix = matchstr(arg,'-suffix=\zs.*')
+ elseif arg =~# '^-default='
+ let default = matchstr(arg,'-default=\zs.*')
+ elseif arg =~# '^-\%(glob\|filter\)='
+ let filter = matchstr(arg,'-\w*=\zs.*')
+ elseif arg !~# '^-'
+ " A literal '\n'. For evaluation below
+ if name == ""
+ let name = arg
+ else
+ let prefix .= "\\n".s:sub(arg,'/=$','/')
+ endif
+ endif
+ endwhile
+ let prefix = s:sub(prefix,'^\\n','')
+ if name !~ '^[A-Za-z]\+$'
+ return s:error("E182: Invalid command name")
+ endif
+ let cmds = 'ESVTD '
+ let cmd = ''
+ while cmds != ''
+ exe 'command! -buffer -bar -bang -nargs=* -complete=customlist,'.s:sid.'CommandList R'.cmd.name." :call s:CommandEdit('".cmd."<bang>','".name."',\"".prefix."\",".string(suffix).",".string(filter).",".string(default).",<f-args>)"
+ let cmd = strpart(cmds,0,1)
+ let cmds = strpart(cmds,1)
+ endwhile
+endfunction
+
+function! s:CommandList(A,L,P)
+ let cmd = matchstr(a:L,'\CR[A-Z]\=\w\+')
+ exe cmd." &"
+ let lp = s:last_prefix . "\n"
+ let res = []
+ while lp != ""
+ let p = matchstr(lp,'.\{-\}\ze\n')
+ let lp = s:sub(lp,'.{-}\n','')
+ let res += rails#app().relglob(p,s:last_filter,s:last_suffix)
+ endwhile
+ if s:last_camelize
+ return s:autocamelize(res,a:A)
+ else
+ return s:completion_filter(res,a:A)
+ endif
+endfunction
+
+function! s:CommandEdit(cmd,name,prefix,suffix,filter,default,...)
+ if a:0 && a:1 == "&"
+ let s:last_prefix = a:prefix
+ let s:last_suffix = a:suffix
+ let s:last_filter = a:filter
+ let s:last_camelize = (a:suffix =~# '\.rb$')
+ else
+ if a:default == "both()"
+ if s:model() != ""
+ let default = s:model()
+ else
+ let default = s:controller()
+ endif
+ elseif a:default == "model()"
+ let default = s:model(1)
+ elseif a:default == "controller()"
+ let default = s:controller(1)
+ else
+ let default = a:default
+ endif
+ call s:EditSimpleRb(a:cmd,a:name,a:0 ? a:1 : default,a:prefix,a:suffix)
+ endif
+endfunction
+
+function! s:EditSimpleRb(cmd,name,target,prefix,suffix,...)
+ let cmd = s:findcmdfor(a:cmd)
+ if a:target == ""
+ " Good idea to emulate error numbers like this?
+ return s:error("E471: Argument required")
+ endif
+ let f = a:0 ? a:target : rails#underscore(a:target)
+ let jump = matchstr(f,'[#!].*\|:\d*\%(:in\)\=$')
+ let f = s:sub(f,'[#!].*|:\d*%(:in)=$','')
+ if jump =~ '^!'
+ let cmd = s:editcmdfor(cmd)
+ endif
+ if f == '.'
+ let f = s:sub(f,'\.$','')
+ else
+ let f .= a:suffix.jump
+ endif
+ let f = s:gsub(a:prefix,'\n',f.'\n').f
+ return s:findedit(cmd,f)
+endfunction
+
+function! s:app_migration(file) dict
+ let arg = a:file
+ if arg =~ '^0$\|^0\=[#:]'
+ let suffix = s:sub(arg,'^0*','')
+ if self.has_file('db/schema.rb')
+ return 'db/schema.rb'.suffix
+ elseif self.has_file('db/'.s:environment().'_structure.sql')
+ return 'db/'.s:environment().'_structure.sql'.suffix
+ else
+ return 'db/schema.rb'.suffix
+ endif
+ elseif arg =~ '^\d$'
+ let glob = '00'.arg.'_*.rb'
+ elseif arg =~ '^\d\d$'
+ let glob = '0'.arg.'_*.rb'
+ elseif arg =~ '^\d\d\d$'
+ let glob = ''.arg.'_*.rb'
+ elseif arg == ''
+ let glob = '*.rb'
+ else
+ let glob = '*'.rails#underscore(arg).'*rb'
+ endif
+ let files = split(glob(self.path('db/migrate/').glob),"\n")
+ if arg == ''
+ return get(files,-1,'')
+ endif
+ call map(files,'strpart(v:val,1+strlen(self.path()))')
+ let keep = get(files,0,'')
+ if glob =~# '^\*.*\*rb'
+ let pattern = glob[1:-4]
+ call filter(files,'v:val =~# ''db/migrate/\d\+_''.pattern.''\.rb''')
+ let keep = get(files,0,keep)
+ endif
+ return keep
+endfunction
+
+call s:add_methods('app', ['migration'])
+
+function! s:migrationEdit(cmd,...)
+ let cmd = s:findcmdfor(a:cmd)
+ let arg = a:0 ? a:1 : ''
+ let migr = arg == "." ? "db/migrate" : rails#app().migration(arg)
+ if migr != ''
+ call s:findedit(cmd,migr)
+ else
+ return s:error("Migration not found".(arg=='' ? '' : ': '.arg))
+ endif
+endfunction
+
+function! s:fixturesEdit(cmd,...)
+ if a:0
+ let c = rails#underscore(a:1)
+ else
+ let c = rails#pluralize(s:model(1))
+ endif
+ if c == ""
+ return s:error("E471: Argument required")
+ endif
+ let e = fnamemodify(c,':e')
+ let e = e == '' ? e : '.'.e
+ let c = fnamemodify(c,':r')
+ let file = get(rails#app().test_suites(),0,'test').'/fixtures/'.c.e
+ if file =~ '\.\w\+$' && rails#app().find_file(c.e,["test/fixtures","spec/fixtures"]) ==# ''
+ call s:edit(a:cmd,file)
+ else
+ call s:findedit(a:cmd,rails#app().find_file(c.e,["test/fixtures","spec/fixtures"],[".yml",".csv"],file))
+ endif
+endfunction
+
+function! s:localeEdit(cmd,...)
+ let c = a:0 ? a:1 : rails#app().default_locale()
+ if c =~# '\.'
+ call s:edit(a:cmd,rails#app().find_file(c,'config/locales',[],'config/locales/'.c))
+ else
+ call s:findedit(a:cmd,rails#app().find_file(c,'config/locales',['.yml','.rb'],'config/locales/'.c))
+ endif
+endfunction
+
+function! s:metalEdit(cmd,...)
+ if a:0
+ call s:EditSimpleRb(a:cmd,"metal",a:1,"app/metal/",".rb")
+ else
+ call s:EditSimpleRb(a:cmd,"metal",'config/boot',"",".rb")
+ endif
+endfunction
+
+function! s:modelEdit(cmd,...)
+ call s:EditSimpleRb(a:cmd,"model",a:0? a:1 : s:model(1),"app/models/",".rb")
+endfunction
+
+function! s:observerEdit(cmd,...)
+ call s:EditSimpleRb(a:cmd,"observer",a:0? a:1 : s:model(1),"app/models/","_observer.rb")
+endfunction
+
+function! s:viewEdit(cmd,...)
+ if a:0 && a:1 =~ '^[^!#:]'
+ let view = matchstr(a:1,'[^!#:]*')
+ elseif rails#buffer().type_name('controller','mailer')
+ let view = s:lastmethod(line('.'))
+ else
+ let view = ''
+ endif
+ if view == ''
+ return s:error("No view name given")
+ elseif view == '.'
+ return s:edit(a:cmd,'app/views')
+ elseif view !~ '/' && s:controller(1) != ''
+ let view = s:controller(1) . '/' . view
+ endif
+ if view !~ '/'
+ return s:error("Cannot find view without controller")
+ endif
+ let file = "app/views/".view
+ let found = s:findview(view)
+ if found != ''
+ let dir = fnamemodify(rails#app().path(found),':h')
+ if !isdirectory(dir)
+ if a:0 && a:1 =~ '!'
+ call mkdir(dir,'p')
+ else
+ return s:error('No such directory')
+ endif
+ endif
+ call s:edit(a:cmd,found)
+ elseif file =~ '\.\w\+$'
+ call s:findedit(a:cmd,file)
+ else
+ let format = s:format(rails#buffer().type_name('mailer') ? 'text' : 'html')
+ if glob(rails#app().path(file.'.'.format).'.*[^~]') != ''
+ let file .= '.' . format
+ endif
+ call s:findedit(a:cmd,file)
+ endif
+endfunction
+
+function! s:findview(name)
+ let self = rails#buffer()
+ let name = a:name
+ let pre = 'app/views/'
+ if name !~# '/'
+ let controller = self.controller_name(1)
+ if controller != ''
+ let name = controller.'/'.name
+ endif
+ endif
+ if name =~# '\.\w\+\.\w\+$' || name =~# '\.'.s:viewspattern().'$'
+ return pre.name
+ else
+ for format in ['.'.s:format('html'), '']
+ for type in s:view_types
+ if self.app().has_file(pre.name.format.'.'.type)
+ return pre.name.format.'.'.type
+ endif
+ endfor
+ endfor
+ endif
+ return ''
+endfunction
+
+function! s:findlayout(name)
+ return s:findview("layouts/".(a:name == '' ? 'application' : a:name))
+endfunction
+
+function! s:layoutEdit(cmd,...)
+ if a:0
+ return s:viewEdit(a:cmd,"layouts/".a:1)
+ endif
+ let file = s:findlayout(s:controller(1))
+ if file == ""
+ let file = s:findlayout("application")
+ endif
+ if file == ""
+ let file = "app/views/layouts/application.html.erb"
+ endif
+ call s:edit(a:cmd,s:sub(file,'^/',''))
+endfunction
+
+function! s:controllerEdit(cmd,...)
+ let suffix = '.rb'
+ if a:0 == 0
+ let controller = s:controller(1)
+ if rails#buffer().type_name() =~# '^view\%(-layout\|-partial\)\@!'
+ let suffix .= '#'.expand('%:t:r')
+ endif
+ else
+ let controller = a:1
+ endif
+ if rails#app().has_file("app/controllers/".controller."_controller.rb") || !rails#app().has_file("app/controllers/".controller.".rb")
+ let suffix = "_controller".suffix
+ endif
+ return s:EditSimpleRb(a:cmd,"controller",controller,"app/controllers/",suffix)
+endfunction
+
+function! s:mailerEdit(cmd,...)
+ return s:EditSimpleRb(a:cmd,"mailer",a:0? a:1 : s:controller(1),"app/mailers/\napp/models/",".rb")
+endfunction
+
+function! s:helperEdit(cmd,...)
+ return s:EditSimpleRb(a:cmd,"helper",a:0? a:1 : s:controller(1),"app/helpers/","_helper.rb")
+endfunction
+
+function! s:stylesheetEdit(cmd,...)
+ let name = a:0 ? a:1 : s:controller(1)
+ if rails#app().has('sass') && rails#app().has_file('public/stylesheets/sass/'.name.'.sass')
+ return s:EditSimpleRb(a:cmd,"stylesheet",name,"public/stylesheets/sass/",".sass",1)
+ elseif rails#app().has('sass') && rails#app().has_file('public/stylesheets/sass/'.name.'.scss')
+ return s:EditSimpleRb(a:cmd,"stylesheet",name,"public/stylesheets/sass/",".scss",1)
+ elseif rails#app().has('lesscss') && rails#app().has_file('app/stylesheets/'.name.'.less')
+ return s:EditSimpleRb(a:cmd,"stylesheet",name,"app/stylesheets/",".less",1)
+ else
+ let types = rails#app().relglob('app/assets/stylesheets/'.name,'.*','')
+ if !empty(types)
+ return s:EditSimpleRb(a:cmd,'stylesheet',name,'app/assets/stylesheets/',types[0],1)
+ else
+ return s:EditSimpleRb(a:cmd,'stylesheet',name,'public/stylesheets/','.css',1)
+ endif
+ endif
+endfunction
+
+function! s:javascriptEdit(cmd,...)
+ let name = a:0 ? a:1 : s:controller(1)
+ if rails#app().has('coffee') && rails#app().has_file('app/scripts/'.name.'.coffee')
+ return s:EditSimpleRb(a:cmd,'javascript',name,'app/scripts/','.coffee',1)
+ elseif rails#app().has('coffee') && rails#app().has_file('app/scripts/'.name.'.js')
+ return s:EditSimpleRb(a:cmd,'javascript',name,'app/scripts/','.js',1)
+ else
+ let types = rails#app().relglob('app/assets/javascripts/'.name,'.*','')
+ if !empty(types)
+ return s:EditSimpleRb(a:cmd,'javascript',name,'app/assets/javascripts/',types[0],1)
+ else
+ return s:EditSimpleRb(a:cmd,'javascript',name,'public/javascripts/','.js',1)
+ endif
+ endif
+endfunction
+
+function! s:unittestEdit(cmd,...)
+ let f = rails#underscore(a:0 ? matchstr(a:1,'[^!#:]*') : s:model(1))
+ let jump = a:0 ? matchstr(a:1,'[!#:].*') : ''
+ if jump =~ '!'
+ let cmd = s:editcmdfor(a:cmd)
+ else
+ let cmd = s:findcmdfor(a:cmd)
+ endif
+ let mapping = {'test': ['test/unit/','_test.rb'], 'spec': ['spec/models/','_spec.rb']}
+ let tests = map(filter(rails#app().test_suites(),'has_key(mapping,v:val)'),'get(mapping,v:val)')
+ if empty(tests)
+ let tests = [mapping['test']]
+ endif
+ for [prefix, suffix] in tests
+ if !a:0 && rails#buffer().type_name('model-aro') && f != '' && f !~# '_observer$'
+ if rails#app().has_file(prefix.f.'_observer'.suffix)
+ return s:findedit(cmd,prefix.f.'_observer'.suffix.jump)
+ endif
+ endif
+ endfor
+ for [prefix, suffix] in tests
+ if rails#app().has_file(prefix.f.suffix)
+ return s:findedit(cmd,prefix.f.suffix.jump)
+ endif
+ endfor
+ return s:EditSimpleRb(a:cmd,"unittest",f.jump,tests[0][0],tests[0][1],1)
+endfunction
+
+function! s:functionaltestEdit(cmd,...)
+ let f = rails#underscore(a:0 ? matchstr(a:1,'[^!#:]*') : s:controller(1))
+ let jump = a:0 ? matchstr(a:1,'[!#:].*') : ''
+ if jump =~ '!'
+ let cmd = s:editcmdfor(a:cmd)
+ else
+ let cmd = s:findcmdfor(a:cmd)
+ endif
+ let mapping = {'test': [['test/functional/'],['_test.rb','_controller_test.rb']], 'spec': [['spec/controllers/','spec/mailers/'],['_spec.rb','_controller_spec.rb']]}
+ let tests = map(filter(rails#app().test_suites(),'has_key(mapping,v:val)'),'get(mapping,v:val)')
+ if empty(tests)
+ let tests = [mapping[tests]]
+ endif
+ for [prefixes, suffixes] in tests
+ for prefix in prefixes
+ for suffix in suffixes
+ if rails#app().has_file(prefix.f.suffix)
+ return s:findedit(cmd,prefix.f.suffix.jump)
+ endif
+ endfor
+ endfor
+ endfor
+ return s:EditSimpleRb(a:cmd,"functionaltest",f.jump,tests[0][0][0],tests[0][1][0],1)
+endfunction
+
+function! s:integrationtestEdit(cmd,...)
+ if !a:0
+ return s:EditSimpleRb(a:cmd,"integrationtest","test/test_helper\nfeatures/support/env\nspec/spec_helper","",".rb")
+ endif
+ let f = rails#underscore(matchstr(a:1,'[^!#:]*'))
+ let jump = matchstr(a:1,'[!#:].*')
+ if jump =~ '!'
+ let cmd = s:editcmdfor(a:cmd)
+ else
+ let cmd = s:findcmdfor(a:cmd)
+ endif
+ let tests = [['test/integration/','_test.rb'], [ 'spec/requests/','_spec.rb'], [ 'spec/integration/','_spec.rb'], [ 'features/','.feature']]
+ call filter(tests, 'isdirectory(rails#app().path(v:val[0]))')
+ if empty(tests)
+ let tests = [['test/integration/','_test.rb']]
+ endif
+ for [prefix, suffix] in tests
+ if rails#app().has_file(prefix.f.suffix)
+ return s:findedit(cmd,prefix.f.suffix.jump)
+ elseif rails#app().has_file(prefix.rails#underscore(f).suffix)
+ return s:findedit(cmd,prefix.rails#underscore(f).suffix.jump)
+ endif
+ endfor
+ return s:EditSimpleRb(a:cmd,"integrationtest",f.jump,tests[0][0],tests[0][1],1)
+endfunction
+
+function! s:specEdit(cmd,...)
+ if a:0
+ return s:EditSimpleRb(a:cmd,"spec",a:1,"spec/","_spec.rb")
+ else
+ call s:EditSimpleRb(a:cmd,"spec","spec_helper","spec/",".rb")
+ endif
+endfunction
+
+function! s:pluginEdit(cmd,...)
+ let cmd = s:findcmdfor(a:cmd)
+ let plugin = ""
+ let extra = ""
+ if RailsFilePath() =~ '\<vendor/plugins/.'
+ let plugin = matchstr(RailsFilePath(),'\<vendor/plugins/\zs[^/]*\ze')
+ let extra = "vendor/plugins/" . plugin . "/\n"
+ endif
+ if a:0
+ if a:1 =~ '^[^/.]*/\=$' && rails#app().has_file("vendor/plugins/".a:1."/init.rb")
+ return s:EditSimpleRb(a:cmd,"plugin",s:sub(a:1,'/$',''),"vendor/plugins/","/init.rb")
+ elseif plugin == ""
+ call s:edit(cmd,"vendor/plugins/".s:sub(a:1,'\.$',''))
+ elseif a:1 == "."
+ call s:findedit(cmd,"vendor/plugins/".plugin)
+ elseif isdirectory(rails#app().path("vendor/plugins/".matchstr(a:1,'^[^/]*')))
+ call s:edit(cmd,"vendor/plugins/".a:1)
+ else
+ call s:findedit(cmd,"vendor/plugins/".a:1."\nvendor/plugins/".plugin."/".a:1)
+ endif
+ else
+ call s:findedit(a:cmd,"Gemfile")
+ endif
+endfunction
+
+function! s:taskEdit(cmd,...)
+ let plugin = ""
+ let extra = ""
+ if RailsFilePath() =~ '\<vendor/plugins/.'
+ let plugin = matchstr(RailsFilePath(),'\<vendor/plugins/[^/]*')
+ let extra = plugin."/tasks/\n".plugin."/lib/tasks/\n"
+ endif
+ if a:0
+ call s:EditSimpleRb(a:cmd,"task",a:1,extra."lib/tasks/",".rake")
+ else
+ call s:findedit(a:cmd,(plugin != "" ? plugin."/Rakefile\n" : "")."Rakefile")
+ endif
+endfunction
+
+function! s:libEdit(cmd,...)
+ let extra = ""
+ if RailsFilePath() =~ '\<vendor/plugins/.'
+ let extra = s:sub(RailsFilePath(),'<vendor/plugins/[^/]*/\zs.*','lib/')."\n"
+ endif
+ if a:0
+ call s:EditSimpleRb(a:cmd,"lib",a:0? a:1 : "",extra."lib/",".rb")
+ else
+ call s:EditSimpleRb(a:cmd,"lib","seeds","db/",".rb")
+ endif
+endfunction
+
+function! s:environmentEdit(cmd,...)
+ if a:0 || rails#app().has_file('config/application.rb')
+ return s:EditSimpleRb(a:cmd,"environment",a:0? a:1 : "../application","config/environments/",".rb")
+ else
+ return s:EditSimpleRb(a:cmd,"environment","environment","config/",".rb")
+ endif
+endfunction
+
+function! s:initializerEdit(cmd,...)
+ return s:EditSimpleRb(a:cmd,"initializer",a:0? a:1 : "../routes","config/initializers/",".rb")
+endfunction
+
+" }}}1
+" Alternate/Related {{{1
+
+function! s:findcmdfor(cmd)
+ let bang = ''
+ if a:cmd =~ '\!$'
+ let bang = '!'
+ let cmd = s:sub(a:cmd,'\!$','')
+ else
+ let cmd = a:cmd
+ endif
+ if cmd =~ '^\d'
+ let num = matchstr(cmd,'^\d\+')
+ let cmd = s:sub(cmd,'^\d+','')
+ else
+ let num = ''
+ endif
+ if cmd == '' || cmd == 'E' || cmd == 'F'
+ return num.'find'.bang
+ elseif cmd == 'S'
+ return num.'sfind'.bang
+ elseif cmd == 'V'
+ return 'vert '.num.'sfind'.bang
+ elseif cmd == 'T'
+ return num.'tabfind'.bang
+ elseif cmd == 'D'
+ return num.'read'.bang
+ else
+ return num.cmd.bang
+ endif
+endfunction
+
+function! s:editcmdfor(cmd)
+ let cmd = s:findcmdfor(a:cmd)
+ let cmd = s:sub(cmd,'<sfind>','split')
+ let cmd = s:sub(cmd,'find>','edit')
+ return cmd
+endfunction
+
+function! s:try(cmd) abort
+ if !exists(":try")
+ " I've seen at least one weird setup without :try
+ exe a:cmd
+ else
+ try
+ exe a:cmd
+ catch
+ call s:error(s:sub(v:exception,'^.{-}:\zeE',''))
+ return 0
+ endtry
+ endif
+ return 1
+endfunction
+
+function! s:findedit(cmd,files,...) abort
+ let cmd = s:findcmdfor(a:cmd)
+ let files = type(a:files) == type([]) ? copy(a:files) : split(a:files,"\n")
+ if len(files) == 1
+ let file = files[0]
+ else
+ let file = get(filter(copy(files),'rails#app().has_file(s:sub(v:val,"#.*|:\\d*$",""))'),0,get(files,0,''))
+ endif
+ if file =~ '[#!]\|:\d*\%(:in\)\=$'
+ let djump = matchstr(file,'!.*\|#\zs.*\|:\zs\d*\ze\%(:in\)\=$')
+ let file = s:sub(file,'[#!].*|:\d*%(:in)=$','')
+ else
+ let djump = ''
+ endif
+ if file == ''
+ let testcmd = "edit"
+ elseif isdirectory(rails#app().path(file))
+ let arg = file == "." ? rails#app().path() : rails#app().path(file)
+ let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').s:escarg(arg)
+ exe testcmd
+ return
+ elseif rails#app().path() =~ '://' || cmd =~ 'edit' || cmd =~ 'split'
+ if file !~ '^/' && file !~ '^\w:' && file !~ '://'
+ let file = s:escarg(rails#app().path(file))
+ endif
+ let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').file
+ else
+ let testcmd = cmd.' '.(a:0 ? a:1 . ' ' : '').file
+ endif
+ if s:try(testcmd)
+ call s:djump(djump)
+ endif
+endfunction
+
+function! s:edit(cmd,file,...)
+ let cmd = s:editcmdfor(a:cmd)
+ let cmd .= ' '.(a:0 ? a:1 . ' ' : '')
+ let file = a:file
+ if file !~ '^/' && file !~ '^\w:' && file !~ '://'
+ exe cmd."`=fnamemodify(rails#app().path(file),':.')`"
+ else
+ exe cmd.file
+ endif
+endfunction
+
+function! s:Alternate(cmd,line1,line2,count,...)
+ if a:0
+ if a:count && a:cmd !~# 'D'
+ return call('s:Find',[1,a:line1.a:cmd]+a:000)
+ elseif a:count
+ return call('s:Edit',[1,a:line1.a:cmd]+a:000)
+ else
+ return call('s:Edit',[1,a:cmd]+a:000)
+ endif
+ else
+ let file = s:getopt(a:count ? 'related' : 'alternate', 'bl')
+ if file == ''
+ let file = rails#buffer().related(a:count)
+ endif
+ if file != ''
+ call s:findedit(a:cmd,file)
+ else
+ call s:warn("No alternate file is defined")
+ endif
+ endif
+endfunction
+
+function! s:Related(cmd,line1,line2,count,...)
+ if a:count == 0 && a:0 == 0
+ return s:Alternate(a:cmd,a:line1,a:line1,a:line1)
+ else
+ return call('s:Alternate',[a:cmd,a:line1,a:line2,a:count]+a:000)
+ endif
+endfunction
+
+function! s:Complete_related(A,L,P)
+ if a:L =~# '^[[:alpha:]]'
+ return s:Complete_edit(a:A,a:L,a:P)
+ else
+ return s:Complete_find(a:A,a:L,a:P)
+ endif
+endfunction
+
+function! s:readable_related(...) dict abort
+ let f = self.name()
+ if a:0 && a:1
+ let lastmethod = self.last_method(a:1)
+ if self.type_name('controller','mailer') && lastmethod != ""
+ let root = s:sub(s:sub(s:sub(f,'/application%(_controller)=\.rb$','/shared_controller.rb'),'/%(controllers|models|mailers)/','/views/'),'%(_controller)=\.rb$','/'.lastmethod)
+ let format = self.last_format(a:1)
+ if format == ''
+ let format = self.type_name('mailer') ? 'text' : 'html'
+ endif
+ if glob(self.app().path().'/'.root.'.'.format.'.*[^~]') != ''
+ return root . '.' . format
+ else
+ return root
+ endif
+ elseif f =~ '\<config/environments/'
+ return "config/database.yml#". fnamemodify(f,':t:r')
+ elseif f =~ '\<config/database\.yml$'
+ if lastmethod != ""
+ return "config/environments/".lastmethod.".rb"
+ else
+ return "config/application.rb\nconfig/environment.rb"
+ endif
+ elseif f =~ '\<config/routes\.rb$' | return "config/database.yml"
+ elseif f =~ '\<config/\%(application\|environment\)\.rb$'
+ return "config/routes.rb"
+ elseif self.type_name('view-layout')
+ return s:sub(s:sub(f,'/views/','/controllers/'),'/layouts/(\k+)\..*$','/\1_controller.rb')
+ elseif self.type_name('view')
+ let controller = s:sub(s:sub(f,'/views/','/controllers/'),'/(\k+%(\.\k+)=)\..*$','_controller.rb#\1')
+ let controller2 = s:sub(s:sub(f,'/views/','/controllers/'),'/(\k+%(\.\k+)=)\..*$','.rb#\1')
+ let mailer = s:sub(s:sub(f,'/views/','/mailers/'),'/(\k+%(\.\k+)=)\..*$','.rb#\1')
+ let model = s:sub(s:sub(f,'/views/','/models/'),'/(\k+)\..*$','.rb#\1')
+ if self.app().has_file(s:sub(controller,'#.{-}$',''))
+ return controller
+ elseif self.app().has_file(s:sub(controller2,'#.{-}$',''))
+ return controller2
+ elseif self.app().has_file(s:sub(mailer,'#.{-}$',''))
+ return mailer
+ elseif self.app().has_file(s:sub(model,'#.{-}$','')) || model =~ '_mailer\.rb#'
+ return model
+ else
+ return controller
+ endif
+ elseif self.type_name('controller')
+ return s:sub(s:sub(f,'/controllers/','/helpers/'),'%(_controller)=\.rb$','_helper.rb')
+ " elseif self.type_name('helper')
+ " return s:findlayout(s:controller())
+ elseif self.type_name('model-arb')
+ let table_name = matchstr(join(self.getline(1,50),"\n"),'\n\s*set_table_name\s*[:"'']\zs\w\+')
+ if table_name == ''
+ let table_name = rails#pluralize(s:gsub(s:sub(fnamemodify(f,':r'),'.{-}<app/models/',''),'/','_'))
+ endif
+ return self.app().migration('0#'.table_name)
+ elseif self.type_name('model-aro')
+ return s:sub(f,'_observer\.rb$','.rb')
+ elseif self.type_name('db-schema')
+ return self.app().migration(1)
+ endif
+ endif
+ if f =~ '\<config/environments/'
+ return "config/application.rb\nconfig/environment.rb"
+ elseif f == 'README'
+ return "config/database.yml"
+ elseif f =~ '\<config/database\.yml$' | return "config/routes.rb"
+ elseif f =~ '\<config/routes\.rb$'
+ return "config/application.rb\nconfig/environment.rb"
+ elseif f =~ '\<config/\%(application\|environment\)\.rb$'
+ return "config/database.yml"
+ elseif f ==# 'Gemfile'
+ return 'Gemfile.lock'
+ elseif f ==# 'Gemfile.lock'
+ return 'Gemfile'
+ elseif f =~ '\<db/migrate/'
+ let migrations = sort(self.app().relglob('db/migrate/','*','.rb'))
+ let me = matchstr(f,'\<db/migrate/\zs.*\ze\.rb$')
+ if !exists('l:lastmethod') || lastmethod == 'down'
+ let candidates = reverse(filter(copy(migrations),'v:val < me'))
+ let migration = "db/migrate/".get(candidates,0,migrations[-1]).".rb"
+ else
+ let candidates = filter(copy(migrations),'v:val > me')
+ let migration = "db/migrate/".get(candidates,0,migrations[0]).".rb"
+ endif
+ return migration . (exists('l:lastmethod') && lastmethod != '' ? '#'.lastmethod : '')
+ elseif f =~ '\<application\.js$'
+ return "app/helpers/application_helper.rb"
+ elseif self.type_name('javascript')
+ return "public/javascripts/application.js"
+ elseif self.type_name('db/schema')
+ return self.app().migration('')
+ elseif self.type_name('view')
+ let spec1 = fnamemodify(f,':s?\<app/?spec/?')."_spec.rb"
+ let spec2 = fnamemodify(f,':r:s?\<app/?spec/?')."_spec.rb"
+ let spec3 = fnamemodify(f,':r:r:s?\<app/?spec/?')."_spec.rb"
+ if self.app().has_file(spec1)
+ return spec1
+ elseif self.app().has_file(spec2)
+ return spec2
+ elseif self.app().has_file(spec3)
+ return spec3
+ elseif self.app().has('spec')
+ return spec2
+ else
+ if self.type_name('view-layout')
+ let dest = fnamemodify(f,':r:s?/layouts\>??').'/layout.'.fnamemodify(f,':e')
+ else
+ let dest = f
+ endif
+ return s:sub(s:sub(dest,'<app/views/','test/functional/'),'/[^/]*$','_controller_test.rb')
+ endif
+ elseif self.type_name('controller-api')
+ let api = s:sub(s:sub(f,'/controllers/','/apis/'),'_controller\.rb$','_api.rb')
+ return api
+ elseif self.type_name('api')
+ return s:sub(s:sub(f,'/apis/','/controllers/'),'_api\.rb$','_controller.rb')
+ elseif self.type_name('fixtures') && f =~ '\<spec/'
+ let file = rails#singularize(fnamemodify(f,":t:r")).'_spec.rb'
+ return file
+ elseif self.type_name('fixtures')
+ let file = rails#singularize(fnamemodify(f,":t:r")).'_test.rb'
+ return file
+ elseif f == ''
+ call s:warn("No filename present")
+ elseif f =~ '\<test/unit/routing_test\.rb$'
+ return 'config/routes.rb'
+ elseif self.type_name('spec-view')
+ return s:sub(s:sub(f,'<spec/','app/'),'_spec\.rb$','')
+ elseif fnamemodify(f,":e") == "rb"
+ let file = fnamemodify(f,":r")
+ if file =~ '_\%(test\|spec\)$'
+ let file = s:sub(file,'_%(test|spec)$','.rb')
+ else
+ let file .= '_test.rb'
+ endif
+ if self.type_name('helper')
+ return s:sub(file,'<app/helpers/','test/unit/helpers/')."\n".s:sub(s:sub(file,'_test\.rb$','_spec.rb'),'<app/helpers/','spec/helpers/')
+ elseif self.type_name('model')
+ return s:sub(file,'<app/models/','test/unit/')."\n".s:sub(s:sub(file,'_test\.rb$','_spec.rb'),'<app/models/','spec/models/')
+ elseif self.type_name('controller')
+ return s:sub(file,'<app/controllers/','test/functional/')."\n".s:sub(s:sub(file,'_test\.rb$','_spec.rb'),'app/controllers/','spec/controllers/')
+ elseif self.type_name('mailer')
+ return s:sub(file,'<app/m%(ailer|odel)s/','test/unit/')."\n".s:sub(s:sub(file,'_test\.rb$','_spec.rb'),'<app/','spec/')
+ elseif self.type_name('test-unit')
+ return s:sub(s:sub(file,'test/unit/helpers/','app/helpers/'),'test/unit/','app/models/')."\n".s:sub(file,'test/unit/','lib/')
+ elseif self.type_name('test-functional')
+ if file =~ '_api\.rb'
+ return s:sub(file,'test/functional/','app/apis/')
+ elseif file =~ '_controller\.rb'
+ return s:sub(file,'test/functional/','app/controllers/')
+ else
+ return s:sub(file,'test/functional/','')
+ endif
+ elseif self.type_name('spec-lib')
+ return s:sub(file,'<spec/','')
+ elseif self.type_name('lib')
+ return s:sub(f, '<lib/(.*)\.rb$', 'test/unit/\1_test.rb')."\n".s:sub(f, '<lib/(.*)\.rb$', 'spec/lib/\1_spec.rb')
+ elseif self.type_name('spec')
+ return s:sub(file,'<spec/','app/')
+ elseif file =~ '\<vendor/.*/lib/'
+ return s:sub(file,'<vendor/.{-}/\zslib/','test/')
+ elseif file =~ '\<vendor/.*/test/'
+ return s:sub(file,'<vendor/.{-}/\zstest/','lib/')
+ else
+ return fnamemodify(file,':t')."\n".s:sub(s:sub(f,'\.rb$','_spec.rb'),'^app/','spec/')
+ endif
+ else
+ return ""
+ endif
+endfunction
+
+call s:add_methods('readable',['related'])
+
+" }}}1
+" Partial Extraction {{{1
+
+function! s:Extract(bang,...) range abort
+ if a:0 == 0 || a:0 > 1
+ return s:error("Incorrect number of arguments")
+ endif
+ if a:1 =~ '[^a-z0-9_/.]'
+ return s:error("Invalid partial name")
+ endif
+ let rails_root = rails#app().path()
+ let ext = expand("%:e")
+ let file = s:sub(a:1,'%(/|^)\zs_\ze[^/]*$','')
+ let first = a:firstline
+ let last = a:lastline
+ let range = first.",".last
+ if rails#buffer().type_name('view-layout')
+ if RailsFilePath() =~ '\<app/views/layouts/application\>'
+ let curdir = 'app/views/shared'
+ if file !~ '/'
+ let file = "shared/" .file
+ endif
+ else
+ let curdir = s:sub(RailsFilePath(),'.*<app/views/layouts/(.*)%(\.\w*)$','app/views/\1')
+ endif
+ else
+ let curdir = fnamemodify(RailsFilePath(),':h')
+ endif
+ let curdir = rails_root."/".curdir
+ let dir = fnamemodify(file,":h")
+ let fname = fnamemodify(file,":t")
+ if fnamemodify(fname,":e") == ""
+ let name = fname
+ let fname .= ".".matchstr(expand("%:t"),'\.\zs.*')
+ elseif fnamemodify(fname,":e") !~ '^'.s:viewspattern().'$'
+ let name = fnamemodify(fname,":r")
+ let fname .= ".".ext
+ else
+ let name = fnamemodify(fname,":r:r")
+ endif
+ let var = "@".name
+ let collection = ""
+ if dir =~ '^/'
+ let out = (rails_root).dir."/_".fname
+ elseif dir == "" || dir == "."
+ let out = (curdir)."/_".fname
+ elseif isdirectory(curdir."/".dir)
+ let out = (curdir)."/".dir."/_".fname
+ else
+ let out = (rails_root)."/app/views/".dir."/_".fname
+ endif
+ if filereadable(out) && !a:bang
+ return s:error('E13: File exists (add ! to override)')
+ endif
+ if !isdirectory(fnamemodify(out,':h'))
+ if a:bang
+ call mkdir(fnamemodify(out,':h'),'p')
+ else
+ return s:error('No such directory')
+ endif
+ endif
+ " No tabs, they'll just complicate things
+ if ext =~? '^\%(rhtml\|erb\|dryml\)$'
+ let erub1 = '\<\%\s*'
+ let erub2 = '\s*-=\%\>'
+ else
+ let erub1 = ''
+ let erub2 = ''
+ endif
+ let spaces = matchstr(getline(first),"^ *")
+ if getline(last+1) =~ '\v^\s*'.erub1.'end'.erub2.'\s*$'
+ let fspaces = matchstr(getline(last+1),"^ *")
+ if getline(first-1) =~ '\v^'.fspaces.erub1.'for\s+(\k+)\s+in\s+([^ %>]+)'.erub2.'\s*$'
+ let collection = s:sub(getline(first-1),'^'.fspaces.erub1.'for\s+(\k+)\s+in\s+([^ >]+)'.erub2.'\s*$','\1>\2')
+ elseif getline(first-1) =~ '\v^'.fspaces.erub1.'([^ %>]+)\.each\s+do\s+\|\s*(\k+)\s*\|'.erub2.'\s*$'
+ let collection = s:sub(getline(first-1),'^'.fspaces.erub1.'([^ %>]+)\.each\s+do\s+\|\s*(\k+)\s*\|'.erub2.'\s*$','\2>\1')
+ endif
+ if collection != ''
+ let var = matchstr(collection,'^\k\+')
+ let collection = s:sub(collection,'^\k+\>','')
+ let first -= 1
+ let last += 1
+ endif
+ else
+ let fspaces = spaces
+ endif
+ let renderstr = "render :partial => '".fnamemodify(file,":r:r")."'"
+ if collection != ""
+ let renderstr .= ", :collection => ".collection
+ elseif "@".name != var
+ let renderstr .= ", :object => ".var
+ endif
+ if ext =~? '^\%(rhtml\|erb\|dryml\)$'
+ let renderstr = "<%= ".renderstr." %>"
+ elseif ext == "rxml" || ext == "builder"
+ let renderstr = "xml << ".s:sub(renderstr,"render ","render(").")"
+ elseif ext == "rjs"
+ let renderstr = "page << ".s:sub(renderstr,"render ","render(").")"
+ elseif ext == "haml"
+ let renderstr = "= ".renderstr
+ elseif ext == "mn"
+ let renderstr = "_".renderstr
+ endif
+ let buf = @@
+ silent exe range."yank"
+ let partial = @@
+ let @@ = buf
+ let old_ai = &ai
+ try
+ let &ai = 0
+ silent exe "norm! :".first.",".last."change\<CR>".fspaces.renderstr."\<CR>.\<CR>"
+ finally
+ let &ai = old_ai
+ endtry
+ if renderstr =~ '<%'
+ norm ^6w
+ else
+ norm ^5w
+ endif
+ let ft = &ft
+ let shortout = fnamemodify(out,':.')
+ silent split `=shortout`
+ silent %delete
+ let &ft = ft
+ let @@ = partial
+ silent put
+ 0delete
+ let @@ = buf
+ if spaces != ""
+ silent! exe '%substitute/^'.spaces.'//'
+ endif
+ silent! exe '%substitute?\%(\w\|[@:"'."'".'-]\)\@<!'.var.'\>?'.name.'?g'
+ 1
+endfunction
+
+" }}}1
+" Migration Inversion {{{1
+
+function! s:mkeep(str)
+ " Things to keep (like comments) from a migration statement
+ return matchstr(a:str,' #[^{].*')
+endfunction
+
+function! s:mextargs(str,num)
+ if a:str =~ '^\s*\w\+\s*('
+ return s:sub(matchstr(a:str,'^\s*\w\+\s*\zs(\%([^,)]\+[,)]\)\{,'.a:num.'\}'),',$',')')
+ else
+ return s:sub(s:sub(matchstr(a:str,'\w\+\>\zs\s*\%([^,){ ]*[, ]*\)\{,'.a:num.'\}'),'[, ]*$',''),'^\s+',' ')
+ endif
+endfunction
+
+function! s:migspc(line)
+ return matchstr(a:line,'^\s*')
+endfunction
+
+function! s:invertrange(beg,end)
+ let str = ""
+ let lnum = a:beg
+ while lnum <= a:end
+ let line = getline(lnum)
+ let add = ""
+ if line == ''
+ let add = ' '
+ elseif line =~ '^\s*\(#[^{].*\)\=$'
+ let add = line
+ elseif line =~ '\<create_table\>'
+ let add = s:migspc(line)."drop_table".s:mextargs(line,1).s:mkeep(line)
+ let lnum = s:endof(lnum)
+ elseif line =~ '\<drop_table\>'
+ let add = s:sub(line,'<drop_table>\s*\(=\s*([^,){ ]*).*','create_table \1 do |t|'."\n".matchstr(line,'^\s*').'end').s:mkeep(line)
+ elseif line =~ '\<add_column\>'
+ let add = s:migspc(line).'remove_column'.s:mextargs(line,2).s:mkeep(line)
+ elseif line =~ '\<remove_column\>'
+ let add = s:sub(line,'<remove_column>','add_column')
+ elseif line =~ '\<add_index\>'
+ let add = s:migspc(line).'remove_index'.s:mextargs(line,1)
+ let mat = matchstr(line,':name\s*=>\s*\zs[^ ,)]*')
+ if mat != ''
+ let add = s:sub(add,'\)=$',', :name => '.mat.'&')
+ else
+ let mat = matchstr(line,'\<add_index\>[^,]*,\s*\zs\%(\[[^]]*\]\|[:"'."'".']\w*["'."'".']\=\)')
+ if mat != ''
+ let add = s:sub(add,'\)=$',', :column => '.mat.'&')
+ endif
+ endif
+ let add .= s:mkeep(line)
+ elseif line =~ '\<remove_index\>'
+ let add = s:sub(s:sub(line,'<remove_index','add_index'),':column\s*\=\>\s*','')
+ elseif line =~ '\<rename_\%(table\|column\|index\)\>'
+ let add = s:sub(line,'<rename_%(table\s*\(=\s*|%(column|index)\s*\(=\s*[^,]*,\s*)\zs([^,]*)(,\s*)([^,]*)','\3\2\1')
+ elseif line =~ '\<change_column\>'
+ let add = s:migspc(line).'change_column'.s:mextargs(line,2).s:mkeep(line)
+ elseif line =~ '\<change_column_default\>'
+ let add = s:migspc(line).'change_column_default'.s:mextargs(line,2).s:mkeep(line)
+ elseif line =~ '\.update_all(\(["'."'".']\).*\1)$' || line =~ '\.update_all \(["'."'".']\).*\1$'
+ " .update_all('a = b') => .update_all('b = a')
+ let pre = matchstr(line,'^.*\.update_all[( ][}'."'".'"]')
+ let post = matchstr(line,'["'."'".'])\=$')
+ let mat = strpart(line,strlen(pre),strlen(line)-strlen(pre)-strlen(post))
+ let mat = s:gsub(','.mat.',','%(,\s*)@<=([^ ,=]{-})(\s*\=\s*)([^,=]{-})%(\s*,)@=','\3\2\1')
+ let add = pre.s:sub(s:sub(mat,'^,',''),',$','').post
+ elseif line =~ '^s\*\%(if\|unless\|while\|until\|for\)\>'
+ let lnum = s:endof(lnum)
+ endif
+ if lnum == 0
+ return -1
+ endif
+ if add == ""
+ let add = s:sub(line,'^\s*\zs.*','raise ActiveRecord::IrreversibleMigration')
+ elseif add == " "
+ let add = ""
+ endif
+ let str = add."\n".str
+ let lnum += 1
+ endwhile
+ let str = s:gsub(str,'(\s*raise ActiveRecord::IrreversibleMigration\n)+','\1')
+ return str
+endfunction
+
+function! s:Invert(bang)
+ let err = "Could not parse method"
+ let src = "up"
+ let dst = "down"
+ let beg = search('\%('.&l:define.'\).*'.src.'\>',"w")
+ let end = s:endof(beg)
+ if beg + 1 == end
+ let src = "down"
+ let dst = "up"
+ let beg = search('\%('.&l:define.'\).*'.src.'\>',"w")
+ let end = s:endof(beg)
+ endif
+ if !beg || !end
+ return s:error(err)
+ endif
+ let str = s:invertrange(beg+1,end-1)
+ if str == -1
+ return s:error(err)
+ endif
+ let beg = search('\%('.&l:define.'\).*'.dst.'\>',"w")
+ let end = s:endof(beg)
+ if !beg || !end
+ return s:error(err)
+ endif
+ if foldclosed(beg) > 0
+ exe beg."foldopen!"
+ endif
+ if beg + 1 < end
+ exe (beg+1).",".(end-1)."delete _"
+ endif
+ if str != ''
+ exe beg.'put =str'
+ exe 1+beg
+ endif
+endfunction
+
+" }}}1
+" Cache {{{1
+
+let s:cache_prototype = {'dict': {}}
+
+function! s:cache_clear(...) dict
+ if a:0 == 0
+ let self.dict = {}
+ elseif has_key(self,'dict') && has_key(self.dict,a:1)
+ unlet! self.dict[a:1]
+ endif
+endfunction
+
+function! rails#cache_clear(...)
+ if exists('b:rails_root')
+ return call(rails#app().cache.clear,a:000,rails#app().cache)
+ endif
+endfunction
+
+function! s:cache_get(...) dict
+ if a:0 == 1
+ return self.dict[a:1]
+ else
+ return self.dict
+ endif
+endfunction
+
+function! s:cache_has(key) dict
+ return has_key(self.dict,a:key)
+endfunction
+
+function! s:cache_needs(key) dict
+ return !has_key(self.dict,a:key)
+endfunction
+
+function! s:cache_set(key,value) dict
+ let self.dict[a:key] = a:value
+endfunction
+
+call s:add_methods('cache', ['clear','needs','has','get','set'])
+
+let s:app_prototype.cache = s:cache_prototype
+
+" }}}1
+" Syntax {{{1
+
+function! s:resetomnicomplete()
+ if exists("+completefunc") && &completefunc == 'syntaxcomplete#Complete'
+ if exists("g:loaded_syntax_completion")
+ " Ugly but necessary, until we have our own completion
+ unlet g:loaded_syntax_completion
+ silent! delfunction syntaxcomplete#Complete
+ endif
+ endif
+endfunction
+
+function! s:helpermethods()
+ return ""
+ \."action_name atom_feed audio_path audio_tag auto_discovery_link_tag "
+ \."button_tag button_to button_to_function "
+ \."cache capture cdata_section check_box check_box_tag collection_select concat content_for content_tag content_tag_for controller controller_name controller_path convert_to_model cookies csrf_meta_tag csrf_meta_tags current_cycle cycle "
+ \."date_select datetime_select debug distance_of_time_in_words distance_of_time_in_words_to_now div_for dom_class dom_id "
+ \."email_field email_field_tag escape_javascript escape_once excerpt "
+ \."favicon_link_tag field_set_tag fields_for file_field file_field_tag flash form_for form_tag "
+ \."grouped_collection_select grouped_options_for_select "
+ \."headers hidden_field hidden_field_tag highlight "
+ \."image_alt image_path image_submit_tag image_tag "
+ \."j javascript_cdata_section javascript_include_tag javascript_path javascript_tag "
+ \."l label label_tag link_to link_to_function link_to_if link_to_unless link_to_unless_current localize logger "
+ \."mail_to "
+ \."number_field number_field_tag number_to_currency number_to_human number_to_human_size number_to_percentage number_to_phone number_with_delimiter number_with_precision "
+ \."option_groups_from_collection_for_select options_for_select options_from_collection_for_select "
+ \."params password_field password_field_tag path_to_audio path_to_image path_to_javascript path_to_stylesheet path_to_video phone_field phone_field_tag pluralize provide "
+ \."radio_button radio_button_tag range_field range_field_tag raw render request request_forgery_protection_token reset_cycle response "
+ \."safe_concat safe_join sanitize sanitize_css search_field search_field_tag select select_date select_datetime select_day select_hour select_minute select_month select_second select_tag select_time select_year session simple_format strip_links strip_tags stylesheet_link_tag stylesheet_path submit_tag "
+ \."t tag telephone_field telephone_field_tag text_area text_area_tag text_field text_field_tag time_ago_in_words time_select time_tag time_zone_options_for_select time_zone_select translate truncate "
+ \."url_field url_field_tag url_for url_options "
+ \."video_path video_tag "
+ \."word_wrap"
+endfunction
+
+function! s:app_user_classes() dict
+ if self.cache.needs("user_classes")
+ let controllers = self.relglob("app/controllers/","**/*",".rb")
+ call map(controllers,'v:val == "application" ? v:val."_controller" : v:val')
+ let classes =
+ \ self.relglob("app/models/","**/*",".rb") +
+ \ controllers +
+ \ self.relglob("app/helpers/","**/*",".rb") +
+ \ self.relglob("lib/","**/*",".rb")
+ call map(classes,'rails#camelize(v:val)')
+ call self.cache.set("user_classes",classes)
+ endif
+ return self.cache.get('user_classes')
+endfunction
+
+function! s:app_user_assertions() dict
+ if self.cache.needs("user_assertions")
+ if self.has_file("test/test_helper.rb")
+ let assertions = map(filter(s:readfile(self.path("test/test_helper.rb")),'v:val =~ "^ def assert_"'),'matchstr(v:val,"^ def \\zsassert_\\w\\+")')
+ else
+ let assertions = []
+ endif
+ call self.cache.set("user_assertions",assertions)
+ endif
+ return self.cache.get('user_assertions')
+endfunction
+
+call s:add_methods('app', ['user_classes','user_assertions'])
+
+function! s:BufSyntax()
+ if (!exists("g:rails_syntax") || g:rails_syntax)
+ let buffer = rails#buffer()
+ let s:javascript_functions = "$ $$ $A $F $H $R $w jQuery"
+ let classes = s:gsub(join(rails#app().user_classes(),' '),'::',' ')
+ if &syntax == 'ruby'
+ if classes != ''
+ exe "syn keyword rubyRailsUserClass ".classes." containedin=rubyClassDeclaration,rubyModuleDeclaration,rubyClass,rubyModule"
+ endif
+ if buffer.type_name() == ''
+ syn keyword rubyRailsMethod params request response session headers cookies flash
+ endif
+ if buffer.type_name('api')
+ syn keyword rubyRailsAPIMethod api_method inflect_names
+ endif
+ if buffer.type_name() ==# 'model' || buffer.type_name('model-arb')
+ syn keyword rubyRailsARMethod default_scope named_scope scope serialize
+ syn keyword rubyRailsARAssociationMethod belongs_to has_one has_many has_and_belongs_to_many composed_of accepts_nested_attributes_for
+ syn keyword rubyRailsARCallbackMethod before_create before_destroy before_save before_update before_validation before_validation_on_create before_validation_on_update
+ syn keyword rubyRailsARCallbackMethod after_create after_destroy after_save after_update after_validation after_validation_on_create after_validation_on_update
+ syn keyword rubyRailsARCallbackMethod around_create around_destroy around_save around_update
+ syn keyword rubyRailsARCallbackMethod after_commit after_find after_initialize after_rollback after_touch
+ syn keyword rubyRailsARClassMethod attr_accessible attr_protected attr_readonly establish_connection set_inheritance_column set_locking_column set_primary_key set_sequence_name set_table_name
+ syn keyword rubyRailsARValidationMethod validate validates validate_on_create validate_on_update validates_acceptance_of validates_associated validates_confirmation_of validates_each validates_exclusion_of validates_format_of validates_inclusion_of validates_length_of validates_numericality_of validates_presence_of validates_size_of validates_uniqueness_of
+ syn keyword rubyRailsMethod logger
+ endif
+ if buffer.type_name('model-aro')
+ syn keyword rubyRailsARMethod observe
+ endif
+ if buffer.type_name('mailer')
+ syn keyword rubyRailsMethod logger url_for polymorphic_path polymorphic_url
+ syn keyword rubyRailsRenderMethod mail render
+ syn keyword rubyRailsControllerMethod attachments default helper helper_attr helper_method
+ endif
+ if buffer.type_name('helper','view')
+ syn keyword rubyRailsViewMethod polymorphic_path polymorphic_url
+ exe "syn keyword rubyRailsHelperMethod ".s:gsub(s:helpermethods(),'<%(content_for|select)\s+','')
+ syn match rubyRailsHelperMethod '\<select\>\%(\s*{\|\s*do\>\|\s*(\=\s*&\)\@!'
+ syn match rubyRailsHelperMethod '\<\%(content_for?\=\|current_page?\)'
+ syn match rubyRailsViewMethod '\.\@<!\<\(h\|html_escape\|u\|url_encode\)\>'
+ if buffer.type_name('view-partial')
+ syn keyword rubyRailsMethod local_assigns
+ endif
+ elseif buffer.type_name('controller')
+ syn keyword rubyRailsMethod params request response session headers cookies flash
+ syn keyword rubyRailsRenderMethod render
+ syn keyword rubyRailsMethod logger polymorphic_path polymorphic_url
+ syn keyword rubyRailsControllerMethod helper helper_attr helper_method filter layout url_for serialize exempt_from_layout filter_parameter_logging hide_action cache_sweeper protect_from_forgery caches_page cache_page caches_action expire_page expire_action rescue_from
+ syn keyword rubyRailsRenderMethod head redirect_to render_to_string respond_with
+ syn match rubyRailsRenderMethod '\<respond_to\>?\@!'
+ syn keyword rubyRailsFilterMethod before_filter append_before_filter prepend_before_filter after_filter append_after_filter prepend_after_filter around_filter append_around_filter prepend_around_filter skip_before_filter skip_after_filter
+ syn keyword rubyRailsFilterMethod verify
+ endif
+ if buffer.type_name('db-migration','db-schema')
+ syn keyword rubyRailsMigrationMethod create_table change_table drop_table rename_table add_column rename_column change_column change_column_default remove_column add_index remove_index rename_index execute
+ endif
+ if buffer.type_name('test')
+ if !empty(rails#app().user_assertions())
+ exe "syn keyword rubyRailsUserMethod ".join(rails#app().user_assertions())
+ endif
+ syn keyword rubyRailsTestMethod add_assertion assert assert_block assert_equal assert_in_delta assert_instance_of assert_kind_of assert_match assert_nil assert_no_match assert_not_equal assert_not_nil assert_not_same assert_nothing_raised assert_nothing_thrown assert_operator assert_raise assert_respond_to assert_same assert_send assert_throws assert_recognizes assert_generates assert_routing flunk fixtures fixture_path use_transactional_fixtures use_instantiated_fixtures assert_difference assert_no_difference assert_valid
+ syn keyword rubyRailsTestMethod test setup teardown
+ if !buffer.type_name('test-unit')
+ syn match rubyRailsTestControllerMethod '\.\@<!\<\%(get\|post\|put\|delete\|head\|process\|assigns\)\>'
+ syn keyword rubyRailsTestControllerMethod get_via_redirect post_via_redirect put_via_redirect delete_via_redirect request_via_redirect
+ syn keyword rubyRailsTestControllerMethod assert_response assert_redirected_to assert_template assert_recognizes assert_generates assert_routing assert_dom_equal assert_dom_not_equal assert_select assert_select_rjs assert_select_encoded assert_select_email assert_tag assert_no_tag
+ endif
+ elseif buffer.type_name('spec')
+ syn keyword rubyRailsTestMethod describe context it its specify shared_examples_for it_should_behave_like before after around subject fixtures controller_name helper_name
+ syn match rubyRailsTestMethod '\<let\>!\='
+ syn keyword rubyRailsTestMethod violated pending expect double mock mock_model stub_model
+ syn match rubyRailsTestMethod '\.\@<!\<stub\>!\@!'
+ if !buffer.type_name('spec-model')
+ syn match rubyRailsTestControllerMethod '\.\@<!\<\%(get\|post\|put\|delete\|head\|process\|assigns\)\>'
+ syn keyword rubyRailsTestControllerMethod integrate_views
+ syn keyword rubyRailsMethod params request response session flash
+ syn keyword rubyRailsMethod polymorphic_path polymorphic_url
+ endif
+ endif
+ if buffer.type_name('task')
+ syn match rubyRailsRakeMethod '^\s*\zs\%(task\|file\|namespace\|desc\|before\|after\|on\)\>\%(\s*=\)\@!'
+ endif
+ if buffer.type_name('model-awss')
+ syn keyword rubyRailsMethod member
+ endif
+ if buffer.type_name('config-routes')
+ syn match rubyRailsMethod '\.\zs\%(connect\|named_route\)\>'
+ syn keyword rubyRailsMethod match get put post delete redirect root resource resources collection member nested scope namespace controller constraints
+ endif
+ syn keyword rubyRailsMethod debugger
+ syn keyword rubyRailsMethod alias_attribute alias_method_chain attr_accessor_with_default attr_internal attr_internal_accessor attr_internal_reader attr_internal_writer delegate mattr_accessor mattr_reader mattr_writer superclass_delegating_accessor superclass_delegating_reader superclass_delegating_writer
+ syn keyword rubyRailsMethod cattr_accessor cattr_reader cattr_writer class_inheritable_accessor class_inheritable_array class_inheritable_array_writer class_inheritable_hash class_inheritable_hash_writer class_inheritable_option class_inheritable_reader class_inheritable_writer inheritable_attributes read_inheritable_attribute reset_inheritable_attributes write_inheritable_array write_inheritable_attribute write_inheritable_hash
+ syn keyword rubyRailsInclude require_dependency
+
+ syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:order\s*=>\s*\)\@<="+ skip=+\\\\\|\\"+ end=+"+ contains=@rubyStringSpecial,railsOrderSpecial
+ syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:order\s*=>\s*\)\@<='+ skip=+\\\\\|\\'+ end=+'+ contains=@rubyStringSpecial,railsOrderSpecial
+ syn match railsOrderSpecial +\c\<\%(DE\|A\)SC\>+ contained
+ syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:conditions\s*=>\s*\[\s*\)\@<="+ skip=+\\\\\|\\"+ end=+"+ contains=@rubyStringSpecial,railsConditionsSpecial
+ syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:conditions\s*=>\s*\[\s*\)\@<='+ skip=+\\\\\|\\'+ end=+'+ contains=@rubyStringSpecial,railsConditionsSpecial
+ syn match railsConditionsSpecial +?\|:\h\w*+ contained
+ syn cluster rubyNotTop add=railsOrderSpecial,railsConditionsSpecial
+
+ " XHTML highlighting inside %Q<>
+ unlet! b:current_syntax
+ let removenorend = !exists("g:html_no_rendering")
+ let g:html_no_rendering = 1
+ syn include @htmlTop syntax/xhtml.vim
+ if removenorend
+ unlet! g:html_no_rendering
+ endif
+ let b:current_syntax = "ruby"
+ " Restore syn sync, as best we can
+ if !exists("g:ruby_minlines")
+ let g:ruby_minlines = 50
+ endif
+ syn sync fromstart
+ exe "syn sync minlines=" . g:ruby_minlines
+ syn case match
+ syn region rubyString matchgroup=rubyStringDelimiter start=+%Q\=<+ end=+>+ contains=@htmlTop,@rubyStringSpecial
+ syn cluster htmlArgCluster add=@rubyStringSpecial
+ syn cluster htmlPreProc add=@rubyStringSpecial
+
+ elseif &syntax =~# '^eruby\>' || &syntax == 'haml'
+ syn case match
+ if classes != ''
+ exe 'syn keyword '.&syntax.'RailsUserClass '.classes.' contained containedin=@'.&syntax.'RailsRegions'
+ endif
+ if &syntax == 'haml'
+ exe 'syn cluster hamlRailsRegions contains=hamlRubyCodeIncluded,hamlRubyCode,hamlRubyHash,@hamlEmbeddedRuby,rubyInterpolation'
+ else
+ exe 'syn cluster erubyRailsRegions contains=erubyOneLiner,erubyBlock,erubyExpression,rubyInterpolation'
+ endif
+ exe 'syn keyword '.&syntax.'RailsHelperMethod '.s:gsub(s:helpermethods(),'<%(content_for|select)\s+','').' contained containedin=@'.&syntax.'RailsRegions'
+ exe 'syn match '.&syntax.'RailsHelperMethod "\<select\>\%(\s*{\|\s*do\>\|\s*(\=\s*&\)\@!" contained containedin=@'.&syntax.'RailsRegions'
+ exe 'syn match '.&syntax.'RailsHelperMethod "\<\%(content_for?\=\|current_page?\)" contained containedin=@'.&syntax.'RailsRegions'
+ exe 'syn keyword '.&syntax.'RailsMethod debugger polymorphic_path polymorphic_url contained containedin=@'.&syntax.'RailsRegions'
+ exe 'syn match '.&syntax.'RailsViewMethod "\.\@<!\<\(h\|html_escape\|u\|url_encode\)\>" contained containedin=@'.&syntax.'RailsRegions'
+ if buffer.type_name('view-partial')
+ exe 'syn keyword '.&syntax.'RailsMethod local_assigns contained containedin=@'.&syntax.'RailsRegions'
+ endif
+ exe 'syn keyword '.&syntax.'RailsRenderMethod render contained containedin=@'.&syntax.'RailsRegions'
+ exe 'syn case match'
+ set isk+=$
+ exe 'syn keyword javascriptRailsFunction contained '.s:javascript_functions
+ exe 'syn cluster htmlJavaScript add=javascriptRailsFunction'
+ elseif &syntax == "yaml"
+ syn case match
+ " Modeled after syntax/eruby.vim
+ unlet! b:current_syntax
+ let g:main_syntax = 'eruby'
+ syn include @rubyTop syntax/ruby.vim
+ unlet g:main_syntax
+ syn cluster yamlRailsRegions contains=yamlRailsOneLiner,yamlRailsBlock,yamlRailsExpression
+ syn region yamlRailsOneLiner matchgroup=yamlRailsDelimiter start="^%%\@!" end="$" contains=@rubyRailsTop containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment keepend oneline
+ syn region yamlRailsBlock matchgroup=yamlRailsDelimiter start="<%%\@!" end="%>" contains=@rubyTop containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment
+ syn region yamlRailsExpression matchgroup=yamlRailsDelimiter start="<%=" end="%>" contains=@rubyTop containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment
+ syn region yamlRailsComment matchgroup=yamlRailsDelimiter start="<%#" end="%>" contains=rubyTodo,@Spell containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment keepend
+ syn match yamlRailsMethod '\.\@<!\<\(h\|html_escape\|u\|url_encode\)\>' contained containedin=@yamlRailsRegions
+ if classes != ''
+ exe "syn keyword yamlRailsUserClass ".classes." contained containedin=@yamlRailsRegions"
+ endif
+ let b:current_syntax = "yaml"
+ elseif &syntax == "html"
+ syn case match
+ set isk+=$
+ exe "syn keyword javascriptRailsFunction contained ".s:javascript_functions
+ syn cluster htmlJavaScript add=javascriptRailsFunction
+ elseif &syntax == "javascript" || &syntax == "coffee"
+ " The syntax file included with Vim incorrectly sets syn case ignore.
+ syn case match
+ set isk+=$
+ exe "syn keyword javascriptRailsFunction ".s:javascript_functions
+
+ endif
+ endif
+ call s:HiDefaults()
+endfunction
+
+function! s:HiDefaults()
+ hi def link rubyRailsAPIMethod rubyRailsMethod
+ hi def link rubyRailsARAssociationMethod rubyRailsARMethod
+ hi def link rubyRailsARCallbackMethod rubyRailsARMethod
+ hi def link rubyRailsARClassMethod rubyRailsARMethod
+ hi def link rubyRailsARValidationMethod rubyRailsARMethod
+ hi def link rubyRailsARMethod rubyRailsMethod
+ hi def link rubyRailsRenderMethod rubyRailsMethod
+ hi def link rubyRailsHelperMethod rubyRailsMethod
+ hi def link rubyRailsViewMethod rubyRailsMethod
+ hi def link rubyRailsMigrationMethod rubyRailsMethod
+ hi def link rubyRailsControllerMethod rubyRailsMethod
+ hi def link rubyRailsFilterMethod rubyRailsMethod
+ hi def link rubyRailsTestControllerMethod rubyRailsTestMethod
+ hi def link rubyRailsTestMethod rubyRailsMethod
+ hi def link rubyRailsRakeMethod rubyRailsMethod
+ hi def link rubyRailsMethod railsMethod
+ hi def link rubyRailsInclude rubyInclude
+ hi def link rubyRailsUserClass railsUserClass
+ hi def link rubyRailsUserMethod railsUserMethod
+ hi def link erubyRailsHelperMethod erubyRailsMethod
+ hi def link erubyRailsViewMethod erubyRailsMethod
+ hi def link erubyRailsRenderMethod erubyRailsMethod
+ hi def link erubyRailsMethod railsMethod
+ hi def link erubyRailsUserMethod railsUserMethod
+ hi def link erubyRailsUserClass railsUserClass
+ hi def link hamlRailsHelperMethod hamlRailsMethod
+ hi def link hamlRailsViewMethod hamlRailsMethod
+ hi def link hamlRailsRenderMethod hamlRailsMethod
+ hi def link hamlRailsMethod railsMethod
+ hi def link hamlRailsUserMethod railsUserMethod
+ hi def link hamlRailsUserClass railsUserClass
+ hi def link railsUserMethod railsMethod
+ hi def link yamlRailsDelimiter Delimiter
+ hi def link yamlRailsMethod railsMethod
+ hi def link yamlRailsComment Comment
+ hi def link yamlRailsUserClass railsUserClass
+ hi def link yamlRailsUserMethod railsUserMethod
+ hi def link javascriptRailsFunction railsMethod
+ hi def link railsUserClass railsClass
+ hi def link railsMethod Function
+ hi def link railsClass Type
+ hi def link railsOrderSpecial railsStringSpecial
+ hi def link railsConditionsSpecial railsStringSpecial
+ hi def link railsStringSpecial Identifier
+endfunction
+
+function! rails#log_syntax()
+ if has('conceal')
+ syn match railslogEscape '\e\[[0-9;]*m' conceal
+ syn match railslogEscapeMN '\e\[[0-9;]*m' conceal nextgroup=railslogModelNum,railslogEscapeMN skipwhite contained
+ syn match railslogEscapeSQL '\e\[[0-9;]*m' conceal nextgroup=railslogSQL,railslogEscapeSQL skipwhite contained
+ else
+ syn match railslogEscape '\e\[[0-9;]*m'
+ syn match railslogEscapeMN '\e\[[0-9;]*m' nextgroup=railslogModelNum,railslogEscapeMN skipwhite contained
+ syn match railslogEscapeSQL '\e\[[0-9;]*m' nextgroup=railslogSQL,railslogEscapeSQL skipwhite contained
+ endif
+ syn match railslogRender '\%(^\s*\%(\e\[[0-9;]*m\)\=\)\@<=\%(Processing\|Rendering\|Rendered\|Redirected\|Completed\)\>'
+ syn match railslogComment '^\s*# .*'
+ syn match railslogModel '\%(^\s*\%(\e\[[0-9;]*m\)\=\)\@<=\u\%(\w\|:\)* \%(Load\%( Including Associations\| IDs For Limited Eager Loading\)\=\|Columns\|Count\|Create\|Update\|Destroy\|Delete all\)\>' skipwhite nextgroup=railslogModelNum,railslogEscapeMN
+ syn match railslogModel '\%(^\s*\%(\e\[[0-9;]*m\)\=\)\@<=SQL\>' skipwhite nextgroup=railslogModelNum,railslogEscapeMN
+ syn region railslogModelNum start='(' end=')' contains=railslogNumber contained skipwhite nextgroup=railslogSQL,railslogEscapeSQL
+ syn match railslogSQL '\u[^\e]*' contained
+ " Destroy generates multiline SQL, ugh
+ syn match railslogSQL '\%(^ \%(\e\[[0-9;]*m\)\=\)\@<=\%(FROM\|WHERE\|ON\|AND\|OR\|ORDER\) .*$'
+ syn match railslogNumber '\<\d\+\>%'
+ syn match railslogNumber '[ (]\@<=\<\d\+\.\d\+\>\.\@!'
+ syn region railslogString start='"' skip='\\"' end='"' oneline contained
+ syn region railslogHash start='{' end='}' oneline contains=railslogHash,railslogString
+ syn match railslogIP '\<\d\{1,3\}\%(\.\d\{1,3}\)\{3\}\>'
+ syn match railslogTimestamp '\<\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\>'
+ syn match railslogSessionID '\<\x\{32\}\>'
+ syn match railslogIdentifier '^\s*\%(Session ID\|Parameters\)\ze:'
+ syn match railslogSuccess '\<2\d\d \u[A-Za-z0-9 ]*\>'
+ syn match railslogRedirect '\<3\d\d \u[A-Za-z0-9 ]*\>'
+ syn match railslogError '\<[45]\d\d \u[A-Za-z0-9 ]*\>'
+ syn match railslogError '^DEPRECATION WARNING\>'
+ syn keyword railslogHTTP OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT
+ syn region railslogStackTrace start=":\d\+:in `\w\+'$" end="^\s*$" keepend fold
+ hi def link railslogEscapeMN railslogEscape
+ hi def link railslogEscapeSQL railslogEscape
+ hi def link railslogEscape Ignore
+ hi def link railslogComment Comment
+ hi def link railslogRender Keyword
+ hi def link railslogModel Type
+ hi def link railslogSQL PreProc
+ hi def link railslogNumber Number
+ hi def link railslogString String
+ hi def link railslogSessionID Constant
+ hi def link railslogIdentifier Identifier
+ hi def link railslogRedirect railslogSuccess
+ hi def link railslogSuccess Special
+ hi def link railslogError Error
+ hi def link railslogHTTP Special
+endfunction
+
+" }}}1
+" Mappings {{{1
+
+function! s:BufMappings()
+ nnoremap <buffer> <silent> <Plug>RailsFind :<C-U>call <SID>Find(v:count1,'E')<CR>
+ nnoremap <buffer> <silent> <Plug>RailsSplitFind :<C-U>call <SID>Find(v:count1,'S')<CR>
+ nnoremap <buffer> <silent> <Plug>RailsVSplitFind :<C-U>call <SID>Find(v:count1,'V')<CR>
+ nnoremap <buffer> <silent> <Plug>RailsTabFind :<C-U>call <SID>Find(v:count1,'T')<CR>
+ if g:rails_mappings
+ if !hasmapto("<Plug>RailsFind")
+ nmap <buffer> gf <Plug>RailsFind
+ endif
+ if !hasmapto("<Plug>RailsSplitFind")
+ nmap <buffer> <C-W>f <Plug>RailsSplitFind
+ endif
+ if !hasmapto("<Plug>RailsTabFind")
+ nmap <buffer> <C-W>gf <Plug>RailsTabFind
+ endif
+ if exists("$CREAM")
+ imap <buffer> <C-CR> <C-O><Plug>RailsFind
+ imap <buffer> <M-[> <C-O><Plug>RailsAlternate
+ imap <buffer> <M-]> <C-O><Plug>RailsRelated
+ endif
+ endif
+ " SelectBuf you're a dirty hack
+ let v:errmsg = ""
+endfunction
+
+" }}}1
+" Database {{{1
+
+function! s:extractdbvar(str,arg)
+ return matchstr("\n".a:str."\n",'\n'.a:arg.'=\zs.\{-\}\ze\n')
+endfunction
+
+function! s:app_dbext_settings(environment) dict
+ if self.cache.needs('dbext_settings')
+ call self.cache.set('dbext_settings',{})
+ endif
+ let cache = self.cache.get('dbext_settings')
+ if !has_key(cache,a:environment)
+ let dict = {}
+ if self.has_file("config/database.yml")
+ let cmdb = 'require %{yaml}; File.open(%q{'.self.path().'/config/database.yml}) {|f| y = YAML::load(f); e = y[%{'
+ let cmde = '}]; i=0; e=y[e] while e.respond_to?(:to_str) && (i+=1)<16; e.each{|k,v|puts k.to_s+%{=}+v.to_s}}'
+ let out = self.lightweight_ruby_eval(cmdb.a:environment.cmde)
+ let adapter = s:extractdbvar(out,'adapter')
+ let adapter = get({'mysql2': 'mysql', 'postgresql': 'pgsql', 'sqlite3': 'sqlite', 'sqlserver': 'sqlsrv', 'sybase': 'asa', 'oracle': 'ora'},adapter,adapter)
+ let dict['type'] = toupper(adapter)
+ let dict['user'] = s:extractdbvar(out,'username')
+ let dict['passwd'] = s:extractdbvar(out,'password')
+ if dict['passwd'] == '' && adapter == 'mysql'
+ " Hack to override password from .my.cnf
+ let dict['extra'] = ' --password='
+ else
+ let dict['extra'] = ''
+ endif
+ let dict['dbname'] = s:extractdbvar(out,'database')
+ if dict['dbname'] == ''
+ let dict['dbname'] = s:extractdbvar(out,'dbfile')
+ endif
+ if dict['dbname'] != '' && dict['dbname'] !~ '^:' && adapter =~? '^sqlite'
+ let dict['dbname'] = self.path(dict['dbname'])
+ endif
+ let dict['profile'] = ''
+ if adapter == 'ora'
+ let dict['srvname'] = s:extractdbvar(out,'database')
+ else
+ let dict['srvname'] = s:extractdbvar(out,'host')
+ endif
+ let dict['host'] = s:extractdbvar(out,'host')
+ let dict['port'] = s:extractdbvar(out,'port')
+ let dict['dsnname'] = s:extractdbvar(out,'dsn')
+ if dict['host'] =~? '^\cDBI:'
+ if dict['host'] =~? '\c\<Trusted[_ ]Connection\s*=\s*yes\>'
+ let dict['integratedlogin'] = 1
+ endif
+ let dict['host'] = matchstr(dict['host'],'\c\<\%(Server\|Data Source\)\s*=\s*\zs[^;]*')
+ endif
+ call filter(dict,'v:val != ""')
+ endif
+ let cache[a:environment] = dict
+ endif
+ return cache[a:environment]
+endfunction
+
+function! s:BufDatabase(...)
+ if exists("s:lock_database") || !exists('g:loaded_dbext') || !exists('b:rails_root')
+ return
+ endif
+ let self = rails#app()
+ let s:lock_database = 1
+ if (a:0 && a:1 > 1)
+ call self.cache.clear('dbext_settings')
+ endif
+ if (a:0 > 1 && a:2 != '')
+ let env = a:2
+ else
+ let env = s:environment()
+ endif
+ if (!self.cache.has('dbext_settings') || !has_key(self.cache.get('dbext_settings'),env)) && (a:0 ? a:1 : 0) <= 0
+ unlet! s:lock_database
+ return
+ endif
+ let dict = self.dbext_settings(env)
+ for key in ['type', 'profile', 'bin', 'user', 'passwd', 'dbname', 'srvname', 'host', 'port', 'dsnname', 'extra', 'integratedlogin']
+ let b:dbext_{key} = get(dict,key,'')
+ endfor
+ if b:dbext_type == 'PGSQL'
+ let $PGPASSWORD = b:dbext_passwd
+ elseif exists('$PGPASSWORD')
+ let $PGPASSWORD = ''
+ endif
+ unlet! s:lock_database
+endfunction
+
+call s:add_methods('app', ['dbext_settings'])
+
+" }}}1
+" Abbreviations {{{1
+
+function! s:selectiveexpand(pat,good,default,...)
+ if a:0 > 0
+ let nd = a:1
+ else
+ let nd = ""
+ endif
+ let c = nr2char(getchar(0))
+ let good = a:good
+ if c == "" " ^]
+ return s:sub(good.(a:0 ? " ".a:1 : ''),'\s+$','')
+ elseif c == "\t"
+ return good.(a:0 ? " ".a:1 : '')
+ elseif c =~ a:pat
+ return good.c.(a:0 ? a:1 : '')
+ else
+ return a:default.c
+ endif
+endfunction
+
+function! s:TheCWord()
+ let l = s:linepeak()
+ if l =~ '\<\%(find\|first\|last\|all\|paginate\)\>'
+ return s:selectiveexpand('..',':conditions => ',':c')
+ elseif l =~ '\<render\s*(\=\s*:partial\s*=>\s*'
+ return s:selectiveexpand('..',':collection => ',':c')
+ elseif l =~ '\<\%(url_for\|link_to\|form_tag\)\>' || l =~ ':url\s*=>\s*{\s*'
+ return s:selectiveexpand('..',':controller => ',':c')
+ else
+ return s:selectiveexpand('..',':conditions => ',':c')
+ endif
+endfunction
+
+function! s:AddSelectiveExpand(abbr,pat,expn,...)
+ let expn = s:gsub(s:gsub(a:expn ,'[\"|]','\\&'),'\<','\\<Lt>')
+ let expn2 = s:gsub(s:gsub(a:0 ? a:1 : '','[\"|]','\\&'),'\<','\\<Lt>')
+ if a:0
+ exe "inoreabbrev <buffer> <silent> ".a:abbr." <C-R>=<SID>selectiveexpand(".string(a:pat).",\"".expn."\",".string(a:abbr).",\"".expn2."\")<CR>"
+ else
+ exe "inoreabbrev <buffer> <silent> ".a:abbr." <C-R>=<SID>selectiveexpand(".string(a:pat).",\"".expn."\",".string(a:abbr).")<CR>"
+ endif
+endfunction
+
+function! s:AddTabExpand(abbr,expn)
+ call s:AddSelectiveExpand(a:abbr,'..',a:expn)
+endfunction
+
+function! s:AddBracketExpand(abbr,expn)
+ call s:AddSelectiveExpand(a:abbr,'[[.]',a:expn)
+endfunction
+
+function! s:AddColonExpand(abbr,expn)
+ call s:AddSelectiveExpand(a:abbr,'[:.]',a:expn)
+endfunction
+
+function! s:AddParenExpand(abbr,expn,...)
+ if a:0
+ call s:AddSelectiveExpand(a:abbr,'(',a:expn,a:1)
+ else
+ call s:AddSelectiveExpand(a:abbr,'(',a:expn,'')
+ endif
+endfunction
+
+function! s:BufAbbreviations()
+ command! -buffer -bar -nargs=* -bang Rabbrev :call s:Abbrev(<bang>0,<f-args>)
+ " Some of these were cherry picked from the TextMate snippets
+ if g:rails_abbreviations
+ let buffer = rails#buffer()
+ " Limit to the right filetypes. But error on the liberal side
+ if buffer.type_name('controller','view','helper','test-functional','test-integration')
+ Rabbrev pa[ params
+ Rabbrev rq[ request
+ Rabbrev rs[ response
+ Rabbrev se[ session
+ Rabbrev hd[ headers
+ Rabbrev coo[ cookies
+ Rabbrev fl[ flash
+ Rabbrev rr( render
+ Rabbrev ra( render :action\ =>\
+ Rabbrev rc( render :controller\ =>\
+ Rabbrev rf( render :file\ =>\
+ Rabbrev ri( render :inline\ =>\
+ Rabbrev rj( render :json\ =>\
+ Rabbrev rl( render :layout\ =>\
+ Rabbrev rp( render :partial\ =>\
+ Rabbrev rt( render :text\ =>\
+ Rabbrev rx( render :xml\ =>\
+ endif
+ if buffer.type_name('view','helper')
+ Rabbrev dotiw distance_of_time_in_words
+ Rabbrev taiw time_ago_in_words
+ endif
+ if buffer.type_name('controller')
+ Rabbrev re( redirect_to
+ Rabbrev rea( redirect_to :action\ =>\
+ Rabbrev rec( redirect_to :controller\ =>\
+ Rabbrev rst( respond_to
+ endif
+ if buffer.type_name() ==# 'model' || buffer.type_name('model-arb')
+ Rabbrev bt( belongs_to
+ Rabbrev ho( has_one
+ Rabbrev hm( has_many
+ Rabbrev habtm( has_and_belongs_to_many
+ Rabbrev co( composed_of
+ Rabbrev va( validates_associated
+ Rabbrev vb( validates_acceptance_of
+ Rabbrev vc( validates_confirmation_of
+ Rabbrev ve( validates_exclusion_of
+ Rabbrev vf( validates_format_of
+ Rabbrev vi( validates_inclusion_of
+ Rabbrev vl( validates_length_of
+ Rabbrev vn( validates_numericality_of
+ Rabbrev vp( validates_presence_of
+ Rabbrev vu( validates_uniqueness_of
+ endif
+ if buffer.type_name('db-migration','db-schema')
+ Rabbrev mac( add_column
+ Rabbrev mrnc( rename_column
+ Rabbrev mrc( remove_column
+ Rabbrev mct( create_table
+ Rabbrev mcht( change_table
+ Rabbrev mrnt( rename_table
+ Rabbrev mdt( drop_table
+ Rabbrev mcc( t.column
+ endif
+ if buffer.type_name('test')
+ Rabbrev ase( assert_equal
+ Rabbrev asko( assert_kind_of
+ Rabbrev asnn( assert_not_nil
+ Rabbrev asr( assert_raise
+ Rabbrev asre( assert_response
+ Rabbrev art( assert_redirected_to
+ endif
+ Rabbrev :a :action\ =>\
+ " hax
+ Rabbrev :c :co________\ =>\
+ inoreabbrev <buffer> <silent> :c <C-R>=<SID>TheCWord()<CR>
+ Rabbrev :i :id\ =>\
+ Rabbrev :o :object\ =>\
+ Rabbrev :p :partial\ =>\
+ Rabbrev logd( logger.debug
+ Rabbrev logi( logger.info
+ Rabbrev logw( logger.warn
+ Rabbrev loge( logger.error
+ Rabbrev logf( logger.fatal
+ Rabbrev fi( find
+ Rabbrev AR:: ActiveRecord
+ Rabbrev AV:: ActionView
+ Rabbrev AC:: ActionController
+ Rabbrev AD:: ActionDispatch
+ Rabbrev AS:: ActiveSupport
+ Rabbrev AM:: ActionMailer
+ Rabbrev AO:: ActiveModel
+ Rabbrev AE:: ActiveResource
+ endif
+endfunction
+
+function! s:Abbrev(bang,...) abort
+ if !exists("b:rails_abbreviations")
+ let b:rails_abbreviations = {}
+ endif
+ if a:0 > 3 || (a:bang && (a:0 != 1))
+ return s:error("Rabbrev: invalid arguments")
+ endif
+ if a:0 == 0
+ for key in sort(keys(b:rails_abbreviations))
+ echo key . join(b:rails_abbreviations[key],"\t")
+ endfor
+ return
+ endif
+ let lhs = a:1
+ let root = s:sub(lhs,'%(::|\(|\[)$','')
+ if a:bang
+ if has_key(b:rails_abbreviations,root)
+ call remove(b:rails_abbreviations,root)
+ endif
+ exe "iunabbrev <buffer> ".root
+ return
+ endif
+ if a:0 > 3 || a:0 < 2
+ return s:error("Rabbrev: invalid arguments")
+ endif
+ let rhs = a:2
+ if has_key(b:rails_abbreviations,root)
+ call remove(b:rails_abbreviations,root)
+ endif
+ if lhs =~ '($'
+ let b:rails_abbreviations[root] = ["(", rhs . (a:0 > 2 ? "\t".a:3 : "")]
+ if a:0 > 2
+ call s:AddParenExpand(root,rhs,a:3)
+ else
+ call s:AddParenExpand(root,rhs)
+ endif
+ return
+ endif
+ if a:0 > 2
+ return s:error("Rabbrev: invalid arguments")
+ endif
+ if lhs =~ ':$'
+ call s:AddColonExpand(root,rhs)
+ elseif lhs =~ '\[$'
+ call s:AddBracketExpand(root,rhs)
+ elseif lhs =~ '\w$'
+ call s:AddTabExpand(lhs,rhs)
+ else
+ return s:error("Rabbrev: unimplemented")
+ endif
+ let b:rails_abbreviations[root] = [matchstr(lhs,'\W*$'),rhs]
+endfunction
+
+" }}}1
+" Settings {{{1
+
+function! s:Set(bang,...)
+ let c = 1
+ let defscope = ''
+ for arg in a:000
+ if arg =~? '^<[abgl]\=>$'
+ let defscope = (matchstr(arg,'<\zs.*\ze>'))
+ elseif arg !~ '='
+ if defscope != '' && arg !~ '^\w:'
+ let arg = defscope.':'.opt
+ endif
+ let val = s:getopt(arg)
+ if val == '' && !has_key(s:opts(),arg)
+ call s:error("No such rails.vim option: ".arg)
+ else
+ echo arg."=".val
+ endif
+ else
+ let opt = matchstr(arg,'[^=]*')
+ let val = s:sub(arg,'^[^=]*\=','')
+ if defscope != '' && opt !~ '^\w:'
+ let opt = defscope.':'.opt
+ endif
+ call s:setopt(opt,val)
+ endif
+ endfor
+endfunction
+
+function! s:getopt(opt,...)
+ let app = rails#app()
+ let opt = a:opt
+ if a:0
+ let scope = a:1
+ elseif opt =~ '^[abgl]:'
+ let scope = tolower(matchstr(opt,'^\w'))
+ let opt = s:sub(opt,'^\w:','')
+ else
+ let scope = 'abgl'
+ endif
+ let lnum = a:0 > 1 ? a:2 : line('.')
+ if scope =~ 'l' && &filetype != 'ruby'
+ let scope = s:sub(scope,'l','b')
+ endif
+ if scope =~ 'l'
+ call s:LocalModelines(lnum)
+ endif
+ let var = s:sname().'_'.opt
+ let lastmethod = s:lastmethod(lnum)
+ if lastmethod == '' | let lastmethod = ' ' | endif
+ " Get buffer option
+ if scope =~ 'l' && exists('b:_'.var) && has_key(b:_{var},lastmethod)
+ return b:_{var}[lastmethod]
+ elseif exists('b:'.var) && (scope =~ 'b' || (scope =~ 'l' && lastmethod == ' '))
+ return b:{var}
+ elseif scope =~ 'a' && has_key(app,'options') && has_key(app.options,opt)
+ return app.options[opt]
+ elseif scope =~ 'g' && exists("g:".s:sname()."_".opt)
+ return g:{var}
+ else
+ return ""
+ endif
+endfunction
+
+function! s:setopt(opt,val)
+ let app = rails#app()
+ if a:opt =~? '[abgl]:'
+ let scope = matchstr(a:opt,'^\w')
+ let opt = s:sub(a:opt,'^\w:','')
+ else
+ let scope = ''
+ let opt = a:opt
+ endif
+ let defscope = get(s:opts(),opt,'a')
+ if scope == ''
+ let scope = defscope
+ endif
+ if &filetype != 'ruby' && (scope ==# 'B' || scope ==# 'l')
+ let scope = 'b'
+ endif
+ let var = s:sname().'_'.opt
+ if opt =~ '\W'
+ return s:error("Invalid option ".a:opt)
+ elseif scope ==# 'B' && defscope == 'l'
+ if !exists('b:_'.var) | let b:_{var} = {} | endif
+ let b:_{var}[' '] = a:val
+ elseif scope =~? 'b'
+ let b:{var} = a:val
+ elseif scope =~? 'a'
+ if !has_key(app,'options') | let app.options = {} | endif
+ let app.options[opt] = a:val
+ elseif scope =~? 'g'
+ let g:{var} = a:val
+ elseif scope =~? 'l'
+ if !exists('b:_'.var) | let b:_{var} = {} | endif
+ let lastmethod = s:lastmethod(lnum)
+ let b:_{var}[lastmethod == '' ? ' ' : lastmethod] = a:val
+ else
+ return s:error("Invalid scope for ".a:opt)
+ endif
+endfunction
+
+function! s:opts()
+ return {'alternate': 'b', 'controller': 'b', 'gnu_screen': 'a', 'model': 'b', 'preview': 'l', 'task': 'b', 'related': 'l', 'root_url': 'a'}
+endfunction
+
+function! s:Complete_set(A,L,P)
+ if a:A =~ '='
+ let opt = matchstr(a:A,'[^=]*')
+ return [opt."=".s:getopt(opt)]
+ else
+ let extra = matchstr(a:A,'^[abgl]:')
+ return filter(sort(map(keys(s:opts()),'extra.v:val')),'s:startswith(v:val,a:A)')
+ endif
+ return []
+endfunction
+
+function! s:BufModelines()
+ if !g:rails_modelines
+ return
+ endif
+ let lines = getline("$")."\n".getline(line("$")-1)."\n".getline(1)."\n".getline(2)."\n".getline(3)."\n"
+ let pat = '\s\+\zs.\{-\}\ze\%(\n\|\s\s\|#{\@!\|%>\|-->\|$\)'
+ let cnt = 1
+ let mat = matchstr(lines,'\C\<Rset'.pat)
+ let matend = matchend(lines,'\C\<Rset'.pat)
+ while mat != "" && cnt < 10
+ let mat = s:sub(mat,'\s+$','')
+ let mat = s:gsub(mat,'\|','\\|')
+ if mat != ''
+ silent! exe "Rset <B> ".mat
+ endif
+ let mat = matchstr(lines,'\C\<Rset'.pat,matend)
+ let matend = matchend(lines,'\C\<Rset'.pat,matend)
+ let cnt += 1
+ endwhile
+endfunction
+
+function! s:LocalModelines(lnum)
+ if !g:rails_modelines
+ return
+ endif
+ let lbeg = s:lastmethodline(a:lnum)
+ let lend = s:endof(lbeg)
+ if lbeg == 0 || lend == 0
+ return
+ endif
+ let lines = "\n"
+ let lnum = lbeg
+ while lnum < lend && lnum < lbeg + 5
+ let lines .= getline(lnum) . "\n"
+ let lnum += 1
+ endwhile
+ let pat = '\s\+\zs.\{-\}\ze\%(\n\|\s\s\|#{\@!\|%>\|-->\|$\)'
+ let cnt = 1
+ let mat = matchstr(lines,'\C\<rset'.pat)
+ let matend = matchend(lines,'\C\<rset'.pat)
+ while mat != "" && cnt < 10
+ let mat = s:sub(mat,'\s+$','')
+ let mat = s:gsub(mat,'\|','\\|')
+ if mat != ''
+ silent! exe "Rset <l> ".mat
+ endif
+ let mat = matchstr(lines,'\C\<rset'.pat,matend)
+ let matend = matchend(lines,'\C\<rset'.pat,matend)
+ let cnt += 1
+ endwhile
+endfunction
+
+" }}}1
+" Detection {{{1
+
+function! s:app_source_callback(file) dict
+ if self.cache.needs('existence')
+ call self.cache.set('existence',{})
+ endif
+ let cache = self.cache.get('existence')
+ if !has_key(cache,a:file)
+ let cache[a:file] = self.has_file(a:file)
+ endif
+ if cache[a:file]
+ sandbox source `=self.path(a:file)`
+ endif
+endfunction
+
+call s:add_methods('app',['source_callback'])
+
+function! RailsBufInit(path)
+ let firsttime = !(exists("b:rails_root") && b:rails_root == a:path)
+ let b:rails_root = a:path
+ if !has_key(s:apps,a:path)
+ let s:apps[a:path] = deepcopy(s:app_prototype)
+ let s:apps[a:path].root = a:path
+ endif
+ let app = s:apps[a:path]
+ let buffer = rails#buffer()
+ " Apparently rails#buffer().calculate_file_type() can be slow if the
+ " underlying file system is slow (even though it doesn't really do anything
+ " IO related). This caching is a temporary hack; if it doesn't cause
+ " problems it should probably be refactored.
+ let b:rails_cached_file_type = buffer.calculate_file_type()
+ if g:rails_history_size > 0
+ if !exists("g:RAILS_HISTORY")
+ let g:RAILS_HISTORY = ""
+ endif
+ let path = a:path
+ let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,path)
+ if has("win32")
+ let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,s:gsub(path,'\\','/'))
+ endif
+ let path = fnamemodify(path,':p:~:h')
+ let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,path)
+ if has("win32")
+ let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,s:gsub(path,'\\','/'))
+ endif
+ let g:RAILS_HISTORY = path."\n".g:RAILS_HISTORY
+ let g:RAILS_HISTORY = s:sub(g:RAILS_HISTORY,'%(.{-}\n){,'.g:rails_history_size.'}\zs.*','')
+ endif
+ call app.source_callback("config/syntax.vim")
+ if expand('%:t') =~ '\.yml\.example$'
+ setlocal filetype=yaml
+ elseif expand('%:e') =~ '^\%(rjs\|rxml\|builder\)$'
+ setlocal filetype=ruby
+ elseif firsttime
+ " Activate custom syntax
+ let &syntax = &syntax
+ endif
+ if expand('%:e') == 'log'
+ nnoremap <buffer> <silent> R :checktime<CR>
+ nnoremap <buffer> <silent> G :checktime<Bar>$<CR>
+ nnoremap <buffer> <silent> q :bwipe<CR>
+ setlocal modifiable filetype=railslog noswapfile autoread foldmethod=syntax
+ if exists('+concealcursor')
+ setlocal concealcursor=nc conceallevel=2
+ else
+ silent %s/\%(\e\[[0-9;]*m\|\r$\)//ge
+ endif
+ setlocal readonly nomodifiable
+ $
+ endif
+ call s:BufSettings()
+ call s:BufCommands()
+ call s:BufAbbreviations()
+ " snippetsEmu.vim
+ if exists('g:loaded_snippet')
+ silent! runtime! ftplugin/rails_snippets.vim
+ " filetype snippets need to come last for higher priority
+ exe "silent! runtime! ftplugin/".&filetype."_snippets.vim"
+ endif
+ let t = rails#buffer().type_name()
+ let t = "-".t
+ let f = '/'.RailsFilePath()
+ if f =~ '[ !#$%\,]'
+ let f = ''
+ endif
+ runtime! macros/rails.vim
+ silent doautocmd User Rails
+ if t != '-'
+ exe "silent doautocmd User Rails".s:gsub(t,'-','.')
+ endif
+ if f != ''
+ exe "silent doautocmd User Rails".f
+ endif
+ call app.source_callback("config/rails.vim")
+ call s:BufModelines()
+ call s:BufMappings()
+ return b:rails_root
+endfunction
+
+function! s:SetBasePath()
+ let self = rails#buffer()
+ if self.app().path() =~ '://'
+ return
+ endif
+ let transformed_path = s:pathsplit(s:pathjoin([self.app().path()]))[0]
+ let add_dot = self.getvar('&path') =~# '^\.\%(,\|$\)'
+ let old_path = s:pathsplit(s:sub(self.getvar('&path'),'^\.%(,|$)',''))
+ call filter(old_path,'!s:startswith(v:val,transformed_path)')
+
+ let path = ['app', 'app/models', 'app/controllers', 'app/helpers', 'config', 'lib', 'app/views']
+ if self.controller_name() != ''
+ let path += ['app/views/'.self.controller_name(), 'public']
+ endif
+ if self.app().has('test')
+ let path += ['test', 'test/unit', 'test/functional', 'test/integration']
+ endif
+ if self.app().has('spec')
+ let path += ['spec', 'spec/models', 'spec/controllers', 'spec/helpers', 'spec/views', 'spec/lib', 'spec/requests', 'spec/integration']
+ endif
+ let path += ['app/*', 'vendor', 'vendor/plugins/*/lib', 'vendor/plugins/*/test', 'vendor/rails/*/lib', 'vendor/rails/*/test']
+ call map(path,'self.app().path(v:val)')
+ call self.setvar('&path',(add_dot ? '.,' : '').s:pathjoin([self.app().path()],path,old_path))
+endfunction
+
+function! s:BufSettings()
+ if !exists('b:rails_root')
+ return ''
+ endif
+ let self = rails#buffer()
+ call s:SetBasePath()
+ let rp = s:gsub(self.app().path(),'[ ,]','\\&')
+ if stridx(&tags,rp.'/tmp/tags') == -1
+ let &l:tags = rp . '/tmp/tags,' . &tags . ',' . rp . '/tags'
+ endif
+ if has("gui_win32") || has("gui_running")
+ let code = '*.rb;*.rake;Rakefile'
+ let templates = '*.'.join(s:view_types,';*.')
+ let fixtures = '*.yml;*.csv'
+ let statics = '*.html;*.css;*.js;*.xml;*.xsd;*.sql;.htaccess;README;README_FOR_APP'
+ let b:browsefilter = ""
+ \."All Rails Files\t".code.';'.templates.';'.fixtures.';'.statics."\n"
+ \."Source Code (*.rb, *.rake)\t".code."\n"
+ \."Templates (*.rhtml, *.rxml, *.rjs)\t".templates."\n"
+ \."Fixtures (*.yml, *.csv)\t".fixtures."\n"
+ \."Static Files (*.html, *.css, *.js)\t".statics."\n"
+ \."All Files (*.*)\t*.*\n"
+ endif
+ call self.setvar('&includeexpr','RailsIncludeexpr()')
+ call self.setvar('&suffixesadd', ".rb,.".join(s:view_types,',.'))
+ let ft = self.getvar('&filetype')
+ if ft =~ '^\%(e\=ruby\|[yh]aml\|coffee\|css\|s[ac]ss\|lesscss\)$'
+ call self.setvar('&shiftwidth',2)
+ call self.setvar('&softtabstop',2)
+ call self.setvar('&expandtab',1)
+ if exists('+completefunc') && self.getvar('&completefunc') == ''
+ call self.setvar('&completefunc','syntaxcomplete#Complete')
+ endif
+ endif
+ if ft == 'ruby'
+ call self.setvar('&define',self.define_pattern())
+ " This really belongs in after/ftplugin/ruby.vim but we'll be nice
+ if exists('g:loaded_surround') && self.getvar('surround_101') == ''
+ call self.setvar('surround_5', "\r\nend")
+ call self.setvar('surround_69', "\1expr: \1\rend")
+ call self.setvar('surround_101', "\r\nend")
+ endif
+ elseif ft == 'yaml' || fnamemodify(self.name(),':e') == 'yml'
+ call self.setvar('&define',self.define_pattern())
+ elseif ft =~# '^eruby\>'
+ if exists("g:loaded_allml")
+ call self.setvar('allml_stylesheet_link_tag', "<%= stylesheet_link_tag '\r' %>")
+ call self.setvar('allml_javascript_include_tag', "<%= javascript_include_tag '\r' %>")
+ call self.setvar('allml_doctype_index', 10)
+ endif
+ if exists("g:loaded_ragtag")
+ call self.setvar('ragtag_stylesheet_link_tag', "<%= stylesheet_link_tag '\r' %>")
+ call self.setvar('ragtag_javascript_include_tag', "<%= javascript_include_tag '\r' %>")
+ call self.setvar('ragtag_doctype_index', 10)
+ endif
+ elseif ft == 'haml'
+ if exists("g:loaded_allml")
+ call self.setvar('allml_stylesheet_link_tag', "= stylesheet_link_tag '\r'")
+ call self.setvar('allml_javascript_include_tag', "= javascript_include_tag '\r'")
+ call self.setvar('allml_doctype_index', 10)
+ endif
+ if exists("g:loaded_ragtag")
+ call self.setvar('ragtag_stylesheet_link_tag', "= stylesheet_link_tag '\r'")
+ call self.setvar('ragtag_javascript_include_tag', "= javascript_include_tag '\r'")
+ call self.setvar('ragtag_doctype_index', 10)
+ endif
+ endif
+ if ft =~# '^eruby\>' || ft ==# 'yaml'
+ " surround.vim
+ if exists("g:loaded_surround")
+ " The idea behind the || part here is that one can normally define the
+ " surrounding to omit the hyphen (since standard ERuby does not use it)
+ " but have it added in Rails ERuby files. Unfortunately, this makes it
+ " difficult if you really don't want a hyphen in Rails ERuby files. If
+ " this is your desire, you will need to accomplish it via a rails.vim
+ " autocommand.
+ if self.getvar('surround_45') == '' || self.getvar('surround_45') == "<% \r %>" " -
+ call self.setvar('surround_45', "<% \r -%>")
+ endif
+ if self.getvar('surround_61') == '' " =
+ call self.setvar('surround_61', "<%= \r %>")
+ endif
+ if self.getvar("surround_35") == '' " #
+ call self.setvar('surround_35', "<%# \r %>")
+ endif
+ if self.getvar('surround_101') == '' || self.getvar('surround_101')== "<% \r %>\n<% end %>" "e
+ call self.setvar('surround_5', "<% \r -%>\n<% end -%>")
+ call self.setvar('surround_69', "<% \1expr: \1 -%>\r<% end -%>")
+ call self.setvar('surround_101', "<% \r -%>\n<% end -%>")
+ endif
+ endif
+ endif
+endfunction
+
+" }}}1
+" Autocommands {{{1
+
+augroup railsPluginAuto
+ autocmd!
+ autocmd User BufEnterRails call s:RefreshBuffer()
+ autocmd User BufEnterRails call s:resetomnicomplete()
+ autocmd User BufEnterRails call s:BufDatabase(-1)
+ autocmd User dbextPreConnection call s:BufDatabase(1)
+ autocmd BufWritePost */config/database.yml call rails#cache_clear("dbext_settings")
+ autocmd BufWritePost */test/test_helper.rb call rails#cache_clear("user_assertions")
+ autocmd BufWritePost */config/routes.rb call rails#cache_clear("named_routes")
+ autocmd BufWritePost */config/environment.rb call rails#cache_clear("default_locale")
+ autocmd BufWritePost */config/environments/*.rb call rails#cache_clear("environments")
+ autocmd BufWritePost */tasks/**.rake call rails#cache_clear("rake_tasks")
+ autocmd BufWritePost */generators/** call rails#cache_clear("generators")
+ autocmd FileType * if exists("b:rails_root") | call s:BufSettings() | endif
+ autocmd Syntax ruby,eruby,yaml,haml,javascript,coffee,railslog if exists("b:rails_root") | call s:BufSyntax() | endif
+ autocmd QuickFixCmdPre *make* call s:push_chdir()
+ autocmd QuickFixCmdPost *make* call s:pop_command()
+augroup END
+
+" }}}1
+" Initialization {{{1
+
+map <SID>xx <SID>xx
+let s:sid = s:sub(maparg("<SID>xx"),'xx$','')
+unmap <SID>xx
+let s:file = expand('<sfile>:p')
+
+if !exists('s:apps')
+ let s:apps = {}
+endif
+
+" }}}1
+
+let &cpo = s:cpo_save
+
+" vim:set sw=2 sts=2:
diff --git a/vim/autoload/snipMate.vim b/vim/autoload/snipMate.vim
new file mode 100644
index 0000000..0ad5a58
--- /dev/null
+++ b/vim/autoload/snipMate.vim
@@ -0,0 +1,435 @@
+fun! Filename(...)
+ let filename = expand('%:t:r')
+ if filename == '' | return a:0 == 2 ? a:2 : '' | endif
+ return !a:0 || a:1 == '' ? filename : substitute(a:1, '$1', filename, 'g')
+endf
+
+fun s:RemoveSnippet()
+ unl! g:snipPos s:curPos s:snipLen s:endCol s:endLine s:prevLen
+ \ s:lastBuf s:oldWord
+ if exists('s:update')
+ unl s:startCol s:origWordLen s:update
+ if exists('s:oldVars') | unl s:oldVars s:oldEndCol | endif
+ endif
+ aug! snipMateAutocmds
+endf
+
+fun snipMate#expandSnip(snip, col)
+ let lnum = line('.') | let col = a:col
+
+ let snippet = s:ProcessSnippet(a:snip)
+ " Avoid error if eval evaluates to nothing
+ if snippet == '' | return '' | endif
+
+ " Expand snippet onto current position with the tab stops removed
+ let snipLines = split(substitute(snippet, '$\d\+\|${\d\+.\{-}}', '', 'g'), "\n", 1)
+
+ let line = getline(lnum)
+ let afterCursor = strpart(line, col - 1)
+ " Keep text after the cursor
+ if afterCursor != "\t" && afterCursor != ' '
+ let line = strpart(line, 0, col - 1)
+ let snipLines[-1] .= afterCursor
+ else
+ let afterCursor = ''
+ " For some reason the cursor needs to move one right after this
+ if line != '' && col == 1 && &ve != 'all' && &ve != 'onemore'
+ let col += 1
+ endif
+ endif
+
+ call setline(lnum, line.snipLines[0])
+
+ " Autoindent snippet according to previous indentation
+ let indent = matchend(line, '^.\{-}\ze\(\S\|$\)') + 1
+ call append(lnum, map(snipLines[1:], "'".strpart(line, 0, indent - 1)."'.v:val"))
+
+ " Open any folds snippet expands into
+ if &fen | sil! exe lnum.','.(lnum + len(snipLines) - 1).'foldopen' | endif
+
+ let [g:snipPos, s:snipLen] = s:BuildTabStops(snippet, lnum, col - indent, indent)
+
+ if s:snipLen
+ aug snipMateAutocmds
+ au CursorMovedI * call s:UpdateChangedSnip(0)
+ au InsertEnter * call s:UpdateChangedSnip(1)
+ aug END
+ let s:lastBuf = bufnr(0) " Only expand snippet while in current buffer
+ let s:curPos = 0
+ let s:endCol = g:snipPos[s:curPos][1]
+ let s:endLine = g:snipPos[s:curPos][0]
+
+ call cursor(g:snipPos[s:curPos][0], g:snipPos[s:curPos][1])
+ let s:prevLen = [line('$'), col('$')]
+ if g:snipPos[s:curPos][2] != -1 | return s:SelectWord() | endif
+ else
+ unl g:snipPos s:snipLen
+ " Place cursor at end of snippet if no tab stop is given
+ let newlines = len(snipLines) - 1
+ call cursor(lnum + newlines, indent + len(snipLines[-1]) - len(afterCursor)
+ \ + (newlines ? 0: col - 1))
+ endif
+ return ''
+endf
+
+" Prepare snippet to be processed by s:BuildTabStops
+fun s:ProcessSnippet(snip)
+ let snippet = a:snip
+ " Evaluate eval (`...`) expressions.
+ " Backquotes prefixed with a backslash "\" are ignored.
+ " Using a loop here instead of a regex fixes a bug with nested "\=".
+ if stridx(snippet, '`') != -1
+ while match(snippet, '\(^\|[^\\]\)`.\{-}[^\\]`') != -1
+ let snippet = substitute(snippet, '\(^\|[^\\]\)\zs`.\{-}[^\\]`\ze',
+ \ substitute(eval(matchstr(snippet, '\(^\|[^\\]\)`\zs.\{-}[^\\]\ze`')),
+ \ "\n\\%$", '', ''), '')
+ endw
+ let snippet = substitute(snippet, "\r", "\n", 'g')
+ let snippet = substitute(snippet, '\\`', '`', 'g')
+ endif
+
+ " Place all text after a colon in a tab stop after the tab stop
+ " (e.g. "${#:foo}" becomes "${:foo}foo").
+ " This helps tell the position of the tab stops later.
+ let snippet = substitute(snippet, '${\d\+:\(.\{-}\)}', '&\1', 'g')
+
+ " Update the a:snip so that all the $# become the text after
+ " the colon in their associated ${#}.
+ " (e.g. "${1:foo}" turns all "$1"'s into "foo")
+ let i = 1
+ while stridx(snippet, '${'.i) != -1
+ let s = matchstr(snippet, '${'.i.':\zs.\{-}\ze}')
+ if s != ''
+ let snippet = substitute(snippet, '$'.i, s.'&', 'g')
+ endif
+ let i += 1
+ endw
+
+ if &et " Expand tabs to spaces if 'expandtab' is set.
+ return substitute(snippet, '\t', repeat(' ', &sts ? &sts : &sw), 'g')
+ endif
+ return snippet
+endf
+
+" Counts occurences of haystack in needle
+fun s:Count(haystack, needle)
+ let counter = 0
+ let index = stridx(a:haystack, a:needle)
+ while index != -1
+ let index = stridx(a:haystack, a:needle, index+1)
+ let counter += 1
+ endw
+ return counter
+endf
+
+" Builds a list of a list of each tab stop in the snippet containing:
+" 1.) The tab stop's line number.
+" 2.) The tab stop's column number
+" (by getting the length of the string between the last "\n" and the
+" tab stop).
+" 3.) The length of the text after the colon for the current tab stop
+" (e.g. "${1:foo}" would return 3). If there is no text, -1 is returned.
+" 4.) If the "${#:}" construct is given, another list containing all
+" the matches of "$#", to be replaced with the placeholder. This list is
+" composed the same way as the parent; the first item is the line number,
+" and the second is the column.
+fun s:BuildTabStops(snip, lnum, col, indent)
+ let snipPos = []
+ let i = 1
+ let withoutVars = substitute(a:snip, '$\d\+', '', 'g')
+ while stridx(a:snip, '${'.i) != -1
+ let beforeTabStop = matchstr(withoutVars, '^.*\ze${'.i.'\D')
+ let withoutOthers = substitute(withoutVars, '${\('.i.'\D\)\@!\d\+.\{-}}', '', 'g')
+
+ let j = i - 1
+ call add(snipPos, [0, 0, -1])
+ let snipPos[j][0] = a:lnum + s:Count(beforeTabStop, "\n")
+ let snipPos[j][1] = a:indent + len(matchstr(withoutOthers, '.*\(\n\|^\)\zs.*\ze${'.i.'\D'))
+ if snipPos[j][0] == a:lnum | let snipPos[j][1] += a:col | endif
+
+ " Get all $# matches in another list, if ${#:name} is given
+ if stridx(withoutVars, '${'.i.':') != -1
+ let snipPos[j][2] = len(matchstr(withoutVars, '${'.i.':\zs.\{-}\ze}'))
+ let dots = repeat('.', snipPos[j][2])
+ call add(snipPos[j], [])
+ let withoutOthers = substitute(a:snip, '${\d\+.\{-}}\|$'.i.'\@!\d\+', '', 'g')
+ while match(withoutOthers, '$'.i.'\(\D\|$\)') != -1
+ let beforeMark = matchstr(withoutOthers, '^.\{-}\ze'.dots.'$'.i.'\(\D\|$\)')
+ call add(snipPos[j][3], [0, 0])
+ let snipPos[j][3][-1][0] = a:lnum + s:Count(beforeMark, "\n")
+ let snipPos[j][3][-1][1] = a:indent + (snipPos[j][3][-1][0] > a:lnum
+ \ ? len(matchstr(beforeMark, '.*\n\zs.*'))
+ \ : a:col + len(beforeMark))
+ let withoutOthers = substitute(withoutOthers, '$'.i.'\ze\(\D\|$\)', '', '')
+ endw
+ endif
+ let i += 1
+ endw
+ return [snipPos, i - 1]
+endf
+
+fun snipMate#jumpTabStop(backwards)
+ let leftPlaceholder = exists('s:origWordLen')
+ \ && s:origWordLen != g:snipPos[s:curPos][2]
+ if leftPlaceholder && exists('s:oldEndCol')
+ let startPlaceholder = s:oldEndCol + 1
+ endif
+
+ if exists('s:update')
+ call s:UpdatePlaceholderTabStops()
+ else
+ call s:UpdateTabStops()
+ endif
+
+ " Don't reselect placeholder if it has been modified
+ if leftPlaceholder && g:snipPos[s:curPos][2] != -1
+ if exists('startPlaceholder')
+ let g:snipPos[s:curPos][1] = startPlaceholder
+ else
+ let g:snipPos[s:curPos][1] = col('.')
+ let g:snipPos[s:curPos][2] = 0
+ endif
+ endif
+
+ let s:curPos += a:backwards ? -1 : 1
+ " Loop over the snippet when going backwards from the beginning
+ if s:curPos < 0 | let s:curPos = s:snipLen - 1 | endif
+
+ if s:curPos == s:snipLen
+ let sMode = s:endCol == g:snipPos[s:curPos-1][1]+g:snipPos[s:curPos-1][2]
+ call s:RemoveSnippet()
+ return sMode ? "\<tab>" : TriggerSnippet()
+ endif
+
+ call cursor(g:snipPos[s:curPos][0], g:snipPos[s:curPos][1])
+
+ let s:endLine = g:snipPos[s:curPos][0]
+ let s:endCol = g:snipPos[s:curPos][1]
+ let s:prevLen = [line('$'), col('$')]
+
+ return g:snipPos[s:curPos][2] == -1 ? '' : s:SelectWord()
+endf
+
+fun s:UpdatePlaceholderTabStops()
+ let changeLen = s:origWordLen - g:snipPos[s:curPos][2]
+ unl s:startCol s:origWordLen s:update
+ if !exists('s:oldVars') | return | endif
+ " Update tab stops in snippet if text has been added via "$#"
+ " (e.g., in "${1:foo}bar$1${2}").
+ if changeLen != 0
+ let curLine = line('.')
+
+ for pos in g:snipPos
+ if pos == g:snipPos[s:curPos] | continue | endif
+ let changed = pos[0] == curLine && pos[1] > s:oldEndCol
+ let changedVars = 0
+ let endPlaceholder = pos[2] - 1 + pos[1]
+ " Subtract changeLen from each tab stop that was after any of
+ " the current tab stop's placeholders.
+ for [lnum, col] in s:oldVars
+ if lnum > pos[0] | break | endif
+ if pos[0] == lnum
+ if pos[1] > col || (pos[2] == -1 && pos[1] == col)
+ let changed += 1
+ elseif col < endPlaceholder
+ let changedVars += 1
+ endif
+ endif
+ endfor
+ let pos[1] -= changeLen * changed
+ let pos[2] -= changeLen * changedVars " Parse variables within placeholders
+ " e.g., "${1:foo} ${2:$1bar}"
+
+ if pos[2] == -1 | continue | endif
+ " Do the same to any placeholders in the other tab stops.
+ for nPos in pos[3]
+ let changed = nPos[0] == curLine && nPos[1] > s:oldEndCol
+ for [lnum, col] in s:oldVars
+ if lnum > nPos[0] | break | endif
+ if nPos[0] == lnum && nPos[1] > col
+ let changed += 1
+ endif
+ endfor
+ let nPos[1] -= changeLen * changed
+ endfor
+ endfor
+ endif
+ unl s:endCol s:oldVars s:oldEndCol
+endf
+
+fun s:UpdateTabStops()
+ let changeLine = s:endLine - g:snipPos[s:curPos][0]
+ let changeCol = s:endCol - g:snipPos[s:curPos][1]
+ if exists('s:origWordLen')
+ let changeCol -= s:origWordLen
+ unl s:origWordLen
+ endif
+ let lnum = g:snipPos[s:curPos][0]
+ let col = g:snipPos[s:curPos][1]
+ " Update the line number of all proceeding tab stops if <cr> has
+ " been inserted.
+ if changeLine != 0
+ let changeLine -= 1
+ for pos in g:snipPos
+ if pos[0] >= lnum
+ if pos[0] == lnum | let pos[1] += changeCol | endif
+ let pos[0] += changeLine
+ endif
+ if pos[2] == -1 | continue | endif
+ for nPos in pos[3]
+ if nPos[0] >= lnum
+ if nPos[0] == lnum | let nPos[1] += changeCol | endif
+ let nPos[0] += changeLine
+ endif
+ endfor
+ endfor
+ elseif changeCol != 0
+ " Update the column of all proceeding tab stops if text has
+ " been inserted/deleted in the current line.
+ for pos in g:snipPos
+ if pos[1] >= col && pos[0] == lnum
+ let pos[1] += changeCol
+ endif
+ if pos[2] == -1 | continue | endif
+ for nPos in pos[3]
+ if nPos[0] > lnum | break | endif
+ if nPos[0] == lnum && nPos[1] >= col
+ let nPos[1] += changeCol
+ endif
+ endfor
+ endfor
+ endif
+endf
+
+fun s:SelectWord()
+ let s:origWordLen = g:snipPos[s:curPos][2]
+ let s:oldWord = strpart(getline('.'), g:snipPos[s:curPos][1] - 1,
+ \ s:origWordLen)
+ let s:prevLen[1] -= s:origWordLen
+ if !empty(g:snipPos[s:curPos][3])
+ let s:update = 1
+ let s:endCol = -1
+ let s:startCol = g:snipPos[s:curPos][1] - 1
+ endif
+ if !s:origWordLen | return '' | endif
+ let l = col('.') != 1 ? 'l' : ''
+ if &sel == 'exclusive'
+ return "\<esc>".l.'v'.s:origWordLen."l\<c-g>"
+ endif
+ return s:origWordLen == 1 ? "\<esc>".l.'gh'
+ \ : "\<esc>".l.'v'.(s:origWordLen - 1)."l\<c-g>"
+endf
+
+" This updates the snippet as you type when text needs to be inserted
+" into multiple places (e.g. in "${1:default text}foo$1bar$1",
+" "default text" would be highlighted, and if the user types something,
+" UpdateChangedSnip() would be called so that the text after "foo" & "bar"
+" are updated accordingly)
+"
+" It also automatically quits the snippet if the cursor is moved out of it
+" while in insert mode.
+fun s:UpdateChangedSnip(entering)
+ if exists('g:snipPos') && bufnr(0) != s:lastBuf
+ call s:RemoveSnippet()
+ elseif exists('s:update') " If modifying a placeholder
+ if !exists('s:oldVars') && s:curPos + 1 < s:snipLen
+ " Save the old snippet & word length before it's updated
+ " s:startCol must be saved too, in case text is added
+ " before the snippet (e.g. in "foo$1${2}bar${1:foo}").
+ let s:oldEndCol = s:startCol
+ let s:oldVars = deepcopy(g:snipPos[s:curPos][3])
+ endif
+ let col = col('.') - 1
+
+ if s:endCol != -1
+ let changeLen = col('$') - s:prevLen[1]
+ let s:endCol += changeLen
+ else " When being updated the first time, after leaving select mode
+ if a:entering | return | endif
+ let s:endCol = col - 1
+ endif
+
+ " If the cursor moves outside the snippet, quit it
+ if line('.') != g:snipPos[s:curPos][0] || col < s:startCol ||
+ \ col - 1 > s:endCol
+ unl! s:startCol s:origWordLen s:oldVars s:update
+ return s:RemoveSnippet()
+ endif
+
+ call s:UpdateVars()
+ let s:prevLen[1] = col('$')
+ elseif exists('g:snipPos')
+ if !a:entering && g:snipPos[s:curPos][2] != -1
+ let g:snipPos[s:curPos][2] = -2
+ endif
+
+ let col = col('.')
+ let lnum = line('.')
+ let changeLine = line('$') - s:prevLen[0]
+
+ if lnum == s:endLine
+ let s:endCol += col('$') - s:prevLen[1]
+ let s:prevLen = [line('$'), col('$')]
+ endif
+ if changeLine != 0
+ let s:endLine += changeLine
+ let s:endCol = col
+ endif
+
+ " Delete snippet if cursor moves out of it in insert mode
+ if (lnum == s:endLine && (col > s:endCol || col < g:snipPos[s:curPos][1]))
+ \ || lnum > s:endLine || lnum < g:snipPos[s:curPos][0]
+ call s:RemoveSnippet()
+ endif
+ endif
+endf
+
+" This updates the variables in a snippet when a placeholder has been edited.
+" (e.g., each "$1" in "${1:foo} $1bar $1bar")
+fun s:UpdateVars()
+ let newWordLen = s:endCol - s:startCol + 1
+ let newWord = strpart(getline('.'), s:startCol, newWordLen)
+ if newWord == s:oldWord || empty(g:snipPos[s:curPos][3])
+ return
+ endif
+
+ let changeLen = g:snipPos[s:curPos][2] - newWordLen
+ let curLine = line('.')
+ let startCol = col('.')
+ let oldStartSnip = s:startCol
+ let updateTabStops = changeLen != 0
+ let i = 0
+
+ for [lnum, col] in g:snipPos[s:curPos][3]
+ if updateTabStops
+ let start = s:startCol
+ if lnum == curLine && col <= start
+ let s:startCol -= changeLen
+ let s:endCol -= changeLen
+ endif
+ for nPos in g:snipPos[s:curPos][3][(i):]
+ " This list is in ascending order, so quit if we've gone too far.
+ if nPos[0] > lnum | break | endif
+ if nPos[0] == lnum && nPos[1] > col
+ let nPos[1] -= changeLen
+ endif
+ endfor
+ if lnum == curLine && col > start
+ let col -= changeLen
+ let g:snipPos[s:curPos][3][i][1] = col
+ endif
+ let i += 1
+ endif
+
+ " "Very nomagic" is used here to allow special characters.
+ call setline(lnum, substitute(getline(lnum), '\%'.col.'c\V'.
+ \ escape(s:oldWord, '\'), escape(newWord, '\&'), ''))
+ endfor
+ if oldStartSnip != s:startCol
+ call cursor(0, startCol + s:startCol - oldStartSnip)
+ endif
+
+ let s:oldWord = newWord
+ let g:snipPos[s:curPos][2] = newWordLen
+endf
+" vim:noet:sw=4:ts=4:ft=vim
diff --git a/vim/autoload/syntastic.vim b/vim/autoload/syntastic.vim
new file mode 100644
index 0000000..a477736
--- /dev/null
+++ b/vim/autoload/syntastic.vim
@@ -0,0 +1,24 @@
+
+function! syntastic#ErrorBalloonExpr()
+ if !exists('b:syntastic_balloons') | return '' | endif
+ return get(b:syntastic_balloons, v:beval_lnum, '')
+endfunction
+
+function! syntastic#HighlightErrors(errors, termfunc, ...)
+ call clearmatches()
+ let forcecb = a:0 && a:1
+ for item in a:errors
+ let group = item['type'] == 'E' ? 'SpellBad' : 'SpellCap'
+ if item['col'] && !forcecb
+ 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
+
diff --git a/vim/autoload/syntastic/c.vim b/vim/autoload/syntastic/c.vim
new file mode 100644
index 0000000..c7232df
--- /dev/null
+++ b/vim/autoload/syntastic/c.vim
@@ -0,0 +1,171 @@
+if exists("g:loaded_syntastic_c_autoload")
+ finish
+endif
+let g:loaded_syntastic_c_autoload = 1
+
+let s:save_cpo = &cpo
+set cpo&vim
+
+" initialize c/cpp syntax checker handlers
+function! s:Init()
+ let s:handlers = []
+ let s:cflags = {}
+
+ call s:RegHandler('gtk', 'syntastic#c#CheckPKG',
+ \ ['gtk', 'gtk+-2.0', 'gtk+', 'glib-2.0', 'glib'])
+ call s:RegHandler('glib', 'syntastic#c#CheckPKG',
+ \ ['glib', 'glib-2.0', 'glib'])
+ call s:RegHandler('glade', 'syntastic#c#CheckPKG',
+ \ ['glade', 'libglade-2.0', 'libglade'])
+ call s:RegHandler('libsoup', 'syntastic#c#CheckPKG',
+ \ ['libsoup', 'libsoup-2.4', 'libsoup-2.2'])
+ call s:RegHandler('webkit', 'syntastic#c#CheckPKG',
+ \ ['webkit', 'webkit-1.0'])
+ call s:RegHandler('cairo', 'syntastic#c#CheckPKG',
+ \ ['cairo', 'cairo'])
+ call s:RegHandler('pango', 'syntastic#c#CheckPKG',
+ \ ['pango', 'pango'])
+ call s:RegHandler('libxml', 'syntastic#c#CheckPKG',
+ \ ['libxml', 'libxml-2.0', 'libxml'])
+ call s:RegHandler('freetype', 'syntastic#c#CheckPKG',
+ \ ['freetype', 'freetype2', 'freetype'])
+ call s:RegHandler('SDL', 'syntastic#c#CheckPKG',
+ \ ['sdl', 'sdl'])
+ call s:RegHandler('opengl', 'syntastic#c#CheckPKG',
+ \ ['opengl', 'gl'])
+ call s:RegHandler('ruby', 'syntastic#c#CheckRuby', [])
+ call s:RegHandler('Python\.h', 'syntastic#c#CheckPython', [])
+ call s:RegHandler('php\.h', 'syntastic#c#CheckPhp', [])
+endfunction
+
+" search the first 100 lines for include statements that are
+" given in the handlers dictionary
+function! syntastic#c#SearchHeaders()
+ let includes = ''
+ let files = []
+ let found = []
+ let lines = filter(getline(1, 100), 'v:val =~# "#\s*include"')
+
+ " search current buffer
+ for line in lines
+ let file = matchstr(line, '"\zs\S\+\ze"')
+ if file != ''
+ call add(files, file)
+ continue
+ endif
+ for handler in s:handlers
+ if line =~# handler["regex"]
+ let includes .= call(handler["func"], handler["args"])
+ call add(found, handler["regex"])
+ break
+ endif
+ endfor
+ endfor
+
+ " search included headers
+ for hfile in files
+ if hfile != ''
+ let filename = expand('%:p:h') . ((has('win32') || has('win64')) ?
+ \ '\' : '/') . hfile
+ try
+ let lines = readfile(filename, '', 100)
+ catch /E484/
+ continue
+ endtry
+ let lines = filter(lines, 'v:val =~# "#\s*include"')
+ for handler in s:handlers
+ if index(found, handler["regex"]) != -1
+ continue
+ endif
+ for line in lines
+ if line =~# handler["regex"]
+ let includes .= call(handler["func"], handler["args"])
+ call add(found, handler["regex"])
+ break
+ endif
+ endfor
+ endfor
+ endif
+ endfor
+
+ return includes
+endfunction
+
+" try to find library with 'pkg-config'
+" search possible libraries from first to last given
+" argument until one is found
+function! syntastic#c#CheckPKG(name, ...)
+ if executable('pkg-config')
+ if !has_key(s:cflags, a:name)
+ for i in range(a:0)
+ let l:cflags = system('pkg-config --cflags '.a:000[i])
+ " since we cannot necessarily trust the pkg-config exit code
+ " we have to check for an error output as well
+ if v:shell_error == 0 && l:cflags !~? 'not found'
+ let l:cflags = ' '.substitute(l:cflags, "\n", '', '')
+ let s:cflags[a:name] = l:cflags
+ return l:cflags
+ endif
+ endfor
+ else
+ return s:cflags[a:name]
+ endif
+ endif
+ return ''
+endfunction
+
+" try to find PHP includes with 'php-config'
+function! syntastic#c#CheckPhp()
+ if executable('php-config')
+ if !exists('s:php_flags')
+ let s:php_flags = system('php-config --includes')
+ let s:php_flags = ' ' . substitute(s:php_flags, "\n", '', '')
+ endif
+ return s:php_flags
+ endif
+ return ''
+endfunction
+
+" try to find the ruby headers with 'rbconfig'
+function! syntastic#c#CheckRuby()
+ if executable('ruby')
+ if !exists('s:ruby_flags')
+ let s:ruby_flags = system('ruby -r rbconfig -e '
+ \ . '''puts Config::CONFIG["archdir"]''')
+ let s:ruby_flags = substitute(s:ruby_flags, "\n", '', '')
+ let s:ruby_flags = ' -I' . s:ruby_flags
+ endif
+ return s:ruby_flags
+ endif
+ return ''
+endfunction
+
+" try to find the python headers with distutils
+function! syntastic#c#CheckPython()
+ if executable('python')
+ if !exists('s:python_flags')
+ let s:python_flags = system('python -c ''from distutils import '
+ \ . 'sysconfig; import sys; sys.stdout.write(sysconfig.get_python_inc())''')
+ let s:python_flags = substitute(s:python_flags, "\n", '', '')
+ let s:python_flags = ' -I' . s:python_flags
+ endif
+ return s:python_flags
+ endif
+ return ''
+endfunction
+
+" return a handler dictionary object
+function! s:RegHandler(regex, function, args)
+ let handler = {}
+ let handler["regex"] = a:regex
+ let handler["func"] = function(a:function)
+ let handler["args"] = a:args
+ call add(s:handlers, handler)
+endfunction
+
+call s:Init()
+
+let &cpo = s:save_cpo
+unlet s:save_cpo
+
+" vim: set et sts=4 sw=4:
diff --git a/vim/autoload/tagbar.vim b/vim/autoload/tagbar.vim
new file mode 100644
index 0000000..48ad320
--- /dev/null
+++ b/vim/autoload/tagbar.vim
@@ -0,0 +1,3099 @@
+" ============================================================================
+" 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
+
+" Initialization {{{1
+
+" Basic init {{{2
+
+if !exists('g:tagbar_ctags_bin')
+ if executable('ctags-exuberant')
+ let g:tagbar_ctags_bin = 'ctags-exuberant'
+ elseif executable('exuberant-ctags')
+ let g:tagbar_ctags_bin = 'exuberant-ctags'
+ elseif executable('exctags')
+ let g:tagbar_ctags_bin = 'exctags'
+ elseif has('macunix') && executable('/usr/local/bin/ctags')
+ " Homebrew default location
+ let g:tagbar_ctags_bin = '/usr/local/bin/ctags'
+ elseif has('macunix') && executable('/opt/local/bin/ctags')
+ " Macports default location
+ let g:tagbar_ctags_bin = '/opt/local/bin/ctags'
+ elseif executable('ctags')
+ let g:tagbar_ctags_bin = 'ctags'
+ elseif executable('ctags.exe')
+ let g:tagbar_ctags_bin = 'ctags.exe'
+ elseif executable('tags')
+ let g:tagbar_ctags_bin = 'tags'
+ else
+ echomsg 'Tagbar: Exuberant ctags not found, skipping plugin'
+ finish
+ endif
+else
+ " reset 'wildignore' temporarily in case *.exe is included in it
+ let wildignore_save = &wildignore
+ set wildignore&
+
+ let g:tagbar_ctags_bin = expand(g:tagbar_ctags_bin)
+
+ let &wildignore = wildignore_save
+
+ if !executable(g:tagbar_ctags_bin)
+ echomsg 'Tagbar: Exuberant ctags not found in specified place,'
+ \ 'skipping plugin'
+ finish
+ endif
+endif
+
+redir => s:ftype_out
+silent filetype
+redir END
+if s:ftype_out !~# 'detection:ON'
+ echomsg 'Tagbar: Filetype detection is turned off, skipping plugin'
+ unlet s:ftype_out
+ finish
+endif
+unlet s:ftype_out
+
+let s:icon_closed = g:tagbar_iconchars[0]
+let s:icon_open = g:tagbar_iconchars[1]
+
+let s:type_init_done = 0
+let s:autocommands_done = 0
+let s:checked_ctags = 0
+let s:window_expanded = 0
+
+let s:access_symbols = {
+ \ 'public' : '+',
+ \ 'protected' : '#',
+ \ 'private' : '-'
+\ }
+
+let g:loaded_tagbar = 1
+
+let s:last_highlight_tline = 0
+let s:debug = 0
+let s:debug_file = ''
+
+" s:Init() {{{2
+function! s:Init()
+ if !s:type_init_done
+ call s:InitTypes()
+ endif
+
+ if !s:checked_ctags
+ if !s:CheckForExCtags()
+ return
+ endif
+ endif
+endfunction
+
+" s:InitTypes() {{{2
+function! s:InitTypes()
+ call s:LogDebugMessage('Initializing types')
+
+ let s:known_types = {}
+
+ " Ant {{{3
+ let type_ant = {}
+ let type_ant.ctagstype = 'ant'
+ let type_ant.kinds = [
+ \ {'short' : 'p', 'long' : 'projects', 'fold' : 0},
+ \ {'short' : 't', 'long' : 'targets', 'fold' : 0}
+ \ ]
+ let s:known_types.ant = type_ant
+ " Asm {{{3
+ let type_asm = {}
+ let type_asm.ctagstype = 'asm'
+ let type_asm.kinds = [
+ \ {'short' : 'm', 'long' : 'macros', 'fold' : 0},
+ \ {'short' : 't', 'long' : 'types', 'fold' : 0},
+ \ {'short' : 'd', 'long' : 'defines', 'fold' : 0},
+ \ {'short' : 'l', 'long' : 'labels', 'fold' : 0}
+ \ ]
+ let s:known_types.asm = type_asm
+ " ASP {{{3
+ let type_aspvbs = {}
+ let type_aspvbs.ctagstype = 'asp'
+ let type_aspvbs.kinds = [
+ \ {'short' : 'd', 'long' : 'constants', 'fold' : 0},
+ \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 's', 'long' : 'subroutines', 'fold' : 0},
+ \ {'short' : 'v', 'long' : 'variables', 'fold' : 0}
+ \ ]
+ let s:known_types.aspvbs = type_aspvbs
+ " Awk {{{3
+ let type_awk = {}
+ let type_awk.ctagstype = 'awk'
+ let type_awk.kinds = [
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
+ \ ]
+ let s:known_types.awk = type_awk
+ " Basic {{{3
+ let type_basic = {}
+ let type_basic.ctagstype = 'basic'
+ let type_basic.kinds = [
+ \ {'short' : 'c', 'long' : 'constants', 'fold' : 0},
+ \ {'short' : 'g', 'long' : 'enumerations', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 'l', 'long' : 'labels', 'fold' : 0},
+ \ {'short' : 't', 'long' : 'types', 'fold' : 0},
+ \ {'short' : 'v', 'long' : 'variables', 'fold' : 0}
+ \ ]
+ let s:known_types.basic = type_basic
+ " BETA {{{3
+ let type_beta = {}
+ let type_beta.ctagstype = 'beta'
+ let type_beta.kinds = [
+ \ {'short' : 'f', 'long' : 'fragments', 'fold' : 0},
+ \ {'short' : 's', 'long' : 'slots', 'fold' : 0},
+ \ {'short' : 'v', 'long' : 'patterns', 'fold' : 0}
+ \ ]
+ let s:known_types.beta = type_beta
+ " C {{{3
+ let type_c = {}
+ let type_c.ctagstype = 'c'
+ let type_c.kinds = [
+ \ {'short' : 'd', 'long' : 'macros', 'fold' : 1},
+ \ {'short' : 'p', 'long' : 'prototypes', 'fold' : 1},
+ \ {'short' : 'g', 'long' : 'enums', 'fold' : 0},
+ \ {'short' : 'e', 'long' : 'enumerators', 'fold' : 0},
+ \ {'short' : 't', 'long' : 'typedefs', 'fold' : 0},
+ \ {'short' : 's', 'long' : 'structs', 'fold' : 0},
+ \ {'short' : 'u', 'long' : 'unions', 'fold' : 0},
+ \ {'short' : 'm', 'long' : 'members', 'fold' : 0},
+ \ {'short' : 'v', 'long' : 'variables', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
+ \ ]
+ let type_c.sro = '::'
+ let type_c.kind2scope = {
+ \ 'g' : 'enum',
+ \ 's' : 'struct',
+ \ 'u' : 'union'
+ \ }
+ let type_c.scope2kind = {
+ \ 'enum' : 'g',
+ \ 'struct' : 's',
+ \ 'union' : 'u'
+ \ }
+ let s:known_types.c = type_c
+ " C++ {{{3
+ let type_cpp = {}
+ let type_cpp.ctagstype = 'c++'
+ let type_cpp.kinds = [
+ \ {'short' : 'd', 'long' : 'macros', 'fold' : 1},
+ \ {'short' : 'p', 'long' : 'prototypes', 'fold' : 1},
+ \ {'short' : 'g', 'long' : 'enums', 'fold' : 0},
+ \ {'short' : 'e', 'long' : 'enumerators', 'fold' : 0},
+ \ {'short' : 't', 'long' : 'typedefs', 'fold' : 0},
+ \ {'short' : 'n', 'long' : 'namespaces', 'fold' : 0},
+ \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
+ \ {'short' : 's', 'long' : 'structs', 'fold' : 0},
+ \ {'short' : 'u', 'long' : 'unions', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 'm', 'long' : 'members', 'fold' : 0},
+ \ {'short' : 'v', 'long' : 'variables', 'fold' : 0}
+ \ ]
+ let type_cpp.sro = '::'
+ let type_cpp.kind2scope = {
+ \ 'g' : 'enum',
+ \ 'n' : 'namespace',
+ \ 'c' : 'class',
+ \ 's' : 'struct',
+ \ 'u' : 'union'
+ \ }
+ let type_cpp.scope2kind = {
+ \ 'enum' : 'g',
+ \ 'namespace' : 'n',
+ \ 'class' : 'c',
+ \ 'struct' : 's',
+ \ 'union' : 'u'
+ \ }
+ let s:known_types.cpp = type_cpp
+ " C# {{{3
+ let type_cs = {}
+ let type_cs.ctagstype = 'c#'
+ let type_cs.kinds = [
+ \ {'short' : 'd', 'long' : 'macros', 'fold' : 1},
+ \ {'short' : 'f', 'long' : 'fields', 'fold' : 0},
+ \ {'short' : 'g', 'long' : 'enums', 'fold' : 0},
+ \ {'short' : 'e', 'long' : 'enumerators', 'fold' : 0},
+ \ {'short' : 't', 'long' : 'typedefs', 'fold' : 0},
+ \ {'short' : 'n', 'long' : 'namespaces', 'fold' : 0},
+ \ {'short' : 'i', 'long' : 'interfaces', 'fold' : 0},
+ \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
+ \ {'short' : 's', 'long' : 'structs', 'fold' : 0},
+ \ {'short' : 'E', 'long' : 'events', 'fold' : 0},
+ \ {'short' : 'm', 'long' : 'methods', 'fold' : 0},
+ \ {'short' : 'p', 'long' : 'properties', 'fold' : 0}
+ \ ]
+ let type_cs.sro = '.'
+ let type_cs.kind2scope = {
+ \ 'n' : 'namespace',
+ \ 'i' : 'interface',
+ \ 'c' : 'class',
+ \ 's' : 'struct',
+ \ 'g' : 'enum'
+ \ }
+ let type_cs.scope2kind = {
+ \ 'namespace' : 'n',
+ \ 'interface' : 'i',
+ \ 'class' : 'c',
+ \ 'struct' : 's',
+ \ 'enum' : 'g'
+ \ }
+ let s:known_types.cs = type_cs
+ " COBOL {{{3
+ let type_cobol = {}
+ let type_cobol.ctagstype = 'cobol'
+ let type_cobol.kinds = [
+ \ {'short' : 'd', 'long' : 'data items', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'file descriptions', 'fold' : 0},
+ \ {'short' : 'g', 'long' : 'group items', 'fold' : 0},
+ \ {'short' : 'p', 'long' : 'paragraphs', 'fold' : 0},
+ \ {'short' : 'P', 'long' : 'program ids', 'fold' : 0},
+ \ {'short' : 's', 'long' : 'sections', 'fold' : 0}
+ \ ]
+ let s:known_types.cobol = type_cobol
+ " DOS Batch {{{3
+ let type_dosbatch = {}
+ let type_dosbatch.ctagstype = 'dosbatch'
+ let type_dosbatch.kinds = [
+ \ {'short' : 'l', 'long' : 'labels', 'fold' : 0},
+ \ {'short' : 'v', 'long' : 'variables', 'fold' : 0}
+ \ ]
+ let s:known_types.dosbatch = type_dosbatch
+ " Eiffel {{{3
+ let type_eiffel = {}
+ let type_eiffel.ctagstype = 'eiffel'
+ let type_eiffel.kinds = [
+ \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'features', 'fold' : 0}
+ \ ]
+ let type_eiffel.sro = '.' " Not sure, is nesting even possible?
+ let type_eiffel.kind2scope = {
+ \ 'c' : 'class',
+ \ 'f' : 'feature'
+ \ }
+ let type_eiffel.scope2kind = {
+ \ 'class' : 'c',
+ \ 'feature' : 'f'
+ \ }
+ let s:known_types.eiffel = type_eiffel
+ " Erlang {{{3
+ let type_erlang = {}
+ let type_erlang.ctagstype = 'erlang'
+ let type_erlang.kinds = [
+ \ {'short' : 'm', 'long' : 'modules', 'fold' : 0},
+ \ {'short' : 'd', 'long' : 'macro definitions', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 'r', 'long' : 'record definitions', 'fold' : 0}
+ \ ]
+ let type_erlang.sro = '.' " Not sure, is nesting even possible?
+ let type_erlang.kind2scope = {
+ \ 'm' : 'module'
+ \ }
+ let type_erlang.scope2kind = {
+ \ 'module' : 'm'
+ \ }
+ let s:known_types.erlang = type_erlang
+ " Flex {{{3
+ " Vim doesn't support Flex out of the box, this is based on rough
+ " guesses and probably requires
+ " http://www.vim.org/scripts/script.php?script_id=2909
+ " Improvements welcome!
+ let type_mxml = {}
+ let type_mxml.ctagstype = 'flex'
+ let type_mxml.kinds = [
+ \ {'short' : 'v', 'long' : 'global variables', 'fold' : 0},
+ \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
+ \ {'short' : 'm', 'long' : 'methods', 'fold' : 0},
+ \ {'short' : 'p', 'long' : 'properties', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 'x', 'long' : 'mxtags', 'fold' : 0}
+ \ ]
+ let type_mxml.sro = '.'
+ let type_mxml.kind2scope = {
+ \ 'c' : 'class'
+ \ }
+ let type_mxml.scope2kind = {
+ \ 'class' : 'c'
+ \ }
+ let s:known_types.mxml = type_mxml
+ " Fortran {{{3
+ let type_fortran = {}
+ let type_fortran.ctagstype = 'fortran'
+ let type_fortran.kinds = [
+ \ {'short' : 'm', 'long' : 'modules', 'fold' : 0},
+ \ {'short' : 'p', 'long' : 'programs', 'fold' : 0},
+ \ {'short' : 'k', 'long' : 'components', 'fold' : 0},
+ \ {'short' : 't', 'long' : 'derived types and structures', 'fold' : 0},
+ \ {'short' : 'c', 'long' : 'common blocks', 'fold' : 0},
+ \ {'short' : 'b', 'long' : 'block data', 'fold' : 0},
+ \ {'short' : 'e', 'long' : 'entry points', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 's', 'long' : 'subroutines', 'fold' : 0},
+ \ {'short' : 'l', 'long' : 'labels', 'fold' : 0},
+ \ {'short' : 'n', 'long' : 'namelists', 'fold' : 0},
+ \ {'short' : 'v', 'long' : 'variables', 'fold' : 0}
+ \ ]
+ let type_fortran.sro = '.' " Not sure, is nesting even possible?
+ let type_fortran.kind2scope = {
+ \ 'm' : 'module',
+ \ 'p' : 'program',
+ \ 'f' : 'function',
+ \ 's' : 'subroutine'
+ \ }
+ let type_fortran.scope2kind = {
+ \ 'module' : 'm',
+ \ 'program' : 'p',
+ \ 'function' : 'f',
+ \ 'subroutine' : 's'
+ \ }
+ let s:known_types.fortran = type_fortran
+ " HTML {{{3
+ let type_html = {}
+ let type_html.ctagstype = 'html'
+ let type_html.kinds = [
+ \ {'short' : 'f', 'long' : 'JavaScript funtions', 'fold' : 0},
+ \ {'short' : 'a', 'long' : 'named anchors', 'fold' : 0}
+ \ ]
+ let s:known_types.html = type_html
+ " Java {{{3
+ let type_java = {}
+ let type_java.ctagstype = 'java'
+ let type_java.kinds = [
+ \ {'short' : 'p', 'long' : 'packages', 'fold' : 1},
+ \ {'short' : 'f', 'long' : 'fields', 'fold' : 0},
+ \ {'short' : 'g', 'long' : 'enum types', 'fold' : 0},
+ \ {'short' : 'e', 'long' : 'enum constants', 'fold' : 0},
+ \ {'short' : 'i', 'long' : 'interfaces', 'fold' : 0},
+ \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
+ \ {'short' : 'm', 'long' : 'methods', 'fold' : 0}
+ \ ]
+ let type_java.sro = '.'
+ let type_java.kind2scope = {
+ \ 'g' : 'enum',
+ \ 'i' : 'interface',
+ \ 'c' : 'class'
+ \ }
+ let type_java.scope2kind = {
+ \ 'enum' : 'g',
+ \ 'interface' : 'i',
+ \ 'class' : 'c'
+ \ }
+ let s:known_types.java = type_java
+ " JavaScript {{{3
+ " JavaScript is weird -- it does have scopes, but ctags doesn't seem to
+ " properly generate the information for them, instead it simply uses the
+ " complete name. So ctags has to be fixed before I can do anything here.
+ " Alternatively jsctags/doctorjs will be used if available.
+ let type_javascript = {}
+ let type_javascript.ctagstype = 'javascript'
+ let jsctags = s:CheckFTCtags('jsctags', 'javascript')
+ if jsctags != ''
+ let type_javascript.kinds = [
+ \ {'short' : 'v', 'long' : 'variables', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
+ \ ]
+ let type_javascript.sro = '.'
+ let type_javascript.kind2scope = {
+ \ 'v' : 'namespace',
+ \ 'f' : 'namespace'
+ \ }
+ let type_javascript.scope2kind = {
+ \ 'namespace' : 'v'
+ \ }
+ let type_javascript.ctagsbin = jsctags
+ let type_javascript.ctagsargs = '-f -'
+ else
+ let type_javascript.kinds = [
+ \ {'short' : 'v', 'long' : 'global variables', 'fold' : 0},
+ \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
+ \ {'short' : 'p', 'long' : 'properties', 'fold' : 0},
+ \ {'short' : 'm', 'long' : 'methods', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
+ \ ]
+ endif
+ let s:known_types.javascript = type_javascript
+ " Lisp {{{3
+ let type_lisp = {}
+ let type_lisp.ctagstype = 'lisp'
+ let type_lisp.kinds = [
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
+ \ ]
+ let s:known_types.lisp = type_lisp
+ " Lua {{{3
+ let type_lua = {}
+ let type_lua.ctagstype = 'lua'
+ let type_lua.kinds = [
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
+ \ ]
+ let s:known_types.lua = type_lua
+ " Make {{{3
+ let type_make = {}
+ let type_make.ctagstype = 'make'
+ let type_make.kinds = [
+ \ {'short' : 'm', 'long' : 'macros', 'fold' : 0}
+ \ ]
+ let s:known_types.make = type_make
+ " Matlab {{{3
+ let type_matlab = {}
+ let type_matlab.ctagstype = 'matlab'
+ let type_matlab.kinds = [
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
+ \ ]
+ let s:known_types.matlab = type_matlab
+ " Ocaml {{{3
+ let type_ocaml = {}
+ let type_ocaml.ctagstype = 'ocaml'
+ let type_ocaml.kinds = [
+ \ {'short' : 'M', 'long' : 'modules or functors', 'fold' : 0},
+ \ {'short' : 'v', 'long' : 'global variables', 'fold' : 0},
+ \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
+ \ {'short' : 'C', 'long' : 'constructors', 'fold' : 0},
+ \ {'short' : 'm', 'long' : 'methods', 'fold' : 0},
+ \ {'short' : 'e', 'long' : 'exceptions', 'fold' : 0},
+ \ {'short' : 't', 'long' : 'type names', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 'r', 'long' : 'structure fields', 'fold' : 0}
+ \ ]
+ let type_ocaml.sro = '.' " Not sure, is nesting even possible?
+ let type_ocaml.kind2scope = {
+ \ 'M' : 'Module',
+ \ 'c' : 'class',
+ \ 't' : 'type'
+ \ }
+ let type_ocaml.scope2kind = {
+ \ 'Module' : 'M',
+ \ 'class' : 'c',
+ \ 'type' : 't'
+ \ }
+ let s:known_types.ocaml = type_ocaml
+ " Pascal {{{3
+ let type_pascal = {}
+ let type_pascal.ctagstype = 'pascal'
+ let type_pascal.kinds = [
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 'p', 'long' : 'procedures', 'fold' : 0}
+ \ ]
+ let s:known_types.pascal = type_pascal
+ " Perl {{{3
+ let type_perl = {}
+ let type_perl.ctagstype = 'perl'
+ let type_perl.kinds = [
+ \ {'short' : 'p', 'long' : 'packages', 'fold' : 1},
+ \ {'short' : 'c', 'long' : 'constants', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'formats', 'fold' : 0},
+ \ {'short' : 'l', 'long' : 'labels', 'fold' : 0},
+ \ {'short' : 's', 'long' : 'subroutines', 'fold' : 0}
+ \ ]
+ let s:known_types.perl = type_perl
+ " PHP {{{3
+ let type_php = {}
+ let type_php.ctagstype = 'php'
+ let type_php.kinds = [
+ \ {'short' : 'i', 'long' : 'interfaces', 'fold' : 0},
+ \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
+ \ {'short' : 'd', 'long' : 'constant definitions', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 'v', 'long' : 'variables', 'fold' : 0},
+ \ {'short' : 'j', 'long' : 'javascript functions', 'fold' : 0}
+ \ ]
+ let s:known_types.php = type_php
+ " Python {{{3
+ let type_python = {}
+ let type_python.ctagstype = 'python'
+ let type_python.kinds = [
+ \ {'short' : 'i', 'long' : 'imports', 'fold' : 1},
+ \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 'm', 'long' : 'members', 'fold' : 0},
+ \ {'short' : 'v', 'long' : 'variables', 'fold' : 0}
+ \ ]
+ let type_python.sro = '.'
+ let type_python.kind2scope = {
+ \ 'c' : 'class',
+ \ 'f' : 'function',
+ \ 'm' : 'function'
+ \ }
+ let type_python.scope2kind = {
+ \ 'class' : 'c',
+ \ 'function' : 'f'
+ \ }
+ let s:known_types.python = type_python
+ " REXX {{{3
+ let type_rexx = {}
+ let type_rexx.ctagstype = 'rexx'
+ let type_rexx.kinds = [
+ \ {'short' : 's', 'long' : 'subroutines', 'fold' : 0}
+ \ ]
+ let s:known_types.rexx = type_rexx
+ " Ruby {{{3
+ let type_ruby = {}
+ let type_ruby.ctagstype = 'ruby'
+ let type_ruby.kinds = [
+ \ {'short' : 'm', 'long' : 'modules', 'fold' : 0},
+ \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'methods', 'fold' : 0},
+ \ {'short' : 'F', 'long' : 'singleton methods', 'fold' : 0}
+ \ ]
+ let type_ruby.sro = '.'
+ let type_ruby.kind2scope = {
+ \ 'c' : 'class',
+ \ 'm' : 'class'
+ \ }
+ let type_ruby.scope2kind = {
+ \ 'class' : 'c'
+ \ }
+ let s:known_types.ruby = type_ruby
+ " Scheme {{{3
+ let type_scheme = {}
+ let type_scheme.ctagstype = 'scheme'
+ let type_scheme.kinds = [
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 's', 'long' : 'sets', 'fold' : 0}
+ \ ]
+ let s:known_types.scheme = type_scheme
+ " Shell script {{{3
+ let type_sh = {}
+ let type_sh.ctagstype = 'sh'
+ let type_sh.kinds = [
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
+ \ ]
+ let s:known_types.sh = type_sh
+ let s:known_types.csh = type_sh
+ let s:known_types.zsh = type_sh
+ " SLang {{{3
+ let type_slang = {}
+ let type_slang.ctagstype = 'slang'
+ let type_slang.kinds = [
+ \ {'short' : 'n', 'long' : 'namespaces', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
+ \ ]
+ let s:known_types.slang = type_slang
+ " SML {{{3
+ let type_sml = {}
+ let type_sml.ctagstype = 'sml'
+ let type_sml.kinds = [
+ \ {'short' : 'e', 'long' : 'exception declarations', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'function definitions', 'fold' : 0},
+ \ {'short' : 'c', 'long' : 'functor definitions', 'fold' : 0},
+ \ {'short' : 's', 'long' : 'signature declarations', 'fold' : 0},
+ \ {'short' : 'r', 'long' : 'structure declarations', 'fold' : 0},
+ \ {'short' : 't', 'long' : 'type definitions', 'fold' : 0},
+ \ {'short' : 'v', 'long' : 'value bindings', 'fold' : 0}
+ \ ]
+ let s:known_types.sml = type_sml
+ " SQL {{{3
+ " The SQL ctags parser seems to be buggy for me, so this just uses the
+ " normal kinds even though scopes should be available. Improvements
+ " welcome!
+ let type_sql = {}
+ let type_sql.ctagstype = 'sql'
+ let type_sql.kinds = [
+ \ {'short' : 'P', 'long' : 'packages', 'fold' : 1},
+ \ {'short' : 'c', 'long' : 'cursors', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 'F', 'long' : 'record fields', 'fold' : 0},
+ \ {'short' : 'L', 'long' : 'block label', 'fold' : 0},
+ \ {'short' : 'p', 'long' : 'procedures', 'fold' : 0},
+ \ {'short' : 's', 'long' : 'subtypes', 'fold' : 0},
+ \ {'short' : 't', 'long' : 'tables', 'fold' : 0},
+ \ {'short' : 'T', 'long' : 'triggers', 'fold' : 0},
+ \ {'short' : 'v', 'long' : 'variables', 'fold' : 0},
+ \ {'short' : 'i', 'long' : 'indexes', 'fold' : 0},
+ \ {'short' : 'e', 'long' : 'events', 'fold' : 0},
+ \ {'short' : 'U', 'long' : 'publications', 'fold' : 0},
+ \ {'short' : 'R', 'long' : 'services', 'fold' : 0},
+ \ {'short' : 'D', 'long' : 'domains', 'fold' : 0},
+ \ {'short' : 'V', 'long' : 'views', 'fold' : 0},
+ \ {'short' : 'n', 'long' : 'synonyms', 'fold' : 0},
+ \ {'short' : 'x', 'long' : 'MobiLink Table Scripts', 'fold' : 0},
+ \ {'short' : 'y', 'long' : 'MobiLink Conn Scripts', 'fold' : 0}
+ \ ]
+ let s:known_types.sql = type_sql
+ " Tcl {{{3
+ let type_tcl = {}
+ let type_tcl.ctagstype = 'tcl'
+ let type_tcl.kinds = [
+ \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
+ \ {'short' : 'm', 'long' : 'methods', 'fold' : 0},
+ \ {'short' : 'p', 'long' : 'procedures', 'fold' : 0}
+ \ ]
+ let s:known_types.tcl = type_tcl
+ " LaTeX {{{3
+ let type_tex = {}
+ let type_tex.ctagstype = 'tex'
+ let type_tex.kinds = [
+ \ {'short' : 'p', 'long' : 'parts', 'fold' : 0},
+ \ {'short' : 'c', 'long' : 'chapters', 'fold' : 0},
+ \ {'short' : 's', 'long' : 'sections', 'fold' : 0},
+ \ {'short' : 'u', 'long' : 'subsections', 'fold' : 0},
+ \ {'short' : 'b', 'long' : 'subsubsections', 'fold' : 0},
+ \ {'short' : 'P', 'long' : 'paragraphs', 'fold' : 0},
+ \ {'short' : 'G', 'long' : 'subparagraphs', 'fold' : 0}
+ \ ]
+ let s:known_types.tex = type_tex
+ " Vera {{{3
+ " Why are variables 'virtual'?
+ let type_vera = {}
+ let type_vera.ctagstype = 'vera'
+ let type_vera.kinds = [
+ \ {'short' : 'd', 'long' : 'macros', 'fold' : 1},
+ \ {'short' : 'g', 'long' : 'enums', 'fold' : 0},
+ \ {'short' : 'T', 'long' : 'typedefs', 'fold' : 0},
+ \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
+ \ {'short' : 'e', 'long' : 'enumerators', 'fold' : 0},
+ \ {'short' : 'm', 'long' : 'members', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 't', 'long' : 'tasks', 'fold' : 0},
+ \ {'short' : 'v', 'long' : 'variables', 'fold' : 0},
+ \ {'short' : 'p', 'long' : 'programs', 'fold' : 0}
+ \ ]
+ let type_vera.sro = '.' " Nesting doesn't seem to be possible
+ let type_vera.kind2scope = {
+ \ 'g' : 'enum',
+ \ 'c' : 'class',
+ \ 'v' : 'virtual'
+ \ }
+ let type_vera.scope2kind = {
+ \ 'enum' : 'g',
+ \ 'class' : 'c',
+ \ 'virtual' : 'v'
+ \ }
+ let s:known_types.vera = type_vera
+ " Verilog {{{3
+ let type_verilog = {}
+ let type_verilog.ctagstype = 'verilog'
+ let type_verilog.kinds = [
+ \ {'short' : 'c', 'long' : 'constants', 'fold' : 0},
+ \ {'short' : 'e', 'long' : 'events', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 'm', 'long' : 'modules', 'fold' : 0},
+ \ {'short' : 'n', 'long' : 'net data types', 'fold' : 0},
+ \ {'short' : 'p', 'long' : 'ports', 'fold' : 0},
+ \ {'short' : 'r', 'long' : 'register data types', 'fold' : 0},
+ \ {'short' : 't', 'long' : 'tasks', 'fold' : 0}
+ \ ]
+ let s:known_types.verilog = type_verilog
+ " VHDL {{{3
+ " The VHDL ctags parser unfortunately doesn't generate proper scopes
+ let type_vhdl = {}
+ let type_vhdl.ctagstype = 'vhdl'
+ let type_vhdl.kinds = [
+ \ {'short' : 'P', 'long' : 'packages', 'fold' : 1},
+ \ {'short' : 'c', 'long' : 'constants', 'fold' : 0},
+ \ {'short' : 't', 'long' : 'types', 'fold' : 0},
+ \ {'short' : 'T', 'long' : 'subtypes', 'fold' : 0},
+ \ {'short' : 'r', 'long' : 'records', 'fold' : 0},
+ \ {'short' : 'e', 'long' : 'entities', 'fold' : 0},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 'p', 'long' : 'procedures', 'fold' : 0}
+ \ ]
+ let s:known_types.vhdl = type_vhdl
+ " Vim {{{3
+ let type_vim = {}
+ let type_vim.ctagstype = 'vim'
+ let type_vim.kinds = [
+ \ {'short' : 'v', 'long' : 'variables', 'fold' : 1},
+ \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
+ \ {'short' : 'a', 'long' : 'autocommand groups', 'fold' : 1},
+ \ {'short' : 'c', 'long' : 'commands', 'fold' : 0},
+ \ {'short' : 'm', 'long' : 'maps', 'fold' : 1}
+ \ ]
+ let s:known_types.vim = type_vim
+ " YACC {{{3
+ let type_yacc = {}
+ let type_yacc.ctagstype = 'yacc'
+ let type_yacc.kinds = [
+ \ {'short' : 'l', 'long' : 'labels', 'fold' : 0}
+ \ ]
+ let s:known_types.yacc = type_yacc
+ " }}}3
+
+ let user_defs = s:GetUserTypeDefs()
+ for [key, value] in items(user_defs)
+ if !has_key(s:known_types, key) ||
+ \ (has_key(value, 'replace') && value.replace)
+ let s:known_types[key] = value
+ else
+ call extend(s:known_types[key], value)
+ endif
+ endfor
+
+ " Create a dictionary of the kind order for fast
+ " access in sorting functions
+ for type in values(s:known_types)
+ let i = 0
+ let type.kinddict = {}
+ for kind in type.kinds
+ let type.kinddict[kind.short] = i
+ let i += 1
+ endfor
+ endfor
+
+ let s:type_init_done = 1
+endfunction
+
+" s:GetUserTypeDefs() {{{2
+function! s:GetUserTypeDefs()
+ call s:LogDebugMessage('Initializing user types')
+
+ redir => defs
+ silent execute 'let g:'
+ redir END
+
+ let deflist = split(defs, '\n')
+ call map(deflist, 'substitute(v:val, ''^\S\+\zs.*'', "", "")')
+ call filter(deflist, 'v:val =~ "^tagbar_type_"')
+
+ let defdict = {}
+ for defstr in deflist
+ let type = substitute(defstr, '^tagbar_type_', '', '')
+ execute 'let defdict["' . type . '"] = g:' . defstr
+ endfor
+
+ " If the user only specified one of kind2scope and scope2kind use it to
+ " generate the other one
+ " Also, transform the 'kind' definitions into dictionary format
+ for def in values(defdict)
+ if has_key(def, 'kinds')
+ let kinds = def.kinds
+ let def.kinds = []
+ for kind in kinds
+ let kindlist = split(kind, ':')
+ let kinddict = {'short' : kindlist[0], 'long' : kindlist[1]}
+ if len(kindlist) == 3
+ let kinddict.fold = kindlist[2]
+ else
+ let kinddict.fold = 0
+ endif
+ call add(def.kinds, kinddict)
+ endfor
+ endif
+
+ if has_key(def, 'kind2scope') && !has_key(def, 'scope2kind')
+ let def.scope2kind = {}
+ for [key, value] in items(def.kind2scope)
+ let def.scope2kind[value] = key
+ endfor
+ elseif has_key(def, 'scope2kind') && !has_key(def, 'kind2scope')
+ let def.kind2scope = {}
+ for [key, value] in items(def.scope2kind)
+ let def.kind2scope[value] = key
+ endfor
+ endif
+ endfor
+
+ return defdict
+endfunction
+
+" s:RestoreSession() {{{2
+" Properly restore Tagbar after a session got loaded
+function! s:RestoreSession()
+ call s:LogDebugMessage('Restoring session')
+
+ let tagbarwinnr = bufwinnr('__Tagbar__')
+ if tagbarwinnr == -1
+ " Tagbar wasn't open in the saved session, nothing to do
+ return
+ else
+ let in_tagbar = 1
+ if winnr() != tagbarwinnr
+ execute tagbarwinnr . 'wincmd w'
+ let in_tagbar = 0
+ endif
+ endif
+
+ call s:Init()
+
+ call s:InitWindow(g:tagbar_autoclose)
+
+ " Leave the Tagbar window and come back so the update event gets triggered
+ wincmd p
+ execute tagbarwinnr . 'wincmd w'
+
+ if !in_tagbar
+ wincmd p
+ endif
+endfunction
+
+" s:MapKeys() {{{2
+function! s:MapKeys()
+ call s:LogDebugMessage('Mapping keys')
+
+ nnoremap <script> <silent> <buffer> <2-LeftMouse>
+ \ :call <SID>JumpToTag(0)<CR>
+ nnoremap <script> <silent> <buffer> <LeftRelease>
+ \ <LeftRelease>:call <SID>CheckMouseClick()<CR>
+
+ inoremap <script> <silent> <buffer> <2-LeftMouse>
+ \ <C-o>:call <SID>JumpToTag(0)<CR>
+ inoremap <script> <silent> <buffer> <LeftRelease>
+ \ <LeftRelease><C-o>:call <SID>CheckMouseClick()<CR>
+
+ nnoremap <script> <silent> <buffer> <CR> :call <SID>JumpToTag(0)<CR>
+ nnoremap <script> <silent> <buffer> p :call <SID>JumpToTag(1)<CR>
+ nnoremap <script> <silent> <buffer> <Space> :call <SID>ShowPrototype()<CR>
+
+ nnoremap <script> <silent> <buffer> + :call <SID>OpenFold()<CR>
+ nnoremap <script> <silent> <buffer> <kPlus> :call <SID>OpenFold()<CR>
+ nnoremap <script> <silent> <buffer> zo :call <SID>OpenFold()<CR>
+ nnoremap <script> <silent> <buffer> - :call <SID>CloseFold()<CR>
+ nnoremap <script> <silent> <buffer> <kMinus> :call <SID>CloseFold()<CR>
+ nnoremap <script> <silent> <buffer> zc :call <SID>CloseFold()<CR>
+ nnoremap <script> <silent> <buffer> o :call <SID>ToggleFold()<CR>
+ nnoremap <script> <silent> <buffer> za :call <SID>ToggleFold()<CR>
+
+ nnoremap <script> <silent> <buffer> * :call <SID>SetFoldLevel(99)<CR>
+ nnoremap <script> <silent> <buffer> <kMultiply>
+ \ :call <SID>SetFoldLevel(99)<CR>
+ nnoremap <script> <silent> <buffer> zR :call <SID>SetFoldLevel(99)<CR>
+ nnoremap <script> <silent> <buffer> = :call <SID>SetFoldLevel(0)<CR>
+ nnoremap <script> <silent> <buffer> zM :call <SID>SetFoldLevel(0)<CR>
+
+ nnoremap <script> <silent> <buffer> <C-N>
+ \ :call <SID>GotoNextToplevelTag(1)<CR>
+ nnoremap <script> <silent> <buffer> <C-P>
+ \ :call <SID>GotoNextToplevelTag(-1)<CR>
+
+ nnoremap <script> <silent> <buffer> s :call <SID>ToggleSort()<CR>
+ nnoremap <script> <silent> <buffer> x :call <SID>ZoomWindow()<CR>
+ nnoremap <script> <silent> <buffer> q :call <SID>CloseWindow()<CR>
+ nnoremap <script> <silent> <buffer> <F1> :call <SID>ToggleHelp()<CR>
+endfunction
+
+" s:CreateAutocommands() {{{2
+function! s:CreateAutocommands()
+ call s:LogDebugMessage('Creating autocommands')
+
+ augroup TagbarAutoCmds
+ autocmd!
+ autocmd BufEnter __Tagbar__ nested call s:QuitIfOnlyWindow()
+ autocmd BufUnload __Tagbar__ call s:CleanUp()
+ autocmd CursorHold __Tagbar__ call s:ShowPrototype()
+
+ autocmd BufWritePost *
+ \ if line('$') < g:tagbar_updateonsave_maxlines |
+ \ call s:AutoUpdate(fnamemodify(expand('<afile>'), ':p')) |
+ \ endif
+ autocmd BufEnter,CursorHold,FileType * call
+ \ s:AutoUpdate(fnamemodify(expand('<afile>'), ':p'))
+ autocmd BufDelete * call
+ \ s:CleanupFileinfo(fnamemodify(expand('<afile>'), ':p'))
+ augroup END
+
+ let s:autocommands_done = 1
+endfunction
+
+" s:CheckForExCtags() {{{2
+" Test whether the ctags binary is actually Exuberant Ctags and not GNU ctags
+" (or something else)
+function! s:CheckForExCtags()
+ call s:LogDebugMessage('Checking for Exuberant Ctags')
+
+ let ctags_cmd = s:EscapeCtagsCmd(g:tagbar_ctags_bin, '--version')
+ if ctags_cmd == ''
+ return
+ endif
+
+ let ctags_output = s:ExecuteCtags(ctags_cmd)
+
+ if v:shell_error || ctags_output !~# 'Exuberant Ctags'
+ echoerr 'Tagbar: Ctags doesn''t seem to be Exuberant Ctags!'
+ echomsg 'GNU ctags will NOT WORK.'
+ \ 'Please download Exuberant Ctags from ctags.sourceforge.net'
+ \ 'and install it in a directory in your $PATH'
+ \ 'or set g:tagbar_ctags_bin.'
+ echomsg 'Executed command: "' . ctags_cmd . '"'
+ if !empty(ctags_output)
+ echomsg 'Command output:'
+ for line in split(ctags_output, '\n')
+ echomsg line
+ endfor
+ endif
+ return 0
+ elseif !s:CheckExCtagsVersion(ctags_output)
+ echoerr 'Tagbar: Exuberant Ctags is too old!'
+ echomsg 'You need at least version 5.5 for Tagbar to work.'
+ \ 'Please download a newer version from ctags.sourceforge.net.'
+ echomsg 'Executed command: "' . ctags_cmd . '"'
+ if !empty(ctags_output)
+ echomsg 'Command output:'
+ for line in split(ctags_output, '\n')
+ echomsg line
+ endfor
+ endif
+ return 0
+ else
+ let s:checked_ctags = 1
+ return 1
+ endif
+endfunction
+
+" s:CheckExCtagsVersion() {{{2
+function! s:CheckExCtagsVersion(output)
+ call s:LogDebugMessage('Checking Exuberant Ctags version')
+
+ if a:output =~ 'Exuberant Ctags Development'
+ return 1
+ endif
+
+ let matchlist = matchlist(a:output, '\vExuberant Ctags (\d+)\.(\d+)')
+ let major = matchlist[1]
+ let minor = matchlist[2]
+
+ return major >= 6 || (major == 5 && minor >= 5)
+endfunction
+
+" s:CheckFTCtags() {{{2
+function! s:CheckFTCtags(bin, ftype)
+ if executable(a:bin)
+ return a:bin
+ endif
+
+ if exists('g:tagbar_type_' . a:ftype)
+ execute 'let userdef = ' . 'g:tagbar_type_' . a:ftype
+ if has_key(userdef, 'ctagsbin')
+ return userdef.ctagsbin
+ else
+ return ''
+ endif
+ endif
+
+ return ''
+endfunction
+
+" Prototypes {{{1
+" Base tag {{{2
+let s:BaseTag = {}
+
+" s:BaseTag._init() {{{3
+function! s:BaseTag._init(name) dict
+ let self.name = a:name
+ let self.fields = {}
+ let self.fields.line = 0
+ let self.path = ''
+ let self.fullpath = a:name
+ let self.depth = 0
+ let self.parent = {}
+ let self.tline = -1
+ let self.fileinfo = {}
+endfunction
+
+" s:BaseTag.isNormalTag() {{{3
+function! s:BaseTag.isNormalTag() dict
+ return 0
+endfunction
+
+" s:BaseTag.isPseudoTag() {{{3
+function! s:BaseTag.isPseudoTag() dict
+ return 0
+endfunction
+
+" s:BaseTag.isKindheader() {{{3
+function! s:BaseTag.isKindheader() dict
+ return 0
+endfunction
+
+" s:BaseTag.getPrototype() {{{3
+function! s:BaseTag.getPrototype() dict
+ return ''
+endfunction
+
+" s:BaseTag._getPrefix() {{{3
+function! s:BaseTag._getPrefix() dict
+ let fileinfo = self.fileinfo
+
+ if has_key(self, 'children') && !empty(self.children)
+ if fileinfo.tagfolds[self.fields.kind][self.fullpath]
+ let prefix = s:icon_closed
+ else
+ let prefix = s:icon_open
+ endif
+ else
+ let prefix = ' '
+ endif
+ if has_key(self.fields, 'access')
+ let prefix .= get(s:access_symbols, self.fields.access, ' ')
+ else
+ let prefix .= ' '
+ endif
+
+ return prefix
+endfunction
+
+" s:BaseTag.initFoldState() {{{3
+function! s:BaseTag.initFoldState() dict
+ let fileinfo = self.fileinfo
+
+ if s:known_files.has(fileinfo.fpath) &&
+ \ has_key(fileinfo._tagfolds_old[self.fields.kind], self.fullpath)
+ " The file has been updated and the tag was there before, so copy its
+ " old fold state
+ let fileinfo.tagfolds[self.fields.kind][self.fullpath] =
+ \ fileinfo._tagfolds_old[self.fields.kind][self.fullpath]
+ elseif self.depth >= fileinfo.foldlevel
+ let fileinfo.tagfolds[self.fields.kind][self.fullpath] = 1
+ else
+ let fileinfo.tagfolds[self.fields.kind][self.fullpath] =
+ \ fileinfo.kindfolds[self.fields.kind]
+ endif
+endfunction
+
+" s:BaseTag.getClosedParentTline() {{{3
+function! s:BaseTag.getClosedParentTline() dict
+ let tagline = self.tline
+ let fileinfo = self.fileinfo
+
+ let parent = self.parent
+ while !empty(parent)
+ if parent.isFolded()
+ let tagline = parent.tline
+ break
+ endif
+ let parent = parent.parent
+ endwhile
+
+ return tagline
+endfunction
+
+" s:BaseTag.isFoldable() {{{3
+function! s:BaseTag.isFoldable() dict
+ return has_key(self, 'children') && !empty(self.children)
+endfunction
+
+" s:BaseTag.isFolded() {{{3
+function! s:BaseTag.isFolded() dict
+ return self.fileinfo.tagfolds[self.fields.kind][self.fullpath]
+endfunction
+
+" s:BaseTag.openFold() {{{3
+function! s:BaseTag.openFold() dict
+ if self.isFoldable()
+ let self.fileinfo.tagfolds[self.fields.kind][self.fullpath] = 0
+ endif
+endfunction
+
+" s:BaseTag.closeFold() {{{3
+function! s:BaseTag.closeFold() dict
+ let newline = line('.')
+
+ if !empty(self.parent) && self.parent.isKindheader()
+ " Tag is child of generic 'kind'
+ call self.parent.closeFold()
+ let newline = self.parent.tline
+ elseif self.isFoldable() && !self.isFolded()
+ " Tag is parent of a scope and is not folded
+ let self.fileinfo.tagfolds[self.fields.kind][self.fullpath] = 1
+ let newline = self.tline
+ elseif !empty(self.parent)
+ " Tag is normal child, so close parent
+ let parent = self.parent
+ let self.fileinfo.tagfolds[parent.fields.kind][parent.fullpath] = 1
+ let newline = parent.tline
+ endif
+
+ return newline
+endfunction
+
+" s:BaseTag.setFolded() {{{3
+function! s:BaseTag.setFolded(folded) dict
+ let self.fileinfo.tagfolds[self.fields.kind][self.fullpath] = a:folded
+endfunction
+
+" s:BaseTag.openParents() {{{3
+function! s:BaseTag.openParents() dict
+ let parent = self.parent
+
+ while !empty(parent)
+ call parent.openFold()
+ let parent = parent.parent
+ endwhile
+endfunction
+
+" Normal tag {{{2
+let s:NormalTag = copy(s:BaseTag)
+
+" s:NormalTag.New() {{{3
+function! s:NormalTag.New(name) dict
+ let newobj = copy(self)
+
+ call newobj._init(a:name)
+
+ return newobj
+endfunction
+
+" s:NormalTag.isNormalTag() {{{3
+function! s:NormalTag.isNormalTag() dict
+ return 1
+endfunction
+
+" s:NormalTag.str() {{{3
+function! s:NormalTag.str() dict
+ let fileinfo = self.fileinfo
+ let typeinfo = s:known_types[fileinfo.ftype]
+
+ let suffix = get(self.fields, 'signature', '')
+ if has_key(self.fields, 'type')
+ let suffix .= ' : ' . self.fields.type
+ elseif has_key(typeinfo, 'kind2scope') &&
+ \ has_key(typeinfo.kind2scope, self.fields.kind)
+ let suffix .= ' : ' . typeinfo.kind2scope[self.fields.kind]
+ endif
+
+ return self._getPrefix() . self.name . suffix . "\n"
+endfunction
+
+" s:NormalTag.getPrototype() {{{3
+function! s:NormalTag.getPrototype() dict
+ return self.prototype
+endfunction
+
+" Pseudo tag {{{2
+let s:PseudoTag = copy(s:BaseTag)
+
+" s:PseudoTag.New() {{{3
+function! s:PseudoTag.New(name) dict
+ let newobj = copy(self)
+
+ call newobj._init(a:name)
+
+ return newobj
+endfunction
+
+" s:PseudoTag.isPseudoTag() {{{3
+function! s:PseudoTag.isPseudoTag() dict
+ return 1
+endfunction
+
+" s:PseudoTag.str() {{{3
+function! s:PseudoTag.str() dict
+ let fileinfo = self.fileinfo
+ let typeinfo = s:known_types[fileinfo.ftype]
+
+ let suffix = get(self.fields, 'signature', '')
+ if has_key(typeinfo.kind2scope, self.fields.kind)
+ let suffix .= ' : ' . typeinfo.kind2scope[self.fields.kind]
+ endif
+
+ return self._getPrefix() . self.name . '*' . suffix
+endfunction
+
+" Kind header {{{2
+let s:KindheaderTag = copy(s:BaseTag)
+
+" s:KindheaderTag.New() {{{3
+function! s:KindheaderTag.New(name) dict
+ let newobj = copy(self)
+
+ call newobj._init(a:name)
+
+ return newobj
+endfunction
+
+" s:KindheaderTag.isKindheader() {{{3
+function! s:KindheaderTag.isKindheader() dict
+ return 1
+endfunction
+
+" s:KindheaderTag.getPrototype() {{{3
+function! s:KindheaderTag.getPrototype() dict
+ return self.name . ': ' .
+ \ self.numtags . ' ' . (self.numtags > 1 ? 'tags' : 'tag')
+endfunction
+
+" s:KindheaderTag.isFoldable() {{{3
+function! s:KindheaderTag.isFoldable() dict
+ return 1
+endfunction
+
+" s:KindheaderTag.isFolded() {{{3
+function! s:KindheaderTag.isFolded() dict
+ return self.fileinfo.kindfolds[self.short]
+endfunction
+
+" s:KindheaderTag.openFold() {{{3
+function! s:KindheaderTag.openFold() dict
+ let self.fileinfo.kindfolds[self.short] = 0
+endfunction
+
+" s:KindheaderTag.closeFold() {{{3
+function! s:KindheaderTag.closeFold() dict
+ let self.fileinfo.kindfolds[self.short] = 1
+ return line('.')
+endfunction
+
+" s:KindheaderTag.toggleFold() {{{3
+function! s:KindheaderTag.toggleFold() dict
+ let fileinfo = s:known_files.getCurrent()
+
+ let fileinfo.kindfolds[self.short] = !fileinfo.kindfolds[self.short]
+endfunction
+
+" File info {{{2
+let s:FileInfo = {}
+
+" s:FileInfo.New() {{{3
+function! s:FileInfo.New(fname, ftype) dict
+ let newobj = copy(self)
+
+ " The complete file path
+ let newobj.fpath = a:fname
+
+ " File modification time
+ let newobj.mtime = getftime(a:fname)
+
+ " The vim file type
+ let newobj.ftype = a:ftype
+
+ " List of the tags that are present in the file, sorted according to the
+ " value of 'g:tagbar_sort'
+ let newobj.tags = []
+
+ " Dictionary of the tags, indexed by line number in the file
+ let newobj.fline = {}
+
+ " Dictionary of the tags, indexed by line number in the tagbar
+ let newobj.tline = {}
+
+ " Dictionary of the folding state of 'kind's, indexed by short name
+ let newobj.kindfolds = {}
+ let typeinfo = s:known_types[a:ftype]
+ " copy the default fold state from the type info
+ for kind in typeinfo.kinds
+ let newobj.kindfolds[kind.short] =
+ \ g:tagbar_foldlevel == 0 ? 1 : kind.fold
+ endfor
+
+ " Dictionary of dictionaries of the folding state of individual tags,
+ " indexed by kind and full path
+ let newobj.tagfolds = {}
+ for kind in typeinfo.kinds
+ let newobj.tagfolds[kind.short] = {}
+ endfor
+
+ " The current foldlevel of the file
+ let newobj.foldlevel = g:tagbar_foldlevel
+
+ return newobj
+endfunction
+
+" s:FileInfo.reset() {{{3
+" Reset stuff that gets regenerated while processing a file and save the old
+" tag folds
+function! s:FileInfo.reset() dict
+ let self.mtime = getftime(self.fpath)
+ let self.tags = []
+ let self.fline = {}
+ let self.tline = {}
+
+ let self._tagfolds_old = self.tagfolds
+ let self.tagfolds = {}
+
+ let typeinfo = s:known_types[self.ftype]
+ for kind in typeinfo.kinds
+ let self.tagfolds[kind.short] = {}
+ endfor
+endfunction
+
+" s:FileInfo.clearOldFolds() {{{3
+function! s:FileInfo.clearOldFolds() dict
+ if exists('self._tagfolds_old')
+ unlet self._tagfolds_old
+ endif
+endfunction
+
+" s:FileInfo.sortTags() {{{3
+function! s:FileInfo.sortTags() dict
+ if has_key(s:compare_typeinfo, 'sort')
+ if s:compare_typeinfo.sort
+ call s:SortTags(self.tags, 's:CompareByKind')
+ else
+ call s:SortTags(self.tags, 's:CompareByLine')
+ endif
+ elseif g:tagbar_sort
+ call s:SortTags(self.tags, 's:CompareByKind')
+ else
+ call s:SortTags(self.tags, 's:CompareByLine')
+ endif
+endfunction
+
+" s:FileInfo.openKindFold() {{{3
+function! s:FileInfo.openKindFold(kind) dict
+ let self.kindfolds[a:kind.short] = 0
+endfunction
+
+" s:FileInfo.closeKindFold() {{{3
+function! s:FileInfo.closeKindFold(kind) dict
+ let self.kindfolds[a:kind.short] = 1
+endfunction
+
+" Known files {{{2
+let s:known_files = {
+ \ '_current' : {},
+ \ '_files' : {}
+\ }
+
+" s:known_files.getCurrent() {{{3
+function! s:known_files.getCurrent() dict
+ return self._current
+endfunction
+
+" s:known_files.setCurrent() {{{3
+function! s:known_files.setCurrent(fileinfo) dict
+ let self._current = a:fileinfo
+endfunction
+
+" s:known_files.get() {{{3
+function! s:known_files.get(fname) dict
+ return get(self._files, a:fname, {})
+endfunction
+
+" s:known_files.put() {{{3
+" Optional second argument is the filename
+function! s:known_files.put(fileinfo, ...) dict
+ if a:0 == 1
+ let self._files[a:1] = a:fileinfo
+ else
+ let fname = a:fileinfo.fpath
+ let self._files[fname] = a:fileinfo
+ endif
+endfunction
+
+" s:known_files.has() {{{3
+function! s:known_files.has(fname) dict
+ return has_key(self._files, a:fname)
+endfunction
+
+" s:known_files.rm() {{{3
+function! s:known_files.rm(fname) dict
+ if s:known_files.has(a:fname)
+ call remove(self._files, a:fname)
+ endif
+endfunction
+
+" Window management {{{1
+" s:ToggleWindow() {{{2
+function! s:ToggleWindow()
+ let tagbarwinnr = bufwinnr("__Tagbar__")
+ if tagbarwinnr != -1
+ call s:CloseWindow()
+ return
+ endif
+
+ call s:OpenWindow('')
+endfunction
+
+" s:OpenWindow() {{{2
+function! s:OpenWindow(flags)
+ let autofocus = a:flags =~# 'f'
+ let jump = a:flags =~# 'j'
+ let autoclose = a:flags =~# 'c'
+
+ " If the tagbar window is already open check jump flag
+ " Also set the autoclose flag if requested
+ let tagbarwinnr = bufwinnr('__Tagbar__')
+ if tagbarwinnr != -1
+ if winnr() != tagbarwinnr && jump
+ execute tagbarwinnr . 'wincmd w'
+ if autoclose
+ let w:autoclose = autoclose
+ endif
+ endif
+ return
+ endif
+
+ call s:Init()
+
+ " Expand the Vim window to accomodate for the Tagbar window if requested
+ if g:tagbar_expand && !s:window_expanded && has('gui_running')
+ let &columns += g:tagbar_width + 1
+ let s:window_expanded = 1
+ endif
+
+ let eventignore_save = &eventignore
+ set eventignore=all
+
+ let openpos = g:tagbar_left ? 'topleft vertical ' : 'botright vertical '
+ exe 'silent keepalt ' . openpos . g:tagbar_width . 'split ' . '__Tagbar__'
+
+ let &eventignore = eventignore_save
+
+ call s:InitWindow(autoclose)
+
+ wincmd p
+
+ " Jump back to the tagbar window if autoclose or autofocus is set. Can't
+ " just stay in it since it wouldn't trigger the update event
+ if g:tagbar_autoclose || autofocus || g:tagbar_autofocus
+ let tagbarwinnr = bufwinnr('__Tagbar__')
+ execute tagbarwinnr . 'wincmd w'
+ endif
+endfunction
+
+" s:InitWindow() {{{2
+function! s:InitWindow(autoclose)
+ setlocal noreadonly " in case the "view" mode is used
+ setlocal buftype=nofile
+ setlocal bufhidden=hide
+ setlocal noswapfile
+ setlocal nobuflisted
+ setlocal nomodifiable
+ setlocal filetype=tagbar
+ setlocal nolist
+ setlocal nonumber
+ setlocal nowrap
+ setlocal winfixwidth
+ setlocal textwidth=0
+ setlocal nocursorline
+ setlocal nocursorcolumn
+
+ if exists('+relativenumber')
+ setlocal norelativenumber
+ endif
+
+ setlocal nofoldenable
+ setlocal foldcolumn=0
+ " Reset fold settings in case a plugin set them globally to something
+ " expensive. Apparently 'foldexpr' gets executed even if 'foldenable' is
+ " off, and then for every appended line (like with :put).
+ setlocal foldmethod&
+ setlocal foldexpr&
+
+ " Earlier versions have a bug in local, evaluated statuslines
+ if v:version > 701 || (v:version == 701 && has('patch097'))
+ setlocal statusline=%!TagbarGenerateStatusline()
+ else
+ setlocal statusline=Tagbar
+ endif
+
+ " Script-local variable needed since compare functions can't
+ " take extra arguments
+ let s:compare_typeinfo = {}
+
+ let s:is_maximized = 0
+ let s:short_help = 1
+
+ let w:autoclose = a:autoclose
+
+ if has('balloon_eval')
+ setlocal balloonexpr=TagbarBalloonExpr()
+ set ballooneval
+ endif
+
+ let cpoptions_save = &cpoptions
+ set cpoptions&vim
+
+ if !hasmapto('JumpToTag', 'n')
+ call s:MapKeys()
+ endif
+
+ if !s:autocommands_done
+ call s:CreateAutocommands()
+ endif
+
+ let &cpoptions = cpoptions_save
+endfunction
+
+" s:CloseWindow() {{{2
+function! s:CloseWindow()
+ let tagbarwinnr = bufwinnr('__Tagbar__')
+ if tagbarwinnr == -1
+ return
+ endif
+
+ let tagbarbufnr = winbufnr(tagbarwinnr)
+
+ if winnr() == tagbarwinnr
+ if winbufnr(2) != -1
+ " Other windows are open, only close the tagbar one
+ close
+ wincmd p
+ endif
+ else
+ " Go to the tagbar window, close it and then come back to the
+ " original window
+ let curbufnr = bufnr('%')
+ execute tagbarwinnr . '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
+
+ " If the Vim window has been expanded, and Tagbar is not open in any other
+ " tabpages, shrink the window again
+ if s:window_expanded
+ let tablist = []
+ for i in range(tabpagenr('$'))
+ call extend(tablist, tabpagebuflist(i + 1))
+ endfor
+
+ if index(tablist, tagbarbufnr) == -1
+ let &columns -= g:tagbar_width + 1
+ let s:window_expanded = 0
+ endif
+ endif
+endfunction
+
+" s:ZoomWindow() {{{2
+function! s:ZoomWindow()
+ if s:is_maximized
+ execute 'vert resize ' . g:tagbar_width
+ let s:is_maximized = 0
+ else
+ vert resize
+ let s:is_maximized = 1
+ endif
+endfunction
+
+" Tag processing {{{1
+" s:ProcessFile() {{{2
+" Execute ctags and put the information into a 'FileInfo' object
+function! s:ProcessFile(fname, ftype)
+ call s:LogDebugMessage('ProcessFile called on ' . a:fname)
+
+ if !s:IsValidFile(a:fname, a:ftype)
+ call s:LogDebugMessage('Not a valid file, returning')
+ return
+ endif
+
+ let ctags_output = s:ExecuteCtagsOnFile(a:fname, a:ftype)
+
+ if ctags_output == -1
+ call s:LogDebugMessage('Ctags error when processing file')
+ " put an empty entry into known_files so the error message is only
+ " shown once
+ call s:known_files.put({}, a:fname)
+ return
+ elseif ctags_output == ''
+ call s:LogDebugMessage('Ctags output empty')
+ return
+ endif
+
+ " If the file has only been updated preserve the fold states, otherwise
+ " create a new entry
+ if s:known_files.has(a:fname)
+ let fileinfo = s:known_files.get(a:fname)
+ call fileinfo.reset()
+ else
+ let fileinfo = s:FileInfo.New(a:fname, a:ftype)
+ endif
+
+ let typeinfo = s:known_types[a:ftype]
+
+ " Parse the ctags output lines
+ call s:LogDebugMessage('Parsing ctags output')
+ let rawtaglist = split(ctags_output, '\n\+')
+ for line in rawtaglist
+ " skip comments
+ if line =~# '^!_TAG_'
+ continue
+ endif
+
+ let parts = split(line, ';"')
+ if len(parts) == 2 " Is a valid tag line
+ let taginfo = s:ParseTagline(parts[0], parts[1], typeinfo, fileinfo)
+ let fileinfo.fline[taginfo.fields.line] = taginfo
+ call add(fileinfo.tags, taginfo)
+ endif
+ endfor
+
+ " Process scoped tags
+ let processedtags = []
+ if has_key(typeinfo, 'kind2scope')
+ call s:LogDebugMessage('Processing scoped tags')
+
+ let scopedtags = []
+ let is_scoped = 'has_key(typeinfo.kind2scope, v:val.fields.kind) ||
+ \ has_key(v:val, "scope")'
+ let scopedtags += filter(copy(fileinfo.tags), is_scoped)
+ call filter(fileinfo.tags, '!(' . is_scoped . ')')
+
+ call s:AddScopedTags(scopedtags, processedtags, {}, 0,
+ \ typeinfo, fileinfo)
+
+ if !empty(scopedtags)
+ echoerr 'Tagbar: ''scopedtags'' not empty after processing,'
+ \ 'this should never happen!'
+ \ 'Please contact the script maintainer with an example.'
+ endif
+ endif
+ call s:LogDebugMessage('Number of top-level tags: ' . len(processedtags))
+
+ " Create a placeholder tag for the 'kind' header for folding purposes
+ for kind in typeinfo.kinds
+
+ let curtags = filter(copy(fileinfo.tags),
+ \ 'v:val.fields.kind ==# kind.short')
+ call s:LogDebugMessage('Processing kind: ' . kind.short .
+ \ ', number of tags: ' . len(curtags))
+
+ if empty(curtags)
+ continue
+ endif
+
+ let kindtag = s:KindheaderTag.New(kind.long)
+ let kindtag.short = kind.short
+ let kindtag.numtags = len(curtags)
+ let kindtag.fileinfo = fileinfo
+
+ for tag in curtags
+ let tag.parent = kindtag
+ endfor
+ endfor
+
+ if !empty(processedtags)
+ call extend(fileinfo.tags, processedtags)
+ endif
+
+ " Clear old folding information from previous file version to prevent leaks
+ call fileinfo.clearOldFolds()
+
+ " Sort the tags
+ let s:compare_typeinfo = typeinfo
+ call fileinfo.sortTags()
+
+ call s:known_files.put(fileinfo)
+endfunction
+
+" s:ExecuteCtagsOnFile() {{{2
+function! s:ExecuteCtagsOnFile(fname, ftype)
+ call s:LogDebugMessage('ExecuteCtagsOnFile called on ' . a:fname)
+
+ let typeinfo = s:known_types[a:ftype]
+
+ if has_key(typeinfo, 'ctagsargs')
+ let ctags_args = ' ' . typeinfo.ctagsargs . ' '
+ else
+ let ctags_args = ' -f - '
+ let ctags_args .= ' --format=2 '
+ let ctags_args .= ' --excmd=pattern '
+ let ctags_args .= ' --fields=nksSa '
+ let ctags_args .= ' --extra= '
+ let ctags_args .= ' --sort=yes '
+
+ " Include extra type definitions
+ if has_key(typeinfo, 'deffile')
+ let ctags_args .= ' --options=' . typeinfo.deffile . ' '
+ endif
+
+ let ctags_type = typeinfo.ctagstype
+
+ let ctags_kinds = ''
+ for kind in typeinfo.kinds
+ let ctags_kinds .= kind.short
+ endfor
+
+ let ctags_args .= ' --language-force=' . ctags_type .
+ \ ' --' . ctags_type . '-kinds=' . ctags_kinds . ' '
+ endif
+
+ if has_key(typeinfo, 'ctagsbin')
+ " reset 'wildignore' temporarily in case *.exe is included in it
+ let wildignore_save = &wildignore
+ set wildignore&
+ let ctags_bin = expand(typeinfo.ctagsbin)
+ let &wildignore = wildignore_save
+ else
+ let ctags_bin = g:tagbar_ctags_bin
+ endif
+
+ let ctags_cmd = s:EscapeCtagsCmd(ctags_bin, ctags_args, a:fname)
+ if ctags_cmd == ''
+ return ''
+ endif
+
+ let ctags_output = s:ExecuteCtags(ctags_cmd)
+
+ if v:shell_error || ctags_output =~ 'Warning: cannot open source file'
+ echoerr 'Tagbar: Could not execute ctags for ' . a:fname . '!'
+ echomsg 'Executed command: "' . ctags_cmd . '"'
+ if !empty(ctags_output)
+ call s:LogDebugMessage('Command output:')
+ call s:LogDebugMessage(ctags_output)
+ echomsg 'Command output:'
+ for line in split(ctags_output, '\n')
+ echomsg line
+ endfor
+ endif
+ return -1
+ endif
+
+ call s:LogDebugMessage('Ctags executed successfully')
+ return ctags_output
+endfunction
+
+" s:ParseTagline() {{{2
+" Structure of a tag line:
+" tagname<TAB>filename<TAB>expattern;"fields
+" fields: <TAB>name:value
+" fields that are always present: kind, line
+function! s:ParseTagline(part1, part2, typeinfo, fileinfo)
+ let basic_info = split(a:part1, '\t')
+
+ let taginfo = s:NormalTag.New(basic_info[0])
+ let taginfo.file = basic_info[1]
+
+ " the pattern can contain tabs and thus may have been split up, so join
+ " the rest of the items together again
+ let pattern = join(basic_info[2:], "\t")
+ let start = 2 " skip the slash and the ^
+ let end = strlen(pattern) - 1
+ if pattern[end - 1] ==# '$'
+ let end -= 1
+ let dollar = '\$'
+ else
+ let dollar = ''
+ endif
+ let pattern = strpart(pattern, start, end - start)
+ let taginfo.pattern = '\V\^\C' . pattern . dollar
+ let prototype = substitute(pattern, '^[[:space:]]\+', '', '')
+ let prototype = substitute(prototype, '[[:space:]]\+$', '', '')
+ let taginfo.prototype = prototype
+
+ let fields = split(a:part2, '\t')
+ let taginfo.fields.kind = remove(fields, 0)
+ for field in fields
+ " can't use split() since the value can contain ':'
+ let delimit = stridx(field, ':')
+ let key = strpart(field, 0, delimit)
+ let val = strpart(field, delimit + 1)
+ if len(val) > 0
+ let taginfo.fields[key] = val
+ endif
+ endfor
+ " Needed for jsctags
+ if has_key(taginfo.fields, 'lineno')
+ let taginfo.fields.line = taginfo.fields.lineno
+ endif
+
+ " Make some information easier accessible
+ if has_key(a:typeinfo, 'scope2kind')
+ for scope in keys(a:typeinfo.scope2kind)
+ if has_key(taginfo.fields, scope)
+ let taginfo.scope = scope
+ let taginfo.path = taginfo.fields[scope]
+
+ let taginfo.fullpath = taginfo.path . a:typeinfo.sro .
+ \ taginfo.name
+ break
+ endif
+ endfor
+ let taginfo.depth = len(split(taginfo.path, '\V' . a:typeinfo.sro))
+ endif
+
+ let taginfo.fileinfo = a:fileinfo
+
+ " Needed for folding
+ try
+ call taginfo.initFoldState()
+ catch /^Vim(\a\+):E716:/ " 'Key not present in Dictionary'
+ " The tag has a 'kind' that doesn't exist in the type definition
+ echoerr 'Your ctags and Tagbar configurations are out of sync!'
+ \ 'Please read '':help tagbar-extend''.'
+ endtry
+
+ return taginfo
+endfunction
+
+" s:AddScopedTags() {{{2
+" Recursively process tags. Unfortunately there is a problem: not all tags in
+" a hierarchy are actually there. For example, in C++ a class can be defined
+" in a header file and implemented in a .cpp file (so the class itself doesn't
+" appear in the .cpp file and thus doesn't generate a tag). Another example
+" are anonymous structures like namespaces, structs, enums, and unions, that
+" also don't get a tag themselves. These tags are thus called 'pseudo-tags' in
+" Tagbar. Properly parsing them is quite tricky, so try not to think about it
+" too much.
+function! s:AddScopedTags(tags, processedtags, parent, depth,
+ \ typeinfo, fileinfo)
+ if !empty(a:parent)
+ let curpath = a:parent.fullpath
+ let pscope = a:typeinfo.kind2scope[a:parent.fields.kind]
+ else
+ let curpath = ''
+ let pscope = ''
+ endif
+
+ let is_cur_tag = 'v:val.depth == a:depth'
+
+ if !empty(curpath)
+ " Check whether the tag is either a direct child at the current depth
+ " or at least a proper grandchild with pseudo-tags in between. If it
+ " is a direct child also check for matching scope.
+ let is_cur_tag .= ' &&
+ \ (v:val.path ==# curpath ||
+ \ match(v:val.path, ''\V\^\C'' . curpath . a:typeinfo.sro) == 0) &&
+ \ (v:val.path ==# curpath ? (v:val.scope ==# pscope) : 1)'
+ endif
+
+ let curtags = filter(copy(a:tags), is_cur_tag)
+
+ if !empty(curtags)
+ call filter(a:tags, '!(' . is_cur_tag . ')')
+
+ let realtags = []
+ let pseudotags = []
+
+ while !empty(curtags)
+ let tag = remove(curtags, 0)
+
+ if tag.path != curpath
+ " tag is child of a pseudo-tag, so create a new pseudo-tag and
+ " add all its children to it
+ let pseudotag = s:ProcessPseudoTag(curtags, tag, a:parent,
+ \ a:typeinfo, a:fileinfo)
+
+ call add(pseudotags, pseudotag)
+ else
+ call add(realtags, tag)
+ endif
+ endwhile
+
+ " Recursively add the children of the tags on the current level
+ for tag in realtags
+ let tag.parent = a:parent
+
+ if !has_key(a:typeinfo.kind2scope, tag.fields.kind)
+ continue
+ endif
+
+ if !has_key(tag, 'children')
+ let tag.children = []
+ endif
+
+ call s:AddScopedTags(a:tags, tag.children, tag, a:depth + 1,
+ \ a:typeinfo, a:fileinfo)
+ endfor
+ call extend(a:processedtags, realtags)
+
+ " Recursively add the children of the tags that are children of the
+ " pseudo-tags on the current level
+ for tag in pseudotags
+ call s:ProcessPseudoChildren(a:tags, tag, a:depth, a:typeinfo,
+ \ a:fileinfo)
+ endfor
+ call extend(a:processedtags, pseudotags)
+ endif
+
+ " Now we have to check if there are any pseudo-tags at the current level
+ " so we have to check for real tags at a lower level, i.e. grandchildren
+ let is_grandchild = 'v:val.depth > a:depth'
+
+ if !empty(curpath)
+ let is_grandchild .=
+ \ ' && match(v:val.path, ''\V\^\C'' . curpath . a:typeinfo.sro) == 0'
+ endif
+
+ let grandchildren = filter(copy(a:tags), is_grandchild)
+
+ if !empty(grandchildren)
+ call s:AddScopedTags(a:tags, a:processedtags, a:parent, a:depth + 1,
+ \ a:typeinfo, a:fileinfo)
+ endif
+endfunction
+
+" s:ProcessPseudoTag() {{{2
+function! s:ProcessPseudoTag(curtags, tag, parent, typeinfo, fileinfo)
+ let curpath = !empty(a:parent) ? a:parent.fullpath : ''
+
+ let pseudoname = substitute(a:tag.path, curpath, '', '')
+ let pseudoname = substitute(pseudoname, '\V\^' . a:typeinfo.sro, '', '')
+ let pseudotag = s:CreatePseudoTag(pseudoname, a:parent, a:tag.scope,
+ \ a:typeinfo, a:fileinfo)
+ let pseudotag.children = [a:tag]
+
+ " get all the other (direct) children of the current pseudo-tag
+ let ispseudochild = 'v:val.path ==# a:tag.path && v:val.scope ==# a:tag.scope'
+ let pseudochildren = filter(copy(a:curtags), ispseudochild)
+ if !empty(pseudochildren)
+ call filter(a:curtags, '!(' . ispseudochild . ')')
+ call extend(pseudotag.children, pseudochildren)
+ endif
+
+ return pseudotag
+endfunction
+
+" s:ProcessPseudoChildren() {{{2
+function! s:ProcessPseudoChildren(tags, tag, depth, typeinfo, fileinfo)
+ for childtag in a:tag.children
+ let childtag.parent = a:tag
+
+ if !has_key(a:typeinfo.kind2scope, childtag.fields.kind)
+ continue
+ endif
+
+ if !has_key(childtag, 'children')
+ let childtag.children = []
+ endif
+
+ call s:AddScopedTags(a:tags, childtag.children, childtag, a:depth + 1,
+ \ a:typeinfo, a:fileinfo)
+ endfor
+
+ let is_grandchild = 'v:val.depth > a:depth &&
+ \ match(v:val.path, ''^\C'' . a:tag.fullpath) == 0'
+ let grandchildren = filter(copy(a:tags), is_grandchild)
+ if !empty(grandchildren)
+ call s:AddScopedTags(a:tags, a:tag.children, a:tag, a:depth + 1,
+ \ a:typeinfo, a:fileinfo)
+ endif
+endfunction
+
+" s:CreatePseudoTag() {{{2
+function! s:CreatePseudoTag(name, parent, scope, typeinfo, fileinfo)
+ if !empty(a:parent)
+ let curpath = a:parent.fullpath
+ let pscope = a:typeinfo.kind2scope[a:parent.fields.kind]
+ else
+ let curpath = ''
+ let pscope = ''
+ endif
+
+ let pseudotag = s:PseudoTag.New(a:name)
+ let pseudotag.fields.kind = a:typeinfo.scope2kind[a:scope]
+
+ let parentscope = substitute(curpath, a:name . '$', '', '')
+ let parentscope = substitute(parentscope,
+ \ '\V\^' . a:typeinfo.sro . '\$', '', '')
+
+ if pscope != ''
+ let pseudotag.fields[pscope] = parentscope
+ let pseudotag.scope = pscope
+ let pseudotag.path = parentscope
+ let pseudotag.fullpath =
+ \ pseudotag.path . a:typeinfo.sro . pseudotag.name
+ endif
+ let pseudotag.depth = len(split(pseudotag.path, '\V' . a:typeinfo.sro))
+
+ let pseudotag.parent = a:parent
+
+ let pseudotag.fileinfo = a:fileinfo
+
+ call pseudotag.initFoldState()
+
+ return pseudotag
+endfunction
+
+" Sorting {{{1
+" s:SortTags() {{{2
+function! s:SortTags(tags, comparemethod)
+ call sort(a:tags, a:comparemethod)
+
+ for tag in a:tags
+ if has_key(tag, 'children')
+ call s:SortTags(tag.children, a:comparemethod)
+ endif
+ endfor
+endfunction
+
+" s:CompareByKind() {{{2
+function! s:CompareByKind(tag1, tag2)
+ let typeinfo = s:compare_typeinfo
+
+ if typeinfo.kinddict[a:tag1.fields.kind] <#
+ \ typeinfo.kinddict[a:tag2.fields.kind]
+ return -1
+ elseif typeinfo.kinddict[a:tag1.fields.kind] >#
+ \ typeinfo.kinddict[a:tag2.fields.kind]
+ return 1
+ else
+ " Ignore '~' prefix for C++ destructors to sort them directly under
+ " the constructors
+ if a:tag1.name[0] ==# '~'
+ let name1 = a:tag1.name[1:]
+ else
+ let name1 = a:tag1.name
+ endif
+ if a:tag2.name[0] ==# '~'
+ let name2 = a:tag2.name[1:]
+ else
+ let name2 = a:tag2.name
+ endif
+
+ if name1 <=# name2
+ return -1
+ else
+ return 1
+ endif
+ endif
+endfunction
+
+" s:CompareByLine() {{{2
+function! s:CompareByLine(tag1, tag2)
+ return a:tag1.fields.line - a:tag2.fields.line
+endfunction
+
+" s:ToggleSort() {{{2
+function! s:ToggleSort()
+ let fileinfo = s:known_files.getCurrent()
+ if empty(fileinfo)
+ return
+ endif
+
+ let curline = line('.')
+
+ match none
+
+ let s:compare_typeinfo = s:known_types[fileinfo.ftype]
+
+ if has_key(s:compare_typeinfo, 'sort')
+ let s:compare_typeinfo.sort = !s:compare_typeinfo.sort
+ else
+ let g:tagbar_sort = !g:tagbar_sort
+ endif
+
+ call fileinfo.sortTags()
+
+ call s:RenderContent()
+
+ execute curline
+endfunction
+
+" Display {{{1
+" s:RenderContent() {{{2
+function! s:RenderContent(...)
+ call s:LogDebugMessage('RenderContent called')
+
+ if a:0 == 1
+ let fileinfo = a:1
+ else
+ let fileinfo = s:known_files.getCurrent()
+ endif
+
+ if empty(fileinfo)
+ call s:LogDebugMessage('Empty fileinfo, returning')
+ return
+ endif
+
+ let tagbarwinnr = bufwinnr('__Tagbar__')
+
+ if &filetype == 'tagbar'
+ let in_tagbar = 1
+ else
+ let in_tagbar = 0
+ let prevwinnr = winnr()
+ execute tagbarwinnr . 'wincmd w'
+ endif
+
+ if !empty(s:known_files.getCurrent()) &&
+ \ fileinfo.fpath ==# s:known_files.getCurrent().fpath
+ " We're redisplaying the same file, so save the view
+ call s:LogDebugMessage('Redisplaying file' . fileinfo.fpath)
+ let saveline = line('.')
+ let savecol = col('.')
+ let topline = line('w0')
+ endif
+
+ let lazyredraw_save = &lazyredraw
+ set lazyredraw
+ let eventignore_save = &eventignore
+ set eventignore=all
+
+ setlocal modifiable
+
+ silent %delete _
+
+ call s:PrintHelp()
+
+ let typeinfo = s:known_types[fileinfo.ftype]
+
+ " Print tags
+ call s:PrintKinds(typeinfo, fileinfo)
+
+ " Delete empty lines at the end of the buffer
+ for linenr in range(line('$'), 1, -1)
+ if getline(linenr) =~ '^$'
+ execute 'silent ' . linenr . 'delete _'
+ else
+ break
+ endif
+ endfor
+
+ setlocal nomodifiable
+
+ if !empty(s:known_files.getCurrent()) &&
+ \ fileinfo.fpath ==# s:known_files.getCurrent().fpath
+ let scrolloff_save = &scrolloff
+ set scrolloff=0
+
+ call cursor(topline, 1)
+ normal! zt
+ call cursor(saveline, savecol)
+
+ let &scrolloff = scrolloff_save
+ else
+ " Make sure as much of the Tagbar content as possible is shown in the
+ " window by jumping to the top after drawing
+ execute 1
+ call winline()
+
+ " Invalidate highlight cache from old file
+ let s:last_highlight_tline = 0
+ endif
+
+ let &lazyredraw = lazyredraw_save
+ let &eventignore = eventignore_save
+
+ if !in_tagbar
+ execute prevwinnr . 'wincmd w'
+ endif
+endfunction
+
+" s:PrintKinds() {{{2
+function! s:PrintKinds(typeinfo, fileinfo)
+ call s:LogDebugMessage('PrintKinds called')
+
+ let first_tag = 1
+
+ for kind in a:typeinfo.kinds
+ let curtags = filter(copy(a:fileinfo.tags),
+ \ 'v:val.fields.kind ==# kind.short')
+ call s:LogDebugMessage('Printing kind: ' . kind.short .
+ \ ', number of (top-level) tags: ' . len(curtags))
+
+ if empty(curtags)
+ continue
+ endif
+
+ if has_key(a:typeinfo, 'kind2scope') &&
+ \ has_key(a:typeinfo.kind2scope, kind.short)
+ " Scoped tags
+ for tag in curtags
+ if g:tagbar_compact && first_tag && s:short_help
+ silent 0put =tag.str()
+ else
+ silent put =tag.str()
+ endif
+
+ " Save the current tagbar line in the tag for easy
+ " highlighting access
+ let curline = line('.')
+ let tag.tline = curline
+ let a:fileinfo.tline[curline] = tag
+
+ " Print children
+ if tag.isFoldable() && !tag.isFolded()
+ for ckind in a:typeinfo.kinds
+ let childtags = filter(copy(tag.children),
+ \ 'v:val.fields.kind ==# ckind.short')
+ if len(childtags) > 0
+ " Print 'kind' header of following children
+ if !has_key(a:typeinfo.kind2scope, ckind.short)
+ silent put =' [' . ckind.long . ']'
+ let a:fileinfo.tline[line('.')] = tag
+ endif
+ for childtag in childtags
+ call s:PrintTag(childtag, 1,
+ \ a:fileinfo, a:typeinfo)
+ endfor
+ endif
+ endfor
+ endif
+
+ if !g:tagbar_compact
+ silent put _
+ endif
+
+ let first_tag = 0
+ endfor
+ else
+ " Non-scoped tags
+ let kindtag = curtags[0].parent
+
+ if kindtag.isFolded()
+ let foldmarker = s:icon_closed
+ else
+ let foldmarker = s:icon_open
+ endif
+
+ if g:tagbar_compact && first_tag && s:short_help
+ silent 0put =foldmarker . ' ' . kind.long
+ else
+ silent put =foldmarker . ' ' . kind.long
+ endif
+
+ let curline = line('.')
+ let kindtag.tline = curline
+ let a:fileinfo.tline[curline] = kindtag
+
+ if !kindtag.isFolded()
+ for tag in curtags
+ let str = tag.str()
+ silent put =' ' . str
+
+ " Save the current tagbar line in the tag for easy
+ " highlighting access
+ let curline = line('.')
+ let tag.tline = curline
+ let a:fileinfo.tline[curline] = tag
+ let tag.depth = 1
+ endfor
+ endif
+
+ if !g:tagbar_compact
+ silent put _
+ endif
+
+ let first_tag = 0
+ endif
+ endfor
+endfunction
+
+" s:PrintTag() {{{2
+function! s:PrintTag(tag, depth, fileinfo, typeinfo)
+ " Print tag indented according to depth
+ silent put =repeat(' ', a:depth * 2) . a:tag.str()
+
+ " Save the current tagbar line in the tag for easy
+ " highlighting access
+ let curline = line('.')
+ let a:tag.tline = curline
+ let a:fileinfo.tline[curline] = a:tag
+
+ " Recursively print children
+ if a:tag.isFoldable() && !a:tag.isFolded()
+ for ckind in a:typeinfo.kinds
+ let childtags = filter(copy(a:tag.children),
+ \ 'v:val.fields.kind ==# ckind.short')
+ if len(childtags) > 0
+ " Print 'kind' header of following children
+ if !has_key(a:typeinfo.kind2scope, ckind.short)
+ silent put =' ' . repeat(' ', a:depth * 2) .
+ \ '[' . ckind.long . ']'
+ let a:fileinfo.tline[line('.')] = a:tag
+ endif
+ for childtag in childtags
+ call s:PrintTag(childtag, a:depth + 1,
+ \ a:fileinfo, a:typeinfo)
+ endfor
+ endif
+ endfor
+ endif
+endfunction
+
+" s:PrintHelp() {{{2
+function! s:PrintHelp()
+ if !g:tagbar_compact && s:short_help
+ silent 0put ='\" Press <F1> for help'
+ silent put _
+ elseif !s:short_help
+ silent 0put ='\" Tagbar keybindings'
+ silent put ='\"'
+ silent put ='\" --------- General ---------'
+ silent put ='\" <Enter> : Jump to tag definition'
+ silent put ='\" <Space> : Display tag prototype'
+ silent put ='\"'
+ silent put ='\" ---------- Folds ----------'
+ silent put ='\" +, zo : Open fold'
+ silent put ='\" -, zc : Close fold'
+ silent put ='\" o, za : Toggle fold'
+ silent put ='\" *, zR : Open all folds'
+ silent put ='\" =, zM : Close all folds'
+ silent put ='\"'
+ silent put ='\" ---------- Misc -----------'
+ silent put ='\" s : Toggle sort'
+ silent put ='\" x : Zoom window in/out'
+ silent put ='\" q : Close window'
+ silent put ='\" <F1> : Remove help'
+ silent put _
+ endif
+endfunction
+
+" s:RenderKeepView() {{{2
+" The gist of this function was taken from NERDTree by Martin Grenfell.
+function! s:RenderKeepView(...)
+ if a:0 == 1
+ let line = a:1
+ else
+ let line = line('.')
+ endif
+
+ let curcol = col('.')
+ let topline = line('w0')
+
+ call s:RenderContent()
+
+ let scrolloff_save = &scrolloff
+ set scrolloff=0
+
+ call cursor(topline, 1)
+ normal! zt
+ call cursor(line, curcol)
+
+ let &scrolloff = scrolloff_save
+
+ redraw
+endfunction
+
+" User actions {{{1
+" s:HighlightTag() {{{2
+function! s:HighlightTag()
+ let tagline = 0
+
+ let tag = s:GetNearbyTag()
+ if !empty(tag)
+ let tagline = tag.tline
+ endif
+
+ " Don't highlight the tag again if it's the same one as last time.
+ " This prevents the Tagbar window from jumping back after scrolling with
+ " the mouse.
+ if tagline == s:last_highlight_tline
+ return
+ else
+ let s:last_highlight_tline = tagline
+ endif
+
+ let eventignore_save = &eventignore
+ set eventignore=all
+
+ let tagbarwinnr = bufwinnr('__Tagbar__')
+ let prevwinnr = winnr()
+ execute tagbarwinnr . 'wincmd w'
+
+ match none
+
+ " No tag above cursor position so don't do anything
+ if tagline == 0
+ execute prevwinnr . 'wincmd w'
+ let &eventignore = eventignore_save
+ redraw
+ return
+ endif
+
+ if g:tagbar_autoshowtag
+ call s:OpenParents(tag)
+ endif
+
+ " Check whether the tag is inside a closed fold and highlight the parent
+ " instead in that case
+ let tagline = tag.getClosedParentTline()
+
+ " Go to the line containing the tag
+ execute tagline
+
+ " Make sure the tag is visible in the window
+ call winline()
+
+ let foldpat = '[' . s:icon_open . s:icon_closed . ' ]'
+ let pattern = '/^\%' . tagline . 'l\s*' . foldpat . '[-+# ]\zs[^( ]\+\ze/'
+ execute 'match TagbarHighlight ' . pattern
+
+ execute prevwinnr . 'wincmd w'
+
+ let &eventignore = eventignore_save
+
+ redraw
+endfunction
+
+" s:JumpToTag() {{{2
+function! s:JumpToTag(stay_in_tagbar)
+ let taginfo = s:GetTagInfo(line('.'), 1)
+
+ let autoclose = w:autoclose
+
+ if empty(taginfo) || has_key(taginfo, 'numtags')
+ return
+ endif
+
+ let tagbarwinnr = winnr()
+
+ let eventignore_save = &eventignore
+ set eventignore=all
+
+ " This elaborate construct will try to switch to the correct
+ " buffer/window; if the buffer isn't currently shown in a window it will
+ " open it in the first window with a non-special buffer in it
+ wincmd p
+ let filebufnr = bufnr(taginfo.fileinfo.fpath)
+ if bufnr('%') != filebufnr
+ let filewinnr = bufwinnr(filebufnr)
+ if filewinnr != -1
+ execute filewinnr . 'wincmd w'
+ else
+ for i in range(1, winnr('$'))
+ execute i . 'wincmd w'
+ if &buftype == ''
+ execute 'buffer ' . filebufnr
+ break
+ endif
+ endfor
+ endif
+ " To make ctrl-w_p work we switch between the Tagbar window and the
+ " correct window once
+ execute tagbarwinnr . 'wincmd w'
+ wincmd p
+ endif
+
+ " Mark current position so it can be jumped back to
+ mark '
+
+ " Jump to the line where the tag is defined. Don't use the search pattern
+ " since it doesn't take the scope into account and thus can fail if tags
+ " with the same name are defined in different scopes (e.g. classes)
+ execute taginfo.fields.line
+
+ " If the file has been changed but not saved, the tag may not be on the
+ " saved line anymore, so search for it in the vicinity of the saved line
+ if match(getline('.'), taginfo.pattern) == -1
+ let interval = 1
+ let forward = 1
+ while search(taginfo.pattern, 'W' . forward ? '' : 'b') == 0
+ if !forward
+ if interval > line('$')
+ break
+ else
+ let interval = interval * 2
+ endif
+ endif
+ let forward = !forward
+ endwhile
+ endif
+
+ " If the tag is on a different line after unsaved changes update the tag
+ " and file infos/objects
+ let curline = line('.')
+ if taginfo.fields.line != curline
+ let taginfo.fields.line = curline
+ let taginfo.fileinfo.fline[curline] = taginfo
+ endif
+
+ " Center the tag in the window
+ normal! z.
+
+ if foldclosed('.') != -1
+ .foldopen!
+ endif
+
+ redraw
+
+ let &eventignore = eventignore_save
+
+ if a:stay_in_tagbar
+ call s:HighlightTag()
+ execute tagbarwinnr . 'wincmd w'
+ elseif g:tagbar_autoclose || autoclose
+ call s:CloseWindow()
+ else
+ call s:HighlightTag()
+ endif
+endfunction
+
+" s:ShowPrototype() {{{2
+function! s:ShowPrototype()
+ let taginfo = s:GetTagInfo(line('.'), 1)
+
+ if empty(taginfo)
+ return
+ endif
+
+ echo taginfo.getPrototype()
+endfunction
+
+" s:ToggleHelp() {{{2
+function! s:ToggleHelp()
+ let s:short_help = !s:short_help
+
+ " Prevent highlighting from being off after adding/removing the help text
+ match none
+
+ call s:RenderContent()
+
+ execute 1
+ redraw
+endfunction
+
+" s:GotoNextToplevelTag() {{{2
+function! s:GotoNextToplevelTag(direction)
+ let curlinenr = line('.')
+ let newlinenr = line('.')
+
+ if a:direction == 1
+ let range = range(line('.') + 1, line('$'))
+ else
+ let range = range(line('.') - 1, 1, -1)
+ endif
+
+ for tmplinenr in range
+ let taginfo = s:GetTagInfo(tmplinenr, 0)
+
+ if empty(taginfo)
+ continue
+ elseif empty(taginfo.parent)
+ let newlinenr = tmplinenr
+ break
+ endif
+ endfor
+
+ if curlinenr != newlinenr
+ execute newlinenr
+ call winline()
+ endif
+
+ redraw
+endfunction
+
+" Folding {{{1
+" s:OpenFold() {{{2
+function! s:OpenFold()
+ let fileinfo = s:known_files.getCurrent()
+ if empty(fileinfo)
+ return
+ endif
+
+ let curline = line('.')
+
+ let tag = s:GetTagInfo(curline, 0)
+ if empty(tag)
+ return
+ endif
+
+ call tag.openFold()
+
+ call s:RenderKeepView()
+endfunction
+
+" s:CloseFold() {{{2
+function! s:CloseFold()
+ let fileinfo = s:known_files.getCurrent()
+ if empty(fileinfo)
+ return
+ endif
+
+ match none
+
+ let curline = line('.')
+
+ let curtag = s:GetTagInfo(curline, 0)
+ if empty(curtag)
+ return
+ endif
+
+ let newline = curtag.closeFold()
+
+ call s:RenderKeepView(newline)
+endfunction
+
+" s:ToggleFold() {{{2
+function! s:ToggleFold()
+ let fileinfo = s:known_files.getCurrent()
+ if empty(fileinfo)
+ return
+ endif
+
+ match none
+
+ let curtag = s:GetTagInfo(line('.'), 0)
+ if empty(curtag)
+ return
+ endif
+
+ let newline = line('.')
+
+ if curtag.isKindheader()
+ call curtag.toggleFold()
+ elseif curtag.isFoldable()
+ if curtag.isFolded()
+ call curtag.openFold()
+ else
+ let newline = curtag.closeFold()
+ endif
+ else
+ let newline = curtag.closeFold()
+ endif
+
+ call s:RenderKeepView(newline)
+endfunction
+
+" s:SetFoldLevel() {{{2
+function! s:SetFoldLevel(level)
+ if a:level < 0
+ echoerr 'Foldlevel can''t be negative'
+ return
+ endif
+
+ let fileinfo = s:known_files.getCurrent()
+ if empty(fileinfo)
+ return
+ endif
+
+ call s:SetFoldLevelRecursive(fileinfo, fileinfo.tags, a:level)
+
+ let typeinfo = s:known_types[fileinfo.ftype]
+
+ " Apply foldlevel to 'kind's
+ if a:level == 0
+ for kind in typeinfo.kinds
+ call fileinfo.closeKindFold(kind)
+ endfor
+ else
+ for kind in typeinfo.kinds
+ call fileinfo.openKindFold(kind)
+ endfor
+ endif
+
+ let fileinfo.foldlevel = a:level
+
+ call s:RenderContent()
+endfunction
+
+" s:SetFoldLevelRecursive() {{{2
+" Apply foldlevel to normal tags
+function! s:SetFoldLevelRecursive(fileinfo, tags, level)
+ for tag in a:tags
+ if tag.depth >= a:level
+ call tag.setFolded(1)
+ else
+ call tag.setFolded(0)
+ endif
+
+ if has_key(tag, 'children')
+ call s:SetFoldLevelRecursive(a:fileinfo, tag.children, a:level)
+ endif
+ endfor
+endfunction
+
+" s:OpenParents() {{{2
+function! s:OpenParents(...)
+ let tagline = 0
+
+ if a:0 == 1
+ let tag = a:1
+ else
+ let tag = s:GetNearbyTag()
+ endif
+
+ call tag.openParents()
+
+ call s:RenderKeepView()
+endfunction
+
+" Helper functions {{{1
+" s:CleanUp() {{{2
+function! s:CleanUp()
+ silent autocmd! TagbarAutoCmds
+
+ unlet s:is_maximized
+ unlet s:compare_typeinfo
+ unlet s:short_help
+endfunction
+
+" s:CleanupFileinfo() {{{2
+function! s:CleanupFileinfo(fname)
+ call s:known_files.rm(a:fname)
+endfunction
+
+" s:QuitIfOnlyWindow() {{{2
+function! s:QuitIfOnlyWindow()
+ " Before quitting Vim, delete the tagbar buffer so that
+ " the '0 mark is correctly set to the previous buffer.
+ if winbufnr(2) == -1
+ " Check if there is more than one tab page
+ if tabpagenr('$') == 1
+ bdelete
+ quit
+ else
+ close
+ endif
+ endif
+endfunction
+
+" s:AutoUpdate() {{{2
+function! s:AutoUpdate(fname)
+ call s:LogDebugMessage('AutoUpdate called on ' . a:fname)
+
+ " Don't do anything if tagbar is not open or if we're in the tagbar window
+ let tagbarwinnr = bufwinnr('__Tagbar__')
+ if tagbarwinnr == -1 || &filetype == 'tagbar'
+ call s:LogDebugMessage('Tagbar window not open or in Tagbar window')
+ return
+ endif
+
+ " Only consider the main filetype in cases like 'python.django'
+ let ftype = get(split(&filetype, '\.'), 0, '')
+ call s:LogDebugMessage('Vim filetype: ' . &filetype .
+ \ ', sanitized filetype: ' . ftype)
+
+ " Don't do anything if the file isn't supported
+ if !s:IsValidFile(a:fname, ftype)
+ call s:LogDebugMessage('Not a valid file, stopping processing')
+ return
+ endif
+
+ " Process the file if it's unknown or the information is outdated
+ " Also test for entries that exist but are empty, which will be the case
+ " if there was an error during the ctags execution
+ if s:known_files.has(a:fname) && !empty(s:known_files.get(a:fname))
+ if s:known_files.get(a:fname).mtime != getftime(a:fname)
+ call s:LogDebugMessage('Filedata outdated, updating ' . a:fname)
+ call s:ProcessFile(a:fname, ftype)
+ endif
+ elseif !s:known_files.has(a:fname)
+ call s:LogDebugMessage('Unknown file, processing ' . a:fname)
+ call s:ProcessFile(a:fname, ftype)
+ endif
+
+ let fileinfo = s:known_files.get(a:fname)
+
+ " If we don't have an entry for the file by now something must have gone
+ " wrong, so don't change the tagbar content
+ if empty(fileinfo)
+ call s:LogDebugMessage('fileinfo empty after processing: ' . a:fname)
+ return
+ endif
+
+ " Display the tagbar content
+ call s:RenderContent(fileinfo)
+
+ " Call setCurrent after rendering so RenderContent can check whether the
+ " same file is redisplayed
+ if !empty(fileinfo)
+ call s:LogDebugMessage('Setting current file to ' . a:fname)
+ call s:known_files.setCurrent(fileinfo)
+ endif
+
+ call s:HighlightTag()
+ call s:LogDebugMessage('AutoUpdate finished successfully')
+endfunction
+
+" s:IsValidFile() {{{2
+function! s:IsValidFile(fname, ftype)
+ if a:fname == '' || a:ftype == ''
+ call s:LogDebugMessage('Empty filename or type')
+ return 0
+ endif
+
+ if !filereadable(a:fname)
+ call s:LogDebugMessage('File not readable')
+ return 0
+ endif
+
+ if !has_key(s:known_types, a:ftype)
+ call s:LogDebugMessage('Unsupported filetype: ' . a:ftype)
+ return 0
+ endif
+
+ return 1
+endfunction
+
+" s:EscapeCtagsCmd() {{{2
+" Assemble the ctags command line in a way that all problematic characters are
+" properly escaped and converted to the system's encoding
+" Optional third parameter is a file name to run ctags on
+function! s:EscapeCtagsCmd(ctags_bin, args, ...)
+ call s:LogDebugMessage('EscapeCtagsCmd called')
+ call s:LogDebugMessage('ctags_bin: ' . a:ctags_bin)
+ call s:LogDebugMessage('ctags_args: ' . a:args)
+
+ if exists('+shellslash')
+ let shellslash_save = &shellslash
+ set noshellslash
+ endif
+
+ if a:0 == 1
+ let fname = shellescape(a:1)
+ else
+ let fname = ''
+ endif
+
+ let ctags_cmd = shellescape(a:ctags_bin) . ' ' . a:args . ' ' . fname
+
+ if exists('+shellslash')
+ let &shellslash = shellslash_save
+ endif
+
+ " Needed for cases where 'encoding' is different from the system's
+ " encoding
+ if g:tagbar_systemenc != &encoding
+ let ctags_cmd = iconv(ctags_cmd, &encoding, g:tagbar_systemenc)
+ elseif $LANG != ''
+ let ctags_cmd = iconv(ctags_cmd, &encoding, $LANG)
+ endif
+
+ call s:LogDebugMessage('Escaped ctags command: ' . ctags_cmd)
+
+ if ctags_cmd == ''
+ echoerr 'Tagbar: Encoding conversion failed!'
+ \ 'Please make sure your system is set up correctly'
+ \ 'and that Vim is compiled with the "iconv" feature.'
+ endif
+
+ return ctags_cmd
+endfunction
+
+" s:ExecuteCtags() {{{2
+" Execute ctags with necessary shell settings
+" Partially based on the discussion at
+" http://vim.1045645.n5.nabble.com/bad-default-shellxquote-in-Widows-td1208284.html
+function! s:ExecuteCtags(ctags_cmd)
+ if exists('+shellslash')
+ let shellslash_save = &shellslash
+ set noshellslash
+ endif
+
+ if &shell =~ 'cmd\.exe'
+ let shellxquote_save = &shellxquote
+ set shellxquote=\"
+ let shellcmdflag_save = &shellcmdflag
+ set shellcmdflag=/s\ /c
+ endif
+
+ let ctags_output = system(a:ctags_cmd)
+
+ if &shell =~ 'cmd\.exe'
+ let &shellxquote = shellxquote_save
+ let &shellcmdflag = shellcmdflag_save
+ endif
+
+ if exists('+shellslash')
+ let &shellslash = shellslash_save
+ endif
+
+ return ctags_output
+endfunction
+
+" s:GetTagInfo() {{{2
+" Return the info dictionary of the tag on the specified line. If the line
+" does not contain a valid tag (for example because it is empty or only
+" contains a pseudo-tag) return an empty dictionary.
+function! s:GetTagInfo(linenr, ignorepseudo)
+ let fileinfo = s:known_files.getCurrent()
+
+ if empty(fileinfo)
+ return {}
+ endif
+
+ " Don't do anything in empty and comment lines
+ let curline = getline(a:linenr)
+ if curline =~ '^\s*$' || curline[0] == '"'
+ return {}
+ endif
+
+ " Check if there is a tag on the current line
+ if !has_key(fileinfo.tline, a:linenr)
+ return {}
+ endif
+
+ let taginfo = fileinfo.tline[a:linenr]
+
+ " Check if the current tag is not a pseudo-tag
+ if a:ignorepseudo && taginfo.isPseudoTag()
+ return {}
+ endif
+
+ return taginfo
+endfunction
+
+" s:GetNearbyTag() {{{2
+" Get the tag info for a file near the cursor in the current file
+function! s:GetNearbyTag()
+ let fileinfo = s:known_files.getCurrent()
+
+ let curline = line('.')
+ let tag = {}
+
+ " If a tag appears in a file more than once (for example namespaces in
+ " C++) only one of them has a 'tline' entry and can thus be highlighted.
+ " The only way to solve this would be to go over the whole tag list again,
+ " making everything slower. Since this should be a rare occurence and
+ " highlighting isn't /that/ important ignore it for now.
+ for line in range(curline, 1, -1)
+ if has_key(fileinfo.fline, line)
+ let tag = fileinfo.fline[line]
+ break
+ endif
+ endfor
+
+ return tag
+endfunction
+
+" s:CheckMouseClick() {{{2
+function! s:CheckMouseClick()
+ let line = getline('.')
+ let curcol = col('.')
+
+ if (match(line, s:icon_open . '[-+ ]') + 1) == curcol
+ call s:CloseFold()
+ elseif (match(line, s:icon_closed . '[-+ ]') + 1) == curcol
+ call s:OpenFold()
+ elseif g:tagbar_singleclick
+ call s:JumpToTag(0)
+ endif
+endfunction
+
+" s:DetermineFiletype() {{{2
+function! s:DetectFiletype(bufnr)
+ " Filetype has already been detected for loaded buffers, but not
+ " necessarily for unloaded ones
+ let ftype = getbufvar(a:bufnr, '&filetype')
+
+ if bufloaded(a:bufnr)
+ return ftype
+ endif
+
+ if ftype != ''
+ return ftype
+ endif
+
+ " Unloaded buffer with non-detected filetype, need to detect filetype
+ " manually
+ let bufname = bufname(a:bufnr)
+
+ let eventignore_save = &eventignore
+ set eventignore=FileType
+ let filetype_save = &filetype
+
+ exe 'doautocmd filetypedetect BufRead ' . bufname
+ let ftype = &filetype
+
+ let &filetype = filetype_save
+ let &eventignore = eventignore_save
+
+ return ftype
+endfunction
+
+
+" TagbarBalloonExpr() {{{2
+function! TagbarBalloonExpr()
+ let taginfo = s:GetTagInfo(v:beval_lnum, 1)
+
+ if empty(taginfo)
+ return
+ endif
+
+ return taginfo.getPrototype()
+endfunction
+
+" TagbarGenerateStatusline() {{{2
+function! TagbarGenerateStatusline()
+ if g:tagbar_sort
+ let text = '[Name]'
+ else
+ let text = '[Order]'
+ endif
+
+ if !empty(s:known_files.getCurrent())
+ let filename = fnamemodify(s:known_files.getCurrent().fpath, ':t')
+ let text .= ' ' . filename
+ endif
+
+ return text
+endfunction
+
+" Debugging {{{1
+" s:StartDebug() {{{2
+function! s:StartDebug(filename)
+ if empty(a:filename)
+ let s:debug_file = 'tagbardebug.log'
+ else
+ let s:debug_file = a:filename
+ endif
+
+ " Empty log file
+ exe 'redir! > ' . s:debug_file
+ redir END
+
+ " Check whether the log file could be created
+ if !filewritable(s:debug_file)
+ echomsg 'Tagbar: Unable to create log file ' . s:debug_file
+ let s:debug_file = ''
+ return
+ endif
+
+ let s:debug = 1
+endfunction
+
+" s:StopDebug() {{{2
+function! s:StopDebug()
+ let s:debug = 0
+ let s:debug_file = ''
+endfunction
+
+" s:LogDebugMessage() {{{2
+function! s:LogDebugMessage(msg)
+ if s:debug
+ exe 'redir >> ' . s:debug_file
+ silent echon strftime('%H:%M:%S') . ': ' . a:msg . "\n"
+ redir END
+ endif
+endfunction
+
+" Autoload functions {{{1
+function! tagbar#ToggleWindow()
+ call s:ToggleWindow()
+endfunction
+
+function! tagbar#OpenWindow(...)
+ let flags = a:0 > 0 ? a:1 : ''
+ call s:OpenWindow(flags)
+endfunction
+
+function! tagbar#CloseWindow()
+ call s:CloseWindow()
+endfunction
+
+function! tagbar#SetFoldLevel(...)
+ call s:SetFoldLevel(a:1)
+endfunction
+
+function! tagbar#OpenParents()
+ call s:OpenParents()
+endfunction
+
+function! tagbar#StartDebug(...)
+ let filename = a:0 > 0 ? a:1 : ''
+ call s:StartDebug(filename)
+endfunction
+
+function! tagbar#StopDebug()
+ call s:StopDebug()
+endfunction
+
+function! tagbar#RestoreSession()
+ call s:RestoreSession()
+endfunction
+
+" Automatically open Tagbar if one of the open buffers contains a supported
+" file
+function! tagbar#autoopen()
+ call s:Init()
+
+ for bufnr in range(1, bufnr('$'))
+ if buflisted(bufnr)
+ let ftype = s:DetectFiletype(bufnr)
+ if s:IsValidFile(bufname(bufnr), ftype)
+ call s:OpenWindow('')
+ return
+ endif
+ endif
+ endfor
+endfunction
+
+" Modeline {{{1
+" vim: ts=8 sw=4 sts=4 et foldenable foldmethod=marker foldcolumn=1
diff --git a/vim/autoload/togglebg.vim b/vim/autoload/togglebg.vim
new file mode 100644
index 0000000..108511f
--- /dev/null
+++ b/vim/autoload/togglebg.vim
@@ -0,0 +1,55 @@
+" Toggle Background
+" Modified: 2011 Apr 29
+" Maintainer: Ethan Schoonover
+" License: OSI approved MIT license
+
+if exists("g:loaded_togglebg")
+ finish
+endif
+let g:loaded_togglebg = 1
+
+" noremap is a bit misleading here if you are unused to vim mapping.
+" in fact, there is remapping, but only of script locally defined remaps, in
+" this case <SID>TogBG. The <script> argument modifies the noremap scope in
+" this regard (and the noremenu below).
+nnoremap <unique> <script> <Plug>ToggleBackground <SID>TogBG
+inoremap <unique> <script> <Plug>ToggleBackground <ESC><SID>TogBG<ESC>a
+vnoremap <unique> <script> <Plug>ToggleBackground <ESC><SID>TogBG<ESC>gv
+nnoremenu <script> Window.Toggle\ Background <SID>TogBG
+inoremenu <script> Window.Toggle\ Background <ESC><SID>TogBG<ESC>a
+vnoremenu <script> Window.Toggle\ Background <ESC><SID>TogBG<ESC>gv
+tmenu Window.Toggle\ Background Toggle light and dark background modes
+nnoremenu <script> ToolBar.togglebg <SID>TogBG
+inoremenu <script> ToolBar.togglebg <ESC><SID>TogBG<ESC>a
+vnoremenu <script> ToolBar.togglebg <ESC><SID>TogBG<ESC>gv
+tmenu ToolBar.togglebg Toggle light and dark background modes
+noremap <SID>TogBG :call <SID>TogBG()<CR>
+
+function! s:TogBG()
+ let &background = ( &background == "dark"? "light" : "dark" )
+ if exists("g:colors_name")
+ exe "colorscheme " . g:colors_name
+ endif
+endfunction
+
+if !exists(":ToggleBG")
+ command ToggleBG :call s:TogBG()
+endif
+
+function! ToggleBackground()
+ echo "Please update your ToggleBackground mapping. ':help togglebg' for information."
+endfunction
+
+function! togglebg#map(mapActivation)
+ try
+ exe "silent! nmap <unique> ".a:mapActivation." <Plug>ToggleBackground"
+ exe "silent! imap <unique> ".a:mapActivation." <Plug>ToggleBackground"
+ exe "silent! vmap <unique> ".a:mapActivation." <Plug>ToggleBackground"
+ finally
+ return 0
+ endtry
+endfunction
+
+if !exists("no_plugin_maps") && !hasmapto('<Plug>ToggleBackground')
+ call togglebg#map("<F5>")
+endif