Repository: spyder-ide/spyder

Issue:
Incorrect syntax highlighting of the words "match" and "case" outside of match/case structures
<!--- **PLEASE READ:** When submitting here, please ensure you've completed the following checklist and checked the boxes to confirm. Issue reports without it may be closed. Thanks! --->

### Issue Report Checklist

* [x] Searched the [issues page](https://github.com/spyder-ide/spyder/issues?q=is%3Aissue) for similar reports
* [x] Read the relevant sections of the [Spyder Troubleshooting Guide](https://github.com/spyder-ide/spyder/wiki/Troubleshooting-Guide-and-FAQ) and followed its advice
* [ ] Reproduced the issue after updating with ``conda update spyder`` (or ``pip``, if not using Anaconda)
* [ ] Could not reproduce inside ``jupyter qtconsole`` (if console-related)
* [x] Tried basic troubleshooting (if a bug/error)
    * [x] Restarted Spyder
    * [ ] Reset preferences with ``spyder --reset``
    * [ ] Reinstalled the latest version of [Anaconda](https://www.anaconda.com/download/)
    * [ ] Tried the other applicable steps from the Troubleshooting Guide
* [x] Completed the **Problem Description**, **Steps to Reproduce** and **Version** sections below


## Problem Description

New syntax highlighting of "match" and "case" in Spyder 5.4.4 highlights those words in the wrong contexts (as well as in the correct contexts). Example in screenshot below: 'match' on line 55 is highlighted when I believe it should not be.

![image](https://github.com/spyder-ide/spyder/assets/141940373/72156db2-e0d8-46e1-8dc4-8c23536b72bf)


### What steps reproduce the problem?

1. open Spyder in dark mode (I did not check light mode)
2. open new .py file
3. typing the words "match" or "case" in the IDE (not iPython console) causes the incorrect syntax highlighting (e.g. an object attribute 'self.case' has both "self" and "case" highlighted -- "self" correctly, "case" incorrectly).

Problem persists with all syntax highlighting themes
(Preferences > Appearance > Syntax Highlighting Themes)


### What is the expected output? What do you see instead?

Expected no highlighting of the words "match" and "case" outside of proper match/case structures.


### Paste Traceback/Error Below (if applicable)
<!--- Copy from error dialog or View > Panes > Internal Console --->
N/A


## Versions
<!--- You can get this information from Help > About Spyder...
or (if Spyder won't launch) the "conda list" command
from the Anaconda Prompt/Terminal/command line. --->

* Spyder version: 5.4.4  (conda)
* Python version: 3.11.3 64-bit
* Qt version: 5.15.2
* PyQt5 version: 5.15.7
* Operating System: Windows-10-10.0.19044-SP0

### Dependencies
<!--- Please go to the menu entry Help > Dependencies,
press the Copy to clipboard button and paste below --->
I'm quite certain these won't matter, but here they are anyway!
```
# Mandatory:
atomicwrites >=1.2.0                                                        :  1.4.0 (OK)
chardet >=2.0.0                                                             :  4.0.0 (OK)
cloudpickle >=0.5.0                                                         :  2.2.1 (OK)
cookiecutter >=1.6.0                                                        :  1.7.3 (OK)
diff_match_patch >=20181111                                                 :  20200713 (OK)
intervaltree >=3.0.2                                                        :  3.1.0 (OK)
IPython >=7.31.1,<9.0.0,!=8.8.0,!=8.9.0,!=8.10.0,!=8.11.0,!=8.12.0,!=8.12.1 :  8.12.2 (OK)
jedi >=0.17.2,<0.19.0                                                       :  0.18.1 (OK)
jellyfish >=0.7                                                             :  0.9.0 (OK)
jsonschema >=3.2.0                                                          :  4.17.3 (OK)
keyring >=17.0.0                                                            :  23.13.1 (OK)
nbconvert >=4.0                                                             :  6.5.4 (OK)
numpydoc >=0.6.0                                                            :  1.5.0 (OK)
paramiko >=2.4.0                                                            :  2.8.1 (OK)
parso >=0.7.0,<0.9.0                                                        :  0.8.3 (OK)
pexpect >=4.4.0                                                             :  4.8.0 (OK)
pickleshare >=0.4                                                           :  0.7.5 (OK)
psutil >=5.3                                                                :  5.9.0 (OK)
pygments >=2.0                                                              :  2.15.1 (OK)
pylint >=2.5.0,<3.0                                                         :  2.16.2 (OK)
pylint_venv >=3.0.2                                                         :  3.0.2 (OK)
pyls_spyder >=0.4.0                                                         :  0.4.0 (OK)
pylsp >=1.7.4,<1.8.0                                                        :  1.7.4 (OK)
pylsp_black >=1.2.0,<3.0.0                                                  :  1.2.1 (OK)
qdarkstyle >=3.0.2,<3.2.0                                                   :  3.0.2 (OK)
qstylizer >=0.2.2                                                           :  0.2.2 (OK)
qtawesome >=1.2.1                                                           :  1.2.2 (OK)
qtconsole >=5.4.2,<5.5.0                                                    :  5.4.2 (OK)
qtpy >=2.1.0                                                                :  2.2.0 (OK)
rtree >=0.9.7                                                               :  1.0.1 (OK)
setuptools >=49.6.0                                                         :  67.8.0 (OK)
sphinx >=0.6.6                                                              :  5.0.2 (OK)
spyder_kernels >=2.4.4,<2.5.0                                               :  2.4.4 (OK)
textdistance >=4.2.0                                                        :  4.2.1 (OK)
three_merge >=0.1.1                                                         :  0.1.1 (OK)
watchdog >=0.10.3                                                           :  2.1.6 (OK)
zmq >=22.1.0                                                                :  25.1.0 (OK)

# Optional:
cython >=0.21                                                               :  None (NOK)
matplotlib >=3.0.0                                                          :  3.7.1 (OK)
numpy >=1.7                                                                 :  1.24.3 (OK)
pandas >=1.1.1                                                              :  1.5.3 (OK)
scipy >=0.17.0                                                              :  1.10.1 (OK)
sympy >=0.7.3                                                               :  1.11.1 (OK)

```



Discussion:
Hey @nukamoi, thanks for reporting. You're right about this and we'll try to fix it in our next version (5.5.0), to be released in a couple of months.

Resolving patch:
diff --git a/spyder/utils/syntaxhighlighters.py b/spyder/utils/syntaxhighlighters.py
--- a/spyder/utils/syntaxhighlighters.py
+++ b/spyder/utils/syntaxhighlighters.py
@@ -14,7 +14,6 @@
 import keyword
 import os
 import re
-import weakref
 
 # Third party imports
 from pygments.lexer import RegexLexer, bygroups
@@ -436,11 +435,9 @@ def make_python_patterns(additional_keywords=[], additional_builtins=[]):
                                 r"\bcls\b",
                                 (r"^\s*@([a-zA-Z_][a-zA-Z0-9_]*)"
                                      r"(\.[a-zA-Z_][a-zA-Z0-9_]*)*")])
-    number_regex = [r"\b[+-]?[0-9]+[lLjJ]?\b",
-                    r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b",
-                    r"\b[+-]?0[oO][0-7]+[lL]?\b",
-                    r"\b[+-]?0[bB][01]+[lL]?\b",
-                    r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?[jJ]?\b"]
+    match_kw = r"\s*(?P<match_kw>match)(?=\s+.+:)"
+    case_kw = r"\s+(?P<case_kw>case)(?=\s+.+:)"
+
     prefix = "r|u|R|U|f|F|fr|Fr|fR|FR|rf|rF|Rf|RF|b|B|br|Br|bR|BR|rb|rB|Rb|RB"
     sqstring =     r"(\b(%s))?'[^'\\\n]*(\\.[^'\\\n]*)*'?" % prefix
     dqstring =     r'(\b(%s))?"[^"\\\n]*(\\.[^"\\\n]*)*"?' % prefix
@@ -454,6 +451,7 @@ def make_python_patterns(additional_keywords=[], additional_builtins=[]):
                    % prefix
     uf_dq3string = r'(\b(%s))?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(\\)?(?!""")$' \
                    % prefix
+
     # Needed to achieve correct highlighting in Python 3.6+
     # See spyder-ide/spyder#7324.
     # Based on
@@ -462,14 +460,15 @@ def make_python_patterns(additional_keywords=[], additional_builtins=[]):
     # In order: Hexnumber, Binnumber, Octnumber, Decnumber,
     # Pointfloat + Exponent, Expfloat, Imagnumber
     number_regex = [
-            r"\b[+-]?0[xX](?:_?[0-9A-Fa-f])+[lL]?\b",
-            r"\b[+-]?0[bB](?:_?[01])+[lL]?\b",
-            r"\b[+-]?0[oO](?:_?[0-7])+[lL]?\b",
-            r"\b[+-]?(?:0(?:_?0)*|[1-9](?:_?[0-9])*)[lL]?\b",
-            r"\b((\.[0-9](?:_?[0-9])*')|\.[0-9](?:_?[0-9])*)"
-            "([eE][+-]?[0-9](?:_?[0-9])*)?[jJ]?\b",
-            r"\b[0-9](?:_?[0-9])*([eE][+-]?[0-9](?:_?[0-9])*)?[jJ]?\b",
-            r"\b[0-9](?:_?[0-9])*[jJ]\b"]
+        r"\b[+-]?0[xX](?:_?[0-9A-Fa-f])+[lL]?\b",
+        r"\b[+-]?0[bB](?:_?[01])+[lL]?\b",
+        r"\b[+-]?0[oO](?:_?[0-7])+[lL]?\b",
+        r"\b[+-]?(?:0(?:_?0)*|[1-9](?:_?[0-9])*)[lL]?\b",
+        r"\b((\.[0-9](?:_?[0-9])*')|\.[0-9](?:_?[0-9])*)"
+        "([eE][+-]?[0-9](?:_?[0-9])*)?[jJ]?\b",
+        r"\b[0-9](?:_?[0-9])*([eE][+-]?[0-9](?:_?[0-9])*)?[jJ]?\b",
+        r"\b[0-9](?:_?[0-9])*[jJ]\b"
+    ]
     number = any("number", number_regex)
 
     string = any("string", [sq3string, dq3string, sqstring, dqstring])
@@ -479,7 +478,7 @@ def make_python_patterns(additional_keywords=[], additional_builtins=[]):
     ufstring4 = any("uf_dq3string", [uf_dq3string])
     ufstring5 = any("ufe_sqstring", [ufe_sqstring])
     ufstring6 = any("ufe_dqstring", [ufe_dqstring])
-    return "|".join([instance, kw, builtin, comment,
+    return "|".join([instance, kw, builtin, comment, match_kw, case_kw,
                      ufstring1, ufstring2, ufstring3, ufstring4, ufstring5,
                      ufstring6, string, number, any("SYNC", [r"\n"])])
 
@@ -505,16 +504,18 @@ def get_code_cell_name(text):
 class PythonSH(BaseSH):
     """Python Syntax Highlighter"""
     # Syntax highlighting rules:
-    add_kw = ['async', 'await', 'match', 'case']
+    add_kw = ['async', 'await']
     PROG = re.compile(make_python_patterns(additional_keywords=add_kw), re.S)
     IDPROG = re.compile(r"\s+(\w+)", re.S)
     ASPROG = re.compile(r"\b(as)\b")
+
     # Syntax highlighting states (from one text block to another):
     (NORMAL, INSIDE_SQ3STRING, INSIDE_DQ3STRING,
      INSIDE_SQSTRING, INSIDE_DQSTRING,
      INSIDE_NON_MULTILINE_STRING) = list(range(6))
     DEF_TYPES = {"def": OutlineExplorerData.FUNCTION,
                  "class": OutlineExplorerData.CLASS}
+
     # Comments suitable for Outline Explorer
     OECOMMENT = re.compile(r'^(# ?--[-]+|##[#]+ )[ -]*[^- ]+')
 
@@ -529,9 +530,10 @@ def highlight_match(self, text, match, key, value, offset,
                         state, import_stmt, oedata):
         """Highlight a single match."""
         start, end = get_span(match, key)
-        start = max([0, start+offset])
-        end = max([0, end+offset])
+        start = max([0, start + offset])
+        end = max([0, end + offset])
         length = end - start
+
         if key == "uf_sq3string":
             self.setFormat(start, length, self.formats["string"])
             state = self.INSIDE_SQ3STRING
@@ -547,6 +549,8 @@ def highlight_match(self, text, match, key, value, offset,
         elif key in ["ufe_sqstring", "ufe_dqstring"]:
             self.setFormat(start, length, self.formats["string"])
             state = self.INSIDE_NON_MULTILINE_STRING
+        elif key in ["match_kw", "case_kw"]:
+            self.setFormat(start, length, self.formats["keyword"])
         else:
             self.setFormat(start, length, self.formats[key])
             if key == "comment":


Test patch: