userdiff: extend Bash pattern to cover more shell function forms

The previous function regex required explicit matching of function bodies using `{`, `(`, `((`, or `[[`, which caused several issues: - It failed to capture valid functions where `{` was on the next line due to line continuation (`\`). - It did not recognize functions with single command body, such as `x () echo hello`. Replacing the function body matching logic with `.*$`, ensures that everything on the function definition line is captured. Additionally, the word regex is refined to better recognize shell syntax, including additional parameter expansion operators and command-line options. Signed-off-by: Moumita Dhar <dhar61595@gmail.com> Acked-by: Johannes Sixt <j6t@kdbg.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Moumita Dhar committed May 16, 2025 at 20:15 UTC ea8a71b40d3fdc91180b951c829cdf41bb6f7da0
8 files changed +128 -8
t/t4018/bash-bashism-style-complete-line-capture new
+4
@@ -0,0 +1,4 @@
1 +function myfunc # RIGHT
2 +{
3 + echo 'ChangeMe'
4 +}
t/t4018/bash-posix-style-complete-line-capture new
+4
@@ -0,0 +1,4 @@
1 +func() { # RIGHT
2 +
3 + ChangeMe
4 +}
t/t4018/bash-posix-style-single-command-function new
+3
@@ -0,0 +1,3 @@
1 +RIGHT() echo "hello"
2 +
3 + ChangeMe
t/t4034-diff-words.sh
+1
@@ -320,6 +320,7 @@ test_expect_success 'unset default driver' '
320
321 test_language_driver ada
322 test_language_driver bibtex
323 +test_language_driver bash
324 test_language_driver cpp
325 test_language_driver csharp
326 test_language_driver css
t/t4034/bash/expect new
+36
@@ -0,0 +1,36 @@
1 +<BOLD>diff --git a/pre b/post<RESET>
2 +<BOLD>index 09ac008..60ba6a2 100644<RESET>
3 +<BOLD>--- a/pre<RESET>
4 +<BOLD>+++ b/post<RESET>
5 +<CYAN>@@ -1,31 +1,31 @@<RESET>
6 +<RED>my_var<RESET><GREEN>new_var<RESET>=10
7 +x=<RED>123<RESET><GREEN>456<RESET>
8 +echo <RED>$1<RESET><GREEN>$2<RESET>
9 +echo <RED>$USER<RESET><GREEN>$USERNAME<RESET>
10 +${<RED>HOME<RESET><GREEN>HOMEDIR<RESET>}
11 +((a<RED>+<RESET><GREEN>+=<RESET>b))
12 +((a<RED>*<RESET><GREEN>*=<RESET>b))
13 +((a<RED>/<RESET><GREEN>/=<RESET>b))
14 +((a<RED>%<RESET><GREEN>%=<RESET>b))
15 +((a<RED>|<RESET><GREEN>|=<RESET>b))
16 +((a<RED>^<RESET><GREEN>^=<RESET>b))
17 +((a<RED>=<RESET><GREEN>==<RESET>b))
18 +((a<RED>!<RESET><GREEN>!=<RESET>b))
19 +((a<RED><<RESET><GREEN><=<RESET>b))
20 +((a<RED>><RESET><GREEN>>=<RESET>b))
21 +$((a<RED><<RESET><GREEN><<<RESET>b))
22 +$((a<RED>><RESET><GREEN>>><RESET>b))
23 +$((a<RED>&<RESET><GREEN>&&<RESET>b))
24 +$((a<RED>|<RESET><GREEN>||<RESET>b))
25 +${a<RED>:<RESET><GREEN>:-<RESET>b}
26 +${a<RED>:<RESET><GREEN>:=<RESET>b}
27 +${a<RED>:<RESET><GREEN>:+<RESET>b}
28 +${a<RED>:<RESET><GREEN>:?<RESET>b}
29 +${a<RED>#<RESET><GREEN>##<RESET>*/}
30 +${a<RED>%<RESET><GREEN>%%<RESET>.*}
31 +${a<RED>^<RESET><GREEN>^^<RESET>}
32 +${a<RED>,<RESET><GREEN>,,<RESET>}
33 +${<GREEN>!<RESET>a}
34 +${a[<RED>*<RESET><GREEN>@<RESET>]}
35 +ls <RED>-a<RESET><GREEN>-x<RESET>
36 +ls <RED>--all<RESET><GREEN>--color<RESET>
t/t4034/bash/post new
+31
@@ -0,0 +1,31 @@
1 +new_var=10
2 +x=456
3 +echo $2
4 +echo $USERNAME
5 +${HOMEDIR}
6 +((a+=b))
7 +((a*=b))
8 +((a/=b))
9 +((a%=b))
10 +((a|=b))
11 +((a^=b))
12 +((a==b))
13 +((a!=b))
14 +((a<=b))
15 +((a>=b))
16 +$((a<<b))
17 +$((a>>b))
18 +$((a&&b))
19 +$((a||b))
20 +${a:-b}
21 +${a:=b}
22 +${a:+b}
23 +${a:?b}
24 +${a##*/}
25 +${a%%.*}
26 +${a^^}
27 +${a,,}
28 +${!a}
29 +${a[@]}
30 +ls -x
31 +ls --color
t/t4034/bash/pre new
+31
@@ -0,0 +1,31 @@
1 +my_var=10
2 +x=123
3 +echo $1
4 +echo $USER
5 +${HOME}
6 +((a+b))
7 +((a*b))
8 +((a/b))
9 +((a%b))
10 +((a|b))
11 +((a^b))
12 +((a=b))
13 +((a!b))
14 +((a<b))
15 +((a>b))
16 +$((a<b))
17 +$((a>b))
18 +$((a&b))
19 +$((a|b))
20 +${a:b}
21 +${a:b}
22 +${a:b}
23 +${a:b}
24 +${a#*/}
25 +${a%.*}
26 +${a^}
27 +${a,}
28 +${a}
29 +${a[*]}
30 +ls -a
31 +ls --all
userdiff.c
+18 -8
@@ -59,20 +59,30 @@ PATTERNS("bash",
59 "("
60 "("
61 /* POSIX identifier with mandatory parentheses */
62 - "[a-zA-Z_][a-zA-Z0-9_]*[ \t]*\\([ \t]*\\))"
62 + "([a-zA-Z_][a-zA-Z0-9_]*[ \t]*\\([ \t]*\\))"
63 "|"
64 /* Bashism identifier with optional parentheses */
65 - "(function[ \t]+[a-zA-Z_][a-zA-Z0-9_]*(([ \t]*\\([ \t]*\\))|([ \t]+))"
65 + "(function[ \t]+[a-zA-Z_][a-zA-Z0-9_]*(([ \t]*\\([ \t]*\\))|([ \t]+)))"
66 ")"
67 - /* Optional whitespace */
68 - "[ \t]*"
69 - /* Compound command starting with `{`, `(`, `((` or `[[` */
70 - "(\\{|\\(\\(?|\\[\\[)"
67 + /* Everything after the function header is captured */
68 + ".*$"
69 /* End of captured text */
70 ")",
71 /* -- */
74 - /* Characters not in the default $IFS value */
75 - "[^ \t]+"),
72 + /* Identifiers: variable and function names */
73 + "[a-zA-Z_][a-zA-Z0-9_]*"
74 + /* Shell variables: $VAR, ${VAR} */
75 + "|\\$[a-zA-Z0-9_]+|\\$\\{"
76 + /*Command list separators and redirection operators */
77 + "|\\|\\||&&|<<|>>"
78 + /* Operators ending in '=' (comparison + compound assignment) */
79 + "|==|!=|<=|>=|[-+*/%&|^]="
80 + /* Additional parameter expansion operators */
81 + "|:=|:-|:\\+|:\\?|##|%%|\\^\\^|,,"
82 + /* Command-line options (to avoid splitting -option) */
83 + "|[-a-zA-Z0-9_]+"
84 + /* Brackets and grouping symbols */
85 + "|\\(|\\)|\\{|\\}|\\[|\\]"),
86 PATTERNS("bibtex",
87 "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
88 /* -- */