From 0020db37ae7c092d07e5865d9bd337cfd623f7c4 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sat, 4 May 2013 00:58:58 +0900 Subject: [PATCH] adds support for right and center justification --- README.md | 87 +++++++++++++++++++++++++------- autoload/easy_align.vim | 107 +++++++++++++++++++++++++--------------- doc/easy_align.txt | 83 +++++++++++++++++++++++++------ plugin/easy_align.vim | 4 +- 4 files changed, 206 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index d576c19..797516c 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,17 @@ vim-easy-align ============== -Yet another Vim alignment plugin without too much ambition. +A simple, easy-to-use Vim alignment plugin. -This plugin clearly has less features than the other pre-existing ones with the similar goals, -but it is simpler, easier to use, and just good enough for the most of the cases. +Demo +---- + +[Screencast](https://vimeo.com/63506219) Usage ----- -Vim-easy-align defines `:EasyAlign` command in the visual mode. +_vim-easy-align_ defines interactive `:EasyAlign` command in the visual mode. For convenience, it is advised that you define a mapping for triggering it in your `.vimrc`. ```vim @@ -18,7 +20,8 @@ vnoremap :EasyAlign With the mapping, you can align selected lines with a few keystrokes. -1. `` key to start EasyAlign command +1. `` key to start interactive EasyAlign command +1. Optional Enter keys to switch justficiation mode (default: left) 1. Optional field number (default: 1) - `1` Alignment around 1st delimiter - `2` Alignment around 2nd delimiter @@ -39,16 +42,17 @@ Alignment rules for the following delimiters have been crafted to meet the most ### Example command sequences -| With visual map | Description | Equivalent command | -| ----------------- | ------------------------------------------------- | -------------------- | -| `=` | Alignment around 1st equals sign (and the likes) | `:'<,'>EasyAlign=` | -| `2=` | Alignment around 2nd equals sign (and the likes) | `:'<,'>EasyAlign2=` | -| `3=` | Alignment around 3rd equals sign (and the likes) | `:'<,'>EasyAlign3=` | -| `*=` | Alignment around all equals signs (and the likes) | `:'<,'>EasyAlign*=` | -| `` | Alignment around 1st space | `:'<,'>EasyAlign\ ` | -| `2` | Alignment around 2nd space | `:'<,'>EasyAlign2\ ` | -| `:` | Alignment around 1st colon | `:'<,'>EasyAlign:` | -| ... | ... | | +| With visual map | Description | Equivalent command | +| ----------------- | ------------------------------------------------- | ----------------------- | +| `=` | Alignment around 1st equals signs (and the likes) | `:'<,'>EasyAlign=` | +| `2=` | Alignment around 2nd equals signs (and the likes) | `:'<,'>EasyAlign2=` | +| `3=` | Alignment around 3rd equals signs (and the likes) | `:'<,'>EasyAlign3=` | +| `*=` | Alignment around all equals signs (and the likes) | `:'<,'>EasyAlign*=` | +| `=` | Right-justified alignment around 1st equals signs | `:'<,'>EasyAlignRight=` | +| `` | Alignment around 1st space | `:'<,'>EasyAlign\ ` | +| `2` | Alignment around 2nd space | `:'<,'>EasyAlign2\ ` | +| `:` | Alignment around 1st colon | `:'<,'>EasyAlign:` | +| ... | ... | | ### Partial alignment in blockwise-visual mode @@ -90,10 +94,57 @@ let g:easy_align_delimiters = { \ } ``` -Demo ----- +Handling unmatched lines +------------------------ -[Screencast](https://vimeo.com/63506219) +EasyAlign by default ignores lines without the matching delimiters. +This is to ignore interleaved comments commonly found in code. + +For example, when aligning the following code block, + +``` +{ + # Quantity of apples + apple: 1, + # Quantity of bananas + bananas: 2, + # Quantity of grapefruits + grapefruits: 3 +} +``` + +we don't want to the comment lines to affect the alignment, +so this is usually what we want. + +``` +{ + # Quantity of apples + apple: 1, + # Quantity of bananas + bananas: 2, + # Quantity of grapefruits + grapefruits: 3 +} +``` + +However, this default behavior is configurable. + +```vim +let g:easy_align_ignore_unmatched = 0 +``` + +Then we get, + +``` +{ + # Quantity of apples + apple: 1, + # Quantity of bananas + bananas: 2, + # Quantity of grapefruits + grapefruits: 3 +} +``` Author ------ diff --git a/autoload/easy_align.vim b/autoload/easy_align.vim index 59215b3..04f2d41 100644 --- a/autoload/easy_align.vim +++ b/autoload/easy_align.vim @@ -2,7 +2,8 @@ if exists("g:easy_align_loaded") finish endif let g:easy_align_loaded = 1 -let g:easy_align_delimiters_merged = { + +let s:easy_align_delimiters_default = { \ ' ': { 'pattern': ' ', 'margin_left': '', 'margin_right': '', 'stick_to_left': 0 }, \ '=': { 'pattern': '<=>\|\(&&\|||\|<<\|>>\)=\|=\~\|=>\|[:+/*!%^=><&|-]\?=', \ 'margin_left': ' ', 'margin_right': ' ', 'stick_to_left': 0 }, @@ -12,18 +13,15 @@ let g:easy_align_delimiters_merged = { \ '.': { 'pattern': '\.', 'margin_left': '', 'margin_right': '', 'stick_to_left': 0 } \ } -if !exists("g:easy_align_delimiters") - let g:easy_align_delimiters = {} -endif +let s:just = ['L', 'R', 'C'] -call extend(g:easy_align_delimiters_merged, g:easy_align_delimiters) - -function! s:do_align(fl, ll, fc, lc, pattern, nth, ml, mr, stick_to_left, recursive) - let lines = {} - let just_len = 0 - let max_delim_len = 0 - let max_tokens = 0 - let pattern = '\s*\(' .a:pattern. '\)\s*' +function! s:do_align(just, fl, ll, fc, lc, pattern, nth, ml, mr, stick_to_left, recursive) + let lines = {} + let max_token_len = 0 + let max_delim_len = 0 + let max_prefix_len = 0 + let max_tokens = 0 + let pattern = '\s*\(' .a:pattern. '\)\s*' for line in range(a:fl, a:ll) let tokens = split(a:lc ? \ strpart(getline(line), a:fc - 1, a:lc - a:fc + 1) : @@ -41,27 +39,47 @@ function! s:do_align(fl, ll, fc, lc, pattern, nth, ml, mr, stick_to_left, recurs endif let last = tokens[nth - 1] - let prefix = (nth > 1 ? join(tokens[0 : nth - 2], '') : '') . substitute(last, pattern.'$', '', '') + let prefix = (nth > 1 ? join(tokens[0 : nth - 2], '') : '') + let token = substitute(last, pattern.'$', '', '') let suffix = join(tokens[nth : -1], '') if match(last, pattern.'$') == -1 - continue + if !exists("g:easy_align_ignore_unmatched") || g:easy_align_ignore_unmatched + continue + else + let delim = '' + endif + else + let delim = matchlist(last, pattern)[1] endif - let delim = matchlist(tokens[nth - 1], pattern)[1] - let just_len = max([len(prefix), just_len]) - let max_delim_len = max([len(delim), max_delim_len]) - let lines[line] = [prefix, suffix, delim] + let max_token_len = max([len(token), max_token_len]) + let max_prefix_len = max([len(prefix), max_prefix_len]) + let max_delim_len = max([len(delim), max_delim_len]) + let lines[line] = [prefix, token, delim, suffix] endfor for [line, tokens] in items(lines) - let [prefix, suffix, delim] = tokens + let [prefix, token, delim, suffix] = tokens - let pad = repeat(' ', just_len - len(prefix)) - if a:stick_to_left - let suffix = pad . suffix + let pad = repeat(' ', max_token_len - len(token) + max_prefix_len - len(prefix)) + if a:just == 0 + if a:stick_to_left + let suffix = pad . suffix + else + let token = token . pad + endif + elseif a:just == 1 + let token = pad . token else - let prefix = prefix . pad + let p1 = strpart(pad, 0, len(pad) / 2) + let p2 = strpart(pad, len(pad) / 2) + if a:stick_to_left + let token = p1 . token + let suffix = p2 . suffix + else + let token = p1. token .p2 + endif endif let delim = repeat(' ', max_delim_len - len(delim)). delim @@ -69,45 +87,51 @@ function! s:do_align(fl, ll, fc, lc, pattern, nth, ml, mr, stick_to_left, recurs let before = strpart(cline, 0, a:fc - 1) let after = a:lc ? strpart(cline, a:lc) : '' - let ml = empty(prefix) ? '' : a:ml + let ml = empty(prefix . token) ? '' : a:ml let mr = (empty(suffix . after) || (empty(suffix) && stridx(after, a:mr) == 0)) ? '' : a:mr - let aligned = join([prefix, ml, delim, mr, suffix], '') + let aligned = join([prefix, token, ml, delim, mr, suffix], '') let aligned = empty(after) ? substitute(aligned, '\s*$', '', '') : aligned call setline(line, before.aligned.after) endfor if a:recursive && a:nth < max_tokens - call s:do_align(a:fl, a:ll, a:fc, a:lc, a:pattern, a:nth + 1, a:ml, a:mr, a:stick_to_left, a:recursive) + call s:do_align(a:just, a:fl, a:ll, a:fc, a:lc, a:pattern, a:nth + 1, a:ml, a:mr, a:stick_to_left, a:recursive) endif endfunction -function! easy_align#align(...) range +function! s:echon(l, n, d) + echon "\rEasyAlign[". s:just[a:l] ."] (" .a:n.a:d. ")" +endfunction + +function! easy_align#align(just, ...) range + let just = a:just let recursive = 0 let n = '' let ch = '' if a:0 == 0 - echon "\reasy-align ()" while 1 + call s:echon(just, n, '') + let c = getchar() let ch = nr2char(c) if c == 3 || c == 27 return + elseif c == 13 + let just = (just + 1) % len(s:just) elseif c >= 48 && c <= 57 if n == '*' - echon "\rField number(*) already specified" - return + break + else + let n = n . nr2char(c) endif - let n = n . nr2char(c) - echon "\reasy-align (". n .")" elseif ch == '*' if !empty(n) - echon "\rField number(". n .") already specified" - return + break + else + let n = '*' endif - let n = '*' - echon "\reasy-align (*)" else break endif @@ -137,9 +161,12 @@ function! easy_align#align(...) range return endif - if has_key(g:easy_align_delimiters_merged, ch) - let dict = g:easy_align_delimiters_merged[ch] - call s:do_align(a:firstline, a:lastline, + let delimiters = extend(copy(s:easy_align_delimiters_default), + \ exists("g:easy_align_delimiters") ? g:easy_align_delimiters : {}) + + if has_key(delimiters, ch) + let dict = delimiters[ch] + call s:do_align(just, a:firstline, a:lastline, \ visualmode() == '' ? min([col("'<"), col("'>")]) : 1, \ visualmode() == '' ? max([col("'<"), col("'>")]) : 0, \ get(dict, 'pattern', ch), @@ -147,7 +174,7 @@ function! easy_align#align(...) range \ get(dict, 'margin_left', ' '), \ get(dict, 'margin_right', ' '), \ get(dict, 'stick_to_left', 0), recursive) - echon "\reasy-align (". (recursive ? '*' : n) . ch .")" + call s:echon(just, (recursive ? '*' : n), ch) else echon "\rUnknown delimiter: ". ch endif diff --git a/doc/easy_align.txt b/doc/easy_align.txt index e1a589d..4f94a11 100644 --- a/doc/easy_align.txt +++ b/doc/easy_align.txt @@ -1,21 +1,16 @@ vim-easy-align *vim-easy-align* *easy-align* ========================================================================= -Author: Junegunn Choi +A simple, easy-to-use Vim alignment plugin. -Yet another Vim alignment plugin without too much ambition. - -This plugin clearly has less features than the other pre-existing ones -with the similar goals, but it is simpler, easier to use, -and just good enough for the most of the cases. - -https://github.com/junegunn/vim-easy-align + Author: Junegunn Choi + Source: https://github.com/junegunn/vim-easy-align EasyAlign *EasyAlign* ------------------------------------------------------------------------- -Vim-easy-align defines `:EasyAlign` command in the visual mode. +vim-easy-align defines interactive `:EasyAlign` command in the visual mode. For convenience, it is advised that you define a mapping for triggering it in your `.vimrc`. @@ -24,13 +19,14 @@ in your `.vimrc`. With this mapping, you can align selected lines with a few keystrokes. -1. key to start EasyAlign command -2. Optional field number (default: 1) +1. key to start interactive EasyAlign command +2. Optional Enter keys to switch justficiation mode (default: left) +3. Optional field number (default: 1) 1 Alignment around 1st delimiter 2 Alignment around 2nd delimiter ... * Alignment around all delimiters (recursive) -3. Delimiter +4. Delimiter General alignment around whitespaces = Operators containing equals sign (=, ==, !=, +=, &&=, ...) : Suitable for formatting JSON or YAML @@ -38,17 +34,28 @@ With this mapping, you can align selected lines with a few keystrokes. , Multi-line method arguments. CSV. | Table markdown +During the key sequence, key will switch justification mode. +(left -> right -> center) + Examples: - = Alignment around 1st equals sign (and the likes) - 2= Alignment around 2nd equals sign (and the likes) - 3= Alignment around 3rd equals sign (and the likes) + = Alignment around 1st equals signs (and the likes) + 2= Alignment around 2nd equals signs (and the likes) + 3= Alignment around 3rd equals signs (and the likes) *= Alignment around all equals signs (and the likes) + = Right-justified alignment around 1st equals signs Alignment around 1st whitespace 2 Alignment around 2nd whitespace : Alignment around 1st colon +EasyAlignRight, EasyAlignCenter *EasyAlignRight* *EasyAlignCenter* +------------------------------------------------------------------------- + +*EasyAlignRight* and *EasyAlignCenter* are the right and center justified +versions of EasyAlign command. + + Partial alignment in blockwise-visual mode ------------------------------------------------------------------------- @@ -56,7 +63,8 @@ In blockwise-visual mode (`CTRL-V`), EasyAlign command aligns only the selected parts, instead of the whole lines in the range. -Defining custom alignment rules + +Defining custom alignment rules *g:easy_align_delimiters* ------------------------------------------------------------------------- let g:easy_align_delimiters = { @@ -69,3 +77,46 @@ Defining custom alignment rules \ } \ } + +Handling unmatched lines *g:easy_align_ignore_unmatched* +------------------------------------------------------------------------- + +EasyAlign by default ignores lines without the matching delimiters. +This is to ignore interleaved comments commonly found in code. + +For example, when aligning the following code, + + { + # Quantity of apples + apple: 1, + # Quantity of bananas + bananas: 2, + # Quantity of grapefruits + grapefruits: 3 + } + +this is usually what we want. + + { + # Quantity of apples + apple: 1, + # Quantity of bananas + bananas: 2, + # Quantity of grapefruits + grapefruits: 3 + } + +However, this default behavior is configurable. + + let g:easy_align_ignore_unmatched = 0 + +Then we get, + + { + # Quantity of apples + apple: 1, + # Quantity of bananas + bananas: 2, + # Quantity of grapefruits + grapefruits: 3 + } diff --git a/plugin/easy_align.vim b/plugin/easy_align.vim index d1c743d..9e9ed08 100644 --- a/plugin/easy_align.vim +++ b/plugin/easy_align.vim @@ -3,4 +3,6 @@ if exists("g:easy_align_plugin_loaded") endif let g:easy_align_plugin_loaded = 1 -command! -nargs=* -range EasyAlign ,call easy_align#align() +command! -nargs=* -range EasyAlign ,call easy_align#align(0, ) +command! -nargs=* -range EasyAlignRight ,call easy_align#align(1, ) +command! -nargs=* -range EasyAlignCenter ,call easy_align#align(2, )