diff --git a/autoload/vital.vim b/autoload/vital.vim index bc0b525..1004dfc 100644 --- a/autoload/vital.vim +++ b/autoload/vital.vim @@ -1,4 +1,4 @@ -function! vital#of(name) +function! vital#of(name) abort let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital') let file = split(files, "\n") if empty(file) diff --git a/autoload/vital/_easymotion.vim b/autoload/vital/_easymotion.vim index 28663c5..a08013a 100644 --- a/autoload/vital/_easymotion.vim +++ b/autoload/vital/_easymotion.vim @@ -5,7 +5,7 @@ let s:globpath_third_arg = v:version > 702 || v:version == 702 && has('patch51') let s:loaded = {} -function! s:import(name, ...) +function! s:import(name, ...) abort let target = {} let functions = [] for a in a:000 @@ -29,7 +29,7 @@ function! s:import(name, ...) return target endfunction -function! s:load(...) dict +function! s:load(...) dict abort for arg in a:000 let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg] let target = split(join(as, ''), '\W\+') @@ -55,21 +55,21 @@ function! s:load(...) dict return self endfunction -function! s:unload() +function! s:unload() abort let s:loaded = {} endfunction -function! s:exists(name) +function! s:exists(name) abort return s:_get_module_path(a:name) !=# '' endfunction -function! s:search(pattern) +function! s:search(pattern) abort let paths = s:_vital_files(a:pattern) let modules = sort(map(paths, 's:_file2module(v:val)')) return s:_uniq(modules) endfunction -function! s:expand_modules(entry, all) +function! s:expand_modules(entry, all) abort if type(a:entry) == type([]) let candidates = s:_concat(map(copy(a:entry), 's:search(v:val)')) if empty(candidates) @@ -93,7 +93,7 @@ function! s:expand_modules(entry, all) return modules endfunction -function! s:_import(name) +function! s:_import(name) abort if type(a:name) == type(0) return s:_build_module(a:name) endif @@ -116,7 +116,7 @@ function! s:_import(name) return s:_build_module(sid) endfunction -function! s:_get_module_path(name) +function! s:_get_module_path(name) abort if s:_is_absolute_path(a:name) && filereadable(a:name) return a:name endif @@ -133,7 +133,7 @@ function! s:_get_module_path(name) return path !=# '' ? path : '' endfunction -function! s:_get_sid_by_script(path) +function! s:_get_sid_by_script(path) abort let path = s:_unify_path(a:path) for line in filter(split(s:_redir('scriptnames'), "\n"), \ 'stridx(v:val, s:self_version) > 0') @@ -145,7 +145,7 @@ function! s:_get_sid_by_script(path) return 0 endfunction -function! s:_file2module(file) +function! s:_file2module(file) abort let filename = fnamemodify(a:file, ':p:gs?[\\/]\+?/?') let tail = matchstr(filename, 'autoload/vital/_\w\+/\zs.*\ze\.vim$') return join(split(tail, '[\\/]\+'), '.') @@ -157,7 +157,7 @@ if filereadable(expand(':r') . '.VIM') " Note: On windows, vim can't expand path names from 8.3 formats. " So if getting full path via and $HOME was set as 8.3 format, " vital load duplicated scripts. Below's :~ avoid this issue. - function! s:_unify_path(path) + function! s:_unify_path(path) abort if has_key(s:_unify_path_cache, a:path) return s:_unify_path_cache[a:path] endif @@ -167,24 +167,24 @@ if filereadable(expand(':r') . '.VIM') return value endfunction else - function! s:_unify_path(path) + function! s:_unify_path(path) abort return resolve(fnamemodify(a:path, ':p:gs?[\\/]\+?/?')) endfunction endif if s:globpath_third_arg - function! s:_runtime_files(path) + function! s:_runtime_files(path) abort return split(globpath(&runtimepath, a:path, 1), "\n") endfunction else - function! s:_runtime_files(path) + function! s:_runtime_files(path) abort return split(globpath(&runtimepath, a:path), "\n") endfunction endif let s:_vital_files_cache_runtimepath = '' let s:_vital_files_cache = [] -function! s:_vital_files(pattern) +function! s:_vital_files(pattern) abort if s:_vital_files_cache_runtimepath !=# &runtimepath let path = printf('autoload/vital/%s/**/*.vim', s:self_version) let s:_vital_files_cache = s:_runtime_files(path) @@ -200,16 +200,16 @@ endfunction " Copy from System.Filepath if has('win16') || has('win32') || has('win64') - function! s:_is_absolute_path(path) + function! s:_is_absolute_path(path) abort return a:path =~? '^[a-z]:[/\\]' endfunction else - function! s:_is_absolute_path(path) + function! s:_is_absolute_path(path) abort return a:path[0] ==# '/' endfunction endif -function! s:_build_module(sid) +function! s:_build_module(sid) abort if has_key(s:loaded, a:sid) return copy(s:loaded[a:sid]) endif @@ -243,13 +243,13 @@ function! s:_build_module(sid) endfunction if exists('+regexpengine') - function! s:_get_functions(sid) + function! s:_get_functions(sid) abort let funcs = s:_redir(printf("function /\\%%#=2^\%d_", a:sid)) let map_pat = '' . a:sid . '_\zs\w\+' return map(split(funcs, "\n"), 'matchstr(v:val, map_pat)') endfunction else - function! s:_get_functions(sid) + function! s:_get_functions(sid) abort let prefix = '' . a:sid . '_' let funcs = s:_redir('function') let filter_pat = '^\s*function ' . prefix @@ -261,11 +261,11 @@ else endif if exists('*uniq') - function! s:_uniq(list) + function! s:_uniq(list) abort return uniq(a:list) endfunction else - function! s:_uniq(list) + function! s:_uniq(list) abort let i = len(a:list) - 1 while 0 < i if a:list[i] ==# a:list[i - 1] @@ -279,7 +279,7 @@ else endfunction endif -function! s:_concat(lists) +function! s:_concat(lists) abort let result_list = [] for list in a:lists let result_list += list @@ -287,7 +287,7 @@ function! s:_concat(lists) return result_list endfunction -function! s:_redir(cmd) +function! s:_redir(cmd) abort let [save_verbose, save_verbosefile] = [&verbose, &verbosefile] set verbose=0 verbosefile= redir => res @@ -297,7 +297,7 @@ function! s:_redir(cmd) return res endfunction -function! vital#{s:self_version}#new() +function! vital#{s:self_version}#new() abort return s:_import('') endfunction diff --git a/autoload/vital/_easymotion/Data/List.vim b/autoload/vital/_easymotion/Data/List.vim index e9a62c2..1b42dbf 100644 --- a/autoload/vital/_easymotion/Data/List.vim +++ b/autoload/vital/_easymotion/Data/List.vim @@ -3,38 +3,38 @@ let s:save_cpo = &cpo set cpo&vim -function! s:pop(list) +function! s:pop(list) abort return remove(a:list, -1) endfunction -function! s:push(list, val) +function! s:push(list, val) abort call add(a:list, a:val) return a:list endfunction -function! s:shift(list) +function! s:shift(list) abort return remove(a:list, 0) endfunction -function! s:unshift(list, val) +function! s:unshift(list, val) abort return insert(a:list, a:val) endfunction -function! s:cons(x, xs) +function! s:cons(x, xs) abort return [a:x] + a:xs endfunction -function! s:conj(xs, x) +function! s:conj(xs, x) abort return a:xs + [a:x] endfunction " Removes duplicates from a list. -function! s:uniq(list) +function! s:uniq(list) abort return s:uniq_by(a:list, 'v:val') endfunction " Removes duplicates from a list. -function! s:uniq_by(list, f) +function! s:uniq_by(list, f) abort let list = map(copy(a:list), printf('[v:val, %s]', a:f)) let i = 0 let seen = {} @@ -50,7 +50,7 @@ function! s:uniq_by(list, f) return map(list, 'v:val[0]') endfunction -function! s:clear(list) +function! s:clear(list) abort if !empty(a:list) unlet! a:list[0 : len(a:list) - 1] endif @@ -59,7 +59,7 @@ endfunction " Concatenates a list of lists. " XXX: Should we verify the input? -function! s:concat(list) +function! s:concat(list) abort let memo = [] for Value in a:list let memo += Value @@ -68,7 +68,7 @@ function! s:concat(list) endfunction " Take each elements from lists to a new list. -function! s:flatten(list, ...) +function! s:flatten(list, ...) abort let limit = a:0 > 0 ? a:1 : -1 let memo = [] if limit == 0 @@ -87,7 +87,7 @@ endfunction " Sorts a list with expression to compare each two values. " a:a and a:b can be used in {expr}. -function! s:sort(list, expr) +function! s:sort(list, expr) abort if type(a:expr) == type(function('function')) return sort(a:list, a:expr) endif @@ -95,14 +95,14 @@ function! s:sort(list, expr) return sort(a:list, 's:_compare') endfunction -function! s:_compare(a, b) +function! s:_compare(a, b) abort return eval(s:expr) endfunction " Sorts a list using a set of keys generated by mapping the values in the list " through the given expr. " v:val is used in {expr} -function! s:sort_by(list, expr) +function! s:sort_by(list, expr) abort let pairs = map(a:list, printf('[v:val, %s]', a:expr)) return map(s:sort(pairs, \ 'a:a[1] ==# a:b[1] ? 0 : a:a[1] ># a:b[1] ? 1 : -1'), 'v:val[0]') @@ -111,7 +111,7 @@ endfunction " Returns a maximum value in {list} through given {expr}. " Returns 0 if {list} is empty. " v:val is used in {expr} -function! s:max_by(list, expr) +function! s:max_by(list, expr) abort if empty(a:list) return 0 endif @@ -123,13 +123,13 @@ endfunction " Returns 0 if {list} is empty. " v:val is used in {expr} " FIXME: -0x80000000 == 0x80000000 -function! s:min_by(list, expr) +function! s:min_by(list, expr) abort return s:max_by(a:list, '-(' . a:expr . ')') endfunction " Returns List of character sequence between [a:from, a:to] " e.g.: s:char_range('a', 'c') returns ['a', 'b', 'c'] -function! s:char_range(from, to) +function! s:char_range(from, to) abort return map( \ range(char2nr(a:from), char2nr(a:to)), \ 'nr2char(v:val)' @@ -138,21 +138,21 @@ endfunction " Returns true if a:list has a:value. " Returns false otherwise. -function! s:has(list, value) +function! s:has(list, value) abort return index(a:list, a:value) isnot -1 endfunction " Returns true if a:list[a:index] exists. " Returns false otherwise. " NOTE: Returns false when a:index is negative number. -function! s:has_index(list, index) +function! s:has_index(list, index) abort " Return true when negative index? " let index = a:index >= 0 ? a:index : len(a:list) + a:index return 0 <= a:index && a:index < len(a:list) endfunction " similar to Haskell's Data.List.span -function! s:span(f, xs) +function! s:span(f, xs) abort let border = len(a:xs) for i in range(len(a:xs)) if !eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g')) @@ -164,41 +164,41 @@ function! s:span(f, xs) endfunction " similar to Haskell's Data.List.break -function! s:break(f, xs) +function! s:break(f, xs) abort return s:span(printf('!(%s)', a:f), a:xs) endfunction " similar to Haskell's Data.List.takeWhile -function! s:take_while(f, xs) +function! s:take_while(f, xs) abort return s:span(a:f, a:xs)[0] endfunction " similar to Haskell's Data.List.partition -function! s:partition(f, xs) +function! s:partition(f, xs) abort return [filter(copy(a:xs), a:f), filter(copy(a:xs), '!(' . a:f . ')')] endfunction " similar to Haskell's Prelude.all -function! s:all(f, xs) +function! s:all(f, xs) abort return !s:any(printf('!(%s)', a:f), a:xs) endfunction " similar to Haskell's Prelude.any -function! s:any(f, xs) +function! s:any(f, xs) abort return !empty(filter(map(copy(a:xs), a:f), 'v:val')) endfunction " similar to Haskell's Prelude.and -function! s:and(xs) +function! s:and(xs) abort return s:all('v:val', a:xs) endfunction " similar to Haskell's Prelude.or -function! s:or(xs) +function! s:or(xs) abort return s:any('v:val', a:xs) endfunction -function! s:map_accum(expr, xs, init) +function! s:map_accum(expr, xs, init) abort let memo = [] let init = a:init for x in a:xs @@ -211,7 +211,7 @@ function! s:map_accum(expr, xs, init) endfunction " similar to Haskell's Prelude.foldl -function! s:foldl(f, init, xs) +function! s:foldl(f, init, xs) abort let memo = a:init for x in a:xs let expr = substitute(a:f, 'v:val', string(x), 'g') @@ -223,7 +223,7 @@ function! s:foldl(f, init, xs) endfunction " similar to Haskell's Prelude.foldl1 -function! s:foldl1(f, xs) +function! s:foldl1(f, xs) abort if len(a:xs) == 0 throw 'foldl1' endif @@ -231,12 +231,12 @@ function! s:foldl1(f, xs) endfunction " similar to Haskell's Prelude.foldr -function! s:foldr(f, init, xs) +function! s:foldr(f, init, xs) abort return s:foldl(a:f, a:init, reverse(copy(a:xs))) endfunction " similar to Haskell's Prelude.fold11 -function! s:foldr1(f, xs) +function! s:foldr1(f, xs) abort if len(a:xs) == 0 throw 'foldr1' endif @@ -244,12 +244,12 @@ function! s:foldr1(f, xs) endfunction " similar to python's zip() -function! s:zip(...) +function! s:zip(...) abort return map(range(min(map(copy(a:000), 'len(v:val)'))), "map(copy(a:000), 'v:val['.v:val.']')") endfunction " similar to zip(), but goes until the longer one. -function! s:zip_fill(xs, ys, filler) +function! s:zip_fill(xs, ys, filler) abort if empty(a:xs) && empty(a:ys) return [] elseif empty(a:ys) @@ -262,14 +262,14 @@ function! s:zip_fill(xs, ys, filler) endfunction " Inspired by Ruby's with_index method. -function! s:with_index(list, ...) +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)) endfunction " similar to Ruby's detect or Haskell's find. " TODO spec and doc -function! s:find(list, default, f) +function! s:find(list, default, f) abort for x in a:list if eval(substitute(a:f, 'v:val', string(x), 'g')) return x @@ -278,14 +278,62 @@ function! s:find(list, default, f) return a:default endfunction +" Returns the index of the first element which satisfies the given expr. +function! s:find_index(xs, f, ...) abort + let len = len(a:xs) + let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : 0 + let default = a:0 > 1 ? a:2 : -1 + if start >=# len || start < 0 + return default + endif + for i in range(start, len - 1) + if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g')) + return i + endif + endfor + return default +endfunction + +" Returns the index of the last element which satisfies the given expr. +function! s:find_last_index(xs, f, ...) abort + let len = len(a:xs) + let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : len - 1 + let default = a:0 > 1 ? a:2 : -1 + if start >=# len || start < 0 + return default + endif + for i in range(start, 0, -1) + if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g')) + return i + endif + endfor + return default +endfunction + +" Similar to find_index but returns the list of indices satisfying the given expr. +function! s:find_indices(xs, f, ...) abort + let len = len(a:xs) + let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : 0 + let result = [] + if start >=# len || start < 0 + return result + endif + for i in range(start, len - 1) + if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g')) + call add(result, i) + endif + endfor + return result +endfunction + " Return non-zero if a:list1 and a:list2 have any common item(s). " Return zero otherwise. -function! s:has_common_items(list1, list2) +function! s:has_common_items(list1, list2) abort return !empty(filter(copy(a:list1), 'index(a:list2, v:val) isnot -1')) endfunction " similar to Ruby's group_by. -function! s:group_by(xs, f) +function! s:group_by(xs, f) abort let result = {} let list = map(copy(a:xs), printf('[v:val, %s]', a:f)) for x in list @@ -301,6 +349,83 @@ function! s:group_by(xs, f) return result endfunction +function! s:_default_compare(a, b) abort + return a:a <# a:b ? -1 : a:a ># a:b ? 1 : 0 +endfunction + +function! s:binary_search(list, value, ...) abort + let Predicate = a:0 >= 1 ? a:1 : 's:_default_compare' + let dic = a:0 >= 2 ? a:2 : {} + let start = 0 + let end = len(a:list) - 1 + + while 1 + if start > end + return -1 + endif + + let middle = (start + end) / 2 + + let compared = call(Predicate, [a:value, a:list[middle]], dic) + + if compared < 0 + let end = middle - 1 + elseif compared > 0 + let start = middle + 1 + else + return middle + endif + endwhile +endfunction + +function! s:product(lists) abort + let result = [[]] + for pool in a:lists + let tmp = [] + for x in result + let tmp += map(copy(pool), 'x + [v:val]') + endfor + let result = tmp + endfor + return result +endfunction + +function! s:permutations(list, ...) abort + if a:0 > 1 + throw 'vital: Data.List: too many arguments' + endif + let r = a:0 == 1 ? a:1 : len(a:list) + if r > len(a:list) + return [] + elseif r < 0 + throw 'vital: Data.List: {r} must be non-negative integer' + endif + let n = len(a:list) + let result = [] + for indices in s:product(map(range(r), 'range(n)')) + if len(s:uniq(indices)) == r + call add(result, map(indices, 'a:list[v:val]')) + endif + endfor + return result +endfunction + +function! s:combinations(list, r) abort + if a:r > len(a:list) + return [] + elseif a:r < 0 + throw 'vital: Data:List: {r} must be non-negative integer' + endif + let n = len(a:list) + let result = [] + for indices in s:permutations(range(n), a:r) + if s:sort(copy(indices), 'a:a - a:b') == indices + call add(result, map(indices, 'a:list[v:val]')) + endif + endfor + return result +endfunction + let &cpo = s:save_cpo unlet s:save_cpo diff --git a/autoload/vital/_easymotion/Over/Commandline/Base.vim b/autoload/vital/_easymotion/Over/Commandline/Base.vim index dc44464..d14514d 100644 --- a/autoload/vital/_easymotion/Over/Commandline/Base.vim +++ b/autoload/vital/_easymotion/Over/Commandline/Base.vim @@ -7,8 +7,8 @@ function! s:_vital_loaded(V) let s:V = a:V let s:String = s:V.import("Over.String") let s:Signals = s:V.import("Over.Signals") + let s:Input = s:V.import("Over.Input") let s:Module = s:V.import("Over.Commandline.Modules") - let s:List = s:V.import("Data.List") let s:base.variables.modules = s:Signals.make() function! s:base.variables.modules.get_slot(val) return a:val.slot.module @@ -20,8 +20,8 @@ function! s:_vital_depends() return [ \ "Over.String", \ "Over.Signals", +\ "Over.Input", \ "Over.Commandline.Modules", -\ "Data.List", \ ] endfunction @@ -168,6 +168,12 @@ function! s:base.backward() endfunction +function! s:base.backward_word(...) + let pat = get(a:, 1, '\k\+\s*\|.') + return matchstr(self.backward(), '\%(' . pat . '\)$') +endfunction + + function! s:base.connect(module, ...) if type(a:module) == type("") return call(self.connect, [s:Module.make(a:module)] + a:000, self) @@ -314,7 +320,7 @@ function! s:base.get(...) let Old_execute = self.execute let self.execute = self.__empty try - let exit_code = self.start() + let exit_code = call(self.start, a:000, self) if exit_code == 0 return self.getline() endif @@ -325,13 +331,36 @@ function! s:base.get(...) endfunction -function! s:base._init() +function! s:base.input_key_stack() + return self.variables.input_key_stack +endfunction + + +function! s:base.input_key_stack_string() + return join(self.variables.input_key_stack, "") +endfunction + + +function! s:base.set_input_key_stack(stack) + let self.variables.input_key_stack = a:stack + return self.variables.input_key_stack +endfunction + + +function! s:base._init_variables() let self.variables.tap_key = "" let self.variables.char = "" let self.variables.input = "" let self.variables.exit = 0 let self.variables.exit_code = 1 let self.variables.enable_keymapping = 1 + let self.variables.input_key_stack = [] + let self.line = deepcopy(s:String.make()) +endfunction + + +function! s:base._init() + call self._init_variables() call self.hl_cursor_off() if !hlexists(self.highlights.cursor) execute "highlight link " . self.highlights.cursor . " Cursor" @@ -360,6 +389,17 @@ function! s:base._execute(command) endfunction +function! s:base._input_char(char) + let char = a:char + let self.variables.input_key = char + let self.variables.char = char + call self.setchar(self.variables.char) + call self.callevent("on_char_pre") + call self.insert(self.variables.input) + call self.callevent("on_char") +endfunction + + function! s:base._input(input, ...) let self.variables.input_key = a:input if self.is_enable_keymapping() @@ -367,39 +407,47 @@ function! s:base._input(input, ...) else let key = a:input endif + if key == "" + return + endif - for char in s:_split_keys(key) - let self.variables.input_key = char - let self.variables.char = char - call self.setchar(self.variables.char) - call self.callevent("on_char_pre") - call self.insert(self.variables.input) - call self.callevent("on_char") - endfor + call self.set_input_key_stack(s:String.split_by_keys(key)) + while !(empty(self.input_key_stack()) || self._is_exit()) + call self._input_char(remove(self.input_key_stack(), 0)) + endwhile +endfunction + + +function! s:base._update() +" call self.callevent("on_update") +" if !getchar(1) +" continue +" endif +" +" call self._input(s:getchar(0)) +" call self.draw() + + call self.callevent("on_update") + call self._input(s:Input.getchar()) + if self._is_exit() + return -1 + endif + call self.draw() endfunction function! s:base._main(...) try call self._init() - let self.line = deepcopy(s:String.make(get(a:, 1, ""))) call self.callevent("on_enter") + call self._input(get(a:, 1, "")) call self.draw() while !self._is_exit() try -" call self.callevent("on_update") -" if !getchar(1) -" continue -" endif -" -" call self._input(s:_getchar(0)) -" call self.draw() - call self._input(s:_getchar()) - if self._is_exit() + if self._update() break endif - call self.draw() catch call self.callevent("on_exception") endtry @@ -438,7 +486,7 @@ endfunction function! s:_unmap(mapping, key) - let keys = s:_split_keys(a:key) + let keys = s:String.split_by_keys(a:key) if len(keys) > 1 return join(map(keys, 's:_unmap(a:mapping, v:val)'), '') endif @@ -468,158 +516,5 @@ function! s:base._get_keymapping() endfunction -function! s:_getchar(...) - while 1 - let char = call("getchar", a:000) - " Workaround for the mappings - if char !=# "\x80\xfd`" - return type(char) == type(0) ? nr2char(char) : char - endif - endwhile -endfunction - - - -function! s:_split(str, pat) - let pat = '\%#=2' . a:pat - let list = split(a:str, pat . '\zs') - return s:List.flatten(map(list, 'v:val == a:pat ? a:pat : v:val =~ pat . ''$'' ? split(v:val, pat) + [a:pat] : v:val')) -endfunction - - -function! s:_split_keystring(str, pats, ...) - if a:str =~ '^(.\{-})$' -\ || a:str =~ "^\(.\\{-})$" - return [a:str] - endif - let pats = a:pats - let index = get(a:, 1, 0) - if !exists("+regexpengine") -\ || index > len(pats) -\ || len(filter(copy(pats), 'a:str =~ ''\%#=2'' . v:val')) == 0 - if len(filter(copy(pats), 'a:str ==# v:val')) == 0 - return split(a:str, '\zs') - else - return [a:str] - endif - endif - if len(filter(copy(pats), 'a:str == v:val')) == 1 - return [a:str] - endif - - let result = [] - let pat = pats[index] - let list = s:_split(a:str, pat) - let result += eval(join(map(list, "s:_split_keystring(v:val, pats, index+1)"), "+")) - return result -endfunction - - - - -let s:special_keys = [ -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\ "\", -\] -" Issues #45 -" \ "\", -" \ "\", - -function! s:_split_keys(str) - return s:_split_keystring(a:str, s:special_keys) -endfunction - - - let &cpo = s:save_cpo unlet s:save_cpo diff --git a/autoload/vital/_easymotion/Over/Commandline/Modules/CursorMove.vim b/autoload/vital/_easymotion/Over/Commandline/Modules/CursorMove.vim index c3a02e8..fb27800 100644 --- a/autoload/vital/_easymotion/Over/Commandline/Modules/CursorMove.vim +++ b/autoload/vital/_easymotion/Over/Commandline/Modules/CursorMove.vim @@ -21,6 +21,15 @@ function! s:module.on_char_pre(cmdline) \ || a:cmdline.is_input("\") call a:cmdline.setline(a:cmdline.line.length()) call a:cmdline.setchar('') + elseif a:cmdline.is_input("\") +\ || a:cmdline.is_input("\") + call a:cmdline.setline(strridx(a:cmdline.backward()[:-2], ' ') + 1) + call a:cmdline.setchar('') + elseif a:cmdline.is_input("\") +\ || a:cmdline.is_input("\") + let p = stridx(a:cmdline.forward()[1:], ' ') + call a:cmdline.setline(p != -1 ? a:cmdline.line.pos() + p + 2 : a:cmdline.line.length()) + call a:cmdline.setchar('') endif endfunction diff --git a/autoload/vital/_easymotion/Over/Commandline/Modules/Delete.vim b/autoload/vital/_easymotion/Over/Commandline/Modules/Delete.vim index 6889115..690020b 100644 --- a/autoload/vital/_easymotion/Over/Commandline/Modules/Delete.vim +++ b/autoload/vital/_easymotion/Over/Commandline/Modules/Delete.vim @@ -19,7 +19,8 @@ function! s:module.on_char_pre(cmdline) call a:cmdline.line.remove_pos() call a:cmdline.setchar('') elseif a:cmdline.is_input("\") - let backward = matchstr(a:cmdline.backward(), '^\zs.\{-}\ze\(\(\w*\)\|\(.\)\)$') + let word = a:cmdline.backward_word() + let backward = a:cmdline.backward()[ : -strlen(word)-1 ] call a:cmdline.setline(backward . a:cmdline.line.pos_word() . a:cmdline.forward()) call a:cmdline.setline(strchars(backward)) call a:cmdline.setchar('') diff --git a/autoload/vital/_easymotion/Over/Commandline/Modules/DrawCommandline.vim b/autoload/vital/_easymotion/Over/Commandline/Modules/DrawCommandline.vim index 7953657..7e57887 100644 --- a/autoload/vital/_easymotion/Over/Commandline/Modules/DrawCommandline.vim +++ b/autoload/vital/_easymotion/Over/Commandline/Modules/DrawCommandline.vim @@ -42,7 +42,7 @@ function! s:_redraw(cmdline) let left = a:cmdline.get_prompt() . a:cmdline.getline() . (empty(a:cmdline.line.pos_word()) ? " " : "") let width = len(left) + 1 - if a:cmdline.get_suffix() != "" + if a:cmdline.get_suffix() != "" let width += len(s:suffix(left, a:cmdline.get_suffix())) - 1 endif diff --git a/autoload/vital/_easymotion/Over/Commandline/Modules/History.vim b/autoload/vital/_easymotion/Over/Commandline/Modules/History.vim index 4e6db3c..14b272d 100644 --- a/autoload/vital/_easymotion/Over/Commandline/Modules/History.vim +++ b/autoload/vital/_easymotion/Over/Commandline/Modules/History.vim @@ -8,31 +8,43 @@ let s:module = { \} function! s:module.histories() - return map(range(&history), 'histget(self.mode, v:val * -1)') + return map(range(1, &history), 'histget(self.mode, v:val * -1)') endfunction +function! s:_should_match_cmdline(cmdline) + return a:cmdline.is_input("\") +\ || a:cmdline.is_input("\") +endfunction -function! s:module.on_enter(...) +function! s:_reset() let s:cmdhist = [] let s:count = 0 + let s:is_match_mode = 0 " /: true, /: false +endfunction + +function! s:module.on_enter(...) + call s:_reset() endfunction function! s:module.on_char_pre(cmdline) if !a:cmdline.is_input("\") && !a:cmdline.is_input("\") - let s:cmdhist = [] - let s:count = 0 + \ && !a:cmdline.is_input("\") && !a:cmdline.is_input("\") + call s:_reset() return else if s:count == 0 && empty(s:cmdhist) + \ || s:is_match_mode != s:_should_match_cmdline(a:cmdline) let cmdline = '^' . a:cmdline.getline() - let s:cmdhist = filter(self.histories(), 'v:val =~ cmdline') + let s:is_match_mode = s:_should_match_cmdline(a:cmdline) + let s:cmdhist = [a:cmdline.getline()] + (s:is_match_mode ? + \ filter(self.histories(), 'v:val =~ cmdline') : self.histories()) endif endif call a:cmdline.setchar("") - if a:cmdline.is_input("\") + if a:cmdline.is_input("\") || a:cmdline.is_input("\") let s:count = max([s:count - 1, 0]) endif - if a:cmdline.is_input("\") + if a:cmdline.is_input("\") || a:cmdline.is_input("\") let s:count = min([s:count + 1, len(s:cmdhist)]) endif call a:cmdline.setline(get(s:cmdhist, s:count, a:cmdline.getline())) diff --git a/autoload/vital/_easymotion/Over/Commandline/Modules/InsertRegister.vim b/autoload/vital/_easymotion/Over/Commandline/Modules/InsertRegister.vim index 93c8404..73207b8 100644 --- a/autoload/vital/_easymotion/Over/Commandline/Modules/InsertRegister.vim +++ b/autoload/vital/_easymotion/Over/Commandline/Modules/InsertRegister.vim @@ -3,6 +3,18 @@ let s:save_cpo = &cpo set cpo&vim +function! s:_vital_loaded(V) + let s:V = a:V + let s:String = s:V.import("Over.String") +endfunction + + +function! s:_vital_depends() + return [ +\ "Over.String", +\ ] +endfunction + function! s:to_string(expr) return type(a:expr) == type("") ? a:expr : string(a:expr) @@ -10,10 +22,24 @@ endfunction function! s:input(cmdline) + let CR_index = index(a:cmdline.input_key_stack(), "\") + if CR_index != -1 + let input = a:cmdline.input_key_stack_string() + let input = input[ : CR_index-1] + call a:cmdline.set_input_key_stack(a:cmdline.input_key_stack()[CR_index+1 : ]) + return eval(input) + endif + + let input_text = "" + if !empty(a:cmdline.input_key_stack()) + let input_text = a:cmdline.input_key_stack_string() + call a:cmdline.set_input_key_stack([]) + endif + call a:cmdline.hl_cursor_on() try redraw - let input = input("=", "", "expression") + let input = input("=", input_text, "expression") if !empty(input) let input = s:to_string(eval(input)) endif @@ -31,14 +57,29 @@ let s:module = { \} -function! s:module.on_enter(...) + +function! s:module.reset() let self.cword = expand("") let self.cWORD = expand("") let self.cfile = expand("") +endfunction + +function! s:module.on_enter(...) + call self.reset() " let self.prefix_key = "" endfunction +function! s:get_cmdline_cword(backward, cword) +" let backward = matchstr(a:backward, '.\{-}\zs\k\+$') + let backward = a:backward + if &incsearch == 0 || a:cword == "" || a:backward == "" || s:String.index(a:cword, backward) != 0 + return a:cword + endif + return a:cword[len(backward) : ] +endfunction + + function! s:module.on_char_pre(cmdline) if a:cmdline.is_input("\") call a:cmdline.setchar('"') @@ -57,7 +98,7 @@ function! s:module.on_char_pre(cmdline) elseif char == "=" call a:cmdline.setchar(s:input(a:cmdline)) elseif char == "\" - call a:cmdline.setchar(self.cword) + call a:cmdline.setchar(s:get_cmdline_cword(a:cmdline.backward_word(), self.cword)) elseif char == "\" call a:cmdline.setchar(self.cWORD) elseif char == "\" diff --git a/autoload/vital/_easymotion/Over/Commandline/Modules/KeyMapping.vim b/autoload/vital/_easymotion/Over/Commandline/Modules/KeyMapping.vim index 0cb217b..a0acf93 100644 --- a/autoload/vital/_easymotion/Over/Commandline/Modules/KeyMapping.vim +++ b/autoload/vital/_easymotion/Over/Commandline/Modules/KeyMapping.vim @@ -2,6 +2,19 @@ scriptencoding utf-8 let s:save_cpo = &cpo set cpo&vim + +function! s:_vital_loaded(V) + let s:Keymapping = a:V.import("Over.Keymapping") +endfunction + + +function! s:_vital_depends() + return [ +\ "Over.Keymapping", +\ ] +endfunction + + let s:emacs = { \ "name" : "KeyMapping_emacs_like" \} @@ -48,6 +61,16 @@ function! s:emacs.keymapping(cmdline) \ "noremap" : 1, \ "lock" : 1, \ }, +\ "\" : { +\ "key" : "\", +\ "noremap" : 1, +\ "lock" : 1, +\ }, +\ "\" : { +\ "key" : "\", +\ "noremap" : 1, +\ "lock" : 1, +\ }, \ } endfunction @@ -57,6 +80,39 @@ function! s:make_emacs() endfunction +let s:vim_cmdline_mapping = { +\ "name" : "KeyMapping_vim_cmdline_mapping", +\ "_cmaps" : {} +\} + +function! s:_auto_cmap() + let cmaps = {} + let cmap_info = s:Keymapping.cmap_rhss(0, 1) + " vital-over currently doesn't support nor mappings + for c in filter(cmap_info, "v:val['expr'] ==# 0 && v:val['buffer'] ==# 0") + let cmaps[s:Keymapping.escape_key(c['lhs'])] = { + \ 'noremap' : c['noremap'], + \ 'key' : s:Keymapping.escape_key(c['rhs']), + \ } + endfor + return cmaps +endfunction + + +function! s:vim_cmdline_mapping.on_enter(cmdline) + let self._cmaps = s:_auto_cmap() +endfunction + + +function! s:vim_cmdline_mapping.keymapping(cmdline) + return self._cmaps +endfunction + +function! s:make_vim_cmdline_mapping() + return deepcopy(s:vim_cmdline_mapping) +endfunction + + let &cpo = s:save_cpo unlet s:save_cpo diff --git a/autoload/vital/_easymotion/Over/Commandline/Modules/Redraw.vim b/autoload/vital/_easymotion/Over/Commandline/Modules/Redraw.vim index 746ad34..480ed21 100644 --- a/autoload/vital/_easymotion/Over/Commandline/Modules/Redraw.vim +++ b/autoload/vital/_easymotion/Over/Commandline/Modules/Redraw.vim @@ -41,6 +41,7 @@ function! s:module.redraw(cmdline) " https://github.com/osyo-manga/vital-over/issues/52 " https://github.com/Lokaltog/vim-easymotion/issues/177#issuecomment-53663431 if &cedit != "" +\ ||(v:version >= 704 && has("patch441")) normal! : else execute "normal! :\" diff --git a/autoload/vital/_easymotion/Over/Input.vim b/autoload/vital/_easymotion/Over/Input.vim new file mode 100644 index 0000000..f162b77 --- /dev/null +++ b/autoload/vital/_easymotion/Over/Input.vim @@ -0,0 +1,25 @@ +scriptencoding utf-8 +let s:save_cpo = &cpo +set cpo&vim + + +function! s:getchar(...) + let mode = get(a:, 1, 0) + while 1 + " Workaround for https://github.com/osyo-manga/vital-over/issues/53 + try + let char = call("getchar", a:000) + catch /^Vim:Interrupt$/ + let char = 3 " + endtry + " Workaround for the mappings + if string(char) !=# "\x80\xfd`" + return mode == 1 ? !!char +\ : type(char) == type(0) ? nr2char(char) : char + endif + endwhile +endfunction + + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/autoload/vital/_easymotion/Over/Keymapping.vim b/autoload/vital/_easymotion/Over/Keymapping.vim new file mode 100644 index 0000000..d5b1ec3 --- /dev/null +++ b/autoload/vital/_easymotion/Over/Keymapping.vim @@ -0,0 +1,55 @@ +scriptencoding utf-8 +let s:save_cpo = &cpo +set cpo&vim + + +function! s:capture(cmd) + let verbose_save = &verbose + let &verbose = 0 + try + redir => result + execute "silent!" a:cmd + redir END + finally + let &verbose = verbose_save + endtry + return result +endfunction + + +function! s:escape_key(key) + execute 'let result = "' . substitute(escape(a:key, '\"'), '\(<.\{-}>\)', '\\\1', 'g') . '"' + return result +endfunction + + +function! s:parse_mapping_lhs(map, mode) + return matchstr(a:map, a:mode . '\s\+\zs\S\{-}\ze\s\+') +endfunction + + +function! s:lhss(mode) + let maps = s:capture(a:mode . "map") + return filter(map(split(maps, "\n"), "s:parse_mapping_lhs(v:val, a:mode)"), 'v:val =~ ''\S\+''') +endfunction + + +function! s:rhss(mode, ...) + let abbr = get(a:, 1, 0) + let dict = get(a:, 2, 0) + return map(s:lhss(a:mode), "maparg(v:val, a:mode, abbr, dict)") +endfunction + + +function! s:cmap_lhss() + return s:lhss("c") +endfunction + + +function! s:cmap_rhss(...) + return call("s:rhss", ["c"] + a:000) +endfunction + + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/autoload/vital/_easymotion/Over/String.vim b/autoload/vital/_easymotion/Over/String.vim index b19cff7..e315237 100644 --- a/autoload/vital/_easymotion/Over/String.vim +++ b/autoload/vital/_easymotion/Over/String.vim @@ -3,17 +3,30 @@ let s:save_cpo = &cpo set cpo&vim +function! s:_vital_loaded(V) + let s:V = a:V + let s:List = s:V.import("Data.List") +endfunction + + +function! s:_vital_depends() + return [ +\ "Data.List", +\ ] +endfunction + + function! s:_clamp(x, max, min) return min([max([a:x, a:max]), a:min]) endfunction let s:base = {} - + function! s:base.set(item) return type(a:item) == type("") ? self.set_str(a:item) -\ : type(a:item) == type(0) ? self.set_pos(a:item) -\ : self +\ : type(a:item) == type(0) ? self.set_pos(a:item) +\ : self endfunction function! s:base.str() @@ -97,5 +110,168 @@ function! s:make(...) endfunction +function! s:_split(str, pat) + let pat = (exists("+regexpengine") ? '\%#=2' : '') . a:pat + let list = split(a:str, pat . '\zs') + return s:List.flatten(map(list, 'v:val == a:pat ? a:pat : v:val =~ pat . ''$'' ? split(v:val, pat) + [a:pat] : v:val')) +endfunction + + +function! s:_split_keystring(str, pats, ...) + if a:str =~ '^(.\{-})$' +\ || a:str =~ "^\(.\\{-})$" + return [a:str] + endif + let pats = a:pats + let index = get(a:, 1, 0) + if !exists("+regexpengine") +\ || index > len(pats) +\ || len(filter(copy(pats), 'a:str =~ ''\%#=2'' . v:val')) == 0 + if len(filter(copy(pats), 'a:str ==# v:val')) == 0 + return split(a:str, '\zs') + else + return [a:str] + endif + endif + if len(filter(copy(pats), 'a:str == v:val')) == 1 + return [a:str] + endif + + let result = [] + let pat = pats[index] + let list = s:_split(a:str, pat) + let result += eval(join(map(list, "s:_split_keystring(v:val, pats, index+1)"), "+")) + return result +endfunction + + +let s:special_keys = [ +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\ "\", +\] +" Issues #45 +" \ "\", +" \ "\", + + +" Workaround +" https://github.com/osyo-manga/vital-over/pull/63 +" http://lingr.com/room/vim/archives/2014/10/29#message-20492403 +if exists("+regexpengine") + function! s:_split_keystring(str, ...) + return split(a:str, '\%#=2' . "\\m\\%(" . get(a:, 1, '') . "\x80\xfc.\\%(\x80..\\|.\\)\\zs\\|\x80..\\zs\\|.\\zs\\)") + endfunction + + function! s:split_by_keys(str) + return s:_split_keystring(a:str, "\\%(\\\|\\)(.\\{-})\\zs\\|") + endfunction +else + function! s:split_by_keys(str) + return s:_split_keystring(a:str, s:special_keys) + endfunction +endif + + +function! s:index(haystack, needle, ...) + let start = get(a:, 1, 0) + let ignorecase = get(a:, 2, &ignorecase) + if ignorecase + return stridx(tolower(a:haystack), tolower(a:needle), start) + else + return stridx(a:haystack, a:needle, start) + endif +endfunction + + let &cpo = s:save_cpo unlet s:save_cpo diff --git a/autoload/vital/easymotion.vital b/autoload/vital/easymotion.vital index e0f200d..4dc36d3 100644 --- a/autoload/vital/easymotion.vital +++ b/autoload/vital/easymotion.vital @@ -1,5 +1,5 @@ easymotion -0116903 +651a023 Over.Commandline.Base Over.Commandline.Modules.Cancel