Implement 'n' key motion offscreen search!
Including some refactoring - s:Easymotion function - handling original & cursor position - autoload/EasyMotion/command_line.vim - s:Getvisualstartposition function
This commit is contained in:
parent
c6a01fe473
commit
f00b912940
@ -75,6 +75,9 @@ function! EasyMotion#reset()
|
||||
\ 'dot_prompt_user_cnt' : 0,
|
||||
\ 'changedtick' : 0,
|
||||
\ }
|
||||
" \ 'start_position' : [],
|
||||
" \ 'cursor_position' : [],
|
||||
|
||||
" is_operator:
|
||||
" Store is_operator value first because mode(1) value will be
|
||||
" changed by some operation.
|
||||
@ -88,6 +91,11 @@ function! EasyMotion#reset()
|
||||
" which will change b:changedtick value. To overwrite g:repeat_tick
|
||||
" value(defined tpope/vim-repeat), I can avoid this side effect of
|
||||
" conflicting with tpope/vim-repeat
|
||||
" start_position:
|
||||
" Original, start cursor position.
|
||||
" cursor_position:
|
||||
" Usually, this valuse is same with start_position, but in
|
||||
" visualmode and 'n' key motion, this value could be different.
|
||||
return ""
|
||||
endfunction "}}}
|
||||
" Motion Functions: {{{
|
||||
@ -112,7 +120,7 @@ function! EasyMotion#S(num_strokes, visualmode, direction) " {{{
|
||||
let is_inclusive = mode(1) ==# 'no' ? 1 : 0
|
||||
endif
|
||||
let s:flag.find_bd = a:direction == 2 ? 1 : 0
|
||||
let re = s:findMotion(a:num_strokes)
|
||||
let re = s:findMotion(a:num_strokes, a:direction)
|
||||
if s:handleEmpty(re, a:visualmode) | return | endif
|
||||
call s:EasyMotion(re, a:direction, a:visualmode ? visualmode() : '', is_inclusive)
|
||||
return s:EasyMotion_is_cancelled
|
||||
@ -126,7 +134,7 @@ function! EasyMotion#T(num_strokes, visualmode, direction) " {{{
|
||||
let is_inclusive = mode(1) ==# 'no' ? 1 : 0
|
||||
endif
|
||||
let s:flag.find_bd = a:direction == 2 ? 1 : 0
|
||||
let re = s:findMotion(a:num_strokes)
|
||||
let re = s:findMotion(a:num_strokes, a:direction)
|
||||
if s:handleEmpty(re, a:visualmode) | return | endif
|
||||
if a:direction == 2
|
||||
let s:flag.bd_t = 1
|
||||
@ -547,9 +555,11 @@ function! s:GetSearchChar(visualmode) " {{{
|
||||
return char
|
||||
endfunction " }}}
|
||||
" -- Find Motion Helper ------------------
|
||||
function! s:findMotion(num_strokes) "{{{
|
||||
function! s:findMotion(num_strokes, direction) "{{{
|
||||
" Find Motion: S,F,T
|
||||
let s:current.is_operator = mode(1) ==# 'no' ? 1: 0
|
||||
" store cursor pos because 'n' key find motion could be jump to offscreen
|
||||
let s:current.original_position = [line('.'), col('.')]
|
||||
let s:flag.regexp = a:num_strokes == -1 ? 1 : 0
|
||||
|
||||
if g:EasyMotion_add_search_history && a:num_strokes == -1
|
||||
@ -557,7 +567,8 @@ function! s:findMotion(num_strokes) "{{{
|
||||
else
|
||||
let s:previous['input'] = get(s:previous, 'input', '')
|
||||
endif
|
||||
let input = EasyMotion#command_line#GetInput(a:num_strokes, s:previous.input)
|
||||
let input = EasyMotion#command_line#GetInput(
|
||||
\ a:num_strokes, s:previous.input, a:direction)
|
||||
let s:previous['input'] = input
|
||||
|
||||
" Check that we have an input char
|
||||
@ -732,8 +743,10 @@ function! s:GetVisualStartPosition(c_pos, v_start, v_end, search_direction) "{{{
|
||||
return a:v_end
|
||||
elseif a:c_pos[0] == a:v_end[0]
|
||||
return a:v_start
|
||||
elseif EasyMotion#helper#is_greater_coords(a:c_pos, a:v_start) == 1
|
||||
return a:v_end
|
||||
else
|
||||
throw 'Unkown a:c_pos'
|
||||
return a:v_start
|
||||
endif
|
||||
"}}}
|
||||
endif
|
||||
@ -1227,12 +1240,21 @@ function! s:EasyMotion(regexp, direction, visualmode, is_inclusive, ...) " {{{
|
||||
let hlchar = a:0 >= 4 ? a:4 : 0
|
||||
"}}}
|
||||
|
||||
let orig_pos = [line('.'), col('.')]
|
||||
let win_first_line = line('w0')
|
||||
let win_last_line = line('w$')
|
||||
" Store s:current original_position & cursor_position {{{
|
||||
" current cursor pos.
|
||||
let s:current.cursor_position = [line('.'), col('.')]
|
||||
" original start position. This value could be changed later in visual
|
||||
" mode
|
||||
let s:current.original_position =
|
||||
\ get(s:current, 'original_position', s:current.cursor_position)
|
||||
"}}}
|
||||
|
||||
let win_first_line = line('w0') " visible first line num
|
||||
let win_last_line = line('w$') " visible last line num
|
||||
|
||||
let targets = []
|
||||
|
||||
" Store info for Repeat motion {{{
|
||||
if s:flag.dot_repeat != 1
|
||||
" Store Regular Expression
|
||||
let s:previous['regexp'] = a:regexp
|
||||
@ -1247,7 +1269,8 @@ function! s:EasyMotion(regexp, direction, visualmode, is_inclusive, ...) " {{{
|
||||
" For special motion flag
|
||||
let s:previous['line_flag'] = s:flag.within_line
|
||||
let s:previous['bd_t_flag'] = s:flag.bd_t " bi-directional t motion
|
||||
endif
|
||||
endif "}}}
|
||||
|
||||
" To avoid side effect of overwriting buffer for tpope/repeat
|
||||
" store current b:changedtick. Use this value later
|
||||
let s:current.changedtick = b:changedtick
|
||||
@ -1263,7 +1286,7 @@ function! s:EasyMotion(regexp, direction, visualmode, is_inclusive, ...) " {{{
|
||||
let search_at_cursor = fixed_column ? 'c' : ''
|
||||
|
||||
if s:flag.within_line == 1
|
||||
let search_stopline = orig_pos[0]
|
||||
let search_stopline = s:current.original_position[0]
|
||||
endif
|
||||
"}}}
|
||||
|
||||
@ -1271,23 +1294,22 @@ function! s:EasyMotion(regexp, direction, visualmode, is_inclusive, ...) " {{{
|
||||
if ! empty(a:visualmode)
|
||||
" Decide at where visual mode start {{{
|
||||
normal! gv
|
||||
let c_pos = orig_pos " current_position
|
||||
let s:current.cursor_position = [line('.'), col('.')]
|
||||
let v_start = [line("'<"),col("'<")] " visual_start_position
|
||||
let v_end = [line("'>"),col("'>")] " visual_end_position
|
||||
|
||||
let v_original_pos = s:GetVisualStartPosition(c_pos, v_start, v_end, search_direction)
|
||||
let v_original_pos = s:GetVisualStartPosition(
|
||||
\ s:current.cursor_position, v_start, v_end, search_direction)
|
||||
"}}}
|
||||
|
||||
" Reselect visual text {{{
|
||||
keepjumps call cursor(v_original_pos)
|
||||
exec "normal! " . a:visualmode
|
||||
keepjumps call cursor(c_pos)
|
||||
keepjumps call cursor(s:current.cursor_position)
|
||||
"}}}
|
||||
" Update orig_pos {{{
|
||||
let orig_pos = v_original_pos
|
||||
" }}}
|
||||
endif
|
||||
" }}}
|
||||
" Update s:current.original_position
|
||||
let s:current.original_position = v_original_pos " overwrite original start positio
|
||||
endif "}}}
|
||||
|
||||
" Handle bi-directional t motion {{{
|
||||
if s:flag.bd_t == 1
|
||||
@ -1335,18 +1357,15 @@ function! s:EasyMotion(regexp, direction, visualmode, is_inclusive, ...) " {{{
|
||||
if a:direction == 2
|
||||
" Forward
|
||||
|
||||
" Jump back original position
|
||||
if ! empty(a:visualmode)
|
||||
keepjumps call cursor(c_pos[0], c_pos[1])
|
||||
else
|
||||
keepjumps call cursor(orig_pos[0], orig_pos[1])
|
||||
endif
|
||||
" Jump back cursor_position
|
||||
keepjumps call cursor(s:current.cursor_position[0],
|
||||
\ s:current.cursor_position[1])
|
||||
|
||||
let targets2 = []
|
||||
if s:flag.within_line == 0
|
||||
let search_stopline = win_last_line
|
||||
else
|
||||
let search_stopline = !empty(a:visualmode) ? c_pos[0] : orig_pos[0]
|
||||
let search_stopline = s:current.cursor_position[0]
|
||||
endif
|
||||
while 1
|
||||
" TODO: refactoring
|
||||
@ -1398,11 +1417,8 @@ function! s:EasyMotion(regexp, direction, visualmode, is_inclusive, ...) " {{{
|
||||
|
||||
" -- Shade inactive source --------------- {{{
|
||||
if g:EasyMotion_do_shade && targets_len != 1 && s:flag.dot_repeat != 1
|
||||
if !empty(a:visualmode)
|
||||
let shade_hl_pos = '\%' . c_pos[0] . 'l\%'. c_pos[1] .'c'
|
||||
else
|
||||
let shade_hl_pos = '\%' . orig_pos[0] . 'l\%'. orig_pos[1] .'c'
|
||||
endif
|
||||
let shade_hl_pos = '\%'. s:current.cursor_position[0] .'l'.
|
||||
\ '\%'. s:current.cursor_position[1] .'c'
|
||||
|
||||
if a:direction == 1
|
||||
" Backward
|
||||
@ -1440,15 +1456,14 @@ function! s:EasyMotion(regexp, direction, visualmode, is_inclusive, ...) " {{{
|
||||
|
||||
" -- Jump back before prompt for visual scroll {{{
|
||||
" Because searchpos() change current cursor position and
|
||||
" if you just use cursor([orig_pos, orig_pos]) to jump back,
|
||||
" if you just use cursor(s:current.cursor_position) to jump back,
|
||||
" current line will become middle of line window
|
||||
if ! empty(a:visualmode)
|
||||
keepjumps call cursor(win_first_line,0)
|
||||
normal! zt
|
||||
" for adjusting cursorline
|
||||
keepjumps call cursor(c_pos)
|
||||
else
|
||||
keepjumps call cursor(orig_pos)
|
||||
" for adjusting cursorline
|
||||
keepjumps call cursor(s:current.cursor_position)
|
||||
endif
|
||||
"}}}
|
||||
|
||||
@ -1463,7 +1478,7 @@ function! s:EasyMotion(regexp, direction, visualmode, is_inclusive, ...) " {{{
|
||||
|
||||
" -- Update cursor position -------------- {{{
|
||||
" First, jump back cursor to original position
|
||||
keepjumps call cursor(orig_pos[0], orig_pos[1])
|
||||
keepjumps call cursor(s:current.original_position)
|
||||
|
||||
" Consider EasyMotion as jump motion :h jump-motion
|
||||
normal! m`
|
||||
@ -1481,7 +1496,8 @@ function! s:EasyMotion(regexp, direction, visualmode, is_inclusive, ...) " {{{
|
||||
" to handling inclusive/exclusive motion
|
||||
if a:direction == 2
|
||||
let true_direction =
|
||||
\ EasyMotion#helper#is_greater_coords(orig_pos, coords) > 0 ?
|
||||
\ EasyMotion#helper#is_greater_coords(
|
||||
\ s:current.original_position, coords) > 0 ?
|
||||
\ 0 : 1
|
||||
" forward : backward
|
||||
else
|
||||
@ -1537,12 +1553,10 @@ function! s:EasyMotion(regexp, direction, visualmode, is_inclusive, ...) " {{{
|
||||
normal! v
|
||||
endif " }}}
|
||||
|
||||
" Adjust screen for visual scroll {{{
|
||||
" Adjust screen especially for visual scroll & offscreen search {{{
|
||||
" Otherwise, cursor line will move middle line of window
|
||||
if ! empty(a:visualmode)
|
||||
keepjumps call cursor(win_first_line, 0)
|
||||
normal! zt
|
||||
endif "}}}
|
||||
keepjumps call cursor(win_first_line, 0)
|
||||
normal! zt
|
||||
|
||||
" Jump to destination
|
||||
keepjumps call cursor(coords[0], coords[1])
|
||||
@ -1587,9 +1601,9 @@ function! s:EasyMotion(regexp, direction, visualmode, is_inclusive, ...) " {{{
|
||||
" -- Restore original cursor position/selection {{{
|
||||
if ! empty(a:visualmode)
|
||||
silent exec 'normal! gv'
|
||||
keepjumps call cursor(c_pos[0], c_pos[1])
|
||||
keepjumps call cursor(s:current.cursor_position)
|
||||
else
|
||||
keepjumps call cursor(orig_pos[0], orig_pos[1])
|
||||
keepjumps call cursor(s:current.original_position)
|
||||
endif
|
||||
" }}}
|
||||
let s:EasyMotion_is_cancelled = 1 " Cancel
|
||||
|
@ -2,7 +2,7 @@
|
||||
" FILE: autoload/EasyMotion/command_line.vim
|
||||
" AUTHOR: haya14busa
|
||||
" Reference: https://github.com/osyo-manga/vim-over
|
||||
" Last Change: 20 Jan 2014.
|
||||
" Last Change: 23 Jan 2014.
|
||||
" License: MIT license {{{
|
||||
" Permission is hereby granted, free of charge, to any person obtaining
|
||||
" a copy of this software and associated documentation files (the
|
||||
@ -37,6 +37,7 @@ function! s:InputPrompt(message, input) "{{{
|
||||
endfunction "}}}
|
||||
function! s:Cancell() " {{{
|
||||
call EasyMotion#highlight#delete_highlight()
|
||||
keepjumps call setpos('.', s:orig_pos)
|
||||
redraw
|
||||
echo 'EasyMotion: Cancelled'
|
||||
return ''
|
||||
@ -71,17 +72,61 @@ function! s:after_input(num_strokes) "{{{
|
||||
call EasyMotion#highlight#delete_highlight()
|
||||
endfunction "}}}
|
||||
function! s:should_use_smartcase(input) "{{{
|
||||
" TODO:
|
||||
if g:EasyMotion_smartcase == 0
|
||||
return 0
|
||||
endif
|
||||
" return 1 if input didn't match upporcase letter
|
||||
return match(a:input, '\u') == -1
|
||||
endfunction "}}}
|
||||
function! EasyMotion#command_line#GetInput(num_strokes, ...) "{{{
|
||||
let previous_input = a:0 == 1 ? a:1 : ''
|
||||
function! s:offscreen_search(re) "{{{
|
||||
" First: search within visible screen range
|
||||
call s:adjust_screen()
|
||||
silent! let pos = searchpos(a:re, s:direction . 'n', s:orig_line_end[1])
|
||||
if pos != [0, 0]
|
||||
" Restore cursor posision
|
||||
keepjumps call setpos('.', s:orig_pos)
|
||||
else
|
||||
" Second: if there were no much, search off screen
|
||||
silent! let pos = searchpos(a:re, s:direction)
|
||||
if pos != [0, 0]
|
||||
" Match
|
||||
keepjumps call setpos('.', pos)
|
||||
" Move cursor
|
||||
if s:direction != 'b'
|
||||
normal! zzH0
|
||||
else
|
||||
normal! zzL0
|
||||
endif
|
||||
else
|
||||
" No much
|
||||
call s:adjust_screen()
|
||||
keepjumps call setpos('.', s:orig_pos)
|
||||
endif
|
||||
endif
|
||||
endfunction "}}}
|
||||
function! s:adjust_screen() "{{{
|
||||
if s:direction != 'b'
|
||||
" Forward
|
||||
keepjumps call setpos('.', s:orig_line_start)
|
||||
normal! zt
|
||||
else
|
||||
" Backward
|
||||
keepjumps call setpos('.', s:orig_line_end)
|
||||
normal! zb
|
||||
endif
|
||||
endfunction "}}}
|
||||
|
||||
function! EasyMotion#command_line#GetInput(num_strokes, prev, direction) "{{{
|
||||
let previous_input = a:prev
|
||||
let s:direction = a:direction == 1 ? 'b' : ''
|
||||
let input = ''
|
||||
let prompt = s:getPromptMessage(a:num_strokes)
|
||||
|
||||
let s:orig_pos = getpos('.')
|
||||
let s:orig_line_start = getpos('w0')
|
||||
let s:orig_line_end = getpos('w$')
|
||||
|
||||
call s:before_input(a:num_strokes)
|
||||
|
||||
while EasyMotion#helper#strchars(input) < a:num_strokes ||
|
||||
@ -135,15 +180,23 @@ function! EasyMotion#command_line#GetInput(num_strokes, ...) "{{{
|
||||
else
|
||||
let input .= s:char
|
||||
endif
|
||||
if g:EasyMotion_inc_highlight
|
||||
call EasyMotion#highlight#delete_highlight('EasyMotionIncSearch')
|
||||
if a:num_strokes == -1 && len(input) > 0
|
||||
let re = input
|
||||
let case_flag = s:should_use_smartcase(input) ? '\c' : '\C'
|
||||
let re .= case_flag
|
||||
call EasyMotion#highlight#add_highlight(re, g:EasyMotion_hl_inc_search)
|
||||
endif
|
||||
|
||||
" Incremental routine {{{
|
||||
if a:num_strokes == -1
|
||||
let re = input
|
||||
let case_flag = s:should_use_smartcase(input) ? '\c' : '\C'
|
||||
let re .= case_flag
|
||||
if g:EasyMotion_inc_highlight "{{{
|
||||
call EasyMotion#highlight#delete_highlight('EasyMotionIncSearch')
|
||||
if len(input) > 0
|
||||
silent! call EasyMotion#highlight#add_highlight(re, g:EasyMotion_hl_inc_search)
|
||||
endif
|
||||
endif "}}}
|
||||
if g:EasyMotion_off_screen_search "{{{
|
||||
call s:offscreen_search(re)
|
||||
endif "}}}
|
||||
endif
|
||||
"}}}
|
||||
endwhile
|
||||
call s:after_input(a:num_strokes)
|
||||
return input
|
||||
|
@ -41,6 +41,7 @@ let g:EasyMotion_move_highlight = get(g: , 'EasyMotion_move_highlight' ,
|
||||
let g:EasyMotion_landing_highlight = get(g: , 'EasyMotion_landing_highlight' , 0)
|
||||
let g:EasyMotion_cursor_highlight = get(g: , 'EasyMotion_cursor_highlight' , 0)
|
||||
let g:EasyMotion_add_search_history = get(g: , 'EasyMotion_add_search_history' , 0)
|
||||
let g:EasyMotion_off_screen_search = get(g: , 'EasyMotion_off_screen_search' , 0)
|
||||
let g:EasyMotion_show_prompt = get(g: , 'EasyMotion_show_prompt' , 1)
|
||||
let g:EasyMotion_prompt =
|
||||
\ get(g: , 'EasyMotion_prompt' , 'Search for {n} character(s): ')
|
||||
|
@ -463,6 +463,7 @@ describe 'Default settings'
|
||||
Expect g:EasyMotion_landing_highlight ==# 0
|
||||
Expect g:EasyMotion_cursor_highlight ==# 0
|
||||
Expect g:EasyMotion_add_search_history ==# 0
|
||||
Expect g:EasyMotion_off_screen_search ==# 0
|
||||
Expect g:EasyMotion_prompt ==# 'Search for {n} character(s): '
|
||||
Expect g:EasyMotion_command_line_key_mappings ==# {}
|
||||
" }}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user