55 Commits
2.2.0 ... 2.6.0

Author SHA1 Message Date
Junegunn Choi
bab23a1917 Update documentation 2013-10-05 15:55:22 +09:00
Junegunn Choi
fa26fd7b42 Remove option value after every cycle 2013-10-05 15:27:50 +09:00
Junegunn Choi
a4a1e27395 Apply ignore_unmatched option on R/C mode if explicitly set
By default, unmatched trailing token from each line is also aligned in
right and center alignment modes unlike in left alignment mode. If
ignore_unmatched option is explicitly set to 1 by user, they will be ignored
even in right/center modes.
2013-10-05 12:23:44 +09:00
Junegunn Choi
98bd9fe6f0 Shorthand notation for ignore_groups/ignore_unmatched
e.g. ig['String']iu0
2013-10-04 02:29:24 +09:00
Junegunn Choi
f944f5bf29 Set g:easy_align_last_command 2013-10-04 02:07:35 +09:00
Junegunn Choi
484b4ba5ec Update doc 2013-10-04 01:50:43 +09:00
Junegunn Choi
9d3afa6206 Merge option values in dictionary and shortcut expression 2013-10-04 01:36:55 +09:00
Junegunn Choi
4c6a8c174d Implement shortcut expression for options 2013-10-03 21:59:51 +09:00
Junegunn Choi
3304f4e95a Make <Down> set both left and right margin to zero
(Undocumented) <Up> will clear *UP* left_margin, right_margin, and stick_to_left
2013-10-03 16:06:23 +09:00
Junegunn Choi
e4f86274e3 Update install instructions 2013-09-22 23:04:20 +09:00
Junegunn Choi
2d575bcc25 Underscore between parens 2013-09-22 22:56:27 +09:00
Junegunn Choi
8a783c2125 Fix a failure in test due to change in interactive mode 2013-09-22 22:21:23 +09:00
Junegunn Choi
5230a2402f Disallow 0 as the first character of N-th 2013-09-22 21:58:31 +09:00
Junegunn Choi
94a750e03e Remove unnecessary prev output wipe 2013-09-22 21:48:14 +09:00
Junegunn Choi
afc95b5ed7 Forgiving interactive mode: Do not terminate on invalid input 2013-09-22 21:17:02 +09:00
Junegunn Choi
1d2c58c06a Field index -> N-th parameter 2013-09-22 03:52:36 +09:00
Junegunn Choi
4cdc450126 Update DEMO.md 2013-09-21 23:53:47 +09:00
Junegunn Choi
7e1be152dc Add DEMO.md 2013-09-21 15:46:40 +09:00
Junegunn Choi
c61a11d150 Update doc
- Field number -> Field index
2013-09-21 13:08:55 +09:00
Junegunn Choi
2797baca6c Disallow non-printable character input 2013-09-21 12:04:56 +09:00
Junegunn Choi
663264ed39 Fix center-alignment
= = eee   = eee = f
= = gg  &&= gg
= = fff

