vim-easymotion/autoload/EasyMotion.vim
haya14busa e319902b9a Compensate deleted lines
When you delete lines above current cursor position by SelectLines or
SelectPhrase, restoring cursor position didn't work intuitively

this commit fix this problem
2013-11-27 23:36:43 +09:00

1048 lines
28 KiB
VimL
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

" EasyMotion - Vim motions on speed!
"
" Author: Kim Silkebækken <kim.silkebaekken+vim@gmail.com>
" Source repository: https://github.com/Lokaltog/vim-easymotion
" Default configuration functions {{{
function! EasyMotion#InitOptions(options) " {{{
for [key, value] in items(a:options)
if ! exists('g:EasyMotion_' . key)
exec 'let g:EasyMotion_' . key . ' = ' . string(value)
endif
endfor
" Reset Migemo Dictionary
let s:migemo_dicts = {}
endfunction " }}}
function! EasyMotion#InitHL(group, colors) " {{{
let group_default = a:group . 'Default'
" Prepare highlighting variables
let guihl = printf('guibg=%s guifg=%s gui=%s', a:colors.gui[0], a:colors.gui[1], a:colors.gui[2])
if !exists('g:CSApprox_loaded')
let ctermhl = &t_Co == 256
\ ? printf('ctermbg=%s ctermfg=%s cterm=%s', a:colors.cterm256[0], a:colors.cterm256[1], a:colors.cterm256[2])
\ : printf('ctermbg=%s ctermfg=%s cterm=%s', a:colors.cterm[0], a:colors.cterm[1], a:colors.cterm[2])
else
let ctermhl = ''
endif
" Create default highlighting group
execute printf('hi default %s %s %s', group_default, guihl, ctermhl)
" Check if the hl group exists
if hlexists(a:group)
redir => hlstatus | exec 'silent hi ' . a:group | redir END
" Return if the group isn't cleared
if hlstatus !~ 'cleared'
return
endif
endif
" No colors are defined for this group, link to defaults
execute printf('hi default link %s %s', a:group, group_default)
endfunction " }}}
function! EasyMotion#InitMappings(motions) "{{{
for motion in keys(a:motions)
call EasyMotion#InitOptions({ 'mapping_' . motion : g:EasyMotion_leader_key . motion })
endfor
if g:EasyMotion_do_mapping
for [motion, fn] in items(a:motions)
if empty(g:EasyMotion_mapping_{motion})
continue
endif
silent exec 'nnoremap <silent> ' . g:EasyMotion_mapping_{motion} . ' :call EasyMotion#' . fn.name . '(0, ' . fn.dir . ')<CR>'
silent exec 'onoremap <silent> ' . g:EasyMotion_mapping_{motion} . ' :call EasyMotion#' . fn.name . '(0, ' . fn.dir . ')<CR>'
silent exec 'vnoremap <silent> ' . g:EasyMotion_mapping_{motion} . ' :<C-U>call EasyMotion#' . fn.name . '(1, ' . fn.dir . ')<CR>'
endfor
endif
endfunction "}}}
function! EasyMotion#InitSpecialMappings(motions) "{{{
for motion in keys(a:motions)
call EasyMotion#InitOptions({ 'special_mapping_' . motion : g:EasyMotion_leader_key . motion })
endfor
if g:EasyMotion_do_mapping
for [motion, fn] in items(a:motions)
if empty(g:EasyMotion_special_mapping_{motion})
continue
endif
if g:EasyMotion_special_{fn.flag}
silent exec 'onoremap <silent> ' . g:EasyMotion_special_mapping_{motion} . ' :call EasyMotion#' . fn.name . '()<CR>'
silent exec 'nnoremap <silent> v' . g:EasyMotion_special_mapping_{motion} . ' :call EasyMotion#' . fn.name . '()<CR>'
silent exec 'nnoremap <silent> y' . g:EasyMotion_special_mapping_{motion} . ' :call EasyMotion#' . fn.name . 'Yank()<CR>'
silent exec 'nnoremap <silent> d' . g:EasyMotion_special_mapping_{motion} . ' :call EasyMotion#' . fn.name . 'Delete()<CR>'
endif
endfor
endif
endfunction "}}}
" }}}
" Motion functions {{{
function! EasyMotion#F(visualmode, direction) " {{{
let char = s:GetSearchChar(a:visualmode)
let re = escape(char, '.$^~')
if g:EasyMotion_use_migemo
if ! has_key(s:migemo_dicts, &l:encoding)
let s:migemo_dicts[&l:encoding] = s:load_migemo_dict()
endif
if re =~# '^\a$'
let re = s:migemo_dicts[&l:encoding][re]
endif
endif
if empty(char)
return
endif
if g:EasyMotion_smartcase && char =~# '\v\U'
let re = '\c' . re
else
let re = '\C' . re
endif
call s:EasyMotion(re, a:direction, a:visualmode ? visualmode() : '', mode(1))
endfunction " }}}
function! EasyMotion#S(visualmode, direction) " {{{
let char = s:GetSearchChar(a:visualmode)
if empty(char)
return
endif
let re = escape(char, '.$^~')
if g:EasyMotion_use_migemo
if ! has_key(s:migemo_dicts, &l:encoding)
let s:migemo_dicts[&l:encoding] = s:load_migemo_dict()
endif
if re =~# '^\a$'
let re = s:migemo_dicts[&l:encoding][re]
endif
endif
if g:EasyMotion_smartcase && char =~# '\v\U'
let re = '\c' . re
else
let re = '\C' . re
endif
call s:EasyMotion(re, a:direction, a:visualmode ? visualmode() : '', mode(1))
endfunction " }}}
function! EasyMotion#T(visualmode, direction) " {{{
let char = s:GetSearchChar(a:visualmode)
if empty(char)
return
endif
let re = escape(char, '.$^~')
if g:EasyMotion_use_migemo
if ! has_key(s:migemo_dicts, &l:encoding)
let s:migemo_dicts[&l:encoding] = s:load_migemo_dict()
endif
if re =~# '^\a$'
let re = s:migemo_dicts[&l:encoding][re]
endif
endif
if a:direction == 1
" backward
let re = re . '\zs.'
else
" forward
let re = '.\ze' . re
endif
if g:EasyMotion_smartcase && char =~# '\v\U'
let re = '\c' . re
else
let re = '\C' . re
endif
call s:EasyMotion(re, a:direction, a:visualmode ? visualmode() : '', mode(1))
endfunction " }}}
function! EasyMotion#WB(visualmode, direction) " {{{
call s:EasyMotion('\(\<.\|^$\)', a:direction, a:visualmode ? visualmode() : '', '')
endfunction " }}}
function! EasyMotion#WBW(visualmode, direction) " {{{
call s:EasyMotion('\(\(^\|\s\)\@<=\S\|^$\)', a:direction, a:visualmode ? visualmode() : '', '')
endfunction " }}}
function! EasyMotion#E(visualmode, direction) " {{{
call s:EasyMotion('\(.\>\|^$\)', a:direction, a:visualmode ? visualmode() : '', mode(1))
endfunction " }}}
function! EasyMotion#EW(visualmode, direction) " {{{
call s:EasyMotion('\(\S\(\s\|$\)\|^$\)', a:direction, a:visualmode ? visualmode() : '', mode(1))
endfunction " }}}
function! EasyMotion#JK(visualmode, direction) " {{{
if g:EasyMotion_startofline
call s:EasyMotion('^\(\w\|\s*\zs\|$\)', a:direction, a:visualmode ? visualmode() : '', '')
else
let prev_column = getpos('.')[2] - 1
call s:EasyMotion('^.\{,' . prev_column . '}\zs\(.\|$\)', a:direction, a:visualmode ? visualmode() : '', '')
endif
endfunction " }}}
function! EasyMotion#Search(visualmode, direction) " {{{
call s:EasyMotion(@/, a:direction, a:visualmode ? visualmode() : '', '')
endfunction " }}}
function! EasyMotion#SelectLines() "{{{
let orig_pos = [line('.'), col('.')]
call s:EasyMotion('^\(\w\|\s*\zs\|$\)', 2, '', '', 0, 0, 1)
if g:EasyMotion_cancelled
keepjumps call cursor(orig_pos[0], orig_pos[1])
return ''
else
let pos1 = [line('.'), col('.')]
keepjumps call cursor(orig_pos[0], orig_pos[1])
call s:EasyMotion('^\(\w\|\s*\zs\|$\)', 2, '', '', pos1[0], 1, 1)
if g:EasyMotion_cancelled
keepjumps call cursor(orig_pos[0], orig_pos[1])
return ''
else
normal! V
keepjumps call cursor(pos1[0], pos1[1])
return 1
endif
endif
endfunction "}}}
function! EasyMotion#SelectLinesYank() "{{{
let orig_pos = [line('.'), col('.')]
call EasyMotion#SelectLines()
normal! y
keepjumps call cursor(orig_pos[0], orig_pos[1])
endfunction "}}}
function! EasyMotion#SelectLinesDelete() "{{{
let orig_pos = [line('.'), col('.')]
" if cancelled?
if EasyMotion#SelectLines()
" Prepare the number of lines "{{{
let start_of_line = line("v")
silent exec "normal!" "o"
let end_of_line = line("v")
"}}}
normal! d
if orig_pos[0] < max([start_of_line,end_of_line])
keepjumps call cursor(orig_pos[0], orig_pos[1])
else
" if delete lines above cursor line
keepjumps call cursor(orig_pos[0]-abs(end_of_line-start_of_line)-1, orig_pos[1])
endif
else
keepjumps call cursor(orig_pos[0], orig_pos[1])
endif
endfunction "}}}
function! EasyMotion#SelectPhrase() "{{{
let chars = s:GetSearchChar2(0)
if empty(chars)
return
endif
let orig_pos = [line('.'), col('.')]
if g:EasyMotion_smartcase && chars[0] =~# '\v\U' || chars[1] =~# '\v\U'
let re = '\c'
else
let re = '\C'
endif
let re = re . escape(chars[0], '.$^~') . '\|' . escape(chars[1], '.$^~')
call s:EasyMotion(re, 2, '', '', 0, 0, 0, 0)
if g:EasyMotion_cancelled
keepjumps call cursor(orig_pos[0], orig_pos[1])
return ''
else
let pos1 = [line('.'), col('.')]
keepjumps call cursor(orig_pos[0], orig_pos[1])
call s:EasyMotion(re, 2, '', '', 0, 0, 0, pos1)
if g:EasyMotion_cancelled
keepjumps call cursor(orig_pos[0], orig_pos[1])
return ''
else
normal! v
keepjumps call cursor(pos1[0], pos1[1])
return 1
endif
endif
endfunction "}}}
function! EasyMotion#SelectPhraseYank() "{{{
let orig_pos = [line('.'), col('.')]
call EasyMotion#SelectPhrase()
normal! y
keepjumps call cursor(orig_pos[0], orig_pos[1])
endfunction "}}}
function! EasyMotion#SelectPhraseDelete() "{{{
let orig_pos = [line('.'), col('.')]
" If cancelled?
if EasyMotion#SelectPhrase()
" Prepare the number of lines "{{{
let start_of_line = line("v")
silent exec "normal!" "o"
let end_of_line = line("v")
"}}}
normal! d
if orig_pos[0] < max([start_of_line,end_of_line])
keepjumps call cursor(orig_pos[0], orig_pos[1])
else
" if you delete phrase above cursor line and phrase is over lines
keepjumps call cursor(orig_pos[0]-abs(end_of_line-start_of_line), orig_pos[1])
endif
else
keepjumps call cursor(orig_pos[0], orig_pos[1])
endif
endfunction "}}}
" }}}
" Helper functions {{{
function! s:Message(message) " {{{
echo 'EasyMotion: ' . a:message
endfunction " }}}
function! s:Prompt(message) " {{{
echohl Question
echo a:message . ': '
echohl None
endfunction " }}}
function! s:VarReset(var, ...) " {{{
if ! exists('s:var_reset')
let s:var_reset = {}
endif
if a:0 == 0 && has_key(s:var_reset, a:var)
" Reset var to original value
call setbufvar("", a:var, s:var_reset[a:var])
elseif a:0 == 1
let new_value = a:0 == 1 ? a:1 : ''
" Store original value
let s:var_reset[a:var] = getbufvar("", a:var)
" Set new var value
call setbufvar("", a:var, new_value)
endif
endfunction " }}}
function! s:SetLines(lines, key) " {{{
try
" Try to join changes with previous undo block
undojoin
catch
endtry
for [line_num, line] in a:lines
call setline(line_num, line[a:key])
endfor
endfunction " }}}
function! s:GetChar() " {{{
let char = getchar()
if char == 27
" Escape key pressed
redraw
call s:Message('Cancelled')
return ''
endif
return nr2char(char)
endfunction " }}}
function! s:GetSearchChar2(visualmode) " {{{
let chars = []
for i in [1, 2]
redraw
call s:Prompt('Search for character ' . i)
let char = s:GetChar()
" Check that we have an input char
if empty(char)
" Restore selection
if ! empty(a:visualmode)
silent exec 'normal! gv'
endif
return ''
endif
call add(chars, char)
endfor
return chars
endfunction " }}}
function! s:GetSearchChar(visualmode) " {{{
call s:Prompt('Search for character')
let char = s:GetChar()
" Check that we have an input char
if empty(char)
" Restore selection
if ! empty(a:visualmode)
silent exec 'normal! gv'
endif
return ''
endif
return char
endfunction " }}}
function! s:load_migemo_dict() "{{{
let enc = &l:encoding
if enc ==# 'utf-8'
return EasyMotion#migemo#utf8#load_dict()
elseif enc ==# 'cp932'
return EasyMotion#migemo#cp932#load_dict()
elseif enc ==# 'euc-jp'
return EasyMotion#migemo#eucjp#load_dict()
else
let g:EasyMotion_use_migemo = 0
throw "Error: ".enc." is not supported. Migemo is made disabled."
endif
endfunction "}}}
" }}}
" Grouping algorithms {{{
let s:grouping_algorithms = {
\ 1: 'SCTree'
\ , 2: 'Original'
\ }
" Single-key/closest target priority tree {{{
" This algorithm tries to assign one-key jumps to all the targets closest to the cursor.
" It works recursively and will work correctly with as few keys as two.
function! s:GroupingAlgorithmSCTree(targets, keys)
" Prepare variables for working
let targets_len = len(a:targets)
let keys_len = len(a:keys)
let groups = {}
let keys = reverse(copy(a:keys))
" Semi-recursively count targets {{{
" We need to know exactly how many child nodes (targets) this branch will have
" in order to pass the correct amount of targets to the recursive function.
" Prepare sorted target count list {{{
" This is horrible, I know. But dicts aren't sorted in vim, so we need to
" work around that. That is done by having one sorted list with key counts,
" and a dict which connects the key with the keys_count list.
let keys_count = []
let keys_count_keys = {}
let i = 0
for key in keys
call add(keys_count, 0)
let keys_count_keys[key] = i
let i += 1
endfor
" }}}
let targets_left = targets_len
let level = 0
let i = 0
while targets_left > 0
" Calculate the amount of child nodes based on the current level
let childs_len = (level == 0 ? 1 : (keys_len - 1) )
for key in keys
" Add child node count to the keys_count array
let keys_count[keys_count_keys[key]] += childs_len
" Subtract the child node count
let targets_left -= childs_len
if targets_left <= 0
" Subtract the targets left if we added too many too
" many child nodes to the key count
let keys_count[keys_count_keys[key]] += targets_left
break
endif
let i += 1
endfor
let level += 1
endwhile
" }}}
" Create group tree {{{
let i = 0
let key = 0
call reverse(keys_count)
for key_count in keys_count
if key_count > 1
" We need to create a subgroup
" Recurse one level deeper
let groups[a:keys[key]] = s:GroupingAlgorithmSCTree(a:targets[i : i + key_count - 1], a:keys)
elseif key_count == 1
" Assign single target key
let groups[a:keys[key]] = a:targets[i]
else
" No target
continue
endif
let key += 1
let i += key_count
endfor
" }}}
" Finally!
return groups
endfunction
" }}}
" Original {{{
function! s:GroupingAlgorithmOriginal(targets, keys)
" Split targets into groups (1 level)
let targets_len = len(a:targets)
let keys_len = len(a:keys)
let groups = {}
let i = 0
let root_group = 0
try
while root_group < targets_len
let groups[a:keys[root_group]] = {}
for key in a:keys
let groups[a:keys[root_group]][key] = a:targets[i]
let i += 1
endfor
let root_group += 1
endwhile
catch | endtry
" Flatten the group array
if len(groups) == 1
let groups = groups[a:keys[0]]
endif
return groups
endfunction
" }}}
" Coord/key dictionary creation {{{
function! s:CreateCoordKeyDict(groups, ...)
" Dict structure:
" 1,2 : a
" 2,3 : b
let sort_list = []
let coord_keys = {}
let group_key = a:0 == 1 ? a:1 : ''
for [key, item] in items(a:groups)
let key = group_key . key "( ! empty(group_key) ? group_key : key)
"let key = ( ! empty(group_key) ? group_key : key)
if type(item) == 3
" Destination coords
" The key needs to be zero-padded in order to
" sort correctly
let dict_key = printf('%05d,%05d', item[0], item[1])
let coord_keys[dict_key] = key
" We need a sorting list to loop correctly in
" PromptUser, dicts are unsorted
call add(sort_list, dict_key)
else
" Item is a dict (has children)
let coord_key_dict = s:CreateCoordKeyDict(item, key)
" Make sure to extend both the sort list and the
" coord key dict
call extend(sort_list, coord_key_dict[0])
call extend(coord_keys, coord_key_dict[1])
endif
unlet item
endfor
return [sort_list, coord_keys]
endfunction
" }}}
" }}}
" Core functions {{{
function! s:PromptUser(groups, allows_repeat, fixed_column) "{{{
" If only one possible match, jump directly to it {{{
let group_values = values(a:groups)
if len(group_values) == 1
redraw
return group_values[0]
endif
" }}}
" Prepare marker lines {{{
let lines = {}
let hl_coords = []
let hl2_first_coords = [] " Highlight for two characters
let hl2_second_coords = [] " Highlight for two characters
let coord_key_dict = s:CreateCoordKeyDict(a:groups)
for dict_key in sort(coord_key_dict[0])
let target_key = coord_key_dict[1][dict_key]
let [line_num, col_num] = split(dict_key, ',')
let line_num = str2nr(line_num)
let col_num = str2nr(col_num)
" Add original line and marker line
if ! has_key(lines, line_num)
let current_line = getline(line_num)
let lines[line_num] = { 'orig': current_line, 'marker': current_line, 'mb_compensation': 0 }
endif
" Solve multibyte issues by matching the byte column
" number instead of the visual column
"echom lines[line_num]['mb_compensation']
let col_num -= lines[line_num]['mb_compensation']
"if col_num < 0
" let col_num = 0
"endif
" Compensate for byte difference between marker
" character and target character
"
" This has to be done in order to match the correct
" column; \%c matches the byte column and not display
" column.
let target_char_len = strdisplaywidth(matchstr(lines[line_num]['marker'], '\%' . col_num . 'c.'))
let target_key_len = strdisplaywidth(target_key)
let target_line_byte_len = strlen(lines[line_num]['marker'])
let target_char_byte_len = strlen(matchstr(lines[line_num]['marker'], '\%' . col_num . 'c.'))
if a:fixed_column
let firstS = match(lines[line_num]['marker'], '\S')
if firstS >= 4
let leftText = strpart(lines[line_num]['marker'], 0, firstS - 3)
else
let leftText = ''
endif
if firstS >= 1
let rightText = strpart(lines[line_num]['marker'], firstS - 1)
elseif firstS == 0
let rightText = ' ' . lines[line_num]['marker']
else
let rightText = ''
endif
if target_key_len < 2
let text = ' ' . target_key
call add(hl_coords, '\%' . line_num . 'l\%2c')
else
let text = target_key
call add(hl2_first_coords, '\%' . line_num . 'l\%1c')
call add(hl2_second_coords, '\%' . line_num . 'l\%2c')
endif
let lines[line_num]['marker'] = text . ' ' . lines[line_num]['marker']
else
if strlen(lines[line_num]['marker']) > 0
" Substitute marker character if line length > 0
let c = 0
while c < target_key_len && c < 2
if strlen(lines[line_num]['marker']) >= col_num + c
" Substitute marker character if line length > 0
if c == 0
let lines[line_num]['marker'] = substitute(
\ lines[line_num]['marker'],
\ '\%' . (col_num + c) . 'c.',
\ strpart(target_key, c, 1) . repeat(' ', target_char_len - 1),
\ '')
else
let lines[line_num]['marker'] = substitute(
\ lines[line_num]['marker'],
\ '\%' . (col_num + c) . 'c.',
\ strpart(target_key, c, 1),
\ '')
endif
else
let lines[line_num]['marker'] = lines[line_num]['marker'] . strpart(target_key, c, 1)
endif
let c += 1
endwhile
else
" Set the line to the marker character if the line is empty
let lines[line_num]['marker'] = target_key
endif
endif
" Add highlighting coordinates
if !a:fixed_column
if target_key_len == 1
call add(hl_coords, '\%' . line_num . 'l\%' . col_num . 'c')
else
call add(hl2_first_coords, '\%' . line_num . 'l\%' . (col_num) . 'c')
call add(hl2_second_coords, '\%' . line_num . 'l\%' . (col_num + 1) . 'c')
endif
endif
" Add marker/target lenght difference for multibyte
" compensation
let lines[line_num]['mb_compensation'] += (target_line_byte_len - strlen(lines[line_num]['marker']) )
endfor
let lines_items = items(lines)
" }}}
" Highlight targets {{{
if len(hl_coords) > 0
let target_hl_id = matchadd(g:EasyMotion_hl_group_target, join(hl_coords, '\|'), 1)
endif
if len(hl2_second_coords) > 0
let target_hl2_second_id = matchadd(g:EasyMotion_hl2_second_group_target, join(hl2_second_coords, '\|'), 1)
endif
if len(hl2_first_coords) > 0
let target_hl2_first_id = matchadd(g:EasyMotion_hl2_first_group_target, join(hl2_first_coords, '\|'), 1)
endif
" }}}
try
" Set lines with markers
call s:SetLines(lines_items, 'marker')
redraw
" Get target character {{{
call s:Prompt('Target key')
let char = s:GetChar()
" }}}
finally
" Restore original lines
call s:SetLines(lines_items, 'orig')
" Break undo history
silent exec 'normal!' "i\<C-g>u\<ESC>"
" Un-highlight targets {{{
if exists('target_hl_id')
call matchdelete(target_hl_id)
endif
if exists('target_hl2_first_id')
call matchdelete(target_hl2_first_id)
endif
if exists('target_hl2_second_id')
call matchdelete(target_hl2_second_id)
endif
" }}}
redraw
endtry
" Check if we have an input char {{{
if empty(char)
throw 'Cancelled'
endif
" }}}
" Check if the input char is valid {{{
if a:allows_repeat && char == '.'
return g:old_target
else
if ! has_key(a:groups, char)
throw 'Invalid target'
endif
" }}}
let target = a:groups[char]
if type(target) == 3
" Return target coordinates
return target
else
" Prompt for new target character
return s:PromptUser(target, a:allows_repeat, a:fixed_column)
endif
endif
endfunction "}}}
function! s:EasyMotion(regexp, direction, visualmode, mode, ...) " {{{
" For SelectLines(), to highlight previous selected line
let hlcurrent = a:0 >= 1 ? a:1 : 0
" For SelectLines(), to allows '.' to repeat the previously pressed
" character
let allows_repeat = a:0 >= 2 ? a:2 : 0
" For SelectLines(), a flag to display character only at the beginning
" of the line
let fixed_column = a:0 >= 3 ? a:3 : 0
let hlchar = a:0 >= 4 ? a:4 : 0
let orig_pos = [line('.'), col('.')]
let targets = []
try
" Reset properties {{{
call s:VarReset('&scrolloff', 0)
call s:VarReset('&modified', 0)
call s:VarReset('&modifiable', 1)
call s:VarReset('&readonly', 0)
call s:VarReset('&spell', 0)
call s:VarReset('&virtualedit', '')
call s:VarReset('&foldmethod', 'manual')
" }}}
" Find motion targets {{{
" Setup searchpos args {{{
let search_direction = (a:direction >= 1 ? 'b' : '')
let search_stopline = line(a:direction >= 1 ? 'w0' : 'w$')
let search_at_cursor = fixed_column ? 'c' : ''
"}}}
" Handle visual mode {{{
if ! empty(a:visualmode)
" Decide at where visual mode start {{{
normal! gv
let c_pos = [line("."),col(".")]
let v_start = [line("'<"),col("'<")]
let v_end = [line("'>"),col("'>")]
let vmode = mode(1)
if match('Vv',vmode) < 0
throw 'Unkown visual mode:'.vmode
elseif vmode ==# 'V' "line-wise Visual
" Line-wise Visual {{{
if v_start[0] == v_end[0]
if search_direction == ''
let v_pos = v_start
elseif search_direction == 'b'
let v_pos = v_end
else
throw 'Unkown search_direction'
endif
else
if c_pos[0] == v_start[0]
let v_pos = v_end
elseif c_pos[0] == v_end[0]
let v_pos = v_start
endif
endif
"}}}
else
" Character-wise or Block-wise Visual"{{{
if c_pos == v_start
let v_pos = v_end
elseif c_pos == v_end
let v_pos = v_start
else
throw 'Unkown c_pos'
endif
"}}}
endif
"}}}
" Reselect visual text {{{
keepjumps call cursor(v_pos)
exec "normal! " . a:visualmode
keepjumps call cursor(c_pos)
"}}}
" Update orig_pos {{{
let orig_pos = v_pos
" }}}
endif
" }}}
" Construct match dict {{{
while 1
" Note: searchpos() has side effect which call jump cursor position.
" You can disable this side effect by add 'n' flags,
" but in this case, it's better to allows jump side effect.
let pos = searchpos(a:regexp, search_direction . search_at_cursor, search_stopline)
let search_at_cursor = ''
" Reached end of search range
if pos == [0, 0]
break
endif
" Skip folded lines
if foldclosed(pos[0]) != -1 && (g:EasyMotion_skipfoldedline == 1 || pos[0] != foldclosed(pos[0]))
continue
endif
call add(targets, pos)
endwhile
"}}}
" Handle direction == 2"{{{
" Reconstruct match dict
if a:direction == 2
if ! empty(a:visualmode)
keepjumps call cursor(c_pos[0], c_pos[1])
else
keepjumps call cursor(orig_pos[0], orig_pos[1])
endif
let targets2 = []
while 1
let pos = searchpos(a:regexp, '', line('w$'))
if pos == [0, 0]
break
endif
" Skip folded lines {{{
if foldclosed(pos[0]) != -1 && (g:EasyMotion_skipfoldedline == 1 || pos[0] != foldclosed(pos[0]))
continue
endif
"}}}
call add(targets2, pos)
endwhile
" Merge match target dict"{{{
let t1 = 0
let t2 = 0
let targets3 = []
while t1 < len(targets) || t2 < len(targets2)
if t1 < len(targets)
call add(targets3, targets[t1])
let t1 += 1
endif
if t2 < len(targets2)
call add(targets3, targets2[t2])
let t2 += 1
endif
endwhile
let targets = targets3
"}}}
endif
"}}}
" Handle no match"{{{
let targets_len = len(targets)
if targets_len == 0
throw 'No matches'
endif
"}}}
" }}}
let GroupingFn = function('s:GroupingAlgorithm' . s:grouping_algorithms[g:EasyMotion_grouping])
let groups = GroupingFn(targets, split(g:EasyMotion_keys, '\zs'))
" Shade inactive source {{{
if g:EasyMotion_do_shade
let shade_hl_pos = '\%' . orig_pos[0] . 'l\%'. orig_pos[1] .'c'
if a:direction == 1
" Backward
let shade_hl_re = '\%'. line('w0') .'l\_.*' . shade_hl_pos
elseif a:direction == 0
" Forward
let shade_hl_re = shade_hl_pos . '\_.*\%'. line('w$') .'l'
elseif a:direction == 2
" Both directions"
let shade_hl_re = '\%'. line('w0') .'l\_.*\%'. line('w$') .'l'
endif
if !fixed_column
let shade_hl_id = matchadd(g:EasyMotion_hl_group_shade, shade_hl_re, 0)
endif
endif
if hlcurrent != 0
let shade_hl_line_id = matchadd(g:EasyMotion_hl_line_group_shade, '\%'. hlcurrent .'l.*', 1)
endif
if !empty(hlchar)
let shade_hl_line_id = matchadd(g:EasyMotion_hl_line_group_shade, '\%'. hlchar[0] .'l\%' . hlchar[1] .'c' , 2)
endif
" }}}
" Prompt user for target group/character"{{{
let coords = s:PromptUser(groups, allows_repeat, fixed_column)
let g:old_target = coords
"}}}
" Update selection {{{
if ! empty(a:visualmode)
keepjumps call cursor(orig_pos[0], orig_pos[1])
exec 'normal! ' . a:visualmode
endif
" }}}
" Handle operator-pending mode {{{
if a:mode == 'no'
" This mode requires that we eat one more
" character to the right if we're using
" a forward motion
if a:direction != 1
let coords[1] += 1
endif
endif
" }}}
" Update cursor position"{{{
call cursor(orig_pos[0], orig_pos[1])
let mark_save = getpos("'e")
call setpos("'e", [bufnr('%'), coords[0], coords[1], 0])
execute 'normal! `e'
call setpos("'e", mark_save)
call s:Message('Jumping to [' . coords[0] . ', ' . coords[1] . ']')
let g:EasyMotion_cancelled = 0
"}}}
catch
redraw
" Show exception message
call s:Message(v:exception)
" Restore original cursor position/selection {{{
if ! empty(a:visualmode)
silent exec 'normal! gv'
keepjumps call cursor(c_pos[0], c_pos[1])
else
keepjumps call cursor(orig_pos[0], orig_pos[1])
endif
" }}}
let g:EasyMotion_cancelled = 1
finally
" Restore properties {{{
call s:VarReset('&scrolloff')
call s:VarReset('&modified')
call s:VarReset('&modifiable')
call s:VarReset('&readonly')
call s:VarReset('&spell')
call s:VarReset('&virtualedit')
call s:VarReset('&foldmethod')
" }}}
" Remove shading {{{
if g:EasyMotion_do_shade && exists('shade_hl_id') && (!fixed_column)
call matchdelete(shade_hl_id)
endif
if (hlcurrent || !empty(hlchar)) && exists('shade_hl_line_id')
call matchdelete(shade_hl_line_id)
endif
" }}}
endtry
endfunction " }}}
" }}}
" vim: fdm=marker:noet:ts=4:sw=4:sts=4