[vim/vim] Fix, improve, and document Vim9 script EX_WHOLE and Ex command shortening issues (PR #20191)

28 views
Skip to first unread message

Peter Kenny

unread,
May 11, 2026, 6:04:07 AMMay 11
to vim/vim, Subscribed

This PR addresses many issues in the treatment of shortened commands. The initial aim was to address many missing instances of EX_WHOLE from commands in ex_cmds.h (including endclass, endenum, public, and others, which I raised as Issue 20032. The more I looked, the more there was to fix, improve or correct, including:

  • Additions to find_ex_command() in ex_docmd.c. The :horizontal minimum abbreviation is :hor, but :ho would be returned by fullcommand('ho'). :k is also fixed (it would return 'k' for any length, e.g., 'kaaaaargh').
  • Tests for the fixes in test_vimscript.vim and test_vim9_script.vim.
  • userfunc.c had enddef hardcoded, which meant E1065 errors for using endd or endde were outliers in that the "cannot be shortened" would not return the shortened, incorrect, command.
  • gen_syntax_vim.vim and vim.vim.base updates. There are too many things to list here, but a key overall improvement is treatment of legacy Vim script and Vim9 script for many commands is improved.
  • Syntax test files are extended and improved, including some things noted with "FIXME", which are fixed in the point above. New test files for shortened commands are added. This includes testing in .vim as well as .txt help files with Vim9 script code blocks. Lots of updated dumps.
  • Some corrections ('cont' is not the shortest form, for example) and fixing omissions related to shortened commands are made to six help files. There are some odd things included such as the different treatment of :dl between legacy Vim script and Vim9 script. A cross-reference to vim9-no-shorten is added in usr_20.txt, which is what sent me on this quest when documenting odd behaviours testing fullcommand() when updating Section 2 of vim9.txt (it can now be finished once these corrections are in place).
  • fullcommand() persisting limitations are flagged as TODO comments in the new tests.

There are some things that could be improved further, but I will create separate issues for those in future.

Testing (native Debian 13):
src: 7728 passing, 0 fail, and only 144 skipped n/a tests.
syntax: Test run on 2026 May 11 18:43:06, OK: 252, FAILED: 0: [], skipped: 0


You can view, comment on, or merge this pull request online at:

  https://github.com/vim/vim/pull/20191

Commit Summary

  • ae8fd16 Fix, improve, and document Vim9 script EX_WHOLE and Ex command shortening

File Changes

(191 files)

Patch Links:


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191@github.com>

h_east

unread,
May 11, 2026, 9:11:32 AMMay 11
to vim/vim, Subscribed

@h-east commented on this pull request.

Reviewed with the support of AI/LLM.


In runtime/syntax/generator/gen_syntax_vim.vim:

>  			\ v.name ==# 'horizontal' ?
 			\		extend(copy(v), {'omit_idx': 2, 'syn_str': 'hor[izontal]'}) :
 			\ v
 			\ })
 
+		" remove 'finally' - handle it separately in vim.vim.base 
⬇️ Suggested change
-		" remove 'finally' - handle it separately in vim.vim.base 
+		" remove 'finally' - handle it separately in vim.vim.base

In runtime/syntax/generator/vim.vim.base:

> @@ -246,9 +246,9 @@ syn match	vimNumber	'\<0z\%(\x\x\)\+\%(\.\%(\x\x\)\+\)*'	skipwhite nextgroup=@vi
 syn case match
 
 " All vimCommands are contained by vimIsCommand. {{{2
-syn cluster vimCmdList	contains=vimAbb,vimAddress,vimAt,vimAutocmd,vimAugroup,vimBehave,vimBreakadd,vimBreakdel,vimBreaklist,vimCall,vimCatch,vimCd,vimCommandModifier,vimConst,vimDoautocmd,vimDebug,vimDebuggreedy,vimDef,vimDefFold,vimDefer,vimDelcommand,vimDelFunction,vimDoCommand,@vimEcho,vimElse,vimEnddef,vimEndfunction,vimEndif,vimEval,vimExecute,vimIsCommand,vimExtCmd,vimExFilter,vimExMark,vimFiletype,vimFor,vimFunction,vimFunctionFold,vimGrep,vimGrepAdd,vimGlobal,vimHelp,vimHelpgrep,vimHighlight,vimHistory,vimImport,vimLanguage,vimLet,vimLoadkeymap,vimLockvar,vimMake,vimMap,vimMark,vimMatch,vimNotFunc,vimNormal,vimProfdel,vimProfile,vimPrompt,vimRedir,vimSet,vimSleep,vimSort,vimSyntax,vimSyntime,vimSynColor,vimSynLink,vimTerminal,vimThrow,vimUniq,vimUnlet,vimUnlockvar,vimUnmap,vimUserCmd,vimVimgrep,vimVimgrepadd,vimWincmd,vimMenu,vimMenutranslate,@vim9CmdList,@vimExUserCmdList,vimLua,vimMzScheme,vimPerl,vimPython,vimPython3,vimPythonX,vimRuby,vimTcl
-syn cluster vim9CmdList	contains=vim9Abstract,vim9Class,vim9Const,vim9Enum,vim9Export,vim9Final,vim9For,vim9Interface,vim9Type,vim9Var
-syn match vimCmdSep	"\\\@1<!|"	skipwhite nextgroup=@vimCmdList,vimSubst1,@vimFunc
+syn cluster vimCmdList	contains=vimAbb,vimAddress,vimAt,vimAutocmd,vimAugroup,vimBehave,vimBreakadd,vimBreakdel,vimBreaklist,vimCall,vimCatch,vimCd,vimCommandModifier,vimConst,vimDoautocmd,vimDebug,vimDebuggreedy,vimDef,vimDefFold,vimDefer,vimDelcommand,vimDelFunction,vimDoCommand,@vimEcho,vimEnddef,vimEndfunction,vimEval,vimExecute,vimIsCommand,vimExtCmd,vimExFilter,vimExMark,vimFiletype,vimFor,vimFunction,vimFunctionFold,vimGrep,vimGrepAdd,vimGlobal,vimHelp,vimHelpgrep,vimHighlight,vimHistory,vimImport,vimLanguage,vimLet,vimLoadkeymap,vimLockvar,vimMake,vimMap,vimMark,vimMatch,vimNormal,vimProfdel,vimProfile,vimPrompt,vimRedir,vimSet,vimSleep,vimSort,vimSyntax,vimSyntime,vimSynColor,vimSynLink,vimTerminal,vimThrow,vimUniq,vimUnlet,vimUnlockvar,vimUnmap,vimUserCmd,vimVimgrep,vimVimgrepadd,vimWincmd,vimMenu,vimMenutranslate,@vim9CmdList,@vimExUserCmdList,vimLua,vimMzScheme,vimPerl,vimPython,vimPython3,vimPythonX,vimRuby,vimTcl,vimWholeFlow9
+syn cluster vim9CmdList	contains=vim9Abstract,vim9Catch,vim9Class,vim9Const,vim9Enum,vim9Export,vim9Final,vim9For,vim9Import,vim9Interface,vim9Throw,vim9Type,vim9Var,vim9WholeFlow9
+syn match vimCmdSep	"\\\@1<!|"	skipwhite nextgroup=@vimCmdList,vimSubst1,@vimFunc,vimInsert

vimCmdSep is defined unconditionally and applies to both legacy and Vim9
script. In Vim9 the Insertions section (around L289) replaces
append/change/insert with Normal highlight on purpose. If a Vim9
buffer contains something like | append, however, vimCmdSep will now
hand control over to vimInsert, which starts a region linked to
vimString. It would be worth double-checking in a real Vim9 buffer
whether this still matches the intent, and if not, guarding the addition
under if !s:vim9script.


In runtime/syntax/generator/vim.vim.base:

> @@ -756,7 +779,7 @@ if s:vim9script
   " super must be followed by '.'
   syn match	vim9Super		contained	"\.\@1<!\<super\.\@="
 
-  VimFoldc syn region	vim9ClassBody	start="\<class\>" matchgroup=vimCommand end="\<endclass\>" contains=@vim9ClassBodyList transparent
+  VimFoldc syn region	vim9ClassBody	start="\v:?<class>" matchgroup=vimCommand end="\v<endclass>" contains=@vim9ClassBodyList transparent

start regex with :? but end without it

What is the intent behind allowing an optional leading colon only on the
start side? :endclass / :endenum / :endinterface are equally valid,
so the asymmetry feels surprising. Since these are transparent regions
the practical impact may be nil, but matching the two sides would be
easier to read and reason about.


In runtime/syntax/generator/vim.vim.base:

> +  syn match	vim9Type		"\<type\>"	skipwhite nextgroup=vim9TypeAlias,vim9TypeAliasError
+"  syn keyword	vim9Type	type		skipwhite nextgroup=vim9TypeAlias,vim9TypeAliasError

commented-out alternative for vim9Type

This is not a leftover of the deleted line - it is an alternative
implementation written with syn keyword. If the intent is to keep it as
a reference, please add a one-liner above it (e.g. " Alternative form using syn keyword:) so the next reader understands it is intentional.


In runtime/syntax/generator/vim.vim.base:

> @@ -809,7 +832,7 @@ if s:vim9script
         \ skipwhite skipempty nextgroup=vim9EnumImplementedInterfaceComment,vim9EnumValue
         \ contains=@vimCommentGroup,vimCommentString
 
-  VimFolde syn region	vim9EnumBody	start="\<enum\>" matchgroup=vimCommand end="\<endenum\>" contains=@vim9EnumBodyList transparent
+  VimFolde syn region	vim9EnumBody	start="\v:?<enum>" matchgroup=vimCommand end="\v<endenum>" contains=@vim9EnumBodyList transparent

Same above.


In runtime/syntax/generator/vim.vim.base:

> @@ -836,17 +859,40 @@ if s:vim9script
         \ nextgroup=vim9AbstractDefParams
         \ contains=vim9DefTypeParam
 
-  VimFoldi syn region	vim9InterfaceBody	start="\<interface\>" matchgroup=vimCommand end="\<endinterface\>" contains=@vim9InterfaceBodyList transparent
+  VimFoldi syn region	vim9InterfaceBody	start="\v:?<interface>" matchgroup=vimCommand end="\v<endinterface>" contains=@vim9InterfaceBodyList transparent

Same above.


In runtime/syntax/generator/vim.vim.base:

> @@ -1478,7 +1530,9 @@ syn region	vimHelpgrepPattern	contained
 
 " Vimgrep: {{{2
 " =======
-syn match	vimVimgrep		"\<l\=vim\%[grep]\>"	skipwhite nextgroup=vimVimgrepBang,vimVimgrepPattern
+syn match	vimVimgrep		"\<lv\%[imgrep]\>"	skipwhite nextgroup=vimVimgrepBang,vimVimgrepPattern
+syn match	vimVimgrep		"\<vimg\%[rep]\>"		skipwhite nextgroup=vimVimgrepBang,vimVimgrepPattern
+" syn match	vimVimgrep		"\<l\=vim\%[grep]\>"	skipwhite nextgroup=vimVimgrepBang,vimVimgrepPattern

Same above.


In runtime/syntax/generator/vim.vim.base:

> @@ -1467,7 +1517,9 @@ syn region	vimHelpArg	contained
 syn match	vimHelpNextCommand	contained	"\ze|[^|]"	skipwhite nextgroup=vimCmdSep
 syn match	vimHelpBang		contained	"\a\@1<=!"	skipwhite nextgroup=vimHelpArg,vimHelpNextCommand
 
-syn match	vimHelpgrep		"\<l\=helpg\%[rep]\>"	skipwhite nextgroup=vimHelpgrepBang,vimHelpgrepPattern
+syn match	vimHelpgrep		"\<lh\%[elpgrep]\>"	skipwhite nextgroup=vimHelpgrepBang,vimHelpgrepPattern
+syn match	vimHelpgrep		"\<helpg\%[rep]\>"	skipwhite nextgroup=vimHelpgrepBang,vimHelpgrepPattern
+" syn match	vimHelpgrep		"\<l\=helpg\%[rep]\>"	skipwhite nextgroup=vimHelpgrepBang,vimHelpgrepPattern

commented-out previous regex

These look like the regexes that were just removed, kept commented out.
Git history already preserves them, so removing the comments would keep
the file cleaner unless there is a specific reason to retain them.


In runtime/syntax/generator/vim.vim.base:

> +  syn match	vimAugroupName	contained	"\%(\\["|[:space:]]\|[^"|[:space:]]\)\+"
+      \                                                                 skipwhite nextgroup=vimCmdSep,vim9Comment
+  syn match	vimAugroupEnd	contained	"\c\<END\>"	skipwhite nextgroup=vimCmdSep,vim9Comment

Duplication between Vim9 and legacy vimAugroup*

The only difference between the two branches is the comment highlight name
(vim9Comment vs vimComment). Extracting that into a local variable
would let the two syn match calls live outside the if block. This is
a stylistic suggestion; the generator file may have its own conventions
that justify the current shape.


In runtime/syntax/generator/vim.vim.base:

>        \                                                                 skipwhite nextgroup=vimCmdSep,vimComment
-syn match	vimAugroupEnd	contained	"\c\<END\>"	skipwhite nextgroup=vimCmdSep,vimComment
+  syn match	vimAugroupEnd	contained	"\c\<END\>"	skipwhite nextgroup=vimCmdSep,vimComment

Same above.


In runtime/syntax/generator/vim.vim.base:

> -syn match	vimDef	"\<def\>"		skipwhite nextgroup=vimDefBang,vimDefName,vimFunctionPattern,vimCmdSep,vimComment
+syn match	vimDef	"\<def\>"		skipwhite nextgroup=vimDefBang,vimDefName,vimFunctionPattern,vimCmdSep,vim9Comment

vimDef nextgroup changed to vim9Comment

def is Vim9-only, so the change is reasonable. However, vimDef
itself is defined outside any s:vim9script guard. It would be worth
checking that a comment written after def in a legacy file (which is a
runtime error but still parsable) is still highlighted as intended.


In runtime/syntax/generator/vim.vim.base:

> +" :t		regular vim	co\%[py]
+" :let		Declarations	vimLet
+" :s? and s?? (:s flag variants)	two and three l	vimSubst
+"   Note these are valid but are different in Vim9 script:
+"   :sc (:scriptnames) :si (:simalt) :sr (:srewind)
+" :dp, :o[pen], :x[it] - no special Vim script handling
+if s:vim9script
+  " Normal is used for these because otherwise they will appear as errors when
+  " initially typing variables, e.g., '  t = true', '  sce = 9', etc.
+  syn match	Normal	"\%#=1\v%(%(^[:[:blank:]]*)|[[:blank:]]+[\x7c][:[:blank:]]*)%(%([[:digit:]]+%(,[[:digit:]]+)?)|[.$]|'[[:alpha:][\]<>'"^.(){}/&]|'[/][^/]*[/]|'[?][^?]*[?])?%(t|dp|k|let)>%([[:blank:]]+|$)"
+  syn match	Normal	"\v%(^[:[:blank:]]*|[[:blank:]]+[|][:[:blank:]]*)%(mod%[e]?|x%[it])\s*$"
+  syn match	Normal	"\%#=1\v%(%(^[:[:blank:]]*)|[[:blank:]]+[\x7c][:[:blank:]]*)%(%([[:digit:]]+%(,[[:digit:]]+)?)|[.$]|'[[:alpha:][\]<>'"^.(){}/&]|'[/][^/]*[/]|'[?][^?]*[?])?o%[pen]%([[:blank:]]+|$)"
+  " All substitute flag variants are invalid in Vim9 script except:
+  syn match	Normal	"\%#=1\v%(%(^[:[:blank:]]*)|[[:blank:]]+[\x7c][:[:blank:]]*)%(sce|scg|sci|scI|scl|scn|scp|sg|sgc|sge|sgi|sgI|sgl|sgn|sgp|sgr|sic|sie|siI|sin|sip|sir|sI|sIc|sIe|sIg|sIi|sIl|sIn|sIp|sIr|src|srg|sri|srI|srl|srn|srp)>[[:blank:]]*$"
+endif
+

The intent of hiding :t/:dp/:k/:let/:mod/:xit/:open and the
substitute flag variants under Normal highlight is described in the
section comment. The individual regexes are dense, though. Adding a
short example next to each (e.g. " matches: ' t = true', '| k = 1')
would make it considerably easier for a future maintainer to verify or
extend them.


In runtime/syntax/generator/vim.vim.base:

> +  " (All vimWholeFlow9 need to be keyword so that commands are matched
+  " correctly in a help filetype's >vim code block)
+  syn keyword	vimWholeFlow9	if	skipwhite nextgroup=@vimExprList,vimNotation
+  syn keyword	vimWholeFlow9	elsei[f]	skipwhite nextgroup=@vimExprList,vimNotation
+  syn keyword	vimWholeFlow9	retu[rn]	skipwhite nextgroup=@vimExprList,vimNotation
+  syn keyword	vimWholeFlow9	wh[ile]	skipwhite nextgroup=@vimExprList,vimNotation
+  syn keyword	vimWholeFlow9	brea[k]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	con[tinue]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	el[se]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	en[dif]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	endfo[r]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	endt[ry]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	endw[hile]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	fina[lly]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	fini[sh]	skipwhite nextgroup=vimComment
+endif

Naming of vim9WholeFlow9 / vimWholeFlow9

The trailing 9 appears twice in vim9WholeFlow9, and the legacy
counterpart vimWholeFlow9 also carries a 9. Which 9 corresponds to
Vim9 and which to EX_WHOLE is not obvious at a glance. A flatter name
such as vim9FlowKeyword / vimFlowKeyword would convey the role more
directly. Purely a naming preference.


In runtime/syntax/generator/vim.vim.base:

> +    syn match Normal	"^[: \t]*\(\d\+\(,\d\+\)\?\)\?a\%[ppend]\s*$"
+    syn match Normal	"^[: \t]*\(\d\+\(,\d\+\)\?\)\?c\%[hange]\s*$"
+    syn match Normal	"^[: \t]*\(\d\+\(,\d\+\)\?\)\?i\%[nsert]\s*$"

The leading ^[: \t]*\(\d\+\(,\d\+\)\?\)\? and trailing \s*$ are
identical in all three patterns; only the command token differs.
Merging them into a single rule with an alternation removes the
duplication and lets the regex engine evaluate the line once instead of
three times

⬇️ Suggested change
-    syn match Normal	"^[: \t]*\(\d\+\(,\d\+\)\?\)\?a\%[ppend]\s*$"
-    syn match Normal	"^[: \t]*\(\d\+\(,\d\+\)\?\)\?c\%[hange]\s*$"
-    syn match Normal	"^[: \t]*\(\d\+\(,\d\+\)\?\)\?i\%[nsert]\s*$"
+    syn match Normal	"^[: \t]*\(\d\+\(,\d\+\)\?\)\?\%(a\%[ppend]\|c\%[hange]\|i\%[nsert]\)\s*$"

I believe there are other places that could be improved in the same way. Please address them as well if possible.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4263242383@github.com>

dkearns

unread,
May 11, 2026, 9:36:56 AMMay 11
to vim/vim, Subscribed
dkearns left a comment (vim/vim#20191)

Thanks.

I haven't had a close look but I think the syntax file changes are going to be easier to merge after #19331, rather than vice versa. I'll try and finish that this week.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/c4421201763@github.com>

Peter Kenny

unread,
May 11, 2026, 4:34:31 PMMay 11
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -756,7 +779,7 @@ if s:vim9script
   " super must be followed by '.'
   syn match	vim9Super		contained	"\.\@1<!\<super\.\@="
 
-  VimFoldc syn region	vim9ClassBody	start="\<class\>" matchgroup=vimCommand end="\<endclass\>" contains=@vim9ClassBodyList transparent
+  VimFoldc syn region	vim9ClassBody	start="\v:?<class>" matchgroup=vimCommand end="\v<endclass>" contains=@vim9ClassBodyList transparent

The asymmetry reflects that :endenum gives an error, as shown below. So, allowing the leading : would be accommodating an invalid command, which is noted in syntax/testdir/input/vim9_ex_commands.vim. (The error returned may vary depending on the enum's content.) So, :endenum is removed from that test file and made a valid pair (:enum E/endenum).

image.png (view on web)

Ideally, :endenum would be Error highlighted too. However, with endenum needing to be in the end=, that's tricky, and may have trade offs between showing the error versus the enum never closing. Someone with more syntax file abilities than me may have a solution, though? @dkearns?


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4267196409@github.com>

Peter Kenny

unread,
May 11, 2026, 4:39:39 PMMay 11
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/gen_syntax_vim.vim:

>  			\ v.name ==# 'horizontal' ?
 			\		extend(copy(v), {'omit_idx': 2, 'syn_str': 'hor[izontal]'}) :
 			\ v
 			\ })
 
+		" remove 'finally' - handle it separately in vim.vim.base 

Thanks, noted, and fixed locally, so will be included in next push.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4267230078@github.com>

Peter Kenny

unread,
May 11, 2026, 5:38:47 PMMay 11
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> +  syn match	vim9Type		"\<type\>"	skipwhite nextgroup=vim9TypeAlias,vim9TypeAliasError
+"  syn keyword	vim9Type	type		skipwhite nextgroup=vim9TypeAlias,vim9TypeAliasError

I should have deleted the commented line 866. Using syn keyword vim9Type type was my initial attempt, but it can incorrectly highlight type() as vim9Type. Using syn match vim9Type "\<type\>" is the right way. Here is an example showing that, including synstack echoed for lines 5 and 7:

image.png (view on web)
I will delete line 866 in my next push.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4267594051@github.com>

Peter Kenny

unread,
May 12, 2026, 12:39:54 AMMay 12
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -756,7 +779,7 @@ if s:vim9script
   " super must be followed by '.'
   syn match	vim9Super		contained	"\.\@1<!\<super\.\@="
 
-  VimFoldc syn region	vim9ClassBody	start="\<class\>" matchgroup=vimCommand end="\<endclass\>" contains=@vim9ClassBodyList transparent
+  VimFoldc syn region	vim9ClassBody	start="\v:?<class>" matchgroup=vimCommand end="\v<endclass>" contains=@vim9ClassBodyList transparent

I'd meant to write about :class and endclass, not :enum and endenum. It's a similar result, except for the error given, though:
image.png (view on web)

And it's a similar story for :endinterface:
image.png (view on web)

Overall, :endclass, :endenum, and :endinterface are equally invalid.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4269182275@github.com>

Peter Kenny

unread,
May 12, 2026, 12:55:09 AMMay 12
to vim/vim, Subscribed
kennypete left a comment (vim/vim#20191)

Thanks.

I haven't had a close look but I think the syntax file changes are going to be easier to merge after #19331, rather than vice versa. I'll try and finish that this week.

No worries. I'm less invested in the syntax changes than the invalid shortenings, incorrect fullcommand() returns, versus the presentation, since it really did trip me up when doing a final review of vim9.txt's Section 2 given how many incorrect things are returned. That said, regarding .vim and .txt (help with >vim or >vim9) syntax, I think you'll find most of the things changed are improvements, including the FIXMEs - the only one I could not solve along the way was the very tricky : help.

I'll work through h-east's comments over the next day or two and re-push what makes sense to change - there are some good suggestions, like the repetitive stuff around append, change, and insert, even if some of those were pre-existing. Improving those in parallel, including the many missing marks that could precede those, could be done too.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/c4427447808@github.com>

Peter Kenny

unread,
May 12, 2026, 3:39:19 AMMay 12
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -809,7 +832,7 @@ if s:vim9script
         \ skipwhite skipempty nextgroup=vim9EnumImplementedInterfaceComment,vim9EnumValue
         \ contains=@vimCommentGroup,vimCommentString
 
-  VimFolde syn region	vim9EnumBody	start="\<enum\>" matchgroup=vimCommand end="\<endenum\>" contains=@vim9EnumBodyList transparent
+  VimFolde syn region	vim9EnumBody	start="\v:?<enum>" matchgroup=vimCommand end="\v<endenum>" contains=@vim9EnumBodyList transparent

Answered in #20191 (comment) above


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4270137295@github.com>

Peter Kenny

unread,
May 12, 2026, 3:40:07 AMMay 12
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -836,17 +859,40 @@ if s:vim9script
         \ nextgroup=vim9AbstractDefParams
         \ contains=vim9DefTypeParam
 
-  VimFoldi syn region	vim9InterfaceBody	start="\<interface\>" matchgroup=vimCommand end="\<endinterface\>" contains=@vim9InterfaceBodyList transparent
+  VimFoldi syn region	vim9InterfaceBody	start="\v:?<interface>" matchgroup=vimCommand end="\v<endinterface>" contains=@vim9InterfaceBodyList transparent

Answered in #20191 (comment) above


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4270143050@github.com>

Peter Kenny

unread,
May 12, 2026, 3:58:45 PMMay 12
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> -syn match	vimDef	"\<def\>"		skipwhite nextgroup=vimDefBang,vimDefName,vimFunctionPattern,vimCmdSep,vimComment
+syn match	vimDef	"\<def\>"		skipwhite nextgroup=vimDefBang,vimDefName,vimFunctionPattern,vimCmdSep,vim9Comment

There is no need for a s:vim9script guard because def is valid in both legacy Vim script and Vim9 script. Then, once it occurs, the scope is Vim9 script through to the end of the line containing enddef. A quotation mark " comment is invalid anywhere within the :def function, including the :def line itself, as the following LHS legacy Vim script shows. The RHS legacy Vim script shows the number sign # comments being used everywhere within, and including, the def/enddef lines.

image.png (view on web)

Conclusion: This is a valid correction.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4275718016@github.com>

Peter Kenny

unread,
May 13, 2026, 4:32:16 AMMay 13
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> +    syn match Normal	"^[: \t]*\(\d\+\(,\d\+\)\?\)\?a\%[ppend]\s*$"
+    syn match Normal	"^[: \t]*\(\d\+\(,\d\+\)\?\)\?c\%[hange]\s*$"
+    syn match Normal	"^[: \t]*\(\d\+\(,\d\+\)\?\)\?i\%[nsert]\s*$"

Yes, there are several of those, though pre-existing - I'll have a look, though don't want to go too far away from the shortening commands/Vim9 script differing commands scope, so may leave some/many if not applicable.

I've found better ways of addressing the a[ppend], c[hange], and i[nsert] at any rate - simpler too - so will comment on those once tested some more.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4279782438@github.com>

Peter Kenny

unread,
May 14, 2026, 3:48:14 AMMay 14
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> +" :t		regular vim	co\%[py]
+" :let		Declarations	vimLet
+" :s? and s?? (:s flag variants)	two and three l	vimSubst
+"   Note these are valid but are different in Vim9 script:
+"   :sc (:scriptnames) :si (:simalt) :sr (:srewind)
+" :dp, :o[pen], :x[it] - no special Vim script handling
+if s:vim9script
+  " Normal is used for these because otherwise they will appear as errors when
+  " initially typing variables, e.g., '  t = true', '  sce = 9', etc.
+  syn match	Normal	"\%#=1\v%(%(^[:[:blank:]]*)|[[:blank:]]+[\x7c][:[:blank:]]*)%(%([[:digit:]]+%(,[[:digit:]]+)?)|[.$]|'[[:alpha:][\]<>'"^.(){}/&]|'[/][^/]*[/]|'[?][^?]*[?])?%(t|dp|k|let)>%([[:blank:]]+|$)"
+  syn match	Normal	"\v%(^[:[:blank:]]*|[[:blank:]]+[|][:[:blank:]]*)%(mod%[e]?|x%[it])\s*$"
+  syn match	Normal	"\%#=1\v%(%(^[:[:blank:]]*)|[[:blank:]]+[\x7c][:[:blank:]]*)%(%([[:digit:]]+%(,[[:digit:]]+)?)|[.$]|'[[:alpha:][\]<>'"^.(){}/&]|'[/][^/]*[/]|'[?][^?]*[?])?o%[pen]%([[:blank:]]+|$)"
+  " All substitute flag variants are invalid in Vim9 script except:
+  syn match	Normal	"\%#=1\v%(%(^[:[:blank:]]*)|[[:blank:]]+[\x7c][:[:blank:]]*)%(sce|scg|sci|scI|scl|scn|scp|sg|sgc|sge|sgi|sgI|sgl|sgn|sgp|sgr|sic|sie|siI|sin|sip|sir|sI|sIc|sIe|sIg|sIi|sIl|sIn|sIp|sIr|src|srg|sri|srI|srl|srn|srp)>[[:blank:]]*$"
+endif
+

Yes, that was not great. Right aim, but overthought. I have rethought how to do these, and it will be much simpler. Still need to test them, though, so should be a day or two away.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4288241918@github.com>

Peter Kenny

unread,
May 14, 2026, 5:21:39 AMMay 14
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> +  " (All vimWholeFlow9 need to be keyword so that commands are matched
+  " correctly in a help filetype's >vim code block)
+  syn keyword	vimWholeFlow9	if	skipwhite nextgroup=@vimExprList,vimNotation
+  syn keyword	vimWholeFlow9	elsei[f]	skipwhite nextgroup=@vimExprList,vimNotation
+  syn keyword	vimWholeFlow9	retu[rn]	skipwhite nextgroup=@vimExprList,vimNotation
+  syn keyword	vimWholeFlow9	wh[ile]	skipwhite nextgroup=@vimExprList,vimNotation
+  syn keyword	vimWholeFlow9	brea[k]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	con[tinue]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	el[se]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	en[dif]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	endfo[r]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	endt[ry]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	endw[hile]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	fina[lly]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	fini[sh]	skipwhite nextgroup=vimComment
+endif

Yes, and I wasn't satisfied with it, though was trying to convey the whole-ness in both. Going with vim9FlowWhole and vimFlow w/o "keyword", but keeping the Whole on the Vim9 script one given that the key point regarding those.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4288797016@github.com>

Peter Kenny

unread,
May 14, 2026, 4:12:40 PMMay 14
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -246,9 +246,9 @@ syn match	vimNumber	'\<0z\%(\x\x\)\+\%(\.\%(\x\x\)\+\)*'	skipwhite nextgroup=@vi
 syn case match
 
 " All vimCommands are contained by vimIsCommand. {{{2
-syn cluster vimCmdList	contains=vimAbb,vimAddress,vimAt,vimAutocmd,vimAugroup,vimBehave,vimBreakadd,vimBreakdel,vimBreaklist,vimCall,vimCatch,vimCd,vimCommandModifier,vimConst,vimDoautocmd,vimDebug,vimDebuggreedy,vimDef,vimDefFold,vimDefer,vimDelcommand,vimDelFunction,vimDoCommand,@vimEcho,vimElse,vimEnddef,vimEndfunction,vimEndif,vimEval,vimExecute,vimIsCommand,vimExtCmd,vimExFilter,vimExMark,vimFiletype,vimFor,vimFunction,vimFunctionFold,vimGrep,vimGrepAdd,vimGlobal,vimHelp,vimHelpgrep,vimHighlight,vimHistory,vimImport,vimLanguage,vimLet,vimLoadkeymap,vimLockvar,vimMake,vimMap,vimMark,vimMatch,vimNotFunc,vimNormal,vimProfdel,vimProfile,vimPrompt,vimRedir,vimSet,vimSleep,vimSort,vimSyntax,vimSyntime,vimSynColor,vimSynLink,vimTerminal,vimThrow,vimUniq,vimUnlet,vimUnlockvar,vimUnmap,vimUserCmd,vimVimgrep,vimVimgrepadd,vimWincmd,vimMenu,vimMenutranslate,@vim9CmdList,@vimExUserCmdList,vimLua,vimMzScheme,vimPerl,vimPython,vimPython3,vimPythonX,vimRuby,vimTcl
-syn cluster vim9CmdList	contains=vim9Abstract,vim9Class,vim9Const,vim9Enum,vim9Export,vim9Final,vim9For,vim9Interface,vim9Type,vim9Var
-syn match vimCmdSep	"\\\@1<!|"	skipwhite nextgroup=@vimCmdList,vimSubst1,@vimFunc
+syn cluster vimCmdList	contains=vimAbb,vimAddress,vimAt,vimAutocmd,vimAugroup,vimBehave,vimBreakadd,vimBreakdel,vimBreaklist,vimCall,vimCatch,vimCd,vimCommandModifier,vimConst,vimDoautocmd,vimDebug,vimDebuggreedy,vimDef,vimDefFold,vimDefer,vimDelcommand,vimDelFunction,vimDoCommand,@vimEcho,vimEnddef,vimEndfunction,vimEval,vimExecute,vimIsCommand,vimExtCmd,vimExFilter,vimExMark,vimFiletype,vimFor,vimFunction,vimFunctionFold,vimGrep,vimGrepAdd,vimGlobal,vimHelp,vimHelpgrep,vimHighlight,vimHistory,vimImport,vimLanguage,vimLet,vimLoadkeymap,vimLockvar,vimMake,vimMap,vimMark,vimMatch,vimNormal,vimProfdel,vimProfile,vimPrompt,vimRedir,vimSet,vimSleep,vimSort,vimSyntax,vimSyntime,vimSynColor,vimSynLink,vimTerminal,vimThrow,vimUniq,vimUnlet,vimUnlockvar,vimUnmap,vimUserCmd,vimVimgrep,vimVimgrepadd,vimWincmd,vimMenu,vimMenutranslate,@vim9CmdList,@vimExUserCmdList,vimLua,vimMzScheme,vimPerl,vimPython,vimPython3,vimPythonX,vimRuby,vimTcl,vimWholeFlow9
+syn cluster vim9CmdList	contains=vim9Abstract,vim9Catch,vim9Class,vim9Const,vim9Enum,vim9Export,vim9Final,vim9For,vim9Import,vim9Interface,vim9Throw,vim9Type,vim9Var,vim9WholeFlow9
+syn match vimCmdSep	"\\\@1<!|"	skipwhite nextgroup=@vimCmdList,vimSubst1,@vimFunc,vimInsert

Yes, it was okay, though can be much better; other changes in the next PR (including not using Normal) address a few collateral things surrounding this attempt at handling those.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4293092101@github.com>

Peter Kenny

unread,
May 14, 2026, 4:31:30 PMMay 14
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -1478,7 +1530,9 @@ syn region	vimHelpgrepPattern	contained
 
 " Vimgrep: {{{2
 " ======-syn match	vimVimgrep		"\<l\=vim\%[grep]\>"	skipwhite nextgroup=vimVimgrepBang,vimVimgrepPattern
+syn match	vimVimgrep		"\<lv\%[imgrep]\>"	skipwhite nextgroup=vimVimgrepBang,vimVimgrepPattern
+syn match	vimVimgrep		"\<vimg\%[rep]\>"		skipwhite nextgroup=vimVimgrepBang,vimVimgrepPattern
+" syn match	vimVimgrep		"\<l\=vim\%[grep]\>"	skipwhite nextgroup=vimVimgrepBang,vimVimgrepPattern

These are better as keywords, and similarly for several others where there is no danger of striking ambiguous keywords/builtin functions - some of those are already noted specifically early on (examples: co[py] and sp[lit] due to the identical builtin function names). Ones like lv[imgrep] and vim[grep] should safely be made keywords. Others are lh[elpgrep], helpg[rep], lmak[e], mak[e], and several more, though I'll test those, of course, before PR2.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4293217692@github.com>

Peter Kenny

unread,
May 15, 2026, 12:58:20 AMMay 15
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> +  syn match	vimAugroupName	contained	"\%(\\["|[:space:]]\|[^"|[:space:]]\)\+"
+      \                                                                 skipwhite nextgroup=vimCmdSep,vim9Comment
+  syn match	vimAugroupEnd	contained	"\c\<END\>"	skipwhite nextgroup=vimCmdSep,vim9Comment

Good call out. I've fixed that to use the @vimScriptComment (renamed to avoid the ambiguity of vimComment and @vimComment). There were several places where I found some remedial work was needed such as lockvar/unlockvar and a Vim9 script comment, so not only will that point made be addressed, but the non-working ones for Vim9 script should be fixed too in the update.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4295492557@github.com>

Peter Kenny

unread,
May 15, 2026, 12:58:45 AMMay 15
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

>        \                                                                 skipwhite nextgroup=vimCmdSep,vimComment
-syn match	vimAugroupEnd	contained	"\c\<END\>"	skipwhite nextgroup=vimCmdSep,vimComment
+  syn match	vimAugroupEnd	contained	"\c\<END\>"	skipwhite nextgroup=vimCmdSep,vimComment

Same as last reply.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4295494000@github.com>

dkearns

unread,
May 15, 2026, 10:37:21 AMMay 15
to vim/vim, Subscribed

@dkearns commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -756,7 +779,7 @@ if s:vim9script
   " super must be followed by '.'
   syn match	vim9Super		contained	"\.\@1<!\<super\.\@="
 
-  VimFoldc syn region	vim9ClassBody	start="\<class\>" matchgroup=vimCommand end="\<endclass\>" contains=@vim9ClassBodyList transparent
+  VimFoldc syn region	vim9ClassBody	start="\v:?<class>" matchgroup=vimCommand end="\v<endclass>" contains=@vim9ClassBodyList transparent

I think the block terminating commands should allow the colon. I can't recall, off the top of my head, why they're parsed like that. :helpgrep :endclass returns several entries, including :help :endlclass which explicitly states "A class is defined between :class and :endclass".

As for error highlighting, my personal view is that it's almost never worth the trouble. I've never found it more useful than absent or incorrect highlighting and I've been annoyed by incorrect error highlighting for almost thirty years. It's so unreliable in so many syntax files that I never trust it anyway.

From an implementation perspective, it's often much more work to match what shouldn't be there and it's a real maintenance burden when refactoring. It's hard enough highlighting valid syntax. :)


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4298950510@github.com>

dkearns

unread,
May 15, 2026, 10:42:55 AMMay 15
to vim/vim, Subscribed

@dkearns commented on this pull request.


In src/ex_docmd.c:

> @@ -4025,6 +4025,19 @@ find_ex_command(
     if (eap->cmdidx == CMD_final && p - eap->cmd == 4 && !vim9)
 	eap->cmdidx = CMD_finally;
 
+    // ":hor" is documented as the minimum abbreviation of ":horizontal";
+    // ":ho" must not be recognized as ":horizontal".

Are we sure this isn't just an error in the initial implementation? Is there a reason not to allow :ho?

The initial commit is 21c3a80


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4299000366@github.com>

Peter Kenny

unread,
May 15, 2026, 3:31:02 PMMay 15
to vim/vim, Subscribed

@kennypete commented on this pull request.


In src/ex_docmd.c:

> @@ -4025,6 +4025,19 @@ find_ex_command(
     if (eap->cmdidx == CMD_final && p - eap->cmd == 4 && !vim9)
 	eap->cmdidx = CMD_finally;
 
+    // ":hor" is documented as the minimum abbreviation of ":horizontal";
+    // ":ho" must not be recognized as ":horizontal".

Honestly (or perhaps that could be, :ho[nestly]?), I mulled it, though didn't challenge it. It is an outlier in not taking its minimum non-conflicting abbreviation, though has been treated as :hor since inception, including in Revert the documentation for :horizontal #16362, where you'd have thought the question of whether it should have allowed :ho would have surfaced. Perhaps Bram had another ho-something command in the works that never materialised. Pure speculation, I guess, unless someone around the initial commit and/or discussions that were not covered in the later help reversion, etc., knows.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4300853575@github.com>

Peter Kenny

unread,
May 15, 2026, 3:47:14 PMMay 15
to vim/vim, Push

@kennypete pushed 1 commit.

  • 732a96b Fix, improve, and document Vim9 script EX_WHOLE and Ex command shortening


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/before/ae8fd168dc32144fc27d77666f0b08bdaa868cb4/after/732a96b3802da58009dd9ecc05387e9ae905bb67@github.com>

Peter Kenny

unread,
May 15, 2026, 4:54:04 PMMay 15
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -756,7 +779,7 @@ if s:vim9script
   " super must be followed by '.'
   syn match	vim9Super		contained	"\.\@1<!\<super\.\@="
 
-  VimFoldc syn region	vim9ClassBody	start="\<class\>" matchgroup=vimCommand end="\<endclass\>" contains=@vim9ClassBodyList transparent
+  VimFoldc syn region	vim9ClassBody	start="\v:?<class>" matchgroup=vimCommand end="\v<endclass>" contains=@vim9ClassBodyList transparent

"It's hard enough highlighting valid syntax. :)" - Fair enough - perhaps something to surface before returning to this later. I can't imagine much use of : with class/endclass, enum/endenum, or interface/endinterface in the wild.

The things that should be vim9NONE or similar are the :s shortened 2/3 character commands, :k with marks, and others. I'd fleshed those out before bailing on the syntax component of this PR, but will look at those again when your 19331 is merged. They may not need Error, but they should not appear to be valid commands in a Vim9 script scope.

Fwiw, before deciding to omit the syntax aspects of this PR, my next update would have limited vimIsCommand to exclude invalid Vim9 script one-character commands. The equivalent in your draft #19331 would be making vimIsCommand this (noting 19331 currently uses \a*, which would not match user commands with digits in them nor Vim commands like :py3):

if s:vim9script
  " Vim9 script vimIsCommand:
  " - :a, :c, :i, :k, :o, :t, and :x are invalid commands in Vim9 script.
  "   This is arbitrary.  They all give:
  "     E1100: Command not supported in Vim9 script (missing :var?): {char}
  "   Applying the same reasoning, :b, :d, :e, etc., could have been
  "   prohibited too, but they remain valid solo lowercase character commands
  "   in Vim9 script.
  " - Any solo uppercase character can be a user-defined command (even :P,
  "   which can be a user-defined command)
  " - Any word character can follow
  syn match vimIsCommand "<Bar>\s*[bdefghjlmnpqrsuvwyzA-Z][[:alnum:]]*" transparent contains=vimCommand,vimNotation
else
  syn match vimIsCommand "<Bar>\s*[[:alpha:]][[:alnum:]]*" transparent contains=vimCommand,vimNotation
endif


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4301318804@github.com>

Peter Kenny

unread,
May 17, 2026, 3:36:27 AMMay 17
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -756,7 +779,7 @@ if s:vim9script
   " super must be followed by '.'
   syn match	vim9Super		contained	"\.\@1<!\<super\.\@="
 
-  VimFoldc syn region	vim9ClassBody	start="\<class\>" matchgroup=vimCommand end="\<endclass\>" contains=@vim9ClassBodyList transparent
+  VimFoldc syn region	vim9ClassBody	start="\v:?<class>" matchgroup=vimCommand end="\v<endclass>" contains=@vim9ClassBodyList transparent

I think the block terminating commands should allow the colon. I can't recall, off the top of my head, why they're parsed like that. :helpgrep :endclass returns several entries, including :help :endlclass which explicitly states "A class is defined between :class and :endclass".

I suppose it comes down to whether it's agreed that it's an inconsistency/oversight that should be corrected or whether to keep it as-is. If the latter, not allowing :endclass, :endinterface, and :endenum in syntax is the right way.

It does feel like an unintentional limitation because, not only the tags, but also the commentary in vim9class.c suggests the : should be permitted:

 * Handle ":class" and ":abstract class" up to ":endclass".
 * Handle ":enum" up to ":endenum".
 * Handle ":interface" up to ":endinterface".

And it looks like a 2-line fix would resolve it in vim9class.c, which does not account for the :. A very discrete PR and easy to test.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4304989785@github.com>

dkearns

unread,
May 17, 2026, 11:24:24 AMMay 17
to vim/vim, Subscribed

@dkearns commented on this pull request.


In runtime/doc/change.txt:

> @@ -87,6 +88,8 @@ For inserting text see |insert.txt|.
 			   :delep	idem
 			   :deletp	idem
 			   :deletep	idem
+			Warning: These weird abbreviations give |E492| in
+			|Vim9| script, except `:dl`, which executes as `:dlist`.

This is a bit of a mess, it would be better if :dli was required in Vim9 script as well. I'm willing to bet that the abbreviated form is is not used in the wild in a Vim9 script, but I've lost these bets before.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4305647553@github.com>

Peter Kenny

unread,
May 17, 2026, 2:45:38 PMMay 17
to vim/vim, Push

@kennypete pushed 1 commit.

  • 3e6afe1 Fix, improve, and document Vim9 script EX_WHOLE and Ex command shortening


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/before/732a96b3802da58009dd9ecc05387e9ae905bb67/after/3e6afe18e84fe590d1d194ce5bef654ce809590a@github.com>

Peter Kenny

unread,
May 20, 2026, 3:51:21 AM (12 days ago) May 20
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -756,7 +779,7 @@ if s:vim9script
   " super must be followed by '.'
   syn match	vim9Super		contained	"\.\@1<!\<super\.\@="
 
-  VimFoldc syn region	vim9ClassBody	start="\<class\>" matchgroup=vimCommand end="\<endclass\>" contains=@vim9ClassBodyList transparent
+  VimFoldc syn region	vim9ClassBody	start="\v:?<class>" matchgroup=vimCommand end="\v<endclass>" contains=@vim9ClassBodyList transparent

#20253 fixes the issue of these three giving errors.


Reply to this email directly, view it on GitHub, or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4326275270@github.com>

Peter Kenny

unread,
May 20, 2026, 3:52:54 AM (12 days ago) May 20
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> +  " (All vimWholeFlow9 need to be keyword so that commands are matched
+  " correctly in a help filetype's >vim code block)
+  syn keyword	vimWholeFlow9	if	skipwhite nextgroup=@vimExprList,vimNotation
+  syn keyword	vimWholeFlow9	elsei[f]	skipwhite nextgroup=@vimExprList,vimNotation
+  syn keyword	vimWholeFlow9	retu[rn]	skipwhite nextgroup=@vimExprList,vimNotation
+  syn keyword	vimWholeFlow9	wh[ile]	skipwhite nextgroup=@vimExprList,vimNotation
+  syn keyword	vimWholeFlow9	brea[k]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	con[tinue]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	el[se]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	en[dif]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	endfo[r]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	endt[ry]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	endw[hile]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	fina[lly]	skipwhite nextgroup=vimComment
+  syn keyword	vimWholeFlow9	fini[sh]	skipwhite nextgroup=vimComment
+endif

Syntax is now out of scope of this PR, so I'm resolving this convo.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4326286695@github.com>

Peter Kenny

unread,
May 20, 2026, 3:53:12 AM (12 days ago) May 20
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> +    syn match Normal	"^[: \t]*\(\d\+\(,\d\+\)\?\)\?a\%[ppend]\s*$"
+    syn match Normal	"^[: \t]*\(\d\+\(,\d\+\)\?\)\?c\%[hange]\s*$"
+    syn match Normal	"^[: \t]*\(\d\+\(,\d\+\)\?\)\?i\%[nsert]\s*$"

Syntax is now out of scope of this PR, so I'm resolving this convo.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4326289558@github.com>

Peter Kenny

unread,
May 20, 2026, 3:53:43 AM (12 days ago) May 20
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> +" :t		regular vim	co\%[py]
+" :let		Declarations	vimLet
+" :s? and s?? (:s flag variants)	two and three l	vimSubst
+"   Note these are valid but are different in Vim9 script:
+"   :sc (:scriptnames) :si (:simalt) :sr (:srewind)
+" :dp, :o[pen], :x[it] - no special Vim script handling
+if s:vim9script
+  " Normal is used for these because otherwise they will appear as errors when
+  " initially typing variables, e.g., '  t = true', '  sce = 9', etc.
+  syn match	Normal	"\%#=1\v%(%(^[:[:blank:]]*)|[[:blank:]]+[\x7c][:[:blank:]]*)%(%([[:digit:]]+%(,[[:digit:]]+)?)|[.$]|'[[:alpha:][\]<>'"^.(){}/&]|'[/][^/]*[/]|'[?][^?]*[?])?%(t|dp|k|let)>%([[:blank:]]+|$)"
+  syn match	Normal	"\v%(^[:[:blank:]]*|[[:blank:]]+[|][:[:blank:]]*)%(mod%[e]?|x%[it])\s*$"
+  syn match	Normal	"\%#=1\v%(%(^[:[:blank:]]*)|[[:blank:]]+[\x7c][:[:blank:]]*)%(%([[:digit:]]+%(,[[:digit:]]+)?)|[.$]|'[[:alpha:][\]<>'"^.(){}/&]|'[/][^/]*[/]|'[?][^?]*[?])?o%[pen]%([[:blank:]]+|$)"
+  " All substitute flag variants are invalid in Vim9 script except:
+  syn match	Normal	"\%#=1\v%(%(^[:[:blank:]]*)|[[:blank:]]+[\x7c][:[:blank:]]*)%(sce|scg|sci|scI|scl|scn|scp|sg|sgc|sge|sgi|sgI|sgl|sgn|sgp|sgr|sic|sie|siI|sin|sip|sir|sI|sIc|sIe|sIg|sIi|sIl|sIn|sIp|sIr|src|srg|sri|srI|srl|srn|srp)>[[:blank:]]*$"
+endif
+

Syntax is now out of scope of this PR, so I'm resolving this convo.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4326291864@github.com>

Peter Kenny

unread,
May 20, 2026, 3:54:27 AM (12 days ago) May 20
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

>        \                                                                 skipwhite nextgroup=vimCmdSep,vimComment
-syn match	vimAugroupEnd	contained	"\c\<END\>"	skipwhite nextgroup=vimCmdSep,vimComment
+  syn match	vimAugroupEnd	contained	"\c\<END\>"	skipwhite nextgroup=vimCmdSep,vimComment

Syntax is now out of scope of this PR, so I'm resolving this convo.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4326297390@github.com>

Peter Kenny

unread,
May 20, 2026, 3:54:47 AM (12 days ago) May 20
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> +  syn match	vimAugroupName	contained	"\%(\\["|[:space:]]\|[^"|[:space:]]\)\+"
+      \                                                                 skipwhite nextgroup=vimCmdSep,vim9Comment
+  syn match	vimAugroupEnd	contained	"\c\<END\>"	skipwhite nextgroup=vimCmdSep,vim9Comment

Syntax is now out of scope of this PR, so I'm resolving this convo.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4326300388@github.com>

Peter Kenny

unread,
May 20, 2026, 3:55:00 AM (12 days ago) May 20
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -1467,7 +1517,9 @@ syn region	vimHelpArg	contained
 syn match	vimHelpNextCommand	contained	"\ze|[^|]"	skipwhite nextgroup=vimCmdSep
 syn match	vimHelpBang		contained	"\a\@1<=!"	skipwhite nextgroup=vimHelpArg,vimHelpNextCommand
 
-syn match	vimHelpgrep		"\<l\=helpg\%[rep]\>"	skipwhite nextgroup=vimHelpgrepBang,vimHelpgrepPattern
+syn match	vimHelpgrep		"\<lh\%[elpgrep]\>"	skipwhite nextgroup=vimHelpgrepBang,vimHelpgrepPattern
+syn match	vimHelpgrep		"\<helpg\%[rep]\>"	skipwhite nextgroup=vimHelpgrepBang,vimHelpgrepPattern
+" syn match	vimHelpgrep		"\<l\=helpg\%[rep]\>"	skipwhite nextgroup=vimHelpgrepBang,vimHelpgrepPattern

Syntax is now out of scope of this PR, so I'm resolving this convo.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4326302489@github.com>

Peter Kenny

unread,
May 20, 2026, 3:55:14 AM (12 days ago) May 20
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -1478,7 +1530,9 @@ syn region	vimHelpgrepPattern	contained
 
 " Vimgrep: {{{2
 " ======-syn match	vimVimgrep		"\<l\=vim\%[grep]\>"	skipwhite nextgroup=vimVimgrepBang,vimVimgrepPattern
+syn match	vimVimgrep		"\<lv\%[imgrep]\>"	skipwhite nextgroup=vimVimgrepBang,vimVimgrepPattern
+syn match	vimVimgrep		"\<vimg\%[rep]\>"		skipwhite nextgroup=vimVimgrepBang,vimVimgrepPattern
+" syn match	vimVimgrep		"\<l\=vim\%[grep]\>"	skipwhite nextgroup=vimVimgrepBang,vimVimgrepPattern

Syntax is now out of scope of this PR, so I'm resolving this convo.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4326306121@github.com>

Peter Kenny

unread,
May 20, 2026, 3:55:37 AM (12 days ago) May 20
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -836,17 +859,40 @@ if s:vim9script
         \ nextgroup=vim9AbstractDefParams
         \ contains=vim9DefTypeParam
 
-  VimFoldi syn region	vim9InterfaceBody	start="\<interface\>" matchgroup=vimCommand end="\<endinterface\>" contains=@vim9InterfaceBodyList transparent
+  VimFoldi syn region	vim9InterfaceBody	start="\v:?<interface>" matchgroup=vimCommand end="\v<endinterface>" contains=@vim9InterfaceBodyList transparent

Syntax is now out of scope of this PR, so I'm resolving this convo.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4326308717@github.com>

Peter Kenny

unread,
May 20, 2026, 3:55:42 AM (12 days ago) May 20
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> @@ -809,7 +832,7 @@ if s:vim9script
         \ skipwhite skipempty nextgroup=vim9EnumImplementedInterfaceComment,vim9EnumValue
         \ contains=@vimCommentGroup,vimCommentString
 
-  VimFolde syn region	vim9EnumBody	start="\<enum\>" matchgroup=vimCommand end="\<endenum\>" contains=@vim9EnumBodyList transparent
+  VimFolde syn region	vim9EnumBody	start="\v:?<enum>" matchgroup=vimCommand end="\v<endenum>" contains=@vim9EnumBodyList transparent

Syntax is now out of scope of this PR, so I'm resolving this convo.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4326310302@github.com>

Peter Kenny

unread,
May 20, 2026, 3:55:51 AM (12 days ago) May 20
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/syntax/generator/vim.vim.base:

> +  syn match	vim9Type		"\<type\>"	skipwhite nextgroup=vim9TypeAlias,vim9TypeAliasError
+"  syn keyword	vim9Type	type		skipwhite nextgroup=vim9TypeAlias,vim9TypeAliasError

Syntax is now out of scope of this PR, so I'm resolving this convo.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4326312559@github.com>

Peter Kenny

unread,
May 20, 2026, 4:09:33 AM (12 days ago) May 20
to vim/vim, Subscribed

@kennypete commented on this pull request.


In runtime/doc/change.txt:

> @@ -87,6 +88,8 @@ For inserting text see |insert.txt|.
 			   :delep	idem
 			   :deletp	idem
 			   :deletep	idem
+			Warning: These weird abbreviations give |E492| in
+			|Vim9| script, except `:dl`, which executes as `:dlist`.

It's a bit of a mess because of :dl not applying to both, and requiring :dli feels arbitrary, just like how :hor is currently required for :horizontal (even though there is no :ho occupied by any other command since it's the only one starting with 'ho').

Other than ensuring that documentation correctly explains things, which is what this change to change.txt would do, they're probably of niche interest anyway, so may be best left as-is, documented as they work, and move on?


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4326429847@github.com>

dkearns

unread,
May 20, 2026, 1:56:36 PM (12 days ago) May 20
to vim/vim, Subscribed

@dkearns commented on this pull request.


In src/ex_docmd.c:

