Skip to content

Commit 45ce308

Browse files
committed
Fix ambi_pair both sides match in '' phase search
Resolve the higher-level shortcoming of `sub_pair`: a pair consists of "begin items" and "end items" groups, but have no indication that they are serving the same purpose. Add a auto-increment `pair_id` to both side of groups. - Raise up the scope of `search_ctx` from `phased_search()` to `#search()`, let the context lives across phases. - When an end_item matched, remember the pair_id in context. Then for any `sub_pair` group, we can check if the opposite has already matched by the pair_id memo.
1 parent 81cc3b6 commit 45ce308

File tree

5 files changed

+44
-18
lines changed

5 files changed

+44
-18
lines changed

autoload/cycle.vim

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
" sub_pair
1111
" sub_pairs
1212
" ambi_pair
13+
" pair_id
1314
" end_with
1415
" begin_with
1516
" matcher
@@ -25,6 +26,7 @@
2526
" subp
2627

2728
let s:tick = 0
29+
let s:pair_id = 0
2830

2931
" }}} Constants
3032

@@ -140,6 +142,7 @@ function! cycle#search(class_name, ...) "{{{
140142
let direction = get(options, 'direction', 1)
141143
let l:count = get(options, 'count', 1)
142144
let matches = []
145+
let search_ctx = {}
143146
let cword = cycle#text#new_cword()
144147
let cchar = cycle#text#new_cchar()
145148

@@ -166,7 +169,7 @@ function! cycle#search(class_name, ...) "{{{
166169
endif
167170
endif
168171

169-
let phase_matches = s:phased_search(phase, groups, direction, l:count)
172+
let phase_matches = s:phased_search(phase, groups, direction, l:count, search_ctx)
170173

171174
if !empty(phase_matches)
172175
call extend(matches, phase_matches)
@@ -177,9 +180,8 @@ function! cycle#search(class_name, ...) "{{{
177180
endfunction "}}}
178181

179182

180-
function! s:phased_search(class_name, groups, direction, count) "{{{
183+
function! s:phased_search(class_name, groups, direction, count, search_ctx) "{{{
181184
let matches = []
182-
let search_ctx = {}
183185

184186
for group in a:groups
185187
if get(group, '_phase_matched', 0)
@@ -203,7 +205,7 @@ function! s:phased_search(class_name, groups, direction, count) "{{{
203205
break
204206
endif
205207

