From f30100d07e947d3f634a7a8291d94d4a531167ae Mon Sep 17 00:00:00 2001 From: manga_osyo Date: Tue, 7 Jun 2016 08:17:10 +0900 Subject: [PATCH] Fix vital. --- autoload/vital.vim | 2 +- autoload/vital/_brightest.vim | 304 +------- autoload/vital/_brightest/Coaster.vim | 15 + autoload/vital/_brightest/Coaster/Buffer.vim | 25 + .../_brightest/Coaster/Buffer/Object.vim | 15 + .../vital/_brightest/Coaster/Highlight.vim | 28 +- autoload/vital/_brightest/Coaster/Search.vim | 35 +- autoload/vital/_brightest/Coaster/Window.vim | 15 + autoload/vital/_brightest/Data/Dict.vim | 116 +++ autoload/vital/_brightest/Data/List.vim | 457 ++++++++++++ autoload/vital/_brightest/Data/String.vim | 661 ++++++++++++++++++ autoload/vital/_brightest/Gift.vim | 15 + autoload/vital/_brightest/Gift/Tabpage.vim | 17 +- autoload/vital/_brightest/Gift/Window.vim | 20 +- autoload/vital/_brightest/Prelude.vim | 104 +-- autoload/vital/_brightest/Vim/Buffer.vim | 70 +- autoload/vital/_brightest/Vim/Guard.vim | 235 +++++++ autoload/vital/brightest.vim | 339 +++++++++ autoload/vital/brightest.vital | 3 +- 19 files changed, 2122 insertions(+), 354 deletions(-) create mode 100644 autoload/vital/_brightest/Data/Dict.vim create mode 100644 autoload/vital/_brightest/Data/List.vim create mode 100644 autoload/vital/_brightest/Data/String.vim create mode 100644 autoload/vital/_brightest/Vim/Guard.vim create mode 100644 autoload/vital/brightest.vim diff --git a/autoload/vital.vim b/autoload/vital.vim index 1004dfc..f1ba849 100644 --- a/autoload/vital.vim +++ b/autoload/vital.vim @@ -1,5 +1,5 @@ function! vital#of(name) abort - let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital') + let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital', 1) let file = split(files, "\n") if empty(file) throw 'vital: version file not found: ' . a:name diff --git a/autoload/vital/_brightest.vim b/autoload/vital/_brightest.vim index 2723144..ae19f5b 100644 --- a/autoload/vital/_brightest.vim +++ b/autoload/vital/_brightest.vim @@ -1,303 +1,5 @@ -let s:self_version = expand(':t:r') -let s:self_file = expand('') +let s:_plugin_name = expand(':t:r') -" 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, ...) abort - let target = {} - let functions = [] - for a in a:000 - if type(a) == type({}) - let target = a - elseif type(a) == type([]) - let functions = a - endif - unlet a - endfor - let module = s:_import(a:name) - if empty(functions) - call extend(target, module, 'keep') - else - for f in functions - if has_key(module, f) && !has_key(target, f) - let target[f] = module[f] - endif - endfor - endif - return target -endfunction - -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\+') - let dict = self - while 1 <= len(target) - let ns = remove(target, 0) - if !has_key(dict, ns) - let dict[ns] = {} - endif - if type(dict[ns]) == type({}) - let dict = dict[ns] - else - unlet dict - break - endif - endwhile - - if exists('dict') - call extend(dict, s:_import(name)) - endif - unlet arg - endfor - return self -endfunction - -function! s:unload() abort - let s:loaded = {} -endfunction - -function! s:exists(name) abort - return s:_get_module_path(a:name) !=# '' -endfunction - -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) 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 - let path = s:_get_module_path(a:name) - if path ==# '' - throw 'vital: module not found: ' . a:name - endif - let sid = s:_get_sid_by_script(path) - if !sid - try - execute 'source' fnameescape(path) - catch /^Vim\%((\a\+)\)\?:E484/ - throw 'vital: module not found: ' . a:name - catch /^Vim\%((\a\+)\)\?:E127/ - " Ignore. - endtry - - let sid = s:_get_sid_by_script(path) - endif - return s:_build_module(sid) -endfunction - -function! s:_get_module_path(name) abort - if s:_is_absolute_path(a:name) && filereadable(a:name) - return a:name - endif - if a:name ==# '' - let paths = [s:self_file] - elseif a:name =~# '\v^\u\w*%(\.\u\w*)*$' - let paths = s:_vital_files(a:name) - else - throw 'vital: Invalid module name: ' . a:name - endif - - call filter(paths, 'filereadable(expand(v:val, 1))') - let path = get(paths, 0, '') - return path !=# '' ? path : '' -endfunction - -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) && s:_unify_path(list[2]) ==# path - return list[1] - 0 - endif - endfor - return 0 -endfunction - -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') - " 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) abort - return resolve(fnamemodify(a:path, ':p:gs?[\\/]?/?')) - endfunction -endif - -if s:globpath_third_arg - function! s:_runtime_files(path) abort - return split(globpath(&runtimepath, a:path, 1), "\n") - endfunction -else - 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) abort - return a:path =~? '^[a-z]:[/\\]' - endfunction -else - function! s:_is_absolute_path(path) abort - return a:path[0] ==# '/' - endfunction -endif - -function! s:_build_module(sid) abort - if has_key(s:loaded, a:sid) - return copy(s:loaded[a:sid]) - endif - let functions = s:_get_functions(a:sid) - - let prefix = '' . a:sid . '_' - let module = {} - for func in functions - let module[func] = function(prefix . func) - endfor - if has_key(module, '_vital_loaded') - let V = vital#{s:self_version}#new() - if has_key(module, '_vital_depends') - let all = {} - let modules = - \ s:_concat(map(module._vital_depends(), - \ 's:expand_modules(v:val, all)')) - call call(V.load, modules, V) - endif - try - call module._vital_loaded(V) - catch - " FIXME: Show an error message for debug. - endtry - endif - if !get(g:, 'vital_debug', 0) - call filter(module, 'v:key =~# "^\\a"') - endif - let s:loaded[a:sid] = module - return copy(module) -endfunction - -if exists('+regexpengine') - 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) abort - let prefix = '' . a:sid . '_' - let funcs = s:_redir('function') - let filter_pat = '^\s*function ' . prefix - let map_pat = prefix . '\zs\w\+' - return map(filter(split(funcs, "\n"), - \ 'stridx(v:val, prefix) > 0 && v:val =~# filter_pat'), - \ 'matchstr(v:val, map_pat)') - endfunction -endif - -if exists('*uniq') - function! s:_uniq(list) abort - return uniq(a:list) - endfunction -else - function! s:_uniq(list) abort - let i = len(a:list) - 1 - while 0 < i - if a:list[i] ==# a:list[i - 1] - call remove(a:list, i) - let i -= 2 - else - let i -= 1 - endif - endwhile - return a:list - endfunction -endif - -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 - silent! execute a:cmd - redir END - let [&verbose, &verbosefile] = [save_verbose, save_verbosefile] - return res -endfunction - -function! vital#{s:self_version}#new() abort - return s:_import('') +function! vital#{s:_plugin_name}#new() abort + return vital#{s:_plugin_name[1:]}#of() endfunction diff --git a/autoload/vital/_brightest/Coaster.vim b/autoload/vital/_brightest/Coaster.vim index e93cef0..232e96e 100644 --- a/autoload/vital/_brightest/Coaster.vim +++ b/autoload/vital/_brightest/Coaster.vim @@ -1,3 +1,18 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Coaster#import() abort + return map({'_vital_depends': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Coaster#import() abort', printf("return map({'_vital_depends': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ scriptencoding utf-8 let s:save_cpo = &cpo set cpo&vim diff --git a/autoload/vital/_brightest/Coaster/Buffer.vim b/autoload/vital/_brightest/Coaster/Buffer.vim index 307674e..0c2d4e5 100644 --- a/autoload/vital/_brightest/Coaster/Buffer.vim +++ b/autoload/vital/_brightest/Coaster/Buffer.vim @@ -1,3 +1,18 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Coaster#Buffer#import() abort + return map({'get_text_line_from_lnum': '', 'setbufline': '', 'pos_less_equal': '', 'execute': '', 'get_line_from_region': '', 'new_temp': '', 'delete': '', 'get_block_from_region': '', 'new': '', 'paste': '', '_vital_depends': '', 'setbufline_if_python': '', 'get_text_from_latest_yank': '', 'get': '', 'get_text_from_region': '', 'as_wise_key': '', 'yank': '', 'get_line_from_pos': '', 'current': '', 'get_region_from_textobj': '', 'get_text_from_pattern': '', 'get_char_from_region': '', 'paste_for_text': '', 'open': '', '_vital_loaded': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Coaster#Buffer#import() abort', printf("return map({'get_text_line_from_lnum': '', 'setbufline': '', 'pos_less_equal': '', 'execute': '', 'get_line_from_region': '', 'new_temp': '', 'delete': '', 'get_block_from_region': '', 'new': '', 'paste': '', '_vital_depends': '', 'setbufline_if_python': '', 'get_text_from_latest_yank': '', 'get': '', 'get_text_from_region': '', 'as_wise_key': '', 'yank': '', 'get_line_from_pos': '', 'current': '', 'get_region_from_textobj': '', 'get_text_from_pattern': '', 'get_char_from_region': '', 'paste_for_text': '', 'open': '', '_vital_loaded': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ scriptencoding utf-8 let s:save_cpo = &cpo set cpo&vim @@ -311,6 +326,16 @@ function! s:new(...) endfunction +function! s:new_temp(...) + let name = get(a:, 1, "") + execute "new" name + let buffer = s:current() + setlocal bufhidden=hide buftype=nofile noswapfile nobuflisted + quit + return buffer +endfunction + + function! s:open(cmd) let buffer = s:new() call buffer.open(a:cmd) diff --git a/autoload/vital/_brightest/Coaster/Buffer/Object.vim b/autoload/vital/_brightest/Coaster/Buffer/Object.vim index d46f04e..431e417 100644 --- a/autoload/vital/_brightest/Coaster/Buffer/Object.vim +++ b/autoload/vital/_brightest/Coaster/Buffer/Object.vim @@ -1,3 +1,18 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Coaster#Buffer#Object#import() abort + return map({'make': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Coaster#Buffer#Object#import() abort', printf("return map({'make': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ scriptencoding utf-8 let s:save_cpo = &cpo set cpo&vim diff --git a/autoload/vital/_brightest/Coaster/Highlight.vim b/autoload/vital/_brightest/Coaster/Highlight.vim index 646fd96..3443b30 100644 --- a/autoload/vital/_brightest/Coaster/Highlight.vim +++ b/autoload/vital/_brightest/Coaster/Highlight.vim @@ -1,3 +1,18 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Coaster#Highlight#import() abort + return map({'highlight': '', 'clear': '', 'delete': '', 'add': '', 'as_windo': '', '_vital_depends': '', 'get_hl_id': '', 'to_list': '', 'clear_all': '', 'delete_all': '', 'to_list_by': '', 'update': '', 'enable': '', 'delete_by': '', 'hl_list': '', 'make': '', 'enable_list': '', 'update_all': '', 'disable': '', 'disable_all': '', 'is_enabled': '', 'enable_all': '', 'is_added': '', '_vital_loaded': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Coaster#Highlight#import() abort', printf("return map({'highlight': '', 'clear': '', 'delete': '', 'add': '', 'as_windo': '', '_vital_depends': '', 'get_hl_id': '', 'to_list': '', 'clear_all': '', 'delete_all': '', 'to_list_by': '', 'update': '', 'enable': '', 'delete_by': '', 'hl_list': '', 'make': '', 'enable_list': '', 'update_all': '', 'disable': '', 'disable_all': '', 'is_enabled': '', 'enable_all': '', 'is_added': '', '_vital_loaded': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ scriptencoding utf-8 let s:save_cpo = &cpo set cpo&vim @@ -23,12 +38,17 @@ endfunction let s:base = { \ "variables" : { \ "hl_list" : {}, -\ "id_list" : {} +\ "id_list" : {}, +\ "id_count" : 0 \ } \} function! s:base.add(name, group, pattern, ...) + if a:name == "" + let self.variables.id_count += 1 + return self.add(self.variables.id_count, a:group, a:pattern, get(a:, 1, 10)) + endif call self.delete(a:name) let priority = get(a:, 1, 10) let self.variables.hl_list[a:name] = { @@ -37,6 +57,7 @@ function! s:base.add(name, group, pattern, ...) \ "priority" : priority, \ "name" : a:name, \ } + return self.variables.hl_list[a:name] endfunction @@ -165,8 +186,9 @@ endfunction function! s:base.highlight(name, group, pattern, ...) let priority = get(a:, 1, 10) - call self.add(a:name, a:group, a:pattern, priority) - call self.enable(a:name) + let result = self.add(a:name, a:group, a:pattern, priority) + call self.enable(result.name) + return result endfunction diff --git a/autoload/vital/_brightest/Coaster/Search.vim b/autoload/vital/_brightest/Coaster/Search.vim index 1217c54..056a26c 100644 --- a/autoload/vital/_brightest/Coaster/Search.vim +++ b/autoload/vital/_brightest/Coaster/Search.vim @@ -1,3 +1,18 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Coaster#Search#import() abort + return map({'region': '', '_vital_depends': '', 'pos_ignore_syntaxes': '', 'pattern_by_range': '', 'pattern_in_region': '', 'pattern_in_region_char': '', 'pattern_in_region_block': '', 'pattern_in_region_line': '', 'region_pair': '', 'pattern_in_range': '', 'count': '', 'text_by_pattern': '', '_vital_loaded': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Coaster#Search#import() abort', printf("return map({'region': '', '_vital_depends': '', 'pos_ignore_syntaxes': '', 'pattern_by_range': '', 'pattern_in_region': '', 'pattern_in_region_char': '', 'pattern_in_region_block': '', 'pattern_in_region_line': '', 'region_pair': '', 'pattern_in_range': '', 'count': '', 'text_by_pattern': '', '_vital_loaded': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ scriptencoding utf-8 let s:save_cpo = &cpo set cpo&vim @@ -39,11 +54,29 @@ function! s:pattern_in_region_char(first, last, pattern) else return printf('\%%%dl\%%(%s\M\)\%%>%dc', a:first[0], a:pattern, a:first[1]-1) \ . "\\|" . printf('\%%>%dl\%%(%s\M\)\%%<%dl', a:first[0], a:pattern, a:last[0]) -\ . >"\\|" . printf('\%%%dl\%%(%s\M\)\%%<%dc', a:last[0], a:pattern, a:last[1]+1) +\ . "\\|" . printf('\%%%dl\%%(%s\M\)\%%<%dc', a:last[0], a:pattern, a:last[1]+1) endif endfunction +" function! s:pattern_in_region_char(first, last, pattern) +" if a:first == a:last +" return printf('\%%%dl\%%%dv', a:first[0], a:first[1]) +" elseif a:first[0] == a:last[0] +" return printf('\%%%dl\%%>%dv\%%(%s\M\)\%%<%dv', a:first[0], a:first[1]-1, a:pattern, a:last[1]+1) +" elseif a:last[0] - a:first[0] == 1 +" return printf('\%%%dl\%%(%s\M\)\%%>%dv', a:first[0], a:pattern, a:first[1]-1) +" \ . "\\|" . printf('\%%%dl\%%(%s\M\)\%%<%dv', a:last[0], a:pattern, a:last[1]+1) +" else +" return printf('\%%%dl\%%(%s\M\)\%%>%dv', a:first[0], a:pattern, a:first[1]-1) +" \ . "\\|" . printf('\%%>%dl\%%(%s\M\)\%%<%dl', a:first[0], a:pattern, a:last[0]) +" \ . "\\|" . printf('\%%%dl\%%(%s\M\)\%%<%dv', a:last[0], a:pattern, a:last[1]+1) +" endif +" endfunction + + + + function! s:pattern_in_region_line(first, last, pattern) return printf('\%%>%dl\%%(%s\M\)\%%<%dl', a:first[0]-1, a:pattern, a:last[0]+1) endfunction diff --git a/autoload/vital/_brightest/Coaster/Window.vim b/autoload/vital/_brightest/Coaster/Window.vim index fd05d7c..e0f4a07 100644 --- a/autoload/vital/_brightest/Coaster/Window.vim +++ b/autoload/vital/_brightest/Coaster/Window.vim @@ -1,3 +1,18 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Coaster#Window#import() abort + return map({'as_windo': '', '_vital_depends': '', 'windo': '', '_vital_loaded': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Coaster#Window#import() abort', printf("return map({'as_windo': '', '_vital_depends': '', 'windo': '', '_vital_loaded': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ scriptencoding utf-8 let s:save_cpo = &cpo set cpo&vim diff --git a/autoload/vital/_brightest/Data/Dict.vim b/autoload/vital/_brightest/Data/Dict.vim new file mode 100644 index 0000000..cd6ff0a --- /dev/null +++ b/autoload/vital/_brightest/Data/Dict.vim @@ -0,0 +1,116 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Data#Dict#import() abort + return map({'pick': '', 'clear': '', 'max_by': '', 'foldl': '', 'swap': '', 'omit': '', 'min_by': '', 'foldr': '', 'make_index': '', 'make': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Data#Dict#import() abort', printf("return map({'pick': '', 'clear': '', 'max_by': '', 'foldl': '', 'swap': '', 'omit': '', 'min_by': '', 'foldr': '', 'make_index': '', 'make': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ +" Utilities for dictionary. + +let s:save_cpo = &cpo +set cpo&vim + +" Makes a dict from keys and values +function! s:make(keys, values, ...) abort + let dict = {} + let fill = a:0 ? a:1 : 0 + for i in range(len(a:keys)) + let key = type(a:keys[i]) == type('') ? a:keys[i] : string(a:keys[i]) + if key ==# '' + throw "vital: Data.Dict: Can't use an empty string for key." + endif + let dict[key] = get(a:values, i, fill) + endfor + return dict +endfunction + +" Swaps keys and values +function! s:swap(dict) abort + return s:make(values(a:dict), keys(a:dict)) +endfunction + +" Makes a index dict from a list +function! s:make_index(list, ...) abort + let value = a:0 ? a:1 : 1 + return s:make(a:list, [], value) +endfunction + +function! s:pick(dict, keys) abort + let new_dict = {} + for key in a:keys + if has_key(a:dict, key) + let new_dict[key] = a:dict[key] + endif + endfor + return new_dict +endfunction + +function! s:omit(dict, keys) abort + let new_dict = copy(a:dict) + for key in a:keys + if has_key(a:dict, key) + call remove(new_dict, key) + endif + endfor + return new_dict +endfunction + +function! s:clear(dict) abort + for key in keys(a:dict) + call remove(a:dict, key) + endfor + return a:dict +endfunction + +function! s:_max_by(dict, expr) abort + let dict = s:swap(map(copy(a:dict), a:expr)) + let key = dict[max(keys(dict))] + return [key, a:dict[key]] +endfunction + +function! s:max_by(dict, expr) abort + if empty(a:dict) + throw 'vital: Data.Dict: Empty dictionary' + endif + return s:_max_by(a:dict, a:expr) +endfunction + +function! s:min_by(dict, expr) abort + if empty(a:dict) + throw 'vital: Data.Dict: Empty dictionary' + endif + return s:_max_by(a:dict, '-(' . a:expr . ')') +endfunction + +function! s:_foldl(f, init, xs) abort + let memo = a:init + for [k, v] in a:xs + let expr = substitute(a:f, 'v:key', string(k), 'g') + let expr = substitute(expr, 'v:val', string(v), 'g') + let expr = substitute(expr, 'v:memo', string(memo), 'g') + unlet memo + let memo = eval(expr) + endfor + return memo +endfunction + +function! s:foldl(f, init, dict) abort + return s:_foldl(a:f, a:init, items(a:dict)) +endfunction + +function! s:foldr(f, init, dict) abort + return s:_foldl(a:f, a:init, reverse(items(a:dict))) +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/_brightest/Data/List.vim b/autoload/vital/_brightest/Data/List.vim new file mode 100644 index 0000000..e652235 --- /dev/null +++ b/autoload/vital/_brightest/Data/List.vim @@ -0,0 +1,457 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Data#List#import() abort + return map({'combinations': '', 'and': '', 'sort_by': '', 'foldr1': '', 'sort': '', 'flatten': '', 'has_index': '', 'find_indices': '', 'any': '', 'unshift': '', 'span': '', 'pop': '', 'binary_search': '', 'uniq_by': '', 'or': '', 'all': '', 'zip': '', 'find_last_index': '', 'find': '', 'partition': '', 'map_accum': '', 'permutations': '', 'break': '', 'max_by': '', 'foldl': '', 'foldr': '', 'find_index': '', 'group_by': '', 'take_while': '', 'conj': '', 'push': '', 'char_range': '', 'cons': '', 'foldl1': '', 'intersect': '', 'concat': '', 'shift': '', 'clear': '', 'has_common_items': '', 'product': '', 'zip_fill': '', 'uniq': '', 'has': '', 'min_by': '', 'with_index': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Data#List#import() abort', printf("return map({'combinations': '', 'and': '', 'sort_by': '', 'foldr1': '', 'sort': '', 'flatten': '', 'has_index': '', 'find_indices': '', 'any': '', 'unshift': '', 'span': '', 'pop': '', 'binary_search': '', 'uniq_by': '', 'or': '', 'all': '', 'zip': '', 'find_last_index': '', 'find': '', 'partition': '', 'map_accum': '', 'permutations': '', 'break': '', 'max_by': '', 'foldl': '', 'foldr': '', 'find_index': '', 'group_by': '', 'take_while': '', 'conj': '', 'push': '', 'char_range': '', 'cons': '', 'foldl1': '', 'intersect': '', 'concat': '', 'shift': '', 'clear': '', 'has_common_items': '', 'product': '', 'zip_fill': '', 'uniq': '', 'has': '', 'min_by': '', 'with_index': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ +" Utilities for list. + +let s:save_cpo = &cpo +set cpo&vim + +function! s:pop(list) abort + return remove(a:list, -1) +endfunction + +function! s:push(list, val) abort + call add(a:list, a:val) + return a:list +endfunction + +function! s:shift(list) abort + return remove(a:list, 0) +endfunction + +function! s:unshift(list, val) abort + return insert(a:list, a:val) +endfunction + +function! s:cons(x, xs) abort + return [a:x] + a:xs +endfunction + +function! s:conj(xs, x) abort + return a:xs + [a:x] +endfunction + +" Removes duplicates from a 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) abort + 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) abort + 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) abort + 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, ...) abort + 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) abort + 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) 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) 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]') +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) abort + 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) 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) abort + 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) 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) 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) 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')) + 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) abort + return s:span(printf('!(%s)', a:f), a:xs) +endfunction + +" similar to Haskell's Data.List.takeWhile +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) 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) abort + return !s:any(printf('!(%s)', a:f), a:xs) +endfunction + +" similar to Haskell's Prelude.any +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) abort + return s:all('v:val', a:xs) +endfunction + +" similar to Haskell's Prelude.or +function! s:or(xs) abort + return s:any('v:val', a:xs) +endfunction + +function! s:map_accum(expr, xs, init) abort + let memo = [] + let init = a:init + for x in a:xs + let expr = substitute(a:expr, 'v:memo', init, 'g') + let expr = substitute(expr, 'v:val', x, 'g') + let [tmp, init] = eval(expr) + call add(memo, tmp) + endfor + return memo +endfunction + +" similar to Haskell's Prelude.foldl +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') + 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) abort + if len(a:xs) == 0 + throw 'vital: Data.List: 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) abort + return s:foldl(a:f, a:init, reverse(copy(a:xs))) +endfunction + +" similar to Haskell's Prelude.fold11 +function! s:foldr1(f, xs) abort + if len(a:xs) == 0 + throw 'vital: Data.List: foldr1' + endif + return s:foldr(a:f, a:xs[-1], a:xs[0:-2]) +endfunction + +" similar to python'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) abort + 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, ...) abort + let base = a:0 > 0 ? a:1 : 0 + return map(copy(a:list), '[v:val, v:key + base]') +endfunction + +" similar to Ruby's detect or Haskell's find. +function! s:find(list, default, f) abort + for x in a:list + if eval(substitute(a:f, 'v:val', string(x), 'g')) + return x + endif + endfor + 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) 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 + +" vim:set et ts=2 sts=2 sw=2 tw=0: diff --git a/autoload/vital/_brightest/Data/String.vim b/autoload/vital/_brightest/Data/String.vim new file mode 100644 index 0000000..3c770ab --- /dev/null +++ b/autoload/vital/_brightest/Data/String.vim @@ -0,0 +1,661 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Data#String#import() abort + return map({'starts_with': '', 'split3': '', 'chop': '', 'unescape': '', 'split_posix_text': '', 'replace': '', 'scan': '', 'strchars': '', 'strwidthpart': '', 'common_head': '', 'reverse': '', 'escape_pattern': '', 'trim_end': '', '_vital_depends': '', 'wrap': '', 'join_posix_lines': '', 'contains_multibyte': '', 'truncate_skipping': '', 'split_leftright': '', 'ends_with': '', 'nsplit': '', 'substitute_last': '', 'strwidthpart_reverse': '', 'unescape_pattern': '', 'levenshtein_distance': '', 'trim_start': '', 'nr2hex': '', 'remove_ansi_sequences': '', 'iconv': '', 'pad_left': '', 'nr2enc_char': '', 'lines': '', 'repair_posix_text': '', 'nr2byte': '', 'trim': '', 'diffidx': '', 'truncate': '', 'split_by_displaywidth': '', 'padding_by_displaywidth': '', 'hash': '', 'chomp': '', 'pad_between_letters': '', 'wcswidth': '', 'dstring': '', 'pad_both_sides': '', 'justify_equal_spacing': '', 'pad_right': '', 'replace_first': '', '_vital_loaded': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Data#String#import() abort', printf("return map({'starts_with': '', 'split3': '', 'chop': '', 'unescape': '', 'split_posix_text': '', 'replace': '', 'scan': '', 'strchars': '', 'strwidthpart': '', 'common_head': '', 'reverse': '', 'escape_pattern': '', 'trim_end': '', '_vital_depends': '', 'wrap': '', 'join_posix_lines': '', 'contains_multibyte': '', 'truncate_skipping': '', 'split_leftright': '', 'ends_with': '', 'nsplit': '', 'substitute_last': '', 'strwidthpart_reverse': '', 'unescape_pattern': '', 'levenshtein_distance': '', 'trim_start': '', 'nr2hex': '', 'remove_ansi_sequences': '', 'iconv': '', 'pad_left': '', 'nr2enc_char': '', 'lines': '', 'repair_posix_text': '', 'nr2byte': '', 'trim': '', 'diffidx': '', 'truncate': '', 'split_by_displaywidth': '', 'padding_by_displaywidth': '', 'hash': '', 'chomp': '', 'pad_between_letters': '', 'wcswidth': '', 'dstring': '', 'pad_both_sides': '', 'justify_equal_spacing': '', 'pad_right': '', 'replace_first': '', '_vital_loaded': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ +" Utilities for string. + +let s:save_cpo = &cpo +set cpo&vim + +function! s:_vital_loaded(V) abort + let s:V = a:V + let s:L = s:V.import('Data.List') +endfunction + +function! s:_vital_depends() abort + return ['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:starts_with(str, prefix) abort + return stridx(a:str, a:prefix) == 0 +endfunction + +function! s:ends_with(str, suffix) abort + let idx = strridx(a:str, a:suffix) + return 0 <= idx && idx + len(a:suffix) == len(a:str) +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:strwidthpart(a:body, a:x) + let snd = s:strwidthpart_reverse(a:body, s:wcswidth(a:body) - s:wcswidth(fst)) + return [fst, snd] +endfunction + +function! s:_split_by_wcswidth(body, x) abort + let memo = [] + let body = a:body + while s: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:trim_start(str) abort + return matchstr(a:str,'^\s*\zs.\{-}$') +endfunction + +function! s:trim_end(str) abort + return matchstr(a:str,'^.\{-}\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 + +function! s:remove_ansi_sequences(text) abort + return substitute(a:text, '\e\[\%(\%(\d;\)\?\d\{1,2}\)\?[mK]', '', 'g') +endfunction + +function! s:escape_pattern(str) abort + " escape characters for no-magic + return escape(a:str, '^$~.*[]\') +endfunction + +function! s:unescape_pattern(str) abort + " unescape characters for no-magic + return s:unescape(a:str, '^$~.*[]\') +endfunction + +function! s:unescape(str, chars) abort + let chars = map(split(a:chars, '\zs'), 'escape(v:val, ''^$~.*[]\'')') + return substitute(a:str, '\\\(' . join(chars, '\|') . '\)', '\1', 'g') +endfunction + +function! s:iconv(expr, from, to) abort + if a:from ==# '' || a:to ==# '' || a:from ==? a:to + return a:expr + endif + let result = iconv(a:expr, a:from, a:to) + return empty(result) ? a:expr : result +endfunction + +" NOTE: +" A definition of a TEXT file is "A file that contains characters organized +" into one or more lines." +" A definition of a LINE is "A sequence of zero or more non- s +" plus a terminating " +" That's why {stdin} always ends with ideally. However, there are +" some programs which does not follow the POSIX rule and a Vim's way to join +" List into TEXT; join({text}, "\n"); does not add to the end of +" the last line. +" That's why add a trailing if it does not exist. +" REF: +" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392 +" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205 +" :help split() +" NOTE: +" it does nothing if the text is a correct POSIX text +function! s:repair_posix_text(text, ...) abort + let newline = get(a:000, 0, "\n") + return a:text =~# '\n$' ? a:text : a:text . newline +endfunction + +" NOTE: +" A definition of a TEXT file is "A file that contains characters organized +" into one or more lines." +" A definition of a LINE is "A sequence of zero or more non- s +" plus a terminating " +" REF: +" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392 +" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205 +function! s:join_posix_lines(lines, ...) abort + let newline = get(a:000, 0, "\n") + return join(a:lines, newline) . newline +endfunction + +" NOTE: +" A definition of a TEXT file is "A file that contains characters organized +" into one or more lines." +" A definition of a LINE is "A sequence of zero or more non- s +" plus a terminating " +" TEXT into List; split({text}, '\r\?\n', 1); add an extra empty line at the +" end of List because the end of TEXT ends with and keepempty=1 is +" specified. (btw. keepempty=0 cannot be used because it will remove +" emptylines in the head and the tail). +" That's why removing a trailing before proceeding to 'split' is required +" REF: +" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392 +" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205 +function! s:split_posix_text(text, ...) abort + let newline = get(a:000, 0, '\r\?\n') + let text = substitute(a:text, newline . '$', '', '') + return split(text, newline, 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/_brightest/Gift.vim b/autoload/vital/_brightest/Gift.vim index 2219cfe..60f7788 100644 --- a/autoload/vital/_brightest/Gift.vim +++ b/autoload/vital/_brightest/Gift.vim @@ -1,3 +1,18 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Gift#import() abort + return map({'flatten': '', 'uniq_tabpagenr': '', 'tabpagewinnr_list': '', 'execute': '', 'getwinvar': '', 'winnr': '', 'jump_window': '', '_vital_depends': '', 'uniq_winnr': '', 'setwinvar': '', 'find': '', 'openable_bufnr_list': '', 'to_fullpath': '', 'bufnr': '', 'set_current_window': '', 'tabpagewinnr': '', 'close_window': '', 'close_window_by': '', 'uniq_winnr_list': '', '_vital_loaded': '', 'find_by': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Gift#import() abort', printf("return map({'flatten': '', 'uniq_tabpagenr': '', 'tabpagewinnr_list': '', 'execute': '', 'getwinvar': '', 'winnr': '', 'jump_window': '', '_vital_depends': '', 'uniq_winnr': '', 'setwinvar': '', 'find': '', 'openable_bufnr_list': '', 'to_fullpath': '', 'bufnr': '', 'set_current_window': '', 'tabpagewinnr': '', 'close_window': '', 'close_window_by': '', 'uniq_winnr_list': '', '_vital_loaded': '', 'find_by': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ scriptencoding utf-8 let s:save_cpo = &cpo set cpo&vim diff --git a/autoload/vital/_brightest/Gift/Tabpage.vim b/autoload/vital/_brightest/Gift/Tabpage.vim index cf1be45..2528457 100644 --- a/autoload/vital/_brightest/Gift/Tabpage.vim +++ b/autoload/vital/_brightest/Gift/Tabpage.vim @@ -1,3 +1,18 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Gift#Tabpage#import() abort + return map({'uniq_nr': '', 'make_uniq_nr': '', 'numbering': '', 'set_prefix': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Gift#Tabpage#import() abort', printf("return map({'uniq_nr': '', 'make_uniq_nr': '', 'numbering': '', 'set_prefix': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ scriptencoding utf-8 let s:save_cpo = &cpo set cpo&vim @@ -26,7 +41,7 @@ endfunction function! s:uniq_nr(...) let tabnr = get(a:, 1, tabpagenr()) - let uniq_nr = gettabvar(tabnr, s:prefix . "_gift_uniq_tabpagenr", -1) + let uniq_nr = get(gettabvar(tabnr, ""), s:prefix . "_gift_uniq_tabpagenr", -1) if uniq_nr == -1 let uniq_nr = s:numbering(tabnr) endif diff --git a/autoload/vital/_brightest/Gift/Window.vim b/autoload/vital/_brightest/Gift/Window.vim index ba39ed1..dedbbff 100644 --- a/autoload/vital/_brightest/Gift/Window.vim +++ b/autoload/vital/_brightest/Gift/Window.vim @@ -1,3 +1,18 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Gift#Window#import() abort + return map({'flatten': '', 'tabpagewinnr_list': '', 'execute': '', 'close': '', 'numbering': '', 'set_prefix': '', '_vital_depends': '', 'exists': '', 'jump': '', 'setvar': '', 'bufnr': '', 'uniq_nr': '', 'make_uniq_nr': '', 'tabpagewinnr': '', 'getvar': '', '_vital_loaded': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Gift#Window#import() abort', printf("return map({'flatten': '', 'tabpagewinnr_list': '', 'execute': '', 'close': '', 'numbering': '', 'set_prefix': '', '_vital_depends': '', 'exists': '', 'jump': '', 'setvar': '', 'bufnr': '', 'uniq_nr': '', 'make_uniq_nr': '', 'tabpagewinnr': '', 'getvar': '', '_vital_loaded': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ scriptencoding utf-8 let s:save_cpo = &cpo set cpo&vim @@ -26,7 +41,6 @@ function! s:flatten(list) endfunction - function! s:tabpagewinnr_list() return s:flatten(map(range(1, tabpagenr("$")), "map(range(1, tabpagewinnr(v:val, '$')), '['.v:val.', v:val]')")) endfunction @@ -53,7 +67,7 @@ endfunction function! s:uniq_nr(...) let winnr = get(a:, 1, winnr()) let tabnr = get(a:, 2, tabpagenr()) - let uniq_nr = gettabwinvar(tabnr, winnr, s:prefix . "_gift_uniq_winnr", -1) + let uniq_nr = get(gettabwinvar(tabnr, winnr, ""), s:prefix . "_gift_uniq_winnr", -1) if uniq_nr == -1 let uniq_nr = s:numbering(winnr, tabnr) endif @@ -84,7 +98,7 @@ endfunction function! s:getvar(nr, varname, ...) let def = get(a:, 1, "") let [tabnr, winnr] = s:tabpagewinnr(a:nr) - return gettabwinvar(tabnr, winnr, a:varname, def) + return get(gettabwinvar(tabnr, winnr, ""), a:varname, def) endfunction diff --git a/autoload/vital/_brightest/Prelude.vim b/autoload/vital/_brightest/Prelude.vim index 9d02b2e..09afb25 100644 --- a/autoload/vital/_brightest/Prelude.vim +++ b/autoload/vital/_brightest/Prelude.vim @@ -1,22 +1,42 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Prelude#import() abort + return map({'escape_pattern': '', 'is_funcref': '', 'path2directory': '', 'wcswidth': '', 'is_string': '', 'input_helper': '', 'is_number': '', 'is_cygwin': '', 'path2project_directory': '', 'strwidthpart_reverse': '', 'input_safe': '', 'is_list': '', 'truncate_skipping': '', 'glob': '', 'truncate': '', 'is_dict': '', 'set_default': '', 'is_numeric': '', 'getchar_safe': '', 'substitute_path_separator': '', 'is_mac': '', 'strwidthpart': '', 'getchar': '', 'is_unix': '', 'is_windows': '', 'globpath': '', 'escape_file_searching': '', 'is_float': '', 'smart_execute_command': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Prelude#import() abort', printf("return map({'escape_pattern': '', 'is_funcref': '', 'path2directory': '', 'wcswidth': '', 'is_string': '', 'input_helper': '', 'is_number': '', 'is_cygwin': '', 'path2project_directory': '', 'strwidthpart_reverse': '', 'input_safe': '', 'is_list': '', 'truncate_skipping': '', 'glob': '', 'truncate': '', 'is_dict': '', 'set_default': '', 'is_numeric': '', 'getchar_safe': '', 'substitute_path_separator': '', 'is_mac': '', 'strwidthpart': '', 'getchar': '', 'is_unix': '', 'is_windows': '', 'globpath': '', 'escape_file_searching': '', 'is_float': '', 'smart_execute_command': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ let s:save_cpo = &cpo set cpo&vim -if v:version ># 703 || -\ (v:version is 703 && has('patch465')) +if v:version > 703 || +\ (v:version == 703 && has('patch465')) function! s:glob(expr) abort return glob(a:expr, 1, 1) endfunction else function! s:glob(expr) abort - let R = glob(a:expr, 1) - return split(R, '\n') + return split(glob(a:expr, 1), '\n') endfunction endif -function! s:globpath(path, expr) abort - let R = globpath(a:path, a:expr, 1) - return split(R, '\n') -endfunction +if v:version > 704 || +\ (v:version == 704 && has('patch279')) + function! s:globpath(path, expr) abort + return globpath(a:path, a:expr, 1, 1) + endfunction +else + function! s:globpath(path, expr) abort + return split(globpath(a:path, a:expr, 1), '\n') + endfunction +endif " Wrapper functions for type(). let [ @@ -27,7 +47,7 @@ let [ \ s:__TYPE_DICT, \ s:__TYPE_FLOAT] = [ \ type(3), - \ type(""), + \ type(''), \ type(function('tr')), \ type([]), \ type({}), @@ -68,12 +88,9 @@ function! s:is_dict(Value) abort return type(a:Value) ==# s:__TYPE_DICT endfunction -function! s:truncate_smart(str, max, footer_width, separator) abort - 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') + let width = s:wcswidth(a:str) if width <= a:max let ret = a:str @@ -90,6 +107,8 @@ 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) @@ -110,6 +129,8 @@ function! s:truncate(str, width) abort endfunction function! s:strwidthpart(str, width) abort + call s:_warn_deprecated('strwidthpart', 'Data.String.strwidthpart') + if a:width <= 0 return '' endif @@ -124,6 +145,8 @@ function! s:strwidthpart(str, width) abort return ret endfunction function! s:strwidthpart_reverse(str, width) abort + call s:_warn_deprecated('strwidthpart_reverse', 'Data.String.strwidthpart_reverse') + if a:width <= 0 return '' endif @@ -141,10 +164,13 @@ endfunction if v:version >= 703 " Use builtin function. function! s:wcswidth(str) abort + call s:_warn_deprecated('wcswidth', 'Data.String.wcswidth') return strwidth(a:str) endfunction else function! s:wcswidth(str) abort + call s:_warn_deprecated('wcswidth', 'Data.String.wcswidth') + if a:str =~# '^[\x00-\x7f]*$' return strlen(a:str) end @@ -209,13 +235,17 @@ function! s:is_unix() abort return s:is_unix endfunction -function! s:_deprecated2(fname) abort - 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) abort - execute a:action . ' ' . (a:word == '' ? '' : '`=a:word`') + execute a:action . ' ' . (a:word ==# '' ? '' : '`=a:word`') endfunction function! s:escape_file_searching(buffer_name) abort @@ -223,6 +253,10 @@ function! s:escape_file_searching(buffer_name) abort endfunction function! s:escape_pattern(str) abort + call s:_warn_deprecated( + \ 'escape_pattern', + \ 'Data.String.escape_pattern', + \) return escape(a:str, '~"\.^$[]*') endfunction @@ -233,7 +267,7 @@ endfunction function! s:getchar_safe(...) abort let c = s:input_helper('getchar', a:000) - return type(c) == type("") ? c : nr2char(c) + return type(c) == type('') ? c : nr2char(c) endfunction function! s:input_safe(...) abort @@ -243,13 +277,13 @@ endfunction function! s:input_helper(funcname, args) abort let success = 0 if inputsave() !=# success - throw 'inputsave() failed' + throw 'vital: Prelude: inputsave() failed' endif try return call(a:funcname, a:args) finally if inputrestore() !=# success - throw 'inputrestore() failed' + throw 'vital: Prelude: inputrestore() failed' endif endtry endfunction @@ -260,16 +294,6 @@ function! s:set_default(var, val) abort endif endfunction -function! s:set_dictionary_helper(variable, keys, pattern) abort - 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) abort return s:is_windows ? substitute(a:path, '\\', '/', 'g') : a:path endfunction @@ -300,7 +324,7 @@ function! s:_path2project_directory_svn(path) abort let find_directory = s:escape_file_searching(search_directory) let d = finddir('.svn', find_directory . ';') - if d == '' + if d ==# '' return '' endif @@ -310,9 +334,9 @@ function! s:_path2project_directory_svn(path) abort let parent_directory = s:path2directory( \ fnamemodify(directory, ':h')) - if parent_directory != '' + if parent_directory !=# '' let d = finddir('.svn', parent_directory . ';') - if d != '' + if d !=# '' let directory = s:_path2project_directory_svn(parent_directory) endif endif @@ -325,7 +349,7 @@ function! s:_path2project_directory_others(vcs, path) abort let find_directory = s:escape_file_searching(search_directory) let d = finddir(vcs, find_directory . ';') - if d == '' + if d ==# '' return '' endif return fnamemodify(d, ':p:h:h') @@ -345,25 +369,25 @@ function! s:path2project_directory(path, ...) abort else let directory = s:_path2project_directory_others(vcs, search_directory) endif - if directory != '' + if directory !=# '' break endif endfor " Search project file. - if directory == '' + if directory ==# '' for d in ['build.xml', 'prj.el', '.project', 'pom.xml', 'package.json', \ 'Makefile', 'configure', 'Rakefile', 'NAnt.build', \ 'P4CONFIG', 'tags', 'gtags'] let d = findfile(d, s:escape_file_searching(search_directory) . ';') - if d != '' + if d !=# '' let directory = fnamemodify(d, ':p:h') break endif endfor endif - if directory == '' + if directory ==# '' " Search /src/ directory. let base = s:substitute_path_separator(search_directory) if base =~# '/src/' @@ -371,7 +395,7 @@ function! s:path2project_directory(path, ...) abort endif endif - if directory == '' && !is_allow_empty + if directory ==# '' && !is_allow_empty " Use original path. let directory = search_directory endif diff --git a/autoload/vital/_brightest/Vim/Buffer.vim b/autoload/vital/_brightest/Vim/Buffer.vim index ad12c08..2a0e262 100644 --- a/autoload/vital/_brightest/Vim/Buffer.vim +++ b/autoload/vital/_brightest/Vim/Buffer.vim @@ -1,13 +1,29 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Vim#Buffer#import() abort + return map({'_vital_depends': '', 'read_content': '', 'get_selected_text': '', 'is_cmdwin': '', 'edit_content': '', 'open': '', 'get_last_selected': '', '_vital_loaded': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Vim#Buffer#import() abort', printf("return map({'_vital_depends': '', 'read_content': '', 'get_selected_text': '', 'is_cmdwin': '', 'edit_content': '', 'open': '', 'get_last_selected': '', '_vital_loaded': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ 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:G = s:V.import('Vim.Guard') endfunction function! s:_vital_depends() abort - return ['Prelude'] + return ['Prelude', 'Vim.Guard'] endfunction if exists('*getcmdwintype') @@ -27,7 +43,7 @@ function! s:open(buffer, opener) abort if s:P.is_funcref(a:opener) let loaded = !bufloaded(a:buffer) call a:opener(a:buffer) - elseif a:buffer is 0 || a:buffer is '' + elseif a:buffer is 0 || a:buffer is# '' let loaded = 1 silent execute a:opener enew @@ -85,10 +101,58 @@ function! s:get_last_selected() abort \ + (end[1] - begin[1] <# 2 ? [] : getline(begin[1]+1, end[1]-1)) \ + [getline(end[1])[: end[2]-2]] endif - return join(lines, "\n") . lastchar . (visualmode() ==# "V" ? "\n" : "") + return join(lines, "\n") . lastchar . (visualmode() ==# 'V' ? "\n" : '') endif endfunction +function! s:read_content(content, ...) abort + let options = extend({ + \ 'tempfile': '', + \ 'fileformat': '', + \ 'encoding': '', + \ 'binary': 0, + \ 'nobinary': 0, + \ 'bad': '', + \ 'edit': 0, + \}, get(a:000, 0, {})) + let tempfile = empty(options.tempfile) ? tempname() : options.tempfile + let optnames = [ + \ empty(options.fileformat) ? '' : '++ff=' . options.fileformat, + \ empty(options.encoding) ? '' : '++enc=' . options.encoding, + \ empty(options.binary) ? '' : '++bin', + \ empty(options.nobinary) ? '' : '++nobin', + \ empty(options.bad) ? '' : '++bad=' . options.bad, + \ empty(options.edit) ? '' : '++edit', + \] + let optname = join(filter(optnames, '!empty(v:val)')) + try + call writefile(a:content, tempfile) + execute printf('keepalt keepjumps read %s%s', + \ empty(optname) ? '' : optname . ' ', + \ fnameescape(tempfile), + \) + finally + call delete(tempfile) + endtry +endfunction + +function! s:edit_content(content, ...) abort + let options = extend({ + \ 'edit': 1, + \}, get(a:000, 0, {})) + let guard = s:G.store(['&l:modifiable']) + let saved_view = winsaveview() + try + let &l:modifiable=1 + silent keepjumps %delete _ + silent call s:read_content(a:content, options) + silent keepjumps 1delete _ + finally + keepjump call winrestview(saved_view) + call guard.restore() + endtry + setlocal nomodified +endfunction let &cpo = s:save_cpo unlet s:save_cpo diff --git a/autoload/vital/_brightest/Vim/Guard.vim b/autoload/vital/_brightest/Vim/Guard.vim new file mode 100644 index 0000000..621dd52 --- /dev/null +++ b/autoload/vital/_brightest/Vim/Guard.vim @@ -0,0 +1,235 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_brightest#Vim#Guard#import() abort + return map({'_vital_depends': '', '_vital_created': '', 'store': '', '_vital_loaded': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_brightest#Vim#Guard#import() abort', printf("return map({'_vital_depends': '', '_vital_created': '', 'store': '', '_vital_loaded': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ +let s:save_cpo = &cpo +set cpo&vim + +" Use a Funcref as a special term _UNDEFINED +function! s:_undefined() abort + return 'undefined' +endfunction +let s:_UNDEFINED = function('s:_undefined') + +function! s:_vital_loaded(V) abort + let s:V = a:V + let s:Prelude = s:V.import('Prelude') + let s:List = s:V.import('Data.List') + let s:Dict = s:V.import('Data.Dict') +endfunction +function! s:_vital_depends() abort + return ['Prelude', 'Data.List', 'Data.Dict'] +endfunction +function! s:_vital_created(module) abort + " define constant variables + if !exists('s:const') + let s:const = {} + let s:const.is_local_variable_supported = + \ v:version > 703 || (v:version == 703 && has('patch560')) + " NOTE: + " The third argument is available from 7.4.242 but it had bug and that + " bug was fixed from 7.4.513 + let s:const.is_third_argument_of_getreg_supported = has('patch-7.4.513') + lockvar s:const + endif + call extend(a:module, s:const) +endfunction +function! s:_throw(msg) abort + throw printf('vital: Vim.Guard: %s', a:msg) +endfunction + +let s:option = {} +function! s:_new_option(name) abort + if a:name !~# '^&' + call s:_throw(printf( + \'An option name "%s" requires to be started from "&"', a:name + \)) + elseif !exists(a:name) + call s:_throw(printf( + \'An option name "%s" does not exist', a:name + \)) + endif + let option = copy(s:option) + let option.name = a:name + let option.value = eval(a:name) + return option +endfunction +function! s:option.restore() abort + execute printf('let %s = %s', self.name, string(self.value)) +endfunction + +let s:register = {} +function! s:_new_register(name) abort + if len(a:name) != 2 + call s:_throw(printf( + \'A register name "%s" requires to be "@" + a single character', a:name + \)) + elseif a:name !~# '^@' + call s:_throw(printf( + \'A register name "%s" requires to be started from "@"', a:name + \)) + elseif a:name =~# '^@[:.%]$' + call s:_throw(printf( + \'A register name "%s" is read only', a:name + \)) + elseif a:name !~# '^@[@0-9a-zA-Z#=*+~_/-]$' + call s:_throw(printf( + \'A register name "%s" does not exist. See ":help let-register"', a:name + \)) + endif + let name = a:name ==# '@@' ? '' : a:name[1] + let register = copy(s:register) + let register.name = name + if s:const.is_third_argument_of_getreg_supported + let register.value = getreg(name, 1, 1) + else + let register.value = getreg(name, 1) + endif + let register.type = getregtype(name) + return register +endfunction +function! s:register.restore() abort + call setreg(self.name, self.value, self.type) +endfunction + +let s:environment = {} +function! s:_new_environment(name) abort + if a:name !~# '^\$' + call s:_throw(printf( + \'An environment variable name "%s" requires to be started from "$"', a:name + \)) + elseif !exists(a:name) + call s:_throw(printf( + \'An environment variable name "%s" does not exist. While Vim cannot unlet environment variable, it requires to exist', a:name + \)) + endif + let environment = copy(s:environment) + let environment.name = a:name + let environment.value = eval(a:name) + return environment +endfunction +function! s:environment.restore() abort + execute printf('let %s = %s', self.name, string(self.value)) +endfunction + +let s:variable = {} +function! s:_new_variable(name, ...) abort + if a:0 == 0 + let m = matchlist(a:name, '^\([bwtg]:\)\(.*\)$') + if empty(m) + call s:_throw(printf( + \ join([ + \ 'An variable name "%s" requires to start from b:, w:, t:, or g:', + \ 'while no {namespace} is specified', + \ ]), + \ a:name, + \)) + endif + let [prefix, name] = m[1 : 2] + let namespace = eval(prefix) + else + let name = a:name + let namespace = a:1 + endif + let variable = copy(s:variable) + let variable.name = name + let variable.value = get(namespace, name, s:_UNDEFINED) + let variable.value = + \ type(variable.value) == type({}) || type(variable.value) == type([]) + \ ? deepcopy(variable.value) + \ : variable.value + let variable._namespace = namespace + return variable +endfunction +function! s:variable.restore() abort + " unlet the variable to prevent variable type mis-match in case + silent! unlet! self._namespace[self.name] + if type(self.value) == type(s:_UNDEFINED) && self.value == s:_UNDEFINED + " do nothing, leave the variable as undefined + else + let self._namespace[self.name] = self.value + endif +endfunction + +let s:instance = {} +function! s:_new_instance(instance, ...) abort + let shallow = get(a:000, 0, 0) + if !s:Prelude.is_list(a:instance) && !s:Prelude.is_dict(a:instance) + call s:_throw(printf( + \'An instance "%s" requires to be List or Dictionary', string(a:instance) + \)) + endif + let instance = copy(s:instance) + let instance.instance = a:instance + let instance.values = shallow ? copy(a:instance) : deepcopy(a:instance) + return instance +endfunction +function! s:instance.restore() abort + if s:Prelude.is_list(self.instance) + call s:List.clear(self.instance) + else + call s:Dict.clear(self.instance) + endif + call extend(self.instance, self.values) +endfunction + +let s:guard = {} +function! s:store(targets) abort + let resources = [] + for meta in a:targets + if s:Prelude.is_list(meta) + if len(meta) == 1 + call add(resources, s:_new_instance(meta[0])) + elseif len(meta) == 2 + if s:Prelude.is_string(meta[0]) + call add(resources, call('s:_new_variable', meta)) + else + call add(resources, call('s:_new_instance', meta)) + endif + else + call s:_throw('List assignment requires one or two elements') + endif + elseif type(meta) == type('') + if meta =~# '^[bwtgls]:' + " Note: + " To improve an error message, handle l:XXX or s:XXX as well + call add(resources, s:_new_variable(meta)) + elseif meta =~# '^&' + call add(resources, s:_new_option(meta)) + elseif meta =~# '^@' + call add(resources, s:_new_register(meta)) + elseif meta =~# '^\$' + call add(resources, s:_new_environment(meta)) + else + call s:_throw(printf( + \ 'Unknown value "%s" was specified', + \ meta + \)) + endif + endif + unlet meta + endfor + let guard = copy(s:guard) + let guard._resources = resources + return guard +endfunction +function! s:guard.restore() abort + for resource in self._resources + call resource.restore() + endfor +endfunction + +let &cpo = s:save_cpo +unlet! s:save_cpo +" vim:set et ts=2 sts=2 sw=2 tw=0 fdm=marker: diff --git a/autoload/vital/brightest.vim b/autoload/vital/brightest.vim new file mode 100644 index 0000000..4f9010c --- /dev/null +++ b/autoload/vital/brightest.vim @@ -0,0 +1,339 @@ +let s:plugin_name = expand(':t:r') +let s:vital_base_dir = expand(':h') +let s:project_root = expand(':h:h:h') +let s:is_vital_vim = s:plugin_name is# 'vital' + +let s:loaded = {} +let s:cache_sid = {} + +" function() wrapper +if v:version > 703 || v:version == 703 && has('patch1170') + function! s:_function(fstr) abort + return function(a:fstr) + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + let s:_s = '' . s:_SID() . '_' + function! s:_function(fstr) abort + return function(substitute(a:fstr, 's:', s:_s, 'g')) + endfunction +endif + +function! vital#{s:plugin_name}#of() abort + return s:new(s:plugin_name) +endfunction + +function! vital#{s:plugin_name}#import(...) abort + if !exists('s:V') + let s:V = s:new(s:plugin_name) + endif + return call(s:V.import, a:000, s:V) +endfunction + +let s:Vital = {} + +function! s:new(plugin_name) abort + let base = deepcopy(s:Vital) + let base._plugin_name = a:plugin_name + return base +endfunction + +function! s:vital_files() abort + if !exists('s:vital_files') + let s:vital_files = map( + \ s:is_vital_vim ? s:_global_vital_files() : s:_self_vital_files(), + \ 'fnamemodify(v:val, ":p:gs?[\\\\/]?/?")') + endif + return copy(s:vital_files) +endfunction +let s:Vital.vital_files = s:_function('s:vital_files') + +function! s:import(name, ...) abort dict + let target = {} + let functions = [] + for a in a:000 + if type(a) == type({}) + let target = a + elseif type(a) == type([]) + let functions = a + endif + unlet a + endfor + let module = self._import(a:name) + if empty(functions) + call extend(target, module, 'keep') + else + for f in functions + if has_key(module, f) && !has_key(target, f) + let target[f] = module[f] + endif + endfor + endif + return target +endfunction +let s:Vital.import = s:_function('s:import') + +function! s:load(...) abort dict + for arg in a:000 + let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg] + let target = split(join(as, ''), '\W\+') + let dict = self + let dict_type = type({}) + while !empty(target) + let ns = remove(target, 0) + if !has_key(dict, ns) + let dict[ns] = {} + endif + if type(dict[ns]) == dict_type + let dict = dict[ns] + else + unlet dict + break + endif + endwhile + if exists('dict') + call extend(dict, self._import(name)) + endif + unlet arg + endfor + return self +endfunction +let s:Vital.load = s:_function('s:load') + +function! s:unload() abort dict + let s:loaded = {} + let s:cache_sid = {} + unlet! s:vital_files +endfunction +let s:Vital.unload = s:_function('s:unload') + +function! s:exists(name) abort dict + if a:name !~# '\v^\u\w*%(\.\u\w*)*$' + throw 'vital: Invalid module name: ' . a:name + endif + return s:_module_path(a:name) isnot# '' +endfunction +let s:Vital.exists = s:_function('s:exists') + +function! s:search(pattern) abort dict + let paths = s:_extract_files(a:pattern, self.vital_files()) + let modules = sort(map(paths, 's:_file2module(v:val)')) + return s:_uniq(modules) +endfunction +let s:Vital.search = s:_function('s:search') + +function! s:plugin_name() abort dict + return self._plugin_name +endfunction +let s:Vital.plugin_name = s:_function('s:plugin_name') + +function! s:_self_vital_files() abort + let builtin = printf('%s/__%s__/', s:vital_base_dir, s:plugin_name) + let installed = printf('%s/_%s/', s:vital_base_dir, s:plugin_name) + let base = builtin . ',' . installed + return split(globpath(base, '**/*.vim', 1), "\n") +endfunction + +function! s:_global_vital_files() abort + let pattern = 'autoload/vital/__*__/**/*.vim' + return split(globpath(&runtimepath, pattern, 1), "\n") +endfunction + +function! s:_extract_files(pattern, files) abort + let tr = {'.': '/', '*': '[^/]*', '**': '.*'} + let target = substitute(a:pattern, '\.\|\*\*\?', '\=tr[submatch(0)]', 'g') + let regexp = printf('autoload/vital/[^/]\+/%s.vim$', target) + return filter(a:files, 'v:val =~# regexp') +endfunction + +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 + +" @param {string} name e.g. Data.List +function! s:_import(name) abort dict + if has_key(s:loaded, a:name) + return copy(s:loaded[a:name]) + endif + let module = self._get_module(a:name) + if has_key(module, '_vital_created') + call module._vital_created(module) + endif + let export_module = filter(copy(module), 'v:key =~# "^\\a"') + " Cache module before calling module.vital_loaded() to avoid cyclic + " dependences but remove the cache if module._vital_loaded() fails. + " let s:loaded[a:name] = export_module + let s:loaded[a:name] = export_module + if has_key(module, '_vital_loaded') + try + call module._vital_loaded(vital#{s:plugin_name}#of()) + catch + unlet s:loaded[a:name] + throw 'vital: fail to call ._vital_loaded(): ' . v:exception + endtry + endif + return copy(s:loaded[a:name]) +endfunction +let s:Vital._import = s:_function('s:_import') + +" s:_get_module() returns module object wihch has all script local functions. +function! s:_get_module(name) abort dict + let funcname = s:_import_func_name(self.plugin_name(), a:name) + if s:_exists_autoload_func_with_source(funcname) + return call(funcname, []) + else + return s:_get_builtin_module(a:name) + endif +endfunction + +function! s:_get_builtin_module(name) abort + return s:sid2sfuncs(s:_module_sid(a:name)) +endfunction + +if s:is_vital_vim + " For vital.vim, we can use s:_get_builtin_module directly + let s:Vital._get_module = s:_function('s:_get_builtin_module') +else + let s:Vital._get_module = s:_function('s:_get_module') +endif + +function! s:_import_func_name(plugin_name, module_name) abort + return printf('vital#_%s#%s#import', a:plugin_name, s:_dot_to_sharp(a:module_name)) +endfunction + +function! s:_module_sid(name) abort + let path = s:_module_path(a:name) + if !filereadable(path) + throw 'vital: module not found: ' . a:name + endif + let vital_dir = s:is_vital_vim ? '__\w\+__' : printf('_\{1,2}%s\%%(__\)\?', s:plugin_name) + let base = join([vital_dir, ''], '[/\\]\+') + let p = base . substitute('' . a:name, '\.', '[/\\\\]\\+', 'g') + let sid = s:_sid(path, p) + if !sid + call s:_source(path) + let sid = s:_sid(path, p) + if !sid + throw printf('vital: cannot get from path: %s', path) + endif + endif + return sid +endfunction + +function! s:_module_path(name) abort + return get(s:_extract_files(a:name, s:vital_files()), 0, '') +endfunction + +function! s:_module_sid_base_dir() abort + return s:is_vital_vim ? &rtp : s:project_root +endfunction + +function! s:_dot_to_sharp(name) abort + return substitute(a:name, '\.', '#', 'g') +endfunction + +" It will sources autoload file if a given func is not already defined. +function! s:_exists_autoload_func_with_source(funcname) abort + if exists('*' . a:funcname) + " Return true if a given func is already defined + return 1 + endif + " source a file which may include a given func definition and try again. + let path = 'autoload/' . substitute(substitute(a:funcname, '#[^#]*$', '.vim', ''), '#', '/', 'g') + call s:_runtime(path) + return exists('*' . a:funcname) +endfunction + +function! s:_runtime(path) abort + execute 'runtime' fnameescape(a:path) +endfunction + +function! s:_source(path) abort + execute 'source' fnameescape(a:path) +endfunction + +" @vimlint(EVL102, 1, l:_) +" @vimlint(EVL102, 1, l:__) +function! s:_sid(path, filter_pattern) abort + let unified_path = s:_unify_path(a:path) + if has_key(s:cache_sid, unified_path) + return s:cache_sid[unified_path] + endif + for line in filter(split(s:_redir(':scriptnames'), "\n"), 'v:val =~# a:filter_pattern') + let [_, sid, path; __] = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$') + if s:_unify_path(path) is# unified_path + let s:cache_sid[unified_path] = sid + return s:cache_sid[unified_path] + endif + endfor + return 0 +endfunction + +function! s:_redir(cmd) abort + let [save_verbose, save_verbosefile] = [&verbose, &verbosefile] + set verbose=0 verbosefile= + redir => res + silent! execute a:cmd + redir END + let [&verbose, &verbosefile] = [save_verbose, save_verbosefile] + return res +endfunction + +if filereadable(expand(':r') . '.VIM') " is case-insensitive or not + let s:_unify_path_cache = {} + " resolve() is slow, so we cache results. + " 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) abort + return resolve(fnamemodify(a:path, ':p:gs?[\\/]?/?')) + endfunction +endif + +" copied and modified from Vim.ScriptLocal +let s:SNR = join(map(range(len("\")), '"[\\x" . printf("%0x", char2nr("\"[v:val])) . "]"'), '') +function! s:sid2sfuncs(sid) abort + let fs = split(s:_redir(printf(':function /^%s%s_', s:SNR, a:sid)), "\n") + let r = {} + let pattern = printf('\m^function\s%d_\zs\w\{-}\ze(', a:sid) + for fname in map(fs, 'matchstr(v:val, pattern)') + let r[fname] = function(s:_sfuncname(a:sid, fname)) + endfor + return r +endfunction + +"" Return funcname of script local functions with SID +function! s:_sfuncname(sid, funcname) abort + return printf('%s_%s', a:sid, a:funcname) +endfunction + +if exists('*uniq') + function! s:_uniq(list) abort + return uniq(a:list) + endfunction +else + function! s:_uniq(list) abort + let i = len(a:list) - 1 + while 0 < i + if a:list[i] ==# a:list[i - 1] + call remove(a:list, i) + endif + let i -= 1 + endwhile + return a:list + endfunction +endif diff --git a/autoload/vital/brightest.vital b/autoload/vital/brightest.vital index cd2dde1..243c5bf 100644 --- a/autoload/vital/brightest.vital +++ b/autoload/vital/brightest.vital @@ -1,5 +1,5 @@ brightest -e3250cf +49faf10e4d39eaa0dee1643acab5fc846bede610 Coaster Prelude @@ -7,3 +7,4 @@ Coaster.Buffer Coaster.Highlight Coaster.Search Coaster.Window +Data.String