= = eee   = eee = f
= = gg  &&= gg
= = f f
2013-09-21 03:31:34 +09:00
Junegunn Choi
df75520f72 Colored, replayable output 2013-09-21 03:11:20 +09:00
Junegunn Choi
a2d52611ee Fix invalid type conversion 2013-09-20 23:19:32 +09:00
Junegunn Choi
fce0a103ff Use try-finally for reverting the setup 2013-09-19 17:27:26 +09:00
Junegunn Choi
1752bf567b Improve message output
- Error message highlighting
- Wipe previous message if needed
- No line wrap on narrow terminal
2013-09-19 17:10:00 +09:00
Junegunn Choi
003194a47f Update example command sequences 2013-09-18 16:18:03 +09:00
Junegunn Choi
c1c6f32e8c Update example command sequence 2013-09-18 15:50:19 +09:00
Junegunn Choi
71edb17182 Update help file 2013-09-17 23:36:58 +09:00
Junegunn Choi
8951e13cf8 Update documentation 2013-09-17 23:32:41 +09:00
Junegunn Choi
a998aee45d Update README.md 2013-09-17 15:57:41 +09:00
Junegunn Choi
912bf6ca93 Update README.md 2013-09-17 15:54:33 +09:00
Junegunn Choi
9cf4fde00c Use number as margins (same effect, but easier to read) 2013-09-17 03:12:30 +09:00
Junegunn Choi
5f5cd49e95 Update README.md 2013-09-17 03:05:43 +09:00
Junegunn Choi
639ade715b Wording 2013-09-15 19:59:36 +09:00
Junegunn Choi
a1f2a697a5 Update README 2013-09-15 19:49:26 +09:00
Junegunn Choi
0fb127cee6 Update doc 2013-09-15 01:28:42 +09:00
Junegunn Choi
d3803b927d Heuristically determine if the user was in visual mode 2013-09-14 23:15:03 +09:00
Junegunn Choi
40a06e3d36 Left arrow key will also set left_margin to zero
- Right arrow key will set left_margin to one
- Up and Down arrow will clear the values of stick_to_left and left_margin
2013-09-14 17:30:51 +09:00
Junegunn Choi
11e0623959 DASH, not LI 2013-09-14 17:23:08 +09:00
Junegunn Choi
e9b6418280 Concept of alignment rule 2013-09-14 17:20:14 +09:00
Junegunn Choi
9f9d18ca71 Fix #8, #9, and #10.
- <Left> and <Right> key to toggle stick_to_left option
- <CTRL-X> in addition to <CTRL-/> to take regular expression
- Retain visual selection display on <CTRL-O/L/R/X>
2013-09-14 12:31:01 +09:00
Junegunn Choi
301bdbcfc7 Regular expression in interactive mode (#8) 2013-09-14 00:40:16 +09:00
Junegunn Choi
56e498a57d Implement mode_sequence expansion (#7) 2013-09-14 00:05:08 +09:00
Junegunn Choi
c0a500728c Update README 2013-09-13 10:34:16 +09:00
Junegunn Choi
2e0f9a73cd Update README.md 2013-09-13 10:26:38 +09:00
Junegunn Choi
6cac16dc3d Include =~# and =~? in = rule (Vimscript) 2013-08-25 23:03:30 +09:00
Junegunn Choi
6b275b8a46 Revert C-style variable definition example 2013-08-23 17:55:35 +09:00
Junegunn Choi
25fec2e955 Implement stick_to_left in center-alignment mode 2013-08-23 17:47:03 +09:00
Junegunn Choi
01337b9c4e Include .= operator in = rule 2013-08-23 17:40:13 +09:00
Junegunn Choi
0f70b20fdb Add related work section (#6) 2013-08-23 13:14:03 +09:00
Junegunn Choi
61be5b3abf Update doc 2013-08-23 01:37:50 +09:00
Junegunn Choi
2ad49b24f0 Add CTRL-O special key for mode_sequence option 2013-08-23 00:09:21 +09:00
Junegunn Choi
5058de6f4c Retain lm and rm values on CTRL-L and CTRL-R 2013-08-22 23:47:25 +09:00
Junegunn Choi
4c5d6d9e82 Improve center-alignment: ignore leading whitespaces 2013-08-22 23:41:21 +09:00
Junegunn Choi
12c319a8ac Implement special keys to switch option values in interactive mode
With this commit, we can now change option values in interactive mode with the
folowing special keys

- CTRL-L  left_margin
- CTRL-R  right_margin
- CTRL-D  delimiter_align
- CTRL-I  indentation
- CTRL-U  ignore_unmatched
- CTRL-G  ignore_groups

('ignores' option has been renamed to 'ignore_groups', but backward-compatible)
2013-08-20 01:38:20 +09:00
12 changed files with 1192 additions and 308 deletions

91
DEMO.md Normal file
View File

@@ -0,0 +1,91 @@
# vim: set buftype=nofile colorcolumn= scrolloff=0 noshowcmd:
vim-easy-align
==============
Where's Lennon?
---------------
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 processing |
|batch | Boolean | false | enables batch processing mode |
|batch_size | Fixnum | nil | maximum number of items in batch|
|logger | Logger | nil | logger instance for debug logs |
```
Operators with = sign
---------------------
```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:pass":r00t:pa55
```
Using blockwise-visual mode or negative N-th parameter
------------------------------------------------------
```ruby
options = { :caching => nil,
:versions => 3,
"cache=blocks" => false }.merge(options)
```
Commas
------
aaa, bb,c
d,eeeeeee
fffff, gggggggggg,
h, , ii
j,,k

View File

@@ -166,8 +166,8 @@ my_object
```
Using blockwise-visual mode or negative field index
---------------------------------------------------
Using blockwise-visual mode or negative N-th parameter
------------------------------------------------------
You can try either:
- select text around `=>` in blockwise-visual mode (`CTRL-V`) and `<Enter>=`
@@ -240,10 +240,10 @@ So, let's define a custom mapping for `#`.
if !exists('g:easy_align_delimiters')
let g:easy_align_delimiters = {}
endif
let g:easy_align_delimiters['#'] = { 'pattern': '#', 'ignores': ['String'] }
let g:easy_align_delimiters['#'] = { 'pattern': '#', 'ignore_groups': ['String'] }
```
Notice that the rule overrides `ignores` attribute in order *not to ignore*
Notice that the rule overrides `ignore_groups` attribute in order *not to ignore*
delimiters highlighted as comments.
Then on `<Enter>#`, we get
@@ -285,7 +285,7 @@ let g:easy_align_ignore_unmatched = 0
" 3. Update the alignment rule with ignore_unmatched option
let g:easy_align_delimiters['#'] = {
\ 'pattern': '#', 'ignores': ['String'], 'ignore_unmatched': 0 }
\ 'pattern': '#', 'ignore_groups': ['String'], 'ignore_unmatched': 0 }
```
Then we get,

348
README.md
View File

@@ -13,7 +13,7 @@ Demo
Features
--------
- Makes the common case easy
- Easy to use
- Comes with a predefined set of alignment rules
- Provides a fast and intuitive interface
- Extensible
@@ -22,34 +22,53 @@ Features
- Optimized for code editing
- Takes advantage of syntax highlighting feature to avoid unwanted alignments
### _"I already have a similar one. Should I switch?"_
Maybe or maybe not. See [related work](https://github.com/junegunn/vim-easy-align#related-work) section.
Installation
------------
Either [download zip file](http://www.vim.org/scripts/script.php?script_id=4520)
and extract in ~/.vim or use [Vundle](https://github.com/gmarik/vundle) (recommended)
or [Pathogen](https://github.com/tpope/vim-pathogen).
and extract in ~/.vim or
[use](https://github.com/tpope/vim-pathogen)
[your](https://github.com/gmarik/vundle)
[favorite](https://github.com/junegunn/vim-plug)
[plugin](https://github.com/Shougo/neobundle.vim)
[manager](https://github.com/MarcWeber/vim-addon-manager).
### With Vundle
Add the following line to your .vimrc,
```vim
Bundle 'junegunn/vim-easy-align'
```
then execute `:BundleInstall` command.
- [Pathogen](https://github.com/tpope/vim-pathogen)
- `git clone https://github.com/junegunn/vim-easy-align.git ~/.vim/bundle/vim-easy-align`
- [Vundle](https://github.com/gmarik/vundle)
1. Add `Bundle 'junegunn/vim-easy-align'` to .vimrc
2. Run `:BundleInstall`
- [NeoBundle](https://github.com/Shougo/neobundle.vim)
1. Add `NeoBundle 'junegunn/vim-easy-align'` to .vimrc
2. Run `:NeoBundleInstall`
- [vim-plug](https://github.com/junegunn/vim-plug)
1. Add `Plug 'junegunn/vim-easy-align'` to .vimrc
2. Run `:PlugInstall`
Usage
-----
_vim-easy-align_ defines `:EasyAlign` command (and the right-align
variant `:EasyAlign!`) in the visual mode.
variant `:EasyAlign!`) for visual mode.
| Mode | Command |
| ------------------------- | ------------------------------------------------ |
| Interactive mode | `:EasyAlign[!] [OPTIONS]` |
| Using predefined rules | `:EasyAlign[!] [FIELD#] DELIMITER_KEY [OPTIONS]` |
| Using regular expressions | `:EasyAlign[!] [FIELD#] /REGEXP/ [OPTIONS]` |
| Using predefined rules | `:EasyAlign[!] [N-th] DELIMITER_KEY [OPTIONS]` |
| Using regular expressions | `:EasyAlign[!] [N-th] /REGEXP/ [OPTIONS]` |
### 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>`,
`=`, `:`, `.`, `|`, and `,`.
Think of it as a shortcut. Instead of writing regular expression and setting
several options, you can just type in a single character.
### Interactive mode
@@ -58,7 +77,7 @@ For convenience, it is advised that you define a mapping for triggering it in
your `.vimrc`.
```vim
vnoremap <silent> <Enter> :EasyAlign<cr>
vnoremap <silent> <Enter> :EasyAlign<Enter>
```
(Of course you can use any key combination as the trigger. e.g. `<Leader>a`)
@@ -66,8 +85,8 @@ vnoremap <silent> <Enter> :EasyAlign<cr>
With the mapping, you can align selected lines of text with only a few keystrokes.
1. `<Enter>` key to start interactive EasyAlign command
1. Optional Enter keys to select alignment mode (left, right, or center)
1. Optional field number (default: 1)
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
- `2` Around the 2nd occurrences of delimiters
- ...
@@ -90,37 +109,87 @@ Alignment rules for the following delimiters have been defined to meet the most
| &#124; | Table markdown |
You can override these default rules or define your own rules with
`g:easy_align_delimiters`, which will be described in the later section.
`g:easy_align_delimiters`, which will be described in
[the later section](https://github.com/junegunn/vim-easy-align#extending-alignment-rules).
#### 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>:` | Alignment around 1st colon | `:'<,'>EasyAlign:` |
| `<Enter>=` | Alignment around 1st equals signs (and the likes) | `:'<,'>EasyAlign=` |
| `<Enter>2=` | Alignment around 2nd equals signs (and the likes) | `:'<,'>EasyAlign2=` |
| `<Enter>3=` | Alignment around 3rd equals signs (and the likes) | `:'<,'>EasyAlign3=` |
| `<Enter>*=` | Alignment around all equals signs (and the likes) | `:'<,'>EasyAlign*=` |
| `<Enter>**=` | Left-right alternating alignment around all equals signs | `:'<,'>EasyAlign**=` |
| `<Enter><Enter>=` | Right alignment around 1st equals signs | `:'<,'>EasyAlign!=` |
| `<Enter><Enter>**=` | Right-left alternating alignment around all equals signs | `:'<,'>EasyAlign!**=` |
| ... | ... | |
| 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:{'s':0,'l':1}` |
| `<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!**=` |
### Non-interactive mode
#### Using regular expressions
Instead of finishing the command with a predefined delimiter key, you can type
in a regular expression after `<CTRL-/>` or `<CTRL-X>` key.
For example, if you want to align text around all occurrences of numbers:
- `<Enter>`
- `*`
- `<CTRL-/>` (or `<CTRL-X>` on GVim)
- `[0-9]\+`
#### Alignment options in interactive mode
While in interactive mode, you can set alignment options using special shortcut
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-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}/` |
| `<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 }` |
After a successful alignment, you can repeat the same operation using the
repeatable, non-interactive command recorded in `g:easy_align_last_command`.
```vim
:<C-R>=g:easy_align_last_command<Enter><Enter>
```
---
### *Intermission*
You can stop reading here. Trust me. All the fancy features described in the
following sections are really powerful but you won't be needing them in most
of the cases.
Go try out vim-easy-align right now, and come back later when you feel like it.
---
### Non-interactive mode (command line)
Instead of going into the interactive mode, you can type in arguments to
`:EasyAlign` command. In non-interactive mode, you can even use arbitrary
regular expressions.
`:EasyAlign` command.
```vim
" Using predefined alignment rules
:EasyAlign[!] [FIELD#] DELIMITER_KEY [OPTIONS]
:EasyAlign[!] [N-th] DELIMITER_KEY [OPTIONS]
" Using arbitrary regular expressions
:EasyAlign[!] [FIELD#] /REGEXP/ [OPTIONS]
:EasyAlign[!] [N-th] /REGEXP/ [OPTIONS]
```
For example, when aligning the following lines around colons and semi-colons,
@@ -139,7 +208,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
format.
- `:EasyAlign * /[:;]\+/ { 'stick_to_left': 1, 'left_margin': '' }`
- `:EasyAlign * /[:;]\+/ { 'stick_to_left': 1, 'left_margin': 0 }`
Then we get:
@@ -154,7 +223,32 @@ You can even omit spaces between the arguments, so concisely (or cryptically):
- `:EasyAlign*/[:;]\+/{'s':1,'l':0}`
Available options will be shown later in the document.
Nice. But let's make it even shorter. When you use write regular expression in
command line, option values can be written in shorthand notation.
- `:EasyAlign*/[:;]\+/s1l0`
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]` |
For your information, the same thing can be done in the interactive mode as well
with the following key combination.
- `<Enter>`
- `*`
- `<Left>`
- `<CTRL-/>` (or `<CTRL-X>` on GVim)
- `[:;]\+`
### Partial alignment in blockwise-visual mode
@@ -181,40 +275,43 @@ 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 field number: `<Enter>-=`
since the same can be easily done using the negative N-th parameter: `<Enter>-=`
Alignment options
-----------------
Option values can be 1) specified as global variables, 2) set on each alignment
rule in `g:easy_align_delimiters`, 3) or given to every `:EasyAlign` command.
Command-line options have the highest precedence, and global variables have the
lowest precedence.
### List of options
| Option | Type | Default | Description |
| ------------------ | ----------------- | --------------------- | ------------------------------------------------------- |
| `left_margin` | number | 0 | Number of spaces to attach before delimiter |
| `left_margin` | string | `''` | String to attach before delimiter |
| `right_margin` | number | 0 | Number of spaces to attach after delimiter |
| `right_margin` | string | `''` | String to attach after delimiter |
| `stick_to_left` | boolean | 0 | Whether to position delimiter on the left-side |
| `ignore_unmatched` | boolean | 1 | Whether to ignore lines without matching delimiter |
| `ignores` | list | ['String', 'Comment'] | Delimiters in these syntax highlight groups are ignored |
| `indentation` | string | `k` | Indentation method (*k*eep, *d*eep, *s*hallow, *n*one) |
| `delimiter_align` | string | `r` | Determines how to align delimiters of different lengths |
| `mode_sequence` | string | | Alignment modes for multiple occurrences of delimiters |
| Option | Type | Default | Description |
| ------------------ | ------- | --------------------- | ------------------------------------------------------- |
| `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 |
| `right_margin` | string | `' '` | String to attach after delimiter |
| `stick_to_left` | boolean | 0 | Whether to position delimiter on the left-side |
| `ignore_groups` | list | ['String', 'Comment'] | Delimiters in these syntax highlight groups are ignored |
| `ignore_unmatched` | boolean | 1 | Whether to ignore lines without matching delimiter |
| `indentation` | string | `k` | Indentation method (*k*eep, *d*eep, *s*hallow, *n*one) |
| `delimiter_align` | string | `r` | Determines how to align delimiters of different lengths |
| `mode_sequence` | string | | Alignment modes for multiple occurrences of delimiters |
Some of the options can be specified using corresponding global variables.
There are 4 ways to set alignment options (from lowest precedence to highest):
| Option | Global variable |
| ------------------ | ------------------------------- |
| `ignore_unmatched` | `g:easy_align_ignore_unmatched` |
| `ignores` | `g:easy_align_ignores` |
| `delimiter_align` | `g:easy_align_delimiter_align` |
| `indentation` | `g:easy_align_indentation` |
1. Some option values can be set with corresponding global variables
2. Option values can be specified in the definition of each alignment rule
3. Option values can be given as arguments to `:EasyAlign` command
4. Option values can be set in interactive mode using special shortcut keys
| Option name | Shortcut key | Abbreviated | Global variable |
| ------------------ | ------------------- | ----------- | ------------------------------- |
| `left_margin` | `CTRL-L` | `l[0-9]+` | |
| `right_margin` | `CTRL-R` | `r[0-9]+` | |
| `stick_to_left` | `<Left>`, `<Right>` | `s[01]` | |
| `ignore_groups` | `CTRL-G` | `ig\[.*\]` | `g:easy_align_ignore_groups` |
| `ignore_unmatched` | `CTRL-U` | `iu[01]` | `g:easy_align_ignore_unmatched` |
| `indentation` | `CTRL-I` | `i[ksdn]` | `g:easy_align_indentation` |
| `delimiter_align` | `CTRL-D` | `d[lrc]` | `g:easy_align_delimiter_align` |
| `mode_sequence` | `CTRL-O` | `m[lrc*]*` | |
### Ignoring delimiters in comments or strings
@@ -226,7 +323,7 @@ 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']
let g:easy_align_ignore_groups = ['Comment', 'String']
```
For example, the following paragraph
@@ -257,13 +354,15 @@ becomes as follows on `<Enter>:` (or `:EasyAlign:`)
Naturally, this feature only works when syntax highlighting is enabled.
You can change the default rule by using one of these 3 methods.
You can change the default rule by using one of these 4 methods.
1. Define global `g:easy_align_ignores` list
2. Define a custom alignment rule in `g:easy_align_delimiters` with `ignores` option
3. Provide `ignores` option to `:EasyAlign` command. e.g. `:EasyAlign:{'is':[]}`
1. Press `CTRL-G` in interactive mode to switch groups
2. Define global `g:easy_align_ignore_groups` list
3. Define a custom rule in `g:easy_align_delimiters` with `ignore_groups` option
4. Provide `ignore_groups` option to `:EasyAlign` command.
e.g. `:EasyAlign:{'ig':[]}`
For example if you set `ignores` option to be an empty list, you get
For example if you set `ignore_groups` option to be an empty list, you get
```ruby
{
@@ -307,12 +406,13 @@ this is usually what we want.
}
```
However, this default behavior is also configurable by using one of these 3
However, this default behavior is also configurable by using one of these 4
methods.
1. Set the global `g:easy_align_ignore_unmatched` variable to 0
2. Define a custom alignment rule with `ignore_unmatched` option set to 0
3. Provide `ignore_unmatched` option to `:EasyAlign` command. e.g. `:EasyAlign:{'iu':0}`
1. Press `CTRL-U` in interactive mode to toggle `ignore_unmatched` option
2. Set the global `g:easy_align_ignore_unmatched` variable to 0
3. Define a custom alignment rule with `ignore_unmatched` option set to 0
4. Provide `ignore_unmatched` option to `:EasyAlign` command. e.g. `:EasyAlign:{'iu':0}`
Then we get,
@@ -362,6 +462,8 @@ banana += apple
cake ||= banana
```
In interactive mode, you can change the option value with `CTRL-D` key.
### Adjusting indentation
By default :EasyAlign command keeps the original indentation of the lines. But
@@ -410,6 +512,8 @@ eggplant = 5
Notice that `idt` is fuzzy-matched to `indentation`.
In interactive mode, you can change the option value with `CTRL-I` key.
### Left/right/center mode switch in interactive mode
In interactive mode, you can choose the alignment mode you want by pressing
@@ -432,7 +536,7 @@ let g:easy_align_bang_interactive_modes = ['c', 'r']
### Alignments over multiple occurrences of delimiters
As stated above, "field number" is used to target specific occurrences of
As stated above, "N-th" parameter is used to target specific occurrences of
the delimiter when it appears multiple times in each line.
To recap:
@@ -458,7 +562,8 @@ To recap:
```
In addition to these, you can fine-tune alignments over multiple occurrences of
the delimiters with 'mode_sequence' option.
the delimiters with 'mode_sequence' option. (The option can also be set
in interactive mode with the special key `CTRL-O`)
```vim
" Left alignment over the first two occurrences of delimiters
@@ -477,6 +582,14 @@ the delimiters with 'mode_sequence' option.
" (**) Alternating alignments (default: lr or rl)
" Right, left, center, right, left, center, ...
:EasyAlign **={ 'm': 'rlc' }
" Right, left, center, center, center, ... repeating alignment
" over the 3rd to the last occurrences of delimiters
:EasyAlign 3={ 'm': 'rlc*' }
" Right, left, center, right, left, center, ... alternating alignment
" over the 3rd to the last occurrences of delimiters
:EasyAlign 3={ 'm': 'rlc**' }
```
### Extending alignment rules
@@ -489,8 +602,8 @@ you can extend the rules by setting a dictionary named `g:easy_align_delimiters`
```vim
let g:easy_align_delimiters = {
\ '>': { 'pattern': '>>\|=>\|>' },
\ '/': { 'pattern': '//\+\|/\*\|\*/', 'ignores': ['String'] },
\ '#': { 'pattern': '#\+', 'ignores': ['String'], 'delimiter_align': 'l' },
\ '/': { 'pattern': '//\+\|/\*\|\*/', 'ignore_groups': ['String'] },
\ '#': { 'pattern': '#\+', 'ignore_groups': ['String'], 'delimiter_align': 'l' },
\ ']': {
\ 'pattern': '[[\]]',
\ 'left_margin': 0,
@@ -517,6 +630,81 @@ Advanced examples and use cases
See [EXAMPLES.md](https://github.com/junegunn/vim-easy-align/blob/master/EXAMPLES.md)
for more examples.
Related work
------------
There are two well-known plugins with the same goal as that of vim-easy-align.
- [DrChip's Alignment Tool for Vim](http://www.drchip.org/astronaut/vim/align.html) (herein will be referred to as "Align")
- [Tabular](https://github.com/godlygeek/tabular)
Both are great plugins with very large user bases. I actually had been a Tabular
user for a couple of years before I finally made up my mind to roll out my own.
So why would someone choose vim-easy-align over those two?
Feature-by-feature comparison I believe is not quite useful, since a typical
user will end up using only a small subset of the features.
So I will mention just a few core benefits of vim-easy-align.
### Ease of use
As the name implies, vim-easy-align is *easier* to use. Its interactive mode
allows you to achieve what you want with just a few keystrokes.
The key sequence is mnemonic, so it's easy to remember and execute.
It even feels like a native Vim command!
- *Right-align*: `<Enter><Enter>`
- around the *second* occurrences: `2`
- 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
complex regular expressions.
_"How am I going to align the third to the last word in each line to the right
without affecting the ones before it?"_
### Clean
vim-easy-align doesn't clutter your workspace with mappings and global
variables. All you would need is a single mapping to the interactive EasyAlign
command, and even that is totally up to you.
### Optimized for code editing
vim-easy-align by default performs syntax-aware alignment, which is invaluable
when editing codes.
Try to come up with a regular expression to correctly format the following code
snippet. With vim-easy-align under default configuration and a mapping, it can
be done with just two keystrokes: `<Enter>:`
```javascript
var jdbc = {
// 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"
};
```
(To be fair, Align also can be configured to consider syntax highlighting with
`g:AlignSkip` function reference which should point to a custom function that
looks up the syntax group of a character on a certain position)
### "Okay. So should I switch?"
Maybe, but I can't really say. I have no ambition to make vim-easy-align
an absolute superior to the others. For some cases, vim-easy-align works better
than the others, but for some other cases, Tabular or Align.vim might be a
better choice.
So try it yourself and see if it works for you!
Author
------

View File

@@ -27,16 +27,16 @@ endif
let g:loaded_easy_align = 1
let s:easy_align_delimiters_default = {
\ ' ': { 'pattern': ' ', 'left_margin': '', 'right_margin': '', 'stick_to_left': 0 },
\ '=': { 'pattern': '===\|<=>\|\(&&\|||\|<<\|>>\)=\|=\~\|=>\|[:+/*!%^=><&|-]\?=[#?]\?',
\ 'left_margin': ' ', 'right_margin': ' ', 'stick_to_left': 0 },
\ ':': { 'pattern': ':', 'left_margin': '', 'right_margin': ' ', 'stick_to_left': 1 },
\ ',': { 'pattern': ',', 'left_margin': '', 'right_margin': ' ', 'stick_to_left': 1 },
\ '|': { 'pattern': '|', 'left_margin': ' ', 'right_margin': ' ', 'stick_to_left': 0 },
\ '.': { 'pattern': '\.', 'left_margin': '', 'right_margin': '', 'stick_to_left': 0 },
\ ' ': { 'pattern': ' ', 'left_margin': 0, 'right_margin': 0, 'stick_to_left': 0 },
\ '=': { 'pattern': '===\|<=>\|\(&&\|||\|<<\|>>\)=\|=\~[#?]\?\|=>\|[:+/*!%^=><&|.-]\?=[#?]\?',
\ 'left_margin': 1, 'right_margin': 1, 'stick_to_left': 0 },
\ ':': { 'pattern': ':', 'left_margin': 0, 'right_margin': 1, 'stick_to_left': 1 },
\ ',': { 'pattern': ',', 'left_margin': 0, 'right_margin': 1, 'stick_to_left': 1 },
\ '|': { 'pattern': '|', 'left_margin': 1, 'right_margin': 1, 'stick_to_left': 0 },
\ '.': { 'pattern': '\.', 'left_margin': 0, 'right_margin': 0, 'stick_to_left': 0 },
\ '{': { 'pattern': '(\@<!{',
\ 'left_margin': ' ', 'right_margin': ' ', 'stick_to_left': 0 },
\ '}': { 'pattern': '}', 'left_margin': ' ', 'right_margin': '', 'stick_to_left': 0 }
\ 'left_margin': 1, 'right_margin': 1, 'stick_to_left': 0 },
\ '}': { 'pattern': '}', 'left_margin': 1, 'right_margin': 0, 'stick_to_left': 0 }
\ }
let s:mode_labels = { 'l': '', 'r': '[R]', 'c': '[C]' }
@@ -44,8 +44,22 @@ let s:mode_labels = { 'l': '', 'r': '[R]', 'c': '[C]' }
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],
\ 'ignores': [3 ], 'ignore_unmatched': [0 ], 'delimiter_align': [1],
\ 'mode_sequence': [1 ]
\ 'ignore_groups': [3 ], 'ignore_unmatched': [0 ], 'delimiter_align': [1],
\ 'mode_sequence': [1 ], 'ignores': [3]
\ }
let s:option_values = {
\ 'indentation': ['shallow', 'deep', 'none', 'keep', -1],
\ 'delimiter_align': ['left', 'center', 'right', -1],
\ 'ignore_unmatched': [0, 1, -1],
\ 'ignore_groups': [[], ['String'], ['Comment'], ['String', 'Comment'], -1]
\ }
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'
\ }
if exists("*strwidth")
@@ -72,21 +86,67 @@ endfunction
function! s:ignored_syntax()
if has('syntax') && exists('g:syntax_on')
" Backward-compatibility
return get(g:, 'easy_align_ignores',
\ (get(g:, 'easy_align_ignore_comment', 1) == 0) ?
\ ['String'] : ['String', 'Comment'])
return get(g:, 'easy_align_ignore_groups',
\ get(g:, 'easy_align_ignores',
\ (get(g:, 'easy_align_ignore_comment', 1) == 0) ?
\ ['String'] : ['String', 'Comment']))
else
return []
endif
endfunction
function! s:echon(l, n, d)
echon "\r"
echon "\rEasyAlign". s:mode_labels[a:l] ." (" .a:n.a:d. ")"
function! s:echon_(tokens)
" http://vim.wikia.com/wiki/How_to_print_full_screen_width_messages
let xy = [&ruler, &showcmd]
try
set noruler noshowcmd
let winlen = winwidth(winnr()) - 2
let len = len(join(map(copy(a:tokens), 'v:val[1]'), ''))
let ellipsis = len > winlen ? '..' : ''
echon "\r"
let yet = 0
for [hl, msg] in a:tokens
if empty(msg) | continue | endif
execute "echohl ". hl
let yet += len(msg)
if yet > winlen - len(ellipsis)
echon msg[ 0 : (winlen - len(ellipsis) - yet - 1) ] . ellipsis
break
else
echon msg
endif
endfor
finally
echohl None
let [&ruler, &showcmd] = xy
endtry
endfunction
function! s:echon(l, n, r, d, o, warn)
let tokens = [
\ ['Function', ':EasyAlign'],
\ ['ModeMsg', get(s:mode_labels, a:l, a:l)],
\ ['None', ' ']]
if a:r == -1 | call add(tokens, ['Comment', '(']) | endif
call add(tokens, [a:n =~ '*' ? 'Repeat' : 'Number', a:n])
call extend(tokens, a:r == 1 ?
\ [['Delimiter', '/'], ['String', a:d], ['Delimiter', '/']] :
\ [['Identifier', a:d == ' ' ? '\ ' : (a:d == '\' ? '\\' : a:d)]])
if a:r == -1 | call extend(tokens, [['Normal', '_'], ['Comment', ')']]) | endif
call add(tokens, ['Statement', empty(a:o) ? '' : ' '.string(a:o)])
if !empty(a:warn)
call add(tokens, ['WarningMsg', ' ('.a:warn.')'])
endif
call s:echon_(tokens)
return join(map(tokens, 'v:val[1]'), '')
endfunction
function! s:exit(msg)
echon "\r". a:msg
call s:echon_([['ErrorMsg', a:msg]])
throw 'exit'
endfunction
@@ -99,16 +159,25 @@ function! s:rtrim(str)
endfunction
function! s:trim(str)
return substitute(a:str, '^\s*\(\S*\)\s*$', '\1', '')
return substitute(a:str, '^\s*\(.\{-}\)\s*$', '\1', '')
endfunction
function! s:fuzzy_lu(key)
if has_key(s:known_options, a:key)
return a:key
endif
let key = tolower(a:key)
let regexp = '^' . substitute(substitute(a:key, '-', '_', 'g'), '\(.\)', '\1.*', 'g')
let matches = filter(keys(s:known_options), 'v:val =~ regexp')
" stl -> ^s.*_t.*_l.*
let regexp1 = '^' .key[0]. '.*' .substitute(key[1 : -1], '\(.\)', '_\1.*', 'g')
let matches = filter(keys(s:known_options), 'v:val =~ regexp1')
if len(matches) == 1
return matches[0]
endif
" stl -> ^s.*t.*l.*
let regexp2 = '^' . substitute(substitute(key, '-', '_', 'g'), '\(.\)', '\1.*', 'g')
let matches = filter(keys(s:known_options), 'v:val =~ regexp2')
if empty(matches)
call s:exit("Unknown option key: ". a:key)
@@ -116,9 +185,12 @@ function! s:fuzzy_lu(key)
return matches[0]
else
" Avoid ambiguity introduced by deprecated margin_left and margin_right
if index(matches, 'mode_sequence') != -1
if sort(matches) == ['margin_left', 'margin_right', 'mode_sequence']
return 'mode_sequence'
endif
if sort(matches) == ['ignore_groups', 'ignores']
return 'ignore_groups'
endif
call s:exit("Ambiguous option key: ". a:key ." (" .join(matches, ', '). ")")
endif
endfunction
@@ -145,6 +217,14 @@ function! s:normalize_options(opts)
return s:validate_options(ret)
endfunction
function! s:compact_options(opts)
let ret = {}
for k in keys(a:opts)
let ret[s:shorthand[k]] = a:opts[k]
endfor
return ret
endfunction
function! s:validate_options(opts)
for k in keys(a:opts)
let v = a:opts[k]
@@ -156,7 +236,7 @@ function! s:validate_options(opts)
return a:opts
endfunction
function! s:split_line(line, nth, modes, cycle, fc, lc, pattern, stick_to_left, ignore_unmatched, ignores)
function! s:split_line(line, nth, modes, cycle, fc, lc, pattern, stick_to_left, ignore_unmatched, ignore_groups)
let mode = ''
let string = a:lc ?
@@ -189,7 +269,7 @@ function! s:split_line(line, nth, modes, cycle, fc, lc, pattern, stick_to_left,
let [match, part, delim] = matches[1 : 3]
endif
let ignorable = s:highlighted_as(a:line, idx + len(part) + a:fc, a:ignores)
let ignorable = s:highlighted_as(a:line, idx + len(part) + a:fc, a:ignore_groups)
if ignorable
let token .= match
else
@@ -212,7 +292,7 @@ function! s:split_line(line, nth, modes, cycle, fc, lc, pattern, stick_to_left,
let leftover = token . strpart(string, idx)
if !empty(leftover)
let ignorable = s:highlighted_as(a:line, len(string) + a:fc - 1, a:ignores)
let ignorable = s:highlighted_as(a:line, len(string) + a:fc - 1, a:ignore_groups)
call add(tokens, leftover)
call add(delims, '')
endif
@@ -232,7 +312,10 @@ function! s:split_line(line, nth, modes, cycle, fc, lc, pattern, stick_to_left,
let delims = []
" Append an empty item to enable right/center alignment of the last token
" - if the last token is not ignorable or ignorable but not the only token
elseif (mode == 'r' || mode == 'c') && (!ignorable || len(tokens) > 1) && a:nth >= 0 " includes -0
elseif a:ignore_unmatched != 1 &&
\ (mode ==? 'r' || mode ==? 'c') &&
\ (!ignorable || len(tokens) > 1) &&
\ a:nth >= 0 " includes -0
call add(tokens, '')
call add(delims, '')
endif
@@ -249,12 +332,12 @@ function! s:max(old, new)
endfunction
function! s:do_align(modes, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth,
\ ml, mr, da, indentation, stick_to_left, ignore_unmatched, ignores, recur)
\ ml, mr, da, indentation, stick_to_left, ignore_unmatched, ignore_groups, recur)
let mode = a:modes[0]
let lines = {}
let min_indent = -1
let max = { 'pivot_len': 0.0, 'token_len': 0, 'just_len': 0, 'delim_len': 0,
\ 'indent': 0, 'tokens': 0 }
\ 'indent': 0, 'tokens': 0, 'strip_len': 0 }
" Phase 1
for line in range(a:fl, a:ll)
@@ -263,7 +346,7 @@ function! s:do_align(modes, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth
let [tokens, delims] = s:split_line(
\ line, a:nth, copy(a:modes), a:recur == 2,
\ a:fc, a:lc, a:pattern,
\ a:stick_to_left, a:ignore_unmatched, a:ignores)
\ a:stick_to_left, a:ignore_unmatched, a:ignore_groups)
" Remember tokens for subsequent recursive calls
let a:all_tokens[line] = tokens
@@ -281,13 +364,13 @@ function! s:do_align(modes, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth
" Calculate the maximum number of tokens for a line within the range
call s:max(max, { 'tokens': len(tokens) })
if a:nth > 0 " Positive field number
if a:nth > 0 " Positive N-th
if len(tokens) < a:nth
continue
endif
let nth = a:nth - 1 " make it 0-based
else " -0 or Negative field number
if a:nth == 0 && mode != 'l'
else " -0 or Negative N-th
if a:nth == 0 && mode !=? 'l'
let nth = len(tokens) - 1
else
let nth = len(tokens) + a:nth
@@ -313,9 +396,14 @@ function! s:do_align(modes, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth
if min_indent < 0 || indent < min_indent
let min_indent = indent
endif
if mode ==? 'c' | let token .= matchstr(token, '^\s*') | endif
let [pw, tw] = [s:strwidth(prefix), s:strwidth(token)]
call s:max(max, { 'indent': indent, 'token_len': tw, 'just_len': pw + tw,
\ 'delim_len': s:strwidth(delim), 'pivot_len': pw + tw / 2.0 })
\ 'delim_len': s:strwidth(delim) })
if mode ==? 'c'
call s:max(max, { 'pivot_len': pw + tw / 2.0,
\ 'strip_len': s:strwidth(s:trim(token)) })
endif
let lines[line] = [nth, prefix, token, delim]
endfor
@@ -340,9 +428,14 @@ function! s:do_align(modes, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth
let [nth, prefix, token, delim] = elems
let token = substitute(token, '^\s*', indent, '')
if mode ==? 'c'
let token = substitute(token, '\s*$', indent, '')
endif
let [pw, tw] = [s:strwidth(prefix), s:strwidth(token)]
call s:max(max,
\ { 'token_len': tw, 'just_len': pw + tw, 'pivot_len': pw + tw / 2.0 })
call s:max(max, { 'token_len': tw, 'just_len': pw + tw })
if mode ==? 'c'
call s:max(max, { 'pivot_len': pw + tw / 2.0 })
endif
let lines[line][2] = token
endfor
@@ -363,24 +456,35 @@ function! s:do_align(modes, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth
" Pad the token with spaces
let [pw, tw] = [s:strwidth(prefix), s:strwidth(token)]
let rpad = ''
if mode == 'l'
if mode ==? 'l'
let pad = repeat(' ', max.just_len - pw - tw)
if a:stick_to_left
let rpad = pad
else
let token = token . pad
endif
elseif mode == 'r'
elseif mode ==? 'r'
let pad = repeat(' ', max.just_len - pw - tw)
let token = pad . token
elseif mode == 'c'
elseif mode ==? 'c'
let p1 = max.pivot_len - (pw + tw / 2.0)
let p2 = (max.token_len - tw) / 2.0
let pf1 = floor(p1)
if pf1 < p1 | let p2 = ceil(p2)
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 token = substitute(token, repeat(' ', strip) . '$', '', '')
if a:stick_to_left
if empty(s:rtrim(token))
let center = len(token) / 2
let [token, rpad] = [strpart(token, 0, center), strpart(token, center)]
else
let [token, rpad] = [s:rtrim(token), matchstr(token, '\s*$')]
endif
endif
endif
let tokens[nth] = token
@@ -412,7 +516,7 @@ function! s:do_align(modes, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth
let lpad = ''
if nth == 0
let ipad = repeat(' ', min_indent - len(token.ml))
if mode == 'l'
if mode ==? 'l'
let token = ipad . token
else
let lpad = ipad
@@ -434,17 +538,51 @@ function! s:do_align(modes, all_tokens, all_delims, fl, ll, fc, lc, pattern, nth
\ a:modes, a:all_tokens, a:all_delims,
\ a:fl, a:ll, a:fc, a:lc, a:pattern,
\ a:nth + 1, a:ml, a:mr, a:da, a:indentation, a:stick_to_left,
\ a:ignore_unmatched, a:ignores, a:recur)
\ a:ignore_unmatched, a:ignore_groups, a:recur)
endif
endfunction
function! s:interactive(modes)
function! s:input(str, default, vis)
if a:vis
normal! gv
redraw
execute "normal! \<esc>"
else
" EasyAlign command can be called without visual selection
redraw
endif
call inputsave()
let got = input(a:str, a:default)
call inputrestore()
return got
endfunction
function! s:atoi(str)
return (a:str =~ '^[0-9]\+$') ? str2nr(a:str) : a:str
endfunction
function! s:shift_opts(opts, key, vals)
let val = s:shift(a:vals, 1)
if type(val) == 0 && val == -1
call remove(a:opts, a:key)
else
let a:opts[a:key] = val
endif
endfunction
function! s:interactive(modes, vis, opts, delims)
let mode = s:shift(a:modes, 1)
let n = ''
let ch = ''
let opts = s:compact_options(a:opts)
let vals = deepcopy(s:option_values)
let regx = 0
let warn = ''
while 1
call s:echon(mode, n, '')
call s:echon(mode, n, -1, '', opts, warn)
let check = 0
let warn = ''
let c = getchar()
let ch = nr2char(c)
@@ -456,34 +594,164 @@ function! s:interactive(modes)
endif
elseif c == 13 " Enter key
let mode = s:shift(a:modes, 1)
if has_key(opts, 'm')
let opts.m = mode . strpart(opts.m, 1)
endif
elseif ch == '-'
if empty(n) | let n = '-'
elseif n == '-' | let n = ''
else | break
else | let check = 1
endif
elseif ch == '*'
if empty(n) | let n = '*'
elseif n == '*' | let n = '**'
elseif n == '**' | let n = ''
else | break
else | let check = 1
endif
elseif c >= 48 && c <= 57 " Numbers
if n[0] == '*' | break
elseif (c == 48 && len(n) > 0) || c > 48 && c <= 57 " Numbers
if n[0] == '*' | let check = 1
else | let n = n . ch
end
elseif ch == "\<C-D>"
call s:shift_opts(opts, 'da', vals['delimiter_align'])
elseif ch == "\<C-I>"
call s:shift_opts(opts, 'idt', vals['indentation'])
elseif ch == "\<C-L>"
let lm = s:input("Left margin: ", get(opts, 'lm', ''), a:vis)
if empty(lm)
let warn = 'Set to default. Input 0 to remove it'
silent! call remove(opts, 'lm')
else
let opts['lm'] = s:atoi(lm)
endif
elseif ch == "\<C-R>"
let rm = s:input("Right margin: ", get(opts, 'rm', ''), a:vis)
if empty(rm)
let warn = 'Set to default. Input 0 to remove it'
silent! call remove(opts, 'rm')
else
let opts['rm'] = s:atoi(rm)
endif
elseif ch == "\<C-U>"
call s:shift_opts(opts, 'iu', vals['ignore_unmatched'])
elseif ch == "\<C-G>"
call s:shift_opts(opts, 'ig', vals['ignore_groups'])
elseif c == "\<Left>"
let opts['stl'] = 1
let opts['lm'] = 0
elseif c == "\<Right>"
let opts['stl'] = 0
let opts['lm'] = 1
elseif c == "\<Down>"
let opts['lm'] = 0
let opts['rm'] = 0
elseif c == "\<Up>"
silent! call remove(opts, 'stl')
silent! call remove(opts, 'lm')
silent! call remove(opts, 'rm')
elseif ch == "\<C-O>"
let modes = tolower(s:input("Mode sequence: ", get(opts, 'm', mode), a:vis))
if match(modes, '^[lrc]\+\*\{0,2}$') != -1
let opts['m'] = modes
let mode = modes[0]
while mode != s:shift(a:modes, 1)
endwhile
else
silent! call remove(opts, 'm')
endif
elseif ch == "\<C-_>" || ch == "\<C-X>"
let prompt = 'Regular expression: '
let ch = s:input(prompt, '', a:vis)
if !empty(ch) && s:valid_regexp(ch)
let regx = 1
break
else
let warn = 'Invalid regular expression: '.ch
endif
elseif ch =~ '[[:print:]]'
let check = 1
else
break
let warn = 'Invalid character'
endif
if check
if has_key(a:delims, ch)
break
else
let warn = 'Unknown delimiter key: '.ch
endif
endif
endwhile
return [mode, n, ch]
return [mode, n, ch, s:normalize_options(opts), regx]
endfunction
function! s:valid_regexp(regexp)
try
call matchlist('', a:regexp)
catch
return 0
endtry
return 1
endfunction
function! s:test_regexp(regexp)
if !s:valid_regexp(a:regexp)
call s:exit('Invalid regular expression: '. a:regexp)
endif
return a:regexp
endfunction
function! s:parse_shortcut_opts(expr)
let opts = {}
let expr = substitute(a:expr, '\s', '', 'g')
let regex =
\ '^\('
\ .'\(l[0-9]\+\)\|\(r[0-9]\+\)\|\(iu[01]\)\|\(s[01]\)\|'
\ .'\(d[clr]\)\|\(m[lrc*]\+\)\|\(i[kdsn]\)\|\(ig\[.*\]\)'
\ .'\)\+$'
if empty(expr)
return opts
elseif expr !~ regex
call s:exit("Invalid expression: ". a:expr)
else
let match = matchlist(expr, regex)
if empty(match) | break | endif
for m in filter(match[ 2 : -1 ], '!empty(v:val)')
let k = tolower(m[0])
let kk = tolower(m[0 : 1])
let rest = m[1 : -1]
if index(['l', 'r', 's'], k) >= 0
let opts[k] = str2nr(rest)
elseif kk == 'iu'
let opts['iu'] = str2nr(m[2 : -1])
elseif kk == 'ig'
try
let arr = eval(m[2 : -1])
if type(arr) == 3
let opts['ig'] = arr
else
throw 'Not an array'
endif
catch
call s:exit("Invalid ignore_groups: ". a:expr)
endtry
elseif k == 'i'
let opts['idt'] = rest
else
let opts[k] = rest
endif
endfor
endif
return s:normalize_options(opts)
endfunction
function! s:parse_args(args)
let n = ''
let ch = ''
let args = a:args
let cand = ''
let option = {}
let n = ''
let ch = ''
let args = a:args
let cand = ''
let opts = {}
" Poor man's option parser
let idx = 0
@@ -497,7 +765,7 @@ function! s:parse_args(args)
let [L, R, C, K, S, D, N] = ['l', 'r', 'c', 'k', 's', 'd', 'n']
let o = eval(cand)
if type(o) == 4
let option = o
let opts = o
if args[midx - 1 : midx] == '\ '
let midx += 1
endif
@@ -511,77 +779,90 @@ function! s:parse_args(args)
endwhile
" Invalid option dictionary
if len(substitute(cand, '\s', '', 'g')) > 2 && empty(option)
if len(substitute(cand, '\s', '', 'g')) > 2 && empty(opts)
call s:exit("Invalid option: ". cand)
else
let opts = s:normalize_options(opts)
endif
" Has /Regexp/?
let matches = matchlist(args, '^\(.\{-}\)\s*/\(.*\)/\s*$')
let matches = matchlist(args, '^\(.\{-}\)\s*/\(.*\)/\(.\{-}\)$')
" Found regexp
if !empty(matches)
let regexp = matches[2]
" Test regexp
try | call matchlist('', regexp)
catch | call s:exit("Invalid regular expression: ". regexp)
endtry
return [matches[1], regexp, option, 1]
if !empty(matches[3])
let opts = extend(s:parse_shortcut_opts(matches[3]), opts)
endif
return [matches[1], s:test_regexp(matches[2]), opts, 1]
else
let tokens = matchlist(args, '^\([1-9][0-9]*\|-[0-9]*\|\*\*\?\)\?\s*\(.\{-}\)\?$')
return [tokens[1], tokens[2], option, 0]
return [tokens[1], tokens[2], opts, 0]
endif
endfunction
function! s:modes(bang)
return get(g:,
\ (a:bang ? 'easy_align_bang_interactive_modes' : 'easy_align_interactive_modes'),
\ (a:bang ? ['r', 'l', 'c'] : ['l', 'r', 'c']))
endfunction
function! s:alternating_modes(mode)
return a:mode ==? 'r' ? ['r', 'l'] : ['l', 'r']
endfunction
function! easy_align#align(bang, expr) range
let modes = get(g:,
\ (a:bang ? 'easy_align_bang_interactive_modes' : 'easy_align_interactive_modes'),
\ (a:bang ? ['r', 'l', 'c'] : ['l', 'r', 'c']))
try
call s:align(a:bang, a:firstline, a:lastline, a:expr)
catch 'exit'
endtry
endfunction
function! s:align(bang, first_line, last_line, expr)
let modes = s:modes(a:bang)
let mode = modes[0]
let recur = 0
let n = ''
let ch = ''
let option = {}
let opts = {}
let regexp = 0
" Heuristically determine if the user was in visual mode
let vis = a:first_line == line("'<") && a:last_line == line("'>")
try
if empty(a:expr)
let [mode, n, ch] = s:interactive(copy(modes))
else
let [n, ch, option, regexp] = s:parse_args(a:expr)
if empty(n) && empty(ch)
let [mode, n, ch] = s:interactive(copy(modes))
elseif empty(ch)
" Try swapping n and ch
let [n, ch] = ['', n]
endif
let delimiters = s:easy_align_delimiters_default
if exists('g:easy_align_delimiters')
let delimiters = extend(copy(delimiters), g:easy_align_delimiters)
endif
if empty(a:expr)
let [mode, n, ch, opts, regexp] = s:interactive(copy(modes), vis, opts, delimiters)
else
let [n, ch, opts, regexp] = s:parse_args(a:expr)
if empty(n) && empty(ch)
let [mode, n, ch, opts, regexp] = s:interactive(copy(modes), vis, opts, delimiters)
elseif empty(ch)
" Try swapping n and ch
let [n, ch] = ['', n]
endif
catch 'exit'
return
endtry
endif
if n == '*' | let [nth, recur] = [1, 1]
elseif n == '**' | let [nth, recur] = [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
call s:exit('Invalid N-th parameter: '. n)
else
let nth = n
endif
let delimiters = s:easy_align_delimiters_default
if exists('g:easy_align_delimiters')
let delimiters = extend(copy(delimiters), g:easy_align_delimiters)
endif
if regexp
let dict = { 'pattern': ch }
else
" Resolving command-line ambiguity
if !empty(a:expr)
" '\ ' => ' '
if ch =~ '^\\\s\+$'
" '\' => ' '
if ch =~ '^\\\s*$'
let ch = ' '
" '\\' => '\'
elseif ch =~ '^\\\\\s*$'
@@ -589,53 +870,61 @@ function! easy_align#align(bang, expr) range
endif
endif
if !has_key(delimiters, ch)
echon "\rUnknown delimiter key: ". ch
return
call s:exit('Unknown delimiter key: '. ch)
endif
let dict = delimiters[ch]
let dict = copy(delimiters[ch])
endif
try
if !empty(option)
let dict = extend(copy(dict), s:normalize_options(option))
endif
catch 'exit'
return
endtry
call extend(dict, opts)
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
let bvisual = char2nr(visualmode()) == 22 " ^V
let bvisual = vis && char2nr(visualmode()) == 22 " ^V
if recur && bvisual
echon "\rRecursive alignment is currently not supported in blockwise-visual mode"
return
call s:exit('Recursive alignment is not supported in blockwise-visual mode')
endif
let aseq = get(dict, 'mode_sequence',
\ recur == 2 ? (mode == 'r' ? ['r', 'l'] : ['l', 'r']) : [mode])
\ recur == 2 ? s:alternating_modes(mode) : [mode])
let mode_expansion = matchstr(aseq, '\*\+$')
if mode_expansion == '*'
let aseq = aseq[0 : -2]
let recur = 1
elseif mode_expansion == '**'
let aseq = aseq[0 : -3]
let recur = 2
endif
let aseq_list = type(aseq) == 1 ? split(tolower(aseq), '\s*') : map(copy(aseq), 'tolower(v:val)')
let aseq_str = join(aseq_list, '')
try
call s:do_align(
\ type(aseq) == 1 ? split(tolower(aseq), '\s*') : map(copy(aseq), 'tolower(v:val)'),
\ {}, {}, a:firstline, a:lastline,
call s:do_align(
\ aseq_list,
\ {}, {}, a:first_line, a:last_line,
\ bvisual ? min([col("'<"), col("'>")]) : 1,
\ bvisual ? max([col("'<"), col("'>")]) : 0,
\ get(dict, 'pattern', ch),
\ nth,
\ ml,
\ mr,
\ get(dict, 'delimiter_align', get(g:, 'easy_align_delimiter_align', 'r')),
\ get(dict, 'indentation', get(g:, 'easy_align_indentation', 'k')),
\ get(dict, 'delimiter_align', get(g:, 'easy_align_delimiter_align', 'r'))[0],
\ get(dict, 'indentation', get(g:, 'easy_align_indentation', 'k'))[0],
\ get(dict, 'stick_to_left', 0),
\ get(dict, 'ignore_unmatched', get(g:, 'easy_align_ignore_unmatched', 1)),
\ get(dict, 'ignores', s:ignored_syntax()),
\ get(dict, 'ignore_unmatched', get(g:, 'easy_align_ignore_unmatched', 2)),
\ get(dict, 'ignore_groups', get(dict, 'ignores', s:ignored_syntax())),
\ recur)
call s:echon(mode, n, regexp ? '/'.ch.'/' : ch)
catch 'exit'
endtry
let copts = s:compact_options(opts)
let nbmode = s:modes(0)[0]
if !has_key(copts, 'm') && (
\ (recur == 2 && join(s:alternating_modes(nbmode), '') != aseq_str) ||
\ (recur != 2 && (aseq_str[0] != nbmode || len(aseq_str) > 1))
\ )
call extend(copts, { 'm': aseq_str })
endif
let g:easy_align_last_command = s:echon('', n, regexp, ch, copts, '')
endfunction

View File

@@ -16,11 +16,11 @@ EasyAlign *:EasyAlign* *:EasyAlign!*
vim-easy-align defines `:EasyAlign` command in the visual mode.
(:EasyAlign! is the right-align version.)
| Mode | Command |
| ------------------------- | ---------------------------------------------- |
| Interactive mode | :EasyAlign[!] [OPTIONS] |
| Using predefined rules | :EasyAlign[!] [FIELD#] DELIMITER_KEY [OPTIONS] |
| Using regular expressions | :EasyAlign[!] [FIELD#] /REGEXP/ [OPTIONS] |
| Mode | Command |
| ------------------------- | -------------------------------------------- |
| Interactive mode | :EasyAlign[!] [OPTIONS] |
| Using predefined rules | :EasyAlign[!] [N-th] DELIMITER_KEY [OPTIONS] |
| Using regular expressions | :EasyAlign[!] [N-th] /REGEXP/ [OPTIONS] |
Interactive mode
@@ -30,13 +30,13 @@ 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`.
vnoremap <silent> <Enter> :EasyAlign<cr>
vnoremap <silent> <Enter> :EasyAlign<Enter>
With this mapping, you can align selected lines of text with a few keystrokes.
1. <Enter> key to start interactive EasyAlign command
2. Optional Enter keys to select alignment mode (left, right, or center)
3. Optional field number (default: 1)
2. Optional: Enter keys to select alignment mode (left, right, or center)
3. Optional: N-th delimiter (default: 1)
1 Around the 1st occurrences of delimiters
2 Around the 2nd occurrences of delimiters
* Around all occurrences of delimiters
@@ -72,18 +72,52 @@ Examples:
<Enter><Enter>**= Right-left alternating alignment around all equals signs
Non-interactive mode
Instead of finishing the command with a predefined delimiter key, you can type
in a regular expression after `<CTRL-/>` or `<CTRL-X>` key.
For example, if you want to align text around all occurrences of numbers:
- <Enter>
- *
- <CTRL-/> (or <CTRL-X> on Gvim)
- [0-9]\+
While in interactive mode, you can adjust some of the alignment options using
special shortcut keys listed below. The meanings of the options will be
described in the following sections.
| Key | Option | Values |
| ------- | ---------------- | -------------------------------------------- |
| 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}/ |
| <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 } |
*g:easy_align_last_command*
After a successful alignment, you can repeat the same operation using the
repeatable, non-interactive command recorded in `g:easy_align_last_command`.
:<C-R>=g:easy_align_last_command<Enter><Enter>
Non-interactive mode (command line)
-------------------------------------------------------------------------
Instead of going into the interactive mode, you can type in arguments to
`:EasyAlign` command. In non-interactive mode, you can even use arbitrary
regular expressions.
`:EasyAlign` command.
" Using predefined alignment rules
:EasyAlign[!] [FIELD#] DELIMITER_KEY [OPTIONS]
:EasyAlign[!] [N-th] DELIMITER_KEY [OPTIONS]
" Using arbitrary regular expressions
:EasyAlign[!] [FIELD#] /REGEXP/ [OPTIONS]
:EasyAlign[!] [N-th] /REGEXP/ [OPTIONS]
For example, when aligning the following lines around colons and semi-colons,
@@ -92,16 +126,16 @@ For example, when aligning the following lines around colons and semi-colons,
try these commands:
- :EasyAlign /[:;]\+/
- :EasyAlign 2/[:;]\+/
- :EasyAlign */[:;]\+/
- :EasyAlign **/[:;]\+/
:EasyAlign /[:;]\+/
:EasyAlign 2/[:;]\+/
:EasyAlign */[:;]\+/
:EasyAlign **/[:;]\+/
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
format.
- :EasyAlign * /[:;]\+/ { 'stick_to_left': 1, 'left_margin': '' }
:EasyAlign * /[:;]\+/ { 'stick_to_left': 1, 'left_margin': 0 }
Then we get:
@@ -110,27 +144,38 @@ Then we get:
Option names are fuzzy-matched, so you can write as follows:
- :EasyAlign * /[:;]\+/ { 'stl': 1, 'l': '' }
:EasyAlign * /[:;]\+/ { 'stl': 1, 'l': '' }
You can even omit spaces between the arguments, so concisely (or cryptically):
- :EasyAlign*/[:;]\+/{'s':1,'l':''}
:EasyAlign*/[:;]\+/{'s':1,'l':''}
Available options are as follows.
Nice. But let's make it even shorter. When you use write regular expression in
command line, option values can be written in shorthand notation.
| Atrribute | Type | Default |
| ---------------- | ---------------- | ----------------------------- |
| left_margin | number or string | 0 |
| right_margin | number or string | 0 |
| stick_to_left | boolean | 0 |
| ignore_unmatched | boolean | 1 |
| ignores | array | ['String', 'Comment'] |
| delimiter_align | string | 'r' |
| | | (right, left, center) |
| indentation | string | 'k' |
| | | (keep, shallow, deep, none) |
| mode_sequence | string | (Depends on field number and |
| | | selected alignment mode) |
:EasyAlign*/[:;]\+/s1l0
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] |
For your information, the same thing can be done in the interactive mode as well
with the following key combination.
- <Enter>
- *
- <Left>
- <CTRL-/> (or <CTRL-X> on GVim)
- [:;]\+
Partial alignment in blockwise-visual mode
@@ -140,7 +185,45 @@ 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 delimiters in comments or strings *g:easy_align_ignores*
Alignment options
-------------------------------------------------------------------------
Available options are as follows.
| Atrribute | Type | Default |
| ---------------- | ---------------- | ----------------------------- |
| left_margin | number or string | 1 |
| right_margin | number or string | 1 |
| stick_to_left | boolean | 0 |
| ignore_unmatched | boolean | 1 |
| ignore_groups | array | ['String', 'Comment'] |
| delimiter_align | string | 'r' |
| | | (right, left, center) |
| indentation | string | 'k' |
| | | (keep, shallow, deep, none) |
| mode_sequence | string | (Depends on N-th param and |
| | | selected alignment mode) |
There are 4 ways to set alignment options (from lowest precedence to highest):
1. Some option values can be set with corresponding global variables
2. Option values can be specified in the definition of each alignment rule
3. Option values can be given as arguments to :EasyAlign command
4. Option values can be set in interactive mode using special shortcut keys
| Option | Shortcut key | Abbreviated | Global variable |
| ---------------- | --------------- | ----------- | ----------------------------- |
| left_margin | CTRL-L | l[0-9]+ | |
| right_margin | CTRL-R | r[0-9]+ | |
| stick_to_left | <Left>, <Right> | s[01] | |
| ignore_groups | CTRL-G | ig\[.*\] | `g:easy_align_ignore_groups` |
| ignore_unmatched | CTRL-U | iu[01] | `g:easy_align_ignore_unmatched` |
| indentation | CTRL-I | i[ksdn] | `g:easy_align_indentation` |
| delimiter_align | CTRL-D | d[lrc] | `g:easy_align_delimiter_align` |
| mode_sequence | CTRL-O | m[lrc*]+ | |
Ignoring delimiters in comments or strings *g:easy_align_ignore_groups*
-------------------------------------------------------------------------
EasyAlign can be configured to ignore delimiters in certain syntax
@@ -150,7 +233,7 @@ 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']
let g:easy_align_ignore_groups = ['Comment', 'String']
For example, the following paragraph
@@ -176,13 +259,15 @@ becomes as follows on '<Enter>:' (or `:EasyAlign:`)
Naturally, this feature only works when syntax highlighting is enabled.
You can change the default rule by using one of these 3 methods.
You can change the default rule by using one of these 4 methods.
1. Define global `g:easy_align_ignores` list
2. Define a custom rule in `g:easy_align_delimiters` with 'ignores' option
3. Provide 'ignores' option to `:EasyAlign` command. e.g. :EasyAlign:{'is':[]}
1. Press CTRL-G in interactive mode to switch groups
2. Define global `g:easy_align_ignore_groups` list
3. Define a custom rule in `g:easy_align_delimiters` with 'ignore_groups' option
4. Provide 'ignore_groups' option to `:EasyAlign` command.
e.g. :EasyAlign:{'is':[]}
For example if you set 'ignores' option to be an empty list, you get
For example if you set 'ignore_groups' option to be an empty list, you get
{
# Quantity of apples: 1
@@ -220,12 +305,13 @@ this is usually what we want.
grapefruits: 3
}
However, this default behavior is also configurable by using one of these 3
However, this default behavior is also configurable by using one of these 4
methods.
1. Set the global `g:easy_align_ignore_unmatched` variable to 0
2. Define a custom alignment rule with 'ignore_unmatched' option set to 0
3. Provide 'ignore_unmatched' option to `:EasyAlign` command.
1. Press CTRL-U in interactive mode to toggle 'ignore_unmatched' option
2. Set the global `g:easy_align_ignore_unmatched` variable to 0
3. Define a custom alignment rule with 'ignore_unmatched' option set to 0
4. Provide 'ignore_unmatched' option to `:EasyAlign` command.
e.g. :EasyAlign:{'iu':0}
Then we get,
@@ -268,6 +354,8 @@ And on ':EasyAlign={'da':c}', center-aligned.
banana += apple
cake ||= banana
In interactive mode, you can change the option value with `CTRL-D` key.
Adjusting indentation *g:easy_align_indentation*
-------------------------------------------------------------------------
@@ -317,6 +405,8 @@ But then again we have 'indentation' option. See the following example.
Notice that 'idt' is fuzzy-matched to 'indentation'.
In interactive mode, you can change the option value with `CTRL-I` key.
Left/right/center mode switch in interactive mode
-------------------------------------------------------------------------
@@ -343,7 +433,7 @@ settings as follows.
Alignments over multiple occurrences of delimiters
-------------------------------------------------------------------------
As stated above, "field number" is used to target specific occurrences of
As stated above, "N-th" parameter is used to target specific occurrences of
the delimiter when it appears multiple times in each line.
To recap:
@@ -367,7 +457,8 @@ To recap:
:EasyAlign! **=
In addition to these, you can fine-tune alignments over multiple occurrences of
the delimiters with 'mode_sequence' option.
the delimiters with 'mode_sequence' option. (The option can also be set
in interactive mode with the special key CTRL-O)
" Left alignment over the first two occurrences of delimiters
:EasyAlign = { 'mode_sequence': 'll' }
@@ -386,6 +477,14 @@ the delimiters with 'mode_sequence' option.
" Right, left, center, right, left, center, ...
:EasyAlign **={ 'm': 'rlc' }
" Right, left, center, center, center, ... repeating alignment
" over the 3rd to the last occurrences of delimiters
:EasyAlign 3={ 'm': 'rlc*' }
" Right, left, center, right, left, center, ... alternating alignment
" over the 3rd to the last occurrences of delimiters
:EasyAlign 3={ 'm': 'rlc**' }
Extending alignment rules *g:easy_align_delimiters*
-------------------------------------------------------------------------
@@ -396,8 +495,8 @@ you can extend the rules by setting a dictionary named
let g:easy_align_delimiters = {
\ '>': { 'pattern': '>>\|=>\|>' },
\ '/': { 'pattern': '//\+\|/\*\|\*/', 'ignores': ['String'] },
\ '#': { 'pattern': '#\+', 'ignores': ['String'] },
\ '/': { 'pattern': '//\+\|/\*\|\*/', 'ignore_groups': ['String'] },
\ '#': { 'pattern': '#\+', 'ignore_groups': ['String'] },
\ ']': {
\ 'pattern': '[[\]]',
\ 'left_margin': 0,

View File

@@ -104,9 +104,59 @@ j, , k
```ruby
a =
a = 1
bbbb .= 2
ccccccc = 3
ccccccccccccccc
ddd = #
eeee === eee = eee = eee = f
fff = ggg += gg &&= gg
g != hhhhhhhh == # 8
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
a =
a = 1
bbbb .= 2
ccccccc = 3
ccccccccccccccc
ddd = #
eeee === eee = eee = eee = f
fff = ggg += gg &&= gg
g != hhhhhhhh == # 8
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
a =
a = 1
bbbb = 2
bbbb .= 2
ccccccc = 3
ccccccccccccccc
ddd = #
@@ -131,7 +181,7 @@ ccccccccccccccc
a =
a = 1
bbbb = 2
bbbb .= 2
ccccccc = 3
ccccccccccccccc
ddd = #
@@ -156,7 +206,7 @@ gg <=> ee
a =
a = 1
bbbb = 2
bbbb .= 2
ccccccc = 3
ccccccccccccccc
ddd = #
@@ -181,7 +231,7 @@ gg <=> ee
a =
a = 1
bbbb = 2
bbbb .= 2
ccccccc = 3
ccccccccccccccc
ddd = #
@@ -655,7 +705,7 @@ string i = "asdf";
a =
a = 1
bbbb = 2
bbbb .= 2
ccccccc = 3
ccccccccccccccc
# eeeeeeeeeeeeeeee
@@ -682,7 +732,7 @@ gg <=> ee
a =
a = 1
bbbb = 2
bbbb .= 2
ccccccc = 3
ccccccccccccccc
# eeeeeeeeeeeeeeee
@@ -709,7 +759,7 @@ gg <=> ee
a =
a = 1
bbbb = 2
bbbb .= 2
ccccccc = 3
ccccccccccccccc
# eeeeeeeeeeeeeeee
@@ -736,7 +786,7 @@ e # asdf
a =
a = 1
bbbb = 2
bbbb .= 2
ccccccc = 3
ccccccccccccccc
# eeeeeeeeeeeeeeee
@@ -763,7 +813,7 @@ gg <=> ee
a =
a = 1
bbbb = 2
bbbb .= 2
ccccccc = 3
ccccccccccccccc
# eeeeeeeeeeeeeeee
@@ -790,7 +840,7 @@ e # asdf
a =
a = 1
bbbb = 2
bbbb .= 2
ccccccc = 3
ccccccccccccccc
# eeeeeeeeeeeeeeee
@@ -921,3 +971,105 @@ queue_size | Fixnum | 1000 | size of each queue
batch_size | Fixnum | nil | number of maximum items to be assigned at once |
logger | Logger | nil | logger instance for debug logs |
```
aaaaa = 123456778901234567890 =
cccccccccccccccccc =12345678 =
aaaa = 123456778901234567890 =
cccccccccccccccccc = 12345678 =
aaaa = 123456778901234567890 =
cccccccccccccccccc = 12345678 =
aaaa = 123456778901234567890 =
cccccccccccccccccc = 12345678 =
aaaa = 123456778901234567890 =
cccccccccccccccccc = 12345678 =
aaaa = 123456778901234567890 =
cccccccccccccccccc = 12345678 =
aaaaaaaaaaaaa = 123456778901234567890 =
cc = 12345678 =
aaaaaaaaaaa = 123
a = 123
aaaaaaaaaaaa = 123
a = 123
aaaaaaaaaaaa = 123
aaaaaaaaaaa = 123
aaaaaaaaaa = 123
aaa = 123
aa = 123
a = 123
aaaaaaaaaaaa = 123
aaaaaaaaaaa = 123
aaaaaaaaaa = 123
aaa = 123
aa = 123
a = 123
aaaaaaaaaaaa = 123
aaaaaaaaaaa = 123
aaaaaaaaaa = 123
aaa = 123
aa = 123
a = 123
aaaa = 123456778901234567890 =
cccccccccccccc = 12345678 =
aaaa = 123456778901234567890 =
bbbbbb = 4
cccccccccccccccccc = 12345678 =
aaaa = 123456778901234567890 =
cccccccccccccccccc = 12345678 =
aaaaa = 123456778901234567890 =
cc = 12345678 =
= aaaaa = 123456778901234567890 =
= cccccccccccccccccc = 12345678 =
=aaaaa = 123456778901234567890 =
= cccccccccccccccccc = 12345678 =
```
a, , bbb
aa, , bb
aaa, , b
aaaa, ,
aaa, b,
aa, bb,
a, bbb,
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
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
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
a = 1
bb = 2
ccccc
ddd = 3
a = 1
bb = 2
ccccc
ddd = 3

View File

@@ -26,7 +26,7 @@ j,,k
a =
a = 1
bbbb = 2
bbbb .= 2
ccccccc = 3
ccccccccccccccc
ddd = #
@@ -156,7 +156,7 @@ string i = "asdf";
a =
a = 1
bbbb = 2
bbbb .= 2
ccccccc = 3
ccccccccccccccc
# eeeeeeeeeeeeeeee
@@ -215,5 +215,59 @@ bbbbbbbbbb = 123456778901234567890
123456 7890
aaaaa = 123456778901234567890 =
cccccccccccccccccc = 12345678 =
```
aaaaa = 123456778901234567890 =
cccccccccccccccccc =12345678 =
aaaa = 123456778901234567890 =
cccccccccccccccccc = 12345678 =
aaaaaaaaaaaaa = 123456778901234567890 =
cc = 12345678 =
aaaaaaaaaaa= 123
a = 123
aaaaaaaaaaaa= 123
a = 123
aaaaaaaaaaaa = 123
aaaaaaaaaaa = 123
aaaaaaaaaa = 123
aaa = 123
aa = 123
a = 123
aaaa = 123456778901234567890 =
cccccccccccccc = 12345678 =
aaaa = 123456778901234567890 =
bbbbbb = 4
cccccccccccccccccc = 12345678 =
aaaa = 123456778901234567890 =
cccccccccccccccccc = 12345678 =
aaaaa = 123456778901234567890 =
cc = 12345678 =
=aaaaa = 123456778901234567890 =
= cccccccccccccccccc = 12345678 =
```
a,,bbb
aa,,bb
aaa,,b
aaaa,,
aaa,b,
aa,bb,
a,bbb,
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

View File

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

View File

@@ -1,45 +0,0 @@
function! GFM()
let syntaxes = {
\ 'ruby': 'syntax/ruby.vim',
\ 'yaml': 'syntax/yaml.vim',
\ 'vim': 'syntax/vim.vim',
\ 'sh': 'syntax/sh.vim',
\ 'python': 'syntax/python.vim',
\ 'java': 'syntax/java.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'
syntax sync fromstart
endfunction
silent! unlet g:easy_align_delimiters
silent! unlet g:easy_align_ignore_unmatched
silent! unlet g:easy_align_ignores
vnoremap <silent> <Enter> :EasyAlign<cr>
noremap <silent> <C-k> <nop>
noremap <silent> <C-j> <nop>
noremap <silent> <C-h> <nop>
noremap <silent> <C-l> <nop>
vnoremap <silent> <C-k> <nop>
vnoremap <silent> <C-j> <nop>
vnoremap <silent> <C-h> <nop>
vnoremap <silent> <C-l> <nop>
set nolazyredraw
set buftype=nofile
set colorcolumn=
silent! ScrollPositionHide
call GFM()

View File

@@ -94,3 +94,15 @@ banana = 'Gros Michel' # comment 2
a()p()p()l()e();():()b()a()n()a()n()a():():()c()a()k()e(
d()a()t()a();();()e()x()c()h()a()n()g()e():();()f()o()r()m()a()t(
apple ;: banana :: cake
data ;;exchange :;format
apple ;: banana :: cake
data ;; exchange :; format
apple;:banana ::cake
data ;;exchange:;format
apple ;: banana :: cake
data ;; exchange :; format

View File

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

View File

@@ -1,5 +1,49 @@
e!
execute 'source '. expand('%:p:h') . '/include.vim'
function! GFM()
let syntaxes = {
\ 'ruby': 'syntax/ruby.vim',
\ 'yaml': 'syntax/yaml.vim',
\ 'vim': 'syntax/vim.vim',
\ 'sh': 'syntax/sh.vim',
\ 'python': 'syntax/python.vim',
\ 'java': 'syntax/java.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'
syntax sync fromstart
endfunction
silent! unlet g:easy_align_delimiters
silent! unlet g:easy_align_ignore_unmatched
silent! unlet g:easy_align_ignores
vnoremap <silent> <Enter> :EasyAlign<cr>
noremap <silent> <C-k> <nop>
noremap <silent> <C-j> <nop>
noremap <silent> <C-h> <nop>
noremap <silent> <C-l> <nop>
vnoremap <silent> <C-k> <nop>
vnoremap <silent> <C-j> <nop>
vnoremap <silent> <C-h> <nop>
vnoremap <silent> <C-l> <nop>
set nolazyredraw
set buftype=nofile
set colorcolumn=
silent! ScrollPositionHide
call GFM()
normal gg
let @b=system('cat '. expand('%:p:r') . '.script')