Skip to content

Commit 649120e

Browse files
committed
Merge pull request #276 from YggdrasiI/master
Change of iskeyword-regexes and test file for motions
2 parents 33f141c + fccf990 commit 649120e

File tree

3 files changed

+306
-8
lines changed

3 files changed

+306
-8
lines changed

autoload/EasyMotion.vim

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,13 +181,21 @@ function! EasyMotion#WB(visualmode, direction) " {{{
181181
endfunction " }}}
182182
function! EasyMotion#WBW(visualmode, direction) " {{{
183183
let s:current.is_operator = mode(1) ==# 'no' ? 1: 0
184-
call s:EasyMotion('\(\(^\|\s\)\@<=\S\|^$\)', a:direction, a:visualmode ? visualmode() : '', 0)
184+
let regex_without_file_ends = '\v(^|\s)\zs\S|^$'
185+
let regex = l:regex_without_file_ends
186+
\ . (a:direction == 1 ? '' : '|%$')
187+
\ . (a:direction == 0 ? '' : '|%^')
188+
call s:EasyMotion(l:regex, a:direction, a:visualmode ? visualmode() : '', 0)
185189
return s:EasyMotion_is_cancelled
186190
endfunction " }}}
187191
function! EasyMotion#WBK(visualmode, direction) " {{{
188192
" vim's iskeyword style word motion
189193
let s:current.is_operator = mode(1) ==# 'no' ? 1: 0
190-
call s:EasyMotion('\(\(\<\|\>\|\s\)\@<=\S\|^$\)', a:direction, a:visualmode ? visualmode() : '', 0)
194+
let regex_without_file_ends = '\v<|^\S|\s\zs\S|>\zs\S|^$'
195+
let regex = l:regex_without_file_ends
196+
\ . (a:direction == 1 ? '' : '|%$')
197+
\ . (a:direction == 0 ? '' : '|%^')
198+
call s:EasyMotion(l:regex, a:direction, a:visualmode ? visualmode() : '', 0)
191199
return s:EasyMotion_is_cancelled
192200
endfunction " }}}
193201
function! EasyMotion#E(visualmode, direction) " {{{
@@ -199,14 +207,30 @@ endfunction " }}}
199207
function! EasyMotion#EW(visualmode, direction) " {{{
200208
let s:current.is_operator = mode(1) ==# 'no' ? 1: 0
201209
let is_inclusive = mode(1) ==# 'no' ? 1 : 0
202-
call s:EasyMotion('\(\S\(\s\|$\)\|^$\)', a:direction, a:visualmode ? visualmode() : '', is_inclusive)
210+
" Note: The stopping positions for 'E' and 'gE' differs. Thus, the regex
211+
" for direction==2 cannot be the same in both directions. This will be
212+
" ignored.
213+
let regex_stub = '\v\S(\s|$)'
214+
let regex = l:regex_stub
215+
\ . (a:direction == 0 ? '' : '|^$|%^')
216+
\ . (a:direction == 1 ? '' : '|%$')
217+
call s:EasyMotion(l:regex, a:direction, a:visualmode ? visualmode() : '', 0)
203218
return s:EasyMotion_is_cancelled
204219
endfunction " }}}
205220
function! EasyMotion#EK(visualmode, direction) " {{{
206221
" vim's iskeyword style word motion
207222
let s:current.is_operator = mode(1) ==# 'no' ? 1: 0
208223
let is_inclusive = mode(1) ==# 'no' ? 1 : 0
209-
call s:EasyMotion('\(\S\(\>\|\<\|\s\)\@=\|^$\)', a:direction, a:visualmode ? visualmode() : '', is_inclusive)
224+
" Note: The stopping positions for 'e' and 'ge' differs. Thus, the regex
225+
" for direction==2 cannot be the same in both directions. This will be
226+
" ignored.
227+
let regex_stub = '\v.\ze>|\S\ze\s*$|\S\ze\s|\k\zs>\S\ze|\S<'
228+
let regex = l:regex_stub
229+
\ . (a:direction == 0 ? '' : '|^$|%^')
230+
\ . (a:direction == 1 ? '' : '|%$')
231+
call s:EasyMotion(l:regex, a:direction, a:visualmode ? visualmode() : '', 0)
232+
233+
210234
return s:EasyMotion_is_cancelled
211235
endfunction " }}}
212236
" -- JK Motion ---------------------------

