File indexing completed on 2024-05-05 11:55:56

0001 /*
0002     SPDX-FileCopyrightText: 2009 Milian Wolff <mail@milianw.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "qalculatehighlighter.h"
0008 
0009 #include <libqalculate/Calculator.h>
0010 #include <libqalculate/Variable.h>
0011 #include <libqalculate/Function.h>
0012 #include <libqalculate/Unit.h>
0013 
0014 #include <KColorScheme>
0015 #include <KLocalizedString>
0016 #include <QLocale>
0017 #include <QDebug>
0018 #include <QRegularExpression>
0019 
0020 QalculateHighlighter::QalculateHighlighter(QObject* parent)
0021     : Cantor::DefaultHighlighter(parent)
0022 {
0023 }
0024 
0025 void QalculateHighlighter::highlightBlock(const QString& text)
0026 {
0027     if ( text.isEmpty() || text.trimmed().isEmpty() || text.startsWith(QLatin1String(">>> "))
0028             // filter error messages, they get highlighted via html
0029             || text.startsWith(i18n("ERROR") + QLatin1Char(':')) || text.startsWith(i18n("WARNING") + QLatin1Char(':')) ) {
0030         return;
0031     }
0032 
0033     int pos = 0;
0034     int count;
0035     ///TODO: Can't we use CALCULATOR->parse() or similar?
0036     ///      Question is how to get the connection between
0037     ///      MathStructur and position+length in @p text
0038     const QStringList words = text.split(QRegularExpression(QStringLiteral("\\b")), QString::SkipEmptyParts);
0039 
0040     qDebug() << "highlight block:" << text;
0041 
0042     CALCULATOR->beginTemporaryStopMessages();
0043 
0044     const QString decimalSymbol = QLocale().decimalPoint();
0045 
0046     for ( int i = 0; i < words.size(); ++i, pos += count ) {
0047         count = words[i].size();
0048         if ( words[i].trimmed().isEmpty() ) {
0049             continue;
0050         }
0051 
0052         qDebug() << "highlight word:" << words[i];
0053 
0054         QTextCharFormat format = errorFormat();
0055 
0056         if ( i < words.size() - 1 && words[i+1].trimmed() == QLatin1String("(") && CALCULATOR->getFunction(words[i].toUtf8().constData()) ) {
0057             // should be a function
0058             qDebug() << "function";
0059             format = functionFormat();
0060         } else if ( isOperatorAndWhitespace(words[i]) ) {
0061             // stuff like ") * (" is an invalid expression, but actually OK
0062 
0063             // check if last number is actually a float
0064             bool isFloat = false;
0065             if ( words[i].trimmed() == decimalSymbol ) {
0066                 if ( i > 0 ) {
0067                     // lookbehind
0068                     QString lastWord = words[i-1].trimmed();
0069                     if ( !lastWord.isEmpty() && lastWord.at(lastWord.size()-1).isNumber() ) {
0070                         qDebug() << "actually float";
0071                         isFloat = true;
0072                     }
0073                 }
0074                 if ( !isFloat && i < words.size() - 1 ) {
0075                     // lookahead
0076                     QString nextWord = words[i+1].trimmed();
0077                     if ( !nextWord.isEmpty() && nextWord.at(0).isNumber() ) {
0078                         qDebug() << "float coming";
0079                         isFloat = true;
0080                     }
0081                 }
0082             }
0083             if ( !isFloat ) {
0084                 qDebug() << "operator / whitespace";
0085                 format = operatorFormat();
0086             } else {
0087                 format = numberFormat();
0088             }
0089         } else {
0090             MathStructure expr = CALCULATOR->parse(words[i].toLatin1().constData());
0091             if ( expr.isNumber() || expr.isNumber_exp() ) {
0092                 qDebug() << "number";
0093                 format = numberFormat();
0094             } else if ( expr.isVariable() ) {
0095                 qDebug() << "variable";
0096                 format = variableFormat();
0097             } else if ( expr.isUndefined() ) {
0098                 qDebug() << "undefined";
0099             } else if ( expr.isUnit() || expr.isUnit_exp() ) {
0100                 qDebug() << "unit";
0101                 format = keywordFormat();
0102             }
0103         }
0104 
0105         setFormat(pos, count, format);
0106     }
0107 
0108     CALCULATOR->endTemporaryStopMessages();
0109 
0110 }
0111 
0112 bool QalculateHighlighter::isOperatorAndWhitespace(const QString& word) const
0113 {
0114     foreach ( const QChar& c, word ) {
0115         if ( c.isLetterOrNumber() ) {
0116             return false;
0117         }
0118     }
0119     return true;
0120 }