kateluaindentscript.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2005 Joseph Wenninger <jowenn@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License version 2 as published by the Free Software Foundation. 00007 00008 This library is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 Library General Public License for more details. 00012 00013 You should have received a copy of the GNU Library General Public License 00014 along with this library; see the file COPYING.LIB. If not, write to 00015 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00016 Boston, MA 02110-1301, USA. 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 //BEGIN temporary, try to use registry later 00059 static KateDocument *katelua_doc; 00060 static Kate::View *katelua_view; 00061 //END 00062 00063 00064 00065 //BEGIN STATIC BINDING FUNCTIONS 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 /*if (lua_isnumber(L,1)) kdDebug()<<"A"<<endl; 00092 if (lua_isfunction(L,2)) kdDebug()<<"B"<<endl; 00093 kdDebug()<<lua_type(L,2)<<endl;*/ 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 //END STATIC BINDING FUNCTIONS 00212 00213 00214 //BEGIN KateLUAIndentScriptImpl 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)/*,m_indenter(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 /*indenter callback setup table*/ 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 /*debug*/ 00276 katelua_registerFunc("katedebug",katelua_katedebug,LUA_GLOBALSINDEX); 00277 00278 /*document interface*/ 00279 kateregistertable(m_interpreter,katelua_documenttable,"document"); 00280 /*view interface*/ 00281 kateregistertable(m_interpreter,katelua_viewtable,"view"); 00282 00283 /*open script*/ 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 //END 00353 00354 //BEGIN KateLUAIndentScriptManager 00355 KateLUAIndentScriptManager::KateLUAIndentScriptManager():KateIndentScriptManagerAbstract() 00356 { 00357 collectScripts(); 00358 } 00359 00360 KateLUAIndentScriptManager::~KateLUAIndentScriptManager () 00361 { 00362 } 00363 00364 void KateLUAIndentScriptManager::collectScripts (bool force) 00365 { 00366 // If there's something in myModeList the Mode List was already built so, don't do it again 00367 if (!m_scripts.isEmpty()) 00368 return; 00369 00370 kdDebug()<<"================================================="<<endl<<"Trying to find Lua scripts"<<endl 00371 <<"================================================="<<endl; 00372 00373 // We'll store the scripts list in this config 00374 KConfig config("katepartluaindentscriptrc", false, false); 00375 #if 0 00376 // figure out if the kate install is too new 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 // Let's get a list of all the .js files 00386 QStringList list = KGlobal::dirs()->findAllResources("data","katepart/scripts/indent/*.lua",false,true); 00387 00388 // Let's iterate through the list and build the Mode List 00389 for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) 00390 { 00391 // Each file has a group ed: 00392 QString Group="Cache "+ *it; 00393 00394 // Let's go to this group 00395 config.setGroup(Group); 00396 00397 // stat the file 00398 struct stat sbuf; 00399 memset (&sbuf, 0, sizeof(sbuf)); 00400 stat(QFile::encodeName(*it), &sbuf); 00401 kdDebug()<<"Lua script file:"<<(*it)<<endl; 00402 // If the group exist and we're not forced to read the .js file, let's build myModeList for katepartjscriptrc 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 /*save the information for retrieval*/ 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 // Syncronize with the file katepartjscriptrc 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 // here the real parsing begins 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 //kdDebug(13050)<<"block content line:"<<bl<<endl<<bl.length()<<" "<<bl.isEmpty()<<endl; 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 //END 00526 00527 #endif 00528 // kate: space-indent on; indent-width 2; replace-tabs on;