diff --git a/EXAMPLES.md b/EXAMPLES.md new file mode 100644 index 0000000..368ee92 --- /dev/null +++ b/EXAMPLES.md @@ -0,0 +1,136 @@ +vim: set scrolloff=1 buftype=nofile colorcolumn=: + +vim-easy-align examples +======================= + +To enable syntax highlighting in the code blocks, define and call the following +function. + +```vim +function! GFM() + let syntaxes = { + \ 'ruby': 'syntax/ruby.vim', + \ 'yaml': 'syntax/yaml.vim', + \ 'vim': 'syntax/vim.vim', + \ 'c': 'syntax/c.vim' + \ } + + for [lang, syn] in items(syntaxes) + unlet b:current_syntax + silent! exec printf("syntax include @%s %s", lang, syn) + exec printf("syntax region %sSnip matchgroup=Snip start='```%s' end='```' contains=@%s", + \ lang, lang, lang) + endfor + let b:current_syntax='mkd' +endfunction +``` + +Alignment around whitespaces +---------------------------- + +``` + +Paul McCartney 1942 +George Harrison 1943 +Ringo Starr 1940 +Pete Best 1941 + +``` + + +Formatting table +---------------- + +| 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 | + + +Alignment around = +------------------ + +```ruby + +a = +a = 1 +bbbb = 2 +ccccccc = 3 +ccccccccccccccc +ddd = 4 +eeee === eee = eee = eee=f +fff = ggg += gg &&= gg +g != hhhhhhhh == 888 +i := 5 +i %= 5 +i *= 5 +j =~ 5 +j >= 5 +aa => 123 +aa <<= 123 +aa >>= 123 +bbb => 123 +c => 1233123 +d => 123 +dddddd &&= 123 +dddddd ||= 123 +dddddd /= 123 +gg <=> ee + +``` + +Formatting YAML (or JSON) +------------------------- + +```yaml + +mysql: + # JDBC driver for MySQL database + driver: com.mysql.jdbc.Driver + # JDBC URL for the connection (jdbc:mysql://HOSTNAME/DATABASE) + url: jdbc:mysql://localhost/test + database: test + user:r00t + +``` + +Partial alignment in block-visual mode / Negative field index +------------------------------------------------------------- + +```ruby + +options = { :caching => nil, + :versions => 3, + "cache=blocks" => false }.merge(options) + +``` + +Commas +------ + +``` + +aaa, bb,c +d,eeeeeee +fffff, gggggggggg, +h, , ii +j,,k + +``` + +Ignoring delimiters in comments and strings +------------------------------------------- + +```c +/* a */ b = c +aa >= bb +// aaa = bbb = cccc +/* aaaa = */ bbbb === cccc " = dddd = " = eeee +aaaaa /* bbbbb */ == ccccc /* != eeeee = */ === fffff +``` + diff --git a/README.md b/README.md index fb54cf4..5abbf49 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Features: - Requires minimal keystrokes - Extensible alignment rules - Aligns text around either _all or n-th_ occurrence(s) of the delimiter -- Ignores comment lines +- Ignores delimiters in certain syntax highlighting groups (e.g. comments, strings) - Ignores lines without a matching delimiter Demo @@ -17,8 +17,6 @@ Demo ![Screencast](https://raw.github.com/junegunn/vim-easy-align/gif/vim-easy-align.gif) -[Screencast](https://vimeo.com/63506219) - Installation ------------ @@ -115,15 +113,24 @@ since the same can be easily done using the negative field number: `-=` Options ------- -| Option | Type | Default | Description | -| ----------------------------- | ---------- | ------- | --------------------------------------- | -| g:easy_align_ignore_comment | boolean | `1` | Ignore comment lines | -| g:easy_align_ignore_unmatched | boolean | `1` | Ignore lines without matching delimiter | -| g:easy_align_delimiters | dictionary | `{}` | Extend or override alignment rules | +| Option | Type | Default | Description | +| ----------------------------- | ---------- | --------------------- | ----------------------------------------------------- | +| g:easy_align_ignores | list | ['String', 'Comment'] | Ignore delimiters in these syntax highlighting groups | +| g:easy_align_ignore_unmatched | boolean | `1` | Ignore lines without matching delimiter | +| g:easy_align_delimiters | dictionary | `{}` | Extend or override alignment rules | -### Ignoring comment lines +### Ignoring delimiters in comments or strings -EasyAlign by default ignores comment lines. +EasyAlign can be configured to ignore delimiters in certain highlight groups, +such as code comments or strings. By default, delimiters that are highlighted as +code comments or strings are ignored. + +```vim +" Default: +" If a delimiter is in a highlight group whose name matches +" any of the followings, it will be ignored. +let g:easy_align_ignores = ['Comment', 'String'] +``` For example, @@ -133,8 +140,8 @@ For example, apple: 1, # Quantity of bananas: 2 bananas: 2, - # Quantity of grapefruits: 3 - grapefruits: 3 + # Quantity of grape:fruits: 3 + 'grape:fruits': 3 } ``` @@ -143,39 +150,42 @@ becomes ```ruby { # Quantity of apples: 1 - apple: 1, + apple: 1, # Quantity of bananas: 2 - bananas: 2, - # Quantity of grapefruits: 3 - grapefruits: 3 + bananas: 2, + # Quantity of grape:fruits: 3 + 'grape:fruits': 3 } ``` -Since finding comment lines is done heuristically using syntax highlighting feature, -this only works when syntax highlighting is enabled. +Naturally, this only works when syntax highlighting is enabled. -If you do not want comment lines to be ignored, you can unset `g:easy_align_ignore_comment` as follows. +You can override `g:easy_align_ignores` to change the rule. ```vim -let g:easy_align_ignore_comment = 0 +" Ignore nothing! +let g:easy_align_ignores = [] ``` Then you get, ```ruby { - # Quantity of apples: 1 - apple: 1, - # Quantity of bananas: 2 - bananas: 2, - # Quantity of grapefruits: 3 - grapefruits: 3 + # Quantity of apples: 1 + apple: 1, + # Quantity of bananas: 2 + bananas: 2, + # Quantity of grape: fruits: 3 + 'grape: fruits': 3 } ``` +Satisfied? :satisfied: + ### Ignoring unmatched lines -Lines without a matching delimiter are ignored as well (except in right-justification mode). +Lines without a matching delimiter are ignored as well (except in +right-justification mode). For example, when aligning the following code block around the colons, @@ -222,6 +232,7 @@ Then we get, ### Extending alignment rules ```vim +" Examples let g:easy_align_delimiters = { \ '>': { 'pattern': '>>\|=>\|>' }, \ '/': { 'pattern': '//\+\|/\*\|\*/' }, diff --git a/autoload/easy_align.vim b/autoload/easy_align.vim index 3cf5743..1a6e48a 100644 --- a/autoload/easy_align.vim +++ b/autoload/easy_align.vim @@ -34,6 +34,8 @@ let s:easy_align_delimiters_default = { \ ',': { 'pattern': ',', 'margin_left': '', 'margin_right': ' ', 'stick_to_left': 1 }, \ '|': { 'pattern': '|', 'margin_left': ' ', 'margin_right': ' ', 'stick_to_left': 0 }, \ '.': { 'pattern': '\.', 'margin_left': '', 'margin_right': '', 'stick_to_left': 0 }, +\ '{': { 'pattern': '(\@ 0 + + if a:nth > 0 " Positive field number if len(tokens) < a:nth continue endif - let nth = a:nth - 1 " 0-based - else + let nth = a:nth - 1 " make it 0-based + else " Negative field number + let nth = len(tokens) + a:nth if match(tokens[len(tokens) - 1], pattern.'$') == -1 - let nth = len(tokens) + a:nth - 1 - else - let nth = len(tokens) + a:nth + let nth -= 1 endif if nth < 0 || nth == len(tokens) @@ -102,53 +145,68 @@ function! s:do_align(just, cl, fl, ll, fc, lc, pattern, nth, ml, mr, stick_to_le let last = tokens[nth] let prefix = (nth > 0 ? join(tokens[0 : nth - 1], '') : '') let token = substitute(last, pattern.'$', '', '') - let suffix = substitute(join(tokens[nth + 1: -1], ''), '^\s*', '', '') - if match(last, pattern.'$') == -1 - if a:just == 0 && get(g:, 'easy_align_ignore_unmatched', 1) - continue - else - let delim = '' - endif - else - let delim = matchlist(last, pattern)[1] + let delim = get(matchlist(last, pattern.'$'), 1, '') + if empty(delim) && a:just == 0 && get(g:, 'easy_align_ignore_unmatched', 1) + continue endif - let max_just_len = max([s:strwidth(token.prefix), max_just_len]) + 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] = [prefix, token, delim, suffix] + let lines[line] = [nth, prefix, token, delim] endfor - for [line, tokens] in items(lines) - let [prefix, token, delim, suffix] = tokens + " Phase 2 + for [line, elems] in items(lines) + let tokens = a:all_tokens[line] + let [nth, prefix, token, delim] = elems + " Remove the leading whitespaces of the next token + if len(tokens) > nth + 1 + let tokens[nth + 1] = substitute(tokens[nth + 1], '^\s*', '', '') + endif + + " Pad the token with spaces let pad = repeat(' ', max_just_len - s:strwidth(prefix) - s:strwidth(token)) + let rpad = '' if a:just == 0 if a:stick_to_left - let suffix = pad . suffix + let rpad = pad else let token = token . pad endif elseif a:just == 1 let token = pad . token endif + let tokens[nth] = token + " Pad the delimiter let delim = repeat(' ', max_delim_len - s:strwidth(delim)). delim + + " Before and after the range (for blockwise visual mode) let cline = getline(line) let before = strpart(cline, 0, a:fc - 1) let after = a:lc ? strpart(cline, a:lc) : '' + " 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(suffix . after) || (empty(suffix) && stridx(after, a:mr) == 0)) ? '' : a:mr - let aligned = join([prefix, token, ml, delim, mr, suffix], '') - let aligned = empty(after) ? substitute(aligned, '\s*$', '', '') : aligned + let mr = (empty(rest) || + \ (empty(rest) && stridx(after, a:mr) == 0)) ? '' : a:mr - call setline(line, before.aligned.after) + " Align the token + let aligned = join([token, ml, delim, mr, rpad], '') + let tokens[nth] = aligned + + " Update the line + let newline = substitute(before.join(tokens, '').after, '\s*$', '', '') + call setline(line, newline) endfor if a:recursive && a:nth < max_tokens let just = a:recursive == 2 ? !a:just : a:just - call s:do_align(just, a:cl, 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(just, a:all_tokens, 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 @@ -178,67 +236,53 @@ function! easy_align#align(just, ...) range elseif c == 13 " Enter key let just = (just + 1) % len(s:just) elseif ch == '-' - if empty(n) - let n = '-' - elseif n == '-' - let n = '' - else - break + if empty(n) | let n = '-' + elseif n == '-' | let n = '' + else | break endif elseif ch == '*' - if empty(n) - let n = '*' - elseif n == '*' - let n = '**' - elseif n == '**' - let n = '' - else - break + if empty(n) | let n = '*' + elseif n == '*' | let n = '**' + elseif n == '**' | let n = '' + else | break endif - elseif c >= 48 && c <= 57 - if n[0] == '*' - break - else - let n = n . ch + elseif c >= 48 && c <= 57 " Numbers + if n[0] == '*' | break + else | let n = n . ch end else break endif endwhile elseif a:0 == 1 - let tokens = matchlist(a:1, '^\([1-9][0-9]*\|-[0-9]*\|\*\)\?\(.\)$') + let tokens = matchlist(a:1, '^\([1-9][0-9]*\|-[0-9]*\|\*\*\?\)\?\(.\)$') if empty(tokens) echo "Invalid arguments: ". a:1 return endif let [n, ch] = tokens[1:2] elseif a:0 == 2 - let n = a:1 - let ch = a:2 + let [n, ch] = a:000 else echo "Invalid number of arguments: ". a:0 ." (expected 0, 1, or 2)" return endif - if n == '*' - let nth = 1 - let recursive = 1 - elseif n == '**' - let nth = 1 - let recursive = 2 - elseif n == '-' - let nth = -1 - elseif empty(n) - let nth = 1 - elseif n != '-0' && n != string(str2nr(n)) + if n == '*' | let [nth, recursive] = [1, 1] + elseif n == '**' | let [nth, recursive] = [1, 2] + elseif n == '-' | let nth = -1 + elseif empty(n) | let nth = 1 + elseif n == '0' || ( n != '-0' && n != string(str2nr(n)) ) echon "\rInvalid field number: ". n return else let nth = n endif - let delimiters = extend(copy(s:easy_align_delimiters_default), - \ get(g:, 'easy_align_delimiters', {})) + let delimiters = s:easy_align_delimiters_default + if exists('g:easy_align_delimiters') + let delimiters = extend(copy(delimiters), g:easy_align_delimiters) + endif if has_key(delimiters, ch) let dict = delimiters[ch] diff --git a/doc/easy_align.txt b/doc/easy_align.txt index 911f726..3ba877e 100644 --- a/doc/easy_align.txt +++ b/doc/easy_align.txt @@ -67,10 +67,17 @@ In blockwise-visual mode (`CTRL-V`), EasyAlign command aligns only the selected text in the block, instead of the whole lines in the range. -Ignoring comment lines *g:easy_align_ignore_comment* +Ignoring delimiters in comments or strings *g:easy_align_ignores* ------------------------------------------------------------------------- -EasyAlign by default ignores comment lines. +EasyAlign can be configured to ignore delimiters in certain highlight +groups, such as code comments or strings. By default, delimiters that are +highlighted as code comments or strings are ignored. + + " Default: + " If a delimiter is in a highlight group whose name matches + " any of the followings, it will be ignored. + let g:easy_align_ignores = ['Comment', 'String'] For example, @@ -79,38 +86,34 @@ For example, apple: 1, # Quantity of bananas: 2 bananas: 2, - # Quantity of grapefruits: 3 - grapefruits: 3 + # Quantity of grape:fruits: 3 + 'grape:fruits': 3 } becomes { # Quantity of apples: 1 - apple: 1, + apple: 1, # Quantity of bananas: 2 - bananas: 2, - # Quantity of grapefruits: 3 - grapefruits: 3 + bananas: 2, + # Quantity of grape:fruits: 3 + 'grape:fruits': 3 } -Since finding comment lines is done heuristically using syntax highlighting -feature, this only works when syntax highlighting is enabled. +You can override `g:easy_align_ignores` to change the rule. -If you do not want comment lines to be ignored, you can unset -`g:easy_align_ignore_comment` as follows. - - let g:easy_align_ignore_comment = 0 + let g:easy_align_ignores = [] Then you get, { - # Quantity of apples: 1 - apple: 1, - # Quantity of bananas: 2 - bananas: 2, - # Quantity of grapefruits: 3 - grapefruits: 3 + # Quantity of apples: 1 + apple: 1, + # Quantity of bananas: 2 + bananas: 2, + # Quantity of grape: fruits: 3 + 'grape: fruits': 3 }