diff --git a/autoload/neosnippet/util.vim b/autoload/neosnippet/util.vim index f1f85e4..c855260 100644 --- a/autoload/neosnippet/util.vim +++ b/autoload/neosnippet/util.vim @@ -42,6 +42,12 @@ function! s:get_list() "{{{ endif return s:List endfunction"}}} +function! s:get_string() "{{{ + if !exists('s:String') + let s:String = neosnippet#util#get_vital().import('Data.String') + endif + return s:String +endfunction"}}} function! s:get_process() "{{{ if !exists('s:Process') let s:Process = neosnippet#util#get_vital().import('Process') @@ -74,10 +80,10 @@ function! neosnippet#util#iconv(...) "{{{ return call(s:get_process().iconv, a:000) endfunction"}}} function! neosnippet#util#truncate(...) "{{{ - return call(s:get_prelude().truncate, a:000) + return call(s:get_string().truncate, a:000) endfunction"}}} function! neosnippet#util#strwidthpart(...) "{{{ - return call(s:get_prelude().strwidthpart, a:000) + return call(s:get_string().strwidthpart, a:000) endfunction"}}} function! neosnippet#util#expand(path) "{{{ 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/_neosnippet.vim b/autoload/vital/_neosnippet.vim index f8eff02..2723144 100644 --- a/autoload/vital/_neosnippet.vim +++ b/autoload/vital/_neosnippet.vim @@ -1,11 +1,12 @@ let s:self_version = expand(':t:r') +let s:self_file = expand('') " Note: The extra argument to globpath() was added in Patch 7.2.051. 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 +30,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,24 +56,45 @@ 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) - let target = substitute(a:pattern, '\.', '/', 'g') - let tailpath = printf('autoload/vital/%s/%s.vim', s:self_version, target) - - let paths = s:_runtime_files(tailpath) +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:_import(name) +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) + throw printf('vital: Any of module %s is not found', string(a:entry)) + endif + if eval(join(map(copy(candidates), 'has_key(a:all, v:val)'), '+')) + let modules = [] + else + let modules = [candidates[0]] + endif + else + let modules = s:search(a:entry) + if empty(modules) + throw printf('vital: Module %s is not found', a:entry) + endif + endif + call filter(modules, '!has_key(a:all, v:val)') + for module in modules + let a:all[module] = 1 + endfor + return modules +endfunction + +function! s:_import(name) abort if type(a:name) == type(0) return s:_build_module(a:name) endif @@ -80,7 +102,7 @@ function! s:_import(name) if path ==# '' throw 'vital: module not found: ' . a:name endif - let sid = get(s:_scripts(), path, 0) + let sid = s:_get_sid_by_script(path) if !sid try execute 'source' fnameescape(path) @@ -90,84 +112,105 @@ function! s:_import(name) " Ignore. endtry - let sid = s:_scripts()[path] + let sid = s:_get_sid_by_script(path) endif 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 s:_unify_path(a:name) + return a:name endif if a:name ==# '' - let tailpath = printf('autoload/vital/%s.vim', s:self_version) + let paths = [s:self_file] elseif a:name =~# '\v^\u\w*%(\.\u\w*)*$' - let target = substitute(a:name, '\W\+', '/', 'g') - let tailpath = printf('autoload/vital/%s/%s.vim', s:self_version, target) + let paths = s:_vital_files(a:name) else throw 'vital: Invalid module name: ' . a:name endif - let paths = s:_runtime_files(tailpath) - call filter(paths, 'filereadable(v:val)') + call filter(paths, 'filereadable(expand(v:val, 1))') let path = get(paths, 0, '') - return path !=# '' ? s:_unify_path(path) : '' + return path !=# '' ? path : '' endfunction -function! s:_scripts() - let scripts = {} +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') let list = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$') - if !empty(list) - let scripts[s:_unify_path(list[2])] = list[1] - 0 + if !empty(list) && s:_unify_path(list[2]) ==# path + return list[1] - 0 endif endfor - return scripts + return 0 endfunction -function! s:_file2module(file) - let filename = s:_unify_path(a: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, '[\\/]\+'), '.') endfunction if filereadable(expand(':r') . '.VIM') - function! s:_unify_path(path) - " 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. - return tolower(fnamemodify(resolve(fnamemodify( - \ a:path, ':p:gs?[\\/]\+?/?')), ':~')) + " resolve() is slow, so we cache results. + let s:_unify_path_cache = {} + " 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) abort + if has_key(s:_unify_path_cache, a:path) + return s:_unify_path_cache[a:path] + endif + let value = tolower(fnamemodify(resolve(fnamemodify( + \ a:path, ':p')), ':~:gs?[\\/]?/?')) + let s:_unify_path_cache[a:path] = value + return value endfunction else - function! s:_unify_path(path) - return resolve(fnamemodify(a:path, ':p:gs?[\\/]\+?/?')) + 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) 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) + let mod = ':p:gs?[\\/]\+?/?' + call map(s:_vital_files_cache, 'fnamemodify(v:val, mod)') + let s:_vital_files_cache_runtimepath = &runtimepath + endif + let target = substitute(a:pattern, '\.', '/', 'g') + let target = substitute(target, '\*', '[^/]*', 'g') + let regexp = printf('autoload/vital/%s/%s.vim', s:self_version, target) + return filter(copy(s:_vital_files_cache), 'v:val =~# regexp') +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 @@ -181,7 +224,11 @@ function! s:_build_module(sid) if has_key(module, '_vital_loaded') let V = vital#{s:self_version}#new() if has_key(module, '_vital_depends') - call call(V.load, module._vital_depends(), V) + 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) @@ -197,13 +244,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 @@ -215,11 +262,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] @@ -233,7 +280,15 @@ else endfunction endif -function! s:_redir(cmd) +function! s:_concat(lists) abort + let result_list = [] + for list in a:lists + let result_list += list + endfor + return result_list +endfunction + +function! s:_redir(cmd) abort let [save_verbose, save_verbosefile] = [&verbose, &verbosefile] set verbose=0 verbosefile= redir => res @@ -243,6 +298,6 @@ 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/_neosnippet/Data/List.vim b/autoload/vital/_neosnippet/Data/List.vim index 29e373e..c8aac65 100644 --- a/autoload/vital/_neosnippet/Data/List.vim +++ b/autoload/vital/_neosnippet/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,13 @@ 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,12 +277,165 @@ 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 +function! s:intersect(list1, list2) abort + let items = [] + " for funcref + for X in a:list1 + if index(a:list2, X) != -1 && index(items, X) == -1 + let items += [X] + endif + endfor + return items +endfunction + +" similar to Ruby's group_by. +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 + let Val = x[0] + let key = type(x[1]) !=# type('') ? string(x[1]) : x[1] + if has_key(result, key) + call add(result[key], Val) + else + let result[key] = [Val] + endif + unlet Val + endfor + 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/_neosnippet/Data/String.vim b/autoload/vital/_neosnippet/Data/String.vim new file mode 100644 index 0000000..a29cc8d --- /dev/null +++ b/autoload/vital/_neosnippet/Data/String.vim @@ -0,0 +1,551 @@ +" Utilities for string. + +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') + let s:L = s:V.import('Data.List') +endfunction + +function! s:_vital_depends() abort + return ['Prelude', 'Data.List'] +endfunction + +" Substitute a:from => a:to by string. +" To substitute by pattern, use substitute() instead. +function! s:replace(str, from, to) abort + return s:_replace(a:str, a:from, a:to, 'g') +endfunction + +" Substitute a:from => a:to only once. +" cf. s:replace() +function! s:replace_first(str, from, to) abort + return s:_replace(a:str, a:from, a:to, '') +endfunction + +" implement of replace() and replace_first() +function! s:_replace(str, from, to, flags) abort + return substitute(a:str, '\V'.escape(a:from, '\'), escape(a:to, '\'), a:flags) +endfunction + +function! s:scan(str, pattern) abort + let list = [] + call substitute(a:str, a:pattern, '\=add(list, submatch(0)) == [] ? "" : ""', 'g') + return list +endfunction + +function! s:reverse(str) abort + return join(reverse(split(a:str, '.\zs')), '') +endfunction + +function! s:common_head(strs) abort + if empty(a:strs) + return '' + endif + let len = len(a:strs) + if len == 1 + return a:strs[0] + endif + let strs = len == 2 ? a:strs : sort(copy(a:strs)) + let pat = substitute(strs[0], '.', '\="[" . escape(submatch(0), "^\\") . "]"', 'g') + return pat == '' ? '' : matchstr(strs[-1], '\C^\%[' . pat . ']') +endfunction + +" Split to two elements of List. ([left, right]) +" e.g.: s:split3('neocomplcache', 'compl') returns ['neo', 'compl', 'cache'] +function! s:split_leftright(expr, pattern) abort + let [left, _, right] = s:split3(a:expr, a:pattern) + return [left, right] +endfunction + +function! s:split3(expr, pattern) abort + let ERROR = ['', '', ''] + if a:expr ==# '' || a:pattern ==# '' + return ERROR + endif + let begin = match(a:expr, a:pattern) + if begin is -1 + return ERROR + endif + let end = matchend(a:expr, a:pattern) + let left = begin <=# 0 ? '' : a:expr[: begin - 1] + let right = a:expr[end :] + return [left, a:expr[begin : end-1], right] +endfunction + +" Slices into strings determines the number of substrings. +" e.g.: s:nsplit("neo compl cache", 2, '\s') returns ['neo', 'compl cache'] +function! s:nsplit(expr, n, ...) abort + let pattern = get(a:000, 0, '\s') + let keepempty = get(a:000, 1, 1) + let ret = [] + let expr = a:expr + if a:n <= 1 + return [expr] + endif + while 1 + let pos = match(expr, pattern) + if pos == -1 + if expr !~ pattern || keepempty + call add(ret, expr) + endif + break + elseif pos >= 0 + let left = pos > 0 ? expr[:pos-1] : '' + if pos > 0 || keepempty + call add(ret, left) + endif + let ml = len(matchstr(expr, pattern)) + if pos == 0 && ml == 0 + let pos = 1 + endif + let expr = expr[pos+ml :] + endif + if len(expr) == 0 + break + endif + if len(ret) == a:n - 1 + call add(ret, expr) + break + endif + endwhile + return ret +endfunction + +" Returns the number of character in a:str. +" NOTE: This returns proper value +" even if a:str contains multibyte character(s). +" s:strchars(str) {{{ +if exists('*strchars') + function! s:strchars(str) abort + return strchars(a:str) + endfunction +else + function! s:strchars(str) abort + return strlen(substitute(copy(a:str), '.', 'x', 'g')) + endfunction +endif "}}} + +" Returns the bool of contains any multibyte character in s:str +function! s:contains_multibyte(str) abort "{{{ + return strlen(a:str) != s:strchars(a:str) +endfunction "}}} + +" Remove last character from a:str. +" NOTE: This returns proper value +" even if a:str contains multibyte character(s). +function! s:chop(str) abort "{{{ + return substitute(a:str, '.$', '', '') +endfunction "}}} + +" Remove last \r,\n,\r\n from a:str. +function! s:chomp(str) abort "{{{ + return substitute(a:str, '\%(\r\n\|[\r\n]\)$', '', '') +endfunction "}}} + +" wrap() and its internal functions +" * _split_by_wcswidth_once() +" * _split_by_wcswidth() +" * _concat() +" * wrap() +" +" NOTE _concat() is just a copy of Data.List.concat(). +" FIXME don't repeat yourself +function! s:_split_by_wcswidth_once(body, x) abort + let fst = s:P.strwidthpart(a:body, a:x) + let snd = s:P.strwidthpart_reverse(a:body, s:P.wcswidth(a:body) - s:P.wcswidth(fst)) + return [fst, snd] +endfunction + +function! s:_split_by_wcswidth(body, x) abort + let memo = [] + let body = a:body + while s:P.wcswidth(body) > a:x + let [tmp, body] = s:_split_by_wcswidth_once(body, a:x) + call add(memo, tmp) + endwhile + call add(memo, body) + return memo +endfunction + +function! s:trim(str) abort + return matchstr(a:str,'^\s*\zs.\{-}\ze\s*$') +endfunction + +function! s:wrap(str,...) abort + let _columns = a:0 > 0 ? a:1 : &columns + return s:L.concat( + \ map(split(a:str, '\r\n\|[\r\n]'), 's:_split_by_wcswidth(v:val, _columns - 1)')) +endfunction + +function! s:nr2byte(nr) abort + if a:nr < 0x80 + return nr2char(a:nr) + elseif a:nr < 0x800 + return nr2char(a:nr/64+192).nr2char(a:nr%64+128) + else + return nr2char(a:nr/4096%16+224).nr2char(a:nr/64%64+128).nr2char(a:nr%64+128) + endif +endfunction + +function! s:nr2enc_char(charcode) abort + if &encoding == 'utf-8' + return nr2char(a:charcode) + endif + let char = s:nr2byte(a:charcode) + if strlen(char) > 1 + let char = strtrans(iconv(char, 'utf-8', &encoding)) + endif + return char +endfunction + +function! s:nr2hex(nr) abort + let n = a:nr + let r = "" + while n + let r = '0123456789ABCDEF'[n % 16] . r + let n = n / 16 + endwhile + return r +endfunction + +" If a ==# b, returns -1. +" If a !=# b, returns first index of different character. +function! s:diffidx(a, b) abort + return a:a ==# a:b ? -1 : strlen(s:common_head([a:a, a:b])) +endfunction + +function! s:substitute_last(expr, pat, sub) abort + return substitute(a:expr, printf('.*\zs%s', a:pat), a:sub, '') +endfunction + +function! s:dstring(expr) abort + let x = substitute(string(a:expr), "^'\\|'$", '', 'g') + let x = substitute(x, "''", "'", 'g') + return printf('"%s"', escape(x, '"')) +endfunction + +function! s:lines(str) abort + return split(a:str, '\r\?\n') +endfunction + +function! s:_pad_with_char(str, left, right, char) abort + return repeat(a:char, a:left). a:str. repeat(a:char, a:right) +endfunction + +function! s:pad_left(str, width, ...) abort + let char = get(a:, 1, ' ') + if strdisplaywidth(char) != 1 + throw "vital: Data.String: Can't use non-half-width characters for padding." + endif + let left = max([0, a:width - strdisplaywidth(a:str)]) + return s:_pad_with_char(a:str, left, 0, char) +endfunction + +function! s:pad_right(str, width, ...) abort + let char = get(a:, 1, ' ') + if strdisplaywidth(char) != 1 + throw "vital: Data.String: Can't use non-half-width characters for padding." + endif + let right = max([0, a:width - strdisplaywidth(a:str)]) + return s:_pad_with_char(a:str, 0, right, char) +endfunction + +function! s:pad_both_sides(str, width, ...) abort + let char = get(a:, 1, ' ') + if strdisplaywidth(char) != 1 + throw "vital: Data.String: Can't use non-half-width characters for padding." + endif + let space = max([0, a:width - strdisplaywidth(a:str)]) + let left = space / 2 + let right = space - left + return s:_pad_with_char(a:str, left, right, char) +endfunction + +function! s:pad_between_letters(str, width, ...) abort + let char = get(a:, 1, ' ') + if strdisplaywidth(char) != 1 + throw "vital: Data.String: Can't use non-half-width characters for padding." + endif + let letters = split(a:str, '\zs') + let each_width = a:width / len(letters) + let str = join(map(letters, 's:pad_both_sides(v:val, each_width, char)'), '') + if a:width - strdisplaywidth(str) > 0 + return char. s:pad_both_sides(str, a:width - 1, char) + endif + return str +endfunction + +function! s:justify_equal_spacing(str, width, ...) abort + let char = get(a:, 1, ' ') + if strdisplaywidth(char) != 1 + throw "vital: Data.String: Can't use non-half-width characters for padding." + endif + let letters = split(a:str, '\zs') + let first_letter = letters[0] + " {width w/o the first letter} / {length w/o the first letter} + let each_width = (a:width - strdisplaywidth(first_letter)) / (len(letters) - 1) + let remainder = (a:width - strdisplaywidth(first_letter)) % (len(letters) - 1) + return first_letter. join(s:L.concat([ +\ map(letters[1:remainder], 's:pad_left(v:val, each_width + 1, char)'), +\ map(letters[remainder + 1:], 's:pad_left(v:val, each_width, char)') +\ ]), '') +endfunction + +function! s:levenshtein_distance(str1, str2) abort + let letters1 = split(a:str1, '\zs') + let letters2 = split(a:str2, '\zs') + let length1 = len(letters1) + let length2 = len(letters2) + let distances = map(range(1, length1 + 1), 'map(range(1, length2 + 1), "0")') + + for i1 in range(0, length1) + let distances[i1][0] = i1 + endfor + for i2 in range(0, length2) + let distances[0][i2] = i2 + endfor + + for i1 in range(1, length1) + for i2 in range(1, length2) + let cost = (letters1[i1 - 1] ==# letters2[i2 - 1]) ? 0 : 1 + + let distances[i1][i2] = min([ + \ distances[i1 - 1][i2 ] + 1, + \ distances[i1 ][i2 - 1] + 1, + \ distances[i1 - 1][i2 - 1] + cost, + \]) + endfor + endfor + + return distances[length1][length2] +endfunction + +function! s:padding_by_displaywidth(expr, width, float) abort + let padding_char = ' ' + let n = a:width - strdisplaywidth(a:expr) + if n <= 0 + let n = 0 + endif + if a:float < 0 + return a:expr . repeat(padding_char, n) + elseif 0 < a:float + return repeat(padding_char, n) . a:expr + else + if n % 2 is 0 + return repeat(padding_char, n / 2) . a:expr . repeat(padding_char, n / 2) + else + return repeat(padding_char, (n - 1) / 2) . a:expr . repeat(padding_char, (n - 1) / 2) . padding_char + endif + endif +endfunction + +function! s:split_by_displaywidth(expr, width, float, is_wrap) abort + if a:width is 0 + return [''] + endif + + let lines = [] + + let cs = split(a:expr, '\zs') + let cs_index = 0 + + let text = '' + while cs_index < len(cs) + if cs[cs_index] is "\n" + let text = s:padding_by_displaywidth(text, a:width, a:float) + let lines += [text] + let text = '' + else + let w = strdisplaywidth(text . cs[cs_index]) + + if w < a:width + let text .= cs[cs_index] + elseif a:width < w + let text = s:padding_by_displaywidth(text, a:width, a:float) + else + let text .= cs[cs_index] + endif + + if a:width <= w + let lines += [text] + let text = '' + if a:is_wrap + if a:width < w + if a:width < strdisplaywidth(cs[cs_index]) + while get(cs, cs_index, "\n") isnot "\n" + let cs_index += 1 + endwhile + continue + else + let text = cs[cs_index] + endif + endif + else + while get(cs, cs_index, "\n") isnot "\n" + let cs_index += 1 + endwhile + continue + endif + endif + + endif + let cs_index += 1 + endwhile + + if !empty(text) + let lines += [ s:padding_by_displaywidth(text, a:width, a:float) ] + endif + + return lines +endfunction + +function! s:hash(str) abort + if exists('*sha256') + return sha256(a:str) + else + " This gives up sha256ing but just adds up char with index. + let sum = 0 + for i in range(len(a:str)) + let sum += char2nr(a:str[i]) * (i + 1) + endfor + + return printf('%x', sum) + endif +endfunction + +function! s:truncate(str, width) abort + " Original function is from mattn. + " http://github.com/mattn/googlereader-vim/tree/master + + 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:truncate_skipping(str, max, footer_width, separator) abort + 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:strwidthpart(str, width) abort + if a:width <= 0 + return '' + endif + let strarr = split(a:str, '\zs') + let width = s:wcswidth(a:str) + let index = len(strarr) + let diff = (index + 1) / 2 + let rightindex = index - 1 + while width > a:width + let index = max([rightindex - diff + 1, 0]) + let partwidth = s:wcswidth(join(strarr[(index):(rightindex)], '')) + if width - partwidth >= a:width || diff <= 1 + let width -= partwidth + let rightindex = index - 1 + endif + if diff > 1 + let diff = diff / 2 + endif + endwhile + return index ? join(strarr[:index - 1], '') : '' +endfunction + +function! s:strwidthpart_reverse(str, width) abort + if a:width <= 0 + return '' + endif + let strarr = split(a:str, '\zs') + let width = s:wcswidth(a:str) + let strlen = len(strarr) + let diff = (strlen + 1) / 2 + let leftindex = 0 + let index = -1 + while width > a:width + let index = min([leftindex + diff, strlen]) - 1 + let partwidth = s:wcswidth(join(strarr[(leftindex):(index)], '')) + if width - partwidth >= a:width || diff <= 1 + let width -= partwidth + let leftindex = index + 1 + endif + if diff > 1 + let diff = diff / 2 + endif + endwhile + return index < strlen ? join(strarr[(index + 1):], '') : '' +endfunction + +if v:version >= 703 + " Use builtin function. + function! s:wcswidth(str) abort + return strwidth(a:str) + endfunction +else + function! s:wcswidth(str) abort + if a:str =~# '^[\x00-\x7f]*$' + return strlen(a:str) + endif + 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 &cpo = s:save_cpo +unlet s:save_cpo + +" vim:set et ts=2 sts=2 sw=2 tw=0: diff --git a/autoload/vital/_neosnippet/Prelude.vim b/autoload/vital/_neosnippet/Prelude.vim index 0738810..0511de7 100644 --- a/autoload/vital/_neosnippet/Prelude.vim +++ b/autoload/vital/_neosnippet/Prelude.vim @@ -3,17 +3,17 @@ set cpo&vim if v:version ># 703 || \ (v:version is 703 && has('patch465')) - function! s:glob(expr) + function! s:glob(expr) abort return glob(a:expr, 1, 1) endfunction else - function! s:glob(expr) + function! s:glob(expr) abort let R = glob(a:expr, 1) return split(R, '\n') endfunction endif -function! s:globpath(path, expr) +function! s:globpath(path, expr) abort let R = globpath(a:path, a:expr, 1) return split(R, '\n') endfunction @@ -36,44 +36,41 @@ let [ " This doesn't match to anything. " Number or Float -function! s:is_numeric(Value) +function! s:is_numeric(Value) abort let _ = type(a:Value) return _ ==# s:__TYPE_NUMBER \ || _ ==# s:__TYPE_FLOAT endfunction " Number -function! s:is_number(Value) +function! s:is_number(Value) abort return type(a:Value) ==# s:__TYPE_NUMBER endfunction " Float -function! s:is_float(Value) +function! s:is_float(Value) abort return type(a:Value) ==# s:__TYPE_FLOAT endfunction " String -function! s:is_string(Value) +function! s:is_string(Value) abort return type(a:Value) ==# s:__TYPE_STRING endfunction " Funcref -function! s:is_funcref(Value) +function! s:is_funcref(Value) abort return type(a:Value) ==# s:__TYPE_FUNCREF endfunction " List -function! s:is_list(Value) +function! s:is_list(Value) abort return type(a:Value) ==# s:__TYPE_LIST endfunction " Dictionary -function! s:is_dict(Value) +function! s:is_dict(Value) abort return type(a:Value) ==# s:__TYPE_DICT endfunction -function! s:truncate_smart(str, max, footer_width, separator) - echoerr 'Prelude.truncate_smart() is obsolete. Use its truncate_skipping() instead; they are equivalent.' - return s:truncate_skipping(a:str, a:max, a:footer_width, a:separator) -endfunction +function! s:truncate_skipping(str, max, footer_width, separator) abort + call s:_warn_deprecated("truncate_skipping", "Data.String.truncate_skipping") -function! s:truncate_skipping(str, max, footer_width, separator) let width = s:wcswidth(a:str) if width <= a:max let ret = a:str @@ -86,10 +83,12 @@ function! s:truncate_skipping(str, max, footer_width, separator) return s:truncate(ret, a:max) endfunction -function! s:truncate(str, width) +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) @@ -109,7 +108,9 @@ function! s:truncate(str, width) return ret endfunction -function! s:strwidthpart(str, width) +function! s:strwidthpart(str, width) abort + call s:_warn_deprecated("strwidthpart", "Data.String.strwidthpart") + if a:width <= 0 return '' endif @@ -123,7 +124,9 @@ function! s:strwidthpart(str, width) return ret endfunction -function! s:strwidthpart_reverse(str, width) +function! s:strwidthpart_reverse(str, width) abort + call s:_warn_deprecated("strwidthpart_reverse", "Data.String.strwidthpart_reverse") + if a:width <= 0 return '' endif @@ -140,11 +143,14 @@ endfunction if v:version >= 703 " Use builtin function. - function! s:wcswidth(str) + function! s:wcswidth(str) abort + call s:_warn_deprecated("wcswidth", "Data.String.wcswidth") return strwidth(a:str) endfunction else - function! s:wcswidth(str) + function! s:wcswidth(str) abort + call s:_warn_deprecated("wcswidth", "Data.String.wcswidth") + if a:str =~# '^[\x00-\x7f]*$' return strlen(a:str) end @@ -164,7 +170,7 @@ else endfunction " UTF-8 only. - function! s:_wcwidth(ucs) + function! s:_wcwidth(ucs) abort let ucs = a:ucs if (ucs >= 0x1100 \ && (ucs <= 0x115f @@ -193,54 +199,58 @@ let s:is_mac = !s:is_windows && !s:is_cygwin \ (!isdirectory('/proc') && executable('sw_vers'))) let s:is_unix = has('unix') -function! s:is_windows() +function! s:is_windows() abort return s:is_windows endfunction -function! s:is_cygwin() +function! s:is_cygwin() abort return s:is_cygwin endfunction -function! s:is_mac() +function! s:is_mac() abort return s:is_mac endfunction -function! s:is_unix() +function! s:is_unix() abort return s:is_unix endfunction -function! s:_deprecated2(fname) - echomsg printf("Vital.Prelude.%s is deprecated!", - \ a:fname) +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) +function! s:smart_execute_command(action, word) abort execute a:action . ' ' . (a:word == '' ? '' : '`=a:word`') endfunction -function! s:escape_file_searching(buffer_name) +function! s:escape_file_searching(buffer_name) abort return escape(a:buffer_name, '*[]?{}, ') endfunction -function! s:escape_pattern(str) +function! s:escape_pattern(str) abort return escape(a:str, '~"\.^$[]*') endfunction -function! s:getchar(...) +function! s:getchar(...) abort let c = call('getchar', a:000) return type(c) == type(0) ? nr2char(c) : c endfunction -function! s:getchar_safe(...) +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(...) +function! s:input_safe(...) abort return s:input_helper('input', a:000) endfunction -function! s:input_helper(funcname, args) +function! s:input_helper(funcname, args) abort let success = 0 if inputsave() !=# success throw 'inputsave() failed' @@ -254,31 +264,21 @@ function! s:input_helper(funcname, args) endtry endfunction -function! s:set_default(var, val) +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:set_dictionary_helper(variable, keys, pattern) - call s:_deprecated2('set_dictionary_helper') - - for key in split(a:keys, '\s*,\s*') - if !has_key(a:variable, key) - let a:variable[key] = a:pattern - endif - endfor -endfunction - -function! s:substitute_path_separator(path) +function! s:substitute_path_separator(path) abort return s:is_windows ? substitute(a:path, '\\', '/', 'g') : a:path endfunction -function! s:path2directory(path) +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) +function! s:_path2project_directory_git(path) abort let parent = a:path while 1 @@ -294,7 +294,7 @@ function! s:_path2project_directory_git(path) endwhile endfunction -function! s:_path2project_directory_svn(path) +function! s:_path2project_directory_svn(path) abort let search_directory = a:path let directory = '' @@ -319,7 +319,7 @@ function! s:_path2project_directory_svn(path) return directory endfunction -function! s:_path2project_directory_others(vcs, path) +function! s:_path2project_directory_others(vcs, path) abort let vcs = a:vcs let search_directory = a:path @@ -331,7 +331,7 @@ function! s:_path2project_directory_others(vcs, path) return fnamemodify(d, ':p:h:h') endfunction -function! s:path2project_directory(path, ...) +function! s:path2project_directory(path, ...) abort let is_allow_empty = get(a:000, 0, 0) let search_directory = s:path2directory(a:path) let directory = '' diff --git a/autoload/vital/_neosnippet/Process.vim b/autoload/vital/_neosnippet/Process.vim index ff6e2fd..f04699b 100644 --- a/autoload/vital/_neosnippet/Process.vim +++ b/autoload/vital/_neosnippet/Process.vim @@ -12,6 +12,14 @@ set cpo&vim " Because these variables are used when this script file is loaded. let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95') let s:is_unix = has('unix') +" As of 7.4.122, the system()'s 1st argument is converted internally by Vim. +" Note that Patch 7.4.122 does not convert system()'s 2nd argument and +" return-value. We must convert them manually. +let s:need_trans = v:version < 704 || (v:version == 704 && !has('patch122')) + +let s:TYPE_DICT = type({}) +let s:TYPE_LIST = type([]) +let s:TYPE_STRING = type("") " Execute program in the background from Vim. @@ -28,17 +36,17 @@ let s:is_unix = has('unix') " " Unix: " using :! , execute program in the background by shell. -function! s:spawn(expr, ...) +function! s:spawn(expr, ...) abort let shellslash = 0 if s:is_windows let shellslash = &l:shellslash setlocal noshellslash endif try - if type(a:expr) is type([]) + if type(a:expr) is s:TYPE_LIST let special = 1 let cmdline = join(map(a:expr, 'shellescape(v:val, special)'), ' ') - elseif type(a:expr) is type("") + elseif type(a:expr) is s:TYPE_STRING let cmdline = a:expr if a:0 && a:1 " for :! command @@ -61,7 +69,7 @@ function! s:spawn(expr, ...) endfunction " iconv() wrapper for safety. -function! s:iconv(expr, from, to) +function! s:iconv(expr, from, to) abort if a:from == '' || a:to == '' || a:from ==? a:to return a:expr endif @@ -70,7 +78,7 @@ function! s:iconv(expr, from, to) endfunction " Check vimproc. -function! s:has_vimproc() +function! s:has_vimproc() abort if !exists('s:exists_vimproc') try call vimproc#version() @@ -88,21 +96,20 @@ endfunction " use_vimproc: bool, " input: string, " timeout: bool, +" background: bool, " } -function! s:system(str, ...) - if type(a:str) is type([]) - let command = join(map(copy(a:str), 's:shellescape(v:val)'), ' ') - elseif type(a:str) is type("") - let command = a:str - else - throw 'Process.system(): invalid argument (value type:'.type(a:str).')' - endif - let command = s:iconv(command, &encoding, 'char') +function! s:system(str, ...) abort + " Process optional arguments at first + " because use_vimproc is required later + " for a:str argument. let input = '' let use_vimproc = s:has_vimproc() - let args = [command] + let background = 0 + let args = [] if a:0 ==# 1 - if type(a:1) is type({}) + " {command} [, {dict}] + " a:1 = {dict} + if type(a:1) is s:TYPE_DICT if has_key(a:1, 'use_vimproc') let use_vimproc = a:1.use_vimproc endif @@ -113,31 +120,56 @@ function! s:system(str, ...) " ignores timeout unless you have vimproc. let args += [a:1.timeout] endif + if has_key(a:1, 'background') + let background = a:1.background + endif + elseif type(a:1) is s:TYPE_STRING + let args += [s:iconv(a:1, &encoding, 'char')] + else + throw 'Process.system(): invalid argument (value type:'.type(a:1).')' endif elseif a:0 >= 2 + " {command} [, {input} [, {timeout}]] + " a:000 = [{input} [, {timeout}]] let [input; rest] = a:000 - let input = s:iconv(a:1, &encoding, 'char') + let input = s:iconv(input, &encoding, 'char') let args += [input] + rest endif + " Process a:str argument. + if type(a:str) is s:TYPE_LIST + let expr = use_vimproc ? '"''" . v:val . "''"' : 's:shellescape(v:val)' + let command = join(map(copy(a:str), expr), ' ') + elseif type(a:str) is s:TYPE_STRING + let command = a:str + else + throw 'Process.system(): invalid argument (value type:'.type(a:str).')' + endif + if s:need_trans + let command = s:iconv(command, &encoding, 'char') + endif + let args = [command] + args + if background && (use_vimproc || !s:is_windows) + let args[0] = args[0] . ' &' + endif + let funcname = use_vimproc ? 'vimproc#system' : 'system' let output = call(funcname, args) let output = s:iconv(output, 'char', &encoding) - return output endfunction -function! s:get_last_status() +function! s:get_last_status() abort return s:has_vimproc() ? \ vimproc#get_last_status() : v:shell_error endfunction if s:is_windows - function! s:shellescape(command) + function! s:shellescape(command) abort return substitute(a:command, '[&()[\]{}^=;!''+,`~]', '^\0', 'g') endfunction else - function! s:shellescape(...) + function! s:shellescape(...) abort return call('shellescape', a:000) endfunction endif diff --git a/autoload/vital/_neosnippet/System/Cache.vim b/autoload/vital/_neosnippet/System/Cache.vim index 54e3405..b7c11ad 100644 --- a/autoload/vital/_neosnippet/System/Cache.vim +++ b/autoload/vital/_neosnippet/System/Cache.vim @@ -3,37 +3,46 @@ let s:save_cpo = &cpo set cpo&vim -function! s:getfilename(cache_dir, filename) +function! s:_vital_loaded(V) abort + let s:V = a:V + let s:S = s:V.import('Data.String') +endfunction + +function! s:_vital_depends() abort + return ['Data.String'] +endfunction + +function! s:getfilename(cache_dir, filename) abort return s:_encode_name(a:cache_dir, a:filename) endfunction -function! s:filereadable(cache_dir, filename) +function! s:filereadable(cache_dir, filename) abort let cache_name = s:_encode_name(a:cache_dir, a:filename) return filereadable(cache_name) endfunction -function! s:readfile(cache_dir, filename) +function! s:readfile(cache_dir, filename) abort let cache_name = s:_encode_name(a:cache_dir, a:filename) return filereadable(cache_name) ? readfile(cache_name) : [] endfunction -function! s:writefile(cache_dir, filename, list) +function! s:writefile(cache_dir, filename, list) abort let cache_name = s:_encode_name(a:cache_dir, a:filename) call writefile(a:list, cache_name) endfunction -function! s:delete(cache_dir, filename) +function! s:delete(cache_dir, filename) abort echoerr 'System.Cache.delete() is obsolete. Use its deletefile() instead.' return call('s:deletefile', a:cache_dir, a:filename) endfunction -function! s:deletefile(cache_dir, filename) +function! s:deletefile(cache_dir, filename) abort let cache_name = s:_encode_name(a:cache_dir, a:filename) return delete(cache_name) endfunction -function! s:_encode_name(cache_dir, filename) +function! s:_encode_name(cache_dir, filename) abort " Check cache directory. if !isdirectory(a:cache_dir) call mkdir(a:cache_dir, 'p') @@ -46,7 +55,7 @@ function! s:_encode_name(cache_dir, filename) return cache_dir . s:_create_hash(cache_dir, a:filename) endfunction -function! s:check_old_cache(cache_dir, filename) +function! s:check_old_cache(cache_dir, filename) abort " Check old cache file. let cache_name = s:_encode_name(a:cache_dir, a:filename) let ret = getftime(cache_name) == -1 @@ -59,20 +68,12 @@ function! s:check_old_cache(cache_dir, filename) return ret endfunction -function! s:_create_hash(dir, str) +function! s:_create_hash(dir, str) abort if len(a:dir) + len(a:str) < 150 let hash = substitute(substitute( \ a:str, ':', '=-', 'g'), '[/\\]', '=+', 'g') - elseif exists('*sha256') - let hash = sha256(a:str) else - " Use simple hash. - let sum = 0 - for i in range(len(a:str)) - let sum += char2nr(a:str[i]) * (i + 1) - endfor - - let hash = printf('%x', sum) + let hash = s:S.hash(a:str) endif return hash diff --git a/autoload/vital/neosnippet.vital b/autoload/vital/neosnippet.vital index 418bf1d..9e91d1d 100644 --- a/autoload/vital/neosnippet.vital +++ b/autoload/vital/neosnippet.vital @@ -1,7 +1,8 @@ neosnippet -d3554a5 +1c73bcc Prelude Data.List Process System.Cache +Data.String