diff --git a/autoload/EasyMotion.vim b/autoload/EasyMotion.vim index a8f563a..6951c1f 100644 --- a/autoload/EasyMotion.vim +++ b/autoload/EasyMotion.vim @@ -187,7 +187,12 @@ endfunction " }}} function! EasyMotion#WBK(visualmode, direction) " {{{ " vim's iskeyword style word motion let s:current.is_operator = mode(1) ==# 'no' ? 1: 0 - call s:EasyMotion('\(\(\<\|\>\|\s\)\@<=\S\|^$\)', a:direction, a:visualmode ? visualmode() : '', 0) + " Note: Previous regex for all directions was '\(\(\<\|\>\|\s\)\@<=\S\|^$\)' + let l:regex_without_file_ends = '\v<|^\S|\s\zs\S|>\zs\S|^$' + let l:regex = l:regex_without_file_ends + \ . (a:direction == 1 ? '' : '|%$') + \ . (a:direction == 0 ? '' : '|%^') + call s:EasyMotion(l:regex, a:direction, a:visualmode ? visualmode() : '', 0) return s:EasyMotion_is_cancelled endfunction " }}} function! EasyMotion#E(visualmode, direction) " {{{ diff --git a/t/compare_movements_spec.vim b/t/compare_movements_spec.vim new file mode 100644 index 0000000..cc15649 --- /dev/null +++ b/t/compare_movements_spec.vim @@ -0,0 +1,190 @@ +"============================================================================= +" FILE: t/compare_movements_spec.vim +" AUTHOR: YggdrasiI +" Test: https://github.com/kana/vim-vspec +" Description: EasyMotion keyword movement test with vim-vspec +" License: MIT license {{{ +" Permission is hereby granted, free of charge, to any person obtaining +" a copy of this software and associated documentation files (the +" "Software"), to deal in the Software without restriction, including +" without limitation the rights to use, copy, modify, merge, publish, +" distribute, sublicense, and/or sell copies of the Software, and to +" permit persons to whom the Software is furnished to do so, subject to +" the following conditions: +" +" The above copyright notice and this permission notice shall be included +" in all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +" IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +" CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +" TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +" SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +" }}} +"============================================================================= + +" Setup {{{ +let s:root_dir = matchstr(system('git rev-parse --show-cdup'), '[^\n]\+') +execute 'set' 'rtp +=./'.s:root_dir +runtime! plugin/EasyMotion.vim +" }}} + +" Functions for Test {{{ +function! AddLine(str) + put! =a:str +endfunction + +function! CursorPos() + return [line('.'), col('.'), getline('.')[col('.')-1]] +endfunction + +" Nested normal to avoid throwing readonly errors. They abort the testing. +function TryNormal(str) + try + exec 'normal ' . a:str + catch /^Vim\%((\a\+)\)\=:E21/ + endtry +endfunction + +let s:to_cursor = {} +function! s:to_cursor.match(actual, expected) + return a:actual == a:expected +endfunction + +" Add metadata about failure. +function! s:to_cursor.failure_message_for_should(actual, expected) + return '' + Expect a:actual[0] > 0 + Expect a:expected[0] > 0 + Expect a:actual[0] <= getpos('$')[1] + Expect a:expected[0] <= getpos('$')[1] + Expect a:actual[1] > 0 + Expect a:expected[1] > 0 + + let l:line1 = getline(a:actual[0]) + let l:line2 = getline(a:expected[0]) + " Change char on cursor to '█'. + let l:line1 = strpart(l:line1, 0, a:actual[1]-1) + \ . '█' + \ . strpart(l:line1, a:actual[1]) + let line2 = strpart(l:line2, 0, a:expected[1]-1) + \ . '█' + \ . strpart(l:line2, a:expected[1]) + let l:msg = 'Line ' . string(a:actual[0]) . ": '" . l:line1 + \ . "', Line " . string(a:expected[0]) . ": '" . l:line2 . "'" + return l:msg +endfunction + +function! CompareMovements(movement1, movement2, backward) + let l:jumpmarks = [ + \ [a:movement1, []], + \ [a:movement2, []], + \ ] + + " Loop through current buffer in both variants {{ + for [l:handler, l:list] in l:jumpmarks + if a:backward == 1 + call cursor(getpos('$')[1:2]) + else + call cursor([1,1]) + endif + + let l:lastpos = [0,0] + + call TryNormal(l:handler) + let l:curpos = getpos(".")[1:2] + + while l:lastpos != l:curpos + let l:list += [l:curpos] + let l:lastpos = l:curpos + call TryNormal(l:handler) + let l:curpos = getpos(".")[1:2] + endwhile + endfor + " }} + + " The resulting lists are stored in l:jumpmarks[*][1], now. + let [l:cursor_positions1, l:cursor_positions2] = [ l:jumpmarks[0][1], l:jumpmarks[1][1] ] + + " Debug output for this script + let g:dbg_msg = printf("(CompareMovements) '%s' vs '%s'\Length of both lists: %d, %d\r Content of lists:\r%s\r\r%s", + \ string(l:jumpmarks[0][0]), + \ string(l:jumpmarks[1][0]), + \ len(l:cursor_positions1), len(l:cursor_positions2), + \ string(l:cursor_positions1), + \ string(l:cursor_positions2)) + Expect g:dbg_msg == v:errmsg + + if l:cursor_positions1 == l:cursor_positions2 + return 0 + endif + + " Search for first unmatching position. {{ + let l:index = 0 + let l:len = min([len(l:cursor_positions2), len(l:cursor_positions1)]) + while l:index < l:len + Expect l:cursor_positions2[l:index] to_cursor l:cursor_positions1[l:index] + let l:index += 1 + endwhile + + " Collision with begin or end of file + if a:backward == 1 + Expect join(['File begin reached after ', len(l:cursor_positions2), ' steps.']) + \ == join(['File begin reached after ', len(l:cursor_positions1), ' steps.']) + else + Expect join(['File end reached after ', len(l:cursor_positions2), ' steps.']) + \ == join(['File end reached after ', len(l:cursor_positions1), ' steps.']) + endif + " }} + + return -1 +endfunction +"}}} + +"Keyword word motion {{{ +describe 'Keyword word motion' + before + new + nmap a + let g:EasyMotion_keys = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + nmap w (easymotion-iskeyword-w) + nmap b (easymotion-iskeyword-b) + call EasyMotion#init() + + call vspec#customize_matcher('to_cursor', s:to_cursor) + end + + after + close! + end + + it 'Simple test to check setup of this test' + call AddLine('word') + Expect CompareMovements('w', '\wa', 0) == 0 + "Expect CompareMovements('b', '\ba', 1) == 0 + end + + "it 'Loop through hand crafted text with rare cases' + " " Hand crafted text with rare cases + " call AddLine('scriptencoding utf-8') + " call AddLine('Test case [ ') + " call AddLine('s! ') + " Expect CompareMovements('w', '\wa', 0) == 0 + " Expect CompareMovements('b', '\ba', 1) == 0 + "end + + "it 'Loop through Vim help buffer and compare movements' + " help motion.txt + " Expect expand('%:t') ==# 'motion.txt' + " "Expect CompareMovements('w', '\wa', 0) == 0 + " "Expect CompareMovements('b', '\ba', 1) == 0 + "end + +end +"}}} + +" __END__ {{{ +" vim: fdm=marker:et:ts=4:sw=4:sts=4 +" }}} diff --git a/t/easymotion_spec.vim b/t/easymotion_spec.vim index 8673f3a..df808a3 100644 --- a/t/easymotion_spec.vim +++ b/t/easymotion_spec.vim @@ -217,7 +217,7 @@ describe 'Default settings' "}}} end - it 'provide default mappings for regrex motion' + it 'provide default mappings for regex motion' "(is_visual, direction) " direction: " - 0: forward @@ -1411,8 +1411,8 @@ describe 'Word motion' close! end - " Default word motion {{ - it 'Default word motion' + " Word motion {{ + it 'Word motion' normal! 0 let l = line('.') Expect CursorPos() == [l,1,'p'] @@ -1430,7 +1430,7 @@ describe 'Word motion' normal bh Expect CursorPos() == [l,1,'p'] end - "}}} + "}} end describe 'Verbose'