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: