I have this function:
function! Find(name)
let l:list=system("find . -name '".a:name."' | perl -ne 'print \"$.\\t$_\"'")
let l:num=strlen(substitute(l:list, "[^\n]", "", "g"))
if l:num < 1
echo "'".a:name."' not found"
return
endif
if l:num != 1
echo l:list
let l:input=input("Which ? (CR=nothing)\n")
if strlen(l:input)==0
return
endif
if strlen(substitute(l:input, "[0-9]", "", "g"))>0
echo "Not a number"
return
endif
if l:input<1 || l:input>l:num
echo "Out of range"
return
endif
let l:line=matchstr("\n".l:list, "\n".l:input."\t[^\n]*")
else
let l:line=l:list
endif
let l:line=substitute(l:line, "^[^\t]*\t./", "", "")
execute ":e ".l:line
endfunction
command! -nargs=1 Find :call Find("<args>")
When I try adding a parameter, so the declaration becomes function! Find(name, search_dir)
, it always tells me i don't have enough parameters when I call the funct开发者_运维百科ion in vim using :Find x y
(where as Find: x
would work when ther was only 1 parameter in the function declaration.
Any idea how I can add multiple parameters?
The end goal is to have a Find function that finds in a specified subdirectory.
An addition to @Peter Rincker answer: you should never use "<args>"
if you want to pass parameter to a function as there is already a builtins <q-args>
and <f-args>
which do not need additional quoting and do not introduce a possibility of code injection (try Find ".string(g:)."
with your code). First will pass all parameters as one item, second will produce a list of parameters suitable for a function call:
command -nargs=+ Find :call Find(<f-args>)
Another things to consider:
- (system() call) Never pass user input to shell as-is, use
shellescape(str, 1)
. You may have unexpected problems here. strlen(substitute(l:input, "[0-9]", "", "g"))>0
condition is equivalent toinput=~#'\D'
, but is much bigger.- You don't need to specify
l:
: it is the default scope inside functions. There is a built-in
glob()
function: the wholesystem()
line can be replaced withjoin(map(split(glob('./*'.escape(a:name, '\`*[]?').'*'), "\n"), 'v:key."\t".v:val'), "\n")
- Don't forget to escape everything you execute:
execute ":e ".l:line
should be written asexecute "e" fnameescape(line)
(it is the third place where code injection is possible in such a simple code snippet!). It is better to use lists here, in this case you don't need to use something to add line numbers:
function s:Find(name) let list=split(glob('./*'.fnameescape(a:name).'*'), "\n") if empty(list) echo string(a:name) "not found" return endif let num=len(list) if num>1 echo map(copy(list), 'v:key."\t".v:val') let input=input("Which?") if empty(input) return elseif input=~#'\D' echo "Not a number" return elseif input<1 || input>num echo "Out of range" return endif let line=list[input] else let line=list[0] endif execute "e" fnameescape(line) endfunction command! -nargs=1 Find :call Find(<f-args)
- Neither you nor me handle the situation where filename that matches pattern contains a newline. I know how to handle this (you can see my vim-fileutils plugin (deprecated) or os.listdir function of os module of frawor plugin (in alpha stage, not posted to vim.org)). I don't think such situation is likely, so just remember that it is possible.
You need to change -nargs=1
to -nargs=+
. This will mean you have to have arguments but does not specify a number. I suggest you change your Find
function to Find(...)
and use a:0
get the number of arguments to error out if an invalid number of arguments are used.
Example function and command with multiple parameters:
command! -nargs=+ -complete=dir Find call Find(<f-args>)
fun! Find(name, ...)
let dir = getcwd()
if a:0 == 1
let dir = getcwd() . '/' . (a:1 =~ '[/\\]$' ? a:1 : a:1 . '/')
elseif a:0 != 0
echohl ErrorMsg
echo "Must supply 1 or 2 arguments"
echohl NONE
endif
let &efm = "%f"
cexpr []
caddexpr split(glob(dir . '**/*' . escape(a:name, '\`*[]?') . '*'), '\n')
copen
aug Find
au!
au BufLeave <buffer> ccl|aug! Find
aug END
endfun
For more help
:h :command-nargs
:h ...
Edit Added example of function that excepts multiple parameters as suggest by @ZyX.
精彩评论