runtime(vim): Update base-syntax, match escape sequences in :command blocks

- Match escape sequences in :command replacement blocks.
- Match :substitute after escape sequences (a temporary fix until Ex
  commands are contained).

fixes: #17326
closes: #17663

Signed-off-by: Doug Kearns <dougkearns@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/runtime/syntax/generator/vim.vim.base b/runtime/syntax/generator/vim.vim.base
index 08ddd11..b9806fa 100644
--- a/runtime/syntax/generator/vim.vim.base
+++ b/runtime/syntax/generator/vim.vim.base
@@ -2,7 +2,7 @@
 " Language:	   Vim script
 " Maintainer:	   Hirohito Higashi <h.east.727 ATMARK gmail.com>
 "	   Doug Kearns <dougkearns@gmail.com>
-" Last Change:	   2025 Jul 03
+" Last Change:	   2025 Jul 04
 " Former Maintainer: Charles E. Campbell
 
 " DO NOT CHANGE DIRECTLY.
@@ -809,7 +809,7 @@
       \ matchgroup=vimSep
       \ start="{"
       \ end="^\s*\zs}"
-      \ contains=@vimDefBodyList
+      \ contains=@vimDefBodyList,@vimUserCmdList
 
 syn match	vimDelcommand		"\<delc\%[ommand]\>"	skipwhite nextgroup=vimDelcommandAttr,vimDelcommandName
 syn match	vimDelcommandAttr	contained	"-buffer\>"		skipwhite nextgroup=vimDelcommandName
@@ -1393,7 +1393,7 @@
 syn match	vimNotation	contained	"\%#=1\%(\\\|<lt>\)\=<\%(sid\|nop\|nul\|lt\|drop\)>"				contains=vimBracket
 syn match	vimNotation	contained	"\%#=1\%(\\\|<lt>\)\=<\%(snr\|plug\|cursorhold\|ignore\|cmd\|scriptcmd\|focus\%(gained\|lost\)\)>"	contains=vimBracket
 " syn match	vimNotation	contained	'\%(\\\|<lt>\)\=<C-R>[0-9a-z"%#:.\-=]'he=e-1				contains=vimBracket
-syn match	vimNotation	contained	'\%#=1\%(\\\|<lt>\)\=<\%(q-\)\=\%(line[12]\|count\|bang\|reg\|args\|mods\|f-args\|f-mods\|lt\)>'	contains=vimBracket
+syn match	vimNotation	contained	'\%#=1\%(\\\|<lt>\)\=<\%([fq]-\)\=\%(line[12]\|count\|bang\|reg\|args\|mods\|lt\)>'		contains=vimBracket skipwhite nextgroup=vimSubst1
 syn match	vimNotation	contained	"\%#=1\%(\\\|<lt>\)\=<\%([cas]file\|abuf\|amatch\|cexpr\|cword\|cWORD\|client\|stack\|script\|sf\=lnum\)>"	contains=vimBracket
 syn match	vimNotation	contained	"\%#=1\%(\\\|<lt>\)\=<\%([scamd]-\)\{0,4}char-\%(\d\+\|0\o\+\|0x\x\+\)>"		contains=vimBracket
 
diff --git a/runtime/syntax/testdir/dumps/vim_ex_command_01.dump b/runtime/syntax/testdir/dumps/vim_ex_command_01.dump
index a653a4d..bbe9104 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_command_01.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_command_01.dump
@@ -17,4 +17,4 @@
 @6|\+0#e000e06&| +0#0000000&|e+0#af5f00255&|c|h|o| +0#0000000&|"+0#e000002&|F|o@1|"| +0#0000000&@56
 @75
 |c+0#af5f00255&|o|m@1|a|n|d|!| +0#0000000&|-+0#e000e06&|a|d@1|r|=|a+0#00e0003&|r|g|u|m|e|n|t|s| +0#0000000&|-+0#e000e06&|b|a|n|g| +0#0000000&|-+0#e000e06&|b|a|r| +0#0000000&|-+0#e000e06&|b|u|f@1|e|r| +0#0000000&|-+0#e000e06&|c|o|m|p|l|e|t|e|=|a+0#00e0003&|r|g|l|i|s|t| +0#0000000&|-+0#e000e06&|c|o|u|n|t|=|1+0#e000002&| +0#0000000&|-+0#e000e06&|@+0#4040ff13&@2