206-
let [index, ctext] = s:group_search(group, a:class_name, search_ctx)
208+
let [index, ctext] = s:group_search(group, a:class_name, a:search_ctx)
207209
if index >= 0
208210
if a:count == '*'
209211
" Grab all group items for CycleSelect
@@ -483,8 +485,9 @@ function! cycle#parse_group(group_attrs) "{{{
483485
endif
484486
" Note that the "end_items" (has `begin_with`) must go first, `ambi_pair`
485487
" relies on this order to make orphaned behave as the begin part.
486-
let end_group_attrs = [end_items, extend(deepcopy(options), {'begin_with': begin_items})]
487-
let begin_group_attrs = [begin_items, extend(deepcopy(options), {'end_with': end_items})]
488+
let s:pair_id += 1
489+
let end_group_attrs = [end_items, extend(deepcopy(options), {'begin_with': begin_items, 'pair_id': s:pair_id})]
490+
let begin_group_attrs = [begin_items, extend(deepcopy(options), {'end_with': end_items, 'pair_id': s:pair_id})]
488491
return [cycle#parse_group(end_group_attrs),
489492
\ cycle#parse_group(begin_group_attrs)]
490493
endif

autoload/cycle/matcher/default.vim

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ function! cycle#matcher#default#test(group, class_name, ctx) abort "{{{
1010
let index = -1
1111
let ctext = cycle#text#new_ctext(a:class_name)
1212

13+
" Skip a sub_pair group if its opposite group has already found
14+
if get(options, 'sub_pair', 0)
15+
let found_pairs = get(a:ctx, 'found_pairs', [])
16+
if index(found_pairs, get(options, 'pair_id')) > -1
17+
return [index, ctext]
18+
endif
19+
endif
20+
1321
for item in a:group.items
1422
if get(options, 'match_word') && a:class_name != 'w'
1523
continue
@@ -44,14 +52,16 @@ function! cycle#matcher#default#test(group, class_name, ctx) abort "{{{
4452

4553
let ambi_pair = get(options, 'ambi_pair')
4654
if !empty(ambi_pair) && index(ambi_pair, text) > -1
47-
" For begin pair, if there was no end pair found, accept it as orphan.
48-
let accept_orphan = !empty(get(options, 'end_with')) && index(get(a:ctx, 'ambi_pair_found', []), text) < 0
55+
" For begin pair, if there was no end pair found, we can accept it as orphan.
56+
" This relies on the assumption that "end_items" were defined first.
57+
let accept_orphan = !empty(get(options, 'end_with'))
4958

5059
if cycle#matcher#default#ambi_pair#test(text, options)
51-
let founds = get(a:ctx, 'ambi_pair_found', [])
52-
let founds = uniq(founds + [text])
53-
call extend(a:ctx, {'ambi_pair_found': founds}, 'force')
60+
let founds = get(a:ctx, 'found_pairs', [])
61+
let founds = uniq(founds + [get(options, 'pair_id', -1)])
62+
call extend(a:ctx, {'found_pairs': founds}, 'force')
5463
elseif !accept_orphan
64+
" Still accept it
5565
continue
5666
endif
5767
endif

doc/cycle.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ By default no options are set.
389389
"before_sub" (List) ~
390390
"after_sub" (List) ~
391391
"ambi_pair" (List) ~
392+
"pair_id" (Number) ~
392393
"restrict_cursor" (Number) ~
393394

394395
Internal usage only, don't set them.

test/group_definition.vimspec

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,24 @@ Describe Group definition
8787
Assert Equals(group, #{items: ['Yes', 'No'], options: #{match_case: 1}})
8888

8989
let groups = cycle#parse_group([['a:b', 'c:d'], 'sub_pairs'])
90+
let [g1, g2] = groups
91+
9092
Assert IsList(groups)
91-
Assert Equals(groups[0], #{items: ['b', 'd'], options: #{sub_pair: 1, begin_with: ['a', 'c']}})
92-
Assert Equals(groups[1], #{items: ['a', 'c'], options: #{sub_pair: 1, end_with: ['b', 'd']}})
93+
Assert Includes(g1, ['items', 'options'])
94+
Assert Includes(g2, ['items', 'options'])
95+
Assert Equals(g1['options']['sub_pair'], 1)
96+
Assert Equals(g2['options']['sub_pair'], 1)
97+
98+
Assert Includes(g1['options'], ['sub_pair', 'pair_id', 'begin_with'])
99+
Assert Includes(g2['options'], ['sub_pair', 'pair_id', 'end_with'])
100+
101+
Assert Equals(g1['items'], ['b', 'd'])
102+
Assert Equals(g1['options']['begin_with'], ['a', 'c'])
103+
104+
Assert Equals(g2['items'], ['a', 'c'])
105+
Assert Equals(g2['options']['end_with'], ['b', 'd'])
106+
107+
Assert Equals(g1['options']['pair_id'], g2['options']['pair_id'])
93108
End
94109

95110
It takes one-time groups

test/sub_pairs.vimspec

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,14 +174,11 @@ Describe Sub pairs
174174

175175
It from end, sub begin (potentially conflict in '' phase)
176176
let line = '" foo "'
177-
call setline(1, '" foo "')
177+
call setline(1, line)
178178
normal! $
179179

180180
execute 'normal \a'
181-
Assert Equals(getline(1), line, 'no change')
182-
Assert Exists('g:cycle_test_conflict.items')
183-
Assert Equals(g:cycle_test_conflict.items, ['`', '`'])
184-
" FIXME: this is not desired behavior
181+
Assert Equals(getline(1), '` foo `')
185182
End
186183

187184
It subs orphaned item even the opposite not found

0 commit comments

Comments
 (0)