patch 9.2.0886: :set completion works for an invalid sub-option name
Commit:
https://github.com/vim/vim/commit/7872568f4240027f1bb6b458eee58c96a474fe25
Author: Hirohito Higashi <
h.eas...@gmail.com>
Date: Fri Jul 31 18:48:43 2026 +0000
patch 9.2.0886: :set completion works for an invalid sub-option name
Problem: Completing the value after a sub-option name also works when the
name merely ends in a valid one, e.g. "invalid_close:".
Solution: Require the name to start the option value or to follow a comma.
Compute the length of the name at compile time.
closes: #20893
Co-Authored-By: Claude Opus 5 (1M context) <
nor...@anthropic.com>
Signed-off-by: Hirohito Higashi <
h.eas...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/src/optionstr.c b/src/optionstr.c
index b656288cb..59a5a7e1d 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -1132,14 +1132,28 @@ did_set_ambiwidth(optset_T *args UNUSED)
}
#if defined(FEAT_TABPANEL) || defined(FEAT_DIFF) || defined(FEAT_PROP_POPUP)
+
+// "name" must be a string literal, the length is computed at compile time.
+# define completing_value_for_subopt(args, name) \
+ completing_value_for_subopt_len(args, name, (int)STRLEN_LITERAL(name))
+
+/*
+ * Return true when completing the value of the sub-option "name" with length
+ * "len", e.g. the value after "close:" in 'completepopup'.
+ */
static bool
-completing_value_for_subopt(optexpand_T *args, char *name_suffix)
+completing_value_for_subopt_len(optexpand_T *args, char *name, int len)
{
- char_u *colon = args->oe_xp->xp_pattern - 1;
- int len = (int)STRLEN(name_suffix);
+ char_u *colon = args->oe_xp->xp_pattern - 1;
+ int off = (int)(colon - args->oe_set_arg);
+
+ if (off < len)
+ return false;
+ // The name must follow a comma when it does not start the option value.
+ if (off > len && *(colon - len - 1) != ',')
+ return false;
- return colon - args->oe_set_arg >= len
- && STRNCMP(colon - len, name_suffix, len) == 0;
+ return STRNCMP(colon - len, name, len) == 0;
}
#endif
diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim
index b1201eb39..93d0e6144 100644
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -663,6 +663,12 @@ func Test_set_completion_string_values()
call feedkeys(":set completepopup=height:10,align:\<Tab>\<C-B>\"\<CR>", 'xt')
call assert_equal('"set completepopup=height:10,align:item', @:)
call assert_equal([], getcompletion('set completepopup=bogusname:', 'cmdline'))
+ " a name ending in a known sub-option name is not a sub-option
+ call assert_equal([], getcompletion('set completepopup=invalid_close:', 'cmdline'))
+ call assert_equal([], getcompletion('set completepopup=xborder:', 'cmdline'))
+ call assert_equal([], getcompletion('set completepopup=xhighlight:', 'cmdline'))
+ call assert_equal(['on', 'off'],
+ \ getcompletion('set completepopup=border:on,close:', 'cmdline'))
call assert_equal(['on', 'off'], getcompletion('set completepopup=close:', 'cmdline'))
call assert_equal(['on', 'off'], getcompletion('set completepopup=close:o', 'cmdline'))
call assert_equal(['off'], getcompletion('set previewpopup=close:of', 'cmdline'))
@@ -687,6 +693,8 @@ func Test_set_completion_string_values()
" diffopt: special handling of algorithm:<alg_list> and inline:<inline_type>
call assert_equal('filler', getcompletion('set diffopt+=', 'cmdline')[0])
call assert_equal([], getcompletion('set diffopt+=iblank,foldcolumn:', 'cmdline'))
+ call assert_equal([], getcompletion('set diffopt+=Xalgorithm:', 'cmdline'))
+ call assert_equal([], getcompletion('set diffopt+=iblank,Xinline:', 'cmdline'))
call assert_equal('patience', getcompletion('set diffopt+=iblank,algorithm:pat*', 'cmdline')[0])
call assert_equal('char', getcompletion('set diffopt+=iwhite,inline:ch*', 'cmdline')[0])
diff --git a/src/version.c b/src/version.c
index 55f755f23..59e9c7bb4 100644
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 886,
/**/
885,
/**/