" minimal.vim command -nargs=1 -complete=custom,TestComplete Test echo <args> function! TestComplete(A, C, P) abort return A endfunction
vim --clean -u minimal.vim:Test abc def<Tab>defBut note that the command has only one argument, so the ArgLead should be abc def, right? Why is it def now? How does Vim define arglead anyway, because it is curcial for plugins to provide correct completion
In step 4, completion should show abc def
9.1.2112
NA
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Hm, that must have been there for a very long time. I always thought that nargs=1 would mean 1 (non-whitespace) argument (not including whitespace separation), but the help actually states differently. It might make sense to update the documentation instead of changing the existing behaviour
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
You sound like "If the bug exists for long enough, it doesn't need fixing". Which plugin actually depend on the current behavior anyway? And who are gonna benefit from the current behavior? Vim has never provided a way to get where ArgLead actually start, so I don't think any plugins can make use of the current behavior.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
I don't think this is a bug. I think this is a document mix too. for what you want to have is a:CmdLine. In my view, ArgLead is 'the leading things for this arg. For example Test a b c d def` is still def. I don't think this has any thing wrong.
I guess what you want is a:cmdline->split()[1 : ]->join().
This is an example I am using for my plugin:
vim9script export def Complete(arglead: string, cmdline: string, cursorpos: number): string var parts = cmdline->strpart(0, cursorpos)->split() if arglead == null_string parts = parts->add(' ') endif if parts->len() == 2 return "a\nb\nc\nd\ne\nf" elseif parts->len() == 3 if parts[1] == 'receive' return "foo\nbar" endif endif return null_string enddef
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
In my view, ArgLead is 'the leading things for this arg'. For example Test a b c d def is still def. I don't think this has any thing wrong.
According to Cambridge dictionary, "leading" means "first". How the hell can "def" be the first in Test a b c d def?
I guess what you want is a:cmdline->split()[1 : ]->join().
That doesn't work if users type Test=abc, in that case =abc will be parsed as argument by Vim.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
According to Cambridge dictionary, "leading" means "first". How the hell can "def" be the first in Test a b c d def?
This 'leading' means for an argument, for example foobar and when I have typed foo, foo is the 'leading' part of this argument.
That doesn't work if users type Test=abc, in that case =abc will be parsed as argument by Vim.
I think I know what you means, maybe what you want is to set one complete function for all commands which is called after :
For your Test=abc example, Complete function won't be called. Because this only be called after Test command was type with an space. So only after you type this space Vim can know which complete function should be used.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
the example you provided only prove that arglead is currently useless for commands with nargs=1
Unfortunately that is true in some condition. You can read this chapter of doc. :h command-completion-custom
The function may use these for determining context. For the "custom" argument, it is not necessary to filter candidates against the (implicit pattern in) ArgLead. Vim will filter the candidates with its regexp engine after function return, and this is probably more efficient in most cases. If 'wildoptions' contains "fuzzy", then the candidates will be filtered using |fuzzy-matching|. For the "customlist" argument, Vim will not filter the returned completion candidates and the user supplied function should filter the candidates.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
This 'leading' means for an argument, for example foobar and when I have typed foo, foo is the 'leading' part of this argument.
The only argument of Test a b cd is a b cd (nargs=1). So if I put my cursor between c and d, the "leading part" should be a b c. Even if it were just a, it still make more sense than if it is c
maybe what you want is to set one complete function for all commands which is called after
No, that is very far from what I mean.
For your Test=abc example, Complete function won't be called.
Yes, it is still called, you could try right with my repro step above.
the example you provided only prove that arglead is currently useless for commands with nargs=1
Unfortunately that is true in some condition.
Good that you agree with me that it is useless. If plugin author can't reliably tell where ArgLead will start, they cannot provide completion.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Good that you agree with me that it is useless. If plugin author can't reliably tell where ArgLead will start, they cannot provide completion.
I am still confusing. Could you tell me what feature do you want to implement in your plugin? Because I think the present things is enough for me.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Yes, it is still called, you could try right with my repro step above.
Oh this is an interesting example. when there is a leading '=', the complete affect will be broken, but it can use when the return value is a:C or a:A or a:C . a:P, but for a:P is nothing. I think this is an exact problem. Is this the main problem you want to solve? or Open an new issue to talk about this things.
If you have interesting to fix this problem set autocmd CmdlineChanged : wildtrigger() can help you to watch it actively.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
The only argument of Test a b cd is a b cd (nargs=1). So if I put my cursor between c and d, the "leading part" should be a b c. Even if it were just a, it still make more sense than if it is c
The argument is seperated by space. Everything seperated by space is an independent argument.
@brianhuster If you want to implement this. return a:C->strpart(0, a:P)->split()[1:]->join() may useful.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
I made a :Py command that is same as :python3, but with smart completion provided by Jedi
Here is the full source
if !has("python3") finish endif command! -nargs=1 -complete=custom,ExPyComplete Py :py3 <args> python3 << EOF import jedi is_nvim = int(vim.eval('has("nvim")')) def ex_python_complete(arg=None): if arg is None: arg = vim.eval('a:ArgLead') text = arg if is_nvim and text.startswith('='): text = "print(" + text[1:] script = jedi.Interpreter(text, [globals()]) completions = script.complete() return "\n".join(arg + c.complete for c in completions) EOF function! ExPyComplete(ArgLead, CmdLine, CursorPos) return py3eval('ex_python_complete()') endfunction
The problem comes when I type something like :Py print(a, i and then type for completion. The completion it shows will include if (which is a keyword and it is invalid there). This is also weird because if I run :Py print(ex_python_complete("print(a, i"), it doesn't print if in result. So I tried to debug and found that Vim only send i as ArgLead
Of course I can use Cmdline as you said, but then I still have to calculate where the completion start. In the above example, ArgLead is just i, but if I assumed that completion should start at the start of the argument and provide completion as print(a, int the cmdline will become :Py print(a, print(a, int which is a bug.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
The argument is seperated by space. Everything seperated by space is an independent argument.
I am talking about nargs=1 here.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Oh I see, maybe such complex things isn't being considered when designed 'ArgLead'.
I am talking about nargs=1 here.
So what you want is to make it a speacial condition, with regarding any of space when nargs=1. Is that right?
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
So what you want is to make it a speacial condition, with regarding any of space when nargs=1. Is that right?
Please read Vim document again. It clearly explain what an argument means when nargs=1
Oh I see, maybe such complex things isn't being considered when designed 'ArgLead'.
It is literally a bug, it contradicts the document.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Maybe I know what you means, it is a bug. The problem is that '-nargs=1' is a special condition, but the complete part doesn't consist with it. Thanks.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
For '=' problem. Would you please open an another issue? thanks.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
For '=' problem. Would you please open another issue? thanks.
If this issue is fixed then this problem will also be fixed so I don't think another issue is necessary
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
I mean:
command -nargs=1 -complete=custom,TestComplete T echo "<args>"
function! TestComplete(A, C, P) abort
return a:C.a:C->split('[ =]')->join() endfunction
will have an output. but
command -nargs=1 -complete=custom,TestComplete T echo "<args>"
function! TestComplete(A, C, P) abort
return a:C->split('[ =]')->join() endfunction
won't have any completion message when typing ':T=abc'
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
You sound like "If the bug exists for long enough, it doesn't need fixing". Which plugins actually depend on the current behavior anyway? And who is going to benefit from the current behavior?
That is a fair point about backwards compatibility, yes — changing this could break existing plugins. The custom command-line completion has been around for a long time, probably since Vim 7.0. We can change it, but it would be a backwards-incompatible change.
Vim has never provided a way to get where ArgLead actually starts, so I don't think any plugins can make use of the current behavior.
That is not entirely true. Vim provides functions like :h getcmdline() and :h getcmdpos() and related APIs, which can be used to determine where ArgLead actually starts. So some plugins may be able to work with the current behavior.
On a side note, some of your phrasing earlier, came across to me as a bit sharp. I understand the technical points you’re making, but I wanted to mention it so we can keep the discussion constructive. Thanks for understanding.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
some of your phrasing earlier, came across to me as a bit sharp
I apologize if I make anyone feel uncomfortable, but I just want to go straight the point
We can change it, but it would be a backwards-incompatible change.
I don't think changing an undocumented behavior is breaking backward compatibility. It is like changing internal/private functions in this case.
Vim provides functions like :h getcmdline() and :h getcmdpos() and related APIs, which can be used to determine where ArgLead actually starts
If plugins already use them (or even just CmdLine, CursorPos arguments) to calculate where ArgLead starts, they will have no problems (they don't need to update the code at all) if we are just changing where ArgLead starts
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
TL;DR this behavior is ancient; changing it would probably constitute a breaking change. We should update the docs. I suggest a new builtin function getcmdarg() to help the OP's case.
FWIW, custom completion (and ArgLead) go back to the tag vim-6-2c from the history repo, aka vim/vim-history@a27612d973 (vim-6.2b (tag vim-6-2b) -> vim-6.2c (tag vim-6-2c), 2003-05-06). It requires the following patch to build (just make) on my macbook (2015/intel running macOS 12.7.6 with an i7):
From 41c67302a8ecc6cfcddb938b2b3f2b16f3841536 Mon Sep 17 00:00:00 2001 From: "D. Ben Knoble" <ben.knob...@gmail.com> Date: Sun, 1 Feb 2026 14:11:15 -0500 Subject: [PATCH] configure: include exit() definition On modern platforms, exit() is defined by stdlib.h and not provided implicitly by compilers. Without the definition, the configure script produces programs that fail to compile (see src/auto/config.log) instead of providing useful information, like sizeof int; these failures make Vim unable to compile. --- src/auto/configure | 1 + 1 file changed, 1 insertion(+) diff --git a/src/auto/configure b/src/auto/configure index 409b1ea8ee..2897799f88 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -605,6 +605,7 @@ fi cat >> confdefs.h <<\EOF #define UNIX 1 +#include <stdlib.h> EOF echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 -- 2.48.1
From there, I can follow the reproduction steps in the OP, but modified, since :source didn't take a range then, and with <q-args> so that the example doesn't error. I also used echomsg instead of return since I couldn't make the completions show up easily (?).
$ cat repro.vim
command -nargs=1 -complete=custom,TestComplete Test echo <q-args>
function! TestComplete(A, C, P) abort
echomsg a:A
endfunction
$ VIMRUNTIME=$PWD/runtime src/vim -u NONE +'source repro.vim'
:Test abc def<C-E><C-U>mess<Enter>
Messages maintainer: Bram Moolenaar <Br...@vim.org>
def
So this has been the behavior since the early days of Vim. I'm not saying it's right or useful, but it would be hard to change now since we could not predict the effects. We should make sure the documentation reflects that the "ArgLead" variable does not consider the effect of -nargs; it considers the "argument currently being completed on" the last stuff after an unescaped space, AFAICT:
/*
* Set the completion context for the argument of a user defined command.
*/
char_u *
set_context_in_user_cmdarg(…) {
// …
// Find start of last argument.
p = arg;
while (*p)
{
if (*p == ' ')
// argument starts after a space
arg = p + 1;
else if (*p == '\\' && *(p + 1) != NUL)
++p; // skip over escaped character
MB_PTR_ADV(p);
}
xp->xp_pattern = arg;
xp->xp_context = context;
(I've blame-traced the origins of this code to roughly vim/vim-history@8fd20c655d (Vi IMproved 2.7 beta, 1994-08-04) via vim/vim-history@cdb313b5e8 (VIM - Vi IMproved 5.0, 1998-02-19), adjusted for escapes in 848f876 (updated for version 7.3.615, 2012-07-25), then refactored in d019039 (patch 8.1.1914: command line expansion code is spread out, 2019-08-23) and b31aec3 (patch 8.2.4398: some command completion functions are too long, 2022-02-16).)
We probably should provide an API to help the OP's code. Using strpart(a:C, 0, a:P) helps, but includes the Test portion (so OP would have to remove that part manually, too). What if we had an API like getcmdarg() that was like getcmdline() but parsed and stripped any range/prefixes/command, leaving just the argument section? It would have to handle all the cases described here (:Test=abc -> =abc, :Test abc def -> abc def, :%Tes ab -> ab, etc.), but that would be something anyone could use in their completion function.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
What if we had an API like getcmdarg() that was like getcmdline() but parsed and stripped any range/prefixes/command, leaving just the argument section? It would have to handle all the cases described here (:Test=abc -> =abc, :Test abc def -> abc def, :%Tes ab -> ab, etc.), but that would be something anyone could use in their completion function.
If you don't want to change it, then the API I need is one thing that would actually allow me to set (not get) the start of completion to right position
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
I suppose the alternative is to change this to behave for -nargs=1 as if the whole thing was the lead and see who complains… that's not really gone well in the past for some changes though 😅
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
What's more, for getcompletion("custom") there's no way to know what -nargs is. However this fuction is almost useless that it doesn't pass paramaters as you are using in cmdline.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
I am not sure why I was tagged. The behavior mentioned here is actually correct in a way (meaning, should not be changed). Vim completes the word before cursor. It does not replace all of the space separated words after the command. arglead just means leading word, not all of the words after the command word. This can be confusing since nargs=1 means argument is the 'string after the command' (which may include space separated words). This could be explained better in the doc. There is also no need for new API. It would be redundant. We already have getcmdline() and getcmdpos() and a host of string manipulation functions.
If you really want to replace many words as part of your custom completion function, you can overwrite the command-line using setcmdline(). You may look into the events that are triggered during completion.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
arglead just means leading word, not all of the words after the command word
The word "leading" is probably confusing, as it has many meanings ("best", etc). But I have checked many sources, and the only meaning I found that can match this context is "first" or "initial".
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
so now we are at a potential doc patch. Any suggestions?
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
I just looked at the doc. For ArgLead, it says "portion of the argument" and "currently being completed" which essentially means "word before the cursor".
ArgLead the leading portion of the argument currently being
completed on
The doc for nargs=1 is also clear since it says "includes spaces".
-nargs=1 Exactly one argument is required, it includes spaces
The reader is expected to connect the dots: In this context (when nargs=1), argument is the string of one or more words (cluster of non-space letters), and ArgLead means the cluster of non-space letters right before the cursor. IMO, nothing needs to be done.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
I just looked at the doc. For
ArgLead, it says "portion of the argument" and "currently being completed" which essentially means "word before the cursor".ArgLead the leading portion of the argument currently being completed onThe doc for
nargs=1is also clear since it says "includes spaces".-nargs=1 Exactly one argument is required, it includes spacesThe reader is expected to connect the dots: In this context (when
nargs=1), argument is the string of one or more words (cluster of non-space letters), andArgLeadmeans the cluster of non-space letters right before the cursor. IMO, nothing needs to be done.
Actually, I think this is precisely what led to the confusion: -nargs=1 defines "argument" with the spaces included, but that's not what ArgLead means!
So perhaps:
diff --git i/runtime/doc/map.txt w/runtime/doc/map.txt index ee46180a6..ed9915eab 100644 --- i/runtime/doc/map.txt +++ w/runtime/doc/map.txt @@ -1694,8 +1694,8 @@ For the "customlist" argument, the function should return the completion candidates as a Vim List. Non-string items in the list are ignored. The function arguments are: - ArgLead the leading portion of the argument currently being - completed on + ArgLead the leading portion of the argument (space separated + word) currently being completed on CmdLine the entire command line CursorPos the cursor position in it (byte index) The function may use these for determining context. For the "custom"
?
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Actually, I think this is precisely what led to the confusion:
-nargs=1defines "argument" with the spaces included, but that's not whatArgLeadmeans!
Correct, and it says so clearly. It further establishes the difference between "argument" and ArgLead by saying argument includes spaces and ArgLead doesn't (since it is only a "portion" of the argument -- that is the "word" being completed).
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Thanks, I have slightly updated this further. As such I keep it like this without any further actions. Thanks all!
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Perfect, thanks Christian.
I am sorry that we are unable to adjust this to make more sense for the -nargs=1 case due to backwards compatibility, but I'll leave it to @brianhuster to propose new complementary functions for their use-case (since using getcmdline() and setcmdline() would probably be a lot of non-trivial code, but I'm not exactly sure of the use case).
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
The simplest use case to think of is file name completion when nargs is 1. As nargs is 1, plugins won't force users to escape special characters like space, but then if the file path actually has a space, completion will be wrong.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()