diff --git a/doc/qnamebuf.txt b/doc/qnamebuf.txt new file mode 100644 index 0000000..2045bf6 --- /dev/null +++ b/doc/qnamebuf.txt @@ -0,0 +1,138 @@ +*qnamebuf.txt* QuickNameBuf: A quick buffer manager + +Author: Matt Spear +QuickNameBuf version 0.06 + +============================================================================== +1. Overview~ + *qnamebuf-overview* + +I really like qname (vimscript #2317) and qbuf (vimscript #1910) and +decided that it would be nice to combine them. The author recommended I +upload under my name. + +============================================================================== +2. Installation ~ + *qnamebuf-installation* + +Extract the downloaded file in your personal |vimfiles| directory (~/.vim +under Unix or %HOMEPATH%\vimfiles under Windows). Restart Vim and execute: +> + helptags ~/.vim/doc +< +============================================================================== +2. Usage ~ + *qnamebuf-usage* + + opens a list of the current buffers, and typing a string filters +the list (by default ala lustyexplorer vimscript #1890). All filtering +is case insensitive (even if using Regular Expressions). If there +are many files open goes to a simplified view (just relative buffer +number, and the filename with path), when filtered enough shows more +information: + 1) Relative Buffer Number + 2) Current Buffer (%), Alternative Buffer (#), or if it is open in a visible split (=) + 3) File name + 4) Buffer number + 5) Relative file path + + +Beyond the keybindinfs of |qnamepicker.txt| qnamefile provides: + Toggle between listed and unlisted buffers + Delete the selected buffer, the explorer stays open and + retains cursor position + Close the window containing the buffer + or or Close the buffer explorer + Open the selected file in a split window + Open the selected file in a vert split window + Open the selected file in a new tab + +============================================================================== +3. Customization ~ + *qnamebuf-customization* + +|g:qnamebuf_hotkey| Set the default key to toggle qnamebuf (defaults to ). + +> + nmap :call QNameBufInit(REGEXP, [SIZE], [FILE_NAME_ONLY], [ALLOW_LEADER]):~ +< +If REGEXP is true use regular expressions instead of the lusty filter. +SIZE controls the size of the popup. Defaults to 1/2 of the visible lines +(|&lines|/2). +If FILE_NAME_ONLY is false then use the path and filename otherwise just use +the filename. Defaults to true. +If ALLOW_LEADER is true then X can be used instead of in the +list above. Defaults to false. + +|g:qnamebuf_unlisted| If set will start in unlisted mode instead of the +default (listed mode). + +============================================================================== +4. Hints ~ + *qnamebuf-hints* + +I find the following mappings very useful (jump to the ith file): + nmap :brewind + nmap :brewind \| 1bn + nmap :brewind \| 2bn + nmap :brewind \| 3bn + nmap :brewind \| 4bn + nmap :brewind \| 5bn + nmap :brewind \| 6bn + nmap :brewind \| 7bn + nmap :brewind \| 8bn + nmap :brewind \| 9bn + +When starting qnamebuf the relative numbers are displayed and this allows +consistent access. + +============================================================================== +6. History~ + *qnamebuf-history* +Version 0.07 + - Refactored to have a generic core and many wrappers + - Increased speed by changing from a custom function for matching to a + regular expression (achieves the same result, but is significantly + faster) +Version 0.06 + - Fixed a bug when switching between listed and unlisted +Version 0.05 + - Fixed a bug which would cause operations to fail when filtered +Version 0.04 + - Save and restore register |@y| instead of overwriting it + - Made the |cmap|s +Version 0.03 + - Mistake in always defining the mapping even if a map to QNameInit exists +Version 0.02 + - Added support for X as a synonym for for |guioptions|+=m users + - Some minor code cleanup +Version 0.01 + - Initial release + +============================================================================== +7. Thanks~ + *qnamebuf-thanks* + +- Vim Devs for vim +- Stefano for finding that doesn't work with the menu shown and an + initial documentation +- Peter for a patch for fixing |@y| being overwritten and pointing out the cmap + noisiness +- pal nart For the amazing qname and qbuf which were the inspiration and basis + for this + +============================================================================== +8. Contact ~ + *qnamebuf-contact* + +If you have questions, bug reports, suggestions, etc. the author can be +contacted at batman900 AT gmail DOT com. The latest version is available at +http://www.vim.org/scripts/script.php?script_id=3217. If you like the script +please vote for it on www.vim.org. + +============================================================================== +License ~ + +This software is licensed under the MIT license. + +vim:tw=78:fo=tcq2:isk=!-~,^*,^\|,^\":ts=8:ft=help:norl: diff --git a/doc/qnamefile.txt b/doc/qnamefile.txt new file mode 100644 index 0000000..2c92003 --- /dev/null +++ b/doc/qnamefile.txt @@ -0,0 +1,98 @@ +*qnamefile.txt* QuickNameBuf: A quick buffer manager + +Author: Matt Spear +QNameFile version 0.07 + +============================================================================== +1. Overview~ + *qnamefile-overview* + +After using qnamebuf for a while I noticed I wanted to be able to choose a +file in a similar manner (by typing a non-). After a refactoring of the +qnamebuf code into a core module |qnamepicker| and modules, this became +possible. + +============================================================================== +2. Installation ~ + *qnamefile-installation* + +Extract the downloaded file in your personal |vimfiles| directory (~/.vim +under Unix or %HOMEPATH%\vimfiles under Windows). Restart Vim and execute: +> + helptags ~/.vim/doc +< + +Requires that there be a find utility available in the path. This should be +available on unix, on Windows GnuWin32 should be sufficient: +http://gnuwin32.sourceforge.net/packages/findutils.htm + +============================================================================== +2. Usage ~ + *qnamefile-usage* + + opens a listing of the files recursively from the current directory. +By default the only files that are not shown are those that are hidden, or are +in a hidden directory (e.g. .vimrc and anything in .vim/). Typing a string +filters the list (by default using an algorithm like lustyexplorer vimscript +#1890). All filtering is case insensitive. + +Beyond the keybindinfs of |qnamepicker.txt| qnamefile provides: + Opens the selected file in a vertical split + Opens the selected file in a split + Opens the selected file in a new tab + Cancels + Cancels + +============================================================================== +3. Customization ~ + *qnamefile-customization* + +|g:qnamefile_hotkey| Set the default key to toggle qnamefile (defaults to ). + +One can create a mapping using something like: +> + nmap :call QNameFileInit(PATH, EXTENSIONS, + INCLUDE_HIDDEN):~ +< +PATH specifies the path to start from, if it is |''| then it will default to +the current working directory (see |:pwd|). +EXTENSIONS is a space separated list of extensions to filter on (e.g. "h cpp +cc cxx c"), if it is |''| then there will be no filtering. +INCLUDE_HIDDEN if true will recurse into hidden directories, and show hidden +files (i.e. files starting with .). + +|g:qnamefile_height| Controls the initial height of the window. + +|g:qnamefile_leader| If true then will allow instead of +in the mappings. + +|g:qnamefile_regexp| If true will use regular expressions instead of a lusty +style selector. + +============================================================================== +6. History~ + *qnamefile-history* +Version 0.07 + - Initial release + +============================================================================== +7. Thanks~ + *qnamefile-thanks* + +- Vim Devs for vim + +============================================================================== +8. Contact ~ + *qnamefile-contact* + +If you have questions, bug reports, suggestions, etc. the author can be +contacted at batman900 AT gmail DOT com. The latest version is available at +http://www.vim.org/scripts/script.php?script_id=3217. If you like the script +please vote for it on www.vim.org. + +============================================================================== +License ~ + +This software is licensed under the MIT license. + +vim:tw=78:fo=tcq2:isk=!-~,^*,^\|,^\":ts=8:ft=help:norl: diff --git a/doc/qnamepicker.txt b/doc/qnamepicker.txt new file mode 100644 index 0000000..04bad1c --- /dev/null +++ b/doc/qnamepicker.txt @@ -0,0 +1,210 @@ +*qnamepicker.txt* QuickNamePicker: A quick list selector library + +Author: Matt Spear +QNamePicker version 0.07 + +============================================================================== +1. Overview~ + *qnamepicker-overview* + +After using qnamebuf for a while I realized it would be useful to have a +generic selector interface and to allow modules to build off this. In this +manner qnamebuf becomes an extension (and I introduced qnamefile). The big +driver for this was the Project plugin (vimscript #69) which I felt very much +needed a lusty picker for the file to open. + +============================================================================== +2. Installation ~ + *qnamepicker-installation* + +Extract the downloaded file in your personal |vimfiles| directory (~/.vim +under Unix or %HOMEPATH%\vimfiles under Windows). Restart Vim and execute: +> + helptags ~/.vim/doc +< +============================================================================== +2. Usage ~ + *qnamepicker-usage* + +This exposes one function for the user: +> + QNamePickerStart(list, dict) +< +|a:list| is the |List| of items to have the user choose from. +|a:dict| has the optional keys: + |acceptors| The set of additional keys to accept on. + By default has [, , ..., ]. + e.g. "acceptors": ["\", "\", "\"]. + |cancelors| The set of keys to close the qnamepicker. + By default has . + e.g. "cancelors": ["\", "\"] + |modifiers| The set of keys to call the modifier_func when pressed. + By default is empty. + e.g. "modifiers": ["\"] + |modifier_func| The callback function to modify the list that is + displayed. The callback is expected to return the new list to + display. Called as: + modifier_func(|index|, |modifier_key|) + where + |a:index| is the index in |a:list| that is selected, if there is + no selected item then it will be -1 (this will happen if there are + no items in the filtered list). + |modifier_key| is the key that was pressed. + |render_func| The function to call to render each item. The callback is + expected to return a string that represents what to show for each item + in the filtered |a:list|. Called as: + render_func(|index|, |rel_index|, |length|, |in_column_mode|) + where + |a:index| is the index in |a:list| of the item to render. + |a:rel_index| is the relative index in the filtered list (so that + one can show the shortcut counts for the ... keys). + |a:length| is the number of items in the filtered list (so that + one can show more information if there are few enough items). + |a:in_column_mode| is true if there are more items than the size + of the window. + |complete_func| The function to call when an item is selected by one of + the |acceptors|. Called as: + complete_func(|index|, |acceptor_key|) + where + |a:index| is the index in |a:index| of the selected item. + |a:acceptor_key| is the acceptor key that was pressed. + |regexp| If true then treats the user input as a regular expression. If + false or not present, then will use a lusty-style selector. + |height| The height of the window. If set to 0 or not present, will + default to one-half of |&lines|. + |use_leader| If true then any word characters ([a-zA-Z0-9]) in acceptors + or cancelors or modifiers will be accessable via . When + false then any word characters are treated as user input (including + ). + +The default controls are: + Delete last char entered + Delete all entered chars + Close the explorer + Choose the currently selected file + Navigate the selection + Move to the first/last item + , ..., Open the first, ..., tenth file in the list + +Note, I wanted these to be but ... are not real key sequences. + +Once a function has been written to use |QNamePickerInit| (say |MyQNameWrapper|), then one should add +a mapping to call the wrapper via: +> + nmap :call MyQNameWrapper():~ +< + +============================================================================== +3. Customization ~ + *qnamepicker-customization* +Every other line can be colored by highlighting |QNamePickerAlt|, e.g. +> + hi QNamePickerAlt gui=NONE guibg=#222222 +< + +============================================================================== +4. Example ~ + *qnamepicker-example* +Included in this are two fairly comprehensive examples: + |qnamebuf| which shows how I used this core to interact with the set of + vim buffers. This is very comprehensive and uses pretty much every + feature of |qnamepicker|. + |qnamefile| which shows the recursive set of files from a given path, and + restricting to a set of extensions. This is a much simpler example, + only requiring a |complete_func| and custom |acceptors|/|cancelors| list. + +These (and the hint example below) should be sufficient to make use of the +plugin. + +============================================================================== +5. Hints ~ + *qnamepicker-hints* + +To plug this into the Project (vimscript #69) plugin one would add something +like: +> + function! LustyProjectFilePicker() + let s:sid = substitute(maparg('', 'n'), '.*\(.\{-}\)_.*', '\1', '') + " As there may not have been a file opened in the project, if a CD is + " specified, then we should cd to it before showing the list so that + " the :. works as expected...all of this is to get to that + let cd_cmd = b:proj_cd_cmd + let infoline = {s:sid}_RecursivelyConstructDirectives(line('.')) + let home = {s:sid}_GetHome(infoline, '') . '/' + let c_d = {s:sid}_GetCd(infoline, home) + let abs = {s:sid}_IsAbsolutePath(home) + if c_d != '' && abs != 2 + if match(g:proj_flags, '\CL') != -1 + call {s:sid}_SetupAutoCommand(c_d) + endif + if !isdirectory(glob(c_d)) + call confirm("From this fold's entry,\nCD=".'"'.c_d.'" is not a valid directory.', "&OK", 1) + else + silent exec cd_cmd.' '.c_d + endif + endif + let ofnames=split(Project_GetAllFnames(1, line("."), ",,"), ',,') + let g:cmd_arr = map(ofnames, "fnamemodify(v:val, ':.')") + call QNamePickerStart(g:cmd_arr, { + \ "complete_func": function("LustyProjectFileCompletion"), + \ "acceptors": ["v", "s", "t", "\", "\", "\"], + \ "use_leader": 1, + \}) + endfunction + + function! LustyProjectFileCompletion(index, key) + let infoline = {s:sid}_RecursivelyConstructDirectives(line('.')) + if a:key == "\" || a:key == "v" + call {s:sid}_OpenEntry2(line('.'), infoline, fnamemodify(g:cmd_arr[a:index], ':p'), 'vert sp') + elseif a:key == "\" || a:key == "s" + call {s:sid}_OpenEntry2(line('.'), infoline, fnamemodify(g:cmd_arr[a:index], ':p'), 'sp') + elseif a:key == "\" || a:key == "t" + call {s:sid}_OpenEntry2(line('.'), infoline, fnamemodify(g:cmd_arr[a:index], ':p'), 'tabe') + else + call {s:sid}_OpenEntry2(line('.'), infoline, fnamemodify(g:cmd_arr[a:index], ':p'), 'e') + endif + endfunction + + nmap a :call LustyProjectFilePicker():~ +< +to the .vimproject_mappings file. + +With this will open the selected file in a vert split window, will +open it in a split window, will open it in a new tab, and will open +it in the last accessed window (this is accomplished by cheating and calling +the |QNameFileCompletion| which is publically exposed from the |qnamefile| +plugin). + +============================================================================== +7. History~ + *qnamepicker-history* +Version 0.07 + - Initial release + +============================================================================== +8. Thanks~ + *qnamepicker-thanks* + +- Vim Devs for vim +- Stefano for finding that doesn't work with the menu shown and an + initial documentation +- Peter for a patch for fixing |@y| being overwritten and pointing out the cmap + noisiness +- pal nart For the amazing qname and qbuf which were the inspiration and basis + for this + +============================================================================== +9. Contact ~ + *qnamepicker-contact* + +If you have questions, bug reports, suggestions, etc. the author can be +contacted at batman900 AT gmail DOT com. The latest version is available at +http://www.vim.org/scripts/script.php?script_id=3217. If you like the script +please vote for it on www.vim.org. + +============================================================================== +License ~ + +This software is licensed under the MIT license. + +vim:tw=78:fo=tcq2:isk=!-~,^*,^\|,^\":ts=8:ft=help:norl: diff --git a/plugin/qnamebuf.vim b/plugin/qnamebuf.vim new file mode 100644 index 0000000..d0e01f9 --- /dev/null +++ b/plugin/qnamebuf.vim @@ -0,0 +1,143 @@ +"============================================================================= +" File: qnamebuf.vim +" Author: batman900 +" Last Change: 5/12/2011 +" Version: 0.07 + +if v:version < 700 + finish +endif + +if exists("g:qnamebuf_loaded") && g:qnamebuf_loaded + finish +endif +let g:qnamebuf_loaded = 1 + +if !exists("g:qnamebuf_hotkey") || g:qnamebuf_hotkey == "" + let g:qnamebuf_hotkey = "" +endif + +if !hasmapto('QNameBufInit') + exe "nmap " g:qnamebuf_hotkey ":call QNameBufInit(0, 0, 1, 0):~" +endif +let s:qnamebuf_hotkey = eval('"\'.g:qnamebuf_hotkey.'"') +let s:modified_string = '[+]' + +let g:qnamebuf_unlisted = 0 + +function! QNameBufInit(regexp, ...) + let s:fileName = (a:0 > 1) ? a:2 : 1 + let s:unlisted = g:qnamebuf_unlisted + let name_arr = s:QNameBufParseLs() + call QNamePickerStart(name_arr, { + \ "render_func": function("QNameBufRender"), + \ "complete_func": function("QNameBufCompletion"), + \ "modifiers": ["l", "d", "c", "\", "\", "\"], + \ "modifier_func": function("QNameBufModifier"), + \ "acceptors": ["v", "s", "t", "\", "\", "\"], + \ "cancelors": ["g", "\", s:qnamebuf_hotkey], + \ "regexp": a:regexp, + \ "use_leader": (a:0 > 2) ? a:3 : 0, + \ "height": (a:0 > 0) ? a:1 : 0, + \}) +endfunction + +function! QNameBufModifier(index, key) + if a:key == "l" || a:key == "\" + let s:unlisted = 1 - s:unlisted + elseif a:key == "d" || a:key == "\" && a:index >= 0 + exe 'bd ' . g:cmd_arr[a:index]['bno'] + elseif a:key == "c" || a:key == "\" && a:index >= 0 + call s:closewindow(g:cmd_arr[a:index]['bno']) + endif + return s:QNameBufParseLs() +endfunction + +function! QNameBufCompletion(index, key) + if a:key == "v" || a:key == "\" + vert split + elseif a:key == "s" || a:key == "\" + split + elseif a:key == "t" || a:key == "\" + tab split + endif + call s:swb(g:cmd_arr[a:index]['bno']) + unlet g:cmd_arr +endfunction + +function! QNameBufRender(index, count, len, columnar) + let rel_len_len = len(a:len) + let rel_len_fill = repeat(' ', rel_len_len - len(a:count) + 1) + let item = g:cmd_arr[a:index] + let name = item['name'] + if a:columnar + return a:count . rel_len_fill . name + else + let name_fill = repeat(' ', s:len_longest_name - len(name) + 1) + let modified_fill = repeat(' ', len(s:modified_string) - len(item['modified'])) + let type = len(item['type']) ? item['type'] : ' ' + return a:count . type . rel_len_fill . name . name_fill + \ . ' ' . item['modified'] . modified_fill + \ . ' <' . item['bno'] . '> ' . item['path'] + endif +endfunction + +function! s:QNameBufParseLs() + let _y = @y + redir @y | silent ls! | redir END + let g:cmd_arr = [] + let name_arr = [] + let s:len_longest_name = 0 + let i = 1 + for _line in split(@y, "\n") + if s:unlisted && _line[3] == "u" && (_line[6] != "-" || _line[5] != " ") + \ || !s:unlisted && _line[3] != "u" + let _bno = matchstr(_line, '^ *\zs\d*')+0 + let _fname = substitute(expand("#"._bno.":p"), '\', '/', 'g') + if _fname == "" + let _fname = "|".matchstr(_line, '"\[\zs[^\]]*')."|" + endif + let _moreinfo = "" + if s:unlisted + let _moreinfo = substitute(_line[5], "[ah]", s:modified_string, "") + else + let _moreinfo = substitute(_line[7], "+", s:modified_string, "") + endif + if _bno == bufnr('') + let _type = '%' + elseif bufwinnr(str2nr(_bno)) > 0 + let _type = '=' + elseif _bno == bufnr('#') + let _type = '#' + else + let _type = ' ' + endif + let _tname = fnamemodify(_fname,":t") + let _path = fnamemodify(_fname,":~:.:h") + let _name = s:fileName ? _tname : _path . '/' . _tname + if len(_name) > s:len_longest_name + let s:len_longest_name = len(_name) + endif + call add(name_arr, _name) + call add(g:cmd_arr, {"bno": _bno, "type": _type, "modified": _moreinfo, "name": _name, "path": _path}) + let i = i + 1 + endif + endfor + let @y = _y + return name_arr +endfunction + +function! s:closewindow(bno) + if bufwinnr(a:bno) != -1 + exe bufwinnr(a:bno) . "winc w|close" + endif +endfunc + +function! s:swb(bno) + if bufwinnr(a:bno) == -1 + exe "hid b" a:bno + else + exe bufwinnr(a:bno) . "winc w" + endif +endfunc + diff --git a/plugin/qnamefile.vim b/plugin/qnamefile.vim new file mode 100644 index 0000000..658f9dc --- /dev/null +++ b/plugin/qnamefile.vim @@ -0,0 +1,71 @@ +"============================================================================= +" File: qnamefile.vim +" Author: batman900 +" Last Change: 5/12/2011 +" Version: 0.07 + +if v:version < 700 + finish +endif + +if exists("g:qnamefile_loaded") && g:qnamefile_loaded + finish +endif +let g:qnamefile_loaded = 1 + +if !exists("g:qnamefile_hotkey") || g:qnamefile_hotkey == "" + let g:qnamefile_hotkey = "" +endif + +if !hasmapto('QNameFileInit') + exe "nmap " g:qnamefile_hotkey ":call QNameFileInit('', '', 0):~" +endif + +let s:qnamefile_hotkey = eval('"\' . g:qnamefile_hotkey . '"') + +let g:qnamefile_height = 0 +let g:qnamefile_leader = 0 +let g:qnamefile_regexp = 0 + +" Find all files from path of the given extension ignoring hidden files +" a:path Where to start searching from +" a:extensions A space separated list of extensions to filter on (e.g. "java cpp h") +function! QNameFileInit(path, extensions, include_hidden) + let path = a:path + if !path + let path = '.' + endif + let ext = '' + if a:extensions + let ext = join(split(a:extensions, ' '), '\|') + let ext = '-and -regex ".*/.*\.\(' . ext . '\)"' + endif + let hidden = '' + if !a:include_hidden + let hidden = '-not -regex ".*/\..*"' + endif + let ofnames = sort(split(system('find ' . a:path . ' -type f ' . hidden . ' ' . ext . ' -print'), "\n")) + let g:cmd_arr = map(ofnames, "fnamemodify(v:val, ':.')") + call QNamePickerStart(g:cmd_arr, { + \ "complete_func": function("QNameFileCompletion"), + \ "acceptors": ["v", "s", "t", "\", "\", "\"], + \ "cancelors": ["g", "\", s:qnamefile_hotkey], + \ "regexp": g:qnamefile_regexp, + \ "use_leader": g:qnamefile_leader, + \ "height": g:qnamefile_height, + \}) +endfunction + +function! QNameFileCompletion(index, key) + if a:key == "s" || a:key == "\" + let cmd = "sp" + elseif a:key == "v" || a:key == "\" + let cmd = "vert sp" + elseif a:key == "t" || a:key == "\" + let cmd = "tabe" + else + let cmd = "e" + endif + exe ':' . cmd . ' ' . g:cmd_arr[a:index] + unlet g:cmd_arr +endfunction diff --git a/plugin/qnamepicker.vim b/plugin/qnamepicker.vim new file mode 100644 index 0000000..7c3c759 --- /dev/null +++ b/plugin/qnamepicker.vim @@ -0,0 +1,380 @@ +"============================================================================= +" File: qnamepicker.vim +" Author: batman900 +" Last Change: 5/12/2011 +" Version: 0.07 + +if v:version < 700 + finish +endif + +if exists("g:qnamepicker_loaded") && g:qnamepicker_loaded + finish +endif +let g:qnamepicker_loaded = 1 + +let s:colPrinter = {"trow": 20} +let s:mapleader = exists('mapleader') ? mapleader : "\\" + +" Start the list picker +" a:list is the set of items to choose from +" a:dict has keys +" acceptors The set of additional keys to accept on +" By default has , , ..., +" e.g. "acceptors": ["\", "\", "\"] +" cancelors The set of keys to cancel on +" By default has +" e.g. "cancelors": ["\", "\"] +" modifiers The set of keys to call the modifier_func +" By default empty +" e.g. "modifiers": ["\"] +" modifier_func The set of keys to modify the list +" modifier_func(index, modifier_key) +" Must return the NEW list to show +" render_func The function to call to render each item +" render_func(index, rel_index, length, in_column_mode) +" complete_func The function to call when an item is selected +" complete_func(index, acceptor_key) +" regexp If should use regexp instead of a lusty style selector +" height The height of the window +" use_leader If should allow X to be used instead of +" By default false +" If true then any word characters ([a-zA-Z0-9]) in acceptors or +" cancelors or modifiers will be accessable via . +" When false then any word characters are ignored. +function! QNamePickerStart(list, dict) + let s:cmdh = &cmdheight + let s:inp = "" + let s:colPrinter.trow = 0 + let s:colPrinter.sel = 0 + let s:inLeader = 0 + let s:useLeader = has_key(a:dict, "use_leader") ? a:dict["use_leader"] : 0 + let s:paste = &paste + set nopaste + "unlet s:modifier_func s:render_func s:complete_func + let s:selectors = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "\", "\", "\", "\", "\", "\", "\", "\", "\", "\"] + let s:acceptors = ["\"] + if has_key(a:dict, "acceptors") + call extend(s:acceptors, a:dict["acceptors"]) + endif + let s:cancelors = ["\"] + if has_key(a:dict, "cancelors") + call extend(s:cancelors, a:dict["cancelors"]) + endif + let s:modifiers = [] + let s:modifier_func = function("s:QNamePickerModIdentity") + if has_key(a:dict, "modifiers") + let s:modifiers = a:dict["modifiers"] + if has_key(a:dict, "modifier_func") + let s:modifier_func = a:dict["modifier_func"] + else + throw "QNamePicker requires modifier_func being specified when specifying modifiers" + endif + endif + let s:render_func = function("s:QNamePickerRender") + if has_key(a:dict, "render_func") + let s:render_func = a:dict["render_func"] + endif + if has_key(a:dict, "complete_func") + let s:complete_func = a:dict["complete_func"] + else + throw "QNamePicker requires complete_func being specified" + endif + let s:regexp = has_key(a:dict, "regexp") ? a:dict['regexp'] : 0 + if has_key(a:dict, "height") + let s:colPrinter.trow = a:dict["height"] + endif + if s:colPrinter.trow == 0 + let s:colPrinter.trow = &lines / 2 + endif + cmap ~ call QNamePickerRun():~ + let s:origList = a:list + let s:indices = range(0, len(s:origList) - 1) + call s:colPrinter.put(s:indices, 0) + exe "set cmdheight=" . (min([s:colPrinter.trow, len(s:origList)]) + 1) +endfunction + +" The main loop. Reads a char from the user, processes it, and ``calls'' +" itself. +function! QNamePickerRun() + let _sel = s:colPrinter.sel + let _len = len(s:indices) + call s:colPrinter.print() + call inputsave() + echo "\rMatch " . _len . '/' . len(s:origList) . ' names: ' . s:inp + let _key = getchar() + if !type(_key) + let _key = nr2char(_key) + endif + if _key == "\" + let s:inp = s:inp[:-2] + if s:colPrinter.sel < 0 | let s:colPrinter.sel = 0 | endif + let s:indices = range(0, len(s:origList)-1) + call s:FilterList() + elseif _key == "\" + let s:inp = "" + if s:colPrinter.sel < 0 | let s:colPrinter.sel = 0 | endif + let s:indices = range(0, len(s:origList)-1) + call s:FilterList() + elseif _key == "'" + " NOTE this causes problems with the regexp so ignore the key + elseif _key == s:mapleader && s:useLeader + let s:inLeader = 1 + elseif s:InArr(s:cancelors, _key) + call s:QNamePickerUnload() + elseif s:InArr(s:acceptors, _key) && _sel < _len + call s:Finish(s:indices[_sel], _key) + elseif s:InArr(s:modifiers, _key) + let s:origList = s:modifier_func((_sel < _len && _sel >= 0) ? s:indices[_sel] : -1, _key) + let s:indices = range(0, len(s:origList) - 1) + call s:FilterList() + if s:colPrinter.sel < 0 | let s:colPrinter.sel = 0 | endif + elseif s:InArr(s:selectors, _key) + let _nr = char2nr(_key) - char2nr("\") + if _nr <= -120 + let _nr = char2nr(_key) - char2nr("1") + endif + if _nr < 0 " Handle that should be index 10 + let _nr = 9 + endif + if _nr < _len + call s:Finish(s:indices[_nr], "\") + endif + elseif _key == "\" + call s:colPrinter.vert(-1) + elseif _key == "\" + call s:colPrinter.vert(1) + elseif _key == "\" + call s:colPrinter.horz(-1) + elseif _key == "\" + call s:colPrinter.horz(1) + elseif _key == "\" + let s:colPrinter.sel = 0 + elseif _key == "\" + let s:colPrinter.sel = _len-1 + elseif strlen(_key) == 1 && char2nr(_key) > 31 + let s:inp = s:inp . _key + if _len == 0 " NOTE for s:regexp it may reach 0 b/c of an invalid regexp instead of there being no real matches + let s:indices = range(0, len(s:origList) - 1) + endif + call s:FilterList() + endif + if _key != s:mapleader && s:inLeader + let s:inLeader = 0 + endif + redraws + call inputrestore() +endfunction + +" Cleans up all the stuff qnamepicker created. +function! s:QNamePickerUnload() + cmap ~ exe "cunmap \x7E" + exe "set cmdheight=" . s:cmdh + if s:paste + set paste + else + set nopaste + endif + unlet s:origList + unlet s:indices + unlet s:colPrinter.rows + unlet s:colPrinter.cols +endfunction + +" Essentially an identity function +function! s:QNamePickerModIdentity(index, key) + return s:origList +endfunction + +" A simple renderer, it shows the relative index, and the +" content in the list given. +function! s:QNamePickerRender(index, count, length, columnar) + let len = len(a:length) + let fill = repeat(' ', len - len(a:count) + 1) + return a:count . fill . s:origList[a:index] +endfunction + +" The actual finish function, called when an acceptors is pressed. +function! s:Finish(item, keypressed) + call s:QNamePickerUnload() + call s:complete_func(a:item, a:keypressed) +endfunction + +" Restricts the set of indices to the set that match the query +" (using the original list as the strings) +function! s:FilterList() + if len(s:inp) > 0 + let query = tolower(s:inp) + if !s:regexp + let query = join(split(query, '\zs'), '.*') + let query = substitute(query, "\\", "\\\\", "g") + let query = substitute(query, "\\.\\.", "\\\\..", "g") + endif + let s:indices = filter(s:indices, "s:origList[v:val] =~ '" . query . "'") + endif + call s:colPrinter.put(s:indices, s:colPrinter.sel) +endfunction + +" Checks if the character is [a-zA-Z0-9] +function! s:IsPrintable(key) + return ("a" <= a:key && a:key <= "z") || ("A" <= a:key && a:key <= "Z") || ("0" <= a:key && a:key <= "9") +endfunction + +" Checks if the key pressed is present in the array given, taking into account +" s:inLeader. +function! s:InArr(arr, key) + for a in a:arr + if s:IsPrintable(a) + if a:key == a && s:inLeader + return 1 + endif + elseif a:key == a + return 1 + endif + endfor + return 0 +endfunction + +" Functions for the s:colPrinter +function! s:colPrinter.put(its, sel) dict + let _cols = [] + let _trow = self.trow + let _len = len(a:its) + + let _its = [] + let c = 1 + for i in a:its + call add(_its, s:render_func(i, c, _len, _len > _trow)) + let c += 1 + endfor + let _i = 0 + while _i < _len + if _i+_trow <= _len + call add(_cols, remove(_its,0,_trow-1)) + else + call add(_cols, _its) + endif + let _i += _trow + endwhile + + let _cpos = [0] + let _cw = [] + let _t = 0 + for _li in _cols + let _w = max(map(copy(_li), 'strlen(v:val)')) + 4 + let _t += _w + call add(_cpos, _t) + call add(_cw, _w) + endfor + + let _rows = [] + for _i in range(_trow) + let _row = [] + for _j in range(len(_cols)) + if _j*_trow+_i < _len + call add(_row, _cols[_j][_i]) + endif + endfor + call add(_rows, _row) + endfor + + let self.cols = _cols + let self.cw = _cw + let self.rows = _rows + let self.cpos = _cpos + let self.len = _len + let self.lcol = 0 + let self.sel = a:sel < _len ? a:sel : _len - 1 +endfunc + +function! s:colPrinter.horz(mv) dict + if self.len < self.trow + return + endif + let _len = self.len + let _trow = self.trow + let _nr = (_len / _trow) + ((_len % _trow != 0) ? 1 : 0) + let _t = self.sel + a:mv * _trow + if _t < 0 && _len > 0 + let _t = abs(_t) % self.trow + if _t == 0 + let self.sel = _trow * _nr - _trow + else + let _tt = _trow * _nr - _t + if _tt >= _len + let self.sel = _trow * (_nr-1) - _t + else + let self.sel = _tt + endif + endif + elseif _t >= 0 && _t < _len + let self.sel = _t + elseif _t >= _len + let self.sel = _t % self.trow + endif +endfunc + +function! s:colPrinter.vert(mv) dict + let _t = self.sel + a:mv + let _len = self.len + if _t < 0 && _len > 0 + let self.sel = _len-1 + elseif _t >= _len + let self.sel = 0 + else + let self.sel = _t + endif +endfunc + +function! s:colPrinter.print() dict + let _len = self.len + let _trow = &cmdheight - 1 + if !_len + echo " [...NO MATCH...]" repeat("\n",_trow) + return + endif + let _sel = self.sel + let _t = _sel/_trow + let _cpos = self.cpos + let _lcol = self.lcol + let _tcol = &columns + if _cpos[_lcol]+_tcol < _cpos[_t+1] + let _rcol = _t + let _pos = _cpos[_t+1]-_tcol-2 + while _cpos[_lcol] < _pos + let _lcol += 1 + endwhile + let _lcol -= _lcol > _t + else + if _t < _lcol + let _lcol = _t + endif + let _rcol = len(_cpos)-1 + let _pos = _cpos[_lcol]+_tcol+2 + while _cpos[_rcol] > _pos + let _rcol -= 1 + endwhile + let _rcol -= _rcol > _lcol + endif + let _cw = self.cw + let _pos = _cpos[_lcol]+_tcol + let self.lcol = _lcol + for _i in range(_trow) + let _row = self.rows[_i] + for _j in range(_lcol,_rcol) + if _j*_trow+_i < _len + let _txt = " " . _row[_j] + let _txt .= repeat(" ", _cw[_j] - strlen(_txt)) + let _txt = _txt[:_pos-_cpos[_j]-2] + if _j*_trow + _i == _sel + echoh Search | echon _txt | echoh None + elseif (_i % 2) == 1 + echoh QNamePickerAlt | echon _txt | echoh None + else + echon _txt + endif + endif + endfor + echon "\n" + endfor +endfunc