diff options
Diffstat (limited to 'kate/part/katejscript.cpp')
-rw-r--r-- | kate/part/katejscript.cpp | 1169 |
1 files changed, 1169 insertions, 0 deletions
diff --git a/kate/part/katejscript.cpp b/kate/part/katejscript.cpp new file mode 100644 index 000000000..024f36500 --- /dev/null +++ b/kate/part/katejscript.cpp @@ -0,0 +1,1169 @@ +/* This file is part of the KDE libraries + Copyright (C) 2005 Christoph Cullmann <cullmann@kde.org> + Copyright (C) 2005 Joseph Wenninger <jowenn@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katejscript.h" + +#include "katedocument.h" +#include "kateview.h" +#include "katefactory.h" +#include "kateconfig.h" +#include "kateautoindent.h" +#include "katehighlight.h" +#include "katetextline.h" + +#include "kateindentscriptabstracts.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <kdebug.h> +#include <kstandarddirs.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kconfig.h> + +#include <kjs/function_object.h> +#include <kjs/interpreter.h> +#include <kjs/lookup.h> + +#include <qfile.h> +#include <qfileinfo.h> +#include <qpopupmenu.h> +#include <qregexp.h> +#include <qtextstream.h> + + +namespace KJS { + +// taken from khtml +// therefor thx to: +// Copyright (C) 1999-2003 Harri Porten (porten@kde.org) +// Copyright (C) 2001-2003 David Faure (faure@kde.org) +// Copyright (C) 2003 Apple Computer, Inc. + +UString::UString(const QString &d) +{ + unsigned int len = d.length(); + UChar *dat = new UChar[len]; + memcpy(dat, d.unicode(), len * sizeof(UChar)); + rep = UString::Rep::create(dat, len); +} + +QString UString::qstring() const +{ + return QString((QChar*) data(), size()); +} + +QConstString UString::qconststring() const +{ + return QConstString((QChar*) data(), size()); +} + +//BEGIN global methods +class KateJSGlobalFunctions : public ObjectImp +{ + public: + KateJSGlobalFunctions(int i, int length); + virtual bool implementsCall() const { return true; } + virtual Value call(ExecState *exec, Object &thisObj, const List &args); + + enum { + Debug + }; + + private: + int id; +}; +KateJSGlobalFunctions::KateJSGlobalFunctions(int i, int length) : ObjectImp(), id(i) +{ + putDirect(lengthPropertyName,length,DontDelete|ReadOnly|DontEnum); +} +Value KateJSGlobalFunctions::call(ExecState *exec, Object &/*thisObj*/, const List &args) +{ + switch (id) { + case Debug: + qDebug("Kate (KJS Scripting): %s", args[0].toString(exec).ascii()); + return Undefined(); + default: + break; + } + + return Undefined(); +} +//END global methods + +} // namespace KJS + +//BEGIN JS API STUFF + +class KateJSGlobal : public KJS::ObjectImp { +public: + virtual KJS::UString className() const { return "global"; } +}; + +class KateJSDocument : public KJS::ObjectImp +{ + public: + KateJSDocument (KJS::ExecState *exec, KateDocument *_doc); + + KJS::Value get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const; + + KJS::Value getValueProperty(KJS::ExecState *exec, int token) const; + + void put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr = KJS::None); + + void putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr); + + const KJS::ClassInfo* classInfo() const { return &info; } + + enum { FullText, + Text, + TextLine, + Lines, + Length, + LineLength, + SetText, + Clear, + InsertText, + RemoveText, + InsertLine, + RemoveLine, + EditBegin, + EditEnd, + IndentWidth, + IndentMode, + SpaceIndent, + MixedIndent, + HighlightMode, + IsInWord, + CanBreakAt, + CanComment, + CommentMarker, + CommentStart, + CommentEnd, + Attribute + }; + + public: + KateDocument *doc; + + static const KJS::ClassInfo info; +}; + +class KateJSView : public KJS::ObjectImp +{ + public: + KateJSView (KJS::ExecState *exec, KateView *_view); + + KJS::Value get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const; + + KJS::Value getValueProperty(KJS::ExecState *exec, int token) const; + + void put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr = KJS::None); + + void putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr); + + const KJS::ClassInfo* classInfo() const { return &info; } + + enum { CursorLine, + CursorColumn, + CursorColumnReal, + SetCursorPosition, + SetCursorPositionReal, + Selection, + HasSelection, + SetSelection, + RemoveSelectedText, + SelectAll, + ClearSelection, + SelStartLine, + SelStartCol, + SelEndLine, + SelEndCol + }; + + public: + KateView *view; + + static const KJS::ClassInfo info; +}; + +class KateJSIndenter : public KJS::ObjectImp +{ + public: + KateJSIndenter (KJS::ExecState *exec); + /* + KJS::Value get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const; + + KJS::Value getValueProperty(KJS::ExecState *exec, int token) const; + + void put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr = KJS::None); + + void putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr); + */ + const KJS::ClassInfo* classInfo() const { return &info; } + + enum { OnChar, + OnLine, + OnNewline, + Dummy + }; + + public: + + static const KJS::ClassInfo info; +}; + +#include "katejscript.lut.h" + +//END + +KateJScript::KateJScript () + : m_global (new KJS::Object (new KateJSGlobal ())) + , m_interpreter (new KJS::Interpreter (*m_global)) + , m_document (new KJS::Object(wrapDocument(m_interpreter->globalExec(), 0))) + , m_view (new KJS::Object (wrapView(m_interpreter->globalExec(), 0))) +{ + // put some stuff into env., this should stay for all executions, as we keep external + // references to the inserted KJS::Objects, this should avoid any garbage collection + m_interpreter->globalObject().put(m_interpreter->globalExec(), "document", *m_document); + m_interpreter->globalObject().put(m_interpreter->globalExec(), "view", *m_view); + m_interpreter->globalObject().put(m_interpreter->globalExec(), "debug", + KJS::Object(new KateJSGlobalFunctions(KateJSGlobalFunctions::Debug,1))); +} + +KateJScript::~KateJScript () +{ + delete m_view; + delete m_document; + delete m_interpreter; + delete m_global; +} + +KJS::ObjectImp *KateJScript::wrapDocument (KJS::ExecState *exec, KateDocument *doc) +{ + return new KateJSDocument(exec, doc); +} + +KJS::ObjectImp *KateJScript::wrapView (KJS::ExecState *exec, KateView *view) +{ + return new KateJSView(exec, view); +} + +bool KateJScript::execute (KateView *view, const QString &script, QString &errorMsg) +{ + // no view, no fun + if (!view) + { + errorMsg = i18n("Could not access view"); + return false; + } + + // init doc & view with new pointers! + static_cast<KateJSDocument *>( m_document->imp() )->doc = view->doc(); + static_cast<KateJSView *>( m_view->imp() )->view = view; + + // run the script for real + KJS::Completion comp (m_interpreter->evaluate(script)); + + if (comp.complType() == KJS::Throw) + { + KJS::ExecState *exec = m_interpreter->globalExec(); + + KJS::Value exVal = comp.value(); + + char *msg = exVal.toString(exec).ascii(); + + int lineno = -1; + + if (exVal.type() == KJS::ObjectType) + { + KJS::Value lineVal = KJS::Object::dynamicCast(exVal).get(exec,"line"); + + if (lineVal.type() == KJS::NumberType) + lineno = int(lineVal.toNumber(exec)); + } + + errorMsg = i18n("Exception, line %1: %2").arg(lineno).arg(msg); + return false; + } + + return true; +} + +//BEGIN KateJSDocument + +// ------------------------------------------------------------------------- +/* Source for KateJSDocumentProtoTable. +@begin KateJSDocumentProtoTable 21 +# +# edit interface stuff + editBegin/End, this is nice start +# + textFull KateJSDocument::FullText DontDelete|Function 0 + textRange KateJSDocument::Text DontDelete|Function 4 + textLine KateJSDocument::TextLine DontDelete|Function 1 + lines KateJSDocument::Lines DontDelete|Function 0 + length KateJSDocument::Length DontDelete|Function 0 + lineLength KateJSDocument::LineLength DontDelete|Function 1 + setText KateJSDocument::SetText DontDelete|Function 1 + clear KateJSDocument::Clear DontDelete|Function 0 + insertText KateJSDocument::InsertText DontDelete|Function 3 + removeText KateJSDocument::RemoveText DontDelete|Function 4 + insertLine KateJSDocument::InsertLine DontDelete|Function 2 + removeLine KateJSDocument::RemoveLine DontDelete|Function 1 + editBegin KateJSDocument::EditBegin DontDelete|Function 0 + editEnd KateJSDocument::EditEnd DontDelete|Function 0 +# +# methods from highlight (and around) +# + isInWord KateJSDocument::IsInWord DontDelete|Function 2 + canBreakAt KateJSDocument::CanBreakAt DontDelete|Function 2 + canComment KateJSDocument::CanComment DontDelete|Function 2 + commentMarker KateJSDocument::CommentMarker DontDelete|Function 1 + commentStart KateJSDocument::CommentStart DontDelete|Function 1 + commentEnd KateJSDocument::CommentEnd DontDelete|Function 1 + attribute KateJSDocument::Attribute DontDelete|Function 2 +@end + +@begin KateJSDocumentTable 6 +# +# Configuration properties +# + indentWidth KateJSDocument::IndentWidth DontDelete|ReadOnly + indentMode KateJSDocument::IndentMode DontDelete|ReadOnly + spaceIndent KateJSDocument::SpaceIndent DontDelete|ReadOnly + mixedIndent KateJSDocument::MixedIndent DontDelete|ReadOnly + highlightMode KateJSDocument::HighlightMode DontDelete|ReadOnly +@end +*/ + +DEFINE_PROTOTYPE("KateJSDocument",KateJSDocumentProto) +IMPLEMENT_PROTOFUNC(KateJSDocumentProtoFunc) +IMPLEMENT_PROTOTYPE(KateJSDocumentProto,KateJSDocumentProtoFunc) + +const KJS::ClassInfo KateJSDocument::info = { "KateJSDocument", 0, 0, 0 }; + +KJS::Value KJS::KateJSDocumentProtoFunc::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args) +{ + KJS_CHECK_THIS( KateJSDocument, thisObj ); + + KateDocument *doc = static_cast<KateJSDocument *>( thisObj.imp() )->doc; + + if (!doc) + return KJS::Undefined(); + + switch (id) + { + case KateJSDocument::FullText: + return KJS::String (doc->text()); + + case KateJSDocument::Text: + return KJS::String (doc->text(args[0].toUInt32(exec), args[1].toUInt32(exec), args[2].toUInt32(exec), args[3].toUInt32(exec))); + + case KateJSDocument::TextLine: + return KJS::String (doc->textLine (args[0].toUInt32(exec))); + + case KateJSDocument::Lines: + return KJS::Number (doc->numLines()); + + case KateJSDocument::Length: + return KJS::Number (doc->length()); + + case KateJSDocument::LineLength: + return KJS::Number (doc->lineLength(args[0].toUInt32(exec))); + + case KateJSDocument::SetText: + return KJS::Boolean (doc->setText(args[0].toString(exec).qstring())); + + case KateJSDocument::Clear: + return KJS::Boolean (doc->clear()); + + case KateJSDocument::InsertText: + return KJS::Boolean (doc->insertText (args[0].toUInt32(exec), args[1].toUInt32(exec), args[2].toString(exec).qstring())); + + case KateJSDocument::RemoveText: + return KJS::Boolean (doc->removeText(args[0].toUInt32(exec), args[1].toUInt32(exec), args[2].toUInt32(exec), args[3].toUInt32(exec))); + + case KateJSDocument::InsertLine: + return KJS::Boolean (doc->insertLine (args[0].toUInt32(exec), args[1].toString(exec).qstring())); + + case KateJSDocument::RemoveLine: + return KJS::Boolean (doc->removeLine (args[0].toUInt32(exec))); + + case KateJSDocument::EditBegin: + doc->editBegin(); + return KJS::Null (); + + case KateJSDocument::EditEnd: + doc->editEnd (); + return KJS::Null (); + + case KateJSDocument::IsInWord: + return KJS::Boolean( doc->highlight()->isInWord( args[0].toString(exec).qstring().at(0), args[1].toUInt32(exec) ) ); + + case KateJSDocument::CanBreakAt: + return KJS::Boolean( doc->highlight()->canBreakAt( args[0].toString(exec).qstring().at(0), args[1].toUInt32(exec) ) ); + + case KateJSDocument::CanComment: + return KJS::Boolean( doc->highlight()->canComment( args[0].toUInt32(exec), args[1].toUInt32(exec) ) ); + + case KateJSDocument::CommentMarker: + return KJS::String( doc->highlight()->getCommentSingleLineStart( args[0].toUInt32(exec) ) ); + + case KateJSDocument::CommentStart: + return KJS::String( doc->highlight()->getCommentStart( args[0].toUInt32(exec) ) ); + + case KateJSDocument::CommentEnd: + return KJS::String( doc->highlight()->getCommentEnd( args[0].toUInt32(exec) ) ); + + case KateJSDocument::Attribute: + return KJS::Number( doc->kateTextLine(args[0].toUInt32(exec))->attribute(args[1].toUInt32(exec)) ); + } + + return KJS::Undefined(); +} + +KJS::Value KateJSDocument::get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const +{ + return KJS::lookupGetValue<KateJSDocument,KJS::ObjectImp>(exec, propertyName, &KateJSDocumentTable, this ); +} + +KJS::Value KateJSDocument::getValueProperty(KJS::ExecState *exec, int token) const +{ + if (!doc) + return KJS::Undefined (); + + switch (token) { + case KateJSDocument::IndentWidth: + return KJS::Number( doc->config()->indentationWidth() ); + + case KateJSDocument::IndentMode: + return KJS::String( KateAutoIndent::modeName( doc->config()->indentationMode() ) ); + + case KateJSDocument::SpaceIndent: + return KJS::Boolean( doc->config()->configFlags() & KateDocumentConfig::cfSpaceIndent ); + + case KateJSDocument::MixedIndent: + return KJS::Boolean( doc->config()->configFlags() & KateDocumentConfig::cfMixedIndent ); + + case KateJSDocument::HighlightMode: + return KJS::String( doc->hlModeName( doc->hlMode() ) ); + } + + return KJS::Undefined (); +} + +void KateJSDocument::put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr) +{ + KJS::lookupPut<KateJSDocument,KJS::ObjectImp>(exec, propertyName, value, attr, &KateJSDocumentTable, this ); +} + +void KateJSDocument::putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr) +{ + if (!doc) + return; +} + +KateJSDocument::KateJSDocument (KJS::ExecState *exec, KateDocument *_doc) + : KJS::ObjectImp (KateJSDocumentProto::self(exec)) + , doc (_doc) +{ +} + +//END + +//BEGIN KateJSView + +// ------------------------------------------------------------------------- +/* Source for KateJSViewProtoTable. +@begin KateJSViewProtoTable 14 + cursorLine KateJSView::CursorLine DontDelete|Function 0 + cursorColumn KateJSView::CursorColumn DontDelete|Function 0 + cursorColumnReal KateJSView::CursorColumnReal DontDelete|Function 0 + setCursorPosition KateJSView::SetCursorPosition DontDelete|Function 2 + setCursorPositionReal KateJSView::SetCursorPositionReal DontDelete|Function 2 + selection KateJSView::Selection DontDelete|Function 0 + hasSelection KateJSView::HasSelection DontDelete|Function 0 + setSelection KateJSView::SetSelection DontDelete|Function 4 + removeSelectedText KateJSView::RemoveSelectedText DontDelete|Function 0 + selectAll KateJSView::SelectAll DontDelete|Function 0 + clearSelection KateJSView::ClearSelection DontDelete|Function 0 +@end +*/ + +/* Source for KateJSViewTable. +@begin KateJSViewTable 5 + selectionStartLine KateJSView::SelStartLine DontDelete|ReadOnly + selectionStartColumn KateJSView::SelStartCol DontDelete|ReadOnly + selectionEndLine KateJSView::SelEndLine DontDelete|ReadOnly + selectionEndColumn KateJSView::SelEndCol DontDelete|ReadOnly +@end +*/ + +DEFINE_PROTOTYPE("KateJSView",KateJSViewProto) +IMPLEMENT_PROTOFUNC(KateJSViewProtoFunc) +IMPLEMENT_PROTOTYPE(KateJSViewProto,KateJSViewProtoFunc) + +const KJS::ClassInfo KateJSView::info = { "KateJSView", 0, &KateJSViewTable, 0 }; + +KJS::Value KJS::KateJSViewProtoFunc::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args) +{ + KJS_CHECK_THIS( KateJSView, thisObj ); + + KateView *view = static_cast<KateJSView *>( thisObj.imp() )->view; + + if (!view) + return KJS::Undefined(); + + switch (id) + { + case KateJSView::CursorLine: + return KJS::Number (view->cursorLine()); + + case KateJSView::CursorColumn: + return KJS::Number (view->cursorColumn()); + + case KateJSView::CursorColumnReal: + return KJS::Number (view->cursorColumnReal()); + + case KateJSView::SetCursorPosition: + return KJS::Boolean( view->setCursorPosition( args[0].toUInt32(exec), args[1].toUInt32(exec) ) ); + + case KateJSView::SetCursorPositionReal: + return KJS::Boolean( view->setCursorPositionReal( args[0].toUInt32(exec), args[1].toUInt32(exec) ) ); + + // SelectionInterface goes in the view, in anticipation of the future + case KateJSView::Selection: + return KJS::String( view->selection() ); + + case KateJSView::HasSelection: + return KJS::Boolean( view->hasSelection() ); + + case KateJSView::SetSelection: + return KJS::Boolean( view->setSelection(args[0].toUInt32(exec), + args[1].toUInt32(exec), + args[2].toUInt32(exec), + args[3].toUInt32(exec)) ); + + case KateJSView::RemoveSelectedText: + return KJS::Boolean( view->removeSelectedText() ); + + case KateJSView::SelectAll: + return KJS::Boolean( view->selectAll() ); + + case KateJSView::ClearSelection: + return KJS::Boolean( view->clearSelection() ); + } + + return KJS::Undefined(); +} + +KateJSView::KateJSView (KJS::ExecState *exec, KateView *_view) + : KJS::ObjectImp (KateJSViewProto::self(exec)) + , view (_view) +{ +} + +KJS::Value KateJSView::get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const +{ + return KJS::lookupGetValue<KateJSView,KJS::ObjectImp>(exec, propertyName, &KateJSViewTable, this ); +} + +KJS::Value KateJSView::getValueProperty(KJS::ExecState *exec, int token) const +{ + if (!view) + return KJS::Undefined (); + + switch (token) { + case KateJSView::SelStartLine: + return KJS::Number( view->selStartLine() ); + + case KateJSView::SelStartCol: + return KJS::Number( view->selStartCol() ); + + case KateJSView::SelEndLine: + return KJS::Number( view->selEndLine() ); + + case KateJSView::SelEndCol: + return KJS::Number( view->selEndCol() ); + } + + return KJS::Undefined (); +} + +void KateJSView::put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr) +{ + KJS::lookupPut<KateJSView,KJS::ObjectImp>(exec, propertyName, value, attr, &KateJSViewTable, this ); +} + +void KateJSView::putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr) +{ + if (!view) + return; + + +} + +//END + +//BEGIN KateJScriptManager + +KateJScriptManager::KateJScriptManager () +{ + m_scripts.setAutoDelete (true); + collectScripts (); +} + +KateJScriptManager::~KateJScriptManager () +{ +} + +void KateJScriptManager::collectScripts (bool force) +{ +// If there's something in myModeList the Mode List was already built so, don't do it again + if (!m_scripts.isEmpty()) + return; + + // We'll store the scripts list in this config + KConfig config("katepartjscriptrc", false, false); + + // figure out if the kate install is too new + config.setGroup ("General"); + if (config.readNumEntry ("Version") > config.readNumEntry ("CachedVersion")) + { + config.writeEntry ("CachedVersion", config.readNumEntry ("Version")); + force = true; + } + + // Let's get a list of all the .js files + QStringList list = KGlobal::dirs()->findAllResources("data","katepart/scripts/*.js",false,true); + + // Let's iterate through the list and build the Mode List + for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) + { + // Each file has a group called: + QString Group="Cache "+ *it; + + // Let's go to this group + config.setGroup(Group); + + // stat the file + struct stat sbuf; + memset (&sbuf, 0, sizeof(sbuf)); + stat(QFile::encodeName(*it), &sbuf); + + // If the group exist and we're not forced to read the .js file, let's build myModeList for katepartjscriptrc + if (!force && config.hasGroup(Group) && (sbuf.st_mtime == config.readNumEntry("lastModified"))) + { + } + else + { + kdDebug (13050) << "add script: " << *it << endl; + + QString desktopFile = (*it).left((*it).length()-2).append ("desktop"); + + kdDebug (13050) << "add script (desktop file): " << desktopFile << endl; + + QFileInfo dfi (desktopFile); + + if (dfi.exists()) + { + KConfig df (desktopFile, true, false); + df.setDesktopGroup (); + + // get cmdname, fallback to baseName, if it is empty, therefor not use the kconfig fallback + QString cmdname = df.readEntry ("X-Kate-Command"); + if (cmdname.isEmpty()) + { + QFileInfo fi (*it); + cmdname = fi.baseName(); + } + + if (m_scripts[cmdname]) + continue; + + KateJScriptManager::Script *s = new KateJScriptManager::Script (); + + s->name = cmdname; + s->filename = *it; + s->desktopFileExists = true; + + m_scripts.insert (s->name, s); + } + else // no desktop file around, fall back to scriptfilename == commandname + { + kdDebug (13050) << "add script: fallback, no desktop file around!" << endl; + + QFileInfo fi (*it); + + if (m_scripts[fi.baseName()]) + continue; + + KateJScriptManager::Script *s = new KateJScriptManager::Script (); + + s->name = fi.baseName(); + s->filename = *it; + s->desktopFileExists = false; + + m_scripts.insert (s->name, s); + } + } + } + + // Syncronize with the file katepartjscriptrc + config.sync(); +} + +bool KateJScriptManager::exec( Kate::View *view, const QString &_cmd, QString &errorMsg ) +{ + // cast it hardcore, we know that it is really a kateview :) + KateView *v = (KateView*) view; + + if ( !v ) + { + errorMsg = i18n("Could not access view"); + return false; + } + + //create a list of args + QStringList args( QStringList::split( QRegExp("\\s+"), _cmd ) ); + QString cmd ( args.first() ); + args.remove( args.first() ); + + kdDebug(13050) << "try to exec: " << cmd << endl; + + if (!m_scripts[cmd]) + { + errorMsg = i18n("Command not found"); + return false; + } + + QFile file (m_scripts[cmd]->filename); + + if ( !file.open( IO_ReadOnly ) ) + { + errorMsg = i18n("JavaScript file not found"); + return false; + } + + QTextStream stream( &file ); + stream.setEncoding (QTextStream::UnicodeUTF8); + + QString source = stream.read (); + + file.close(); + + return KateFactory::self()->jscript()->execute(v, source, errorMsg); +} + +bool KateJScriptManager::help( Kate::View *, const QString &cmd, QString &msg ) +{ + if (!m_scripts[cmd] || !m_scripts[cmd]->desktopFileExists) + return false; + + KConfig df (m_scripts[cmd]->desktopFilename(), true, false); + df.setDesktopGroup (); + + msg = df.readEntry ("X-Kate-Help"); + + if (msg.isEmpty()) + return false; + + return true; +} + +QStringList KateJScriptManager::cmds() +{ + QStringList l; + + QDictIterator<KateJScriptManager::Script> it( m_scripts ); + for( ; it.current(); ++it ) + l << it.current()->name; + + return l; +} + +//END + + + + +//BEGIN KateJSIndenter + +// ------------------------------------------------------------------------- +/* Source for KateJSIndenterProtoTable. +@begin KateJSIndenterProtoTable 1 + Dummy KateJSIndenter::Dummy DontDelete +@end +*/ + +/* Source for KateJSIndenterTable. +@begin KateJSIndenterTable 3 + onchar KateJSIndenter::OnChar DontDelete + onnewline KateJSIndenter::OnNewline DontDelete + online KateJSIndenter::OnLine DontDelete + +@end +*/ + +KateJSIndenter::KateJSIndenter (KJS::ExecState *exec) + : KJS::ObjectImp (KateJSViewProto::self(exec)) +{ +} + +DEFINE_PROTOTYPE("KateJSIndenter",KateJSIndenterProto) +IMPLEMENT_PROTOFUNC(KateJSIndenterProtoFunc) +IMPLEMENT_PROTOTYPE(KateJSIndenterProto,KateJSIndenterProtoFunc) + +const KJS::ClassInfo KateJSIndenter::info = { "KateJSIndenter", 0, &KateJSIndenterTable, 0 }; + +KJS::Value KJS::KateJSIndenterProtoFunc::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args) +{ + KJS_CHECK_THIS( KateJSIndenter, thisObj ); + + return KJS::Undefined(); +} + +//END + +//BEGIN KateIndentJScriptImpl +KateIndentJScriptImpl::KateIndentJScriptImpl(const QString& internalName, + const QString &filePath, const QString &niceName, + const QString ©right, double version): + KateIndentScriptImplAbstract(internalName,filePath,niceName,copyright,version),m_interpreter(0),m_indenter(0) +{ +} + + +KateIndentJScriptImpl::~KateIndentJScriptImpl() +{ + deleteInterpreter(); +} + +void KateIndentJScriptImpl::decRef() +{ + KateIndentScriptImplAbstract::decRef(); + if (refCount()==0) + { + deleteInterpreter(); + } +} + +void KateIndentJScriptImpl::deleteInterpreter() +{ + m_docWrapper=0; + m_viewWrapper=0; + delete m_indenter; + m_indenter=0; + delete m_interpreter; + m_interpreter=0; +} + +bool KateIndentJScriptImpl::setupInterpreter(QString &errorMsg) +{ + if (!m_interpreter) + { + kdDebug(13050)<<"Setting up interpreter"<<endl; + m_interpreter=new KJS::Interpreter(KJS::Object(new KateJSGlobal())); + m_docWrapper=new KateJSDocument(m_interpreter->globalExec(),0); + m_viewWrapper=new KateJSView(m_interpreter->globalExec(),0); + m_indenter=new KJS::Object(new KateJSIndenter(m_interpreter->globalExec())); + m_interpreter->globalObject().put(m_interpreter->globalExec(),"document",KJS::Object(m_docWrapper),KJS::DontDelete | KJS::ReadOnly); + m_interpreter->globalObject().put(m_interpreter->globalExec(),"view",KJS::Object(m_viewWrapper),KJS::DontDelete | KJS::ReadOnly); + m_interpreter->globalObject().put(m_interpreter->globalExec(),"debug", KJS::Object(new + KateJSGlobalFunctions(KateJSGlobalFunctions::Debug,1))); + m_interpreter->globalObject().put(m_interpreter->globalExec(),"indenter",*m_indenter,KJS::DontDelete | KJS::ReadOnly); + QFile file (filePath()); + + if ( !file.open( IO_ReadOnly ) ) + { + errorMsg = i18n("JavaScript file not found"); + deleteInterpreter(); + return false; + } + + QTextStream stream( &file ); + stream.setEncoding (QTextStream::UnicodeUTF8); + + QString source = stream.read (); + + file.close(); + + KJS::Completion comp (m_interpreter->evaluate(source)); + if (comp.complType() == KJS::Throw) + { + KJS::ExecState *exec = m_interpreter->globalExec(); + + KJS::Value exVal = comp.value(); + + char *msg = exVal.toString(exec).ascii(); + + int lineno = -1; + + if (exVal.type() == KJS::ObjectType) + { + KJS::Value lineVal = KJS::Object::dynamicCast(exVal).get(exec,"line"); + + if (lineVal.type() == KJS::NumberType) + lineno = int(lineVal.toNumber(exec)); + } + + errorMsg = i18n("Exception, line %1: %2").arg(lineno).arg(msg); + deleteInterpreter(); + return false; + } else { + return true; + } + } else return true; +} + + +inline static bool KateIndentJScriptCall(Kate::View *view, QString &errorMsg, KateJSDocument *docWrapper, KateJSView *viewWrapper, + KJS::Interpreter *interpreter, KJS::Object lookupobj,const KJS::Identifier& func,KJS::List params) +{ + // no view, no fun + if (!view) + { + errorMsg = i18n("Could not access view"); + return false; + } + + KateView *v=(KateView*)view; + + KJS::Object o=lookupobj.get(interpreter->globalExec(),func).toObject(interpreter->globalExec()); + if (interpreter->globalExec()->hadException()) + { + errorMsg=interpreter->globalExec()->exception().toString(interpreter->globalExec()).qstring(); + kdDebug(13050)<<"Exception(1):"<<errorMsg<<endl; + interpreter->globalExec()->clearException(); + return false; + } + + // init doc & view with new pointers! + docWrapper->doc = v->doc(); + viewWrapper->view = v; + + /*kdDebug(13050)<<"Call Object:"<<o.toString(interpreter->globalExec()).ascii()<<endl;*/ + o.call(interpreter->globalExec(),interpreter->globalObject(),params); + if (interpreter->globalExec()->hadException()) + { + errorMsg=interpreter->globalExec()->exception().toString(interpreter->globalExec()).ascii(); + kdDebug(13050)<<"Exception(2):"<<errorMsg<<endl; + interpreter->globalExec()->clearException(); + return false; + } + return true; +} + +bool KateIndentJScriptImpl::processChar(Kate::View *view, QChar c, QString &errorMsg ) +{ + + kdDebug(13050)<<"KateIndentJScriptImpl::processChar"<<endl; + if (!setupInterpreter(errorMsg)) return false; + KJS::List params; + params.append(KJS::String(QString(c))); + return KateIndentJScriptCall(view,errorMsg,m_docWrapper,m_viewWrapper,m_interpreter,*m_indenter,KJS::Identifier("onchar"),params); +} + +bool KateIndentJScriptImpl::processLine(Kate::View *view, const KateDocCursor &line, QString &errorMsg ) +{ + kdDebug(13050)<<"KateIndentJScriptImpl::processLine"<<endl; + if (!setupInterpreter(errorMsg)) return false; + return KateIndentJScriptCall(view,errorMsg,m_docWrapper,m_viewWrapper,m_interpreter,*m_indenter,KJS::Identifier("online"),KJS::List()); +} + +bool KateIndentJScriptImpl::processNewline( class Kate::View *view, const KateDocCursor &begin, bool needcontinue, QString &errorMsg ) +{ + kdDebug(13050)<<"KateIndentJScriptImpl::processNewline"<<endl; + if (!setupInterpreter(errorMsg)) return false; + return KateIndentJScriptCall(view,errorMsg,m_docWrapper,m_viewWrapper,m_interpreter,*m_indenter,KJS::Identifier("onnewline"),KJS::List()); +} +//END + +//BEGIN KateIndentJScriptManager +KateIndentJScriptManager::KateIndentJScriptManager():KateIndentScriptManagerAbstract() +{ + m_scripts.setAutoDelete (true); + collectScripts (); +} + +KateIndentJScriptManager::~KateIndentJScriptManager () +{ +} + +void KateIndentJScriptManager::collectScripts (bool force) +{ +// If there's something in myModeList the Mode List was already built so, don't do it again + if (!m_scripts.isEmpty()) + return; + + + // We'll store the scripts list in this config + KConfig config("katepartindentjscriptrc", false, false); +#if 0 + // figure out if the kate install is too new + config.setGroup ("General"); + if (config.readNumEntry ("Version") > config.readNumEntry ("CachedVersion")) + { + config.writeEntry ("CachedVersion", config.readNumEntry ("Version")); + force = true; + } +#endif + + // Let's get a list of all the .js files + QStringList list = KGlobal::dirs()->findAllResources("data","katepart/scripts/indent/*.js",false,true); + + // Let's iterate through the list and build the Mode List + for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) + { + // Each file has a group ed: + QString Group="Cache "+ *it; + + // Let's go to this group + config.setGroup(Group); + + // stat the file + struct stat sbuf; + memset (&sbuf, 0, sizeof(sbuf)); + stat(QFile::encodeName(*it), &sbuf); + + // If the group exist and we're not forced to read the .js file, let's build myModeList for katepartjscriptrc + bool readnew=false; + if (!force && config.hasGroup(Group) && (sbuf.st_mtime == config.readNumEntry("lastModified"))) + { + config.setGroup(Group); + QString filePath=*it; + QString internalName=config.readEntry("internlName","KATE-ERROR"); + if (internalName=="KATE-ERROR") readnew=true; + else + { + QString niceName=config.readEntry("niceName",internalName); + QString copyright=config.readEntry("copyright",i18n("(Unknown)")); + double version=config.readDoubleNumEntry("version",0.0); + KateIndentJScriptImpl *s=new KateIndentJScriptImpl( + internalName,filePath,niceName,copyright,version); + m_scripts.insert (internalName, s); + } + } + else readnew=true; + if (readnew) + { + QFileInfo fi (*it); + + if (m_scripts[fi.baseName()]) + continue; + + QString internalName=fi.baseName(); + QString filePath=*it; + QString niceName=internalName; + QString copyright=i18n("(Unknown)"); + double version=0.0; + parseScriptHeader(filePath,&niceName,©right,&version); + /*save the information for retrieval*/ + config.setGroup(Group); + config.writeEntry("lastModified",sbuf.st_mtime); + config.writeEntry("internalName",internalName); + config.writeEntry("niceName",niceName); + config.writeEntry("copyright",copyright); + config.writeEntry("version",version); + KateIndentJScriptImpl *s=new KateIndentJScriptImpl( + internalName,filePath,niceName,copyright,version); + m_scripts.insert (internalName, s); + } + } + + // Syncronize with the file katepartjscriptrc + config.sync(); +} + +KateIndentScript KateIndentJScriptManager::script(const QString &scriptname) { + KateIndentJScriptImpl *s=m_scripts[scriptname]; + kdDebug(13050)<<scriptname<<"=="<<s<<endl; + return KateIndentScript(s); +} + +void KateIndentJScriptManager::parseScriptHeader(const QString &filePath, + QString *niceName,QString *copyright,double *version) +{ + QFile f(QFile::encodeName(filePath)); + if (!f.open(IO_ReadOnly) ) { + kdDebug(13050)<<"Header could not be parsed, because file could not be opened"<<endl; + return; + } + QTextStream st(&f); + st.setEncoding (QTextStream::UnicodeUTF8); + if (!st.readLine().upper().startsWith("/**KATE")) { + kdDebug(13050)<<"No header found"<<endl; + f.close(); + return; + } + // here the real parsing begins + kdDebug(13050)<<"Parsing indent script header"<<endl; + enum {NOTHING=0,COPYRIGHT=1} currentState=NOTHING; + QString line; + QString tmpblockdata=""; + QRegExp endExpr("[\\s\\t]*\\*\\*\\/[\\s\\t]*$"); + QRegExp keyValue("[\\s\\t]*\\*\\s*(.+):(.*)$"); + QRegExp blockContent("[\\s\\t]*\\*(.*)$"); + while ((line=st.readLine())!=QString::null) { + if (endExpr.exactMatch(line)) { + kdDebug(13050)<<"end of config block"<<endl; + if (currentState==NOTHING) break; + if (currentState==COPYRIGHT) { + *copyright=tmpblockdata; + break; + } + Q_ASSERT(0); + } + if (currentState==NOTHING) + { + if (keyValue.exactMatch(line)) { + QStringList sl=keyValue.capturedTexts(); + kdDebug(13050)<<"key:"<<sl[1]<<endl<<"value:"<<sl[2]<<endl; + kdDebug(13050)<<"key-length:"<<sl[1].length()<<endl<<"value-length:"<<sl[2].length()<<endl; + QString key=sl[1]; + QString value=sl[2]; + if (key=="NAME") (*niceName)=value.stripWhiteSpace(); + else if (key=="VERSION") (*version)=value.stripWhiteSpace().toDouble(0); + else if (key=="COPYRIGHT") + { + tmpblockdata=""; + if (value.stripWhiteSpace().length()>0) tmpblockdata=value; + currentState=COPYRIGHT; + } else kdDebug(13050)<<"ignoring key"<<endl; + } + } else { + if (blockContent.exactMatch(line)) + { + QString bl=blockContent.capturedTexts()[1]; + //kdDebug(13050)<<"block content line:"<<bl<<endl<<bl.length()<<" "<<bl.isEmpty()<<endl; + if (bl.isEmpty()) + { + (*copyright)=tmpblockdata; + kdDebug(13050)<<"Copyright block:"<<endl<<(*copyright)<<endl; + currentState=NOTHING; + } else tmpblockdata=tmpblockdata+"\n"+bl; + } + } + } + f.close(); +} +//END +// kate: space-indent on; indent-width 2; replace-tabs on; |