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 }