Merge branch 'overwin' #130
This commit is contained in:
commit
291d92be70
51
README.md
51
README.md
@ -101,6 +101,57 @@ Jeffrey Way of Nettuts+ has also [written
|
||||
a tutorial](http://net.tutsplus.com/tutorials/other/vim-essential-plugin-easymotion/)
|
||||
about EasyMotion.
|
||||
|
||||
New features in version 3.0
|
||||
====
|
||||
|
||||
### Overwin motions
|
||||
![](https://raw.githubusercontent.com/haya14busa/i/2753bd4dd1dfdf5962dbdbffabf24244e4e14243/easymotion/overwin-motions.gif)
|
||||
|
||||
EasyMotion now supports moving cursor across/over window.
|
||||
Since it doesn't make sense that moving cursor to other window while Visual or
|
||||
Operator-pending mode, overwin motions only provides mappings for Normal
|
||||
mode. Please use `nmap` to use overwin motions. Overwin motions only
|
||||
supports bi-directional motions.
|
||||
|
||||
#### Example configuration
|
||||
|
||||
```vim
|
||||
" <Leader>f{char} to move to {char}
|
||||
map <Leader>f <Plug>(easymotion-bd-f)
|
||||
nmap <Leader>f <Plug>(easymotion-overwin-f)
|
||||
|
||||
" s{char}{char} to move to {char}{char}
|
||||
nmap s <Plug>(easymotion-overwin-f2)
|
||||
|
||||
" Move to line
|
||||
map <Leader>L <Plug>(easymotion-bd-jk)
|
||||
nmap <Leader>L <Plug>(easymotion-overwin-line)
|
||||
|
||||
" Move to word
|
||||
map <Leader>w <Plug>(easymotion-bd-w)
|
||||
nmap <Leader>w <Plug>(easymotion-overwin-w)
|
||||
```
|
||||
|
||||
#### Integration with incsearch.vim
|
||||
|
||||
```vim
|
||||
" You can use other keymappings like <C-l> instead of <CR> if you want to
|
||||
" use these mappings as default search and somtimes want to move cursor with
|
||||
" EasyMotion.
|
||||
function! s:incsearch_config(...) abort
|
||||
return incsearch#util#deepextend(deepcopy({
|
||||
\ 'modules': [incsearch#config#easymotion#module({'overwin': 1)],
|
||||
\ 'keymap': {
|
||||
\ "\<CR>": '<Over>(easymotion)'
|
||||
\ },
|
||||
\ 'is_expr': 0
|
||||
\ }), get(a:, 1, {}))
|
||||
endfunction
|
||||
|
||||
noremap <silent><expr> / incsearch#go(<SID>incsearch_config())
|
||||
noremap <silent><expr> ? incsearch#go(<SID>incsearch_config({'command': '?'}))
|
||||
noremap <silent><expr> g/ incsearch#go(<SID>incsearch_config({'is_stay': 1}))
|
||||
```
|
||||
|
||||
New features in version 2.0
|
||||
====
|
||||
|
@ -138,6 +138,10 @@ function! EasyMotion#S(num_strokes, visualmode, direction) " {{{
|
||||
call s:EasyMotion(re, a:direction, a:visualmode ? visualmode() : '', is_inclusive)
|
||||
return s:EasyMotion_is_cancelled
|
||||
endfunction " }}}
|
||||
function! EasyMotion#OverwinF(num_strokes) " {{{
|
||||
let re = s:findMotion(a:num_strokes, s:DIRECTION.bidirection)
|
||||
return EasyMotion#overwin#move(re)
|
||||
endfunction "}}}
|
||||
function! EasyMotion#T(num_strokes, visualmode, direction) " {{{
|
||||
if a:direction == 1
|
||||
let is_inclusive = 0
|
||||
@ -272,7 +276,8 @@ let s:config = {
|
||||
\ 'visualmode': s:FALSE,
|
||||
\ 'direction': s:DIRECTION.forward,
|
||||
\ 'inclusive': s:FALSE,
|
||||
\ 'accept_cursor_pos': s:FALSE
|
||||
\ 'accept_cursor_pos': s:FALSE,
|
||||
\ 'overwin': s:FALSE
|
||||
\ }
|
||||
|
||||
function! s:default_config() abort
|
||||
@ -284,9 +289,13 @@ endfunction
|
||||
|
||||
function! EasyMotion#go(...) abort
|
||||
let c = extend(s:default_config(), get(a:, 1, {}))
|
||||
if c.overwin
|
||||
return EasyMotion#overwin#move(c.pattern)
|
||||
else
|
||||
let s:current.is_operator = mode(1) ==# 'no' ? 1: 0
|
||||
call s:EasyMotion(c.pattern, c.direction, c.visualmode ? visualmode() : '', c.inclusive, c)
|
||||
return s:EasyMotion_is_cancelled
|
||||
endif
|
||||
endfunction
|
||||
function! EasyMotion#User(pattern, visualmode, direction, inclusive, ...) " {{{
|
||||
let s:current.is_operator = mode(1) ==# 'no' ? 1: 0
|
||||
|
@ -137,6 +137,12 @@ endfunction "}}}
|
||||
function! s:search.on_leave(cmdline) "{{{
|
||||
if s:num_strokes == -1
|
||||
call EasyMotion#highlight#delete_highlight(g:EasyMotion_hl_inc_search)
|
||||
if g:EasyMotion_do_shade
|
||||
call EasyMotion#highlight#delete_highlight(g:EasyMotion_hl_group_shade)
|
||||
endif
|
||||
endif
|
||||
if g:EasyMotion_cursor_highlight
|
||||
call EasyMotion#highlight#delete_highlight(g:EasyMotion_hl_inc_cursor)
|
||||
endif
|
||||
endfunction "}}}
|
||||
function! s:search.on_char(cmdline) "{{{
|
||||
|
24
autoload/EasyMotion/overwin.vim
Normal file
24
autoload/EasyMotion/overwin.vim
Normal file
@ -0,0 +1,24 @@
|
||||
let s:V = vital#of('easymotion')
|
||||
let s:HitAHintMotion = s:V.import('HitAHint.Motion')
|
||||
|
||||
function! EasyMotion#overwin#move(pattern) abort
|
||||
return s:HitAHintMotion.move(a:pattern, {
|
||||
\ 'keys': g:EasyMotion_keys,
|
||||
\ 'use_upper': g:EasyMotion_use_upper,
|
||||
\ 'highlight': {
|
||||
\ 'shade': g:EasyMotion_hl_group_shade,
|
||||
\ 'target': g:EasyMotion_hl_group_target,
|
||||
\ },
|
||||
\ 'jump_first_target_keys':
|
||||
\ (g:EasyMotion_enter_jump_first ? ["\<CR>"] : []) +
|
||||
\ (g:EasyMotion_space_jump_first ? ["\<Space>"] : [])
|
||||
\ })
|
||||
endfunction
|
||||
|
||||
function! EasyMotion#overwin#line() abort
|
||||
return EasyMotion#overwin#move('^')
|
||||
endfunction
|
||||
|
||||
function! EasyMotion#overwin#w() abort
|
||||
return EasyMotion#overwin#move('\(\<.\|^$\)')
|
||||
endfunction
|
@ -237,26 +237,16 @@ function! s:_build_module(sid) abort
|
||||
for func in functions
|
||||
let module[func] = function(prefix . func)
|
||||
endfor
|
||||
if has_key(module, '_vital_created')
|
||||
call module._vital_created(module)
|
||||
endif
|
||||
let export_module = filter(copy(module), 'v:key =~# "^\\a"')
|
||||
let s:loaded[a:sid] = get(g:, 'vital_debug', 0) ? module : export_module
|
||||
if has_key(module, '_vital_loaded')
|
||||
let V = vital#{s:self_version}#new()
|
||||
if has_key(module, '_vital_depends')
|
||||
let all = {}
|
||||
let modules =
|
||||
\ s:_concat(map(module._vital_depends(),
|
||||
\ 's:expand_modules(v:val, all)'))
|
||||
call call(V.load, modules, V)
|
||||
endif
|
||||
try
|
||||
call module._vital_loaded(V)
|
||||
catch
|
||||
" FIXME: Show an error message for debug.
|
||||
endtry
|
||||
endif
|
||||
if !get(g:, 'vital_debug', 0)
|
||||
call filter(module, 'v:key =~# "^\\a"')
|
||||
endif
|
||||
let s:loaded[a:sid] = module
|
||||
return copy(module)
|
||||
return copy(s:loaded[a:sid])
|
||||
endfunction
|
||||
|
||||
if exists('+regexpengine')
|
||||
|
@ -225,7 +225,7 @@ endfunction
|
||||
" similar to Haskell's Prelude.foldl1
|
||||
function! s:foldl1(f, xs) abort
|
||||
if len(a:xs) == 0
|
||||
throw 'foldl1'
|
||||
throw 'vital: Data.List: foldl1'
|
||||
endif
|
||||
return s:foldl(a:f, a:xs[0], a:xs[1:])
|
||||
endfunction
|
||||
@ -238,7 +238,7 @@ endfunction
|
||||
" similar to Haskell's Prelude.fold11
|
||||
function! s:foldr1(f, xs) abort
|
||||
if len(a:xs) == 0
|
||||
throw 'foldr1'
|
||||
throw 'vital: Data.List: foldr1'
|
||||
endif
|
||||
return s:foldr(a:f, a:xs[-1], a:xs[0:-2])
|
||||
endfunction
|
||||
@ -264,7 +264,7 @@ endfunction
|
||||
" Inspired by Ruby's with_index method.
|
||||
function! s:with_index(list, ...) abort
|
||||
let base = a:0 > 0 ? a:1 : 0
|
||||
return s:zip(a:list, range(base, len(a:list)+base-1))
|
||||
return map(copy(a:list), '[v:val, v:key + base]')
|
||||
endfunction
|
||||
|
||||
" similar to Ruby's detect or Haskell's find.
|
||||
|
269
autoload/vital/_easymotion/Data/Set.vim
Normal file
269
autoload/vital/_easymotion/Data/Set.vim
Normal file
@ -0,0 +1,269 @@
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
let s:TRUE = !0
|
||||
let s:FALSE = 0
|
||||
|
||||
function! s:set(...) abort
|
||||
return call(s:set._new, a:000, s:set)
|
||||
endfunction
|
||||
|
||||
function! s:frozenset(...) abort
|
||||
return call(s:frozenset._new, a:000, s:frozenset)
|
||||
endfunction
|
||||
|
||||
function! s:_hash_func(x) abort
|
||||
return a:x
|
||||
endfunction
|
||||
|
||||
let s:_base_set = {
|
||||
\ '_is_set' : s:TRUE,
|
||||
\ '_data' : {},
|
||||
\ '_hash_func' : function('s:_hash_func')
|
||||
\ }
|
||||
|
||||
function! s:_base_set._new(...) abort
|
||||
let obj = deepcopy(self)
|
||||
let xs = get(a:, 1, [])
|
||||
let obj._hash_func = get(a:, 2, obj._hash_func)
|
||||
call obj._set_data(xs)
|
||||
return obj
|
||||
endfunction
|
||||
|
||||
"" Return the union of two sets as a new set.
|
||||
" (I.e. all elements that are in either set.)
|
||||
function! s:_base_set.union(t) abort
|
||||
let r = deepcopy(self)
|
||||
call r._update(a:t)
|
||||
return r
|
||||
endfunction
|
||||
let s:_base_set.or = s:_base_set.union
|
||||
|
||||
"" Return the intersection of two sets as a new set.
|
||||
" (I.e. all elements that are in both sets.)
|
||||
function! s:_base_set.intersection(t) abort
|
||||
let t = self._to_set(a:t)
|
||||
let [little, big] = self.len() <= t.len() ? [self, t] : [t, self]
|
||||
return self._new(filter(copy(big.to_list()), 'little.in(v:val)'))
|
||||
endfunction
|
||||
let s:_base_set.and = s:_base_set.intersection
|
||||
|
||||
"" Return the symmetric difference of two sets as a new set.
|
||||
" (I.e. all elements that are in exactly one of the sets.)
|
||||
function! s:_base_set.symmetric_difference(t) abort
|
||||
let t = self._to_set(a:t)
|
||||
return self._new(filter(copy(self.to_list()), '!t.in(v:val)')
|
||||
\ + filter(copy(t.to_list()), '!self.in(v:val)'))
|
||||
endfunction
|
||||
let s:_base_set.xor = s:_base_set.symmetric_difference
|
||||
|
||||
"" Return the difference of two sets as a new Set.
|
||||
function! s:_base_set.difference(t) abort
|
||||
let t = self._to_set(a:t)
|
||||
return self._new(filter(copy(self.to_list()), '!t.in(v:val)'))
|
||||
endfunction
|
||||
let s:_base_set.sub = s:_base_set.difference
|
||||
|
||||
"" Report whether another set contains this set.
|
||||
function! s:_base_set.issubset(t) abort
|
||||
let t = self._to_set(a:t)
|
||||
return self.len() > t.len() ? s:FALSE
|
||||
\ : empty(filter(copy(self.to_list()), '!t.in(v:val)'))
|
||||
endfunction
|
||||
|
||||
"" Report whether this set contains another set.
|
||||
function! s:_base_set.issuperset(t) abort
|
||||
let t = self._to_set(a:t)
|
||||
return self.len() < t.len() ? s:FALSE
|
||||
\ : empty(filter(copy(t.to_list()), '!self.in(v:val)'))
|
||||
endfunction
|
||||
|
||||
" less than equal & greater than equal
|
||||
let s:_base_set.le = s:_base_set.issubset
|
||||
let s:_base_set.ge = s:_base_set.issuperset
|
||||
|
||||
" less than
|
||||
function! s:_base_set.lt(t) abort
|
||||
let t = self._to_set(a:t)
|
||||
return self.len() < t.len() && self.issubset(t)
|
||||
endfunction
|
||||
|
||||
" greater than
|
||||
function! s:_base_set.gt(t) abort
|
||||
let t = self._to_set(a:t)
|
||||
return self.len() > t.len() && self.issuperset(t)
|
||||
endfunction
|
||||
|
||||
function! s:_base_set.len() abort
|
||||
return len(self._data)
|
||||
endfunction
|
||||
|
||||
function! s:_base_set.to_list() abort
|
||||
return values(self._data)
|
||||
endfunction
|
||||
|
||||
function! s:_base_set._update(xs) abort
|
||||
for X in (s:_is_set(a:xs) ? a:xs.to_list() : a:xs)
|
||||
call self._add(X)
|
||||
unlet X
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:_base_set._add(x) abort
|
||||
let key = self._hash(a:x)
|
||||
if !has_key(self._data, key)
|
||||
let self._data[key] = a:x
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Report whether an element is a member of a set.
|
||||
function! s:_base_set.in(x) abort
|
||||
return has_key(self._data, self._hash(a:x))
|
||||
endfunction
|
||||
|
||||
function! s:_base_set._to_set(x) abort
|
||||
return s:_is_set(a:x) ? a:x : self._new(a:x)
|
||||
endfunction
|
||||
|
||||
function! s:_base_set._clear() abort
|
||||
let self._data = {}
|
||||
endfunction
|
||||
|
||||
function! s:_base_set._set_data(xs) abort
|
||||
call self._clear()
|
||||
call self._update(a:xs)
|
||||
endfunction
|
||||
|
||||
function! s:_base_set._hash(x) abort
|
||||
return string(self._hash_func(a:x))
|
||||
endfunction
|
||||
|
||||
" frozenset: Immutable set class.
|
||||
|
||||
let s:frozenset = deepcopy(s:_base_set)
|
||||
|
||||
" Set: Mutable set class.
|
||||
|
||||
let s:set = deepcopy(s:_base_set)
|
||||
|
||||
" Update a set with the union of itself and another.
|
||||
function! s:set.update(iterable) abort
|
||||
call self._update(a:iterable)
|
||||
endfunction
|
||||
|
||||
" Update a set with the union of itself and another.
|
||||
function! s:set.ior(t) abort
|
||||
call self.update(a:t)
|
||||
return self
|
||||
endfunction
|
||||
|
||||
" Update a set with the intersection of itself and another.
|
||||
function! s:set.intersection_update(t) abort
|
||||
let r = self.and(a:t).to_list()
|
||||
call self.clear()
|
||||
call self.update(r)
|
||||
endfunction
|
||||
|
||||
" Update a set with the intersection of itself and another.
|
||||
function! s:set.iand(t) abort
|
||||
call self.intersection_update(a:t)
|
||||
return self
|
||||
endfunction
|
||||
|
||||
" Update a set with the symmetric difference of itself and another.
|
||||
function! s:set.symmetric_difference_update(t) abort
|
||||
let t = self._to_set(a:t)
|
||||
if self is t
|
||||
call self.clear()
|
||||
return
|
||||
endif
|
||||
for X in t.to_list()
|
||||
if self.in(X)
|
||||
call self.remove(X)
|
||||
else
|
||||
call self._add(X)
|
||||
endif
|
||||
unlet X
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
" Update a set with the symmetric difference of itself and another.
|
||||
function! s:set.ixor(t) abort
|
||||
call self.symmetric_difference_update(a:t)
|
||||
return self
|
||||
endfunction
|
||||
|
||||
" Remove all elements of another set from this set.
|
||||
function! s:set.difference_update(t) abort
|
||||
let t = self._to_set(a:t)
|
||||
if self is t
|
||||
call self.clear()
|
||||
return
|
||||
endif
|
||||
for X in filter(t.to_list(), 'self.in(v:val)')
|
||||
call self.remove(X)
|
||||
unlet X
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
" Remove all elements of another set from this set.
|
||||
function! s:set.isub(t) abort
|
||||
call self.difference_update(a:t)
|
||||
return self
|
||||
endfunction
|
||||
|
||||
" Remove all elements from this set.
|
||||
function! s:set.clear() abort
|
||||
call self._clear()
|
||||
endfunction
|
||||
|
||||
"" Add an element to a set.
|
||||
" This has no effect if the element is already present.
|
||||
function! s:set.add(x) abort
|
||||
return self._add(a:x)
|
||||
endfunction
|
||||
|
||||
"" Remove an element from a set; it must be a member.
|
||||
" If the element is not a member, throw Exception.
|
||||
function! s:set.remove(e) abort
|
||||
try
|
||||
unlet self._data[self._hash(a:e)]
|
||||
catch /^Vim\%((\a\+)\)\?:E716/
|
||||
call s:_throw('the element is not a member')
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
"" Remove an element from a set if it is a member.
|
||||
" If the element is not a member, do nothing.
|
||||
function! s:set.discard(e) abort
|
||||
try
|
||||
call self.remove(a:e)
|
||||
catch /vital: Data.Set: the element is not a member/
|
||||
" Do nothing
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" Remove and return an arbitrary set element.
|
||||
function! s:set.pop() abort
|
||||
try
|
||||
let k = keys(self._data)[0]
|
||||
catch /^Vim\%((\a\+)\)\?:E684/
|
||||
call s:_throw('set is empty')
|
||||
endtry
|
||||
let v = self._data[k]
|
||||
unlet self._data[k]
|
||||
return v
|
||||
endfunction
|
||||
|
||||
" Helper:
|
||||
|
||||
function! s:_is_set(x) abort
|
||||
return type(a:x) is type({}) && get(a:x, '_is_set', s:FALSE)
|
||||
endfunction
|
||||
|
||||
function! s:_throw(message) abort
|
||||
throw 'vital: Data.Set: ' . a:message
|
||||
endfunction
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
111
autoload/vital/_easymotion/HitAHint/Hint.vim
Normal file
111
autoload/vital/_easymotion/HitAHint/Hint.vim
Normal file
@ -0,0 +1,111 @@
|
||||
" function() wrapper
|
||||
if v:version > 703 || v:version == 703 && has('patch1170')
|
||||
let s:_function = function('function')
|
||||
else
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
let s:_s = '<SNR>' . s:_SID() . '_'
|
||||
function! s:_function(fstr) abort
|
||||
return function(substitute(a:fstr, 's:', s:_s, 'g'))
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! s:_assert(...) abort
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! s:_vital_loaded(V) abort
|
||||
if a:V.exists('Vim.PowerAssert')
|
||||
let s:assert = a:V.import('Vim.PowerAssert').assert
|
||||
else
|
||||
let s:assert = s:_function('s:_assert')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" TERMS:
|
||||
" key: A character to generate hint. e.g. a,b,c,d,e,f,...
|
||||
" hint: A hint is a combination of keys. e.g. a,b,ab,abc,..
|
||||
|
||||
" s:create() assigns keys to each targets and generate hint dict.
|
||||
" Example:
|
||||
" let targets = [1, 2, 3, 4, 5, 6]
|
||||
" echo s:label(targets, ['a', 'b', 'c'])
|
||||
" " => {
|
||||
" 'a': 1,
|
||||
" 'b': {
|
||||
" 'a': 2,
|
||||
" 'b': 3
|
||||
" },
|
||||
" 'c': {
|
||||
" 'a': 4,
|
||||
" 'b': 5,
|
||||
" 'c': 6
|
||||
" }
|
||||
" }
|
||||
" Hint-to-target:
|
||||
" a -> 1
|
||||
" ba -> 2
|
||||
" bb -> 3
|
||||
" ca -> 4
|
||||
" cb -> 5
|
||||
" cc -> 6
|
||||
" @param {list<T>} targets
|
||||
" @param {list<string>} keys each key should be uniq
|
||||
" @return Tree{string: (T|Tree)}
|
||||
function! s:create(targets, keys) abort
|
||||
exe s:assert('len(a:keys) > 1')
|
||||
let groups = {}
|
||||
let keys_count = reverse(s:_keys_count(len(a:targets), len(a:keys)))
|
||||
|
||||
let target_idx = 0
|
||||
let key_idx = 0
|
||||
for key_count in keys_count
|
||||
if key_count > 1
|
||||
" We need to create a subgroup
|
||||
" Recurse one level deeper
|
||||
let sub_targets = a:targets[target_idx : target_idx + key_count - 1]
|
||||
let groups[a:keys[key_idx]] = s:create(sub_targets, a:keys)
|
||||
elseif key_count == 1
|
||||
" Assign single target key_idx
|
||||
let groups[a:keys[key_idx]] = a:targets[target_idx]
|
||||
else
|
||||
" No target
|
||||
continue
|
||||
endif
|
||||
let key_idx += 1
|
||||
let target_idx += key_count
|
||||
endfor
|
||||
return groups
|
||||
endfunction
|
||||
|
||||
" s:_keys_count() generates list which represents how many targets to be
|
||||
" assigned to the key.
|
||||
" If the count > 1, use tree recursively.
|
||||
" Example:
|
||||
" echo s:_keys_count(5, 3)
|
||||
" " => [3, 1, 1]
|
||||
" echo s:_keys_count(8, 3)
|
||||
" " => [3, 3, 2]
|
||||
" @param {number} target_len
|
||||
" @param {number} keys_len
|
||||
function! s:_keys_count(targets_len, keys_len) abort
|
||||
exe s:assert('a:keys_len > 1')
|
||||
let _keys_count = repeat([0], a:keys_len)
|
||||
let is_first_level = 1
|
||||
let targets_left_cnt = a:targets_len
|
||||
while targets_left_cnt > 0
|
||||
let cnt_to_add = is_first_level ? 1 : a:keys_len - 1
|
||||
for i in range(a:keys_len)
|
||||
let _keys_count[i] += cnt_to_add
|
||||
let targets_left_cnt -= cnt_to_add
|
||||
if targets_left_cnt <= 0
|
||||
let _keys_count[i] += targets_left_cnt
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
let is_first_level = 0
|
||||
endwhile
|
||||
exe s:assert('len(_keys_count) is# a:keys_len')
|
||||
return _keys_count
|
||||
endfunction
|
684
autoload/vital/_easymotion/HitAHint/Motion.vim
Normal file
684
autoload/vital/_easymotion/HitAHint/Motion.vim
Normal file
@ -0,0 +1,684 @@
|
||||
function! s:_vital_loaded(V) abort
|
||||
let s:Hint = a:V.import('HitAHint.Hint')
|
||||
let s:PHighlight = a:V.import('Palette.Highlight')
|
||||
let s:Buffer = a:V.import('Vim.Buffer')
|
||||
let s:Prelude = a:V.import('Prelude')
|
||||
let s:Set = a:V.import('Data.Set')
|
||||
endfunction
|
||||
|
||||
function! s:_vital_depends() abort
|
||||
return [
|
||||
\ 'HitAHint.Hint',
|
||||
\ 'Palette.Highlight',
|
||||
\ 'Vim.Buffer',
|
||||
\ 'Prelude',
|
||||
\ 'Data.Set',
|
||||
\ ]
|
||||
endfunction
|
||||
|
||||
let s:TRUE = !0
|
||||
let s:FALSE = 0
|
||||
let s:DIRECTION = {'forward': 0, 'backward': 1}
|
||||
|
||||
" s:move() moves cursor over/accross window with Hit-A-Hint feature like
|
||||
" vim-easymotion
|
||||
" @param {dict} config
|
||||
function! s:move(pattern, ...) abort
|
||||
let o = s:new_overwin(get(a:, 1, {}))
|
||||
return o.pattern(a:pattern)
|
||||
endfunction
|
||||
|
||||
function! s:move_f(...) abort
|
||||
echo 'Target: '
|
||||
let c = s:getchar()
|
||||
return s:move(c, get(a:, 1, {}))
|
||||
endfunction
|
||||
|
||||
function! s:move_f2() abort
|
||||
echo 'Target: '
|
||||
let c = s:getchar()
|
||||
redraw
|
||||
echo 'Target: ' . c
|
||||
let c2 = s:getchar()
|
||||
return s:move(s:Prelude.escape_pattern(c . c2), get(a:, 1, {}))
|
||||
endfunction
|
||||
|
||||
|
||||
let s:overwin = {
|
||||
\ 'config': {
|
||||
\ 'keys': 'asdghklqwertyuiopzxcvbnmfj;',
|
||||
\ 'use_upper': s:FALSE,
|
||||
\ 'auto_land': s:TRUE,
|
||||
\ 'highlight': {
|
||||
\ 'shade': 'HitAHintShade',
|
||||
\ 'target': 'HitAHintTarget',
|
||||
\ },
|
||||
\ 'jump_first_target_keys': [],
|
||||
\ }
|
||||
\ }
|
||||
|
||||
function! s:_init_hl() abort
|
||||
highlight default HitAHintShade ctermfg=242 guifg=#777777
|
||||
highlight default HitAHintTarget ctermfg=81 guifg=#66D9EF
|
||||
endfunction
|
||||
|
||||
call s:_init_hl()
|
||||
|
||||
augroup vital-hit-a-hint-motion-default-highlight
|
||||
autocmd!
|
||||
autocmd ColorScheme * call s:_init_hl()
|
||||
augroup END
|
||||
|
||||
|
||||
function! s:new_overwin(...) abort
|
||||
let o = deepcopy(s:overwin)
|
||||
call s:deepextend(o.config, get(a:, 1, {}))
|
||||
return o
|
||||
endfunction
|
||||
|
||||
function! s:overwin.pattern(pattern) abort
|
||||
let winpos = self.select_winpos(self.gather_poses_overwin(a:pattern), self.config.keys)
|
||||
if winpos is# -1
|
||||
else
|
||||
let [winnr_str, pos] = winpos
|
||||
let winnr = str2nr(winnr_str)
|
||||
if winnr is# winnr()
|
||||
normal! m`
|
||||
else
|
||||
call s:move_to_win(winnr)
|
||||
endif
|
||||
call cursor(pos)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:overwin.select_winpos(winnr2poses, keys) abort
|
||||
let wposes = s:winnr2poses_to_list(a:winnr2poses)
|
||||
if self.config.auto_land && len(wposes) is# 1
|
||||
return wposes[0]
|
||||
endif
|
||||
return self.choose_prompt(s:Hint.create(wposes, a:keys))
|
||||
endfunction
|
||||
|
||||
" s:wpos_to_hint() returns dict whose key is position with window and whose
|
||||
" value is the hints.
|
||||
" @param Tree{string: ((winnr, (number,number))|Tree)} hint_dict
|
||||
" @return {{winnr: {string: list<char>}}} poskey to hint for each window
|
||||
" e.g.
|
||||
" {
|
||||
" '1': {
|
||||
" '00168:00004': ['b', 'c', 'b'],
|
||||
" '00174:00001': ['b', 'c', 'a'],
|
||||
" '00188:00004': ['b', 'b'],
|
||||
" '00190:00001': ['b', 'a'],
|
||||
" '00191:00016': ['a', 'c'],
|
||||
" '00192:00004': ['a', 'b'],
|
||||
" '00195:00035': ['a', 'a']
|
||||
" },
|
||||
" '3': {
|
||||
" '00168:00004': ['c', 'c', 'c'],
|
||||
" '00174:00001': ['c', 'c', 'b'],
|
||||
" '00188:00004': ['c', 'c', 'a'],
|
||||
" '00190:00001': ['c', 'b'],
|
||||
" '00191:00016': ['c', 'a'],
|
||||
" '00192:00004': ['b', 'c', 'c']
|
||||
" }
|
||||
" }
|
||||
function! s:create_win2pos2hint(hint_dict) abort
|
||||
return s:_create_win2pos2hint({}, a:hint_dict)
|
||||
endfunction
|
||||
|
||||
function! s:_create_win2pos2hint(dict, hint_dict, ...) abort
|
||||
let prefix = get(a:, 1, [])
|
||||
for [hint, v] in items(a:hint_dict)
|
||||
if type(v) is# type({})
|
||||
call s:_create_win2pos2hint(a:dict, v, prefix + [hint])
|
||||
else
|
||||
let [winnr, pos] = v
|
||||
let a:dict[winnr] = get(a:dict, winnr, {})
|
||||
let a:dict[winnr][s:pos2poskey(pos)] = prefix + [hint]
|
||||
endif
|
||||
unlet v
|
||||
endfor
|
||||
return a:dict
|
||||
endfunction
|
||||
|
||||
" s:pos2poskey() convertes pos to poskey to use pos as dictionary keys and
|
||||
" sort pos correctly.
|
||||
" @param {(number,number)} pos
|
||||
" @return string
|
||||
" e.g. [1, 1] -> '00001:00001'
|
||||
function! s:pos2poskey(pos) abort
|
||||
return join(map(copy(a:pos), "printf('%05d', v:val)"), ':')
|
||||
endfunction
|
||||
|
||||
" s:poskey2pos() convertes poskey to pos.
|
||||
" @param {string} poskey e.g. '00001:00001'
|
||||
" @return {(number,number)}
|
||||
" e.g. '00001:00001' -> [1, 1]
|
||||
function! s:poskey2pos(poskey) abort
|
||||
return map(split(a:poskey, ':'), 'str2nr(v:val)')
|
||||
endfunction
|
||||
|
||||
function! s:overwin.choose_prompt(hint_dict) abort
|
||||
if empty(a:hint_dict)
|
||||
redraw
|
||||
echo 'No target'
|
||||
return -1
|
||||
endif
|
||||
let hinter = s:Hinter.new(a:hint_dict, self.config)
|
||||
try
|
||||
call hinter.before()
|
||||
call hinter.show_hint()
|
||||
redraw
|
||||
echo 'Target key: '
|
||||
let c = s:getchar()
|
||||
if self.config.use_upper
|
||||
let c = toupper(c)
|
||||
endif
|
||||
catch
|
||||
echo v:exception
|
||||
return -1
|
||||
finally
|
||||
call hinter.after()
|
||||
endtry
|
||||
|
||||
" Jump to first target if target key is in config.jump_first_target_keys.
|
||||
if index(self.config.jump_first_target_keys, c) isnot# -1
|
||||
let c = split(self.config.keys, '\zs')[0]
|
||||
endif
|
||||
|
||||
if has_key(a:hint_dict, c)
|
||||
let target = a:hint_dict[c]
|
||||
return type(target) is# type({}) ? self.choose_prompt(target) : target
|
||||
else
|
||||
redraw
|
||||
echo 'Invalid target: ' . c
|
||||
return -1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Hinter show hints accross window.
|
||||
" save_lines: {{winnr: {lnum: string}}}
|
||||
" w2l2c2h: winnr to lnum to col num to hints. col2hints is tuple because we
|
||||
" need sorted col to hints pair.
|
||||
" save_syntax: {{winnr: &syntax}}
|
||||
" {{winnr: {lnum: list<(cnum, list<char>)>}}}
|
||||
let s:Hinter = {
|
||||
\ 'save_lines': {},
|
||||
\ 'w2l2c2h': {},
|
||||
\ 'winnrs': [],
|
||||
\ 'save_syntax': {},
|
||||
\ 'save_conceallevel': {},
|
||||
\ 'save_concealcursor': {},
|
||||
\ 'save_modified': {},
|
||||
\ 'save_modifiable': {},
|
||||
\ 'save_readonly': {},
|
||||
\ 'save_undo': {},
|
||||
\ 'highlight_ids': {},
|
||||
\ }
|
||||
|
||||
function! s:Hinter.new(hint_dict, config) abort
|
||||
let s = deepcopy(self)
|
||||
let s.config = a:config
|
||||
let win2pos2hint = s:create_win2pos2hint(a:hint_dict)
|
||||
let s.winnrs = map(keys(win2pos2hint), 'str2nr(v:val)')
|
||||
let s.win2pos2hint = win2pos2hint
|
||||
let s.w2l2c2h = s:win2pos2hint_to_w2l2c2h(win2pos2hint)
|
||||
call s._save_lines()
|
||||
return s
|
||||
endfunction
|
||||
|
||||
function! s:Hinter.before() abort
|
||||
call self.modify_env()
|
||||
call self.disable_conceal_in_other_win()
|
||||
endfunction
|
||||
|
||||
function! s:Hinter.after() abort
|
||||
call self.restore_lines()
|
||||
call self.restore_env()
|
||||
call self.restore_conceal_in_other_win()
|
||||
endfunction
|
||||
|
||||
function! s:Hinter._save_lines() abort
|
||||
let nr = winnr()
|
||||
try
|
||||
for [winnr, pos2hint] in items(self.win2pos2hint)
|
||||
call s:move_to_win(winnr)
|
||||
let lnums = map(copy(keys(pos2hint)), 's:poskey2pos(v:val)[0]')
|
||||
let self.save_lines[winnr] = get(self.save_lines, winnr, {})
|
||||
for lnum in lnums
|
||||
let self.save_lines[winnr][lnum] = getline(lnum)
|
||||
endfor
|
||||
endfor
|
||||
finally
|
||||
call s:move_to_win(nr)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:Hinter.restore_lines() abort
|
||||
let nr = winnr()
|
||||
try
|
||||
for [winnr, lnum2line] in items(self.save_lines)
|
||||
call s:move_to_win(winnr)
|
||||
for [lnum, line] in items(lnum2line)
|
||||
call s:setline(lnum, line)
|
||||
endfor
|
||||
endfor
|
||||
finally
|
||||
call s:move_to_win(nr)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:Hinter.modify_env() abort
|
||||
let nr = winnr()
|
||||
try
|
||||
let self.highlight_id_cursor = matchadd('Cursor', '\%#', 1000001)
|
||||
for winnr in self.winnrs
|
||||
call s:move_to_win(winnr)
|
||||
let self.save_conceal = s:PHighlight.get('Conceal')
|
||||
let self.save_syntax[winnr] = &syntax
|
||||
let self.save_conceallevel[winnr] = &l:conceallevel
|
||||
let self.save_concealcursor[winnr] = &l:concealcursor
|
||||
let self.save_modified[winnr] = &l:modified
|
||||
let self.save_modifiable[winnr] = &l:modifiable
|
||||
let self.save_readonly[winnr] = &l:readonly
|
||||
|
||||
let self.save_undo[winnr] = s:undo_lock.save()
|
||||
|
||||
setlocal modifiable
|
||||
setlocal noreadonly
|
||||
|
||||
ownsyntax overwin
|
||||
syntax clear
|
||||
setlocal conceallevel=2
|
||||
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[winnr] += [matchadd(self.config.highlight.shade, '\_.*', 100)]
|
||||
endfor
|
||||
catch
|
||||
call s:throw(v:throwpoint . ' ' . v:exception)
|
||||
finally
|
||||
call s:move_to_win(nr)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:Hinter.restore_env() abort
|
||||
let nr = winnr()
|
||||
try
|
||||
call matchdelete(self.highlight_id_cursor)
|
||||
for winnr in self.winnrs
|
||||
call s:move_to_win(winnr)
|
||||
" Clear syntax defined by Hit-A-Hint motion before restoring syntax.
|
||||
syntax clear HitAHintTarget
|
||||
let &syntax = self.save_syntax[winnr]
|
||||
call s:PHighlight.set('Conceal', self.save_conceal)
|
||||
let &l:conceallevel = self.save_conceallevel[winnr]
|
||||
let &l:concealcursor = self.save_concealcursor[winnr]
|
||||
|
||||
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]
|
||||
call matchdelete(id)
|
||||
endfor
|
||||
endfor
|
||||
catch
|
||||
call s:throw(v:throwpoint . ' ' . v:exception)
|
||||
finally
|
||||
call s:move_to_win(nr)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
let s:undo_lock = {}
|
||||
|
||||
function! s:undo_lock.save() abort
|
||||
let undo = deepcopy(self)
|
||||
call undo._save()
|
||||
return undo
|
||||
endfunction
|
||||
|
||||
function! s:undo_lock._save() abort
|
||||
if undotree().seq_last == 0
|
||||
" if there are no undo history, disable undo feature by setting
|
||||
" 'undolevels' to -1 and restore it.
|
||||
let self.save_undolevels = &l:undolevels
|
||||
let &l:undolevels = -1
|
||||
elseif !s:Buffer.is_cmdwin()
|
||||
" command line window doesn't support :wundo.
|
||||
let self.undofile = tempname()
|
||||
execute 'wundo!' self.undofile
|
||||
else
|
||||
let self.is_cmdwin = s:TRUE
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:undo_lock.restore() abort
|
||||
if has_key(self, 'save_undolevels')
|
||||
let &l:undolevels = self.save_undolevels
|
||||
endif
|
||||
if has_key(self, 'undofile') && filereadable(self.undofile)
|
||||
silent execute 'rundo' self.undofile
|
||||
call delete(self.undofile)
|
||||
endif
|
||||
if has_key(self, 'is_cmdwin')
|
||||
" XXX: it breaks undo history. AFAIK, there are no way to save and restore
|
||||
" undo history in commandline window.
|
||||
call self.undobreak()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:undo_lock.undobreak() abort
|
||||
let old_undolevels = &l:undolevels
|
||||
setlocal undolevels=-1
|
||||
keepjumps call setline('.', getline('.'))
|
||||
let &l:undolevels = old_undolevels
|
||||
endfunction
|
||||
|
||||
function! s:Hinter.disable_conceal_in_other_win() abort
|
||||
let allwinnrs = s:Set.set(range(1, winnr('$')))
|
||||
let other_winnrs = allwinnrs.sub(self.winnrs).to_list()
|
||||
for w in other_winnrs
|
||||
if 'help' !=# getwinvar(w, '&buftype')
|
||||
call setwinvar(w, 'overwin_save_conceallevel', getwinvar(w, '&conceallevel'))
|
||||
call setwinvar(w, '&conceallevel', 0)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:Hinter.restore_conceal_in_other_win() abort
|
||||
let allwinnrs = s:Set.set(range(1, winnr('$')))
|
||||
let other_winnrs = allwinnrs.sub(self.winnrs).to_list()
|
||||
for w in other_winnrs
|
||||
if 'help' !=# getwinvar(w, '&buftype')
|
||||
call setwinvar(w, '&conceallevel', getwinvar(w, 'overwin_save_conceallevel'))
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
" ._pos2hint_to_line2col2hint() converts pos2hint to line2col2hint dict whose
|
||||
" key is line number and whose value is list of tuple of col number to hint.
|
||||
" line2col2hint is for show hint with replacing line by line.
|
||||
" col should be sorted.
|
||||
" @param {{string: list<char>}} pos2hint
|
||||
" @return {number: [(number, list<char>)]}
|
||||
function! s:Hinter._pos2hint_to_line2col2hint(pos2hint) abort
|
||||
let line2col2hint = {}
|
||||
let poskeys = sort(keys(a:pos2hint))
|
||||
for poskey in poskeys
|
||||
let [lnum, cnum] = s:poskey2pos(poskey)
|
||||
let line2col2hint[lnum] = get(line2col2hint, lnum, [])
|
||||
let line2col2hint[lnum] += [[cnum, a:pos2hint[poskey]]]
|
||||
endfor
|
||||
return line2col2hint
|
||||
endfunction
|
||||
|
||||
function! s:Hinter.show_hint() abort
|
||||
let nr = winnr()
|
||||
try
|
||||
for winnr in self.winnrs
|
||||
call s:move_to_win(winnr)
|
||||
call self._show_hint_for_win(winnr)
|
||||
endfor
|
||||
finally
|
||||
call s:move_to_win(nr)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:Hinter._show_hint_for_win(winnr) abort
|
||||
for [lnum, col2hint] in items(self.w2l2c2h[a:winnr])
|
||||
call self._show_hint_for_line(a:winnr, lnum, col2hint)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:Hinter._show_hint_for_line(winnr, lnum, col2hint) abort
|
||||
let line = self.save_lines[a:winnr][a:lnum]
|
||||
let col_offset = 0
|
||||
let prev_cnum = -1
|
||||
let next_offset = 0
|
||||
for [cnum, hint] in a:col2hint
|
||||
let col_num = cnum + col_offset
|
||||
|
||||
let is_consecutive = cnum is# prev_cnum + 1
|
||||
if !is_consecutive
|
||||
let col_num += next_offset
|
||||
else
|
||||
let save_next_offset = next_offset
|
||||
endif
|
||||
|
||||
let [line, offset, next_offset] = self._replace_line_for_hint(a:lnum, col_num, line, hint)
|
||||
|
||||
if is_consecutive
|
||||
let col_offset += save_next_offset
|
||||
endif
|
||||
let col_offset += offset
|
||||
|
||||
call s:show_hint_pos(a:lnum, col_num, hint[0])
|
||||
if len(hint) > 1
|
||||
call s:show_hint_pos(a:lnum, col_num + 1, hint[1])
|
||||
endif
|
||||
|
||||
let prev_cnum = cnum
|
||||
endfor
|
||||
call s:setline(a:lnum, line)
|
||||
endfunction
|
||||
|
||||
" ._replace_line_for_hint() replaces line to show hints.
|
||||
" - It appends space if the line is empty
|
||||
" - It replaces <Tab> to space if the target character is <Tab>
|
||||
" - It replaces next target character if it's <Tab> and len(hint) > 1
|
||||
" Replacing line changes col number, so it returns offset of col number.
|
||||
" As for replaceing next target character, the timing to calculate offset
|
||||
" depends on the col number of next hint in the same line, so it returns
|
||||
" `next_offset` instead of returning offset all at once.
|
||||
" @return {(string, number, number)} (line, offset, next_offset)
|
||||
function! s:Hinter._replace_line_for_hint(lnum, col_num, line, hint) abort
|
||||
let line = a:line
|
||||
let col_num = a:col_num
|
||||
let target = matchstr(line, '\%' . col_num .'c.')
|
||||
" Append one space for empty line or match at end of line
|
||||
if target is# ''
|
||||
let hintwidth = strdisplaywidth(join(a:hint[:1], ''))
|
||||
let line .= repeat(' ', hintwidth)
|
||||
return [line, hintwidth, 0]
|
||||
endif
|
||||
|
||||
let offset = 0
|
||||
if target is# "\t"
|
||||
let [line, offset] = self._replace_tab_target(a:lnum, col_num, line)
|
||||
elseif strdisplaywidth(target) > 1
|
||||
let line = self._replace_text_to_space(line, a:lnum, col_num, strdisplaywidth(target))
|
||||
let offset = strdisplaywidth(target) - len(target)
|
||||
endif
|
||||
|
||||
let next_offset = 0
|
||||
if len(a:hint) > 1
|
||||
" pass [] as hint to stop recursion.
|
||||
let [line, next_offset, _] = self._replace_line_for_hint(a:lnum, col_num + offset + 1, line, [])
|
||||
endif
|
||||
return [line, offset, next_offset]
|
||||
endfunction
|
||||
|
||||
" @return {(line, offset)}
|
||||
function! s:Hinter._replace_tab_target(lnum, col_num, line) abort
|
||||
let space_len = s:tab2spacelen(a:line, a:col_num)
|
||||
let line = self._replace_text_to_space(a:line, a:lnum, a:col_num, space_len)
|
||||
return [line, space_len - 1]
|
||||
endfunction
|
||||
|
||||
function! s:Hinter._replace_text_to_space(line, lnum, col_num, len) abort
|
||||
let target = printf('\%%%dc.', a:col_num)
|
||||
let line = substitute(a:line, target, repeat(' ', a:len), '')
|
||||
return line
|
||||
endfunction
|
||||
|
||||
" @param {number} col_num col_num is 1 origin like col()
|
||||
function! s:tab2spacelen(line, col_num) abort
|
||||
let before_line = a:col_num > 2 ? a:line[: a:col_num - 2]
|
||||
\ : a:col_num is# 2 ? a:line[0]
|
||||
\ : ''
|
||||
let vcol_num = 1
|
||||
for c in split(before_line, '\zs')
|
||||
let vcol_num += c is# "\t" ? s:_virtual_tab2spacelen(vcol_num) : len(c)
|
||||
endfor
|
||||
return s:_virtual_tab2spacelen(vcol_num)
|
||||
endfunction
|
||||
|
||||
function! s:_virtual_tab2spacelen(col_num) abort
|
||||
return &tabstop - ((a:col_num - 1) % &tabstop)
|
||||
endfunction
|
||||
|
||||
function! s:win2pos2hint_to_w2l2c2h(win2pos2hint) abort
|
||||
let w2l2c2h = {}
|
||||
for [winnr, pos2hint] in items(a:win2pos2hint)
|
||||
let w2l2c2h[winnr] = s:pos2hint_to_line2col2hint(pos2hint)
|
||||
endfor
|
||||
return w2l2c2h
|
||||
endfunction
|
||||
|
||||
" s:pos2hint_to_line2col2hint() converts pos2hint to line2col2hint dict whose
|
||||
" key is line number and whose value is list of tuple of col number to hint.
|
||||
" line2col2hint is for show hint with replacing line by line.
|
||||
" col should be sorted.
|
||||
" @param {{string: list<char>}} pos2hint
|
||||
" @return {number: [(number, list<char>)]}
|
||||
function! s:pos2hint_to_line2col2hint(pos2hint) abort
|
||||
let line2col2hint = {}
|
||||
let poskeys = sort(keys(a:pos2hint))
|
||||
for poskey in poskeys
|
||||
let [lnum, cnum] = s:poskey2pos(poskey)
|
||||
let line2col2hint[lnum] = get(line2col2hint, lnum, [])
|
||||
let line2col2hint[lnum] += [[cnum, a:pos2hint[poskey]]]
|
||||
endfor
|
||||
return line2col2hint
|
||||
endfunction
|
||||
|
||||
" @param {number} winnr
|
||||
function! s:move_to_win(winnr) abort
|
||||
if a:winnr !=# winnr()
|
||||
execute a:winnr . 'wincmd w'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" @param {regex} pattern
|
||||
" @return {{winnr: list<list<(number,number))>}}
|
||||
function! s:overwin.gather_poses_overwin(pattern) abort
|
||||
return s:wincall(function('s:gather_poses'), [a:pattern])
|
||||
endfunction
|
||||
|
||||
" s:gather_poses() aggregates patterm matched positions in visible current
|
||||
" window for both direction excluding poses in fold.
|
||||
" @return {{list<list<(number,number))>}}
|
||||
function! s:gather_poses(pattern) abort
|
||||
let f = s:gather_visible_matched_poses(a:pattern, s:DIRECTION.forward, s:TRUE)
|
||||
let b = s:gather_visible_matched_poses(a:pattern, s:DIRECTION.backward, s:FALSE)
|
||||
return filter(f + b, '!s:is_in_fold(v:val[0])')
|
||||
endfunction
|
||||
|
||||
" s:gather_visible_matched_poses() aggregates pattern matched positions in visible current
|
||||
" window.
|
||||
" @param {regex} pattern
|
||||
" @param {enum<DIRECTION>} direction see s:DIRECTION
|
||||
" @param {bool} allow_cursor_pos_match
|
||||
" @return {list<list<(number,number)>>} positions
|
||||
function! s:gather_visible_matched_poses(pattern, direction, allow_cursor_pos_match) abort
|
||||
let stop_line = line(a:direction is# s:DIRECTION.forward ? 'w$' : 'w0')
|
||||
let search_flag = (a:direction is# s:DIRECTION.forward ? '' : 'b')
|
||||
let c_flag = a:allow_cursor_pos_match ? 'c' : ''
|
||||
let view = winsaveview()
|
||||
let poses = []
|
||||
keepjumps let pos = searchpos(a:pattern, c_flag . search_flag, stop_line)
|
||||
while pos != [0, 0]
|
||||
let poses += [pos]
|
||||
keepjumps let pos = searchpos(a:pattern, search_flag, stop_line)
|
||||
endwhile
|
||||
call winrestview(view)
|
||||
return poses
|
||||
endfunction
|
||||
|
||||
" @param {{winnr: list<list<(number,number))>}} winnr2poses
|
||||
" @param {number?} first_winnr the top winnr poses in returned list
|
||||
" @return {list<{list<(winnr, (number,number))}>}
|
||||
function! s:winnr2poses_to_list(winnr2poses, ...) abort
|
||||
let first_winnr = get(a:, 1, winnr())
|
||||
let first_winnr_poses = []
|
||||
let other_poses = []
|
||||
for [winnr_str, poses] in items(a:winnr2poses)
|
||||
let winnr = str2nr(winnr_str)
|
||||
if winnr is# first_winnr
|
||||
let first_winnr_poses = map(copy(poses), '[winnr, v:val]')
|
||||
else
|
||||
let other_poses += map(copy(poses), '[winnr, v:val]')
|
||||
endif
|
||||
endfor
|
||||
return first_winnr_poses + other_poses
|
||||
endfunction
|
||||
|
||||
" @param {number} lnum line number
|
||||
function! s:is_in_fold(lnum) abort
|
||||
return foldclosed(a:lnum) != -1
|
||||
endfunction
|
||||
|
||||
function! s:getchar(...) abort
|
||||
let mode = get(a:, 1, 0)
|
||||
while 1
|
||||
let char = call('getchar', a:000)
|
||||
" Workaround for the <expr> mappings
|
||||
if string(char) !~# "\x80\xfd`"
|
||||
return mode == 1 ? !!char
|
||||
\ : type(char) == type(0) ? nr2char(char) : char
|
||||
endif
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
" @param {funcref} func
|
||||
" @param {arglist} list<S>
|
||||
" @param {dict?} dict for :h call()
|
||||
" @return {{winnr: <T>}}
|
||||
function! s:wincall(func, arglist, ...) abort
|
||||
let dict = get(a:, 1, {})
|
||||
let r = {}
|
||||
let start_winnr = winnr()
|
||||
let r[start_winnr] = call(a:func, a:arglist, dict)
|
||||
if s:Buffer.is_cmdwin()
|
||||
return r
|
||||
endif
|
||||
noautocmd wincmd w
|
||||
while winnr() isnot# start_winnr
|
||||
let r[winnr()] = call(a:func, a:arglist, dict)
|
||||
noautocmd wincmd w
|
||||
endwhile
|
||||
return r
|
||||
endfunction
|
||||
|
||||
function! s:show_hint_pos(lnum, cnum, char) abort
|
||||
let p = '\%'. a:lnum . 'l\%'. a:cnum . 'c.'
|
||||
exec "syntax match HitAHintTarget '". p . "' conceal cchar=". a:char
|
||||
endfunction
|
||||
|
||||
" deepextend (nest: 1)
|
||||
function! s:deepextend(expr1, expr2) abort
|
||||
let expr2 = copy(a:expr2)
|
||||
for [k, V] in items(a:expr1)
|
||||
if (type(V) is type({}) || type(V) is type([])) && has_key(expr2, k)
|
||||
let a:expr1[k] = extend(a:expr1[k], expr2[k])
|
||||
unlet expr2[k]
|
||||
endif
|
||||
unlet V
|
||||
endfor
|
||||
return extend(a:expr1, expr2)
|
||||
endfunction
|
||||
|
||||
function! s:setline(lnum, text) abort
|
||||
if getline(a:lnum) isnot# a:text
|
||||
call setline(a:lnum, a:text)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:throw(message) abort
|
||||
throw 'vital: HitAHint.Motion: ' . a:message
|
||||
endfunction
|
385
autoload/vital/_easymotion/Prelude.vim
Normal file
385
autoload/vital/_easymotion/Prelude.vim
Normal file
@ -0,0 +1,385 @@
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
if v:version ># 703 ||
|
||||
\ (v:version is 703 && has('patch465'))
|
||||
function! s:glob(expr) abort
|
||||
return glob(a:expr, 1, 1)
|
||||
endfunction
|
||||
else
|
||||
function! s:glob(expr) abort
|
||||
let R = glob(a:expr, 1)
|
||||
return split(R, '\n')
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! s:globpath(path, expr) abort
|
||||
let R = globpath(a:path, a:expr, 1)
|
||||
return split(R, '\n')
|
||||
endfunction
|
||||
|
||||
" Wrapper functions for type().
|
||||
let [
|
||||
\ s:__TYPE_NUMBER,
|
||||
\ s:__TYPE_STRING,
|
||||
\ s:__TYPE_FUNCREF,
|
||||
\ s:__TYPE_LIST,
|
||||
\ s:__TYPE_DICT,
|
||||
\ s:__TYPE_FLOAT] = [
|
||||
\ type(3),
|
||||
\ type(''),
|
||||
\ type(function('tr')),
|
||||
\ type([]),
|
||||
\ type({}),
|
||||
\ has('float') ? type(str2float('0')) : -1]
|
||||
" __TYPE_FLOAT = -1 when -float
|
||||
" This doesn't match to anything.
|
||||
|
||||
" Number or Float
|
||||
function! s:is_numeric(Value) abort
|
||||
let _ = type(a:Value)
|
||||
return _ ==# s:__TYPE_NUMBER
|
||||
\ || _ ==# s:__TYPE_FLOAT
|
||||
endfunction
|
||||
|
||||
" Number
|
||||
function! s:is_number(Value) abort
|
||||
return type(a:Value) ==# s:__TYPE_NUMBER
|
||||
endfunction
|
||||
|
||||
" Float
|
||||
function! s:is_float(Value) abort
|
||||
return type(a:Value) ==# s:__TYPE_FLOAT
|
||||
endfunction
|
||||
" String
|
||||
function! s:is_string(Value) abort
|
||||
return type(a:Value) ==# s:__TYPE_STRING
|
||||
endfunction
|
||||
" Funcref
|
||||
function! s:is_funcref(Value) abort
|
||||
return type(a:Value) ==# s:__TYPE_FUNCREF
|
||||
endfunction
|
||||
" List
|
||||
function! s:is_list(Value) abort
|
||||
return type(a:Value) ==# s:__TYPE_LIST
|
||||
endfunction
|
||||
" Dictionary
|
||||
function! s:is_dict(Value) abort
|
||||
return type(a:Value) ==# s:__TYPE_DICT
|
||||
endfunction
|
||||
|
||||
function! s:truncate_skipping(str, max, footer_width, separator) abort
|
||||
call s:_warn_deprecated('truncate_skipping', 'Data.String.truncate_skipping')
|
||||
|
||||
let width = s:wcswidth(a:str)
|
||||
if width <= a:max
|
||||
let ret = a:str
|
||||
else
|
||||
let header_width = a:max - s:wcswidth(a:separator) - a:footer_width
|
||||
let ret = s:strwidthpart(a:str, header_width) . a:separator
|
||||
\ . s:strwidthpart_reverse(a:str, a:footer_width)
|
||||
endif
|
||||
|
||||
return s:truncate(ret, a:max)
|
||||
endfunction
|
||||
|
||||
function! s:truncate(str, width) abort
|
||||
" Original function is from mattn.
|
||||
" http://github.com/mattn/googlereader-vim/tree/master
|
||||
|
||||
call s:_warn_deprecated('truncate', 'Data.String.truncate')
|
||||
|
||||
if a:str =~# '^[\x00-\x7f]*$'
|
||||
return len(a:str) < a:width ?
|
||||
\ printf('%-'.a:width.'s', a:str) : strpart(a:str, 0, a:width)
|
||||
endif
|
||||
|
||||
let ret = a:str
|
||||
let width = s:wcswidth(a:str)
|
||||
if width > a:width
|
||||
let ret = s:strwidthpart(ret, a:width)
|
||||
let width = s:wcswidth(ret)
|
||||
endif
|
||||
|
||||
if width < a:width
|
||||
let ret .= repeat(' ', a:width - width)
|
||||
endif
|
||||
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
function! s:strwidthpart(str, width) abort
|
||||
call s:_warn_deprecated('strwidthpart', 'Data.String.strwidthpart')
|
||||
|
||||
if a:width <= 0
|
||||
return ''
|
||||
endif
|
||||
let ret = a:str
|
||||
let width = s:wcswidth(a:str)
|
||||
while width > a:width
|
||||
let char = matchstr(ret, '.$')
|
||||
let ret = ret[: -1 - len(char)]
|
||||
let width -= s:wcswidth(char)
|
||||
endwhile
|
||||
|
||||
return ret
|
||||
endfunction
|
||||
function! s:strwidthpart_reverse(str, width) abort
|
||||
call s:_warn_deprecated('strwidthpart_reverse', 'Data.String.strwidthpart_reverse')
|
||||
|
||||
if a:width <= 0
|
||||
return ''
|
||||
endif
|
||||
let ret = a:str
|
||||
let width = s:wcswidth(a:str)
|
||||
while width > a:width
|
||||
let char = matchstr(ret, '^.')
|
||||
let ret = ret[len(char) :]
|
||||
let width -= s:wcswidth(char)
|
||||
endwhile
|
||||
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
if v:version >= 703
|
||||
" Use builtin function.
|
||||
function! s:wcswidth(str) abort
|
||||
call s:_warn_deprecated('wcswidth', 'Data.String.wcswidth')
|
||||
return strwidth(a:str)
|
||||
endfunction
|
||||
else
|
||||
function! s:wcswidth(str) abort
|
||||
call s:_warn_deprecated('wcswidth', 'Data.String.wcswidth')
|
||||
|
||||
if a:str =~# '^[\x00-\x7f]*$'
|
||||
return strlen(a:str)
|
||||
end
|
||||
|
||||
let mx_first = '^\(.\)'
|
||||
let str = a:str
|
||||
let width = 0
|
||||
while 1
|
||||
let ucs = char2nr(substitute(str, mx_first, '\1', ''))
|
||||
if ucs == 0
|
||||
break
|
||||
endif
|
||||
let width += s:_wcwidth(ucs)
|
||||
let str = substitute(str, mx_first, '', '')
|
||||
endwhile
|
||||
return width
|
||||
endfunction
|
||||
|
||||
" UTF-8 only.
|
||||
function! s:_wcwidth(ucs) abort
|
||||
let ucs = a:ucs
|
||||
if (ucs >= 0x1100
|
||||
\ && (ucs <= 0x115f
|
||||
\ || ucs == 0x2329
|
||||
\ || ucs == 0x232a
|
||||
\ || (ucs >= 0x2e80 && ucs <= 0xa4cf
|
||||
\ && ucs != 0x303f)
|
||||
\ || (ucs >= 0xac00 && ucs <= 0xd7a3)
|
||||
\ || (ucs >= 0xf900 && ucs <= 0xfaff)
|
||||
\ || (ucs >= 0xfe30 && ucs <= 0xfe6f)
|
||||
\ || (ucs >= 0xff00 && ucs <= 0xff60)
|
||||
\ || (ucs >= 0xffe0 && ucs <= 0xffe6)
|
||||
\ || (ucs >= 0x20000 && ucs <= 0x2fffd)
|
||||
\ || (ucs >= 0x30000 && ucs <= 0x3fffd)
|
||||
\ ))
|
||||
return 2
|
||||
endif
|
||||
return 1
|
||||
endfunction
|
||||
endif
|
||||
|
||||
let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95')
|
||||
let s:is_cygwin = has('win32unix')
|
||||
let s:is_mac = !s:is_windows && !s:is_cygwin
|
||||
\ && (has('mac') || has('macunix') || has('gui_macvim') ||
|
||||
\ (!isdirectory('/proc') && executable('sw_vers')))
|
||||
let s:is_unix = has('unix')
|
||||
|
||||
function! s:is_windows() abort
|
||||
return s:is_windows
|
||||
endfunction
|
||||
|
||||
function! s:is_cygwin() abort
|
||||
return s:is_cygwin
|
||||
endfunction
|
||||
|
||||
function! s:is_mac() abort
|
||||
return s:is_mac
|
||||
endfunction
|
||||
|
||||
function! s:is_unix() abort
|
||||
return s:is_unix
|
||||
endfunction
|
||||
|
||||
function! s:_warn_deprecated(name, alternative) abort
|
||||
try
|
||||
echohl Error
|
||||
echomsg 'Prelude.' . a:name . ' is deprecated! Please use ' . a:alternative . ' instead.'
|
||||
finally
|
||||
echohl None
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:smart_execute_command(action, word) abort
|
||||
execute a:action . ' ' . (a:word ==# '' ? '' : '`=a:word`')
|
||||
endfunction
|
||||
|
||||
function! s:escape_file_searching(buffer_name) abort
|
||||
return escape(a:buffer_name, '*[]?{}, ')
|
||||
endfunction
|
||||
|
||||
function! s:escape_pattern(str) abort
|
||||
return escape(a:str, '~"\.^$[]*')
|
||||
endfunction
|
||||
|
||||
function! s:getchar(...) abort
|
||||
let c = call('getchar', a:000)
|
||||
return type(c) == type(0) ? nr2char(c) : c
|
||||
endfunction
|
||||
|
||||
function! s:getchar_safe(...) abort
|
||||
let c = s:input_helper('getchar', a:000)
|
||||
return type(c) == type('') ? c : nr2char(c)
|
||||
endfunction
|
||||
|
||||
function! s:input_safe(...) abort
|
||||
return s:input_helper('input', a:000)
|
||||
endfunction
|
||||
|
||||
function! s:input_helper(funcname, args) abort
|
||||
let success = 0
|
||||
if inputsave() !=# success
|
||||
throw 'vital: Prelude: inputsave() failed'
|
||||
endif
|
||||
try
|
||||
return call(a:funcname, a:args)
|
||||
finally
|
||||
if inputrestore() !=# success
|
||||
throw 'vital: Prelude: inputrestore() failed'
|
||||
endif
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:set_default(var, val) abort
|
||||
if !exists(a:var) || type({a:var}) != type(a:val)
|
||||
let {a:var} = a:val
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:substitute_path_separator(path) abort
|
||||
return s:is_windows ? substitute(a:path, '\\', '/', 'g') : a:path
|
||||
endfunction
|
||||
|
||||
function! s:path2directory(path) abort
|
||||
return s:substitute_path_separator(isdirectory(a:path) ? a:path : fnamemodify(a:path, ':p:h'))
|
||||
endfunction
|
||||
|
||||
function! s:_path2project_directory_git(path) abort
|
||||
let parent = a:path
|
||||
|
||||
while 1
|
||||
let path = parent . '/.git'
|
||||
if isdirectory(path) || filereadable(path)
|
||||
return parent
|
||||
endif
|
||||
let next = fnamemodify(parent, ':h')
|
||||
if next == parent
|
||||
return ''
|
||||
endif
|
||||
let parent = next
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
function! s:_path2project_directory_svn(path) abort
|
||||
let search_directory = a:path
|
||||
let directory = ''
|
||||
|
||||
let find_directory = s:escape_file_searching(search_directory)
|
||||
let d = finddir('.svn', find_directory . ';')
|
||||
if d ==# ''
|
||||
return ''
|
||||
endif
|
||||
|
||||
let directory = fnamemodify(d, ':p:h:h')
|
||||
|
||||
" Search parent directories.
|
||||
let parent_directory = s:path2directory(
|
||||
\ fnamemodify(directory, ':h'))
|
||||
|
||||
if parent_directory !=# ''
|
||||
let d = finddir('.svn', parent_directory . ';')
|
||||
if d !=# ''
|
||||
let directory = s:_path2project_directory_svn(parent_directory)
|
||||
endif
|
||||
endif
|
||||
return directory
|
||||
endfunction
|
||||
|
||||
function! s:_path2project_directory_others(vcs, path) abort
|
||||
let vcs = a:vcs
|
||||
let search_directory = a:path
|
||||
|
||||
let find_directory = s:escape_file_searching(search_directory)
|
||||
let d = finddir(vcs, find_directory . ';')
|
||||
if d ==# ''
|
||||
return ''
|
||||
endif
|
||||
return fnamemodify(d, ':p:h:h')
|
||||
endfunction
|
||||
|
||||
function! s:path2project_directory(path, ...) abort
|
||||
let is_allow_empty = get(a:000, 0, 0)
|
||||
let search_directory = s:path2directory(a:path)
|
||||
let directory = ''
|
||||
|
||||
" Search VCS directory.
|
||||
for vcs in ['.git', '.bzr', '.hg', '.svn']
|
||||
if vcs ==# '.git'
|
||||
let directory = s:_path2project_directory_git(search_directory)
|
||||
elseif vcs ==# '.svn'
|
||||
let directory = s:_path2project_directory_svn(search_directory)
|
||||
else
|
||||
let directory = s:_path2project_directory_others(vcs, search_directory)
|
||||
endif
|
||||
if directory !=# ''
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
" Search project file.
|
||||
if directory ==# ''
|
||||
for d in ['build.xml', 'prj.el', '.project', 'pom.xml', 'package.json',
|
||||
\ 'Makefile', 'configure', 'Rakefile', 'NAnt.build',
|
||||
\ 'P4CONFIG', 'tags', 'gtags']
|
||||
let d = findfile(d, s:escape_file_searching(search_directory) . ';')
|
||||
if d !=# ''
|
||||
let directory = fnamemodify(d, ':p:h')
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
if directory ==# ''
|
||||
" Search /src/ directory.
|
||||
let base = s:substitute_path_separator(search_directory)
|
||||
if base =~# '/src/'
|
||||
let directory = base[: strridx(base, '/src/') + 3]
|
||||
endif
|
||||
endif
|
||||
|
||||
if directory ==# '' && !is_allow_empty
|
||||
" Use original path.
|
||||
let directory = search_directory
|
||||
endif
|
||||
|
||||
return s:substitute_path_separator(directory)
|
||||
endfunction
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
||||
" vim:set et ts=2 sts=2 sw=2 tw=0:
|
96
autoload/vital/_easymotion/Vim/Buffer.vim
Normal file
96
autoload/vital/_easymotion/Vim/Buffer.vim
Normal file
@ -0,0 +1,96 @@
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
function! s:_vital_loaded(V) abort
|
||||
let s:V = a:V
|
||||
let s:P = s:V.import('Prelude')
|
||||
endfunction
|
||||
|
||||
function! s:_vital_depends() abort
|
||||
return ['Prelude']
|
||||
endfunction
|
||||
|
||||
if exists('*getcmdwintype')
|
||||
function! s:is_cmdwin() abort
|
||||
return getcmdwintype() !=# ''
|
||||
endfunction
|
||||
else
|
||||
function! s:is_cmdwin() abort
|
||||
return bufname('%') ==# '[Command Line]'
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! s:open(buffer, opener) abort
|
||||
let save_wildignore = &wildignore
|
||||
let &wildignore = ''
|
||||
try
|
||||
if s:P.is_funcref(a:opener)
|
||||
let loaded = !bufloaded(a:buffer)
|
||||
call a:opener(a:buffer)
|
||||
elseif a:buffer is 0 || a:buffer is# ''
|
||||
let loaded = 1
|
||||
silent execute a:opener
|
||||
enew
|
||||
else
|
||||
let loaded = !bufloaded(a:buffer)
|
||||
if s:P.is_string(a:buffer)
|
||||
execute a:opener '`=a:buffer`'
|
||||
elseif s:P.is_number(a:buffer)
|
||||
silent execute a:opener
|
||||
execute a:buffer 'buffer'
|
||||
else
|
||||
throw 'vital: Vim.Buffer: Unknown opener type.'
|
||||
endif
|
||||
endif
|
||||
finally
|
||||
let &wildignore = save_wildignore
|
||||
endtry
|
||||
return loaded
|
||||
endfunction
|
||||
|
||||
function! s:get_selected_text(...) abort
|
||||
echohl WarningMsg
|
||||
echom "[WARN] s:get_selected_text() is deprecated. Use 's:get_last_selected()'."
|
||||
echohl None
|
||||
return call('s:get_last_selected', a:000)
|
||||
endfunction
|
||||
|
||||
" Get the last selected text in visual mode
|
||||
" without using |gv| to avoid |textlock|.
|
||||
" NOTE:
|
||||
" * This function uses |gv| only when using |CTRL-V|
|
||||
" because |gv| is the only way to get selected text
|
||||
" when using <C-v>$ .
|
||||
" Please see #192 for the details.
|
||||
" * If you don't care about |textlock|,
|
||||
" you can use simple version of this function.
|
||||
" https://github.com/vim-jp/vital.vim/commit/39aae80f3839fdbeebd838ff14d87327a6b889a9
|
||||
function! s:get_last_selected() abort
|
||||
if visualmode() ==# "\<C-v>"
|
||||
let save = getreg('"', 1)
|
||||
let save_type = getregtype('"')
|
||||
try
|
||||
normal! gv""y
|
||||
return @"
|
||||
finally
|
||||
call setreg('"', save, save_type)
|
||||
endtry
|
||||
else
|
||||
let [begin, end] = [getpos("'<"), getpos("'>")]
|
||||
let lastchar = matchstr(getline(end[1])[end[2]-1 :], '.')
|
||||
if begin[1] ==# end[1]
|
||||
let lines = [getline(begin[1])[begin[2]-1 : end[2]-2]]
|
||||
else
|
||||
let lines = [getline(begin[1])[begin[2]-1 :]]
|
||||
\ + (end[1] - begin[1] <# 2 ? [] : getline(begin[1]+1, end[1]-1))
|
||||
\ + [getline(end[1])[: end[2]-2]]
|
||||
endif
|
||||
return join(lines, "\n") . lastchar . (visualmode() ==# 'V' ? "\n" : '')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
||||
" vim:set et ts=2 sts=2 sw=2 tw=0:
|
@ -3,6 +3,15 @@ set cpo&vim
|
||||
|
||||
|
||||
|
||||
function! s:echo(hl, msg) abort
|
||||
execute 'echohl' a:hl
|
||||
try
|
||||
echo a:msg
|
||||
finally
|
||||
echohl None
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:echomsg(hl, msg) abort
|
||||
execute 'echohl' a:hl
|
||||
try
|
||||
|
@ -1,5 +1,5 @@
|
||||
easymotion
|
||||
423c2c0
|
||||
65c9afb0799cb950cbaf9258aefc6c3ad700a98f
|
||||
|
||||
Over.Commandline.Base
|
||||
Over.Commandline.Modules.Cancel
|
||||
@ -17,3 +17,4 @@ Over.Commandline.Modules.Exit
|
||||
Over.Commandline.Modules.DrawCommandline
|
||||
Over.Commandline.Modules.ExceptionMessage
|
||||
Over.Commandline.Modules.ExceptionExit
|
||||
HitAHint.Motion
|
||||
|
@ -218,6 +218,12 @@ EasyMotion <Plug> table *easymotion-plug-table*
|
||||
<Plug>(easymotion-Tln) | See |<Plug>(easymotion-Tln)|
|
||||
<Plug>(easymotion-bd-tln) | See |<Plug>(easymotion-bd-tln)|
|
||||
|
||||
Over Window Motion | (No assignment by default)
|
||||
----------------------------------|---------------------------------
|
||||
<Plug>(easymotion-overwin-f) | See |<Plug>(easymotion-overwin-f)|
|
||||
<Plug>(easymotion-overwin-f2) | See |<Plug>(easymotion-overwin-f2)|
|
||||
<Plug>(easymotion-overwin-line) | See |<Plug>(easymotion-overwin-line)|
|
||||
<Plug>(easymotion-overwin-w) | See |<Plug>(easymotion-overwin-w)|
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
More mappings *easymotion-more-mappings*
|
||||
@ -254,6 +260,83 @@ Bidirection ~
|
||||
<Plug>(easymotion-bd-n) *<Plug>(easymotion-bd-n)*
|
||||
Jump to latest "/" or "?" forward. See |n| & |N|.
|
||||
|
||||
*easymotion-overwin-motions*
|
||||
Overwin Motions~
|
||||
|
||||
Overwin motions supports moving cursor across/over |window|. Since it
|
||||
doesn't make sense that moving cursor to other window while |Visual| or
|
||||
|Operator-pending| mode, overwin motions only provides mappings for |Normal|
|
||||
mode. Please use |nmap| to use overwin motions. overwin motions only
|
||||
supports bi-directional motions.
|
||||
|
||||
<Plug>(easymotion-overwin-f){char} *n_<Plug>(easymotion-overwin-f)*
|
||||
Like |<Plug>(easymotion-s)| or |<Plug>(easymotion-bd-f)|, but
|
||||
supports moving cursor to other window. >
|
||||
nmap <Leader>f <Plug>(easymotion-overwin-f)
|
||||
xmap <Leader>f <Plug>(easymotion-bd-f)
|
||||
omap <Leader>f <Plug>(easymotion-bd-f)
|
||||
<
|
||||
<Plug>(easymotion-overwin-f2){char}{char} *n_<Plug>(easymotion-overwin-f2)*
|
||||
Like |<Plug>(easymotion-s2)| or |<Plug>(easymotion-bd-f2)|,
|
||||
but supports moving cursor to other window. >
|
||||
nmap <Leader>s <Plug>(easymotion-overwin-f2)
|
||||
xmap <Leader>s <Plug>(easymotion-bd-f2)
|
||||
omap <Leader>s <Plug>(easymotion-bd-f2)
|
||||
<
|
||||
<Plug>(easymotion-overwin-line) *n_<Plug>(easymotion-overwin-line)*
|
||||
Like |<Plug>(easymotion-bd-jk)| but supports moving cursor to
|
||||
other window. >
|
||||
nmap <Leader>L <Plug>(easymotion-overwin-line)
|
||||
xmap <Leader>L <Plug>(easymotion-bd-jk)
|
||||
omap <Leader>L <Plug>(easymotion-bd-jk)
|
||||
<
|
||||
<Plug>(easymotion-overwin-w) *n_<Plug>(easymotion-overwin-w)*
|
||||
Like |<Plug>(easymotion-bd-w)| but supports moving cursor to
|
||||
other window. >
|
||||
nmap <Leader>w <Plug>(easymotion-overwin-w)
|
||||
xmap <Leader>w <Plug>(easymotion-bd-w)
|
||||
omap <Leader>w <Plug>(easymotion-bd-w)
|
||||
<
|
||||
For ovrewin n-character find motions~
|
||||
*easymotion-do-not-support-overwin-n-char-motions*
|
||||
Ovrewin n-character find motions is equivalent to
|
||||
*<Plug>(easymotion-overwin-sn)* (doesn't exist. It's like
|
||||
|<Plug>(easymotion-sn)| but supports moving cursor to other
|
||||
window).
|
||||
EasyMotion itself doesn't support this mapping, but with
|
||||
|incsearch.vim|[1] and incsearch-easymotion.vim[2], you can get
|
||||
equivalent mappings. incsearch.vim is more compatible with
|
||||
Vim's default search and it works with EasyMotion.
|
||||
|
||||
[1] https://github.com/haya14busa/incsearch.vim
|
||||
[2] https://github.com/haya14busa/incsearch-easymotion.vim
|
||||
>
|
||||
" You can use other keymappings like <C-l> instead of <CR> if you want to
|
||||
" use these mappings as default search and somtimes want to move cursor with
|
||||
" EasyMotion.
|
||||
function! s:incsearch_config(...) abort
|
||||
return incsearch#util#deepextend(deepcopy({
|
||||
\ 'modules': [incsearch#config#easymotion#module({'overwin': 1)],
|
||||
\ 'keymap': {
|
||||
\ "\<CR>": '<Over>(easymotion)'
|
||||
\ },
|
||||
\ 'is_expr': 0
|
||||
\ }), get(a:, 1, {}))
|
||||
endfunction
|
||||
|
||||
noremap <silent><expr> / incsearch#go(<SID>incsearch_config())
|
||||
noremap <silent><expr> ? incsearch#go(<SID>incsearch_config({'command': '?'}))
|
||||
noremap <silent><expr> g/ incsearch#go(<SID>incsearch_config({'is_stay': 1}))
|
||||
<
|
||||
*easymotion-overwin-limitation*
|
||||
Since archtecture of overwin motions is different from other easymotion
|
||||
motions, there are some limitations.
|
||||
|
||||
1. |EasyMotion_do_shade| by default and currently you cannot turned off
|
||||
this option.
|
||||
2. Highlight for target is always EasyMotionTarget (|EasyMotion_highlight|)
|
||||
even for two key targets.
|
||||
|
||||
Jump To Anywhere ~
|
||||
|
||||
<Plug>(easymotion-jumptoanywhere) *<Plug>(easymotion-jumptoanywhere)*
|
||||
@ -491,11 +574,21 @@ Within line motion ~
|
||||
\ '(\l)\zs(\u)' . '|' .
|
||||
|
||||
Multi Input Find Motion!~
|
||||
*easymotion-multi-input*
|
||||
*easymotion-two-key*
|
||||
*easymotion-{find}n*
|
||||
*<Plug>(easymotion-{find}n)*
|
||||
*<Plug>(easymotion-{find}2)*
|
||||
All Find motions (s,f,F,t,T,sl,fl,Fl,tl,Tl, see below) support this feature!
|
||||
(|l| means within line motion)
|
||||
*easymotion-multi-input* *easymotion-two-key* *easymotion-{find}n*
|
||||
*<Plug>(easymotion-{find}n)* *<Plug>(easymotion-{find}2)*
|
||||
*<Plug>(easymotion-s2)* *<Plug>(easymotion-f2)* *<Plug>(easymotion-F2)*
|
||||
*<Plug>(easymotion-t2)* *<Plug>(easymotion-T2)*
|
||||
*<Plug>(easymotion-bd-f2)* *<Plug>(easymotion-bd-t2)*
|
||||
*<Plug>(easymotion-sl2)* *<Plug>(easymotion-fl2)* *<Plug>(easymotion-Fl2)*
|
||||
*<Plug>(easymotion-tl2)* *<Plug>(easymotion-Tl2)*
|
||||
*<Plug>(easymotion-bd-fl2)* *<Plug>(easymotion-bd-tl2)*
|
||||
|
||||
*<Plug>(easymotion-sn)* *<Plug>(easymotion-fn)* *<Plug>(easymotion-Fn)*
|
||||
*<Plug>(easymotion-tn)* *<Plug>(easymotion-Tn)* *<Plug>(easymotion-bd-tn)*
|
||||
*<Plug>(easymotion-sln)* *<Plug>(easymotion-fln)* *<Plug>(easymotion-Fln)*
|
||||
*<Plug>(easymotion-tln)* *<Plug>(easymotion-Tln)* *<Plug>(easymotion-bd-tln)*
|
||||
|
||||
EasyMotion provide another find motion by multi input target.
|
||||
|
||||
@ -530,19 +623,6 @@ keymapping,
|
||||
>
|
||||
let g:EasyMotion_inc_highlight = 0
|
||||
<
|
||||
All Find motions (s,f,F,t,T,sl,fl,Fl,tl,Tl, see below) support this feature!
|
||||
(|l| means within line motion)
|
||||
|
||||
*<Plug>(easymotion-sn)* *<Plug>(easymotion-fn)* *<Plug>(easymotion-Fn)*
|
||||
*<Plug>(easymotion-tn)* *<Plug>(easymotion-Tn)* *<Plug>(easymotion-bd-tn)*
|
||||
*<Plug>(easymotion-sln)* *<Plug>(easymotion-fln)* *<Plug>(easymotion-Fln)*
|
||||
*<Plug>(easymotion-tln)* *<Plug>(easymotion-Tln)* *<Plug>(easymotion-bd-tln)*
|
||||
|
||||
|
||||
*<Plug>(easymotion-s2)* *<Plug>(easymotion-f2)* *<Plug>(easymotion-F2)*
|
||||
*<Plug>(easymotion-t2)* *<Plug>(easymotion-T2)* *<Plug>(easymotion-bd-t2)*
|
||||
*<Plug>(easymotion-sl2)* *<Plug>(easymotion-fl2)* *<Plug>(easymotion-Fl2)*
|
||||
*<Plug>(easymotion-tl2)* *<Plug>(easymotion-Tl2)* *<Plug>(easymotion-bd-tl2)*
|
||||
|
||||
Find Motion Command Line~
|
||||
*easymotion-command-line*
|
||||
|
@ -132,6 +132,12 @@ call s:motion_map_helper({
|
||||
\ 'Tln' : {'fnc' : 'TL' , 'cnt' : -1, 'direction' : 1},
|
||||
\ 'bd-tln' : {'fnc' : 'TL' , 'cnt' : -1, 'direction' : 2},
|
||||
\ })
|
||||
|
||||
nnoremap <silent> <Plug>(easymotion-overwin-f) :<C-u>call EasyMotion#OverwinF(1)<CR>
|
||||
nnoremap <silent> <Plug>(easymotion-overwin-f2) :<C-u>call EasyMotion#OverwinF(2)<CR>
|
||||
nnoremap <silent> <Plug>(easymotion-overwin-line) :<C-u>call EasyMotion#overwin#line()<CR>
|
||||
nnoremap <silent> <Plug>(easymotion-overwin-w) :<C-u>call EasyMotion#overwin#w()<CR>
|
||||
|
||||
"}}}
|
||||
|
||||
" -- Word Motion {{{
|
||||
|
Loading…
Reference in New Issue
Block a user