Add a proper support for \@=

This commit is contained in:
Junegunn Choi
2013-08-12 00:54:34 +09:00
parent 5e4ec85956
commit a2811dc253
7 changed files with 113 additions and 83 deletions

View File

@@ -127,7 +127,6 @@ Try `<Enter>:` here, to align text around only the first occurrences of colons.
In this case, you don't want to align around all the colons: `<Enter>*:`. In this case, you don't want to align around all the colons: `<Enter>*:`.
```yaml ```yaml
mysql: mysql:
# JDBC driver for MySQL database: # JDBC driver for MySQL database:
driver: com.mysql.jdbc.Driver driver: com.mysql.jdbc.Driver
@@ -135,7 +134,6 @@ mysql:
url: jdbc:mysql://localhost/test url: jdbc:mysql://localhost/test
database: test database: test
"user:pass":r00t:pa55 "user:pass":r00t:pa55
``` ```
Formatting multi-line method chaining Formatting multi-line method chaining
@@ -220,7 +218,7 @@ So, how do we align the trailing comments in the above lines?
Simply try `<Enter>-<space>`. The spaces in the comments are ignored, so the Simply try `<Enter>-<space>`. The spaces in the comments are ignored, so the
trailing comment in each line is considered to be a single chunk. trailing comment in each line is considered to be a single chunk.
But this doesn't work in the following case. But that doesn't work in the following case.
```ruby ```ruby
apple = 1 # comment not aligned apple = 1 # comment not aligned
@@ -262,17 +260,26 @@ command:
In this case, the second line is ignored as it doesn't contain a `#`. (The one In this case, the second line is ignored as it doesn't contain a `#`. (The one
highlighted as String is ignored.) If you don't want the second line to be highlighted as String is ignored.) If you don't want the second line to be
ignored, set `g:easy_align_ignore_unmatched` to 0, or use the following ignored, there are three options:
commands:
1. Set `g:easy_align_ignore_unmatched` to 0
2. Use the following commands:
```vim ```vim
" Using predefined rule with delimiter key # " Using predefined rule with delimiter key #
" - "iu" is fuzzy-matched to "*i*gnore_*u*nmatched"
:EasyAlign#{'iu':0}` :EasyAlign#{'iu':0}`
" Using regular expression /#/ " Using regular expression /#/
:EasyAlign/#/{'is':['String'],'iu':0}` :EasyAlign/#/{'is':['String'],'iu':0}`
``` ```
3. Update the rule with `ignore_unmatched`
```vim
let g:easy_align_delimiters['#'] = { 'pattern': '#', 'ignores': ['String'], 'ignore_unmatched': 0 } }
```
Then we get, Then we get,
```ruby ```ruby
@@ -302,18 +309,18 @@ static double pi = 3.14;
``` ```
Not bad. However, the names of the variables, `str`, `count`, and `pi` are not Not bad. However, the names of the variables, `str`, `count`, and `pi` are not
aligned with each other. Can we do better? You can clearly see that simple aligned with each other. Can we do better? We can clearly see that simple
`<Enter><space>` won't properly align those names. So we define a rule for such `<Enter><space>` won't properly align those names.
cases. So let's define an alignment rule than can handle this case.
```vim ```vim
let g:easy_align_delimiters['d'] = { let g:easy_align_delimiters['d'] = {
\ 'pattern': '\(const\|static\)\@<!\s\+', \ 'pattern': '\(const\|static\)\@<! ',
\ 'left_margin': 0, 'right_margin': 0 \ 'left_margin': 0, 'right_margin': 0
\ } \ }
``` ```
This new rule aligns text around whitespaces that are not preceded by This new rule aligns text around spaces that are *not* preceded by
`const` or `static`. Let's try it with `<Enter>d`. `const` or `static`. Let's try it with `<Enter>d`.
```c ```c
@@ -322,8 +329,8 @@ int64_t count = 1 + 2;
static double pi = 3.14; static double pi = 3.14;
``` ```
Okay, now the names are aligned. We select the lines again with `gv`, and then Okay, the names are now aligned. We select the lines again with `gv`, and then
press `<Enter>=` to finalize our alignment. press `<Enter>=` to finish our alignment.
```c ```c
const char* str = "Hello"; const char* str = "Hello";
@@ -331,8 +338,8 @@ int64_t count = 1 + 2;
static double pi = 3.14; static double pi = 3.14;
``` ```
Looks good. However, this rule is not sufficient to handle more complex cases So far, so good. However, this rule is not sufficient to handle more complex
such as C++ templates or Java generics. Take the following examples. cases involving C++ templates or Java generics. Take the following examples:
```c ```c
const char* str = "Hello"; const char* str = "Hello";
@@ -341,7 +348,7 @@ static double pi = 3.14;
static std::map<std::string, float>* scores = pointer; static std::map<std::string, float>* scores = pointer;
``` ```
We see that our rule fails with the new fourth line. We see that our rule above doesn't work anymore.
```c ```c
const char* str = "Hello"; const char* str = "Hello";
@@ -354,12 +361,12 @@ So what do we do? Let's try to improve our alignment rule.
```vim ```vim
let g:easy_align_delimiters['d'] = { let g:easy_align_delimiters['d'] = {
\ 'pattern': '\s\+\(\S\+\s*[;=]\)\@=', \ 'pattern': ' \(\S\+\s*[;=]\)\@=',
\ 'left_margin': 0, 'right_margin': 0 \ 'left_margin': 0, 'right_margin': 0
\ } \ }
``` ```
Now the new rule has changed to align text around whitespaces that are followed Now the new rule has changed to align text around spaces that are followed
by some non-whitespace characters and then an equals sign or a semi-colon. by some non-whitespace characters and then an equals sign or a semi-colon.
Try `<Enter>d` Try `<Enter>d`

