Skip to content

Commit 65ee835

Browse files
committed
Merge branch 'experiment-preserve-syntax'
2 parents d157c4d + 74fc979 commit 65ee835

File tree

3 files changed

+169
-76
lines changed

3 files changed

+169
-76
lines changed

autoload/EasyMotion/overwin.vim

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ function! EasyMotion#overwin#move(pattern) abort
1111
\ },
1212
\ 'jump_first_target_keys':
1313
\ (g:EasyMotion_enter_jump_first ? ["\<CR>"] : []) +
14-
\ (g:EasyMotion_space_jump_first ? ["\<Space>"] : [])
14+
\ (g:EasyMotion_space_jump_first ? ["\<Space>"] : []),
15+
\ 'do_shade': g:EasyMotion_do_shade,
1516
\ })
1617
endfunction
1718

autoload/vital/_easymotion/HitAHint/Motion.vim

Lines changed: 166 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,21 @@ let s:TRUE = !0
2020
let s:FALSE = 0
2121
let s:DIRECTION = {'forward': 0, 'backward': 1}
2222

23+
" Check Vim version
24+
function! s:has_patch(major, minor, patch) abort
25+
let l:version = (a:major * 100 + a:minor)
26+
return has('patch-' . a:major . '.' . a:minor . '.' . a:patch) ||
27+
\ (v:version > l:version) ||
28+
\ (v:version == l:version && 'patch' . a:patch)
29+
endfunction
30+
31+
" matchadd('Conceal', {pattern}, {priority}, -1, {'conceal': {char}}}) can
32+
" highlight pattern and conceal target correctly even if the target is keyword
33+
" characters.
34+
" - http://ftp.vim.org/vim/patches/7.4/7.4.792
35+
" - https://groups.google.com/forum/#!searchin/vim_dev/matchadd$20conceal/vim_dev/8bKa98GhHdk/VOzIBhd1m8YJ
36+
let s:can_preserve_syntax = s:has_patch(7, 4, 792)
37+
2338
" s:move() moves cursor over/accross window with Hit-A-Hint feature like
2439
" vim-easymotion
2540
" @param {dict} config
@@ -54,6 +69,7 @@ let s:overwin = {
5469
\ 'target': 'HitAHintTarget',
5570
\ },
5671
\ 'jump_first_target_keys': [],
72+
\ 'do_shade': s:TRUE,
5773
\ }
5874
\ }
5975

@@ -106,7 +122,27 @@ function! s:overwin.select_winpos(winnr2poses, keys) abort
106122
if self.config.auto_land && len(wposes) is# 1
107123
return wposes[0]
108124
endif
109-
return self.choose_prompt(s:Hint.create(wposes, a:keys))
125+
call self.set_options()
126+
try
127+
return self.choose_prompt(s:Hint.create(wposes, a:keys))
128+
finally
129+
call self.restore_options()
130+
endtry
131+
endfunction
132+
133+
function! s:overwin.set_options() abort
134+
" s:move_to_win() takes long time if 'foldmethod' == 'syntax' or 'expr'
135+
let self.save_foldmethod = {}
136+
for winnr in range(1, winnr('$'))
137+
let self.save_foldmethod[winnr] = getwinvar(winnr, '&foldmethod')
138+
call setwinvar(winnr, '&foldmethod', 'manual')
139+
endfor
140+
endfunction
141+
142+
function! s:overwin.restore_options() abort
143+
for winnr in range(1, winnr('$'))
144+
call setwinvar(winnr, '&foldmethod', self.save_foldmethod[winnr])
145+
endfor
110146
endfunction
111147

112148
" s:wpos_to_hint() returns dict whose key is position with window and whose
@@ -186,7 +222,7 @@ function! s:overwin.choose_prompt(hint_dict) abort
186222
let c = toupper(c)
187223
endif
188224
catch
189-
echo v:exception
225+
echo v:throwpoint . ':' . v:exception
190226
return -1
191227
finally
192228
call hinter.after()
@@ -230,21 +266,30 @@ let s:Hinter = {
230266
function! s:Hinter.new(hint_dict, config) abort
231267
let s = deepcopy(self)
232268
let s.config = a:config
233-
let win2pos2hint = s:create_win2pos2hint(a:hint_dict)
234-
let s.winnrs = map(keys(win2pos2hint), 'str2nr(v:val)')
235-
let s.win2pos2hint = win2pos2hint
236-
let s.w2l2c2h = s:win2pos2hint_to_w2l2c2h(win2pos2hint)
237-
call s._save_lines()
269+
call s.init(a:hint_dict)
238270
return s
239271
endfunction
240272

273+
function! s:Hinter.init(hint_dict) abort
274+
let win2pos2hint = s:create_win2pos2hint(a:hint_dict)
275+
let self.winnrs = sort(map(keys(win2pos2hint), 'str2nr(v:val)'))
276+
let self.win2pos2hint = win2pos2hint
277+
let self.w2l2c2h = s:win2pos2hint_to_w2l2c2h(win2pos2hint)
278+
let self.hl_target_ids = {}
279+
for winnr in self.winnrs
280+
let self.hl_target_ids[winnr] = []
281+
endfor
282+
call self._save_lines()
283+
endfunction
284+
241285
function! s:Hinter.before() abort
242-
call self.modify_env()
286+
let self.highlight_id_cursor = matchadd('Cursor', '\%#', 101)
287+
call self.save_options()
243288
call self.disable_conceal_in_other_win()
244289
endfunction
245290

246291
function! s:Hinter.after() abort
247-
call self.restore_lines()
292+
call matchdelete(self.highlight_id_cursor)
248293
call self.restore_env()
249294
call self.restore_conceal_in_other_win()
250295
endfunction
@@ -265,83 +310,94 @@ function! s:Hinter._save_lines() abort
265310
endtry
266311
endfunction
267312

268-
function! s:Hinter.restore_lines() abort
269-
let nr = winnr()
270-
try
271-
for [winnr, lnum2line] in items(self.save_lines)
272-
call s:move_to_win(winnr)
273-
for [lnum, line] in items(lnum2line)
274-
call s:setline(lnum, line)
275-
endfor
276-
endfor
277-
finally
278-
call s:move_to_win(nr)
279-
endtry
313+
function! s:Hinter.restore_lines_for_win(winnr) abort
314+
let lnum2line = self.save_lines[a:winnr]
315+
for [lnum, line] in items(lnum2line)
316+
call s:setline(lnum, line)
317+
endfor
280318
endfunction
281319

282-
function! s:Hinter.modify_env() abort
283-
let nr = winnr()
284-
try
285-
let self.highlight_id_cursor = matchadd('Cursor', '\%#', 1000001)
286-
for winnr in self.winnrs
287-
call s:move_to_win(winnr)
288-
let self.save_conceal = s:PHighlight.get('Conceal')
289-
let self.save_syntax[winnr] = &syntax
290-
let self.save_conceallevel[winnr] = &l:conceallevel
291-
let self.save_concealcursor[winnr] = &l:concealcursor
292-
let self.save_modified[winnr] = &l:modified
293-
let self.save_modifiable[winnr] = &l:modifiable
294-
let self.save_readonly[winnr] = &l:readonly
320+
function! s:Hinter.save_options() abort
321+
for winnr in self.winnrs
322+
let self.save_syntax[winnr] = getwinvar(winnr, '&syntax')
323+
let self.save_conceallevel[winnr] = getwinvar(winnr, '&conceallevel')
324+
let self.save_concealcursor[winnr] = getwinvar(winnr, '&concealcursor')
325+
let self.save_modified[winnr] = getwinvar(winnr, '&modified')
326+
let self.save_modifiable[winnr] = getwinvar(winnr, '&modifiable')
327+
let self.save_readonly[winnr] = getwinvar(winnr, '&readonly')
328+
endfor
329+
endfunction
330+
331+
function! s:Hinter.restore_options() abort
332+
for winnr in self.winnrs
333+
call setwinvar(winnr, '&conceallevel', self.save_conceallevel[winnr])
334+
call setwinvar(winnr, '&concealcursor', self.save_concealcursor[winnr])
335+
call setwinvar(winnr, '&modified', self.save_modified[winnr])
336+
call setwinvar(winnr, '&modifiable', self.save_modifiable[winnr])
337+
call setwinvar(winnr, '&readonly', self.save_readonly[winnr])
338+
endfor
339+
endfunction
295340

296-
let self.save_undo[winnr] = s:undo_lock.save()
341+
function! s:Hinter.modify_env_for_win(winnr) abort
342+
let self.save_conceal = s:PHighlight.get('Conceal')
343+
let self.save_undo[a:winnr] = s:undo_lock.save()
297344

298-
setlocal modifiable
299-
setlocal noreadonly
345+
setlocal modifiable
346+
setlocal noreadonly
300347

301-
ownsyntax overwin
348+
if !s:can_preserve_syntax
349+
ownsyntax overwin
350+
endif
351+
352+
setlocal conceallevel=2
353+
setlocal concealcursor=ncv
354+
355+
let self.highlight_ids[a:winnr] = get(self.highlight_ids, a:winnr, [])
356+
if self.config.do_shade
357+
if !s:can_preserve_syntax
302358
syntax clear
303-
setlocal conceallevel=2
304-
setlocal concealcursor=ncv
305-
execute 'highlight! link Conceal' self.config.highlight.target
359+
endif
360+
let self.highlight_ids[a:winnr] += [matchadd(self.config.highlight.shade, '\_.*', 100)]
361+
endif
306362

307-
let self.highlight_ids[winnr] = get(self.highlight_ids, winnr, [])
308-
let self.highlight_ids[winnr] += [matchadd(self.config.highlight.shade, '\_.*', 100)]
309-
endfor
310-
catch
311-
call s:throw(v:throwpoint . ' ' . v:exception)
312-
finally
313-
call s:move_to_win(nr)
314-
endtry
363+
" XXX: other plugins specific handling
364+
if getbufvar('%', 'indentLine_enabled', 0)
365+
silent! syntax clear IndentLine
366+
endif
315367
endfunction
316368

317369
function! s:Hinter.restore_env() abort
370+
call s:PHighlight.set('Conceal', self.save_conceal)
318371
let nr = winnr()
319372
try
320-
call matchdelete(self.highlight_id_cursor)
321373
for winnr in self.winnrs
322374
call s:move_to_win(winnr)
323-
" Clear syntax defined by Hit-A-Hint motion before restoring syntax.
324-
syntax clear HitAHintTarget
325-
let &syntax = self.save_syntax[winnr]
326-
call s:PHighlight.set('Conceal', self.save_conceal)
327-
let &l:conceallevel = self.save_conceallevel[winnr]
328-
let &l:concealcursor = self.save_concealcursor[winnr]
375+
call self.restore_lines_for_win(winnr)
376+
call self.remove_hints(winnr)
329377

330-
call self.save_undo[winnr].restore()
378+
if !s:can_preserve_syntax && self.config.do_shade
379+
let &syntax = self.save_syntax[winnr]
380+
endif
331381

332-
let &l:modified = self.save_modified[winnr]
333-
let &l:modifiable = self.save_modifiable[winnr]
334-
let &l:readonly = self.save_readonly[winnr]
382+
call self.save_undo[winnr].restore()
335383

336384
for id in self.highlight_ids[winnr]
337385
call matchdelete(id)
338386
endfor
387+
388+
" XXX: other plugins specific handling
389+
if getbufvar('%', 'indentLine_enabled', 0) && exists(':IndentLinesEnable') is# 2
390+
call setbufvar('%', 'indentLine_enabled', 0)
391+
:IndentLinesEnable
392+
endif
339393
endfor
340394
catch
341395
call s:throw(v:throwpoint . ' ' . v:exception)
342396
finally
343397
call s:move_to_win(nr)
344398
endtry
399+
400+
call self.restore_options()
345401
endfunction
346402

347403
let s:undo_lock = {}
@@ -440,12 +496,24 @@ function! s:Hinter.show_hint() abort
440496
endfunction
441497

442498
function! s:Hinter._show_hint_for_win(winnr) abort
499+
call self.modify_env_for_win(a:winnr)
500+
501+
let hints = []
443502
for [lnum, col2hint] in items(self.w2l2c2h[a:winnr])
444-
call self._show_hint_for_line(a:winnr, lnum, col2hint)
503+
let hints += self._show_hint_for_line(a:winnr, lnum, col2hint)
504+
endfor
505+
" Restore syntax and show hints after replacing all lines for performance.
506+
if !self.config.do_shade
507+
let &l:syntax = self.save_syntax[a:winnr]
508+
endif
509+
execute 'highlight! link Conceal' self.config.highlight.target
510+
for [lnum, cnum, char] in hints
511+
call self.show_hint_pos(lnum, cnum, char, a:winnr)
445512
endfor
446513
endfunction
447514

448515
function! s:Hinter._show_hint_for_line(winnr, lnum, col2hint) abort
516+
let hints = [] " [lnum, cnum, char]
449517
let line = self.save_lines[a:winnr][a:lnum]
450518
let col_offset = 0
451519
let prev_cnum = -1
@@ -467,14 +535,15 @@ function! s:Hinter._show_hint_for_line(winnr, lnum, col2hint) abort
467535
endif
468536
let col_offset += offset
469537

470-
call s:show_hint_pos(a:lnum, col_num, hint[0])
538+
let hints = [[a:lnum, col_num, hint[0]]] + hints
471539
if len(hint) > 1
472-
call s:show_hint_pos(a:lnum, col_num + 1, hint[1])
540+
let hints = [[a:lnum, col_num + 1, hint[1]]] + hints
473541
endif
474542

475543
let prev_cnum = cnum
476544
endfor
477545
call s:setline(a:lnum, line)
546+
return hints
478547
endfunction
479548

480549
" ._replace_line_for_hint() replaces line to show hints.
@@ -489,11 +558,13 @@ endfunction
489558
function! s:Hinter._replace_line_for_hint(lnum, col_num, line, hint) abort
490559
let line = a:line
491560
let col_num = a:col_num
561+
let do_replace_target = !(self.config.do_shade || s:can_preserve_syntax)
492562
let target = matchstr(line, '\%' . col_num .'c.')
493563
" Append one space for empty line or match at end of line
494564
if target is# ''
495565
let hintwidth = strdisplaywidth(join(a:hint[:1], ''))
496-
let line .= repeat(' ', hintwidth)
566+
let char = do_replace_target ? ' ' : '.'
567+
let line .= repeat(char, hintwidth)
497568
return [line, hintwidth, 0]
498569
endif
499570

@@ -503,12 +574,20 @@ function! s:Hinter._replace_line_for_hint(lnum, col_num, line, hint) abort
503574
elseif strdisplaywidth(target) > 1
504575
let line = self._replace_text_to_space(line, a:lnum, col_num, strdisplaywidth(target))
505576
let offset = strdisplaywidth(target) - len(target)
577+
else
578+
if do_replace_target
579+
" The priority of :syn-cchar is always under the priority of keywords.
580+
" So, Hit-A-Hint replaces targets character with '.'.
581+
let space = '.'
582+
let line = substitute(line, '\%' . col_num . 'c.', space, '')
583+
let offset = len(space) - len(target)
584+
endif
506585
endif
507586

508587
let next_offset = 0
509-
if len(a:hint) > 1
510-
" pass [] as hint to stop recursion.
511-
let [line, next_offset, _] = self._replace_line_for_hint(a:lnum, col_num + offset + 1, line, [])
588+
if len(a:hint) > 1 && target isnot# "\t"
589+
" pass [' '] as hint to stop recursion.
590+
let [line, next_offset, _] = self._replace_line_for_hint(a:lnum, col_num + offset + 1, line, [' '])
512591
endif
513592
return [line, offset, next_offset]
514593
endfunction
@@ -526,6 +605,26 @@ function! s:Hinter._replace_text_to_space(line, lnum, col_num, len) abort
526605
return line
527606
endfunction
528607

608+
function! s:Hinter.show_hint_pos(lnum, cnum, char, winnr) abort
609+
let p = '\%'. a:lnum . 'l\%'. a:cnum . 'c.'
610+
if s:can_preserve_syntax
611+
let self.hl_target_ids[a:winnr] += [matchadd('Conceal', p, 101, -1, {'conceal': a:char})]
612+
else
613+
exec "syntax match HitAHintTarget '". p . "' contains=NONE containedin=.* conceal cchar=". a:char
614+
endif
615+
endfunction
616+
617+
function! s:Hinter.remove_hints(winnr) abort
618+
if s:can_preserve_syntax
619+
for id in self.hl_target_ids[a:winnr]
620+
call matchdelete(id)
621+
endfor
622+
else
623+
" Clear syntax defined by Hit-A-Hint motion before restoring syntax.
624+
syntax clear HitAHintTarget
625+
endif
626+
endfunction
627+
529628
" @param {number} col_num col_num is 1 origin like col()
530629
function! s:tab2spacelen(line, col_num) abort
531630
let before_line = a:col_num > 2 ? a:line[: a:col_num - 2]
@@ -665,11 +764,6 @@ function! s:wincall(func, arglist, ...) abort
665764
return r
666765
endfunction
667766

668-
function! s:show_hint_pos(lnum, cnum, char) abort
669-
let p = '\%'. a:lnum . 'l\%'. a:cnum . 'c.'
670-
exec "syntax match HitAHintTarget '". p . "' conceal cchar=". a:char
671-
endfunction
672-
673767
" deepextend (nest: 1)
674768
function! s:deepextend(expr1, expr2) abort
675769
let expr2 = copy(a:expr2)

doc/easymotion.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,7 @@ For ovrewin n-character find motions~
332332
Since archtecture of overwin motions is different from other easymotion
333333
motions, there are some limitations.
334334

335-
1. |EasyMotion_do_shade| by default and currently you cannot turned off
336-
this option.
337-
2. Highlight for target is always EasyMotionTarget (|EasyMotion_highlight|)
335+
1. Highlight for target is always EasyMotionTarget (|EasyMotion_highlight|)
338336
even for two key targets.
339337

340338
Jump To Anywhere ~

0 commit comments

Comments
 (0)