From 2832a76ceafae9059010f5be490bbf9f61dfc588 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sun, 27 Oct 2013 03:10:04 +0900 Subject: [PATCH] Implement filter option (#16, #17) This commit implements filter option which can be used to filter lines within the range based on the given pattern. The value of filter option should be either `g/pattern/` or `v/pattern/`. The former aligns lines that match the pattern, the latter aligns lines that do not match the pattern. --- README.md | 26 ++++++++++++ autoload/easy_align.vim | 46 +++++++++++++++++---- doc/easy_align.txt | 27 +++++++++++++ test/interactive.vader | 90 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9bf2815..c590699 100644 --- a/README.md +++ b/README.md @@ -314,6 +314,7 @@ Alignment options | Option | Type | Default | Description | | ------------------ | ------- | --------------------- | ------------------------------------------------------- | +| `filter` | string | | Line filtering expression: `g/../` or `v/../` | | `left_margin` | number | 1 | Number of spaces to attach before delimiter | | `left_margin` | string | `' '` | String to attach before delimiter | | `right_margin` | number | 1 | Number of spaces to attach after delimiter | @@ -334,6 +335,7 @@ There are 4 ways to set alignment options (from lowest precedence to highest): | Option name | Shortcut key | Abbreviated | Global variable | | ------------------ | ------------------- | ----------- | ------------------------------- | +| `filter` | `CTRL-F` | `[gv]/.*/` | | | `left_margin` | `CTRL-L` | `l[0-9]+` | | | `right_margin` | `CTRL-R` | `r[0-9]+` | | | `stick_to_left` | ``, `` | `s[01]` | | @@ -343,6 +345,30 @@ There are 4 ways to set alignment options (from lowest precedence to highest): | `delimiter_align` | `CTRL-D` | `d[lrc]` | `g:easy_align_delimiter_align` | | `mode_sequence` | `CTRL-O` | `m[lrc*]*` | | +### Filtering lines + +With `filter` option, you can align lines that only match or do not match a +given pattern. There are several ways to set the pattern. + +1. Press `CTRL-F` in interactive mode and input `g/pat/` or `v/pat/` +2. In command-line, it can be written in dictionary format: `{'filter': 'g/pat/'}` +3. Or in shorthand notation: `g/pat/` or `v/pat/` + +(You don't need to escape '/'s in the regular expression) + +#### Examples + +```vim +" Start interactive mode with filter option set to g/hello/ +EasyAlign g/hello/ + +" Start live interactive mode with filter option set to v/goodbye/ +LiveEasyAlign v/goodbye/ + +" Align the lines with 'hi' around the first colons +EasyAlign:g/hi/ +``` + ### Ignoring delimiters in comments or strings EasyAlign can be configured to ignore delimiters in certain syntax highlight diff --git a/autoload/easy_align.vim b/autoload/easy_align.vim index 61b66a0..114156f 100644 --- a/autoload/easy_align.vim +++ b/autoload/easy_align.vim @@ -47,7 +47,7 @@ let s:known_options = { \ 'margin_left': [0, 1], 'margin_right': [0, 1], 'stick_to_left': [0], \ 'left_margin': [0, 1], 'right_margin': [0, 1], 'indentation': [1], \ 'ignore_groups': [3 ], 'ignore_unmatched': [0 ], 'delimiter_align': [1], -\ 'mode_sequence': [1 ], 'ignores': [3] +\ 'mode_sequence': [1 ], 'ignores': [3], 'filter': [1] \ } let s:option_values = { @@ -61,7 +61,7 @@ let s:shorthand = { \ 'margin_left': 'lm', 'margin_right': 'rm', 'stick_to_left': 'stl', \ 'left_margin': 'lm', 'right_margin': 'rm', 'indentation': 'idt', \ 'ignore_groups': 'ig', 'ignore_unmatched': 'iu', 'delimiter_align': 'da', -\ 'mode_sequence': 'm', 'ignores': 'ig' +\ 'mode_sequence': 'm', 'ignores': 'ig', 'filter': 'f' \ } if exists("*strwidth") @@ -333,9 +333,16 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, nth, r let max = { 'pivot_len': 0.0, 'token_len': 0, 'just_len': 0, 'delim_len': 0, \ 'indent': 0, 'tokens': 0, 'strip_len': 0 } let d = a:dict + let [f, fx] = s:parse_filter(d.filter) " Phase 1 for line in range(a:fl, a:ll) + if f == 1 && getline(line) !~ fx + continue + elseif f == -1 && getline(line) =~ fx + continue + endif + if !has_key(a:all_tokens, line) " Split line into the tokens by the delimiters let [tokens, delims] = s:split_line( @@ -703,6 +710,16 @@ function! s:interactive(range, modes, n, d, opts, rules, vis, live) else let warn = 'Invalid regular expression: '.ch endif + elseif ch == "\" + let f = s:input("Filter (g/../ or v/../): ", get(opts, 'f', ''), a:vis) + let m = matchlist(f, '^[gv]/\(.\{-}\)/\?$') + if empty(f) + silent! call remove(opts, 'f') + elseif !empty(m) && s:valid_regexp(m[1]) + let opts['f'] = f + else + let warn = 'Invalid filter expression' + endif elseif ch =~ '[[:print:]]' let check = 1 else @@ -756,9 +773,9 @@ function! s:test_regexp(regexp) endfunction let s:shorthand_regex = - \ '\s*\(' + \ '\s*\%(' \ .'\(lm\?[0-9]\+\)\|\(rm\?[0-9]\+\)\|\(iu[01]\)\|\(s\%(tl\)\?[01]\)\|' - \ .'\(da\?[clr]\)\|\(ms\?[lrc*]\+\)\|\(i\%(dt\)\?[kdsn]\)\|\(ig\[.*\]\)' + \ .'\(da\?[clr]\)\|\(ms\?[lrc*]\+\)\|\(i\%(dt\)\?[kdsn]\)\|\([gv]/.*/\)\|\(ig\[.*\]\)' \ .'\)\+\s*$' function! s:parse_shorthand_opts(expr) @@ -773,13 +790,17 @@ function! s:parse_shorthand_opts(expr) else let match = matchlist(expr, regex) if empty(match) | break | endif - for m in filter(match[ 2 : -1 ], '!empty(v:val)') - for key in ['lm', 'rm', 'l', 'r', 'stl', 's', 'iu', 'da', 'd', 'ms', 'm', 'ig', 'i'] + for m in filter(match[ 1 : -1 ], '!empty(v:val)') + for key in ['lm', 'rm', 'l', 'r', 'stl', 's', 'iu', 'da', 'd', 'ms', 'm', 'ig', 'i', 'g', 'v'] if stridx(tolower(m), key) == 0 let rest = strpart(m, len(key)) if key == 'i' | let key = 'idt' | endif + if key == 'g' || key == 'v' + let rest = key.rest + let key = 'f' + endif - if key == 'idt' || index(['d', 'm'], key[0]) >= 0 + if key == 'idt' || index(['d', 'f', 'm'], key[0]) >= 0 let opts[key] = rest elseif key == 'ig' try @@ -877,6 +898,15 @@ function! s:parse_args(args) endif endfunction +function! s:parse_filter(f) + let m = matchlist(a:f, '^\([gv]\)/\(.\{-}\)/\?$') + if empty(m) + return [0, ''] + else + return [m[1] == 'g' ? 1 : -1, m[2]] + endif +endfunction + function! s:interactive_modes(bang) return get(g:, \ (a:bang ? 'easy_align_bang_interactive_modes' : 'easy_align_interactive_modes'), @@ -936,6 +966,8 @@ function! s:build_dict(delimiters, ch, regexp, opts) \ get(dict, 'ignore_unmatched', get(g:, 'easy_align_ignore_unmatched', 2)) let dict.ignore_groups = \ get(dict, 'ignore_groups', get(dict, 'ignores', s:ignored_syntax())) + let dict.filter = + \ get(dict, 'filter', '') return dict endfunction diff --git a/doc/easy_align.txt b/doc/easy_align.txt index e8cc732..4c27f25 100644 --- a/doc/easy_align.txt +++ b/doc/easy_align.txt @@ -201,6 +201,7 @@ The following table summarizes the shorthand notation. | Option | Expression | | -------------- | ---------- | +| filter | [gv]/.*/ | | left_margin | l[0-9]+ | | right_margin | r[0-9]+ | | stick_to_left | s[01] | @@ -234,6 +235,7 @@ Available options are as follows. | Atrribute | Type | Default | | ---------------- | ---------------- | ----------------------------- | +| filter | string | | | left_margin | number or string | 1 | | right_margin | number or string | 1 | | stick_to_left | boolean | 0 | @@ -255,6 +257,7 @@ There are 4 ways to set alignment options (from lowest precedence to highest): | Option | Shortcut key | Abbreviated | Global variable | | ---------------- | --------------- | ----------- | ----------------------------- | +| filter | CTRL-F | [gv]/.*/ | | | left_margin | CTRL-L | l[0-9]+ | | | right_margin | CTRL-R | r[0-9]+ | | | stick_to_left | , | s[01] | | @@ -265,6 +268,30 @@ There are 4 ways to set alignment options (from lowest precedence to highest): | mode_sequence | CTRL-O | m[lrc*]+ | | +Filtering lines +------------------------------------------------------------------------- + +With filter option, you can align lines that only match or do not match a +given pattern. There are several ways to set the pattern. + +1. Press CTRL-F in interactive mode and input g/pat/ or v/pat/ +2. In command-line, it can be written in dictionary format: {'filter': 'g/pat/'} +3. Or in shorthand notation: g/pat/ or v/pat/ + +Examples: + + " Start interactive mode with filter option set to g/hello/ + EasyAlign g/hello/ + + " Start live interactive mode with filter option set to v/goodbye/ + LiveEasyAlign v/goodbye/ + + " Align the lines with 'hi' around the first colons + EasyAlign : g/hi/ + +(You don't need to escape '/'s in the regular expression) + + Ignoring delimiters in comments or strings *g:easy_align_ignore_groups* ------------------------------------------------------------------------- diff --git a/test/interactive.vader b/test/interactive.vader index 76d666f..5fb27ef 100644 --- a/test/interactive.vader +++ b/test/interactive.vader @@ -1343,5 +1343,95 @@ Expect: a = b = c aabba = bbbbb +########################################################### + +Given (test filter option): + aaa=aaa=aaa + aaaaa=aaaaa=aaaaa + aaaaaaa=aaaaaaa=aaaaaaab + bbbbb=bbbbb=bbbbb + aaa=aaa=aaa + +Do (g/a/): + vip\ + \g/a/\ + *= + +Expect: + aaa = aaa = aaa + aaaaa = aaaaa = aaaaa + aaaaaaa = aaaaaaa = aaaaaaab + bbbbb=bbbbb=bbbbb + aaa = aaa = aaa + +Do (g/a - you can omit the trailing /): + vip\ + \g/a\ + *= + +Expect: + aaa = aaa = aaa + aaaaa = aaaaa = aaaaa + aaaaaaa = aaaaaaa = aaaaaaab + bbbbb=bbbbb=bbbbb + aaa = aaa = aaa + +Do (v/b/): + vip\ + \v/b/\ + *= + +Expect: + aaa = aaa = aaa + aaaaa = aaaaa = aaaaa + aaaaaaa=aaaaaaa=aaaaaaab + bbbbb=bbbbb=bbbbb + aaa = aaa = aaa + +Do (invalid filter expression): + vip\ + \haha\ + *= + +Expect: + aaa = aaa = aaa + aaaaa = aaaaa = aaaaa + aaaaaaa = aaaaaaa = aaaaaaab + bbbbb = bbbbb = bbbbb + aaa = aaa = aaa + +Execute (g-filter in shorthand notation): + %EasyAlign*=g/a/ + +Expect: + aaa = aaa = aaa + aaaaa = aaaaa = aaaaa + aaaaaaa = aaaaaaa = aaaaaaab + bbbbb=bbbbb=bbbbb + aaa = aaa = aaa + +Execute (v-filter in shorthand notation): + %EasyAlign*=v/b/ + +Expect: + aaa = aaa = aaa + aaaaa = aaaaa = aaaaa + aaaaaaa=aaaaaaa=aaaaaaab + bbbbb=bbbbb=bbbbb + aaa = aaa = aaa + +Execute (filter in dictionary format): + %EasyAlign*={'filter': 'v/b/'} + +Expect: + aaa = aaa = aaa + aaaaa = aaaaa = aaaaa + aaaaaaa=aaaaaaa=aaaaaaab + bbbbb=bbbbb=bbbbb + aaa = aaa = aaa + +########################################################### + Execute: Restore +