-| +0#0000000&@56|1|9|,|1| @9|1|0|%| 
+| +0#0000000&@56|1|9|,|1| @10|8|%| 
diff --git a/runtime/syntax/testdir/dumps/vim_ex_command_02.dump b/runtime/syntax/testdir/dumps/vim_ex_command_02.dump
index 9909f2f..d3b9106 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_command_02.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_command_02.dump
@@ -17,4 +17,4 @@
 @75
 |c+0#af5f00255&|o|m@1|a|n|d|!| +0#0000000&|-+0#e000e06&|c|o|m|p|l|e|t|e|=|c+0#00e0003&|u|s|t|o|m|,+0#0000000&|s+0#00e0e07&|:|C|o|m|p|l|e|t|e|r|1| +0#0000000&|F+0#0000001#ffff4012|o@1| +0#0000000#ffffff0|e+0#af5f00255&|c|h|o| +0#0000000&|"+0#e000002&|F|o@1|"| +0#0000000&@21
 |c+0#af5f00255&|o|m@1|a|n|d|!| +0#0000000&|-+0#e000e06&|c|o|m|p|l|e|t|e|=|c+0#00e0003&|u|s|t|o|m|l|i|s|t|,+0#0000000&|s+0#00e0e07&|:|C|o|m|p|l|e|t|e|r|2| +0#0000000&|F+0#0000001#ffff4012|o@1| +0#0000000#ffffff0|e+0#af5f00255&|c|h|o| +0#0000000&|"+0#e000002&|F|o@1|"| +0#0000000&@17
-@57|3|4|,|1| @9|2@1|%| 
+@57|3|4|,|1| @9|1|9|%| 
diff --git a/runtime/syntax/testdir/dumps/vim_ex_command_03.dump b/runtime/syntax/testdir/dumps/vim_ex_command_03.dump
index c035fb6..a0478b8 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_command_03.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_command_03.dump
@@ -17,4 +17,4 @@
 @6|\+0#e000e06&| +0#0000000&|-+0#e000e06&|b|u|f@1|e|r| +0#0000000&@59
 @6|\+0#e000e06&| +0#0000000&|-+0#e000e06&|c|o|m|p|l|e|t|e|=|b+0#00e0003&|u|f@1|e|r| +0#0000000&@50
 @6|\+0#e000e06&| +0#0000000&|-+0#e000e06&|c|o|u|n|t| +0#0000000&@60
-@57|5|0|,|0|-|1| @7|3|5|%| 
+@57|5|0|,|0|-|1| @7|3|0|%| 
diff --git a/runtime/syntax/testdir/dumps/vim_ex_command_04.dump b/runtime/syntax/testdir/dumps/vim_ex_command_04.dump
index a263a18..e1c2498 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_command_04.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_command_04.dump
@@ -17,4 +17,4 @@
 @6|\+0#e000e06&| +0#0000000&|-+0#e000e06&|n|a|r|g|s|=|*+0#00e0003&| +0#0000000&@58
 @6|\+0#e000e06&| +0#0000000&|-+0#e000e06&|r|a|n|g|e| +0#0000000&@60
 @6|\+0#e000e06&| +0#0000000&|-+0#e000e06&|r|e|g|i|s|t|e|r| +0#0000000&@57
-@57|6|8|,|7| @9|4|9|%| 
+@57|6|8|,|7| @9|4|3|%| 
diff --git a/runtime/syntax/testdir/dumps/vim_ex_command_05.dump b/runtime/syntax/testdir/dumps/vim_ex_command_05.dump
index 7b78054..f2e09ad 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_command_05.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_command_05.dump
@@ -17,4 +17,4 @@
 @6|"+0#0000e05&|\| |c|o|m@1|e|n|t| +0#0000000&@58
 @6|\+0#e000e06&| +0#0000000&|-+0#e000e06&|c|o|u|n|t| +0#0000000&@60
 @6|"+0#0000e05&|\| |c|o|m@1|e|n|t| +0#0000000&@58
