summaryrefslogtreecommitdiff
path: root/vim/bundle/tabular/plugin/Tabular.vim
diff options
context:
space:
mode:
Diffstat (limited to 'vim/bundle/tabular/plugin/Tabular.vim')
-rw-r--r--vim/bundle/tabular/plugin/Tabular.vim346
1 files changed, 346 insertions, 0 deletions
diff --git a/vim/bundle/tabular/plugin/Tabular.vim b/vim/bundle/tabular/plugin/Tabular.vim
new file mode 100644
index 0000000..e73329a
--- /dev/null
+++ b/vim/bundle/tabular/plugin/Tabular.vim
@@ -0,0 +1,346 @@
+" Tabular: Align columnar data using regex-designated column boundaries
+" Maintainer: Matthew Wozniski (godlygeek@gmail.com)
+" Date: Thu, 03 May 2012 20:49:32 -0400
+" Version: 1.0
+"
+" Long Description:
+" Sometimes, it's useful to line up text. Naturally, it's nicer to have the
+" computer do this for you, since aligning things by hand quickly becomes
+" unpleasant. While there are other plugins for aligning text, the ones I've
+" tried are either impossibly difficult to understand and use, or too simplistic
+" to handle complicated tasks. This plugin aims to make the easy things easy
+" and the hard things possible, without providing an unnecessarily obtuse
+" interface. It's still a work in progress, and criticisms are welcome.
+"
+" License:
+" Copyright (c) 2012, Matthew J. Wozniski
+" All rights reserved.
+"
+" Redistribution and use in source and binary forms, with or without
+" modification, are permitted provided that the following conditions are met:
+" * Redistributions of source code must retain the above copyright notice,
+" this list of conditions and the following disclaimer.
+" * Redistributions in binary form must reproduce the above copyright
+" notice, this list of conditions and the following disclaimer in the
+" documentation and/or other materials provided with the distribution.
+" * The names of the contributors may not be used to endorse or promote
+" products derived from this software without specific prior written
+" permission.
+"
+" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
+" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+" NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT,
+" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+" Abort if running in vi-compatible mode or the user doesn't want us.
+if &cp || exists('g:tabular_loaded')
+ if &cp && &verbose
+ echo "Not loading Tabular in compatible mode."
+ endif
+ finish
+endif
+
+let g:tabular_loaded = 1
+
+" Stupid vimscript crap {{{1
+let s:savecpo = &cpo
+set cpo&vim
+
+" Private Things {{{1
+
+" Dictionary of command name to command
+let s:TabularCommands = {}
+
+" Generate tab completion list for :Tabularize {{{2
+" Return a list of commands that match the command line typed so far.
+" NOTE: Tries to handle commands with spaces in the name, but Vim doesn't seem
+" to handle that terribly well... maybe I should give up on that.
+function! s:CompleteTabularizeCommand(argstart, cmdline, cursorpos)
+ let names = keys(s:TabularCommands)
+ if exists("b:TabularCommands")
+ let names += keys(b:TabularCommands)
+ endif
+
+ let cmdstart = substitute(a:cmdline, '^\s*\S\+\s*', '', '')
+
+ return filter(names, 'v:val =~# ''^\V'' . escape(cmdstart, ''\'')')
+endfunction
+
+" Choose the proper command map from the given command line {{{2
+" Returns [ command map, command line with leading <buffer> removed ]
+function! s:ChooseCommandMap(commandline)
+ let map = s:TabularCommands
+ let cmd = a:commandline
+
+ if cmd =~# '^<buffer>\s\+'
+ if !exists('b:TabularCommands')
+ let b:TabularCommands = {}
+ endif
+ let map = b:TabularCommands
+ let cmd = substitute(cmd, '^<buffer>\s\+', '', '')
+ endif
+
+ return [ map, cmd ]
+endfunction
+
+" Parse '/pattern/format' into separate pattern and format parts. {{{2
+" If parsing fails, return [ '', '' ]
+function! s:ParsePattern(string)
+ if a:string[0] != '/'
+ return ['','']
+ endif
+
+ let pat = '\\\@<!\%(\\\\\)\{-}\zs/' . tabular#ElementFormatPattern() . '*$'
+ let format = matchstr(a:string[1:-1], pat)
+ if !empty(format)
+ let format = format[1 : -1]
+ let pattern = a:string[1 : -len(format) - 2]
+ else
+ let pattern = a:string[1 : -1]
+ endif
+
+ return [pattern, format]
+endfunction
+
+" Split apart a list of | separated expressions. {{{2
+function! s:SplitCommands(string)
+ if a:string =~ '^\s*$'
+ return []
+ endif
+
+ let end = match(a:string, "[\"'|]")
+
+ " Loop until we find a delimiting | or end-of-string
+ while end != -1 && (a:string[end] != '|' || a:string[end+1] == '|')
+ if a:string[end] == "'"
+ let end = match(a:string, "'", end+1) + 1
+ if end == 0
+ throw "No matching end single quote"
+ endif
+ elseif a:string[end] == '"'
+ " Find a " preceded by an even number of \ (or 0)
+ let pattern = '\%(\\\@<!\%(\\\\\)*\)\@<="'
+ let end = matchend(a:string, pattern, end+1) + 1
+ if end == 0
+ throw "No matching end double quote"
+ endif
+ else " Found ||
+ let end += 2
+ endif
+
+ let end = match(a:string, "[\"'|]", end)
+ endwhile
+
+ if end == 0 || a:string[0 : end - (end > 0)] =~ '^\s*$'
+ throw "Empty element"
+ endif
+
+ if end == -1
+ let rv = [ a:string ]
+ else
+ let rv = [ a:string[0 : end-1] ] + s:SplitCommands(a:string[end+1 : -1])
+ endif
+
+ return rv
+endfunction
+
+" Public Things {{{1
+
+" Command associating a command name with a simple pattern command {{{2
+" AddTabularPattern[!] [<buffer>] name /pattern[/format]
+"
+" If <buffer> is provided, the command will only be available in the current
+" buffer, and will be used instead of any global command with the same name.
+"
+" If a command with the same name and scope already exists, it is an error,
+" unless the ! is provided, in which case the existing command will be
+" replaced.
+"
+" pattern is a regex describing the delimiter to be used.
+"
+" format describes the format pattern to be used. The default will be used if
+" none is provided.
+com! -nargs=+ -bang AddTabularPattern
+ \ call AddTabularPattern(<q-args>, <bang>0)
+
+function! AddTabularPattern(command, force)
+ try
+ let [ commandmap, rest ] = s:ChooseCommandMap(a:command)
+
+ let name = matchstr(rest, '.\{-}\ze\s*/')
+ let pattern = substitute(rest, '.\{-}\s*\ze/', '', '')
+
+ let [ pattern, format ] = s:ParsePattern(pattern)
+
+ if empty(name) || empty(pattern)
+ throw "Invalid arguments!"
+ endif
+
+ if !a:force && has_key(commandmap, name)
+ throw string(name) . " is already defined, use ! to overwrite."
+ endif
+
+ let command = "tabular#TabularizeStrings(a:lines, " . string(pattern)
+
+ if !empty(format)
+ let command .= ", " . string(format)
+ endif
+
+ let command .= ")"
+
+ let commandmap[name] = { 'pattern' : pattern, 'commands' : [ command ] }
+ catch
+ echohl ErrorMsg
+ echomsg "AddTabularPattern: " . v:exception
+ echohl None
+ endtry
+endfunction
+
+" Command associating a command name with a pipeline of functions {{{2
+" AddTabularPipeline[!] [<buffer>] name /pattern/ func [ | func2 [ | func3 ] ]
+"
+" If <buffer> is provided, the command will only be available in the current
+" buffer, and will be used instead of any global command with the same name.
+"
+" If a command with the same name and scope already exists, it is an error,
+" unless the ! is provided, in which case the existing command will be
+" replaced.
+"
+" pattern is a regex that will be used to determine which lines will be
+" filtered. If the cursor line doesn't match the pattern, using the command
+" will be a no-op, otherwise the cursor and all contiguous lines matching the
+" pattern will be filtered.
+"
+" Each 'func' argument represents a function to be called. This function
+" will have access to a:lines, a List containing one String per line being
+" filtered.
+com! -nargs=+ -bang AddTabularPipeline
+ \ call AddTabularPipeline(<q-args>, <bang>0)
+
+function! AddTabularPipeline(command, force)
+ try
+ let [ commandmap, rest ] = s:ChooseCommandMap(a:command)
+
+ let name = matchstr(rest, '.\{-}\ze\s*/')
+ let pattern = substitute(rest, '.\{-}\s*\ze/', '', '')
+
+ let commands = matchstr(pattern, '^/.\{-}\\\@<!\%(\\\\\)\{-}/\zs.*')
+ let pattern = matchstr(pattern, '/\zs.\{-}\\\@<!\%(\\\\\)\{-}\ze/')
+
+ if empty(name) || empty(pattern)
+ throw "Invalid arguments!"
+ endif
+
+ if !a:force && has_key(commandmap, name)
+ throw string(name) . " is already defined, use ! to overwrite."
+ endif
+
+ let commandlist = s:SplitCommands(commands)
+
+ if empty(commandlist)
+ throw "Must provide a list of functions!"
+ endif
+
+ let commandmap[name] = { 'pattern' : pattern, 'commands' : commandlist }
+ catch
+ echohl ErrorMsg
+ echomsg "AddTabularPipeline: " . v:exception
+ echohl None
+ endtry
+endfunction
+
+" Tabularize /pattern[/format] {{{2
+" Tabularize name
+"
+" Align text, either using the given pattern, or the command associated with
+" the given name.
+com! -nargs=* -range -complete=customlist,<SID>CompleteTabularizeCommand
+ \ Tabularize <line1>,<line2>call Tabularize(<q-args>)
+
+function! Tabularize(command, ...) range
+ let piperange_opt = {}
+ if a:0
+ let piperange_opt = a:1
+ endif
+
+ if empty(a:command)
+ if !exists("s:last_tabularize_command")
+ echohl ErrorMsg
+ echomsg "Tabularize hasn't been called yet; no pattern/command to reuse!"
+ echohl None
+ return
+ endif
+ else
+ let s:last_tabularize_command = a:command
+ endif
+
+ let command = s:last_tabularize_command
+
+ let range = a:firstline . ',' . a:lastline
+
+ try
+ let [ pattern, format ] = s:ParsePattern(command)
+
+ if !empty(pattern)
+ let cmd = "tabular#TabularizeStrings(a:lines, " . string(pattern)
+
+ if !empty(format)
+ let cmd .= "," . string(format)
+ endif
+
+ let cmd .= ")"
+
+ exe range . 'call tabular#PipeRangeWithOptions(pattern, [ cmd ], '
+ \ . 'piperange_opt)'
+ else
+ if exists('b:TabularCommands') && has_key(b:TabularCommands, command)
+ let usercmd = b:TabularCommands[command]
+ elseif has_key(s:TabularCommands, command)
+ let usercmd = s:TabularCommands[command]
+ else
+ throw "Unrecognized command " . string(command)
+ endif
+
+ exe range . 'call tabular#PipeRangeWithOptions(usercmd["pattern"], '
+ \ . 'usercmd["commands"], piperange_opt)'
+ endif
+ catch
+ echohl ErrorMsg
+ echomsg "Tabularize: " . v:exception
+ echohl None
+ return
+ endtry
+endfunction
+
+" GTabularize /pattern[/format] {{{2
+" GTabularize name
+"
+" Align text on only matching lines, either using the given pattern, or the
+" command associated with the given name. Mnemonically, this is similar to
+" the :global command, which takes some action on all rows matching a pattern
+" in a range. This command is different from normal :Tabularize in 3 ways:
+" 1) If a line in the range does not match the pattern, it will be left
+" unchanged, and not in any way affect the outcome of other lines in the
+" range (at least, normally - but Pipelines can and will still look at
+" non-matching rows unless they are specifically written to be aware of
+" tabular#DoGTabularize() and handle it appropriately).
+" 2) No automatic range determination - :Tabularize automatically expands
+" a single-line range (or a call with no range) to include all adjacent
+" matching lines. That behavior does not make sense for this command.
+" 3) If called without a range, it will act on all lines in the buffer (like
+" :global) rather than only a single line
+com! -nargs=* -range=% -complete=customlist,<SID>CompleteTabularizeCommand
+ \ GTabularize <line1>,<line2>
+ \ call Tabularize(<q-args>, { 'mode': 'GTabularize' } )
+
+" Stupid vimscript crap, part 2 {{{1
+let &cpo = s:savecpo
+unlet s:savecpo
+
+" vim:set sw=2 sts=2 fdm=marker: