created 2005 · complexity intermediate · author Nall-ohki · version 7.0
Some people like to align program statements, for example, so that the "=
" in assignments are lined up. This tip provides a script that defaults to aligning "=
", but which can align using a search pattern. The script may not work correctly when tab characters are present.
Examples[]
For example, consider the lines:
let range = '%' let foo = 'Vim' let something = 42
With this tip, if you visually select the lines then press \a
(assuming the default backslash leader key), the lines would be changed to:
let range = '%' let foo = 'Vim' let something = 42
Now consider:
int Add(int a, int b); void Point(doublePt dp); const *char GetString(unsigned int index);
Selecting these lines, then entering the command :Align \S\+(
would align the function names. Repeating, with command :Align (
would then align the (
. Doing both results in:
int Add (int a, int b); void Point (doublePt dp); const *char GetString (unsigned int index);
Script[]
Save the following in a file, say, align.vim
. Enter the command :so align.vim
to source the script.
command! -nargs=? -range Align <line1>,<line2>call AlignSection('<args>') vnoremap <silent> <Leader>a :Align<CR> function! AlignSection(regex) range let extra = 1 let sep = empty(a:regex) ? '=' : a:regex let maxpos = 0 let section = getline(a:firstline, a:lastline) for line in section let pos = match(line, ' *'.sep) if maxpos < pos let maxpos = pos endif endfor call map(section, 'AlignLine(v:val, sep, maxpos, extra)') call setline(a:firstline, section) endfunction function! AlignLine(line, sep, maxpos, extra) let m = matchlist(a:line, '\(.\{-}\) \{-}\('.a:sep.'.*\)') if empty(m) return a:line endif let spaces = repeat(' ', a:maxpos - strlen(m[1]) + a:extra) return m[1] . spaces . m[2] endfunction
Related tips[]
TO DO
- We have a number of tips which use the terms "align", "justify", "format" in various, often inconsistent, ways. Need to check each tip and see they use the words correctly.
- Need to clean up following tips. Some might be merged, but want to avoid undue complexity.
Align text using Vim
- 139 Align text plugin describes the Align plugin (script 294)
- 253 Specify a column with bar
- 319 Simple text alignment
- 893 Align numbers at decimal point
- 894 Regex-based text alignment this tip
Align text using an external script
- 570 Align text into a table Perl script intend merging this to 139
Align tables using Vim
Invoke an external text formatting tool
Comments[]
See Align text plugin which describes the Align plugin. Separators are regular expression patterns, and one may restrict Align to operate only on lines which satisfy (or don't satisfy) a regular expression pattern (:AlignCtrl g pattern
or :AlignCtrl v pattern
).
Alternative script[]
TO DO
- Examine following. Is it worth keeping?
I decided to extend the tip's functionality to be able to do left, right and shift alignments. Shift alignments are basically indentation alignments that are useful for aligning decimal points on numbers.
Align()
also allows alignment of words before and after the regex pattern. For words before the pattern, enter a positive number when prompted. For words after the pattern, enter a negative number instead. For example, to choose the second word after the regex pattern, enter -2
. After that, select the type of alignment to be done.
"place in vimrc - tested on gvim 6.3 set magic "align code - select lines with Visual Block using <Ctrl-V><move down> vmap <C-F4> :call Align(Prompt("0"),Prompt("1","0"),Prompt("2","l"))<CR> "align code function function Align(regex, wnum, align) range let range = a:firstline.",".a:lastline let curcol = 0 let maxcol = 0 "find maximum column let words = Words(a:wnum, a:align, "find") let i = a:firstline while i <= a:lastline let line = getline(i) if line =~ a:regex if a:wnum < 0 let curcol = matchend(line, a:regex.words) else let curcol = match(line, words.a:regex) endif let maxcol = curcol > maxcol ? curcol : maxcol endif let i = i + 1 endwhile "perform alignment let i = a:firstline while i <= a:lastline let line = getline(i) if line =~ a:regex if a:wnum < 0 let curcol = matchend(line, a:regex.words) else let curcol = match(line, words.a:regex) endif let pad = "" while strlen(pad) < (maxcol - curcol) let pad = pad." " endwhile "determine padding location let words2 = Words(a:wnum, a:align, "pad") if a:wnum < 0 let curcol = matchend(line, a:regex.words2) else let curcol = match(line, words2.a:regex) endif "left-word or shift aligned if a:align == "s" call setline(i, pad.strpart(line, 0, curcol).strpart(line, curcol)) else call setline(i, strpart(line, 0, curcol).pad.strpart(line, curcol)) endif endif let i = i + 1 endwhile execute "normal gv" endfunction "set up words regular expression function Words(wnum, align, action) if a:align == "r" if a:action == "find" if a:wnum > 0 let words = "\\(\\S*\\s*\\)\\{".a:wnum."}" elseif a:wnum < -1 let words = "\\(\\s*\\S*\\)\\{".(-a:wnum)."}" elseif a:wnum == -1 let words = "\\s*\\S*" else let words = "" endif elseif a:action == "pad" if a:wnum >= 0 let words = "\\(\\S*\\s*\\)\\{".(a:wnum + 1)."}" elseif a:wnum < -1 let words = "\\s*\\(\\S*\\s*\\)\\{".(-a:wnum - 1)."}" elseif a:wnum == -1 let words = "\\s*" else let words = "" endif endif else if a:wnum > 0 let words = "\\(\\S*\\s*\\)\\{".a:wnum."}" elseif a:wnum < -1 let words = "\\s*\\(\\S*\\s*\\)\\{".(-a:wnum - 1)."}" elseif a:wnum == -1 let words = "\\s*" else let words = "" endif endif return words endfunction "prompt user for alignment settings function Prompt(str, ...) if a:str if a:str == "1" let str = "How many words before pattern? " elseif a:str == "2" let str = "Alignment [(l)eft (r)ight (s)hift]? " endif else let str = "Pattern? " endif let default = a:0 ? a:1 : "" execute "let ret = input(\"".str."\", \"".default."\")" return ret endfunction