t/compare_movements_spec.vim

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
"=============================================================================
2+
" FILE: t/compare_movements_spec.vim
3+
" AUTHOR: YggdrasiI
4+
" Test: https://github.com/kana/vim-vspec
5+
" Description: EasyMotion keyword movement test with vim-vspec
6+
" License: MIT license {{{
7+
" Permission is hereby granted, free of charge, to any person obtaining
8+
" a copy of this software and associated documentation files (the
9+
" "Software"), to deal in the Software without restriction, including
10+
" without limitation the rights to use, copy, modify, merge, publish,
11+
" distribute, sublicense, and/or sell copies of the Software, and to
12+
" permit persons to whom the Software is furnished to do so, subject to
13+
" the following conditions:
14+
"
15+
" The above copyright notice and this permission notice shall be included
16+
" in all copies or substantial portions of the Software.
17+
"
18+
" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19+
" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20+
" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21+
" IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22+
" CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23+
" TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24+
" SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25+
" }}}
26+
"=============================================================================
27+
28+
" Setup {{{
29+
let s:root_dir = matchstr(system('git rev-parse --show-cdup'), '[^\n]\+')
30+
31+
" The consumed time depends from the length of the text and could be really high
32+
" on vimdoc pages. (See it 'Loop through Vim help buffer and compare movements')
33+
" Reduce this value to stop CompareMovements(...) before it reached the end of the
34+
" buffer.
35+
let s:maximal_number_of_compared_movments = 10000
36+
execute 'set' 'rtp +=./'.s:root_dir
37+
runtime! plugin/EasyMotion.vim
38+
" }}}
39+
40+
" Functions for Test {{{
41+
function! AddLine(str)
42+
put =a:str
43+
endfunction
44+
45+
function! CursorPos()
46+
return [line('.'), col('.'), getline('.')[col('.')-1]]
47+
endfunction
48+
49+
" Nested normal to avoid throwing readonly errors. They abort the testing.
50+
function TryNormal(str)
51+
try
52+
exec 'normal ' . a:str
53+
catch /^Vim\%((\a\+)\)\=:E21/
54+
endtry
55+
return 0
56+
endfunction
57+
58+
let s:to_cursor = {}
59+
function! s:to_cursor.match(actual, expected)
60+
return a:actual == a:expected
61+
endfunction
62+
63+
" Add metadata about failure.
64+
function! s:to_cursor.failure_message_for_should(actual, expected)
65+
Expect a:actual[0] > 0
66+
Expect a:expected[0] > 0
67+
Expect a:actual[0] <= getpos('$')[1]
68+
Expect a:expected[0] <= getpos('$')[1]
69+
Expect a:actual[1] > 0
70+
Expect a:expected[1] > 0
71+
72+
let line1 = getline(a:actual[0])
73+
let line2 = getline(a:expected[0])
74+
" Change char on cursor to '█'.
75+
let line1 = strpart(l:line1, 0, a:actual[1]-1)
76+
\ . ''
77+
\ . strpart(l:line1, a:actual[1])
78+
let line2 = strpart(l:line2, 0, a:expected[1]-1)
79+
\ . ''
80+
\ . strpart(l:line2, a:expected[1])
81+
" Separation of both cases with \n would be nice, but
82+
" vim-vspec allow oneliners as return string, only.
83+
let msg = 'Line ' . string(a:actual[0]) . ": '" . l:line1
84+
\ . "',\x09\x09 Line " . string(a:expected[0]) . ": '" . l:line2 . "'\x0a"
85+
return l:msg
86+
endfunction
87+
88+
function! CompareMovements(movement1, movement2, backward)
89+
let jumpmarks = [
90+
\ [a:movement1, []],
91+
\ [a:movement2, []],
92+
\ ]
93+
94+
" Loop through current buffer in both variants {{
95+
for [l:handler, l:list] in l:jumpmarks
96+
if a:backward == 1
97+
let last_line = line('$')
98+
let last_char = len(getline(l:last_line))
99+
call cursor(l:last_line, l:last_char)
100+
else
101+
call cursor([1,1])
102+
endif
103+
104+
let lastpos = [0,0]
105+
106+
" Centralize line. Otherwise, Easymotion functions aborts
107+
" at the end of the (virtual) window.
108+
call TryNormal('zz')
109+
call TryNormal(l:handler)
110+
let curpos = getpos(".")[1:2]
111+
112+
while l:lastpos != l:curpos
113+
let list += [l:curpos]
114+
let lastpos = l:curpos
115+
call TryNormal('zz')
116+
call TryNormal(l:handler)
117+
let curpos = getpos(".")[1:2]
118+
" Abort after a fixed number of steps.
119+
if len(l:list) > s:maximal_number_of_compared_movments
120+
break
121+
endif
122+
endwhile
123+
endfor
124+
" }}
125+
126+
" The resulting lists are stored in l:jumpmarks[*][1], now.
127+
let [l:cursor_positions1, l:cursor_positions2] = [ l:jumpmarks[0][1], l:jumpmarks[1][1] ]
128+
129+
if l:cursor_positions1 == l:cursor_positions2
130+
return 0
131+
endif
132+
133+
" Search for first unmatching position. {{
134+
let index = 0
135+
let len = min([len(l:cursor_positions2), len(l:cursor_positions1)])
136+
while l:index < l:len
137+
Expect l:cursor_positions2[l:index] to_cursor l:cursor_positions1[l:index]
138+
let index += 1
139+
endwhile
140+
141+
" Collision with begin or end of file or while loop aborts to early.
142+
if a:backward == 1
143+
Expect join([a:movement2, ': File begin reached after ', len(l:cursor_positions2), ' steps.'])
144+
\ == join([a:movement1, ': File begin reached after ', len(l:cursor_positions1), ' steps.'])
145+
else
146+
Expect l:cursor_positions2[l:index-1] to_cursor l:cursor_positions1[l:index]
147+
Expect join([a:movement2, ': File end reached after ', len(l:cursor_positions2), ' steps.'])
148+
\ == join([a:movement1, ': File end reached after ', len(l:cursor_positions1), ' steps.'])
149+
endif
150+
" }}
151+
152+
return -1
153+
endfunction
154+
155+
" Hand crafted text with rare cases
156+
function! InsertTestText1()
157+
158+
" Blanks at document begin
159+
call AddLine('')
160+
call AddLine(' ')
161+
call AddLine('')
162+
163+
call AddLine('scriptencoding utf-8')
164+
165+
" '^\s*[not-\k]'-case
166+
call AddLine('!foo')
167+
call AddLine(' !bar')
168+
169+
call AddLine('<!{}>s! ')
170+
171+
" Blanks at document end
172+
call AddLine('')
173+
call AddLine(' ')
174+
call AddLine('')
175+
endfunction
176+
177+
"}}}
178+
179+
"Keyword word motion {{{
180+
describe 'Keyword word motion'
181+
before
182+
new
183+
resize 10
184+
nmap a <Nop>
185+
let g:EasyMotion_keys = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
186+
let g:EasyMotion_maximal_jumpmarks = 2 " Error for value 1 unanalyzed.
187+
nmap <Leader>w <Plug>(easymotion-iskeyword-w)
188+
nmap <Leader>b <Plug>(easymotion-iskeyword-b)
189+
nmap <Leader>e <Plug>(easymotion-iskeyword-e)
190+
nmap <Leader>ge <Plug>(easymotion-iskeyword-ge)
191+
nmap <Leader>W <Plug>(easymotion-W)
192+
nmap <Leader>B <Plug>(easymotion-B)
193+
nmap <Leader>E <Plug>(easymotion-E)
194+
nmap <Leader>gE <Plug>(easymotion-gE)
195+
call EasyMotion#init()
196+
call vspec#customize_matcher('to_cursor', s:to_cursor)
197+
end
198+
199+
after
200+
close!
201+
end
202+
203+
it 'Simple test to check setup of this test'
204+
" Check if a is remapped to <Nop> to avoid start of insert mode.
205+
normal aa\<Esc>
206+
Expect getline(1) == ''
207+
208+
call AddLine('word')
209+
Expect CompareMovements('w', 'w', 0) == 0
210+
Expect CompareMovements('w', '\wa', 0) == 0
211+
Expect CompareMovements('b', '\ba', 1) == 0
212+
Expect CompareMovements('e', '\ea', 0) == 0
213+
Expect CompareMovements('ge', '\gea', 1) == 0
214+
Expect CompareMovements('W', '\Wa', 0) == 0
215+
Expect CompareMovements('B', '\Ba', 1) == 0
216+
Expect CompareMovements('E', '\Ea', 0) == 0
217+
Expect CompareMovements('gE', '\gEa', 1) == 0
218+
end
219+
220+
it 'w'
221+
call InsertTestText1()
222+
Expect CompareMovements('w', '\wa', 0) == 0
223+
end
224+
225+
it 'b'
226+
call InsertTestText1()
227+
Expect CompareMovements('b', '\ba', 1) == 0
228+
end
229+
230+
it 'e'
231+
call InsertTestText1()
232+
Expect CompareMovements('e', '\ea', 0) == 0
233+
end
234+
235+
it 'ge'
236+
call InsertTestText1()
237+
Expect CompareMovements('ge', '\gea', 1) == 0
238+
end
239+
240+
it 'W'
241+
call InsertTestText1()
242+
Expect CompareMovements('W', 'W', 0) == 0
243+
end
244+
245+
it 'B'
246+
call InsertTestText1()
247+
Expect CompareMovements('B', 'B', 1) == 0
248+
end
249+
250+
it 'E'
251+
call InsertTestText1()
252+
Expect CompareMovements('E', 'E', 0) == 0
253+
end
254+
255+
it 'gE'
256+
call InsertTestText1()
257+
Expect CompareMovements('gE', 'gE', 1) == 0
258+
end
259+
260+
" Really time consuming test...
261+
"it 'Loop through Vim help buffer and compare movements'
262+
" help motion.txt
263+
" Expect expand('%:t') ==# 'motion.txt'
264+
" "Optional: Copy text into editable buffer
265+
" exec "normal! Gygg\<C-W>cP"
266+
" Expect CompareMovements('w', '\wa', 0) == 0
267+
"end
268+
269+
end
270+
"}}}
271+
272+
" __END__ {{{
273+
" vim: fdm=marker:et:ts=4:sw=4:sts=4
274+
" }}}

t/easymotion_spec.vim

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ describe 'Default settings'
217217
"}}}
218218
end
219219

220-
it 'provide default <Plug> mappings for regrex motion'
220+
it 'provide default <Plug> mappings for regex motion'
221221
"(is_visual, direction)
222222
" direction:
223223
" - 0: forward
@@ -1411,8 +1411,8 @@ describe 'Word motion'
14111411
close!
14121412
end
14131413

1414-
" Default word motion {{
1415-
it 'Default word motion'
1414+
" Word motion {{
1415+
it 'Word motion'
14161416
normal! 0
14171417
let l = line('.')
14181418
Expect CursorPos() == [l,1,'p']
@@ -1430,7 +1430,7 @@ describe 'Word motion'
14301430
normal bh
14311431
Expect CursorPos() == [l,1,'p']
14321432
end
1433-
"}}}
1433+
"}}
14341434
end
14351435

14361436
describe 'Verbose'

0 commit comments

Comments
 (0)