From 90a5487974c457de34faf02d13a6131242dd0dc4 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 16 Aug 2013 00:15:57 +0900 Subject: [PATCH] Add `indentation` option --- README.md | 62 +++++++++++++++++++----- autoload/easy_align.vim | 45 ++++++++++++++---- doc/easy_align.txt | 76 +++++++++++++++++++++++------- test/basic.expected | 102 ++++++++++++++++++++++++++++++++++++++++ test/basic.md | 6 +++ test/basic.script | 2 +- 6 files changed, 254 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 3e17432..bf99815 100644 --- a/README.md +++ b/README.md @@ -187,16 +187,17 @@ lowest precedence. ### List of options -| Option | Type | Default | Description | -| ------------------ | ----------------- | ----------------------- | ------------------------------------------------------- | -| `left_margin` | number | 0 | Number of spaces to attach before delimiter | -| `left_margin` | string | `''` | String to attach before delimiter | -| `right_margin` | number | 0 | Number of spaces to attach after delimiter | -| `right_margin` | string | `''` | String to attach after delimiter | -| `stick_to_left` | boolean | 0 | Whether to position delimiter on the left-side | -| `ignore_unmatched` | boolean | 1 | Whether to ignore lines without matching delimiter | -| `ignores` | array | `['String', 'Comment']` | Delimiters in these syntax highlight groups are ignored | -| `delimiter_align` | string | `r` | Determines how to align delimiters of different lengths | +| Option | Type | Default | Description | +| ------------------ | ----------------- | --------------------- | ------------------------------------------------------- | +| `left_margin` | number | 0 | Number of spaces to attach before delimiter | +| `left_margin` | string | `''` | String to attach before delimiter | +| `right_margin` | number | 0 | Number of spaces to attach after delimiter | +| `right_margin` | string | `''` | String to attach after delimiter | +| `stick_to_left` | boolean | 0 | Whether to position delimiter on the left-side | +| `ignore_unmatched` | boolean | 1 | Whether to ignore lines without matching delimiter | +| `ignores` | list | ['String', 'Comment'] | Delimiters in these syntax highlight groups are ignored | +| `indentation` | string | `k` | Indentation method (*k*eep, *d*eep, *s*hallow) | +| `delimiter_align` | string | `r` | Determines how to align delimiters of different lengths | Some of the options can be specified using corresponding global variables. @@ -352,6 +353,45 @@ banana += apple cake ||= banana ``` +### Adjusting indentation + +By default :EasyAlign command keeps the original indentation of the lines. But +then again we have `indentation` option. See the following example. + +```ruby + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + # Default: _k_eep the original indentation + # :EasyAlign= + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + # Use the _s_hallowest indentation among the lines + # :EasyAlign={'idt':s} + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + # Use the _d_eepest indentation among the lines + # :EasyAlign={'idt':d} + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 +``` + +Notice that `idt` is fuzzy-matched to `indentation`. + ### Extending alignment rules Although the default rules should cover the most of the use cases, @@ -365,7 +405,7 @@ let g:easy_align_delimiters = { \ '/': { 'pattern': '//\+\|/\*\|\*/', 'ignores': ['String'] }, \ '#': { 'pattern': '#\+', 'ignores': ['String'], 'delimiter_align': 'l' }, \ ']': { -\ 'pattern': '[\[\]]', +\ 'pattern': '[[\]]', \ 'left_margin': 0, \ 'right_margin': 0, \ 'stick_to_left': 0 diff --git a/autoload/easy_align.vim b/autoload/easy_align.vim index 6ccdc51..76b3372 100644 --- a/autoload/easy_align.vim +++ b/autoload/easy_align.vim @@ -42,9 +42,9 @@ let s:easy_align_delimiters_default = { let s:just = ['', '[R]'] let s:known_options = { -\ 'margin_left': [0, 1], 'margin_right': [0, 1], 'stick_to_left': [0], -\ 'left_margin': [0, 1], 'right_margin': [0, 1], -\ 'ignores': [3], 'ignore_unmatched': [0], 'delimiter_align': [1] +\ 'margin_left': [0, 1], 'margin_right': [0, 1], 'stick_to_left': [0], +\ 'left_margin': [0, 1], 'right_margin': [0, 1], 'indentation': [1], +\ 'ignores': [3 ], 'ignore_unmatched': [0 ], 'delimiter_align': [1] \ } if exists("*strwidth") @@ -200,12 +200,14 @@ function! s:split_line(line, fc, lc, pattern, stick_to_left, ignore_unmatched, i return [tokens, delims] endfunction -function! s:do_align(just, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth, ml, mr, da, stick_to_left, ignore_unmatched, ignores, recursive) +function! s:do_align(just, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth, + \ ml, mr, da, indentation, stick_to_left, ignore_unmatched, ignores, recursive) let lines = {} let max_just_len = 0 let max_delim_len = 0 let max_tokens = 0 let min_indent = -1 + let max_indent = 0 " Phase 1 for line in range(a:fl, a:ll) @@ -257,11 +259,35 @@ function! s:do_align(just, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth, if min_indent < 0 || indent < min_indent let min_indent = indent endif + let max_indent = max([indent, max_indent]) let max_just_len = max([s:strwidth(prefix.token), max_just_len]) let max_delim_len = max([s:strwidth(delim), max_delim_len]) let lines[line] = [nth, prefix, token, delim] endfor + " Phase 1-5: indentation handling (only on a:nth == 1) + if a:nth == 1 + if a:indentation ==? 'd' + let indent = repeat(' ', max_indent) + elseif a:indentation ==? 's' + let indent = repeat(' ', min_indent) + elseif a:indentation !=? 'k' + call s:exit('Invalid indentation: ' . a:indentation) + end + + if a:indentation !=? 'k' + let max_just_len = 0 + for [line, elems] in items(lines) + let [nth, prefix, token, delim] = elems + + let token = substitute(token, '^\s*', indent, '') + let max_just_len = max([max_just_len, s:strwidth(token)]) + + let lines[line][2] = token + endfor + endif + endif + " Phase 2 for [line, elems] in items(lines) let tokens = a:all_tokens[line] @@ -314,7 +340,7 @@ function! s:do_align(just, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth, " Adjust indentation of the lines starting with a delimiter let lpad = '' if nth == 0 - let ipad = repeat(' ', min_indent - len(strpart(before.token, '^\s\+').ml)) + let ipad = repeat(' ', min_indent - len(token.ml)) if a:just == 0 let token = ipad . token else @@ -334,8 +360,8 @@ function! s:do_align(just, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth, if a:recursive && a:nth < max_tokens let just = a:recursive == 2 ? !a:just : a:just call s:do_align(just, a:all_tokens, a:all_delims, a:fl, a:ll, a:fc, a:lc, a:pattern, - \ a:nth + 1, a:ml, a:mr, a:da, a:stick_to_left, a:ignore_unmatched, - \ a:ignores, a:recursive) + \ a:nth + 1, a:ml, a:mr, a:da, a:indentation, a:stick_to_left, + \ a:ignore_unmatched, a:ignores, a:recursive) endif endfunction @@ -394,8 +420,8 @@ function! s:parse_args(args) let cand = strpart(args, midx) try - let [l, r, c] = ['l', 'r', 'c'] - let [L, R, C] = ['l', 'r', 'c'] + let [l, r, c, k, s, d] = ['l', 'r', 'c', 'k', 's', 'd'] + let [L, R, C, K, S, D] = ['l', 'r', 'c', 'k', 's', 'd'] let o = eval(cand) if type(o) == 4 let option = o @@ -520,6 +546,7 @@ function! easy_align#align(just, expr) range \ ml, \ mr, \ get(dict, 'delimiter_align', get(g:, 'easy_align_delimiter_align', 'r')), + \ get(dict, 'indentation', 'k'), \ get(dict, 'stick_to_left', 0), \ get(dict, 'ignore_unmatched', get(g:, 'easy_align_ignore_unmatched', 1)), \ get(dict, 'ignores', s:ignored_syntax()), diff --git a/doc/easy_align.txt b/doc/easy_align.txt index fee72ac..76652f8 100644 --- a/doc/easy_align.txt +++ b/doc/easy_align.txt @@ -40,7 +40,7 @@ With this mapping, you can align selected lines of text with a few keystrokes. 2 Around the 2nd occurrences of delimiters * Around all occurrences of delimiters ** Left-right alternating alignment around all delimiters - - Around the last occurrences of delimiters (`-1`) + - Around the last occurrences of delimiters (-1) -2 Around the second to last occurrences of delimiters ... 4. Delimiter key (a single keystroke) @@ -93,7 +93,7 @@ try these commands: - :EasyAlign */[:;]\+/ - :EasyAlign **/[:;]\+/ -Notice that you can't append `\zs` to your regular expression to put delimiters +Notice that you can't append '\zs' to your regular expression to put delimiters on the left. It can be done by providing additional options in Vim dictionary format. @@ -114,20 +114,21 @@ You can even omit spaces between the arguments, so concisely (or cryptically): Available options are as follows. -| Atrribute | Type | Default | -| ---------------- | ---------------- | ----------------------- | -| left_margin | number or string | 0 | -| right_margin | number or string | 0 | -| stick_to_left | boolean | 0 | -| delimiter_align | string | 'r' | -| ignore_unmatched | boolean | 1 | -| ignores | array | `['String', 'Comment']` | +| Atrribute | Type | Default | +| ---------------- | ---------------- | --------------------- | +| left_margin | number or string | 0 | +| right_margin | number or string | 0 | +| stick_to_left | boolean | 0 | +| delimiter_align | string | 'r' | +| ignore_unmatched | boolean | 1 | +| ignores | array | ['String', 'Comment'] | +| indentation | string | 'k' | Partial alignment in blockwise-visual mode ------------------------------------------------------------------------- -In blockwise-visual mode (`CTRL-V`), EasyAlign command aligns only +In blockwise-visual mode (CTRL-V), EasyAlign command aligns only the selected text in the block, instead of the whole lines in the range. @@ -154,7 +155,7 @@ For example, the following paragraph 'grape:fruits': 3 } -becomes as follows on `:` +becomes as follows on ':' { # Quantity of apples: 1 @@ -173,7 +174,7 @@ array, " Ignore nothing! let g:easy_align_ignores = [] -or providing `ignores` option directly to :EasyAlign command +or providing 'ignores' option directly to :EasyAlign command :EasyAlign:{'is':[]} @@ -221,7 +222,7 @@ One way is to set the global `g:easy_align_ignore_unmatched` variable to 0. let g:easy_align_ignore_unmatched = 0 -Or in non-interactive mode, you can provide `ignore_unmatched` option to +Or in non-interactive mode, you can provide 'ignore_unmatched' option to `:EasyAlign` command as follows. :EasyAlign:{'iu':0} @@ -238,12 +239,11 @@ Then we get, } - Aligning delimiters of different lengths *g:easy_align_delimiter_align* ------------------------------------------------------------------------- Global `g:easy_align_delimiter_align` option and rule-wise/command-wise -`delimiter_align` option determines how matched delimiters of different +'delimiter_align' option determines how matched delimiters of different lengths are aligned. apple = 1 @@ -256,18 +256,58 @@ By default, delimiters are right-aligned as follows. banana += apple cake ||= banana -However, with `:EasyAlign={'da':l}`, delimiters are left-aligned. +However, with ':EasyAlign={'da':l}', delimiters are left-aligned. apple = 1 banana += apple cake ||= banana -And on `:EasyAlign={'da':c}`, center-aligned. +And on ':EasyAlign={'da':c}', center-aligned. apple = 1 banana += apple cake ||= banana + +Adjusting indentation +------------------------------------------------------------------------- + +By default :EasyAlign command keeps the original indentation of the lines. +But then again we have 'indentation' option. See the following example. + + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + # Default: _k_eep the original indentation + # :EasyAlign= + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + # Use the _s_hallowest indentation among the lines + # :EasyAlign={'idt':s} + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + # Use the _d_eepest indentation among the lines + # :EasyAlign={'idt':d} + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + +Notice that 'idt' is fuzzy-matched to 'indentation'. + + Extending alignment rules *g:easy_align_delimiters* ------------------------------------------------------------------------- diff --git a/test/basic.expected b/test/basic.expected index 2013dfb..23fdc74 100644 --- a/test/basic.expected +++ b/test/basic.expected @@ -504,3 +504,105 @@ George Harrison 1943 .. -- ...... ... - .....- + apple = 1 = 2 + banana = 2 = 2 + cake = 3 = 2 + daisy = 4 = 2 + eggplant = 5 = 2 + + apple = 1 = 2 + banana = 2 = 2 + cake = 3 = 2 + daisy = 4 = 2 + eggplant = 5 = 2 + + apple = 1 = 2 + banana = 2 = 2 + cake = 3 = 2 + daisy = 4 = 2 + eggplant = 5 = 2 + +1 apple = 1 = 2 +1 banana = 2 = 2 +1 cake = 3 = 2 +1 daisy = 4 = 2 +1 eggplant = 5 = 2 + +apple = 1 +banana = 2 +cake = 3 +daisy = 4 +eggplant = 5 + + a pple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + apple = 1 = 2 + banana = 2 = 2 + cake = 3 = 2 + daisy = 4 = 2 + eggplant = 5 = 2 + + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + diff --git a/test/basic.md b/test/basic.md index 4a48d08..f89393e 100644 --- a/test/basic.md +++ b/test/basic.md @@ -130,3 +130,9 @@ Pete Best 1941 ..--...... ...-.....- + apple = 1 + banana = 2 + cake = 3 + daisy = 4 + eggplant = 5 + diff --git a/test/basic.script b/test/basic.script index 3d3f249..4d58507 100644 --- a/test/basic.script +++ b/test/basic.script @@ -1 +1 @@ -4Gvipjyvip Pvip 2 Pvip * Pvip Pvip 2 Pvip * Pvip ** Pvip - Pvip -2 Pvip -1 Pvip ** 60zzvipjyvip *|Pvip *|Pvip **|80zzvip **|gv 3|vip *|90zzvip *,100zzvipjyvip =Pvip *=Pvip **=Pvip =vip 2=198Gvipjyvip =Pvip -=Pf:jj3E =209Gvip - 214zzvipjyvip #P:let g:easy_align_delimiters = { '#': {'pattern': '#\+', 'ignores': ['String'] } } vip #227zzvip :239zzvip *=vipjyP:let g:easy_align_ignores = [] vip *=:unlet g:easy_align_delimiters :unlet g:easy_align_ignores 4Gvipy4GP7Gojkkvip:EasyAlign /1/{'ml':'{{', 'mr-r':'}}'} vipjyPvip:EasyAlign */../ 263zzvipjygv .Pvip *.Pvip * .Pvip .vip 2.Pvip **.Pvip **.Pvip -.G303zzvip .310zzvipjygv *|Pvip *|Pvip |gv -|gv **|gv *|gv **|jji jjjhi vip ** |339Gpvip:EasyAlign*|{'ml': 5, 'mrr': 0 } 349Gpvip:EasyAlign*/|/{'ml':'<', 'mrr': 4} 362G:let g:easy_align_delimiters = { 'd': { 'pattern': '\s\+\(\S\+\s*[;=]\)\@=', 'left_margin': 0, 'right_margin': 0 } } vip dgv =236GvipjyPvip :377Gvip gv 2 382Gvipjyvip:EasyAlign/-\+/ Pvip:EasyAlign2/-\+/ Pvip:EasyAlign*/-\+/ Pvip:EasyAlign*/-\+/{'da': L} Pvip:EasyAlign/-\+/{'da': c} Pvip:EasyAlign*/-\+/{'delimiter_align':'C'} Pvip:EasyAlign*/-\+/{'da': 'x'} 381Gpvip:EasyAlign/-\+/{'da':r} :let g:easy_align_delimiter_align = 'l' Pvip:EasyAlign/-\+/ Pvip:EasyAlign*/-\+/ :unlet g:easy_align_delimiter_align :let g:easy_align_delimiters['\'] = { 'pattern': '-\+', 'delimiter_align': 'c' } Pvip \Pvip *-vip *\Pvip:EasyAlign *\\ {'da':l} Pvip:EasyAlign*\\ Pvip:EasyAlign*\\{'da': 'R'} 377GvipjyPvip:EasyAlign\ {'l': '<<<'} gv:EasyAlign 2 \ {'l': '{{{'} +4Gvipjyvip Pvip 2 Pvip * Pvip Pvip 2 Pvip * Pvip ** Pvip - Pvip -2 Pvip -1 Pvip ** 60zzvipjyvip *|Pvip *|Pvip **|80zzvip **|gv 3|vip *|90zzvip *,100zzvipjyvip =Pvip *=Pvip **=Pvip =vip 2=198Gvipjyvip =Pvip -=Pf:jj3E =209Gvip - 214zzvipjyvip #P:let g:easy_align_delimiters = { '#': {'pattern': '#\+', 'ignores': ['String'] } } vip #227zzvip :239zzvip *=vipjyP:let g:easy_align_ignores = [] vip *=:unlet g:easy_align_delimiters :unlet g:easy_align_ignores 4Gvipy4GP7Gojkkvip:EasyAlign /1/{'ml':'{{', 'mr-r':'}}'} vipjyPvip:EasyAlign */../ 263zzvipjygv .Pvip *.Pvip * .Pvip .vip 2.Pvip **.Pvip **.Pvip -.G303zzvip .310zzvipjygv *|Pvip *|Pvip |gv -|gv **|gv *|gv **|jji jjjhi vip ** |339Gpvip:EasyAlign*|{'ml': 5, 'mrr': 0 } 349Gpvip:EasyAlign*/|/{'ml':'<', 'mrr': 4} 362G:let g:easy_align_delimiters = { 'd': { 'pattern': '\s\+\(\S\+\s*[;=]\)\@=', 'left_margin': 0, 'right_margin': 0 } } vip dgv =236GvipjyPvip :377Gvip gv 2 382Gvipjyvip:EasyAlign/-\+/ Pvip:EasyAlign2/-\+/ Pvip:EasyAlign*/-\+/ Pvip:EasyAlign*/-\+/{'da': L} Pvip:EasyAlign/-\+/{'da': c} Pvip:EasyAlign*/-\+/{'delimiter_align':'C'} Pvip:EasyAlign*/-\+/{'da': 'x'} 381Gpvip:EasyAlign/-\+/{'da':r} :let g:easy_align_delimiter_align = 'l' Pvip:EasyAlign/-\+/ Pvip:EasyAlign*/-\+/ :unlet g:easy_align_delimiter_align :let g:easy_align_delimiters['\'] = { 'pattern': '-\+', 'delimiter_align': 'c' } Pvip \Pvip *-vip *\Pvip:EasyAlign *\\ {'da':l} Pvip:EasyAlign*\\ Pvip:EasyAlign*\\{'da': 'R'} 377GvipjyPvip:EasyAlign\ {'l': '<<<'} gv:EasyAlign 2 \ {'l': '{{{'} 507Gvipjygv:EasyAlign= Pvip:EasyAlign = {'idt': s} Pvip:EasyAlign= {'idt': d} Pvip:EasyAlign**={'indentation': 'd'} Pvip:EasyAlign!= Pvip:EasyAlign! **= {'indent': s} Pvip:EasyAlign!* = {'idt': D} Pvip:EasyAlign! ={'idt':'S'} 507GPvip:EasyAlign-={'idt':d} PA = 2j.j.j.j.vip:EasyAlign2={'idt': s} 507GPljjjj$:EasyAlign = {'idt': s} Pljjjj$:EasyAlign={'indent':d} Phhxxvip:EasyAlign={'idt':'s'} 525Gvipjy507GPPvip:EasyAlign*={'idt':s} Pvip:EasyAlign!**={'idt':s} Pvip:EasyAlign = {'idt': D} 525G^hhr1jr1jr1jr1jr1llkkkk$:EasyAlign = { 'idt': s }