From 63d8549694485f1f16833c2c7fc206f40492250a Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sat, 11 Jul 2009 19:18:11 +0200 Subject: new plugins and keybindings --- .vim/autoload/Align.vim | 1029 +++++++++++++++++++++++++++++++++++++++++++ .vim/autoload/AlignMaps.vim | 330 ++++++++++++++ .vim/autoload/snipMate.vim | 392 +++++++++++++++++ 3 files changed, 1751 insertions(+) create mode 100644 .vim/autoload/Align.vim create mode 100644 .vim/autoload/AlignMaps.vim create mode 100644 .vim/autoload/snipMate.vim (limited to '.vim/autoload') 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 + " 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 == "\" + 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 = '\\\@, is needed. {{{1 +" However, 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\" + 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! ' + 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/==/\="\\"/ge + 'a,'zs/\([!<>:]\)=/\=submatch(1)."\"/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\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\\" + 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/snipMate.vim b/.vim/autoload/snipMate.vim new file mode 100644 index 0000000..618eddd --- /dev/null +++ b/.vim/autoload/snipMate.vim @@ -0,0 +1,392 @@ +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:endSnip s:endSnipLine s:prevLen +endf + +fun snipMate#expandSnip(snip, col) + let lnum = line('.') | let col = a:col + + let snippet = s:ProcessSnippet(a:snip) + if snippet == '' | return '' | endif + + let snipLines = split(substitute(snippet, '$\d\+\|${\d\+.\{-}}', '', 'g'), "\n", 1) + + let line = getline(lnum) + let afterCursor = strpart(line, col - 1) + 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")) + 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 + let s:curPos = 0 + let s:endSnip = g:snipPos[s:curPos][1] + let s:endSnipLine = 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 + +fun s:ProcessSnippet(snip) + let snippet = a:snip + " Evaluate eval (`...`) expressions. + " 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, '`.\{-}`', + \ substitute(eval(matchstr(snippet, '`\zs.\{-}\ze`')), + \ "\n\\%$", '', ''), '') + endw + let snippet = substitute(snippet, "\r", "\n", '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 + +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 + +" This function 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() + if exists('s:update') + call s:UpdatePlaceholderTabStops() + else + call s:UpdateTabStops() + endif + + let s:curPos += 1 + if s:curPos == s:snipLen + let sMode = s:endSnip == g:snipPos[s:curPos-1][1]+g:snipPos[s:curPos-1][2] + call s:RemoveSnippet() + return sMode ? "\" : TriggerSnippet() + endif + + call cursor(g:snipPos[s:curPos][0], g:snipPos[s:curPos][1]) + + let s:endSnipLine = g:snipPos[s:curPos][0] + let s:endSnip = 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:startSnip s:origWordLen s:update + if !exists('s:origPos') | 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[s:curPos + 1:] + let changed = pos[0] == curLine && pos[1] > s:origSnipPos + 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:origPos + 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:origSnipPos + for [lnum, col] in s:origPos + 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:endSnip s:origPos s:origSnipPos +endf + +fun s:UpdateTabStops() + let changeLine = s:endSnipLine - g:snipPos[s:curPos][0] + let changeCol = s:endSnip - 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 has + " been inserted. + if changeLine != 0 + let changeLine -= 1 + for pos in g:snipPos[s:curPos + 1:] + 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[s:curPos + 1:] + 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:endSnip = -1 + let s:startSnip = g:snipPos[s:curPos][1] - 1 + endif + if !s:origWordLen | return '' | endif + let l = col('.') != 1 ? 'l' : '' + if &sel == 'exclusive' + return "\".l.'v'.s:origWordLen."l\" + endif + return s:origWordLen == 1 ? "\".l.'gh' + \ : "\".l.'v'.(s:origWordLen - 1)."l\" +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. +au CursorMovedI * call s:UpdateChangedSnip(0) +au InsertEnter * call s:UpdateChangedSnip(1) +fun s:UpdateChangedSnip(entering) + if exists('s:update') " If modifying a placeholder + if !exists('s:origPos') && s:curPos + 1 < s:snipLen + " Save the old snippet & word length before it's updated + " s:startSnip must be saved too, in case text is added + " before the snippet (e.g. in "foo$1${2}bar${1:foo}"). + let s:origSnipPos = s:startSnip + let s:origPos = deepcopy(g:snipPos[s:curPos][3]) + endif + let col = col('.') - 1 + + if s:endSnip != -1 + let changeLen = col('$') - s:prevLen[1] + let s:endSnip += changeLen + else " When being updated the first time, after leaving select mode + if a:entering | return | endif + let s:endSnip = col - 1 + endif + + " If the cursor moves outside the snippet, quit it + if line('.') != g:snipPos[s:curPos][0] || col < s:startSnip || + \ col - 1 > s:endSnip + unl! s:startSnip s:origWordLen s:origPos s:update + return s:RemoveSnippet() + endif + + call s:UpdateVars() + let s:prevLen[1] = col('$') + elseif exists('g:snipPos') + let col = col('.') + let lnum = line('.') + let changeLine = line('$') - s:prevLen[0] + + if lnum == s:endSnipLine + let s:endSnip += col('$') - s:prevLen[1] + let s:prevLen = [line('$'), col('$')] + endif + if changeLine != 0 + let s:endSnipLine += changeLine + let s:endSnip = col + endif + + " Delete snippet if cursor moves out of it in insert mode + if (lnum == s:endSnipLine && (col > s:endSnip || col < g:snipPos[s:curPos][1])) + \ || lnum > s:endSnipLine || 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:endSnip - s:startSnip + 1 + let newWord = strpart(getline('.'), s:startSnip, 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:startSnip + let updateTabStops = changeLen != 0 + let i = 0 + + for [lnum, col] in g:snipPos[s:curPos][3] + if updateTabStops + let start = s:startSnip + if lnum == curLine && col <= start + let s:startSnip -= changeLen + let s:endSnip -= 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:startSnip + call cursor(0, startCol + s:startSnip - oldStartSnip) + endif + + let s:oldWord = newWord + let g:snipPos[s:curPos][2] = newWordLen +endf +" vim:noet:sw=4:ts=4:ft=vim -- cgit v1.2.3-24-g4f1b