@@ -20,6 +20,21 @@ let s:TRUE = !0
2020let s: FALSE = 0
2121let 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
110146endfunction
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 = {
230266function ! 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
239271endfunction
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+
241285function ! 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 ()
244289endfunction
245290
246291function ! 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 ()
250295endfunction
@@ -265,83 +310,94 @@ function! s:Hinter._save_lines() abort
265310 endtry
266311endfunction
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
280318endfunction
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
315367endfunction
316368
317369function ! 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 ()
345401endfunction
346402
347403let s: undo_lock = {}
@@ -440,12 +496,24 @@ function! s:Hinter.show_hint() abort
440496endfunction
441497
442498function ! 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
446513endfunction
447514
448515function ! 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
478547endfunction
479548
480549" ._replace_line_for_hint() replaces line to show hints.
@@ -489,11 +558,13 @@ endfunction
489558function ! 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]
514593endfunction
@@ -526,6 +605,26 @@ function! s:Hinter._replace_text_to_space(line, lnum, col_num, len) abort
526605 return line
527606endfunction
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()
530629function ! 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
666765endfunction
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)
674768function ! s: deepextend (expr1, expr2) abort
675769 let expr2 = copy (a: expr2 )
0 commit comments