From c012f55efda29f09179e921cf148d79deb57616e Mon Sep 17 00:00:00 2001 From: Nick Shipp Date: Sun, 7 May 2017 09:04:01 -0400 Subject: Much maturering of vim configs --- vim/bundle/tabular/.gitignore | 1 + vim/bundle/tabular/LICENSE.md | 24 ++ vim/bundle/tabular/README.md | 29 ++ vim/bundle/tabular/after/plugin/TabularMaps.vim | 73 +++++ vim/bundle/tabular/autoload/tabular.vim | 409 ++++++++++++++++++++++++ vim/bundle/tabular/doc/Tabular.txt | 260 +++++++++++++++ vim/bundle/tabular/plugin/Tabular.vim | 346 ++++++++++++++++++++ 7 files changed, 1142 insertions(+) create mode 100644 vim/bundle/tabular/.gitignore create mode 100644 vim/bundle/tabular/LICENSE.md create mode 100644 vim/bundle/tabular/README.md create mode 100644 vim/bundle/tabular/after/plugin/TabularMaps.vim create mode 100644 vim/bundle/tabular/autoload/tabular.vim create mode 100644 vim/bundle/tabular/doc/Tabular.txt create mode 100644 vim/bundle/tabular/plugin/Tabular.vim (limited to 'vim/bundle/tabular') diff --git a/vim/bundle/tabular/.gitignore b/vim/bundle/tabular/.gitignore new file mode 100644 index 0000000..0a56e3f --- /dev/null +++ b/vim/bundle/tabular/.gitignore @@ -0,0 +1 @@ +/doc/tags diff --git a/vim/bundle/tabular/LICENSE.md b/vim/bundle/tabular/LICENSE.md new file mode 100644 index 0000000..2cc76e9 --- /dev/null +++ b/vim/bundle/tabular/LICENSE.md @@ -0,0 +1,24 @@ +Copyright (c) 2016, 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. diff --git a/vim/bundle/tabular/README.md b/vim/bundle/tabular/README.md new file mode 100644 index 0000000..adbfeb0 --- /dev/null +++ b/vim/bundle/tabular/README.md @@ -0,0 +1,29 @@ +Tabular +============== +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. + +See [Aligning Text with Tabular.vim](http://vimcasts.org/episodes/aligning-text-with-tabular-vim/) +for a screencast that shows how Tabular.vim works. + +See [doc/Tabular.txt](http://raw.github.com/godlygeek/tabular/master/doc/Tabular.txt) +for detailed documentation. + +Installation +============== +If you don't have a preferred installation method, I recommend installing +[pathogen.vim](https://github.com/tpope/vim-pathogen), and then simply +copy and paste: + + mkdir -p ~/.vim/bundle + cd ~/.vim/bundle + git clone git://github.com/godlygeek/tabular.git + +Once help tags have been generated (either using Pathogen's `:Helptags` +command, or by pointing vim's `:helptags` command at the directory where you +installed Tabular), you can view the manual with `:help tabular`. diff --git a/vim/bundle/tabular/after/plugin/TabularMaps.vim b/vim/bundle/tabular/after/plugin/TabularMaps.vim new file mode 100644 index 0000000..998346f --- /dev/null +++ b/vim/bundle/tabular/after/plugin/TabularMaps.vim @@ -0,0 +1,73 @@ +" Copyright (c) 2016, 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. + +if !exists(':Tabularize') || get(g:, 'no_default_tabular_maps', 0) + finish " Tabular.vim wasn't loaded or the default maps are unwanted +endif + +let s:save_cpo = &cpo +set cpo&vim + +AddTabularPattern! assignment /[|&+*/%<>=!~-]\@!=]=\|=\~\)\@![|&+*/%<>=!~-]*=/l1r1 +AddTabularPattern! two_spaces / /l0 + +AddTabularPipeline! multiple_spaces / / map(a:lines, "substitute(v:val, ' *', ' ', 'g')") | tabular#TabularizeStrings(a:lines, ' ', 'l0') + +AddTabularPipeline! argument_list /(.*)/ map(a:lines, 'substitute(v:val, ''\s*\([(,)]\)\s*'', ''\1'', ''g'')') + \ | tabular#TabularizeStrings(a:lines, '[(,)]', 'l0') + \ | map(a:lines, 'substitute(v:val, ''\(\s*\),'', '',\1 '', "g")') + \ | map(a:lines, 'substitute(v:val, ''\s*)'', ")", "g")') + +function! SplitCDeclarations(lines) + let rv = [] + for line in a:lines + " split the line into declaractions + let split = split(line, '\s*[,;]\s*') + " separate the type from the first declaration + let type = substitute(split[0], '\%(\%([&*]\s*\)*\)\=\k\+$', '', '') + " add the ; back on every declaration + call map(split, 'v:val . ";"') + " add the first element to the return as-is, and remove it from the list + let rv += [ remove(split, 0) ] + " transform the other elements by adding the type on at the beginning + call map(split, 'type . v:val') + " and add them all to the return + let rv += split + endfor + return rv +endfunction + +AddTabularPipeline! split_declarations /,.*;/ SplitCDeclarations(a:lines) + +AddTabularPattern! ternary_operator /^.\{-}\zs?\|:/l1 + +AddTabularPattern! cpp_io /<<\|>>/l1 + +AddTabularPattern! pascal_assign /:=/l1 + +AddTabularPattern! trailing_c_comments /\/\*\|\*\/\|\/\//l1 + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/vim/bundle/tabular/autoload/tabular.vim b/vim/bundle/tabular/autoload/tabular.vim new file mode 100644 index 0000000..f60a73c --- /dev/null +++ b/vim/bundle/tabular/autoload/tabular.vim @@ -0,0 +1,409 @@ +" 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. + +" Stupid vimscript crap {{{1 +let s:savecpo = &cpo +set cpo&vim + +" Private Functions {{{1 + +" Return the number of bytes in a string after expanding tabs to spaces. {{{2 +" This expansion is done based on the current value of 'tabstop' +if exists('*strdisplaywidth') + " Needs vim 7.3 + let s:Strlen = function("strdisplaywidth") +else + function! s:Strlen(string) + " Implement the tab handling part of strdisplaywidth for vim 7.2 and + " earlier - not much that can be done about handling doublewidth + " characters. + let rv = 0 + let i = 0 + + for char in split(a:string, '\zs') + if char == "\t" + let rv += &ts - i + let i = 0 + else + let rv += 1 + let i = (i + 1) % &ts + endif + endfor + + return rv + endfunction +endif + +" Align a string within a field {{{2 +" These functions do not trim leading and trailing spaces. + +" Right align 'string' in a field of size 'fieldwidth' +function! s:Right(string, fieldwidth) + let spaces = a:fieldwidth - s:Strlen(a:string) + return matchstr(a:string, '^\s*') . repeat(" ", spaces) . substitute(a:string, '^\s*', '', '') +endfunction + +" Left align 'string' in a field of size 'fieldwidth' +function! s:Left(string, fieldwidth) + let spaces = a:fieldwidth - s:Strlen(a:string) + return a:string . repeat(" ", spaces) +endfunction + +" Center align 'string' in a field of size 'fieldwidth' +function! s:Center(string, fieldwidth) + let spaces = a:fieldwidth - s:Strlen(a:string) + let right = spaces / 2 + let left = right + (right * 2 != spaces) + return repeat(" ", left) . a:string . repeat(" ", right) +endfunction + +" Remove spaces around a string {{{2 + +" Remove all trailing spaces from a string. +function! s:StripTrailingSpaces(string) + return matchstr(a:string, '^.\{-}\ze\s*$') +endfunction + +" Remove all leading spaces from a string. +function! s:StripLeadingSpaces(string) + return matchstr(a:string, '^\s*\zs.*$') +endfunction + +" Split a string into fields and delimiters {{{2 +" Like split(), but include the delimiters as elements +" All odd numbered elements are delimiters +" All even numbered elements are non-delimiters (including zero) +function! s:SplitDelim(string, delim) + let rv = [] + let beg = 0 + + let len = len(a:string) + let searchoff = 0 + + while 1 + let mid = match(a:string, a:delim, beg + searchoff, 1) + if mid == -1 || mid == len + break + endif + + let matchstr = matchstr(a:string, a:delim, beg + searchoff, 1) + let length = strlen(matchstr) + + if length == 0 && beg == mid + " Zero-length match for a zero-length delimiter - advance past it + let searchoff += 1 + continue + endif + + if beg == mid + let rv += [ "" ] + else + let rv += [ a:string[beg : mid-1] ] + endif + + let rv += [ matchstr ] + + let beg = mid + length + let searchoff = 0 + endwhile + + let rv += [ strpart(a:string, beg) ] + + return rv +endfunction + +" Replace lines from `start' to `start + len - 1' with the given strings. {{{2 +" If more lines are needed to show all strings, they will be added. +" If there are too few strings to fill all lines, lines will be removed. +function! s:SetLines(start, len, strings) + if a:start > line('$') + 1 || a:start < 1 + throw "Invalid start line!" + endif + + if len(a:strings) > a:len + let fensave = &fen + let view = winsaveview() + call append(a:start + a:len - 1, repeat([''], len(a:strings) - a:len)) + call winrestview(view) + let &fen = fensave + elseif len(a:strings) < a:len + let fensave = &fen + let view = winsaveview() + sil exe (a:start + len(a:strings)) . ',' . (a:start + a:len - 1) . 'd_' + call winrestview(view) + let &fen = fensave + endif + + call setline(a:start, a:strings) +endfunction + +" Runs the given commandstring argument as an expression. {{{2 +" The commandstring expression is expected to reference the a:lines argument. +" If the commandstring expression returns a list the items of that list will +" replace the items in a:lines, otherwise the expression is assumed to have +" modified a:lines itself. +function! s:FilterString(lines, commandstring) + exe 'let rv = ' . a:commandstring + + if type(rv) == type(a:lines) && rv isnot a:lines + call filter(a:lines, 0) + call extend(a:lines, rv) + endif +endfunction + +" Public API {{{1 + +if !exists("g:tabular_default_format") + let g:tabular_default_format = "l1" +endif + +let s:formatelempat = '\%([lrc]\d\+\)' + +function! tabular#ElementFormatPattern() + return s:formatelempat +endfunction + +" Given a list of strings and a delimiter, split each string on every +" occurrence of the delimiter pattern, format each element according to either +" the provided format (optional) or the default format, and join them back +" together with enough space padding to guarantee that the nth delimiter of +" each string is aligned. +function! tabular#TabularizeStrings(strings, delim, ...) + if a:0 > 1 + echoerr "TabularizeStrings accepts only 2 or 3 arguments (got ".(a:0+2).")" + return 1 + endif + + let formatstr = (a:0 ? a:1 : g:tabular_default_format) + + if formatstr !~? s:formatelempat . '\+' + echoerr "Tabular: Invalid format \"" . formatstr . "\" specified!" + return 1 + endif + + let format = split(formatstr, s:formatelempat . '\zs') + + let lines = map(a:strings, 's:SplitDelim(v:val, a:delim)') + + " Strip spaces + " - Only from non-delimiters; spaces in delimiters must have been matched + " intentionally + " - Don't strip leading spaces from the first element; we like indenting. + for line in lines + if len(line) == 1 && s:do_gtabularize + continue " Leave non-matching lines unchanged for GTabularize + endif + + if line[0] !~ '^\s*$' + let line[0] = s:StripTrailingSpaces(line[0]) + endif + if len(line) >= 3 + for i in range(2, len(line)-1, 2) + let line[i] = s:StripLeadingSpaces(s:StripTrailingSpaces(line[i])) + endfor + endif + endfor + + " Find the max length of each field + let maxes = [] + for line in lines + if len(line) == 1 && s:do_gtabularize + continue " non-matching lines don't affect field widths for GTabularize + endif + + for i in range(len(line)) + if i == len(maxes) + let maxes += [ s:Strlen(line[i]) ] + else + let maxes[i] = max( [ maxes[i], s:Strlen(line[i]) ] ) + endif + endfor + endfor + + let lead_blank = empty(filter(copy(lines), 'v:val[0] =~ "\\S"')) + + " Concatenate the fields, according to the format pattern. + for idx in range(len(lines)) + let line = lines[idx] + + if len(line) == 1 && s:do_gtabularize + let lines[idx] = line[0] " GTabularize doesn't change non-matching lines + continue + endif + + for i in range(len(line)) + let how = format[i % len(format)][0] + let pad = format[i % len(format)][1:-1] + + if how =~? 'l' + let field = s:Left(line[i], maxes[i]) + elseif how =~? 'r' + let field = s:Right(line[i], maxes[i]) + elseif how =~? 'c' + let field = s:Center(line[i], maxes[i]) + endif + + let line[i] = field . (lead_blank && i == 0 ? '' : repeat(" ", pad)) + endfor + + let lines[idx] = s:StripTrailingSpaces(join(line, '')) + endfor +endfunction + +" Apply 0 or more filters, in sequence, to selected text in the buffer {{{2 +" The lines to be filtered are determined as follows: +" If the function is called with a range containing multiple lines, then +" those lines will be used as the range. +" If the function is called with no range or with a range of 1 line, then +" if GTabularize mode is being used, +" the range will not be adjusted +" if "includepat" is not specified, +" that 1 line will be filtered, +" if "includepat" is specified and that line does not match it, +" no lines will be filtered +" if "includepat" is specified and that line does match it, +" all contiguous lines above and below the specified line matching the +" pattern will be filtered. +" +" The remaining arguments must each be a filter to apply to the text. +" Each filter must either be a String evaluating to a function to be called. +function! tabular#PipeRange(includepat, ...) range + exe a:firstline . ',' . a:lastline + \ . 'call tabular#PipeRangeWithOptions(a:includepat, a:000, {})' +endfunction + +" Extended version of tabular#PipeRange, which +" 1) Takes the list of filters as an explicit list rather than as varargs +" 2) Supports passing a dictionary of options to control the routine. +" Currently, the only supported option is 'mode', which determines whether +" to behave as :Tabularize or as :GTabularize +" This allows me to add new features here without breaking API compatibility +" in the future. +function! tabular#PipeRangeWithOptions(includepat, filterlist, options) range + let top = a:firstline + let bot = a:lastline + + let s:do_gtabularize = (get(a:options, 'mode', '') ==# 'GTabularize') + + if !s:do_gtabularize + " In the default mode, apply range extension logic + if a:includepat != '' && top == bot + if top < 0 || top > line('$') || getline(top) !~ a:includepat + return + endif + while top > 1 && getline(top-1) =~ a:includepat + let top -= 1 + endwhile + while bot < line('$') && getline(bot+1) =~ a:includepat + let bot += 1 + endwhile + endif + endif + + let lines = map(range(top, bot), 'getline(v:val)') + + for filter in a:filterlist + if type(filter) != type("") + echoerr "PipeRange: Bad filter: " . string(filter) + endif + + call s:FilterString(lines, filter) + + unlet filter + endfor + + call s:SetLines(top, bot - top + 1, lines) +endfunction + +" Part of the public interface so interested pipelines can query this and +" adjust their behavior appropriately. +function! tabular#DoGTabularize() + return s:do_gtabularize +endfunction + +function! s:SplitDelimTest(string, delim, expected) + let result = s:SplitDelim(a:string, a:delim) + + if result !=# a:expected + echomsg 'Test failed!' + echomsg ' string=' . string(a:string) . ' delim=' . string(a:delim) + echomsg ' Returned=' . string(result) + echomsg ' Expected=' . string(a:expected) + endif +endfunction + +function! tabular#SplitDelimUnitTest() + let assignment = '[|&+*/%<>=!~-]\@!=]=\|=\~\)\@![|&+*/%<>=!~-]*=' + let two_spaces = ' ' + let ternary_operator = '^.\{-}\zs?\|:' + let cpp_io = '<<\|>>' + let pascal_assign = ':=' + let trailing_c_comments = '\/\*\|\*\/\|\/\/' + + call s:SplitDelimTest('a+=b', assignment, ['a', '+=', 'b']) + call s:SplitDelimTest('a-=b', assignment, ['a', '-=', 'b']) + call s:SplitDelimTest('a!=b', assignment, ['a!=b']) + call s:SplitDelimTest('a==b', assignment, ['a==b']) + call s:SplitDelimTest('a&=b', assignment, ['a', '&=', 'b']) + call s:SplitDelimTest('a|=b', assignment, ['a', '|=', 'b']) + call s:SplitDelimTest('a=b=c', assignment, ['a', '=', 'b', '=', 'c']) + + call s:SplitDelimTest('a b c', two_spaces, ['a', ' ', 'b', ' ', 'c']) + call s:SplitDelimTest('a b c', two_spaces, ['a b', ' ', ' c']) + call s:SplitDelimTest('ab c', two_spaces, ['ab', ' ', '', ' ', 'c']) + + call s:SplitDelimTest('a?b:c', ternary_operator, ['a', '?', 'b', ':', 'c']) + + call s:SplitDelimTest('a< + :let g:tabular_loaded = 1 + +============================================================================== +1. Description *tabular-intro* + +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. + +============================================================================== +2. Walkthrough *tabular-walkthrough* *:Tabularize* + +Tabular's commands are based largely on regular expressions. The basic +technique used by Tabular is taking some regex to match field delimiters, +splitting the input lines at those delimiters, trimming unnecessary spaces +from the non-delimiter parts, padding the non-delimiter parts of the lines +with spaces to make them the same length, and joining things back together +again. + +For instance, consider starting with the following lines: +> + Some short phrase,some other phrase + A much longer phrase here,and another long phrase +< +Let's say we want to line these lines up at the commas. We can tell +Tabularize to do this by passing a pattern matching , to the Tabularize +command: +> + :Tabularize /, + + Some short phrase , some other phrase + A much longer phrase here , and another long phrase +< +I encourage you to try copying those lines to another buffer and trying to +call :Tabularize. You'll want to take notice of two things quickly: First, +instead of requiring a range, Tabularize tries to figure out what you want to +happen. Since it knows that you want to act on lines matching a comma, it +will look upwards and downwards for lines around the current line that match a +comma, and consider all contiguous lines matching the pattern to be the range +to be acted upon. You can always override this by specifying a range, though. + +The second thing you should notice is that you'll almost certainly be able to +abbreviate :Tabularize to :Tab - using this form in mappings and scripts is +discouraged as it will make conflicts with other scripts more likely, but for +interactive use it's a nice timesaver. Another convenience feature is that +running :Tabularize without providing a new pattern will cause it to reuse the +last pattern it was called with. + +So, anyway, now the commas line up. Splitting the lines on commas, Tabular +realized that 'Some short phrase' would need to be padded with spaces to match +the length of 'A much longer phrase here', and it did that before joining the +lines back together. You'll also notice that, in addition to the spaces +inserting for padding, extra spaces were inserted between fields. That's +because by default, Tabular prints things left-aligned with one space between +fields. If you wanted to print things right-aligned with no spaces between +fields, you would provide a different format to the Tabularize command: +> + :Tabularize /,/r0 + + Some short phrase, some other phrase + A much longer phrase here,and another long phrase +< +A format specifier is either l, r, or c, followed by one or more digits. If +the letter is l, the field will be left aligned, similarly for r and right +aligning and c and center aligning. The number following the letter is the +number of spaces padding to insert before the start of the next field. +Multiple format specifiers can be added to the same command - each field will +be printed with the next format specifier in the list; when they all have been +used the first will be used again, and so on. So, the last command right +aligned every field, then inserted 0 spaces of padding before the next field. +What if we wanted to right align the text before the comma, and left align the +text after the comma? The command would look like this: +> + :Tabularize /,/r1c1l0 + + Some short phrase , some other phrase + A much longer phrase here , and another long phrase +< +That command would be read as "Align the matching text, splitting fields on +commas. Print everything before the first comma right aligned, then 1 space, +then the comma center aligned, then 1 space, then everything after the comma +left aligned." Notice that the alignment of the field the comma is in is +irrelevant - since it's only 1 cell wide, it looks the same whether it's right, +left, or center aligned. Also notice that the 0 padding spaces specified for +the 3rd field are unused - but they would be used if there were enough fields +to require looping through the fields again. For instance: +> + abc,def,ghi + a,b + a,b,c + + :Tabularize /,/r1c1l0 + + abc , def, ghi + a , b + a , b , c +< +Notice that now, the format pattern has been reused; field 4 (the second comma) +is right aligned, field 5 is center aligned. No spaces were inserted between +the 3rd field (containing "def") and the 4th field (the second comma) because +the format specified 'l0'. + +But, what if you only wanted to act on the first comma on the line, rather than +all of the commas on the line? Let's say we want everything before the first +comma right aligned, then the comma, then everything after the comma left +aligned: +> + abc,def,ghi + a,b + a,b,c + + :Tabularize /^[^,]*\zs,/r0c0l0 + + abc,def,ghi + a,b + a,b,c +< +Here, we used a Vim regex that would only match the first comma on the line. +It matches the beginning of the line, followed by all the non-comma characters +up to the first comma, and then forgets about what it matched so far and +pretends that the match starts exactly at the comma. + +But, now that this command does exactly what we want it to, it's become pretty +unwieldy. It would be unpleasant to need to type that more than once or +twice. The solution is to assign a name to it. +> + :AddTabularPattern first_comma /^[^,]*\zs,/r0c0l0 +< +Now, typing ":Tabularize first_comma" will do the same thing as typing the +whole pattern out each time. Of course this is more useful if you store the +name in a file to be used later. + +NOTE: In order to make these new commands available every time vim starts, +you'll need to put those new commands into a .vim file in a plugin directory +somewhere in your 'runtimepath'. In order to make sure that Tabular.vim has +already been loaded before your file tries to use :AddTabularPattern or +:AddTabularPipeline, the new file should be installed in an after/plugin +directory in 'runtimepath'. In general, it will be safe to find out where the +TabularMaps.vim plugin was installed, and place other files extending +Tabular.vim in the same directory as TabularMaps.vim. For more information, +and some suggested best practices, check out the |tabular-scripting| section. + +Lastly, we'll approach the case where tabular cannot achieve your desired goal +just by splitting lines appart, trimming whitespace, padding with whitespace, +and rejoining the lines. As an example, consider the multiple_spaces command +from TabularMaps.vim. The goal is to split using two or more spaces as a +field delimiter, and join fields back together, properly lined up, with only +two spaces between the end of each field and the beginning of the next. +Unfortunately, Tabular can't do this with only the commands we know so far: +> + :Tabularize / / +< +The above function won't work, because it will consider "a b" as 5 fields +delimited by two pairs of 2 spaces ( 'a', ' ', '', ' ', 'b' ) instead of as +3 fields delimited by one set of 2 or more spaces ( 'a', ' ', 'b' ). +> + :Tabularize / \+/ +< +The above function won't work either, because it will leave the delimiter as 4 +spaces when used against "a b", meaning that we would fail at our goal of +collapsing everything down to two spaces between fields. So, we need a new +command to get around this: +> + :AddTabularPipeline multiple_spaces / \{2,}/ + \ map(a:lines, "substitute(v:val, ' \{2,}', ' ', 'g')") + \ | tabular#TabularizeStrings(a:lines, ' ', 'l0') +< +Yeah. I know it looks complicated. Bear with me. I probably will try to add +in some shortcuts for this syntax, but this verbose will be guaranteed to +always work. + +You should already recognize the name being assigned. The next thing to +happen is / \{2,}/ which is a pattern specifying which lines should +automatically be included in the range when no range is given. Without this, +there would be no pattern to use for extending the range. Everything after +that is a | separated list of expressions to be evaluated. In the context in +which they will be evaluated, a:lines will be set to a List of Strings +containing the text of the lines being filtered as they procede through the +pipeline you've set up. The \ at the start of the lines are just vim's line +continuation marker; you needn't worry much about them. So, the first +expression in the pipeline transforms each line by replacing every instance of +2 or more spaces with exactly two spaces. The second command in the pipeline +performs the equivalent of ":Tabularize / /l0"; the only difference is that +it is operating on a List of Strings rather than text in the buffer. At the +end of the pipeline, the Strings in the modified a:lines (or the return value +of the last expression in the pipeline, if it returns a List) will replace the +chosen range. + +============================================================================== +3. Extending *tabular-scripting* + +As mentioned above, the most important consideration when extending Tabular +with new maps or commands is that your plugin must be loaded after Tabular.vim +has finished loading, and only if Tabular.vim has loaded successfully. The +easiest approach to making sure it loads after Tabular.vim is simply putting +the new file (we'll call it "tabular_extra.vim" as an example) into an +"after/plugin/" directory in 'runtimepath', for instance: +> + ~/.vim/after/plugin/tabular_extra.vim +< +The default set of mappings, found in "TabularMaps.vim", is installed in +the after/plugin/ subdirectory of whatever directory Tabular was installed to. + +The other important consideration is making sure that your commands are only +called if Tabular.vim was actually loaded. The easiest way to do this is by +checking for the existence of the :Tabularize command at the start of your +plugin. A short example plugin would look like this: +> + " after/plugin/my_tabular_commands.vim + " Provides extra :Tabularize commands + + if !exists(':Tabularize') + finish " Give up here; the Tabular plugin musn't have been loaded + endif + + " Make line wrapping possible by resetting the 'cpo' option, first saving it + let s:save_cpo = &cpo + set cpo&vim + + AddTabularPattern! asterisk /*/l1 + + AddTabularPipeline! remove_leading_spaces /^ / + \ map(a:lines, "substitute(v:val, '^ *', '', '')") + + " Restore the saved value of 'cpo' + let &cpo = s:save_cpo + unlet s:save_cpo +< +============================================================================== +vim:tw=78:fo=tcq2:isk=!-~,^*,^\|,^\":ts=8:ft=help:norl: 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 removed ] +function! s:ChooseCommandMap(commandline) + let map = s:TabularCommands + let cmd = a:commandline + + if cmd =~# '^\s\+' + if !exists('b:TabularCommands') + let b:TabularCommands = {} + endif + let map = b:TabularCommands + let cmd = substitute(cmd, '^\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 = '\\\@ 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[!] [] name /pattern[/format] +" +" If 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(, 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[!] [] name /pattern/ func [ | func2 [ | func3 ] ] +" +" If 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(, 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, '^/.\{-}\\\@CompleteTabularizeCommand + \ Tabularize ,call Tabularize() + +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,CompleteTabularizeCommand + \ GTabularize , + \ call Tabularize(, { 'mode': 'GTabularize' } ) + +" Stupid vimscript crap, part 2 {{{1 +let &cpo = s:savecpo +unlet s:savecpo + +" vim:set sw=2 sts=2 fdm=marker: -- cgit v1.2.3-54-g00ecf