File indexing completed on 2024-04-21 15:55:23
0001 /****************************************************************************** 0002 Copyright (C) 2006-2017 by Michel Ludwig (michel.ludwig@kdemail.net) 0003 2011-2012 by Holger Danielsson (holger.danielsson@versanet.de) 0004 ******************************************************************************/ 0005 0006 /************************************************************************** 0007 * * 0008 * This program is free software; you can redistribute it and/or modify * 0009 * it under the terms of the GNU General Public License as published by * 0010 * the Free Software Foundation; either version 2 of the License, or * 0011 * (at your option) any later version. * 0012 * * 0013 ***************************************************************************/ 0014 0015 #include "scripting/script.h" 0016 0017 #include <QFile> 0018 #include <QTextStream> 0019 #include <QScriptValue> 0020 #include <QTimer> 0021 0022 #include <KActionCollection> 0023 #include <KTextEditor/Range> 0024 #include <KTextEditor/Cursor> 0025 #include <KLocalizedString> 0026 #include <KMessageBox> 0027 0028 #include <iostream> 0029 0030 #include "kileinfo.h" 0031 #include "kiledebug.h" 0032 #include "scripting/kilescriptobject.h" 0033 #include "scripting/kilescriptview.h" 0034 #include "scripting/kilescriptdocument.h" 0035 0036 namespace KileScript { 0037 0038 /* 0039 // Modified declaration from <khtml/ecma/kjs_proxy.h> 0040 // Acknowledgements go to: 0041 // Copyright (C) 1999 Harri Porten (porten@kde.org) 0042 // Copyright (C) 2001 Peter Kelly (pmk@post.com) 0043 0044 class KJSCPUGuard { 0045 public: 0046 KJSCPUGuard() {} 0047 void start(unsigned int msec=5000, unsigned int i_msec=0); 0048 void stop(); 0049 private: 0050 void (*oldAlarmHandler)(int); 0051 static void alarmHandler(int); 0052 itimerval oldtv; 0053 }; 0054 0055 // Modified implementation originating from <khtml/ecma/kjs_proxy.cpp> 0056 // Acknowledgements go to: 0057 // Copyright (C) 1999-2001 Harri Porten (porten@kde.org) 0058 // Copyright (C) 2001,2003 Peter Kelly (pmk@post.com) 0059 // Copyright (C) 2001-2003 David Faure (faure@kde.org) 0060 void KJSCPUGuard::start(unsigned int ms, unsigned int i_ms) 0061 { 0062 oldAlarmHandler = signal(SIGVTALRM, alarmHandler); 0063 itimerval tv = { 0064 { i_ms / 1000, (i_ms % 1000) * 1000 }, 0065 { ms / 1000, (ms % 1000) * 1000 } 0066 }; 0067 setitimer(ITIMER_VIRTUAL, &tv, &oldtv); 0068 } 0069 0070 void KJSCPUGuard::stop() 0071 { 0072 setitimer(ITIMER_VIRTUAL, &oldtv, Q_NULLPTR); 0073 signal(SIGVTALRM, oldAlarmHandler); 0074 } 0075 0076 void KJSCPUGuard::alarmHandler(int) { 0077 0078 // KJS::ExecState::requestTerminate(); 0079 } 0080 */ 0081 0082 #ifdef __GNUC__ 0083 #warning "Fix time limit functionality!" 0084 #endif 0085 0086 0087 //BEGIN QtScript conversion functions for Cursors and Ranges 0088 /** Conversion function from KTextEditor::Cursor to QtScript cursor */ 0089 static QScriptValue cursorToScriptValue(QScriptEngine *engine, const KTextEditor::Cursor &cursor) 0090 { 0091 QString code = QString("new Cursor(%1, %2);").arg(cursor.line()) 0092 .arg(cursor.column()); 0093 return engine->evaluate(code); 0094 } 0095 0096 /** Conversion function from QtScript cursor to KTextEditor::Cursor */ 0097 static void cursorFromScriptValue(const QScriptValue &obj, KTextEditor::Cursor &cursor) 0098 { 0099 cursor.setPosition(obj.property(QStringLiteral("line")).toInt32(), 0100 obj.property(QStringLiteral("column")).toInt32()); 0101 } 0102 0103 /** Conversion function from QtScript range to KTextEditor::Range */ 0104 static QScriptValue rangeToScriptValue(QScriptEngine *engine, const KTextEditor::Range &range) 0105 { 0106 QString code = QString("new Range(%1, %2, %3, %4);").arg(range.start().line()) 0107 .arg(range.start().column()) 0108 .arg(range.end().line()) 0109 .arg(range.end().column()); 0110 return engine->evaluate(code); 0111 } 0112 0113 /** Conversion function from QtScript range to KTextEditor::Range */ 0114 static void rangeFromScriptValue(const QScriptValue &obj, KTextEditor::Range &range) 0115 { 0116 range.setStart(KTextEditor::Cursor(obj.property(QStringLiteral("start")).property(QStringLiteral("line")).toInt32(), 0117 obj.property(QStringLiteral("start")).property(QStringLiteral("column")).toInt32())); 0118 range.setEnd(KTextEditor::Cursor(obj.property(QStringLiteral("end")).property(QStringLiteral("line")).toInt32(), 0119 obj.property(QStringLiteral("end")).property(QStringLiteral("column")).toInt32())); 0120 } 0121 //END 0122 0123 ////////////////////////////// Script ////////////////////////////// 0124 0125 /* The IDs of the scripts are used to maintain correct bindings with QAction objects, i.e. for example, we 0126 * want to make sure the action script_execution_0 always refers to same script (the script with id 0 !), even 0127 * after reloading all the scripts. 0128 */ 0129 0130 Script::Script(unsigned int id, const QString& file) 0131 : m_id(id), m_file(file), m_action(Q_NULLPTR), m_sequencetype(KEY_SEQUENCE) 0132 { 0133 m_name = QFileInfo(file).fileName(); 0134 0135 if(m_name.endsWith(QLatin1String(".js"))) { // remove the extension 0136 m_name = m_name.left(m_name.length() - 3); 0137 } 0138 } 0139 0140 QString Script::getCode() const 0141 { 0142 return readFile(m_file); 0143 } 0144 0145 QString Script::getName() const 0146 { 0147 return m_name; 0148 } 0149 0150 QString Script::getFileName() const 0151 { 0152 return m_file; 0153 } 0154 0155 unsigned int Script::getID() const 0156 { 0157 return m_id; 0158 } 0159 0160 void Script::setID(unsigned int id) 0161 { 0162 m_id = id; 0163 } 0164 0165 void Script::setActionObject(QAction * action) 0166 { 0167 m_action = action; 0168 } 0169 0170 // const QAction * Script::getActionObject() const 0171 // { 0172 // return m_action; 0173 // } 0174 0175 QAction * Script::getActionObject() const 0176 { 0177 return m_action; 0178 } 0179 0180 void Script::setKeySequence(const QString& str) 0181 { 0182 m_keySequence = str; 0183 } 0184 0185 QString Script::getKeySequence() const 0186 { 0187 return m_keySequence; 0188 } 0189 0190 0191 int Script::getSequenceType() const 0192 { 0193 return m_sequencetype; 0194 } 0195 0196 void Script::setSequenceType(int type) 0197 { 0198 m_sequencetype = type; 0199 } 0200 0201 QString Script::readFile(const QString &filename) { 0202 QFile file(filename); 0203 if ( !file.open(QIODevice::ReadOnly) ) { 0204 KILE_DEBUG_MAIN << i18n("Unable to find '%1'", filename); 0205 return QString(); 0206 } else { 0207 QTextStream stream(&file); 0208 stream.setCodec("UTF-8"); 0209 QString text = stream.readAll(); 0210 file.close(); 0211 return text; 0212 } 0213 } 0214 0215 ////////////////////////////// ScriptEnvironment ////////////////////////////// 0216 0217 ScriptEnvironment::ScriptEnvironment(KileInfo *kileInfo, 0218 KileScriptView *scriptView, KileScriptDocument *scriptDocument, 0219 KileScriptObject *scriptObject, const QString &pluginCode) 0220 : m_kileInfo(kileInfo), m_scriptView(scriptView), m_scriptDocument(scriptDocument), 0221 m_kileScriptObject(scriptObject), m_enginePluginCode(pluginCode) 0222 { 0223 0224 KILE_DEBUG_MAIN << "create ScriptEnvironment"; 0225 m_engine = new QScriptEngine(); 0226 qScriptRegisterMetaType(m_engine, cursorToScriptValue, cursorFromScriptValue); 0227 qScriptRegisterMetaType(m_engine, rangeToScriptValue, rangeFromScriptValue); 0228 } 0229 0230 ScriptEnvironment::~ScriptEnvironment() 0231 { 0232 delete m_engine; 0233 } 0234 0235 // Executes script code in this environment. 0236 void ScriptEnvironment::execute(const Script *script) 0237 { 0238 // initialize engine to work with Cursor and Range objects 0239 m_engine->evaluate(m_enginePluginCode, i18n("Cursor/Range plugin")); 0240 0241 if(m_engine->hasUncaughtException()) { 0242 scriptError(i18n("Cursor/Range plugin")); 0243 return; 0244 } 0245 else { 0246 KILE_DEBUG_MAIN << "Cursor/Range plugin successfully installed "; 0247 } 0248 0249 // set global objects 0250 if(m_scriptView->view()) { 0251 m_engine->globalObject().setProperty("view", m_engine->newQObject(m_scriptView)); 0252 m_engine->globalObject().setProperty("document", m_engine->newQObject(m_scriptDocument)); 0253 } 0254 m_engine->globalObject().setProperty("kile", m_engine->newQObject(m_kileScriptObject)); 0255 0256 // export debug function 0257 m_engine->globalObject().setProperty("debug", m_engine->newFunction(KileScript::debug)); 0258 0259 // start engine 0260 m_engine->evaluate(script->getCode()); 0261 0262 // success or error 0263 if(m_engine->hasUncaughtException()) { 0264 scriptError(script->getName()); 0265 } 0266 else { 0267 KILE_DEBUG_MAIN << "script finished without errors"; 0268 } 0269 0270 //FIXME: add time execution limit once it becomes available 0271 // bool useGuard = KileConfig::timeLimitEnabled(); 0272 // uint timeLimit = (uint)KileConfig::timeLimit(); 0273 // KJSCPUGuard guard; 0274 // if(useGuard) { 0275 // guard.start(timeLimit*1000); 0276 // } 0277 // KJS::Completion completion = m_interpreter->evaluate(QString(), 0, s); 0278 // if(useGuard) { 0279 // guard.stop(); 0280 // } 0281 QTimer::singleShot(0, m_scriptView->view(), SLOT(setFocus())); 0282 0283 // remove global objects 0284 m_engine->globalObject().setProperty("view", QScriptValue()); 0285 m_engine->globalObject().setProperty("document", QScriptValue()); 0286 m_engine->globalObject().setProperty("kile", QScriptValue()); 0287 } 0288 0289 // Executes script code in this environment. 0290 void ScriptEnvironment::scriptError(const QString &name) 0291 { 0292 int errorline = m_engine->uncaughtExceptionLineNumber(); 0293 QScriptValue exception = m_engine->uncaughtException(); 0294 QString errormessage = ( exception.isError() ) ? exception.toString() : QString(); 0295 QString message = i18n("An error has occurred at line %1 during the execution of the script \"%2\":\n%3", errorline, name, errormessage); 0296 KMessageBox::error(m_kileInfo->mainWindow(), message, i18n("Error")); 0297 } 0298 0299 ////////////////////////////// ScriptHelpers ////////////////////////////// 0300 0301 QScriptValue debug(QScriptContext *context, QScriptEngine *engine) 0302 { 0303 QStringList message; 0304 for(int i = 0; i < context->argumentCount(); ++i) { 0305 message << context->argument(i).toString(); 0306 } 0307 // debug output in blue to distinguish it from other debug output 0308 std::cout << "\033[34m" << qPrintable(message.join(' ')) << "\033[0m\n"; 0309 return engine->nullValue(); 0310 } 0311 0312 }