mirror of
https://github.com/chenasraf/vim-matchup.git
synced 2026-05-18 01:38:57 +00:00
Implement text object repetition and count
This commit is contained in:
52
README.md
52
README.md
@@ -131,15 +131,18 @@ words are `else` and `elseif`. The `if`/`endif` pair is called an
|
||||
is an [inclusive] motion.
|
||||
|
||||
#### (b.1) full set of text objects
|
||||
- `i%` the inside of an open to close block
|
||||
- `1i%` the inside of an any block
|
||||
- `{count}i%` If count is not 1, the inside open-to-close block
|
||||
- `i%` the inside of an any block
|
||||
- `1i%` the inside of an open-to-close block
|
||||
- `{count}i%` If count is greater than 1, the inside of the `{count}`th
|
||||
surrounding open-to-close block
|
||||
|
||||
- `a%` an open-to-close block.
|
||||
- `1a%` an any block. Includes mids but does not include open and close.
|
||||
- `{count}a%` if `{count}` is greater than 1, the `{count}` surrounding open-to-close block.
|
||||
- `a%` an any block.
|
||||
- `1a%` an open-to-close block. Includes mids but does not include open
|
||||
and close words.
|
||||
- `{count}a%` if `{count}` is greater than 1, the `{count}`th surrounding
|
||||
open-to-close block.
|
||||
|
||||
Note: by default objects involving `matchpairs` such as `(){}[]` are
|
||||
Note: by default objects involving `matchpairs` such as `(){}[]` are
|
||||
performed character-wise, while `matchwords` such as `if`/`endif` are
|
||||
performed line-wise.
|
||||
The -wise can be forced using "v", "V", or `^V`
|
||||
@@ -289,7 +292,7 @@ let g:matchup_text_obj_enabled = 0
|
||||
```
|
||||
defaults: 1
|
||||
|
||||
To enable the experimental [transmute](#d3-parallel-transmutation)
|
||||
To enable the experimental [transmute](#d1-parallel-transmutation)
|
||||
module,
|
||||
```vim
|
||||
let g:matchup_transmute_enabled = 1
|
||||
@@ -335,9 +338,37 @@ let g:matchup_matchparen_status_offscreen = 0
|
||||
```
|
||||
default: 1
|
||||
|
||||
Adjust timeouts for matchparen highlighting
|
||||
```
|
||||
let g:matchup_matchparen_timeout = 300
|
||||
let g:matchparen_insert_timeout = 60
|
||||
```
|
||||
default: 300, 60
|
||||
|
||||
### motion
|
||||
|
||||
### text_obj
|
||||
To allow `{count}%`,
|
||||
```vim
|
||||
g:matchup_motion_override_Npercent = 1
|
||||
```
|
||||
default: 0
|
||||
|
||||
If enabled, cursor will land on the end of mid and close words while
|
||||
moving downwards (`%`/`]%`). While moving upwards (`g%`, `[%`) the cursor
|
||||
will land on the beginning. To disable,
|
||||
```vim
|
||||
let g:matchup_motion_cursor_end = 0
|
||||
```
|
||||
default: 1
|
||||
|
||||
### Module text_obj
|
||||
|
||||
Modify the set of operators which may operate
|
||||
[line-wise](#line-wise-operator-text-object-combinations)
|
||||
```vim
|
||||
let g:matchup_text_obj_linewise_operators' = ['d', 'y']
|
||||
```
|
||||
default: `['d', 'y']`
|
||||
|
||||
### transmute
|
||||
|
||||
@@ -523,6 +554,9 @@ Convert between single-line and multi-line blocks. Mappings undecided.
|
||||
- write proper vim doc
|
||||
- thoroughly test with unicode, tabs
|
||||
- add screenshots and animations
|
||||
- add file type `quirks` module
|
||||
- investigate whether `&selection`/`&virtualedit` options are important
|
||||
- can match-up be integrated with
|
||||
[vim-surround](https://github.com/tpope/vim-surround)?
|
||||
- complete parallel transmutation in an efficient way.
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ function! s:init_options()
|
||||
call s:init_option('matchup_text_obj_enabled', 1)
|
||||
call s:init_option('matchup_text_obj_linewise_operators', ['d', 'y'])
|
||||
|
||||
call s:init_option('matchup_transmute_enabled', 1)
|
||||
call s:init_option('matchup_transmute_enabled', 0)
|
||||
|
||||
call s:init_option('matchup_imap_enabled', 1)
|
||||
|
||||
|
||||
@@ -204,23 +204,39 @@ function! matchup#delim#get_matching(delim, ...) " {{{1
|
||||
endfunction
|
||||
|
||||
" }}}1
|
||||
function! matchup#delim#get_surrounding(type) " {{{1
|
||||
function! matchup#delim#get_surrounding(type, ...) " {{{1
|
||||
call matchup#perf#tic('delim#get_surrounding')
|
||||
|
||||
let l:save_pos = matchup#pos#get_cursor()
|
||||
let l:pos_val_cursor = matchup#pos#val(l:save_pos)
|
||||
let l:pos_val_last = l:pos_val_cursor
|
||||
let l:pos_val_open = l:pos_val_cursor - 1
|
||||
|
||||
let l:count = a:0 >= 1 ? a:1 : 1
|
||||
let l:counter = l:count
|
||||
|
||||
" provided count == 0 refers to local any block
|
||||
let l:local = l:count == 0 ? 1 : 0
|
||||
|
||||
while l:pos_val_open < l:pos_val_last
|
||||
let l:open = matchup#delim#get_prev(a:type, 'open')
|
||||
let l:open = matchup#delim#get_prev(a:type,
|
||||
\ l:local ? 'open_mid' : 'open')
|
||||
if empty(l:open) | break | endif
|
||||
" echo l:open.lnum l:open.cnum | sleep 1
|
||||
let l:close = matchup#delim#get_matching(l:open)
|
||||
" echo l:close.lnum l:close.cnum | sleep 1
|
||||
|
||||
let l:match = matchup#delim#get_matching(l:open, 1)
|
||||
let l:close = l:local ? l:open.links.next : l:open.links.close
|
||||
|
||||
let l:pos_val_try = matchup#pos#val(l:close)
|
||||
\ + strdisplaywidth(l:close.match) - 1
|
||||
if l:pos_val_try >= l:pos_val_cursor
|
||||
call matchup#pos#set_cursor(l:save_pos)
|
||||
return [l:open, l:close]
|
||||
if l:counter <= 1
|
||||
" restore cursor and accept
|
||||
call matchup#pos#set_cursor(l:save_pos)
|
||||
call matchup#perf#toc('delim#get_surrounding', 'accept')
|
||||
return [l:open, l:close]
|
||||
endif
|
||||
call matchup#pos#set_cursor(matchup#pos#prev(l:open))
|
||||
let l:counter -= 1
|
||||
else
|
||||
call matchup#pos#set_cursor(matchup#pos#prev(l:open))
|
||||
let l:pos_val_last = l:pos_val_open
|
||||
@@ -228,7 +244,9 @@ function! matchup#delim#get_surrounding(type) " {{{1
|
||||
endif
|
||||
endwhile
|
||||
|
||||
" restore cursor and return failure
|
||||
call matchup#pos#set_cursor(l:save_pos)
|
||||
call matchup#perf#toc('delim#get_surrounding', 'fail')
|
||||
return [{}, {}]
|
||||
endfunction
|
||||
|
||||
@@ -1332,7 +1350,8 @@ function! s:init_delim_regexes() " {{{1
|
||||
|
||||
let l:re.delim_tex = s:init_delim_regexes_generator('delim_tex')
|
||||
|
||||
for l:k in ['open', 'close', 'both', 'mid', 'both_all']
|
||||
" XXX use keys(sidedict)
|
||||
for l:k in ['open', 'close', 'both', 'mid', 'both_all', 'open_mid']
|
||||
let l:re.delim_all[l:k] = l:re.delim_tex[l:k]
|
||||
let l:re.all[l:k] = l:re.delim_all[l:k]
|
||||
endfor
|
||||
@@ -1546,6 +1565,7 @@ let s:sidedict = {
|
||||
\ 'close' : ['close'],
|
||||
\ 'both' : ['close', 'open'],
|
||||
\ 'both_all' : ['close', 'mid', 'open'],
|
||||
\ 'open_mid' : ['mid', 'open'],
|
||||
\}
|
||||
|
||||
let s:basetypes = {
|
||||
|
||||
@@ -23,82 +23,106 @@ function! matchup#text_obj#init_module() " {{{1
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
" MAXCOL is probably a lot bigger in actuality, but we don't want
|
||||
" to support such long lines
|
||||
let s:MAXCOL = 0x7fff
|
||||
|
||||
" }}}1
|
||||
function! matchup#text_obj#delimited(is_inner, visual, type) " {{{1
|
||||
" get the current selection, move to end of range
|
||||
if a:visual
|
||||
let l:selection = getpos("'<")[1:2] + getpos("'>")[1:2]
|
||||
call matchup#pos#set_cursor(getpos("'>"))
|
||||
endif
|
||||
|
||||
let [l:open, l:close] = matchup#delim#get_surrounding(a:type)
|
||||
" determine if operator is able to act line-wise (i.e., for inner)
|
||||
let l:linewise = index(g:matchup_text_obj_linewise_operators,
|
||||
\ v:operator) >= 0
|
||||
|
||||
if empty(l:open)
|
||||
if a:visual
|
||||
normal! gv
|
||||
endif
|
||||
return
|
||||
endif
|
||||
" try up to three times (rarely)
|
||||
for l:try_again in range(3)
|
||||
" on the first try, we use v:count which may be zero
|
||||
" on the next tries, use the previous count plus one
|
||||
let [l:open, l:close] = matchup#delim#get_surrounding(
|
||||
\ a:type, l:try_again ? (v:count1 + l:try_again) : v:count)
|
||||
|
||||
let [l:l1, l:c1, l:l2, l:c2] = [l:open.lnum, l:open.cnum,
|
||||
\ l:close.lnum, l:close.cnum]
|
||||
|
||||
" Determine if operator is linewise
|
||||
let l:linewise = index(g:matchup_text_obj_linewise_operators,
|
||||
\ v:operator) >= 0
|
||||
let l:linewise = 1
|
||||
|
||||
" Adjust the borders
|
||||
if a:is_inner
|
||||
let c1 += len(l:open.match)
|
||||
let c2 -= 1
|
||||
|
||||
let l:is_inline = (l:l2 - l:l1) > 1 ? 1 : 0
|
||||
|
||||
" \ && match(strpart(getline(l1), c1 ), '^\s*$') >= 0
|
||||
" \ && match(strpart(getline(l2), 0, c2), '^\s*$') >= 0
|
||||
|
||||
if l:is_inline
|
||||
let l1 += 1
|
||||
let c1 = strlen(matchstr(getline(l1), '^\s*')) + 1
|
||||
let l2 -= 1
|
||||
let c2 = strlen(getline(l2))
|
||||
if c2 == 0 && !l:linewise
|
||||
let l2 -= 1
|
||||
let c2 = len(getline(l2)) + 1
|
||||
endif
|
||||
elseif c2 == 0
|
||||
let l2 -= 1
|
||||
let c2 = len(getline(l2)) + 1
|
||||
endif
|
||||
else
|
||||
let c2 += len(l:close.match) - 1
|
||||
endif
|
||||
|
||||
" select next pair if we reached the same selection
|
||||
if a:visual && l:selection == [l1, c1, l2, c2]
|
||||
echo 'foobar' | sleep 1
|
||||
call matchup#pos#set_cursor(matchup#pos#next([l2, c2]))
|
||||
let [l:open, l:close] = matchup#delim#get_surrounding(a:type)
|
||||
if empty(l:open)
|
||||
if empty(l:open)
|
||||
if a:visual
|
||||
normal! gv
|
||||
return
|
||||
endif
|
||||
let [l1, c1, l2, c2] = [l:open.lnum, l:open.cnum,
|
||||
\ l:close.lnum, l:close.cnum + len(l:close.match) - 1]
|
||||
return
|
||||
endif
|
||||
|
||||
let l:is_inline = (l2 - l1) > 1
|
||||
\ && match(strpart(getline(l1), 0, c1-1), '^\s*$') >= 0
|
||||
\ && match(strpart(getline(l2), 0, c2), '^\s*$') >= 0
|
||||
" heuristic to handle overlapping any-blocks;
|
||||
" if the start delimiter is inside our already visually selected
|
||||
" area, try again but this time find open instead of open_mid
|
||||
if a:visual && !l:try_again
|
||||
\ && (l:open.lnum > l:selection[0]
|
||||
\ || l:open.lnum == l:selection[0]
|
||||
\ && l:open.cnum >= l:selection[1])
|
||||
let [l:open, l:close] = matchup#delim#get_surrounding(
|
||||
\ a:type, v:count1 + l:try_again)
|
||||
endif
|
||||
|
||||
" Determine the select mode
|
||||
let l:select_mode = l:is_inline && l:linewise ? 'V'
|
||||
let [l:l1, l:c1, l:l2, l:c2] = [l:open.lnum, l:open.cnum,
|
||||
\ l:close.lnum, l:close.cnum]
|
||||
|
||||
" special case: if inner, and the current selection coincides
|
||||
" with the open and close positions, try for a second time
|
||||
" this allows vi% in [[ ]] to work
|
||||
if l:selection[1] == l:c1 && l:selection[3] == l:c2
|
||||
continue
|
||||
endif
|
||||
|
||||
let l:is_multiline = (l:l2 - l:l1) > 1 ? 1 : 0
|
||||
|
||||
" adjust the borders
|
||||
if a:is_inner
|
||||
let l:c1 += len(l:open.match)
|
||||
let l:c2 -= 1
|
||||
|
||||
if l:is_multiline
|
||||
let l:l1 += 1
|
||||
let l:c1 = strlen(matchstr(getline(l:l1), '^\s*')) + 1
|
||||
let l:l2 -= 1
|
||||
let l:c2 = strlen(getline(l:l2))
|
||||
if l:c2 == 0 && !l:linewise
|
||||
let l:l2 -= 1
|
||||
let l:c2 = len(getline(l:l2)) + 1
|
||||
endif
|
||||
elseif l:c2 == 0
|
||||
let l:l2 -= 1
|
||||
let l:c2 = len(getline(l2)) + 1
|
||||
endif
|
||||
else
|
||||
let l:c2 += len(l:close.match) - 1
|
||||
endif
|
||||
|
||||
" in visual line mode, force new selection to be larger
|
||||
if a:visual && visualmode() ==# 'V'
|
||||
\ && (l:l1 > l:selection[0] || l:l2 < l:selection[2])
|
||||
continue
|
||||
endif
|
||||
|
||||
" try again if we reached the same selection
|
||||
" for visual line mode, only check line numbers
|
||||
" workaround for cases where the cursor might get fooled
|
||||
" into going into one of the inner blocks
|
||||
if a:visual && (l:selection == [l:l1, l:c1, l:l2, l:c2]
|
||||
\ || visualmode() ==# 'V'
|
||||
\ && [l:selection[0], l:selection[2]] == [l:l1, l:l2])
|
||||
continue
|
||||
else
|
||||
break
|
||||
endif
|
||||
|
||||
endfor
|
||||
|
||||
" determine the proper select mode
|
||||
let l:select_mode = l:is_multiline && l:linewise ? 'V'
|
||||
\ : (v:operator ==# ':') ? visualmode() : 'v'
|
||||
|
||||
echo l:is_inline l:linewise v:operator visualmode()
|
||||
\ l:l2 l:l1 l:l2-l:l1 | sleep 1
|
||||
|
||||
" Apply selection
|
||||
" apply selection
|
||||
execute 'normal!' l:select_mode
|
||||
call matchup#pos#set_cursor(l1, c1)
|
||||
normal! o
|
||||
|
||||
Reference in New Issue
Block a user