[gtksourceview] sh.lang: Complement for parenthesized subshells



commit d47b53da58583f717835689ec88a241d73f34f76
Author: neyfag <11970-neyfag users noreply gitlab gnome org>
Date:   Mon Nov 18 12:15:39 2019 +0100

    sh.lang: Complement for parenthesized subshells

 data/language-specs/sh.lang       | 214 ++++++++++++++++++++++++++++++--------
 tests/syntax-highlighting/file.sh |   9 +-
 2 files changed, 179 insertions(+), 44 deletions(-)
---
diff --git a/data/language-specs/sh.lang b/data/language-specs/sh.lang
index 54461801..bfca0f93 100644
--- a/data/language-specs/sh.lang
+++ b/data/language-specs/sh.lang
@@ -42,13 +42,16 @@
     <style id="subshell"            name="Subshell"            map-to="def:preprocessor"/>
   </styles>
 
-<keyword-char-class>[^()`&amp;|;]</keyword-char-class>
-
   <definitions>
 
     <define-regex id="command-name">[a-zA-Z_][a-zA-Z0-9_.-]*</define-regex>
     <define-regex id="identifier">[a-zA-Z_][a-zA-Z0-9_]*</define-regex>
     <define-regex id="unescaped-space">(?&lt;!\\)\s</define-regex>
+    <define-regex id="brackets-prefix">(?&lt;=\%{unescaped-space}|[`&amp;|;]|^)</define-regex>
+    <define-regex id="brackets-suffix">(?=\s|[)`&amp;|;]|$)</define-regex>
+    <define-regex id="command-suffix">(?=[)`&amp;|;])</define-regex>
+    <define-regex id="lb">(?&lt;=\%{unescaped-space}|[()`&amp;|;]|^)</define-regex>
+    <define-regex id="rb">(?=\s|[()`&amp;|;]|$)</define-regex>
 
     <!-- we cannot use def:shell-like-comment, because
          m4.lang needs to replace this context -->
@@ -71,6 +74,7 @@
         <context ref="variable"/>
         <context ref="backtick-subshell"/>
         <context ref="command-substitution"/>
+        <context ref="arithmetic-expansion"/>
       </include>
     </context>
 
@@ -80,8 +84,8 @@
     </context>
 
     <context id="subshell" style-ref="subshell">
-      <start>(?&lt;=\%{unescaped-space}|[`&amp;|;]|^)\((?!\()</start>
-      <end>\)(?=\s|[`&amp;|;]|$)</end>
+      <start>\%{brackets-prefix}\((?!\()</start>
+      <end>\)\%{brackets-suffix}</end>
       <include>
         <context sub-pattern="0" where="start" style-ref="keyword"/>
         <context sub-pattern="0" where="end" style-ref="keyword"/>
@@ -99,6 +103,114 @@
       </include>
     </context>
 
+    <context id="process-substitution" style-ref="subshell">
+      <start>(?&lt;=\%{unescaped-space}|^)[&lt;&gt;]\((?!\()</start>
+      <end>\)\%{brackets-suffix}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="keyword"/>
+        <context sub-pattern="0" where="end" style-ref="keyword"/>
+        <context ref="sh"/>
+      </include>
+    </context>
+
+    <context id="reference-by-name" style-ref="variable">
+      <match>\%{identifier}</match>
+    </context>
+
+    <context id="numeral-system">
+      <include>
+        <context id="base-n-integer" style-ref="def:base-n-integer">
+          <match extended="true">
+            (?&lt;![\w\.]) ([2-9]|[1-5][0-9]|6[0-4])\#[0-9a-zA-Z_@]+ (?![\w\.])
+          </match>
+        </context>
+        <context ref="def:hexadecimal"/>
+        <context ref="def:octal"/>
+        <context ref="def:decimal"/>
+      </include>
+    </context>
+
+    <context id="evaluation-nesting">
+      <include>
+        <context ref="def:escape"/>
+        <context ref="def:line-continue"/>
+        <context ref="single-quoted-string"/>
+        <context ref="double-quoted-string"/>
+        <context ref="backtick-subshell"/>
+        <context ref="command-substitution"/>
+        <context ref="variable"/>
+        <context ref="reference-by-name"/>
+        <context ref="numeral-system"/>
+      </include>
+    </context>
+    
+    <!-- Defined like this, "precedence" also emulates the inclusion of
+    "arithmetic-evaluation" and "arithmetic-expansion" -->
+    <context id="evaluation-precedence">
+      <start>\$?\(</start>
+      <end>\)</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="keyword"/>
+        <context sub-pattern="0" where="end" style-ref="keyword"/>
+        <context ref="evaluation-nesting"/>
+        <context ref="evaluation-precedence"/>
+      </include>
+    </context>
+
+    <context id="arithmetic-evaluation">
+      <start>\%{brackets-prefix}\({2}(?!\()</start>
+      <end>\){2}\%{brackets-suffix}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="keyword"/>
+        <context sub-pattern="0" where="end" style-ref="keyword"/>
+        <context ref="evaluation-nesting"/>
+        <context ref="evaluation-precedence"/>
+      </include>
+    </context>
+
+    <context id="arithmetic-expansion">
+      <start>\$\({2}(?!\()</start>
+      <end>\){2}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="keyword"/>
+        <context sub-pattern="0" where="end" style-ref="keyword"/>
+        <context id="expansion-nesting">
+          <include>
+            <context ref="def:line-continue"/>
+            <context ref="double-quoted-string"/>
+            <context ref="backtick-subshell"/>
+            <context ref="command-substitution"/>
+            <context ref="variable"/>
+            <context ref="reference-by-name"/>
+            <context ref="numeral-system"/>
+          </include>
+        </context>
+        <context id="expansion-precedence">
+          <start>\$?\(</start>
+          <end>\)</end>
+          <include>
+            <context sub-pattern="0" where="start" style-ref="keyword"/>
+            <context sub-pattern="0" where="end" style-ref="keyword"/>
+            <context ref="expansion-nesting"/>
+            <context ref="expansion-precedence"/>
+          </include>
+        </context>
+      </include>
+    </context>
+
+    <!-- Exclusions above in <start>/<end> tags enable highlighting only for
+      unambiguous nesting of subshells and arithmetic evaluations/expansions.
+      This covers the remainder and array definition, with default highlighting -->
+    <context id="default-subshell">
+      <start>\(</start>
+      <end>\)</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="keyword"/>
+        <context sub-pattern="0" where="end" style-ref="keyword"/>
+        <context ref="sh"/>
+      </include>
+    </context>
+
     <!-- Treated separately, not including itself nor command-like contexts
       (see https://gitlab.gnome.org/GNOME/gtksourceview/issues/94) -->
     <context id="backtick-subshell" style-ref="subshell">
@@ -112,6 +224,11 @@
         <context ref="double-quoted-string"/>
         <context ref="subshell"/>
         <context ref="command-substitution"/>
+        <context ref="process-substitution"/>
+        <context ref="arithmetic-evaluation"/>
+        <context ref="arithmetic-expansion"/>
+        <!-- Must be included after all other parenthesized subshells -->
+        <context ref="default-subshell"/>
         <context ref="punctuator"/>
         <context ref="function"/>
         <context ref="here-doc"/>
@@ -119,7 +236,9 @@
         <context ref="operator"/>
         <context ref="variable"/>
         <context ref="stand-alone-variable-definition"/>
-        <context ref="built-in-command"/>
+        <context ref="reserved-word"/>
+        <context ref="case-command"/>
+        <context ref="for-command"/>
       </include>
     </context>
 
@@ -128,7 +247,7 @@
     </context>
 
     <context id="function" style-ref="function">
-      <match>(?&lt;=\%{unescaped-space}|\%[)\%{command-name}\s*\(\)</match>
+      <match>\%{lb}\%{command-name}\s*\(\)</match>
     </context>
 
     <context id="redirection" style-ref="others">
@@ -219,6 +338,7 @@
         <context ref="variable"/>
         <context ref="backtick-subshell"/>
         <context ref="command-substitution"/>
+        <context ref="arithmetic-expansion"/>
       </include>
     </context>
 
@@ -230,31 +350,28 @@
     </context>
 
     <context id="stand-alone-variable-definition">
-      <match>(?&lt;=\%{unescaped-space}|\%[)(\%{identifier})\+?=</match>
+      <match>\%{lb}(\%{identifier})\+?=</match>
       <include>
         <context sub-pattern="1" style-ref="variable-definition"/>
       </include>
     </context>
 
     <context id="variable-definition-command" end-at-line-end="true">
-      <start extended="true">
-        (?&lt;=\%{unescaped-space}|\%[)
-        (declare|local|typeset|readonly|export)
-        (?=\s|\%])
-      </start>
-      <end>\%]</end>
+      <start>\%{lb}(declare|local|typeset|readonly|export)\%{rb}</start>
+      <end>\%{command-suffix}</end>
       <include>
         <context sub-pattern="0" where="start" style-ref="keyword"/>
-        <context sub-pattern="0" where="end" style-ref="keyword"/>
         <context ref="def:escape"/>
         <context ref="def:line-continue"/>
         <context ref="line-comment"/>
         <context ref="single-quoted-string"/>
         <context ref="double-quoted-string"/>
-        <context ref="here-doc"/>
         <context ref="redirection"/>
         <context ref="backtick-subshell"/>
         <context ref="command-substitution"/>
+        <context ref="arithmetic-expansion"/>
+        <!-- Must be included after all other parenthesized subshells -->
+        <context ref="default-subshell"/>
         <context ref="variable"/>
         <context style-ref="variable-definition">
           <match>(?&lt;=\%{unescaped-space})\%{identifier}</match>
@@ -269,20 +386,15 @@
       </include>
     </context>
 
-    <context id="built-in-command-1" style-ref="keyword">
-      <prefix>(?&lt;=\%{unescaped-space}|\%[)</prefix>
-      <suffix>(?=\s|\%])</suffix>
+    <!-- To prevent conflicts, keywords below should be removed from the list
+         before being used in any container context -->
+    <context id="reserved-word" style-ref="keyword">
+      <prefix>\%{lb}</prefix>
+      <suffix>\%{rb}</suffix>
       <keyword>\!</keyword>
       <keyword>\{</keyword>
       <keyword>\}</keyword>
       <keyword>\:</keyword>
-    </context>
-
-    <!-- To prevent conflicts, keywords below should be removed from the list
-         before being used in any container context -->
-    <context id="built-in-command-2" style-ref="keyword">
-      <prefix>(?&lt;=\%{unescaped-space}|\%[)</prefix>
-      <suffix>(?=\s|\%])</suffix>
       <keyword>do</keyword>
       <keyword>done</keyword>
       <keyword>elif</keyword>
@@ -300,14 +412,14 @@
     </context>
 
     <context id="case-command">
-      <start>(?&lt;=\%{unescaped-space}|\%[)case(?=\s)</start>
+      <start>\%{lb}case(?=\s)</start>
       <include>
         <context sub-pattern="0" where="start" style-ref="keyword"/>
         <!-- Must be included before any reference context
           (replaces <end> tag, to enforce priority) -->
         <context style-ref="keyword" end-parent="true">
-          <prefix>(?&lt;=\%{unescaped-space}|\%[)</prefix>
-          <suffix>(?=\s|\%])</suffix>
+          <prefix>\%{lb}</prefix>
+          <suffix>\%{rb}</suffix>
           <keyword>esac</keyword>
         </context>
         <context ref="sh"/>
@@ -315,7 +427,7 @@
     </context>
 
     <context id="for-command" end-at-line-end="true">
-      <start>(?&lt;=\%{unescaped-space}|\%[)for(?=\s)</start>
+      <start>\%{lb}for(?=\s)</start>
       <end>(?=\S)</end>
       <include>
         <context sub-pattern="0" where="start" style-ref="keyword"/>
@@ -326,21 +438,33 @@
       </include>
     </context>
 
+    <context id="let-command" end-at-line-end="true">
+      <start>\%{lb}let(?=\s)</start>
+      <end>\%{command-suffix}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="keyword"/>
+        <context ref="line-comment"/>
+        <context ref="redirection"/>
+        <context ref="evaluation-nesting"/>
+        <context ref="evaluation-precedence"/>
+      </include>
+    </context>
+
     <context id="built-in-command">
       <include>
-        <context ref="built-in-command-1"/>
-        <context ref="built-in-command-2"/>
+        <context ref="reserved-word"/>
         <context ref="case-command"/>
         <context ref="for-command"/>
+        <context ref="let-command"/>
       </include>
     </context>
 
     <context id="generic-command" end-at-line-end="true">
-      <start extended="true">
-        (?&lt;=\%{unescaped-space}|\%[)
-        (?=\%{command-name}(\s|\%]))
-      </start>
-      <end>(?=[)`&amp;|;])</end>
+      <start>\%{lb}(?=\%{command-name}\%{rb})</start>
+      <!-- \%{command-suffix} + '(', to prevent false positives in ambiguous
+        nesting of subshells and arithmetic evaluations/expansions,
+        like (((a-(b+c)))) -->
+      <end>\%{command-suffix}|(?=\()</end>
       <include>
         <!-- Must be included first -->
         <context once-only="true">
@@ -348,7 +472,7 @@
           <include>
             <context style-ref="keyword" end-parent="true">
               <prefix></prefix>
-              <suffix></suffix>
+              <suffix>\%{rb}</suffix>
               <keyword>\.</keyword>
               <keyword>alias</keyword>
               <keyword>bg</keyword>
@@ -374,7 +498,6 @@
               <keyword>help</keyword>
               <keyword>history</keyword>
               <keyword>jobs</keyword>
-              <keyword>let</keyword>
               <keyword>logout</keyword>
               <keyword>popd</keyword>
               <keyword>printf</keyword>
@@ -396,7 +519,7 @@
             </context>
             <context style-ref="common-command" end-parent="true">
               <prefix></prefix>
-              <suffix></suffix>
+              <suffix>\%{rb}</suffix>
               <keyword>ar</keyword>
               <keyword>awk</keyword>
               <keyword>basename</keyword>
@@ -573,10 +696,12 @@
         <context ref="line-comment"/>
         <context ref="single-quoted-string"/>
         <context ref="double-quoted-string"/>
-        <context ref="here-doc"/>
-        <context ref="redirection"/>
         <context ref="backtick-subshell"/>
         <context ref="command-substitution"/>
+        <context ref="process-substitution"/>
+        <context ref="arithmetic-expansion"/>
+        <context ref="here-doc"/>
+        <context ref="redirection"/>
         <context ref="variable"/>
       </include>
     </context>
@@ -589,9 +714,14 @@
         <context ref="def:escape"/>
         <context ref="single-quoted-string"/>
         <context ref="double-quoted-string"/>
-        <context ref="subshell"/>
         <context ref="backtick-subshell"/>
+        <context ref="subshell"/>
         <context ref="command-substitution"/>
+        <context ref="process-substitution"/>
+        <context ref="arithmetic-evaluation"/>
+        <context ref="arithmetic-expansion"/>
+        <!-- Must be included after all other parenthesized subshells -->
+        <context ref="default-subshell"/>
         <context ref="punctuator"/>
         <context ref="function"/>
         <context ref="here-doc"/>
diff --git a/tests/syntax-highlighting/file.sh b/tests/syntax-highlighting/file.sh
index 6361c083..8a1529e0 100644
--- a/tests/syntax-highlighting/file.sh
+++ b/tests/syntax-highlighting/file.sh
@@ -41,7 +41,7 @@ if var=$(cmd); then some; fi
 test -f xxx && var=xxx || var=yyy
 echo text | var=xxx cmd & var=yyy
 declare -i '-r' "-x" var1=val1 var2=$val1 var3=`cmd1` \
-  var4=$(cmd2) var5=xxx\ yyy var6 # Comment
+  var4=$(cmd2) var5=xxx\ yyy var6=("${ar[@]}") var7 # Comment
 var+=xxx; (var=yyy); { var=zzz; }
 case $1 in
   item) var=xxx;;
@@ -60,7 +60,7 @@ arg; do echo $arg; done
 
 # Generic command (e.g. echo)
 echo for case grep $var ${var/x/y} $(cmd) `cmd` \
-  'a' "b" \\ | grep 'pattern'
+  'a' "b" \\ | grep 'pattern' > >(tee file)
 echo echo; echo echo & echo echo
 echo
 
@@ -73,3 +73,8 @@ echo
 'no special characters'
 "$var, ${var/x/y}, $(cmd), `cmd`, \
 \$, \`, \", \\, \ "
+
+# Arithmetic evaluation/expansion
+var1=$((var2+2#101+$(cmd)+($var3+ \
+  "$var4")/0x1f))
+let var1='1'+010-23+`cmd`+var2 # Comment


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]