Preserve syntax and support g:EasyMotion_do_shade for overwin motions

This commit is contained in:
haya14busa 2016-01-21 08:22:43 +09:00
parent aaa9a4ab95
commit 87e7b88bdd
2 changed files with 103 additions and 71 deletions

View File

@ -11,7 +11,8 @@ function! EasyMotion#overwin#move(pattern) abort
\ }, \ },
\ 'jump_first_target_keys': \ 'jump_first_target_keys':
\ (g:EasyMotion_enter_jump_first ? ["\<CR>"] : []) + \ (g:EasyMotion_enter_jump_first ? ["\<CR>"] : []) +
\ (g:EasyMotion_space_jump_first ? ["\<Space>"] : []) \ (g:EasyMotion_space_jump_first ? ["\<Space>"] : []),
\ 'do_shade': g:EasyMotion_do_shade,
\ }) \ })
endfunction endfunction

View File

@ -54,6 +54,7 @@ let s:overwin = {
\ 'target': 'HitAHintTarget', \ 'target': 'HitAHintTarget',
\ }, \ },
\ 'jump_first_target_keys': [], \ 'jump_first_target_keys': [],
\ 'do_shade': s:TRUE,
\ } \ }
\ } \ }
@ -106,7 +107,27 @@ function! s:overwin.select_winpos(winnr2poses, keys) abort
if self.config.auto_land && len(wposes) is# 1 if self.config.auto_land && len(wposes) is# 1
return wposes[0] return wposes[0]
endif endif
call self.set_options()
try
return self.choose_prompt(s:Hint.create(wposes, a:keys)) return self.choose_prompt(s:Hint.create(wposes, a:keys))
finally
call self.restore_options()
endtry
endfunction
function! s:overwin.set_options() abort
" s:move_to_win() takes long time if 'foldmethod' == 'syntax' or 'expr'
let self.save_foldmethod = {}
for winnr in range(1, winnr('$'))
let self.save_foldmethod[winnr] = getwinvar(winnr, '&foldmethod')
call setwinvar(winnr, '&foldmethod', 'manual')
endfor
endfunction
function! s:overwin.restore_options() abort
for winnr in range(1, winnr('$'))
call setwinvar(winnr, '&foldmethod', self.save_foldmethod[winnr])
endfor
endfunction endfunction
" s:wpos_to_hint() returns dict whose key is position with window and whose " s:wpos_to_hint() returns dict whose key is position with window and whose
@ -186,7 +207,7 @@ function! s:overwin.choose_prompt(hint_dict) abort
let c = toupper(c) let c = toupper(c)
endif endif
catch catch
echo v:exception echo v:throwpoint . ':' . v:exception
return -1 return -1
finally finally
call hinter.after() call hinter.after()
@ -231,7 +252,7 @@ function! s:Hinter.new(hint_dict, config) abort
let s = deepcopy(self) let s = deepcopy(self)
let s.config = a:config let s.config = a:config
let win2pos2hint = s:create_win2pos2hint(a:hint_dict) let win2pos2hint = s:create_win2pos2hint(a:hint_dict)
let s.winnrs = map(keys(win2pos2hint), 'str2nr(v:val)') let s.winnrs = sort(map(keys(win2pos2hint), 'str2nr(v:val)'))
let s.win2pos2hint = win2pos2hint let s.win2pos2hint = win2pos2hint
let s.w2l2c2h = s:win2pos2hint_to_w2l2c2h(win2pos2hint) let s.w2l2c2h = s:win2pos2hint_to_w2l2c2h(win2pos2hint)
call s._save_lines() call s._save_lines()
@ -239,12 +260,13 @@ function! s:Hinter.new(hint_dict, config) abort
endfunction endfunction
function! s:Hinter.before() abort function! s:Hinter.before() abort
call self.modify_env() let self.highlight_id_cursor = matchadd('Cursor', '\%#', 101)
call self.save_options()
call self.disable_conceal_in_other_win() call self.disable_conceal_in_other_win()
endfunction endfunction
function! s:Hinter.after() abort function! s:Hinter.after() abort
call self.restore_lines() call matchdelete(self.highlight_id_cursor)
call self.restore_env() call self.restore_env()
call self.restore_conceal_in_other_win() call self.restore_conceal_in_other_win()
endfunction endfunction
@ -265,75 +287,68 @@ function! s:Hinter._save_lines() abort
endtry endtry
endfunction endfunction
function! s:Hinter.restore_lines() abort function! s:Hinter.restore_lines_for_win(winnr) abort
let nr = winnr() let lnum2line = self.save_lines[a:winnr]
try
for [winnr, lnum2line] in items(self.save_lines)
call s:move_to_win(winnr)
for [lnum, line] in items(lnum2line) for [lnum, line] in items(lnum2line)
call s:setline(lnum, line) call s:setline(lnum, line)
endfor endfor
endfor
finally
call s:move_to_win(nr)
endtry
endfunction endfunction
function! s:Hinter.modify_env() abort function! s:Hinter.save_options() abort
let nr = winnr()
try
let self.highlight_id_cursor = matchadd('Cursor', '\%#', 1000001)
for winnr in self.winnrs for winnr in self.winnrs
call s:move_to_win(winnr) let self.save_syntax[winnr] = getwinvar(winnr, '&syntax')
let self.save_conceal = s:PHighlight.get('Conceal') let self.save_conceallevel[winnr] = getwinvar(winnr, '&conceallevel')
let self.save_syntax[winnr] = &syntax let self.save_concealcursor[winnr] = getwinvar(winnr, '&concealcursor')
let self.save_conceallevel[winnr] = &l:conceallevel let self.save_modified[winnr] = getwinvar(winnr, '&modified')
let self.save_concealcursor[winnr] = &l:concealcursor let self.save_modifiable[winnr] = getwinvar(winnr, '&modifiable')
let self.save_modified[winnr] = &l:modified let self.save_readonly[winnr] = getwinvar(winnr, '&readonly')
let self.save_modifiable[winnr] = &l:modifiable endfor
let self.save_readonly[winnr] = &l:readonly endfunction
let self.save_undo[winnr] = s:undo_lock.save() function! s:Hinter.restore_options() abort
for winnr in self.winnrs
call setwinvar(winnr, '&conceallevel', self.save_conceallevel[winnr])
call setwinvar(winnr, '&concealcursor', self.save_concealcursor[winnr])
call setwinvar(winnr, '&modified', self.save_modified[winnr])
call setwinvar(winnr, '&modifiable', self.save_modifiable[winnr])
call setwinvar(winnr, '&readonly', self.save_readonly[winnr])
endfor
endfunction
function! s:Hinter.modify_env_for_win(winnr) abort
let self.save_conceal = s:PHighlight.get('Conceal')
let self.save_undo[a:winnr] = s:undo_lock.save()
setlocal modifiable setlocal modifiable
setlocal noreadonly setlocal noreadonly
ownsyntax overwin ownsyntax overwin
syntax enable
" syntax clear
setlocal conceallevel=2 setlocal conceallevel=2
setlocal concealcursor=ncv setlocal concealcursor=ncv
execute 'highlight! link Conceal' self.config.highlight.target
let self.highlight_ids[winnr] = get(self.highlight_ids, winnr, []) let self.highlight_ids[a:winnr] = get(self.highlight_ids, a:winnr, [])
" let self.highlight_ids[winnr] += [matchadd(self.config.highlight.shade, '\_.*', 100)] if self.config.do_shade
endfor syntax clear
catch let self.highlight_ids[a:winnr] += [matchadd(self.config.highlight.shade, '\_.*', 100)]
call s:throw(v:throwpoint . ' ' . v:exception) endif
finally
call s:move_to_win(nr)
endtry
endfunction endfunction
function! s:Hinter.restore_env() abort function! s:Hinter.restore_env() abort
syntax clear HitAHintTarget
call s:PHighlight.set('Conceal', self.save_conceal)
let nr = winnr() let nr = winnr()
try try
call matchdelete(self.highlight_id_cursor)
for winnr in self.winnrs for winnr in self.winnrs
call s:move_to_win(winnr) call s:move_to_win(winnr)
call self.restore_lines_for_win(winnr)
" Clear syntax defined by Hit-A-Hint motion before restoring syntax. " Clear syntax defined by Hit-A-Hint motion before restoring syntax.
syntax clear HitAHintTarget syntax clear HitAHintTarget
if self.config.do_shade
let &syntax = self.save_syntax[winnr] let &syntax = self.save_syntax[winnr]
call s:PHighlight.set('Conceal', self.save_conceal) endif
let &l:conceallevel = self.save_conceallevel[winnr]
let &l:concealcursor = self.save_concealcursor[winnr]
call self.save_undo[winnr].restore() call self.save_undo[winnr].restore()
let &l:modified = self.save_modified[winnr]
let &l:modifiable = self.save_modifiable[winnr]
let &l:readonly = self.save_readonly[winnr]
for id in self.highlight_ids[winnr] for id in self.highlight_ids[winnr]
call matchdelete(id) call matchdelete(id)
endfor endfor
@ -343,6 +358,8 @@ function! s:Hinter.restore_env() abort
finally finally
call s:move_to_win(nr) call s:move_to_win(nr)
endtry endtry
call self.restore_options()
endfunction endfunction
let s:undo_lock = {} let s:undo_lock = {}
@ -441,12 +458,24 @@ function! s:Hinter.show_hint() abort
endfunction endfunction
function! s:Hinter._show_hint_for_win(winnr) abort function! s:Hinter._show_hint_for_win(winnr) abort
call self.modify_env_for_win(a:winnr)
let hints = []
for [lnum, col2hint] in items(self.w2l2c2h[a:winnr]) for [lnum, col2hint] in items(self.w2l2c2h[a:winnr])
call self._show_hint_for_line(a:winnr, lnum, col2hint) let hints += self._show_hint_for_line(a:winnr, lnum, col2hint)
endfor
" Restore syntax and show hints after replacing all lines for performance.
if !self.config.do_shade
let &l:syntax = self.save_syntax[a:winnr]
endif
execute 'highlight! link Conceal' self.config.highlight.target
for [lnum, cnum, char] in hints
call s:show_hint_pos(lnum, cnum, char)
endfor endfor
endfunction endfunction
function! s:Hinter._show_hint_for_line(winnr, lnum, col2hint) abort function! s:Hinter._show_hint_for_line(winnr, lnum, col2hint) abort
let hints = [] " [lnum, cnum, char]
let line = self.save_lines[a:winnr][a:lnum] let line = self.save_lines[a:winnr][a:lnum]
let col_offset = 0 let col_offset = 0
let prev_cnum = -1 let prev_cnum = -1
@ -468,15 +497,15 @@ function! s:Hinter._show_hint_for_line(winnr, lnum, col2hint) abort
endif endif
let col_offset += offset let col_offset += offset
call s:show_hint_pos(a:lnum, col_num, hint[0]) let hints = [[a:lnum, col_num, hint[0]]] + hints
if len(hint) > 1 if len(hint) > 1
" call s:show_hint_pos(a:lnum, col_num + 1, hint[1]) let hints = [[a:lnum, col_num + 1, hint[1]]] + hints
call s:show_hint_pos(a:lnum, col_num + len(nr2char(8233)), hint[1])
endif endif
let prev_cnum = cnum let prev_cnum = cnum
endfor endfor
call s:setline(a:lnum, line) call s:setline(a:lnum, line)
return hints
endfunction endfunction
" ._replace_line_for_hint() replaces line to show hints. " ._replace_line_for_hint() replaces line to show hints.
@ -495,7 +524,8 @@ function! s:Hinter._replace_line_for_hint(lnum, col_num, line, hint) abort
" Append one space for empty line or match at end of line " Append one space for empty line or match at end of line
if target is# '' if target is# ''
let hintwidth = strdisplaywidth(join(a:hint[:1], '')) let hintwidth = strdisplaywidth(join(a:hint[:1], ''))
let line .= repeat(' ', hintwidth) let char = self.config.do_shade ? ' ' : '.'
let line .= repeat(char, hintwidth)
return [line, hintwidth, 0] return [line, hintwidth, 0]
endif endif
@ -506,18 +536,19 @@ function! s:Hinter._replace_line_for_hint(lnum, col_num, line, hint) abort
let line = self._replace_text_to_space(line, a:lnum, col_num, strdisplaywidth(target)) let line = self._replace_text_to_space(line, a:lnum, col_num, strdisplaywidth(target))
let offset = strdisplaywidth(target) - len(target) let offset = strdisplaywidth(target) - len(target)
else else
" let line = substitute(line, '\%' . col_num . 'c.', ' ', '') if !self.config.do_shade
" 8233 " The priority of :syn-cchar is always under the priority of keywords.
" let space = '' " So, Hit-A-Hint replaces targets character with '.'.
let space = nr2char(8233) let space = '.'
let line = substitute(line, '\%' . col_num . 'c.', space, '') let line = substitute(line, '\%' . col_num . 'c.', space, '')
let offset = len(space) - len(target) let offset = len(space) - len(target)
endif endif
endif
let next_offset = 0 let next_offset = 0
if len(a:hint) > 1 if len(a:hint) > 1 && target isnot# "\t"
" pass [] as hint to stop recursion. " pass [' '] as hint to stop recursion.
let [line, next_offset, _] = self._replace_line_for_hint(a:lnum, col_num + offset + 1, line, []) let [line, next_offset, _] = self._replace_line_for_hint(a:lnum, col_num + offset + 1, line, [' '])
endif endif
return [line, offset, next_offset] return [line, offset, next_offset]
endfunction endfunction