File indexing completed on 2024-04-28 11:20:36

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2013 Filipe Saraiva <filipe@kde.org>
0004 */
0005 
0006 #include "pythonhighlighter.h"
0007 #include "pythonkeywords.h"
0008 #include "pythonsession.h"
0009 
0010 PythonHighlighter::PythonHighlighter(QObject* parent, PythonSession* session) : Cantor::DefaultHighlighter(parent, session)
0011 {
0012     addRule(QRegularExpression(QStringLiteral("\\b\\w+(?=\\()")), functionFormat());
0013 
0014     //Code highlighting the different keywords
0015     addKeywords(PythonKeywords::instance()->keywords());
0016     addFunctions(PythonKeywords::instance()->functions());
0017     addVariables(PythonKeywords::instance()->variables());
0018 }
0019 
0020 void PythonHighlighter::highlightBlock(const QString &text)
0021 {
0022     if (skipHighlighting(text))
0023         return;
0024 
0025     // Do some backend independent highlighting (brackets etc.)
0026     DefaultHighlighter::highlightBlock(text);
0027 
0028     const int IN_MULTILINE_COMMENT = 1;
0029     const int IN_SMALL_QUOTE_STRING = 2;
0030     const int IN_SINGLE_QUOTE_STRING = 4;
0031     const int IN_TRIPLE_QUOTE_STRING = 8;
0032 
0033     static const QRegularExpression multiLineCommentStartEnd(QStringLiteral("'''"));
0034     static const QRegularExpression smallQuoteStartEnd(QStringLiteral("'"));
0035     static const QRegularExpression singleQuoteStringStartEnd(QStringLiteral("\""));
0036     static const QRegularExpression tripleQuoteStringStartEnd(QStringLiteral("\"\"\""));
0037     static const QRegularExpression singleLineCommentStart(QStringLiteral("#"));
0038 
0039     int state = previousBlockState();
0040     if (state == -1) {
0041         state = 0;
0042     }
0043 
0044     QList<int> flags = {
0045         IN_TRIPLE_QUOTE_STRING,
0046         IN_SINGLE_QUOTE_STRING,
0047         IN_SMALL_QUOTE_STRING,
0048         IN_MULTILINE_COMMENT
0049     };
0050     const QVector<QRegularExpression> regexps = {
0051         tripleQuoteStringStartEnd,
0052         singleQuoteStringStartEnd,
0053         smallQuoteStartEnd,
0054         multiLineCommentStartEnd
0055     };
0056     QList<QTextCharFormat> formats = {
0057         stringFormat(),
0058         stringFormat(),
0059         stringFormat(),
0060         commentFormat()
0061     };
0062 
0063     int pos = 0;
0064     while (pos < text.length()) {
0065         // Trying to close current environments
0066         bool triggered = false;
0067         for (int i = 0; i < flags.size() && !triggered; i++) {
0068             int flag = flags[i];
0069             QTextCharFormat &format = formats[i];
0070             if (state & flag) {
0071                 const QRegularExpressionMatch match = regexps.at(i).match(text, pos);
0072                 int length;
0073                 if (!match.hasMatch()) {
0074                     length = text.length() - pos;
0075                 } else { // found a match
0076                     length = match.capturedStart(0) - pos + match.capturedLength(0);
0077                     state -= flag;
0078                 }
0079                 setFormat(pos, length, format);
0080                 pos = pos + length;
0081                 triggered = true;
0082             }
0083         }
0084         if (triggered) {
0085             continue;
0086         }
0087 
0088         QRegularExpressionMatch minMatch;
0089         int minPos = INT_MAX;
0090         int minIdx = -1;
0091         for (int i = 0; i < regexps.size(); i++) {
0092             const QRegularExpressionMatch match = regexps.at(i).match(text, pos);
0093             if (match.hasMatch()) {
0094                 minPos = qMin(minPos, match.capturedStart(0));
0095                 minIdx = i;
0096                 minMatch = match;
0097             }
0098         }
0099 
0100         const int singleLineCommentStartPos = text.indexOf(singleLineCommentStart, pos);
0101 
0102         if (singleLineCommentStartPos != -1
0103             && singleLineCommentStartPos < minPos) {
0104             setFormat(singleLineCommentStartPos, text.length() - singleLineCommentStartPos, commentFormat());
0105         break;
0106             } else if (minMatch.hasMatch()) {
0107                 state += flags[minIdx];
0108                 pos = minPos + minMatch.capturedLength(0);
0109                 setFormat(minPos, minMatch.capturedLength(0), formats[minIdx]);
0110             } else {
0111                 break;
0112             }
0113     }
0114 
0115     setCurrentBlockState(state);
0116 }