mirror of
https://github.com/junegunn/vim-easy-align.git
synced 2025-11-18 06:43:40 -05:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61aeefb095 | ||
|
|
36697b4a72 | ||
|
|
9b20830c70 | ||
|
|
5f59570c9f | ||
|
|
5b48e997a1 | ||
|
|
67ac2f74e6 | ||
|
|
94524ec3d3 | ||
|
|
3e700c6ce4 | ||
|
|
3ee8cdfd9e | ||
|
|
a6e707b1b3 | ||
|
|
34a555c220 | ||
|
|
912bb949f7 | ||
|
|
81138d0e2c | ||
|
|
214efaad0c | ||
|
|
29078629f5 | ||
|
|
3ab67e2870 | ||
|
|
8dd2daaa9e | ||
|
|
e3f7352ab8 | ||
|
|
0fe7d17908 | ||
|
|
27b95b4ce0 | ||
|
|
5c870f60d9 | ||
|
|
cb71847a11 | ||
|
|
811ea63b11 | ||
|
|
9c9a16c759 | ||
|
|
1e627366e7 | ||
|
|
32224d88f2 |
136
EXAMPLES.md
Normal file
136
EXAMPLES.md
Normal file
@@ -0,0 +1,136 @@
|
||||
vim: set scrolloff=1 buftype=nofile colorcolumn=:
|
||||
|
||||
vim-easy-align examples
|
||||
=======================
|
||||
|
||||
To enable syntax highlighting in the code blocks, define and call the following
|
||||
function.
|
||||
|
||||
```vim
|
||||
function! GFM()
|
||||
let syntaxes = {
|
||||
\ 'ruby': 'syntax/ruby.vim',
|
||||
\ 'yaml': 'syntax/yaml.vim',
|
||||
\ 'vim': 'syntax/vim.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'
|
||||
endfunction
|
||||
```
|
||||
|
||||
Alignment around whitespaces
|
||||
----------------------------
|
||||
|
||||
```
|
||||
|
||||
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 batch processing |
|
||||
|batch | Boolean | false | enables batch processing mode |
|
||||
|batch_size | Fixnum | nil | number of maximum items to be assigned at once |
|
||||
|logger | Logger | nil | logger instance for debug logs |
|
||||
|
||||
|
||||
Alignment around =
|
||||
------------------
|
||||
|
||||
```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
|
||||
|
||||
```
|
||||
|
||||
Partial alignment in block-visual mode / Negative field index
|
||||
-------------------------------------------------------------
|
||||
|
||||
```ruby
|
||||
|
||||
options = { :caching => nil,
|
||||
:versions => 3,
|
||||
"cache=blocks" => false }.merge(options)
|
||||
|
||||
```
|
||||
|
||||
Commas
|
||||
------
|
||||
|
||||
```
|
||||
|
||||
aaa, bb,c
|
||||
d,eeeeeee
|
||||
fffff, gggggggggg,
|
||||
h, , ii
|
||||
j,,k
|
||||
|
||||
```
|
||||
|
||||
Ignoring delimiters in comments and strings
|
||||
-------------------------------------------
|
||||
|
||||
```c
|
||||
/* a */ b = c
|
||||
aa >= bb
|
||||
// aaa = bbb = cccc
|
||||
/* aaaa = */ bbbb === cccc " = dddd = " = eeee
|
||||
aaaaa /* bbbbb */ == ccccc /* != eeeee = */ === fffff
|
||||
```
|
||||
|
||||
238
README.md
238
README.md
@@ -8,7 +8,15 @@ Demo
|
||||
|
||||

|
||||
|
||||
[Screencast](https://vimeo.com/63506219)
|
||||
Features
|
||||
--------
|
||||
|
||||
- Optimized for code editing
|
||||
- Designed to require minimal keystrokes
|
||||
- Extensible alignment rules
|
||||
- Aligns text around either _all or n-th_ occurrence(s) of the delimiter
|
||||
- Ignores delimiters in certain syntax highlight groups (e.g. comments, strings)
|
||||
- Ignores lines without a matching delimiter
|
||||
|
||||
Installation
|
||||
------------
|
||||
@@ -27,7 +35,8 @@ Usage
|
||||
-----
|
||||
|
||||
_vim-easy-align_ defines interactive `:EasyAlign` command in the visual mode.
|
||||
For convenience, it is advised that you define a mapping for triggering it in your `.vimrc`.
|
||||
For convenience, it is advised that you define a mapping for triggering it in
|
||||
your `.vimrc`.
|
||||
|
||||
```vim
|
||||
vnoremap <silent> <Enter> :EasyAlign<cr>
|
||||
@@ -38,41 +47,48 @@ With the mapping, you can align selected lines with a few keystrokes.
|
||||
1. `<Enter>` key to start interactive EasyAlign command
|
||||
1. Optional Enter keys to toggle right-justification mode
|
||||
1. Optional field number (default: 1)
|
||||
- `1` Alignment around 1st delimiters
|
||||
- `2` Alignment around 2nd delimiters
|
||||
- `1` Around the 1st occurrences of delimiters
|
||||
- `2` Around the 2nd occurrences of delimiters
|
||||
- ...
|
||||
- `*` Alignment around all delimiters (recursive)
|
||||
1. Delimiter (`<space>`, `=`, `:`, `.`, `|`, `,`)
|
||||
- `*` Around all occurrences of delimiters
|
||||
- `**` Left-right alternating alignment around all delimiters
|
||||
- `-` Around the last occurrences of delimiters (`-1`)
|
||||
- `-2` Around the second to last occurrences of delimiters
|
||||
- ...
|
||||
1. Delimiter key (a single keystroke; `<space>`, `=`, `:`, `.`, `|`, `,`)
|
||||
|
||||
Alignment rules for the following delimiters have been crafted to meet the most needs.
|
||||
Alignment rules for the following delimiters have been defined to meet the most needs.
|
||||
|
||||
| Delimiter | Description/Use cases |
|
||||
| --------- | ---------------------------------------------------------- |
|
||||
| `<space>` | General alignment around spaces |
|
||||
| `=` | Operators containing equals sign (=, ==, !=, +=, &&=, ...) |
|
||||
| `:` | Suitable for formatting JSON or YAML |
|
||||
| `.` | Multi-line method chaining |
|
||||
| `,` | Multi-line method arguments |
|
||||
| | | Table markdown |
|
||||
| Delimiter key | Description/Use cases |
|
||||
| ------------- | ---------------------------------------------------------- |
|
||||
| `<space>` | General alignment around spaces |
|
||||
| `=` | Operators containing equals sign (=, ==, !=, +=, &&=, ...) |
|
||||
| `:` | Suitable for formatting JSON or YAML |
|
||||
| `.` | Multi-line method chaining |
|
||||
| `,` | Multi-line method arguments |
|
||||
| | | Table markdown |
|
||||
|
||||
### Example command sequences
|
||||
|
||||
| With visual map | Description | Equivalent command |
|
||||
| ----------------- | ------------------------------------------------- | ----------------------- |
|
||||
| `<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><Enter>=` | Right-justified alignment around 1st equals signs | `:'<,'>EasyAlignRight=` |
|
||||
| `<Enter><space>` | Alignment around 1st space | `:'<,'>EasyAlign\ ` |
|
||||
| `<Enter>2<space>` | Alignment around 2nd space | `:'<,'>EasyAlign2\ ` |
|
||||
| `<Enter>:` | Alignment around 1st colon | `:'<,'>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>:` | 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-justified alignment around 1st equals signs | `:'<,'>EasyAlignRight=` |
|
||||
| `<Enter><Enter>**=` | Right-left alternating alignment around all equals signs | `:'<,'>EasyAlignRight**=` |
|
||||
| ... | ... | |
|
||||
|
||||
### Partial alignment in blockwise-visual mode
|
||||
|
||||
In blockwise-visual mode (`CTRL-V`), EasyAlign command aligns only the selected
|
||||
parts, instead of the whole lines in the range.
|
||||
text in the block, instead of the whole lines in the range.
|
||||
|
||||
Consider the following case where you want to align text around `=>` operators.
|
||||
|
||||
@@ -82,9 +98,9 @@ my_hash = { :a => 1,
|
||||
:aaa => 3 }
|
||||
```
|
||||
|
||||
In non-blockwise visual mode (`v` / `V`), `<Enter>=` won't work since the assignment
|
||||
operator in the first line gets in the way.
|
||||
So we instead enter blockwise-visual mode (`CTRL-V`), and select the text *around*
|
||||
In non-blockwise visual mode (`v` / `V`), `<Enter>=` won't work since the
|
||||
assignment operator in the first line gets in the way. So we instead enter
|
||||
blockwise-visual mode (`CTRL-V`), and select the text *around*
|
||||
`=>` operators, then press `<Enter>=`.
|
||||
|
||||
```ruby
|
||||
@@ -93,56 +109,111 @@ my_hash = { :a => 1,
|
||||
:aaa => 3 }
|
||||
```
|
||||
|
||||
Defining custom alignment rules
|
||||
-------------------------------
|
||||
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>-=`
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
| ----------------------------- | ---------- | --------------------- | -------------------------------------------------- |
|
||||
| g:easy_align_ignores | list | ['String', 'Comment'] | Ignore delimiters in these syntax highlight groups |
|
||||
| g:easy_align_ignore_unmatched | boolean | `1` | Ignore lines without matching delimiter |
|
||||
| g:easy_align_delimiters | dictionary | `{}` | Extend or override alignment rules |
|
||||
|
||||
### Ignoring delimiters in comments or strings
|
||||
|
||||
EasyAlign can be configured to ignore delimiters in certain syntax highlight
|
||||
groups, such as code comments or strings. By default, delimiters that are
|
||||
highlighted as code comments or strings are ignored.
|
||||
|
||||
```vim
|
||||
let g:easy_align_delimiters = {
|
||||
\ '>': { 'pattern': '>>\|=>\|>' },
|
||||
\ '/': { 'pattern': '//*' },
|
||||
\ 'x': {
|
||||
\ 'pattern': '[xX]',
|
||||
\ 'margin_left': ' <<<',
|
||||
\ 'margin_right': '>>> ',
|
||||
\ 'stick_to_left': 0
|
||||
\ }
|
||||
\ }
|
||||
" 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']
|
||||
```
|
||||
|
||||
Handling unmatched lines
|
||||
------------------------
|
||||
For example, the following paragraph
|
||||
|
||||
EasyAlign by default ignores lines without the matching delimiters (except in right-justification mode).
|
||||
This is to ignore interleaved comments commonly found in code.
|
||||
|
||||
For example, when aligning the following code block,
|
||||
|
||||
```
|
||||
```ruby
|
||||
{
|
||||
# Quantity of apples
|
||||
# Quantity of apples: 1
|
||||
apple: 1,
|
||||
# Quantity of bananas
|
||||
# Quantity of bananas: 2
|
||||
bananas: 2,
|
||||
# Quantity of grapefruits
|
||||
grapefruits: 3
|
||||
# Quantity of grape:fruits: 3
|
||||
'grape:fruits': 3
|
||||
}
|
||||
```
|
||||
|
||||
we don't want the comment lines to affect the alignment,
|
||||
so this is usually what we want.
|
||||
becomes as follows on `<Enter>:`
|
||||
|
||||
```
|
||||
```ruby
|
||||
{
|
||||
# Quantity of apples
|
||||
apple: 1,
|
||||
# Quantity of bananas
|
||||
bananas: 2,
|
||||
# Quantity of grapefruits
|
||||
# Quantity of apples: 1
|
||||
apple: 1,
|
||||
# Quantity of bananas: 2
|
||||
bananas: 2,
|
||||
# Quantity of grape:fruits: 3
|
||||
'grape:fruits': 3
|
||||
}
|
||||
```
|
||||
|
||||
Naturally, this feature only works when syntax highlighting is enabled.
|
||||
|
||||
You can change the default rule by defining `g:easy_align_ignores` array.
|
||||
|
||||
```vim
|
||||
" Ignore nothing!
|
||||
let g:easy_align_ignores = []
|
||||
```
|
||||
|
||||
Then you get,
|
||||
|
||||
```ruby
|
||||
{
|
||||
# Quantity of apples: 1
|
||||
apple: 1,
|
||||
# Quantity of bananas: 2
|
||||
bananas: 2,
|
||||
# Quantity of grape: fruits: 3
|
||||
'grape: fruits': 3
|
||||
}
|
||||
```
|
||||
|
||||
Satisfied? :satisfied:
|
||||
|
||||
### Ignoring unmatched lines
|
||||
|
||||
Lines without any matching delimiter are ignored as well (except in
|
||||
right-justification mode).
|
||||
|
||||
For example, when aligning the following code block around the colons,
|
||||
|
||||
```ruby
|
||||
{
|
||||
apple: proc {
|
||||
this_line_does_not_have_a_colon
|
||||
},
|
||||
bananas: 2,
|
||||
grapefruits: 3
|
||||
}
|
||||
```
|
||||
|
||||
However, this default behavior is configurable.
|
||||
this is usually what we want.
|
||||
|
||||
```ruby
|
||||
{
|
||||
apple: proc {
|
||||
this_line_does_not_have_a_colon
|
||||
},
|
||||
bananas: 2,
|
||||
grapefruits: 3
|
||||
}
|
||||
```
|
||||
|
||||
However, this default behavior is also configurable.
|
||||
|
||||
```vim
|
||||
let g:easy_align_ignore_unmatched = 0
|
||||
@@ -150,18 +221,45 @@ let g:easy_align_ignore_unmatched = 0
|
||||
|
||||
Then we get,
|
||||
|
||||
```
|
||||
```ruby
|
||||
{
|
||||
# Quantity of apples
|
||||
apple: 1,
|
||||
# Quantity of bananas
|
||||
bananas: 2,
|
||||
# Quantity of grapefruits
|
||||
grapefruits: 3
|
||||
apple: proc {
|
||||
this_line_does_not_have_a_colon
|
||||
},
|
||||
bananas: 2,
|
||||
grapefruits: 3
|
||||
}
|
||||
```
|
||||
|
||||
### Extending alignment rules
|
||||
|
||||
```vim
|
||||
" Examples
|
||||
let g:easy_align_delimiters = {
|
||||
\ '>': { 'pattern': '>>\|=>\|>' },
|
||||
\ '/': { 'pattern': '//\+\|/\*\|\*/' },
|
||||
\ '#': { 'pattern': '#\+' },
|
||||
\ ']': {
|
||||
\ 'pattern': '[\[\]]',
|
||||
\ 'margin_left': '',
|
||||
\ 'margin_right': '',
|
||||
\ 'stick_to_left': 0
|
||||
\ },
|
||||
\ ')': {
|
||||
\ 'pattern': '[()]',
|
||||
\ 'margin_left': '',
|
||||
\ 'margin_right': '',
|
||||
\ 'stick_to_left': 0
|
||||
\ }
|
||||
\ }
|
||||
```
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
[Junegunn Choi](https://github.com/junegunn)
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
MIT
|
||||
|
||||
@@ -1,95 +1,217 @@
|
||||
if exists("g:easy_align_loaded")
|
||||
" Copyright (c) 2013 Junegunn Choi
|
||||
"
|
||||
" MIT License
|
||||
"
|
||||
" Permission is hereby granted, free of charge, to any person obtaining
|
||||
" a copy of this software and associated documentation files (the
|
||||
" "Software"), to deal in the Software without restriction, including
|
||||
" without limitation the rights to use, copy, modify, merge, publish,
|
||||
" distribute, sublicense, and/or sell copies of the Software, and to
|
||||
" permit persons to whom the Software is furnished to do so, subject to
|
||||
" the following conditions:
|
||||
"
|
||||
" The above copyright notice and this permission notice shall be
|
||||
" included in all copies or substantial portions of the Software.
|
||||
"
|
||||
" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
if exists("g:loaded_easy_align")
|
||||
finish
|
||||
endif
|
||||
let g:easy_align_loaded = 1
|
||||
let g:loaded_easy_align = 1
|
||||
|
||||
let s:easy_align_delimiters_default = {
|
||||
\ ' ': { 'pattern': ' ', 'margin_left': '', 'margin_right': '', 'stick_to_left': 0 },
|
||||
\ '=': { 'pattern': '===\|<=>\|\(&&\|||\|<<\|>>\)=\|=\~\|=>\|[:+/*!%^=><&|-]\?=',
|
||||
\ '=': { 'pattern': '===\|<=>\|\(&&\|||\|<<\|>>\)=\|=\~\|=>\|[:+/*!%^=><&|-]\?=[#?]\?',
|
||||
\ 'margin_left': ' ', 'margin_right': ' ', 'stick_to_left': 0 },
|
||||
\ ':': { 'pattern': ':', 'margin_left': '', 'margin_right': ' ', 'stick_to_left': 1 },
|
||||
\ ',': { 'pattern': ',', 'margin_left': '', 'margin_right': ' ', 'stick_to_left': 1 },
|
||||
\ '|': { 'pattern': '|', 'margin_left': ' ', 'margin_right': ' ', 'stick_to_left': 0 },
|
||||
\ '.': { 'pattern': '\.', 'margin_left': '', 'margin_right': '', 'stick_to_left': 0 }
|
||||
\ '.': { 'pattern': '\.', 'margin_left': '', 'margin_right': '', 'stick_to_left': 0 },
|
||||
\ '{': { 'pattern': '(\@<!{',
|
||||
\ 'margin_left': ' ', 'margin_right': ' ', 'stick_to_left': 0 },
|
||||
\ '}': { 'pattern': '}', 'margin_left': ' ', 'margin_right': '', 'stick_to_left': 0 }
|
||||
\ }
|
||||
|
||||
let s:just = ['', '[R]']
|
||||
|
||||
function! s:do_align(just, fl, ll, fc, lc, pattern, nth, ml, mr, stick_to_left, recursive)
|
||||
let lines = {}
|
||||
let max_just_len = 0
|
||||
let max_delim_len = 0
|
||||
let max_tokens = 0
|
||||
let pattern = '\s*\(' .a:pattern. '\)\s*'
|
||||
if exists("*strwidth")
|
||||
function! s:strwidth(str)
|
||||
return strwidth(a:str)
|
||||
endfunction
|
||||
else
|
||||
function! s:strwidth(str)
|
||||
return len(split(a:str, '\zs'))
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! s:highlighted_as(line, col, groups)
|
||||
if empty(a:groups) | return 0 | endif
|
||||
let hl = synIDattr(synID(a:line, a:col, 0), 'name')
|
||||
for grp in a:groups
|
||||
if hl =~# grp
|
||||
return 1
|
||||
endif
|
||||
endfor
|
||||
return 0
|
||||
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'])
|
||||
else
|
||||
return []
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:do_align(just, all_tokens, fl, ll, fc, lc, pattern, nth, ml, mr, stick_to_left, recursive)
|
||||
let lines = {}
|
||||
let max_just_len = 0
|
||||
let max_delim_len = 0
|
||||
let max_tokens = 0
|
||||
let pattern = '\s*\(' .a:pattern. '\)\s' . (a:stick_to_left ? '*' : '\{-}')
|
||||
let ignored_syntax = s:ignored_syntax()
|
||||
|
||||
" Phase 1
|
||||
for line in range(a:fl, a:ll)
|
||||
let tokens = split(a:lc ?
|
||||
\ strpart(getline(line), a:fc - 1, a:lc - a:fc + 1) :
|
||||
\ strpart(getline(line), a:fc - 1),
|
||||
\ pattern.'\zs')
|
||||
if !has_key(a:all_tokens, line)
|
||||
" Split line into the tokens by the delimiters
|
||||
let raw_tokens = split(a:lc ?
|
||||
\ strpart(getline(line), a:fc - 1, a:lc - a:fc + 1) :
|
||||
\ strpart(getline(line), a:fc - 1),
|
||||
\ pattern.'\zs')
|
||||
let concat = 0
|
||||
if empty(ignored_syntax)
|
||||
let tokens = raw_tokens
|
||||
else
|
||||
" Concat adjacent tokens that are split by ignorable delimiters
|
||||
let tokens = []
|
||||
let idx = 0
|
||||
for token in raw_tokens
|
||||
let idx += len(token)
|
||||
if concat
|
||||
let tokens[len(tokens) - 1] .= token
|
||||
else
|
||||
call add(tokens, token)
|
||||
endif
|
||||
let concat = s:highlighted_as(line, idx + a:fc - 1, ignored_syntax)
|
||||
endfor
|
||||
endif
|
||||
|
||||
" Preserve indentation - merge first two tokens
|
||||
if !empty(tokens) && match(tokens[0], '^\s*$') != -1
|
||||
let tokens = extend([join(tokens[0:1], '')], tokens[2:-1])
|
||||
endif
|
||||
|
||||
" Skip comment line
|
||||
if concat && len(tokens) == 1
|
||||
let tokens = []
|
||||
endif
|
||||
|
||||
" Remember tokens for subsequent recursive calls
|
||||
let a:all_tokens[line] = tokens
|
||||
else
|
||||
let tokens = a:all_tokens[line]
|
||||
endif
|
||||
|
||||
" Skip empty lines
|
||||
if empty(tokens)
|
||||
continue
|
||||
endif
|
||||
|
||||
" Preserve indentation
|
||||
if match(tokens[0], '^\s*$') != -1
|
||||
let tokens = extend([join(tokens[0:1], '')], tokens[2:-1])
|
||||
endif
|
||||
" Calculate the maximum number of tokens for a line within the range
|
||||
let max_tokens = max([len(tokens), max_tokens])
|
||||
|
||||
if len(tokens) < a:nth
|
||||
continue
|
||||
if a:nth > 0 " Positive field number
|
||||
if len(tokens) < a:nth
|
||||
continue
|
||||
endif
|
||||
let nth = a:nth - 1 " make it 0-based
|
||||
else " Negative field number
|
||||
let nth = len(tokens) + a:nth
|
||||
if match(tokens[len(tokens) - 1], pattern.'$') == -1
|
||||
let nth -= 1
|
||||
endif
|
||||
|
||||
if nth < 0 || nth == len(tokens)
|
||||
continue
|
||||
endif
|
||||
endif
|
||||
let nth = a:nth - 1 " 0-based
|
||||
|
||||
let last = tokens[nth]
|
||||
let prefix = (nth > 0 ? join(tokens[0 : nth - 1], '') : '')
|
||||
let token = substitute(last, pattern.'$', '', '')
|
||||
let suffix = join(tokens[nth + 1: -1], '')
|
||||
|
||||
if match(last, pattern.'$') == -1
|
||||
if a:just == 0 && (!exists("g:easy_align_ignore_unmatched") || g:easy_align_ignore_unmatched)
|
||||
continue
|
||||
else
|
||||
let delim = ''
|
||||
endif
|
||||
else
|
||||
let delim = matchlist(last, pattern)[1]
|
||||
let delim = get(matchlist(last, pattern.'$'), 1, '')
|
||||
if empty(delim) && a:just == 0 && get(g:, 'easy_align_ignore_unmatched', 1)
|
||||
continue
|
||||
endif
|
||||
|
||||
let max_just_len = max([len(token.prefix), max_just_len])
|
||||
let max_delim_len = max([len(delim), max_delim_len])
|
||||
let lines[line] = [prefix, token, delim, suffix]
|
||||
let max_just_len = max([s:strwidth(prefix.token), max_just_len])
|
||||
let max_delim_len = max([s:strwidth(delim), max_delim_len])
|
||||
let lines[line] = [nth, prefix, token, delim]
|
||||
endfor
|
||||
|
||||
for [line, tokens] in items(lines)
|
||||
let [prefix, token, delim, suffix] = tokens
|
||||
" Phase 2
|
||||
for [line, elems] in items(lines)
|
||||
let tokens = a:all_tokens[line]
|
||||
let [nth, prefix, token, delim] = elems
|
||||
|
||||
let pad = repeat(' ', max_just_len - len(prefix) - len(token))
|
||||
" Remove the leading whitespaces of the next token
|
||||
if len(tokens) > nth + 1
|
||||
let tokens[nth + 1] = substitute(tokens[nth + 1], '^\s*', '', '')
|
||||
endif
|
||||
|
||||
" Pad the token with spaces
|
||||
let pad = repeat(' ', max_just_len - s:strwidth(prefix) - s:strwidth(token))
|
||||
let rpad = ''
|
||||
if a:just == 0
|
||||
if a:stick_to_left
|
||||
let suffix = pad . suffix
|
||||
let rpad = pad
|
||||
else
|
||||
let token = token . pad
|
||||
endif
|
||||
elseif a:just == 1
|
||||
let token = pad . token
|
||||
endif
|
||||
let tokens[nth] = token
|
||||
|
||||
let delim = repeat(' ', max_delim_len - len(delim)). delim
|
||||
" Pad the delimiter
|
||||
let delim = repeat(' ', max_delim_len - s:strwidth(delim)). delim
|
||||
|
||||
" Before and after the range (for blockwise visual mode)
|
||||
let cline = getline(line)
|
||||
let before = strpart(cline, 0, a:fc - 1)
|
||||
let after = a:lc ? strpart(cline, a:lc) : ''
|
||||
|
||||
" Determine the left and right margin around the delimiter
|
||||
let rest = join(tokens[nth + 1 : -1], '')
|
||||
let ml = empty(prefix . token) ? '' : a:ml
|
||||
let mr = (empty(suffix . after) || (empty(suffix) && stridx(after, a:mr) == 0)) ? '' : a:mr
|
||||
let aligned = join([prefix, token, ml, delim, mr, suffix], '')
|
||||
let aligned = empty(after) ? substitute(aligned, '\s*$', '', '') : aligned
|
||||
let mr = (empty(rest) ||
|
||||
\ (empty(rest) && stridx(after, a:mr) == 0)) ? '' : a:mr
|
||||
|
||||
call setline(line, before.aligned.after)
|
||||
" Align the token
|
||||
let aligned = join([token, ml, delim, mr, rpad], '')
|
||||
let tokens[nth] = aligned
|
||||
|
||||
" Update the line
|
||||
let newline = substitute(before.join(tokens, '').after, '\s*$', '', '')
|
||||
call setline(line, newline)
|
||||
endfor
|
||||
|
||||
if a:recursive && a:nth < max_tokens
|
||||
call s:do_align(a:just, a:fl, a:ll, a:fc, a:lc, a:pattern, a:nth + 1, a:ml, a:mr, a:stick_to_left, a:recursive)
|
||||
let just = a:recursive == 2 ? !a:just : a:just
|
||||
call s:do_align(just, a:all_tokens, a:fl, a:ll, a:fc, a:lc, a:pattern,
|
||||
\ a:nth + 1, a:ml, a:mr, a:stick_to_left, a:recursive)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
@@ -110,65 +232,74 @@ function! easy_align#align(just, ...) range
|
||||
|
||||
let c = getchar()
|
||||
let ch = nr2char(c)
|
||||
if c == 3 || c == 27
|
||||
if c == 3 || c == 27 " CTRL-C / ESC
|
||||
return
|
||||
elseif c == 13
|
||||
elseif c == '<27>kb' " Backspace
|
||||
if len(n) > 0
|
||||
let n = strpart(n, 0, len(n) - 1)
|
||||
endif
|
||||
elseif c == 13 " Enter key
|
||||
let just = (just + 1) % len(s:just)
|
||||
elseif c >= 48 && c <= 57
|
||||
if n == '*'
|
||||
break
|
||||
else
|
||||
let n = n . nr2char(c)
|
||||
elseif ch == '-'
|
||||
if empty(n) | let n = '-'
|
||||
elseif n == '-' | let n = ''
|
||||
else | break
|
||||
endif
|
||||
elseif ch == '*'
|
||||
if !empty(n)
|
||||
break
|
||||
else
|
||||
let n = '*'
|
||||
if empty(n) | let n = '*'
|
||||
elseif n == '*' | let n = '**'
|
||||
elseif n == '**' | let n = ''
|
||||
else | break
|
||||
endif
|
||||
elseif c >= 48 && c <= 57 " Numbers
|
||||
if n[0] == '*' | break
|
||||
else | let n = n . ch
|
||||
end
|
||||
else
|
||||
break
|
||||
endif
|
||||
endwhile
|
||||
elseif a:0 == 1
|
||||
let tokens = matchlist(a:1, '^\([1-9][0-9]*\|\*\)\?\(.\)$')
|
||||
let tokens = matchlist(a:1, '^\([1-9][0-9]*\|-[0-9]*\|\*\*\?\)\?\(.\)$')
|
||||
if empty(tokens)
|
||||
echo "Invalid arguments: ". a:1
|
||||
return
|
||||
endif
|
||||
let [n, ch] = tokens[1:2]
|
||||
elseif a:0 == 2
|
||||
let n = a:1
|
||||
let ch = a:2
|
||||
let [n, ch] = a:000
|
||||
else
|
||||
echo "Invalid number of arguments: ". a:0 ." (expected 0, 1, or 2)"
|
||||
return
|
||||
endif
|
||||
|
||||
if n == '*'
|
||||
let n = 1
|
||||
let recursive = 1
|
||||
elseif empty(n)
|
||||
let n = 1
|
||||
elseif n != string(str2nr(n))
|
||||
if n == '*' | let [nth, recursive] = [1, 1]
|
||||
elseif n == '**' | let [nth, recursive] = [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
|
||||
else
|
||||
let nth = n
|
||||
endif
|
||||
|
||||
let delimiters = extend(copy(s:easy_align_delimiters_default),
|
||||
\ exists("g:easy_align_delimiters") ? g:easy_align_delimiters : {})
|
||||
let delimiters = s:easy_align_delimiters_default
|
||||
if exists('g:easy_align_delimiters')
|
||||
let delimiters = extend(copy(delimiters), g:easy_align_delimiters)
|
||||
endif
|
||||
|
||||
if has_key(delimiters, ch)
|
||||
let dict = delimiters[ch]
|
||||
call s:do_align(just, a:firstline, a:lastline,
|
||||
call s:do_align(just, {}, a:firstline, a:lastline,
|
||||
\ visualmode() == '' ? min([col("'<"), col("'>")]) : 1,
|
||||
\ visualmode() == '' ? max([col("'<"), col("'>")]) : 0,
|
||||
\ get(dict, 'pattern', ch),
|
||||
\ n,
|
||||
\ nth,
|
||||
\ get(dict, 'margin_left', ' '),
|
||||
\ get(dict, 'margin_right', ' '),
|
||||
\ get(dict, 'stick_to_left', 0), recursive)
|
||||
call s:echon(just, (recursive ? '*' : n), ch)
|
||||
call s:echon(just, n, ch)
|
||||
else
|
||||
echon "\rUnknown delimiter: ". ch
|
||||
endif
|
||||
|
||||
@@ -22,11 +22,14 @@ With this mapping, you can align selected lines with a few keystrokes.
|
||||
1. <Enter> key to start interactive EasyAlign command
|
||||
2. Optional Enter keys to switch justficiation mode (default: left)
|
||||
3. Optional field number (default: 1)
|
||||
1 Alignment around 1st delimiters
|
||||
2 Alignment around 2nd delimiters
|
||||
1 Around the 1st occurrences of delimiters
|
||||
2 Around the 2nd occurrences of delimiters
|
||||
* Around all occurrences of delimiters
|
||||
** Left-right alternating alignment around all delimiters
|
||||
- Around the last occurrences of delimiters (`-1`)
|
||||
-2 Around the second to last occurrences of delimiters
|
||||
...
|
||||
* Alignment around all delimiters (recursive)
|
||||
4. Delimiter
|
||||
4. Delimiter key (a single keystroke)
|
||||
<space> General alignment around whitespaces
|
||||
= Operators containing equals sign (=, ==, !=, +=, &&=, ...)
|
||||
: Suitable for formatting JSON or YAML
|
||||
@@ -38,14 +41,17 @@ During the key sequence, <Enter> key will toggle right-justification mode.
|
||||
|
||||
Examples:
|
||||
|
||||
<Enter>= Alignment around 1st equals signs (and the likes)
|
||||
<Enter>2= Alignment around 2nd equals signs (and the likes)
|
||||
<Enter>3= Alignment around 3rd equals signs (and the likes)
|
||||
<Enter>*= Alignment around all equals signs (and the likes)
|
||||
<Enter><Enter>= Right-justified alignment around 1st equals signs
|
||||
<Enter><space> Alignment around 1st whitespace
|
||||
<Enter>2<space> Alignment around 2nd whitespace
|
||||
<Enter>: Alignment around 1st colon
|
||||
<Enter><space> Alignment around 1st whitespaces
|
||||
<Enter>2<space> Alignment around 2nd whitespaces
|
||||
<Enter>-<space> Alignment around the last whitespaces
|
||||
<Enter>: Alignment around 1st colon
|
||||
<Enter>= Alignment around 1st equals signs (and the likes)
|
||||
<Enter>2= Alignment around 2nd equals signs (and the likes)
|
||||
<Enter>3= Alignment around 3rd equals signs (and the likes)
|
||||
<Enter>*= Alignment around all equals signs (and the likes)
|
||||
<Enter>**= Left-right alternating alignment around all equals signs
|
||||
<Enter><Enter>= Right-justified alignment around 1st equals signs
|
||||
<Enter><Enter>**= Right-left alternating alignment around all equals signs
|
||||
|
||||
|
||||
EasyAlignRight *EasyAlignRight*
|
||||
@@ -58,65 +64,122 @@ Partial alignment in blockwise-visual mode
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
In blockwise-visual mode (`CTRL-V`), EasyAlign command aligns only
|
||||
the selected parts, instead of the whole lines in the range.
|
||||
the selected text in the block, instead of the whole lines in the range.
|
||||
|
||||
|
||||
|
||||
Defining custom alignment rules *g:easy_align_delimiters*
|
||||
Ignoring delimiters in comments or strings *g:easy_align_ignores*
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
let g:easy_align_delimiters = {
|
||||
\ '/': { 'pattern': '//*' },
|
||||
\ 'x': {
|
||||
\ 'pattern': '[xX]',
|
||||
\ 'margin_left': ' <<<',
|
||||
\ 'margin_right': '>>> ',
|
||||
\ 'stick_to_left': 0
|
||||
\ }
|
||||
\ }
|
||||
EasyAlign can be configured to ignore delimiters in certain syntax
|
||||
highlight groups, such as code comments or strings. By default, delimiters
|
||||
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']
|
||||
|
||||
For example, the following paragraph
|
||||
|
||||
{
|
||||
# Quantity of apples: 1
|
||||
apple: 1,
|
||||
# Quantity of bananas: 2
|
||||
bananas: 2,
|
||||
# Quantity of grape:fruits: 3
|
||||
'grape:fruits': 3
|
||||
}
|
||||
|
||||
becomes as follows on `<Enter>:`
|
||||
|
||||
{
|
||||
# Quantity of apples: 1
|
||||
apple: 1,
|
||||
# Quantity of bananas: 2
|
||||
bananas: 2,
|
||||
# Quantity of grape:fruits: 3
|
||||
'grape:fruits': 3
|
||||
}
|
||||
|
||||
Naturally, this feature only works when syntax highlighting is enabled.
|
||||
|
||||
You can change the default rule by defining `g:easy_align_ignores` array.
|
||||
|
||||
" Ignore nothing!
|
||||
let g:easy_align_ignores = []
|
||||
|
||||
Then you get,
|
||||
|
||||
{
|
||||
# Quantity of apples: 1
|
||||
apple: 1,
|
||||
# Quantity of bananas: 2
|
||||
bananas: 2,
|
||||
# Quantity of grape: fruits: 3
|
||||
'grape: fruits': 3
|
||||
}
|
||||
|
||||
|
||||
Handling unmatched lines *g:easy_align_ignore_unmatched*
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
EasyAlign by default ignores lines without the matching delimiters
|
||||
(except in right-justification mode).
|
||||
This is to ignore interleaved comments commonly found in code.
|
||||
Lines without any matching delimiter are ignored as well (except in
|
||||
right-justification mode).
|
||||
|
||||
For example, when aligning the following code,
|
||||
For example, when aligning the following code block around the colons,
|
||||
|
||||
{
|
||||
# Quantity of apples
|
||||
apple: 1,
|
||||
# Quantity of bananas
|
||||
apple: proc {
|
||||
this_line_does_not_have_a_colon
|
||||
},
|
||||
bananas: 2,
|
||||
# Quantity of grapefruits
|
||||
grapefruits: 3
|
||||
}
|
||||
|
||||
this is usually what we want.
|
||||
|
||||
{
|
||||
# Quantity of apples
|
||||
apple: 1,
|
||||
# Quantity of bananas
|
||||
apple: proc {
|
||||
this_line_does_not_have_a_colon
|
||||
},
|
||||
bananas: 2,
|
||||
# Quantity of grapefruits
|
||||
grapefruits: 3
|
||||
}
|
||||
|
||||
However, this default behavior is configurable.
|
||||
However, this default behavior is also configurable.
|
||||
|
||||
let g:easy_align_ignore_unmatched = 0
|
||||
|
||||
Then we get,
|
||||
|
||||
{
|
||||
# Quantity of apples
|
||||
apple: 1,
|
||||
# Quantity of bananas
|
||||
bananas: 2,
|
||||
# Quantity of grapefruits
|
||||
grapefruits: 3
|
||||
apple: proc {
|
||||
this_line_does_not_have_a_colon
|
||||
},
|
||||
bananas: 2,
|
||||
grapefruits: 3
|
||||
}
|
||||
|
||||
|
||||
Extending alignment rules *g:easy_align_delimiters*
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
let g:easy_align_delimiters = {
|
||||
\ '>': { 'pattern': '>>\|=>\|>' },
|
||||
\ '/': { 'pattern': '//\+' },
|
||||
\ '#': { 'pattern': '#\+' },
|
||||
\ ']': {
|
||||
\ 'pattern': '[\[\]]',
|
||||
\ 'margin_left': '',
|
||||
\ 'margin_right': '',
|
||||
\ 'stick_to_left': 0
|
||||
\ },
|
||||
\ ')': {
|
||||
\ 'pattern': '[()]',
|
||||
\ 'margin_left': '',
|
||||
\ 'margin_right': '',
|
||||
\ 'stick_to_left': 0
|
||||
\ }
|
||||
\ }
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,30 @@
|
||||
if exists("g:easy_align_plugin_loaded")
|
||||
" Copyright (c) 2013 Junegunn Choi
|
||||
"
|
||||
" MIT License
|
||||
"
|
||||
" Permission is hereby granted, free of charge, to any person obtaining
|
||||
" a copy of this software and associated documentation files (the
|
||||
" "Software"), to deal in the Software without restriction, including
|
||||
" without limitation the rights to use, copy, modify, merge, publish,
|
||||
" distribute, sublicense, and/or sell copies of the Software, and to
|
||||
" permit persons to whom the Software is furnished to do so, subject to
|
||||
" the following conditions:
|
||||
"
|
||||
" The above copyright notice and this permission notice shall be
|
||||
" included in all copies or substantial portions of the Software.
|
||||
"
|
||||
" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
if exists("g:loaded_easy_align_plugin")
|
||||
finish
|
||||
endif
|
||||
let g:easy_align_plugin_loaded = 1
|
||||
let g:loaded_easy_align_plugin = 1
|
||||
|
||||
command! -nargs=* -range EasyAlign <line1>,<line2>call easy_align#align(0, <f-args>)
|
||||
command! -nargs=* -range EasyAlignRight <line1>,<line2>call easy_align#align(1, <f-args>)
|
||||
|
||||
Reference in New Issue
Block a user