1/*
2 SPDX-FileCopyrightText: 2008 Paul Giannaros <paul@giannaros.org>
3 SPDX-FileCopyrightText: 2009-2018 Dominik Haumann <dhaumann@kde.org>
4 SPDX-FileCopyrightText: 2010 Joseph Wenninger <jowenn@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "katescript.h"
10
11#include "katepartdebug.h"
12#include "katescriptdocument.h"
13#include "katescripteditor.h"
14#include "katescripthelpers.h"
15#include "katescriptview.h"
16#include "kateview.h"
17
18#include <KLocalizedString>
19#include <iostream>
20
21#include <QFile>
22#include <QFileInfo>
23#include <QJSEngine>
24#include <QQmlEngine>
25
26KateScript::KateScript(const QString &urlOrScript, enum InputType inputType)
27 : m_url(inputType == InputURL ? urlOrScript : QString())
28 , m_inputType(inputType)
29 , m_script(inputType == InputSCRIPT ? urlOrScript : QString())
30{
31}
32
33KateScript::~KateScript()
34{
35 if (m_loadSuccessful) {
36 // remove data...
37 delete m_editor;
38 delete m_document;
39 delete m_view;
40 delete m_engine;
41 }
42}
43
44QString KateScript::backtrace(const QJSValue &error, const QString &header)
45{
46 QString bt;
47 if (!header.isNull()) {
48 bt += header + QLatin1String(":\n");
49 }
50 if (error.isError()) {
51 bt += error.toString() + QLatin1String("\nStrack trace:\n") + error.property(QStringLiteral("stack")).toString();
52 }
53
54 return bt;
55}
56
57void KateScript::displayBacktrace(const QJSValue &error, const QString &header)
58{
59 if (!m_engine) {
60 std::cerr << "KateScript::displayBacktrace: no engine, cannot display error\n";
61 return;
62 }
63 std::cerr << "\033[31m" << qPrintable(backtrace(error, header)) << "\033[0m" << '\n';
64}
65
66void KateScript::clearExceptions()
67{
68 if (!load()) {
69 return;
70 }
71}
72
73QJSValue KateScript::global(const QString &name)
74{
75 // load the script if necessary
76 if (!load()) {
77 return QJSValue::UndefinedValue;
78 }
79 return m_engine->globalObject().property(name);
80}
81
82QJSValue KateScript::function(const QString &name)
83{
84 QJSValue value = global(name);
85 if (!value.isCallable()) {
86 return QJSValue::UndefinedValue;
87 }
88 return value;
89}
90
91bool KateScript::load()
92{
93 if (m_loaded) {
94 return m_loadSuccessful;
95 }
96
97 m_loaded = true;
98 m_loadSuccessful = false; // here set to false, and at end of function to true
99
100 // read the script file into memory
101 QString source;
102 if (m_inputType == InputURL) {
103 if (!Kate::Script::readFile(sourceUrl: m_url, sourceCode&: source)) {
104 return false;
105 }
106 } else {
107 source = m_script;
108 }
109
110 // create script engine, register meta types
111 m_engine = new QJSEngine();
112
113 // export read & require function and add the require guard object
114 auto scriptHelper = new Kate::ScriptHelper(m_engine);
115 QJSValue functions = m_engine->newQObject(object: scriptHelper);
116 m_engine->globalObject().setProperty(QStringLiteral("functions"), value: functions);
117 m_engine->globalObject().setProperty(QStringLiteral("read"), value: functions.property(QStringLiteral("read")));
118 m_engine->globalObject().setProperty(QStringLiteral("require"), value: functions.property(QStringLiteral("require")));
119 m_engine->globalObject().setProperty(QStringLiteral("require_guard"), value: m_engine->newObject());
120
121 // View and Document expose JS Range objects in the API, which will fail to work
122 // if Range is not included. range.js includes cursor.js
123 scriptHelper->require(QStringLiteral("range.js"));
124
125 // export debug function
126 m_engine->globalObject().setProperty(QStringLiteral("debug"), value: functions.property(QStringLiteral("debug")));
127
128 // export translation functions
129 m_engine->globalObject().setProperty(QStringLiteral("i18n"), value: functions.property(QStringLiteral("_i18n")));
130 m_engine->globalObject().setProperty(QStringLiteral("i18nc"), value: functions.property(QStringLiteral("_i18nc")));
131 m_engine->globalObject().setProperty(QStringLiteral("i18np"), value: functions.property(QStringLiteral("_i18np")));
132 m_engine->globalObject().setProperty(QStringLiteral("i18ncp"), value: functions.property(QStringLiteral("_i18ncp")));
133
134 // register default styles as ds* global properties
135 m_engine->globalObject().setProperty(QStringLiteral("dsNormal"), value: KSyntaxHighlighting::Theme::TextStyle::Normal);
136 m_engine->globalObject().setProperty(QStringLiteral("dsKeyword"), value: KSyntaxHighlighting::Theme::TextStyle::Keyword);
137 m_engine->globalObject().setProperty(QStringLiteral("dsFunction"), value: KSyntaxHighlighting::Theme::TextStyle::Function);
138 m_engine->globalObject().setProperty(QStringLiteral("dsVariable"), value: KSyntaxHighlighting::Theme::TextStyle::Variable);
139 m_engine->globalObject().setProperty(QStringLiteral("dsControlFlow"), value: KSyntaxHighlighting::Theme::TextStyle::ControlFlow);
140 m_engine->globalObject().setProperty(QStringLiteral("dsOperator"), value: KSyntaxHighlighting::Theme::TextStyle::Operator);
141 m_engine->globalObject().setProperty(QStringLiteral("dsBuiltIn"), value: KSyntaxHighlighting::Theme::TextStyle::BuiltIn);
142 m_engine->globalObject().setProperty(QStringLiteral("dsExtension"), value: KSyntaxHighlighting::Theme::TextStyle::Extension);
143 m_engine->globalObject().setProperty(QStringLiteral("dsPreprocessor"), value: KSyntaxHighlighting::Theme::TextStyle::Preprocessor);
144 m_engine->globalObject().setProperty(QStringLiteral("dsAttribute"), value: KSyntaxHighlighting::Theme::TextStyle::Attribute);
145 m_engine->globalObject().setProperty(QStringLiteral("dsChar"), value: KSyntaxHighlighting::Theme::TextStyle::Char);
146 m_engine->globalObject().setProperty(QStringLiteral("dsSpecialChar"), value: KSyntaxHighlighting::Theme::TextStyle::SpecialChar);
147 m_engine->globalObject().setProperty(QStringLiteral("dsString"), value: KSyntaxHighlighting::Theme::TextStyle::String);
148 m_engine->globalObject().setProperty(QStringLiteral("dsVerbatimString"), value: KSyntaxHighlighting::Theme::TextStyle::VerbatimString);
149 m_engine->globalObject().setProperty(QStringLiteral("dsSpecialString"), value: KSyntaxHighlighting::Theme::TextStyle::SpecialString);
150 m_engine->globalObject().setProperty(QStringLiteral("dsImport"), value: KSyntaxHighlighting::Theme::TextStyle::Import);
151 m_engine->globalObject().setProperty(QStringLiteral("dsDataType"), value: KSyntaxHighlighting::Theme::TextStyle::DataType);
152 m_engine->globalObject().setProperty(QStringLiteral("dsDecVal"), value: KSyntaxHighlighting::Theme::TextStyle::DecVal);
153 m_engine->globalObject().setProperty(QStringLiteral("dsBaseN"), value: KSyntaxHighlighting::Theme::TextStyle::BaseN);
154 m_engine->globalObject().setProperty(QStringLiteral("dsFloat"), value: KSyntaxHighlighting::Theme::TextStyle::Float);
155 m_engine->globalObject().setProperty(QStringLiteral("dsConstant"), value: KSyntaxHighlighting::Theme::TextStyle::Constant);
156 m_engine->globalObject().setProperty(QStringLiteral("dsComment"), value: KSyntaxHighlighting::Theme::TextStyle::Comment);
157 m_engine->globalObject().setProperty(QStringLiteral("dsDocumentation"), value: KSyntaxHighlighting::Theme::TextStyle::Documentation);
158 m_engine->globalObject().setProperty(QStringLiteral("dsAnnotation"), value: KSyntaxHighlighting::Theme::TextStyle::Annotation);
159 m_engine->globalObject().setProperty(QStringLiteral("dsCommentVar"), value: KSyntaxHighlighting::Theme::TextStyle::CommentVar);
160 m_engine->globalObject().setProperty(QStringLiteral("dsRegionMarker"), value: KSyntaxHighlighting::Theme::TextStyle::RegionMarker);
161 m_engine->globalObject().setProperty(QStringLiteral("dsInformation"), value: KSyntaxHighlighting::Theme::TextStyle::Information);
162 m_engine->globalObject().setProperty(QStringLiteral("dsWarning"), value: KSyntaxHighlighting::Theme::TextStyle::Warning);
163 m_engine->globalObject().setProperty(QStringLiteral("dsAlert"), value: KSyntaxHighlighting::Theme::TextStyle::Alert);
164 m_engine->globalObject().setProperty(QStringLiteral("dsOthers"), value: KSyntaxHighlighting::Theme::TextStyle::Others);
165 m_engine->globalObject().setProperty(QStringLiteral("dsError"), value: KSyntaxHighlighting::Theme::TextStyle::Error);
166
167 // register scripts itself
168 QJSValue result = m_engine->evaluate(program: source, fileName: m_url);
169 if (hasException(object: result, file: m_url)) {
170 return false;
171 }
172
173 // AFTER SCRIPT: set the view/document objects as necessary
174 m_engine->globalObject().setProperty(QStringLiteral("editor"), value: m_engine->newQObject(object: m_editor = new KateScriptEditor()));
175 m_engine->globalObject().setProperty(QStringLiteral("document"), value: m_engine->newQObject(object: m_document = new KateScriptDocument(m_engine)));
176 m_engine->globalObject().setProperty(QStringLiteral("view"), value: m_engine->newQObject(object: m_view = new KateScriptView(m_engine)));
177
178 // yip yip!
179 m_loadSuccessful = true;
180
181 return true;
182}
183
184QJSValue KateScript::evaluate(const QString &program, const FieldMap &env)
185{
186 if (!load()) {
187 qCWarning(LOG_KTE) << "load of script failed:" << program;
188 return QJSValue();
189 }
190
191 // Wrap the arguments in a function to avoid polluting the global object
192 QString programWithContext =
193 QLatin1String("(function(") + QStringList(env.keys()).join(sep: QLatin1Char(',')) + QLatin1String(") { return ") + program + QLatin1String("})");
194 QJSValue programFunction = m_engine->evaluate(program: programWithContext);
195 Q_ASSERT(programFunction.isCallable());
196
197 QJSValueList args;
198 args.reserve(asize: env.size());
199 for (auto it = env.begin(); it != env.end(); it++) {
200 args << it.value();
201 }
202
203 QJSValue result = programFunction.call(args);
204 if (result.isError()) {
205 qCWarning(LOG_KTE) << "Error evaluating script: " << result.toString();
206 }
207
208 return result;
209}
210
211bool KateScript::hasException(const QJSValue &object, const QString &file)
212{
213 if (object.isError()) {
214 m_errorMessage = i18n("Error loading script %1", file);
215 displayBacktrace(error: object, header: m_errorMessage);
216 delete m_engine;
217 m_engine = nullptr;
218 m_loadSuccessful = false;
219 return true;
220 }
221 return false;
222}
223
224bool KateScript::setView(KTextEditor::ViewPrivate *view)
225{
226 if (!load()) {
227 return false;
228 }
229 // setup the stuff
230 m_document->setDocument(view->doc());
231 m_view->setView(view);
232 return true;
233}
234
235void KateScript::setGeneralHeader(const KateScriptHeader &generalHeader)
236{
237 m_generalHeader = generalHeader;
238}
239
240KateScriptHeader &KateScript::generalHeader()
241{
242 return m_generalHeader;
243}
244

source code of ktexteditor/src/script/katescript.cpp