mirror of
https://github.com/inkarkat/vim-ingo-library.git
synced 2026-05-29 11:18:51 +02:00
163 lines
6.9 KiB
VimL
163 lines
6.9 KiB
VimL
" ingo/cmdargs/glob.vim: Functions for expanding file glob arguments.
|
|
"
|
|
" DEPENDENCIES:
|
|
"
|
|
" Copyright: (C) 2012-2020 Ingo Karkat
|
|
" The VIM LICENSE applies to this script; see ':help copyright'.
|
|
"
|
|
" Maintainer: Ingo Karkat <ingo@karkat.de>
|
|
|
|
function! ingo#cmdargs#glob#ExpandSingle( fileglob, ... )
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Expand any file wildcards in a:fileglob to a list of normal filespecs.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" None.
|
|
"* INPUTS:
|
|
" a:fileglob File glob (already processed by
|
|
" ingo#cmdargs#file#Unescape()).
|
|
" a:isKeepNoMatch Optional flag that lets globs that have no matches be kept
|
|
" and returned as-is, instead of being removed. Set this when
|
|
" you want to support creating new files.
|
|
" a:isKeepDirectories Optional flag that keeps directories in the list.
|
|
"* RETURN VALUES:
|
|
" List of normal filespecs; globs have been expanded. To consume this in
|
|
" another Vim command, use:
|
|
" join(map(l:filespecs, 'fnameescape(v:val)))
|
|
"******************************************************************************
|
|
" XXX: Special Vim variables are expanded by -complete=file, but (in Vim
|
|
" 7.3), escaped special names are _not_ correctly re-escaped, and a
|
|
" following glob() or expand() will mistakenly expand them. Because of the
|
|
" auto-expansion, any unescaped special Vim variable that gets here is in
|
|
" fact a literal special filename. We don't even need to re-escape and
|
|
" glob() it, just return it verbatim.
|
|
if a:fileglob =~# '^\%(%\|#\d\?\)\%(:\a\)*$\|^<\%(cfile\|cword\|cWORD\)>\%(:\a\)*$'
|
|
return [a:fileglob]
|
|
else
|
|
" Filter out directories; we're usually only interested in files.
|
|
let l:specs = (a:0 && a:1 ? split(expand(a:fileglob), '\n') : ingo#compat#glob(a:fileglob, 0, 1))
|
|
return (a:0 >= 2 && a:2 ? l:specs : filter(l:specs, '! isdirectory(v:val)'))
|
|
endif
|
|
endfunction
|
|
function! ingo#cmdargs#glob#Expand( fileglobs, ... )
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Expand any file wildcards in a:fileglobs to a list of normal filespecs.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" None.
|
|
"* INPUTS:
|
|
" a:fileglobs Either space-separated arguments string (from a :command
|
|
" -complete=file ... <q-args> custom command), or a list of
|
|
" fileglobs (already processed by
|
|
" ingo#cmdargs#file#Unescape()).
|
|
" a:isKeepNoMatch Optional flag that lets globs that have no matches be kept
|
|
" and returned as-is, instead of being removed. Set this when
|
|
" you want to support creating new files.
|
|
" a:isKeepDirectories Optional flag that keeps directories in the list.
|
|
"* RETURN VALUES:
|
|
" List of filespecs; globs have been expanded. To consume this in another Vim
|
|
" command, use:
|
|
" join(map(l:filespecs, 'fnameescape(v:val)))
|
|
"******************************************************************************
|
|
let l:fileglobs = (type(a:fileglobs) == type([]) ? a:fileglobs : ingo#cmdargs#file#SplitAndUnescape(a:fileglobs))
|
|
|
|
let l:filespecs = []
|
|
for l:fileglob in l:fileglobs
|
|
call extend(l:filespecs, call('ingo#cmdargs#glob#ExpandSingle', [l:fileglob] + a:000))
|
|
endfor
|
|
return l:filespecs
|
|
endfunction
|
|
|
|
function! s:FileLinePredicate( filespec ) abort
|
|
let l:names = matchlist(a:filespec, '\(.\{-1,}\):\%(\(\d\+\)\%(:\(\d*\):\?\)\?\)\?$')
|
|
return (! empty(l:names) && filereadable(l:names[1]))
|
|
endfunction
|
|
if ! exists('g:IngoLibrary_SpecialFilePredicates')
|
|
let g:IngoLibrary_SpecialFilePredicates = []
|
|
call add(g:IngoLibrary_SpecialFilePredicates, 'v:val =~# ''^\w\+:/''') " Assume that files that start with "protocol:/" do exist (usually handled by the netrw plugin)
|
|
|
|
if exists('g:loaded_file_line') && g:loaded_file_line
|
|
call add(g:IngoLibrary_SpecialFilePredicates, function('s:FileLinePredicate'))
|
|
endif
|
|
endif
|
|
function! ingo#cmdargs#glob#IsSpecialFile( filespec ) abort
|
|
for l:SpecialFileReadablePredicate in g:IngoLibrary_SpecialFilePredicates
|
|
if ingo#actions#EvaluateWithValOrFunc(l:SpecialFileReadablePredicate, a:filespec)
|
|
return 1
|
|
endif
|
|
unlet! l:SpecialFileReadablePredicate
|
|
endfor
|
|
return 0
|
|
endfunction
|
|
function! s:ContainsNoWildcards( fileglob )
|
|
" Note: This is only an empirical approximation; it is not perfect.
|
|
if ingo#os#IsWinOrDos()
|
|
return a:fileglob !~ '[*?]'
|
|
else
|
|
return a:fileglob !~ '\\\@<![*?{[]'
|
|
endif
|
|
endfunction
|
|
function! ingo#cmdargs#glob#Resolve( fileglobs )
|
|
"*******************************************************************************
|
|
"* PURPOSE:
|
|
" Expand any file wildcards in a:fileglobs, convert to normal filespecs
|
|
" and assemble file statistics. Like ingo#cmdargs#glob#Expand(), but
|
|
" additionally returns statistics.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" None.
|
|
"* INPUTS:
|
|
" a:fileglobs Raw list of file patterns.
|
|
"* RETURN VALUES:
|
|
" [filespecs, statistics] First element is a list of the resolved
|
|
" filespecs (in normal, not Ex syntax), second
|
|
" element is a dictionary containing the file
|
|
" statistics.
|
|
"*******************************************************************************
|
|
let l:statistics = { 'files': 0, 'removed': 0, 'nonexisting': 0 }
|
|
let l:filespecs = []
|
|
for l:fileglob in a:fileglobs
|
|
let l:resolvedFilespecs = ingo#cmdargs#glob#ExpandSingle(l:fileglob)
|
|
if empty(l:resolvedFilespecs)
|
|
" To treat the file pattern as a filespec, we must emulate one
|
|
" effect of glob(): It removes superfluous escaping of spaces in the
|
|
" filespec (but leaves other escaped characters like 'C:\\foo'
|
|
" as-is). Without this substitution, the filereadable() check won't
|
|
" work.
|
|
let l:normalizedPotentialFilespec = substitute(l:fileglob, '\\\@<!\\ ', ' ', 'g')
|
|
|
|
" The globbing yielded no files; however:
|
|
if filereadable(l:normalizedPotentialFilespec) || ingo#cmdargs#glob#IsSpecialFile(l:normalizedPotentialFilespec)
|
|
" a) The file pattern itself represents an existing file. This
|
|
" happens if a file is passed that matches one of the
|
|
" 'wildignore' patterns. In this case, as the file has been
|
|
" explicitly passed to us, we include it.
|
|
let l:filespecs += [l:normalizedPotentialFilespec]
|
|
elseif s:ContainsNoWildcards(l:fileglob)
|
|
" b) The file pattern contains no wildcards and represents a
|
|
" to-be-created file.
|
|
let l:filespecs += [l:fileglob]
|
|
let l:statistics.nonexisting += 1
|
|
else
|
|
" Nothing matched this file pattern, or whatever matched is
|
|
" covered by the 'wildignore' patterns and not a file itself.
|
|
let l:statistics.removed += 1
|
|
endif
|
|
else
|
|
" We include whatever the globbing returned; 'wildignore' patterns
|
|
" are filtered out.
|
|
let l:filespecs += l:resolvedFilespecs
|
|
endif
|
|
endfor
|
|
|
|
let l:statistics.files = len(l:filespecs)
|
|
return [l:filespecs, l:statistics]
|
|
endfunction
|
|
|
|
" vim: set ts=8 sts=4 sw=4 noexpandtab ff=unix fdm=syntax :
|