]> git.r.bdr.sh - rbdr/dotfiles/blame_incremental - vim/autoload/Align.vim
Merge branch 'master' of github.com:benbeltran/dotfiles
[rbdr/dotfiles] / vim / autoload / Align.vim
... / ...
CommitLineData
1" Align: tool to align multiple fields based on one or more separators
2" Author: Charles E. Campbell, Jr.
3" Date: Mar 03, 2009
4" Version: 35
5" GetLatestVimScripts: 294 1 :AutoInstall: Align.vim
6" GetLatestVimScripts: 1066 1 :AutoInstall: cecutil.vim
7" Copyright: Copyright (C) 1999-2007 Charles E. Campbell, Jr. {{{1
8" Permission is hereby granted to use and distribute this code,
9" with or without modifications, provided that this copyright
10" notice is copied with it. Like anything else that's free,
11" Align.vim is provided *as is* and comes with no warranty
12" of any kind, either expressed or implied. By using this
13" plugin, you agree that in no event will the copyright
14" holder be liable for any damages resulting from the use
15" of this software.
16"
17" Romans 1:16,17a : For I am not ashamed of the gospel of Christ, for it is {{{1
18" the power of God for salvation for everyone who believes; for the Jew first,
19" and also for the Greek. For in it is revealed God's righteousness from
20" faith to faith.
21
22" ---------------------------------------------------------------------
23" Load Once: {{{1
24if exists("g:loaded_Align") || &cp
25 finish
26endif
27let g:loaded_Align = "v35"
28if v:version < 700
29 echohl WarningMsg
30 echo "***warning*** this version of Align needs vim 7.0"
31 echohl Normal
32 finish
33endif
34let s:keepcpo= &cpo
35set cpo&vim
36"DechoTabOn
37
38" ---------------------------------------------------------------------
39" Debugging Support: {{{1
40"if !exists("g:loaded_Decho") | runtime plugin/Decho.vim | endif
41
42" ---------------------------------------------------------------------
43" Options: {{{1
44if !exists("g:Align_xstrlen")
45 if &enc == "latin1" || $LANG == "en_US.UTF-8" || !has("multi_byte")
46 let g:Align_xstrlen= 0
47 else
48 let g:Align_xstrlen= 1
49 endif
50endif
51
52" ---------------------------------------------------------------------
53" Align#AlignCtrl: enter alignment patterns here {{{1
54"
55" Styles = all alignment-break patterns are equivalent
56" C cycle through alignment-break pattern(s)
57" l left-justified alignment
58" r right-justified alignment
59" c center alignment
60" - skip separator, treat as part of field
61" : treat rest of line as field
62" + repeat previous [lrc] style
63" < left justify separators
64" > right justify separators
65" | center separators
66"
67" Builds = s:AlignPat s:AlignCtrl s:AlignPatQty
68" C s:AlignPat s:AlignCtrl s:AlignPatQty
69" p s:AlignPrePad
70" P s:AlignPostPad
71" w s:AlignLeadKeep
72" W s:AlignLeadKeep
73" I s:AlignLeadKeep
74" l s:AlignStyle
75" r s:AlignStyle
76" - s:AlignStyle
77" + s:AlignStyle
78" : s:AlignStyle
79" c s:AlignStyle
80" g s:AlignGPat
81" v s:AlignVPat
82" < s:AlignSep
83" > s:AlignSep
84" | s:AlignSep
85fun! Align#AlignCtrl(...)
86
87" call Dfunc("AlignCtrl(...) a:0=".a:0)
88
89 " save options that will be changed
90 let keep_search = @/
91 let keep_ic = &ic
92
93 " turn ignorecase off
94 set noic
95
96 " clear visual mode so that old visual-mode selections don't
97 " get applied to new invocations of Align().
98 if v:version < 602
99 if !exists("s:Align_gavemsg")
100 let s:Align_gavemsg= 1
101 echomsg "Align needs at least Vim version 6.2 to clear visual-mode selection"
102 endif
103 elseif exists("s:dovisclear")
104" call Decho("clearing visual mode a:0=".a:0." a:1<".a:1.">")
105 let clearvmode= visualmode(1)
106 endif
107
108 " set up a list akin to an argument list
109 if a:0 > 0
110 let A= s:QArgSplitter(a:1)
111 else
112 let A=[0]
113 endif
114
115 if A[0] > 0
116 let style = A[1]
117
118 " Check for bad separator patterns (zero-length matches)
119 " (but zero-length patterns for g/v is ok)
120 if style !~# '[gv]'
121 let ipat= 2
122 while ipat <= A[0]
123 if "" =~ A[ipat]
124 echoerr "AlignCtrl: separator<".A[ipat]."> matches zero-length string"
125 let &ic= keep_ic
126" call Dret("AlignCtrl")
127 return
128 endif
129 let ipat= ipat + 1
130 endwhile
131 endif
132 endif
133
134" call Decho("AlignCtrl() A[0]=".A[0])
135 if !exists("s:AlignStyle")
136 let s:AlignStyle= "l"
137 endif
138 if !exists("s:AlignPrePad")
139 let s:AlignPrePad= 0
140 endif
141 if !exists("s:AlignPostPad")
142 let s:AlignPostPad= 0
143 endif
144 if !exists("s:AlignLeadKeep")
145 let s:AlignLeadKeep= 'w'
146 endif
147
148 if A[0] == 0
149 " ----------------------
150 " List current selection
151 " ----------------------
152 if !exists("s:AlignPatQty")
153 let s:AlignPatQty= 0
154 endif
155 echo "AlignCtrl<".s:AlignCtrl."> qty=".s:AlignPatQty." AlignStyle<".s:AlignStyle."> Padding<".s:AlignPrePad."|".s:AlignPostPad."> LeadingWS=".s:AlignLeadKeep." AlignSep=".s:AlignSep
156" call Decho("AlignCtrl<".s:AlignCtrl."> qty=".s:AlignPatQty." AlignStyle<".s:AlignStyle."> Padding<".s:AlignPrePad."|".s:AlignPostPad."> LeadingWS=".s:AlignLeadKeep." AlignSep=".s:AlignSep)
157 if exists("s:AlignGPat") && !exists("s:AlignVPat")
158 echo "AlignGPat<".s:AlignGPat.">"
159 elseif !exists("s:AlignGPat") && exists("s:AlignVPat")
160 echo "AlignVPat<".s:AlignVPat.">"
161 elseif exists("s:AlignGPat") && exists("s:AlignVPat")
162 echo "AlignGPat<".s:AlignGPat."> AlignVPat<".s:AlignVPat.">"
163 endif
164 let ipat= 1
165 while ipat <= s:AlignPatQty
166 echo "Pat".ipat."<".s:AlignPat_{ipat}.">"
167" call Decho("Pat".ipat."<".s:AlignPat_{ipat}.">")
168 let ipat= ipat + 1
169 endwhile
170
171 else
172 " ----------------------------------
173 " Process alignment control settings
174 " ----------------------------------
175" call Decho("process the alignctrl settings")
176" call Decho("style<".style.">")
177
178 if style ==? "default"
179 " Default: preserve initial leading whitespace, left-justified,
180 " alignment on '=', one space padding on both sides
181 if exists("s:AlignCtrlStackQty")
182 " clear AlignCtrl stack
183 while s:AlignCtrlStackQty > 0
184 call Align#AlignPop()
185 endwhile
186 unlet s:AlignCtrlStackQty
187 endif
188 " Set AlignCtrl to its default value
189 call Align#AlignCtrl("Ilp1P1=<",'=')
190 call Align#AlignCtrl("g")
191 call Align#AlignCtrl("v")
192 let s:dovisclear = 1
193 let &ic = keep_ic
194 let @/ = keep_search
195" call Dret("AlignCtrl")
196 return
197 endif
198
199 if style =~# 'm'
200 " map support: Do an AlignPush now and the next call to Align()
201 " will do an AlignPop at exit
202" call Decho("style case m: do AlignPush")
203 call Align#AlignPush()
204 let s:DoAlignPop= 1
205 endif
206
207 " = : record a list of alignment patterns that are equivalent
208 if style =~# "="
209" call Decho("style case =: record list of equiv alignment patterns")
210 let s:AlignCtrl = '='
211 if A[0] >= 2
212 let s:AlignPatQty= 1
213 let s:AlignPat_1 = A[2]
214 let ipat = 3
215 while ipat <= A[0]
216 let s:AlignPat_1 = s:AlignPat_1.'\|'.A[ipat]
217 let ipat = ipat + 1
218 endwhile
219 let s:AlignPat_1= '\('.s:AlignPat_1.'\)'
220" call Decho("AlignCtrl<".s:AlignCtrl."> AlignPat<".s:AlignPat_1.">")
221 endif
222
223 "c : cycle through alignment pattern(s)
224 elseif style =~# 'C'
225" call Decho("style case C: cycle through alignment pattern(s)")
226 let s:AlignCtrl = 'C'
227 if A[0] >= 2
228 let s:AlignPatQty= A[0] - 1
229 let ipat = 1
230 while ipat < A[0]
231 let s:AlignPat_{ipat}= A[ipat+1]
232" call Decho("AlignCtrl<".s:AlignCtrl."> AlignQty=".s:AlignPatQty." AlignPat_".ipat."<".s:AlignPat_{ipat}.">")
233 let ipat= ipat + 1
234 endwhile
235 endif
236 endif
237
238 if style =~# 'p'
239 let s:AlignPrePad= substitute(style,'^.*p\(\d\+\).*$','\1','')
240" call Decho("style case p".s:AlignPrePad.": pre-separator padding")
241 if s:AlignPrePad == ""
242 echoerr "AlignCtrl: 'p' needs to be followed by a numeric argument'
243 let @/ = keep_search
244 let &ic= keep_ic
245" call Dret("AlignCtrl")
246 return
247 endif
248 endif
249
250 if style =~# 'P'
251 let s:AlignPostPad= substitute(style,'^.*P\(\d\+\).*$','\1','')
252" call Decho("style case P".s:AlignPostPad.": post-separator padding")
253 if s:AlignPostPad == ""
254 echoerr "AlignCtrl: 'P' needs to be followed by a numeric argument'
255 let @/ = keep_search
256 let &ic= keep_ic
257" call Dret("AlignCtrl")
258 return
259 endif
260 endif
261
262 if style =~# 'w'
263" call Decho("style case w: ignore leading whitespace")
264 let s:AlignLeadKeep= 'w'
265 elseif style =~# 'W'
266" call Decho("style case w: keep leading whitespace")
267 let s:AlignLeadKeep= 'W'
268 elseif style =~# 'I'
269" call Decho("style case w: retain initial leading whitespace")
270 let s:AlignLeadKeep= 'I'
271 endif
272
273 if style =~# 'g'
274 " first list item is a "g" selector pattern
275" call Decho("style case g: global selector pattern")
276 if A[0] < 2
277 if exists("s:AlignGPat")
278 unlet s:AlignGPat
279" call Decho("unlet s:AlignGPat")
280 endif
281 else
282 let s:AlignGPat= A[2]
283" call Decho("s:AlignGPat<".s:AlignGPat.">")
284 endif
285 elseif style =~# 'v'
286 " first list item is a "v" selector pattern
287" call Decho("style case v: global selector anti-pattern")
288 if A[0] < 2
289 if exists("s:AlignVPat")
290 unlet s:AlignVPat
291" call Decho("unlet s:AlignVPat")
292 endif
293 else
294 let s:AlignVPat= A[2]
295" call Decho("s:AlignVPat<".s:AlignVPat.">")
296 endif
297 endif
298
299 "[-lrc+:] : set up s:AlignStyle
300 if style =~# '[-lrc+:]'
301" call Decho("style case [-lrc+:]: field justification")
302 let s:AlignStyle= substitute(style,'[^-lrc:+]','','g')
303" call Decho("AlignStyle<".s:AlignStyle.">")
304 endif
305
306 "[<>|] : set up s:AlignSep
307 if style =~# '[<>|]'
308" call Decho("style case [-lrc+:]: separator justification")
309 let s:AlignSep= substitute(style,'[^<>|]','','g')
310" call Decho("AlignSep ".s:AlignSep)
311 endif
312 endif
313
314 " sanity
315 if !exists("s:AlignCtrl")
316 let s:AlignCtrl= '='
317 endif
318
319 " restore search and options
320 let @/ = keep_search
321 let &ic= keep_ic
322
323" call Dret("AlignCtrl ".s:AlignCtrl.'p'.s:AlignPrePad.'P'.s:AlignPostPad.s:AlignLeadKeep.s:AlignStyle)
324 return s:AlignCtrl.'p'.s:AlignPrePad.'P'.s:AlignPostPad.s:AlignLeadKeep.s:AlignStyle
325endfun
326
327" ---------------------------------------------------------------------
328" s:MakeSpace: returns a string with spacecnt blanks {{{1
329fun! s:MakeSpace(spacecnt)
330" call Dfunc("MakeSpace(spacecnt=".a:spacecnt.")")
331 let str = ""
332 let spacecnt = a:spacecnt
333 while spacecnt > 0
334 let str = str . " "
335 let spacecnt = spacecnt - 1
336 endwhile
337" call Dret("MakeSpace <".str.">")
338 return str
339endfun
340
341" ---------------------------------------------------------------------
342" Align#Align: align selected text based on alignment pattern(s) {{{1
343fun! Align#Align(hasctrl,...) range
344" call Dfunc("Align#Align(hasctrl=".a:hasctrl.",...) a:0=".a:0)
345
346 " sanity checks
347 if string(a:hasctrl) != "0" && string(a:hasctrl) != "1"
348 echohl Error|echo 'usage: Align#Align(hasctrl<'.a:hasctrl.'> (should be 0 or 1),"separator(s)" (you have '.a:0.') )'|echohl None
349" call Dret("Align#Align")
350 return
351 endif
352 if exists("s:AlignStyle") && s:AlignStyle == ":"
353 echohl Error |echo '(Align#Align) your AlignStyle is ":", which implies "do-no-alignment"!'|echohl None
354" call Dret("Align#Align")
355 return
356 endif
357
358 " set up a list akin to an argument list
359 if a:0 > 0
360 let A= s:QArgSplitter(a:1)
361 else
362 let A=[0]
363 endif
364
365 " if :Align! was used, then the first argument is (should be!) an AlignCtrl string
366 " Note that any alignment control set this way will be temporary.
367 let hasctrl= a:hasctrl
368" call Decho("hasctrl=".hasctrl)
369 if a:hasctrl && A[0] >= 1
370" call Decho("Align! : using A[1]<".A[1]."> for AlignCtrl")
371 if A[1] =~ '[gv]'
372 let hasctrl= hasctrl + 1
373 call Align#AlignCtrl('m')
374 call Align#AlignCtrl(A[1],A[2])
375" call Decho("Align! : also using A[2]<".A[2]."> for AlignCtrl")
376 elseif A[1] !~ 'm'
377 call Align#AlignCtrl(A[1]."m")
378 else
379 call Align#AlignCtrl(A[1])
380 endif
381 endif
382
383 " Check for bad separator patterns (zero-length matches)
384 let ipat= 1 + hasctrl
385 while ipat <= A[0]
386 if "" =~ A[ipat]
387 echoerr "Align: separator<".A[ipat]."> matches zero-length string"
388" call Dret("Align#Align")
389 return
390 endif
391 let ipat= ipat + 1
392 endwhile
393
394 " record current search pattern for subsequent restoration
395 let keep_search= @/
396 let keep_ic = &ic
397 let keep_report= &report
398 set noic report=10000
399
400 if A[0] > hasctrl
401 " Align will accept a list of separator regexps
402" call Decho("A[0]=".A[0].": accepting list of separator regexp")
403
404 if s:AlignCtrl =~# "="
405 "= : consider all separators to be equivalent
406" call Decho("AlignCtrl: record list of equivalent alignment patterns")
407 let s:AlignCtrl = '='
408 let s:AlignPat_1 = A[1 + hasctrl]
409 let s:AlignPatQty= 1
410 let ipat = 2 + hasctrl
411 while ipat <= A[0]
412 let s:AlignPat_1 = s:AlignPat_1.'\|'.A[ipat]
413 let ipat = ipat + 1
414 endwhile
415 let s:AlignPat_1= '\('.s:AlignPat_1.'\)'
416" call Decho("AlignCtrl<".s:AlignCtrl."> AlignPat<".s:AlignPat_1.">")
417
418 elseif s:AlignCtrl =~# 'C'
419 "c : cycle through alignment pattern(s)
420" call Decho("AlignCtrl: cycle through alignment pattern(s)")
421 let s:AlignCtrl = 'C'
422 let s:AlignPatQty= A[0] - hasctrl
423 let ipat = 1
424 while ipat <= s:AlignPatQty
425 let s:AlignPat_{ipat}= A[(ipat + hasctrl)]
426" call Decho("AlignCtrl<".s:AlignCtrl."> AlignQty=".s:AlignPatQty." AlignPat_".ipat."<".s:AlignPat_{ipat}.">")
427 let ipat= ipat + 1
428 endwhile
429 endif
430 endif
431
432 " Initialize so that begline<endline and begcol<endcol.
433 " Ragged right: check if the column associated with '< or '>
434 " is greater than the line's string length -> ragged right.
435 " Have to be careful about visualmode() -- it returns the last visual
436 " mode used whether or not it was used currently.
437 let begcol = virtcol("'<")-1
438 let endcol = virtcol("'>")-1
439 if begcol > endcol
440 let begcol = virtcol("'>")-1
441 let endcol = virtcol("'<")-1
442 endif
443" call Decho("begcol=".begcol." endcol=".endcol)
444 let begline = a:firstline
445 let endline = a:lastline
446 if begline > endline
447 let begline = a:lastline
448 let endline = a:firstline
449 endif
450" call Decho("begline=".begline." endline=".endline)
451 let fieldcnt = 0
452 if (begline == line("'>") && endline == line("'<")) || (begline == line("'<") && endline == line("'>"))
453 let vmode= visualmode()
454" call Decho("vmode=".vmode)
455 if vmode == "\<c-v>"
456 if exists("g:Align_xstrlen") && g:Align_xstrlen
457 let ragged = ( col("'>") > s:Strlen(getline("'>")) || col("'<") > s:Strlen(getline("'<")) )
458 else
459 let ragged = ( col("'>") > strlen(getline("'>")) || col("'<") > strlen(getline("'<")) )
460 endif
461 else
462 let ragged= 1
463 endif
464 else
465 let ragged= 1
466 endif
467 if ragged
468 let begcol= 0
469 endif
470" call Decho("lines[".begline.",".endline."] col[".begcol.",".endcol."] ragged=".ragged." AlignCtrl<".s:AlignCtrl.">")
471
472 " Keep user options
473 let etkeep = &l:et
474 let pastekeep= &l:paste
475 setlocal et paste
476
477 " convert selected range of lines to use spaces instead of tabs
478 " but if first line's initial white spaces are to be retained
479 " then use 'em
480 if begcol <= 0 && s:AlignLeadKeep == 'I'
481 " retain first leading whitespace for all subsequent lines
482 let bgntxt= substitute(getline(begline),'^\(\s*\).\{-}$','\1','')
483" call Decho("retaining 1st leading whitespace: bgntxt<".bgntxt.">")
484 set noet
485 endif
486 exe begline.",".endline."ret"
487
488 " Execute two passes
489 " First pass: collect alignment data (max field sizes)
490 " Second pass: perform alignment
491 let pass= 1
492 while pass <= 2
493" call Decho(" ")
494" call Decho("---- Pass ".pass.": ----")
495
496 let line= begline
497 while line <= endline
498 " Process each line
499 let txt = getline(line)
500" call Decho(" ")
501" call Decho("Pass".pass.": Line ".line." <".txt.">")
502
503 " AlignGPat support: allows a selector pattern (akin to g/selector/cmd )
504 if exists("s:AlignGPat")
505" call Decho("Pass".pass.": AlignGPat<".s:AlignGPat.">")
506 if match(txt,s:AlignGPat) == -1
507" call Decho("Pass".pass.": skipping")
508 let line= line + 1
509 continue
510 endif
511 endif
512
513 " AlignVPat support: allows a selector pattern (akin to v/selector/cmd )
514 if exists("s:AlignVPat")
515" call Decho("Pass".pass.": AlignVPat<".s:AlignVPat.">")
516 if match(txt,s:AlignVPat) != -1
517" call Decho("Pass".pass.": skipping")
518 let line= line + 1
519 continue
520 endif
521 endif
522
523 " Always skip blank lines
524 if match(txt,'^\s*$') != -1
525" call Decho("Pass".pass.": skipping")
526 let line= line + 1
527 continue
528 endif
529
530 " Extract visual-block selected text (init bgntxt, endtxt)
531 if exists("g:Align_xstrlen") && g:Align_xstrlen
532 let txtlen= s:Strlen(txt)
533 else
534 let txtlen= strlen(txt)
535 endif
536 if begcol > 0
537 " Record text to left of selected area
538 let bgntxt= strpart(txt,0,begcol)
539" call Decho("Pass".pass.": record text to left: bgntxt<".bgntxt.">")
540 elseif s:AlignLeadKeep == 'W'
541 let bgntxt= substitute(txt,'^\(\s*\).\{-}$','\1','')
542" call Decho("Pass".pass.": retaining all leading ws: bgntxt<".bgntxt.">")
543 elseif s:AlignLeadKeep == 'w' || !exists("bgntxt")
544 " No beginning text
545 let bgntxt= ""
546" call Decho("Pass".pass.": no beginning text")
547 endif
548 if ragged
549 let endtxt= ""
550 else
551 " Elide any text lying outside selected columnar region
552 let endtxt= strpart(txt,endcol+1,txtlen-endcol)
553 let txt = strpart(txt,begcol,endcol-begcol+1)
554 endif
555" call Decho(" ")
556" call Decho("Pass".pass.": bgntxt<".bgntxt.">")
557" call Decho("Pass".pass.": txt<". txt .">")
558" call Decho("Pass".pass.": endtxt<".endtxt.">")
559 if !exists("s:AlignPat_{1}")
560 echohl Error|echo "no separators specified!"|echohl None
561" call Dret("Align#Align")
562 return
563 endif
564
565 " Initialize for both passes
566 let seppat = s:AlignPat_{1}
567 let ifield = 1
568 let ipat = 1
569 let bgnfield = 0
570 let endfield = 0
571 let alignstyle = s:AlignStyle
572 let doend = 1
573 let newtxt = ""
574 let alignprepad = s:AlignPrePad
575 let alignpostpad= s:AlignPostPad
576 let alignsep = s:AlignSep
577 let alignophold = " "
578 let alignop = "l"
579" call Decho("Pass".pass.": initial alignstyle<".alignstyle."> seppat<".seppat.">")
580
581 " Process each field on the line
582 while doend > 0
583
584 " C-style: cycle through pattern(s)
585 if s:AlignCtrl == 'C' && doend == 1
586 let seppat = s:AlignPat_{ipat}
587" call Decho("Pass".pass.": processing field: AlignCtrl=".s:AlignCtrl." ipat=".ipat." seppat<".seppat.">")
588 let ipat = ipat + 1
589 if ipat > s:AlignPatQty
590 let ipat = 1
591 endif
592 endif
593
594 " cyclic alignment/justification operator handling
595 let alignophold = alignop
596 let alignop = strpart(alignstyle,0,1)
597 if alignop == '+' || doend == 2
598 let alignop= alignophold
599 else
600 let alignstyle = strpart(alignstyle,1).strpart(alignstyle,0,1)
601 let alignopnxt = strpart(alignstyle,0,1)
602 if alignop == ':'
603 let seppat = '$'
604 let doend = 2
605" call Decho("Pass".pass.": alignop<:> case: setting seppat<$> doend==2")
606 endif
607 endif
608
609 " cylic separator alignment specification handling
610 let alignsepop= strpart(alignsep,0,1)
611 let alignsep = strpart(alignsep,1).alignsepop
612
613 " mark end-of-field and the subsequent end-of-separator.
614 " Extend field if alignop is '-'
615 let endfield = match(txt,seppat,bgnfield)
616 let sepfield = matchend(txt,seppat,bgnfield)
617 let skipfield= sepfield
618" call Decho("Pass".pass.": endfield=match(txt<".txt.">,seppat<".seppat.">,bgnfield=".bgnfield.")=".endfield)
619 while alignop == '-' && endfield != -1
620 let endfield = match(txt,seppat,skipfield)
621 let sepfield = matchend(txt,seppat,skipfield)
622 let skipfield = sepfield
623 let alignop = strpart(alignstyle,0,1)
624 let alignstyle= strpart(alignstyle,1).strpart(alignstyle,0,1)
625" call Decho("Pass".pass.": extend field: endfield<".strpart(txt,bgnfield,endfield-bgnfield)."> alignop<".alignop."> alignstyle<".alignstyle.">")
626 endwhile
627 let seplen= sepfield - endfield
628" call Decho("Pass".pass.": seplen=[sepfield=".sepfield."] - [endfield=".endfield."]=".seplen)
629
630 if endfield != -1
631 if pass == 1
632 " ---------------------------------------------------------------------
633 " Pass 1: Update FieldSize to max
634" call Decho("Pass".pass.": before lead/trail remove: field<".strpart(txt,bgnfield,endfield-bgnfield).">")
635 let field = substitute(strpart(txt,bgnfield,endfield-bgnfield),'^\s*\(.\{-}\)\s*$','\1','')
636 if s:AlignLeadKeep == 'W'
637 let field = bgntxt.field
638 let bgntxt= ""
639 endif
640 if exists("g:Align_xstrlen") && g:Align_xstrlen
641 let fieldlen = s:Strlen(field)
642 else
643 let fieldlen = strlen(field)
644 endif
645 let sFieldSize = "FieldSize_".ifield
646 if !exists(sFieldSize)
647 let FieldSize_{ifield}= fieldlen
648" call Decho("Pass".pass.": set FieldSize_{".ifield."}=".FieldSize_{ifield}." <".field.">")
649 elseif fieldlen > FieldSize_{ifield}
650 let FieldSize_{ifield}= fieldlen
651" call Decho("Pass".pass.": oset FieldSize_{".ifield."}=".FieldSize_{ifield}." <".field.">")
652 endif
653 let sSepSize= "SepSize_".ifield
654 if !exists(sSepSize)
655 let SepSize_{ifield}= seplen
656" call Decho(" set SepSize_{".ifield."}=".SepSize_{ifield}." <".field.">")
657 elseif seplen > SepSize_{ifield}
658 let SepSize_{ifield}= seplen
659" call Decho("Pass".pass.": oset SepSize_{".ifield."}=".SepSize_{ifield}." <".field.">")
660 endif
661
662 else
663 " ---------------------------------------------------------------------
664 " Pass 2: Perform Alignment
665 let prepad = strpart(alignprepad,0,1)
666 let postpad = strpart(alignpostpad,0,1)
667 let alignprepad = strpart(alignprepad,1).strpart(alignprepad,0,1)
668 let alignpostpad = strpart(alignpostpad,1).strpart(alignpostpad,0,1)
669 let field = substitute(strpart(txt,bgnfield,endfield-bgnfield),'^\s*\(.\{-}\)\s*$','\1','')
670 if s:AlignLeadKeep == 'W'
671 let field = bgntxt.field
672 let bgntxt= ""
673 endif
674 if doend == 2
675 let prepad = 0
676 let postpad= 0
677 endif
678 if exists("g:Align_xstrlen") && g:Align_xstrlen
679 let fieldlen = s:Strlen(field)
680 else
681 let fieldlen = strlen(field)
682 endif
683 let sep = s:MakeSpace(prepad).strpart(txt,endfield,sepfield-endfield).s:MakeSpace(postpad)
684 if seplen < SepSize_{ifield}
685 if alignsepop == "<"
686 " left-justify separators
687 let sep = sep.s:MakeSpace(SepSize_{ifield}-seplen)
688 elseif alignsepop == ">"
689 " right-justify separators
690 let sep = s:MakeSpace(SepSize_{ifield}-seplen).sep
691 else
692 " center-justify separators
693 let sepleft = (SepSize_{ifield} - seplen)/2
694 let sepright = SepSize_{ifield} - seplen - sepleft
695 let sep = s:MakeSpace(sepleft).sep.s:MakeSpace(sepright)
696 endif
697 endif
698 let spaces = FieldSize_{ifield} - fieldlen
699" 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.">")
700
701 " Perform alignment according to alignment style justification
702 if spaces > 0
703 if alignop == 'c'
704 " center the field
705 let spaceleft = spaces/2
706 let spaceright= FieldSize_{ifield} - spaceleft - fieldlen
707 let newtxt = newtxt.s:MakeSpace(spaceleft).field.s:MakeSpace(spaceright).sep
708 elseif alignop == 'r'
709 " right justify the field
710 let newtxt= newtxt.s:MakeSpace(spaces).field.sep
711 elseif ragged && doend == 2
712 " left justify rightmost field (no trailing blanks needed)
713 let newtxt= newtxt.field
714 else
715 " left justfiy the field
716 let newtxt= newtxt.field.s:MakeSpace(spaces).sep
717 endif
718 elseif ragged && doend == 2
719 " field at maximum field size and no trailing blanks needed
720 let newtxt= newtxt.field
721 else
722 " field is at maximum field size already
723 let newtxt= newtxt.field.sep
724 endif
725" call Decho("Pass".pass.": newtxt<".newtxt.">")
726 endif " pass 1/2
727
728 " bgnfield indexes to end of separator at right of current field
729 " Update field counter
730 let bgnfield= sepfield
731 let ifield = ifield + 1
732 if doend == 2
733 let doend= 0
734 endif
735 " handle end-of-text as end-of-field
736 elseif doend == 1
737 let seppat = '$'
738 let doend = 2
739 else
740 let doend = 0
741 endif " endfield != -1
742 endwhile " doend loop (as well as regularly separated fields)
743
744 if pass == 2
745 " Write altered line to buffer
746" call Decho("Pass".pass.": bgntxt<".bgntxt."> line=".line)
747" call Decho("Pass".pass.": newtxt<".newtxt.">")
748" call Decho("Pass".pass.": endtxt<".endtxt.">")
749 call setline(line,bgntxt.newtxt.endtxt)
750 endif
751
752 let line = line + 1
753 endwhile " line loop
754
755 let pass= pass + 1
756 endwhile " pass loop
757" call Decho("end of two pass loop")
758
759 " Restore user options
760 let &l:et = etkeep
761 let &l:paste = pastekeep
762
763 if exists("s:DoAlignPop")
764 " AlignCtrl Map support
765 call Align#AlignPop()
766 unlet s:DoAlignPop
767 endif
768
769 " restore current search pattern
770 let @/ = keep_search
771 let &ic = keep_ic
772 let &report = keep_report
773
774" call Dret("Align#Align")
775 return
776endfun
777
778" ---------------------------------------------------------------------
779" Align#AlignPush: this command/function pushes an alignment control string onto a stack {{{1
780fun! Align#AlignPush()
781" call Dfunc("AlignPush()")
782
783 " initialize the stack
784 if !exists("s:AlignCtrlStackQty")
785 let s:AlignCtrlStackQty= 1
786 else
787 let s:AlignCtrlStackQty= s:AlignCtrlStackQty + 1
788 endif
789
790 " construct an AlignCtrlStack entry
791 if !exists("s:AlignSep")
792 let s:AlignSep= ''
793 endif
794 let s:AlignCtrlStack_{s:AlignCtrlStackQty}= s:AlignCtrl.'p'.s:AlignPrePad.'P'.s:AlignPostPad.s:AlignLeadKeep.s:AlignStyle.s:AlignSep
795" call Decho("AlignPush: AlignCtrlStack_".s:AlignCtrlStackQty."<".s:AlignCtrlStack_{s:AlignCtrlStackQty}.">")
796
797 " push [GV] patterns onto their own stack
798 if exists("s:AlignGPat")
799 let s:AlignGPat_{s:AlignCtrlStackQty}= s:AlignGPat
800 else
801 let s:AlignGPat_{s:AlignCtrlStackQty}= ""
802 endif
803 if exists("s:AlignVPat")
804 let s:AlignVPat_{s:AlignCtrlStackQty}= s:AlignVPat
805 else
806 let s:AlignVPat_{s:AlignCtrlStackQty}= ""
807 endif
808
809" call Dret("AlignPush")
810endfun
811
812" ---------------------------------------------------------------------
813" Align#AlignPop: this command/function pops an alignment pattern from a stack {{{1
814" and into the AlignCtrl variables.
815fun! Align#AlignPop()
816" call Dfunc("Align#AlignPop()")
817
818 " sanity checks
819 if !exists("s:AlignCtrlStackQty")
820 echoerr "AlignPush needs to be used prior to AlignPop"
821" call Dret("Align#AlignPop <> : AlignPush needs to have been called first")
822 return ""
823 endif
824 if s:AlignCtrlStackQty <= 0
825 unlet s:AlignCtrlStackQty
826 echoerr "AlignPush needs to be used prior to AlignPop"
827" call Dret("Align#AlignPop <> : AlignPop needs to have been called first")
828 return ""
829 endif
830
831 " pop top of AlignCtrlStack and pass value to AlignCtrl
832 let retval=s:AlignCtrlStack_{s:AlignCtrlStackQty}
833 unlet s:AlignCtrlStack_{s:AlignCtrlStackQty}
834 call Align#AlignCtrl(retval)
835
836 " pop G pattern stack
837 if s:AlignGPat_{s:AlignCtrlStackQty} != ""
838 call Align#AlignCtrl('g',s:AlignGPat_{s:AlignCtrlStackQty})
839 else
840 call Align#AlignCtrl('g')
841 endif
842 unlet s:AlignGPat_{s:AlignCtrlStackQty}
843
844 " pop V pattern stack
845 if s:AlignVPat_{s:AlignCtrlStackQty} != ""
846 call Align#AlignCtrl('v',s:AlignVPat_{s:AlignCtrlStackQty})
847 else
848 call Align#AlignCtrl('v')
849 endif
850
851 unlet s:AlignVPat_{s:AlignCtrlStackQty}
852 let s:AlignCtrlStackQty= s:AlignCtrlStackQty - 1
853
854" call Dret("Align#AlignPop <".retval."> : AlignCtrlStackQty=".s:AlignCtrlStackQty)
855 return retval
856endfun
857
858" ---------------------------------------------------------------------
859" Align#AlignReplaceQuotedSpaces: {{{1
860fun! Align#AlignReplaceQuotedSpaces()
861" call Dfunc("AlignReplaceQuotedSpaces()")
862
863 let l:line = getline(line("."))
864 if exists("g:Align_xstrlen") && g:Align_xstrlen
865 let l:linelen = s:Strlen(l:line)
866 else
867 let l:linelen = strlen(l:line)
868 endif
869 let l:startingPos = 0
870 let l:startQuotePos = 0
871 let l:endQuotePos = 0
872 let l:spacePos = 0
873 let l:quoteRe = '\\\@<!"'
874
875" "call Decho("in replace spaces. line=" . line('.'))
876 while (1)
877 let l:startQuotePos = match(l:line, l:quoteRe, l:startingPos)
878 if (l:startQuotePos < 0)
879" "call Decho("No more quotes to the end of line")
880 break
881 endif
882 let l:endQuotePos = match(l:line, l:quoteRe, l:startQuotePos + 1)
883 if (l:endQuotePos < 0)
884" "call Decho("Mismatched quotes")
885 break
886 endif
887 let l:spaceReplaceRe = '^.\{' . (l:startQuotePos + 1) . '}.\{-}\zs\s\ze.*.\{' . (linelen - l:endQuotePos) . '}$'
888" "call Decho('spaceReplaceRe="' . l:spaceReplaceRe . '"')
889 let l:newStr = substitute(l:line, l:spaceReplaceRe, '%', '')
890 while (l:newStr != l:line)
891" "call Decho('newstr="' . l:newStr . '"')
892 let l:line = l:newStr
893 let l:newStr = substitute(l:line, l:spaceReplaceRe, '%', '')
894 endwhile
895 let l:startingPos = l:endQuotePos + 1
896 endwhile
897 call setline(line('.'), l:line)
898
899" call Dret("AlignReplaceQuotedSpaces")
900endfun
901
902" ---------------------------------------------------------------------
903" s:QArgSplitter: to avoid \ processing by <f-args>, <q-args> is needed. {{{1
904" However, <q-args> doesn't split at all, so this function returns a list
905" of arguments which has been:
906" * split at whitespace
907" * unless inside "..."s. One may escape characters with a backslash inside double quotes.
908" along with a leading length-of-list.
909"
910" Examples: %Align "\"" will align on "s
911" %Align " " will align on spaces
912"
913" The resulting list: qarglist[0] corresponds to a:0
914" qarglist[i] corresponds to a:{i}
915fun! s:QArgSplitter(qarg)
916" call Dfunc("s:QArgSplitter(qarg<".a:qarg.">)")
917
918 if a:qarg =~ '".*"'
919 " handle "..." args, which may include whitespace
920 let qarglist = []
921 let args = a:qarg
922" call Decho("handle quoted arguments: args<".args.">")
923 while args != ""
924 let iarg = 0
925 let arglen = strlen(args)
926" call Decho("args[".iarg."]<".args[iarg]."> arglen=".arglen)
927 " find index to first not-escaped '"'
928 while args[iarg] != '"' && iarg < arglen
929 if args[iarg] == '\'
930 let args= strpart(args,1)
931 endif
932 let iarg= iarg + 1
933 endwhile
934" call Decho("args<".args."> iarg=".iarg." arglen=".arglen)
935
936 if iarg > 0
937 " handle left of quote or remaining section
938" call Decho("handle left of quote or remaining section")
939 if args[iarg] == '"'
940 let qarglist= qarglist + split(strpart(args,0,iarg-1))
941 else
942 let qarglist= qarglist + split(strpart(args,0,iarg))
943 endif
944 let args = strpart(args,iarg)
945 let arglen = strlen(args)
946
947 elseif iarg < arglen && args[0] == '"'
948 " handle "quoted" section
949" call Decho("handle quoted section")
950 let iarg= 1
951 while args[iarg] != '"' && iarg < arglen
952 if args[iarg] == '\'
953 let args= strpart(args,1)
954 endif
955 let iarg= iarg + 1
956 endwhile
957" call Decho("args<".args."> iarg=".iarg." arglen=".arglen)
958 if args[iarg] == '"'
959 call add(qarglist,strpart(args,1,iarg-1))
960 let args= strpart(args,iarg+1)
961 else
962 let qarglist = qarglist + split(args)
963 let args = ""
964 endif
965 endif
966" call Decho("qarglist".string(qarglist)." iarg=".iarg." args<".args.">")
967 endwhile
968
969 else
970 " split at all whitespace
971 let qarglist= split(a:qarg)
972 endif
973
974 let qarglistlen= len(qarglist)
975 let qarglist = insert(qarglist,qarglistlen)
976" call Dret("s:QArgSplitter ".string(qarglist))
977 return qarglist
978endfun
979
980" ---------------------------------------------------------------------
981" s:Strlen: this function returns the length of a string, even if its {{{1
982" using two-byte etc characters.
983" Currently, its only used if g:Align_xstrlen is set to a
984" nonzero value. Solution from Nicolai Weibull, vim docs
985" (:help strlen()), Tony Mechelynck, and my own invention.
986fun! s:Strlen(x)
987" call Dfunc("s:Strlen(x<".a:x.">")
988 if g:Align_xstrlen == 1
989 " number of codepoints (Latin a + combining circumflex is two codepoints)
990 " (comment from TM, solution from NW)
991 let ret= strlen(substitute(a:x,'.','c','g'))
992
993 elseif g:Align_xstrlen == 2
994 " number of spacing codepoints (Latin a + combining circumflex is one spacing
995 " codepoint; a hard tab is one; wide and narrow CJK are one each; etc.)
996 " (comment from TM, solution from TM)
997 let ret=strlen(substitute(a:x, '.\Z', 'x', 'g'))
998
999 elseif g:Align_xstrlen == 3
1000 " virtual length (counting, for instance, tabs as anything between 1 and
1001 " 'tabstop', wide CJK as 2 rather than 1, Arabic alif as zero when immediately
1002 " preceded by lam, one otherwise, etc.)
1003 " (comment from TM, solution from me)
1004 let modkeep= &l:mod
1005 exe "norm! o\<esc>"
1006 call setline(line("."),a:x)
1007 let ret= virtcol("$") - 1
1008 d
1009 let &l:mod= modkeep
1010
1011 else
1012 " at least give a decent default
1013 ret= strlen(a:x)
1014 endif
1015" call Dret("s:Strlen ".ret)
1016 return ret
1017endfun
1018
1019" ---------------------------------------------------------------------
1020" Set up default values: {{{1
1021"call Decho("-- Begin AlignCtrl Initialization --")
1022call Align#AlignCtrl("default")
1023"call Decho("-- End AlignCtrl Initialization --")
1024
1025" ---------------------------------------------------------------------
1026" Restore: {{{1
1027let &cpo= s:keepcpo
1028unlet s:keepcpo
1029" vim: ts=4 fdm=marker