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