BornAgain  1.19.0
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/coregui/Views/InfoWidgets/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 
40 PythonSyntaxHighlighter::PythonSyntaxHighlighter(QTextDocument* parent) : QSyntaxHighlighter(parent)
41 {
42  keywords = QStringList() << "and"
43  << "assert"
44  << "break"
45  << "class"
46  << "continue"
47  << "def"
48  << "del"
49  << "elif"
50  << "else"
51  << "except"
52  << "exec"
53  << "finally"
54  << "for"
55  << "from"
56  << "global"
57  << "if"
58  << "import"
59  << "in"
60  << "is"
61  << "lambda"
62  << "not"
63  << "or"
64  << "pass"
65  << "print"
66  << "raise"
67  << "return"
68  << "try"
69  << "while"
70  << "yield"
71  << "None"
72  << "True"
73  << "False";
74 
75  operators = QStringList() << "=" <<
76  // Comparison
77  "=="
78  << "!="
79  << "<"
80  << "<="
81  << ">"
82  << ">=" <<
83  // Arithmetic
84  "\\+"
85  << "-"
86  << "\\*"
87  << "/"
88  << "//"
89  << "%"
90  << "\\*\\*" <<
91  // In-place
92  "\\+="
93  << "-="
94  << "\\*="
95  << "/="
96  << "%=" <<
97  // Bitwise
98  "\\^"
99  << "\\|"
100  << "&"
101  << "~"
102  << ">>"
103  << "<<";
104 
105  braces = QStringList() << "{"
106  << "}"
107  << "\\("
108  << "\\)"
109  << "\\["
110  << "]";
111 
112  basicStyles.insert("keyword", getTextCharFormat("blue"));
113  basicStyles.insert("operator", getTextCharFormat("red"));
114  basicStyles.insert("brace", getTextCharFormat("darkGray"));
115  basicStyles.insert("defclass", getTextCharFormat("black", "bold"));
116  basicStyles.insert("brace", getTextCharFormat("black"));
117  basicStyles.insert("string", getTextCharFormat("magenta"));
118  basicStyles.insert("string2", getTextCharFormat("darkMagenta"));
119  basicStyles.insert("comment", getTextCharFormat("darkGreen", "italic"));
120  basicStyles.insert("self", getTextCharFormat("black", "italic"));
121  basicStyles.insert("numbers", getTextCharFormat("brown"));
122 
123  triSingleQuote.setPattern("'''");
124  triDoubleQuote.setPattern("\"\"\"");
125 
126  initializeRules();
127 }
128 
130 {
131  for (QString currKeyword : keywords) {
132  rules.append(HighlightingRule(QString("\\b%1\\b").arg(currKeyword), 0,
133  basicStyles.value("keyword")));
134  }
135  for (QString currOperator : operators) {
136  rules.append(
137  HighlightingRule(QString("%1").arg(currOperator), 0, basicStyles.value("operator")));
138  }
139  for (QString currBrace : braces) {
140  rules.append(HighlightingRule(QString("%1").arg(currBrace), 0, basicStyles.value("brace")));
141  }
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(
148  HighlightingRule("\"[^\"\\\\]*(\\\\.[^\"\\\\]*)*\"", 0, basicStyles.value("string")));
149  // Single-quoted string, possibly containing escape sequences
150  // FF: originally in python : r"'[^'\\]*(\\.[^'\\]*)*'"
151  rules.append(HighlightingRule("'[^'\\\\]*(\\\\.[^'\\\\]*)*'", 0, basicStyles.value("string")));
152 
153  // 'def' followed by an identifier
154  // FF: originally: r'\bdef\b\s*(\w+)'
155  rules.append(HighlightingRule("\\bdef\\b\\s*(\\w+)", 1, basicStyles.value("defclass")));
156  // 'class' followed by an identifier
157  // FF: originally: r'\bclass\b\s*(\w+)'
158  rules.append(HighlightingRule("\\bclass\\b\\s*(\\w+)", 1, basicStyles.value("defclass")));
159 
160  // From '#' until a newline
161  // FF: originally: r'#[^\\n]*'
162  rules.append(HighlightingRule("#[^\\n]*", 0, basicStyles.value("comment")));
163 
164  // Numeric literals
165  rules.append(HighlightingRule("\\b[+-]?[0-9]+[lL]?\\b", 0,
166  basicStyles.value("numbers"))); // r'\b[+-]?[0-9]+[lL]?\b'
167  rules.append(
168  HighlightingRule("\\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\\b", 0,
169  basicStyles.value("numbers"))); // r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b'
170  rules.append(HighlightingRule(
171  "\\b[+-]?[0-9]+(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b", 0,
172  basicStyles.value("numbers"))); // r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b'
173 }
174 
176 {
177  for (HighlightingRule currRule : rules) {
178  int idx = currRule.pattern.indexIn(text, 0);
179  while (idx >= 0) {
180  // Get index of Nth match
181  idx = currRule.pattern.pos(currRule.nth);
182  int length = currRule.pattern.cap(currRule.nth).length();
183  setFormat(idx, length, currRule.format);
184  idx = currRule.pattern.indexIn(text, idx + length);
185  }
186  }
187  setCurrentBlockState(0);
188 
189  // Do multi-line strings
190  if (!matchMultiline(text, triSingleQuote, 1, basicStyles.value("string2")))
191  matchMultiline(text, triDoubleQuote, 2, basicStyles.value("string2"));
192 }
193 
194 bool PythonSyntaxHighlighter::matchMultiline(const QString& text, const QRegExp& delimiter,
195  const int inState, const QTextCharFormat& style)
196 {
197  int start = -1;
198  int add = -1;
199  int end = -1;
200  int length = 0;
201 
202  // If inside triple-single quotes, start at 0
203  if (previousBlockState() == inState) {
204  start = 0;
205  add = 0;
206  }
207  // Otherwise, look for the delimiter on this line
208  else {
209  start = delimiter.indexIn(text);
210  // Move past this match
211  add = delimiter.matchedLength();
212  }
213  // As long as there's a delimiter match on this line...
214  while (start >= 0) {
215  // Look for the ending delimiter
216  end = delimiter.indexIn(text, start + add);
217  // Ending delimiter on this line?
218  if (end >= add) {
219  length = end - start + add + delimiter.matchedLength();
220  setCurrentBlockState(0);
221  }
222  // No; multi-line string
223  else {
224  setCurrentBlockState(inState);
225  length = text.length() - start + add;
226  }
227  // Apply formatting and look for next
228  setFormat(start, length, style);
229  start = delimiter.indexIn(text, start + length);
230  }
231  // Return True if still inside a multi-line string, False otherwise
232  if (currentBlockState() == inState)
233  return true;
234  else
235  return false;
236 }
237 
238 const QTextCharFormat PythonSyntaxHighlighter::getTextCharFormat(const QString& colorName,
239  const QString& style)
240 {
241  QTextCharFormat charFormat;
242  QColor color(colorName);
243  charFormat.setForeground(color);
244  if (style.contains("bold", Qt::CaseInsensitive))
245  charFormat.setFontWeight(QFont::Bold);
246  if (style.contains("italic", Qt::CaseInsensitive))
247  charFormat.setFontItalic(true);
248  return charFormat;
249 }
Defines class PythonSyntaxHighlighter.
Container to describe a highlighting rule.
void highlightBlock(const QString &text)
const QTextCharFormat getTextCharFormat(const QString &colorName, const QString &style="")
QHash< QString, QTextCharFormat > basicStyles
bool matchMultiline(const QString &text, const QRegExp &delimiter, const int inState, const QTextCharFormat &style)
Highlighst multi-line strings, returns true if after processing we are still within the.
PythonSyntaxHighlighter(QTextDocument *parent=0)
QList< HighlightingRule > rules