Mercurial > dotfiles
view .vim/plugin/fuzzyfinder.vim @ 0:c30d68fbd368
Initial import from svn.
author | Augie Fackler <durin42@gmail.com> |
---|---|
date | Wed, 26 Nov 2008 10:56:09 -0600 |
parents | |
children |
line wrap: on
line source
"============================================================================= " fuzzyfinder.vim : Fuzzy/Partial pattern explorer for " buffer/file/MRU/command/favorite/tag/etc. "============================================================================= " " Author: Takeshi NISHIDA <ns9tks@DELETE-ME.gmail.com> " Version: 2.13, for Vim 7.1 " Licence: MIT Licence " URL: http://www.vim.org/scripts/script.php?script_id=1984 " " GetLatestVimScripts: 1984 1 :AutoInstall: fuzzyfinder.vim " "============================================================================= " DOCUMENT: {{{1 " Japanese: http://vim.g.hatena.ne.jp/keyword/fuzzyfinder.vim " "----------------------------------------------------------------------------- " Description: " Fuzzyfinder provides convenient ways to quickly reach the buffer/file you " want. Fuzzyfinder finds matching files/buffers with a fuzzy/partial " pattern to which it converted the entered pattern. " " E.g.: entered pattern -> fuzzy pattern / partial pattern " abc -> *a*b*c* / *abc* " a?c -> *a?c* / *a?c* " dir/file -> dir/*f*i*l*e* / dir/*file* " d*r/file -> d*r/*f*i*l*e* / d*r/*file* " ../**/s -> ../**/*s* / ../**/*s* " " (** allows searching a directory tree.) " " You will be happy when: " "./OhLongLongLongLongLongFile.txt" " "./AhLongLongLongLongLongName.txt" " "./AhLongLongLongLongLongFile.txt" <- you want :O " Type "AF" and "AhLongLongLongLongLongFile.txt" will be select. :D " " Fuzzyfinder has some modes: " - Buffer mode " - File mode " - Directory mode (yet another :cd command) " - MRU-file mode (most recently used files) " - MRU-command mode (most recently used command-lines) " - Favorite-file mode " - Tag mode (yet another :tag command) " - Tagged-file mode (files which are included in current tags) " " Fuzzyfinder supports the multibyte. " "----------------------------------------------------------------------------- " Installation: " Drop this file in your plugin directory. " "----------------------------------------------------------------------------- " Usage: " Starting Fuzzyfinder: " You can start Fuzzyfinder by the following commands: " " :FuzzyFinderBuffer - launchs buffer-mode Fuzzyfinder. " :FuzzyFinderFile - launchs file-mode Fuzzyfinder. " :FuzzyFinderDir - launchs directory-mode Fuzzyfinder. " :FuzzyFinderMruFile - launchs MRU-file-mode Fuzzyfinder. " :FuzzyFinderMruCmd - launchs MRU-command-mode Fuzzyfinder. " :FuzzyFinderFavFile - launchs favorite-file-mode Fuzzyfinder. " :FuzzyFinderTag - launchs tag-mode Fuzzyfinder. " :FuzzyFinderTaggedFile - launchs tagged-file-mode Fuzzyfinder. " " It is recommended to map these commands. These commands can take initial " text as a command argument. The text will be entered after Fuzzyfinder " launched. If a command was executed with a ! modifier (e.g. " :FuzzyFinderTag!), it enables the partial matching instead of the fuzzy " matching. " " " In Fuzzyfinder: " The entered pattern is converted to the fuzzy pattern and buffers/files " which match the pattern is shown in a completion menu. " " A completion menu is shown when you type at the end of the line and the " length of entered pattern is more than setting value. By default, it is " shown at the beginning. " " If too many items (200, by default) were matched, the completion is " aborted to reduce nonresponse. " " If an item were matched with entered pattern exactly, it is shown first. " The item whose file name has longer prefix matching is placed upper. " Also, an item which matched more sequentially is placed upper. The item " whose index were matched with a number suffixed with entered pattern is " placed lower. the first item in the completion menu will be selected " automatically. " " You can open a selected item in various ways: " <CR> - opens in a previous window. " <C-j> - opens in a split window. " <C-k> - opens in a vertical-split window. " <C-]> - opens in a new tab page. " In MRU-command mode, <CR> executes a selected command and others just " put it into a command-line. These key mappings are customizable. " " To cancel and return to previous window, leave Insert mode. " " To Switch the mode without leaving Insert mode, use <C-l> or <C-o>. " This key mapping is customizable. " " If you want to temporarily change whether or not to ignore case, use " <C-t>. This key mapping is customizable. " " To Hide The Completion Temporarily Menu In Fuzzyfinder: " You can close it by <C-e> and reopen it by <C-x><C-u>. " " About Highlighting: " Fuzzyfinder highlights the buffer with "Error" group when the completion " item was not found or the completion process was aborted. " " About Alternative Approach For Tag Jump: " Following mappings are replacements for :tag and <C-]>: " " nnoremap <silent> <C-f><C-t> :FuzzyFinderTag!<CR> " nnoremap <silent> <C-]> :FuzzyFinderTag! <C-r>=expand('<cword>')<CR><CR> " " In the tag mode, it is recommended to use partial matching instead of " fuzzy matching. " " About Tagged File Mode: " The files which are included in the current tags are the ones which are " related to the current working environment. So this mode is a pseudo " project mode. " " About Usage Of Command Argument: " As an example, if you want to launch file-mode Fuzzyfinder with the full " path of current directory, map like below: " " nnoremap <C-p> :FuzzyFinderFile <C-r>=fnamemodify(getcwd(), ':p')<CR><CR> " " Instead, if you want the directory of current buffer and not current " directory: " " nnoremap <C-p> :FuzzyFinderFile <C-r>=expand('%:~:.')[:-1-len(expand('%:~:.:t'))]<CR><CR> " " About Abbreviations And Multiple Search: " You can use abbreviations and multiple search in each mode. For example, " set as below: " " let g:FuzzyFinderOptions.Base.abbrev_map = { " \ "^WORK" : [ " \ "~/project/**/src/", " \ ".vim/plugin/", " \ ], " \ } " " And type "WORKtxt" in file-mode Fuzzyfinder, then it searches by " following patterns: " " "~/project/**/src/*t*x*t*" " ".vim/plugin/*t*x*t*" " " Adding Favorite Files: " You can add a favorite file by the following commands: " " :FuzzyFinderAddFavFile {filename} " " If you do not specify the filename, current file name is used. " " About Information File: " Fuzzyfinder writes information of the MRU, favorite, etc to the file by " default (~/.vimfuzzyfinder). " :FuzzyFinderEditInfo command is helpful in editing your information " file. This command reads the information file in new unnamed buffer. " Write the buffer and the information file will be updated. " " About Cache: " Once a cache was created, It is not updated automatically to improve " response by default. To update it, use :FuzzyFinderRemoveCache command. " " About Migemo: " Migemo is a search method for Japanese language. " "----------------------------------------------------------------------------- " Options: " You can set options via g:FuzzyFinderOptions which is a dictionary. See " the folded section named "GLOBAL OPTIONS:" for details. To easily set " options for customization, put necessary entries from GLOBAL OPTIONS into " your vimrc file and edit those values. " "----------------------------------------------------------------------------- " Setting Example: " let g:FuzzyFinderOptions = { 'Base':{}, 'Buffer':{}, 'File':{}, 'Dir':{}, 'MruFile':{}, 'MruCmd':{}, 'FavFile':{}, 'Tag':{}, 'TaggedFile':{}} " let g:FuzzyFinderOptions.Base.ignore_case = 1 " let g:FuzzyFinderOptions.Base.abbrev_map = { " \ '\C^VR' : [ " \ '$VIMRUNTIME/**', " \ '~/.vim/**', " \ '$VIM/.vim/**', " \ '$VIM/vimfiles/**', " \ ], " \ } " let g:FuzzyFinderOptions.MruFile.max_item = 200 " let g:FuzzyFinderOptions.MruCmd.max_item = 200 " nnoremap <silent> <C-n> :FuzzyFinderBuffer<CR> " nnoremap <silent> <C-m> :FuzzyFinderFile <C-r>=expand('%:~:.')[:-1-len(expand('%:~:.:t'))]<CR><CR> " nnoremap <silent> <C-j> :FuzzyFinderMruFile<CR> " nnoremap <silent> <C-k> :FuzzyFinderMruCmd<CR> " nnoremap <silent> <C-p> :FuzzyFinderDir <C-r>=expand('%:p:~')[:-1-len(expand('%:p:~:t'))]<CR><CR> " nnoremap <silent> <C-f><C-d> :FuzzyFinderDir<CR> " nnoremap <silent> <C-f><C-f> :FuzzyFinderFavFile<CR> " nnoremap <silent> <C-f><C-t> :FuzzyFinderTag!<CR> " nnoremap <silent> <C-f><C-g> :FuzzyFinderTaggedFile<CR> " noremap <silent> g] :FuzzyFinderTag! <C-r>=expand('<cword>')<CR><CR> " nnoremap <silent> <C-f>F :FuzzyFinderAddFavFile<CR> " nnoremap <silent> <C-f><C-e> :FuzzyFinderEditInfo<CR> " "----------------------------------------------------------------------------- " Special Thanks: " Vincent Wang " Ingo Karkat " Nikolay Golubev " Brian Doyle " id:secondlife " Matt Tolton " "----------------------------------------------------------------------------- " ChangeLog: " 2.13: " - Fixed a bug that a directory disappeared when a file in that directroy " was being opened in File/Mru-File mode. " " 2.12: " - Changed to be able to show completion items in the order of recently " used in Buffer mode. " - Added g:FuzzyFinderOptions.Buffer.mru_order option. " " 2.11: " - Changed that a dot sequence of entered pattern is expanded to parent " directroies in File/Dir mode. " E.g.: "foo/...bar" -> "foo/../../bar" " - Fixed a bug that a prompt string was excessively inserted. " " 2.10: " - Changed not to show a current buffer in a completion menu. " - Fixed a bug that a filename to open was not been escaped. " - Added 'prompt' option. " - Added 'prompt_highlight' option. " - Removed g:FuzzyFinderOptions.MruFile.no_special_buffer option. " " 2.9: " - Enhanced <BS> behavior in Fuzzyfinder and added 'smart_bs' option. " - Fixed a bug that entered pattern was not been escaped. " - Fixed not to insert "zv" with "c/pattern<CR>" command in Normal mode. " - Avoid the slow down problem caused by filereadable() check for the MRU " information in BufEnter/BufWritePost. " " 2.8.1: " - Fixed a bug caused by the non-escaped buffer name "[Fuzzyfinder]". " - Fixed a command to open in a new tab page in Buffer mode. " 2.8: " - Added 'trim_length' option. " - Added 'switch_order' option. " - Fixed a bug that entered command did not become the newest in the " history. " - Fixed a bug that folds could not open with <CR> in a command-line when " searching. " - Removed 'excluded_indicator' option. Now a completion list in Buffer " mode is the same as a result of :buffers. " " 2.7: " - Changed to find an item whose index is matched with the number " suffixed with entered pattern. " - Fixed the cache bug after changing current directroy in File mode. " " 2.6.2: " - Fixed not to miss changes in options when updates the MRU information. " " 2.6.1: " - Fixed a bug related to floating-point support. " - Added support for GetLatestVimScripts. " " 2.6: " - Revived MRU-command mode. The problem with a command-line abbreviation " was solved. " - Changed the specification of the information file. " - Added :FuzzyFinderEditInfo command. " 2.5.1: " - Fixed to be able to match "foo/./bar" by "foo/**/bar" in File mode. " - Fixed to be able to open a space-containing file in File mode. " - Fixed to honor the current working directory properly in File mode. " " 2.5: " - Fixed the bug that a wrong initial text is entered after switching to a " next mode. " - Fixed the bug that it does not return to previous window after leaving " Fuzzyfinder one. " " 2.4: " - Fixed the bug that Fuzzyfinder fails to open a file caused by auto-cd " plugin/script. " " 2.3: " - Added a key mapping to open items in a new tab page and " g:FuzzyFinderOptions.Base.key_open_tab opton. " - Changed to show Fuzzyfinder window above last window even if " 'splitbelow' was set. " - Changed to set nocursorline and nocursorcolumn in Fuzzyfinder. " - Fixed not to push up a buffer number unlimitedly. " " 2.2: " - Added new feature, which is the partial matching. " - Fixed the bug that an error occurs when "'" was entered. " " 2.1: " - Restructured the option system AGAIN. Sorry :p " - Changed to inherit a typed text when switching a mode without leaving " Insert mode. " - Changed commands which launch explorers to be able to take a argument " for initial text. " - Changed to complete file names by relative path and not full path in " the buffer/mru-file/tagged-file mode. " - Changed to highlight a typed text when the completion item was not " found or the completion process was aborted. " - Changed to create caches for each tag file and not working directory " in the tag/tagged-file mode. " - Fixed the bug that the buffer mode couldn't open a unnamed buffer. " - Added 'matching_limit' option. " - Removed 'max_match' option. Use 'matching_limit' option instead. " - Removed 'initial_text' option. Use command argument instead. " - Removed the MRU-command mode. " " 2.0: " - Added the tag mode. " - Added the tagged-file mode. " - Added :FuzzyFinderRemoveCache command. " - Restructured the option system. many options are changed names or " default values of some options. " - Changed to hold and reuse caches of completion lists by default. " - Changed to set filetype 'fuzzyfinder'. " - Disabled the MRU-command mode by default because there are problems. " - Removed FuzzyFinderAddMode command. " " 1.5: " - Added the directory mode. " - Fixed the bug that it caused an error when switch a mode in Insert " mode. " - Changed g:FuzzyFinder_KeySwitchMode type to a list. " " 1.4: " - Changed the specification of the information file. " - Added the MRU-commands mode. " - Renamed :FuzzyFinderAddFavorite command to :FuzzyFinderAddFavFile. " - Renamed g:FuzzyFinder_MruModeVars option to " g:FuzzyFinder_MruFileModeVars. " - Renamed g:FuzzyFinder_FavoriteModeVars option to " g:FuzzyFinder_FavFileModeVars. " - Changed to show registered time of each item in MRU/favorite mode. " - Added 'timeFormat' option for MRU/favorite modes. " " 1.3: " - Fixed a handling of multi-byte characters. " " 1.2: " - Added support for Migemo. (Migemo is Japanese search method.) " " 1.1: " - Added the favorite mode. " - Added new features, which are abbreviations and multiple search. " - Added 'abbrevMap' option for each mode. " - Added g:FuzzyFinder_MruModeVars['ignoreSpecialBuffers'] option. " - Fixed the bug that it did not work correctly when a user have mapped " <C-p> or <Down>. " " 1.0: " - Added the MRU mode. " - Added commands to add and use original mode. " - Improved the sorting algorithm for completion items. " - Added 'initialInput' option to automatically insert a text at the " beginning of a mode. " - Changed that 'excludedPath' option works for the entire path. " - Renamed some options. " - Changed default values of some options. " - Packed the mode-specific options to dictionaries. " - Removed some options. " " 0.6: " - Fixed some bugs. " 0.5: " - Improved response by aborting processing too many items. " - Changed to be able to open a buffer/file not only in previous window " but also in new window. " - Fixed a bug that recursive searching with '**' does not work. " - Added g:FuzzyFinder_CompletionItemLimit option. " - Added g:FuzzyFinder_KeyOpen option. " " 0.4: " - Improved response of the input. " - Improved the sorting algorithm for completion items. It is based on " the matching level. 1st is perfect matching, 2nd is prefix matching, " and 3rd is fuzzy matching. " - Added g:FuzzyFinder_ExcludePattern option. " - Removed g:FuzzyFinder_WildIgnore option. " - Removed g:FuzzyFinder_EchoPattern option. " - Removed g:FuzzyFinder_PathSeparator option. " - Changed the default value of g:FuzzyFinder_MinLengthFile from 1 to 0. " " 0.3: " - Added g:FuzzyFinder_IgnoreCase option. " - Added g:FuzzyFinder_KeyToggleIgnoreCase option. " - Added g:FuzzyFinder_EchoPattern option. " - Changed the open command in a buffer mode from ":edit" to ":buffer" to " avoid being reset cursor position. " - Changed the default value of g:FuzzyFinder_KeyToggleMode from " <C-Space> to <F12> because <C-Space> does not work on some CUI " environments. " - Changed to avoid being loaded by Vim before 7.0. " - Fixed a bug with making a fuzzy pattern which has '\'. " " 0.2: " - A bug it does not work on Linux is fixed. " " 0.1: " - First release. " " }}}1 "============================================================================= " INCLUDE GUARD: {{{1 if exists('loaded_fuzzyfinder') || v:version < 701 finish endif let loaded_fuzzyfinder = 1 " }}}1 "============================================================================= " FUNCTION: {{{1 "----------------------------------------------------------------------------- " LIST FUNCTIONS: function! s:Unique(in) let sorted = sort(a:in) if len(sorted) < 2 return sorted endif let last = remove(sorted, 0) let result = [last] for item in sorted if item != last call add(result, item) let last = item endif endfor return result endfunction " [ [0], [1,2], [3] ] -> [ 0, 1, 2, 3 ] function! s:Concat(in) let result = [] for l in a:in let result += l endfor return result endfunction " [ [ 0, 1 ], [ 2, 3, 4 ], ] -> [ [0,2], [0,3], [0,4], [1,2], [1,3], [1,4] ] function! s:CartesianProduct(lists) if empty(a:lists) return [] endif "let result = map((a:lists[0]), '[v:val]') let result = [ [] ] for l in a:lists let temp = [] for r in result let temp += map(copy(l), 'add(copy(r), v:val)') endfor let result = temp endfor return result endfunction " copy + filter + limit function! s:FilterEx(in, expr, limit) if a:limit <= 0 return filter(copy(a:in), a:expr) endif let result = [] let stride = a:limit * 3 / 2 " x1.5 for i in range(0, len(a:in) - 1, stride) let result += filter(a:in[i : i + stride - 1], a:expr) if len(result) >= a:limit return remove(result, 0, a:limit - 1) endif endfor return result endfunction " function! s:FilterMatching(entries, key, pattern, index, limit) return s:FilterEx(a:entries, 'v:val[''' . a:key . '''] =~ ' . string(a:pattern) . ' || v:val.index == ' . a:index, a:limit) endfunction function! s:ExtendIndexToEach(in, offset) for i in range(len(a:in)) let a:in[i].index = i + a:offset endfor return a:in endfunction function! s:UpdateMruList(mrulist, new_item, key, max_item, excluded) let result = copy(a:mrulist) let result = filter(result,'v:val[a:key] != a:new_item[a:key]') let result = insert(result, a:new_item) let result = filter(result, 'v:val[a:key] !~ a:excluded') return result[0 : a:max_item - 1] endfunction "----------------------------------------------------------------------------- " STRING FUNCTIONS: " trims a:str and add a:mark if a length of a:str is more than a:len function! s:TrimLast(str, len) if a:len <= 0 || len(a:str) <= a:len return a:str endif return a:str[:(a:len - len(s:ABBR_TRIM_MARK) - 1)] . s:ABBR_TRIM_MARK endfunction " takes suffix numer. if no digits, returns -1 function! s:SuffixNumber(str) let s = matchstr(a:str, '\d\+$') return (len(s) ? str2nr(s) : -1) endfunction function! s:ConvertWildcardToRegexp(expr) let re = escape(a:expr, '\') for [pat, sub] in [ [ '*', '\\.\\*' ], [ '?', '\\.' ], [ '[', '\\[' ], ] let re = substitute(re, pat, sub, 'g') endfor return '\V' . re endfunction " "foo/bar/hoge" -> { head: "foo/bar/", tail: "hoge" } function! s:SplitPath(path) let dir = matchstr(a:path, '^.*[/\\]') return { \ 'head' : dir, \ 'tail' : a:path[strlen(dir):] \ } endfunction function! s:EscapeFilename(fn) return escape(a:fn, " \t\n*?[{`$%#'\"|!<") endfunction " "foo/.../bar/...hoge" -> "foo/.../bar/../../hoge" function! s:ExpandTailDotSequenceToParentDir(base) return substitute(a:base, '^\(.*[/\\]\)\?\zs\.\(\.\+\)\ze[^/\\]*$', \ '\=repeat(".." . s:PATH_SEPARATOR, len(submatch(2)))', '') endfunction "----------------------------------------------------------------------------- " FUNCTIONS FOR COMPLETION ITEM: function! s:FormatCompletionItem(expr, number, abbr, trim_len, time, base_pattern, evals_path_tail) if a:evals_path_tail let rate = s:EvaluateMatchingRate(s:SplitPath(matchstr(a:expr, '^.*[^/\\]')).tail, \ s:SplitPath(a:base_pattern).tail) else let rate = s:EvaluateMatchingRate(a:expr, a:base_pattern) endif return { \ 'word' : a:expr, \ 'abbr' : s:TrimLast((a:number >= 0 ? printf('%2d: ', a:number) : '') . a:abbr, a:trim_len), \ 'menu' : printf('%s[%s]', (len(a:time) ? a:time . ' ' : ''), s:MakeRateStar(rate, 5)), \ 'ranks' : [-rate, (a:number >= 0 ? a:number : a:expr)] \ } endfunction function! s:EvaluateMatchingRate(expr, pattern) if a:expr == a:pattern return s:MATCHING_RATE_BASE endif let rate = 0 let rate_increment = (s:MATCHING_RATE_BASE * 9) / (len(a:pattern) * 10) " zero divide ok let matched = 1 let i_pattern = 0 for i_expr in range(len(a:expr)) if a:expr[i_expr] == a:pattern[i_pattern] let rate += rate_increment let matched = 1 let i_pattern += 1 if i_pattern >= len(a:pattern) break endif elseif matched let rate_increment = rate_increment / 2 let matched = 0 endif endfor return rate endfunction function! s:MakeRateStar(rate, base) let len = (a:base * a:rate) / s:MATCHING_RATE_BASE return repeat('*', len) . repeat('.', a:base - len) endfunction "----------------------------------------------------------------------------- " MISC FUNCTIONS: function! s:IsAvailableMode(mode) return exists('a:mode.mode_available') && a:mode.mode_available endfunction function! s:GetAvailableModes() return filter(values(g:FuzzyFinderMode), 's:IsAvailableMode(v:val)') endfunction function! s:GetSortedAvailableModes() let modes = filter(items(g:FuzzyFinderMode), 's:IsAvailableMode(v:val[1])') let modes = map(modes, 'extend(v:val[1], { "ranks" : [v:val[1].switch_order, v:val[0]] })') return sort(modes, 's:CompareRanks') endfunction function! s:GetSidPrefix() return matchstr(expand('<sfile>'), '<SNR>\d\+_') endfunction function! s:OnCmdCR() for m in s:GetAvailableModes() call m.extend_options() call m.on_command_pre(getcmdtype() . getcmdline()) endfor " lets last entry become the newest in the history if getcmdtype() =~ '[:/=@]' call histadd(getcmdtype(), getcmdline()) endif " this is not mapped again (:help recursive_mapping) return "\<CR>" endfunction function! s:ExpandAbbrevMap(base, abbrev_map) let result = [a:base] " expand for [pattern, sub_list] in items(a:abbrev_map) let exprs = result let result = [] for expr in exprs let result += map(copy(sub_list), 'substitute(expr, pattern, v:val, "g")') endfor endfor return s:Unique(result) endfunction " "**" is expanded to ["**", "."]. E.g.: "foo/**/bar" -> [ "foo/./bar", "foo/**/bar" ] function! s:ExpandEx(dir) if a:dir !~ '\S' return [''] endif " [ ["foo/"], ["**/", "./" ], ["bar/"] ] let lists = [] for i in split(a:dir, '[/\\]\zs') let m = matchlist(i, '^\*\{2,}\([/\\]*\)$') call add(lists, (empty(m) ? [i] : [i, '.' . m[1]])) endfor " expand wlidcards return split(join(map(s:CartesianProduct(lists), 'expand(join(v:val, ""))'), "\n"), "\n") endfunction function! s:EnumExpandedDirsEntries(dir, excluded) let dirs = s:ExpandEx(a:dir) let entries = s:Concat(map(copy(dirs), 'split(glob(v:val . ".*"), "\n") + ' . \ 'split(glob(v:val . "*" ), "\n")')) if len(dirs) <= 1 call map(entries, 'extend(s:SplitPath(v:val), { "suffix" : (isdirectory(v:val) ? s:PATH_SEPARATOR : ""), "head" : a:dir })') else call map(entries, 'extend(s:SplitPath(v:val), { "suffix" : (isdirectory(v:val) ? s:PATH_SEPARATOR : "") })') endif if len(a:excluded) call filter(entries, '(v:val.head . v:val.tail . v:val.suffix) !~ a:excluded') endif return entries endfunction function! s:GetTagList(tagfile) return map(readfile(a:tagfile), 'matchstr(v:val, ''^[^!\t][^\t]*'')') endfunction function! s:GetTaggedFileList(tagfile) execute 'cd ' . fnamemodify(a:tagfile, ':h') let result = map(readfile(a:tagfile), 'fnamemodify(matchstr(v:val, ''^[^!\t][^\t]*\t\zs[^\t]\+''), '':p:~'')') cd - return result endfunction function! s:HighlightPrompt(prompt, highlight) syntax clear execute printf('syntax match %s /^\V%s/', a:highlight, escape(a:prompt, '\')) endfunction function! s:HighlightError() syntax clear syntax match Error /^.*$/ endfunction function! s:CompareTimeDescending(i1, i2) return a:i1.time == a:i2.time ? 0 : a:i1.time > a:i2.time ? -1 : +1 endfunction function! s:CompareRanks(i1, i2) if exists('a:i1.ranks') && exists('a:i2.ranks') for i in range(min([len(a:i1.ranks), len(a:i2.ranks)])) if a:i1.ranks[i] > a:i2.ranks[i] return +1 elseif a:i1.ranks[i] < a:i2.ranks[i] return -1 endif endfor endif return 0 endfunction function! s:GetCurrentTagFiles() return sort(filter(map(tagfiles(), 'fnamemodify(v:val, '':p'')'), 'filereadable(v:val)')) endfunction " }}}1 "============================================================================= " OBJECT: {{{1 "----------------------------------------------------------------------------- let g:FuzzyFinderMode = { 'Base' : {} } function! g:FuzzyFinderMode.Base.launch(initial_text, partial_matching, prev_bufnr, tag_files) " initializes this object call self.extend_options() let self.partial_matching = a:partial_matching let self.prev_bufnr = a:prev_bufnr let self.tag_files = a:tag_files " to get local value of current buffer let self.last_col = -1 call s:InfoFileManager.load() if !s:IsAvailableMode(self) echo 'This mode is not available: ' . self.to_str() return endif call s:WindowManager.activate(self.make_complete_func('CompleteFunc')) call s:OptionManager.set('completeopt', 'menuone') call s:OptionManager.set('ignorecase', self.ignore_case) " local autocommands augroup FuzzyfinderLocal autocmd! execute 'autocmd CursorMovedI <buffer> call ' . self.to_str('on_cursor_moved_i()') execute 'autocmd InsertLeave <buffer> nested call ' . self.to_str('on_insert_leave()' ) augroup END " local mapping for [lhs, rhs] in [ \ [ self.key_open , self.to_str('on_cr(0, 0)' ) ], \ [ self.key_open_split , self.to_str('on_cr(1, 0)' ) ], \ [ self.key_open_vsplit, self.to_str('on_cr(2, 0)' ) ], \ [ self.key_open_tab , self.to_str('on_cr(3, 0)' ) ], \ [ '<BS>' , self.to_str('on_bs()' ) ], \ [ '<C-h>' , self.to_str('on_bs()' ) ], \ [ self.key_next_mode , self.to_str('on_switch_mode(+1)' ) ], \ [ self.key_prev_mode , self.to_str('on_switch_mode(-1)' ) ], \ [ self.key_ignore_case, self.to_str('on_switch_ignore_case()') ], \ ] " hacks to be able to use feedkeys(). execute printf('inoremap <buffer> <silent> %s <C-r>=%s ? "" : ""<CR>', lhs, rhs) endfor call self.on_mode_enter() " Starts Insert mode and makes CursorMovedI event now. Command prompt is " needed to forces a completion menu to update every typing. call setline(1, self.prompt . a:initial_text) call feedkeys("A", 'n') " startinsert! does not work in InsertLeave handler endfunction function! g:FuzzyFinderMode.Base.on_cursor_moved_i() let ln = getline('.') let cl = col('.') if !self.exists_prompt(ln) " if command prompt is removed "call setline('.', self.prompt . ln) call setline('.', self.restore_prompt(ln)) call feedkeys(repeat("\<Right>", len(getline('.')) - len(ln)), 'n') elseif cl <= len(self.prompt) " if the cursor is moved before command prompt call feedkeys(repeat("\<Right>", len(self.prompt) - cl + 1), 'n') elseif cl > strlen(ln) && cl != self.last_col " if the cursor is placed on the end of the line and has been actually moved. let self.last_col = cl call feedkeys("\<C-x>\<C-u>", 'n') endif endfunction function! g:FuzzyFinderMode.Base.on_insert_leave() let text = getline('.') call self.on_mode_leave() call self.empty_cache_if_existed(0) call s:OptionManager.restore_all() call s:WindowManager.deactivate() " switchs to next mode, or finishes fuzzyfinder. if exists('s:reserved_switch_mode') let m = self.next_mode(s:reserved_switch_mode < 0) call m.launch(self.remove_prompt(text), self.partial_matching, self.prev_bufnr, self.tag_files) unlet s:reserved_switch_mode else if exists('s:reserved_command') call feedkeys(self.on_open(s:reserved_command[0], s:reserved_command[1]), 'n') unlet s:reserved_command endif endif endfunction function! g:FuzzyFinderMode.Base.on_buf_enter() endfunction function! g:FuzzyFinderMode.Base.on_buf_write_post() endfunction function! g:FuzzyFinderMode.Base.on_command_pre(cmd) endfunction function! g:FuzzyFinderMode.Base.on_cr(index, check_dir) if pumvisible() call feedkeys(printf("\<C-y>\<C-r>=%s(%d, 1) ? '' : ''\<CR>", self.to_str('on_cr'), a:index), 'n') elseif !a:check_dir || getline('.') !~ '[/\\]$' let s:reserved_command = [self.remove_prompt(getline('.')), a:index] call feedkeys("\<Esc>", 'n') endif endfunction function! g:FuzzyFinderMode.Base.on_bs() let bs_count = 1 if self.smart_bs && col('.') > 2 && getline('.')[col('.') - 2] =~ '[/\\]' let bs_count = len(matchstr(getline('.')[:col('.') - 3], '[^/\\]*$')) + 1 endif call feedkeys((pumvisible() ? "\<C-e>" : "") . repeat("\<BS>", bs_count), 'n') endfunction function! g:FuzzyFinderMode.Base.on_mode_enter() endfunction function! g:FuzzyFinderMode.Base.on_mode_leave() endfunction function! g:FuzzyFinderMode.Base.on_open(expr, mode) return [ \ ':edit ', \ ':split ', \ ':vsplit ', \ ':tabedit ', \ ][a:mode] . s:EscapeFilename(a:expr) . "\<CR>" endfunction function! g:FuzzyFinderMode.Base.on_switch_mode(next_prev) let s:reserved_switch_mode = a:next_prev call feedkeys("\<Esc>", 'n') endfunction function! g:FuzzyFinderMode.Base.on_switch_ignore_case() let &ignorecase = !&ignorecase echo "ignorecase = " . &ignorecase let self.last_col = -1 call self.on_cursor_moved_i() endfunction " export string list function! g:FuzzyFinderMode.Base.serialize_info() let header = self.to_key() . "\t" return map(copy(self.info), 'header . string(v:val)') endfunction " import related items from string list function! g:FuzzyFinderMode.Base.deserialize_info(lines) let header = self.to_key() . "\t" let self.info = map(filter(copy(a:lines), 'v:val[: len(header) - 1] ==# header'), \ 'eval(v:val[len(header) :])') endfunction function! g:FuzzyFinderMode.Base.complete(findstart, base) if a:findstart return 0 elseif !self.exists_prompt(a:base) || len(self.remove_prompt(a:base)) < self.min_length return [] endif call s:HighlightPrompt(self.prompt, self.prompt_highlight) " FIXME: ExpandAbbrevMap duplicates index let result = [] for expanded_base in s:ExpandAbbrevMap(self.remove_prompt(a:base), self.abbrev_map) let result += self.on_complete(expanded_base) endfor call sort(result, 's:CompareRanks') if empty(result) call s:HighlightError() else call feedkeys("\<C-p>\<Down>", 'n') endif return result endfunction " This function is set to 'completefunc' which doesn't accept dictionary-functions. function! g:FuzzyFinderMode.Base.make_complete_func(name) execute printf("function! s:%s(findstart, base)\n" . \ " return %s.complete(a:findstart, a:base)\n" . \ "endfunction", a:name, self.to_str()) return s:GetSidPrefix() . a:name endfunction " fuzzy : 'str' -> {'base':'str', 'wi':'*s*t*r*', 're':'\V\.\*s\.\*t\.\*r\.\*'} " partial: 'str' -> {'base':'str', 'wi':'*str*', 're':'\V\.\*str\.\*'} function! g:FuzzyFinderMode.Base.make_pattern(base) if self.partial_matching let wi = (a:base !~ '^[*?]' ? '*' : '') . a:base . \ (a:base =~ '[^*?]$' ? '*' : '') let re = s:ConvertWildcardToRegexp(wi) return { 'base': a:base, 'wi':wi, 're': re } else let wi = '' for char in split(a:base, '\zs') if wi !~ '[*?]$' && char !~ '[*?]' let wi .= '*'. char else let wi .= char endif endfor if wi !~ '[*?]$' let wi .= '*' endif let re = s:ConvertWildcardToRegexp(wi) if self.migemo_support && a:base !~ '[^\x01-\x7e]' let re .= '\|\m.*' . substitute(migemo(a:base), '\\_s\*', '.*', 'g') . '.*' endif return { 'base': a:base, 'wi':wi, 're': re } endif endfunction " glob with caching-feature, etc. function! g:FuzzyFinderMode.Base.glob_ex(dir, file, excluded, index, matching_limit) let key = fnamemodify(a:dir, ':p') call extend(self, { 'cache' : {} }, 'keep') if !exists('self.cache[key]') echo 'Caching file list...' let self.cache[key] = s:EnumExpandedDirsEntries(key, a:excluded) call s:ExtendIndexToEach(self.cache[key], 1) endif echo 'Filtering file list...' "return map(s:FilterEx(self.cache[key], 'v:val.tail =~ ' . string(a:file), a:matching_limit), return map(s:FilterMatching(self.cache[key], 'tail', a:file, a:index, a:matching_limit), \ '{ "index" : v:val.index, "path" : (v:val.head == key ? a:dir : v:val.head) . v:val.tail . v:val.suffix }') endfunction function! g:FuzzyFinderMode.Base.glob_dir_ex(dir, file, excluded, index, matching_limit) let key = fnamemodify(a:dir, ':p') call extend(self, { 'cache' : {} }, 'keep') if !exists('self.cache[key]') echo 'Caching file list...' let self.cache[key] = filter(s:EnumExpandedDirsEntries(key, a:excluded), 'len(v:val.suffix)') call insert(self.cache[key], { 'head' : key, 'tail' : '..', 'suffix' : s:PATH_SEPARATOR }) call insert(self.cache[key], { 'head' : key, 'tail' : '.' , 'suffix' : '' }) call s:ExtendIndexToEach(self.cache[key], 1) endif echo 'Filtering file list...' "return map(s:FilterEx(self.cache[key], 'v:val.tail =~ ' . string(a:file), a:matching_limit), return map(s:FilterMatching(self.cache[key], 'tail', a:file, a:index, a:matching_limit), \ '{ "index" : v:val.index, "path" : (v:val.head == key ? a:dir : v:val.head) . v:val.tail . v:val.suffix }') endfunction function! g:FuzzyFinderMode.Base.empty_cache_if_existed(force) if exists('self.cache') && (a:force || !exists('self.lasting_cache') || !self.lasting_cache) unlet self.cache "let self.cache = (type(self.cache) == type({}) ? {} : " \ type(self.cache) == type([]) ? [] : " \ type(self.cache) == type('') ? '' : 0) endif endfunction function! g:FuzzyFinderMode.Base.to_key() return filter(keys(g:FuzzyFinderMode), 'g:FuzzyFinderMode[v:val] is self')[0] endfunction " returns 'g:FuzzyFinderMode.{key}{.argument}' function! g:FuzzyFinderMode.Base.to_str(...) return 'g:FuzzyFinderMode.' . self.to_key() . (a:0 > 0 ? '.' . a:1 : '') endfunction " takes in g:FuzzyFinderOptions function! g:FuzzyFinderMode.Base.extend_options() let n = filter(keys(g:FuzzyFinderMode), 'g:FuzzyFinderMode[v:val] is self')[0] call extend(self, g:FuzzyFinderOptions.Base, 'force') call extend(self, g:FuzzyFinderOptions[self.to_key()], 'force') endfunction function! g:FuzzyFinderMode.Base.next_mode(rev) let modes = (a:rev ? s:GetSortedAvailableModes() : reverse(s:GetSortedAvailableModes())) let m_last = modes[-1] for m in modes if m is self break endif let m_last = m endfor return m_last " vim crashed using map() endfunction function! g:FuzzyFinderMode.Base.exists_prompt(in) return strlen(a:in) >= strlen(self.prompt) && a:in[:strlen(self.prompt) -1] ==# self.prompt endfunction function! g:FuzzyFinderMode.Base.remove_prompt(in) return a:in[(self.exists_prompt(a:in) ? strlen(self.prompt) : 0):] endfunction function! g:FuzzyFinderMode.Base.restore_prompt(in) let i = 0 while i < len(self.prompt) && i < len(a:in) && self.prompt[i] ==# a:in[i] let i += 1 endwhile return self.prompt . a:in[i : ] endfunction "----------------------------------------------------------------------------- let g:FuzzyFinderMode.Buffer = copy(g:FuzzyFinderMode.Base) function! g:FuzzyFinderMode.Buffer.on_complete(base) let patterns = self.make_pattern(a:base) let result = s:FilterMatching(self.cache, 'path', patterns.re, s:SuffixNumber(patterns.base), 0) return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, v:val.time, a:base, 1)') endfunction function! g:FuzzyFinderMode.Buffer.on_open(expr, mode) " attempts to convert the path to the number for handling unnamed buffer return printf([ \ ':%sbuffer', \ ':%ssbuffer', \ ':vertical :%ssbuffer', \ ':tab :%ssbuffer', \ ][a:mode] . "\<CR>", filter(self.cache, 'v:val.path == a:expr')[0].buf_nr) endfunction function! g:FuzzyFinderMode.Buffer.on_mode_enter() let self.cache = map(filter(range(1, bufnr('$')), 'buflisted(v:val) && v:val != self.prev_bufnr'), \ 'self.make_item(v:val)') if self.mru_order call s:ExtendIndexToEach(sort(self.cache, 's:CompareTimeDescending'), 1) endif endfunction function! g:FuzzyFinderMode.Buffer.on_buf_enter() call self.update_buf_times() endfunction function! g:FuzzyFinderMode.Buffer.on_buf_write_post() call self.update_buf_times() endfunction function! g:FuzzyFinderMode.Buffer.update_buf_times() if !exists('self.buf_times') let self.buf_times = {} endif let self.buf_times[bufnr('%')] = localtime() endfunction function! g:FuzzyFinderMode.Buffer.make_item(nr) return { \ 'index' : a:nr, \ 'buf_nr' : a:nr, \ 'path' : empty(bufname(a:nr)) ? '[No Name]' : fnamemodify(bufname(a:nr), ':~:.'), \ 'time' : (exists('self.buf_times[a:nr]') ? strftime(self.time_format, self.buf_times[a:nr]) : ''), \ } endfunction "----------------------------------------------------------------------------- let g:FuzzyFinderMode.File = copy(g:FuzzyFinderMode.Base) function! g:FuzzyFinderMode.File.on_complete(base) let base = s:ExpandTailDotSequenceToParentDir(a:base) let patterns = map(s:SplitPath(base), 'self.make_pattern(v:val)') let result = self.glob_ex(patterns.head.base, patterns.tail.re, self.excluded_path, s:SuffixNumber(patterns.tail.base), self.matching_limit) let result = filter(result, 'bufnr("^" . v:val.path . "$") != self.prev_bufnr') if len(result) >= self.matching_limit call s:HighlightError() endif return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, "", base, 1)') endfunction "----------------------------------------------------------------------------- let g:FuzzyFinderMode.Dir = copy(g:FuzzyFinderMode.Base) function! g:FuzzyFinderMode.Dir.on_complete(base) let base = s:ExpandTailDotSequenceToParentDir(a:base) let patterns = map(s:SplitPath(base), 'self.make_pattern(v:val)') let result = self.glob_dir_ex(patterns.head.base, patterns.tail.re, self.excluded_path, s:SuffixNumber(patterns.tail.base), 0) return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, "", base, 1)') endfunction function! g:FuzzyFinderMode.Dir.on_open(expr, mode) return ':cd ' . escape(a:expr, ' ') . [ \ "\<CR>", \ "", \ "", \ "", \ ][a:mode] endfunction "----------------------------------------------------------------------------- let g:FuzzyFinderMode.MruFile = copy(g:FuzzyFinderMode.Base) function! g:FuzzyFinderMode.MruFile.on_complete(base) let patterns = self.make_pattern(a:base) let result = s:FilterMatching(self.cache, 'path', patterns.re, s:SuffixNumber(patterns.base), 0) return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, v:val.time, a:base, 1)') endfunction function! g:FuzzyFinderMode.MruFile.on_mode_enter() let self.cache = copy(self.info) let self.cache = filter(self.cache, 'bufnr("^" . v:val.path . "$") != self.prev_bufnr') let self.cache = filter(self.cache, 'filereadable(v:val.path)') let self.cache = map(self.cache, '{ "path" : fnamemodify(v:val.path, ":~:."), "time" : strftime(self.time_format, v:val.time) }') let self.cache = s:ExtendIndexToEach(self.cache, 1) endfunction function! g:FuzzyFinderMode.MruFile.on_buf_enter() call self.update_info() endfunction function! g:FuzzyFinderMode.MruFile.on_buf_write_post() call self.update_info() endfunction function! g:FuzzyFinderMode.MruFile.update_info() "if !empty(&buftype) || !filereadable(expand('%')) if !empty(&buftype) return endif call s:InfoFileManager.load() let self.info = s:UpdateMruList(self.info, { 'path' : expand('%:p'), 'time' : localtime() }, \ 'path', self.max_item, self.excluded_path) call s:InfoFileManager.save() endfunction "----------------------------------------------------------------------------- let g:FuzzyFinderMode.MruCmd = copy(g:FuzzyFinderMode.Base) function! g:FuzzyFinderMode.MruCmd.on_complete(base) let patterns = self.make_pattern(a:base) let result = s:FilterMatching(self.cache, 'command', patterns.re, s:SuffixNumber(patterns.base), 0) return map(result, 's:FormatCompletionItem(v:val.command, v:val.index, v:val.command, self.trim_length, v:val.time, a:base, 0)') endfunction function! g:FuzzyFinderMode.MruCmd.on_open(expr, mode) redraw " use feedkeys to remap <CR> return a:expr . [ \ "\<C-r>=feedkeys(\"\\<CR>\", 'm')?'':''\<CR>", \ "", \ "", \ "", \ ][a:mode] endfunction function! g:FuzzyFinderMode.MruCmd.on_mode_enter() let self.cache = s:ExtendIndexToEach(map(copy(self.info), \ '{ "command" : v:val.command, "time" : strftime(self.time_format, v:val.time) }'), 1) endfunction function! g:FuzzyFinderMode.MruCmd.on_command_pre(cmd) call self.update_info(a:cmd) endfunction function! g:FuzzyFinderMode.MruCmd.update_info(cmd) call s:InfoFileManager.load() let self.info = s:UpdateMruList(self.info, { 'command' : a:cmd, 'time' : localtime() }, \ 'command', self.max_item, self.excluded_command) call s:InfoFileManager.save() endfunction "----------------------------------------------------------------------------- let g:FuzzyFinderMode.FavFile = copy(g:FuzzyFinderMode.Base) function! g:FuzzyFinderMode.FavFile.on_complete(base) let patterns = self.make_pattern(a:base) let result = s:FilterMatching(self.cache, 'path', patterns.re, s:SuffixNumber(patterns.base), 0) return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, v:val.time, a:base, 1)') endfunction function! g:FuzzyFinderMode.FavFile.on_mode_enter() let self.cache = copy(self.info) let self.cache = filter(self.cache, 'bufnr("^" . v:val.path . "$") != self.prev_bufnr') let self.cache = map(self.cache, '{ "path" : fnamemodify(v:val.path, ":~:."), "time" : strftime(self.time_format, v:val.time) }') let self.cache = s:ExtendIndexToEach(self.cache, 1) endfunction function! g:FuzzyFinderMode.FavFile.add(in_file, adds) call s:InfoFileManager.load() let file = fnamemodify((empty(a:in_file) ? expand('%') : a:in_file), ':p:~') call filter(self.info, 'v:val.path != file') if a:adds call add(self.info, { 'path' : file, 'time' : localtime() }) endif call s:InfoFileManager.save() endfunction "----------------------------------------------------------------------------- let g:FuzzyFinderMode.Tag = copy(g:FuzzyFinderMode.Base) function! g:FuzzyFinderMode.Tag.on_complete(base) let patterns = self.make_pattern(a:base) let result = self.find_tag(patterns.re, self.matching_limit) if len(result) >= self.matching_limit call s:HighlightError() endif return map(result, 's:FormatCompletionItem(v:val, -1, v:val, self.trim_length, "", a:base, 1)') endfunction function! g:FuzzyFinderMode.Tag.on_open(expr, mode) return [ \ ':tjump ', \ ':stjump ', \ ':vertical :stjump ', \ ':tab :stjump ', \ ][a:mode] . a:expr . "\<CR>" endfunction function! g:FuzzyFinderMode.Tag.find_tag(pattern, matching_limit) if !len(self.tag_files) return [] endif let key = join(self.tag_files, "\n") " cache not created or tags file updated? call extend(self, { 'cache' : {} }, 'keep') if !exists('self.cache[key]') || max(map(copy(self.tag_files), 'getftime(v:val) >= self.cache[key].time')) echo 'Caching tag list...' let self.cache[key] = { \ 'time' : localtime(), \ 'data' : s:Unique(s:Concat(map(copy(self.tag_files), 's:GetTagList(v:val)'))), \ } endif echo 'Filtering tag list...' return s:FilterEx(self.cache[key].data, 'v:val =~ ' . string(a:pattern), a:matching_limit) endfunction "----------------------------------------------------------------------------- let g:FuzzyFinderMode.TaggedFile = copy(g:FuzzyFinderMode.Base) function! g:FuzzyFinderMode.TaggedFile.on_complete(base) let patterns = self.make_pattern(a:base) echo 'Making tagged file list...' let result = self.find_tagged_file(patterns.re, self.matching_limit) if len(result) >= self.matching_limit call s:HighlightError() endif return map(result, 's:FormatCompletionItem(v:val, -1, v:val, self.trim_length, "", a:base, 1)') endfunction function! g:FuzzyFinderMode.TaggedFile.find_tagged_file(pattern, matching_limit) if !len(self.tag_files) return [] endif let key = join(self.tag_files, "\n") " cache not created or tags file updated? call extend(self, { 'cache' : {} }, 'keep') if !exists('self.cache[key]') || max(map(copy(self.tag_files), 'getftime(v:val) >= self.cache[key].time')) echo 'Caching tagged-file list...' let self.cache[key] = { \ 'time' : localtime(), \ 'data' : s:Unique(s:Concat(map(copy(self.tag_files), 's:GetTaggedFileList(v:val)'))), \ } endif echo 'Filtering tagged-file list...' return s:FilterEx(map(self.cache[key].data, 'fnamemodify(v:val, '':.'')'), \ 'v:val =~ ' . string(a:pattern), \ a:matching_limit) endfunction "----------------------------------------------------------------------------- " sets or restores temporary options let s:OptionManager = { 'originals' : {} } function! s:OptionManager.set(name, value) call extend(self.originals, { a:name : eval('&' . a:name) }, 'keep') execute printf('let &%s = a:value', a:name) endfunction function! s:OptionManager.restore_all() for [name, value] in items(self.originals) execute printf('let &%s = value', name) endfor let self.originals = {} endfunction "----------------------------------------------------------------------------- " manages buffer/window for fuzzyfinder let s:WindowManager = { 'buf_nr' : -1 } function! s:WindowManager.activate(complete_func) let self.prev_winnr = winnr() let cwd = getcwd() if !bufexists(self.buf_nr) leftabove 1new file `='[Fuzzyfinder]'` let self.buf_nr = bufnr('%') elseif bufwinnr(self.buf_nr) == -1 leftabove 1split execute self.buf_nr . 'buffer' delete _ elseif bufwinnr(self.buf_nr) != bufwinnr('%') execute bufwinnr(self.buf_nr) . 'wincmd w' endif " countermeasure for auto-cd script execute ':lcd ' . cwd setlocal filetype=fuzzyfinder setlocal bufhidden=delete setlocal buftype=nofile setlocal noswapfile setlocal nobuflisted setlocal modifiable setlocal nocursorline " for highlighting setlocal nocursorcolumn " for highlighting let &l:completefunc = a:complete_func redraw " for 'lazyredraw' " suspend autocomplpop.vim if exists(':AutoComplPopLock') :AutoComplPopLock endif endfunction function! s:WindowManager.deactivate() " resume autocomplpop.vim if exists(':AutoComplPopUnlock') :AutoComplPopUnlock endif close execute self.prev_winnr . 'wincmd w' endfunction "----------------------------------------------------------------------------- let s:InfoFileManager = { 'originals' : {} } function! s:InfoFileManager.load() for m in s:GetAvailableModes() let m.info = [] endfor try let lines = readfile(expand(self.get_info_file())) catch /.*/ return endtry " compatibility check if !count(lines, self.get_info_version_line()) call self.warn_old_info() let g:FuzzyFinderOptions.Base.info_file = '' return endif for m in s:GetAvailableModes() call m.deserialize_info(lines) endfor endfunction function! s:InfoFileManager.save() let lines = [ self.get_info_version_line() ] for m in s:GetAvailableModes() let lines += m.serialize_info() endfor try call writefile(lines, expand(self.get_info_file())) catch /.*/ endtry endfunction function! s:InfoFileManager.edit() new file `='[FuzzyfinderInfo]'` let self.bufnr = bufnr('%') setlocal filetype=vim setlocal bufhidden=delete setlocal buftype=acwrite setlocal noswapfile augroup FuzzyfinderInfo autocmd! autocmd BufWriteCmd <buffer> call s:InfoFileManager.on_buf_write_cmd() augroup END execute '0read ' . expand(self.get_info_file()) setlocal nomodified endfunction function! s:InfoFileManager.on_buf_write_cmd() for m in s:GetAvailableModes() call m.deserialize_info(getline(1, '$')) endfor call self.save() setlocal nomodified execute printf('%dbdelete! ', self.bufnr) echo "Information file updated" endfunction function! s:InfoFileManager.get_info_version_line() return "VERSION\t206" endfunction function! s:InfoFileManager.get_info_file() return g:FuzzyFinderOptions.Base.info_file endfunction function! s:InfoFileManager.warn_old_info() echohl WarningMsg echo printf("==================================================\n" . \ " Your Fuzzyfinder information file is no longer \n" . \ " supported. Please remove \n" . \ " %-48s\n" . \ "==================================================\n" , \ '"' . expand(self.get_info_file()) . '".') echohl None endfunction " }}}1 "============================================================================= " GLOBAL OPTIONS: {{{1 " stores user-defined g:FuzzyFinderOptions ------------------------------ {{{2 let user_options = (exists('g:FuzzyFinderOptions') ? g:FuzzyFinderOptions : {}) " }}}2 " Initializes g:FuzzyFinderOptions. let g:FuzzyFinderOptions = { 'Base':{}, 'Buffer':{}, 'File':{}, 'Dir':{}, 'MruFile':{}, 'MruCmd':{}, 'FavFile':{}, 'Tag':{}, 'TaggedFile':{}} "----------------------------------------------------------------------------- " [All Mode] This is mapped to select completion item or finish input and " open a buffer/file in previous window. let g:FuzzyFinderOptions.Base.key_open = '<CR>' " [All Mode] This is mapped to select completion item or finish input and " open a buffer/file in split new window let g:FuzzyFinderOptions.Base.key_open_split = '<C-j>' " [All Mode] This is mapped to select completion item or finish input and " open a buffer/file in vertical-split new window. let g:FuzzyFinderOptions.Base.key_open_vsplit = '<C-k>' " [All Mode] This is mapped to select completion item or finish input and " open a buffer/file in a new tab page. let g:FuzzyFinderOptions.Base.key_open_tab = '<C-]>' " [All Mode] This is mapped to switch to the next mode. let g:FuzzyFinderOptions.Base.key_next_mode = '<C-l>' " [All Mode] This is mapped to switch to the previous mode. let g:FuzzyFinderOptions.Base.key_prev_mode = '<C-o>' " [All Mode] This is mapped to temporarily switch whether or not to ignore " case. let g:FuzzyFinderOptions.Base.key_ignore_case = '<C-t>' " [All Mode] This is the file name to write information of the MRU, etc. If " "" was set, Fuzzyfinder does not write to the file. let g:FuzzyFinderOptions.Base.info_file = '~/.vimfuzzyfinder' " [All Mode] Fuzzyfinder does not start a completion if a length of entered " text is less than this. let g:FuzzyFinderOptions.Base.min_length = 0 " [All Mode] This is a dictionary. Each value must be a list. All matchs of a " key in entered text is expanded with the value. let g:FuzzyFinderOptions.Base.abbrev_map = {} " [All Mode] Fuzzyfinder ignores case in search patterns if non-zero is set. let g:FuzzyFinderOptions.Base.ignore_case = 1 " [All Mode] This is a string to format time string. See :help strftime() for " details. let g:FuzzyFinderOptions.Base.time_format = '(%x %H:%M:%S)' " [All Mode] If a length of completion item is more than this, it is trimmed " when shown in completion menu. let g:FuzzyFinderOptions.Base.trim_length = 80 " [All Mode] Fuzzyfinder does not remove caches of completion lists at the end " of explorer to reuse at the next time if non-zero was set. let g:FuzzyFinderOptions.Base.lasting_cache = 1 " [All Mode] Fuzzyfinder uses Migemo if non-zero is set. let g:FuzzyFinderOptions.Base.migemo_support = 0 "----------------------------------------------------------------------------- " [Buffer Mode] This disables all functions of this mode if zero was set. let g:FuzzyFinderOptions.Buffer.mode_available = 1 " [Buffer Mode] The prompt string. let g:FuzzyFinderOptions.Buffer.prompt = '>Buffer>' " [Buffer Mode] The highlight group name for a prompt string. let g:FuzzyFinderOptions.Buffer.prompt_highlight = 'Question' " [Buffer Mode] Pressing <BS> after a path separator deletes one directory " name if non-zero is set. let g:FuzzyFinderOptions.Buffer.smart_bs = 1 " [Buffer Mode] The completion items is sorted in the order of recently used " if non-zero is set. let g:FuzzyFinderOptions.Buffer.mru_order = 1 " [Buffer Mode] This is used to sort modes for switching to the next/previous " mode. let g:FuzzyFinderOptions.Buffer.switch_order = 10 "----------------------------------------------------------------------------- " [File Mode] This disables all functions of this mode if zero was set. let g:FuzzyFinderOptions.File.mode_available = 1 " [File Mode] The prompt string. let g:FuzzyFinderOptions.File.prompt = '>File>' " [File Mode] The highlight group name for a prompt string. let g:FuzzyFinderOptions.File.prompt_highlight = 'Question' " [File Mode] Pressing <BS> after a path separator deletes one directory name " if non-zero is set. let g:FuzzyFinderOptions.File.smart_bs = 1 " [File Mode] This is used to sort modes for switching to the next/previous " mode. let g:FuzzyFinderOptions.File.switch_order = 20 " [File Mode] The items matching this are excluded from the completion list. let g:FuzzyFinderOptions.File.excluded_path = '\v\~$|\.o$|\.exe$|\.bak$|\.swp$|((^|[/\\])\.[/\\]$)' " [File Mode] If a number of matched items was over this, the completion " process is aborted. let g:FuzzyFinderOptions.File.matching_limit = 200 "----------------------------------------------------------------------------- " [Directory Mode] This disables all functions of this mode if zero was set. let g:FuzzyFinderOptions.Dir.mode_available = 1 " [Directory Mode] The prompt string. let g:FuzzyFinderOptions.Dir.prompt = '>Dir>' " [Directory Mode] The highlight group name for a prompt string. let g:FuzzyFinderOptions.Dir.prompt_highlight = 'Question' " [Directory Mode] Pressing <BS> after a path separator deletes one directory " name if non-zero is set. let g:FuzzyFinderOptions.Dir.smart_bs = 1 " [Directory Mode] This is used to sort modes for switching to the " next/previous mode. let g:FuzzyFinderOptions.Dir.switch_order = 30 " [Directory Mode] The items matching this are excluded from the completion " list. let g:FuzzyFinderOptions.Dir.excluded_path = '\v(^|[/\\])\.{1,2}[/\\]$' "----------------------------------------------------------------------------- " [Mru-File Mode] This disables all functions of this mode if zero was set. let g:FuzzyFinderOptions.MruFile.mode_available = 1 " [Mru-File Mode] The prompt string. let g:FuzzyFinderOptions.MruFile.prompt = '>MruFile>' " [Mru-File Mode] The highlight group name for a prompt string. let g:FuzzyFinderOptions.MruFile.prompt_highlight = 'Question' " [Mru-File Mode] Pressing <BS> after a path separator deletes one directory " name if non-zero is set. let g:FuzzyFinderOptions.MruFile.smart_bs = 1 " [Mru-File Mode] This is used to sort modes for switching to the " next/previous mode. let g:FuzzyFinderOptions.MruFile.switch_order = 40 " [Mru-File Mode] The items matching this are excluded from the completion " list. let g:FuzzyFinderOptions.MruFile.excluded_path = '\v\~$|\.bak$|\.swp$' " [Mru-File Mode] This is an upper limit of MRU items to be stored. let g:FuzzyFinderOptions.MruFile.max_item = 99 "----------------------------------------------------------------------------- " [Mru-Cmd Mode] This disables all functions of this mode if zero was set. let g:FuzzyFinderOptions.MruCmd.mode_available = 1 " [Mru-Cmd Mode] The prompt string. let g:FuzzyFinderOptions.MruCmd.prompt = '>MruCmd>' " [Mru-Cmd Mode] The highlight group name for a prompt string. let g:FuzzyFinderOptions.MruCmd.prompt_highlight = 'Question' " [Mru-Cmd Mode] Pressing <BS> after a path separator deletes one directory " name if non-zero is set. let g:FuzzyFinderOptions.MruCmd.smart_bs = 0 " [Mru-Cmd Mode] This is used to sort modes for switching to the next/previous " mode. let g:FuzzyFinderOptions.MruCmd.switch_order = 50 " [Mru-Cmd Mode] The items matching this are excluded from the completion " list. let g:FuzzyFinderOptions.MruCmd.excluded_command = '^$' " [Mru-Cmd Mode] This is an upper limit of MRU items to be stored. let g:FuzzyFinderOptions.MruCmd.max_item = 99 "----------------------------------------------------------------------------- " [Favorite-File Mode] This disables all functions of this mode if zero was " set. let g:FuzzyFinderOptions.FavFile.mode_available = 1 " [Favorite-File Mode] The prompt string. let g:FuzzyFinderOptions.FavFile.prompt = '>FavFile>' " [Favorite-File Mode] The highlight group name for a prompt string. let g:FuzzyFinderOptions.FavFile.prompt_highlight = 'Question' " [Favorite-File Mode] Pressing <BS> after a path separator deletes one " directory name if non-zero is set. let g:FuzzyFinderOptions.FavFile.smart_bs = 1 " [Favorite-File Mode] This is used to sort modes for switching to the " next/previous mode. let g:FuzzyFinderOptions.FavFile.switch_order = 60 "----------------------------------------------------------------------------- " [Tag Mode] This disables all functions of this mode if zero was set. let g:FuzzyFinderOptions.Tag.mode_available = 1 " [Tag Mode] The prompt string. let g:FuzzyFinderOptions.Tag.prompt = '>Tag>' " [Tag Mode] The highlight group name for a prompt string. let g:FuzzyFinderOptions.Tag.prompt_highlight = 'Question' " [Tag Mode] Pressing <BS> after a path separator deletes one directory name " if non-zero is set. let g:FuzzyFinderOptions.Tag.smart_bs = 0 " [Tag Mode] This is used to sort modes for switching to the next/previous " mode. let g:FuzzyFinderOptions.Tag.switch_order = 70 " [Tag Mode] The items matching this are excluded from the completion list. let g:FuzzyFinderOptions.Tag.excluded_path = '\v\~$|\.bak$|\.swp$' " [Tag Mode] If a number of matched items was over this, the completion " process is aborted. let g:FuzzyFinderOptions.Tag.matching_limit = 200 "----------------------------------------------------------------------------- " [Tagged-File Mode] This disables all functions of this mode if zero was set. let g:FuzzyFinderOptions.TaggedFile.mode_available = 1 " [Tagged-File Mode] The prompt string. let g:FuzzyFinderOptions.TaggedFile.prompt = '>TaggedFile>' " [Tagged-File Mode] The highlight group name for a prompt string. let g:FuzzyFinderOptions.TaggedFile.prompt_highlight = 'Question' " [Tagged-File Mode] Pressing <BS> after a path separator deletes one " directory name if non-zero is set. let g:FuzzyFinderOptions.TaggedFile.smart_bs = 0 " [Tagged-File Mode] This is used to sort modes for switching to the " next/previous mode. let g:FuzzyFinderOptions.TaggedFile.switch_order = 80 " [Tagged-File Mode] If a number of matched items was over this, the " completion process is aborted. let g:FuzzyFinderOptions.TaggedFile.matching_limit = 200 " overwrites default values of g:FuzzyFinderOptions with user-defined values - {{{2 call map(user_options, 'extend(g:FuzzyFinderOptions[v:key], v:val, ''force'')') call map(copy(g:FuzzyFinderMode), 'v:val.extend_options()') " }}}2 " }}}1 "============================================================================= " COMMANDS/AUTOCOMMANDS/MAPPINGS/ETC.: {{{1 let s:PATH_SEPARATOR = (has('win32') || has('win64') ? '\' : '/') let s:MATCHING_RATE_BASE = 10000000 let s:ABBR_TRIM_MARK = '...' augroup FuzzyfinderGlobal autocmd! autocmd BufEnter * for m in s:GetAvailableModes() | call m.extend_options() | call m.on_buf_enter() | endfor autocmd BufWritePost * for m in s:GetAvailableModes() | call m.extend_options() | call m.on_buf_write_post() | endfor augroup END " cnoremap has a problem, which doesn't expand cabbrev. cmap <silent> <expr> <CR> <SID>OnCmdCR() command! -bang -narg=? -complete=buffer FuzzyFinderBuffer call g:FuzzyFinderMode.Buffer.launch (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles()) command! -bang -narg=? -complete=file FuzzyFinderFile call g:FuzzyFinderMode.File.launch (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles()) command! -bang -narg=? -complete=dir FuzzyFinderDir call g:FuzzyFinderMode.Dir.launch (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles()) command! -bang -narg=? -complete=file FuzzyFinderMruFile call g:FuzzyFinderMode.MruFile.launch (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles()) command! -bang -narg=? -complete=file FuzzyFinderMruCmd call g:FuzzyFinderMode.MruCmd.launch (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles()) command! -bang -narg=? -complete=file FuzzyFinderFavFile call g:FuzzyFinderMode.FavFile.launch (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles()) command! -bang -narg=? -complete=tag FuzzyFinderTag call g:FuzzyFinderMode.Tag.launch (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles()) command! -bang -narg=? -complete=file FuzzyFinderTaggedFile call g:FuzzyFinderMode.TaggedFile.launch(<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles()) command! -bang -narg=? -complete=file FuzzyFinderEditInfo call s:InfoFileManager.edit() command! -bang -narg=? -complete=file FuzzyFinderAddFavFile call g:FuzzyFinderMode.FavFile.add(<q-args>, 1) command! -bang -narg=0 FuzzyFinderRemoveCache for m in s:GetAvailableModes() | call m.empty_cache_if_existed(1) | endfor " }}}1 "============================================================================= " vim: set fdm=marker: