summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordkearns <dougkearns@gmail.com>2024-02-27 07:10:18 +1100
committerGitHub <noreply@github.com>2024-02-26 21:10:18 +0100
commit2c51e15b66a4be9b5134c495ef546479aaa89ce9 (patch)
treead0e6050b27be98ea5434376e01feee710503ac2
parentff2b79d23956263ab0120623c37e0b4498be01db (diff)
runtime(vim): Update syntax file, improve :substitute matching (#14093)
- Differentiate between :substitute and substitute(), fixes #13883. - Match all allowed :substitute delimiters. - Remove leading context from :substitute matches. Signed-off-by: Doug Kearns <dougkearns@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
-rw-r--r--runtime/syntax/generator/vim.vim.base80
-rw-r--r--runtime/syntax/testdir/dumps/vim_ex_commands_23.dump2
-rw-r--r--runtime/syntax/testdir/dumps/vim_ex_commands_25.dump2
-rw-r--r--runtime/syntax/testdir/dumps/vim_ex_commands_26.dump2
-rw-r--r--runtime/syntax/testdir/dumps/vim_ex_commands_56.dump2
-rw-r--r--runtime/syntax/testdir/dumps/vim_ex_commands_58.dump2
-rw-r--r--runtime/syntax/testdir/dumps/vim_ex_commands_59.dump2
-rw-r--r--runtime/syntax/testdir/dumps/vim_ex_substitute_00.dump20
-rw-r--r--runtime/syntax/testdir/dumps/vim_ex_substitute_01.dump20
-rw-r--r--runtime/syntax/testdir/dumps/vim_ex_substitute_02.dump20
-rw-r--r--runtime/syntax/testdir/dumps/vim_ex_substitute_03.dump20
-rw-r--r--runtime/syntax/testdir/dumps/vim_ex_substitute_04.dump20
-rw-r--r--runtime/syntax/testdir/dumps/vim_ex_substitute_99.dump20
-rw-r--r--runtime/syntax/testdir/input/vim_ex_substitute.vim86
-rw-r--r--runtime/syntax/vim.vim82
15 files changed, 293 insertions, 87 deletions
diff --git a/runtime/syntax/generator/vim.vim.base b/runtime/syntax/generator/vim.vim.base
index 99a0488b34..6527fe178f 100644
--- a/runtime/syntax/generator/vim.vim.base
+++ b/runtime/syntax/generator/vim.vim.base
@@ -157,16 +157,16 @@ endif
" Numbers {{{2
" =======
-syn match vimNumber '\<\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '-\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '\<0[xX]\x\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '\<0[zZ][a-zA-Z0-9.]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '0[0-7]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '0[bB][01]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
+syn match vimNumber '\<\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
+syn match vimNumber '-\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
+syn match vimNumber '\<0[xX]\x\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
+syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
+syn match vimNumber '\<0[zZ][a-zA-Z0-9.]\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
+syn match vimNumber '0[0-7]\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
+syn match vimNumber '0[bB][01]\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
" All vimCommands are contained by vimIsCommand. {{{2
-syn match vimCmdSep "[:|]\+" skipwhite nextgroup=vimAbb,vimAddress,vimAutoCmd,vimAugroup,vimBehave,vimEcho,vimEchoHL,vimExecute,vimIsCommand,vimExtCmd,vimFilter,vimGlobal,vimHighlight,vimLet,vimMap,vimMark,vimNorm,vimSet,vimSyntax,vimUnlet,vimUnmap,vimUserCmd
+syn match vimCmdSep "[:|]\+" skipwhite nextgroup=vimAbb,vimAddress,vimAutoCmd,vimAugroup,vimBehave,vimEcho,vimEchoHL,vimExecute,vimIsCommand,vimExtCmd,vimFilter,vimGlobal,vimHighlight,vimLet,vimMap,vimMark,vimNorm,vimSet,vimSubst1,vimSyntax,vimUnlet,vimUnmap,vimUserCmd
syn match vimIsCommand "\<\%(\h\w*\|[23]mat\%[ch]\)\>" contains=vimCommand
syn match vimVar contained "\<\h[a-zA-Z0-9#_]*\>"
syn match vimVar "\<[bwglstav]:\h[a-zA-Z0-9#_]*\>"
@@ -194,7 +194,6 @@ syn match vimBehave "\<be\%[have]\>" nextgroup=vimBehaveBang,vimBehaveModel,vi
syn match vimBehaveBang contained "\a\@1<=!" nextgroup=vimBehaveModel skipwhite
syn keyword vimBehaveModel contained mswin xterm
-hi def link vimBehaveBang vimBang
" Filetypes {{{2
" =========
syn match vimFiletype "\<filet\%[ype]\(\s\+\I\i*\)*" skipwhite contains=vimFTCmd,vimFTOption,vimFTError
@@ -270,12 +269,12 @@ syn region vimKeymap matchgroup=vimCommand start="\<loadk\%[eymap]\>" end="\%$"
" Special Filenames, Modifiers, Extension Removal: {{{2
" ===============================================
-syn match vimSpecFile "<c\(word\|WORD\)>" nextgroup=vimSpecFileMod,vimSubst
-syn match vimSpecFile "<\([acs]file\|amatch\|abuf\)>" nextgroup=vimSpecFileMod,vimSubst
-syn match vimSpecFile "\s%[ \t:]"ms=s+1,me=e-1 nextgroup=vimSpecFileMod,vimSubst
-syn match vimSpecFile "\s%$"ms=s+1 nextgroup=vimSpecFileMod,vimSubst
-syn match vimSpecFile "\s%<"ms=s+1,me=e-1 nextgroup=vimSpecFileMod,vimSubst
-syn match vimSpecFile "#\d\+\|[#%]<\>" nextgroup=vimSpecFileMod,vimSubst
+syn match vimSpecFile "<c\(word\|WORD\)>" nextgroup=vimSpecFileMod,vimSubst1
+syn match vimSpecFile "<\([acs]file\|amatch\|abuf\)>" nextgroup=vimSpecFileMod,vimSubst1
+syn match vimSpecFile "\s%[ \t:]"ms=s+1,me=e-1 nextgroup=vimSpecFileMod,vimSubst1
+syn match vimSpecFile "\s%$"ms=s+1 nextgroup=vimSpecFileMod,vimSubst1
+syn match vimSpecFile "\s%<"ms=s+1,me=e-1 nextgroup=vimSpecFileMod,vimSubst1
+syn match vimSpecFile "#\d\+\|[#%]<\>" nextgroup=vimSpecFileMod,vimSubst1
syn match vimSpecFileMod "\(:[phtre]\)\+" contained
" User-Specified Commands: {{{2
@@ -356,33 +355,34 @@ syn match vimStringInterpolationBrace "}}"
syn cluster vimSubstList contains=vimPatSep,vimPatRegion,vimPatSepErr,vimSubstTwoBS,vimSubstRange,vimNotation
syn cluster vimSubstRepList contains=vimSubstSubstr,vimSubstTwoBS,vimNotation
syn cluster vimSubstList add=vimCollection
-syn match vimSubst "\(:\+\s*\|^\s*\||\s*\)\<\%(\<s\%[ubstitute]\>\|\<sm\%[agic]\>\|\<sno\%[magic]\>\)[:#[:alpha:]]\@!" nextgroup=vimSubstPat
-"syn match vimSubst "\%(^\|[^\\]\)\<s\%[ubstitute]\>[:#[:alpha:]]\@!" nextgroup=vimSubstPat contained
-syn match vimSubst "\%(^\|[^\\\"']\)\<s\%[ubstitute]\>[:#[:alpha:]\"']\@!" nextgroup=vimSubstPat contained
-syn match vimSubst "/\zs\<s\%[ubstitute]\>\ze/" nextgroup=vimSubstPat
-syn match vimSubst "\(:\+\s*\|^\s*\)s\ze#.\{-}#.\{-}#" nextgroup=vimSubstPat
-syn match vimSubst1 contained "\<s\%[ubstitute]\>" nextgroup=vimSubstPat
-syn match vimSubst2 contained "s\%[ubstitute]\>" nextgroup=vimSubstPat
-syn region vimSubstPat contained matchgroup=vimSubstDelim start="\z([^a-zA-Z( \t[\]&]\)"rs=s+1 skip="\\\\\|\\\z1" end="\z1"re=e-1,me=e-1 contains=@vimSubstList nextgroup=vimSubstRep4 oneline
-syn region vimSubstRep4 contained matchgroup=vimSubstDelim start="\z(.\)" skip="\\\\\|\\\z1" end="\z1" matchgroup=vimNotation end="<[cC][rR]>" contains=@vimSubstRepList nextgroup=vimSubstFlagErr oneline
-syn region vimCollection contained transparent start="\\\@<!\[" skip="\\\[" end="\]" contains=vimCollClass
-syn match vimCollClassErr contained "\[:.\{-\}:\]"
-syn match vimCollClass contained transparent "\%#=1\[:\(alnum\|alpha\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|xdigit\|retu\%[rn]\|tab\|escape\|backspace\):\]"
-syn match vimSubstSubstr contained "\\z\=\d"
-syn match vimSubstTwoBS contained "\\\\"
-syn match vimSubstFlagErr contained "[^< \t\r|]\+" contains=vimSubstFlags
-syn match vimSubstFlags contained "[&cegiIlnpr#]\+"
+syn match vimSubst "^\s*\%(s\%[ubstitute]\|sm\%[agic]\|sno\%[magic]\)\>[\"#|]\@!" nextgroup=vimSubstPat
+syn match vimSubst "^\s*\%(s\%[ubstitute]\|sm\%[agic]\|sno\%[magic]\)_\@=" nextgroup=vimSubstPat
+syn match vimSubst "^\s*\%(s\%[ubstitute]\|sm\%[agic]\>\|sno\%[magic]\)\ze#.\{-}#.\{-}#" nextgroup=vimSubstPat
+syn match vimSubst1 contained "\%(s\%[ubstitute]\|sm\%[agic]\>\|sno\%[magic]\)\>[\"#|]\@!" nextgroup=vimSubstPat
+syn match vimSubst1 contained "\%(s\%[ubstitute]\|sm\%[agic]\>\|sno\%[magic]\)_\@=" nextgroup=vimSubstPat
+syn match vimSubst1 contained "\%(s\%[ubstitute]\|sm\%[agic]\>\|sno\%[magic]\)\ze#.\{-}#.\{-}#" nextgroup=vimSubstPat
+" TODO: Vim9 illegal separators for abbreviated :s form are [-.:], :su\%[...] required
+" : # is allowed but "not recommended" (see :h pattern-delimiter)
+syn region vimSubstPat contained matchgroup=vimSubstDelim start="\z([!#$%&'()*+,-./:;<=>?@[\]^_`{}~]\)"rs=s+1 skip="\\\\\|\\\z1" end="\z1"re=e-1,me=e-1 contains=@vimSubstList nextgroup=vimSubstRep4 oneline
+syn region vimSubstRep4 contained matchgroup=vimSubstDelim start="\z(.\)" skip="\\\\\|\\\z1" end="\z1" matchgroup=vimNotation end="<[cC][rR]>" contains=@vimSubstRepList nextgroup=vimSubstFlagErr oneline
+syn region vimCollection contained transparent start="\\\@<!\[" skip="\\\[" end="\]" contains=vimCollClass
+syn match vimCollClassErr contained "\[:.\{-\}:\]"
+syn match vimCollClass contained transparent "\%#=1\[:\(alnum\|alpha\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|xdigit\|retu\%[rn]\|tab\|escape\|backspace\):\]"
+syn match vimSubstSubstr contained "\\z\=\d"
+syn match vimSubstTwoBS contained "\\\\"
+syn match vimSubstFlagErr contained "[^< \t\r|]\+" contains=vimSubstFlags
+syn match vimSubstFlags contained "[&cegiIlnpr#]\+"
" 'String': {{{2
syn match vimString "[^(,]'[^']\{-}\zs'"
" Marks, Registers, Addresses, Filters: {{{2
-syn match vimMark "'[a-zA-Z0-9]\ze[-+,!]" nextgroup=vimFilter,vimMarkNumber,vimSubst
-syn match vimMark "'[<>]\ze[-+,!]" nextgroup=vimFilter,vimMarkNumber,vimSubst
-syn match vimMark ",\zs'[<>]\ze" nextgroup=vimFilter,vimMarkNumber,vimSubst
-syn match vimMark "[!,:]\zs'[a-zA-Z0-9]" nextgroup=vimFilter,vimMarkNumber,vimSubst
-syn match vimMark "\<norm\%[al]\s\zs'[a-zA-Z0-9]" nextgroup=vimFilter,vimMarkNumber,vimSubst
-syn match vimMarkNumber "[-+]\d\+" contained contains=vimOper nextgroup=vimSubst2
+syn match vimMark "'[a-zA-Z0-9]\ze[-+,!]" nextgroup=vimFilter,vimMarkNumber,vimSubst1
+syn match vimMark "'[<>]\ze[-+,!]" nextgroup=vimFilter,vimMarkNumber,vimSubst1
+syn match vimMark ",\zs'[<>]\ze" nextgroup=vimFilter,vimMarkNumber,vimSubst1
+syn match vimMark "[!,:]\zs'[a-zA-Z0-9]" nextgroup=vimFilter,vimMarkNumber,vimSubst1
+syn match vimMark "\<norm\%[al]\s\zs'[a-zA-Z0-9]" nextgroup=vimFilter,vimMarkNumber,vimSubst1
+syn match vimMarkNumber "[-+]\d\+" contained contains=vimOper nextgroup=vimSubst1
syn match vimPlainMark contained "'[a-zA-Z0-9]"
syn match vimRange "[`'][a-zA-Z0-9],[`'][a-zA-Z0-9]" contains=vimMark skipwhite nextgroup=vimFilter
@@ -713,8 +713,8 @@ syn match vimCommentTitleLeader '"\s\+'ms=s+1 contained
" ====================
syn match vimSearch '^\s*[/?].*' contains=vimSearchDelim
syn match vimSearchDelim '^\s*\zs[/?]\|[/?]$' contained
-syn region vimGlobal matchgroup=Statement start='\<g\%[lobal]!\=/' skip='\\.' end='/' skipwhite nextgroup=vimSubst
-syn region vimGlobal matchgroup=Statement start='\<v\%[global]!\=/' skip='\\.' end='/' skipwhite nextgroup=vimSubst
+syn region vimGlobal matchgroup=Statement start='\<g\%[lobal]!\=/' skip='\\.' end='/' skipwhite nextgroup=vimSubst1
+syn region vimGlobal matchgroup=Statement start='\<v\%[global]!\=/' skip='\\.' end='/' skipwhite nextgroup=vimSubst1
" Embedded Scripts: {{{2
" ================
@@ -934,7 +934,7 @@ if !exists("skip_vim_syntax_inits")
hi def link vimAutoCmdMod Special
hi def link vimAutoSet vimCommand
hi def link vimBang vimOper
- hi def link vimBehaveBang vimBang
+ hi def link vimBehaveBang vimBang
hi def link vimBehaveModel vimBehave
hi def link vimBehave vimCommand
hi def link vimBracket Delimiter
diff --git a/runtime/syntax/testdir/dumps/vim_ex_commands_23.dump b/runtime/syntax/testdir/dumps/vim_ex_commands_23.dump
index d75c4b008d..6baf838d15 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_commands_23.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_commands_23.dump
@@ -15,6 +15,6 @@
|:|r+0#af5f00255&|u|n|d|o| +0#0000000&@68
|:|r+0#af5f00255&|u|n|t|i|m|e| +0#0000000&@66
|:|r+0#af5f00255&|v|i|m|i|n|f|o| +0#0000000&@65
-|:+0#af5f00255&|s|u|b|s|t|i|t|u|t|e| +0#0000000&@63
+|:|s+0#af5f00255&|u|b|s|t|i|t|u|t|e| +0#0000000&@63
|:|s+0#af5f00255&|N|e|x|t| +0#0000000&@68
@57|4|1|5|,|1| @8|3|4|%|
diff --git a/runtime/syntax/testdir/dumps/vim_ex_commands_25.dump b/runtime/syntax/testdir/dumps/vim_ex_commands_25.dump
index 746b0d80ae..6b40f2cace 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_commands_25.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_commands_25.dump
@@ -11,7 +11,7 @@
|:|s+0#af5f00255&|l|e@1|p| +0#0000000&@68
|:|s+0#af5f00255&|l|e@1|p|!| +0#0000000&@67
|:|s+0#af5f00255&|l|a|s|t| +0#0000000&@68
-|:+0#af5f00255&|s|m|a|g|i|c| +0#0000000&@67
+|:|s+0#af5f00255&|m|a|g|i|c| +0#0000000&@67
|:|s+0#af5f00255&|m|a|p| +0#0000000&@69
|:|s+0#af5f00255&|m|a|p|c|l|e|a|r| +0#0000000&@64
|:|s+0#af5f00255&|m|e|n|u| +0#0000000&@68
diff --git a/runtime/syntax/testdir/dumps/vim_ex_commands_26.dump b/runtime/syntax/testdir/dumps/vim_ex_commands_26.dump
index db27187c1d..f388e4fe3e 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_commands_26.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_commands_26.dump
@@ -1,5 +1,5 @@
|:+0&#ffffff0|s+0#af5f00255&|n|e|x|t| +0#0000000&@68
-|:+0#af5f00255&|s|n|o|m|a|g|i|c| +0#0000000&@65
+|:|s+0#af5f00255&|n|o|m|a|g|i|c| +0#0000000&@65
|:|s+0#af5f00255&|n|o|r|e|m|a|p| +0#0000000&@65
|:|s+0#af5f00255&|n|o|r|e|m|e|n|u| +0#0000000&@64
|:|s+0#af5f00255&|o|r|t| +0#0000000&@69
diff --git a/runtime/syntax/testdir/dumps/vim_ex_commands_56.dump b/runtime/syntax/testdir/dumps/vim_ex_commands_56.dump
index 89b1df7a41..ba5754108d 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_commands_56.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_commands_56.dump
@@ -15,6 +15,6 @@
|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |r+0#af5f00255&|u|n|d|o| +0#0000000&@56
|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |r+0#af5f00255&|u|n|t|i|m|e| +0#0000000&@54
|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |r+0#af5f00255&|v|i|m|i|n|f|o| +0#0000000&@53
-|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||+0#af5f00255&| |s|u|b|s|t|i|t|u|t|e| +0#0000000&@51
+|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|u|b|s|t|i|t|u|t|e| +0#0000000&@51
|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|N|e|x|t| +0#0000000&@56
@57|1|0@1|9|,|1| @7|8|5|%|
diff --git a/runtime/syntax/testdir/dumps/vim_ex_commands_58.dump b/runtime/syntax/testdir/dumps/vim_ex_commands_58.dump
index 28f0d89185..1d24fd5106 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_commands_58.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_commands_58.dump
@@ -11,7 +11,7 @@
|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|l|e@1|p| +0#0000000&@56
|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|l|e@1|p|!| +0#0000000&@55
|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|l|a|s|t| +0#0000000&@56
-|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||+0#af5f00255&| |s|m|a|g|i|c| +0#0000000&@55
+|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|m|a|g|i|c| +0#0000000&@55
|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|m|a|p| +0#0000000&@57
|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|m|a|p|c|l|e|a|r| +0#0000000&@52
|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|m|e|n|u| +0#0000000&@56
diff --git a/runtime/syntax/testdir/dumps/vim_ex_commands_59.dump b/runtime/syntax/testdir/dumps/vim_ex_commands_59.dump
index d346f93a5a..920861c527 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_commands_59.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_commands_59.dump
@@ -1,5 +1,5 @@
|c+0#af5f00255#ffffff0|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|n|e|x|t| +0#0000000&@56
-|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||+0#af5f00255&| |s|n|o|m|a|g|i|c| +0#0000000&@53
+|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|n|o|m|a|g|i|c| +0#0000000&@53
|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|n|o|r|e|m|a|p| +0#0000000&@53
|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|n|o|r|e|m|e|n|u| +0#0000000&@52
|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|o|r|t| +0#0000000&@57
diff --git a/runtime/syntax/testdir/dumps/vim_ex_substitute_00.dump b/runtime/syntax/testdir/dumps/vim_ex_substitute_00.dump
new file mode 100644
index 0000000000..31094c952d
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/vim_ex_substitute_00.dump
@@ -0,0 +1,20 @@
+>s+0#af5f00255#ffffff0|u|b|s|t|i|t|u|t|e|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|&| +0#0000000&@54
+|s+0#af5f00255&|u|b|s|t|i|t|u|t|e|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|c|e|g|i|I|n|p|#|l|r| +0#0000000&@45
+@75
+|s+0#af5f00255&|n|o|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|&| +0#0000000&@56
+|s+0#af5f00255&|n|o|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|c|e|g|i|I|n|p|#|l|r| +0#0000000&@47
+@75
+|s+0#af5f00255&|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|&| +0#0000000&@58
+|s+0#af5f00255&|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|c|e|g|i|I|n|p|#|l|r| +0#0000000&@49
+@75
+|:|s+0#af5f00255&|u|b|s|t|i|t|u|t|e|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|&| +0#0000000&@53
+|:|s+0#af5f00255&|u|b|s|t|i|t|u|t|e|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|c|e|g|i|I|n|p|#|l|r| +0#0000000&@44
+@75
+|:|s+0#af5f00255&|n|o|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|&| +0#0000000&@55
+|:|s+0#af5f00255&|n|o|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|c|e|g|i|I|n|p|#|l|r| +0#0000000&@46
+@75
+|:|s+0#af5f00255&|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|&| +0#0000000&@57
+|:|s+0#af5f00255&|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|c|e|g|i|I|n|p|#|l|r| +0#0000000&@48
+@75
+|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|u|b|s|t|i|t|u|t|e|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|&| +0#0000000&@41
+|"|i|n|p|u|t|/|v|i|m|_|e|x|_|s|u|b|s|t|i|t|u|t|e|.|v|i|m|"| |8|6|L|,| |1|5|1|0|B| @16|1|,|1| @10|T|o|p|
diff --git a/runtime/syntax/testdir/dumps/vim_ex_substitute_01.dump b/runtime/syntax/testdir/dumps/vim_ex_substitute_01.dump
new file mode 100644
index 0000000000..806101c296
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/vim_ex_substitute_01.dump
@@ -0,0 +1,20 @@
+|:+0&#ffffff0|s+0#af5f00255&|n|o|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|c|e|g|i|I|n|p|#|l|r| +0#0000000&@46
+@75
+|:|s+0#af5f00255&|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|&| +0#0000000&@57
+|:|s+0#af5f00255&|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|c|e|g|i|I|n|p|#|l|r| +0#0000000&@48
+@75
+>c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|u|b|s|t|i|t|u|t|e|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|&| +0#0000000&@41
+|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|u|b|s|t|i|t|u|t|e|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|c|e|g|i|I|n|p|#|l|r| +0#0000000&@32
+@75
+|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|n|o|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|&| +0#0000000&@43
+|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|n|o|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|c|e|g|i|I|n|p|#|l|r| +0#0000000&@34
+@75
+|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|&| +0#0000000&@45
+|c+0#af5f00255&|a|l@1| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&||| |s+0#af5f00255&|m|a|g|i|c|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&|c|e|g|i|I|n|p|#|l|r| +0#0000000&@36
+@75
+|l+0#af5f00255&|e|t| +0#0000000&|f+0#00e0e07&|o@1| +0#0000000&|=+0#af5f00255&| +0#0000000&|s|t|r|-+0#af5f00255&|>|s+0#00e0e07&|u|b|s|t|i|t|u|t|e|(+0#e000e06&|s+0#00e0e07&|t|r|,+0#0000000&| |p+0#00e0e07&|a|t|,+0#0000000&| |s+0#00e0e07&|u|b|,+0#0000000&| |f+0#00e0e07&|l|a|g|s|)+0#e000e06&| +0#0000000&@27
+@75
+|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&@60
+| +0#af5f00255&@1|s|u|b|s|t|i|t|u|t|e|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&| +0#0000000&@53
+@2|l+0#af5f00255&|e|t| +0#0000000&|b+0#00e0e07&|a|r| +0#0000000&|=+0#af5f00255&| +0#0000000&|s|t|r|-+0#af5f00255&|>|s+0#00e0e07&|u|b|s|t|i|t|u|t|e|(+0#e000e06&|s+0#00e0e07&|t|r|,+0#0000000&| |p+0#00e0e07&|a|t|,+0#0000000&| |s+0#00e0e07&|u|b|,+0#0000000&| |f+0#00e0e07&|l|a|g|s|)+0#e000e06&| +0#0000000&@25
+@57|1|9|,|1| @9|1|9|%|
diff --git a/runtime/syntax/testdir/dumps/vim_ex_substitute_02.dump b/runtime/syntax/testdir/dumps/vim_ex_substitute_02.dump
new file mode 100644
index 0000000000..b0e722d26f
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/vim_ex_substitute_02.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@1|l+0#af5f00255&|e|t| +0#0000000&|b+0#00e0e07&|a|r| +0#0000000&|=+0#af5f00255&| +0#0000000&|s|t|r|-+0#af5f00255&|>|s+0#00e0e07&|u|b|s|t|i|t|u|t|e|(+0#e000e06&|s+0#00e0e07&|t|r|,+0#0000000&| |p+0#00e0e07&|a|t|,+0#0000000&| |s+0#00e0e07&|u|b|,+0#0000000&| |f+0#00e0e07&|l|a|g|s|)+0#e000e06&| +0#0000000&@25
+|e+0#af5f00255&|n|d|f|u|n|c|t|i|o|n| +0#0000000&@63
+@75
+|d+0#af5f00255&|e|f| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&@65
+| +0#af5f00255&@1|s|u|b|s|t|i|t|u|t|e|/+0#e000e06&|f+0#0000000&|o@1|/+0#e000e06&|b+0#0000000&|a|r|/+0#e000e06&| +0#0000000&@53
+@2>l+0#af5f00255&|e|t| +0#0000000&|b+0#00e0e07&|a|r| +0#0000000&|=+0#af5f00255&| +0#0000000&|s|t|r|-+0#af5f00255&|>|s+0#00e0e07&|u|b|s|t|i|t|u|t|e|(+0#e000e06&|s+0#00e0e07&|t|r|,+0#0000000&| |p+0#00e0e07&|a|t|,+0#0000000&| |s+0#00e0e07&|u|b|,+0#0000000&| |f+0#00e0e07&|l|a|g|s|)+0#e000e06&| +0#0000000&@25
+|e+0#af5f00255&|n|d@1|e|f| +0#0000000&@68
+@75
+|"+0#0000e05&| |v|a|r|i|o|u|s| |d|e|l|i|m|i|t|e|r|s| +0#0000000&@54
+@75
+|s+0#af5f00255&|!+0#e000e06&|/+0#0000000&|!+0#e000e06&|/+0#0000000&@1|!+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|"+0#0000e05&| |s|"|/|"|/@1|"| |"| |c|o|m@1|e|n|t| |(|w|o|r|k|s| |b|u|t| |d|i|s|a|l@1|o|w|e|d|)| +0#0000000&@32
+|s+0#af5f00255&|#+0#e000e06&|/+0#0000000&|#+0#e000e06&|/+0#0000000&@1|#+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|$+0#e000e06&|/+0#0000000&|$+0#e000e06&|/+0#0000000&@1|$+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|%+0#e000e06&|/+0#0000000&|%+0#e000e06&|/+0#0000000&@1|%+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|&+0#e000e06&|/+0#0000000&|&+0#e000e06&|/+0#0000000&@1|&+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|'+0#e000e06&|/+0#0000000&|'+0#e000e06&|/+0#0000000&@1|'+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|"+0#0000e05&| |F+0#0000001#ffff4012|I|X|M|E| +0#0000e05#ffffff0|-| |m|a|t|c|h|e|s| |v|i|m|U|s|e|r|F|u|n|c| +0#0000000&@45
+|"+0#0000e05&| |s|(|/|(|/@1|(| |"| |c|o|m@1|e|n|t| +0#0000000&@55
+@57|3|7|,|3| @9|4|6|%|
diff --git a/runtime/syntax/testdir/dumps/vim_ex_substitute_03.dump b/runtime/syntax/testdir/dumps/vim_ex_substitute_03.dump
new file mode 100644
index 0000000000..cb12bb38a6
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/vim_ex_substitute_03.dump
@@ -0,0 +1,20 @@
+|"+0#0000e05#ffffff0| |s|(|/|(|/@1|(| |"| |c|o|m@1|e|n|t| +0#0000000&@55
+|s+0#af5f00255&|)+0#e000e06&|/+0#0000000&|)+0#e000e06&|/+0#0000000&@1|)+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|*+0#e000e06&|/+0#0000000&|*+0#e000e06&|/+0#0000000&@1|*+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|++0#e000e06&|/+0#0000000&|++0#e000e06&|/+0#0000000&@1|++0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|,+0#e000e06&|/+0#0000000&|,+0#e000e06&|/+0#0000000&@1|,+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+>s+0#af5f00255&|-+0#e000e06&|/+0#0000000&|-+0#e000e06&|/+0#0000000&@1|-+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|.+0#e000e06&|/+0#0000000&|.+0#e000e06&|/+0#0000000&@1|.+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|/+0#e000e06&|X+0#0000000&|/+0#e000e06&|X+0#0000000&@1|/+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|:+0#e000e06&|/+0#0000000&|:+0#e000e06&|/+0#0000000&@1|:+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|;+0#e000e06&|/+0#0000000&|;+0#e000e06&|/+0#0000000&@1|;+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|<+0#e000e06&|/+0#0000000&|<+0#e000e06&|/+0#0000000&@1|<+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|=+0#e000e06&|/+0#0000000&|=+0#e000e06&|/+0#0000000&@1|=+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|>+0#e000e06&|/+0#0000000&|>+0#e000e06&|/+0#0000000&@1|>+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|?+0#e000e06&|/+0#0000000&|?+0#e000e06&|/+0#0000000&@1|?+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|@+0#e000e06&|/+0#0000000&|@+0#e000e06&|/+0#0000000&@1|@+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|[+0#e000e06&|/+0#0000000&|[+0#e000e06&|/+0#0000000&@1|[+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|"+0#0000e05&| |s|\|/|\|/@1|\| |"| |c|o|m@1|e|n|t| |(|d|i|s|a|l@1|o|w|e|d|)| +0#0000000&@42
+|s+0#af5f00255&|]+0#e000e06&|/+0#0000000&|]+0#e000e06&|/+0#0000000&@1|]+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|^+0#e000e06&|/+0#0000000&|^+0#e000e06&|/+0#0000000&@1|^+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+@57|5@1|,|1| @9|7|3|%|
diff --git a/runtime/syntax/testdir/dumps/vim_ex_substitute_04.dump b/runtime/syntax/testdir/dumps/vim_ex_substitute_04.dump
new file mode 100644
index 0000000000..b37826020c
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/vim_ex_substitute_04.dump
@@ -0,0 +1,20 @@
+|s+0#af5f00255#ffffff0|^+0#e000e06&|/+0#0000000&|^+0#e000e06&|/+0#0000000&@1|^+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|_+0#e000e06&|/+0#0000000&|_+0#e000e06&|/+0#0000000&@1|_+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|`+0#e000e06&|/+0#0000000&|`+0#e000e06&|/+0#0000000&@1|`+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|{+0#e000e06&|/+0#0000000&|{+0#e000e06&|/+0#0000000&@1|{+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|"+0#0000e05&| |s|||/|||/@1||| |"| |c|o|m@1|e|n|t| |(|d|i|s|a|l@1|o|w|e|d|)| +0#0000000&@42
+>s+0#af5f00255&|}+0#e000e06&|/+0#0000000&|}+0#e000e06&|/+0#0000000&@1|}+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|~+0#e000e06&|/+0#0000000&|~+0#e000e06&|/+0#0000000&@1|~+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+@75
+@75
+|"+0#0000e05&| |I|s@1|u|e| |#|1|3|8@1|3| +0#0000000&@60
+@75
+|s|t|r|[|s|]| @68
+|s|t|r|(+0#e000e06&|s+0#00e0e07&|)+0#e000e06&| +0#0000000&@68
+@75
+|d+0#af5f00255&|e|f| +0#0000000&|T|e|s|t|(+0#e000e06&|)| +0#0000000&@64
+@2|s|t|r|[|s|]| @66
+@2|s|t|r|(+0#e000e06&|s+0#00e0e07&|)+0#e000e06&| +0#0000000&@66
+|e+0#af5f00255&|n|d@1|e|f| +0#0000000&@68
+@75
+@57|7|3|,|1| @9|B|o|t|
diff --git a/runtime/syntax/testdir/dumps/vim_ex_substitute_99.dump b/runtime/syntax/testdir/dumps/vim_ex_substitute_99.dump
new file mode 100644
index 0000000000..88cb7dc218
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/vim_ex_substitute_99.dump
@@ -0,0 +1,20 @@
+|s+0#af5f00255#ffffff0|^+0#e000e06&|/+0#0000000&|^+0#e000e06&|/+0#0000000&@1|^+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|_+0#e000e06&|/+0#0000000&|_+0#e000e06&|/+0#0000000&@1|_+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|`+0#e000e06&|/+0#0000000&|`+0#e000e06&|/+0#0000000&@1|`+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|{+0#e000e06&|/+0#0000000&|{+0#e000e06&|/+0#0000000&@1|{+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|"+0#0000e05&| |s|||/|||/@1||| |"| |c|o|m@1|e|n|t| |(|d|i|s|a|l@1|o|w|e|d|)| +0#0000000&@42
+|s+0#af5f00255&|}+0#e000e06&|/+0#0000000&|}+0#e000e06&|/+0#0000000&@1|}+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+|s+0#af5f00255&|~+0#e000e06&|/+0#0000000&|~+0#e000e06&|/+0#0000000&@1|~+0#e000e06&| +0#0000000&|"+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@57
+@75
+@75
+|"+0#0000e05&| |I|s@1|u|e| |#|1|3|8@1|3| +0#0000000&@60
+@75
+|s|t|r|[|s|]| @68
+|s|t|r|(+0#e000e06&|s+0#00e0e07&|)+0#e000e06&| +0#0000000&@68
+@75
+|d+0#af5f00255&|e|f| +0#0000000&|T|e|s|t|(+0#e000e06&|)| +0#0000000&@64
+@2|s|t|r|[|s|]| @66
+@2|s|t|r|(+0#e000e06&|s+0#00e0e07&|)+0#e000e06&| +0#0000000&@66
+|e+0#af5f00255&|n|d@1|e|f| +0#0000000&@68
+> @74
+@57|8|6|,|0|-|1| @7|B|o|t|
diff --git a/runtime/syntax/testdir/input/vim_ex_substitute.vim b/runtime/syntax/testdir/input/vim_ex_substitute.vim
new file mode 100644
index 0000000000..340d573ac1
--- /dev/null
+++ b/runtime/syntax/testdir/input/vim_ex_substitute.vim
@@ -0,0 +1,86 @@
+substitute/foo/bar/&
+substitute/foo/bar/cegiInp#lr
+
+snomagic/foo/bar/&
+snomagic/foo/bar/cegiInp#lr
+
+smagic/foo/bar/&
+smagic/foo/bar/cegiInp#lr
+
+:substitute/foo/bar/&
+:substitute/foo/bar/cegiInp#lr
+
+:snomagic/foo/bar/&
+:snomagic/foo/bar/cegiInp#lr
+
+:smagic/foo/bar/&
+:smagic/foo/bar/cegiInp#lr
+
+call Foo() | substitute/foo/bar/&
+call Foo() | substitute/foo/bar/cegiInp#lr
+
+call Foo() | snomagic/foo/bar/&
+call Foo() | snomagic/foo/bar/cegiInp#lr
+
+call Foo() | smagic/foo/bar/&
+call Foo() | smagic/foo/bar/cegiInp#lr
+
+let foo = str->substitute(str, pat, sub, flags)
+
+function Foo()
+ substitute/foo/bar/
+ let bar = str->substitute(str, pat, sub, flags)
+endfunction
+
+def Foo()
+ substitute/foo/bar/
+ let bar = str->substitute(str, pat, sub, flags)
+enddef
+
+" various delimiters
+
+s!/!//! " comment
+" s"/"//" " comment (works but disallowed)
+s#/#//# " comment
+s$/$//$ " comment
+s%/%//% " comment
+s&/&//& " comment
+s'/'//' " comment
+" FIXME - matches vimUserFunc
+" s(/(//( " comment
+s)/)//) " comment
+s*/*//* " comment
+s+/+//+ " comment
+s,/,//, " comment
+s-/-//- " comment
+s././/. " comment
+s/X/XX/ " comment
+s:/://: " comment
+s;/;//; " comment
+s</<//< " comment
+s=/=//= " comment
+s>/>//> " comment
+s?/?//? " comment
+s@/@//@ " comment
+s[/[//[ " comment
+" s\/\//\ " comment (disallowed)
+s]/]//] " comment
+s^/^//^ " comment
+s_/_//_ " comment
+s`/`//` " comment
+s{/{//{ " comment
+" s|/|//| " comment (disallowed)
+s}/}//} " comment
+s~/~//~ " comment
+
+
+" Issue #13883
+
+str[s]
+str(s)
+
+def Test()
+ str[s]
+ str(s)
+enddef
+
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 7dde1a3408..c845651c98 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -3,7 +3,7 @@
" Maintainer: Hirohito Higashi <h.east.727 ATMARK gmail.com>
" Doug Kearns <dougkearns@gmail.com>
" URL: https://github.com/vim-jp/syntax-vim-ex
-" Last Change: Feb 23, 2024
+" Last Change: Feb 27, 2024
" Former Maintainer: Charles E. Campbell
" Base File URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_VIM
" Base File Version: 9.0-25
@@ -199,16 +199,16 @@ endif
" Numbers {{{2
" =======
-syn match vimNumber '\<\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '-\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '\<0[xX]\x\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '\<0[zZ][a-zA-Z0-9.]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '0[0-7]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '0[bB][01]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
+syn match vimNumber '\<\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
+syn match vimNumber '-\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
+syn match vimNumber '\<0[xX]\x\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
+syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
+syn match vimNumber '\<0[zZ][a-zA-Z0-9.]\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
+syn match vimNumber '0[0-7]\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
+syn match vimNumber '0[bB][01]\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment
" All vimCommands are contained by vimIsCommand. {{{2
-syn match vimCmdSep "[:|]\+" skipwhite nextgroup=vimAbb,vimAddress,vimAutoCmd,vimAugroup,vimBehave,vimEcho,vimEchoHL,vimExecute,vimIsCommand,vimExtCmd,vimFilter,vimGlobal,vimHighlight,vimLet,vimMap,vimMark,vimNorm,vimSet,vimSyntax,vimUnlet,vimUnmap,vimUserCmd
+syn match vimCmdSep "[:|]\+" skipwhite nextgroup=vimAbb,vimAddress,vimAutoCmd,vimAugroup,vimBehave,vimEcho,vimEchoHL,vimExecute,vimIsCommand,vimExtCmd,vimFilter,vimGlobal,vimHighlight,vimLet,vimMap,vimMark,vimNorm,vimSet,vimSubst1,vimSyntax,vimUnlet,vimUnmap,vimUserCmd
syn match vimIsCommand "\<\%(\h\w*\|[23]mat\%[ch]\)\>" contains=vimCommand
syn match vimVar contained "\<\h[a-zA-Z0-9#_]*\>"
syn match vimVar "\<[bwglstav]:\h[a-zA-Z0-9#_]*\>"
@@ -236,7 +236,6 @@ syn match vimBehave "\<be\%[have]\>" nextgroup=vimBehaveBang,vimBehaveModel,vi
syn match vimBehaveBang contained "\a\@1<=!" nextgroup=vimBehaveModel skipwhite
syn keyword vimBehaveModel contained mswin xterm
-hi def link vimBehaveBang vimBang
" Filetypes {{{2
" =========
syn match vimFiletype "\<filet\%[ype]\(\s\+\I\i*\)*" skipwhite contains=vimFTCmd,vimFTOption,vimFTError
@@ -312,12 +311,12 @@ syn region vimKeymap matchgroup=vimCommand start="\<loadk\%[eymap]\>" end="\%$"
" Special Filenames, Modifiers, Extension Removal: {{{2
" ===============================================
-syn match vimSpecFile "<c\(word\|WORD\)>" nextgroup=vimSpecFileMod,vimSubst
-syn match vimSpecFile "<\([acs]file\|amatch\|abuf\)>" nextgroup=vimSpecFileMod,vimSubst
-syn match vimSpecFile "\s%[ \t:]"ms=s+1,me=e-1 nextgroup=vimSpecFileMod,vimSubst
-syn match vimSpecFile "\s%$"ms=s+1 nextgroup=vimSpecFileMod,vimSubst
-syn match vimSpecFile "\s%<"ms=s+1,me=e-1 nextgroup=vimSpecFileMod,vimSubst
-syn match vimSpecFile "#\d\+\|[#%]<\>" nextgroup=vimSpecFileMod,vimSubst
+syn match vimSpecFile "<c\(word\|WORD\)>" nextgroup=vimSpecFileMod,vimSubst1
+syn match vimSpecFile "<\([acs]file\|amatch\|abuf\)>" nextgroup=vimSpecFileMod,vimSubst1
+syn match vimSpecFile "\s%[ \t:]"ms=s+1,me=e-1 ne