From 00fc3281c34f2d3dcc848340bcb3bc1815577edf Mon Sep 17 00:00:00 2001 From: haya14busa Date: Fri, 15 Jan 2016 00:59:56 +0900 Subject: [PATCH] Now, you can move cursor even over/across window - implement (easymotion-overwin-f) - implement (easymotion-overwin-f2) - TODO: - test - doc - care configuration --- autoload/EasyMotion.vim | 4 + autoload/EasyMotion/command_line.vim | 6 + autoload/EasyMotion/overwin.vim | 6 + autoload/vital/_easymotion.vim | 24 +- autoload/vital/_easymotion/Data/List.vim | 6 +- autoload/vital/_easymotion/Data/Set.vim | 269 ++++++++ autoload/vital/_easymotion/HitAHint/Hint.vim | 111 ++++ .../vital/_easymotion/HitAHint/Motion.vim | 623 ++++++++++++++++++ autoload/vital/_easymotion/Prelude.vim | 385 +++++++++++ autoload/vital/_easymotion/Vim/Buffer.vim | 96 +++ autoload/vital/_easymotion/Vim/Message.vim | 9 + autoload/vital/easymotion.vital | 3 +- plugin/EasyMotion.vim | 6 +- 13 files changed, 1526 insertions(+), 22 deletions(-) create mode 100644 autoload/EasyMotion/overwin.vim create mode 100644 autoload/vital/_easymotion/Data/Set.vim create mode 100644 autoload/vital/_easymotion/HitAHint/Hint.vim create mode 100644 autoload/vital/_easymotion/HitAHint/Motion.vim create mode 100644 autoload/vital/_easymotion/Prelude.vim create mode 100644 autoload/vital/_easymotion/Vim/Buffer.vim diff --git a/autoload/EasyMotion.vim b/autoload/EasyMotion.vim index 18f5a94..421c78b 100644 --- a/autoload/EasyMotion.vim +++ b/autoload/EasyMotion.vim @@ -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 diff --git a/autoload/EasyMotion/command_line.vim b/autoload/EasyMotion/command_line.vim index 9a80fd8..f66c509 100644 --- a/autoload/EasyMotion/command_line.vim +++ b/autoload/EasyMotion/command_line.vim @@ -150,6 +150,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) "{{{ diff --git a/autoload/EasyMotion/overwin.vim b/autoload/EasyMotion/overwin.vim new file mode 100644 index 0000000..e6cdb07 --- /dev/null +++ b/autoload/EasyMotion/overwin.vim @@ -0,0 +1,6 @@ +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) +endfunction diff --git a/autoload/vital/_easymotion.vim b/autoload/vital/_easymotion.vim index 7f81595..3ce8c6b 100644 --- a/autoload/vital/_easymotion.vim +++ b/autoload/vital/_easymotion.vim @@ -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 + call module._vital_loaded(V) 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') diff --git a/autoload/vital/_easymotion/Data/List.vim b/autoload/vital/_easymotion/Data/List.vim index c8aac65..a98671c 100644 --- a/autoload/vital/_easymotion/Data/List.vim +++ b/autoload/vital/_easymotion/Data/List.vim @@ -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. diff --git a/autoload/vital/_easymotion/Data/Set.vim b/autoload/vital/_easymotion/Data/Set.vim new file mode 100644 index 0000000..4cbb17c --- /dev/null +++ b/autoload/vital/_easymotion/Data/Set.vim @@ -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 diff --git a/autoload/vital/_easymotion/HitAHint/Hint.vim b/autoload/vital/_easymotion/HitAHint/Hint.vim new file mode 100644 index 0000000..3360232 --- /dev/null +++ b/autoload/vital/_easymotion/HitAHint/Hint.vim @@ -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(''), '\zs\d\+\ze__SID$') + endfunction + let s:_s = '' . 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} targets +" @param {list} 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 diff --git a/autoload/vital/_easymotion/HitAHint/Motion.vim b/autoload/vital/_easymotion/HitAHint/Motion.vim new file mode 100644 index 0000000..841cd14 --- /dev/null +++ b/autoload/vital/_easymotion/HitAHint/Motion.vim @@ -0,0 +1,623 @@ +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;', +\ 'user_upper': s:FALSE, +\ 'highlight': { +\ 'shade': 'HitAHintShade', +\ 'target': 'HitAHintTarget', +\ }, +\ } +\ } + +function! s:_init_hl() abort + highlight HitAHintShade ctermfg=242 guifg=#777777 + " highlight HitAHintTarget cterm=bold ctermfg=196 gui=bold guifg=#ff0000 + " highlight HitAHintTarget term=standout ctermfg=81 gui=bold guifg=#66D9EF + highlight HitAHintTarget ctermfg=81 gui=bold 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 + return self.choose_prompt(s:create_hint_dict(a:winnr2poses, a:keys)) +endfunction + +function! s:create_hint_dict(winnr2poses, keys) abort + let wposes = s:winnr2poses_to_list(a:winnr2poses) + let hint_dict = s:Hint.create(wposes, a:keys) + return hint_dict +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}}} 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.user_upper + let c = toupper(c) + endif + finally + call hinter.after() + endtry + + 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)>}}} +let s:Hinter = { +\ 'save_lines': {}, +\ 'w2l2c2h': {}, +\ 'winnrs': [], +\ 'save_syntax': {}, +\ 'save_conceallevel': {}, +\ 'save_concealcursor': {}, +\ 'save_modified': {}, +\ 'save_modifiable': {}, +\ 'save_readonly': {}, +\ 'highlight_ids': {}, +\ } + +" @param {{winnr: {string: list}}} +" function! s:Hinter.new(win2pos2hint) abort +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_env() + call self.restore_conceal_in_other_win() + call self.restore_lines() +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) + if line isnot# getline(lnum) + call setline(lnum, line) + endif + 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_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 + + setlocal modifiable + setlocal noreadonly + + let self.save_conceal = s:PHighlight.get('Conceal') + + 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 + 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) + 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] + + " Turn off &l:modified before restoring thie value so that restore undo + " state. It's important to turn off this option after manipulating + " buffer text. + let &l:modified = 0 + 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 + finally + call s:move_to_win(nr) + endtry +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}} pos2hint +" @return {number: [(number, list)]} +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 cnum isnot# 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 +endfunction + +" ._replace_line_for_hint() replaces line to show hints. +" - It appends space if the line is empty +" - It replaces to space if the target character is +" - It replaces next target character if it's 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) + call setline(a:lnum, line) + 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), '') + call setline(a:lnum, line) + 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}} pos2hint +" @return {number: [(number, list)]} +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}} +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}} +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 see s:DIRECTION +" @param {bool} allow_cursor_pos_match +" @return {list>} 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}} 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(...) + let mode = get(a:, 1, 0) + while 1 + let char = call('getchar', a:000) + " Workaround for the 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 +" @param {dict?} dict for :h call() +" @return {{winnr: }} +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 diff --git a/autoload/vital/_easymotion/Prelude.vim b/autoload/vital/_easymotion/Prelude.vim new file mode 100644 index 0000000..87d49b7 --- /dev/null +++ b/autoload/vital/_easymotion/Prelude.vim @@ -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: diff --git a/autoload/vital/_easymotion/Vim/Buffer.vim b/autoload/vital/_easymotion/Vim/Buffer.vim new file mode 100644 index 0000000..b323f41 --- /dev/null +++ b/autoload/vital/_easymotion/Vim/Buffer.vim @@ -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 $ . +" 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() ==# "\" + 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: diff --git a/autoload/vital/_easymotion/Vim/Message.vim b/autoload/vital/_easymotion/Vim/Message.vim index b807a3f..3e69d60 100644 --- a/autoload/vital/_easymotion/Vim/Message.vim +++ b/autoload/vital/_easymotion/Vim/Message.vim @@ -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 diff --git a/autoload/vital/easymotion.vital b/autoload/vital/easymotion.vital index a467696..52c6b09 100644 --- a/autoload/vital/easymotion.vital +++ b/autoload/vital/easymotion.vital @@ -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 diff --git a/plugin/EasyMotion.vim b/plugin/EasyMotion.vim index 5fc1c62..811c7c5 100644 --- a/plugin/EasyMotion.vim +++ b/plugin/EasyMotion.vim @@ -130,7 +130,11 @@ call s:motion_map_helper({ \ 'tln' : {'fnc' : 'TL' , 'cnt' : -1, 'direction' : 0}, \ 'Tln' : {'fnc' : 'TL' , 'cnt' : -1, 'direction' : 1}, \ 'bd-tln' : {'fnc' : 'TL' , 'cnt' : -1, 'direction' : 2}, - \ }) + \ }) + +nnoremap (easymotion-overwin-f) :call EasyMotion#OverwinF(1) +nnoremap (easymotion-overwin-f2) :call EasyMotion#OverwinF(2) + "}}} " -- Word Motion {{{