calculatorrunner: Enforce using Qalculate

If libqalculate is not present on the system, calculator runner falls
back to using QJSEngine, which results in significantly reduced
precision. Make calculator runner unconditionally dependent on
Qalculate and remove conditional fallback code to QJSEngine.

BUG: 433730
wilder-5.24
Zoltan Puskas 5 years ago committed by Nate Graham
parent 92c1f7e777
commit 4c5e454024
  1. 5
      CMakeLists.txt
  2. 54
      runners/calculator/CMakeLists.txt
  3. 5
      runners/calculator/autotests/calculatorrunnertest.cpp
  4. 179
      runners/calculator/calculatorrunner.cpp
  5. 8
      runners/calculator/calculatorrunner.h

@ -70,6 +70,11 @@ set_package_properties(KF5Baloo PROPERTIES DESCRIPTION "File Searching"
TYPE RECOMMENDED
PURPOSE "Needed for the File Search runner."
)
find_package(Qalculate REQUIRED)
set_package_properties(Qalculate PROPERTIES DESCRIPTION "Qalculate Library"
URL "https://qalculate.github.io/"
PURPOSE "Needed for precise computation in the calculator runner."
)
find_package(KWinDBusInterface CONFIG REQUIRED)

@ -3,48 +3,30 @@
add_definitions(-DTRANSLATION_DOMAIN=\"plasma_runner_calculatorrunner\")
find_package(Qalculate)
set_package_properties(Qalculate PROPERTIES DESCRIPTION "Qalculate Library"
URL "https://qalculate.github.io/"
TYPE OPTIONAL
PURPOSE "Needed to enable advanced features of the calculator runner"
)
if ( QALCULATE_FOUND )
add_definitions(-DENABLE_QALCULATE)
set(EXTERNAL_LIBS
KF5::KIOCore
KF5::Runner
KF5::I18n
Qt::Network
Qt::Widgets
)
set(qalculate_engine_SRCS
set(qalculate_engine_SRCS
qalculate_engine.cpp
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated")
kde_enable_exceptions()
endif()
)
set(krunner_calculatorrunner_SRCS
calculatorrunner.cpp
)
if ( QALCULATE_FOUND )
kcoreaddons_add_plugin(calculator SOURCES ${qalculate_engine_SRCS} ${krunner_calculatorrunner_SRCS} INSTALL_NAMESPACE "kf5/krunner")
target_link_libraries(calculator
${QALCULATE_LIBRARIES}
${CLN_LIBRARIES}
KF5::KIOCore
KF5::Runner
KF5::I18n
Qt::Network
Qt::Widgets
)
else ()
kcoreaddons_add_plugin(calculator SOURCES ${krunner_calculatorrunner_SRCS} INSTALL_NAMESPACE "kf5/krunner")
target_link_libraries(calculator
KF5::Runner
KF5::I18n
Qt::Gui
Qt::Qml
Qt::Widgets
)
endif ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated")
kde_enable_exceptions()
kcoreaddons_add_plugin(calculator SOURCES ${qalculate_engine_SRCS} ${krunner_calculatorrunner_SRCS} INSTALL_NAMESPACE "kf5/krunner")
target_link_libraries(calculator
${QALCULATE_LIBRARIES}
${CLN_LIBRARIES}
${EXTERNAL_LIBS}
)
if(BUILD_TESTING)
add_subdirectory(autotests)

@ -49,11 +49,9 @@ void CalculatorRunnerTest::testQuery_data()
QTest::newRow("simple power") << "2^3" << "8";
QTest::newRow("x as multiplication sign") << "25x4" << "100";
#ifdef ENABLE_QALCULATE
QTest::newRow("single digit factorial") << "5!" << "120";
QTest::newRow("superscripted number") << ""
<< "8"; // BUG: 435932
#endif
QTest::newRow("hex to decimal lower case") << "0xf" << "15";
QTest::newRow("hex to decimal upper case") << "0xF" << "15";
@ -70,9 +68,6 @@ void CalculatorRunnerTest::testQuery_data()
void CalculatorRunnerTest::testApproximation()
{
#ifndef ENABLE_QALCULATE
QSKIP("Approximations are only with Qalculate supported");
#endif
launchQuery("5^1234567");
QCOMPARE(manager->matches().size(), 1);
QCOMPARE(manager->matches().constFirst().subtext(), "Approximation");

@ -10,13 +10,7 @@
#include "calculatorrunner.h"
#ifdef ENABLE_QALCULATE
#include "qalculate_engine.h"
#else
#include <QClipboard>
#include <QGuiApplication>
#include <QJSEngine>
#endif
#include <QDebug>
#include <QIcon>
@ -30,9 +24,7 @@ K_PLUGIN_CLASS_WITH_JSON(CalculatorRunner, "plasma-runner-calculator.json")
CalculatorRunner::CalculatorRunner(QObject *parent, const KPluginMetaData &metaData, const QVariantList &args)
: Plasma::AbstractRunner(parent, metaData, args)
{
#ifdef ENABLE_QALCULATE
m_engine = new QalculateEngine;
#endif
setObjectName(QStringLiteral("Calculator"));
@ -49,126 +41,9 @@ CalculatorRunner::CalculatorRunner(QObject *parent, const KPluginMetaData &metaD
CalculatorRunner::~CalculatorRunner()
{
#ifdef ENABLE_QALCULATE
delete m_engine;
#endif
}
#ifndef ENABLE_QALCULATE
void CalculatorRunner::powSubstitutions(QString &cmd)
{
if (cmd.contains(QLatin1String("e+"), Qt::CaseInsensitive)) {
cmd.replace(QLatin1String("e+"), QLatin1String("*10^"), Qt::CaseInsensitive);
}
if (cmd.contains(QLatin1String("e-"), Qt::CaseInsensitive)) {
cmd.replace(QLatin1String("e-"), QLatin1String("*10^-"), Qt::CaseInsensitive);
}
// the below code is scary mainly because we have to honor priority
// honor decimal numbers and parenthesis.
while (cmd.contains(QLatin1Char('^'))) {
int where = cmd.indexOf(QLatin1Char('^'));
cmd.replace(where, 1, QLatin1Char(','));
int preIndex = where - 1;
int postIndex = where + 1;
int count = 0;
QChar decimalSymbol = QLocale().decimalPoint();
// avoid out of range on weird commands
preIndex = qMax(0, preIndex);
postIndex = qMin(postIndex, cmd.length() - 1);
// go backwards looking for the beginning of the number or expression
while (preIndex != 0) {
QChar current = cmd.at(preIndex);
QChar next = cmd.at(preIndex - 1);
// qDebug() << "index " << preIndex << " char " << current;
if (current == QLatin1Char(')')) {
count++;
} else if (current == QLatin1Char('(')) {
count--;
} else {
if (((next <= QLatin1Char('9')) && (next >= QLatin1Char('0'))) || next == decimalSymbol) {
preIndex--;
continue;
}
}
if (count == 0) {
// check for functions
if (!((next <= QLatin1Char('z')) && (next >= QLatin1Char('a')))) {
break;
}
}
preIndex--;
}
// go forwards looking for the end of the number or expression
count = 0;
while (postIndex != cmd.size() - 1) {
QChar current = cmd.at(postIndex);
QChar next = cmd.at(postIndex + 1);
// check for functions
if ((count == 0) && (current <= QLatin1Char('z')) && (current >= QLatin1Char('a'))) {
postIndex++;
continue;
}
if (current == QLatin1Char('(')) {
count++;
} else if (current == QLatin1Char(')')) {
count--;
} else {
if (((next <= QLatin1Char('9')) && (next >= QLatin1Char('0'))) || next == decimalSymbol) {
postIndex++;
continue;
}
}
if (count == 0) {
break;
}
postIndex++;
}
preIndex = qMax(0, preIndex);
postIndex = qMin(postIndex, cmd.length());
cmd.insert(preIndex, QLatin1String("pow("));
// +1 +4 == next position to the last number after we add 4 new characters pow(
cmd.insert(postIndex + 1 + 4, QLatin1Char(')'));
// qDebug() << "from" << preIndex << " to " << postIndex << " got: " << cmd;
}
}
void CalculatorRunner::hexSubstitutions(QString &cmd)
{
if (cmd.contains(QLatin1String("0x"))) {
// Append +0 so that the calculator can serve also as a hex converter
cmd.append(QLatin1String("+0"));
bool ok;
int pos = 0;
QString hex;
while (cmd.contains(QLatin1String("0x"))) {
hex.clear();
pos = cmd.indexOf(QLatin1String("0x"), pos);
for (int q = 0; q < cmd.size(); q++) { // find end of hex number
QChar current = cmd[pos + q + 2];
if (((current <= QLatin1Char('9')) && (current >= QLatin1Char('0'))) || ((current <= QLatin1Char('F')) && (current >= QLatin1Char('A')))
|| ((current <= QLatin1Char('f')) && (current >= QLatin1Char('a')))) { // Check if valid hex sign
hex[q] = current;
} else {
break;
}
}
cmd = cmd.replace(pos, 2 + hex.length(), QString::number(hex.toInt(&ok, 16))); // replace hex with decimal
}
}
}
#endif
void CalculatorRunner::userFriendlySubstitutions(QString &cmd)
{
if (QLocale().decimalPoint() != QLatin1Char('.')) {
@ -179,21 +54,6 @@ void CalculatorRunner::userFriendlySubstitutions(QString &cmd)
// this ensures that the results are valid, see BUG: 406388
cmd.replace(QLatin1Char(','), QLatin1Char('.'), Qt::CaseInsensitive);
}
// the following substitutions are not needed with libqalculate
#ifndef ENABLE_QALCULATE
hexSubstitutions(cmd);
powSubstitutions(cmd);
QRegularExpression re(QStringLiteral("(\\d+)and(\\d+)"));
cmd.replace(re, QStringLiteral("\\1&\\2"));
re.setPattern(QStringLiteral("(\\d+)or(\\d+)"));
cmd.replace(re, QStringLiteral("\\1|\\2"));
re.setPattern(QStringLiteral("(\\d+)xor(\\d+)"));
cmd.replace(re, QStringLiteral("\\1^\\2"));
#endif
}
void CalculatorRunner::match(Plasma::RunnerContext &context)
@ -253,10 +113,6 @@ void CalculatorRunner::match(Plasma::RunnerContext &context)
}
userFriendlySubstitutions(cmd);
#ifndef ENABLE_QALCULATE
// needed for accessing math functions like sin(),....
cmd.replace(QRegularExpression(QStringLiteral("([a-zA-Z]+)")), QStringLiteral("Math.\\1"));
#endif
bool isApproximate = false;
QString result = calculate(cmd, &isApproximate);
@ -281,7 +137,6 @@ void CalculatorRunner::match(Plasma::RunnerContext &context)
QString CalculatorRunner::calculate(const QString &term, bool *isApproximate)
{
#ifdef ENABLE_QALCULATE
QString result;
try {
@ -291,47 +146,13 @@ QString CalculatorRunner::calculate(const QString &term, bool *isApproximate)
}
return result.replace(QLatin1Char('.'), QLocale().decimalPoint(), Qt::CaseInsensitive);
#else
Q_UNUSED(isApproximate);
// qDebug() << "calculating" << term;
QJSEngine eng;
QJSValue result = eng.evaluate(QStringLiteral("var result = %1; result").arg(term));
if (result.isError()) {
return QString();
}
const QString resultString = result.toString();
if (resultString.isEmpty()) {
return QString();
}
if (!resultString.contains(QLatin1Char('.'))) {
return resultString;
}
// ECMAScript has issues with the last digit in simple rational computations
// This script rounds off the last digit; see bug 167986
QString roundedResultString = eng.evaluate(QStringLiteral("var exponent = 14-(1+Math.floor(Math.log(Math.abs(result))/Math.log(10)));\
var order=Math.pow(10,exponent);\
(order > 0? Math.round(result*order)/order : 0)"))
.toString();
roundedResultString.replace(QLatin1Char('.'), QLocale().decimalPoint(), Qt::CaseInsensitive);
return roundedResultString;
#endif
}
void CalculatorRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match)
{
Q_UNUSED(context)
if (match.selectedAction()) {
#ifdef ENABLE_QALCULATE
m_engine->copyToClipboard();
#else
QGuiApplication::clipboard()->setText(match.text());
#endif
}
}

@ -11,9 +11,7 @@
#include <QAction>
#include <QMimeData>
#ifdef ENABLE_QALCULATE
class QalculateEngine;
#endif
#include <krunner/abstractrunner.h>
@ -38,13 +36,7 @@ private:
QString calculate(const QString &term, bool *isApproximate);
void userFriendlyMultiplication(QString &cmd);
void userFriendlySubstitutions(QString &cmd);
#ifndef ENABLE_QALCULATE
void powSubstitutions(QString &cmd);
void hexSubstitutions(QString &cmd);
#endif
#ifdef ENABLE_QALCULATE
QalculateEngine *m_engine;
#endif
QList<QAction *> m_actions;
};

Loading…
Cancel
Save