1
0
Fork 0
mirror of synced 2024-11-05 17:08:57 -05:00
ultimate-vim/vim_plugins_src/lookupfile-1.8/plugin/lookupfile.vim

576 lines
16 KiB
VimL

" lookupfile.vim: Lookup filenames by pattern
" Author: Hari Krishna Dara (hari.vim at gmail dot com)
" Last Change: 14-Jun-2007 @ 18:30
" Created: 11-May-2006
" Requires: Vim-7.1, genutils.vim(2.3)
" Version: 1.8.0
" Licence: This program is free software; you can redistribute it and/or
" modify it under the terms of the GNU General Public License.
" See http://www.gnu.org/copyleft/gpl.txt
" Download From:
" http://www.vim.org//script.php?script_id=1581
" Usage:
" See :help lookupfile.txt
if exists('loaded_lookupfile')
finish
endif
if v:version < 701
echomsg 'lookupfile: You need at least Vim 7.1'
finish
endif
if !exists('loaded_genutils')
runtime plugin/genutils.vim
endif
if !exists('loaded_genutils') || loaded_genutils < 203
echomsg 'lookupfile: You need a newer version of genutils.vim plugin'
finish
endif
let g:loaded_lookupfile = 108
" Make sure line-continuations won't cause any problem. This will be restored
" at the end
let s:save_cpo = &cpo
set cpo&vim
if !exists('g:LookupFile_TagExpr')
let g:LookupFile_TagExpr = '&tags'
endif
if !exists('g:LookupFile_LookupFunc')
let g:LookupFile_LookupFunc = ''
endif
if !exists('g:LookupFile_LookupNotifyFunc')
let g:LookupFile_LookupNotifyFunc = ''
endif
if !exists('g:LookupFile_LookupAcceptFunc')
let g:LookupFile_LookupAcceptFunc = ''
endif
if !exists('g:LookupFile_MinPatLength')
let g:LookupFile_MinPatLength = 4
endif
if !exists('g:LookupFile_PreservePatternHistory')
let g:LookupFile_PreservePatternHistory = 1
endif
if !exists('g:LookupFile_PreserveLastPattern')
let g:LookupFile_PreserveLastPattern = 1
endif
if !exists('g:LookupFile_ShowFiller')
let g:LookupFile_ShowFiller = 1
endif
if !exists('g:LookupFile_AlwaysAcceptFirst')
let g:LookupFile_AlwaysAcceptFirst = 0
endif
if !exists('g:LookupFile_FileFilter')
let g:LookupFile_FileFilter = ''
endif
if !exists('g:LookupFile_AllowNewFiles')
let g:LookupFile_AllowNewFiles = 1
endif
if !exists('g:LookupFile_SortMethod')
let g:LookupFile_SortMethod = 'alpha'
endif
if !exists('g:LookupFile_Bufs_BufListExpr')
let g:LookupFile_Bufs_BufListExpr = ''
endif
if !exists('g:LookupFile_Bufs_SkipUnlisted')
let g:LookupFile_Bufs_SkipUnlisted = 1
endif
if !exists('g:LookupFile_Bufs_LikeBufCmd')
let g:LookupFile_Bufs_LikeBufCmd = 1
endif
if !exists('g:LookupFile_UsingSpecializedTags')
let g:LookupFile_UsingSpecializedTags = 0
endif
if !exists('g:LookupFile_DefaultCmd')
let g:LookupFile_DefaultCmd = ':LUTags'
endif
if !exists('g:LookupFile_EnableRemapCmd')
let g:LookupFile_EnableRemapCmd = 1
endif
if !exists('g:LookupFile_DisableDefaultMap')
let g:LookupFile_DisableDefaultMap = 0
endif
if !exists('g:LookupFile_UpdateTime')
let g:LookupFile_UpdateTime = 300
endif
if !exists('g:LookupFile_OnCursorMovedI')
let g:LookupFile_OnCursorMovedI = 0
endif
if !exists('g:LookupFile_EscCancelsPopup')
let g:LookupFile_EscCancelsPopup = 1
endif
if !exists('g:LookupFile_SearchForBufsInTabs')
let g:LookupFile_SearchForBufsInTabs = 1
endif
if !exists('g:LookupFile_TagsExpandCamelCase')
let g:LookupFile_TagsExpandCamelCase = 1
endif
if !exists('g:LookupFile_RecentFileListSize')
let g:LookupFile_RecentFileListSize = 20
endif
if (! exists("no_plugin_maps") || ! no_plugin_maps) &&
\ (! exists("no_lookupfile_maps") || ! no_lookupfile_maps)
noremap <script> <silent> <Plug>LookupFile :LookupFile<CR>
if ! g:LookupFile_DisableDefaultMap
if !hasmapto('<Plug>LookupFile', 'n')
nmap <unique> <silent> <F5> <Plug>LookupFile
endif
if !hasmapto('<Plug>LookupFile', 'i')
inoremap <Plug>LookupFileCE <C-E>
imap <unique> <expr> <silent> <F5> (pumvisible() ? "\<Plug>LookupFileCE" :
\ "")."\<Esc>\<Plug>LookupFile"
endif
endif
endif
command! -nargs=? -bang -complete=file LookupFile :call
\ <SID>LookupUsing('lookupfile', "<bang>", <q-args>, 0)
command! -nargs=? -bang -complete=tag LUTags :call
\ <SID>LookupUsing('Tags', "<bang>", <q-args>, 0)
command! -nargs=? -bang -complete=file LUPath :call
\ <SID>LookupUsing('Path', "<bang>", <q-args>, g:LookupFile_MinPatLength)
command! -nargs=? -bang -complete=file LUArgs :call
\ <SID>LookupUsing('Args', "<bang>", <q-args>, 0)
command! -nargs=? -bang -complete=file LUBufs :call
\ <SID>LookupUsing('Bufs', "<bang>", <q-args>, 0)
command! -nargs=? -bang -complete=dir LUWalk :call
\ <SID>LookupUsing('Walk', "<bang>", <q-args>, 0)
function! s:RemapLookupFile(cmd)
let cmd = (a:cmd != '') ? a:cmd : ':LUTags'
" It is not straight-forward to determine the right completion method.
exec 'command! -nargs=? -bang -complete=file LookupFile' cmd
endfunction
call s:RemapLookupFile(g:LookupFile_DefaultCmd)
let s:mySNR = ''
function! s:SNR()
if s:mySNR == ''
let s:mySNR = matchstr(expand('<sfile>'), '<SNR>\d\+_\zeSNR$')
endif
return s:mySNR
endfun
let s:baseBufNr = 0
function! s:LookupUsing(ftr, bang, initPat, minPatLen)
let cmd = ':LUTags'
if a:ftr != 'Tags'
call s:SaveSett('LookupFunc')
call s:SaveSett('LookupNotifyFunc')
call s:SaveSett('MinPatLength')
unlet! g:LookupFile_LookupFunc g:LookupFile_LookupNotifyFunc
let g:LookupFile_LookupFunc = function(s:SNR().'Lookup'.a:ftr)
let g:LookupFile_LookupNotifyFunc = function(s:SNR().'LookupReset')
let g:LookupFile_MinPatLength = a:minPatLen
let s:baseBufNr = bufnr('%')
let cmd = ':LU'.a:ftr
endif
if g:LookupFile_EnableRemapCmd
call s:RemapLookupFile(cmd)
endif
call lookupfile#OpenWindow(a:bang, a:initPat)
if exists('*s:Config'.a:ftr)
call s:Config{a:ftr}()
endif
aug LookupReset
au!
au BufHidden <buffer> call <SID>LookupReset()
aug END
endfunction
function! s:LookupReset()
if exists('s:saved')
for sett in keys(s:saved)
unlet! g:LookupFile_{sett}
let g:LookupFile_{sett} = s:saved[sett]
endfor
unlet s:saved
endif
if exists('s:cleanup')
for cmd in s:cleanup
try
exec cmd
catch
echoerr v:exception . ', while executing cleanup command: ' . cmd
endtry
endfor
unlet s:cleanup
endif
aug ConfigIdo
au!
aug END
endfunction
function! s:SaveSett(sett)
if !exists('s:saved')
let s:saved = {}
endif
" Avoid overwriting the original value.
if !has_key(s:saved, a:sett)
let s:saved[a:sett] = g:LookupFile_{a:sett}
endif
endfunction
function! s:AddCleanup(cmd)
if !exists('s:cleanup')
let s:cleanup = []
endif
if index(s:cleanup, a:cmd) == -1
call add(s:cleanup, a:cmd)
endif
endfunction
function! s:LookupPath(pattern)
let filePat = a:pattern
let matchingExactCase = s:MatchingExactCase(filePat)
" Remove leading or trailing '*'s as we add a star anyway. This also removes
" '**' unless it is followed by a slash.
let filePat = substitute(filePat, '^\*\+\|\*\+$', '', 'g')
" On windows, the case is anyway ignored.
if !genutils#OnMS() && !matchingExactCase
let filePat = s:FilePatIgnoreCase(filePat)
endif
let fl = split(globpath(&path, (filePat != '') ? '*'.filePat.'*' : '*'),
\ "\n")
let regexPat = s:TranslateFileRegex(filePat)
" This is a psuedo case-sensitive match for windows, when 'smartcase' is
" set.
if genutils#OnMS() && matchingExactCase
set verbose=15
call filter(fl, 'v:val =~# regexPat')
set verbose=0
endif
return map(fl,
\ '{'.
\ ' "word": v:val,'.
\ ' "abbr": fnamemodify(v:val, ":t"), '.
\ ' "menu": fnamemodify(v:val, ":h"), '.
\ ' "dup": 1'.
\ '}')
endfunction
function! s:LookupArgs(pattern)
return map(filter(argv(), 'v:val =~ a:pattern'),
\ '{'.
\ ' "word":fnamemodify(v:val, ":p"), '.
\ ' "abbr": v:val, '.
\ ' "menu": substitute(v:val, a:pattern, "[&]", ""), '.
\ ' "dup": 1'.
\ '}')
endfunction
let s:bufList = [1]
function! s:LookupBufs(pattern)
let results = []
if g:LookupFile_Bufs_BufListExpr != ''
let buflist = eval(g:LookupFile_Bufs_BufListExpr)
else
" Since we need to generate the same range again and again, it is better to
" cache the list.
if s:bufList[-1] != bufnr('$')
call extend(s:bufList, range(s:bufList[-1], bufnr('$')))
endif
let buflist = s:bufList
endif
let lastBufNr = bufnr('$')
let i = 1
if g:LookupFile_Bufs_LikeBufCmd
let pattern = s:TranslateFileRegex(a:pattern)
else
let pattern = a:pattern
endif
for bufNr in buflist
if ! bufexists(bufNr)
call remove(buflist, i)
continue
endif
try
if g:LookupFile_Bufs_SkipUnlisted && ! buflisted(bufNr)
continue
endif
let fname = expand('#'.bufNr.':p')
if g:LookupFile_Bufs_LikeBufCmd
let bname = bufname(bufNr)
let dir = ''
else
let bname = fnamemodify(bufname(bufNr), ':t')
let dir = fnamemodify(bufname(bufNr), ':h').'/'
endif
if bname =~ pattern
call add(results, {
\ 'word': fname,
\ 'abbr': bname,
\ 'menu': dir.substitute(bname, pattern, '[&]', ''),
\ 'dup': 1,
\ })
endif
finally
let i = i + 1
endtry
endfor
return results
endfunction
function! s:LookupWalk(pattern)
" We will wait till '/' is typed
if a:pattern =~ '\*\*$'
return []
endif
let showOnlyDirs = 0
" Determine the parent dir.
if a:pattern =~ '//$'
let parent = strpart(a:pattern, 0, strlen(a:pattern)-1)
let filePat = ''
if parent ==# g:lookupfile#lastPattern
return filter(g:lookupfile#lastResults, 'v:val["kind"] == "/"')
endif
let showOnlyDirs = 1
else
let parent = matchstr(a:pattern, '^.*/')
let filePat = strpart(a:pattern, len(parent))
endif
let matchingExactCase = s:MatchingExactCase(filePat)
" Remove leading or trailing '*'s as we add a star anyway. This also makes
" '**' as '', but we rule this case out already.
let filePat = substitute(filePat, '^\*\+\|\*\+$', '', 'g')
" On windows, the case is anyway ignored.
if !genutils#OnMS() && !matchingExactCase
let filePat = s:FilePatIgnoreCase(filePat)
endif
"exec BPBreak(1)
let _shellslash = &shellslash
set shellslash
try
let files = glob(parent.((filePat != '') ? '*'.filePat.'*' : '*'))
catch
" Ignore errors in patterns.
let files = ''
finally
let &shellslash = _shellslash
endtry
let fl = split(files, "\<NL>")
let regexPat = s:TranslateFileRegex(filePat)
" This is a psuedo case-sensitive match for windows, when 'smartcase' is
" set.
if genutils#OnMS() && matchingExactCase
call filter(fl, 'fnamemodify(v:val, ":t") =~# regexPat')
endif
" Find the start of path component that uses any of the *, [], ? or {
" wildcard. Path until this is unambiguously common to all, so we can strip
" it off, for brevity.
let firstWildIdx = match(a:pattern, '[^/]*\%(\*\|\[\|?\|{\)')
return s:FormatFileResults(fl, firstWildIdx!=-1 ? firstWildIdx :
\ strlen(parent), regexPat, matchingExactCase, showOnlyDirs)
endfunction
function! s:FormatFileResults(fl, parentLen, matchPat, matchingCase, dirsOnly)
let entries = []
for f in a:fl
if isdirectory(f)
let suffx = '/'
else
if a:dirsOnly
continue
endif
let suffx = ''
endif
let word = f.suffx
let fname = matchstr(f, '[^/]*$')
let dir = fnamemodify(f, ':h').'/'
if dir != '/' && a:parentLen != -1
let dir = strpart(dir, a:parentLen)
else
let dir = ''
endif
"let dir = (dir == '/'?'':dir)
call add(entries, {
\ 'word': word,
\ 'abbr': fname.suffx,
\ 'menu': (a:matchPat!='') ? dir.substitute(fname,
\ (a:matchingCase?'\C':'\c').a:matchPat, '[&]', '') :
\ dir.fname,
\ 'kind': suffx,
\ 'dup': 1
\ })
endfor
return entries
endfunction
function! s:ConfigBufs()
" Allow switching to file mode.
inoremap <expr> <buffer> <C-F> <SID>IdoSwitchTo('file')
call s:AddCleanup('iunmap <buffer> <C-F>')
if g:LookupFile_Bufs_BufListExpr != ''
call s:SaveSett('SortMethod')
let g:LookupFile_SortMethod = ''
endif
endfunction
function! s:ConfigWalk()
call s:SaveSett('LookupAcceptFunc')
unlet! g:LookupFile_LookupAcceptFunc
let g:LookupFile_LookupAcceptFunc = function(s:SNR().'IdoAccept')
" Make sure we have the right slashes, in case user passed in init path
" with wrong slashes.
call setline('.', substitute(getline('.'), '\\', '/', 'g'))
inoremap <buffer> <expr> <BS> <SID>IdoBS()
inoremap <buffer> <expr> <S-BS> <SID>IdoBS()
call s:AddCleanup('iunmap <buffer> <BS>')
imap <buffer> <expr> <Tab> <SID>IdoTab()
call s:AddCleanup('iunmap <buffer> <Tab>')
inoremap <expr> <buffer> <C-B> <SID>IdoSwitchTo('buffer')
call s:AddCleanup('iunmap <buffer> <C-B>')
endfunction
function! s:IdoSwitchTo(mode)
call s:LookupReset()
if a:mode == 'buffer'
let tok = matchstr(getline('.'), '[^/]*$')
let cmd = 'LUBufs'.(tok == "" ? '!' : ' '.tok)
else
let cmd = 'LUWalk '.s:GetDefDir().getline('.')
endif
return (pumvisible()?"\<C-E>":'')."\<Esc>:".cmd."\<CR>"
endfunction
function! s:IdoAccept(splitWin, key)
let refreshCmd = "\<C-O>:call lookupfile#LookupFile(0)\<CR>\<C-O>:\<BS>"
if getline('.') !=# g:lookupfile#lastPattern && getline('.')[strlen(getline('.'))-1] == '/'
return refreshCmd
elseif getline('.') ==# g:lookupfile#lastPattern
\ && len(g:lookupfile#lastResults) > 0
\ && g:lookupfile#lastResults[0]['kind'] == '/'
" When the first entry is a directory, accept it, and trigger a fresh
" completion on that.
return "\<C-N>\<C-R>=(getline('.') == lookupfile#lastPattern)?\"\\<C-N>\":''\<CR>".refreshCmd
endif
return lookupfile#AcceptFile(a:splitWin, a:key)
endfunction
function! s:IdoBS()
if lookupfile#IsPopupHidden() == 1
return "\<BS>"
endif
if getline('.') !~ '/$'
return (pumvisible() ? "\<C-E>" : '')."\<BS>"
else
" Determine the number of <BS>'s required to remove the patch component.
let lastComp = matchstr(getline('.'), '[^/]*/$')
return (pumvisible() ? (getline('.') ==# g:lookupfile#lastPattern ?
\ "\<C-E>" : "\<C-Y>") : '') . repeat("\<BS>", strlen(lastComp))
endif
endfunction
function! s:IdoTab()
" When no pattern yet, fill up with current directory.
if !pumvisible() && getline('.') == ''
return s:GetDefDir()
else
return "\<Tab>"
endif
endfunction
function! s:GetDefDir()
return substitute(expand('#'.s:baseBufNr.':p:h'), '\\', '/', 'g').'/'
endfunction
" Convert file wildcards ("*", "?" etc. |file-pattern|) to a Vim string
" regex metachars (see |pattern.txt|). Returns metachars that work in "very
" nomagic" mode.
let s:fileWild = {}
function! s:TranslateFileWild(fileWild)
let strRegex = ''
if a:fileWild ==# '*'
let strRegex = '\[^/]\*'
elseif a:fileWild ==# '**'
let strRegex = '\.\*'
elseif a:fileWild ==# '?'
let strRegex = '\.'
elseif a:fileWild ==# '['
let strRegex = '\['
endif
return strRegex
endfunction
" Convert a |file-pattern| to a Vim string regex (see |pattern.txt|).
" No error checks for now, for simplicity.
function! s:TranslateFileRegex(filePat)
let pat = substitute(a:filePat, '\(\*\*\|\*\|\[\)',
\ '\=s:TranslateFileWild(submatch(1))', 'g')
let unprotectedMeta = genutils#CrUnProtectedCharsPattern('?,', 1)
let pat = substitute(pat, unprotectedMeta,
\ '\=s:TranslateFileWild(submatch(1))', 'g')
return (pat == '') ? pat : '\V'.pat
endfunction
" Translates the file pattern to ignore case on non-case-insensitive systems.
function! s:FilePatIgnoreCase(filePat)
return substitute(a:filePat, '\(\[.\{-}]\)\|\(\a\)',
\ '\=s:TranslateAlpha(submatch(0))', 'g')
endfunction
function! s:TranslateAlpha(pat)
if a:pat =~"^["
return substitute(substitute(a:pat, '-\@<!\a-\@!', '&\u&', 'g'),
\ '\(\a\)-\(\a\)', '\1-\2\u\1-\u\2', 'g')
else
return substitute(a:pat, '\a', '[\l&\u&]', 'g')
endif
endfunction
function! s:MatchingExactCase(filePat)
if &ignorecase
if &smartcase && a:filePat =~# '\u'
let matchingExactCase = 1
else
let matchingExactCase = 0
endif
else
if genutils#OnMS()
let matchingExactCase = 0
else
let matchingExactCase = 1
endif
endif
return matchingExactCase
endfunction
" Restore cpo.
let &cpo = s:save_cpo
unlet s:save_cpo
" vim6:fdm=marker et sw=2