Krita Source Code Documentation
Loading...
Searching...
No Matches
syntax.py
Go to the documentation of this file.
1"""
2SPDX-FileCopyrightText: 2017 Eliakin Costa <eliakim170@gmail.com>
3
4SPDX-License-Identifier: GPL-2.0-or-later
5"""
6# syntax.py: taken from https://wiki.python.org/moin/PyQt/Python%20syntax%20highlighting
7
8try:
9 from PyQt6.QtCore import QRegularExpression
10 from PyQt6.QtGui import QSyntaxHighlighter
11except:
12 from PyQt5.QtCore import QRegularExpression
13 from PyQt5.QtGui import QSyntaxHighlighter
14
15
16class PythonHighlighter (QSyntaxHighlighter):
17
18 """Syntax highlighter for the Python language.
19 """
20 # Python keywords
21 keywords = [
22 'and', 'assert', 'break', 'class', 'continue', 'def',
23 'del', 'elif', 'else', 'except', 'exec', 'finally',
24 'for', 'from', 'global', 'if', 'import', 'in',
25 'is', 'lambda', 'not', 'or', 'pass', 'print',
26 'raise', 'return', 'try', 'while', 'yield',
27 'None', 'True', 'False',
28 ]
29
30 # Python operators
31 operators = [
32 '=',
33 # Comparison
34 '==', '!=', '<', '<=', '>', '>=',
35 # Arithmetic
36 r'\+', '-', r'\*', '/', '//', r'\%', r'\*\*',
37 # In-place
38 r'\+=', '-=', r'\*=', '/=', r'\%=',
39 # Bitwise
40 r'\^', r'\|', r'\&', r'\~', '>>', '<<',
41 ]
42
43 # Python braces
44 braces = [
45 r'\{', r'\}', r'\‍(', r'\‍)', r'\[', r'\]',
46 ]
47
48 def __init__(self, document, syntaxStyle):
49 QSyntaxHighlighter.__init__(self, document)
50
51 self.syntaxStyle = syntaxStyle
52 self.document = document
53
54 # Multi-line strings (expression, flag, style)
55 # FIXME: The triple-quotes in these two lines will mess up the
56 # syntax highlighting from this point onward
57 self.tri_single = (QRegularExpression(r"""'''(?!")"""), 1, 'string2')
58 self.tri_double = (QRegularExpression(r'''"""(?!')'''), 2, 'string2')
59
60 rules = []
61
62 # Keyword, operator, and brace rules
63 rules += [(r'\b%s\b' % w, 0, 'keyword')
64 for w in PythonHighlighter.keywords]
65 rules += [(r'%s' % o, 0, 'operator')
66 for o in PythonHighlighter.operators]
67 rules += [(r'%s' % b, 0, 'brace')
68 for b in PythonHighlighter.braces]
69
70 # All other rules
71 rules += [
72 # 'self'
73 (r'\bself\b', 0, 'self'),
74
75 # Double-quoted string, possibly containing escape sequences
76 (r'"[^"\\]*(\\.[^"\\]*)*"', 0, 'string'),
77 # Single-quoted string, possibly containing escape sequences
78 (r"'[^'\\]*(\\.[^'\\]*)*'", 0, 'string'),
79
80 # 'def' followed by an identifier
81 (r'\bdef\b\s*(\w+)', 1, 'defclass'),
82 # 'class' followed by an identifier
83 (r'\bclass\b\s*(\w+)', 1, 'defclass'),
84
85 # From '#' until a newline
86 (r'#[^\n]*', 0, 'comment'),
87
88 # Numeric literals
89 (r'\b[+-]?[0-9]+[lL]?\b', 0, 'numbers'),
90 (r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, 'numbers'),
91 (r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0, 'numbers'),
92 ]
93
94 # Build a QRegularExpression for each pattern
95 self.rules = [(QRegularExpression(pat), index, identifier)
96 for (pat, index, identifier) in rules]
97
98 def highlightBlock(self, text):
99 """Apply syntax highlighting to the given block of text."""
100 # Do other syntax formatting
101 for expression, nth, identifier in self.rules:
102 matchIter = expression.globalMatch(text)
103 while matchIter.hasNext():
104 match = matchIter.next()
105 index = match.capturedStart(nth)
106 length = match.capturedLength(nth)
107 self.setFormat(index, length, self.syntaxStyle[identifier])
108
109 self.setCurrentBlockState(0)
110
111 # Do multi-line strings
112 in_multiline = self.match_multiline(text, *self.tri_single)
113 if not in_multiline:
114 in_multiline = self.match_multiline(text, *self.tri_double)
115
116 def match_multiline(self, text, delimiter, in_state, style):
117 """Do highlighting of multi-line strings. ``delimiter`` should be a
118 ``QRegularExpression`` for triple-single-quotes or triple-double-quotes, and
119 ``in_state`` should be a unique integer to represent the corresponding
120 state changes when inside those strings. Returns True if we're still
121 inside a multi-line string when this function is finished.
122 """
123 # If inside triple-single quotes, start at 0
124 if self.previousBlockState() == in_state:
125 start = 0
126 add = 0
127 # Otherwise, look for the delimiter on this line
128 else:
129 start = delimiter.match(text).capturedStart()
130 # Move past this match
131 add = delimiter.match(text).capturedLength(delimiter.match(text).lastCapturedIndex())
132
133 # As long as there's a delimiter match on this line...
134 while start >= 0:
135 # Look for the ending delimiter
136 match = delimiter.match(text, start + add)
137 end = match.capturedStart()
138 # Ending delimiter on this line?
139 if end >= add:
140 length = end - start + add + match.capturedLength(match.lastCapturedIndex())
141 self.setCurrentBlockState(0)
142 # No; multi-line string
143 else:
144 self.setCurrentBlockState(in_state)
145 length = len(text) - start + add
146 # Apply formatting
147 self.setFormat(start, length, self.syntaxStyle[style])
148 # Look for the next match
149 start = delimiter.match(text, start + length).capturedStart()
150
151 # Return True if still inside a multi-line string, False otherwise
152 if self.currentBlockState() == in_state:
153 return True
154 else:
155 return False
156
157 def getSyntaxStyle(self):
158 return self.syntaxStyle
159
160 def setSyntaxStyle(self, syntaxStyle):
161 self.syntaxStyle = syntaxStyle
match_multiline(self, text, delimiter, in_state, style)
Definition syntax.py:116