Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to make use of two custom, complete functions in Vimscript?

Tags:

vim

Is there a way to achieve the following in Vim?

command! -nargs=* -complete=customlist,CustomFunc1 -complete=customlist,CustomFunc2 Foo call MyFunction(<f-args>)

The user will be able to tab-complete two arguments when calling the function Foo from Vim command line. The auto-complete will pull from two different lists.

E.g.:

:Foo arg1 good<TAB> whi<TAB>

Pressing Tab completes the words:

:Foo arg1 goodyear white
like image 419
centerback Avatar asked Aug 04 '11 07:08

centerback


People also ask

What are the functions in vimscript?

In the previous chapters, you have seen Vimscript native functions ( len (), filter (), map (), etc.) and custom functions in action. In this chapter, you will go deeper to learn how functions work.

What is a funcref in Vim?

A funcref is a reference to a function. It is one of Vimscript's basic data types mentioned in Ch. 24. The expression function ("SecondBreakfast") above is an example of funcref. Vim has a built-in function function () that returns a funcref when you pass it a function name (string).

How to get Meow string in vimscript?

:function GetMeow() : return "Meow String!" :endfunction Now try it out by running this command: :echom GetMeow() Vim will call the function and give the result to echom, which will display Meow String!. Calling Functions We can already see that there are two different ways of calling functions in Vimscript.

How do I call a function in Vim?

Now try it out by running this command: :echom GetMeow() Vim will call the function and give the result to echom, which will display Meow String!. Calling Functions We can already see that there are two different ways of calling functions in Vimscript.


2 Answers

There is sufficient information passed to completion function through its arguments. Knowing current cursor position in the command line to be completed, it is possible to determine the number of the argument that is currently being edited. Here is the function that returns that number as the only completion suggestion:

" Custom completion function for the command 'Foo'
function! FooComplete(arg, line, pos)
    let l = split(a:line[:a:pos-1], '\%(\%(\%(^\|[^\\]\)\\\)\@<!\s\)\+', 1)
    let n = len(l) - index(l, 'Foo') - 1
    return [string(n)]
endfunction

Substitute the last line with a call to one of the functions completing specific argument (assuming they are already written). For instance:

    let funcs = ['FooCompleteFirst', 'FooCompleteSecond']
    return call(funcs[n], [a:arg, a:line, a:pos])

Note that it is necessary to ignore whitespace-separated words before the command name, because those could be the limits of a range, or a count, if the command has either of them (spaces are allowed in both).

The regular expression used to split command line into arguments takes into account escaped whitespace which is a part of an argument, and not a separator. (Of course, completion functions should escape whitespace in suggested candidates, as usual in case of the command having more than one possible argument.)

like image 147
ib. Avatar answered Nov 04 '22 22:11

ib.


There's no built-in way for vim to do this. What I'd do in this situation is embed the logic into the completion function. When you set -complete=customlist,CompletionFunction, the specified function is invoked with three arguments, in this order:

  • The current argument
  • The entire command line up to this point
  • The cursor position

So, you can analyze these and call another function depending on whether it looks like you're on the second parameter. Here's an example:

command! -nargs=* -complete=customlist,FooComplete Foo call Foo(<f-args>)
function! Foo(...)
  " ...
endfunction

function! FooComplete(current_arg, command_line, cursor_position)
  " split by whitespace to get the separate components:
  let parts = split(a:command_line, '\s\+')

  if len(parts) > 2
    " then we're definitely finished with the first argument:
    return SecondCompletion(a:current_arg)
  elseif len(parts) > 1 && a:current_arg =~ '^\s*$'
    " then we've entered the first argument, but the current one is still blank:
    return SecondCompletion(a:current_arg)
  else
    " we're still on the first argument:
    return FirstCompletion(a:current_arg)
  endif
endfunction

function! FirstCompletion(arg)
  " ...
endfunction

function! SecondCompletion(arg)
  " ...
endfunction

One problem with this example is that it would fail for completions that contain whitespace, so if that's a possibility, you're going to have to make more careful checks.

like image 43
Andrew Radev Avatar answered Nov 04 '22 20:11

Andrew Radev