00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "config.h"
00020 #ifdef HAVE_LUA
00021
00022 #include "kateluaindentscript.h"
00023 #include "katedocument.h"
00024 #include "kateview.h"
00025
00026 #include <sys/types.h>
00027 #include <sys/stat.h>
00028 #include <unistd.h>
00029
00030 #include <qfile.h>
00031 #include <qfileinfo.h>
00032 #include <kstandarddirs.h>
00033
00034 #include <kconfig.h>
00035 #include <kglobal.h>
00036 #include <klocale.h>
00037
00038 extern "C" {
00039 #include <lua.h>
00040 #include <lualib.h>
00041 #include <lauxlib.h>
00042 }
00043
00044 #define ONCHAR 1
00045 #define ONNEWLINE 2
00046 #define ONCHARSTR "kateonchar"
00047 #define ONNEWLINESTR "kateonnewline"
00048
00049 #define katelua_registerFunc(n,f,t) \
00050 (lua_pushstring(m_interpreter, n), \
00051 lua_pushcfunction(m_interpreter, f), \
00052 lua_settable(m_interpreter, t))
00053
00054 #define katelua_registerNumConst(n,v,t) \
00055 (lua_pushstring(m_interpreter, n), \
00056 lua_pushnumber(m_interpreter, v), \
00057 lua_settable(m_interpreter, t))
00058
00059
00060 static KateDocument *katelua_doc;
00061 static Kate::View *katelua_view;
00062
00063
00064
00065
00066
00067 typedef struct KATELUA_FUNCTIONS {
00068 char *name;
00069 lua_CFunction func;
00070 } KATELUA_FUNCTIONS;
00071
00072 static int katelua_katedebug(lua_State *L) {
00073 int n=lua_gettop(L);
00074 for (int i=1;i<=n;i++) {
00075 if (lua_isnil(L,i)) kdDebug()<<"NIL VALUE"<<endl;
00076 else if (lua_isstring(L,i)) kdDebug()<<lua_tostring(L,i)<<endl;
00077 else if (lua_isboolean(L,i)) kdDebug()<<(bool)lua_toboolean(L,i)<<endl;
00078 else if (lua_isnumber(L,i)) kdDebug()<<lua_tonumber(L,i)<<endl;
00079 else kdDebug()<<"Invalid type for katedebug:"<<lua_type(L,i)<<endl;
00080 }
00081 return 0;
00082 }
00083
00084 static int katelua_indenter_register(lua_State *L) {
00085 int n=lua_gettop(L);
00086 if (n!=2) {
00087 lua_pushstring(L,i18n("indenter.register requires 2 parameters (event id, function to call)").utf8().data());
00088 lua_error(L);
00089 }
00090 if ( (!lua_isfunction(L,2)) || (!lua_isnumber(L,1)))
00091 {
00092
00093
00094
00095 lua_pushstring(L,i18n("indenter.register requires 2 parameters (event id (number), function to call (function))").utf8().data());
00096 lua_error(L);
00097 }
00098 switch ((int)lua_tonumber(L,1))
00099 {
00100 case ONCHAR:
00101 lua_pushstring(L,ONCHARSTR);
00102 lua_pushstring(L,ONCHARSTR);
00103 break;
00104 case ONNEWLINE:
00105 lua_pushstring(L,ONNEWLINESTR);
00106 lua_pushstring(L,ONNEWLINESTR);
00107 break;
00108 default:
00109 lua_pushstring(L,i18n("indenter.register:invalid event id").utf8().data());
00110 lua_error(L);
00111 }
00112 lua_gettable(L,LUA_REGISTRYINDEX);
00113 if (!lua_isnil(L,lua_gettop(L))) {
00114 lua_pushstring(L,i18n("indenter.register:there is already a function set for given").utf8().data());
00115 lua_error(L);
00116 }
00117 lua_pop(L,1);
00118 lua_pushvalue(L,2);
00119 lua_settable(L,LUA_REGISTRYINDEX);
00120 kdDebug()<<"katelua_indenter_register: Success"<<endl;
00121 return 0;
00122 }
00123
00124
00125 static int katelua_document_textline(lua_State *L) {
00126 if (lua_gettop(L)!=1) {
00127 lua_pushstring(L,i18n("document.textLine:One parameter (line number) required").utf8().data());
00128 lua_error(L);
00129 }
00130 if (!lua_isnumber(L,1)) {
00131 lua_pushstring(L,i18n("document.textLine:One parameter (line number) required (number)").utf8().data());
00132 lua_error(L);
00133 }
00134 lua_pushstring(L,katelua_doc->textLine((uint)lua_tonumber(L,1)).utf8().data());
00135 return 1;
00136 }
00137
00138 static int katelua_document_removeText(lua_State *L) {
00139 if (lua_gettop(L)!=4) {
00140 lua_pushstring(L,i18n("document.removeText:Four parameters needed (start line, start col,end line, end col)").utf8().data());
00141 lua_error(L);
00142 }
00143 if ((!lua_isnumber(L,1)) || (!lua_isnumber(L,2)) ||(!lua_isnumber(L,3)) || (!lua_isnumber(L,4))) {
00144 lua_pushstring(L,i18n("document.removeText:Four parameters needed (start line, start col,end line, end col) (4x number)").utf8().data());
00145 lua_error(L);
00146 }
00147 lua_pushboolean(L,katelua_doc->removeText((uint)lua_tonumber(L,1),(uint)lua_tonumber(L,2),(uint)lua_tonumber(L,3),(uint)lua_tonumber(L,4)));
00148 return 1;
00149 }
00150
00151 static int katelua_document_insertText(lua_State *L) {
00152 if (lua_gettop(L)!=3) {
00153 lua_pushstring(L,i18n("document.insertText:Three parameters needed (line,col,text)").utf8().data());
00154 lua_error(L);
00155 }
00156 if ((!lua_isnumber(L,1)) || (!lua_isnumber(L,2)) ||(!lua_isstring(L,3)) ) {
00157 lua_pushstring(L,i18n("document.removeText:Three parameters needed (line,col,text) (number,number,string)").utf8().data());
00158 lua_error(L);
00159 }
00160 lua_pushboolean(L,katelua_doc->insertText((uint)lua_tonumber(L,1),(uint)lua_tonumber(L,2),QString::fromUtf8(lua_tostring(L,3))));
00161 return 1;
00162 }
00163
00164 static int katelua_view_cursorline(lua_State *L) {
00165 lua_pushnumber(L,katelua_view->cursorLine());
00166 return 1;
00167 }
00168 static int katelua_view_cursorcolumn(lua_State *L) {
00169 lua_pushnumber(L,katelua_view->cursorColumn());
00170 return 1;
00171 }
00172 static int katelua_view_cursorposition(lua_State *L) {
00173 lua_pushnumber(L,katelua_view->cursorLine());
00174 lua_pushnumber(L,katelua_view->cursorColumn());
00175 return 2;
00176
00177 }
00178 static int katelua_view_setcursorpositionreal(lua_State *L) {
00179 return 0;
00180 }
00181
00182 static const struct KATELUA_FUNCTIONS katelua_documenttable[4]= {
00183 {"textLine",katelua_document_textline},
00184 {"removeText",katelua_document_removeText},
00185 {"insertText",katelua_document_insertText},
00186 {0,0}
00187 };
00188
00189 static const struct KATELUA_FUNCTIONS katelua_viewtable[5]= {
00190 {"cursorLine",katelua_view_cursorline},
00191 {"cursorColumn",katelua_view_cursorcolumn},
00192 {"cursorPosition",katelua_view_cursorposition},
00193 {"setCursorPositionReal",katelua_view_setcursorpositionreal},
00194 {0,0}
00195 };
00196
00197 static void kateregistertable(lua_State* m_interpreter,const KATELUA_FUNCTIONS funcs[],char * tablename) {
00198 lua_newtable(m_interpreter);
00199 int table=lua_gettop(m_interpreter);
00200 for (uint i=0;funcs[i].name!=0;i++)
00201 {
00202 katelua_registerFunc(funcs[i].name,funcs[i].func,table);
00203 }
00204
00205 lua_pushstring(m_interpreter,tablename);
00206 lua_pushvalue(m_interpreter,table);
00207 lua_settable(m_interpreter,LUA_GLOBALSINDEX);
00208 lua_pop(m_interpreter,1);
00209
00210 }
00211
00212
00213
00214
00215
00216 KateLUAIndentScriptImpl::KateLUAIndentScriptImpl(const QString& internalName,
00217 const QString &filePath, const QString &niceName,
00218 const QString ©right, double version):
00219 KateIndentScriptImplAbstract(internalName,filePath,niceName,copyright,version),m_interpreter(0)
00220 {
00221 }
00222
00223
00224 KateLUAIndentScriptImpl::~KateLUAIndentScriptImpl()
00225 {
00226 deleteInterpreter();
00227 }
00228
00229 void KateLUAIndentScriptImpl::decRef()
00230 {
00231 KateIndentScriptImplAbstract::decRef();
00232 if (refCount()==0)
00233 {
00234 deleteInterpreter();
00235 }
00236 }
00237
00238 void KateLUAIndentScriptImpl::deleteInterpreter()
00239 {
00240 if (m_interpreter)
00241 {
00242 lua_close(m_interpreter);
00243 m_interpreter=0;
00244 }
00245 }
00246
00247 bool KateLUAIndentScriptImpl::setupInterpreter(QString &errorMsg)
00248 {
00249 if (m_interpreter) return true;
00250 m_interpreter=lua_open();
00251
00252 if (!m_interpreter)
00253 {
00254 errorMsg=i18n("LUA interpreter could not be initialized");
00255 return false;
00256 }
00257 luaopen_base(m_interpreter);
00258 luaopen_string( m_interpreter );
00259 luaopen_table( m_interpreter );
00260 luaopen_math( m_interpreter );
00261 luaopen_io( m_interpreter );
00262 luaopen_debug( m_interpreter );
00263
00264
00265
00266 lua_newtable(m_interpreter);
00267 int indentertable=lua_gettop(m_interpreter);
00268 katelua_registerFunc("register",katelua_indenter_register,indentertable);
00269 katelua_registerNumConst("OnChar",ONCHAR,indentertable);
00270 katelua_registerNumConst("OnNewline",ONNEWLINE,indentertable);
00271 lua_pushstring(m_interpreter,"indenter");
00272 lua_pushvalue(m_interpreter,indentertable);
00273 lua_settable(m_interpreter,LUA_GLOBALSINDEX);
00274 lua_pop(m_interpreter,1);
00275
00276
00277 katelua_registerFunc("katedebug",katelua_katedebug,LUA_GLOBALSINDEX);
00278
00279
00280 kateregistertable(m_interpreter,katelua_documenttable,"document");
00281
00282 kateregistertable(m_interpreter,katelua_viewtable,"view");
00283
00284
00285 lua_pushstring(m_interpreter,"dofile");
00286 lua_gettable(m_interpreter,LUA_GLOBALSINDEX);
00287 QCString fn=QFile::encodeName(filePath());
00288 lua_pushstring(m_interpreter,fn.data());
00289 int execresult=lua_pcall(m_interpreter,1,1,0);
00290 if (execresult==0) {
00291 kdDebug()<<"Lua script has been loaded successfully. Lua interpreter version:"<<lua_version()<<endl;
00292 return true;
00293 } else {
00294 errorMsg=i18n("Lua indenting script had errors: %1").arg(lua_tostring(m_interpreter,lua_gettop(m_interpreter)));
00295 kdDebug()<<errorMsg<<endl;
00296 deleteInterpreter();
00297
00298 return false;
00299 }
00300 }
00301
00302
00303 bool KateLUAIndentScriptImpl::processChar(Kate::View *view, QChar c, QString &errorMsg )
00304 {
00305 if (!setupInterpreter(errorMsg)) return false;
00306 katelua_doc=((KateView*)view)->doc();
00307 katelua_view=view;
00308 int oldtop=lua_gettop(m_interpreter);
00309 lua_pushstring(m_interpreter,ONCHARSTR);
00310 lua_gettable(m_interpreter,LUA_REGISTRYINDEX);
00311 bool result=true;
00312 if (!lua_isnil(m_interpreter,lua_gettop(m_interpreter)))
00313 {
00314 lua_pushstring(m_interpreter,QString(c).utf8().data());
00315 if (lua_pcall(m_interpreter,1,0,0)!=0)
00316 {
00317 errorMsg=i18n("Lua indenting script had errors: %1").arg(lua_tostring(m_interpreter,lua_gettop(m_interpreter)));
00318 kdDebug()<<errorMsg<<endl;
00319 result=false;
00320 }
00321 }
00322 lua_settop(m_interpreter,oldtop);
00323 return result;
00324 }
00325
00326 bool KateLUAIndentScriptImpl::processLine(Kate::View *view, const KateDocCursor &line, QString &errorMsg )
00327 {
00328 if (!setupInterpreter(errorMsg)) return false;
00329 return true;
00330 }
00331
00332 bool KateLUAIndentScriptImpl::processNewline( class Kate::View *view, const KateDocCursor &begin, bool needcontinue, QString &errorMsg )
00333 {
00334 if (!setupInterpreter(errorMsg)) return false;
00335 katelua_doc=((KateView*)view)->doc();
00336 katelua_view=view;
00337 int oldtop=lua_gettop(m_interpreter);
00338 lua_pushstring(m_interpreter,ONNEWLINESTR);
00339 lua_gettable(m_interpreter,LUA_REGISTRYINDEX);
00340 bool result=true;
00341 if (!lua_isnil(m_interpreter,lua_gettop(m_interpreter)))
00342 {
00343 if (lua_pcall(m_interpreter,0,0,0)!=0)
00344 {
00345 errorMsg=i18n("Lua indenting script had errors: %1").arg(lua_tostring(m_interpreter,lua_gettop(m_interpreter)));
00346 kdDebug()<<errorMsg<<endl;
00347 result=false;
00348 }
00349 }
00350 lua_settop(m_interpreter,oldtop);
00351 return result;
00352 }
00353
00354
00355
00356 KateLUAIndentScriptManager::KateLUAIndentScriptManager():KateIndentScriptManagerAbstract()
00357 {
00358 collectScripts();
00359 }
00360
00361 KateLUAIndentScriptManager::~KateLUAIndentScriptManager ()
00362 {
00363 }
00364
00365 void KateLUAIndentScriptManager::collectScripts (bool force)
00366 {
00367
00368 if (!m_scripts.isEmpty())
00369 return;
00370
00371 kdDebug()<<"================================================="<<endl<<"Trying to find Lua scripts"<<endl
00372 <<"================================================="<<endl;
00373
00374
00375 KConfig config("katepartluaindentscriptrc", false, false);
00376 #if 0
00377
00378 config.setGroup ("General");
00379 if (config.readNumEntry ("Version") > config.readNumEntry ("CachedVersion"))
00380 {
00381 config.writeEntry ("CachedVersion", config.readNumEntry ("Version"));
00382 force = true;
00383 }
00384 #endif
00385
00386
00387 QStringList list = KGlobal::dirs()->findAllResources("data","katepart/scripts/indent/*.lua",false,true);
00388
00389
00390 for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
00391 {
00392
00393 QString Group="Cache "+ *it;
00394
00395
00396 config.setGroup(Group);
00397
00398
00399 struct stat sbuf;
00400 memset (&sbuf, 0, sizeof(sbuf));
00401 stat(QFile::encodeName(*it), &sbuf);
00402 kdDebug()<<"Lua script file:"<<(*it)<<endl;
00403
00404 bool readnew=false;
00405 if (!force && config.hasGroup(Group) && (sbuf.st_mtime == config.readNumEntry("lastModified")))
00406 {
00407 config.setGroup(Group);
00408 QString filePath=*it;
00409 QString internalName=config.readEntry("internlName","KATE-ERROR");
00410 if (internalName=="KATE-ERROR") readnew=true;
00411 else
00412 {
00413 QString niceName=config.readEntry("niceName",internalName);
00414 QString copyright=config.readEntry("copyright",i18n("(Unknown)"));
00415 double version=config.readDoubleNumEntry("version",0.0);
00416 KateLUAIndentScriptImpl *s=new KateLUAIndentScriptImpl(
00417 internalName,filePath,niceName,copyright,version);
00418 m_scripts.insert (internalName, s);
00419 }
00420 }
00421 else readnew=true;
00422 if (readnew)
00423 {
00424 QFileInfo fi (*it);
00425
00426 if (m_scripts[fi.baseName()])
00427 continue;
00428
00429 QString internalName=fi.baseName();
00430 QString filePath=*it;
00431 QString niceName=internalName;
00432 QString copyright=i18n("(Unknown)");
00433 double version=0.0;
00434 parseScriptHeader(filePath,&niceName,©right,&version);
00435
00436 config.setGroup(Group);
00437 config.writeEntry("lastModified",sbuf.st_mtime);
00438 config.writeEntry("internalName",internalName);
00439 config.writeEntry("niceName",niceName);
00440 config.writeEntry("copyright",copyright);
00441 config.writeEntry("version",version);
00442 KateLUAIndentScriptImpl *s=new KateLUAIndentScriptImpl(
00443 internalName,filePath,niceName,copyright,version);
00444 m_scripts.insert (internalName, s);
00445 }
00446 }
00447
00448
00449 config.sync();
00450 }
00451
00452 KateIndentScript KateLUAIndentScriptManager::script(const QString &scriptname) {
00453 KateLUAIndentScriptImpl *s=m_scripts[scriptname];
00454 kdDebug(13050)<<scriptname<<"=="<<s<<endl;
00455 return KateIndentScript(s);
00456 }
00457
00458 void KateLUAIndentScriptManager::parseScriptHeader(const QString &filePath,
00459 QString *niceName,QString *copyright,double *version)
00460 {
00461 #if 0
00462 QFile f(QFile::encodeName(filePath));
00463 if (!f.open(IO_ReadOnly) ) {
00464 kdDebug(13050)<<"Header could not be parsed, because file could not be opened"<<endl;
00465 return;
00466 }
00467 QTextStream st(&f);
00468 st.setEncoding (QTextStream::UnicodeUTF8);
00469 if (!st.readLine().upper().startsWith("/**KATE")) {
00470 kdDebug(13050)<<"No header found"<<endl;
00471 f.close();
00472 return;
00473 }
00474
00475 kdDebug(13050)<<"Parsing indent script header"<<endl;
00476 enum {NOTHING=0,COPYRIGHT=1} currentState=NOTHING;
00477 QString line;
00478 QString tmpblockdata="";
00479 QRegExp endExpr("[\\s\\t]*\\*\\*\\/[\\s\\t]*$");
00480 QRegExp keyValue("[\\s\\t]*\\*\\s*(.+):(.*)$");
00481 QRegExp blockContent("[\\s\\t]*\\*(.*)$");
00482 while ((line=st.readLine())!=QString::null) {
00483 if (endExpr.exactMatch(line)) {
00484 kdDebug(13050)<<"end of config block"<<endl;
00485 if (currentState==NOTHING) break;
00486 if (currentState==COPYRIGHT) {
00487 *copyright=tmpblockdata;
00488 break;
00489 }
00490 Q_ASSERT(0);
00491 }
00492 if (currentState==NOTHING)
00493 {
00494 if (keyValue.exactMatch(line)) {
00495 QStringList sl=keyValue.capturedTexts();
00496 kdDebug(13050)<<"key:"<<sl[1]<<endl<<"value:"<<sl[2]<<endl;
00497 kdDebug(13050)<<"key-length:"<<sl[1].length()<<endl<<"value-length:"<<sl[2].length()<<endl;
00498 QString key=sl[1];
00499 QString value=sl[2];
00500 if (key=="NAME") (*niceName)=value.stripWhiteSpace();
00501 else if (key=="VERSION") (*version)=value.stripWhiteSpace().toDouble(0);
00502 else if (key=="COPYRIGHT")
00503 {
00504 tmpblockdata="";
00505 if (value.stripWhiteSpace().length()>0) tmpblockdata=value;
00506 currentState=COPYRIGHT;
00507 } else kdDebug(13050)<<"ignoring key"<<endl;
00508 }
00509 } else {
00510 if (blockContent.exactMatch(line))
00511 {
00512 QString bl=blockContent.capturedTexts()[1];
00513
00514 if (bl.isEmpty())
00515 {
00516 (*copyright)=tmpblockdata;
00517 kdDebug(13050)<<"Copyright block:"<<endl<<(*copyright)<<endl;
00518 currentState=NOTHING;
00519 } else tmpblockdata=tmpblockdata+"\n"+bl;
00520 }
00521 }
00522 }
00523 f.close();
00524 #endif
00525 }
00526
00527
00528 #endif
00529