diff --git a/autoload/vital/_easymotion/Data/List.vim b/autoload/vital/_easymotion/Data/List.vim new file mode 100644 index 0000000..4a1fbca --- /dev/null +++ b/autoload/vital/_easymotion/Data/List.vim @@ -0,0 +1,283 @@ +" Utilities for list. + +let s:save_cpo = &cpo +set cpo&vim + +function! s:pop(list) + return remove(a:list, -1) +endfunction + +function! s:push(list, val) + call add(a:list, a:val) + return a:list +endfunction + +function! s:shift(list) + return remove(a:list, 0) +endfunction + +function! s:unshift(list, val) + return insert(a:list, a:val) +endfunction + +function! s:cons(x, xs) + return [a:x] + a:xs +endfunction + +function! s:conj(xs, x) + return a:xs + [a:x] +endfunction + +" Removes duplicates from a list. +function! s:uniq(list, ...) + if a:0 + echomsg "Vital.Data.List.uniq() with 2 arguments is deprecated! Please use uniq_by() instead, if you still want to use the 2nd argument." + return s:uniq_by(a:list, a:1) + else + return s:uniq_by(a:list, 'v:val') + endif +endfunction + +" Removes duplicates from a list. +function! s:uniq_by(list, f) + let list = map(copy(a:list), printf('[v:val, %s]', a:f)) + let i = 0 + let seen = {} + while i < len(list) + let key = string(list[i][1]) + if has_key(seen, key) + call remove(list, i) + else + let seen[key] = 1 + let i += 1 + endif + endwhile + return map(list, 'v:val[0]') +endfunction + +function! s:clear(list) + if !empty(a:list) + unlet! a:list[0 : len(a:list) - 1] + endif + return a:list +endfunction + +" Concatenates a list of lists. +" XXX: Should we verify the input? +function! s:concat(list) + let memo = [] + for Value in a:list + let memo += Value + endfor + return memo +endfunction + +" Take each elements from lists to a new list. +function! s:flatten(list, ...) + let limit = a:0 > 0 ? a:1 : -1 + let memo = [] + if limit == 0 + return a:list + endif + let limit -= 1 + for Value in a:list + let memo += + \ type(Value) == type([]) ? + \ s:flatten(Value, limit) : + \ [Value] + unlet! Value + endfor + return memo +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) + if type(a:expr) == type(function('function')) + return sort(a:list, a:expr) + endif + let s:expr = a:expr + return sort(a:list, 's:_compare') +endfunction + +function! s:_compare(a, b) + 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) + 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]') +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) + if empty(a:list) + return 0 + endif + let list = map(copy(a:list), a:expr) + return a:list[index(list, max(list))] +endfunction + +" Returns a minimum value in {list} through given {expr}. +" Returns 0 if {list} is empty. +" v:val is used in {expr} +" FIXME: -0x80000000 == 0x80000000 +function! s:min_by(list, expr) + 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) + return map( + \ range(char2nr(a:from), char2nr(a:to)), + \ 'nr2char(v:val)' + \) +endfunction + +" Returns true if a:list has a:value. +" Returns false otherwise. +function! s:has(list, value) + 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) + " 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) + let border = len(a:xs) + for i in range(len(a:xs)) + if !eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g')) + let border = i + break + endif + endfor + return border == 0 ? [[], copy(a:xs)] : [a:xs[: border - 1], a:xs[border :]] +endfunction + +" similar to Haskell's Data.List.break +function! s:break(f, xs) + return s:span(printf('!(%s)', a:f), a:xs) +endfunction + +" similar to Haskell's Data.List.takeWhile +function! s:take_while(f, xs) + return s:span(a:f, a:xs)[0] +endfunction + +" similar to Haskell's Data.List.partition +function! s:partition(f, xs) + 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) + return !s:any(printf('!(%s)', a:f), a:xs) +endfunction + +" similar to Haskell's Prelude.any +function! s:any(f, xs) + return !empty(filter(map(copy(a:xs), a:f), 'v:val')) +endfunction + +" similar to Haskell's Prelude.and +function! s:and(xs) + return s:all('v:val', a:xs) +endfunction + +" similar to Haskell's Prelude.or +function! s:or(xs) + return s:any('v:val', a:xs) +endfunction + +" similar to Haskell's Prelude.foldl +function! s:foldl(f, init, xs) + let memo = a:init + for x in a:xs + let expr = substitute(a:f, 'v:val', string(x), 'g') + let expr = substitute(expr, 'v:memo', string(memo), 'g') + unlet memo + let memo = eval(expr) + endfor + return memo +endfunction + +" similar to Haskell's Prelude.foldl1 +function! s:foldl1(f, xs) + if len(a:xs) == 0 + throw 'foldl1' + endif + return s:foldl(a:f, a:xs[0], a:xs[1:]) +endfunction + +" similar to Haskell's Prelude.foldr +function! s:foldr(f, init, xs) + return s:foldl(a:f, a:init, reverse(copy(a:xs))) +endfunction + +" similar to Haskell's Prelude.fold11 +function! s:foldr1(f, xs) + if len(a:xs) == 0 + throw 'foldr1' + endif + return s:foldr(a:f, a:xs[-1], a:xs[0:-2]) +endfunction + +" similar to python's zip() +function! s:zip(...) + 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) + if empty(a:xs) && empty(a:ys) + return [] + elseif empty(a:ys) + return s:cons([a:xs[0], a:filler], s:zip_fill(a:xs[1 :], [], a:filler)) + elseif empty(a:xs) + return s:cons([a:filler, a:ys[0]], s:zip_fill([], a:ys[1 :], a:filler)) + else + return s:cons([a:xs[0], a:ys[0]], s:zip_fill(a:xs[1 :], a:ys[1: ], a:filler)) + endif +endfunction + +" Inspired by Ruby's with_index method. +function! s:with_index(list, ...) + 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) + for x in a:list + if eval(substitute(a:f, 'v:val', string(x), 'g')) + return x + endif + endfor + return a:default +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) + return !empty(filter(copy(a:list1), 'index(a:list2, v:val) isnot -1')) +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/Over/Commandline.vim b/autoload/vital/_easymotion/Over/Commandline.vim index 01b31f5..f612752 100644 --- a/autoload/vital/_easymotion/Over/Commandline.vim +++ b/autoload/vital/_easymotion/Over/Commandline.vim @@ -21,13 +21,20 @@ function! s:_vital_loaded(V) for module in s:modules let s:{module} = s:V.import('Over.Commandline.Modules.' . module) endfor - let s:String = s:V.import("Over.String") + let s:String = s:V.import("Over.String") + let s:Signals = s:V.import("Over.Signals") + let s:base.variables.modules = s:Signals.make() + function! s:base.variables.modules.get_slot(val) + return a:val.slot.module + endfunction endfunction function! s:_vital_depends() - return ["Over.String"] -\ + map(copy(s:modules), "'Over.Commandline.Modules.' . v:val") + return [ +\ "Over.String", +\ "Over.Signals", +\ ] + map(copy(s:modules), "'Over.Commandline.Modules.' . v:val") endfunction @@ -45,6 +52,7 @@ function! s:make_plain(prompt) let result.prompt = a:prompt call result.connect("Enter") call result.connect("Cancel") + call result.connect(result, "_") return result endfunction @@ -75,7 +83,6 @@ let s:base = { \ "tap_key" : "", \ "exit" : 0, \ "keymapping" : {}, -\ "modules" : {}, \ }, \ "highlights" : { \ "prompt" : "NONE", @@ -166,28 +173,30 @@ function! s:base.connect(module, ...) if type(a:module) == type("") return call(self.connect, [s:get_module(a:module).make()] + a:000, self) endif - let name = get(a:, 1, a:module.name) - let self.variables.modules[name] = a:module + let name = a:0 > 0 ? a:1 : a:module.name + let slot = self.variables.modules.find_first_by("get(v:val.slot, 'name', '') == " . string(name)) + if empty(slot) + call self.variables.modules.connect({ "name" : name, "module" : a:module }) + else + let slot.slot.module = a:module + endif +" let self.variables.modules[name] = a:module endfunction function! s:base.disconnect(name) - unlet self.variables.modules[a:name] + return self.variables.modules.disconnect_by( +\ "get(v:val.slot, 'name', '') == " . string(a:name) +\ ) +" unlet self.variables.modules[a:name] endfunction -for s:_ in ["enter", "leave", "char", "char_pre", "execute_pre", "execute_failed", "execute", "cancel"] - execute join([ -\ "function! s:base._on_" . s:_ . "()", -\ " call map(copy(self.variables.modules), 'has_key(v:val, \"on_" . s:_ . "\") ? v:val.on_" . s:_ . "(self) : 0')", -\ " call self.on_" . s:_ . "()", -\ "endfunction", -\ ], "\n") - - execute "function! s:base.on_" . s:_ . "()" - endfunction -endfor -unlet s:_ +function! s:base.callevent(event) + call self.variables.modules.sort_by("has_key(v:val.slot.module, 'priority') ? v:val.slot.module.priority('" . a:event . "') : 0") + return self.variables.modules.call(a:event, [self]) +" call map(filter(copy(self.variables.modules), "has_key(v:val, a:event)"), "v:val." . a:event . "(self)") +endfunction function! s:base.cmap(lhs, rhs) @@ -310,16 +319,16 @@ endfunction function! s:base._execute() call s:redraw() - call self._on_execute_pre() + call self.callevent("on_execute_pre") try call self.execute() catch echohl ErrorMsg echo matchstr(v:exception, 'Vim\((\w*)\)\?:\zs.*\ze') echohl None - call self._on_execute_failed() + call self.callevent("on_execute_failed") finally - call self._on_execute() + call self.callevent("on_execute") endtry endfunction @@ -328,7 +337,7 @@ function! s:base._main(...) try call self._init() let self.line = deepcopy(s:String.make(get(a:, 1, ""))) - call self._on_enter() + call self.callevent("on_enter") while !self._is_exit() call s:_echo_cmdline(self) @@ -339,18 +348,18 @@ function! s:base._main(...) call self.setchar(self.variables.char) - call self._on_char_pre() + call self.callevent("on_char_pre") call self.insert(self.variables.input) - call self._on_char() + call self.callevent("on_char") endwhile catch echohl ErrorMsg | echo v:throwpoint . " " . v:exception | echohl None return -1 finally call self._finish() - call self._on_leave() + call s:redraw() + call self.callevent("on_leave") endtry - call s:redraw() return self.exit_code() endfunction @@ -410,9 +419,12 @@ endfunction function! s:base._get_keymapping() let result = {} - for module in values(self.variables.modules) +" for module in values(self.variables.modules) + for module in self.variables.modules.slots() if has_key(module, "keymapping") - call extend(result, module.keymapping(self)) + if module isnot self + call extend(result, module.keymapping(self)) + endif endif endfor return extend(extend(result, self.variables.keymapping), self.keymapping()) diff --git a/autoload/vital/_easymotion/Over/Signals.vim b/autoload/vital/_easymotion/Over/Signals.vim new file mode 100644 index 0000000..41cae27 --- /dev/null +++ b/autoload/vital/_easymotion/Over/Signals.vim @@ -0,0 +1,104 @@ +scriptencoding utf-8 +let s:save_cpo = &cpo +set cpo&vim + + +function! s:_vital_loaded(V) + let s:V = a:V + let s:L = s:V.import("Data.List") +endfunction + + +function! s:_vital_depends() + return ["Data.List"] +endfunction + + +let s:base = { +\ "variables" : { +\ "slots" : [], +\ "counter" : 0, +\ } +\} + + +function! s:base.connect(slot) + let self.variables.counter += 1 + let slot = { "id" : self.variables.counter, "slot" : a:slot } + call add(self.variables.slots, slot) + return slot +endfunction + + +function! s:base.disconnect(slot) + if empty(a:slot) + return -1 + endif + for i in range(len(self.variables.slots)) + if self.variables.slots[i].id == a:slot.id + unlet self.variables.slots[i] + return + endif + endfor + return -1 +endfunction + + +function! s:base.disconnect_by(expr) + return self.disconnect(self.find_first_by(a:expr)) +endfunction + + +function! s:call(list, func, ...) + let args = get(a:, 1, []) + let def = get(a:, 2, 0) + return map(copy(a:list), "has_key(v:val, a:func) ? call(v:val.".a:func.", args, v:val) : def") +endfunction + +function! s:base.call(func, ...) + return call("s:call", [self.slots(), a:func] + a:000) +endfunction + + +function! s:base.find_by(expr) + return filter(copy(self.variables.slots), a:expr) +endfunction + + +function! s:base.find_first_by(expr) + return get(self.find_by(a:expr), 0, {}) +endfunction + + +function! s:base.sort_by(expr) + let self.variables.slots = s:L.sort_by(self.variables.slots, a:expr) +endfunction + + +function! s:base.get_slot(val) + return a:val.slot +endfunction + + +function! s:base.slots() + return map(copy(self.variables.slots), "self.get_slot(v:val)") +endfunction + + +" function! s:base.dict() +" let result = {} +" for _ in self.variables.slots +" let result[_.id] = _.value +" endfor +" return result +" endfunction + + +function! s:make() + let result = deepcopy(s:base) + return result +endfunction + + +let &cpo = s:save_cpo +unlet s:save_cpo