View File

@@ -139,54 +139,75 @@ function! s:validate_options(opts)
return a:opts return a:opts
endfunction endfunction
function! s:do_align(just, all_tokens, fl, ll, fc, lc, pattern, nth, ml, mr, stick_to_left, ignore_unmatched, ignores, recursive) function! s:split_line(line, fc, lc, pattern, stick_to_left, ignore_unmatched, ignores)
let left = a:lc ?
\ strpart(getline(a:line), a:fc - 1, a:lc - a:fc + 1) :
\ strpart(getline(a:line), a:fc - 1)
let idx = 0
" Do not allow \zs
" 1: whole match
" 2: token
" 3: delimiter
let pattern = '^\(\(.\{-}\s*\)\(' .a:pattern. '\)\s' . (a:stick_to_left ? '*' : '\{-}') . '\)'
let tokens = []
let delims = []
" Phase 1: split
let ignorable = 0
let token = ''
while 1
let matches = matchlist(left, pattern, idx)
if empty(matches) | break | endif
let ignorable = s:highlighted_as(a:line, idx + len(matches[2]) + a:fc, a:ignores)
if ignorable
let token .= matches[1]
else
call add(tokens, token . matches[1])
call add(delims, matches[3])
let token = ''
endif
if empty(matches[1])
call s:exit("Invalid regular expression")
endif
let idx += len(matches[1])
endwhile
let leftover = token . strpart(left, idx)
if !empty(leftover)
call add(tokens, leftover)
call add(delims, '')
endif
" Skip comment line
if ignorable && len(tokens) == 1 && a:ignore_unmatched
let tokens = []
let delims = []
endif
return [tokens, delims]
endfunction
function! s:do_align(just, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth, ml, mr, stick_to_left, ignore_unmatched, ignores, recursive)
let lines = {} let lines = {}
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 min_indent = -1
let pattern = '\s*\(' .a:pattern. '\)\s' . (a:stick_to_left ? '*' : '\{-}')
" Phase 1 " Phase 1
for line in range(a:fl, a:ll) for line in range(a:fl, a:ll)
if !has_key(a:all_tokens, line) if !has_key(a:all_tokens, line)
" Split line into the tokens by the delimiters " Split line into the tokens by the delimiters
let raw_tokens = split(a:lc ? let [tokens, delims] = s:split_line(line, a:fc, a:lc, a:pattern, a:stick_to_left, a:ignore_unmatched, a:ignores)
\ strpart(getline(line), a:fc - 1, a:lc - a:fc + 1) :
\ strpart(getline(line), a:fc - 1),
\ pattern.'\zs')
let concat = 0
if empty(a:ignores)
let tokens = raw_tokens
else
" Concat adjacent tokens that are split by ignorable delimiters
let tokens = []
let idx = 0
for token in raw_tokens
let idx += len(token)
if concat
let tokens[len(tokens) - 1] .= token
else
call add(tokens, token)
endif
let concat = s:highlighted_as(line, idx + a:fc - 1, a:ignores)
endfor
endif
" Preserve indentation - merge first two tokens
if !empty(tokens) && match(tokens[0], '^\s*$') != -1
let tokens = extend([join(tokens[0:1], '')], tokens[2:-1])
endif
" Skip comment line
if concat && len(tokens) == 1 && a:ignore_unmatched
let tokens = []
endif
" Remember tokens for subsequent recursive calls " Remember tokens for subsequent recursive calls
let a:all_tokens[line] = tokens let a:all_tokens[line] = tokens
let a:all_delims[line] = delims
else else
let tokens = a:all_tokens[line] let tokens = a:all_tokens[line]
let delims = a:all_delims[line]
endif endif
" Skip empty lines " Skip empty lines
@@ -204,7 +225,7 @@ function! s:do_align(just, all_tokens, fl, ll, fc, lc, pattern, nth, ml, mr, sti
let nth = a:nth - 1 " make it 0-based let nth = a:nth - 1 " make it 0-based
else " Negative field number else " Negative field number
let nth = len(tokens) + a:nth let nth = len(tokens) + a:nth
if match(tokens[len(tokens) - 1], pattern.'$') == -1 if empty(delims[len(delims) - 1])
let nth -= 1 let nth -= 1
endif endif
@@ -213,11 +234,10 @@ function! s:do_align(just, all_tokens, fl, ll, fc, lc, pattern, nth, ml, mr, sti
endif endif
endif endif
let last = tokens[nth] let prefix = nth > 0 ? join(tokens[0 : nth - 1], '') : ''
let prefix = (nth > 0 ? join(tokens[0 : nth - 1], '') : '') let delim = delims[nth]
let token = substitute(last, pattern.'$', '', '') let token = s:rtrim( tokens[nth] )
let token = s:rtrim( strpart(token, 0, len(token) - len(s:rtrim(delim))) )
let delim = get(matchlist(last, pattern.'$'), 1, '')
if empty(delim) && !exists('tokens[nth + 1]') && a:just == 0 && a:ignore_unmatched if empty(delim) && !exists('tokens[nth + 1]') && a:just == 0 && a:ignore_unmatched
continue continue
endif endif
@@ -234,6 +254,7 @@ function! s:do_align(just, all_tokens, fl, ll, fc, lc, pattern, nth, ml, mr, sti
" Phase 2 " Phase 2
for [line, elems] in items(lines) for [line, elems] in items(lines)
let tokens = a:all_tokens[line] let tokens = a:all_tokens[line]
let delims = a:all_delims[line]
let [nth, prefix, token, delim] = elems let [nth, prefix, token, delim] = elems
" Remove the leading whitespaces of the next token " Remove the leading whitespaces of the next token
@@ -296,7 +317,7 @@ function! s:do_align(just, all_tokens, fl, ll, fc, lc, pattern, nth, ml, mr, sti
if a:recursive && a:nth < max_tokens if a:recursive && a:nth < max_tokens
let just = a:recursive == 2 ? !a:just : a:just let just = a:recursive == 2 ? !a:just : a:just
call s:do_align(just, a:all_tokens, a:fl, a:ll, a:fc, a:lc, a:pattern, call s:do_align(just, a:all_tokens, a:all_delims, a:fl, a:ll, a:fc, a:lc, a:pattern,
\ a:nth + 1, a:ml, a:mr, a:stick_to_left, a:ignore_unmatched, \ a:nth + 1, a:ml, a:mr, a:stick_to_left, a:ignore_unmatched,
\ a:ignores, a:recursive) \ a:ignores, a:recursive)
endif endif
@@ -384,10 +405,6 @@ function! s:parse_args(args)
try | call matchlist('', regexp) try | call matchlist('', regexp)
catch | call s:exit("Invalid regular expression: ". regexp) catch | call s:exit("Invalid regular expression: ". regexp)
endtry endtry
" Unsupported regular expression
if match(regexp, '\\zs') != -1
call s:exit("Using \\zs is not allowed. Use stick_to_left option instead.")
endif
return [matches[1], regexp, option, 1] return [matches[1], regexp, option, 1]
else else
let tokens = matchlist(args, '^\([1-9][0-9]*\|-[0-9]*\|\*\*\?\)\?\s*\(.\{-}\)\?$') let tokens = matchlist(args, '^\([1-9][0-9]*\|-[0-9]*\|\*\*\?\)\?\s*\(.\{-}\)\?$')
@@ -461,7 +478,8 @@ function! easy_align#align(just, expr) range
if type(ml) == 0 | let ml = repeat(' ', ml) | endif if type(ml) == 0 | let ml = repeat(' ', ml) | endif
if type(mr) == 0 | let mr = repeat(' ', mr) | endif if type(mr) == 0 | let mr = repeat(' ', mr) | endif
call s:do_align(just, {}, a:firstline, a:lastline, try
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),
@@ -473,5 +491,7 @@ function! easy_align#align(just, expr) range
\ get(dict, 'ignores', s:ignored_syntax()), \ get(dict, 'ignores', s:ignored_syntax()),
\ recur) \ recur)
call s:echon(just, n, regexp ? '/'.ch.'/' : ch) call s:echon(just, n, regexp ? '/'.ch.'/' : ch)
catch 'exit'
endtry
endfunction endfunction

View File

@@ -233,6 +233,14 @@ banana = 'Gros Michel' # comment 2
```yaml ```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:pass": r00t:pa55
mysql: mysql:
# JDBC driver for MySQL database: # JDBC driver for MySQL database:
driver: com.mysql.jdbc.Driver driver: com.mysql.jdbc.Driver
@@ -337,15 +345,6 @@ my_object .
| logger | Logger | nil | logger instance for debug logs | | logger | Logger | nil | logger instance for debug logs |
```c
const char* str = "Hello";
int64_t count = 1 + 2;
static double pi = 3.14;
static std::map<std::string, float>* scores = pointer;
```
|Option |Type |Default |Description | |Option |Type |Default |Description |
|-- |-- |-- |-- | |-- |-- |-- |-- |
|threads |Fixnum |1 |number of threads in the thread pool | |threads |Fixnum |1 |number of threads in the thread pool |
@@ -366,3 +365,12 @@ static std::map<std::string, float>* scores = pointer;
| batch_size<| Fixnum <| nil <| number of maximum items to be assigned at once<| | batch_size<| Fixnum <| nil <| number of maximum items to be assigned at once<|
| logger <| Logger <| nil <| logger instance for debug logs <| | logger <| Logger <| nil <| logger instance for debug logs <|
```c
const char* str = "Hello";
int64_t count = 1 + 2;
static double pi = 3.14;
static std::map<std::string, float>* scores = pointer;
```

View File

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

View File

@@ -39,4 +39,5 @@ set buftype=nofile
silent! ScrollPositionHide silent! ScrollPositionHide
call GFM() call GFM()
syntax sync fromstart

View File

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