Usability improvements

- Preserve indentation when a line starts with a delimiter
  - Useful for aligning multi-line method chains
- Renamed `margin_{left,right}` to `{left,right}_margin
  - Makes it easier to type in (`l`, `r`)
  - `margin_{left,right}` is still supported
- Margins can be specified as the number of spaces, or as an arbitrary string
This commit is contained in:
Junegunn Choi
2013-08-05 23:41:31 +09:00
parent 20124bc622
commit 2486b9c6df
9 changed files with 223 additions and 49 deletions

View File

@@ -141,6 +141,31 @@ mysql:
``` ```
Formatting multi-line method chaining
-------------------------------------
Try `<Enter>.` or `<Enter>*.` on the following lines.
```ruby
my_object
.method1().chain()
.second_method().call()
.third().call()
.method_4().execute()
```
Notice that the indentation is adjusted to match the shortest one among those of
the lines starting with the delimiter.
```ruby
my_object
.method1() .chain()
.second_method().call()
.third() .call()
.method_4() .execute()
```
Partial alignment in block-visual mode / Negative field index Partial alignment in block-visual mode / Negative field index
------------------------------------------------------------- -------------------------------------------------------------

View File

@@ -127,7 +127,7 @@ 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 on the left. It can be done by providing additional options in Vim dictionary
format. format.
- `:EasyAlign * /[:;]\+/ { 'stick_to_left': 1, 'margin_left': '' }` - `:EasyAlign * /[:;]\+/ { 'stick_to_left': 1, 'left_margin': '' }`
Then we get: Then we get:
@@ -136,18 +136,18 @@ Then we get:
Options keys are fuzzy-matched, so you can write as follows: Options keys are fuzzy-matched, so you can write as follows:
- `:EasyAlign * /[:;]\+/ { 'stl': 1, 'ml': '' }` - `:EasyAlign * /[:;]\+/ { 'stl': 1, 'l': 0 }`
You can even omit spaces between the arguments, so concisely (or cryptically): You can even omit spaces between the arguments, so concisely (or cryptically):
- `:EasyAlign*/[:;]\+/{'stl':1,'ml':''}` - `:EasyAlign*/[:;]\+/{'s':1,'l':0}`
Available options for each alignment are as follows. Available options for each alignment are as follows.
| Atrribute | Type | Default | | Atrribute | Type | Default |
| ---------------- | ------- | ----------------------- | | ---------------- | ---------------- | ----------------------- |
| margin_left | string | `' '` | | left_margin | number or string | 0 |
| margin_right | string | `' '` | | right_margin | number or string | 0 |
| stick_to_left | boolean | 0 | | stick_to_left | boolean | 0 |
| ignore_unmatched | boolean | 1 | | ignore_unmatched | boolean | 1 |
| ignores | array | `['String', 'Comment']` | | ignores | array | `['String', 'Comment']` |
@@ -331,14 +331,14 @@ let g:easy_align_delimiters = {
\ '#': { 'pattern': '#\+', 'ignores': ['String'] }, \ '#': { 'pattern': '#\+', 'ignores': ['String'] },
\ ']': { \ ']': {
\ 'pattern': '[\[\]]', \ 'pattern': '[\[\]]',
\ 'margin_left': '', \ 'left_margin': 0,
\ 'margin_right': '', \ 'right_margin': 0,
\ 'stick_to_left': 0 \ 'stick_to_left': 0
\ }, \ },
\ ')': { \ ')': {
\ 'pattern': '[()]', \ 'pattern': '[()]',
\ 'margin_left': '', \ 'left_margin': 0,
\ 'margin_right': '', \ 'right_margin': 0,
\ 'stick_to_left': 0 \ 'stick_to_left': 0
\ } \ }
\ } \ }

View File

@@ -27,23 +27,24 @@ endif
let g:loaded_easy_align = 1 let g:loaded_easy_align = 1
let s:easy_align_delimiters_default = { let s:easy_align_delimiters_default = {
\ ' ': { 'pattern': ' ', 'margin_left': '', 'margin_right': '', 'stick_to_left': 0 }, \ ' ': { 'pattern': ' ', 'left_margin': '', 'right_margin': '', 'stick_to_left': 0 },
\ '=': { 'pattern': '===\|<=>\|\(&&\|||\|<<\|>>\)=\|=\~\|=>\|[:+/*!%^=><&|-]\?=[#?]\?', \ '=': { 'pattern': '===\|<=>\|\(&&\|||\|<<\|>>\)=\|=\~\|=>\|[:+/*!%^=><&|-]\?=[#?]\?',
\ 'margin_left': ' ', 'margin_right': ' ', 'stick_to_left': 0 }, \ 'left_margin': ' ', 'right_margin': ' ', 'stick_to_left': 0 },
\ ':': { 'pattern': ':', 'margin_left': '', 'margin_right': ' ', 'stick_to_left': 1 }, \ ':': { 'pattern': ':', 'left_margin': '', 'right_margin': ' ', 'stick_to_left': 1 },
\ ',': { 'pattern': ',', 'margin_left': '', 'margin_right': ' ', 'stick_to_left': 1 }, \ ',': { 'pattern': ',', 'left_margin': '', 'right_margin': ' ', 'stick_to_left': 1 },
\ '|': { 'pattern': '|', 'margin_left': ' ', 'margin_right': ' ', 'stick_to_left': 0 }, \ '|': { 'pattern': '|', 'left_margin': ' ', 'right_margin': ' ', 'stick_to_left': 0 },
\ '.': { 'pattern': '\.', 'margin_left': '', 'margin_right': '', 'stick_to_left': 0 }, \ '.': { 'pattern': '\.', 'left_margin': '', 'right_margin': '', 'stick_to_left': 0 },
\ '{': { 'pattern': '(\@<!{', \ '{': { 'pattern': '(\@<!{',
\ 'margin_left': ' ', 'margin_right': ' ', 'stick_to_left': 0 }, \ 'left_margin': ' ', 'right_margin': ' ', 'stick_to_left': 0 },
\ '}': { 'pattern': '}', 'margin_left': ' ', 'margin_right': '', 'stick_to_left': 0 } \ '}': { 'pattern': '}', 'left_margin': ' ', 'right_margin': '', 'stick_to_left': 0 }
\ } \ }
let s:just = ['', '[R]'] let s:just = ['', '[R]']
let s:known_options = { let s:known_options = {
\ 'pattern': 1, 'margin_left': 1, 'margin_right': 1, 'stick_to_left': 0, \ 'pattern': [1], 'margin_left': [0, 1], 'margin_right': [0, 1], 'stick_to_left': [0],
\ 'ignores': 3, 'ignore_unmatched': 0 \ 'left_margin': [0, 1], 'right_margin': [0, 1],
\ 'ignores': [3], 'ignore_unmatched': [0]
\ } \ }
if exists("*strwidth") if exists("*strwidth")
@@ -101,7 +102,7 @@ function! s:fuzzy_lu(key)
return a:key return a:key
endif endif
let regexp = '^' . substitute(a:key, '\(.\)', '\1.*', 'g') let regexp = '^' . substitute(substitute(a:key, '-', '_', 'g'), '\(.\)', '\1.*', 'g')
let matches = filter(keys(s:known_options), 'v:val =~ regexp') let matches = filter(keys(s:known_options), 'v:val =~ regexp')
if empty(matches) if empty(matches)
@@ -116,14 +117,18 @@ endfunction
function! s:normalize_options(opts) function! s:normalize_options(opts)
let ret = {} let ret = {}
for [k, v] in items(a:opts) for [k, v] in items(a:opts)
let ret[s:fuzzy_lu(k)] = v let k = s:fuzzy_lu(k)
" Backward-compatibility
if k == 'margin_left' | let k = 'left_margin' | endif
if k == 'margin_right' | let k = 'right_margin' | endif
let ret[k] = v
endfor endfor
return s:validate_options(ret) return s:validate_options(ret)
endfunction endfunction
function! s:validate_options(opts) function! s:validate_options(opts)
for [k, v] in items(a:opts) for [k, v] in items(a:opts)
if type(v) != s:known_options[k] if index(s:known_options[k], type(v)) == -1
call s:exit("Invalid type for option: ". k) call s:exit("Invalid type for option: ". k)
endif endif
endfor endfor
@@ -135,6 +140,7 @@ function! s:do_align(just, all_tokens, fl, ll, fc, lc, pattern, nth, ml, mr, sti
let max_just_len = 0 let max_just_len = 0
let max_delim_len = 0 let max_delim_len = 0
let max_tokens = 0 let max_tokens = 0
let min_indent = -1
let pattern = '\s*\(' .a:pattern. '\)\s' . (a:stick_to_left ? '*' : '\{-}') let pattern = '\s*\(' .a:pattern. '\)\s' . (a:stick_to_left ? '*' : '\{-}')
" Phase 1 " Phase 1
@@ -212,6 +218,10 @@ function! s:do_align(just, all_tokens, fl, ll, fc, lc, pattern, nth, ml, mr, sti
continue continue
endif endif
let indent = len(matchstr(tokens[0], '^\s\+'))
if min_indent < 0 || indent < min_indent
let min_indent = indent
endif
let max_just_len = max([s:strwidth(prefix.token), 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 max_delim_len = max([s:strwidth(delim), max_delim_len])
let lines[line] = [nth, prefix, token, delim] let lines[line] = [nth, prefix, token, delim]
@@ -260,8 +270,19 @@ function! s:do_align(just, all_tokens, fl, ll, fc, lc, pattern, nth, ml, mr, sti
let mr = (empty(rest) || let mr = (empty(rest) ||
\ (empty(rest) && stridx(after, a:mr) == 0)) ? '' : a:mr \ (empty(rest) && stridx(after, a:mr) == 0)) ? '' : a:mr
" 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))
if a:just == 0
let token = ipad . token
else
let lpad = ipad
endif
endif
" Align the token " Align the token
let aligned = join([token, ml, delim, mr, rpad], '') let aligned = join([lpad, token, ml, delim, mr, rpad], '')
let tokens[nth] = aligned let tokens[nth] = aligned
" Update the line " Update the line
@@ -431,13 +452,18 @@ function! easy_align#align(just, expr) range
return return
endtry endtry
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 s:do_align(just, {}, a:firstline, a:lastline, call s:do_align(just, {}, a:firstline, a:lastline,
\ visualmode() == '' ? min([col("'<"), col("'>")]) : 1, \ visualmode() == '' ? min([col("'<"), col("'>")]) : 1,
\ visualmode() == '' ? max([col("'<"), col("'>")]) : 0, \ visualmode() == '' ? max([col("'<"), col("'>")]) : 0,
\ get(dict, 'pattern', ch), \ get(dict, 'pattern', ch),
\ nth, \ nth,
\ get(dict, 'margin_left', ' '), \ ml,
\ get(dict, 'margin_right', ' '), \ mr,
\ get(dict, 'stick_to_left', 0), \ get(dict, 'stick_to_left', 0),
\ get(dict, 'ignore_unmatched', get(g:, 'easy_align_ignore_unmatched', 1)), \ get(dict, 'ignore_unmatched', get(g:, 'easy_align_ignore_unmatched', 1)),
\ get(dict, 'ignores', s:ignored_syntax()), \ get(dict, 'ignores', s:ignored_syntax()),

View File

@@ -95,7 +95,7 @@ try these commands:
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. on the left. It can be done by providing additional options.
- :EasyAlign * /[:;]\+/ { 'stick_to_left': 1, 'margin_left': '' } - :EasyAlign * /[:;]\+/ { 'stick_to_left': 1, 'left_margin': '' }
Then we get: Then we get:
@@ -104,18 +104,18 @@ Then we get:
Options keys are fuzzy-matched, so you can write as follows: Options keys are fuzzy-matched, so you can write as follows:
- :EasyAlign * /[:;]\+/ { 'stl': 1, 'ml': '' } - :EasyAlign * /[:;]\+/ { 'stl': 1, 'l': '' }
You can even omit spaces between the arguments, so concisely (or cryptically): You can even omit spaces between the arguments, so concisely (or cryptically):
- :EasyAlign*/[:;]\+/{'stl':1,'ml':''} - :EasyAlign*/[:;]\+/{'s':1,'l':''}
Available options for each alignment are as follows. Available options for each alignment are as follows.
| Atrribute | Type | Default | | Atrribute | Type | Default |
| ---------------- | ------- | ----------------------- | | ---------------- | ---------------- | ----------------------- |
| margin_left | string | `' '` | | left_margin | number or string | 0 |
| margin_right | string | `' '` | | right_margin | number or string | 0 |
| stick_to_left | boolean | 0 | | stick_to_left | boolean | 0 |
| ignore_unmatched | boolean | 1 | | ignore_unmatched | boolean | 1 |
| ignores | array | `['String', 'Comment']` | | ignores | array | `['String', 'Comment']` |
@@ -231,14 +231,14 @@ Extending alignment rules *g:easy_align_delimiters*
\ '#': { 'pattern': '#\+' }, \ '#': { 'pattern': '#\+' },
\ ']': { \ ']': {
\ 'pattern': '[\[\]]', \ 'pattern': '[\[\]]',
\ 'margin_left': '', \ 'left_margin': 0,
\ 'margin_right': '', \ 'right_margin': 0,
\ 'stick_to_left': 0 \ 'stick_to_left': 0
\ }, \ },
\ ')': { \ ')': {
\ 'pattern': '[()]', \ 'pattern': '[()]',
\ 'margin_left': '', \ 'left_margin': 0,
\ 'margin_right': '', \ 'right_margin': 0,
\ 'stick_to_left': 0 \ 'stick_to_left': 0
\ } \ }
\ } \ }

View File

@@ -257,3 +257,102 @@ aa >= bb
aaaaa /* bbbbb */ == ccccc /* != eeeee = */ === fffff aaaaa /* bbbbb */ == ccccc /* != eeeee = */ === fffff
``` ```
my_object
.method1 .chain
.second_method.call
.third .call
.method_4 .execute
my_object
.method1 . chain
.second_method. call
.third . call
.method_4 .execute
my_object
. method1.chain
.second_method.call
. third.call
. method_4.execute
my_object
. method1.chain
.second_method.call
. third.call
. method_4.execute
my_object
. method1. chain
.second_method. call
. third. call
. method_4.execute
my_object
.method1 .chain
.second_method.call
.third .call
.method_4 .execute
my_object
.method1.chain
.second_method.call
.third.call
.method_4.execute
my_object .
method1 .chain.
second_method.call.
third .call.
method_4 .execute
| 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 |
| 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 |
| 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 |
|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 |
| 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 <|

View File

@@ -84,3 +84,26 @@ aa >= bb
aaaaa /* bbbbb */ == ccccc /* != eeeee = */ === fffff aaaaa /* bbbbb */ == ccccc /* != eeeee = */ === fffff
``` ```
my_object
.method1.chain
.second_method.call
.third.call
.method_4.execute
my_object.
method1.chain.
second_method.call.
third.call.
method_4.execute
| 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 |

View File

@@ -1 +1 @@
4Gvipjyvip 4Gvipjyvip

View File

@@ -1 +1 @@
4Gvipjyvip:EasyAlign: 4Gvipjyvip:EasyAlign:

View File

@@ -1,4 +1,5 @@
source include.vim e!
execute 'source '. expand('%:p:h') . '/include.vim'
while line('.') < line('$') while line('.') < line('$')
normal 30j normal 30j
@@ -6,7 +7,7 @@ while line('.') < line('$')
endwhile endwhile
normal gg normal gg
let @b=system('cat '. expand('%:r') . '.script') let @b=system('cat '. expand('%:p:r') . '.script')
let @a='@b:vert diffsplit ' . expand('%:r') . '.expected let @a='@b:vert diffsplit ' . expand('%:p:r') . '.expected
' '
" Syntax highlighting doesn't work " Syntax highlighting doesn't work