> @@ -4025,6 +4025,19 @@ find_ex_command(
     if (eap->cmdidx == CMD_final && p - eap->cmd == 4 && !vim9)
 	eap->cmdidx = CMD_finally;
 
+    // ":hor" is documented as the minimum abbreviation of ":horizontal";
+    // ":ho" must not be recognized as ":horizontal".
+    if (eap->cmdidx == CMD_horizontal && p - eap->cmd == 2)
+	eap->cmdidx = CMD_SIZE;
+
+    // ":k{x}" takes exactly one mark character; reject it if more than one
+    // non-whitespace character immediately follows "k".
+    if (eap->cmdidx == CMD_k && STRLEN(eap->cmd) > 3)
+	eap->cmdidx = CMD_SIZE;
+
+    if (eap->cmdidx == CMD_k && STRLEN(eap->cmd) == 3 && eap->cmd[1] != ' ')
+	eap->cmdidx = CMD_SIZE;
+

:k supports both tail comments and trailing bar which this change will break.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4330883986@github.com>

Peter Kenny

unread,
May 20, 2026, 5:35:15 PM (12 days ago) May 20
to vim/vim, Subscribed

@kennypete commented on this pull request.


In src/ex_docmd.c:

> @@ -4025,6 +4025,19 @@ find_ex_command(
     if (eap->cmdidx == CMD_final && p - eap->cmd == 4 && !vim9)
 	eap->cmdidx = CMD_finally;
 
+    // ":hor" is documented as the minimum abbreviation of ":horizontal";
+    // ":ho" must not be recognized as ":horizontal".
+    if (eap->cmdidx == CMD_horizontal && p - eap->cmd == 2)
+	eap->cmdidx = CMD_SIZE;
+
+    // ":k{x}" takes exactly one mark character; reject it if more than one
+    // non-whitespace character immediately follows "k".
+    if (eap->cmdidx == CMD_k && STRLEN(eap->cmd) > 3)
+	eap->cmdidx = CMD_SIZE;
+
+    if (eap->cmdidx == CMD_k && STRLEN(eap->cmd) == 3 && eap->cmd[1] != ' ')
+	eap->cmdidx = CMD_SIZE;
+

Yes, right. I was too focused on fullcommand(). I've revised that and will push an update to it shortly (which works with those, and will add more tests for the variants with/without :, whitespace optionality before/after the k, trailing | or legacy Vim script " comments, etc.).


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4332416038@github.com>

dkearns

unread,
May 21, 2026, 6:15:33 AM (11 days ago) May 21
to vim/vim, Subscribed
dkearns left a comment (vim/vim#20191)

:k is also fixed (it would return 'k' for any length, e.g., 'kaaaaargh').

I'm not convinced what is being fixed here is a bug. While :k is unusual it seems reasonable to me that :mark kaaaaargh and :kaaaaargh should behave in the same way, as they currently do.

:mark aaaaargh
echo fullcommand('mark aaaaargh')
:kaaaaargh
echo fullcommand('kaaaaargh')

produces the following

Error detected while processing /tmp/k.vim:
line    1:
E488: Trailing characters: aaaaargh
mark
line    3:
E488: Trailing characters: aaaaargh
k

The command identification function isn't intended to perform any argument validation, at least generally.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/c4507102930@github.com>

Peter Kenny

unread,
May 21, 2026, 5:13:10 PM (11 days ago) May 21
to vim/vim, Subscribed
kennypete left a comment (vim/vim#20191)

:k is also fixed (it would return 'k' for any length, e.g., 'kaaaaargh').

I'm not convinced what is being fixed here is a bug. While :k is unusual it seems reasonable to me that :mark aaaaargh and :kaaaaargh should behave in the same way, as they currently do.
...

The command identification function isn't intended to perform any argument validation, at least generally.

Generally, sure, it's not required because, using :mark as the example, it requires whitespace after mark. :markaaaaargh is the applicable comparison, which does return '' whereas today :kaaaaargh returns k, which is nonsense, and is what needs fixing.

:call append('$', '" ' .. fullcommand(':mark aaaaargh'))
:call append('$', '" ' .. fullcommand(':markaaaaargh'))
:call append('$', '" ' .. fullcommand(':k aaaaargh'))
:call append('$', '" ' .. fullcommand(':kaaaaargh'))
:call append('$', '" ' .. v:versionlong)
"----------------------------------------------------
" ':kaaaaargh' returns a command for an invalid command:

" mark
" 
" k
" k
" 9020500


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/c4512811193@github.com>

Peter Kenny

unread,
May 21, 2026, 11:35:46 PM (11 days ago) May 21
to vim/vim, Push

@kennypete pushed 1 commit.

  • 36dd37a Fix, improve, and document Vim9 script EX_WHOLE and Ex command shortening


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/before/3e6afe18e84fe590d1d194ce5bef654ce809590a/after/36dd37a95dd1395f5d7a5caf32bc3c68a0892c7b@github.com>

Peter Kenny

unread,
May 21, 2026, 11:42:42 PM (11 days ago) May 21
to vim/vim, Subscribed

@kennypete commented on this pull request.


In src/ex_docmd.c:

> @@ -4025,6 +4025,19 @@ find_ex_command(
     if (eap->cmdidx == CMD_final && p - eap->cmd == 4 && !vim9)
 	eap->cmdidx = CMD_finally;
 
+    // ":hor" is documented as the minimum abbreviation of ":horizontal";
+    // ":ho" must not be recognized as ":horizontal".
+    if (eap->cmdidx == CMD_horizontal && p - eap->cmd == 2)
+	eap->cmdidx = CMD_SIZE;
+
+    // ":k{x}" takes exactly one mark character; reject it if more than one
+    // non-whitespace character immediately follows "k".
+    if (eap->cmdidx == CMD_k && STRLEN(eap->cmd) > 3)
+	eap->cmdidx = CMD_SIZE;
+
+    if (eap->cmdidx == CMD_k && STRLEN(eap->cmd) == 3 && eap->cmd[1] != ' ')
+	eap->cmdidx = CMD_SIZE;
+

The fix to cover this is added now. :k really is an oddity with k-mark and k-whitespace-mark being acceptable. Tests are extended too, and are all clear.


Reply to this email directly, view it on GitHub, or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4342283983@github.com>

dkearns

unread,
May 22, 2026, 1:22:21 PM (10 days ago) May 22
to vim/vim, Subscribed

@dkearns commented on this pull request.


In src/testdir/test_vimscript.vim:

> +" Test using fullcommand() {{{1
+func Test_builtin_fullcommand()
+  " :hor is the minimum abbreviation of :horizontal; :ho is not valid
+  call assert_equal('', fullcommand('ho', v:false))
+  call assert_equal('horizontal', fullcommand('hor', v:false))
+  " :k may take at most one mark {a-zA-Z'} argument, with optional whitespace
+  call assert_equal('k', fullcommand('k', v:false))
+  call assert_equal('k', fullcommand('kz', v:false))
+  call assert_equal('k', fullcommand('k z', v:false))
+  call assert_equal('k', fullcommand('   : 	  k    z', v:false))
+  call assert_equal('k', fullcommand(':3k z | echo "k"', v:false))
+  call assert_equal('k', fullcommand(":k'" .. ' " legacy comment', v:false))
+  call assert_equal('k', fullcommand(':ka"legacy_comment_no_space', v:false))
+  call assert_equal('k', fullcommand(":ka|echo 'bar no space'", v:false))
+  call assert_equal('', fullcommand('kzz', v:false))
+  call assert_equal('', fullcommand('kz7', v:false))

This one is debatable but there are the :py[23] exceptions.


In src/testdir/test_vimscript.vim:

> +  call assert_equal('', fullcommand('k"', v:false))
+  call assert_equal('', fullcommand('k^', v:false))

These should return :k like :mark" and :mark^. The missing and invalid arguments aren't relevant to the command identification.


In src/ex_docmd.c:

> +	    if (!ASCII_ISALPHA(c) && c != '\'')
+	        eap->cmdidx = CMD_SIZE;

This is doing argument validation which is the job of the actual command action. :mark and :k should execute identically, ignoring the single exception at hand.


In src/ex_docmd.c:

> +		char_u after_k = k_pos[1];
+		if (after_k != NUL && vim_strchr((char_u *)"|\" \t", after_k) == NULL)
+		    eap->cmdidx = CMD_SIZE;

Sorry if my earlier comment implied this was necessary, it's not. We've already determined the command, the rest of the line is irrelevant. I was just pointing out the valid commands that were also being broken.


In src/ex_docmd.c:

> @@ -4025,6 +4025,19 @@ find_ex_command(
     if (eap->cmdidx == CMD_final && p - eap->cmd == 4 && !vim9)
 	eap->cmdidx = CMD_finally;
 
+    // ":hor" is documented as the minimum abbreviation of ":horizontal";
+    // ":ho" must not be recognized as ":horizontal".
+    if (eap->cmdidx == CMD_horizontal && p - eap->cmd == 2)
+	eap->cmdidx = CMD_SIZE;
+
+    // ":k{x}" takes exactly one mark character; reject it if more than one
+    // non-whitespace character immediately follows "k".
+    if (eap->cmdidx == CMD_k && STRLEN(eap->cmd) > 3)
+	eap->cmdidx = CMD_SIZE;
+
+    if (eap->cmdidx == CMD_k && STRLEN(eap->cmd) == 3 && eap->cmd[1] != ' ')
+	eap->cmdidx = CMD_SIZE;
+

Rather than assigning CMD_k and then reversing that decision if it smells funny, it would be better to only assign it correctly the first time.

I think the following (untested, I can't build at the moment) would be sufficient?

diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index c30c898d7..1cf0b01e1 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -3609,8 +3609,7 @@ one_letter_cmd(char_u *p, cmdidx_T *idx)
 {
     if (in_vim9script())
 	return FALSE;
-    if (p[0] == 'k'
-	    && (p[1] != 'e' || (p[1] == 'e' && p[2] != 'e')))
+    if (p[0] == 'k' && !(ASCII_ISLOWER(p[1]) && ASCII_ISLOWER(p[2])))
     {
 	*idx = CMD_k;
 	return TRUE;

Using ASCII_ISALNUM if we want to generalise the py[23] style command exception.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4347330423@github.com>

dkearns

unread,
May 22, 2026, 1:23:42 PM (10 days ago) May 22
to vim/vim, Subscribed
dkearns left a comment (vim/vim#20191)

I was intending :mark as a stand in for :k as well. So to reiterate, I don't think it's unreasonable to expect :kfoobar to behave identically to :k foobar with the :keep* command as exceptions.

The command tail boundary in legacy script is usually the last alpha (or alnum character if you include :py and friends), so :mark< works. However, the tail boundary for :k is the single character "k", again with the keep* exceptions.

The reason I'm questioning this is because we don't really get to make up the rules here. POSIX says: "Commands that consist of the character 'k', followed by a character that can be used as the name of a mark, shall be equivalent to the mark command followed by a <blank>, followed by the character that followed the 'k'." and "Historically, the k command could be followed by the mark name without intervening characters. POSIX.1-2024 requires conformance to historical practice."

I can read that either way. Testing with historical Vi produces:

:k foobar
Extra characters at end of "k" command
:kfoobar
Extra characters at end of "k" command

That version does not, of course, have multiple commands starting with "k". But again I'm not sure that changes anything from the compliance angle.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/c4521118714@github.com>

Peter Kenny

unread,
May 23, 2026, 7:46:31 PM (9 days ago) May 23
to vim/vim, Subscribed
kennypete left a comment (vim/vim#20191)

I was intending :mark as a stand in for :k as well. So to reiterate, I don't think it's unreasonable to expect :kfoobar to behave identically to :k foobar with the :keep* command as exceptions.

The command tail boundary in legacy script is usually the last alpha (or alnum character if you include :py and friends), so :mark< works. However, the tail boundary for :k is the single character "k", again with the keep* exceptions.

The reason I'm questioning this is because we don't really get to make up the rules here. [POSIX says] [...]

I think the problem here is my (incorrect) presumption that fullcommand(), as documented currently, can be interpreted as implying that the command passed to it will not return a full command if the command passed to it is invalid (when used in a script literally). :kfoobar is one such instance. There are loads in the :s 2- and 3- letter commands where you can pass nonsense but still get back substitute, e.g., :sIrmyword, as shown in the script, below.

So, I think what's best here is to ditch my :k addition entirely, but add something like this to builtin.txt's help under fullcommand():

	Note: Command validation is not performed.  Results depend on Vim's
	internal command-specific identification rules.  Examples:
>vim9
	vim9script
	def FullCmd(cmd: string): string
	  if fullcommand(cmd, false) == ''
	    return $"NOT a command: :{cmd}"
	  else
	    return $":{fullcommand(cmd, false)}"
	  endif
	enddef
	echo FullCmd('mark myword')	# :mark
	echo FullCmd('markmyword')	# NOT a command: :markmyword
	echo FullCmd('kw " is k')	# :k
	echo FullCmd('kmyword " is k')	# :k
	echo FullCmd('sIrmyword')	# :substitute
	echo FullCmd('sIr | myword')	# :substitute
	echo FullCmd('echo_myword')	# :echo
	echo FullCmd('echo1myword')	# :echo
	echo FullCmd('echomyword')	# NOT a command: :echomyword
	echo FullCmd('.,$g/myword')	# :global
	echo FullCmd('.,$g"myword')	# :global
	echo FullCmd('.,$g😊️myword')	# :global
<
image.png (view on web)

At least then it would be clearer that apparently invalid commands may return a valid full command.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/c4526832844@github.com>

Peter Kenny

unread,
May 23, 2026, 7:48:58 PM (9 days ago) May 23
to vim/vim, Subscribed

@kennypete commented on this pull request.


In src/ex_docmd.c:

> +		char_u after_k = k_pos[1];
+		if (after_k != NUL && vim_strchr((char_u *)"|\" \t", after_k) == NULL)
+		    eap->cmdidx = CMD_SIZE;

No longer an issue once I remove k handling per #20191 (comment)


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4351557086@github.com>

Peter Kenny

unread,
May 23, 2026, 7:49:14 PM (9 days ago) May 23
to vim/vim, Subscribed

@kennypete commented on this pull request.


In src/ex_docmd.c:

> +	    if (!ASCII_ISALPHA(c) && c != '\'')
+	        eap->cmdidx = CMD_SIZE;

No longer an issue once I remove k handling per #20191 (comment)


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4351557266@github.com>

Peter Kenny

unread,
May 23, 2026, 7:51:13 PM (9 days ago) May 23
to vim/vim, Subscribed

@kennypete commented on this pull request.


In src/testdir/test_vimscript.vim:

> +  call assert_equal('', fullcommand('k"', v:false))
+  call assert_equal('', fullcommand('k^', v:false))

No longer an issue once I remove k handling per #20191 (comment) though I'll update them to reflect what is returned for them.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4351558592@github.com>

Peter Kenny

unread,
May 23, 2026, 7:51:37 PM (9 days ago) May 23
to vim/vim, Subscribed

@kennypete commented on this pull request.


In src/testdir/test_vimscript.vim:

> +" Test using fullcommand() {{{1
+func Test_builtin_fullcommand()
+  " :hor is the minimum abbreviation of :horizontal; :ho is not valid
+  call assert_equal('', fullcommand('ho', v:false))
+  call assert_equal('horizontal', fullcommand('hor', v:false))
+  " :k may take at most one mark {a-zA-Z'} argument, with optional whitespace
+  call assert_equal('k', fullcommand('k', v:false))
+  call assert_equal('k', fullcommand('kz', v:false))
+  call assert_equal('k', fullcommand('k z', v:false))
+  call assert_equal('k', fullcommand('   : 	  k    z', v:false))
+  call assert_equal('k', fullcommand(':3k z | echo "k"', v:false))
+  call assert_equal('k', fullcommand(":k'" .. ' " legacy comment', v:false))
+  call assert_equal('k', fullcommand(':ka"legacy_comment_no_space', v:false))
+  call assert_equal('k', fullcommand(":ka|echo 'bar no space'", v:false))
+  call assert_equal('', fullcommand('kzz', v:false))
+  call assert_equal('', fullcommand('kz7', v:false))

No longer an issue once I remove k handling per #20191 (comment) though I'll update them to reflect what is returned for them.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4351558893@github.com>

Peter Kenny

unread,
May 24, 2026, 3:47:08 PM (8 days ago) May 24
to vim/vim, Subscribed
kennypete left a comment (vim/vim#20191)

Made this Draft as it is now dependent on #20253 being merged because the test files covering :end* are common to this PR (test_vim9_class.vim particularly is well out of date here now).


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/c4529781503@github.com>

dkearns

unread,
May 26, 2026, 1:19:11 PM (6 days ago) May 26
to vim/vim, Subscribed

@dkearns commented on this pull request.


In src/ex_docmd.c:

> @@ -4025,6 +4025,19 @@ find_ex_command(
     if (eap->cmdidx == CMD_final && p - eap->cmd == 4 && !vim9)
 	eap->cmdidx = CMD_finally;
 
+    // ":hor" is documented as the minimum abbreviation of ":horizontal";
+    // ":ho" must not be recognized as ":horizontal".
+    if (eap->cmdidx == CMD_horizontal && p - eap->cmd == 2)
+	eap->cmdidx = CMD_SIZE;
+
+    // ":k{x}" takes exactly one mark character; reject it if more than one
+    // non-whitespace character immediately follows "k".
+    if (eap->cmdidx == CMD_k && STRLEN(eap->cmd) > 3)
+	eap->cmdidx = CMD_SIZE;
+
+    if (eap->cmdidx == CMD_k && STRLEN(eap->cmd) == 3 && eap->cmd[1] != ' ')
+	eap->cmdidx = CMD_SIZE;
+

I'm removing all 'k' handling changes per #20191 (comment) though what you have there, i.e., if (p[0] == 'k' && !(ASCII_ISLOWER(p[1]) && ASCII_ISLOWER(p[2]))) would mean kafoobar would no longer return k (which you note later as POSIX compliant) so best it's left as-is anyway. A Note added to builtin.txt should address the potential for inferring that the command passed should be a valid command, which I've provided later.

This was my suggested change if we were going to change the :kfoobar behaviour to not match as :k.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/review/4365950622@github.com>

dkearns

unread,
May 26, 2026, 1:23:48 PM (6 days ago) May 26
to vim/vim, Subscribed
dkearns left a comment (vim/vim#20191)

I think the problem here is my (incorrect) presumption that fullcommand(), as documented currently, can be interpreted as implying that the command passed to it will not return a full command if the command passed to it is invalid (when used in a script literally). :kfoobar is one such instance. There are loads in the :s 2- and 3- letter commands where you can pass nonsense but still get back substitute, e.g., :sIrmyword, as shown in the script, below.

Yes, it just does the bare minimum to determine the command name. This is usually a simple lookup of \a\+ in the command table. A range is also allowed/skipped for any command, even those that don't allow it.

As it doesn't really offer anything other than a better night's sleep it is probably better to leave it alone.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/c4546823727@github.com>

Christian Brabandt

unread,
May 31, 2026, 3:08:34 PM (yesterday) May 31
to vim/vim, Subscribed
chrisbra left a comment (vim/vim#20191)

Thanks all, let me merge it now


Reply to this email directly, view it on GitHub, or unsubscribe.

Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20191/c4587764408@github.com>

Reply all
Reply to author
Forward
0 new messages