-@57|8|6|,|0|-|1| @7|6|4|%| 
+@57|8|6|,|0|-|1| @7|5@1|%| 
diff --git a/runtime/syntax/testdir/dumps/vim_ex_command_06.dump b/runtime/syntax/testdir/dumps/vim_ex_command_06.dump
index 4e9926f..d61480c 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_command_06.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_command_06.dump
@@ -17,4 +17,4 @@
 |"+0#0000e05&| |e|r@1|o|r|s| +0#0000000&@66
 @75
 |c+0#af5f00255&|o|m@1|a|n|d|!| +0#0000000&|-+0#ffffff16#ff404010|b|a|d|a|t@1|r|=+0#0000000#ffffff0|a|r|g|u|m|e|n|t|s| |-+0#e000e06&|b|a|n|g| +0#0000000&|-+0#ffffff16#ff404010|b|a|d|a|t@1|r| +0#0000000#ffffff0|-+0#e000e06&|n|a|r|g|s|=|*+0#00e0003&| +0#0000000&|F+0#0000001#ffff4012|o@1| +0#0000000#ffffff0|e+0#af5f00255&|c|h|o| +0#0000000&|"+0#e000002&|F|o@1|"| +0#0000000&@8
-@57|1|0|4|,|7| @8|7|8|%| 
+@57|1|0|4|,|7| @8|6|8|%| 
diff --git a/runtime/syntax/testdir/dumps/vim_ex_command_07.dump b/runtime/syntax/testdir/dumps/vim_ex_command_07.dump
index 3d51e30..312f519 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_command_07.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_command_07.dump
@@ -17,4 +17,4 @@
 |c+0#af5f00255&|o|m|c|l|e|a|r| +0#0000000&||| |e+0#af5f00255&|c|h|o| +0#0000000&|"+0#e000002&|F|o@1|"| +0#0000000&@53
 @75
 @75
-@57|1|2@1|,|1| @8|9|2|%| 
+@57|1|2@1|,|1| @8|8|0|%| 
diff --git a/runtime/syntax/testdir/dumps/vim_ex_command_08.dump b/runtime/syntax/testdir/dumps/vim_ex_command_08.dump
index 19a6c36..9120dd9 100644
--- a/runtime/syntax/testdir/dumps/vim_ex_command_08.dump
+++ b/runtime/syntax/testdir/dumps/vim_ex_command_08.dump
@@ -8,13 +8,13 @@
 @75
 |c+0#af5f00255&|o|m@1|a|n|d|!| +0#0000000&|-+0#e000e06&|b|a|n|g| +0#0000000&|-+0#e000e06&|n|a|r|g|s|=|*+0#00e0003&| +0#0000000&|-+0#e000e06&|c|o|m|p|l|e|t|e|=|f+0#00e0003&|i|l|e| +0#0000000&|M+0#0000001#ffff4012|a|k|e| +0#0000000#ffffff0|A|s|y|n|c|R|u|n| |-+0#af5f00255&|p+0#00e0e07&|r|o|g|r|a|m|=+0#af5f00255&|m+0#00e0e07&|a|k|e| +0#0000000&|@| |<+0#e000e06&|a|r|g|s|>
 | +0#0000000&@74
-|~+0#4040ff13&| @73
-|~| @73
-|~| @73
-|~| @73
-|~| @73
-|~| @73
-|~| @73
-|~| @73
-|~| @73
-| +0#0000000&@56|1|4|0|,|0|-|1| @6|B|o|t| 
+@75
+|"+0#0000e05&| |I|s@1|u|e| |#|1|7|3|2|6| |(|s|y|n|t|a|x| |h|i|g|h|l|i|g|h|t|i|n|g| |b|r|e|a|k|s| |w|i|t|h| |c|o|m|p|l|e|x| |:|s| |c|o|m|a|m|n|d|)| +0#0000000&@7
+@75
+|c+0#af5f00255&|o|m@1|a|n|d| +0#0000000&|-+0#e000e06&|r|a|n|g|e|=|%+0#00e0003&| +0#0000000&|-+0#e000e06&|n|a|r|g|s|=|?+0#00e0003&| +0#0000000&|-+0#e000e06&|b|a|n|g| +0#0000000&|T+0#0000001#ffff4012|b| +0#0000000#ffffff0|{+0#e000e06&| +0#0000000&@38
+@4|i+0#af5f00255&|f| +0#0000000&|"+0#e000002&|<|b|a|n|g|>|"| +0#0000000&|=+0#af5f00255&@1| +0#0000000&|"+0#e000002&|!|"| +0#0000000&@52
+@8|:|<+0#e000e06&|l|i|n|e|1|>|,+0#0000000&|<+0#e000e06&|l|i|n|e|2|>|s+0#af5f00255&|/+0#e000e06&|\+0#0000000&|v|"|[|^|"|]|*|"|/+0#e000e06&|\+0#0000000&|=|s|u|b|s|t|i|t|u|t|e|(|s|u|b|m|a|t|c|h|(|0|)|,| |"| |"|,| |"|•|"|,| |"|g|"
+|)|/+0#e000e06&|g|e| +0#0000000&@70
+@4|e+0#af5f00255&|n|d|i|f| +0#0000000&@65
+@4|i+0#af5f00255&|f| +0#0000000&|"+0#e000002&|<|a|r|g|s|>|"| +0#0000000&|=+0#af5f00255&@1| +0#0000000&|"+0#e000002&@1| +0#0000000&@53
+@57|1|4|0|,|0|-|1| @6|9|2|%| 
diff --git a/runtime/syntax/testdir/dumps/vim_ex_command_09.dump b/runtime/syntax/testdir/dumps/vim_ex_command_09.dump
new file mode 100644
index 0000000..1b0afce
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/vim_ex_command_09.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@3|i+0#af5f00255&|f| +0#0000000&|"+0#e000002&|<|a|r|g|s|>|"| +0#0000000&|=+0#af5f00255&@1| +0#0000000&|"+0#e000002&@1| +0#0000000&@53
+@8|:|<+0#e000e06&|l|i|n|e|1|>|,+0#0000000&|<+0#e000e06&|l|i|n|e|2|>|!+0#af5f00255&|c+0#00e0e07&|o|l|u|m|n| +0#0000000&|-+0#af5f00255&|t+0#00e0e07&| +0#0000000&@40
+@4|e+0#af5f00255&|l|s|e| +0#0000000&@66
+@8|:|<+0#e000e06&|l|i|n|e|1|>|,+0#0000000&|<+0#e000e06&|l|i|n|e|2|>|!+0#af5f00255&|c+0#00e0e07&|o|l|u|m|n| +0#0000000&|-+0#af5f00255&|t+0#00e0e07&| +0#0000000&|-+0#af5f00255&|s+0#00e0e07&|'+0#0000000&|<+0#e000e06&|a|r|g|s|>|'+0#0000000&| @29
+@4|e+0#af5f00255&|n|d|i|f| +0#0000000&@65
+@4>i+0#af5f00255&|f| +0#0000000&|"+0#e000002&|<|b|a|n|g|>|"| +0#0000000&|=+0#af5f00255&@1| +0#0000000&|"+0#e000002&|!|"| +0#0000000&@52
+@8|:|<+0#e000e06&|l|i|n|e|1|>|,+0#0000000&|<+0#e000e06&|l|i|n|e|2|>|s+0#af5f00255&|/+0#e000e06&|•+0#0000000&|/+0#e000e06&| +0#0000000&|/+0#e000e06&|g|e| +0#0000000&@42
+@4|e+0#af5f00255&|n|d|i|f| +0#0000000&@65
+|}+0#e000e06&| +0#0000000&@73
+@75
+|c+0#af5f00255&|o|m@1|a|n|d| +0#0000000&|-+0#e000e06&|r|a|n|g|e|=|%+0#00e0003&| +0#0000000&|-+0#e000e06&|n|a|r|g|s|=|?+0#00e0003&| +0#0000000&|-+0#e000e06&|b|a|n|g| +0#0000000&|T+0#0000001#ffff4012|b| +0#0000000#ffffff0|:|<+0#e000e06&|l|i|n|e|1|>|,+0#0000000&|<+0#e000e06&|l|i|n|e|2|>|s+0#af5f00255&|/+0#e000e06&|\+0#0000000&|v|"|[|^|"|]|*|"|/+0#e000e06&|\+0#0000000&|=|s|u|b|s|t|i|t|u|t|e
+|(|s|u|b|m|a|t|c|h|(|0|)|,| |"| |"|,| |"|•|"|,| |"|g|"|)|/+0#e000e06&|g|e| +0#0000000&@43
+@75
+|~+0#4040ff13&| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+| +0#0000000&@56|1|5|7|,|5| @8|B|o|t| 
diff --git a/runtime/syntax/testdir/input/vim_ex_command.vim b/runtime/syntax/testdir/input/vim_ex_command.vim
index 4679b08..8342b68 100644
--- a/runtime/syntax/testdir/input/vim_ex_command.vim
+++ b/runtime/syntax/testdir/input/vim_ex_command.vim
@@ -142,3 +142,22 @@
 
 command! -bang -nargs=* -complete=file Make AsyncRun -program=make @ <args>
 
