Kate
katescript.cpp
Go to the documentation of this file.
00001 // This file is part of the KDE libraries 00002 // Copyright (C) 2008 Paul Giannaros <paul@giannaros.org> 00003 // Copyright (C) 2009, 2010 Dominik Haumann <dhaumann kde org> 00004 // Copyright (C) 2010 Joseph Wenninger <jowenn@kde.org> 00005 // 00006 // This library is free software; you can redistribute it and/or 00007 // modify it under the terms of the GNU Library General Public 00008 // License as published by the Free Software Foundation; either 00009 // version 2 of the License, or (at your option) version 3. 00010 // 00011 // This library is distributed in the hope that it will be useful, 00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 // Library General Public License for more details. 00015 // 00016 // You should have received a copy of the GNU Library General Public License 00017 // along with this library; see the file COPYING.LIB. If not, write to 00018 // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 // Boston, MA 02110-1301, USA. 00020 00021 #include "katescript.h" 00022 #include "katescriptdocument.h" 00023 #include "katescriptview.h" 00024 #include "katescripthelpers.h" 00025 #include "kateview.h" 00026 #include "katedocument.h" 00027 00028 #include <iostream> 00029 00030 #include <QFile> 00031 00032 #include <QScriptEngine> 00033 #include <QScriptValue> 00034 #include <QScriptContext> 00035 #include <QFileInfo> 00036 00037 #include <kdebug.h> 00038 #include <klocale.h> 00039 #include <kstandarddirs.h> 00040 #include <klocalizedstring.h> 00041 00042 //BEGIN conversion functions for Cursors and Ranges 00044 static QScriptValue cursorToScriptValue(QScriptEngine *engine, const KTextEditor::Cursor &cursor) 00045 { 00046 QString code = QString("new Cursor(%1, %2);").arg(cursor.line()) 00047 .arg(cursor.column()); 00048 return engine->evaluate(code); 00049 } 00050 00052 static void cursorFromScriptValue(const QScriptValue &obj, KTextEditor::Cursor &cursor) 00053 { 00054 cursor.setPosition(obj.property("line").toInt32(), 00055 obj.property("column").toInt32()); 00056 } 00057 00059 static QScriptValue rangeToScriptValue(QScriptEngine *engine, const KTextEditor::Range &range) 00060 { 00061 QString code = QString("new Range(%1, %2, %3, %4);").arg(range.start().line()) 00062 .arg(range.start().column()) 00063 .arg(range.end().line()) 00064 .arg(range.end().column()); 00065 return engine->evaluate(code); 00066 } 00067 00069 static void rangeFromScriptValue(const QScriptValue &obj, KTextEditor::Range &range) 00070 { 00071 range.start().setPosition(obj.property("start").property("line").toInt32(), 00072 obj.property("start").property("column").toInt32()); 00073 range.end().setPosition(obj.property("end").property("line").toInt32(), 00074 obj.property("end").property("column").toInt32()); 00075 } 00076 //END 00077 00078 00079 00080 bool KateScript::s_scriptingApiLoaded = false; 00081 00082 void KateScript::reloadScriptingApi() 00083 { 00084 s_scriptingApiLoaded = false; 00085 } 00086 00087 bool KateScript::readFile(const QString& sourceUrl, QString& sourceCode) 00088 { 00089 sourceCode = QString(); 00090 00091 QFile file(sourceUrl); 00092 if (!file.open(QIODevice::ReadOnly)) { 00093 kDebug(13050) << i18n("Unable to find '%1'", sourceUrl); 00094 return false; 00095 } else { 00096 QTextStream stream(&file); 00097 stream.setCodec("UTF-8"); 00098 sourceCode = stream.readAll(); 00099 file.close(); 00100 } 00101 return true; 00102 } 00103 00104 00105 KateScript::KateScript(const QString &urlOrScript, enum InputType inputType) 00106 : m_loaded(false) 00107 , m_loadSuccessful(false) 00108 , m_url(inputType == InputURL ? urlOrScript : QString()) 00109 , m_engine(0) 00110 , m_document(0) 00111 , m_view(0) 00112 , m_inputType(inputType) 00113 , m_script(inputType == InputSCRIPT ? urlOrScript : QString()) 00114 { 00115 } 00116 00117 KateScript::~KateScript() 00118 { 00119 if(m_loadSuccessful) { 00120 // unload i18n catalog if available + loaded 00121 if (!generalHeader().catalog().isEmpty()) { 00122 kDebug() << "unloading i18n catalog" << generalHeader().catalog(); 00123 KGlobal::locale()->removeCatalog(generalHeader().catalog()); 00124 } 00125 00126 // remove data... 00127 delete m_engine; 00128 delete m_document; 00129 delete m_view; 00130 } 00131 } 00132 00133 QString KateScript::backtrace( const QScriptValue& error, const QString& header ) 00134 { 00135 QString bt; 00136 if(!header.isNull()) 00137 bt += header + ":\n"; 00138 if(error.isError()) 00139 bt += error.toString() + '\n'; 00140 00141 bt += m_engine->uncaughtExceptionBacktrace().join("\n") + '\n'; 00142 00143 return bt; 00144 } 00145 00146 void KateScript::displayBacktrace(const QScriptValue &error, const QString &header) 00147 { 00148 if(!m_engine) { 00149 std::cerr << "KateScript::displayBacktrace: no engine, cannot display error\n"; 00150 return; 00151 } 00152 std::cerr << "\033[31m" << qPrintable(backtrace(error, header)) << "\033[0m" << '\n'; 00153 } 00154 00155 void KateScript::clearExceptions() 00156 { 00157 if (!load()) 00158 return; 00159 m_engine->clearExceptions(); 00160 } 00161 00162 QScriptValue KateScript::global(const QString &name) 00163 { 00164 // load the script if necessary 00165 if(!load()) 00166 return QScriptValue(); 00167 return m_engine->globalObject().property(name); 00168 } 00169 00170 QScriptValue KateScript::function(const QString &name) 00171 { 00172 QScriptValue value = global(name); 00173 if(!value.isFunction()) 00174 return QScriptValue(); 00175 return value; 00176 } 00177 00178 bool KateScript::initApi () 00179 { 00180 // cache file names 00181 static QStringList apiFileBaseNames; 00182 static QHash<QString, QString> apiBaseName2FileName; 00183 static QHash<QString, QString> apiBaseName2Content; 00184 00185 // read katepart javascript api 00186 if (!s_scriptingApiLoaded) { 00187 s_scriptingApiLoaded = true; 00188 apiFileBaseNames.clear (); 00189 apiBaseName2FileName.clear (); 00190 apiBaseName2Content.clear (); 00191 00192 // get all api files 00193 const QStringList list = KGlobal::dirs()->findAllResources("data","katepart/api/*.js", KStandardDirs::NoDuplicates); 00194 00195 for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it ) 00196 { 00197 // get abs filename.... 00198 QFileInfo fi(*it); 00199 const QString absPath = fi.absoluteFilePath(); 00200 const QString baseName = fi.baseName (); 00201 00202 // remember filenames 00203 apiFileBaseNames.append (baseName); 00204 apiBaseName2FileName[baseName] = absPath; 00205 00206 // read the file 00207 QString content; 00208 readFile(absPath, content); 00209 apiBaseName2Content[baseName] = content; 00210 } 00211 00212 // sort... 00213 apiFileBaseNames.sort (); 00214 } 00215 00216 // register all script apis found 00217 for ( QStringList::ConstIterator it = apiFileBaseNames.constBegin(); it != apiFileBaseNames.constEnd(); ++it ) 00218 { 00219 // try to load into engine, bail out one error, use fullpath for error messages 00220 QScriptValue apiObject = m_engine->evaluate(apiBaseName2Content[*it], apiBaseName2FileName[*it]); 00221 if (hasException(apiObject, apiBaseName2FileName[*it])) { 00222 return false; 00223 } 00224 } 00225 00226 // success ;) 00227 return true; 00228 } 00229 00230 bool KateScript::load() 00231 { 00232 if(m_loaded) 00233 return m_loadSuccessful; 00234 00235 m_loaded = true; 00236 m_loadSuccessful = false; // here set to false, and at end of function to true 00237 00238 // read the script file into memory 00239 QString source; 00240 if (m_inputType == InputURL) { 00241 if (!readFile(m_url, source)) { 00242 return false; 00243 } 00244 } else source = m_script; 00245 00246 // create script engine, register meta types 00247 m_engine = new QScriptEngine(); 00248 qScriptRegisterMetaType (m_engine, cursorToScriptValue, cursorFromScriptValue); 00249 qScriptRegisterMetaType (m_engine, rangeToScriptValue, rangeFromScriptValue); 00250 00251 // init API 00252 if (!initApi ()) 00253 return false; 00254 00255 // register scripts itself 00256 QScriptValue result = m_engine->evaluate(source, m_url); 00257 if (hasException(result, m_url)) { 00258 return false; 00259 } 00260 00261 // yip yip! 00262 initEngine(); 00263 m_loadSuccessful = true; 00264 // load i18n catalog if available 00265 if (!generalHeader().catalog().isEmpty()) { 00266 kDebug() << "loading i18n catalog" << generalHeader().catalog(); 00267 KGlobal::locale()->insertCatalog(generalHeader().catalog()); 00268 } 00269 return true; 00270 } 00271 00272 bool KateScript::hasException(const QScriptValue& object, const QString& file) 00273 { 00274 if(m_engine->hasUncaughtException()) { 00275 displayBacktrace(object, i18n("Error loading script %1\n", file)); 00276 m_errorMessage = i18n("Error loading script %1", file); 00277 delete m_engine; 00278 m_engine = 0; 00279 m_loadSuccessful = false; 00280 return true; 00281 } 00282 return false; 00283 } 00284 00285 00286 void KateScript::initEngine() 00287 { 00288 // set the view/document objects as necessary 00289 m_engine->globalObject().setProperty("document", m_engine->newQObject(m_document = new KateScriptDocument())); 00290 m_engine->globalObject().setProperty("view", m_engine->newQObject(m_view = new KateScriptView())); 00291 00292 // export debug function 00293 m_engine->globalObject().setProperty("debug", m_engine->newFunction(Kate::Script::debug)); 00294 00295 // export translation functions 00296 m_engine->globalObject().setProperty("i18n", m_engine->newFunction(Kate::Script::i18n)); 00297 m_engine->globalObject().setProperty("i18nc", m_engine->newFunction(Kate::Script::i18nc)); 00298 m_engine->globalObject().setProperty("i18ncp", m_engine->newFunction(Kate::Script::i18ncp)); 00299 m_engine->globalObject().setProperty("i18np", m_engine->newFunction(Kate::Script::i18np)); 00300 } 00301 00302 bool KateScript::setView(KateView *view) 00303 { 00304 if (!load()) 00305 return false; 00306 // setup the stuff 00307 m_document->setDocument (view->doc()); 00308 m_view->setView (view); 00309 return true; 00310 } 00311 00312 void KateScript::setGeneralHeader(const KateScriptHeader& generalHeader) 00313 { 00314 m_generalHeader = generalHeader; 00315 } 00316 00317 KateScriptHeader& KateScript::generalHeader() 00318 { 00319 return m_generalHeader; 00320 } 00321 00322 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE 4.6 API Reference