00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "katejscript.h"
00021
00022 #include "katedocument.h"
00023 #include "kateview.h"
00024 #include "katefactory.h"
00025 #include "kateconfig.h"
00026 #include "kateautoindent.h"
00027 #include "katehighlight.h"
00028 #include "katetextline.h"
00029
00030 #include "kateindentscriptabstracts.h"
00031
00032 #include <sys/types.h>
00033 #include <sys/stat.h>
00034 #include <unistd.h>
00035
00036 #include <kdebug.h>
00037 #include <kstandarddirs.h>
00038 #include <klocale.h>
00039 #include <kmessagebox.h>
00040 #include <kconfig.h>
00041
00042 #include <kjs/function_object.h>
00043 #include <kjs/interpreter.h>
00044 #include <kjs/lookup.h>
00045
00046 #include <qfile.h>
00047 #include <qfileinfo.h>
00048 #include <qpopupmenu.h>
00049 #include <qregexp.h>
00050 #include <qtextstream.h>
00051
00052
00053 namespace KJS {
00054
00055
00056
00057
00058
00059
00060
00061 UString::UString(const QString &d)
00062 {
00063 unsigned int len = d.length();
00064 UChar *dat = new UChar[len];
00065 memcpy(dat, d.unicode(), len * sizeof(UChar));
00066 rep = UString::Rep::create(dat, len);
00067 }
00068
00069 QString UString::qstring() const
00070 {
00071 return QString((QChar*) data(), size());
00072 }
00073
00074 QConstString UString::qconststring() const
00075 {
00076 return QConstString((QChar*) data(), size());
00077 }
00078
00079
00080 class KateJSGlobalFunctions : public ObjectImp
00081 {
00082 public:
00083 KateJSGlobalFunctions(int i, int length);
00084 virtual bool implementsCall() const { return true; }
00085 virtual Value call(ExecState *exec, Object &thisObj, const List &args);
00086
00087 enum {
00088 Debug
00089 };
00090
00091 private:
00092 int id;
00093 };
00094 KateJSGlobalFunctions::KateJSGlobalFunctions(int i, int length) : ObjectImp(), id(i)
00095 {
00096 putDirect(lengthPropertyName,length,DontDelete|ReadOnly|DontEnum);
00097 }
00098 Value KateJSGlobalFunctions::call(ExecState *exec, Object &, const List &args)
00099 {
00100 switch (id) {
00101 case Debug:
00102 qDebug("Kate (KJS Scripting): %s", args[0].toString(exec).ascii());
00103 return Undefined();
00104 default:
00105 break;
00106 }
00107
00108 return Undefined();
00109 }
00110
00111
00112 }
00113
00114
00115
00116 class KateJSGlobal : public KJS::ObjectImp {
00117 public:
00118 virtual KJS::UString className() const { return "global"; }
00119 };
00120
00121 class KateJSDocument : public KJS::ObjectImp
00122 {
00123 public:
00124 KateJSDocument (KJS::ExecState *exec, KateDocument *_doc);
00125
00126 KJS::Value get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const;
00127
00128 KJS::Value getValueProperty(KJS::ExecState *exec, int token) const;
00129
00130 void put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr = KJS::None);
00131
00132 void putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr);
00133
00134 const KJS::ClassInfo* classInfo() const { return &info; }
00135
00136 enum { FullText,
00137 Text,
00138 TextLine,
00139 Lines,
00140 Length,
00141 LineLength,
00142 SetText,
00143 Clear,
00144 InsertText,
00145 RemoveText,
00146 InsertLine,
00147 RemoveLine,
00148 EditBegin,
00149 EditEnd,
00150 IndentWidth,
00151 IndentMode,
00152 SpaceIndent,
00153 MixedIndent,
00154 HighlightMode,
00155 IsInWord,
00156 CanBreakAt,
00157 CanComment,
00158 CommentMarker,
00159 CommentStart,
00160 CommentEnd,
00161 Attribute
00162 };
00163
00164 public:
00165 KateDocument *doc;
00166
00167 static const KJS::ClassInfo info;
00168 };
00169
00170 class KateJSView : public KJS::ObjectImp
00171 {
00172 public:
00173 KateJSView (KJS::ExecState *exec, KateView *_view);
00174
00175 KJS::Value get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const;
00176
00177 KJS::Value getValueProperty(KJS::ExecState *exec, int token) const;
00178
00179 void put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr = KJS::None);
00180
00181 void putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr);
00182
00183 const KJS::ClassInfo* classInfo() const { return &info; }
00184
00185 enum { CursorLine,
00186 CursorColumn,
00187 CursorColumnReal,
00188 SetCursorPosition,
00189 SetCursorPositionReal,
00190 Selection,
00191 HasSelection,
00192 SetSelection,
00193 RemoveSelectedText,
00194 SelectAll,
00195 ClearSelection,
00196 SelStartLine,
00197 SelStartCol,
00198 SelEndLine,
00199 SelEndCol
00200 };
00201
00202 public:
00203 KateView *view;
00204
00205 static const KJS::ClassInfo info;
00206 };
00207
00208 class KateJSIndenter : public KJS::ObjectImp
00209 {
00210 public:
00211 KateJSIndenter (KJS::ExecState *exec);
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221 const KJS::ClassInfo* classInfo() const { return &info; }
00222
00223 enum { OnChar,
00224 OnLine,
00225 OnNewline,
00226 Dummy
00227 };
00228
00229 public:
00230
00231 static const KJS::ClassInfo info;
00232 };
00233
00234 #include "katejscript.lut.h"
00235
00236
00237
00238 KateJScript::KateJScript ()
00239 : m_global (new KJS::Object (new KateJSGlobal ()))
00240 , m_interpreter (new KJS::Interpreter (*m_global))
00241 , m_document (new KJS::Object(wrapDocument(m_interpreter->globalExec(), 0)))
00242 , m_view (new KJS::Object (wrapView(m_interpreter->globalExec(), 0)))
00243 {
00244
00245
00246 m_interpreter->globalObject().put(m_interpreter->globalExec(), "document", *m_document);
00247 m_interpreter->globalObject().put(m_interpreter->globalExec(), "view", *m_view);
00248 m_interpreter->globalObject().put(m_interpreter->globalExec(), "debug",
00249 KJS::Object(new KateJSGlobalFunctions(KateJSGlobalFunctions::Debug,1)));
00250 }
00251
00252 KateJScript::~KateJScript ()
00253 {
00254 delete m_view;
00255 delete m_document;
00256 delete m_interpreter;
00257 delete m_global;
00258 }
00259
00260 KJS::ObjectImp *KateJScript::wrapDocument (KJS::ExecState *exec, KateDocument *doc)
00261 {
00262 return new KateJSDocument(exec, doc);
00263 }
00264
00265 KJS::ObjectImp *KateJScript::wrapView (KJS::ExecState *exec, KateView *view)
00266 {
00267 return new KateJSView(exec, view);
00268 }
00269
00270 bool KateJScript::execute (KateView *view, const QString &script, QString &errorMsg)
00271 {
00272
00273 if (!view)
00274 {
00275 errorMsg = i18n("Could not access view");
00276 return false;
00277 }
00278
00279
00280 static_cast<KateJSDocument *>( m_document->imp() )->doc = view->doc();
00281 static_cast<KateJSView *>( m_view->imp() )->view = view;
00282
00283
00284 KJS::Completion comp (m_interpreter->evaluate(script));
00285
00286 if (comp.complType() == KJS::Throw)
00287 {
00288 KJS::ExecState *exec = m_interpreter->globalExec();
00289
00290 KJS::Value exVal = comp.value();
00291
00292 char *msg = exVal.toString(exec).ascii();
00293
00294 int lineno = -1;
00295
00296 if (exVal.type() == KJS::ObjectType)
00297 {
00298 KJS::Value lineVal = KJS::Object::dynamicCast(exVal).get(exec,"line");
00299
00300 if (lineVal.type() == KJS::NumberType)
00301 lineno = int(lineVal.toNumber(exec));
00302 }
00303
00304 errorMsg = i18n("Exception, line %1: %2").arg(lineno).arg(msg);
00305 return false;
00306 }
00307
00308 return true;
00309 }
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357 DEFINE_PROTOTYPE("KateJSDocument",KateJSDocumentProto)
00358 IMPLEMENT_PROTOFUNC(KateJSDocumentProtoFunc)
00359 IMPLEMENT_PROTOTYPE(KateJSDocumentProto,KateJSDocumentProtoFunc)
00360
00361 const KJS::ClassInfo KateJSDocument::info = { "KateJSDocument", 0, 0, 0 };
00362
00363 KJS::Value KJS::KateJSDocumentProtoFunc::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args)
00364 {
00365 KJS_CHECK_THIS( KateJSDocument, thisObj );
00366
00367 KateDocument *doc = static_cast<KateJSDocument *>( thisObj.imp() )->doc;
00368
00369 if (!doc)
00370 return KJS::Undefined();
00371
00372 switch (id)
00373 {
00374 case KateJSDocument::FullText:
00375 return KJS::String (doc->text());
00376
00377 case KateJSDocument::Text:
00378 return KJS::String (doc->text(args[0].toUInt32(exec), args[1].toUInt32(exec), args[2].toUInt32(exec), args[3].toUInt32(exec)));
00379
00380 case KateJSDocument::TextLine:
00381 return KJS::String (doc->textLine (args[0].toUInt32(exec)));
00382
00383 case KateJSDocument::Lines:
00384 return KJS::Number (doc->numLines());
00385
00386 case KateJSDocument::Length:
00387 return KJS::Number (doc->length());
00388
00389 case KateJSDocument::LineLength:
00390 return KJS::Number (doc->lineLength(args[0].toUInt32(exec)));
00391
00392 case KateJSDocument::SetText:
00393 return KJS::Boolean (doc->setText(args[0].toString(exec).qstring()));
00394
00395 case KateJSDocument::Clear:
00396 return KJS::Boolean (doc->clear());
00397
00398 case KateJSDocument::InsertText:
00399 return KJS::Boolean (doc->insertText (args[0].toUInt32(exec), args[1].toUInt32(exec), args[2].toString(exec).qstring()));
00400
00401 case KateJSDocument::RemoveText:
00402 return KJS::Boolean (doc->removeText(args[0].toUInt32(exec), args[1].toUInt32(exec), args[2].toUInt32(exec), args[3].toUInt32(exec)));
00403
00404 case KateJSDocument::InsertLine:
00405 return KJS::Boolean (doc->insertLine (args[0].toUInt32(exec), args[1].toString(exec).qstring()));
00406
00407 case KateJSDocument::RemoveLine:
00408 return KJS::Boolean (doc->removeLine (args[0].toUInt32(exec)));
00409
00410 case KateJSDocument::EditBegin:
00411 doc->editBegin();
00412 return KJS::Null ();
00413
00414 case KateJSDocument::EditEnd:
00415 doc->editEnd ();
00416 return KJS::Null ();
00417
00418 case KateJSDocument::IsInWord:
00419 return KJS::Boolean( doc->highlight()->isInWord( args[0].toString(exec).qstring().at(0), args[1].toUInt32(exec) ) );
00420
00421 case KateJSDocument::CanBreakAt:
00422 return KJS::Boolean( doc->highlight()->canBreakAt( args[0].toString(exec).qstring().at(0), args[1].toUInt32(exec) ) );
00423
00424 case KateJSDocument::CanComment:
00425 return KJS::Boolean( doc->highlight()->canComment( args[0].toUInt32(exec), args[1].toUInt32(exec) ) );
00426
00427 case KateJSDocument::CommentMarker:
00428 return KJS::String( doc->highlight()->getCommentSingleLineStart( args[0].toUInt32(exec) ) );
00429
00430 case KateJSDocument::CommentStart:
00431 return KJS::String( doc->highlight()->getCommentStart( args[0].toUInt32(exec) ) );
00432
00433 case KateJSDocument::CommentEnd:
00434 return KJS::String( doc->highlight()->getCommentEnd( args[0].toUInt32(exec) ) );
00435
00436 case KateJSDocument::Attribute:
00437 return KJS::Number( doc->kateTextLine(args[0].toUInt32(exec))->attribute(args[1].toUInt32(exec)) );
00438 }
00439
00440 return KJS::Undefined();
00441 }
00442
00443 KJS::Value KateJSDocument::get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const
00444 {
00445 return KJS::lookupGetValue<KateJSDocument,KJS::ObjectImp>(exec, propertyName, &KateJSDocumentTable, this );
00446 }
00447
00448 KJS::Value KateJSDocument::getValueProperty(KJS::ExecState *exec, int token) const
00449 {
00450 if (!doc)
00451 return KJS::Undefined ();
00452
00453 switch (token) {
00454 case KateJSDocument::IndentWidth:
00455 return KJS::Number( doc->config()->indentationWidth() );
00456
00457 case KateJSDocument::IndentMode:
00458 return KJS::String( KateAutoIndent::modeName( doc->config()->indentationMode() ) );
00459
00460 case KateJSDocument::SpaceIndent:
00461 return KJS::Boolean( doc->config()->configFlags() & KateDocumentConfig::cfSpaceIndent );
00462
00463 case KateJSDocument::MixedIndent:
00464 return KJS::Boolean( doc->config()->configFlags() & KateDocumentConfig::cfMixedIndent );
00465
00466 case KateJSDocument::HighlightMode:
00467 return KJS::String( doc->hlModeName( doc->hlMode() ) );
00468 }
00469
00470 return KJS::Undefined ();
00471 }
00472
00473 void KateJSDocument::put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr)
00474 {
00475 KJS::lookupPut<KateJSDocument,KJS::ObjectImp>(exec, propertyName, value, attr, &KateJSDocumentTable, this );
00476 }
00477
00478 void KateJSDocument::putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr)
00479 {
00480 if (!doc)
00481 return;
00482 }
00483
00484 KateJSDocument::KateJSDocument (KJS::ExecState *exec, KateDocument *_doc)
00485 : KJS::ObjectImp (KateJSDocumentProto::self(exec))
00486 , doc (_doc)
00487 {
00488 }
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520 DEFINE_PROTOTYPE("KateJSView",KateJSViewProto)
00521 IMPLEMENT_PROTOFUNC(KateJSViewProtoFunc)
00522 IMPLEMENT_PROTOTYPE(KateJSViewProto,KateJSViewProtoFunc)
00523
00524 const KJS::ClassInfo KateJSView::info = { "KateJSView", 0, &KateJSViewTable, 0 };
00525
00526 KJS::Value KJS::KateJSViewProtoFunc::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args)
00527 {
00528 KJS_CHECK_THIS( KateJSView, thisObj );
00529
00530 KateView *view = static_cast<KateJSView *>( thisObj.imp() )->view;
00531
00532 if (!view)
00533 return KJS::Undefined();
00534
00535 switch (id)
00536 {
00537 case KateJSView::CursorLine:
00538 return KJS::Number (view->cursorLine());
00539
00540 case KateJSView::CursorColumn:
00541 return KJS::Number (view->cursorColumn());
00542
00543 case KateJSView::CursorColumnReal:
00544 return KJS::Number (view->cursorColumnReal());
00545
00546 case KateJSView::SetCursorPosition:
00547 return KJS::Boolean( view->setCursorPosition( args[0].toUInt32(exec), args[1].toUInt32(exec) ) );
00548
00549 case KateJSView::SetCursorPositionReal:
00550 return KJS::Boolean( view->setCursorPositionReal( args[0].toUInt32(exec), args[1].toUInt32(exec) ) );
00551
00552
00553 case KateJSView::Selection:
00554 return KJS::String( view->selection() );
00555
00556 case KateJSView::HasSelection:
00557 return KJS::Boolean( view->hasSelection() );
00558
00559 case KateJSView::SetSelection:
00560 return KJS::Boolean( view->setSelection(args[0].toUInt32(exec),
00561 args[1].toUInt32(exec),
00562 args[2].toUInt32(exec),
00563 args[3].toUInt32(exec)) );
00564
00565 case KateJSView::RemoveSelectedText:
00566 return KJS::Boolean( view->removeSelectedText() );
00567
00568 case KateJSView::SelectAll:
00569 return KJS::Boolean( view->selectAll() );
00570
00571 case KateJSView::ClearSelection:
00572 return KJS::Boolean( view->clearSelection() );
00573 }
00574
00575 return KJS::Undefined();
00576 }
00577
00578 KateJSView::KateJSView (KJS::ExecState *exec, KateView *_view)
00579 : KJS::ObjectImp (KateJSViewProto::self(exec))
00580 , view (_view)
00581 {
00582 }
00583
00584 KJS::Value KateJSView::get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const
00585 {
00586 return KJS::lookupGetValue<KateJSView,KJS::ObjectImp>(exec, propertyName, &KateJSViewTable, this );
00587 }
00588
00589 KJS::Value KateJSView::getValueProperty(KJS::ExecState *exec, int token) const
00590 {
00591 if (!view)
00592 return KJS::Undefined ();
00593
00594 switch (token) {
00595 case KateJSView::SelStartLine:
00596 return KJS::Number( view->selStartLine() );
00597
00598 case KateJSView::SelStartCol:
00599 return KJS::Number( view->selStartCol() );
00600
00601 case KateJSView::SelEndLine:
00602 return KJS::Number( view->selEndLine() );
00603
00604 case KateJSView::SelEndCol:
00605 return KJS::Number( view->selEndCol() );
00606 }
00607
00608 return KJS::Undefined ();
00609 }
00610
00611 void KateJSView::put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr)
00612 {
00613 KJS::lookupPut<KateJSView,KJS::ObjectImp>(exec, propertyName, value, attr, &KateJSViewTable, this );
00614 }
00615
00616 void KateJSView::putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr)
00617 {
00618 if (!view)
00619 return;
00620
00621
00622 }
00623
00624
00625
00626
00627
00628 KateJScriptManager::KateJScriptManager ()
00629 {
00630 m_scripts.setAutoDelete (true);
00631 collectScripts ();
00632 }
00633
00634 KateJScriptManager::~KateJScriptManager ()
00635 {
00636 }
00637
00638 void KateJScriptManager::collectScripts (bool force)
00639 {
00640
00641 if (!m_scripts.isEmpty())
00642 return;
00643
00644
00645 KConfig config("katepartjscriptrc", false, false);
00646
00647
00648 config.setGroup ("General");
00649 if (config.readNumEntry ("Version") > config.readNumEntry ("CachedVersion"))
00650 {
00651 config.writeEntry ("CachedVersion", config.readNumEntry ("Version"));
00652 force = true;
00653 }
00654
00655
00656 QStringList list = KGlobal::dirs()->findAllResources("data","katepart/scripts/*.js",false,true);
00657
00658
00659 for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
00660 {
00661
00662 QString Group="Cache "+ *it;
00663
00664
00665 config.setGroup(Group);
00666
00667
00668 struct stat sbuf;
00669 memset (&sbuf, 0, sizeof(sbuf));
00670 stat(QFile::encodeName(*it), &sbuf);
00671
00672
00673 if (!force && config.hasGroup(Group) && (sbuf.st_mtime == config.readNumEntry("lastModified")))
00674 {
00675 }
00676 else
00677 {
00678 kdDebug (13050) << "add script: " << *it << endl;
00679
00680 QString desktopFile = (*it).left((*it).length()-2).append ("desktop");
00681
00682 kdDebug (13050) << "add script (desktop file): " << desktopFile << endl;
00683
00684 QFileInfo dfi (desktopFile);
00685
00686 if (dfi.exists())
00687 {
00688 KConfig df (desktopFile, true, false);
00689 df.setDesktopGroup ();
00690
00691
00692 QString cmdname = df.readEntry ("X-Kate-Command");
00693 if (cmdname.isEmpty())
00694 {
00695 QFileInfo fi (*it);
00696 cmdname = fi.baseName();
00697 }
00698
00699 if (m_scripts[cmdname])
00700 continue;
00701
00702 KateJScriptManager::Script *s = new KateJScriptManager::Script ();
00703
00704 s->name = cmdname;
00705 s->filename = *it;
00706 s->desktopFileExists = true;
00707
00708 m_scripts.insert (s->name, s);
00709 }
00710 else
00711 {
00712 kdDebug (13050) << "add script: fallback, no desktop file around!" << endl;
00713
00714 QFileInfo fi (*it);
00715
00716 if (m_scripts[fi.baseName()])
00717 continue;
00718
00719 KateJScriptManager::Script *s = new KateJScriptManager::Script ();
00720
00721 s->name = fi.baseName();
00722 s->filename = *it;
00723 s->desktopFileExists = false;
00724
00725 m_scripts.insert (s->name, s);
00726 }
00727 }
00728 }
00729
00730
00731 config.sync();
00732 }
00733
00734 bool KateJScriptManager::exec( Kate::View *view, const QString &_cmd, QString &errorMsg )
00735 {
00736
00737 KateView *v = (KateView*) view;
00738
00739 if ( !v )
00740 {
00741 errorMsg = i18n("Could not access view");
00742 return false;
00743 }
00744
00745
00746 QStringList args( QStringList::split( QRegExp("\\s+"), _cmd ) );
00747 QString cmd ( args.first() );
00748 args.remove( args.first() );
00749
00750 kdDebug(13050) << "try to exec: " << cmd << endl;
00751
00752 if (!m_scripts[cmd])
00753 {
00754 errorMsg = i18n("Command not found");
00755 return false;
00756 }
00757
00758 QFile file (m_scripts[cmd]->filename);
00759
00760 if ( !file.open( IO_ReadOnly ) )
00761 {
00762 errorMsg = i18n("JavaScript file not found");
00763 return false;
00764 }
00765
00766 QTextStream stream( &file );
00767 stream.setEncoding (QTextStream::UnicodeUTF8);
00768
00769 QString source = stream.read ();
00770
00771 file.close();
00772
00773 return KateFactory::self()->jscript()->execute(v, source, errorMsg);
00774 }
00775
00776 bool KateJScriptManager::help( Kate::View *, const QString &cmd, QString &msg )
00777 {
00778 if (!m_scripts[cmd] || !m_scripts[cmd]->desktopFileExists)
00779 return false;
00780
00781 KConfig df (m_scripts[cmd]->desktopFilename(), true, false);
00782 df.setDesktopGroup ();
00783
00784 msg = df.readEntry ("X-Kate-Help");
00785
00786 if (msg.isEmpty())
00787 return false;
00788
00789 return true;
00790 }
00791
00792 QStringList KateJScriptManager::cmds()
00793 {
00794 QStringList l;
00795
00796 QDictIterator<KateJScriptManager::Script> it( m_scripts );
00797 for( ; it.current(); ++it )
00798 l << it.current()->name;
00799
00800 return l;
00801 }
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826 KateJSIndenter::KateJSIndenter (KJS::ExecState *exec)
00827 : KJS::ObjectImp (KateJSViewProto::self(exec))
00828 {
00829 }
00830
00831 DEFINE_PROTOTYPE("KateJSIndenter",KateJSIndenterProto)
00832 IMPLEMENT_PROTOFUNC(KateJSIndenterProtoFunc)
00833 IMPLEMENT_PROTOTYPE(KateJSIndenterProto,KateJSIndenterProtoFunc)
00834
00835 const KJS::ClassInfo KateJSIndenter::info = { "KateJSIndenter", 0, &KateJSIndenterTable, 0 };
00836
00837 KJS::Value KJS::KateJSIndenterProtoFunc::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args)
00838 {
00839 KJS_CHECK_THIS( KateJSIndenter, thisObj );
00840
00841 return KJS::Undefined();
00842 }
00843
00844
00845
00846
00847 KateIndentJScriptImpl::KateIndentJScriptImpl(const QString& internalName,
00848 const QString &filePath, const QString &niceName,
00849 const QString ©right, double version):
00850 KateIndentScriptImplAbstract(internalName,filePath,niceName,copyright,version),m_interpreter(0),m_indenter(0)
00851 {
00852 }
00853
00854
00855 KateIndentJScriptImpl::~KateIndentJScriptImpl()
00856 {
00857 deleteInterpreter();
00858 }
00859
00860 void KateIndentJScriptImpl::decRef()
00861 {
00862 KateIndentScriptImplAbstract::decRef();
00863 if (refCount()==0)
00864 {
00865 deleteInterpreter();
00866 }
00867 }
00868
00869 void KateIndentJScriptImpl::deleteInterpreter()
00870 {
00871 m_docWrapper=0;
00872 m_viewWrapper=0;
00873 delete m_indenter;
00874 m_indenter=0;
00875 delete m_interpreter;
00876 m_interpreter=0;
00877 }
00878
00879 bool KateIndentJScriptImpl::setupInterpreter(QString &errorMsg)
00880 {
00881 if (!m_interpreter)
00882 {
00883 kdDebug(13050)<<"Setting up interpreter"<<endl;
00884 m_interpreter=new KJS::Interpreter(KJS::Object(new KateJSGlobal()));
00885 m_docWrapper=new KateJSDocument(m_interpreter->globalExec(),0);
00886 m_viewWrapper=new KateJSView(m_interpreter->globalExec(),0);
00887 m_indenter=new KJS::Object(new KateJSIndenter(m_interpreter->globalExec()));
00888 m_interpreter->globalObject().put(m_interpreter->globalExec(),"document",KJS::Object(m_docWrapper),KJS::DontDelete | KJS::ReadOnly);
00889 m_interpreter->globalObject().put(m_interpreter->globalExec(),"view",KJS::Object(m_viewWrapper),KJS::DontDelete | KJS::ReadOnly);
00890 m_interpreter->globalObject().put(m_interpreter->globalExec(),"debug", KJS::Object(new
00891 KateJSGlobalFunctions(KateJSGlobalFunctions::Debug,1)));
00892 m_interpreter->globalObject().put(m_interpreter->globalExec(),"indenter",*m_indenter,KJS::DontDelete | KJS::ReadOnly);
00893 QFile file (filePath());
00894
00895 if ( !file.open( IO_ReadOnly ) )
00896 {
00897 errorMsg = i18n("JavaScript file not found");
00898 deleteInterpreter();
00899 return false;
00900 }
00901
00902 QTextStream stream( &file );
00903 stream.setEncoding (QTextStream::UnicodeUTF8);
00904
00905 QString source = stream.read ();
00906
00907 file.close();
00908
00909 KJS::Completion comp (m_interpreter->evaluate(source));
00910 if (comp.complType() == KJS::Throw)
00911 {
00912 KJS::ExecState *exec = m_interpreter->globalExec();
00913
00914 KJS::Value exVal = comp.value();
00915
00916 char *msg = exVal.toString(exec).ascii();
00917
00918 int lineno = -1;
00919
00920 if (exVal.type() == KJS::ObjectType)
00921 {
00922 KJS::Value lineVal = KJS::Object::dynamicCast(exVal).get(exec,"line");
00923
00924 if (lineVal.type() == KJS::NumberType)
00925 lineno = int(lineVal.toNumber(exec));
00926 }
00927
00928 errorMsg = i18n("Exception, line %1: %2").arg(lineno).arg(msg);
00929 deleteInterpreter();
00930 return false;
00931 } else {
00932 return true;
00933 }
00934 } else return true;
00935 }
00936
00937
00938 inline static bool KateIndentJScriptCall(Kate::View *view, QString &errorMsg, KateJSDocument *docWrapper, KateJSView *viewWrapper,
00939 KJS::Interpreter *interpreter, KJS::Object lookupobj,const KJS::Identifier& func,KJS::List params)
00940 {
00941
00942 if (!view)
00943 {
00944 errorMsg = i18n("Could not access view");
00945 return false;
00946 }
00947
00948 KateView *v=(KateView*)view;
00949
00950 KJS::Object o=lookupobj.get(interpreter->globalExec(),func).toObject(interpreter->globalExec());
00951 if (interpreter->globalExec()->hadException())
00952 {
00953 errorMsg=interpreter->globalExec()->exception().toString(interpreter->globalExec()).qstring();
00954 kdDebug(13050)<<"Exception(1):"<<errorMsg<<endl;
00955 interpreter->globalExec()->clearException();
00956 return false;
00957 }
00958
00959
00960 docWrapper->doc = v->doc();
00961 viewWrapper->view = v;
00962
00963
00964 o.call(interpreter->globalExec(),interpreter->globalObject(),params);
00965 if (interpreter->globalExec()->hadException())
00966 {
00967 errorMsg=interpreter->globalExec()->exception().toString(interpreter->globalExec()).ascii();
00968 kdDebug(13050)<<"Exception(2):"<<errorMsg<<endl;
00969 interpreter->globalExec()->clearException();
00970 return false;
00971 }
00972 return true;
00973 }
00974
00975 bool KateIndentJScriptImpl::processChar(Kate::View *view, QChar c, QString &errorMsg )
00976 {
00977
00978 kdDebug(13050)<<"KateIndentJScriptImpl::processChar"<<endl;
00979 if (!setupInterpreter(errorMsg)) return false;
00980 KJS::List params;
00981 params.append(KJS::String(QString(c)));
00982 return KateIndentJScriptCall(view,errorMsg,m_docWrapper,m_viewWrapper,m_interpreter,*m_indenter,KJS::Identifier("onchar"),params);
00983 }
00984
00985 bool KateIndentJScriptImpl::processLine(Kate::View *view, const KateDocCursor &line, QString &errorMsg )
00986 {
00987 kdDebug(13050)<<"KateIndentJScriptImpl::processLine"<<endl;
00988 if (!setupInterpreter(errorMsg)) return false;
00989 return KateIndentJScriptCall(view,errorMsg,m_docWrapper,m_viewWrapper,m_interpreter,*m_indenter,KJS::Identifier("online"),KJS::List());
00990 }
00991
00992 bool KateIndentJScriptImpl::processNewline( class Kate::View *view, const KateDocCursor &begin, bool needcontinue, QString &errorMsg )
00993 {
00994 kdDebug(13050)<<"KateIndentJScriptImpl::processNewline"<<endl;
00995 if (!setupInterpreter(errorMsg)) return false;
00996 return KateIndentJScriptCall(view,errorMsg,m_docWrapper,m_viewWrapper,m_interpreter,*m_indenter,KJS::Identifier("onnewline"),KJS::List());
00997 }
00998
00999
01000
01001 KateIndentJScriptManager::KateIndentJScriptManager():KateIndentScriptManagerAbstract()
01002 {
01003 m_scripts.setAutoDelete (true);
01004 collectScripts ();
01005 }
01006
01007 KateIndentJScriptManager::~KateIndentJScriptManager ()
01008 {
01009 }
01010
01011 void KateIndentJScriptManager::collectScripts (bool force)
01012 {
01013
01014 if (!m_scripts.isEmpty())
01015 return;
01016
01017
01018
01019 KConfig config("katepartindentjscriptrc", false, false);
01020 #if 0
01021
01022 config.setGroup ("General");
01023 if (config.readNumEntry ("Version") > config.readNumEntry ("CachedVersion"))
01024 {
01025 config.writeEntry ("CachedVersion", config.readNumEntry ("Version"));
01026 force = true;
01027 }
01028 #endif
01029
01030
01031 QStringList list = KGlobal::dirs()->findAllResources("data","katepart/scripts/indent/*.js",false,true);
01032
01033
01034 for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
01035 {
01036
01037 QString Group="Cache "+ *it;
01038
01039
01040 config.setGroup(Group);
01041
01042
01043 struct stat sbuf;
01044 memset (&sbuf, 0, sizeof(sbuf));
01045 stat(QFile::encodeName(*it), &sbuf);
01046
01047
01048 bool readnew=false;
01049 if (!force && config.hasGroup(Group) && (sbuf.st_mtime == config.readNumEntry("lastModified")))
01050 {
01051 config.setGroup(Group);
01052 QString filePath=*it;
01053 QString internalName=config.readEntry("internlName","KATE-ERROR");
01054 if (internalName=="KATE-ERROR") readnew=true;
01055 else
01056 {
01057 QString niceName=config.readEntry("niceName",internalName);
01058 QString copyright=config.readEntry("copyright",i18n("(Unknown)"));
01059 double version=config.readDoubleNumEntry("version",0.0);
01060 KateIndentJScriptImpl *s=new KateIndentJScriptImpl(
01061 internalName,filePath,niceName,copyright,version);
01062 m_scripts.insert (internalName, s);
01063 }
01064 }
01065 else readnew=true;
01066 if (readnew)
01067 {
01068 QFileInfo fi (*it);
01069
01070 if (m_scripts[fi.baseName()])
01071 continue;
01072
01073 QString internalName=fi.baseName();
01074 QString filePath=*it;
01075 QString niceName=internalName;
01076 QString copyright=i18n("(Unknown)");
01077 double version=0.0;
01078 parseScriptHeader(filePath,&niceName,©right,&version);
01079
01080 config.setGroup(Group);
01081 config.writeEntry("lastModified",sbuf.st_mtime);
01082 config.writeEntry("internalName",internalName);
01083 config.writeEntry("niceName",niceName);
01084 config.writeEntry("copyright",copyright);
01085 config.writeEntry("version",version);
01086 KateIndentJScriptImpl *s=new KateIndentJScriptImpl(
01087 internalName,filePath,niceName,copyright,version);
01088 m_scripts.insert (internalName, s);
01089 }
01090 }
01091
01092
01093 config.sync();
01094 }
01095
01096 KateIndentScript KateIndentJScriptManager::script(const QString &scriptname) {
01097 KateIndentJScriptImpl *s=m_scripts[scriptname];
01098 kdDebug(13050)<<scriptname<<"=="<<s<<endl;
01099 return KateIndentScript(s);
01100 }
01101
01102 void KateIndentJScriptManager::parseScriptHeader(const QString &filePath,
01103 QString *niceName,QString *copyright,double *version)
01104 {
01105 QFile f(QFile::encodeName(filePath));
01106 if (!f.open(IO_ReadOnly) ) {
01107 kdDebug(13050)<<"Header could not be parsed, because file could not be opened"<<endl;
01108 return;
01109 }
01110 QTextStream st(&f);
01111 st.setEncoding (QTextStream::UnicodeUTF8);
01112 if (!st.readLine().upper().startsWith("/**KATE")) {
01113 kdDebug(13050)<<"No header found"<<endl;
01114 f.close();
01115 return;
01116 }
01117
01118 kdDebug(13050)<<"Parsing indent script header"<<endl;
01119 enum {NOTHING=0,COPYRIGHT=1} currentState=NOTHING;
01120 QString line;
01121 QString tmpblockdata="";
01122 QRegExp endExpr("[\\s\\t]*\\*\\*\\/[\\s\\t]*$");
01123 QRegExp keyValue("[\\s\\t]*\\*\\s*(.+):(.*)$");
01124 QRegExp blockContent("[\\s\\t]*\\*(.*)$");
01125 while ((line=st.readLine())!=QString::null) {
01126 if (endExpr.exactMatch(line)) {
01127 kdDebug(13050)<<"end of config block"<<endl;
01128 if (currentState==NOTHING) break;
01129 if (currentState==COPYRIGHT) {
01130 *copyright=tmpblockdata;
01131 break;
01132 }
01133 Q_ASSERT(0);
01134 }
01135 if (currentState==NOTHING)
01136 {
01137 if (keyValue.exactMatch(line)) {
01138 QStringList sl=keyValue.capturedTexts();
01139 kdDebug(13050)<<"key:"<<sl[1]<<endl<<"value:"<<sl[2]<<endl;
01140 kdDebug(13050)<<"key-length:"<<sl[1].length()<<endl<<"value-length:"<<sl[2].length()<<endl;
01141 QString key=sl[1];
01142 QString value=sl[2];
01143 if (key=="NAME") (*niceName)=value.stripWhiteSpace();
01144 else if (key=="VERSION") (*version)=value.stripWhiteSpace().toDouble(0);
01145 else if (key=="COPYRIGHT")
01146 {
01147 tmpblockdata="";
01148 if (value.stripWhiteSpace().length()>0) tmpblockdata=value;
01149 currentState=COPYRIGHT;
01150 } else kdDebug(13050)<<"ignoring key"<<endl;
01151 }
01152 } else {
01153 if (blockContent.exactMatch(line))
01154 {
01155 QString bl=blockContent.capturedTexts()[1];
01156
01157 if (bl.isEmpty())
01158 {
01159 (*copyright)=tmpblockdata;
01160 kdDebug(13050)<<"Copyright block:"<<endl<<(*copyright)<<endl;
01161 currentState=NOTHING;
01162 } else tmpblockdata=tmpblockdata+"\n"+bl;
01163 }
01164 }
01165 }
01166 f.close();
01167 }
01168
01169