+
+" Issue #17326 (syntax highlighting breaks with complex :s comamnd)
+
+command -range=% -nargs=? -bang Tb {
+    if "<bang>" == "!"
+        :<line1>,<line2>s/\v"[^"]*"/\=substitute(submatch(0), " ", "•", "g")/ge
+    endif
+    if "<args>" == ""
+        :<line1>,<line2>!column -t
+    else
+        :<line1>,<line2>!column -t -s'<args>'
+    endif
+    if "<bang>" == "!"
+        :<line1>,<line2>s/•/ /ge
+    endif
+}
+
+command -range=% -nargs=? -bang Tb :<line1>,<line2>s/\v"[^"]*"/\=substitute(submatch(0), " ", "•", "g")/ge
+
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 38dc61a..528706f 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -2,7 +2,7 @@
 " Language:	   Vim script
 " Maintainer:	   Hirohito Higashi <h.east.727 ATMARK gmail.com>
 "	   Doug Kearns <dougkearns@gmail.com>
-" Last Change:	   2025 Jul 03
+" Last Change:	   2025 Jul 04
 " Former Maintainer: Charles E. Campbell
 
 " DO NOT CHANGE DIRECTLY.
@@ -865,7 +865,7 @@
       \ matchgroup=vimSep
       \ start="{"
       \ end="^\s*\zs}"
-      \ contains=@vimDefBodyList
+      \ contains=@vimDefBodyList,@vimUserCmdList
 
 syn match	vimDelcommand		"\<delc\%[ommand]\>"	skipwhite nextgroup=vimDelcommandAttr,vimDelcommandName
 syn match	vimDelcommandAttr	contained	"-buffer\>"		skipwhite nextgroup=vimDelcommandName
@@ -1455,7 +1455,7 @@
 syn match	vimNotation	contained	"\%#=1\%(\\\|<lt>\)\=<\%(sid\|nop\|nul\|lt\|drop\)>"				contains=vimBracket
 syn match	vimNotation	contained	"\%#=1\%(\\\|<lt>\)\=<\%(snr\|plug\|cursorhold\|ignore\|cmd\|scriptcmd\|focus\%(gained\|lost\)\)>"	contains=vimBracket
 " syn match	vimNotation	contained	'\%(\\\|<lt>\)\=<C-R>[0-9a-z"%#:.\-=]'he=e-1				contains=vimBracket
-syn match	vimNotation	contained	'\%#=1\%(\\\|<lt>\)\=<\%(q-\)\=\%(line[12]\|count\|bang\|reg\|args\|mods\|f-args\|f-mods\|lt\)>'	contains=vimBracket
+syn match	vimNotation	contained	'\%#=1\%(\\\|<lt>\)\=<\%([fq]-\)\=\%(line[12]\|count\|bang\|reg\|args\|mods\|lt\)>'		contains=vimBracket skipwhite nextgroup=vimSubst1
 syn match	vimNotation	contained	"\%#=1\%(\\\|<lt>\)\=<\%([cas]file\|abuf\|amatch\|cexpr\|cword\|cWORD\|client\|stack\|script\|sf\=lnum\)>"	contains=vimBracket
 syn match	vimNotation	contained	"\%#=1\%(\\\|<lt>\)\=<\%([scamd]-\)\{0,4}char-\%(\d\+\|0\o\+\|0x\x\+\)>"		contains=vimBracket