diff --git a/autoload/EasyMotion.vim b/autoload/EasyMotion.vim index 241b314..430bfb2 100644 --- a/autoload/EasyMotion.vim +++ b/autoload/EasyMotion.vim @@ -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,13 +555,20 @@ 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 - let s:previous['input'] = get(s:previous, 'input', '') - let input = EasyMotion#command_line#GetInput(a:num_strokes, s:previous.input) + if g:EasyMotion_add_search_history && a:num_strokes == -1 + let s:previous['input'] = @/ + else + let s:previous['input'] = get(s:previous, 'input', '') + endif + 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 @@ -562,6 +577,11 @@ function! s:findMotion(num_strokes) "{{{ endif let re = s:convertRegep(input) + + if g:EasyMotion_add_search_history && a:num_strokes == -1 + let @/ = re + endif + return re endfunction "}}} function! s:convertRegep(input) "{{{ @@ -591,7 +611,7 @@ function! s:convertMigemo(re) "{{{ return EasyMotion#cmigemo#getMigemoPattern(re) endif - " EasyMoton migemo one key dict + " EasyMotion migemo one key dict if ! has_key(s:migemo_dicts, &l:encoding) let s:migemo_dicts[&l:encoding] = EasyMotion#helper#load_migemo_dict() endif @@ -723,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 @@ -761,6 +783,15 @@ endfunction "}}} function! EasyMotion#is_active() "{{{ return s:EasyMotion_is_active endfunction "}}} +function! EasyMotion#activate(is_visual) "{{{ + let s:EasyMotion_is_active = 1 + call EasyMotion#attach_active_autocmd() + call EasyMotion#highlight#add_highlight(s:previous.regexp, 'EasyMotionMoveHL') + call EasyMotion#highlight#attach_autocmd() + if a:is_visual == 1 + normal! gv + endif +endfunction "}}} "}}} " Grouping Algorithms: {{{ let s:grouping_algorithms = { @@ -1209,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 @@ -1229,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 @@ -1245,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 "}}} @@ -1253,23 +1294,21 @@ 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 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 @@ -1317,18 +1356,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 @@ -1380,11 +1416,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 @@ -1422,15 +1455,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 "}}} @@ -1445,7 +1477,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` @@ -1463,7 +1495,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 @@ -1519,12 +1552,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]) @@ -1569,9 +1600,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 diff --git a/autoload/EasyMotion/command_line.vim b/autoload/EasyMotion/command_line.vim index 0243dda..fc1ae4d 100644 --- a/autoload/EasyMotion/command_line.vim +++ b/autoload/EasyMotion/command_line.vim @@ -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:save_orig_pos) redraw echo 'EasyMotion: Cancelled' return '' @@ -71,17 +72,63 @@ 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$') + + let s:save_orig_pos = deepcopy(s:orig_pos) + call s:before_input(a:num_strokes) while EasyMotion#helper#strchars(input) < a:num_strokes || @@ -129,21 +176,46 @@ function! EasyMotion#command_line#GetInput(num_strokes, ...) "{{{ break elseif EasyMotion#command_line#is_input("\") break + elseif EasyMotion#command_line#is_input("\") + exec "normal! \" + let s:orig_pos = getpos('.') + let s:orig_line_start = getpos('w0') + let s:orig_line_end = getpos('w$') + elseif EasyMotion#command_line#is_input("\") + exec "normal! \" + let s:orig_pos = getpos('.') + let s:orig_line_start = getpos('w0') + let s:orig_line_end = getpos('w$') + elseif EasyMotion#command_line#is_input("\") + call setpos('.', s:save_orig_pos) + let s:orig_pos = s:save_orig_pos + let s:orig_line_start = getpos('w0') + let s:orig_line_end = getpos('w$') + elseif EasyMotion#command_line#is_input("\") + normal! zR elseif char2nr(s:char) == 128 || char2nr(s:char) < 27 " Do nothing for special key continue 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 diff --git a/doc/easymotion.txt b/doc/easymotion.txt index ae98b6e..0146768 100644 --- a/doc/easymotion.txt +++ b/doc/easymotion.txt @@ -1,4 +1,4 @@ -*easymotion.txt* Version 2.0 Last change:22 Jan 2014. +*easymotion.txt* Version 2.0 Last change:23 Jan 2014. ______ __ ___ __ _ @@ -28,7 +28,7 @@ CONTENTS *easymotion-contents* 4.6 EasyMotion_smartsign ........... |EasyMotion_smartsign| 4.7 EasyMotion_use_migemo .......... |EasyMotion_use_migemo| 4.8 EasyMotion_use_upper .......... |EasyMotion_use_upper| - 4.9 Custom highlighting ........... |easymotion-custom-hl| + 4.9 Custom highlighting ............ |easymotion-custom-hl| 4.10 Custom mappings ............... |easymotion-custom-mappings| 4.10.1 Leader key .............. |easymotion-leader-key| 4.10.2 Custom keys ............. |easymotion-custom-keys| @@ -38,6 +38,7 @@ CONTENTS *easymotion-contents* 4.12 EasyMotion_enter_jump_first ... |EasyMotion_enter_jump_first| 4.13 EasyMotion_prompt ............. |EasyMotion_prompt| 4.14 EasyMotion_highlight .......... |EasyMotion_highlight| + 4.15 EasyMotion_add_search_history.. |EasyMotion_add_search_history| 5. License ............................ |easymotion-license| 6. Known bugs ......................... |easymotion-known-bugs| 7. Contributing ....................... |easymotion-contributing| @@ -947,6 +948,24 @@ Default: < Default: 0 +4.15 EasyMotion_add_search_history *g:EasyMotion_add_search_history* + + If you set this option to 1, 'n' key find motion add inputed pattern to + vim default search history. +> + let g:EasyMotion_add_search_history = 1 +< + Default: 0 + +4.15 EasyMotion_off_screen_search *g:EasyMotion_off_screen_search* + + If you set this option to 1, 'n' key find motion could search patterns + even in off-screen range. +> + let g:EasyMotion_off_screen_search = 1 +< + Default: 0 + ============================================================================== 5. License *easymotion-license* diff --git a/plugin/EasyMotion.vim b/plugin/EasyMotion.vim index e5c4b38..98dc256 100644 --- a/plugin/EasyMotion.vim +++ b/plugin/EasyMotion.vim @@ -40,6 +40,8 @@ let g:EasyMotion_inc_highlight = get(g: , 'EasyMotion_inc_highlight' , let g:EasyMotion_move_highlight = get(g: , 'EasyMotion_move_highlight' , 1) 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): ') @@ -367,6 +369,9 @@ map (easymotion-clever-s2) map (easymotion-clever-sn) \ EasyMotion#is_active() ? '(easymotion-next)' : '(easymotion-sn)' +noremap (easymotion-activate) :call EasyMotion#activate(0) +xnoremap (easymotion-activate) :call EasyMotion#activate(1) + " }}} " == Default key mapping {{{ diff --git a/t/easymotion_spec.vim b/t/easymotion_spec.vim index e89b94d..be6be43 100644 --- a/t/easymotion_spec.vim +++ b/t/easymotion_spec.vim @@ -1,7 +1,7 @@ "============================================================================= " FILE: t/easymotion_spec.vim " AUTHOR: haya14busa -" Last Change: 22 Jan 2014. +" Last Change: 23 Jan 2014. " Test: https://github.com/kana/vim-vspec " Refer: https://github.com/rhysd/clever-f.vim " Description: EasyMotion test with vim-vspec @@ -393,6 +393,15 @@ describe 'Default settings' Expect maparg('(easymotion-special-pd)', 'n') \ ==# ':call EasyMotion#SelectPhraseDelete()' " }}} + + " Activate {{{ + Expect maparg('(easymotion-activate)', 'n') + \ ==# ':call EasyMotion#activate(0)' + Expect maparg('(easymotion-activate)', 'o') + \ ==# ':call EasyMotion#activate(0)' + Expect maparg('(easymotion-activate)', 'v') + \ ==# ':call EasyMotion#activate(1)' + " }}} end it 'provide autoload functions' @@ -453,6 +462,8 @@ describe 'Default settings' Expect g:EasyMotion_move_highlight ==# 1 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 ==# {} " }}} @@ -1195,4 +1206,111 @@ describe 'bi-directional t motion' end "}}} +" off-screen search {{{ +describe 'off-screen search' + before + new + let g:EasyMotion_keys = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + let g:EasyMotion_off_screen_search = 1 + map s/ (easymotion-sn) + map f/ (easymotion-fn) + map F/ (easymotion-Fn) + map t/ (easymotion-tn) + map T/ (easymotion-Tn) + call EasyMotion#init() + call AddLine('deco-chan deco-chan') + call AddLine('vim') + for i in range(50) + call AddLine('poge1 2huga 3hiyo 4poyo') + endfor + " 12345678901234567890123 + end + + after + let g:EasyMotion_off_screen_search = 0 + close! + end + + " provide search with off-screen range {{{ + it 'provide search with off-screen range' + normal! gg0 + let l = line('.') + Expect CursorPos() == [l,1,'p'] + + exec "normal s/vim\" + Expect CursorPos() == [51,1,'v'] + + normal! gg0 + exec "normal f/im\" + Expect CursorPos() == [51,2,'i'] + + set wrapscan + Expect &wrapscan == 1 + normal! gg0 + exec "normal F/im\" + Expect CursorPos() == [51,2,'i'] + + " Cancel + normal! gg0 + exec "normal s/vim\" + Expect CursorPos() == [l,1,'p'] + + " Label + normal! gg0 + exec "normal s/deco-chan\\" + Expect CursorPos() == [l,1,'p'] + + normal! gg0 + exec "normal s/deco-chan\a" + Expect CursorPos() == [52,1,'d'] + + normal! gg0 + exec "normal s/deco-chan\b" + Expect CursorPos() == [52,11,'d'] + + normal! gg0 + exec "normal t/chan\a" + Expect CursorPos() == [52,5,'-'] + + normal! gg0 + exec "normal t/chan\b" + Expect CursorPos() == [52,15,'-'] + + end + "}}} +end + +describe 'dot notoff-screen search' + before + new + let g:EasyMotion_keys = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + let g:EasyMotion_off_screen_search = 0 + map s/ (easymotion-sn) + call EasyMotion#init() + call AddLine('deco-chan deco-chan') + call AddLine('vim') + for i in range(50) + call AddLine('poge1 2huga 3hiyo 4poyo') + endfor + " 12345678901234567890123 + end + + after + close! + end + + " provide search with off-screen range {{{ + it 'provide search with off-screen range' + normal! gg0 + let l = line('.') + Expect CursorPos() == [l,1,'p'] + + exec "normal s/vim\" + Expect CursorPos() != [51,1,'v'] + Expect CursorPos() == [l,1,'p'] + end + "}}} +end +"}}} + " vim: fdm=marker:et:ts=4:sw=4:sts=4