File indexing completed on 2024-04-14 03:55:19

0001 /*
0002     SPDX-FileCopyrightText: 2008 Paul Giannaros <paul@giannaros.org>
0003     SPDX-FileCopyrightText: 2009-2018 Dominik Haumann <dhaumann@kde.org>
0004     SPDX-FileCopyrightText: 2010 Joseph Wenninger <jowenn@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "katescript.h"
0010 
0011 #include "katepartdebug.h"
0012 #include "katescriptdocument.h"
0013 #include "katescripteditor.h"
0014 #include "katescripthelpers.h"
0015 #include "katescriptview.h"
0016 #include "kateview.h"
0017 
0018 #include <KLocalizedString>
0019 #include <iostream>
0020 
0021 #include <QFile>
0022 #include <QFileInfo>
0023 #include <QJSEngine>
0024 #include <QQmlEngine>
0025 
0026 KateScript::KateScript(const QString &urlOrScript, enum InputType inputType)
0027     : m_url(inputType == InputURL ? urlOrScript : QString())
0028     , m_inputType(inputType)
0029     , m_script(inputType == InputSCRIPT ? urlOrScript : QString())
0030 {
0031 }
0032 
0033 KateScript::~KateScript()
0034 {
0035     if (m_loadSuccessful) {
0036         // remove data...
0037         delete m_editor;
0038         delete m_document;
0039         delete m_view;
0040         delete m_engine;
0041     }
0042 }
0043 
0044 QString KateScript::backtrace(const QJSValue &error, const QString &header)
0045 {
0046     QString bt;
0047     if (!header.isNull()) {
0048         bt += header + QLatin1String(":\n");
0049     }
0050     if (error.isError()) {
0051         bt += error.toString() + QLatin1String("\nStrack trace:\n") + error.property(QStringLiteral("stack")).toString();
0052     }
0053 
0054     return bt;
0055 }
0056 
0057 void KateScript::displayBacktrace(const QJSValue &error, const QString &header)
0058 {
0059     if (!m_engine) {
0060         std::cerr << "KateScript::displayBacktrace: no engine, cannot display error\n";
0061         return;
0062     }
0063     std::cerr << "\033[31m" << qPrintable(backtrace(error, header)) << "\033[0m" << '\n';
0064 }
0065 
0066 void KateScript::clearExceptions()
0067 {
0068     if (!load()) {
0069         return;
0070     }
0071 }
0072 
0073 QJSValue KateScript::global(const QString &name)
0074 {
0075     // load the script if necessary
0076     if (!load()) {
0077         return QJSValue::UndefinedValue;
0078     }
0079     return m_engine->globalObject().property(name);
0080 }
0081 
0082 QJSValue KateScript::function(const QString &name)
0083 {
0084     QJSValue value = global(name);
0085     if (!value.isCallable()) {
0086         return QJSValue::UndefinedValue;
0087     }
0088     return value;
0089 }
0090 
0091 bool KateScript::load()
0092 {
0093     if (m_loaded) {
0094         return m_loadSuccessful;
0095     }
0096 
0097     m_loaded = true;
0098     m_loadSuccessful = false; // here set to false, and at end of function to true
0099 
0100     // read the script file into memory
0101     QString source;
0102     if (m_inputType == InputURL) {
0103         if (!Kate::Script::readFile(m_url, source)) {
0104             return false;
0105         }
0106     } else {
0107         source = m_script;
0108     }
0109 
0110     // create script engine, register meta types
0111     m_engine = new QJSEngine();
0112 
0113     // export read & require function and add the require guard object
0114     auto scriptHelper = new Kate::ScriptHelper(m_engine);
0115     QJSValue functions = m_engine->newQObject(scriptHelper);
0116     m_engine->globalObject().setProperty(QStringLiteral("functions"), functions);
0117     m_engine->globalObject().setProperty(QStringLiteral("read"), functions.property(QStringLiteral("read")));
0118     m_engine->globalObject().setProperty(QStringLiteral("require"), functions.property(QStringLiteral("require")));
0119     m_engine->globalObject().setProperty(QStringLiteral("require_guard"), m_engine->newObject());
0120 
0121     // View and Document expose JS Range objects in the API, which will fail to work
0122     // if Range is not included. range.js includes cursor.js
0123     scriptHelper->require(QStringLiteral("range.js"));
0124 
0125     // export debug function
0126     m_engine->globalObject().setProperty(QStringLiteral("debug"), functions.property(QStringLiteral("debug")));
0127 
0128     // export translation functions
0129     m_engine->globalObject().setProperty(QStringLiteral("i18n"), functions.property(QStringLiteral("_i18n")));
0130     m_engine->globalObject().setProperty(QStringLiteral("i18nc"), functions.property(QStringLiteral("_i18nc")));
0131     m_engine->globalObject().setProperty(QStringLiteral("i18np"), functions.property(QStringLiteral("_i18np")));
0132     m_engine->globalObject().setProperty(QStringLiteral("i18ncp"), functions.property(QStringLiteral("_i18ncp")));
0133 
0134     // register default styles as ds* global properties
0135     m_engine->globalObject().setProperty(QStringLiteral("dsNormal"), KSyntaxHighlighting::Theme::TextStyle::Normal);
0136     m_engine->globalObject().setProperty(QStringLiteral("dsKeyword"), KSyntaxHighlighting::Theme::TextStyle::Keyword);
0137     m_engine->globalObject().setProperty(QStringLiteral("dsFunction"), KSyntaxHighlighting::Theme::TextStyle::Function);
0138     m_engine->globalObject().setProperty(QStringLiteral("dsVariable"), KSyntaxHighlighting::Theme::TextStyle::Variable);
0139     m_engine->globalObject().setProperty(QStringLiteral("dsControlFlow"), KSyntaxHighlighting::Theme::TextStyle::ControlFlow);
0140     m_engine->globalObject().setProperty(QStringLiteral("dsOperator"), KSyntaxHighlighting::Theme::TextStyle::Operator);
0141     m_engine->globalObject().setProperty(QStringLiteral("dsBuiltIn"), KSyntaxHighlighting::Theme::TextStyle::BuiltIn);
0142     m_engine->globalObject().setProperty(QStringLiteral("dsExtension"), KSyntaxHighlighting::Theme::TextStyle::Extension);
0143     m_engine->globalObject().setProperty(QStringLiteral("dsPreprocessor"), KSyntaxHighlighting::Theme::TextStyle::Preprocessor);
0144     m_engine->globalObject().setProperty(QStringLiteral("dsAttribute"), KSyntaxHighlighting::Theme::TextStyle::Attribute);
0145     m_engine->globalObject().setProperty(QStringLiteral("dsChar"), KSyntaxHighlighting::Theme::TextStyle::Char);
0146     m_engine->globalObject().setProperty(QStringLiteral("dsSpecialChar"), KSyntaxHighlighting::Theme::TextStyle::SpecialChar);
0147     m_engine->globalObject().setProperty(QStringLiteral("dsString"), KSyntaxHighlighting::Theme::TextStyle::String);
0148     m_engine->globalObject().setProperty(QStringLiteral("dsVerbatimString"), KSyntaxHighlighting::Theme::TextStyle::VerbatimString);
0149     m_engine->globalObject().setProperty(QStringLiteral("dsSpecialString"), KSyntaxHighlighting::Theme::TextStyle::SpecialString);
0150     m_engine->globalObject().setProperty(QStringLiteral("dsImport"), KSyntaxHighlighting::Theme::TextStyle::Import);
0151     m_engine->globalObject().setProperty(QStringLiteral("dsDataType"), KSyntaxHighlighting::Theme::TextStyle::DataType);
0152     m_engine->globalObject().setProperty(QStringLiteral("dsDecVal"), KSyntaxHighlighting::Theme::TextStyle::DecVal);
0153     m_engine->globalObject().setProperty(QStringLiteral("dsBaseN"), KSyntaxHighlighting::Theme::TextStyle::BaseN);
0154     m_engine->globalObject().setProperty(QStringLiteral("dsFloat"), KSyntaxHighlighting::Theme::TextStyle::Float);
0155     m_engine->globalObject().setProperty(QStringLiteral("dsConstant"), KSyntaxHighlighting::Theme::TextStyle::Constant);
0156     m_engine->globalObject().setProperty(QStringLiteral("dsComment"), KSyntaxHighlighting::Theme::TextStyle::Comment);
0157     m_engine->globalObject().setProperty(QStringLiteral("dsDocumentation"), KSyntaxHighlighting::Theme::TextStyle::Documentation);
0158     m_engine->globalObject().setProperty(QStringLiteral("dsAnnotation"), KSyntaxHighlighting::Theme::TextStyle::Annotation);
0159     m_engine->globalObject().setProperty(QStringLiteral("dsCommentVar"), KSyntaxHighlighting::Theme::TextStyle::CommentVar);
0160     m_engine->globalObject().setProperty(QStringLiteral("dsRegionMarker"), KSyntaxHighlighting::Theme::TextStyle::RegionMarker);
0161     m_engine->globalObject().setProperty(QStringLiteral("dsInformation"), KSyntaxHighlighting::Theme::TextStyle::Information);
0162     m_engine->globalObject().setProperty(QStringLiteral("dsWarning"), KSyntaxHighlighting::Theme::TextStyle::Warning);
0163     m_engine->globalObject().setProperty(QStringLiteral("dsAlert"), KSyntaxHighlighting::Theme::TextStyle::Alert);
0164     m_engine->globalObject().setProperty(QStringLiteral("dsOthers"), KSyntaxHighlighting::Theme::TextStyle::Others);
0165     m_engine->globalObject().setProperty(QStringLiteral("dsError"), KSyntaxHighlighting::Theme::TextStyle::Error);
0166 
0167     // register scripts itself
0168     QJSValue result = m_engine->evaluate(source, m_url);
0169     if (hasException(result, m_url)) {
0170         return false;
0171     }
0172 
0173     // AFTER SCRIPT: set the view/document objects as necessary
0174     m_engine->globalObject().setProperty(QStringLiteral("editor"), m_engine->newQObject(m_editor = new KateScriptEditor()));
0175     m_engine->globalObject().setProperty(QStringLiteral("document"), m_engine->newQObject(m_document = new KateScriptDocument(m_engine)));
0176     m_engine->globalObject().setProperty(QStringLiteral("view"), m_engine->newQObject(m_view = new KateScriptView(m_engine)));
0177 
0178     // yip yip!
0179     m_loadSuccessful = true;
0180 
0181     return true;
0182 }
0183 
0184 QJSValue KateScript::evaluate(const QString &program, const FieldMap &env)
0185 {
0186     if (!load()) {
0187         qCWarning(LOG_KTE) << "load of script failed:" << program;
0188         return QJSValue();
0189     }
0190 
0191     // Wrap the arguments in a function to avoid polluting the global object
0192     QString programWithContext =
0193         QLatin1String("(function(") + QStringList(env.keys()).join(QLatin1Char(',')) + QLatin1String(") { return ") + program + QLatin1String("})");
0194     QJSValue programFunction = m_engine->evaluate(programWithContext);
0195     Q_ASSERT(programFunction.isCallable());
0196 
0197     QJSValueList args;
0198     args.reserve(env.size());
0199     for (auto it = env.begin(); it != env.end(); it++) {
0200         args << it.value();
0201     }
0202 
0203     QJSValue result = programFunction.call(args);
0204     if (result.isError()) {
0205         qCWarning(LOG_KTE) << "Error evaluating script: " << result.toString();
0206     }
0207 
0208     return result;
0209 }
0210 
0211 bool KateScript::hasException(const QJSValue &object, const QString &file)
0212 {
0213     if (object.isError()) {
0214         m_errorMessage = i18n("Error loading script %1", file);
0215         displayBacktrace(object, m_errorMessage);
0216         delete m_engine;
0217         m_engine = nullptr;
0218         m_loadSuccessful = false;
0219         return true;
0220     }
0221     return false;
0222 }
0223 
0224 bool KateScript::setView(KTextEditor::ViewPrivate *view)
0225 {
0226     if (!load()) {
0227         return false;
0228     }
0229     // setup the stuff
0230     m_document->setDocument(view->doc());
0231     m_view->setView(view);
0232     return true;
0233 }
0234 
0235 void KateScript::setGeneralHeader(const KateScriptHeader &generalHeader)
0236 {
0237     m_generalHeader = generalHeader;
0238 }
0239 
0240 KateScriptHeader &KateScript::generalHeader()
0241 {
0242     return m_generalHeader;
0243 }