You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1170 lines
35 KiB
1170 lines
35 KiB
/* 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;
|