From e573b3020c032400eed60b649a2cbf55266e6bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Thu, 30 Jun 2016 16:03:25 +0200 Subject: Add current configurations from old repository --- .../vim-addon-mw-utils/autoload/buf_utils.vim | 24 ++ .../autoload/cached_file_contents.vim | 104 +++++ .../vim-addon-mw-utils/autoload/env_reload.vim | 12 + vim/bundle/vim-addon-mw-utils/autoload/funcref.vim | 95 +++++ vim/bundle/vim-addon-mw-utils/autoload/glob.vim | 27 ++ .../vim-addon-mw-utils/autoload/tiny_cmd.vim | 19 + .../autoload/tovl/scratch_buffer.vim | 103 +++++ .../autoload/tovl/ui/filter_list.vim | 473 +++++++++++++++++++++ 8 files changed, 857 insertions(+) create mode 100644 vim/bundle/vim-addon-mw-utils/autoload/buf_utils.vim create mode 100644 vim/bundle/vim-addon-mw-utils/autoload/cached_file_contents.vim create mode 100644 vim/bundle/vim-addon-mw-utils/autoload/env_reload.vim create mode 100644 vim/bundle/vim-addon-mw-utils/autoload/funcref.vim create mode 100644 vim/bundle/vim-addon-mw-utils/autoload/glob.vim create mode 100644 vim/bundle/vim-addon-mw-utils/autoload/tiny_cmd.vim create mode 100644 vim/bundle/vim-addon-mw-utils/autoload/tovl/scratch_buffer.vim create mode 100644 vim/bundle/vim-addon-mw-utils/autoload/tovl/ui/filter_list.vim (limited to 'vim/bundle/vim-addon-mw-utils/autoload') diff --git a/vim/bundle/vim-addon-mw-utils/autoload/buf_utils.vim b/vim/bundle/vim-addon-mw-utils/autoload/buf_utils.vim new file mode 100644 index 0000000..d14d404 --- /dev/null +++ b/vim/bundle/vim-addon-mw-utils/autoload/buf_utils.vim @@ -0,0 +1,24 @@ +" buf_identifier is either a buf_nr or a filename +" If any window shows the buffer move to the buffer +" If not show it in current window (by c-w s c^ you can always +" reshow the last buffer +" +" Example: buf_utils#GotoBuf("/tmp/tfile.txt", {'create': 1}) +" returns: The command which was used to switch to the buffer +fun! buf_utils#GotoBuf(buf_identifier, opts) + let buf_nr = bufnr(a:buf_identifier) + if buf_nr == -1 && ( get(a:opts, 'create', 0) || has_key(a:opts, 'create_cmd')) + exec get(a:opts,'create_cmd','e').' '.fnameescape(a:buf_identifier) + return "e" + else + let win_nr = bufwinnr(buf_nr) + if win_nr == -1 + exec 'b '.buf_nr + return "b" + else + exec win_nr.'wincmd w' + return "w" + endif + wincmd w" + endif +endf diff --git a/vim/bundle/vim-addon-mw-utils/autoload/cached_file_contents.vim b/vim/bundle/vim-addon-mw-utils/autoload/cached_file_contents.vim new file mode 100644 index 0000000..3fc3ce4 --- /dev/null +++ b/vim/bundle/vim-addon-mw-utils/autoload/cached_file_contents.vim @@ -0,0 +1,104 @@ +" cached_file_contents.vim +" @License: GPL (see http://www.gnu.org/licenses/gpl.txt) +" @Last Change: 2010-01-03. +" @Revision: 0.3.0 + +"exec vam#DefineAndBind('s:c','g:cache_dir_options','{}') +if !exists('g:cache_dir_options') | let g:cache_dir_options = {} | endif | let s:c = g:cache_dir_options + +let s:c['cache_dir'] = get(s:c, 'cache_dir', expand('$HOME').'/.vim-cache') +let s:c['scanned_files'] = get(s:c, 'scanned_files', {}) +let s:scanned_files = s:c['scanned_files'] + + +let s:define_cache_file = "let this_dir = s:c['cache_dir'].'/cached-file-contents' | let cache_file = expand(this_dir.'/'.substitute(string([func_as_string, a:file]),'[[\\]{}:/\\,''\"# ]\\+','_','g'))" + +" read a file, run function to extract contents and cache the result returned +" by that function in memory. Optionally the result can be cached on disk as +" because VimL can be slow! +" +" file : the file to be read +" func: { 'func': function which will be called by funcref#Call +" , 'version' : if this version changes cache will be invalidate automatically +" , 'ftime_check': optional, default 1. if set to 0 cache isn't updated when file changes and file is in cache +" } +" +" default: what to return if file doesn't exist +" think twice about adding lines. This function is called many times. +function! cached_file_contents#CachedFileContents(file, func, ...) abort + let ignore_ftime = a:0 > 0 ? a:1 : 0 + " using string for default so that is evaluated when needed only + let use_file_cache = get(a:func, 'use_file_cache', 0) + + " simple kind of normalization. necessary when using file caching + " this seems to be slower: + " let file = fnamemodify(a:file, ':p') " simple kind of normalization. necessary when using file caching + " / = assume its an absolute path + " let file = a:file[0] == '/' ? a:file : expand(a:file, ':p') + let file = a:file[0] == '/' ? a:file : fnamemodify(a:file, ':p') " simple kind of normalization. necessary when using file caching + let func_as_string = string(a:func['func']) + + if (!has_key(s:scanned_files, func_as_string)) + let s:scanned_files[func_as_string] = {} + endif + let dict = s:scanned_files[func_as_string] + if use_file_cache && !has_key(dict, a:file) + exec s:define_cache_file + if filereadable(cache_file) + let dict[file] = eval(readfile(cache_file,'b')[0]) + endif + endif + if has_key(dict, a:file) + let d = dict[a:file] + if use_file_cache + \ && (ignore_ftime || getftime(a:file) <= d['ftime']) + \ && d['version'] == a:func['version'] + return dict[a:file]['scan_result'] + endif + endif + let scan_result = funcref#Call(a:func['func'], [a:file] ) + let dict[a:file] = {"ftime": getftime(a:file), 'version': a:func['version'], "scan_result": scan_result } + if use_file_cache + if !exists('cache_file') | exec s:define_cache_file | endif + if !isdirectory(this_dir) | call mkdir(this_dir,'p',0700) | endif + call writefile([string(dict[a:file])], cache_file) + endif + return scan_result +endfunction + +fun! cached_file_contents#ClearScanCache() + let s:c['scanned_files'] = {} + + " Don't run rm -fr. Ask user to run it. It cache_dir may have been set to + " $HOME ! (should nevere be the case but who knows + echoe "run manually in your shell: rm -fr ".shellescape(s:c['cache_dir'])."/*" +endf + +fun! cached_file_contents#Test() + + " usually you use a global option so that the function can be reused + let my_interpreting_func = {'func' : funcref#Function('return len(readfile(ARGS[0]))'), 'version': 2, 'use_file_cache':1} + let my_interpreting_func2 = {'func' : funcref#Function('return ARGS[0]') , 'version': 2, 'use_file_cache':1} + + let tmp = tempname() + call writefile(['some text','2nd line'], tmp) + + let r = [ cached_file_contents#CachedFileContents(tmp, my_interpreting_func) + \ , cached_file_contents#CachedFileContents(tmp, my_interpreting_func2) ] + if r != [2, tmp] + throw "test failed 1, got ".string(r) + endif + unlet r + + sleep 3 + + " now let's change contents + call writefile(['some text','2nd line','3rd line'], tmp) + + let r = cached_file_contents#CachedFileContents(tmp, my_interpreting_func) + if 3 != r + throw "test failed 2, got ".string(r) + endif + + echo "test passed" +endf diff --git a/vim/bundle/vim-addon-mw-utils/autoload/env_reload.vim b/vim/bundle/vim-addon-mw-utils/autoload/env_reload.vim new file mode 100644 index 0000000..d8af33a --- /dev/null +++ b/vim/bundle/vim-addon-mw-utils/autoload/env_reload.vim @@ -0,0 +1,12 @@ +" in sh/bash you can type export to get a list of environment variables +" This function assigns those env vars to Vim. +" Does not delete env vars yet +" Example: env_reload#ReloadEnv(system("sh -c 'export'") +fun! env_reload#ReloadEnv(bash_export_command_output) + for i in split(a:bash_export_command_output,"\n") + let m = matchlist(i, 'export \([^=]\+\)="\(.*\)"') + if empty(m) | continue | endif + " don't care about quoted values right now. + exec 'let $'.m[1].'='.string(m[2]) + endfor +endf diff --git a/vim/bundle/vim-addon-mw-utils/autoload/funcref.vim b/vim/bundle/vim-addon-mw-utils/autoload/funcref.vim new file mode 100644 index 0000000..20eec54 --- /dev/null +++ b/vim/bundle/vim-addon-mw-utils/autoload/funcref.vim @@ -0,0 +1,95 @@ +" funcref.vim +" @License: GPL (see http://www.gnu.org/licenses/gpl.txt) +" @Last Change: 2010-01-03. +" @Revision: 0.1.0 + +" documentation see doc/funcref.txt + +" usage: +" funcref#Function("filename#Function") +" optionally pass arguments: +" funcref#Function("filename#Function",{'args': [2]}) +" optionally define self: +" funcref#Function("filename#Function",{'self': object}) +function! funcref#Function(name,...) + let d = a:0 > 0 ? a:1 : {} + let d['faked_function_reference'] = a:name + return d +endfunction + +" args : same as used for call(f,[args], self) +" f must be either +" - a string which can be evaled (use "return 'value'" to return a value) +" - a Vim function reference created by function('..') +" - a faked function reference created by funcref#Function(..) +" +" the last "self" argument can be overriden by the function reference +" You can pass arguments in a closure like style +function! funcref#Call(...) + let args = copy(a:000) + + " add parameters: + if (len(args) < 2) + call add(args, []) + endif + + + let isDict = type(args[0]) == type({}) + + " prepend parameters which were passed by faked function reference: + if isDict && has_key(args[0], 'args') + let args[1] = args[0]['args']+args[1] + endif + + " always pass self. this way you can call functions from dictionaries not + " refering to self + if (len(args) < 3) + call add(args, {}) + endif + + " the funcref overrides self: + if isDict && has_key(args[0], 'self') + let args[2] = args[0]['self'] + endif + + if type(a:1) == 2 + " funcref: function must have been laoded + return call(function('call'), args) + elseif isDict && has_key(args[0], 'faked_function_reference') + let Fun = args[0]['faked_function_reference'] + if type(Fun) == type('') + \ && (Fun[:len('return ')-1] == 'return ' + \ || Fun[:len('call ')-1] == 'call ' + \ || Fun[:len('if ')-1] == 'if ' + \ || Fun[:len('let ')-1] == 'let ' + \ || Fun[:len('echo ')-1] == 'echo ' + \ || Fun[:len('exec ')-1] == 'exec ' + \ || Fun[:len('debug ')-1] == 'debug ') + " it doesn't make sense to list all vim commands here + " So if you want to execute another action consider using + " funcref#Function('exec '.string('aw')) or such + + " function is a String, call exec + let ARGS = args[1] + let SELF = args[2] + exec Fun + else + " pseudo function, let's load it.. + if type(Fun) == 1 + if !exists('*'.Fun) + " lazily load function + let file = substitute(substitute(Fun,'#[^#]*$','',''),'#','/','g') + exec 'runtime /autoload/'.file.'.vim' + endif + let Fun2 = function(Fun) + else + let Fun2 = Fun + endif + let args[0] = Fun + return call(function('call'), args) + endif + else + " no function, return the value + return args[0] + endif +endfunction diff --git a/vim/bundle/vim-addon-mw-utils/autoload/glob.vim b/vim/bundle/vim-addon-mw-utils/autoload/glob.vim new file mode 100644 index 0000000..9a0f79a --- /dev/null +++ b/vim/bundle/vim-addon-mw-utils/autoload/glob.vim @@ -0,0 +1,27 @@ +exec vam#DefineAndBind('s:c','g:glob_like', '{}') + +" ignore vcs stuff, Don't think you want those.. +let s:c['regex_ignore_directory'] = '\<\%([_.]darcs\|\.git\|.svn\|.hg\|.cvs\|.bzr\)\>' +let s:c['glob_cache'] = get(s:c, 'glob_cache', {}) +let s:glob_cache = s:c['glob_cache'] + +fun! glob#Glob(pattern, ...) + let pattern = a:pattern + if pattern[0] == '~' + let pattern = $HOME.pattern[1:] + endif + let opts = a:0 > 0 ? a:1 : {} + " never cache current directory. You're very likely to edit files in it. + + let c = getcwd() + let cachable = get(opts, 'cachable', 0) && pattern[:len(c)-1] != c + if cachable && has_key(s:glob_cache, pattern) + return s:glob_cache[pattern] + endif + + " FIXME: don't recurse into \.git directory (thus reimplement glob in vimL!) + let r = filter(split(glob(pattern),"\n"),'v:val !~ '.string(s:c['regex_ignore_directory'])) + if cachable | let s:glob_cache[pattern] = r | endif + return r +endf + diff --git a/vim/bundle/vim-addon-mw-utils/autoload/tiny_cmd.vim b/vim/bundle/vim-addon-mw-utils/autoload/tiny_cmd.vim new file mode 100644 index 0000000..052bbe0 --- /dev/null +++ b/vim/bundle/vim-addon-mw-utils/autoload/tiny_cmd.vim @@ -0,0 +1,19 @@ +" vim suffers: + +exec vam#DefineAndBind('s:c','g:vim_tiny_cmd', '{}') + +fun! tiny_cmd#Put(a) + let new = get(s:c,'next',0) +1 + let s:c['next'] = new + let s:c[new] = a:a + return new +endf + +fun! tiny_cmd#Get(nr) + return s:c[a:nr] +endf + +" Get and remove item +fun! tiny_cmd#Pop(nr) + let r = s:c[a:nr] | unlet s:c[a:nr] | return r +endf diff --git a/vim/bundle/vim-addon-mw-utils/autoload/tovl/scratch_buffer.vim b/vim/bundle/vim-addon-mw-utils/autoload/tovl/scratch_buffer.vim new file mode 100644 index 0000000..217dca1 --- /dev/null +++ b/vim/bundle/vim-addon-mw-utils/autoload/tovl/scratch_buffer.vim @@ -0,0 +1,103 @@ +" old code + +augroup TOVLWrite +augroup end + +" =========== scratch buffer ========================================= +" a scratch buffer is a temporary buffer where the user can enter some text +" It can be used to get commit messages, edit configuration options and so on + +function! tovl#scratch_buffer#KeepIntactLineNr() + let i = 0 + while getline(i)!= b:keepIntact && i < line('$') + let i = i+1 + endwhile + if i > line('$') + return -1 + else + return i + endif +endfunction + +" opens a buffer and runs an action when the buffer is written +" keys: +" name : the name of the buffer +" onWrite : will be called on write +" onWrite is responsible for setlocal nomodified to indicate that +" saving has been successful +" help : callback returning additional information lines +" getContent : callback returning lines +" cmds : extra commands to be run (optional) +" (maybe you prefer adding them the default way afer the +" ScratchBuffer call. They'll be rerun on GetContents +" sp_cmd : the command to use to create the new buffer. Defaults to :e +" buftype : ... +" modifiable : 1 / 0 defaults to 1 +function! tovl#scratch_buffer#ScratchBuffer(opts) + let a:opts['name'] = get(a:opts,'name', 'strach_buffer_without_name') + exec get(a:opts, 'sp_cmd', 'e').' '.escape(a:opts['name'],' ') + let b:settings = a:opts + let b:settings['modifiable'] = get(a:opts,'modifiable', 1) + setlocal buftype=acwrite + command! -buffer -nargs=0 Help call tovl#scratch_buffer#Help() + + " setup write notification + au TOVLWrite BufWriteCmd call tovl#scratch_buffer#Write() + + if has_key(a:opts,'getContent') + command! -buffer -nargs=0 GetContents call tovl#scratch_buffer#GetContents() + GetContents + if !b:settings['modifiable'] + setlocal nomodifiable + endif + endif + "let u=&undolevels + "setlocal undolevels=-1 + "exec 'setlocal undolevels='.u + + " mark buffer as not modified + setlocal nomodified + + au BufReadCmd GetContents + + " run addittional commands + for cmd in get(a:opts,'cmds',[]) + exec cmd + endfor + silent echo get(a:opts,'echo_help', "type :Help for help") +endfunction + +" =========== utility functions ====================================== + +function! tovl#scratch_buffer#Write() + if has_key(b:settings, 'onWrite') + call funcref#Call(b:settings['onWrite']) + else + echo "don't know how to write. Option hasn't been passed" + endif +endfunction + +function! tovl#scratch_buffer#GetContents() + setlocal modifiable + " empty buffer + %g!//d + call append(0, funcref#Call(b:settings['getContent'])) + if !b:settings['modifiable'] + setlocal nomodifiable + endif + for cmd in get(b:settings,'cmds',[]) + exec cmd + endfor +endfunction + +function! tovl#scratch_buffer#Help() + let help = ["use :e! to reload contents, ZZ or :w(q) to write and quit" + \ ,"" + \ ,"Help for this scratch buffer:" + \ ,"=======================================================","",""] + \ + funcref#Call(get(b:settings, 'help', [])) + call tovl#scratch_buffer#ScratchBuffer({ + \ 'name' : "return Help of ".b:settings['name'], + \ 'getContent' : help + \ }) +endfunction diff --git a/vim/bundle/vim-addon-mw-utils/autoload/tovl/ui/filter_list.vim b/vim/bundle/vim-addon-mw-utils/autoload/tovl/ui/filter_list.vim new file mode 100644 index 0000000..74b2ab4 --- /dev/null +++ b/vim/bundle/vim-addon-mw-utils/autoload/tovl/ui/filter_list.vim @@ -0,0 +1,473 @@ +" OLD CODE ! +" I should contribute the multiple filter feature to tlib + +" filter list displays a list of items +" you can white / black filter them by regular expressions (similar to the +" tlib TToC command +" However you can edit the filters afterwards and select the cols which should +" be shown + +fun! tovl#ui#filter_list#ListTest() + call tovl#ui#filter_list#ListView({ + \ 'aligned' : 1, + \ 'Continuation' : funcref#Function('echo string(ARGS[0])'), + \ 'items' : [ {"aa" : "a\nAAAAAAAAAAA", 'bb' : "bbbbbbbbbbbbb\nB" }, + \ {"aa" : "2a\n2AAAAAAAAAAAA", "bb" : "2 bbbbbbbbbbbbb\n2B"}, + \ {"aa" : "XXX", "bb" : "YY"} ], + \ }) + +endfun + +fun! s:Intersection(a, b) + return filter(copy(a:a), 'index(a:b, v:val) >= 0') +endf + +fun! tovl#ui#filter_list#ListTestGotoLineCurrentBuf() + let nr=1 + let lines = [] + for l in getline(0,line('$')) + call add(lines, {'nr': nr, 'line' :l}) + let nr = nr +1 + endfor + call tovl#ui#filter_list#ListView({ + \ 'aligned' : 0, + \ 'keys' : ['nr','line'], + \ 'number' : 1, + \ 'selectByIdOrFilter' : 1, + \ 'Continuation' : funcref#Function('exec ARGS[0]["nr"]'), + \ 'items' : lines, + \ }) +endfun + +" opens a new filtered list +" keys of opts parameters: +" Continuation: This function will be called with the selected items +" items: { key : (string or dict) } +" items willl be modified. use copy(youritems) as argument to prevent +" this. An item is either a string or a dict +" (eg {'file' : .., 'line': ... , 'msg' : .. ) +" keys: list of keys to be shown (optional) +" filter: list of inital filters which must be applied +" contains [ { filter: .. , keep : .. }, ] see FilterItems() below +" aligned: default 0 +" sp_cmd: the command to be used to create the new buffer (default ':e') +" init : 0 / 1 (default 1): wether to show the view right now +" number: 0 /1 (default 1): number items ? +" selectByIdOrFilter: 1: start in getchar() loop so that the user can select +" the item even faster +" auto: only do this if all items fit on screen +" (recommend) +" cmds: extra cmds to be run +" cursorAt : at which item to put the cursor? +" +" If you don't like the default view you can override UpdateDisplay +" +" Usage examples of this list control: +" - db results +" - replacement of the quickfix window +" - select a buffer etc +fun! tovl#ui#filter_list#ListView(opts) + " ActivateAddons theonevimlib + let d = {} + let d.items = a:opts.items + let d.cursorAt = get(a:opts, 'cursorAt', 0) + let d.aligned = get(a:opts, 'aligned', 0) + let d.sep = ' ' + let d.filter = get(a:opts, 'filter', []) + " using sp because of bd! (FIXME) + let d.sp_cmd = get(a:opts, 'sp_cmd', 'sp') + let d.allKeys = {} + let d.closeOnContinuation = get(a:opts,'closeOnContinuation',1) + " don't recommend OnSingleMatch, use OnSingleMatchCR instead + let d.continueOnSingleMatch = get(a:opts, 'continueOnSingleMatch',0) + let d.continueOnSingleMatchCR = get(a:opts, 'continueOnSingleMatchCR',1) + let d.selectByIdOrFilter = get(a:opts, 'selectByIdOrFilter', 0) + let d.linesToItems = {} + let d.number = get(a:opts, 'number', 1) + let d.cmds = get(a:opts, 'cmds', []) + let d.syn_cmds = get(a:opts, 'syn_cmds', []) + + if has_key(a:opts,'keys') | let d.keys = a:opts.keys | endif + if has_key(a:opts,'Continuation') | let d.Continuation = a:opts.Continuation | endif + + " cache already filtered items in case we want to view really long results + " contains [ { filter : { regex: .. , keep : .. } , items : .. , cursorAt :}, + " { filter : { ... } , items: .. , cursorAt : } + let d.cached = [] + " id of buffer + let d.buffer = -1 + let d.modeText = '' + + fun d.HelpText() + return [ "you've entered the the help of the powerful filtered view buffer", + \ "", + \ "type f to start filtering items by regex", + \ "type F to start dropping items by regex", + \ "k / K will ask you for the key to apply the filter to first", + \ "apply the filter by and press again to select item", + \ "", + \ "use :ShowAppliedFilters to list active filters", + \ "use :ToggleAlignment to toggle alignment", + \ "", + \ "TODO: Implement sorting, implement interface to change keys (displayed columns)" + \ ] + endfun + + " create new scratch buffer + " preprocess items calculating line count and maxwidth for all items + fun d.NewBufferAndInit() + let self.bufferId = bufnr(bufname('%')) + for idx in range(0,len(self.items)-1) + if type(self.items[idx]) != 4 + " no dict yet, make it one + let self.items[idx] = {'string_line' : self.items[idx]} + endif + let new = {} + for [k,v] in items(self.items[idx]) + let lines = split(v,"\n") + let self.items[idx][k] = { 'text' : v, 'rows' : len(lines), 'cols' : max(map(copy(lines),'len(v:val)')), 'lines' : lines } + let self.allKeys[k] = 1 + unlet k v + endfor + endfor + call tovl#scratch_buffer#ScratchBuffer({ + \ 'help' : funcref#Function(self.HelpText,{ 'self' : self }), + \ 'sp_cmd' : self.sp_cmd, + \ 'cmds' : self.cmds + \ }) + " I assume we have some kind of formatting anyway. Thus breaking lines is bad! + set nowrap + setlocal cursorline + let b:filtered_view = self + command! -buffer -nargs=0 ToggleAlignment call b:filtered_view.ToggleAlignment() + command! -buffer -nargs=0 ShowAppliedFilters call b:filtered_view.ShowAppliedFilters() + command! -buffer -nargs=0 RemoveFilters call b:filtered_view.RemoveFilters() + noremap f :call b:filtered_view.FilterFromKeyboard(1,'') + " noremap f :call b:filtered_view.FilterFromKeyboard(1) + noremap F :call b:filtered_view.FilterFromKeyboard(0,'') + if has_key(self,'Continuation') + nnoremap :call b:filtered_view.Continue() + endif + "noremap k + "noremap K + + let [items, cursorAt] = self.FilteredItems() + " len(items) is an approximation because one item can have multiple + " lines.. However adding the lines first to check takes too much time + if self.selectByIdOrFilter == 1 || (self.selectByIdOrFilter == 'auto' && winheight('%') > len(items) ) + call self.SelectByIdOrFilter() + else + " user should choose how to proceed + call self.UpdateDisplay() + endif + endfun + + " user interface + fun d.ToggleAlignment() + let self.aligned = !self.aligned + call self.UpdateDisplay() + endfun + fun d.ShowAppliedFilters() + for i in self.filter | echo string(i) | endfor + endfun + fun d.RemoveFilters() + let self.filter = [] + call self.UpdateDisplay() + endfun + fun d.Continue() + let item = self.CurrentItem() + call self.DoContinue(item) + endfun + fun d.DoContinue(v) + if self.closeOnContinuation | bw! | endif + call funcref#Call(self.Continuation,[a:v]) + endfun + + fun d.MapToOriginal(v) + if has_key(a:v, 'string_line') + return a:v.string_line.text + else + let d = {} + for [k,v] in items(a:v) + let d[k] = v.text + unlet k v + endfor + return d + endif + endfun + + fun d.CurrentItem() + let idx=line('.')-len(self.headerLines) + while idx >= 0 + if has_key(self.linesToItems, idx) + return self.MapToOriginal(self.FilteredItems()[0][self.linesToItems[idx]]) + else + let idx = idx -1 + endif + endwhile + throw "internal error, couldn't determine selected item!" + endfun + + " updates the filter cache and returns the final filtered items + fun d.FilteredItems() + " update cache + let idx = 0 + let [items, cursorAt] = [self.items, self.cursorAt] + for idx in range(0, len(self.filter)-1) + if idx +1 > len(self.cached) || self.cached[idx]['filter'] != self.filter[idx] + let self.cached = self.cached[:idx-1] + let [items, cursorAt] = self.FilterItem(copy(items), self.filter[idx], cursorAt) + call add(self.cached, { 'cursorAt' : cursorAt, 'items' : items, 'filter' : self.filter[idx]}) + else + let ci = self.cached[idx] + let [items, cursorAt] = [ci['items'], ci['cursorAt']] + endif + endfor + return [items, cursorAt] + endfun + + " calling this will return a set of lines which are expected to be the new + " buffer contents. The self.linesToItems dict is updated + fun d.UpdateDisplay() + + if empty(self.filter) + let self.statusline= 'no filter applied, :Help for help' + else + let self.statusline = len(self.filter).' '.string(self.filter[-1]) + endif + + let self.linesToItems = {} + let [items, cursorAt] = self.FilteredItems() + "let num_width = printf('%.0f', trunc(log10(len(items))+1)) + let num_width = 4 + if self.aligned + " get column width.. (probably will not work with unicde characters.. I + " don't have a better solution) + let maxlens={} + for i in items + for [k,v] in items(i) + if get(maxlens,k,0) < v.cols + let maxlens[k] = v.cols + endif + endfor + endfor + endif + + " format lines + let self.headerLines = [self.modeText] + let lines = copy(self.headerLines) + let lines_count = 0 + if self.number + let fmt_startA = '%'.num_width.'s)' + let fmt_startB = '%'.num_width.'s' + else + let fmt_startA = '' | let fmt_startB = '' + endif + let cursorAtLine = 1 " sane default + for idx in range(0,len(items)-1) + let self.linesToItems[lines_count + 1] = idx + let i = items[idx] + let keys = has_key(self,'keys') + \ ? s:Intersection(self.keys, keys(i)) + \ : keys(i) + let fmt = '' + let args = [i] + let cols = [] + for k in keys + let fmt .= self.sep.'%-'.(self.aligned ? maxlens[k] : i[k]['cols']).'s' + call add(cols, i[k]) + endfor + for row in range(0, max([1] + map(copy(cols),'v:val["rows"]'))-1) + let fmt_args = row == 0 ? [fmt_startA.fmt] : [fmt_startB.fmt] + if self.number + call add(fmt_args, row == 0 ? idx : '') + endif + for c in cols + call add(fmt_args, c.rows <= row ? '' : c.lines[row]) + endfor + call add(lines, call('printf', fmt_args)) + let lines_count += 1 + endfor + if idx == cursorAt + let cursorAtLine = lines_count + endif + endfor + " update stauts line to show last applied filter + " disabled cause it causes trouble on :wincmd w + " setlocal statusline=%!b:filtered_view.statusline + + " syntax + syn clear + for s in self.syn_cmds | exec s | endfor + let id = 0 + " highlight filter regex in buffer as well + let syn_ids = [ 'Underlined', 'Todo', 'Error', 'Type', 'Statement' ] + for f in self.filter + if !f.keep || !has_key(f, 'regex') | continue | endif + if f.regex != '' + try + exec 'syn match '.syn_ids[id % len(syn_ids)].' '.string(f.regex) + catch /.*/ + " ignore errors such as \ without following characters. Thus just + " ignore and wait for the next character + endtry + endif + let id = id +1 + endfor + if len(lines) > winheight('%') + call extend(lines, self.headerLines) + endif + normal ggdG + call append(0, lines) + " place cursor + exec (cursorAtLine+1) + " move cursor into the middle of the window + normal zz + endf + + " filter = keys : + " filter = string to be executed containing Val + " keep = 1 keep on match + " = 0 drop on match + " key (optional) + " cursorAt: at which item to put the cursor + " if that item is deleted it will be placed at the item above + " optional: key of dict if dict + fun d.FilterItem(items, filter, cursorAt) + let filter = 'Val =~ '.string(a:filter.regex) + let keep = a:filter.keep + let cursorAt = a:cursorAt + + for idx in reverse(range(0, len(a:items)-1)) + let i = a:items[idx] + if has_key(a:filter,'key') + let key = a:filter.key + if has_key(i, key) + " key given, only filter by this column + let Val = i[key]['text'] + exec 'let any = '.filter + else + let any = 0 + endif + else + let any = 0 + " no key given, try all + for x in values(i) + let Val = x['text'] + exec 'let any = '.filter + if any | break | endif + endfor + endif + if any != keep + call remove(a:items, idx) + if idx <= cursorAt + let cursorAt = cursorAt -1 + endif + endif + endfor + return [a:items, cursorAt] + endfun + + " if the user enters a number select by index else start filtering.. + fun d.SelectByIdOrFilter() + let idx='' + let items = self.FilteredItems()[0] + try + let self.modeText = '[0-9]* : select by index| : escape getchar() loop, any char: start filtering' + call self.UpdateDisplay() | redraw + while 1 + let c=getchar() + if index([13,10],c) >= 0 + return self.DoContinue(self.MapToOriginal(items[idx])) + elseif index([27], c) >=0 + " esc, abort + return + else + if type(c) == 0 + let c = nr2char(c) + endif + if c == "\" || index(map(range(0,10),'v:val.""'),c) >= 0 + if c == "\" + let idx = idx[:-2] + else + let idx .= c + endif + if idx < len(items) && idx.'0' > len(items) || idx == 0 && len(items) < 10 + " only match + return self.DoContinue(self.MapToOriginal(items[idx])) + endif + else + return self.FilterFromKeyboard(1,c) + endif + endif + endwhile + finally + let self.modeText = '' + endtry + endfun + + " gets a regular expresion filter by keybaord and updates the display while + " you're typing. The regex ist shown in the statusline + fun d.FilterFromKeyboard(keep, start, ...) + let self.modeText = 'press ESC to exit getchar() loop' + call self.UpdateDisplay() | redraw + + try + let key_text = a:0 > 0 ? 'key : '.a:1 : '' + let filter_bak = self.filter + let filter = copy(self.filter) + let start = a:start + let filter_new = '' + while 1 + if start != '' + " use c= last char to force updating display etc + let filter_new = start[:-2] + let c = start[-1:] + let start = '' + else + let c=getchar() + endif + if index([13,10],c) >= 0 + " c-j or return, accept new filter + let items = self.FilteredItems() + if len(items) == 1 && has_key(self, 'Continuation') && self.continueOnSingleMatchCR + call self.DoContinue(self.MapToOriginal(items[0])) + endif + return + elseif index([27], c) >=0 + " esc, abort + let self.filter = filter_bak + call self.UpdateDisplay() + return + else + if type(c) == 0 + let c = nr2char(c) + endif + if c == "\" + let filter_new = filter_new[:-2] + else + let filter_new .= c + endif + let d = {'keep' : a:keep, 'regex' : filter_new } + if a:0 > 0 + let d['key'] = a:1 + endif + let self.filter = copy(filter_bak) + call add(self.filter, d) + let items = self.FilteredItems() + if len(items) == 1 && has_key(self, 'Continuation') && self.continueOnSingleMatch + call self.DoContinue(self.MapToOriginal(items[0])) + return + endif + call self.UpdateDisplay() | redraw + endif + endwhile + finally + let self.modeText = '' + endtry + endfun + + if get(a:opts,'init',1) + call d.NewBufferAndInit() + endif +endfun -- cgit v1.2.3