Commit: runtime(sh): Fix shFunctionTwo and shFunctionFour definitions for Bash

Christian Brabandt <[email protected]> Thu, 6 Aug 2026 21:15:05 +0200
Newsgroups gmane.editors.vim.devel
Message-ID <[email protected]>
runtime(sh): Fix shFunctionTwo and shFunctionFour definitions for Bash

Commit: https://github.com/vim/vim/commit/90e8cb0094508e9e6932adcf42f5003251c47c26
Author: Aliaksei Budavei <[email protected]>
Date:   Thu Aug 6 18:58:42 2026 +0000

    runtime(sh): Fix shFunctionTwo and shFunctionFour definitions for Bash
    
    Claim e.g. "function f () { :; }" as shFunctionTwo while
    observing that parentheses after the function name are
    optional when the "function" word is used and do not delimit
    its body if the latter follows them in "{}" (which should
    not be taken for granted with limited backtracking).
    
    closes: #20878
    
    Signed-off-by: Aliaksei Budavei <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim
index 90a712a00..901923b0d 100644
--- a/runtime/syntax/sh.vim
+++ b/runtime/syntax/sh.vim
@@ -651,9 +651,13 @@ if exists("b:is_bash")
     syn match shFunctionCmdOne	"\%#=1^\s*\zs\%(\%(\<\k\+\|[^()<>|&$;	 ]\+\)\+\)\@>\s*()\ze\_s*\%(\%(for\|case\|select\|if\|while\|until\)\>\|\[\[\s\|((\)"	skipwhite skipnl nextgroup=@shFunctionCmds contains=shFunctionParens
     syn match shFunctionCmdTwo	"\%#=1\%(\%(\<\k\+\>\|[^()<>|&$;	 ]\+\)\+\)\@>\ze\s*\%(()\ze\)\=\_s*\%(\<\%(for\|case\|select\|if\|while\|until\)\>\|\[\[\s\|((\)"	contained skipwhite skipnl nextgroup=@shFunctionCmds contains=shFunctionParens
     syn match shFunctionOne	"\%#=1^\s*\zs\%(\%(\<\k\+\|[^()<>|&$;	 ]\+\)\+\)\@>\s*()\ze\_s*{"	skipwhite skipnl nextgroup=shFunctionExpr contains=shFunctionParens
-    syn match shFunctionTwo	"\%#=1\%(\%(\<\k\+\|[^()<>|&$;	 ]\+\)\+\)\@>\ze\s*\%(()\ze\)\=\_s*{"	contained skipwhite skipnl nextgroup=shFunctionExpr contains=shFunctionParens
     syn match shFunctionThree	"\%#=1^\s*\zs\%(\%(\<\k\+\|[^()<>|&$;	 ]\+\)\+\)\@>\s*()\ze\_s*((\@!"	skipwhite skipnl nextgroup=shFunctionSubSh contains=shFunctionParens
-    syn match shFunctionFour	"\%#=1\%(\%(\<\k\+\|[^()<>|&$;	 ]\+\)\+\)\@>\ze\s*\%(\%(()\ze\)\=\)\@>\_s*((\@!"	contained skipwhite skipnl nextgroup=shFunctionSubSh contains=shFunctionParens
+    " Proof against future changes by inducing priority-driven "backtracking"
+    " between shFunctionFour (goes before) and shFunctionTwo (goes after) so
+    " that e.g. "function f () {}" is still claimed by shFunctionTwo (observe
+    " "f[[:blank:]]()").
+    syn match shFunctionFour	"\%#=1\%(\%(\<\k\+\|[^()<>|&$;	 ]\+\)\+\)\@>\ze\%(\%(\s*()\ze\)\=\)\@>\_s*((\@!"	contained skipwhite skipnl nextgroup=shFunctionSubSh contains=shFunctionParens
+    syn match shFunctionTwo	"\%#=1\%(\%(\<\k\+\|[^()<>|&$;	 ]\+\)\+\)\@>\ze\%(\%(\s*()\ze\)\=\)\@>\_s*{"	contained skipwhite skipnl nextgroup=shFunctionExpr contains=shFunctionParens
     " Claim empty array assignments.
     syn match shArrayEmptyDecl	"\%#=1\ze\%(\<\h\w*=\)\@>()"	transparent nextgroup=shVariable
     " Claim commented out function declaration headers.
diff --git a/runtime/syntax/testdir/dumps/sh_functions_bash_06.dump b/runtime/syntax/testdir/dumps/sh_functions_bash_06.dump
index 7fb4a3970..8ce19c913 100644
--- a/runtime/syntax/testdir/dumps/sh_functions_bash_06.dump
+++ b/runtime/syntax/testdir/dumps/sh_functions_bash_06.dump
@@ -1,8 +1,8 @@
 ||+0#0000e05#a8a8a8255| |}+0#0000001#ffff4012|;+0#0000000#ffffff0| |e+0#af5f00255&|v|a|l| +0#0000000&|"+0#af5f00255&|\+0#e000e06&|f|u+0#e000002&|n|c|t|i|o|n|"+0#af5f00255&| +0#0000000&|$+0#e000e06&|@| +0#0000000&@50
 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72
-| +0#0000e05#a8a8a8255@1|f+0#af5f00255#ffffff0|u|n|c|t|i|o|n| +0#0000000&|f+0#00e0e07&|u|n|c|t|i|o|n|#|f|u|n|c|t|i|o|n| |(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@41
-| +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@3|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@59
-| +0#0000e05#a8a8a8255@1|}+0#e000e06#ffffff0|;+0#0000000&| |e+0#af5f00255&|v|a|l| +0#0000000&|"+0#af5f00255&|f+0#e000002&|u|n|c|t|i|o|n|#|f|u|n|c|t|i|o|n|"+0#af5f00255&| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@40
+|-+0#0000e05#a8a8a8255| |f+0#af5f00255#ffffff0|u|n|c|t|i|o|n| +0#0000000&|f+0#00e0e07&|u|n|c|t|i|o|n|#|f|u|n|c|t|i|o|n| |(+0#e000e06&|)| +0#0000000&|{+0#0000001#ffff4012| +0#0000000#ffffff0@41
+||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@59
+||+0#0000e05#a8a8a8255| |}+0#0000001#ffff4012|;+0#0000000#ffffff0| |e+0#af5f00255&|v|a|l| +0#0000000&|"+0#af5f00255&|f+0#e000002&|u|n|c|t|i|o|n|#|f|u|n|c|t|i|o|n|"+0#af5f00255&| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@40
 | +0#0000e05#a8a8a8255@1> +0#0000000#ffffff0@72
 |-+0#0000e05#a8a8a8255| |f+0#00e0e07#ffffff0|u|n|c|t|i|o|n|#| |(+0#e000e06&|)| +0#0000000&|{+0#0000001#ffff4012| +0#0000000#ffffff0@58
 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@59

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups "vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1ws3YX-00GUb3-BI%40256bit.org.