You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
313 lines
8.4 KiB
313 lines
8.4 KiB
" vim match-up - even better matching |
|
" |
|
" Maintainer: Andy Massimino |
|
" Email: a@normed.space |
|
" |
|
|
|
let s:save_cpo = &cpo |
|
set cpo&vim |
|
|
|
" TODO this can probably be simplified |
|
function! matchup#motion#op(motion) abort |
|
call matchup#motion_force() |
|
let l:sid = matchup#motion_sid() |
|
let s:v_operator = v:operator |
|
execute 'normal' l:sid.'(wise)' . (v:count > 0 ? v:count : '') |
|
\ . l:sid.'(matchup-'.a:motion.')' |
|
unlet s:v_operator |
|
endfunction |
|
|
|
function matchup#motion#getoper() |
|
return get(s:, 'v_operator', '') |
|
endfunction |
|
|
|
function! matchup#motion#find_matching_pair(visual, down) " {{{1 |
|
let [l:count, l:count1] = [v:count, v:count1] |
|
|
|
let l:is_oper = !empty(get(s:, 'v_operator', '')) |
|
|
|
if a:visual && !l:is_oper |
|
normal! gv |
|
endif |
|
|
|
if a:down && l:count > g:matchup_motion_override_Npercent |
|
" TODO: dv50% does not work properly |
|
if a:visual && l:is_oper |
|
normal! V |
|
endif |
|
exe 'normal!' l:count.'%' |
|
return |
|
endif |
|
|
|
" disable the timeout |
|
call matchup#perf#timeout_start(0) |
|
|
|
" get a delim where the cursor is |
|
let l:delim = matchup#delim#get_current('all', 'both_all') |
|
if empty(l:delim) |
|
" otherwise search forward |
|
let l:delim = matchup#delim#get_next('all', 'both_all') |
|
if empty(l:delim) | return | endif |
|
endif |
|
|
|
" loop count number of times |
|
for l:dummy in range(l:count1) |
|
let l:matches = matchup#delim#get_matching(l:delim, 1) |
|
if len(l:matches) <= (l:delim.side ==# 'mid' ? 2 : 1) | return | endif |
|
if !has_key(l:delim, 'links') | return | endif |
|
let l:delim = get(l:delim.links, a:down ? 'next' : 'prev', {}) |
|
if empty(l:delim) | return | endif |
|
endfor |
|
|
|
if a:visual && l:is_oper |
|
normal! gv |
|
endif |
|
|
|
let l:exclusive = l:is_oper && (g:v_motion_force ==# 'v') |
|
let l:forward = ((a:down && l:delim.side !=# 'open') |
|
\ || l:delim.side ==# 'close') |
|
|
|
" go to the end of the delimiter, if necessary |
|
let l:column = l:delim.cnum |
|
if g:matchup_motion_cursor_end && !l:is_oper && l:forward |
|
let l:column = matchup#delim#jump_target(l:delim) |
|
endif |
|
|
|
let l:start_pos = matchup#pos#get_cursor() |
|
|
|
normal! m` |
|
|
|
" column position of last character in match |
|
let l:eom = l:delim.cnum + matchup#delim#end_offset(l:delim) |
|
|
|
if l:is_oper && l:forward |
|
let l:column = l:exclusive ? (l:column - 1) : l:eom |
|
endif |
|
|
|
if l:is_oper && l:exclusive |
|
\ && matchup#pos#smaller(l:delim, l:start_pos) |
|
normal! o |
|
call matchup#pos#set_cursor(matchup#pos#prev(l:start_pos)) |
|
normal! o |
|
endif |
|
|
|
" special handling for d% |
|
let [l:start_lnum, l:start_cnum] = l:start_pos[1:2] |
|
if get(s:, 'v_operator', '') ==# 'd' && l:start_lnum != l:delim.lnum |
|
\ && g:v_motion_force ==# '' |
|
let l:tl = [l:start_lnum, l:start_cnum] |
|
let [l:tl, l:br, l:swap] = l:tl[0] <= l:delim.lnum |
|
\ ? [l:tl, [l:delim.lnum, l:eom], 0] |
|
\ : [[l:delim.lnum, l:delim.cnum], l:tl, 1] |
|
|
|
if getline(l:tl[0]) =~# '^[ \t]*\%'.l:tl[1].'c' |
|
\ && getline(l:br[0]) =~# '\%'.(l:br[1]+1).'c[ \t]*$' |
|
if l:swap |
|
normal! o |
|
call matchup#pos#set_cursor(l:br[0], strlen(getline(l:br[0]))+1) |
|
normal! o |
|
let l:column = 1 |
|
else |
|
normal! o |
|
call matchup#pos#set_cursor(l:tl[0], 1) |
|
normal! o |
|
let l:column = strlen(getline(l:br[0]))+1 |
|
endif |
|
endif |
|
endif |
|
|
|
let l:lnum = l:delim.lnum |
|
|
|
" make adjustments for selection option 'exclusive |
|
if l:forward && a:visual && &selection ==# 'exclusive' |
|
let [l:lnum, l:column] = matchup#pos#next_eol(l:lnum, l:column)[1:2] |
|
endif |
|
if !l:forward && l:is_oper && &selection ==# 'exclusive' |
|
normal! o |
|
call matchup#pos#set_cursor(matchup#pos#next_eol( |
|
\ matchup#pos#get_cursor())) |
|
normal! o |
|
endif |
|
|
|
call matchup#pos#set_cursor(l:lnum, l:column) |
|
if stridx(&foldopen, 'percent') >= 0 |
|
normal! zv |
|
endif |
|
endfunction |
|
|
|
" }}}1 |
|
function! matchup#motion#find_unmatched(visual, down, ...) " {{{1 |
|
call matchup#perf#tic('motion#find_unmatched') |
|
|
|
let l:opts = a:0 ? a:1 : {} |
|
let l:count = v:count1 |
|
|
|
let l:is_oper = !empty(get(s:, 'v_operator', '')) |
|
let l:exclusive = l:is_oper |
|
\ && g:v_motion_force !=# 'v' && g:v_motion_force !=# "\<c-v>" |
|
|
|
if a:visual |
|
normal! gv |
|
endif |
|
|
|
" set the timeout fairly high by default |
|
let l:timeout = get(l:opts, 'timeout', 750) |
|
call matchup#perf#timeout_start(l:timeout) |
|
|
|
for l:tries in range(3) |
|
let [l:open, l:close] = matchup#delim#get_surrounding('delim_all', |
|
\ l:tries ? l:count : 1, |
|
\ { 'check_skip': get(l:opts, '__where_impl__', 0) }) |
|
|
|
if empty(l:open) || empty(l:close) |
|
call matchup#perf#toc('motion#find_unmatched', 'fail'.l:tries) |
|
return |
|
endif |
|
|
|
let l:delim = a:down ? l:close : l:open |
|
|
|
let l:save_pos = matchup#pos#get_cursor() |
|
let l:new_pos = [l:delim.lnum, l:delim.cnum] |
|
|
|
" this is an exclusive motion, ]% |
|
if l:delim.side ==# 'close' |
|
if l:exclusive |
|
let l:new_pos[1] -= 1 |
|
else |
|
let l:new_pos[1] += matchup#delim#end_offset(l:delim) |
|
endif |
|
endif |
|
|
|
" if the cursor didn't move, increment count |
|
if matchup#pos#equal(l:save_pos, l:new_pos) |
|
let l:count += 1 |
|
elseif l:tries |
|
break |
|
endif |
|
|
|
if l:count <= 1 |
|
break |
|
endif |
|
endfor |
|
|
|
if a:down && !l:is_oper |
|
let l:new_pos[1] = matchup#delim#jump_target(l:delim) |
|
endif |
|
|
|
" this is an exclusive motion, [% |
|
if !a:down && l:exclusive |
|
normal! o |
|
call matchup#pos#set_cursor(matchup#pos#prev( |
|
\ matchup#pos#get_cursor())) |
|
normal! o |
|
endif |
|
|
|
" handle selection option 'exclusive' going backwards |
|
if !a:down && l:is_oper && &selection ==# 'exclusive' |
|
normal! o |
|
call matchup#pos#set_cursor(matchup#pos#next_eol( |
|
\ matchup#pos#get_cursor())) |
|
normal! o |
|
endif |
|
|
|
" handle selection option 'exclusive' going forwards |
|
if a:down && l:is_oper && &selection ==# 'exclusive' |
|
let l:new_pos = matchup#pos#next_eol(l:new_pos)[1:2] |
|
endif |
|
|
|
if get(l:opts, '__where_impl__', 0) |
|
let l:opts.delim = l:delim |
|
else |
|
normal! m` |
|
endif |
|
call matchup#pos#set_cursor(l:new_pos) |
|
|
|
call matchup#perf#toc('motion#find_unmatched', 'done') |
|
endfunction |
|
|
|
" }}}1 |
|
function! matchup#motion#jump_inside(visual) " {{{1 |
|
let l:count = v:count1 |
|
|
|
let l:save_pos = matchup#pos#get_cursor() |
|
|
|
call matchup#perf#timeout_start(750) |
|
|
|
if a:visual |
|
normal! gv |
|
endif |
|
|
|
for l:counter in range(l:count) |
|
if l:counter |
|
let l:delim = matchup#delim#get_next('all', 'open') |
|
else |
|
let l:delim = matchup#delim#get_current('all', 'open') |
|
if empty(l:delim) |
|
let l:delim = matchup#delim#get_next('all', 'open') |
|
endif |
|
endif |
|
if empty(l:delim) |
|
call matchup#pos#set_cursor(l:save_pos) |
|
return |
|
endif |
|
|
|
let l:new_pos = [l:delim.lnum, l:delim.cnum] |
|
let l:new_pos[1] += matchup#delim#end_offset(l:delim) |
|
call matchup#pos#set_cursor(matchup#pos#next(l:new_pos)) |
|
endfor |
|
|
|
call matchup#pos#set_cursor(l:save_pos) |
|
|
|
" convert to [~, lnum, cnum, ~] format |
|
let l:new_pos = matchup#pos#next(l:new_pos) |
|
|
|
" this is an exclusive motion except when dealing with whitespace |
|
let l:is_oper = !empty(get(s:, 'v_operator', '')) |
|
if l:is_oper |
|
\ && g:v_motion_force !=# 'v' && g:v_motion_force !=# "\<c-v>" |
|
while matchup#util#in_whitespace(l:new_pos[1], l:new_pos[2]) |
|
let l:new_pos = matchup#pos#next(l:new_pos) |
|
endwhile |
|
let l:new_pos = matchup#pos#prev(l:new_pos) |
|
endif |
|
|
|
" jump ahead if inside indent |
|
if !l:is_oper && matchup#util#in_indent(l:new_pos[1], l:new_pos[2]) |
|
let l:new_pos[2] = 1 + strlen(matchstr( |
|
\ getline(l:new_pos[1]), '^\s\+')) |
|
endif |
|
|
|
" handle selection option 'exclusive' (motion only goes forwards) |
|
if a:visual && &selection ==# 'exclusive' |
|
let l:new_pos = matchup#pos#next_eol(l:new_pos) |
|
endif |
|
|
|
normal! m` |
|
call matchup#pos#set_cursor(l:new_pos) |
|
endfunction |
|
|
|
" }}}1 |
|
function! matchup#motion#insert_mode() " {{{1 |
|
call matchup#perf#timeout_start(0) " disable the timeout |
|
|
|
let l:delim = matchup#delim#get_current( |
|
\ 'all', 'both_all', {'insertmode': 1}) |
|
if empty(l:delim) | return | endif |
|
|
|
let l:matches = matchup#delim#get_matching(l:delim, 1) |
|
if len(l:matches) <= (l:delim.side ==# 'mid' ? 2 : 1) | return | endif |
|
if !has_key(l:delim, 'links') | return | endif |
|
let l:delim = get(l:delim.links, 'next', {}) |
|
if empty(l:delim) | return | endif |
|
|
|
let l:new_pos = [l:delim.lnum, l:delim.cnum] |
|
let l:new_pos[1] += matchup#delim#end_offset(l:delim) |
|
call matchup#pos#set_cursor(matchup#pos#next_eol(l:new_pos)) |
|
endfunction |
|
|
|
" }}}1 |
|
|
|
let &cpo = s:save_cpo |
|
|
|
" vim: fdm=marker sw=2 |
|
|
|
|