3

I have a command line tool that requires three parameters in the form of:

command input1 input2

I'm trying to write a custom complete function that handles completion for different arguments... well, differently.

Determining which argument I'm on and returning a different set of completion options isn't hard; I've got that figured out:

function! CustomCompletion(argLead, line, pos)
   let argList = split(a:line)
   if     len(argList) == 1
      " Do completion for arg 1 (the command)
   elseif len(argList) == 2
      " Do completion for arg 2
   elseif len(argList) == 3
      " Do completion for arg 3
   endif
endfunction

My problem is with the "argLead" variable. :h command-completion-custom says this:

ArgLead: the leading portion of the argument currently being completed on.

Places Iv'e researched seem to imply that this variable contains the current white-space separated argument that the cursor is on. However whenever I use it, the variable contains the whole command line (spaces and all). Additionally when I press tab after say, the second argument, the tab completion tries to match the whole command line instead just the current argument.

Is there some way to tell the completion function to parse args based on white-space?

Oh, and I'm calling this function in the thrid paramater of input() if that makes any difference.

let user = input("Enter a command\n", "", "custom,CustomCompletion")

Thanks

  • 1
    I may be wrong, but I think the problem comes from the input() function, not the completion function. When you write a completion function for a command, you can tell the latter that it must wait for several arguments thanks to the attribute -nargs=+. I don't think there's something equivalent for input() which is probably why it will always send the whole command line as an argument to the completion function. Maybe you could make 3 distinct calls to input(): vpaste.net/FovYz and build user afterwards. – saginaw Feb 25 '16 at 20:11
  • Otherwise, your completion function would have several problems to solve. If input() can only complete the whole command line, your completion function will have to return not just the completion of an argument, but the whole command. Besides, at the moment, your completion function chooses which candidates to return based on the number of "words" on the line (if len(argList) == 1), but to work properly I think it should also take into account the position of the cursor. That problem seems to be solved in the link you gave though. – saginaw Feb 25 '16 at 20:33
  • It would help if you could be more specific. Especially regarding how you want to use this. Based on the first part of the question, I get the impression that you are defining a command, e.g. with command -complete .... Later, I understand this is not the case, and it gets confusing. Can you explain how the tool should be used when it works as expected? – Karl Yngve Lervåg Feb 26 '16 at 6:59
  • Sorry, I try not to bore people with details that I don't think help solve the problem. The context is that I'm designing a "package manager" within vim for multiple directory projects. This tool will make it easier for me to search within a predefined set of directories easily (And don't tell me there's a plugin for it. I wanted to write it myself :). This issue comes up in the tool to edit the packages. Once the tool is activated via a user command, the user is then prompted (via an input() ) for a command. They can also supply optional arguments to the command. i.e. add Foo . – Tumbler41 Feb 26 '16 at 19:43
  • This will add the current directory to package Foo. Anyway I have a workaround (posted below). If you have a better answer feel free to post it, but I can work with what I've got for now. – Tumbler41 Feb 26 '16 at 19:46
2

I believe that saginaw is correct. The problem resides in the fact that input() handles completion differently than the command! function. I don't think that the author of the input() function gave much thought to custom completion and only added partial support for it. As saginaw mentions, the intput() function has no -nargs parameter like command!. As a result the ArgLead parameter will always be the whole command line, and consequently the match will always be performed against the whole command line.

Workaround:

One possible solution is to prepend the previously typed arguments for completion. That way when the match is performed it will at least be able to see that the user input matches the predefined completions. I accomplished this in this way:

function! CustomCompletion(argLead, line, pos)
   let argList = split(a:line)
   let returnString = ""
   if     len(argList) == 1
      " This is the first argument so nothing special here

   elseif len(argList) == 2
      " Second argument needs to add first argument to completion
      for item in myCompletionList
         let returnString .= argList[0] . " " . item . "\n"
      endfor
      return returnString

   endif
endfunction

This does work reasonably well. The drawback is that if you have windmenu set, the suggestions include the whole command line which is kind of annoying and hard to see if you have a lot of items.

Example: Whole command line match

At present I see no other alternatives, so this will have to do.

As far as the other issue that saginaw mentioned, that is solved (as he stated) here. However, I didn't include it in the code since I didn't think it was relevant to the current problem and would just clutter the example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.