Vim Tips Wiki
(Reword and replace script)
(copy in JB todo notes from 200812 tip)
Line 1: Line 1:
{{Duplicate|893|139|570}}
 
 
{{TipImported
 
{{TipImported
 
|id=894
 
|id=894
Line 72: Line 71:
 
endfunction
 
endfunction
 
</pre>
 
</pre>
  +
  +
==Related tips==
  +
{{todo}}
  +
*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'''
  +
*[[VimTip139|139 Align text plugin]] describes the {{script|id=294|text=Align}} plugin (script 294)
  +
*[[VimTip253|253 Specify a column with bar]]
  +
*[[VimTip319|319 Simple text alignment]]
  +
*[[VimTip893|893 Align numbers at decimal point]]
  +
*894 Regex-based text alignment ''this tip''
  +
  +
'''Align text using an external script'''
  +
*[[VimTip570|570 Align text into a table]] Perl script '''intend merging this to 139'''
  +
  +
'''Align tables using Vim'''
  +
*[[VimTip547|547 Smarter Table Editing]]
  +
*[[VimTip554|554 Smarter Table Editing II]]
  +
  +
'''Invoke an external text formatting tool'''
  +
*[[VimTip584|584 Par text reformatter]]
   
 
==Comments==
 
==Comments==

Revision as of 04:48, 7 April 2009

Tip 894 Printable Monobook Previous Next

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

Align text using an external script

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