dblume commited on 2024-10-26 01:08:07
Showing 13 changed files, with 2368 additions and 1 deletions.
See https://github.com/godlygeek/tabular
This common command will usually do what I need:
:Tab /|
| ... | ... |
@@ -0,0 +1,24 @@ |
| 1 |
+Copyright (c) 2016, Matthew J. Wozniski |
|
| 2 |
+All rights reserved. |
|
| 3 |
+ |
|
| 4 |
+Redistribution and use in source and binary forms, with or without |
|
| 5 |
+modification, are permitted provided that the following conditions are met: |
|
| 6 |
+ * Redistributions of source code must retain the above copyright notice, |
|
| 7 |
+ this list of conditions and the following disclaimer. |
|
| 8 |
+ * Redistributions in binary form must reproduce the above copyright |
|
| 9 |
+ notice, this list of conditions and the following disclaimer in the |
|
| 10 |
+ documentation and/or other materials provided with the distribution. |
|
| 11 |
+ * The names of the contributors may not be used to endorse or promote |
|
| 12 |
+ products derived from this software without specific prior written |
|
| 13 |
+ permission. |
|
| 14 |
+ |
|
| 15 |
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS |
|
| 16 |
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
| 17 |
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
|
| 18 |
+NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
| 19 |
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 20 |
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
|
| 21 |
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
| 22 |
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
| 23 |
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
|
| 24 |
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| ... | ... |
@@ -0,0 +1,38 @@ |
| 1 |
+Tabular |
|
| 2 |
+============== |
|
| 3 |
+Sometimes, it's useful to line up text. Naturally, it's nicer to have the |
|
| 4 |
+computer do this for you, since aligning things by hand quickly becomes |
|
| 5 |
+unpleasant. While there are other plugins for aligning text, the ones I've |
|
| 6 |
+tried are either impossibly difficult to understand and use, or too simplistic |
|
| 7 |
+to handle complicated tasks. This plugin aims to make the easy things easy |
|
| 8 |
+and the hard things possible, without providing an unnecessarily obtuse |
|
| 9 |
+interface. It's still a work in progress, and criticisms are welcome. |
|
| 10 |
+ |
|
| 11 |
+See [Aligning Text with Tabular.vim](http://vimcasts.org/episodes/aligning-text-with-tabular-vim/) |
|
| 12 |
+for a screencast that shows how Tabular.vim works. |
|
| 13 |
+ |
|
| 14 |
+See [doc/Tabular.txt](http://raw.github.com/godlygeek/tabular/master/doc/Tabular.txt) |
|
| 15 |
+for detailed documentation. |
|
| 16 |
+ |
|
| 17 |
+Installation |
|
| 18 |
+============== |
|
| 19 |
+ |
|
| 20 |
+## Vim 8.1+ |
|
| 21 |
+ |
|
| 22 |
+No third-party package manager is required! Clone into: |
|
| 23 |
+ |
|
| 24 |
+`.vim/pack/plugins/start` |
|
| 25 |
+ |
|
| 26 |
+Make sure you include `packloadall` in your `.vimrc`. |
|
| 27 |
+ |
|
| 28 |
+## Pathogen |
|
| 29 |
+ |
|
| 30 |
+ mkdir -p ~/.vim/bundle |
|
| 31 |
+ cd ~/.vim/bundle |
|
| 32 |
+ git clone https://github.com/godlygeek/tabular.git |
|
| 33 |
+ |
|
| 34 |
+Once help tags have been generated (either using Pathogen's `:Helptags` |
|
| 35 |
+command, or by pointing vim's `:helptags` command at the directory where you |
|
| 36 |
+installed Tabular), you can view the manual with `:help tabular`. |
|
| 37 |
+ |
|
| 38 |
+See [pathogen.vim](https://github.com/tpope/vim-pathogen) for help or for package manager installation. |
| ... | ... |
@@ -0,0 +1,74 @@ |
| 1 |
+" Copyright (c) 2016, Matthew J. Wozniski |
|
| 2 |
+" All rights reserved. |
|
| 3 |
+" |
|
| 4 |
+" Redistribution and use in source and binary forms, with or without |
|
| 5 |
+" modification, are permitted provided that the following conditions are met: |
|
| 6 |
+" * Redistributions of source code must retain the above copyright notice, |
|
| 7 |
+" this list of conditions and the following disclaimer. |
|
| 8 |
+" * Redistributions in binary form must reproduce the above copyright |
|
| 9 |
+" notice, this list of conditions and the following disclaimer in the |
|
| 10 |
+" documentation and/or other materials provided with the distribution. |
|
| 11 |
+" * The names of the contributors may not be used to endorse or promote |
|
| 12 |
+" products derived from this software without specific prior written |
|
| 13 |
+" permission. |
|
| 14 |
+" |
|
| 15 |
+" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS |
|
| 16 |
+" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
| 17 |
+" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
|
| 18 |
+" NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
| 19 |
+" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 20 |
+" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
|
| 21 |
+" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
| 22 |
+" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
| 23 |
+" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
|
| 24 |
+" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 25 |
+ |
|
| 26 |
+if !exists(':Tabularize') || get(g:, 'no_default_tabular_maps', 0)
|
|
| 27 |
+ finish " Tabular.vim wasn't loaded or the default maps are unwanted |
|
| 28 |
+endif |
|
| 29 |
+ |
|
| 30 |
+let s:save_cpo = &cpo |
|
| 31 |
+set cpo&vim |
|
| 32 |
+ |
|
| 33 |
+AddTabularPattern! assignment /[|&+*/%<>=!~-]\@<!\([<>!=]=\|=\~\)\@![|&+*/%<>=!~-]*=/l1r1 |
|
| 34 |
+AddTabularPattern! two_spaces / /l0 |
|
| 35 |
+ |
|
| 36 |
+AddTabularPipeline! multiple_spaces / / map(a:lines, "substitute(v:val, ' *', ' ', 'g')") | tabular#TabularizeStrings(a:lines, ' ', 'l0') |
|
| 37 |
+AddTabularPipeline! spaces / / map(a:lines, "substitute(v:val, ' *' , ' ' , 'g')") | tabular#TabularizeStrings(a:lines, ' ' , 'l0') |
|
| 38 |
+ |
|
| 39 |
+AddTabularPipeline! argument_list /(.*)/ map(a:lines, 'substitute(v:val, ''\s*\([(,)]\)\s*'', ''\1'', ''g'')') |
|
| 40 |
+ \ | tabular#TabularizeStrings(a:lines, '[(,)]', 'l0') |
|
| 41 |
+ \ | map(a:lines, 'substitute(v:val, ''\(\s*\),'', '',\1 '', "g")') |
|
| 42 |
+ \ | map(a:lines, 'substitute(v:val, ''\s*)'', ")", "g")') |
|
| 43 |
+ |
|
| 44 |
+function! SplitCDeclarations(lines) |
|
| 45 |
+ let rv = [] |
|
| 46 |
+ for line in a:lines |
|
| 47 |
+ " split the line into declaractions |
|
| 48 |
+ let split = split(line, '\s*[,;]\s*') |
|
| 49 |
+ " separate the type from the first declaration |
|
| 50 |
+ let type = substitute(split[0], '\%(\%([&*]\s*\)*\)\=\k\+$', '', '') |
|
| 51 |
+ " add the ; back on every declaration |
|
| 52 |
+ call map(split, 'v:val . ";"') |
|
| 53 |
+ " add the first element to the return as-is, and remove it from the list |
|
| 54 |
+ let rv += [ remove(split, 0) ] |
|
| 55 |
+ " transform the other elements by adding the type on at the beginning |
|
| 56 |
+ call map(split, 'type . v:val') |
|
| 57 |
+ " and add them all to the return |
|
| 58 |
+ let rv += split |
|
| 59 |
+ endfor |
|
| 60 |
+ return rv |
|
| 61 |
+endfunction |
|
| 62 |
+ |
|
| 63 |
+AddTabularPipeline! split_declarations /,.*;/ SplitCDeclarations(a:lines) |
|
| 64 |
+ |
|
| 65 |
+AddTabularPattern! ternary_operator /^.\{-}\zs?\|:/l1
|
|
| 66 |
+ |
|
| 67 |
+AddTabularPattern! cpp_io /<<\|>>/l1 |
|
| 68 |
+ |
|
| 69 |
+AddTabularPattern! pascal_assign /:=/l1 |
|
| 70 |
+ |
|
| 71 |
+AddTabularPattern! trailing_c_comments /\/\*\|\*\/\|\/\//l1 |
|
| 72 |
+ |
|
| 73 |
+let &cpo = s:save_cpo |
|
| 74 |
+unlet s:save_cpo |
| ... | ... |
@@ -0,0 +1,437 @@ |
| 1 |
+" Tabular: Align columnar data using regex-designated column boundaries |
|
| 2 |
+" Maintainer: Matthew Wozniski (godlygeek@gmail.com) |
|
| 3 |
+" Date: Thu, 03 May 2012 20:49:32 -0400 |
|
| 4 |
+" Version: 1.0 |
|
| 5 |
+" |
|
| 6 |
+" Long Description: |
|
| 7 |
+" Sometimes, it's useful to line up text. Naturally, it's nicer to have the |
|
| 8 |
+" computer do this for you, since aligning things by hand quickly becomes |
|
| 9 |
+" unpleasant. While there are other plugins for aligning text, the ones I've |
|
| 10 |
+" tried are either impossibly difficult to understand and use, or too simplistic |
|
| 11 |
+" to handle complicated tasks. This plugin aims to make the easy things easy |
|
| 12 |
+" and the hard things possible, without providing an unnecessarily obtuse |
|
| 13 |
+" interface. It's still a work in progress, and criticisms are welcome. |
|
| 14 |
+" |
|
| 15 |
+" License: |
|
| 16 |
+" Copyright (c) 2012, Matthew J. Wozniski |
|
| 17 |
+" All rights reserved. |
|
| 18 |
+" |
|
| 19 |
+" Redistribution and use in source and binary forms, with or without |
|
| 20 |
+" modification, are permitted provided that the following conditions are met: |
|
| 21 |
+" * Redistributions of source code must retain the above copyright notice, |
|
| 22 |
+" this list of conditions and the following disclaimer. |
|
| 23 |
+" * Redistributions in binary form must reproduce the above copyright |
|
| 24 |
+" notice, this list of conditions and the following disclaimer in the |
|
| 25 |
+" documentation and/or other materials provided with the distribution. |
|
| 26 |
+" * The names of the contributors may not be used to endorse or promote |
|
| 27 |
+" products derived from this software without specific prior written |
|
| 28 |
+" permission. |
|
| 29 |
+" |
|
| 30 |
+" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS |
|
| 31 |
+" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
| 32 |
+" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
|
| 33 |
+" NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
| 34 |
+" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 35 |
+" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
|
| 36 |
+" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
| 37 |
+" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
| 38 |
+" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
|
| 39 |
+" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 40 |
+ |
|
| 41 |
+" Stupid vimscript crap {{{1
|
|
| 42 |
+let s:savecpo = &cpo |
|
| 43 |
+set cpo&vim |
|
| 44 |
+ |
|
| 45 |
+" Private Functions {{{1
|
|
| 46 |
+ |
|
| 47 |
+" Return the number of bytes in a string after expanding tabs to spaces. {{{2
|
|
| 48 |
+" This expansion is done based on the current value of 'tabstop' |
|
| 49 |
+if exists('*strdisplaywidth')
|
|
| 50 |
+ " Needs vim 7.3 |
|
| 51 |
+ let s:Strlen = function("strdisplaywidth")
|
|
| 52 |
+else |
|
| 53 |
+ function! s:Strlen(string) |
|
| 54 |
+ " Implement the tab handling part of strdisplaywidth for vim 7.2 and |
|
| 55 |
+ " earlier - not much that can be done about handling doublewidth |
|
| 56 |
+ " characters. |
|
| 57 |
+ let rv = 0 |
|
| 58 |
+ let i = 0 |
|
| 59 |
+ |
|
| 60 |
+ for char in split(a:string, '\zs') |
|
| 61 |
+ if char == "\t" |
|
| 62 |
+ let rv += &ts - i |
|
| 63 |
+ let i = 0 |
|
| 64 |
+ else |
|
| 65 |
+ let rv += 1 |
|
| 66 |
+ let i = (i + 1) % &ts |
|
| 67 |
+ endif |
|
| 68 |
+ endfor |
|
| 69 |
+ |
|
| 70 |
+ return rv |
|
| 71 |
+ endfunction |
|
| 72 |
+endif |
|
| 73 |
+ |
|
| 74 |
+" Align a string within a field {{{2
|
|
| 75 |
+" These functions do not trim leading and trailing spaces. |
|
| 76 |
+ |
|
| 77 |
+" Right align 'string' in a field of size 'fieldwidth' |
|
| 78 |
+function! s:Right(string, fieldwidth) |
|
| 79 |
+ let spaces = a:fieldwidth - s:Strlen(a:string) |
|
| 80 |
+ return matchstr(a:string, '^\s*') . repeat(" ", spaces) . substitute(a:string, '^\s*', '', '')
|
|
| 81 |
+endfunction |
|
| 82 |
+ |
|
| 83 |
+" Left align 'string' in a field of size 'fieldwidth' |
|
| 84 |
+function! s:Left(string, fieldwidth) |
|
| 85 |
+ let spaces = a:fieldwidth - s:Strlen(a:string) |
|
| 86 |
+ return a:string . repeat(" ", spaces)
|
|
| 87 |
+endfunction |
|
| 88 |
+ |
|
| 89 |
+" Center align 'string' in a field of size 'fieldwidth' |
|
| 90 |
+function! s:Center(string, fieldwidth) |
|
| 91 |
+ let spaces = a:fieldwidth - s:Strlen(a:string) |
|
| 92 |
+ let right = spaces / 2 |
|
| 93 |
+ let left = right + (right * 2 != spaces) |
|
| 94 |
+ return repeat(" ", left) . a:string . repeat(" ", right)
|
|
| 95 |
+endfunction |
|
| 96 |
+ |
|
| 97 |
+" Remove spaces around a string {{{2
|
|
| 98 |
+ |
|
| 99 |
+" Remove all trailing spaces from a string. |
|
| 100 |
+function! s:StripTrailingSpaces(string) |
|
| 101 |
+ return matchstr(a:string, '^.\{-}\ze\s*$')
|
|
| 102 |
+endfunction |
|
| 103 |
+ |
|
| 104 |
+" Remove all leading spaces from a string. |
|
| 105 |
+function! s:StripLeadingSpaces(string) |
|
| 106 |
+ return matchstr(a:string, '^\s*\zs.*$') |
|
| 107 |
+endfunction |
|
| 108 |
+ |
|
| 109 |
+" Find the longest common indent for a list of strings {{{2
|
|
| 110 |
+" If a string is shorter than the others but contains no non-whitespace |
|
| 111 |
+" characters, it does not end the match. This provides consistency with |
|
| 112 |
+" vim's behavior that blank lines don't have trailing spaces. |
|
| 113 |
+function! s:LongestCommonIndent(strings) |
|
| 114 |
+ if empty(a:strings) |
|
| 115 |
+ return '' |
|
| 116 |
+ endif |
|
| 117 |
+ |
|
| 118 |
+ let n = 0 |
|
| 119 |
+ while 1 |
|
| 120 |
+ let ns = join(map(copy(a:strings), 'v:val[n]'), '') |
|
| 121 |
+ if ns !~ '^ \+$\|^\t\+$' |
|
| 122 |
+ break |
|
| 123 |
+ endif |
|
| 124 |
+ let n += 1 |
|
| 125 |
+ endwhile |
|
| 126 |
+ |
|
| 127 |
+ return strpart(a:strings[0], 0, n) |
|
| 128 |
+endfunction |
|
| 129 |
+ |
|
| 130 |
+" Split a string into fields and delimiters {{{2
|
|
| 131 |
+" Like split(), but include the delimiters as elements |
|
| 132 |
+" All odd numbered elements are delimiters |
|
| 133 |
+" All even numbered elements are non-delimiters (including zero) |
|
| 134 |
+function! s:SplitDelim(string, delim) |
|
| 135 |
+ let rv = [] |
|
| 136 |
+ let beg = 0 |
|
| 137 |
+ |
|
| 138 |
+ let len = len(a:string) |
|
| 139 |
+ let searchoff = 0 |
|
| 140 |
+ |
|
| 141 |
+ while 1 |
|
| 142 |
+ let mid = match(a:string, a:delim, beg + searchoff, 1) |
|
| 143 |
+ if mid == -1 || mid == len |
|
| 144 |
+ break |
|
| 145 |
+ endif |
|
| 146 |
+ |
|
| 147 |
+ let matchstr = matchstr(a:string, a:delim, beg + searchoff, 1) |
|
| 148 |
+ let length = strlen(matchstr) |
|
| 149 |
+ |
|
| 150 |
+ if length == 0 && beg == mid |
|
| 151 |
+ " Zero-length match for a zero-length delimiter - advance past it |
|
| 152 |
+ let searchoff += 1 |
|
| 153 |
+ continue |
|
| 154 |
+ endif |
|
| 155 |
+ |
|
| 156 |
+ if beg == mid |
|
| 157 |
+ let rv += [ "" ] |
|
| 158 |
+ else |
|
| 159 |
+ let rv += [ a:string[beg : mid-1] ] |
|
| 160 |
+ endif |
|
| 161 |
+ |
|
| 162 |
+ let rv += [ matchstr ] |
|
| 163 |
+ |
|
| 164 |
+ let beg = mid + length |
|
| 165 |
+ let searchoff = 0 |
|
| 166 |
+ endwhile |
|
| 167 |
+ |
|
| 168 |
+ let rv += [ strpart(a:string, beg) ] |
|
| 169 |
+ |
|
| 170 |
+ return rv |
|
| 171 |
+endfunction |
|
| 172 |
+ |
|
| 173 |
+" Replace lines from `start' to `start + len - 1' with the given strings. {{{2
|
|
| 174 |
+" If more lines are needed to show all strings, they will be added. |
|
| 175 |
+" If there are too few strings to fill all lines, lines will be removed. |
|
| 176 |
+function! s:SetLines(start, len, strings) |
|
| 177 |
+ if a:start > line('$') + 1 || a:start < 1
|
|
| 178 |
+ throw "Invalid start line!" |
|
| 179 |
+ endif |
|
| 180 |
+ |
|
| 181 |
+ if len(a:strings) > a:len |
|
| 182 |
+ let fensave = &fen |
|
| 183 |
+ let view = winsaveview() |
|
| 184 |
+ call append(a:start + a:len - 1, repeat([''], len(a:strings) - a:len)) |
|
| 185 |
+ call winrestview(view) |
|
| 186 |
+ let &fen = fensave |
|
| 187 |
+ elseif len(a:strings) < a:len |
|
| 188 |
+ let fensave = &fen |
|
| 189 |
+ let view = winsaveview() |
|
| 190 |
+ sil exe (a:start + len(a:strings)) . ',' . (a:start + a:len - 1) . 'd_' |
|
| 191 |
+ call winrestview(view) |
|
| 192 |
+ let &fen = fensave |
|
| 193 |
+ endif |
|
| 194 |
+ |
|
| 195 |
+ call setline(a:start, a:strings) |
|
| 196 |
+endfunction |
|
| 197 |
+ |
|
| 198 |
+" Runs the given commandstring argument as an expression. {{{2
|
|
| 199 |
+" The commandstring expression is expected to reference the a:lines argument. |
|
| 200 |
+" If the commandstring expression returns a list the items of that list will |
|
| 201 |
+" replace the items in a:lines, otherwise the expression is assumed to have |
|
| 202 |
+" modified a:lines itself. |
|
| 203 |
+function! s:FilterString(lines, commandstring) |
|
| 204 |
+ exe 'let rv = ' . a:commandstring |
|
| 205 |
+ |
|
| 206 |
+ if type(rv) == type(a:lines) && rv isnot a:lines |
|
| 207 |
+ call filter(a:lines, 0) |
|
| 208 |
+ call extend(a:lines, rv) |
|
| 209 |
+ endif |
|
| 210 |
+endfunction |
|
| 211 |
+ |
|
| 212 |
+" Public API {{{1
|
|
| 213 |
+ |
|
| 214 |
+if !exists("g:tabular_default_format")
|
|
| 215 |
+ let g:tabular_default_format = "l1" |
|
| 216 |
+endif |
|
| 217 |
+ |
|
| 218 |
+let s:formatelempat = '\%([lrc]\d\+\)' |
|
| 219 |
+ |
|
| 220 |
+function! tabular#ElementFormatPattern() |
|
| 221 |
+ return s:formatelempat |
|
| 222 |
+endfunction |
|
| 223 |
+ |
|
| 224 |
+" Given a list of strings and a delimiter, split each string on every |
|
| 225 |
+" occurrence of the delimiter pattern, format each element according to either |
|
| 226 |
+" the provided format (optional) or the default format, and join them back |
|
| 227 |
+" together with enough space padding to guarantee that the nth delimiter of |
|
| 228 |
+" each string is aligned. |
|
| 229 |
+function! tabular#TabularizeStrings(strings, delim, ...) |
|
| 230 |
+ if a:0 > 1 |
|
| 231 |
+ echoerr "TabularizeStrings accepts only 2 or 3 arguments (got ".(a:0+2).")" |
|
| 232 |
+ return 1 |
|
| 233 |
+ endif |
|
| 234 |
+ |
|
| 235 |
+ let formatstr = (a:0 ? a:1 : g:tabular_default_format) |
|
| 236 |
+ |
|
| 237 |
+ if formatstr !~? s:formatelempat . '\+' |
|
| 238 |
+ echoerr "Tabular: Invalid format \"" . formatstr . "\" specified!" |
|
| 239 |
+ return 1 |
|
| 240 |
+ endif |
|
| 241 |
+ |
|
| 242 |
+ let format = split(formatstr, s:formatelempat . '\zs') |
|
| 243 |
+ |
|
| 244 |
+ let lines = a:strings |
|
| 245 |
+ |
|
| 246 |
+ call map(lines, 's:SplitDelim(v:val, a:delim)') |
|
| 247 |
+ |
|
| 248 |
+ let first_fields = [] |
|
| 249 |
+ |
|
| 250 |
+ " Strip spaces from non-delimiters; spaces in delimiters must have been |
|
| 251 |
+ " matched intentionally |
|
| 252 |
+ for line in lines |
|
| 253 |
+ if len(line) == 1 && s:do_gtabularize |
|
| 254 |
+ continue " Leave non-matching lines unchanged for GTabularize |
|
| 255 |
+ endif |
|
| 256 |
+ |
|
| 257 |
+ call add(first_fields, line[0]) |
|
| 258 |
+ |
|
| 259 |
+ if len(line) >= 1 |
|
| 260 |
+ for i in range(0, len(line)-1, 2) |
|
| 261 |
+ let line[i] = s:StripLeadingSpaces(s:StripTrailingSpaces(line[i])) |
|
| 262 |
+ endfor |
|
| 263 |
+ endif |
|
| 264 |
+ endfor |
|
| 265 |
+ |
|
| 266 |
+ let common_indent = s:LongestCommonIndent(first_fields) |
|
| 267 |
+ |
|
| 268 |
+ " Find the max length of each field |
|
| 269 |
+ let maxes = [] |
|
| 270 |
+ for line in lines |
|
| 271 |
+ if len(line) == 1 && s:do_gtabularize |
|
| 272 |
+ continue " non-matching lines don't affect field widths for GTabularize |
|
| 273 |
+ endif |
|
| 274 |
+ |
|
| 275 |
+ for i in range(len(line)) |
|
| 276 |
+ if i == len(maxes) |
|
| 277 |
+ let maxes += [ s:Strlen(line[i]) ] |
|
| 278 |
+ else |
|
| 279 |
+ let maxes[i] = max( [ maxes[i], s:Strlen(line[i]) ] ) |
|
| 280 |
+ endif |
|
| 281 |
+ endfor |
|
| 282 |
+ endfor |
|
| 283 |
+ |
|
| 284 |
+ " Concatenate the fields, according to the format pattern. |
|
| 285 |
+ for idx in range(len(lines)) |
|
| 286 |
+ let line = lines[idx] |
|
| 287 |
+ |
|
| 288 |
+ if len(line) == 1 && s:do_gtabularize |
|
| 289 |
+ let lines[idx] = line[0] " GTabularize doesn't change non-matching lines |
|
| 290 |
+ continue |
|
| 291 |
+ endif |
|
| 292 |
+ |
|
| 293 |
+ for i in range(len(line)) |
|
| 294 |
+ let how = format[i % len(format)][0] |
|
| 295 |
+ let pad = format[i % len(format)][1:-1] |
|
| 296 |
+ |
|
| 297 |
+ if how =~? 'l' |
|
| 298 |
+ let field = s:Left(line[i], maxes[i]) |
|
| 299 |
+ elseif how =~? 'r' |
|
| 300 |
+ let field = s:Right(line[i], maxes[i]) |
|
| 301 |
+ elseif how =~? 'c' |
|
| 302 |
+ let field = s:Center(line[i], maxes[i]) |
|
| 303 |
+ endif |
|
| 304 |
+ |
|
| 305 |
+ let line[i] = field . repeat(" ", pad)
|
|
| 306 |
+ endfor |
|
| 307 |
+ |
|
| 308 |
+ let prefix = common_indent |
|
| 309 |
+ if len(line) == 1 && s:do_gtabularize |
|
| 310 |
+ " We didn't strip the indent in this case; nothing to put back. |
|
| 311 |
+ let prefix = '' |
|
| 312 |
+ endif |
|
| 313 |
+ |
|
| 314 |
+ let lines[idx] = s:StripTrailingSpaces(prefix . join(line, '')) |
|
| 315 |
+ endfor |
|
| 316 |
+endfunction |
|
| 317 |
+ |
|
| 318 |
+" Apply 0 or more filters, in sequence, to selected text in the buffer {{{2
|
|
| 319 |
+" The lines to be filtered are determined as follows: |
|
| 320 |
+" If the function is called with a range containing multiple lines, then |
|
| 321 |
+" those lines will be used as the range. |
|
| 322 |
+" If the function is called with no range or with a range of 1 line, then |
|
| 323 |
+" if GTabularize mode is being used, |
|
| 324 |
+" the range will not be adjusted |
|
| 325 |
+" if "includepat" is not specified, |
|
| 326 |
+" that 1 line will be filtered, |
|
| 327 |
+" if "includepat" is specified and that line does not match it, |
|
| 328 |
+" no lines will be filtered |
|
| 329 |
+" if "includepat" is specified and that line does match it, |
|
| 330 |
+" all contiguous lines above and below the specified line matching the |
|
| 331 |
+" pattern will be filtered. |
|
| 332 |
+" |
|
| 333 |
+" The remaining arguments must each be a filter to apply to the text. |
|
| 334 |
+" Each filter must either be a String evaluating to a function to be called. |
|
| 335 |
+function! tabular#PipeRange(includepat, ...) range |
|
| 336 |
+ exe a:firstline . ',' . a:lastline |
|
| 337 |
+ \ . 'call tabular#PipeRangeWithOptions(a:includepat, a:000, {})'
|
|
| 338 |
+endfunction |
|
| 339 |
+ |
|
| 340 |
+" Extended version of tabular#PipeRange, which |
|
| 341 |
+" 1) Takes the list of filters as an explicit list rather than as varargs |
|
| 342 |
+" 2) Supports passing a dictionary of options to control the routine. |
|
| 343 |
+" Currently, the only supported option is 'mode', which determines whether |
|
| 344 |
+" to behave as :Tabularize or as :GTabularize |
|
| 345 |
+" This allows me to add new features here without breaking API compatibility |
|
| 346 |
+" in the future. |
|
| 347 |
+function! tabular#PipeRangeWithOptions(includepat, filterlist, options) range |
|
| 348 |
+ let top = a:firstline |
|
| 349 |
+ let bot = a:lastline |
|
| 350 |
+ |
|
| 351 |
+ let s:do_gtabularize = (get(a:options, 'mode', '') ==# 'GTabularize') |
|
| 352 |
+ |
|
| 353 |
+ if !s:do_gtabularize |
|
| 354 |
+ " In the default mode, apply range extension logic |
|
| 355 |
+ if a:includepat != '' && top == bot |
|
| 356 |
+ if top < 0 || top > line('$') || getline(top) !~ a:includepat
|
|
| 357 |
+ return |
|
| 358 |
+ endif |
|
| 359 |
+ while top > 1 && getline(top-1) =~ a:includepat |
|
| 360 |
+ let top -= 1 |
|
| 361 |
+ endwhile |
|
| 362 |
+ while bot < line('$') && getline(bot+1) =~ a:includepat
|
|
| 363 |
+ let bot += 1 |
|
| 364 |
+ endwhile |
|
| 365 |
+ endif |
|
| 366 |
+ endif |
|
| 367 |
+ |
|
| 368 |
+ let lines = map(range(top, bot), 'getline(v:val)') |
|
| 369 |
+ |
|
| 370 |
+ for filter in a:filterlist |
|
| 371 |
+ if type(filter) != type("")
|
|
| 372 |
+ echoerr "PipeRange: Bad filter: " . string(filter) |
|
| 373 |
+ endif |
|
| 374 |
+ |
|
| 375 |
+ call s:FilterString(lines, filter) |
|
| 376 |
+ |
|
| 377 |
+ unlet filter |
|
| 378 |
+ endfor |
|
| 379 |
+ |
|
| 380 |
+ call s:SetLines(top, bot - top + 1, lines) |
|
| 381 |
+endfunction |
|
| 382 |
+ |
|
| 383 |
+" Part of the public interface so interested pipelines can query this and |
|
| 384 |
+" adjust their behavior appropriately. |
|
| 385 |
+function! tabular#DoGTabularize() |
|
| 386 |
+ return s:do_gtabularize |
|
| 387 |
+endfunction |
|
| 388 |
+ |
|
| 389 |
+function! s:SplitDelimTest(string, delim, expected) |
|
| 390 |
+ let result = s:SplitDelim(a:string, a:delim) |
|
| 391 |
+ |
|
| 392 |
+ if result !=# a:expected |
|
| 393 |
+ echomsg 'Test failed!' |
|
| 394 |
+ echomsg ' string=' . string(a:string) . ' delim=' . string(a:delim) |
|
| 395 |
+ echomsg ' Returned=' . string(result) |
|
| 396 |
+ echomsg ' Expected=' . string(a:expected) |
|
| 397 |
+ endif |
|
| 398 |
+endfunction |
|
| 399 |
+ |
|
| 400 |
+function! tabular#SplitDelimUnitTest() |
|
| 401 |
+ let assignment = '[|&+*/%<>=!~-]\@<!\([<>!=]=\|=\~\)\@![|&+*/%<>=!~-]*=' |
|
| 402 |
+ let two_spaces = ' ' |
|
| 403 |
+ let ternary_operator = '^.\{-}\zs?\|:'
|
|
| 404 |
+ let cpp_io = '<<\|>>' |
|
| 405 |
+ let pascal_assign = ':=' |
|
| 406 |
+ let trailing_c_comments = '\/\*\|\*\/\|\/\/' |
|
| 407 |
+ |
|
| 408 |
+ call s:SplitDelimTest('a+=b', assignment, ['a', '+=', 'b'])
|
|
| 409 |
+ call s:SplitDelimTest('a-=b', assignment, ['a', '-=', 'b'])
|
|
| 410 |
+ call s:SplitDelimTest('a!=b', assignment, ['a!=b'])
|
|
| 411 |
+ call s:SplitDelimTest('a==b', assignment, ['a==b'])
|
|
| 412 |
+ call s:SplitDelimTest('a&=b', assignment, ['a', '&=', 'b'])
|
|
| 413 |
+ call s:SplitDelimTest('a|=b', assignment, ['a', '|=', 'b'])
|
|
| 414 |
+ call s:SplitDelimTest('a=b=c', assignment, ['a', '=', 'b', '=', 'c'])
|
|
| 415 |
+ |
|
| 416 |
+ call s:SplitDelimTest('a b c', two_spaces, ['a', ' ', 'b', ' ', 'c'])
|
|
| 417 |
+ call s:SplitDelimTest('a b c', two_spaces, ['a b', ' ', ' c'])
|
|
| 418 |
+ call s:SplitDelimTest('ab c', two_spaces, ['ab', ' ', '', ' ', 'c'])
|
|
| 419 |
+ |
|
| 420 |
+ call s:SplitDelimTest('a?b:c', ternary_operator, ['a', '?', 'b', ':', 'c'])
|
|
| 421 |
+ |
|
| 422 |
+ call s:SplitDelimTest('a<<b<<c', cpp_io, ['a', '<<', 'b', '<<', 'c'])
|
|
| 423 |
+ |
|
| 424 |
+ call s:SplitDelimTest('a:=b=c', pascal_assign, ['a', ':=', 'b=c'])
|
|
| 425 |
+ |
|
| 426 |
+ call s:SplitDelimTest('x//foo', trailing_c_comments, ['x', '//', 'foo'])
|
|
| 427 |
+ call s:SplitDelimTest('x/*foo*/',trailing_c_comments, ['x', '/*', 'foo', '*/', ''])
|
|
| 428 |
+ |
|
| 429 |
+ call s:SplitDelimTest('#ab#cd#ef', '[^#]*', ['#', 'ab', '#', 'cd', '#', 'ef', ''])
|
|
| 430 |
+ call s:SplitDelimTest('#ab#cd#ef', '#\zs', ['#', '', 'ab#', '', 'cd#', '', 'ef'])
|
|
| 431 |
+endfunction |
|
| 432 |
+ |
|
| 433 |
+" Stupid vimscript crap, part 2 {{{1
|
|
| 434 |
+let &cpo = s:savecpo |
|
| 435 |
+unlet s:savecpo |
|
| 436 |
+ |
|
| 437 |
+" vim:set sw=2 sts=2 fdm=marker: |
| ... | ... |
@@ -0,0 +1,260 @@ |
| 1 |
+*Tabular.txt* Configurable, flexible, intuitive text aligning |
|
| 2 |
+ |
|
| 3 |
+ *tabular* *tabular.vim* |
|
| 4 |
+ |
|
| 5 |
+ #|#|#|#|#| #| #| ~ |
|
| 6 |
+ #| #|#|#| #|#|#| #| #| #| #|#|#| #| #|#| ~ |
|
| 7 |
+ #| #| #| #| #| #| #| #| #| #| #|#| ~ |
|
| 8 |
+ #| #| #| #| #| #| #| #| #| #| #| ~ |
|
| 9 |
+ #| #|#|#| #|#|#| #|#|#| #| #|#|#| #| ~ |
|
| 10 |
+ |
|
| 11 |
+ For Vim version 7.0 or newer |
|
| 12 |
+ |
|
| 13 |
+ By Matt Wozniski |
|
| 14 |
+ mjw@drexel.edu |
|
| 15 |
+ |
|
| 16 |
+ Reference Manual ~ |
|
| 17 |
+ |
|
| 18 |
+ *tabular-toc* |
|
| 19 |
+ |
|
| 20 |
+1. Description |tabular-intro| |
|
| 21 |
+2. Walkthrough |tabular-walkthrough| |
|
| 22 |
+3. Scripting |tabular-scripting| |
|
| 23 |
+ |
|
| 24 |
+The functionality mentioned here is a plugin, see |add-plugin|. |
|
| 25 |
+You can avoid loading this plugin by setting the "Tabular_loaded" global |
|
| 26 |
+variable in your |vimrc| file: > |
|
| 27 |
+ :let g:tabular_loaded = 1 |
|
| 28 |
+ |
|
| 29 |
+============================================================================== |
|
| 30 |
+1. Description *tabular-intro* |
|
| 31 |
+ |
|
| 32 |
+Sometimes, it's useful to line up text. Naturally, it's nicer to have the |
|
| 33 |
+computer do this for you, since aligning things by hand quickly becomes |
|
| 34 |
+unpleasant. While there are other plugins for aligning text, the ones I've |
|
| 35 |
+tried are either impossibly difficult to understand and use, or too simplistic |
|
| 36 |
+to handle complicated tasks. This plugin aims to make the easy things easy |
|
| 37 |
+and the hard things possible, without providing an unnecessarily obtuse |
|
| 38 |
+interface. It's still a work in progress, and criticisms are welcome. |
|
| 39 |
+ |
|
| 40 |
+============================================================================== |
|
| 41 |
+2. Walkthrough *tabular-walkthrough* *:Tabularize* |
|
| 42 |
+ |
|
| 43 |
+Tabular's commands are based largely on regular expressions. The basic |
|
| 44 |
+technique used by Tabular is taking some regex to match field delimiters, |
|
| 45 |
+splitting the input lines at those delimiters, trimming unnecessary spaces |
|
| 46 |
+from the non-delimiter parts, padding the non-delimiter parts of the lines |
|
| 47 |
+with spaces to make them the same length, and joining things back together |
|
| 48 |
+again. |
|
| 49 |
+ |
|
| 50 |
+For instance, consider starting with the following lines: |
|
| 51 |
+> |
|
| 52 |
+ Some short phrase,some other phrase |
|
| 53 |
+ A much longer phrase here,and another long phrase |
|
| 54 |
+< |
|
| 55 |
+Let's say we want to line these lines up at the commas. We can tell |
|
| 56 |
+Tabularize to do this by passing a pattern matching , to the Tabularize |
|
| 57 |
+command: |
|
| 58 |
+> |
|
| 59 |
+ :Tabularize /, |
|
| 60 |
+ |
|
| 61 |
+ Some short phrase , some other phrase |
|
| 62 |
+ A much longer phrase here , and another long phrase |
|
| 63 |
+< |
|
| 64 |
+I encourage you to try copying those lines to another buffer and trying to |
|
| 65 |
+call :Tabularize. You'll want to take notice of two things quickly: First, |
|
| 66 |
+instead of requiring a range, Tabularize tries to figure out what you want to |
|
| 67 |
+happen. Since it knows that you want to act on lines matching a comma, it |
|
| 68 |
+will look upwards and downwards for lines around the current line that match a |
|
| 69 |
+comma, and consider all contiguous lines matching the pattern to be the range |
|
| 70 |
+to be acted upon. You can always override this by specifying a range, though. |
|
| 71 |
+ |
|
| 72 |
+The second thing you should notice is that you'll almost certainly be able to |
|
| 73 |
+abbreviate :Tabularize to :Tab - using this form in mappings and scripts is |
|
| 74 |
+discouraged as it will make conflicts with other scripts more likely, but for |
|
| 75 |
+interactive use it's a nice timesaver. Another convenience feature is that |
|
| 76 |
+running :Tabularize without providing a new pattern will cause it to reuse the |
|
| 77 |
+last pattern it was called with. |
|
| 78 |
+ |
|
| 79 |
+So, anyway, now the commas line up. Splitting the lines on commas, Tabular |
|
| 80 |
+realized that 'Some short phrase' would need to be padded with spaces to match |
|
| 81 |
+the length of 'A much longer phrase here', and it did that before joining the |
|
| 82 |
+lines back together. You'll also notice that, in addition to the spaces |
|
| 83 |
+inserting for padding, extra spaces were inserted between fields. That's |
|
| 84 |
+because by default, Tabular prints things left-aligned with one space between |
|
| 85 |
+fields. If you wanted to print things right-aligned with no spaces between |
|
| 86 |
+fields, you would provide a different format to the Tabularize command: |
|
| 87 |
+> |
|
| 88 |
+ :Tabularize /,/r0 |
|
| 89 |
+ |
|
| 90 |
+ Some short phrase, some other phrase |
|
| 91 |
+ A much longer phrase here,and another long phrase |
|
| 92 |
+< |
|
| 93 |
+A format specifier is either l, r, or c, followed by one or more digits. If |
|
| 94 |
+the letter is l, the field will be left aligned, similarly for r and right |
|
| 95 |
+aligning and c and center aligning. The number following the letter is the |
|
| 96 |
+number of spaces padding to insert before the start of the next field. |
|
| 97 |
+Multiple format specifiers can be added to the same command - each field will |
|
| 98 |
+be printed with the next format specifier in the list; when they all have been |
|
| 99 |
+used the first will be used again, and so on. So, the last command right |
|
| 100 |
+aligned every field, then inserted 0 spaces of padding before the next field. |
|
| 101 |
+What if we wanted to right align the text before the comma, and left align the |
|
| 102 |
+text after the comma? The command would look like this: |
|
| 103 |
+> |
|
| 104 |
+ :Tabularize /,/r1c1l0 |
|
| 105 |
+ |
|
| 106 |
+ Some short phrase , some other phrase |
|
| 107 |
+ A much longer phrase here , and another long phrase |
|
| 108 |
+< |
|
| 109 |
+That command would be read as "Align the matching text, splitting fields on |
|
| 110 |
+commas. Print everything before the first comma right aligned, then 1 space, |
|
| 111 |
+then the comma center aligned, then 1 space, then everything after the comma |
|
| 112 |
+left aligned." Notice that the alignment of the field the comma is in is |
|
| 113 |
+irrelevant - since it's only 1 cell wide, it looks the same whether it's right, |
|
| 114 |
+left, or center aligned. Also notice that the 0 padding spaces specified for |
|
| 115 |
+the 3rd field are unused - but they would be used if there were enough fields |
|
| 116 |
+to require looping through the fields again. For instance: |
|
| 117 |
+> |
|
| 118 |
+ abc,def,ghi |
|
| 119 |
+ a,b |
|
| 120 |
+ a,b,c |
|
| 121 |
+ |
|
| 122 |
+ :Tabularize /,/r1c1l0 |
|
| 123 |
+ |
|
| 124 |
+ abc , def, ghi |
|
| 125 |
+ a , b |
|
| 126 |
+ a , b , c |
|
| 127 |
+< |
|
| 128 |
+Notice that now, the format pattern has been reused; field 4 (the second comma) |
|
| 129 |
+is right aligned, field 5 is center aligned. No spaces were inserted between |
|
| 130 |
+the 3rd field (containing "def") and the 4th field (the second comma) because |
|
| 131 |
+the format specified 'l0'. |
|
| 132 |
+ |
|
| 133 |
+But, what if you only wanted to act on the first comma on the line, rather than |
|
| 134 |
+all of the commas on the line? Let's say we want everything before the first |
|
| 135 |
+comma right aligned, then the comma, then everything after the comma left |
|
| 136 |
+aligned: |
|
| 137 |
+> |
|
| 138 |
+ abc,def,ghi |
|
| 139 |
+ a,b |
|
| 140 |
+ a,b,c |
|
| 141 |
+ |
|
| 142 |
+ :Tabularize /^[^,]*\zs,/r0c0l0 |
|
| 143 |
+ |
|
| 144 |
+ abc,def,ghi |
|
| 145 |
+ a,b |
|
| 146 |
+ a,b,c |
|
| 147 |
+< |
|
| 148 |
+Here, we used a Vim regex that would only match the first comma on the line. |
|
| 149 |
+It matches the beginning of the line, followed by all the non-comma characters |
|
| 150 |
+up to the first comma, and then forgets about what it matched so far and |
|
| 151 |
+pretends that the match starts exactly at the comma. |
|
| 152 |
+ |
|
| 153 |
+But, now that this command does exactly what we want it to, it's become pretty |
|
| 154 |
+unwieldy. It would be unpleasant to need to type that more than once or |
|
| 155 |
+twice. The solution is to assign a name to it. |
|
| 156 |
+> |
|
| 157 |
+ :AddTabularPattern first_comma /^[^,]*\zs,/r0c0l0 |
|
| 158 |
+< |
|
| 159 |
+Now, typing ":Tabularize first_comma" will do the same thing as typing the |
|
| 160 |
+whole pattern out each time. Of course this is more useful if you store the |
|
| 161 |
+name in a file to be used later. |
|
| 162 |
+ |
|
| 163 |
+NOTE: In order to make these new commands available every time vim starts, |
|
| 164 |
+you'll need to put those new commands into a .vim file in a plugin directory |
|
| 165 |
+somewhere in your 'runtimepath'. In order to make sure that Tabular.vim has |
|
| 166 |
+already been loaded before your file tries to use :AddTabularPattern or |
|
| 167 |
+:AddTabularPipeline, the new file should be installed in an after/plugin |
|
| 168 |
+directory in 'runtimepath'. In general, it will be safe to find out where the |
|
| 169 |
+TabularMaps.vim plugin was installed, and place other files extending |
|
| 170 |
+Tabular.vim in the same directory as TabularMaps.vim. For more information, |
|
| 171 |
+and some suggested best practices, check out the |tabular-scripting| section. |
|
| 172 |
+ |
|
| 173 |
+Lastly, we'll approach the case where tabular cannot achieve your desired goal |
|
| 174 |
+just by splitting lines apart, trimming whitespace, padding with whitespace, |
|
| 175 |
+and rejoining the lines. As an example, consider the multiple_spaces command |
|
| 176 |
+from TabularMaps.vim. The goal is to split using two or more spaces as a |
|
| 177 |
+field delimiter, and join fields back together, properly lined up, with only |
|
| 178 |
+two spaces between the end of each field and the beginning of the next. |
|
| 179 |
+Unfortunately, Tabular can't do this with only the commands we know so far: |
|
| 180 |
+> |
|
| 181 |
+ :Tabularize / / |
|
| 182 |
+< |
|
| 183 |
+The above function won't work, because it will consider "a b" as 5 fields |
|
| 184 |
+delimited by two pairs of 2 spaces ( 'a', ' ', '', ' ', 'b' ) instead of as |
|
| 185 |
+3 fields delimited by one set of 2 or more spaces ( 'a', ' ', 'b' ). |
|
| 186 |
+> |
|
| 187 |
+ :Tabularize / \+/ |
|
| 188 |
+< |
|
| 189 |
+The above function won't work either, because it will leave the delimiter as 4 |
|
| 190 |
+spaces when used against "a b", meaning that we would fail at our goal of |
|
| 191 |
+collapsing everything down to two spaces between fields. So, we need a new |
|
| 192 |
+command to get around this: |
|
| 193 |
+> |
|
| 194 |
+ :AddTabularPipeline multiple_spaces / \{2,}/
|
|
| 195 |
+ \ map(a:lines, "substitute(v:val, ' \{2,}', ' ', 'g')")
|
|
| 196 |
+ \ | tabular#TabularizeStrings(a:lines, ' ', 'l0') |
|
| 197 |
+< |
|
| 198 |
+Yeah. I know it looks complicated. Bear with me. I probably will try to add |
|
| 199 |
+in some shortcuts for this syntax, but this verbose will be guaranteed to |
|
| 200 |
+always work. |
|
| 201 |
+ |
|
| 202 |
+You should already recognize the name being assigned. The next thing to |
|
| 203 |
+happen is / \{2,}/ which is a pattern specifying which lines should
|
|
| 204 |
+automatically be included in the range when no range is given. Without this, |
|
| 205 |
+there would be no pattern to use for extending the range. Everything after |
|
| 206 |
+that is a | separated list of expressions to be evaluated. In the context in |
|
| 207 |
+which they will be evaluated, a:lines will be set to a List of Strings |
|
| 208 |
+containing the text of the lines being filtered as they procede through the |
|
| 209 |
+pipeline you've set up. The \ at the start of the lines are just vim's line |
|
| 210 |
+continuation marker; you needn't worry much about them. So, the first |
|
| 211 |
+expression in the pipeline transforms each line by replacing every instance of |
|
| 212 |
+2 or more spaces with exactly two spaces. The second command in the pipeline |
|
| 213 |
+performs the equivalent of ":Tabularize / /l0"; the only difference is that |
|
| 214 |
+it is operating on a List of Strings rather than text in the buffer. At the |
|
| 215 |
+end of the pipeline, the Strings in the modified a:lines (or the return value |
|
| 216 |
+of the last expression in the pipeline, if it returns a List) will replace the |
|
| 217 |
+chosen range. |
|
| 218 |
+ |
|
| 219 |
+============================================================================== |
|
| 220 |
+3. Extending *tabular-scripting* |
|
| 221 |
+ |
|
| 222 |
+As mentioned above, the most important consideration when extending Tabular |
|
| 223 |
+with new maps or commands is that your plugin must be loaded after Tabular.vim |
|
| 224 |
+has finished loading, and only if Tabular.vim has loaded successfully. The |
|
| 225 |
+easiest approach to making sure it loads after Tabular.vim is simply putting |
|
| 226 |
+the new file (we'll call it "tabular_extra.vim" as an example) into an |
|
| 227 |
+"after/plugin/" directory in 'runtimepath', for instance: |
|
| 228 |
+> |
|
| 229 |
+ ~/.vim/after/plugin/tabular_extra.vim |
|
| 230 |
+< |
|
| 231 |
+The default set of mappings, found in "TabularMaps.vim", is installed in |
|
| 232 |
+the after/plugin/ subdirectory of whatever directory Tabular was installed to. |
|
| 233 |
+ |
|
| 234 |
+The other important consideration is making sure that your commands are only |
|
| 235 |
+called if Tabular.vim was actually loaded. The easiest way to do this is by |
|
| 236 |
+checking for the existence of the :Tabularize command at the start of your |
|
| 237 |
+plugin. A short example plugin would look like this: |
|
| 238 |
+> |
|
| 239 |
+ " after/plugin/my_tabular_commands.vim |
|
| 240 |
+ " Provides extra :Tabularize commands |
|
| 241 |
+ |
|
| 242 |
+ if !exists(':Tabularize')
|
|
| 243 |
+ finish " Give up here; the Tabular plugin musn't have been loaded |
|
| 244 |
+ endif |
|
| 245 |
+ |
|
| 246 |
+ " Make line wrapping possible by resetting the 'cpo' option, first saving it |
|
| 247 |
+ let s:save_cpo = &cpo |
|
| 248 |
+ set cpo&vim |
|
| 249 |
+ |
|
| 250 |
+ AddTabularPattern! asterisk /*/l1 |
|
| 251 |
+ |
|
| 252 |
+ AddTabularPipeline! remove_leading_spaces /^ / |
|
| 253 |
+ \ map(a:lines, "substitute(v:val, '^ *', '', '')") |
|
| 254 |
+ |
|
| 255 |
+ " Restore the saved value of 'cpo' |
|
| 256 |
+ let &cpo = s:save_cpo |
|
| 257 |
+ unlet s:save_cpo |
|
| 258 |
+< |
|
| 259 |
+============================================================================== |
|
| 260 |
+vim:tw=78:fo=tcq2:isk=!-~,^*,^\|,^\":ts=8:ft=help:norl: |
| ... | ... |
@@ -0,0 +1,350 @@ |
| 1 |
+" Tabular: Align columnar data using regex-designated column boundaries |
|
| 2 |
+" Maintainer: Matthew Wozniski (godlygeek@gmail.com) |
|
| 3 |
+" Date: Thu, 03 May 2012 20:49:32 -0400 |
|
| 4 |
+" Version: 1.0 |
|
| 5 |
+" |
|
| 6 |
+" Long Description: |
|
| 7 |
+" Sometimes, it's useful to line up text. Naturally, it's nicer to have the |
|
| 8 |
+" computer do this for you, since aligning things by hand quickly becomes |
|
| 9 |
+" unpleasant. While there are other plugins for aligning text, the ones I've |
|
| 10 |
+" tried are either impossibly difficult to understand and use, or too simplistic |
|
| 11 |
+" to handle complicated tasks. This plugin aims to make the easy things easy |
|
| 12 |
+" and the hard things possible, without providing an unnecessarily obtuse |
|
| 13 |
+" interface. It's still a work in progress, and criticisms are welcome. |
|
| 14 |
+" |
|
| 15 |
+" License: |
|
| 16 |
+" Copyright (c) 2012, Matthew J. Wozniski |
|
| 17 |
+" All rights reserved. |
|
| 18 |
+" |
|
| 19 |
+" Redistribution and use in source and binary forms, with or without |
|
| 20 |
+" modification, are permitted provided that the following conditions are met: |
|
| 21 |
+" * Redistributions of source code must retain the above copyright notice, |
|
| 22 |
+" this list of conditions and the following disclaimer. |
|
| 23 |
+" * Redistributions in binary form must reproduce the above copyright |
|
| 24 |
+" notice, this list of conditions and the following disclaimer in the |
|
| 25 |
+" documentation and/or other materials provided with the distribution. |
|
| 26 |
+" * The names of the contributors may not be used to endorse or promote |
|
| 27 |
+" products derived from this software without specific prior written |
|
| 28 |
+" permission. |
|
| 29 |
+" |
|
| 30 |
+" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS |
|
| 31 |
+" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
| 32 |
+" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
|
| 33 |
+" NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
| 34 |
+" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 35 |
+" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
|
| 36 |
+" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
| 37 |
+" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
| 38 |
+" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
|
| 39 |
+" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 40 |
+ |
|
| 41 |
+" Abort if running in vi-compatible mode or the user doesn't want us. |
|
| 42 |
+if &cp || exists('g:tabular_loaded')
|
|
| 43 |
+ if &cp && &verbose |
|
| 44 |
+ echo "Not loading Tabular in compatible mode." |
|
| 45 |
+ endif |
|
| 46 |
+ finish |
|
| 47 |
+endif |
|
| 48 |
+ |
|
| 49 |
+let g:tabular_loaded = 1 |
|
| 50 |
+ |
|
| 51 |
+" Stupid vimscript crap {{{1
|
|
| 52 |
+let s:savecpo = &cpo |
|
| 53 |
+set cpo&vim |
|
| 54 |
+ |
|
| 55 |
+" Private Things {{{1
|
|
| 56 |
+ |
|
| 57 |
+" Dictionary of command name to command |
|
| 58 |
+let s:TabularCommands = {}
|
|
| 59 |
+ |
|
| 60 |
+" Generate tab completion list for :Tabularize {{{2
|
|
| 61 |
+" Return a list of commands that match the command line typed so far. |
|
| 62 |
+" NOTE: Tries to handle commands with spaces in the name, but Vim doesn't seem |
|
| 63 |
+" to handle that terribly well... maybe I should give up on that. |
|
| 64 |
+function! s:CompleteTabularizeCommand(argstart, cmdline, cursorpos) |
|
| 65 |
+ let names = keys(s:TabularCommands) |
|
| 66 |
+ if exists("b:TabularCommands")
|
|
| 67 |
+ let names += keys(b:TabularCommands) |
|
| 68 |
+ endif |
|
| 69 |
+ |
|
| 70 |
+ let cmdstart = substitute(a:cmdline, '^\s*\S\+\s*', '', '') |
|
| 71 |
+ |
|
| 72 |
+ return filter(names, 'v:val =~# ''^\V'' . escape(cmdstart, ''\'')') |
|
| 73 |
+endfunction |
|
| 74 |
+ |
|
| 75 |
+" Choose the proper command map from the given command line {{{2
|
|
| 76 |
+" Returns [ command map, command line with leading <buffer> removed ] |
|
| 77 |
+function! s:ChooseCommandMap(commandline) |
|
| 78 |
+ let map = s:TabularCommands |
|
| 79 |
+ let cmd = a:commandline |
|
| 80 |
+ |
|
| 81 |
+ if cmd =~# '^<buffer>\s\+' |
|
| 82 |
+ if !exists('b:TabularCommands')
|
|
| 83 |
+ let b:TabularCommands = {}
|
|
| 84 |
+ endif |
|
| 85 |
+ let map = b:TabularCommands |
|
| 86 |
+ let cmd = substitute(cmd, '^<buffer>\s\+', '', '') |
|
| 87 |
+ endif |
|
| 88 |
+ |
|
| 89 |
+ return [ map, cmd ] |
|
| 90 |
+endfunction |
|
| 91 |
+ |
|
| 92 |
+" Parse '/pattern/format' into separate pattern and format parts. {{{2
|
|
| 93 |
+" If parsing fails, return [ '', '' ] |
|
| 94 |
+function! s:ParsePattern(string) |
|
| 95 |
+ if a:string[0] != '/' |
|
| 96 |
+ return ['',''] |
|
| 97 |
+ endif |
|
| 98 |
+ |
|
| 99 |
+ let pat = '\\\@<!\%(\\\\\)\{-}\zs/' . tabular#ElementFormatPattern() . '*$'
|
|
| 100 |
+ let format = matchstr(a:string[1:-1], pat) |
|
| 101 |
+ if !empty(format) |
|
| 102 |
+ let format = format[1 : -1] |
|
| 103 |
+ let pattern = a:string[1 : -len(format) - 2] |
|
| 104 |
+ else |
|
| 105 |
+ let pattern = a:string[1 : -1] |
|
| 106 |
+ endif |
|
| 107 |
+ |
|
| 108 |
+ return [pattern, format] |
|
| 109 |
+endfunction |
|
| 110 |
+ |
|
| 111 |
+" Split apart a list of | separated expressions. {{{2
|
|
| 112 |
+function! s:SplitCommands(string) |
|
| 113 |
+ if a:string =~ '^\s*$' |
|
| 114 |
+ return [] |
|
| 115 |
+ endif |
|
| 116 |
+ |
|
| 117 |
+ let end = match(a:string, "[\"'|]") |
|
| 118 |
+ |
|
| 119 |
+ " Loop until we find a delimiting | or end-of-string |
|
| 120 |
+ while end != -1 && (a:string[end] != '|' || a:string[end+1] == '|') |
|
| 121 |
+ if a:string[end] == "'" |
|
| 122 |
+ let end = match(a:string, "'", end+1) + 1 |
|
| 123 |
+ if end == 0 |
|
| 124 |
+ throw "No matching end single quote" |
|
| 125 |
+ endif |
|
| 126 |
+ elseif a:string[end] == '"' |
|
| 127 |
+ " Find a " preceded by an even number of \ (or 0) |
|
| 128 |
+ let pattern = '\%(\\\@<!\%(\\\\\)*\)\@<="' |
|
| 129 |
+ let end = matchend(a:string, pattern, end+1) + 1 |
|
| 130 |
+ if end == 0 |
|
| 131 |
+ throw "No matching end double quote" |
|
| 132 |
+ endif |
|
| 133 |
+ else " Found || |
|
| 134 |
+ let end += 2 |
|
| 135 |
+ endif |
|
| 136 |
+ |
|
| 137 |
+ let end = match(a:string, "[\"'|]", end) |
|
| 138 |
+ endwhile |
|
| 139 |
+ |
|
| 140 |
+ if end == 0 || a:string[0 : end - (end > 0)] =~ '^\s*$' |
|
| 141 |
+ throw "Empty element" |
|
| 142 |
+ endif |
|
| 143 |
+ |
|
| 144 |
+ if end == -1 |
|
| 145 |
+ let rv = [ a:string ] |
|
| 146 |
+ else |
|
| 147 |
+ let rv = [ a:string[0 : end-1] ] + s:SplitCommands(a:string[end+1 : -1]) |
|
| 148 |
+ endif |
|
| 149 |
+ |
|
| 150 |
+ return rv |
|
| 151 |
+endfunction |
|
| 152 |
+ |
|
| 153 |
+" Public Things {{{1
|
|
| 154 |
+ |
|
| 155 |
+" Command associating a command name with a simple pattern command {{{2
|
|
| 156 |
+" AddTabularPattern[!] [<buffer>] name /pattern[/format] |
|
| 157 |
+" |
|
| 158 |
+" If <buffer> is provided, the command will only be available in the current |
|
| 159 |
+" buffer, and will be used instead of any global command with the same name. |
|
| 160 |
+" |
|
| 161 |
+" If a command with the same name and scope already exists, it is an error, |
|
| 162 |
+" unless the ! is provided, in which case the existing command will be |
|
| 163 |
+" replaced. |
|
| 164 |
+" |
|
| 165 |
+" pattern is a regex describing the delimiter to be used. |
|
| 166 |
+" |
|
| 167 |
+" format describes the format pattern to be used. The default will be used if |
|
| 168 |
+" none is provided. |
|
| 169 |
+com! -nargs=+ -bang AddTabularPattern |
|
| 170 |
+ \ call AddTabularPattern(<q-args>, <bang>0) |
|
| 171 |
+ |
|
| 172 |
+function! AddTabularPattern(command, force) |
|
| 173 |
+ try |
|
| 174 |
+ let [ commandmap, rest ] = s:ChooseCommandMap(a:command) |
|
| 175 |
+ |
|
| 176 |
+ let name = matchstr(rest, '.\{-}\ze\s*/')
|
|
| 177 |
+ let pattern = substitute(rest, '.\{-}\s*\ze/', '', '')
|
|
| 178 |
+ |
|
| 179 |
+ let [ pattern, format ] = s:ParsePattern(pattern) |
|
| 180 |
+ |
|
| 181 |
+ if empty(name) || empty(pattern) |
|
| 182 |
+ throw "Invalid arguments!" |
|
| 183 |
+ endif |
|
| 184 |
+ |
|
| 185 |
+ if !a:force && has_key(commandmap, name) |
|
| 186 |
+ throw string(name) . " is already defined, use ! to overwrite." |
|
| 187 |
+ endif |
|
| 188 |
+ |
|
| 189 |
+ let command = "tabular#TabularizeStrings(a:lines, " . string(pattern) |
|
| 190 |
+ |
|
| 191 |
+ if !empty(format) |
|
| 192 |
+ let command .= ", " . string(format) |
|
| 193 |
+ endif |
|
| 194 |
+ |
|
| 195 |
+ let command .= ")" |
|
| 196 |
+ |
|
| 197 |
+ let commandmap[name] = { 'pattern' : pattern, 'commands' : [ command ] }
|
|
| 198 |
+ catch |
|
| 199 |
+ echohl ErrorMsg |
|
| 200 |
+ echomsg "AddTabularPattern: " . v:exception |
|
| 201 |
+ echohl None |
|
| 202 |
+ endtry |
|
| 203 |
+endfunction |
|
| 204 |
+ |
|
| 205 |
+" Command associating a command name with a pipeline of functions {{{2
|
|
| 206 |
+" AddTabularPipeline[!] [<buffer>] name /pattern/ func [ | func2 [ | func3 ] ] |
|
| 207 |
+" |
|
| 208 |
+" If <buffer> is provided, the command will only be available in the current |
|
| 209 |
+" buffer, and will be used instead of any global command with the same name. |
|
| 210 |
+" |
|
| 211 |
+" If a command with the same name and scope already exists, it is an error, |
|
| 212 |
+" unless the ! is provided, in which case the existing command will be |
|
| 213 |
+" replaced. |
|
| 214 |
+" |
|
| 215 |
+" pattern is a regex that will be used to determine which lines will be |
|
| 216 |
+" filtered. If the cursor line doesn't match the pattern, using the command |
|
| 217 |
+" will be a no-op, otherwise the cursor and all contiguous lines matching the |
|
| 218 |
+" pattern will be filtered. |
|
| 219 |
+" |
|
| 220 |
+" Each 'func' argument represents a function to be called. This function |
|
| 221 |
+" will have access to a:lines, a List containing one String per line being |
|
| 222 |
+" filtered. |
|
| 223 |
+com! -nargs=+ -bang AddTabularPipeline |
|
| 224 |
+ \ call AddTabularPipeline(<q-args>, <bang>0) |
|
| 225 |
+ |
|
| 226 |
+function! AddTabularPipeline(command, force) |
|
| 227 |
+ try |
|
| 228 |
+ let [ commandmap, rest ] = s:ChooseCommandMap(a:command) |
|
| 229 |
+ |
|
| 230 |
+ let name = matchstr(rest, '.\{-}\ze\s*/')
|
|
| 231 |
+ let pattern = substitute(rest, '.\{-}\s*\ze/', '', '')
|
|
| 232 |
+ |
|
| 233 |
+ let commands = matchstr(pattern, '^/.\{-}\\\@<!\%(\\\\\)\{-}/\zs.*')
|
|
| 234 |
+ let pattern = matchstr(pattern, '/\zs.\{-}\\\@<!\%(\\\\\)\{-}\ze/')
|
|
| 235 |
+ |
|
| 236 |
+ if empty(name) || empty(pattern) |
|
| 237 |
+ throw "Invalid arguments!" |
|
| 238 |
+ endif |
|
| 239 |
+ |
|
| 240 |
+ if !a:force && has_key(commandmap, name) |
|
| 241 |
+ throw string(name) . " is already defined, use ! to overwrite." |
|
| 242 |
+ endif |
|
| 243 |
+ |
|
| 244 |
+ let commandlist = s:SplitCommands(commands) |
|
| 245 |
+ |
|
| 246 |
+ if empty(commandlist) |
|
| 247 |
+ throw "Must provide a list of functions!" |
|
| 248 |
+ endif |
|
| 249 |
+ |
|
| 250 |
+ let commandmap[name] = { 'pattern' : pattern, 'commands' : commandlist }
|
|
| 251 |
+ catch |
|
| 252 |
+ echohl ErrorMsg |
|
| 253 |
+ echomsg "AddTabularPipeline: " . v:exception |
|
| 254 |
+ echohl None |
|
| 255 |
+ endtry |
|
| 256 |
+endfunction |
|
| 257 |
+ |
|
| 258 |
+" Tabularize /pattern[/format] {{{2
|
|
| 259 |
+" Tabularize name |
|
| 260 |
+" |
|
| 261 |
+" Align text, either using the given pattern, or the command associated with |
|
| 262 |
+" the given name. |
|
| 263 |
+com! -nargs=* -range -complete=customlist,<SID>CompleteTabularizeCommand |
|
| 264 |
+ \ Tabularize <line1>,<line2>call Tabularize(<q-args>) |
|
| 265 |
+ |
|
| 266 |
+function! Tabularize(command, ...) range |
|
| 267 |
+ let piperange_opt = {}
|
|
| 268 |
+ if a:0 |
|
| 269 |
+ let piperange_opt = a:1 |
|
| 270 |
+ endif |
|
| 271 |
+ |
|
| 272 |
+ if empty(a:command) |
|
| 273 |
+ if !exists("s:last_tabularize_command")
|
|
| 274 |
+ echohl ErrorMsg |
|
| 275 |
+ echomsg "Tabularize hasn't been called yet; no pattern/command to reuse!" |
|
| 276 |
+ echohl None |
|
| 277 |
+ return |
|
| 278 |
+ endif |
|
| 279 |
+ else |
|
| 280 |
+ let s:last_tabularize_command = a:command |
|
| 281 |
+ endif |
|
| 282 |
+ |
|
| 283 |
+ let command = s:last_tabularize_command |
|
| 284 |
+ |
|
| 285 |
+ let range = a:firstline . ',' . a:lastline |
|
| 286 |
+ |
|
| 287 |
+ try |
|
| 288 |
+ let [ pattern, format ] = s:ParsePattern(command) |
|
| 289 |
+ |
|
| 290 |
+ if !empty(pattern) |
|
| 291 |
+ let cmd = "tabular#TabularizeStrings(a:lines, " . string(pattern) |
|
| 292 |
+ |
|
| 293 |
+ if !empty(format) |
|
| 294 |
+ let cmd .= "," . string(format) |
|
| 295 |
+ endif |
|
| 296 |
+ |
|
| 297 |
+ let cmd .= ")" |
|
| 298 |
+ |
|
| 299 |
+ exe range . 'call tabular#PipeRangeWithOptions(pattern, [ cmd ], ' |
|
| 300 |
+ \ . 'piperange_opt)' |
|
| 301 |
+ else |
|
| 302 |
+ if exists('b:TabularCommands') && has_key(b:TabularCommands, command)
|
|
| 303 |
+ let usercmd = b:TabularCommands[command] |
|
| 304 |
+ elseif has_key(s:TabularCommands, command) |
|
| 305 |
+ let usercmd = s:TabularCommands[command] |
|
| 306 |
+ else |
|
| 307 |
+ throw "Unrecognized command " . string(command) |
|
| 308 |
+ endif |
|
| 309 |
+ |
|
| 310 |
+ exe range . 'call tabular#PipeRangeWithOptions(usercmd["pattern"], ' |
|
| 311 |
+ \ . 'usercmd["commands"], piperange_opt)' |
|
| 312 |
+ endif |
|
| 313 |
+ catch |
|
| 314 |
+ echohl ErrorMsg |
|
| 315 |
+ echomsg "Tabularize: " . v:exception |
|
| 316 |
+ echohl None |
|
| 317 |
+ return |
|
| 318 |
+ endtry |
|
| 319 |
+endfunction |
|
| 320 |
+ |
|
| 321 |
+function! TabularizeHasPattern() |
|
| 322 |
+ return exists("s:last_tabularize_command")
|
|
| 323 |
+endfunction |
|
| 324 |
+ |
|
| 325 |
+" GTabularize /pattern[/format] {{{2
|
|
| 326 |
+" GTabularize name |
|
| 327 |
+" |
|
| 328 |
+" Align text on only matching lines, either using the given pattern, or the |
|
| 329 |
+" command associated with the given name. Mnemonically, this is similar to |
|
| 330 |
+" the :global command, which takes some action on all rows matching a pattern |
|
| 331 |
+" in a range. This command is different from normal :Tabularize in 3 ways: |
|
| 332 |
+" 1) If a line in the range does not match the pattern, it will be left |
|
| 333 |
+" unchanged, and not in any way affect the outcome of other lines in the |
|
| 334 |
+" range (at least, normally - but Pipelines can and will still look at |
|
| 335 |
+" non-matching rows unless they are specifically written to be aware of |
|
| 336 |
+" tabular#DoGTabularize() and handle it appropriately). |
|
| 337 |
+" 2) No automatic range determination - :Tabularize automatically expands |
|
| 338 |
+" a single-line range (or a call with no range) to include all adjacent |
|
| 339 |
+" matching lines. That behavior does not make sense for this command. |
|
| 340 |
+" 3) If called without a range, it will act on all lines in the buffer (like |
|
| 341 |
+" :global) rather than only a single line |
|
| 342 |
+com! -nargs=* -range=% -complete=customlist,<SID>CompleteTabularizeCommand |
|
| 343 |
+ \ GTabularize <line1>,<line2> |
|
| 344 |
+ \ call Tabularize(<q-args>, { 'mode': 'GTabularize' } )
|
|
| 345 |
+ |
|
| 346 |
+" Stupid vimscript crap, part 2 {{{1
|
|
| 347 |
+let &cpo = s:savecpo |
|
| 348 |
+unlet s:savecpo |
|
| 349 |
+ |
|
| 350 |
+" vim:set sw=2 sts=2 fdm=marker: |
| ... | ... |
@@ -0,0 +1,24 @@ |
| 1 |
+Copyright (c) 2016, Matthew J. Wozniski |
|
| 2 |
+All rights reserved. |
|
| 3 |
+ |
|
| 4 |
+Redistribution and use in source and binary forms, with or without |
|
| 5 |
+modification, are permitted provided that the following conditions are met: |
|
| 6 |
+ * Redistributions of source code must retain the above copyright notice, |
|
| 7 |
+ this list of conditions and the following disclaimer. |
|
| 8 |
+ * Redistributions in binary form must reproduce the above copyright |
|
| 9 |
+ notice, this list of conditions and the following disclaimer in the |
|
| 10 |
+ documentation and/or other materials provided with the distribution. |
|
| 11 |
+ * The names of the contributors may not be used to endorse or promote |
|
| 12 |
+ products derived from this software without specific prior written |
|
| 13 |
+ permission. |
|
| 14 |
+ |
|
| 15 |
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS |
|
| 16 |
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
| 17 |
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
|
| 18 |
+NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
| 19 |
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 20 |
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
|
| 21 |
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
| 22 |
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
| 23 |
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
|
| 24 |
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| ... | ... |
@@ -0,0 +1,38 @@ |
| 1 |
+Tabular |
|
| 2 |
+============== |
|
| 3 |
+Sometimes, it's useful to line up text. Naturally, it's nicer to have the |
|
| 4 |
+computer do this for you, since aligning things by hand quickly becomes |
|
| 5 |
+unpleasant. While there are other plugins for aligning text, the ones I've |
|
| 6 |
+tried are either impossibly difficult to understand and use, or too simplistic |
|
| 7 |
+to handle complicated tasks. This plugin aims to make the easy things easy |
|
| 8 |
+and the hard things possible, without providing an unnecessarily obtuse |
|
| 9 |
+interface. It's still a work in progress, and criticisms are welcome. |
|
| 10 |
+ |
|
| 11 |
+See [Aligning Text with Tabular.vim](http://vimcasts.org/episodes/aligning-text-with-tabular-vim/) |
|
| 12 |
+for a screencast that shows how Tabular.vim works. |
|
| 13 |
+ |
|
| 14 |
+See [doc/Tabular.txt](http://raw.github.com/godlygeek/tabular/master/doc/Tabular.txt) |
|
| 15 |
+for detailed documentation. |
|
| 16 |
+ |
|
| 17 |
+Installation |
|
| 18 |
+============== |
|
| 19 |
+ |
|
| 20 |
+## Vim 8.1+ |
|
| 21 |
+ |
|
| 22 |
+No third-party package manager is required! Clone into: |
|
| 23 |
+ |
|
| 24 |
+`.vim/pack/plugins/start` |
|
| 25 |
+ |
|
| 26 |
+Make sure you include `packloadall` in your `.vimrc`. |
|
| 27 |
+ |
|
| 28 |
+## Pathogen |
|
| 29 |
+ |
|
| 30 |
+ mkdir -p ~/.vim/bundle |
|
| 31 |
+ cd ~/.vim/bundle |
|
| 32 |
+ git clone https://github.com/godlygeek/tabular.git |
|
| 33 |
+ |
|
| 34 |
+Once help tags have been generated (either using Pathogen's `:Helptags` |
|
| 35 |
+command, or by pointing vim's `:helptags` command at the directory where you |
|
| 36 |
+installed Tabular), you can view the manual with `:help tabular`. |
|
| 37 |
+ |
|
| 38 |
+See [pathogen.vim](https://github.com/tpope/vim-pathogen) for help or for package manager installation. |
| ... | ... |
@@ -0,0 +1,74 @@ |
| 1 |
+" Copyright (c) 2016, Matthew J. Wozniski |
|
| 2 |
+" All rights reserved. |
|
| 3 |
+" |
|
| 4 |
+" Redistribution and use in source and binary forms, with or without |
|
| 5 |
+" modification, are permitted provided that the following conditions are met: |
|
| 6 |
+" * Redistributions of source code must retain the above copyright notice, |
|
| 7 |
+" this list of conditions and the following disclaimer. |
|
| 8 |
+" * Redistributions in binary form must reproduce the above copyright |
|
| 9 |
+" notice, this list of conditions and the following disclaimer in the |
|
| 10 |
+" documentation and/or other materials provided with the distribution. |
|
| 11 |
+" * The names of the contributors may not be used to endorse or promote |
|
| 12 |
+" products derived from this software without specific prior written |
|
| 13 |
+" permission. |
|
| 14 |
+" |
|
| 15 |
+" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS |
|
| 16 |
+" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
| 17 |
+" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
|
| 18 |
+" NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
| 19 |
+" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 20 |
+" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
|
| 21 |
+" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
| 22 |
+" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
| 23 |
+" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
|
| 24 |
+" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 25 |
+ |
|
| 26 |
+if !exists(':Tabularize') || get(g:, 'no_default_tabular_maps', 0)
|
|
| 27 |
+ finish " Tabular.vim wasn't loaded or the default maps are unwanted |
|
| 28 |
+endif |
|
| 29 |
+ |
|
| 30 |
+let s:save_cpo = &cpo |
|
| 31 |
+set cpo&vim |
|
| 32 |
+ |
|
| 33 |
+AddTabularPattern! assignment /[|&+*/%<>=!~-]\@<!\([<>!=]=\|=\~\)\@![|&+*/%<>=!~-]*=/l1r1 |
|
| 34 |
+AddTabularPattern! two_spaces / /l0 |
|
| 35 |
+ |
|
| 36 |
+AddTabularPipeline! multiple_spaces / / map(a:lines, "substitute(v:val, ' *', ' ', 'g')") | tabular#TabularizeStrings(a:lines, ' ', 'l0') |
|
| 37 |
+AddTabularPipeline! spaces / / map(a:lines, "substitute(v:val, ' *' , ' ' , 'g')") | tabular#TabularizeStrings(a:lines, ' ' , 'l0') |
|
| 38 |
+ |
|
| 39 |
+AddTabularPipeline! argument_list /(.*)/ map(a:lines, 'substitute(v:val, ''\s*\([(,)]\)\s*'', ''\1'', ''g'')') |
|
| 40 |
+ \ | tabular#TabularizeStrings(a:lines, '[(,)]', 'l0') |
|
| 41 |
+ \ | map(a:lines, 'substitute(v:val, ''\(\s*\),'', '',\1 '', "g")') |
|
| 42 |
+ \ | map(a:lines, 'substitute(v:val, ''\s*)'', ")", "g")') |
|
| 43 |
+ |
|
| 44 |
+function! SplitCDeclarations(lines) |
|
| 45 |
+ let rv = [] |
|
| 46 |
+ for line in a:lines |
|
| 47 |
+ " split the line into declaractions |
|
| 48 |
+ let split = split(line, '\s*[,;]\s*') |
|
| 49 |
+ " separate the type from the first declaration |
|
| 50 |
+ let type = substitute(split[0], '\%(\%([&*]\s*\)*\)\=\k\+$', '', '') |
|
| 51 |
+ " add the ; back on every declaration |
|
| 52 |
+ call map(split, 'v:val . ";"') |
|
| 53 |
+ " add the first element to the return as-is, and remove it from the list |
|
| 54 |
+ let rv += [ remove(split, 0) ] |
|
| 55 |
+ " transform the other elements by adding the type on at the beginning |
|
| 56 |
+ call map(split, 'type . v:val') |
|
| 57 |
+ " and add them all to the return |
|
| 58 |
+ let rv += split |
|
| 59 |
+ endfor |
|
| 60 |
+ return rv |
|
| 61 |
+endfunction |
|
| 62 |
+ |
|
| 63 |
+AddTabularPipeline! split_declarations /,.*;/ SplitCDeclarations(a:lines) |
|
| 64 |
+ |
|
| 65 |
+AddTabularPattern! ternary_operator /^.\{-}\zs?\|:/l1
|
|
| 66 |
+ |
|
| 67 |
+AddTabularPattern! cpp_io /<<\|>>/l1 |
|
| 68 |
+ |
|
| 69 |
+AddTabularPattern! pascal_assign /:=/l1 |
|
| 70 |
+ |
|
| 71 |
+AddTabularPattern! trailing_c_comments /\/\*\|\*\/\|\/\//l1 |
|
| 72 |
+ |
|
| 73 |
+let &cpo = s:save_cpo |
|
| 74 |
+unlet s:save_cpo |
| ... | ... |
@@ -0,0 +1,437 @@ |
| 1 |
+" Tabular: Align columnar data using regex-designated column boundaries |
|
| 2 |
+" Maintainer: Matthew Wozniski (godlygeek@gmail.com) |
|
| 3 |
+" Date: Thu, 03 May 2012 20:49:32 -0400 |
|
| 4 |
+" Version: 1.0 |
|
| 5 |
+" |
|
| 6 |
+" Long Description: |
|
| 7 |
+" Sometimes, it's useful to line up text. Naturally, it's nicer to have the |
|
| 8 |
+" computer do this for you, since aligning things by hand quickly becomes |
|
| 9 |
+" unpleasant. While there are other plugins for aligning text, the ones I've |
|
| 10 |
+" tried are either impossibly difficult to understand and use, or too simplistic |
|
| 11 |
+" to handle complicated tasks. This plugin aims to make the easy things easy |
|
| 12 |
+" and the hard things possible, without providing an unnecessarily obtuse |
|
| 13 |
+" interface. It's still a work in progress, and criticisms are welcome. |
|
| 14 |
+" |
|
| 15 |
+" License: |
|
| 16 |
+" Copyright (c) 2012, Matthew J. Wozniski |
|
| 17 |
+" All rights reserved. |
|
| 18 |
+" |
|
| 19 |
+" Redistribution and use in source and binary forms, with or without |
|
| 20 |
+" modification, are permitted provided that the following conditions are met: |
|
| 21 |
+" * Redistributions of source code must retain the above copyright notice, |
|
| 22 |
+" this list of conditions and the following disclaimer. |
|
| 23 |
+" * Redistributions in binary form must reproduce the above copyright |
|
| 24 |
+" notice, this list of conditions and the following disclaimer in the |
|
| 25 |
+" documentation and/or other materials provided with the distribution. |
|
| 26 |
+" * The names of the contributors may not be used to endorse or promote |
|
| 27 |
+" products derived from this software without specific prior written |
|
| 28 |
+" permission. |
|
| 29 |
+" |
|
| 30 |
+" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS |
|
| 31 |
+" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
| 32 |
+" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
|
| 33 |
+" NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
| 34 |
+" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 35 |
+" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
|
| 36 |
+" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
| 37 |
+" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
| 38 |
+" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
|
| 39 |
+" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 40 |
+ |
|
| 41 |
+" Stupid vimscript crap {{{1
|
|
| 42 |
+let s:savecpo = &cpo |
|
| 43 |
+set cpo&vim |
|
| 44 |
+ |
|
| 45 |
+" Private Functions {{{1
|
|
| 46 |
+ |
|
| 47 |
+" Return the number of bytes in a string after expanding tabs to spaces. {{{2
|
|
| 48 |
+" This expansion is done based on the current value of 'tabstop' |
|
| 49 |
+if exists('*strdisplaywidth')
|
|
| 50 |
+ " Needs vim 7.3 |
|
| 51 |
+ let s:Strlen = function("strdisplaywidth")
|
|
| 52 |
+else |
|
| 53 |
+ function! s:Strlen(string) |
|
| 54 |
+ " Implement the tab handling part of strdisplaywidth for vim 7.2 and |
|
| 55 |
+ " earlier - not much that can be done about handling doublewidth |
|
| 56 |
+ " characters. |
|
| 57 |
+ let rv = 0 |
|
| 58 |
+ let i = 0 |
|
| 59 |
+ |
|
| 60 |
+ for char in split(a:string, '\zs') |
|
| 61 |
+ if char == "\t" |
|
| 62 |
+ let rv += &ts - i |
|
| 63 |
+ let i = 0 |
|
| 64 |
+ else |
|
| 65 |
+ let rv += 1 |
|
| 66 |
+ let i = (i + 1) % &ts |
|
| 67 |
+ endif |
|
| 68 |
+ endfor |
|
| 69 |
+ |
|
| 70 |
+ return rv |
|
| 71 |
+ endfunction |
|
| 72 |
+endif |
|
| 73 |
+ |
|
| 74 |
+" Align a string within a field {{{2
|
|
| 75 |
+" These functions do not trim leading and trailing spaces. |
|
| 76 |
+ |
|
| 77 |
+" Right align 'string' in a field of size 'fieldwidth' |
|
| 78 |
+function! s:Right(string, fieldwidth) |
|
| 79 |
+ let spaces = a:fieldwidth - s:Strlen(a:string) |
|
| 80 |
+ return matchstr(a:string, '^\s*') . repeat(" ", spaces) . substitute(a:string, '^\s*', '', '')
|
|
| 81 |
+endfunction |
|
| 82 |
+ |
|
| 83 |
+" Left align 'string' in a field of size 'fieldwidth' |
|
| 84 |
+function! s:Left(string, fieldwidth) |
|
| 85 |
+ let spaces = a:fieldwidth - s:Strlen(a:string) |
|
| 86 |
+ return a:string . repeat(" ", spaces)
|
|
| 87 |
+endfunction |
|
| 88 |
+ |
|
| 89 |
+" Center align 'string' in a field of size 'fieldwidth' |
|
| 90 |
+function! s:Center(string, fieldwidth) |
|
| 91 |
+ let spaces = a:fieldwidth - s:Strlen(a:string) |
|
| 92 |
+ let right = spaces / 2 |
|
| 93 |
+ let left = right + (right * 2 != spaces) |
|
| 94 |
+ return repeat(" ", left) . a:string . repeat(" ", right)
|
|
| 95 |
+endfunction |
|
| 96 |
+ |
|
| 97 |
+" Remove spaces around a string {{{2
|
|
| 98 |
+ |
|
| 99 |
+" Remove all trailing spaces from a string. |
|
| 100 |
+function! s:StripTrailingSpaces(string) |
|
| 101 |
+ return matchstr(a:string, '^.\{-}\ze\s*$')
|
|
| 102 |
+endfunction |
|
| 103 |
+ |
|
| 104 |
+" Remove all leading spaces from a string. |
|
| 105 |
+function! s:StripLeadingSpaces(string) |
|
| 106 |
+ return matchstr(a:string, '^\s*\zs.*$') |
|
| 107 |
+endfunction |
|
| 108 |
+ |
|
| 109 |
+" Find the longest common indent for a list of strings {{{2
|
|
| 110 |
+" If a string is shorter than the others but contains no non-whitespace |
|
| 111 |
+" characters, it does not end the match. This provides consistency with |
|
| 112 |
+" vim's behavior that blank lines don't have trailing spaces. |
|
| 113 |
+function! s:LongestCommonIndent(strings) |
|
| 114 |
+ if empty(a:strings) |
|
| 115 |
+ return '' |
|
| 116 |
+ endif |
|
| 117 |
+ |
|
| 118 |
+ let n = 0 |
|
| 119 |
+ while 1 |
|
| 120 |
+ let ns = join(map(copy(a:strings), 'v:val[n]'), '') |
|
| 121 |
+ if ns !~ '^ \+$\|^\t\+$' |
|
| 122 |
+ break |
|
| 123 |
+ endif |
|
| 124 |
+ let n += 1 |
|
| 125 |
+ endwhile |
|
| 126 |
+ |
|
| 127 |
+ return strpart(a:strings[0], 0, n) |
|
| 128 |
+endfunction |
|
| 129 |
+ |
|
| 130 |
+" Split a string into fields and delimiters {{{2
|
|
| 131 |
+" Like split(), but include the delimiters as elements |
|
| 132 |
+" All odd numbered elements are delimiters |
|
| 133 |
+" All even numbered elements are non-delimiters (including zero) |
|
| 134 |
+function! s:SplitDelim(string, delim) |
|
| 135 |
+ let rv = [] |
|
| 136 |
+ let beg = 0 |
|
| 137 |
+ |
|
| 138 |
+ let len = len(a:string) |
|
| 139 |
+ let searchoff = 0 |
|
| 140 |
+ |
|
| 141 |
+ while 1 |
|
| 142 |
+ let mid = match(a:string, a:delim, beg + searchoff, 1) |
|
| 143 |
+ if mid == -1 || mid == len |
|
| 144 |
+ break |
|
| 145 |
+ endif |
|
| 146 |
+ |
|
| 147 |
+ let matchstr = matchstr(a:string, a:delim, beg + searchoff, 1) |
|
| 148 |
+ let length = strlen(matchstr) |
|
| 149 |
+ |
|
| 150 |
+ if length == 0 && beg == mid |
|
| 151 |
+ " Zero-length match for a zero-length delimiter - advance past it |
|
| 152 |
+ let searchoff += 1 |
|
| 153 |
+ continue |
|
| 154 |
+ endif |
|
| 155 |
+ |
|
| 156 |
+ if beg == mid |
|
| 157 |
+ let rv += [ "" ] |
|
| 158 |
+ else |
|
| 159 |
+ let rv += [ a:string[beg : mid-1] ] |
|
| 160 |
+ endif |
|
| 161 |
+ |
|
| 162 |
+ let rv += [ matchstr ] |
|
| 163 |
+ |
|
| 164 |
+ let beg = mid + length |
|
| 165 |
+ let searchoff = 0 |
|
| 166 |
+ endwhile |
|
| 167 |
+ |
|
| 168 |
+ let rv += [ strpart(a:string, beg) ] |
|
| 169 |
+ |
|
| 170 |
+ return rv |
|
| 171 |
+endfunction |
|
| 172 |
+ |
|
| 173 |
+" Replace lines from `start' to `start + len - 1' with the given strings. {{{2
|
|
| 174 |
+" If more lines are needed to show all strings, they will be added. |
|
| 175 |
+" If there are too few strings to fill all lines, lines will be removed. |
|
| 176 |
+function! s:SetLines(start, len, strings) |
|
| 177 |
+ if a:start > line('$') + 1 || a:start < 1
|
|
| 178 |
+ throw "Invalid start line!" |
|
| 179 |
+ endif |
|
| 180 |
+ |
|
| 181 |
+ if len(a:strings) > a:len |
|
| 182 |
+ let fensave = &fen |
|
| 183 |
+ let view = winsaveview() |
|
| 184 |
+ call append(a:start + a:len - 1, repeat([''], len(a:strings) - a:len)) |
|
| 185 |
+ call winrestview(view) |
|
| 186 |
+ let &fen = fensave |
|
| 187 |
+ elseif len(a:strings) < a:len |
|
| 188 |
+ let fensave = &fen |
|
| 189 |
+ let view = winsaveview() |
|
| 190 |
+ sil exe (a:start + len(a:strings)) . ',' . (a:start + a:len - 1) . 'd_' |
|
| 191 |
+ call winrestview(view) |
|
| 192 |
+ let &fen = fensave |
|
| 193 |
+ endif |
|
| 194 |
+ |
|
| 195 |
+ call setline(a:start, a:strings) |
|
| 196 |
+endfunction |
|
| 197 |
+ |
|
| 198 |
+" Runs the given commandstring argument as an expression. {{{2
|
|
| 199 |
+" The commandstring expression is expected to reference the a:lines argument. |
|
| 200 |
+" If the commandstring expression returns a list the items of that list will |
|
| 201 |
+" replace the items in a:lines, otherwise the expression is assumed to have |
|
| 202 |
+" modified a:lines itself. |
|
| 203 |
+function! s:FilterString(lines, commandstring) |
|
| 204 |
+ exe 'let rv = ' . a:commandstring |
|
| 205 |
+ |
|
| 206 |
+ if type(rv) == type(a:lines) && rv isnot a:lines |
|
| 207 |
+ call filter(a:lines, 0) |
|
| 208 |
+ call extend(a:lines, rv) |
|
| 209 |
+ endif |
|
| 210 |
+endfunction |
|
| 211 |
+ |
|
| 212 |
+" Public API {{{1
|
|
| 213 |
+ |
|
| 214 |
+if !exists("g:tabular_default_format")
|
|
| 215 |
+ let g:tabular_default_format = "l1" |
|
| 216 |
+endif |
|
| 217 |
+ |
|
| 218 |
+let s:formatelempat = '\%([lrc]\d\+\)' |
|
| 219 |
+ |
|
| 220 |
+function! tabular#ElementFormatPattern() |
|
| 221 |
+ return s:formatelempat |
|
| 222 |
+endfunction |
|
| 223 |
+ |
|
| 224 |
+" Given a list of strings and a delimiter, split each string on every |
|
| 225 |
+" occurrence of the delimiter pattern, format each element according to either |
|
| 226 |
+" the provided format (optional) or the default format, and join them back |
|
| 227 |
+" together with enough space padding to guarantee that the nth delimiter of |
|
| 228 |
+" each string is aligned. |
|
| 229 |
+function! tabular#TabularizeStrings(strings, delim, ...) |
|
| 230 |
+ if a:0 > 1 |
|
| 231 |
+ echoerr "TabularizeStrings accepts only 2 or 3 arguments (got ".(a:0+2).")" |
|
| 232 |
+ return 1 |
|
| 233 |
+ endif |
|
| 234 |
+ |
|
| 235 |
+ let formatstr = (a:0 ? a:1 : g:tabular_default_format) |
|
| 236 |
+ |
|
| 237 |
+ if formatstr !~? s:formatelempat . '\+' |
|
| 238 |
+ echoerr "Tabular: Invalid format \"" . formatstr . "\" specified!" |
|
| 239 |
+ return 1 |
|
| 240 |
+ endif |
|
| 241 |
+ |
|
| 242 |
+ let format = split(formatstr, s:formatelempat . '\zs') |
|
| 243 |
+ |
|
| 244 |
+ let lines = a:strings |
|
| 245 |
+ |
|
| 246 |
+ call map(lines, 's:SplitDelim(v:val, a:delim)') |
|
| 247 |
+ |
|
| 248 |
+ let first_fields = [] |
|
| 249 |
+ |
|
| 250 |
+ " Strip spaces from non-delimiters; spaces in delimiters must have been |
|
| 251 |
+ " matched intentionally |
|
| 252 |
+ for line in lines |
|
| 253 |
+ if len(line) == 1 && s:do_gtabularize |
|
| 254 |
+ continue " Leave non-matching lines unchanged for GTabularize |
|
| 255 |
+ endif |
|
| 256 |
+ |
|
| 257 |
+ call add(first_fields, line[0]) |
|
| 258 |
+ |
|
| 259 |
+ if len(line) >= 1 |
|
| 260 |
+ for i in range(0, len(line)-1, 2) |
|
| 261 |
+ let line[i] = s:StripLeadingSpaces(s:StripTrailingSpaces(line[i])) |
|
| 262 |
+ endfor |
|
| 263 |
+ endif |
|
| 264 |
+ endfor |
|
| 265 |
+ |
|
| 266 |
+ let common_indent = s:LongestCommonIndent(first_fields) |
|
| 267 |
+ |
|
| 268 |
+ " Find the max length of each field |
|
| 269 |
+ let maxes = [] |
|
| 270 |
+ for line in lines |
|
| 271 |
+ if len(line) == 1 && s:do_gtabularize |
|
| 272 |
+ continue " non-matching lines don't affect field widths for GTabularize |
|
| 273 |
+ endif |
|
| 274 |
+ |
|
| 275 |
+ for i in range(len(line)) |
|
| 276 |
+ if i == len(maxes) |
|
| 277 |
+ let maxes += [ s:Strlen(line[i]) ] |
|
| 278 |
+ else |
|
| 279 |
+ let maxes[i] = max( [ maxes[i], s:Strlen(line[i]) ] ) |
|
| 280 |
+ endif |
|
| 281 |
+ endfor |
|
| 282 |
+ endfor |
|
| 283 |
+ |
|
| 284 |
+ " Concatenate the fields, according to the format pattern. |
|
| 285 |
+ for idx in range(len(lines)) |
|
| 286 |
+ let line = lines[idx] |
|
| 287 |
+ |
|
| 288 |
+ if len(line) == 1 && s:do_gtabularize |
|
| 289 |
+ let lines[idx] = line[0] " GTabularize doesn't change non-matching lines |
|
| 290 |
+ continue |
|
| 291 |
+ endif |
|
| 292 |
+ |
|
| 293 |
+ for i in range(len(line)) |
|
| 294 |
+ let how = format[i % len(format)][0] |
|
| 295 |
+ let pad = format[i % len(format)][1:-1] |
|
| 296 |
+ |
|
| 297 |
+ if how =~? 'l' |
|
| 298 |
+ let field = s:Left(line[i], maxes[i]) |
|
| 299 |
+ elseif how =~? 'r' |
|
| 300 |
+ let field = s:Right(line[i], maxes[i]) |
|
| 301 |
+ elseif how =~? 'c' |
|
| 302 |
+ let field = s:Center(line[i], maxes[i]) |
|
| 303 |
+ endif |
|
| 304 |
+ |
|
| 305 |
+ let line[i] = field . repeat(" ", pad)
|
|
| 306 |
+ endfor |
|
| 307 |
+ |
|
| 308 |
+ let prefix = common_indent |
|
| 309 |
+ if len(line) == 1 && s:do_gtabularize |
|
| 310 |
+ " We didn't strip the indent in this case; nothing to put back. |
|
| 311 |
+ let prefix = '' |
|
| 312 |
+ endif |
|
| 313 |
+ |
|
| 314 |
+ let lines[idx] = s:StripTrailingSpaces(prefix . join(line, '')) |
|
| 315 |
+ endfor |
|
| 316 |
+endfunction |
|
| 317 |
+ |
|
| 318 |
+" Apply 0 or more filters, in sequence, to selected text in the buffer {{{2
|
|
| 319 |
+" The lines to be filtered are determined as follows: |
|
| 320 |
+" If the function is called with a range containing multiple lines, then |
|
| 321 |
+" those lines will be used as the range. |
|
| 322 |
+" If the function is called with no range or with a range of 1 line, then |
|
| 323 |
+" if GTabularize mode is being used, |
|
| 324 |
+" the range will not be adjusted |
|
| 325 |
+" if "includepat" is not specified, |
|
| 326 |
+" that 1 line will be filtered, |
|
| 327 |
+" if "includepat" is specified and that line does not match it, |
|
| 328 |
+" no lines will be filtered |
|
| 329 |
+" if "includepat" is specified and that line does match it, |
|
| 330 |
+" all contiguous lines above and below the specified line matching the |
|
| 331 |
+" pattern will be filtered. |
|
| 332 |
+" |
|
| 333 |
+" The remaining arguments must each be a filter to apply to the text. |
|
| 334 |
+" Each filter must either be a String evaluating to a function to be called. |
|
| 335 |
+function! tabular#PipeRange(includepat, ...) range |
|
| 336 |
+ exe a:firstline . ',' . a:lastline |
|
| 337 |
+ \ . 'call tabular#PipeRangeWithOptions(a:includepat, a:000, {})'
|
|
| 338 |
+endfunction |
|
| 339 |
+ |
|
| 340 |
+" Extended version of tabular#PipeRange, which |
|
| 341 |
+" 1) Takes the list of filters as an explicit list rather than as varargs |
|
| 342 |
+" 2) Supports passing a dictionary of options to control the routine. |
|
| 343 |
+" Currently, the only supported option is 'mode', which determines whether |
|
| 344 |
+" to behave as :Tabularize or as :GTabularize |
|
| 345 |
+" This allows me to add new features here without breaking API compatibility |
|
| 346 |
+" in the future. |
|
| 347 |
+function! tabular#PipeRangeWithOptions(includepat, filterlist, options) range |
|
| 348 |
+ let top = a:firstline |
|
| 349 |
+ let bot = a:lastline |
|
| 350 |
+ |
|
| 351 |
+ let s:do_gtabularize = (get(a:options, 'mode', '') ==# 'GTabularize') |
|
| 352 |
+ |
|
| 353 |
+ if !s:do_gtabularize |
|
| 354 |
+ " In the default mode, apply range extension logic |
|
| 355 |
+ if a:includepat != '' && top == bot |
|
| 356 |
+ if top < 0 || top > line('$') || getline(top) !~ a:includepat
|
|
| 357 |
+ return |
|
| 358 |
+ endif |
|
| 359 |
+ while top > 1 && getline(top-1) =~ a:includepat |
|
| 360 |
+ let top -= 1 |
|
| 361 |
+ endwhile |
|
| 362 |
+ while bot < line('$') && getline(bot+1) =~ a:includepat
|
|
| 363 |
+ let bot += 1 |
|
| 364 |
+ endwhile |
|
| 365 |
+ endif |
|
| 366 |
+ endif |
|
| 367 |
+ |
|
| 368 |
+ let lines = map(range(top, bot), 'getline(v:val)') |
|
| 369 |
+ |
|
| 370 |
+ for filter in a:filterlist |
|
| 371 |
+ if type(filter) != type("")
|
|
| 372 |
+ echoerr "PipeRange: Bad filter: " . string(filter) |
|
| 373 |
+ endif |
|
| 374 |
+ |
|
| 375 |
+ call s:FilterString(lines, filter) |
|
| 376 |
+ |
|
| 377 |
+ unlet filter |
|
| 378 |
+ endfor |
|
| 379 |
+ |
|
| 380 |
+ call s:SetLines(top, bot - top + 1, lines) |
|
| 381 |
+endfunction |
|
| 382 |
+ |
|
| 383 |
+" Part of the public interface so interested pipelines can query this and |
|
| 384 |
+" adjust their behavior appropriately. |
|
| 385 |
+function! tabular#DoGTabularize() |
|
| 386 |
+ return s:do_gtabularize |
|
| 387 |
+endfunction |
|
| 388 |
+ |
|
| 389 |
+function! s:SplitDelimTest(string, delim, expected) |
|
| 390 |
+ let result = s:SplitDelim(a:string, a:delim) |
|
| 391 |
+ |
|
| 392 |
+ if result !=# a:expected |
|
| 393 |
+ echomsg 'Test failed!' |
|
| 394 |
+ echomsg ' string=' . string(a:string) . ' delim=' . string(a:delim) |
|
| 395 |
+ echomsg ' Returned=' . string(result) |
|
| 396 |
+ echomsg ' Expected=' . string(a:expected) |
|
| 397 |
+ endif |
|
| 398 |
+endfunction |
|
| 399 |
+ |
|
| 400 |
+function! tabular#SplitDelimUnitTest() |
|
| 401 |
+ let assignment = '[|&+*/%<>=!~-]\@<!\([<>!=]=\|=\~\)\@![|&+*/%<>=!~-]*=' |
|
| 402 |
+ let two_spaces = ' ' |
|
| 403 |
+ let ternary_operator = '^.\{-}\zs?\|:'
|
|
| 404 |
+ let cpp_io = '<<\|>>' |
|
| 405 |
+ let pascal_assign = ':=' |
|
| 406 |
+ let trailing_c_comments = '\/\*\|\*\/\|\/\/' |
|
| 407 |
+ |
|
| 408 |
+ call s:SplitDelimTest('a+=b', assignment, ['a', '+=', 'b'])
|
|
| 409 |
+ call s:SplitDelimTest('a-=b', assignment, ['a', '-=', 'b'])
|
|
| 410 |
+ call s:SplitDelimTest('a!=b', assignment, ['a!=b'])
|
|
| 411 |
+ call s:SplitDelimTest('a==b', assignment, ['a==b'])
|
|
| 412 |
+ call s:SplitDelimTest('a&=b', assignment, ['a', '&=', 'b'])
|
|
| 413 |
+ call s:SplitDelimTest('a|=b', assignment, ['a', '|=', 'b'])
|
|
| 414 |
+ call s:SplitDelimTest('a=b=c', assignment, ['a', '=', 'b', '=', 'c'])
|
|
| 415 |
+ |
|
| 416 |
+ call s:SplitDelimTest('a b c', two_spaces, ['a', ' ', 'b', ' ', 'c'])
|
|
| 417 |
+ call s:SplitDelimTest('a b c', two_spaces, ['a b', ' ', ' c'])
|
|
| 418 |
+ call s:SplitDelimTest('ab c', two_spaces, ['ab', ' ', '', ' ', 'c'])
|
|
| 419 |
+ |
|
| 420 |
+ call s:SplitDelimTest('a?b:c', ternary_operator, ['a', '?', 'b', ':', 'c'])
|
|
| 421 |
+ |
|
| 422 |
+ call s:SplitDelimTest('a<<b<<c', cpp_io, ['a', '<<', 'b', '<<', 'c'])
|
|
| 423 |
+ |
|
| 424 |
+ call s:SplitDelimTest('a:=b=c', pascal_assign, ['a', ':=', 'b=c'])
|
|
| 425 |
+ |
|
| 426 |
+ call s:SplitDelimTest('x//foo', trailing_c_comments, ['x', '//', 'foo'])
|
|
| 427 |
+ call s:SplitDelimTest('x/*foo*/',trailing_c_comments, ['x', '/*', 'foo', '*/', ''])
|
|
| 428 |
+ |
|
| 429 |
+ call s:SplitDelimTest('#ab#cd#ef', '[^#]*', ['#', 'ab', '#', 'cd', '#', 'ef', ''])
|
|
| 430 |
+ call s:SplitDelimTest('#ab#cd#ef', '#\zs', ['#', '', 'ab#', '', 'cd#', '', 'ef'])
|
|
| 431 |
+endfunction |
|
| 432 |
+ |
|
| 433 |
+" Stupid vimscript crap, part 2 {{{1
|
|
| 434 |
+let &cpo = s:savecpo |
|
| 435 |
+unlet s:savecpo |
|
| 436 |
+ |
|
| 437 |
+" vim:set sw=2 sts=2 fdm=marker: |
| ... | ... |
@@ -0,0 +1,260 @@ |
| 1 |
+*Tabular.txt* Configurable, flexible, intuitive text aligning |
|
| 2 |
+ |
|
| 3 |
+ *tabular* *tabular.vim* |
|
| 4 |
+ |
|
| 5 |
+ #|#|#|#|#| #| #| ~ |
|
| 6 |
+ #| #|#|#| #|#|#| #| #| #| #|#|#| #| #|#| ~ |
|
| 7 |
+ #| #| #| #| #| #| #| #| #| #| #|#| ~ |
|
| 8 |
+ #| #| #| #| #| #| #| #| #| #| #| ~ |
|
| 9 |
+ #| #|#|#| #|#|#| #|#|#| #| #|#|#| #| ~ |
|
| 10 |
+ |
|
| 11 |
+ For Vim version 7.0 or newer |
|
| 12 |
+ |
|
| 13 |
+ By Matt Wozniski |
|
| 14 |
+ mjw@drexel.edu |
|
| 15 |
+ |
|
| 16 |
+ Reference Manual ~ |
|
| 17 |
+ |
|
| 18 |
+ *tabular-toc* |
|
| 19 |
+ |
|
| 20 |
+1. Description |tabular-intro| |
|
| 21 |
+2. Walkthrough |tabular-walkthrough| |
|
| 22 |
+3. Scripting |tabular-scripting| |
|
| 23 |
+ |
|
| 24 |
+The functionality mentioned here is a plugin, see |add-plugin|. |
|
| 25 |
+You can avoid loading this plugin by setting the "Tabular_loaded" global |
|
| 26 |
+variable in your |vimrc| file: > |
|
| 27 |
+ :let g:tabular_loaded = 1 |
|
| 28 |
+ |
|
| 29 |
+============================================================================== |
|
| 30 |
+1. Description *tabular-intro* |
|
| 31 |
+ |
|
| 32 |
+Sometimes, it's useful to line up text. Naturally, it's nicer to have the |
|
| 33 |
+computer do this for you, since aligning things by hand quickly becomes |
|
| 34 |
+unpleasant. While there are other plugins for aligning text, the ones I've |
|
| 35 |
+tried are either impossibly difficult to understand and use, or too simplistic |
|
| 36 |
+to handle complicated tasks. This plugin aims to make the easy things easy |
|
| 37 |
+and the hard things possible, without providing an unnecessarily obtuse |
|
| 38 |
+interface. It's still a work in progress, and criticisms are welcome. |
|
| 39 |
+ |
|
| 40 |
+============================================================================== |
|
| 41 |
+2. Walkthrough *tabular-walkthrough* *:Tabularize* |
|
| 42 |
+ |
|
| 43 |
+Tabular's commands are based largely on regular expressions. The basic |
|
| 44 |
+technique used by Tabular is taking some regex to match field delimiters, |
|
| 45 |
+splitting the input lines at those delimiters, trimming unnecessary spaces |
|
| 46 |
+from the non-delimiter parts, padding the non-delimiter parts of the lines |
|
| 47 |
+with spaces to make them the same length, and joining things back together |
|
| 48 |
+again. |
|
| 49 |
+ |
|
| 50 |
+For instance, consider starting with the following lines: |
|
| 51 |
+> |
|
| 52 |
+ Some short phrase,some other phrase |
|
| 53 |
+ A much longer phrase here,and another long phrase |
|
| 54 |
+< |
|
| 55 |
+Let's say we want to line these lines up at the commas. We can tell |
|
| 56 |
+Tabularize to do this by passing a pattern matching , to the Tabularize |
|
| 57 |
+command: |
|
| 58 |
+> |
|
| 59 |
+ :Tabularize /, |
|
| 60 |
+ |
|
| 61 |
+ Some short phrase , some other phrase |
|
| 62 |
+ A much longer phrase here , and another long phrase |
|
| 63 |
+< |
|
| 64 |
+I encourage you to try copying those lines to another buffer and trying to |
|
| 65 |
+call :Tabularize. You'll want to take notice of two things quickly: First, |
|
| 66 |
+instead of requiring a range, Tabularize tries to figure out what you want to |
|
| 67 |
+happen. Since it knows that you want to act on lines matching a comma, it |
|
| 68 |
+will look upwards and downwards for lines around the current line that match a |
|
| 69 |
+comma, and consider all contiguous lines matching the pattern to be the range |
|
| 70 |
+to be acted upon. You can always override this by specifying a range, though. |
|
| 71 |
+ |
|
| 72 |
+The second thing you should notice is that you'll almost certainly be able to |
|
| 73 |
+abbreviate :Tabularize to :Tab - using this form in mappings and scripts is |
|
| 74 |
+discouraged as it will make conflicts with other scripts more likely, but for |
|
| 75 |
+interactive use it's a nice timesaver. Another convenience feature is that |
|
| 76 |
+running :Tabularize without providing a new pattern will cause it to reuse the |
|
| 77 |
+last pattern it was called with. |
|
| 78 |
+ |
|
| 79 |
+So, anyway, now the commas line up. Splitting the lines on commas, Tabular |
|
| 80 |
+realized that 'Some short phrase' would need to be padded with spaces to match |
|
| 81 |
+the length of 'A much longer phrase here', and it did that before joining the |
|
| 82 |
+lines back together. You'll also notice that, in addition to the spaces |
|
| 83 |
+inserting for padding, extra spaces were inserted between fields. That's |
|
| 84 |
+because by default, Tabular prints things left-aligned with one space between |
|
| 85 |
+fields. If you wanted to print things right-aligned with no spaces between |
|
| 86 |
+fields, you would provide a different format to the Tabularize command: |
|
| 87 |
+> |
|
| 88 |
+ :Tabularize /,/r0 |
|
| 89 |
+ |
|
| 90 |
+ Some short phrase, some other phrase |
|
| 91 |
+ A much longer phrase here,and another long phrase |
|
| 92 |
+< |
|
| 93 |
+A format specifier is either l, r, or c, followed by one or more digits. If |
|
| 94 |
+the letter is l, the field will be left aligned, similarly for r and right |
|
| 95 |
+aligning and c and center aligning. The number following the letter is the |
|
| 96 |
+number of spaces padding to insert before the start of the next field. |
|
| 97 |
+Multiple format specifiers can be added to the same command - each field will |
|
| 98 |
+be printed with the next format specifier in the list; when they all have been |
|
| 99 |
+used the first will be used again, and so on. So, the last command right |
|
| 100 |
+aligned every field, then inserted 0 spaces of padding before the next field. |
|
| 101 |
+What if we wanted to right align the text before the comma, and left align the |
|
| 102 |
+text after the comma? The command would look like this: |
|
| 103 |
+> |
|
| 104 |
+ :Tabularize /,/r1c1l0 |
|
| 105 |
+ |
|
| 106 |
+ Some short phrase , some other phrase |
|
| 107 |
+ A much longer phrase here , and another long phrase |
|
| 108 |
+< |
|
| 109 |
+That command would be read as "Align the matching text, splitting fields on |
|
| 110 |
+commas. Print everything before the first comma right aligned, then 1 space, |
|
| 111 |
+then the comma center aligned, then 1 space, then everything after the comma |
|
| 112 |
+left aligned." Notice that the alignment of the field the comma is in is |
|
| 113 |
+irrelevant - since it's only 1 cell wide, it looks the same whether it's right, |
|
| 114 |
+left, or center aligned. Also notice that the 0 padding spaces specified for |
|
| 115 |
+the 3rd field are unused - but they would be used if there were enough fields |
|
| 116 |
+to require looping through the fields again. For instance: |
|
| 117 |
+> |
|
| 118 |
+ abc,def,ghi |
|
| 119 |
+ a,b |
|
| 120 |
+ a,b,c |
|
| 121 |
+ |
|
| 122 |
+ :Tabularize /,/r1c1l0 |
|
| 123 |
+ |
|
| 124 |
+ abc , def, ghi |
|
| 125 |
+ a , b |
|
| 126 |
+ a , b , c |
|
| 127 |
+< |
|
| 128 |
+Notice that now, the format pattern has been reused; field 4 (the second comma) |
|
| 129 |
+is right aligned, field 5 is center aligned. No spaces were inserted between |
|
| 130 |
+the 3rd field (containing "def") and the 4th field (the second comma) because |
|
| 131 |
+the format specified 'l0'. |
|
| 132 |
+ |
|
| 133 |
+But, what if you only wanted to act on the first comma on the line, rather than |
|
| 134 |
+all of the commas on the line? Let's say we want everything before the first |
|
| 135 |
+comma right aligned, then the comma, then everything after the comma left |
|
| 136 |
+aligned: |
|
| 137 |
+> |
|
| 138 |
+ abc,def,ghi |
|
| 139 |
+ a,b |
|
| 140 |
+ a,b,c |
|
| 141 |
+ |
|
| 142 |
+ :Tabularize /^[^,]*\zs,/r0c0l0 |
|
| 143 |
+ |
|
| 144 |
+ abc,def,ghi |
|
| 145 |
+ a,b |
|
| 146 |
+ a,b,c |
|
| 147 |
+< |
|
| 148 |
+Here, we used a Vim regex that would only match the first comma on the line. |
|
| 149 |
+It matches the beginning of the line, followed by all the non-comma characters |
|
| 150 |
+up to the first comma, and then forgets about what it matched so far and |
|
| 151 |
+pretends that the match starts exactly at the comma. |
|
| 152 |
+ |
|
| 153 |
+But, now that this command does exactly what we want it to, it's become pretty |
|
| 154 |
+unwieldy. It would be unpleasant to need to type that more than once or |
|
| 155 |
+twice. The solution is to assign a name to it. |
|
| 156 |
+> |
|
| 157 |
+ :AddTabularPattern first_comma /^[^,]*\zs,/r0c0l0 |
|
| 158 |
+< |
|
| 159 |
+Now, typing ":Tabularize first_comma" will do the same thing as typing the |
|
| 160 |
+whole pattern out each time. Of course this is more useful if you store the |
|
| 161 |
+name in a file to be used later. |
|
| 162 |
+ |
|
| 163 |
+NOTE: In order to make these new commands available every time vim starts, |
|
| 164 |
+you'll need to put those new commands into a .vim file in a plugin directory |
|
| 165 |
+somewhere in your 'runtimepath'. In order to make sure that Tabular.vim has |
|
| 166 |
+already been loaded before your file tries to use :AddTabularPattern or |
|
| 167 |
+:AddTabularPipeline, the new file should be installed in an after/plugin |
|
| 168 |
+directory in 'runtimepath'. In general, it will be safe to find out where the |
|
| 169 |
+TabularMaps.vim plugin was installed, and place other files extending |
|
| 170 |
+Tabular.vim in the same directory as TabularMaps.vim. For more information, |
|
| 171 |
+and some suggested best practices, check out the |tabular-scripting| section. |
|
| 172 |
+ |
|
| 173 |
+Lastly, we'll approach the case where tabular cannot achieve your desired goal |
|
| 174 |
+just by splitting lines apart, trimming whitespace, padding with whitespace, |
|
| 175 |
+and rejoining the lines. As an example, consider the multiple_spaces command |
|
| 176 |
+from TabularMaps.vim. The goal is to split using two or more spaces as a |
|
| 177 |
+field delimiter, and join fields back together, properly lined up, with only |
|
| 178 |
+two spaces between the end of each field and the beginning of the next. |
|
| 179 |
+Unfortunately, Tabular can't do this with only the commands we know so far: |
|
| 180 |
+> |
|
| 181 |
+ :Tabularize / / |
|
| 182 |
+< |
|
| 183 |
+The above function won't work, because it will consider "a b" as 5 fields |
|
| 184 |
+delimited by two pairs of 2 spaces ( 'a', ' ', '', ' ', 'b' ) instead of as |
|
| 185 |
+3 fields delimited by one set of 2 or more spaces ( 'a', ' ', 'b' ). |
|
| 186 |
+> |
|
| 187 |
+ :Tabularize / \+/ |
|
| 188 |
+< |
|
| 189 |
+The above function won't work either, because it will leave the delimiter as 4 |
|
| 190 |
+spaces when used against "a b", meaning that we would fail at our goal of |
|
| 191 |
+collapsing everything down to two spaces between fields. So, we need a new |
|
| 192 |
+command to get around this: |
|
| 193 |
+> |
|
| 194 |
+ :AddTabularPipeline multiple_spaces / \{2,}/
|
|
| 195 |
+ \ map(a:lines, "substitute(v:val, ' \{2,}', ' ', 'g')")
|
|
| 196 |
+ \ | tabular#TabularizeStrings(a:lines, ' ', 'l0') |
|
| 197 |
+< |
|
| 198 |
+Yeah. I know it looks complicated. Bear with me. I probably will try to add |
|
| 199 |
+in some shortcuts for this syntax, but this verbose will be guaranteed to |
|
| 200 |
+always work. |
|
| 201 |
+ |
|
| 202 |
+You should already recognize the name being assigned. The next thing to |
|
| 203 |
+happen is / \{2,}/ which is a pattern specifying which lines should
|
|
| 204 |
+automatically be included in the range when no range is given. Without this, |
|
| 205 |
+there would be no pattern to use for extending the range. Everything after |
|
| 206 |
+that is a | separated list of expressions to be evaluated. In the context in |
|
| 207 |
+which they will be evaluated, a:lines will be set to a List of Strings |
|
| 208 |
+containing the text of the lines being filtered as they procede through the |
|
| 209 |
+pipeline you've set up. The \ at the start of the lines are just vim's line |
|
| 210 |
+continuation marker; you needn't worry much about them. So, the first |
|
| 211 |
+expression in the pipeline transforms each line by replacing every instance of |
|
| 212 |
+2 or more spaces with exactly two spaces. The second command in the pipeline |
|
| 213 |
+performs the equivalent of ":Tabularize / /l0"; the only difference is that |
|
| 214 |
+it is operating on a List of Strings rather than text in the buffer. At the |
|
| 215 |
+end of the pipeline, the Strings in the modified a:lines (or the return value |
|
| 216 |
+of the last expression in the pipeline, if it returns a List) will replace the |
|
| 217 |
+chosen range. |
|
| 218 |
+ |
|
| 219 |
+============================================================================== |
|
| 220 |
+3. Extending *tabular-scripting* |
|
| 221 |
+ |
|
| 222 |
+As mentioned above, the most important consideration when extending Tabular |
|
| 223 |
+with new maps or commands is that your plugin must be loaded after Tabular.vim |
|
| 224 |
+has finished loading, and only if Tabular.vim has loaded successfully. The |
|
| 225 |
+easiest approach to making sure it loads after Tabular.vim is simply putting |
|
| 226 |
+the new file (we'll call it "tabular_extra.vim" as an example) into an |
|
| 227 |
+"after/plugin/" directory in 'runtimepath', for instance: |
|
| 228 |
+> |
|
| 229 |
+ ~/.vim/after/plugin/tabular_extra.vim |
|
| 230 |
+< |
|
| 231 |
+The default set of mappings, found in "TabularMaps.vim", is installed in |
|
| 232 |
+the after/plugin/ subdirectory of whatever directory Tabular was installed to. |
|
| 233 |
+ |
|
| 234 |
+The other important consideration is making sure that your commands are only |
|
| 235 |
+called if Tabular.vim was actually loaded. The easiest way to do this is by |
|
| 236 |
+checking for the existence of the :Tabularize command at the start of your |
|
| 237 |
+plugin. A short example plugin would look like this: |
|
| 238 |
+> |
|
| 239 |
+ " after/plugin/my_tabular_commands.vim |
|
| 240 |
+ " Provides extra :Tabularize commands |
|
| 241 |
+ |
|
| 242 |
+ if !exists(':Tabularize')
|
|
| 243 |
+ finish " Give up here; the Tabular plugin musn't have been loaded |
|
| 244 |
+ endif |
|
| 245 |
+ |
|
| 246 |
+ " Make line wrapping possible by resetting the 'cpo' option, first saving it |
|
| 247 |
+ let s:save_cpo = &cpo |
|
| 248 |
+ set cpo&vim |
|
| 249 |
+ |
|
| 250 |
+ AddTabularPattern! asterisk /*/l1 |
|
| 251 |
+ |
|
| 252 |
+ AddTabularPipeline! remove_leading_spaces /^ / |
|
| 253 |
+ \ map(a:lines, "substitute(v:val, '^ *', '', '')") |
|
| 254 |
+ |
|
| 255 |
+ " Restore the saved value of 'cpo' |
|
| 256 |
+ let &cpo = s:save_cpo |
|
| 257 |
+ unlet s:save_cpo |
|
| 258 |
+< |
|
| 259 |
+============================================================================== |
|
| 260 |
+vim:tw=78:fo=tcq2:isk=!-~,^*,^\|,^\":ts=8:ft=help:norl: |
| ... | ... |
@@ -0,0 +1,350 @@ |
| 1 |
+" Tabular: Align columnar data using regex-designated column boundaries |
|
| 2 |
+" Maintainer: Matthew Wozniski (godlygeek@gmail.com) |
|
| 3 |
+" Date: Thu, 03 May 2012 20:49:32 -0400 |
|
| 4 |
+" Version: 1.0 |
|
| 5 |
+" |
|
| 6 |
+" Long Description: |
|
| 7 |
+" Sometimes, it's useful to line up text. Naturally, it's nicer to have the |
|
| 8 |
+" computer do this for you, since aligning things by hand quickly becomes |
|
| 9 |
+" unpleasant. While there are other plugins for aligning text, the ones I've |
|
| 10 |
+" tried are either impossibly difficult to understand and use, or too simplistic |
|
| 11 |
+" to handle complicated tasks. This plugin aims to make the easy things easy |
|
| 12 |
+" and the hard things possible, without providing an unnecessarily obtuse |
|
| 13 |
+" interface. It's still a work in progress, and criticisms are welcome. |
|
| 14 |
+" |
|
| 15 |
+" License: |
|
| 16 |
+" Copyright (c) 2012, Matthew J. Wozniski |
|
| 17 |
+" All rights reserved. |
|
| 18 |
+" |
|
| 19 |
+" Redistribution and use in source and binary forms, with or without |
|
| 20 |
+" modification, are permitted provided that the following conditions are met: |
|
| 21 |
+" * Redistributions of source code must retain the above copyright notice, |
|
| 22 |
+" this list of conditions and the following disclaimer. |
|
| 23 |
+" * Redistributions in binary form must reproduce the above copyright |
|
| 24 |
+" notice, this list of conditions and the following disclaimer in the |
|
| 25 |
+" documentation and/or other materials provided with the distribution. |
|
| 26 |
+" * The names of the contributors may not be used to endorse or promote |
|
| 27 |
+" products derived from this software without specific prior written |
|
| 28 |
+" permission. |
|
| 29 |
+" |
|
| 30 |
+" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS |
|
| 31 |
+" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
| 32 |
+" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
|
| 33 |
+" NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
| 34 |
+" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 35 |
+" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
|
| 36 |
+" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
| 37 |
+" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
| 38 |
+" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
|
| 39 |
+" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 40 |
+ |
|
| 41 |
+" Abort if running in vi-compatible mode or the user doesn't want us. |
|
| 42 |
+if &cp || exists('g:tabular_loaded')
|
|
| 43 |
+ if &cp && &verbose |
|
| 44 |
+ echo "Not loading Tabular in compatible mode." |
|
| 45 |
+ endif |
|
| 46 |
+ finish |
|
| 47 |
+endif |
|
| 48 |
+ |
|
| 49 |
+let g:tabular_loaded = 1 |
|
| 50 |
+ |
|
| 51 |
+" Stupid vimscript crap {{{1
|
|
| 52 |
+let s:savecpo = &cpo |
|
| 53 |
+set cpo&vim |
|
| 54 |
+ |
|
| 55 |
+" Private Things {{{1
|
|
| 56 |
+ |
|
| 57 |
+" Dictionary of command name to command |
|
| 58 |
+let s:TabularCommands = {}
|
|
| 59 |
+ |
|
| 60 |
+" Generate tab completion list for :Tabularize {{{2
|
|
| 61 |
+" Return a list of commands that match the command line typed so far. |
|
| 62 |
+" NOTE: Tries to handle commands with spaces in the name, but Vim doesn't seem |
|
| 63 |
+" to handle that terribly well... maybe I should give up on that. |
|
| 64 |
+function! s:CompleteTabularizeCommand(argstart, cmdline, cursorpos) |
|
| 65 |
+ let names = keys(s:TabularCommands) |
|
| 66 |
+ if exists("b:TabularCommands")
|
|
| 67 |
+ let names += keys(b:TabularCommands) |
|
| 68 |
+ endif |
|
| 69 |
+ |
|
| 70 |
+ let cmdstart = substitute(a:cmdline, '^\s*\S\+\s*', '', '') |
|
| 71 |
+ |
|
| 72 |
+ return filter(names, 'v:val =~# ''^\V'' . escape(cmdstart, ''\'')') |
|
| 73 |
+endfunction |
|
| 74 |
+ |
|
| 75 |
+" Choose the proper command map from the given command line {{{2
|
|
| 76 |
+" Returns [ command map, command line with leading <buffer> removed ] |
|
| 77 |
+function! s:ChooseCommandMap(commandline) |
|
| 78 |
+ let map = s:TabularCommands |
|
| 79 |
+ let cmd = a:commandline |
|
| 80 |
+ |
|
| 81 |
+ if cmd =~# '^<buffer>\s\+' |
|
| 82 |
+ if !exists('b:TabularCommands')
|
|
| 83 |
+ let b:TabularCommands = {}
|
|
| 84 |
+ endif |
|
| 85 |
+ let map = b:TabularCommands |
|
| 86 |
+ let cmd = substitute(cmd, '^<buffer>\s\+', '', '') |
|
| 87 |
+ endif |
|
| 88 |
+ |
|
| 89 |
+ return [ map, cmd ] |
|
| 90 |
+endfunction |
|
| 91 |
+ |
|
| 92 |
+" Parse '/pattern/format' into separate pattern and format parts. {{{2
|
|
| 93 |
+" If parsing fails, return [ '', '' ] |
|
| 94 |
+function! s:ParsePattern(string) |
|
| 95 |
+ if a:string[0] != '/' |
|
| 96 |
+ return ['',''] |
|
| 97 |
+ endif |
|
| 98 |
+ |
|
| 99 |
+ let pat = '\\\@<!\%(\\\\\)\{-}\zs/' . tabular#ElementFormatPattern() . '*$'
|
|
| 100 |
+ let format = matchstr(a:string[1:-1], pat) |
|
| 101 |
+ if !empty(format) |
|
| 102 |
+ let format = format[1 : -1] |
|
| 103 |
+ let pattern = a:string[1 : -len(format) - 2] |
|
| 104 |
+ else |
|
| 105 |
+ let pattern = a:string[1 : -1] |
|
| 106 |
+ endif |
|
| 107 |
+ |
|
| 108 |
+ return [pattern, format] |
|
| 109 |
+endfunction |
|
| 110 |
+ |
|
| 111 |
+" Split apart a list of | separated expressions. {{{2
|
|
| 112 |
+function! s:SplitCommands(string) |
|
| 113 |
+ if a:string =~ '^\s*$' |
|
| 114 |
+ return [] |
|
| 115 |
+ endif |
|
| 116 |
+ |
|
| 117 |
+ let end = match(a:string, "[\"'|]") |
|
| 118 |
+ |
|
| 119 |
+ " Loop until we find a delimiting | or end-of-string |
|
| 120 |
+ while end != -1 && (a:string[end] != '|' || a:string[end+1] == '|') |
|
| 121 |
+ if a:string[end] == "'" |
|
| 122 |
+ let end = match(a:string, "'", end+1) + 1 |
|
| 123 |
+ if end == 0 |
|
| 124 |
+ throw "No matching end single quote" |
|
| 125 |
+ endif |
|
| 126 |
+ elseif a:string[end] == '"' |
|
| 127 |
+ " Find a " preceded by an even number of \ (or 0) |
|
| 128 |
+ let pattern = '\%(\\\@<!\%(\\\\\)*\)\@<="' |
|
| 129 |
+ let end = matchend(a:string, pattern, end+1) + 1 |
|
| 130 |
+ if end == 0 |
|
| 131 |
+ throw "No matching end double quote" |
|
| 132 |
+ endif |
|
| 133 |
+ else " Found || |
|
| 134 |
+ let end += 2 |
|
| 135 |
+ endif |
|
| 136 |
+ |
|
| 137 |
+ let end = match(a:string, "[\"'|]", end) |
|
| 138 |
+ endwhile |
|
| 139 |
+ |
|
| 140 |
+ if end == 0 || a:string[0 : end - (end > 0)] =~ '^\s*$' |
|
| 141 |
+ throw "Empty element" |
|
| 142 |
+ endif |
|
| 143 |
+ |
|
| 144 |
+ if end == -1 |
|
| 145 |
+ let rv = [ a:string ] |
|
| 146 |
+ else |
|
| 147 |
+ let rv = [ a:string[0 : end-1] ] + s:SplitCommands(a:string[end+1 : -1]) |
|
| 148 |
+ endif |
|
| 149 |
+ |
|
| 150 |
+ return rv |
|
| 151 |
+endfunction |
|
| 152 |
+ |
|
| 153 |
+" Public Things {{{1
|
|
| 154 |
+ |
|
| 155 |
+" Command associating a command name with a simple pattern command {{{2
|
|
| 156 |
+" AddTabularPattern[!] [<buffer>] name /pattern[/format] |
|
| 157 |
+" |
|
| 158 |
+" If <buffer> is provided, the command will only be available in the current |
|
| 159 |
+" buffer, and will be used instead of any global command with the same name. |
|
| 160 |
+" |
|
| 161 |
+" If a command with the same name and scope already exists, it is an error, |
|
| 162 |
+" unless the ! is provided, in which case the existing command will be |
|
| 163 |
+" replaced. |
|
| 164 |
+" |
|
| 165 |
+" pattern is a regex describing the delimiter to be used. |
|
| 166 |
+" |
|
| 167 |
+" format describes the format pattern to be used. The default will be used if |
|
| 168 |
+" none is provided. |
|
| 169 |
+com! -nargs=+ -bang AddTabularPattern |
|
| 170 |
+ \ call AddTabularPattern(<q-args>, <bang>0) |
|
| 171 |
+ |
|
| 172 |
+function! AddTabularPattern(command, force) |
|
| 173 |
+ try |
|
| 174 |
+ let [ commandmap, rest ] = s:ChooseCommandMap(a:command) |
|
| 175 |
+ |
|
| 176 |
+ let name = matchstr(rest, '.\{-}\ze\s*/')
|
|
| 177 |
+ let pattern = substitute(rest, '.\{-}\s*\ze/', '', '')
|
|
| 178 |
+ |
|
| 179 |
+ let [ pattern, format ] = s:ParsePattern(pattern) |
|
| 180 |
+ |
|
| 181 |
+ if empty(name) || empty(pattern) |
|
| 182 |
+ throw "Invalid arguments!" |
|
| 183 |
+ endif |
|
| 184 |
+ |
|
| 185 |
+ if !a:force && has_key(commandmap, name) |
|
| 186 |
+ throw string(name) . " is already defined, use ! to overwrite." |
|
| 187 |
+ endif |
|
| 188 |
+ |
|
| 189 |
+ let command = "tabular#TabularizeStrings(a:lines, " . string(pattern) |
|
| 190 |
+ |
|
| 191 |
+ if !empty(format) |
|
| 192 |
+ let command .= ", " . string(format) |
|
| 193 |
+ endif |
|
| 194 |
+ |
|
| 195 |
+ let command .= ")" |
|
| 196 |
+ |
|
| 197 |
+ let commandmap[name] = { 'pattern' : pattern, 'commands' : [ command ] }
|
|
| 198 |
+ catch |
|
| 199 |
+ echohl ErrorMsg |
|
| 200 |
+ echomsg "AddTabularPattern: " . v:exception |
|
| 201 |
+ echohl None |
|
| 202 |
+ endtry |
|
| 203 |
+endfunction |
|
| 204 |
+ |
|
| 205 |
+" Command associating a command name with a pipeline of functions {{{2
|
|
| 206 |
+" AddTabularPipeline[!] [<buffer>] name /pattern/ func [ | func2 [ | func3 ] ] |
|
| 207 |
+" |
|
| 208 |
+" If <buffer> is provided, the command will only be available in the current |
|
| 209 |
+" buffer, and will be used instead of any global command with the same name. |
|
| 210 |
+" |
|
| 211 |
+" If a command with the same name and scope already exists, it is an error, |
|
| 212 |
+" unless the ! is provided, in which case the existing command will be |
|
| 213 |
+" replaced. |
|
| 214 |
+" |
|
| 215 |
+" pattern is a regex that will be used to determine which lines will be |
|
| 216 |
+" filtered. If the cursor line doesn't match the pattern, using the command |
|
| 217 |
+" will be a no-op, otherwise the cursor and all contiguous lines matching the |
|
| 218 |
+" pattern will be filtered. |
|
| 219 |
+" |
|
| 220 |
+" Each 'func' argument represents a function to be called. This function |
|
| 221 |
+" will have access to a:lines, a List containing one String per line being |
|
| 222 |
+" filtered. |
|
| 223 |
+com! -nargs=+ -bang AddTabularPipeline |
|
| 224 |
+ \ call AddTabularPipeline(<q-args>, <bang>0) |
|
| 225 |
+ |
|
| 226 |
+function! AddTabularPipeline(command, force) |
|
| 227 |
+ try |
|
| 228 |
+ let [ commandmap, rest ] = s:ChooseCommandMap(a:command) |
|
| 229 |
+ |
|
| 230 |
+ let name = matchstr(rest, '.\{-}\ze\s*/')
|
|
| 231 |
+ let pattern = substitute(rest, '.\{-}\s*\ze/', '', '')
|
|
| 232 |
+ |
|
| 233 |
+ let commands = matchstr(pattern, '^/.\{-}\\\@<!\%(\\\\\)\{-}/\zs.*')
|
|
| 234 |
+ let pattern = matchstr(pattern, '/\zs.\{-}\\\@<!\%(\\\\\)\{-}\ze/')
|
|
| 235 |
+ |
|
| 236 |
+ if empty(name) || empty(pattern) |
|
| 237 |
+ throw "Invalid arguments!" |
|
| 238 |
+ endif |
|
| 239 |
+ |
|
| 240 |
+ if !a:force && has_key(commandmap, name) |
|
| 241 |
+ throw string(name) . " is already defined, use ! to overwrite." |
|
| 242 |
+ endif |
|
| 243 |
+ |
|
| 244 |
+ let commandlist = s:SplitCommands(commands) |
|
| 245 |
+ |
|
| 246 |
+ if empty(commandlist) |
|
| 247 |
+ throw "Must provide a list of functions!" |
|
| 248 |
+ endif |
|
| 249 |
+ |
|
| 250 |
+ let commandmap[name] = { 'pattern' : pattern, 'commands' : commandlist }
|
|
| 251 |
+ catch |
|
| 252 |
+ echohl ErrorMsg |
|
| 253 |
+ echomsg "AddTabularPipeline: " . v:exception |
|
| 254 |
+ echohl None |
|
| 255 |
+ endtry |
|
| 256 |
+endfunction |
|
| 257 |
+ |
|
| 258 |
+" Tabularize /pattern[/format] {{{2
|
|
| 259 |
+" Tabularize name |
|
| 260 |
+" |
|
| 261 |
+" Align text, either using the given pattern, or the command associated with |
|
| 262 |
+" the given name. |
|
| 263 |
+com! -nargs=* -range -complete=customlist,<SID>CompleteTabularizeCommand |
|
| 264 |
+ \ Tabularize <line1>,<line2>call Tabularize(<q-args>) |
|
| 265 |
+ |
|
| 266 |
+function! Tabularize(command, ...) range |
|
| 267 |
+ let piperange_opt = {}
|
|
| 268 |
+ if a:0 |
|
| 269 |
+ let piperange_opt = a:1 |
|
| 270 |
+ endif |
|
| 271 |
+ |
|
| 272 |
+ if empty(a:command) |
|
| 273 |
+ if !exists("s:last_tabularize_command")
|
|
| 274 |
+ echohl ErrorMsg |
|
| 275 |
+ echomsg "Tabularize hasn't been called yet; no pattern/command to reuse!" |
|
| 276 |
+ echohl None |
|
| 277 |
+ return |
|
| 278 |
+ endif |
|
| 279 |
+ else |
|
| 280 |
+ let s:last_tabularize_command = a:command |
|
| 281 |
+ endif |
|
| 282 |
+ |
|
| 283 |
+ let command = s:last_tabularize_command |
|
| 284 |
+ |
|
| 285 |
+ let range = a:firstline . ',' . a:lastline |
|
| 286 |
+ |
|
| 287 |
+ try |
|
| 288 |
+ let [ pattern, format ] = s:ParsePattern(command) |
|
| 289 |
+ |
|
| 290 |
+ if !empty(pattern) |
|
| 291 |
+ let cmd = "tabular#TabularizeStrings(a:lines, " . string(pattern) |
|
| 292 |
+ |
|
| 293 |
+ if !empty(format) |
|
| 294 |
+ let cmd .= "," . string(format) |
|
| 295 |
+ endif |
|
| 296 |
+ |
|
| 297 |
+ let cmd .= ")" |
|
| 298 |
+ |
|
| 299 |
+ exe range . 'call tabular#PipeRangeWithOptions(pattern, [ cmd ], ' |
|
| 300 |
+ \ . 'piperange_opt)' |
|
| 301 |
+ else |
|
| 302 |
+ if exists('b:TabularCommands') && has_key(b:TabularCommands, command)
|
|
| 303 |
+ let usercmd = b:TabularCommands[command] |
|
| 304 |
+ elseif has_key(s:TabularCommands, command) |
|
| 305 |
+ let usercmd = s:TabularCommands[command] |
|
| 306 |
+ else |
|
| 307 |
+ throw "Unrecognized command " . string(command) |
|
| 308 |
+ endif |
|
| 309 |
+ |
|
| 310 |
+ exe range . 'call tabular#PipeRangeWithOptions(usercmd["pattern"], ' |
|
| 311 |
+ \ . 'usercmd["commands"], piperange_opt)' |
|
| 312 |
+ endif |
|
| 313 |
+ catch |
|
| 314 |
+ echohl ErrorMsg |
|
| 315 |
+ echomsg "Tabularize: " . v:exception |
|
| 316 |
+ echohl None |
|
| 317 |
+ return |
|
| 318 |
+ endtry |
|
| 319 |
+endfunction |
|
| 320 |
+ |
|
| 321 |
+function! TabularizeHasPattern() |
|
| 322 |
+ return exists("s:last_tabularize_command")
|
|
| 323 |
+endfunction |
|
| 324 |
+ |
|
| 325 |
+" GTabularize /pattern[/format] {{{2
|
|
| 326 |
+" GTabularize name |
|
| 327 |
+" |
|
| 328 |
+" Align text on only matching lines, either using the given pattern, or the |
|
| 329 |
+" command associated with the given name. Mnemonically, this is similar to |
|
| 330 |
+" the :global command, which takes some action on all rows matching a pattern |
|
| 331 |
+" in a range. This command is different from normal :Tabularize in 3 ways: |
|
| 332 |
+" 1) If a line in the range does not match the pattern, it will be left |
|
| 333 |
+" unchanged, and not in any way affect the outcome of other lines in the |
|
| 334 |
+" range (at least, normally - but Pipelines can and will still look at |
|
| 335 |
+" non-matching rows unless they are specifically written to be aware of |
|
| 336 |
+" tabular#DoGTabularize() and handle it appropriately). |
|
| 337 |
+" 2) No automatic range determination - :Tabularize automatically expands |
|
| 338 |
+" a single-line range (or a call with no range) to include all adjacent |
|
| 339 |
+" matching lines. That behavior does not make sense for this command. |
|
| 340 |
+" 3) If called without a range, it will act on all lines in the buffer (like |
|
| 341 |
+" :global) rather than only a single line |
|
| 342 |
+com! -nargs=* -range=% -complete=customlist,<SID>CompleteTabularizeCommand |
|
| 343 |
+ \ GTabularize <line1>,<line2> |
|
| 344 |
+ \ call Tabularize(<q-args>, { 'mode': 'GTabularize' } )
|
|
| 345 |
+ |
|
| 346 |
+" Stupid vimscript crap, part 2 {{{1
|
|
| 347 |
+let &cpo = s:savecpo |
|
| 348 |
+unlet s:savecpo |
|
| 349 |
+ |
|
| 350 |
+" vim:set sw=2 sts=2 fdm=marker: |
| ... | ... |
@@ -63,7 +63,8 @@ See [config.dlma.com](http://config.dlma.com) for more. |
| 63 | 63 |
4. [visual-star-search](http://got-ravings.blogspot.com/2008/07/vim-pr0n-visual-search-mappings.html), so * and # work in visual mode too. |
| 64 | 64 |
5. [git-tab](https://github.com/dblume/gittab), use integrated context-sensitive git commands |
| 65 | 65 |
6. [rainbow](https://github.com/luochen1990/rainbow), for matching colored parentheses. |
| 66 |
- 7. Assorted favorite colors like [desert](https://github.com/dblume/desert.vim). |
|
| 66 |
+ 7. [tabular](https://github.com/godlygeek/tabular), for when aligning tables. |
|
| 67 |
+ 8. Assorted favorite colors like [desert](https://github.com/dblume/desert.vim). |
|
| 67 | 68 |
3. Neovim resources |
| 68 | 69 |
1. .config/nvim/init.vim |
| 69 | 70 |
2. .config/nvim/colors/nvim\_desert.vim |
| 70 | 71 |