1 /*
2 SPDX-License-Identifier: GPL-2.0-or-later
3 SPDX-FileCopyrightText: 2013 Filipe Saraiva <filipe@kde.org>
4 */
5
6 #include "pythonhighlighter.h"
7 #include "pythonkeywords.h"
8 #include "pythonsession.h"
9
10 #include <QTextEdit>
11 #include <QDebug>
12
PythonHighlighter(QObject * parent,PythonSession * session)13 PythonHighlighter::PythonHighlighter(QObject* parent, PythonSession* session) : Cantor::DefaultHighlighter(parent, session)
14 {
15 qDebug() << "PythonHighlighter constructor";
16 addRule(QRegularExpression(QStringLiteral("\\b\\w+(?=\\()")), functionFormat());
17
18 //Code highlighting the different keywords
19 addKeywords(PythonKeywords::instance()->keywords());
20 addFunctions(PythonKeywords::instance()->functions());
21 addVariables(PythonKeywords::instance()->variables());
22 }
23
highlightBlock(const QString & text)24 void PythonHighlighter::highlightBlock(const QString &text)
25 {
26 if (skipHighlighting(text)) {
27 return;
28 }
29
30 // Do some backend independent highlighting (brackets etc.)
31 DefaultHighlighter::highlightBlock(text);
32
33 const int IN_MULTILINE_COMMENT = 1;
34 const int IN_SMALL_QUOTE_STRING = 2;
35 const int IN_SINGLE_QUOTE_STRING = 4;
36 const int IN_TRIPLE_QUOTE_STRING = 8;
37
38 static const QRegularExpression multiLineCommentStartEnd(QStringLiteral("'''"));
39 static const QRegularExpression smallQuoteStartEnd(QStringLiteral("'"));
40 static const QRegularExpression singleQuoteStringStartEnd(QStringLiteral("\""));
41 static const QRegularExpression tripleQuoteStringStartEnd(QStringLiteral("\"\"\""));
42 static const QRegularExpression singleLineCommentStart(QStringLiteral("#"));
43
44 int state = previousBlockState();
45 if (state == -1) {
46 state = 0;
47 }
48
49 QList<int> flags = {
50 IN_TRIPLE_QUOTE_STRING,
51 IN_SINGLE_QUOTE_STRING,
52 IN_SMALL_QUOTE_STRING,
53 IN_MULTILINE_COMMENT
54 };
55 const QVector<QRegularExpression> regexps = {
56 tripleQuoteStringStartEnd,
57 singleQuoteStringStartEnd,
58 smallQuoteStartEnd,
59 multiLineCommentStartEnd
60 };
61 QList<QTextCharFormat> formats = {
62 stringFormat(),
63 stringFormat(),
64 stringFormat(),
65 commentFormat()
66 };
67
68 int pos = 0;
69 while (pos < text.length()) {
70 // Trying to close current environments
71 bool triggered = false;
72 for (int i = 0; i < flags.size() && !triggered; i++) {
73 int flag = flags[i];
74 QTextCharFormat &format = formats[i];
75 if (state & flag) {
76 const QRegularExpressionMatch match = regexps.at(i).match(text, pos);
77 int length;
78 if (!match.hasMatch()) {
79 length = text.length() - pos;
80 } else { // found a match
81 length = match.capturedStart(0) - pos + match.capturedLength(0);
82 state -= flag;
83 }
84 setFormat(pos, length, format);
85 pos = pos + length;
86 triggered = true;
87 }
88 }
89 if (triggered) {
90 continue;
91 }
92
93 QRegularExpressionMatch minMatch;
94 int minPos = INT_MAX;
95 int minIdx = -1;
96 for (int i = 0; i < regexps.size(); i++) {
97 const QRegularExpressionMatch match = regexps.at(i).match(text, pos);
98 if (match.hasMatch()) {
99 minPos = qMin(minPos, match.capturedStart(0));
100 minIdx = i;
101 minMatch = match;
102 }
103 }
104
105 const int singleLineCommentStartPos = text.indexOf(singleLineCommentStart, pos);
106
107 if (singleLineCommentStartPos != -1
108 && singleLineCommentStartPos < minPos) {
109 setFormat(singleLineCommentStartPos, text.length() - singleLineCommentStartPos, commentFormat());
110 break;
111 } else if (minMatch.hasMatch()) {
112 state += flags[minIdx];
113 pos = minPos + minMatch.capturedLength(0);
114 setFormat(minPos, minMatch.capturedLength(0), formats[minIdx]);
115 } else {
116 break;
117 }
118 }
119
120 setCurrentBlockState(state);
121 }
122