mirror of
https://github.com/junegunn/vim-easy-align.git
synced 2025-11-10 02:43:49 -05:00
Add a proper support for \@=
This commit is contained in:
41
EXAMPLES.md
41
EXAMPLES.md
@@ -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`
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
4Gvipjyvip
|
4Gvipjyvip
|
||||||
|
|||||||
@@ -39,4 +39,5 @@ set buftype=nofile
|
|||||||
silent! ScrollPositionHide
|
silent! ScrollPositionHide
|
||||||
|
|
||||||
call GFM()
|
call GFM()
|
||||||
|
syntax sync fromstart
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user