mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-02-03 12:40:05 +08:00
314 lines
8.4 KiB
VimL
314 lines
8.4 KiB
VimL
" 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
|
|
|