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