• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

Kate

katescriptmanager.cpp

Go to the documentation of this file.
00001 // This file is part of the KDE libraries
00002 // Copyright (C) 2005 Christoph Cullmann <cullmann@kde.org>
00003 // Copyright (C) 2005 Joseph Wenninger <jowenn@kde.org>
00004 // Copyright (C) 2006, 2009 Dominik Haumann <dhaumann kde org>
00005 // Copyright (C) 2008 Paul Giannaros <paul@giannaros.org>
00006 // Copyright (C) 2010 Joseph Wenninger <jowenn@kde.org>
00007 //
00008 // This library is free software; you can redistribute it and/or
00009 // modify it under the terms of the GNU Library General Public
00010 // License version 2 as published by the Free Software Foundation.
00011 //
00012 // This library is distributed in the hope that it will be useful,
00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015 // Library General Public License for more details.
00016 //
00017 // You should have received a copy of the GNU Library General Public License
00018 // along with this library; see the file COPYING.LIB.  If not, write to
00019 // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020 // Boston, MA 02110-1301, USA.
00021 
00022 #include "katescriptmanager.h"
00023 
00024 #include <sys/types.h>
00025 #include <sys/stat.h>
00026 #include <unistd.h>
00027 
00028 #include <QFile>
00029 #include <QFileInfo>
00030 #include <QStringList>
00031 #include <QMap>
00032 #include <QUuid>
00033 
00034 #include <kconfig.h>
00035 #include <kconfiggroup.h>
00036 #include <kstandarddirs.h>
00037 #include <kde_file.h>
00038 
00039 #include "kateglobal.h"
00040 #include "katecmd.h"
00041 
00042 KateScriptManager::KateScriptManager() : QObject(), KTextEditor::Command()
00043 {
00044   KateCmd::self()->registerCommand (this);
00045 
00046   // false = force (ignore cache)
00047   collect("katepartscriptrc", "katepart/script/*.js", false);
00048 }
00049 
00050 KateScriptManager::~KateScriptManager()
00051 {
00052   KateCmd::self()->unregisterCommand (this);
00053   qDeleteAll(m_indentationScripts);
00054   qDeleteAll(m_commandLineScripts);
00055 }
00056 
00057 KateIndentScript *KateScriptManager::indenter(const QString &language)
00058 {
00059   KateIndentScript *highestPriorityIndenter = 0;
00060   foreach(KateIndentScript *indenter, m_languageToIndenters.value(language.toLower())) {
00061     // don't overwrite if there is already a result with a higher priority
00062     if(highestPriorityIndenter && indenter->indentHeader().priority() < highestPriorityIndenter->indentHeader().priority()) {
00063 #ifdef DEBUG_SCRIPTMANAGER
00064       kDebug(13050) << "Not overwriting indenter for"
00065                     << language << "as the priority isn't big enough (" <<
00066                     indenter->indentHeader().priority() << '<'
00067                     << highestPriorityIndenter->indentHeader().priority() << ')';
00068 #endif
00069     }
00070     else {
00071       highestPriorityIndenter = indenter;
00072     }
00073   }
00074 
00075 #ifdef DEBUG_SCRIPTMANAGER
00076   if(highestPriorityIndenter) {
00077     kDebug(13050) << "Found indenter" << highestPriorityIndenter->url() << "for" << language;
00078   } else {
00079     kDebug(13050) << "No indenter for" << language;
00080   }
00081 #endif
00082 
00083   return highestPriorityIndenter;
00084 }
00085 
00086 void KateScriptManager::collect(const QString& resourceFile,
00087                                 const QString& directory,
00088                                 bool force)
00089 {
00090   KConfig cfgFile(resourceFile, KConfig::NoGlobals);
00091 
00092   force = false;
00093   {
00094     KConfigGroup config(&cfgFile, "General");
00095     // If KatePart version does not match, better force a true reload
00096     if(KateGlobal::katePartVersion() != config.readEntry("kate-version", QString("0.0"))) {
00097       config.writeEntry("kate-version", KateGlobal::katePartVersion());
00098       force = true;
00099     }
00100   }
00101   // get a list of all .js files
00102   const QStringList list = KGlobal::dirs()->findAllResources("data", directory, KStandardDirs::NoDuplicates);
00103   // clear out the old scripts and reserve enough space
00104   qDeleteAll(m_indentationScripts);
00105   qDeleteAll(m_commandLineScripts);
00106   m_indentationScripts.clear();
00107   m_commandLineScripts.clear();
00108 
00109   m_languageToIndenters.clear();
00110   m_indentationScriptMap.clear();
00111 
00112   // iterate through the files and read info out of cache or file
00113   foreach(const QString &fileName, list) {
00114     // get abs filename....
00115     QFileInfo fi(fileName);
00116     const QString absPath = fi.absoluteFilePath();
00117     const QString baseName = fi.baseName ();
00118 
00119     // each file has a group
00120     const QString group = "Cache "+ fileName;
00121     KConfigGroup config(&cfgFile, group);
00122 
00123     // stat the file to get the last-modified-time
00124     KDE_struct_stat sbuf;
00125     memset(&sbuf, 0, sizeof(sbuf));
00126     KDE::stat(fileName, &sbuf);
00127 
00128     // check whether file is already cached
00129     bool useCache = false;
00130     if(!force && cfgFile.hasGroup(group)) {
00131       useCache = (sbuf.st_mtime == config.readEntry("last-modified", 0));
00132     }
00133 
00134     // read key/value pairs from the cached file if possible
00135     // otherwise, parse it and then save the needed information to the cache.
00136     QHash<QString, QString> pairs;
00137     if(useCache) {
00138       const QMap<QString, QString> entries = config.entryMap();
00139       for(QMap<QString, QString>::ConstIterator entry = entries.constBegin();
00140           entry != entries.constEnd();
00141           ++entry)
00142         pairs[entry.key()] = entry.value();
00143     }
00144     else if(parseMetaInformation(fileName, pairs)) {
00145       config.writeEntry("last-modified", int(sbuf.st_mtime));
00146       // iterate keys and save cache
00147       for(QHash<QString, QString>::ConstIterator item = pairs.constBegin();
00148           item != pairs.constEnd();
00149           ++item)
00150         config.writeEntry(item.key(), item.value());
00151     }
00152     else {
00153       // parseMetaInformation will have informed the user of the problem
00154       continue;
00155     }
00156     // make sure we have the necessary meta data items we need for proper execution
00157     KateScriptHeader generalHeader;
00158     QString type = pairs.take("type");
00159     if(type == "indentation") {
00160       generalHeader.setScriptType(Kate::IndentationScript);
00161     } else if (type == "commands") {
00162       generalHeader.setScriptType(Kate::CommandLineScript);
00163     } else {
00164       kDebug(13050) << "Script value error: No type specified in script meta data: "
00165                       << qPrintable(fileName) << '\n';
00166       continue;
00167     }
00168 
00169     generalHeader.setLicense(pairs.take("license"));
00170     generalHeader.setAuthor(pairs.take("author"));
00171     generalHeader.setRevision(pairs.take("revision").toInt());
00172     generalHeader.setKateVersion(pairs.take("kate-version"));
00173     generalHeader.setCatalog(pairs.take("i18n-catalog"));
00174 
00175     // now, cast accordingly based on type
00176     switch(generalHeader.scriptType()) {
00177       case Kate::IndentationScript: {
00178         KateIndentScriptHeader indentHeader;
00179         indentHeader.setName(pairs.take("name"));
00180         indentHeader.setBaseName(baseName);
00181         if (indentHeader.name().isNull()) {
00182           kDebug( 13050 ) << "Script value error: No name specified in script meta data: "
00183                  << qPrintable(fileName) << '\n' << "-> skipping indenter" << '\n';
00184           continue;
00185         }
00186 
00187         // required style?
00188         indentHeader.setRequiredStyle(pairs.take("required-syntax-style"));
00189         // which languages does this support?
00190         QString indentLanguages = pairs.take("indent-languages");
00191         if(!indentLanguages.isNull()) {
00192           indentHeader.setIndentLanguages(indentLanguages.split(','));
00193         }
00194         else {
00195           indentHeader.setIndentLanguages(QStringList() << indentHeader.name());
00196 
00197 #ifdef DEBUG_SCRIPTMANAGER
00198           kDebug( 13050 ) << "Script value warning: No indent-languages specified for indent "
00199                     << "script " << qPrintable(fileName) << ". Using the name ("
00200                     << qPrintable(indentHeader.name()) << ")\n";
00201 #endif
00202         }
00203         // priority?
00204         bool convertedToInt;
00205         int priority = pairs.take("priority").toInt(&convertedToInt);
00206 
00207 #ifdef DEBUG_SCRIPTMANAGER
00208         if(!convertedToInt) {
00209           kDebug( 13050 ) << "Script value warning: Unexpected or no priority value "
00210                     << "in: " << qPrintable(fileName) << ". Setting priority to 0\n";
00211         }
00212 #endif
00213 
00214         indentHeader.setPriority(convertedToInt ? priority : 0);
00215         KateIndentScript *script = new KateIndentScript(fileName, indentHeader);
00216         script->setGeneralHeader(generalHeader);
00217         foreach(const QString &language, indentHeader.indentLanguages()) {
00218           m_languageToIndenters[language.toLower()].push_back(script);
00219         }
00220 
00221         m_indentationScriptMap.insert(indentHeader.baseName(), script);
00222         m_indentationScripts.append(script);
00223         break;
00224       }
00225       case Kate::CommandLineScript: {
00226         KateCommandLineScriptHeader commandHeader;
00227         commandHeader.setFunctions(pairs.take("functions").split(QRegExp("\\s*,\\s*"), QString::SkipEmptyParts));
00228         if (commandHeader.functions().isEmpty()) {
00229           kDebug(13050) << "Script value error: No functions specified in script meta data: "
00230                         << qPrintable(fileName) << '\n' << "-> skipping script" << '\n';
00231           continue;
00232         }
00233         KateCommandLineScript* script = new KateCommandLineScript(fileName, commandHeader);
00234         script->setGeneralHeader(generalHeader);
00235         m_commandLineScripts.push_back(script);
00236         break;
00237       }
00238       case Kate::UnknownScript:
00239       default:
00240         kDebug( 13050 ) << "Script value warning: Unknown type ('" << qPrintable(type) << "'): "
00241                   << qPrintable(fileName) << '\n';
00242     }
00243   }
00244 
00245 
00246 
00247 #ifdef DEBUG_SCRIPTMANAGER
00248  // XX Test
00249   if(indenter("Python")) {
00250     kDebug( 13050 ) << "Python: " << indenter("Python")->global("triggerCharacters").isValid() << "\n";
00251     kDebug( 13050 ) << "Python: " << indenter("Python")->function("triggerCharacters").isValid() << "\n";
00252     kDebug( 13050 ) << "Python: " << indenter("Python")->global("blafldsjfklas").isValid() << "\n";
00253     kDebug( 13050 ) << "Python: " << indenter("Python")->function("indent").isValid() << "\n";
00254   }
00255   if(indenter("C"))
00256     kDebug( 13050 ) << "C: " << qPrintable(indenter("C")->url()) << "\n";
00257   if(indenter("lisp"))
00258     kDebug( 13050 ) << "LISP: " << qPrintable(indenter("Lisp")->url()) << "\n";
00259 #endif
00260 
00261   cfgFile.sync();
00262 }
00263 
00264 
00265 bool KateScriptManager::parseMetaInformation(const QString& url,
00266                                              QHash<QString, QString> &pairs)
00267 {
00268   // a valid script file -must- have the following format:
00269   // The first line must contain the string 'kate-script'.
00270   // All following lines have to have the format 'key : value'. So the value
00271   // is separated by a colon. Leading non-letter characters are ignored, that
00272   // include C and C++ comments for example.
00273   // Parsing the header stops at the first line with no ':'.
00274 
00275   QFile file(url);
00276   if(!file.open(QIODevice::ReadOnly)) {
00277     kDebug( 13050 ) << "Script parse error: Cannot open file " << qPrintable(url) << '\n';
00278     return false;
00279   }
00280 
00281   kDebug(13050) << "Update script: " << url;
00282   QTextStream ts(&file);
00283   ts.setCodec("UTF-8");
00284   if(!ts.readLine().contains("kate-script")) {
00285     kDebug( 13050 ) << "Script parse error: No header found in " << qPrintable(url) << '\n';
00286     file.close();
00287     return false;
00288   }
00289 
00290   QString line;
00291   while(!(line = ts.readLine()).isNull()) {
00292     int colon = line.indexOf(':');
00293     if(colon <= 0)
00294       break; // no colon -> end of header found
00295 
00296     // if -1 then 0. if >= 0, move after star.
00297     int start = 0; // start points to first letter. idea: skip '*' and '//'
00298     while(start < line.length() && !line.at(start).isLetter())
00299       ++start;
00300 
00301     QString key = line.mid(start, colon - start).trimmed();
00302     QString value = line.right(line.length() - (colon + 1)).trimmed();
00303     pairs[key] = value;
00304 
00305 #ifdef DEBUG_SCRIPTMANAGER
00306     kDebug(13050) << "KateScriptManager::parseMetaInformation: found pair: "
00307                   << "(" << key << " | " << value << ")";
00308 #endif
00309   }
00310 
00311   file.close();
00312   return true;
00313 }
00314 
00315 void KateScriptManager::reload()
00316 {
00317   KateScript::reloadScriptingApi();
00318   collect("katepartscriptrc", "katepart/script/*.js", true);
00319   emit reloaded();
00320 }
00321 
00323 
00324 bool KateScriptManager::exec(KTextEditor::View *view, const QString &_cmd, QString &errorMsg)
00325 {
00326   QStringList args(_cmd.split(QRegExp("\\s+"), QString::SkipEmptyParts));
00327   QString cmd(args.first());
00328   args.removeFirst();
00329 
00330   if(!view) {
00331     errorMsg = i18n("Could not access view");
00332     return false;
00333   }
00334 
00335   if (cmd == "reload-scripts") {
00336     reload();
00337     return true;
00338   } else {
00339     errorMsg = i18n("Command not found: %1", cmd);
00340     return false;
00341   }
00342 }
00343 
00344 bool KateScriptManager::help(KTextEditor::View *view, const QString &cmd, QString &msg)
00345 {
00346   Q_UNUSED(view)
00347 
00348   if (cmd == "reload-scripts") {
00349     msg = i18n("Reload all JavaScript files (indenters, command line scripts, etc).");
00350     return true;
00351   } else {
00352     msg = i18n("Command not found: %1", cmd);
00353     return false;
00354   }
00355 }
00356 
00357 const QStringList &KateScriptManager::cmds()
00358 {
00359   static QStringList l;
00360 
00361   l.clear();
00362   l << "reload-scripts";
00363 
00364   return l;
00365 }
00366 
00367 
00368 
00369 KTextEditor::TemplateScript* KateScriptManager::registerTemplateScript (QObject* owner, const QString& script)
00370 {
00371   KateTemplateScript* kts = new KateTemplateScript(script);
00372   m_templateScripts.append(kts);
00373 
00374   connect(owner, SIGNAL(destroyed(QObject*)),
00375           this, SLOT(slotTemplateScriptOwnerDestroyed(QObject*)));
00376   m_ownerScript.insertMulti(owner, kts);
00377   return kts;
00378 }
00379 
00380 void KateScriptManager::unregisterTemplateScript(KTextEditor::TemplateScript* templateScript)
00381 {
00382   int index = m_templateScripts.indexOf(templateScript);
00383   if (index != -1) {
00384     QObject* k = m_ownerScript.key(templateScript);
00385     m_ownerScript.remove(k, templateScript);
00386     m_templateScripts.removeAt(index);
00387     delete templateScript;
00388   }
00389 }
00390 
00391 void KateScriptManager::slotTemplateScriptOwnerDestroyed(QObject* owner)
00392 {
00393   while (m_ownerScript.contains(owner)) {
00394     KTextEditor::TemplateScript* templateScript = m_ownerScript.take(owner);
00395     kDebug() << "Destroying template script" << templateScript;
00396     m_templateScripts.removeAll(templateScript);
00397     delete templateScript;
00398   }
00399 }
00400 
00401 KateTemplateScript* KateScriptManager::templateScript(KTextEditor::TemplateScript* templateScript)
00402 {
00403   if (m_templateScripts.contains(templateScript)) {
00404     return static_cast<KateTemplateScript*>(templateScript);
00405   }
00406 
00407   return 0;
00408 }
00409 
00410 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal