27 Commits
2.7.0 ... 2.9.1

Author SHA1 Message Date
Junegunn Choi
4b706efeb3 Fix helpfile 2013-12-08 00:48:59 +09:00
Junegunn Choi
9bb987599a Tags for ToC 2013-12-08 00:36:18 +09:00
Junegunn Choi
b8e93f96b8 Convert current README.md into helpfile. Added TOC (#22) 2013-12-08 00:14:50 +09:00
Junegunn Choi
6d85e93476 Make repeatable in visual mode 2013-12-06 02:11:57 +09:00
Junegunn Choi
9271fc3f8f Make visual-operator repeatable (#24) 2013-12-05 23:16:33 +09:00
Junegunn Choi
d3eec7fa8b Remove an unnecessary function call 2013-12-05 20:50:46 +09:00
Junegunn Choi
62387b78e0 Fix normal-mode repeat affected by visual-mode operator 2013-12-05 13:36:01 +09:00
Junegunn Choi
f400e4d8d1 Wording 2013-12-03 02:27:30 +09:00
Junegunn Choi
9716fb3917 One-minute guide 2013-12-03 02:25:40 +09:00
Junegunn Choi
353ae9b071 One-minute guide 2013-12-03 02:21:05 +09:00
Junegunn Choi
ba4ba6e3c4 Fix vim-repeat integration 2013-12-02 00:33:33 +09:00
Junegunn Choi
da284f908e <Plug>(EasyAlign) / <Plug>(LiveEasyAlign) for vim-repeat integration 2013-12-01 22:58:13 +09:00
Junegunn Choi
8d4d5b7941 Update documentation 2013-11-30 13:07:26 +09:00
Junegunn Choi
165bda1d95 Add <Plug>(EasyAlignOperator) 2013-11-30 13:04:32 +09:00
Junegunn Choi
ddc1e00b81 Update README
- Add <silent> to opfunc mapping
2013-11-30 11:55:54 +09:00
Junegunn Choi
462a800064 Generic operator function with interactive mode 2013-11-30 11:41:11 +09:00
Junegunn Choi
3921d0bab3 Fix #20 - Alignment with tabs inserting spaces 2013-11-20 07:27:39 +09:00
Junegunn Choi
ea1ea51ef1 Fix link to slow GIF 2013-11-15 02:27:14 +09:00
Junegunn Choi
2daae46a43 Fix indentation option to work with hard tabs (#19) 2013-10-31 12:32:07 +09:00
Junegunn Choi
2b119f9bb6 Fix issue #19 (hard tab indentation) 2013-10-31 11:08:02 +09:00
Junegunn Choi
15bcbc9499 Update readme 2013-10-29 23:24:14 +09:00
Junegunn Choi
54e6b5d866 Update screenshot 2013-10-29 22:17:23 +09:00
Junegunn Choi
1f28ce346e Update README 2013-10-27 03:14:40 +09:00
Junegunn Choi
2832a76cea Implement filter option (#16, #17)
This commit implements filter option which can be used to filter lines within
the range based on the given pattern. The value of filter option should be
either `g/pattern/` or `v/pattern/`. The former aligns lines that match the
pattern, the latter aligns lines that do not match the pattern.
2013-10-27 03:10:04 +09:00
Junegunn Choi
1a232ac19b Revert "Make user confirm regular expression on live interactive mode"
This reverts commit a76cfdb8ae.
2013-10-25 02:17:52 +09:00
Junegunn Choi
a76cfdb8ae Make user confirm regular expression on live interactive mode 2013-10-25 02:11:43 +09:00
Junegunn Choi
c3a7842b0d Remove a redundant Expect block 2013-10-22 11:24:46 +09:00
6 changed files with 1399 additions and 373 deletions

214
README.md
View File

@@ -6,9 +6,9 @@ A simple, easy-to-use Vim alignment plugin.
Demo
----
![Screencast](https://raw.github.com/junegunn/vim-easy-align/gif/vim-easy-align.gif)
![Screencast](https://raw.github.com/junegunn/i/master/vim-easy-align.gif)
(Too fast? Slower GIF is [here](https://raw.github.com/junegunn/vim-easy-align/gif/vim-easy-align-slow.gif))
(Too fast? Slower GIF is [here](https://raw.github.com/junegunn/i/master/vim-easy-align-slow.gif))
Features
--------
@@ -49,11 +49,52 @@ and extract in ~/.vim or
1. Add `Plug 'junegunn/vim-easy-align'` to .vimrc
2. Run `:PlugInstall`
TL;DR - One-minute guide
------------------------
Add the following mappings to your .vimrc.
```vim
" Start interactive EasyAlign in visual mode
vmap <Enter> <Plug>(EasyAlign)
" Start interactive EasyAlign with a Vim movement
nmap <Leader>a <Plug>(EasyAlign)
```
And with the following lines of text,
```
apple = red
grass=green
sky=blue
```
try these commands:
- `vip<Enter>=`
- `v`isual-select `i`nner `p`aragraph
- Start EasyAlign command (`<Enter>`)
- Align around `=`
- `<Leader>aip=`
- Start EasyAlign command (`<Leader>a`) for `i`nner `p`aragraph
- Align around `=`
Notice that the commands are repeatable with `.` key if you have installed
[repeat.vim](https://github.com/tpope/vim-repeat). Install
[visualrepeat](https://github.com/vim-scripts/visualrepeat) as well if you want
to repeat in visual mode. Or you can add the following mapping to your .vimrc.
```vim
" Repeat alignment in visual mode with . key
vmap . <Plug>(EasyAlignRepeat)
```
Usage
-----
_vim-easy-align_ defines `:EasyAlign` command (and the right-align
variant `:EasyAlign!`) for visual mode.
variant `:EasyAlign!`).
| Mode | Command |
| ------------------------- | ------------------------------------------------ |
@@ -65,7 +106,7 @@ variant `:EasyAlign!`) for visual mode.
### Concept of _alignment rule_
An *alignment rule* is a predefined set of options for common alignment tasks,
which is identified by a single character, *DELIMITER KEY*, such as `<space>`,
which is identified by a single character, *DELIMITER KEY*, such as `<Space>`,
`=`, `:`, `.`, `|`, `&`, and `,`.
Think of it as a shortcut. Instead of writing regular expression and setting
@@ -74,18 +115,35 @@ several options, you can just type in a single character.
### Interactive mode
The command will go into the interactive mode when no argument is given.
For convenience, it is advised that you define a mapping for triggering it in
your `.vimrc`.
```vim
vnoremap <silent> <Enter> :EasyAlign<Enter>
:EasyAlign
```
(Of course you can use any key combination as the trigger. e.g. `<Leader>a`)
However, it is strongly advised that you define mappings for triggering it in
your `.vimrc` as follows. (Of course you can use any key combination as the
trigger.)
```vim
" For visual mode (e.g. vip<Enter>)
vmap <Enter> <Plug>(EasyAlign)
" For normal mode, with Vim movement (e.g. <Leader>aip)
nmap <Leader>a <Plug>(EasyAlign)
```
The advantages of using the above mappings are two-fold:
- They require less keystrokes
- They make alignments repeatable with
[repeat.vim](https://github.com/tpope/vim-repeat) and
[visualrepeat](https://github.com/vim-scripts/visualrepeat)
With the mapping, you can align selected lines of text with only a few keystrokes.
1. `<Enter>` key to start interactive EasyAlign command
1. `<Enter>` key in visual mode, or `<Leader>a` followed by a Vim movement to
start interactive EasyAlign command
1. Optional: Enter keys to select alignment mode (left, right, or center)
1. Optional: N-th delimiter (default: 1)
- `1` Around the 1st occurrences of delimiters
@@ -96,19 +154,19 @@ With the mapping, you can align selected lines of text with only a few keystroke
- `-` Around the last occurrences of delimiters (`-1`)
- `-2` Around the second to last occurrences of delimiters
- ...
1. Delimiter key (a single keystroke; `<space>`, `=`, `:`, `.`, `|`, `&`, `,`)
1. Delimiter key (a single keystroke; `<Space>`, `=`, `:`, `.`, `|`, `&`, `,`)
Alignment rules for the following delimiters have been defined to meet the most needs.
| Delimiter key | Description/Use cases |
| ------------- | -------------------------------------------------------------------- |
| `<space>` | General alignment around whitespaces |
| `<Space>` | General alignment around whitespaces |
| `=` | Operators containing equals sign (`=`, `==,` `!=`, `+=`, `&&=`, ...) |
| `:` | Suitable for formatting JSON or YAML |
| `.` | Multi-line method chaining |
| `,` | Multi-line method arguments |
| `&` | LaTeX tables (matches `&` and `\\`) |
| &#124; | Table markdown |
| `<Bar>` | Table markdown |
You can override these default rules or define your own rules with
`g:easy_align_delimiters`, which will be described in
@@ -116,21 +174,21 @@ You can override these default rules or define your own rules with
#### Example command sequences
| With visual map | Description | Equivalent command |
| ------------------- | ------------------------------------------------------- | ---------------------- |
| `<Enter><space>` | Alignment around 1st whitespaces | `:'<,'>EasyAlign\ ` |
| `<Enter>2<space>` | Alignment around 2nd whitespaces | `:'<,'>EasyAlign2\ ` |
| `<Enter>-<space>` | Alignment around the last whitespaces | `:'<,'>EasyAlign-\ ` |
| `<Enter>-2<space>` | Alignment around the 2nd to last whitespaces | `:'<,'>EasyAlign-2\ ` |
| `<Enter>:` | Alignment around 1st colon (`key: value`) | `:'<,'>EasyAlign:` |
| `<Enter><Right>:` | Alignment around 1st colon (`key : value`) | `:'<,'>EasyAlign:s0l1` |
| `<Enter>=` | Alignment around 1st operators with = | `:'<,'>EasyAlign=` |
| `<Enter>2=` | Alignment around 2nd operators with = | `:'<,'>EasyAlign2=` |
| `<Enter>3=` | Alignment around 3rd operators with = | `:'<,'>EasyAlign3=` |
| `<Enter>*=` | Alignment around all operators with = | `:'<,'>EasyAlign*=` |
| `<Enter>**=` | Left-right alternating alignment around all = operators | `:'<,'>EasyAlign**=` |
| `<Enter><Enter>=` | Right alignment around 1st equals signs | `:'<,'>EasyAlign!=` |
| `<Enter><Enter>**=` | Right-left alternating alignment around all = operators | `:'<,'>EasyAlign!**=` |
| With visual map | Description | Equivalent command |
| ------------------- | ---------------------------------- | ---------------------- |
| `<Enter><Space>` | Around 1st whitespaces | `:'<,'>EasyAlign\ ` |
| `<Enter>2<Space>` | Around 2nd whitespaces | `:'<,'>EasyAlign2\ ` |
| `<Enter>-<Space>` | Around the last whitespaces | `:'<,'>EasyAlign-\ ` |
| `<Enter>-2<Space>` | Around the 2nd to last whitespaces | `:'<,'>EasyAlign-2\ ` |
| `<Enter>:` | Around 1st colon (`key: value`) | `:'<,'>EasyAlign:` |
| `<Enter><Right>:` | Around 1st colon (`key : value`) | `:'<,'>EasyAlign:s0l1` |
| `<Enter>=` | Around 1st operators with = | `:'<,'>EasyAlign=` |
| `<Enter>2=` | Around 2nd operators with = | `:'<,'>EasyAlign2=` |
| `<Enter>3=` | Around 3rd operators with = | `:'<,'>EasyAlign3=` |
| `<Enter>*=` | Around all operators with = | `:'<,'>EasyAlign*=` |
| `<Enter>**=` | Left-right alternating around = | `:'<,'>EasyAlign**=` |
| `<Enter><Enter>=` | Right alignment around 1st = | `:'<,'>EasyAlign!=` |
| `<Enter><Enter>**=` | Right-left alternating around = | `:'<,'>EasyAlign!**=` |
#### Using regular expressions
@@ -150,14 +208,15 @@ keys listed below. The meaning of each option will be described in
[the following sections](https://github.com/junegunn/vim-easy-align#alignment-options).
| Key | Option | Values |
| -------- | ------------------ | -------------------------------------------------- |
| --------- | ------------------ | -------------------------------------------------- |
| `CTRL-F` | `filter` | Input string (`[gv]/.*/?`) |
| `CTRL-I` | `indentation` | shallow, deep, none, keep |
| `CTRL-L` | `left_margin` | Input number or string |
| `CTRL-R` | `right_margin` | Input number or string |
| `CTRL-D` | `delimiter_align` | left, center, right |
| `CTRL-U` | `ignore_unmatched` | 0, 1 |
| `CTRL-G` | `ignore_groups` | [], ['String'], ['Comment'], ['String', 'Comment'] |
| `CTRL-O` | `mode_sequence` | Input string of `/[lrc]+\*{0,2}/` |
| `CTRL-O` | `mode_sequence` | Input string (`/[lrc]+\*{0,2}/`) |
| `<Left>` | `stick_to_left` | `{ 'stick_to_left': 1, 'left_margin': 0 }` |
| `<Right>` | `stick_to_left` | `{ 'stick_to_left': 0, 'left_margin': 1 }` |
| `<Down>` | `*_margin` | `{ 'left_margin': 0, 'right_margin': 0 }` |
@@ -169,18 +228,33 @@ repeatable, non-interactive command recorded in `g:easy_align_last_command`.
:<C-R>=g:easy_align_last_command<Enter><Enter>
```
### EasyAlign as Vim operator
With normal-mode map to `<Plug>(EasyAlign)`, EasyAlign command becomes a Vim
operator that can be used with any Vim movement.
```vim
nmap <Leader>a <Plug>(EasyAlign)
```
Now without going into visual mode, you can align the lines in the paragraph
with `<Leader>aip=`, `<Leader>aip*|`, or `<Leader>aip:`. And if you have
installed [repeat.vim](https://github.com/tpope/vim-repeat) by Tim Pope, the
exact alignment can be repeated with `.` key.
### Live interactive mode
If you're performing a complex alignment where multiple options should be
carefully adjusted, try "live interactive mode" which aligns the text on-the-fly
as you type in.
carefully adjusted, try "live interactive mode" where you can preview the result
of the alignment on-the-fly as you type in.
Live interactive mode can be started with `:LiveEasyAlign` command which takes
the same parameters as `:EasyAlign`. I suggest you define the following mapping
in addition to the one for `:EasyAlign` command.
the same parameters as `:EasyAlign`. I suggest you define mappings such as
follows in addition to the ones for `:EasyAlign` command.
```vim
vnoremap <silent> <Leader><Enter> :LiveEasyAlign<Enter>
vmap <Leader><Enter> <Plug>(LiveEasyAlign)
nmap <Leader>A <Plug>(LiveEasyAlign)
```
In live interactive mode, you have to type in the same delimiter (or `CTRL-X` on
@@ -240,16 +314,17 @@ notation.
The following table summarizes the shorthand notation.
| Option | Expression |
| ---------------- | ---------- |
| left_margin | `l[0-9]+` |
| right_margin | `r[0-9]+` |
| stick_to_left | `s[01]` |
| ignore_unmatched | `iu[01]` |
| ignore_groups | `ig\[.*\]` |
| delimiter_align | `d[lrc]` |
| mode_sequence | `m[lrc*]*` |
| indentation | `i[ksdn]` |
| Option | Expression |
| ------------------ | ---------- |
| `filter` | `[gv]/.*/` |
| `left_margin` | `l[0-9]+` |
| `right_margin` | `r[0-9]+` |
| `stick_to_left` | `s[01]` |
| `ignore_unmatched` | `iu[01]` |
| `ignore_groups` | `ig\[.*\]` |
| `delimiter_align` | `d[lrc]` |
| `mode_sequence` | `m[lrc*]*` |
| `indentation` | `i[ksdn]` |
For your information, the same thing can be done in the interactive mode as well
with the following key combination.
@@ -287,25 +362,6 @@ my_hash = { :a => 1,
However, in this case, we don't really need blockwise visual mode
since the same can be easily done using the negative N-th parameter: `<Enter>-=`
### EasyAlign as Vim operator
You can define an operator function which executes EasyAlign command, so that it
can be used with a Vim movement.
```vim
function! s:easy_align_1st_eq(type, ...)
'[,']EasyAlign=
endfunction
nnoremap <Leader>= :set opfunc=<SID>easy_align_1st_eq<Enter>g@
function! s:easy_align_1st_colon(type, ...)
'[,']EasyAlign:
endfunction
nnoremap <Leader>: :set opfunc=<SID>easy_align_1st_colon<Enter>g@
```
Now without going into visual mode, you can align the lines in the paragraph
by `<Leader>=ip` or `<Leader>:ip`.
Alignment options
-----------------
@@ -314,6 +370,7 @@ Alignment options
| Option | Type | Default | Description |
| ------------------ | ------- | --------------------- | ------------------------------------------------------- |
| `filter` | string | | Line filtering expression: `g/../` or `v/../` |
| `left_margin` | number | 1 | Number of spaces to attach before delimiter |
| `left_margin` | string | `' '` | String to attach before delimiter |
| `right_margin` | number | 1 | Number of spaces to attach after delimiter |
@@ -334,6 +391,7 @@ There are 4 ways to set alignment options (from lowest precedence to highest):
| Option name | Shortcut key | Abbreviated | Global variable |
| ------------------ | ------------------- | ----------- | ------------------------------- |
| `filter` | `CTRL-F` | `[gv]/.*/` | |
| `left_margin` | `CTRL-L` | `l[0-9]+` | |
| `right_margin` | `CTRL-R` | `r[0-9]+` | |
| `stick_to_left` | `<Left>`, `<Right>` | `s[01]` | |
@@ -343,6 +401,30 @@ There are 4 ways to set alignment options (from lowest precedence to highest):
| `delimiter_align` | `CTRL-D` | `d[lrc]` | `g:easy_align_delimiter_align` |
| `mode_sequence` | `CTRL-O` | `m[lrc*]*` | |
### Filtering lines
With `filter` option, you can align lines that only match or do not match a
given pattern. There are several ways to set the pattern.
1. Press `CTRL-F` in interactive mode and input `g/pat/` or `v/pat/`
2. In command-line, it can be written in dictionary format: `{'filter': 'g/pat/'}`
3. Or in shorthand notation: `g/pat/` or `v/pat/`
(You don't need to escape '/'s in the regular expression)
#### Examples
```vim
" Start interactive mode with filter option set to g/hello/
EasyAlign g/hello/
" Start live interactive mode with filter option set to v/goodbye/
LiveEasyAlign v/goodbye/
" Align the lines with 'hi' around the first colons
EasyAlign:g/hi/
```
### Ignoring delimiters in comments or strings
EasyAlign can be configured to ignore delimiters in certain syntax highlight
@@ -619,9 +701,9 @@ Although the default rules should cover the most of the use cases,
you can extend the rules by setting a dictionary named `g:easy_align_delimiters`.
You may refer to the definitions of the default alignment rules
[here](https://github.com/junegunn/vim-easy-align/blob/2.6.1/autoload/easy_align.vim#L29).
[here](https://github.com/junegunn/vim-easy-align/blob/2.9.0/autoload/easy_align.vim#L29).
#### Example
#### Examples
```vim
let g:easy_align_delimiters = {
@@ -717,7 +799,7 @@ It even feels like a native Vim command!
- *Right-align*: `<Enter><Enter>`
- around the *second* occurrences: `2`
- of *whitespaces*: `<space>`
- of *whitespaces*: `<Space>`
For the simplest cases, Tabular and Align are also easy to use. But sooner or
later, you will find yourself scratching your head, trying to come up with some

View File

@@ -47,7 +47,7 @@ let s:known_options = {
\ 'margin_left': [0, 1], 'margin_right': [0, 1], 'stick_to_left': [0],
\ 'left_margin': [0, 1], 'right_margin': [0, 1], 'indentation': [1],
\ 'ignore_groups': [3 ], 'ignore_unmatched': [0 ], 'delimiter_align': [1],
\ 'mode_sequence': [1 ], 'ignores': [3]
\ 'mode_sequence': [1 ], 'ignores': [3], 'filter': [1]
\ }
let s:option_values = {
@@ -61,14 +61,16 @@ let s:shorthand = {
\ 'margin_left': 'lm', 'margin_right': 'rm', 'stick_to_left': 'stl',
\ 'left_margin': 'lm', 'right_margin': 'rm', 'indentation': 'idt',
\ 'ignore_groups': 'ig', 'ignore_unmatched': 'iu', 'delimiter_align': 'da',
\ 'mode_sequence': 'm', 'ignores': 'ig'
\ 'mode_sequence': 'm', 'ignores': 'ig', 'filter': 'f'
\ }
if exists("*strwidth")
let s:strwidth = function('strwidth')
function! s:strwidth(str)
return strwidth(a:str) + len(matchstr(a:str, '^\t*')) * (&tabstop - 1)
endfunction
else
function! s:strwidth(str)
return len(split(a:str, '\zs'))
return len(split(a:str, '\zs')) + len(matchstr(a:str, '^\t*')) * (&tabstop - 1)
endfunction
endif
@@ -333,9 +335,16 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, nth, r
let max = { 'pivot_len': 0.0, 'token_len': 0, 'just_len': 0, 'delim_len': 0,
\ 'indent': 0, 'tokens': 0, 'strip_len': 0 }
let d = a:dict
let [f, fx] = s:parse_filter(d.filter)
" Phase 1
for line in range(a:fl, a:ll)
if f == 1 && getline(line) !~ fx
continue
elseif f == -1 && getline(line) =~ fx
continue
endif
if !has_key(a:all_tokens, line)
" Split line into the tokens by the delimiters
let [tokens, delims] = s:split_line(
@@ -387,11 +396,13 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, nth, r
continue
endif
let indent = match(tokens[0], '^\s*\zs')
let indent = s:strwidth(matchstr(tokens[0], '^\s*'))
if min_indent < 0 || indent < min_indent
let min_indent = indent
endif
if mode ==? 'c' | let token .= matchstr(token, '^\s*') | endif
if mode ==? 'c'
let token .= substitute(matchstr(token, '^\s*'), '\t', repeat(' ', &tabstop), 'g')
endif
let [pw, tw] = [s:strwidth(prefix), s:strwidth(token)]
let max.indent = max([max.indent, indent])
let max.token_len = max([max.token_len, tw])
@@ -411,11 +422,11 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, nth, r
if a:nth == 1
let idt = d.indentation
if idt ==? 'd'
let indent = repeat(' ', max.indent)
let indent = max.indent
elseif idt ==? 's'
let indent = repeat(' ', min_indent)
let indent = min_indent
elseif idt ==? 'n'
let indent = ''
let indent = 0
elseif idt !=? 'k'
call s:exit('Invalid indentation: ' . idt)
end
@@ -428,9 +439,22 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, nth, r
for [line, elems] in items(lines)
let [nth, prefix, token, delim] = elems
let token = substitute(token, '^\s*', indent, '')
let tindent = matchstr(token, '^\s*')
while 1
let len = s:strwidth(tindent)
if len < indent
let tindent .= repeat(' ', indent - len)
break
elseif len > indent
let tindent = tindent[0 : -2]
else
break
endif
endwhile
let token = tindent . s:ltrim(token)
if mode ==? 'c'
let token = substitute(token, '\s*$', indent, '')
let token = substitute(token, '\s*$', repeat(' ', indent), '')
endif
let [pw, tw] = [s:strwidth(prefix), s:strwidth(token)]
let max.token_len = max([max.token_len, tw])
@@ -469,7 +493,8 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, nth, r
endif
elseif mode ==? 'r'
let pad = repeat(' ', max.just_len - pw - tw)
let token = pad . token
let indent = matchstr(token, '^\s*')
let token = indent . pad . s:ltrim(token)
elseif mode ==? 'c'
let p1 = max.pivot_len - (pw + tw / 2.0)
let p2 = (max.token_len - tw) / 2.0
@@ -478,7 +503,8 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, nth, r
else | let p2 = floor(p2)
endif
let strip = float2nr(ceil((max.token_len - max.strip_len) / 2.0))
let token = repeat(' ', float2nr(pf1)) .token. repeat(' ', float2nr(p2))
let indent = matchstr(token, '^\s*')
let token = indent. repeat(' ', float2nr(pf1)) .s:ltrim(token). repeat(' ', float2nr(p2))
let token = substitute(token, repeat(' ', strip) . '$', '', '')
if d.stick_to_left
@@ -520,7 +546,7 @@ function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, nth, r
" Adjust indentation of the lines starting with a delimiter
let lpad = ''
if nth == 0
let ipad = repeat(' ', min_indent - len(token.ml))
let ipad = repeat(' ', min_indent - s:strwidth(token.ml))
if mode ==? 'l'
let token = ipad . token
else
@@ -703,6 +729,16 @@ function! s:interactive(range, modes, n, d, opts, rules, vis, live)
else
let warn = 'Invalid regular expression: '.ch
endif
elseif ch == "\<C-F>"
let f = s:input("Filter (g/../ or v/../): ", get(opts, 'f', ''), a:vis)
let m = matchlist(f, '^[gv]/\(.\{-}\)/\?$')
if empty(f)
silent! call remove(opts, 'f')
elseif !empty(m) && s:valid_regexp(m[1])
let opts['f'] = f
else
let warn = 'Invalid filter expression'
endif
elseif ch =~ '[[:print:]]'
let check = 1
else
@@ -756,9 +792,9 @@ function! s:test_regexp(regexp)
endfunction
let s:shorthand_regex =
\ '\s*\('
\ '\s*\%('
\ .'\(lm\?[0-9]\+\)\|\(rm\?[0-9]\+\)\|\(iu[01]\)\|\(s\%(tl\)\?[01]\)\|'
\ .'\(da\?[clr]\)\|\(ms\?[lrc*]\+\)\|\(i\%(dt\)\?[kdsn]\)\|\(ig\[.*\]\)'
\ .'\(da\?[clr]\)\|\(ms\?[lrc*]\+\)\|\(i\%(dt\)\?[kdsn]\)\|\([gv]/.*/\)\|\(ig\[.*\]\)'
\ .'\)\+\s*$'
function! s:parse_shorthand_opts(expr)
@@ -773,13 +809,17 @@ function! s:parse_shorthand_opts(expr)
else
let match = matchlist(expr, regex)
if empty(match) | break | endif
for m in filter(match[ 2 : -1 ], '!empty(v:val)')
for key in ['lm', 'rm', 'l', 'r', 'stl', 's', 'iu', 'da', 'd', 'ms', 'm', 'ig', 'i']
for m in filter(match[ 1 : -1 ], '!empty(v:val)')
for key in ['lm', 'rm', 'l', 'r', 'stl', 's', 'iu', 'da', 'd', 'ms', 'm', 'ig', 'i', 'g', 'v']
if stridx(tolower(m), key) == 0
let rest = strpart(m, len(key))
if key == 'i' | let key = 'idt' | endif
if key == 'g' || key == 'v'
let rest = key.rest
let key = 'f'
endif
if key == 'idt' || index(['d', 'm'], key[0]) >= 0
if key == 'idt' || index(['d', 'f', 'm'], key[0]) >= 0
let opts[key] = rest
elseif key == 'ig'
try
@@ -877,6 +917,15 @@ function! s:parse_args(args)
endif
endfunction
function! s:parse_filter(f)
let m = matchlist(a:f, '^\([gv]\)/\(.\{-}\)/\?$')
if empty(m)
return [0, '']
else
return [m[1] == 'g' ? 1 : -1, m[2]]
endif
endfunction
function! s:interactive_modes(bang)
return get(g:,
\ (a:bang ? 'easy_align_bang_interactive_modes' : 'easy_align_interactive_modes'),
@@ -936,6 +985,8 @@ function! s:build_dict(delimiters, ch, regexp, opts)
\ get(dict, 'ignore_unmatched', get(g:, 'easy_align_ignore_unmatched', 2))
let dict.ignore_groups =
\ get(dict, 'ignore_groups', get(dict, 'ignores', s:ignored_syntax()))
let dict.filter =
\ get(dict, 'filter', '')
return dict
endfunction
@@ -990,10 +1041,16 @@ function s:summarize(opts, recur, mode_sequence)
return copts
endfunction
function! s:align(bang, live, first_line, last_line, expr)
function! s:align(bang, live, visualmode, first_line, last_line, expr)
" Heuristically determine if the user was in visual mode
let vis = a:first_line == line("'<") && a:last_line == line("'>")
let bvis = vis && char2nr(visualmode()) == 22 " ^V
if empty(a:visualmode)
let vis = a:first_line == line("'<") && a:last_line == line("'>")
let bvis = vis && visualmode() == "\<C-V>"
" Visual-mode explicitly given
else
let vis = 1
let bvis = a:visualmode == "\<C-V>"
end
let range = [a:first_line, a:last_line]
let modes = s:interactive_modes(a:bang)
let mode = modes[0]
@@ -1029,9 +1086,9 @@ function! s:align(bang, live, first_line, last_line, expr)
endtry
endfunction
function! easy_align#align(bang, live, expr) range
function! easy_align#align(bang, live, visualmode, expr) range
try
call s:align(a:bang, a:live, a:firstline, a:lastline, a:expr)
call s:align(a:bang, a:live, a:visualmode, a:firstline, a:lastline, a:expr)
catch 'exit'
endtry
endfunction

File diff suppressed because it is too large Load Diff

View File

@@ -26,5 +26,112 @@ if exists("g:loaded_easy_align_plugin")
endif
let g:loaded_easy_align_plugin = 1
command! -nargs=* -range -bang EasyAlign <line1>,<line2>call easy_align#align('<bang>' == '!', 0, <q-args>)
command! -nargs=* -range -bang LiveEasyAlign <line1>,<line2>call easy_align#align('<bang>' == '!', 1, <q-args>)
command! -nargs=* -range -bang EasyAlign <line1>,<line2>call easy_align#align('<bang>' == '!', 0, '', <q-args>)
command! -nargs=* -range -bang LiveEasyAlign <line1>,<line2>call easy_align#align('<bang>' == '!', 1, '', <q-args>)
let s:last_command = 'EasyAlign'
function! s:remember_visual(mode)
let s:last_visual = [a:mode, abs(line("'>") - line("'<")), abs(col("'>") - col("'<"))]
endfunction
function! s:repeat_visual()
let [mode, ldiff, cdiff] = s:last_visual
let cmd = 'normal! '.mode
if ldiff > 0
let cmd .= ldiff . 'j'
endif
let ve_save = &virtualedit
try
if mode == "\<C-V>"
if cdiff > 0
let cmd .= cdiff . 'l'
endif
set virtualedit+=block
endif
execute cmd.":\<C-r>=g:easy_align_last_command\<Enter>\<Enter>"
call s:set_repeat()
finally
if ve_save != &virtualedit
let &virtualedit = ve_save
endif
endtry
endfunction
function! s:repeat_in_visual()
if exists('g:easy_align_last_command')
call s:remember_visual(visualmode())
call s:repeat_visual()
endif
endfunction
function! s:set_repeat()
silent! call repeat#set("\<Plug>(EasyAlignRepeat)")
endfunction
function! s:generic_easy_align_op(type, vmode, live)
let sel_save = &selection
let &selection = "inclusive"
if a:vmode
let vmode = a:type
call s:remember_visual(vmode)
else
let tail = "\<C-c>"
if a:type == 'line'
silent execute "normal! '[V']".tail
elseif a:type == 'block'
silent execute "normal! `[\<C-V>`]".tail
else
silent execute "normal! `[v`]".tail
endif
let vmode = ''
unlet! s:last_visual
endif
try
if get(g:, 'easy_align_need_repeat', 0)
execute "'<,'>". g:easy_align_last_command
else
'<,'>call easy_align#align('<bang>' == '!', a:live, vmode, '')
end
call s:set_repeat()
finally
let &selection = sel_save
endtry
endfunction
function! s:easy_align_op(type, ...)
call s:generic_easy_align_op(a:type, a:0, 0)
endfunction
function! s:live_easy_align_op(type, ...)
call s:generic_easy_align_op(a:type, a:0, 1)
endfunction
function! s:easy_align_repeat()
if exists('s:last_visual')
call s:repeat_visual()
else
try
let g:easy_align_need_repeat = 1
normal! .
finally
unlet! g:easy_align_need_repeat
endtry
endif
endfunction
nnoremap <silent> <Plug>(EasyAlign) :set opfunc=<SID>easy_align_op<Enter>g@
vnoremap <silent> <Plug>(EasyAlign) :<C-U>call <SID>easy_align_op(visualmode(), 1)<Enter>
nnoremap <silent> <Plug>(LiveEasyAlign) :set opfunc=<SID>live_easy_align_op<Enter>g@
vnoremap <silent> <Plug>(LiveEasyAlign) :<C-U>call <SID>live_easy_align_op(visualmode(), 1)<Enter>
" vim-repeat support
nnoremap <silent> <Plug>(EasyAlignRepeat) :call <SID>easy_align_repeat()<Enter>
vnoremap <silent> <Plug>(EasyAlignRepeat) :<C-U>call <SID>repeat_in_visual()<Enter>
" Backward-compatibility (deprecated)
nnoremap <silent> <Plug>(EasyAlignOperator) :set opfunc=<SID>easy_align_op<Enter>g@

View File

@@ -1,3 +1,6 @@
Execute:
Save &tabstop
Given (Table):
|a|b|c|d|
| -|-|>-|-|
@@ -40,3 +43,80 @@ Execute:
Expect:
a <|>b <|>c <|
aa<|>bb<|>cc<|
Given (Tab-indented code (#20)):
class MyUnitTest(unittest.TestCase):
def test_base(self):
n2f = {}
n2v = {}
f2v = {}
n2gv = {}
n2vt = {}
Execute:
set tabstop=1
%EasyAlign=
Expect:
class MyUnitTest(unittest.TestCase):
def test_base(self):
n2f = {}
n2v = {}
f2v = {}
n2gv = {}
n2vt = {}
Execute:
set tabstop=2
%EasyAlign=
Expect:
class MyUnitTest(unittest.TestCase):
def test_base(self):
n2f = {}
n2v = {}
f2v = {}
n2gv = {}
n2vt = {}
Execute:
set tabstop=4
%EasyAlign=
Expect:
class MyUnitTest(unittest.TestCase):
def test_base(self):
n2f = {}
n2v = {}
f2v = {}
n2gv = {}
n2vt = {}
Execute:
set tabstop=8
%EasyAlign=
Expect:
class MyUnitTest(unittest.TestCase):
def test_base(self):
n2f = {}
n2v = {}
f2v = {}
n2gv = {}
n2vt = {}
Execute:
set tabstop=12
%EasyAlign=
Expect:
class MyUnitTest(unittest.TestCase):
def test_base(self):
n2f = {}
n2v = {}
f2v = {}
n2gv = {}
n2vt = {}
Execute:
Restore

View File

@@ -2,7 +2,7 @@ Execute (Clean up test environment):
Save g:easy_align_ignore_groups, g:easy_align_ignore_unmatched
Save g:easy_align_indentation, g:easy_align_delimiter_align
Save g:easy_align_interactive_modes, g:easy_align_bang_interactive_modes
Save g:easy_align_delimiters
Save g:easy_align_delimiters, &tabstop
Save mapleader
" TODO: revert after test
@@ -15,11 +15,19 @@ Execute (Clean up test environment):
let g:easy_align_delimiters = {}
let mapleader = ' '
vnoremap <silent> <Enter> :EasyAlign<Enter>
vnoremap <silent> r<Enter> :EasyAlign!<Enter>
vnoremap <silent> <Leader><Enter> :LiveEasyAlign<Enter>
vnoremap <silent> <Leader>r<Enter> :LiveEasyAlign!<Enter>
" " Legacy
" vnoremap <silent> <Enter> :EasyAlign<Enter>
" vnoremap <silent> <Leader><Enter> :LiveEasyAlign<Enter>
" nmap <leader>A <Plug>(EasyAlignOperator)
vmap <Enter> <Plug>(EasyAlign)
vmap <leader><Enter> <Plug>(LiveEasyAlign)
nmap <leader>A <Plug>(EasyAlign)
vmap <leader>. <Plug>(EasyAlignRepeat)
###########################################################
Given (space-separated columns):
@@ -39,6 +47,15 @@ Expect:
111 22 333 444 55555 6666 7 88888
1111 2 33 444 555 66 777 8
Do (left-align using operator map):
\<Space>Aip*\<Space>
Expect:
1 22222 33 444 555 6666 7 888
11 222 3333 4 55 6666 77 888
111 22 333 444 55555 6666 7 88888
1111 2 33 444 555 66 777 8
Do (right-align):
vip
\<Enter>\<Enter>
@@ -257,12 +274,6 @@ Expect:
Ri ng o St ar r 19 40 mm
Pe te Be st 19 41
Expect:
Paul McCartney 1942
George Harrison 1943mmdd
Ringo Starr 1940mm
Pete Best 1941
###########################################################
Given ruby (delimiters in comments and strings):
@@ -1349,5 +1360,265 @@ Expect:
a = b = c
aabba = bbbbb
###########################################################
Given (test filter option):
aaa=aaa=aaa
aaaaa=aaaaa=aaaaa
aaaaaaa=aaaaaaa=aaaaaaab
bbbbb=bbbbb=bbbbb
aaa=aaa=aaa
Do (g/a/):
vip\<Enter>
\<C-F>g/a/\<Enter>
*=
Expect:
aaa = aaa = aaa
aaaaa = aaaaa = aaaaa
aaaaaaa = aaaaaaa = aaaaaaab
bbbbb=bbbbb=bbbbb
aaa = aaa = aaa
Do (g/a - you can omit the trailing /):
vip\<Enter>
\<C-F>g/a\<Enter>
*=
Expect:
aaa = aaa = aaa
aaaaa = aaaaa = aaaaa
aaaaaaa = aaaaaaa = aaaaaaab
bbbbb=bbbbb=bbbbb
aaa = aaa = aaa
Do (v/b/):
vip\<Enter>
\<C-F>v/b/\<Enter>
*=
Expect:
aaa = aaa = aaa
aaaaa = aaaaa = aaaaa
aaaaaaa=aaaaaaa=aaaaaaab
bbbbb=bbbbb=bbbbb
aaa = aaa = aaa
Do (invalid filter expression):
vip\<Enter>
\<C-F>haha\<Enter>
*=
Expect:
aaa = aaa = aaa
aaaaa = aaaaa = aaaaa
aaaaaaa = aaaaaaa = aaaaaaab
bbbbb = bbbbb = bbbbb
aaa = aaa = aaa
Execute (g-filter in shorthand notation):
%EasyAlign*=g/a/
Expect:
aaa = aaa = aaa
aaaaa = aaaaa = aaaaa
aaaaaaa = aaaaaaa = aaaaaaab
bbbbb=bbbbb=bbbbb
aaa = aaa = aaa
Execute (v-filter in shorthand notation):
%EasyAlign*=v/b/
Expect:
aaa = aaa = aaa
aaaaa = aaaaa = aaaaa
aaaaaaa=aaaaaaa=aaaaaaab
bbbbb=bbbbb=bbbbb
aaa = aaa = aaa
Execute (filter in dictionary format):
%EasyAlign*={'filter': 'v/b/'}
Expect:
aaa = aaa = aaa
aaaaa = aaaaa = aaaaa
aaaaaaa=aaaaaaa=aaaaaaab
bbbbb=bbbbb=bbbbb
aaa = aaa = aaa
###########################################################
Given (hard-tab indentation (#19)):
a=1=3
bbb=2=4
ccccc=4=5
fff=4=6
Do (Left alignment):
vip\<Enter>=
Expect:
a = 1=3
bbb = 2=4
ccccc = 4=5
fff = 4=6
Do (Right alignment):
vip\<Enter>\<Enter>=
Expect:
a = 1=3
bbb = 2=4
ccccc = 4=5
fff = 4=6
Do (Center alignment):
vip\<Enter>\<Enter>\<Enter>=
Expect:
a = 1=3
bbb = 2=4
ccccc = 4=5
fff = 4=6
Do (Left alignment with shallow indentation):
vip\<Enter>\<C-I>=
Expect:
a = 1=3
bbb = 2=4
ccccc = 4=5
fff = 4=6
Do (Center alignment with deep indentation):
vip\<Enter>\<Enter>\<Enter>\<C-I>\<C-I>=
Expect:
a = 1=3
bbb = 2=4
ccccc = 4=5
fff = 4=6
Given (hard-tab indentation - dictionary (#19)):
ddict={'homePage':'360452',
'key':'TEST',
'name':'DocumentationAPITestingSpace',
'type':'global',
'url':'http://localhost:8090/display/TEST'}
Do (Right alignment):
vip\<Enter>\<Enter>:
Expect:
ddict={'homePage': '360452',
'key': 'TEST',
'name': 'DocumentationAPITestingSpace',
'type': 'global',
'url': 'http://localhost:8090/display/TEST'}
###########################################################
Given (Two paragraphs (requires vim-repeat)):
a = 1
bb = 2
ccc = 3
dddd = 4
d = 1
cc = 2
bbb = 3
aaaa = 4
_____ = 5
Do (Align and repeat):
\<Space>Aip\<Enter>=
6G
.
Expect:
a = 1
bb = 2
ccc = 3
dddd = 4
d = 1
cc = 2
bbb = 3
aaaa = 4
_____ = 5
Do (Visual-mode operator is also repeatable):
vip\<Enter>\<Enter>=
6G
.
Expect:
a = 1
bb = 2
ccc = 3
dddd = 4
d = 1
cc = 2
bbb = 3
aaaa = 4
_____ = 5
Do (Repeatable in visual mode):
2GvG\<Space>.
Expect:
a = 1
bb = 2
ccc = 3
dddd = 4
d = 1
cc = 2
bbb = 3
aaaa = 4
_____ = 5
Given:
:: a : 1
:: bb : 2
:: ccc : 3
:: dd : 4
:: e : 5
:: :: a:1
:: :: b :2
:: :: ccc : 3
:: :: dd : 4
:: :: e : 5
:: :: f : 6
Do (Blockwise-visual-operator is also repeatable):
fa
\<C-V>
f1
4j
\<Enter>:
7G
fa
.
Expect:
:: a: 1
:: bb: 2
:: ccc: 3
:: dd: 4
:: e: 5
:: :: a: 1
:: :: b: 2
:: :: ccc: 3
:: :: dd: 4
:: :: e: 5
:: :: f : 6
###########################################################
Execute:
Restore