@@ -20,6 +20,21 @@ let s:TRUE = !0
20
20
let s: FALSE = 0
21
21
let s: DIRECTION = {' forward' : 0 , ' backward' : 1 }
22
22
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
+
23
38
" s:move() moves cursor over/accross window with Hit-A-Hint feature like
24
39
" vim-easymotion
25
40
" @param {dict} config
@@ -54,6 +69,7 @@ let s:overwin = {
54
69
\ ' target' : ' HitAHintTarget' ,
55
70
\ },
56
71
\ ' jump_first_target_keys' : [],
72
+ \ ' do_shade' : s: TRUE ,
57
73
\ }
58
74
\ }
59
75
@@ -106,7 +122,27 @@ function! s:overwin.select_winpos(winnr2poses, keys) abort
106
122
if self .config.auto_land && len (wposes) is # 1
107
123
return wposes[0 ]
108
124
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
110
146
endfunction
111
147
112
148
" 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
186
222
let c = toupper (c )
187
223
endif
188
224
catch
189
- echo v: exception
225
+ echo v: throwpoint . ' : ' . v: exception
190
226
return -1
191
227
finally
192
228
call hinter.after ()
@@ -230,21 +266,30 @@ let s:Hinter = {
230
266
function ! s: Hinter .new (hint_dict, config) abort
231
267
let s = deepcopy (self )
232
268
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 )
238
270
return s
239
271
endfunction
240
272
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
+
241
285
function ! s: Hinter .before () abort
242
- call self .modify_env ()
286
+ let self .highlight_id_cursor = matchadd (' Cursor' , ' \%#' , 101 )
287
+ call self .save_options ()
243
288
call self .disable_conceal_in_other_win ()
244
289
endfunction
245
290
246
291
function ! s: Hinter .after () abort
247
- call self .restore_lines ( )
292
+ call matchdelete ( self .highlight_id_cursor )
248
293
call self .restore_env ()
249
294
call self .restore_conceal_in_other_win ()
250
295
endfunction
@@ -265,83 +310,94 @@ function! s:Hinter._save_lines() abort
265
310
endtry
266
311
endfunction
267
312
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
280
318
endfunction
281
319
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
295
340
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 ()
297
344
298
- setlocal modifiable
299
- setlocal noreadonly
345
+ setlocal modifiable
346
+ setlocal noreadonly
300
347
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
302
358
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
306
362
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
315
367
endfunction
316
368
317
369
function ! s: Hinter .restore_env () abort
370
+ call s: PHighlight .set (' Conceal' , self .save_conceal)
318
371
let nr = winnr ()
319
372
try
320
- call matchdelete (self .highlight_id_cursor)
321
373
for winnr in self .winnrs
322
374
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 )
329
377
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
331
381
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 ()
335
383
336
384
for id in self .highlight_ids[winnr ]
337
385
call matchdelete (id)
338
386
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
339
393
endfor
340
394
catch
341
395
call s: throw (v: throwpoint . ' ' . v: exception )
342
396
finally
343
397
call s: move_to_win (nr)
344
398
endtry
399
+
400
+ call self .restore_options ()
345
401
endfunction
346
402
347
403
let s: undo_lock = {}
@@ -440,12 +496,24 @@ function! s:Hinter.show_hint() abort
440
496
endfunction
441
497
442
498
function ! s: Hinter ._show_hint_for_win (winnr ) abort
499
+ call self .modify_env_for_win (a: winnr )
500
+
501
+ let hints = []
443
502
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 )
445
512
endfor
446
513
endfunction
447
514
448
515
function ! s: Hinter ._show_hint_for_line (winnr , lnum, col2hint) abort
516
+ let hints = [] " [lnum, cnum, char]
449
517
let line = self .save_lines[a: winnr ][a: lnum ]
450
518
let col_offset = 0
451
519
let prev_cnum = -1
@@ -467,14 +535,15 @@ function! s:Hinter._show_hint_for_line(winnr, lnum, col2hint) abort
467
535
endif
468
536
let col_offset += offset
469
537
470
- call s: show_hint_pos ( a: lnum , col_num, hint[0 ])
538
+ let hints = [[ a: lnum , col_num, hint[0 ]]] + hints
471
539
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
473
541
endif
474
542
475
543
let prev_cnum = cnum
476
544
endfor
477
545
call s: setline (a: lnum , line )
546
+ return hints
478
547
endfunction
479
548
480
549
" ._replace_line_for_hint() replaces line to show hints.
@@ -489,11 +558,13 @@ endfunction
489
558
function ! s: Hinter ._replace_line_for_hint (lnum, col_num, line , hint) abort
490
559
let line = a: line
491
560
let col_num = a: col_num
561
+ let do_replace_target = ! (self .config.do_shade || s: can_preserve_syntax )
492
562
let target = matchstr (line , ' \%' . col_num .' c.' )
493
563
" Append one space for empty line or match at end of line
494
564
if target is # ' '
495
565
let hintwidth = strdisplaywidth (join (a: hint [:1 ], ' ' ))
496
- let line .= repeat (' ' , hintwidth)
566
+ let char = do_replace_target ? ' ' : ' .'
567
+ let line .= repeat (char, hintwidth)
497
568
return [line , hintwidth, 0 ]
498
569
endif
499
570
@@ -503,12 +574,20 @@ function! s:Hinter._replace_line_for_hint(lnum, col_num, line, hint) abort
503
574
elseif strdisplaywidth (target) > 1
504
575
let line = self ._replace_text_to_space (line , a: lnum , col_num, strdisplaywidth (target))
505
576
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
506
585
endif
507
586
508
587
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 , [' ' ])
512
591
endif
513
592
return [line , offset, next_offset]
514
593
endfunction
@@ -526,6 +605,26 @@ function! s:Hinter._replace_text_to_space(line, lnum, col_num, len) abort
526
605
return line
527
606
endfunction
528
607
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
+
529
628
" @param {number} col_num col_num is 1 origin like col()
530
629
function ! s: tab2spacelen (line , col_num) abort
531
630
let before_line = a: col_num > 2 ? a: line [: a: col_num - 2 ]
@@ -665,11 +764,6 @@ function! s:wincall(func, arglist, ...) abort
665
764
return r
666
765
endfunction
667
766
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
-
673
767
" deepextend (nest: 1)
674
768
function ! s: deepextend (expr1, expr2) abort
675
769
let expr2 = copy (a: expr2 )
0 commit comments