修改vim函数不断给我:“没有足够的函数参数”

时间:2011-04-21 17:17:20

标签: vim

我有这个功能:

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>")

当我尝试添加一个参数时,声明变为function! Find(name, search_dir),当我使用:Find x y调用vim中的函数时,它总是告诉我没有足够的参数(其中{{1}当函数声明中只有1个参数时,它将起作用。

知道如何添加多个参数吗?

最终目标是在指定的子目录中找到一个Find函数。

2 个答案:

答案 0 :(得分:1)

您需要将-nargs=1更改为-nargs=+。这意味着您必须拥有参数但不指定数字。我建议您将Find函数更改为Find(...),如果使用的参数数量无效,请使用a:0获取错误参数的数量。

具有多个参数的示例函数和命令:

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

获取更多帮助

:h :command-nargs
:h ...

修改 添加了除@ZyX建议的多个参数之外的函数示例。

答案 1 :(得分:1)

@Peter Rincker的补充答案:如果您想将参数传递给函数,则不应使用"<args>",因为已经存在不需要的内置<q-args><f-args>额外的引用,并没有引入代码注入的可能性(尝试Find ".string(g:)."与您的代码)。首先将所有参数作为一个项传递,第二个将产生适合函数调用的参数列表:

command -nargs=+ Find :call Find(<f-args>)

需要考虑的另一件事:

  1. (system()call)切勿将用户输入原样传递给shell,使用shellescape(str, 1)。你可能会遇到意想不到的问题。
  2. strlen(substitute(l:input, "[0-9]", "", "g"))>0条件相当于input=~#'\D',但要大得多。
  3. 您无需指定l::它是函数内的默认范围。
  4. 内置glob()功能:整个system()行可以替换为

    join(map(split(glob('./*'.escape(a:name, '\`*[]?').'*'), "\n"), 'v:key."\t".v:val'), "\n")
    
  5. 不要忘记逃避你执行的所有事情:execute ":e ".l:line应该写成execute "e" fnameescape(line)(这是第三个可以在如此简单的代码片段中注入代码的地方!)。
  6. 最好在这里使用列表,在这种情况下,您不需要使用某些东西来添加行号:

    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)
    
  7. 你和我都没有处理匹配pattern的文件名包含换行符的情况。我知道如何处理这个问题(你可以看到我的vim-fileutils插件(已弃用)或os.listdir os插件frawor模块的功能(在alpha阶段,未发布到vim .ORG))。我认为这种情况不太可能,所以请记住这是可能的。