citadel

My dotfiles, scripts and nix configs
git clone git://jb55.com/citadel
Log | Files | Refs | README | LICENSE

Tabular.vim (8445B)


      1 " Tabular:     Align columnar data using regex-designated column boundaries
      2 " Maintainer:  Matthew Wozniski (mjw@drexel.edu)
      3 " Date:        Thu, 11 Oct 2007 00:35:34 -0400
      4 " Version:     0.1
      5 
      6 " Abort if running in vi-compatible mode or the user doesn't want us.
      7 if &cp || exists('g:tabular_loaded')
      8   if &cp && &verbose
      9     echo "Not loading Tabular in compatible mode."
     10   endif
     11   finish
     12 endif
     13 
     14 let g:tabular_loaded = 1
     15 
     16 " Stupid vimscript crap                                                   {{{1
     17 let s:savecpo = &cpo
     18 set cpo&vim
     19 
     20 " Private Things                                                          {{{1
     21 
     22 " Dictionary of command name to command
     23 let s:TabularCommands = {}
     24 
     25 " Generate tab completion list for :Tabularize                            {{{2
     26 " Return a list of commands that match the command line typed so far.
     27 " NOTE: Tries to handle commands with spaces in the name, but Vim doesn't seem
     28 "       to handle that terribly well... maybe I should give up on that.
     29 function! s:CompleteTabularizeCommand(argstart, cmdline, cursorpos)
     30   let names = keys(s:TabularCommands)
     31   if exists("b:TabularCommands")
     32     let names += keys(b:TabularCommands)
     33   endif
     34 
     35   let cmdstart = substitute(a:cmdline, '^\s*\S\+\s*', '', '')
     36 
     37   return filter(names, 'v:val =~# ''^\V'' . escape(cmdstart, ''\'')')
     38 endfunction
     39 
     40 " Choose the proper command map from the given command line               {{{2
     41 " Returns [ command map, command line with leading <buffer> removed ]
     42 function! s:ChooseCommandMap(commandline)
     43   let map = s:TabularCommands
     44   let cmd = a:commandline
     45 
     46   if cmd =~# '^<buffer>\s\+'
     47     if !exists('b:TabularCommands')
     48       let b:TabularCommands = {}
     49     endif
     50     let map = b:TabularCommands
     51     let cmd = substitute(cmd, '^<buffer>\s\+', '', '')
     52   endif
     53 
     54   return [ map, cmd ]
     55 endfunction
     56 
     57 " Parse '/pattern/format' into separate pattern and format parts.         {{{2
     58 " If parsing fails, return [ '', '' ]
     59 function! s:ParsePattern(string)
     60   if a:string[0] != '/'
     61     return ['','']
     62   endif
     63 
     64   let pat = '\\\@<!\%(\\\\\)\{-}\zs/' . tabular#ElementFormatPattern() . '*$'
     65   let format = matchstr(a:string[1:-1], pat)
     66   if !empty(format)
     67     let format = format[1 : -1]
     68     let pattern = a:string[1 : -len(format) - 2]
     69   else
     70     let pattern = a:string[1 : -1]
     71   endif
     72 
     73   return [pattern, format]
     74 endfunction
     75 
     76 " Split apart a list of | separated expressions.                          {{{2
     77 function! s:SplitCommands(string)
     78   if a:string =~ '^\s*$'
     79     return []
     80   endif
     81 
     82   let end = match(a:string, "[\"'|]")
     83 
     84   " Loop until we find a delimiting | or end-of-string
     85   while end != -1 && (a:string[end] != '|' || a:string[end+1] == '|')
     86     if a:string[end] == "'"
     87       let end = match(a:string, "'", end+1) + 1
     88       if end == 0
     89         throw "No matching end single quote"
     90       endif
     91     elseif a:string[end] == '"'
     92       " Find a " preceded by an even number of \ (or 0)
     93       let pattern = '\%(\\\@<!\%(\\\\\)*\)\@<="'
     94       let end = matchend(a:string, pattern, end+1) + 1
     95       if end == 0
     96         throw "No matching end double quote"
     97       endif
     98     else " Found ||
     99       let end += 2
    100     endif
    101 
    102     let end = match(a:string, "[\"'|]", end)
    103   endwhile
    104 
    105   if end == 0 || a:string[0 : end - (end > 0)] =~ '^\s*$'
    106     throw "Empty element"
    107   endif
    108 
    109   if end == -1
    110     let rv = [ a:string ]
    111   else
    112     let rv = [ a:string[0 : end-1] ] + s:SplitCommands(a:string[end+1 : -1])
    113   endif
    114 
    115   return rv
    116 endfunction
    117 
    118 " Public Things                                                           {{{1
    119 
    120 " Command associating a command name with a simple pattern command        {{{2
    121 " AddTabularPattern[!] [<buffer>] name /pattern[/format]
    122 "
    123 " If <buffer> is provided, the command will only be available in the current
    124 " buffer, and will be used instead of any global command with the same name.
    125 "
    126 " If a command with the same name and scope already exists, it is an error,
    127 " unless the ! is provided, in which case the existing command will be
    128 " replaced.
    129 "
    130 " pattern is a regex describing the delimiter to be used.
    131 "
    132 " format describes the format pattern to be used.  The default will be used if
    133 " none is provided.
    134 com! -nargs=+ -bang AddTabularPattern
    135    \ call AddTabularPattern(<q-args>, <bang>0)
    136 
    137 function! AddTabularPattern(command, force)
    138   try
    139     let [ commandmap, rest ] = s:ChooseCommandMap(a:command)
    140 
    141     let name = matchstr(rest, '.\{-}\ze\s*/')
    142     let pattern = substitute(rest, '.\{-}\s*\ze/', '', '')
    143 
    144     let [ pattern, format ] = s:ParsePattern(pattern)
    145 
    146     if empty(name) || empty(pattern)
    147       throw "Invalid arguments!"
    148     endif
    149 
    150     if !a:force && has_key(commandmap, name)
    151       throw string(name) . " is already defined, use ! to overwrite."
    152     endif
    153 
    154     let command = "tabular#TabularizeStrings(a:lines, " . string(pattern)
    155 
    156     if !empty(format)
    157       let command .=  ", " . string(format)
    158     endif
    159 
    160     let command .= ")"
    161 
    162     let commandmap[name] = ":call tabular#PipeRange("
    163           \ . string(pattern) . ","
    164           \ . string(command) . ")"
    165   catch
    166     echohl ErrorMsg
    167     echomsg "AddTabularPattern: " . v:exception
    168     echohl None
    169   endtry
    170 endfunction
    171 
    172 " Command associating a command name with a pipeline of functions         {{{2
    173 " AddTabularPipeline[!] [<buffer>] name /pattern/ func [ | func2 [ | func3 ] ]
    174 "
    175 " If <buffer> is provided, the command will only be available in the current
    176 " buffer, and will be used instead of any global command with the same name.
    177 "
    178 " If a command with the same name and scope already exists, it is an error,
    179 " unless the ! is provided, in which case the existing command will be
    180 " replaced.
    181 "
    182 " pattern is a regex that will be used to determine which lines will be
    183 " filtered.  If the cursor line doesn't match the pattern, using the command
    184 " will be a no-op, otherwise the cursor and all contiguous lines matching the
    185 " pattern will be filtered.
    186 "
    187 " Each 'func' argument represents a function to be called.  This function
    188 " will have access to a:lines, a List containing one String per line being
    189 " filtered.
    190 com! -nargs=+ -bang AddTabularPipeline
    191    \ call AddTabularPipeline(<q-args>, <bang>0)
    192 
    193 function! AddTabularPipeline(command, force)
    194   try
    195     let [ commandmap, rest ] = s:ChooseCommandMap(a:command)
    196 
    197     let name = matchstr(rest, '.\{-}\ze\s*/')
    198     let pattern = substitute(rest, '.\{-}\s*\ze/', '', '')
    199 
    200     let commands = matchstr(pattern, '^/.\{-}\\\@<!\%(\\\\\)\{-}/\zs.*')
    201     let pattern = matchstr(pattern, '/\zs.\{-}\\\@<!\%(\\\\\)\{-}\ze/')
    202 
    203     if empty(name) || empty(pattern)
    204       throw "Invalid arguments!"
    205     endif
    206 
    207     if !a:force && has_key(commandmap, name)
    208       throw string(name) . " is already defined, use ! to overwrite."
    209     endif
    210 
    211     let commandlist = s:SplitCommands(commands)
    212 
    213     if empty(commandlist)
    214       throw "Must provide a list of functions!"
    215     endif
    216 
    217     let cmd = ":call tabular#PipeRange(" . string(pattern)
    218 
    219     for command in commandlist
    220       let cmd .= "," . string(command)
    221     endfor
    222 
    223     let cmd .= ")"
    224 
    225     let commandmap[name] = cmd
    226   catch
    227     echohl ErrorMsg
    228     echomsg "AddTabularPipeline: " . v:exception
    229     echohl None
    230   endtry
    231 endfunction
    232 
    233 " Tabularize /pattern[/format]                                            {{{2
    234 " Tabularize name
    235 "
    236 " Align text, either using the given pattern, or the command associated with
    237 " the given name.
    238 com! -nargs=+ -range -complete=customlist,<SID>CompleteTabularizeCommand
    239    \ Tabularize <line1>,<line2>call Tabularize(<q-args>)
    240 
    241 function! Tabularize(command) range
    242   let range = a:firstline . ',' . a:lastline
    243 
    244   try
    245     let [ pattern, format ] = s:ParsePattern(a:command)
    246 
    247     if !empty(pattern)
    248       let cmd  = "tabular#TabularizeStrings(a:lines, " . string(pattern)
    249 
    250       if !empty(format)
    251         let cmd .= "," . string(format)
    252       endif
    253 
    254       let cmd .= ")"
    255 
    256       exe range . 'call tabular#PipeRange(pattern, cmd)'
    257     else
    258       if exists('b:TabularCommands') && has_key(b:TabularCommands, a:command)
    259         let command = b:TabularCommands[a:command]
    260       elseif has_key(s:TabularCommands, a:command)
    261         let command = s:TabularCommands[a:command]
    262       else
    263         throw "Unrecognized command " . string(a:command)
    264       endif
    265 
    266       exe range . command
    267     endif
    268   catch
    269     echohl ErrorMsg
    270     echomsg "Tabularize: " . v:exception
    271     echohl None
    272     return
    273   endtry
    274 endfunction
    275 
    276 " Stupid vimscript crap, part 2                                           {{{1
    277 let &cpo = s:savecpo
    278 unlet s:savecpo
    279 
    280 " vim:set sw=2 sts=2 fdm=marker: