BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
PythonSyntaxHighlighter.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/View/Info/PythonSyntaxHighlighter.cpp
6 //! @brief Defines class PythonSyntaxHighlighter
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
15 /*
16 This is a C++ port of the following PyQt example
17 http://diotavelli.net/PyQtWiki/Python%20syntax%20highlighting
18 C++ port by Frankie Simon (docklight.de, www.fuh-edv.de)
19 
20 The following free software license applies for this file ("X11 license"):
21 
22 Permission is hereby granted, free of charge, to any person obtaining a copy of this software
23 and associated documentation files (the "Software"), to deal in the Software without restriction,
24 including without limitation the rights to use, copy, modify, merge, publish, distribute,
25 sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
26 furnished to do so, subject to the following conditions:
27 
28 The above copyright notice and this permission notice shall be included in all copies or substantial
29 portions of the Software.
30 
31 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
32 NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
33 NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
35 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 */
37 
39 
41  : QSyntaxHighlighter(parent)
42 {
43  keywords = QStringList() << "and"
44  << "assert"
45  << "break"
46  << "class"
47  << "continue"
48  << "def"
49  << "del"
50  << "elif"
51  << "else"
52  << "except"
53  << "exec"
54  << "finally"
55  << "for"
56  << "from"
57  << "global"
58  << "if"
59  << "import"
60  << "in"
61  << "is"
62  << "lambda"
63  << "not"
64  << "or"
65  << "pass"
66  << "print"
67  << "raise"
68  << "return"
69  << "try"
70  << "while"
71  << "yield"
72  << "None"
73  << "True"
74  << "False";
75 
76  operators = QStringList() << "=" <<
77  // Comparison
78  "=="
79  << "!="
80  << "<"
81  << "<="
82  << ">"
83  << ">=" <<
84  // Arithmetic
85  "\\+"
86  << "-"
87  << "\\*"
88  << "/"
89  << "//"
90  << "%"
91  << "\\*\\*" <<
92  // In-place
93  "\\+="
94  << "-="
95  << "\\*="
96  << "/="
97  << "%=" <<
98  // Bitwise
99  "\\^"
100  << "\\|"
101  << "&"
102  << "~"
103  << ">>"
104  << "<<";
105 
106  braces = QStringList() << "{"
107  << "}"
108  << "\\("
109  << "\\)"
110  << "\\["
111  << "]";
112 
113  basicStyles.insert("keyword", getTextCharFormat("blue"));
114  basicStyles.insert("operator", getTextCharFormat("red"));
115  basicStyles.insert("brace", getTextCharFormat("darkGray"));
116  basicStyles.insert("defclass", getTextCharFormat("black", "bold"));
117  basicStyles.insert("brace", getTextCharFormat("black"));
118  basicStyles.insert("string", getTextCharFormat("magenta"));
119  basicStyles.insert("string2", getTextCharFormat("darkMagenta"));
120  basicStyles.insert("comment", getTextCharFormat("darkGreen", "italic"));
121  basicStyles.insert("self", getTextCharFormat("black", "italic"));
122  basicStyles.insert("numbers", getTextCharFormat("brown"));
123 
124  triSingleQuote.setPattern("'''");
125  triDoubleQuote.setPattern(R"(""")");
126 
127  initializeRules();
128 }
129 
131 {
132  for (const QString& currKeyword : keywords) {
133  rules.append(HighlightingRule(QString("\\b%1\\b").arg(currKeyword), 0,
134  basicStyles.value("keyword")));
135  }
136  for (const QString& currOperator : operators) {
137  rules.append(
138  HighlightingRule(QString("%1").arg(currOperator), 0, basicStyles.value("operator")));
139  }
140  for (const QString& currBrace : braces)
141  rules.append(HighlightingRule(QString("%1").arg(currBrace), 0, basicStyles.value("brace")));
142  // 'self'
143  rules.append(HighlightingRule("\\bself\\b", 0, basicStyles.value("self")));
144 
145  // Double-quoted string, possibly containing escape sequences
146  // FF: originally in python : r'"[^"\\]*(\\.[^"\\]*)*"'
147  rules.append(HighlightingRule(R"("[^"\\]*(\\.[^"\\]*)*")", 0, basicStyles.value("string")));
148  // Single-quoted string, possibly containing escape sequences
149  // FF: originally in python : r"'[^'\\]*(\\.[^'\\]*)*'"
150  rules.append(HighlightingRule(R"('[^'\\]*(\\.[^'\\]*)*')", 0, basicStyles.value("string")));
151 
152  // 'def' followed by an identifier
153  // FF: originally: r'\bdef\b\s*(\w+)'
154  rules.append(HighlightingRule(R"(\bdef\b\s*(\w+))", 1, basicStyles.value("defclass")));
155  // 'class' followed by an identifier
156  // FF: originally: r'\bclass\b\s*(\w+)'
157  rules.append(HighlightingRule(R"(\bclass\b\s*(\w+))", 1, basicStyles.value("defclass")));
158 
159  // From '#' until a newline
160  // FF: originally: r'#[^\\n]*'
161  rules.append(HighlightingRule("#[^\\n]*", 0, basicStyles.value("comment")));
162 
163  // Numeric literals
164  rules.append(HighlightingRule("\\b[+-]?[0-9]+[lL]?\\b", 0,
165  basicStyles.value("numbers"))); // r'\b[+-]?[0-9]+[lL]?\b'
166  rules.append(
167  HighlightingRule("\\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\\b", 0,
168  basicStyles.value("numbers"))); // r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b'
169  rules.append(HighlightingRule(
170  R"(\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b)", 0,
171  basicStyles.value("numbers"))); // r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b'
172 }
173 
174 void PythonSyntaxHighlighter::highlightBlock(const QString& text)
175 {
176  for (HighlightingRule currRule : rules) {
177  int idx = currRule.pattern.indexIn(text, 0);
178  while (idx >= 0) {
179  // Get index of Nth match
180  idx = currRule.pattern.pos(currRule.nth);
181  int length = currRule.pattern.cap(currRule.nth).length();
182  setFormat(idx, length, currRule.format);
183  idx = currRule.pattern.indexIn(text, idx + length);
184  }
185  }
186  setCurrentBlockState(0);
187 
188  // Do multi-line strings
189  if (!matchMultiline(text, triSingleQuote, 1, basicStyles.value("string2")))
190  matchMultiline(text, triDoubleQuote, 2, basicStyles.value("string2"));
191 }
192 
193 bool PythonSyntaxHighlighter::matchMultiline(const QString& text, const QRegExp& delimiter,
194  const int inState, const QTextCharFormat& style)
195 {
196  int start = -1;
197  int add = -1;
198  int end = -1;
199  int length = 0;
200 
201  // If inside triple-single quotes, start at 0
202  if (previousBlockState() == inState) {
203  start = 0;
204  add = 0;
205  }
206  // Otherwise, look for the delimiter on this line
207  else {
208  start = delimiter.indexIn(text);
209  // Move past this match
210  add = delimiter.matchedLength();
211  }
212  // As long as there's a delimiter match on this line...
213  while (start >= 0) {
214  // Look for the ending delimiter
215  end = delimiter.indexIn(text, start + add);
216  // Ending delimiter on this line?
217  if (end >= add) {
218  length = end - start + add + delimiter.matchedLength();
219  setCurrentBlockState(0);
220  }
221  // No; multi-line string
222  else {
223  setCurrentBlockState(inState);
224  length = text.length() - start + add;
225  }
226  // Apply formatting and look for next
227  setFormat(start, length, style);
228  start = delimiter.indexIn(text, start + length);
229  }
230  // Return True if still inside a multi-line string, False otherwise
231  return currentBlockState() == inState;
232 }
233 
234 QTextCharFormat PythonSyntaxHighlighter::getTextCharFormat(const QString& colorName,
235  const QString& style)
236 {
237  QTextCharFormat charFormat;
238  QColor color(colorName);
239  charFormat.setForeground(color);
240  if (style.contains("bold", Qt::CaseInsensitive))
241  charFormat.setFontWeight(QFont::Bold);
242  if (style.contains("italic", Qt::CaseInsensitive))
243  charFormat.setFontItalic(true);
244  return charFormat;
245 }
Defines class PythonSyntaxHighlighter.
Container to describe a highlighting rule. Based on a regular expression, a relevant match # and the ...
PythonSyntaxHighlighter(QTextDocument *parent=nullptr)
void highlightBlock(const QString &text) override
QHash< QString, QTextCharFormat > basicStyles
bool matchMultiline(const QString &text, const QRegExp &delimiter, int inState, const QTextCharFormat &style)
Highlighst multi-line strings, returns true if after processing we are still within the.
QTextCharFormat getTextCharFormat(const QString &colorName, const QString &style="")
QList< HighlightingRule > rules