File indexing completed on 2024-06-23 04:28:04

0001 """
0002 SPDX-FileCopyrightText: 2017 Eliakin Costa <eliakim170@gmail.com>
0003 
0004 SPDX-License-Identifier: GPL-2.0-or-later
0005 """
0006 # syntax.py: taken from https://wiki.python.org/moin/PyQt/Python%20syntax%20highlighting
0007 
0008 
0009 from PyQt5.QtCore import QRegExp
0010 from PyQt5.QtGui import QSyntaxHighlighter
0011 
0012 
0013 class PythonHighlighter (QSyntaxHighlighter):
0014 
0015     """Syntax highlighter for the Python language.
0016     """
0017     # Python keywords
0018     keywords = [
0019         'and', 'assert', 'break', 'class', 'continue', 'def',
0020         'del', 'elif', 'else', 'except', 'exec', 'finally',
0021         'for', 'from', 'global', 'if', 'import', 'in',
0022         'is', 'lambda', 'not', 'or', 'pass', 'print',
0023         'raise', 'return', 'try', 'while', 'yield',
0024         'None', 'True', 'False',
0025     ]
0026 
0027     # Python operators
0028     operators = [
0029         '=',
0030         # Comparison
0031         '==', '!=', '<', '<=', '>', '>=',
0032         # Arithmetic
0033         '\+', '-', '\*', '/', '//', '\%', '\*\*',
0034         # In-place
0035         '\+=', '-=', '\*=', '/=', '\%=',
0036         # Bitwise
0037         '\^', '\|', '\&', '\~', '>>', '<<',
0038     ]
0039 
0040     # Python braces
0041     braces = [
0042         '\{', '\}', '\(', '\)', '\[', '\]',
0043     ]
0044 
0045     def __init__(self, document, syntaxStyle):
0046         QSyntaxHighlighter.__init__(self, document)
0047 
0048         self.syntaxStyle = syntaxStyle
0049         self.document = document
0050 
0051         # Multi-line strings (expression, flag, style)
0052         # FIXME: The triple-quotes in these two lines will mess up the
0053         # syntax highlighting from this point onward
0054         self.tri_single = (QRegExp(r"""'''(?!")"""), 1, 'string2')
0055         self.tri_double = (QRegExp(r'''"""(?!')'''), 2, 'string2')
0056 
0057         rules = []
0058 
0059         # Keyword, operator, and brace rules
0060         rules += [(r'\b%s\b' % w, 0, 'keyword')
0061                   for w in PythonHighlighter.keywords]
0062         rules += [(r'%s' % o, 0, 'operator')
0063                   for o in PythonHighlighter.operators]
0064         rules += [(r'%s' % b, 0, 'brace')
0065                   for b in PythonHighlighter.braces]
0066 
0067         # All other rules
0068         rules += [
0069             # 'self'
0070             (r'\bself\b', 0, 'self'),
0071 
0072             # Double-quoted string, possibly containing escape sequences
0073             (r'"[^"\\]*(\\.[^"\\]*)*"', 0, 'string'),
0074             # Single-quoted string, possibly containing escape sequences
0075             (r"'[^'\\]*(\\.[^'\\]*)*'", 0, 'string'),
0076 
0077             # 'def' followed by an identifier
0078             (r'\bdef\b\s*(\w+)', 1, 'defclass'),
0079             # 'class' followed by an identifier
0080             (r'\bclass\b\s*(\w+)', 1, 'defclass'),
0081 
0082             # From '#' until a newline
0083             (r'#[^\n]*', 0, 'comment'),
0084 
0085             # Numeric literals
0086             (r'\b[+-]?[0-9]+[lL]?\b', 0, 'numbers'),
0087             (r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, 'numbers'),
0088             (r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0, 'numbers'),
0089         ]
0090 
0091         # Build a QRegExp for each pattern
0092         self.rules = [(QRegExp(pat), index, identifier)
0093                       for (pat, index, identifier) in rules]
0094 
0095     def highlightBlock(self, text):
0096         """Apply syntax highlighting to the given block of text."""
0097         # Do other syntax formatting
0098         for expression, nth, identifier in self.rules:
0099             index = expression.indexIn(text, 0)
0100 
0101             while index >= 0:
0102                 # We actually want the index of the nth match
0103                 index = expression.pos(nth)
0104                 length = len(expression.cap(nth))
0105                 self.setFormat(index, length, self.syntaxStyle[identifier])
0106                 index = expression.indexIn(text, index + length)
0107 
0108         self.setCurrentBlockState(0)
0109 
0110         # Do multi-line strings
0111         in_multiline = self.match_multiline(text, *self.tri_single)
0112         if not in_multiline:
0113             in_multiline = self.match_multiline(text, *self.tri_double)
0114 
0115     def match_multiline(self, text, delimiter, in_state, style):
0116         """Do highlighting of multi-line strings. ``delimiter`` should be a
0117         ``QRegExp`` for triple-single-quotes or triple-double-quotes, and
0118         ``in_state`` should be a unique integer to represent the corresponding
0119         state changes when inside those strings. Returns True if we're still
0120         inside a multi-line string when this function is finished.
0121         """
0122         # If inside triple-single quotes, start at 0
0123         if self.previousBlockState() == in_state:
0124             start = 0
0125             add = 0
0126         # Otherwise, look for the delimiter on this line
0127         else:
0128             start = delimiter.indexIn(text)
0129             # Move past this match
0130             add = delimiter.matchedLength()
0131 
0132         # As long as there's a delimiter match on this line...
0133         while start >= 0:
0134             # Look for the ending delimiter
0135             end = delimiter.indexIn(text, start + add)
0136             # Ending delimiter on this line?
0137             if end >= add:
0138                 length = end - start + add + delimiter.matchedLength()
0139                 self.setCurrentBlockState(0)
0140             # No; multi-line string
0141             else:
0142                 self.setCurrentBlockState(in_state)
0143                 length = len(text) - start + add
0144             # Apply formatting
0145             self.setFormat(start, length, self.syntaxStyle[style])
0146             # Look for the next match
0147             start = delimiter.indexIn(text, start + length)
0148 
0149         # Return True if still inside a multi-line string, False otherwise
0150         if self.currentBlockState() == in_state:
0151             return True
0152         else:
0153             return False
0154 
0155     def getSyntaxStyle(self):
0156         return self.syntaxStyle
0157 
0158     def setSyntaxStyle(self, syntaxStyle):
0159         self.syntaxStyle = syntaxStyle