diff --git a/README.md b/README.md index b05099b..9bf2815 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ variant `:EasyAlign!`) for visual mode. | Interactive mode | `:EasyAlign[!] [OPTIONS]` | | Using predefined rules | `:EasyAlign[!] [N-th] DELIMITER_KEY [OPTIONS]` | | Using regular expressions | `:EasyAlign[!] [N-th] /REGEXP/ [OPTIONS]` | +| Live interactive mode | `:LiveEasyAlign[!] [...]` | ### Concept of _alignment rule_ @@ -168,17 +169,24 @@ repeatable, non-interactive command recorded in `g:easy_align_last_command`. :=g:easy_align_last_command ``` ---- +### Live interactive mode -### *Intermission* +If you're performing a complex alignment where multiple options should be +carefully adjusted, try "live interactive mode" which aligns the text on-the-fly +as you type in. -You can stop reading here. Trust me. All the fancy features described in the -following sections are really powerful but you won't be needing them in most -of the cases. +Live interactive mode can be started with `:LiveEasyAlign` command which takes +the same parameters as `:EasyAlign`. I suggest you define the following mapping +in addition to the one for `:EasyAlign` command. -Go try out vim-easy-align right now, and come back later when you feel like it. +```vim +vnoremap :LiveEasyAlign +``` ---- +In live interactive mode, you have to type in the same delimiter (or `CTRL-X` on +regular expression) again to finalize the alignment. This allows you to preview +the result of the alignment and freely change the delimiter using backspace key +without leaving the interactive mode. ### Using `EasyAlign` in command line diff --git a/autoload/easy_align.vim b/autoload/easy_align.vim index fde5e95..8ae0380 100644 --- a/autoload/easy_align.vim +++ b/autoload/easy_align.vim @@ -326,13 +326,13 @@ function! s:split_line(line, nth, modes, cycle, fc, lc, pattern, stick_to_left, return [tokens, delims] endfunction -function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth, - \ ml, mr, da, indentation, stick_to_left, ignore_unmatched, ignore_groups, recur) +function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, nth, recur, dict) let mode = a:modes[0] let lines = {} let min_indent = -1 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 " Phase 1 for line in range(a:fl, a:ll) @@ -340,8 +340,8 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, patter " Split line into the tokens by the delimiters let [tokens, delims] = s:split_line( \ line, a:nth, copy(a:modes), a:recur == 2, - \ a:fc, a:lc, a:pattern, - \ a:stick_to_left, a:ignore_unmatched, a:ignore_groups) + \ a:fc, a:lc, d.pattern, + \ d.stick_to_left, d.ignore_unmatched, d.ignore_groups) " Remember tokens for subsequent recursive calls let a:all_tokens[line] = tokens @@ -383,7 +383,7 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, patter let delim = delims[nth] let token = s:rtrim( tokens[nth] ) let token = s:rtrim( strpart(token, 0, len(token) - len(s:rtrim(delim))) ) - if empty(delim) && !exists('tokens[nth + 1]') && a:ignore_unmatched + if empty(delim) && !exists('tokens[nth + 1]') && d.ignore_unmatched continue endif @@ -409,17 +409,18 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, patter " Phase 1-5: indentation handling (only on a:nth == 1) if a:nth == 1 - if a:indentation ==? 'd' + let idt = d.indentation + if idt ==? 'd' let indent = repeat(' ', max.indent) - elseif a:indentation ==? 's' + elseif idt ==? 's' let indent = repeat(' ', min_indent) - elseif a:indentation ==? 'n' + elseif idt ==? 'n' let indent = '' - elseif a:indentation !=? 'k' - call s:exit('Invalid indentation: ' . a:indentation) + elseif idt !=? 'k' + call s:exit('Invalid indentation: ' . idt) end - if a:indentation !=? 'k' + if idt !=? 'k' let max.just_len = 0 let max.token_len = 0 let max.pivot_len = 0 @@ -461,7 +462,7 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, patter let rpad = '' if mode ==? 'l' let pad = repeat(' ', max.just_len - pw - tw) - if a:stick_to_left + if d.stick_to_left let rpad = pad else let token = token . pad @@ -480,7 +481,7 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, patter let token = repeat(' ', float2nr(pf1)) .token. repeat(' ', float2nr(p2)) let token = substitute(token, repeat(' ', strip) . '$', '', '') - if a:stick_to_left + if d.stick_to_left if empty(s:rtrim(token)) let center = len(token) / 2 let [token, rpad] = [strpart(token, 0, center), strpart(token, center)] @@ -493,15 +494,16 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, patter " Pad the delimiter let dpadl = max.delim_len - s:strwidth(delim) - if a:da ==? 'l' + let da = d.delimiter_align + if da ==? 'l' let [dl, dr] = ['', repeat(' ', dpadl)] - elseif a:da ==? 'c' + elseif da ==? 'c' let dl = repeat(' ', dpadl / 2) let dr = repeat(' ', dpadl - dpadl / 2) - elseif a:da ==? 'r' + elseif da ==? 'r' let [dl, dr] = [repeat(' ', dpadl), ''] else - call s:exit('Invalid delimiter_align: ' . a:da) + call s:exit('Invalid delimiter_align: ' . da) endif " Before and after the range (for blockwise visual mode) @@ -511,8 +513,8 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, patter " Determine the left and right margin around the delimiter let rest = join(tokens[nth + 1 : -1], '') - let ml = empty(prefix . token) ? '' : a:ml - let mr = empty(rest.after) ? '' : a:mr + let ml = empty(prefix . token) ? '' : d.ml + let mr = empty(rest.after) ? '' : d.mr " Adjust indentation of the lines starting with a delimiter let lpad = '' @@ -535,12 +537,8 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, patter if a:nth < max.tokens && (a:recur || len(a:modes) > 1) call s:shift(a:modes, a:recur == 2) - return [ - \ a:todo, - \ a:modes, 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:indentation, a:stick_to_left, - \ a:ignore_unmatched, a:ignore_groups, a:recur] + return [a:todo, a:modes, a:all_tokens, a:all_delims, + \ a:fl, a:ll, a:fc, a:lc, a:nth + 1, a:recur, a:dict] endif return [a:todo] endfunction @@ -571,26 +569,56 @@ function! s:shift_opts(opts, key, vals) endif endfunction -function! s:interactive(modes, vis, opts, delims) +function! s:interactive(range, modes, n, d, opts, rules, vis, live) let mode = s:shift(a:modes, 1) - let n = '' + let n = a:n + let d = a:d let ch = '' let opts = s:compact_options(a:opts) let vals = deepcopy(s:option_values) let regx = 0 let warn = '' + let undo = 0 while 1 - call s:echon(mode, n, -1, '', opts, warn) + " Live preview + let rdrw = 0 + if undo + silent! undo + let undo = 0 + let rdrw = 1 + endif + if a:live && !empty(d) + let output = s:process(a:range, mode, n, d, s:normalize_options(opts), regx, a:rules, 0) + let &undolevels = &undolevels " Break undo block + call s:update_lines(output.todo) + let undo = 1 + let rdrw = 1 + endif + if rdrw + if a:vis + normal! gv + endif + redraw + if a:vis | execute "normal! \" | endif + endif + call s:echon(mode, n, -1, regx ? '/'.d.'/' : d, opts, warn) + let check = 0 let warn = '' let c = getchar() let ch = nr2char(c) if c == 3 || c == 27 " CTRL-C / ESC + if undo + silent! undo + endif throw 'exit' elseif c == "\" - if len(n) > 0 + if !empty(d) + let d = '' + let regx = 0 + elseif len(n) > 0 let n = strpart(n, 0, len(n) - 1) endif elseif c == 13 " Enter key @@ -609,7 +637,7 @@ function! s:interactive(modes, vis, opts, delims) elseif n == '**' | let n = '' else | let check = 1 endif - elseif (c == 48 && len(n) > 0) || c > 48 && c <= 57 " Numbers + elseif empty(d) && ((c == 48 && len(n) > 0) || c > 48 && c <= 57) " Numbers if n[0] == '*' | let check = 1 else | let n = n . ch end @@ -661,11 +689,16 @@ function! s:interactive(modes, vis, opts, delims) silent! call remove(opts, 'm') endif elseif ch == "\" || ch == "\" + if a:live && regx && !empty(d) + break + endif + let prompt = 'Regular expression: ' let ch = s:input(prompt, '', a:vis) if !empty(ch) && s:valid_regexp(ch) let regx = 1 - break + let d = ch + if !a:live | break | endif else let warn = 'Invalid regular expression: '.ch endif @@ -676,14 +709,33 @@ function! s:interactive(modes, vis, opts, delims) endif if check - if has_key(a:delims, ch) - break + if empty(d) + if has_key(a:rules, ch) + let d = ch + if !a:live + break + endif + else + let warn = 'Unknown delimiter key: '.ch + endif else - let warn = 'Unknown delimiter key: '.ch + if regx + let warn = 'Press to finish' + else + if d == ch + break + else + let warn = 'Press '''.d.''' again to finish' + endif + end endif endif endwhile - return [mode, n, ch, s:normalize_options(opts), regx] + if a:live + let copts = call('s:summarize', output.summarize) + let g:easy_align_last_command = s:echon('', n, regx, d, copts, '') + end + return [mode, n, ch, opts, regx] endfunction function! s:valid_regexp(regexp) @@ -751,6 +803,9 @@ function! s:parse_shorthand_opts(expr) endfunction function! s:parse_args(args) + if empty(a:args) + return ['', '', {}, 0] + endif let n = '' let ch = '' let args = a:args @@ -804,55 +859,42 @@ function! s:parse_args(args) return [matches[1], s:test_regexp(matches[2]), opts, 1] else let tokens = matchlist(args, '^\([1-9][0-9]*\|-[0-9]*\|\*\*\?\)\?\s*\(.\{-}\)\?$') - return [tokens[1], tokens[2], opts, 0] + " Try swapping n and ch + let [n, ch] = empty(tokens[2]) ? reverse(tokens[1:2]) : tokens[1:2] + + " Resolving command-line ambiguity + " '\ ' => ' ' + " '\' => ' ' + if ch =~ '^\\\s*$' + let ch = ' ' + " '\\' => '\' + elseif ch =~ '^\\\\\s*$' + let ch = '\' + endif + + return [n, ch, opts, 0] endif endfunction -function! s:modes(bang) +function! s:interactive_modes(bang) return get(g:, \ (a:bang ? 'easy_align_bang_interactive_modes' : 'easy_align_interactive_modes'), \ (a:bang ? ['r', 'l', 'c'] : ['l', 'r', 'c'])) endfunction function! s:alternating_modes(mode) - return a:mode ==? 'r' ? ['r', 'l'] : ['l', 'r'] + return a:mode ==? 'r' ? 'rl' : 'lr' endfunction -function! easy_align#align(bang, expr) range - try - call s:align(a:bang, a:firstline, a:lastline, a:expr) - catch 'exit' - endtry +function! s:update_lines(todo) + for [line, content] in items(a:todo) + call setline(line, s:rtrim(content)) + endfor endfunction -function! s:align(bang, first_line, last_line, expr) - let modes = s:modes(a:bang) - let mode = modes[0] - let recur = 0 - let n = '' - let ch = '' - let opts = {} - let regexp = 0 - " Heuristically determine if the user was in visual mode - let vis = a:first_line == line("'<") && a:last_line == line("'>") - - let delimiters = s:easy_align_delimiters_default - if exists('g:easy_align_delimiters') - let delimiters = extend(copy(delimiters), g:easy_align_delimiters) - endif - - if empty(a:expr) - let [mode, n, ch, opts, regexp] = s:interactive(copy(modes), vis, opts, delimiters) - else - let [n, ch, opts, regexp] = s:parse_args(a:expr) - if empty(n) && empty(ch) - let [mode, n, ch, opts, regexp] = s:interactive(copy(modes), vis, opts, delimiters) - elseif empty(ch) - " Try swapping n and ch - let [n, ch] = ['', n] - endif - endif - +function! s:parse_nth(n) + let n = a:n + let recur = 0 if n == '*' | let [nth, recur] = [1, 1] elseif n == '**' | let [nth, recur] = [1, 2] elseif n == '-' | let nth = -1 @@ -862,93 +904,134 @@ function! s:align(bang, first_line, last_line, expr) else let nth = n endif + return [nth, recur] +endfunction - if regexp - let dict = { 'pattern': ch } +function! s:build_dict(delimiters, ch, regexp, opts) + if a:regexp + let dict = { 'pattern': a:ch } else - " Resolving command-line ambiguity - if !empty(a:expr) - " '\ ' => ' ' - " '\' => ' ' - if ch =~ '^\\\s*$' - let ch = ' ' - " '\\' => '\' - elseif ch =~ '^\\\\\s*$' - let ch = '\' - endif + if !has_key(a:delimiters, a:ch) + call s:exit('Unknown delimiter key: '. a:ch) endif - if !has_key(delimiters, ch) - call s:exit('Unknown delimiter key: '. ch) - endif - let dict = copy(delimiters[ch]) + let dict = copy(a:delimiters[a:ch]) endif - - call extend(dict, opts) + call extend(dict, a:opts) let ml = get(dict, 'left_margin', ' ') let mr = get(dict, 'right_margin', ' ') if type(ml) == 0 | let ml = repeat(' ', ml) | endif if type(mr) == 0 | let mr = repeat(' ', mr) | endif + call extend(dict, { 'ml': ml, 'mr': mr }) - let bvisual = vis && char2nr(visualmode()) == 22 " ^V + let dict.pattern = get(dict, 'pattern', a:ch) + let dict.delimiter_align = + \ get(dict, 'delimiter_align', get(g:, 'easy_align_delimiter_align', 'r'))[0] + let dict.indentation = + \ get(dict, 'indentation', get(g:, 'easy_align_indentation', 'k'))[0] + let dict.stick_to_left = + \ get(dict, 'stick_to_left', 0) + let dict.ignore_unmatched = + \ 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())) + return dict +endfunction - if recur && bvisual +function! s:build_mode_sequence(expr, recur) + let [expr, recur] = [a:expr, a:recur] + let suffix = matchstr(a:expr, '\*\+$') + if suffix == '*' + let expr = expr[0 : -2] + let recur = 1 + elseif suffix == '**' + let expr = expr[0 : -3] + let recur = 2 + endif + return [tolower(expr), recur] +endfunction + +function! s:process(range, mode, n, ch, opts, regexp, rules, bvis) + let [nth, recur] = s:parse_nth(a:n) + let dict = s:build_dict(a:rules, a:ch, a:regexp, a:opts) + let [mode_sequence, recur] = s:build_mode_sequence( + \ get(dict, 'mode_sequence', recur == 2 ? s:alternating_modes(a:mode) : a:mode), + \ recur) + + if recur && a:bvis call s:exit('Recursive alignment is not supported in blockwise-visual mode') endif - let aseq = get(dict, 'mode_sequence', - \ recur == 2 ? s:alternating_modes(mode) : [mode]) - let mode_expansion = matchstr(aseq, '\*\+$') - if mode_expansion == '*' - let aseq = aseq[0 : -2] - let recur = 1 - elseif mode_expansion == '**' - let aseq = aseq[0 : -3] - let recur = 2 - endif - let aseq_list = type(aseq) == 1 ? split(tolower(aseq), '\s*') : map(copy(aseq), 'tolower(v:val)') - let aseq_str = join(aseq_list, '') - - let todo = {} let args = [ - \ todo, - \ aseq_list, - \ {}, {}, a:first_line, a:last_line, - \ bvisual ? min([col("'<"), col("'>")]) : 1, - \ bvisual ? max([col("'<"), col("'>")]) : 0, - \ get(dict, 'pattern', ch), - \ nth, - \ ml, - \ mr, - \ get(dict, 'delimiter_align', get(g:, 'easy_align_delimiter_align', 'r'))[0], - \ get(dict, 'indentation', get(g:, 'easy_align_indentation', 'k'))[0], - \ get(dict, 'stick_to_left', 0), - \ get(dict, 'ignore_unmatched', get(g:, 'easy_align_ignore_unmatched', 2)), - \ get(dict, 'ignore_groups', get(dict, 'ignores', s:ignored_syntax())), - \ recur ] + \ {}, split(mode_sequence, '\zs'), + \ {}, {}, a:range[0], a:range[1], + \ a:bvis ? min([col("'<"), col("'>")]) : 1, + \ a:bvis ? max([col("'<"), col("'>")]) : 0, + \ nth, recur, dict ] while len(args) > 1 let args = call('s:do_align', args) endwhile + " todo: lines to update + " summarize: arguments to s:summarize + return { 'todo': args[0], 'summarize': [ a:opts, recur, mode_sequence ] } +endfunction + +function s:summarize(opts, recur, mode_sequence) + let copts = s:compact_options(a:opts) + let nbmode = s:interactive_modes(0)[0] + if !has_key(copts, 'm') && ( + \ (a:recur == 2 && s:alternating_modes(nbmode) != a:mode_sequence) || + \ (a:recur != 2 && (a:mode_sequence[0] != nbmode || len(a:mode_sequence) > 1)) + \ ) + call extend(copts, { 'm': a:mode_sequence }) + endif + return copts +endfunction + +function! s:align(bang, live, first_line, last_line, expr) + " Heuristically determine if the user was in visual mode + let vis = a:first_line == line("'<") && a:last_line == line("'>") + let bvis = vis && char2nr(visualmode()) == 22 " ^V + let range = [a:first_line, a:last_line] + let modes = s:interactive_modes(a:bang) + let mode = modes[0] + + if bvis && a:live + call s:exit('Live mode is not supported in blockwise-visual mode') + endif + + let rules = s:easy_align_delimiters_default + if exists('g:easy_align_delimiters') + let rules = extend(copy(rules), g:easy_align_delimiters) + endif + + let [n, ch, opts, regexp] = s:parse_args(a:expr) + let bypass_fold = get(g:, 'easy_align_bypass_fold', 0) let ofm = &l:foldmethod try if bypass_fold | let &l:foldmethod = 'manual' | endif - for [line, content] in items(todo) - call setline(line, s:rtrim(content)) - endfor + + if empty(n) && empty(ch) || a:live + let [mode, n, ch, opts, regexp] = s:interactive(range, copy(modes), n, ch, opts, rules, vis, a:live) + endif + + if !a:live + let output = s:process(range, mode, n, ch, s:normalize_options(opts), regexp, rules, bvis) + call s:update_lines(output.todo) + let copts = call('s:summarize', output.summarize) + let g:easy_align_last_command = s:echon('', n, regexp, ch, copts, '') + endif finally if bypass_fold | let &l:foldmethod = ofm | endif endtry - - let copts = s:compact_options(opts) - let nbmode = s:modes(0)[0] - if !has_key(copts, 'm') && ( - \ (recur == 2 && join(s:alternating_modes(nbmode), '') != aseq_str) || - \ (recur != 2 && (aseq_str[0] != nbmode || len(aseq_str) > 1)) - \ ) - call extend(copts, { 'm': aseq_str }) - endif - let g:easy_align_last_command = s:echon('', n, regexp, ch, copts, '') +endfunction + +function! easy_align#align(bang, live, expr) range + try + call s:align(a:bang, a:live, a:firstline, a:lastline, a:expr) + catch 'exit' + endtry endfunction diff --git a/doc/easy_align.txt b/doc/easy_align.txt index 34e56a9..e8cc732 100644 --- a/doc/easy_align.txt +++ b/doc/easy_align.txt @@ -108,6 +108,25 @@ repeatable, non-interactive command recorded in `g:easy_align_last_command`. :=g:easy_align_last_command +Live interactive mode *:LiveEasyAlign* *:LiveEasyAlign!* +------------------------------------------------------------------------- + +If you're performing a complex alignment where multiple options should be +carefully adjusted, try "live interactive mode" which aligns the text +on-the-fly as you type in. + +Live interactive mode can be started with `:LiveEasyAlign` command which +takes the same parameters as `:EasyAlign`. I suggest you define the +following mapping in addition to the one for `:EasyAlign` command. + + vnoremap :LiveEasyAlign + +In live interactive mode, you have to type in the same delimiter (or +`CTRL-X` on regular expression) again to finalize the alignment. This +allows you to preview the result of the alignment and freely change the +delimiter using backspace key without leaving the interactive mode. + + Left/right/center mode switch in interactive mode ------------------------------------------------------------------------- *g:easy_align_interactive_modes* diff --git a/plugin/easy_align.vim b/plugin/easy_align.vim index 6e7045d..0334379 100644 --- a/plugin/easy_align.vim +++ b/plugin/easy_align.vim @@ -26,4 +26,5 @@ if exists("g:loaded_easy_align_plugin") endif let g:loaded_easy_align_plugin = 1 -command! -nargs=* -range -bang EasyAlign ,call easy_align#align('' == '!', ) +command! -nargs=* -range -bang EasyAlign ,call easy_align#align('' == '!', 0, ) +command! -nargs=* -range -bang LiveEasyAlign ,call easy_align#align('' == '!', 1, ) diff --git a/test/commandline.vader b/test/commandline.vader index cae4783..92ad53e 100644 --- a/test/commandline.vader +++ b/test/commandline.vader @@ -16,6 +16,7 @@ Given (fruits): Execute (regular expression): %EasyAlign/[:;]\+/ + AssertEqual ':EasyAlign /[:;]\+/', g:easy_align_last_command Expect: apple ;:;; banana::cake diff --git a/test/interactive.vader b/test/interactive.vader index 8806984..04a370f 100644 --- a/test/interactive.vader +++ b/test/interactive.vader @@ -3,6 +3,7 @@ Execute (Clean up test environment): Save g:easy_align_indentation, g:easy_align_delimiter_align Save g:easy_align_interactive_modes, g:easy_align_bang_interactive_modes Save g:easy_align_delimiters + Save mapleader " TODO: revert after test silent! unlet g:easy_align_ignore_groups @@ -13,8 +14,11 @@ Execute (Clean up test environment): silent! unlet g:easy_align_bang_interactive_modes let g:easy_align_delimiters = {} - vnoremap :EasyAlign - vnoremap :EasyAlign! + let mapleader = ' ' + vnoremap :EasyAlign + vnoremap r :EasyAlign! + vnoremap :LiveEasyAlign + vnoremap r :LiveEasyAlign! ########################################################### @@ -477,6 +481,62 @@ Expect: |batch_size|Fixnum | nil|number of maximum items to be assigned at once| |logger |Logger | nil|logger instance for debug logs | +Do (live interactive mode): + vip\\ + | + \(\ + \)\ + *** + ** + \\\ + **|\| + +Expect: + |)Option (|) Type(|)Default(|) Description(| + |)-- (|) --(|)-- (|) --(| + |)threads (|) Fixnum(|)1 (|) number of threads in the thread pool(| + |)queues (|) Fixnum(|)1 (|) number of concurrent queues(| + |)queue_size(|) Fixnum(|)1000 (|) size of each queue(| + |)interval (|)Numeric(|)0 (|) dispatcher interval for batch processing(| + |)batch (|)Boolean(|)false (|) enables batch processing mode(| + |)batch_size(|) Fixnum(|)nil (|)number of maximum items to be assigned at once(| + |)logger (|) Logger(|)nil (|) logger instance for debug logs(| + +Do (live interactive mode!): + vip\r\ + | + \[\ + \]\ + *** + ** + \\\ + ** + \\ + \|\ + \ + \ + +Expect: + [|] Option[|]Type [|]Default[|]Description [|[ + [|] --[|]-- [|] --[|]-- [|[ + [|] threads[|]Fixnum [|] 1[|]number of threads in the thread pool [|[ + [|] queues[|]Fixnum [|] 1[|]number of concurrent queues [|[ + [|]queue_size[|]Fixnum [|] 1000[|]size of each queue [|[ + [|] interval[|]Numeric[|] 0[|]dispatcher interval for batch processing [|[ + [|] batch[|]Boolean[|] false[|]enables batch processing mode [|[ + [|]batch_size[|]Fixnum [|] nil[|]number of maximum items to be assigned at once[|[ + [|] logger[|]Logger [|] nil[|]logger instance for debug logs [|[ + +Execute (g:easy_align_last_command should be set): + Assert exists('g:easy_align_last_command') + unlet g:easy_align_last_command + +Do: + vip\\**|\ + +Execute (g:easy_align_last_command should not be set if interrupted): + Assert !exists('g:easy_align_last_command') + ########################################################### Given (comma-separated items): @@ -1214,7 +1274,7 @@ Expect: ccc = 3 Do: - vip\\= + vipr\= Expect: a = 1 @@ -1222,7 +1282,7 @@ Expect: ccc = 3 Do: - vip\\\= + vipr\\= Expect: a = 1 @@ -1230,7 +1290,7 @@ Expect: ccc = 3 Do: - vip\\\\= + vipr\\\= Expect: a = 1