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 #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 //BEGIN temporary, try to use registry later
00060 static KateDocument *katelua_doc;
00061 static Kate::View *katelua_view;
00062 //END
00063 
00064 
00065 
00066 //BEGIN STATIC BINDING FUNCTIONS
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     /*if (lua_isnumber(L,1)) kdDebug()<<"A"<<endl;
00093     if (lua_isfunction(L,2)) kdDebug()<<"B"<<endl;
00094     kdDebug()<<lua_type(L,2)<<endl;*/
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 //END STATIC BINDING FUNCTIONS
00213 
00214 
00215 //BEGIN KateLUAIndentScriptImpl
00216 KateLUAIndentScriptImpl::KateLUAIndentScriptImpl(const QString& internalName,
00217         const QString  &filePath, const QString &niceName,
00218         const QString &copyright, double version):
00219           KateIndentScriptImplAbstract(internalName,filePath,niceName,copyright,version),m_interpreter(0)/*,m_indenter(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   /*indenter callback setup table*/
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   /*debug*/
00277   katelua_registerFunc("katedebug",katelua_katedebug,LUA_GLOBALSINDEX);
00278 
00279   /*document interface*/
00280   kateregistertable(m_interpreter,katelua_documenttable,"document");
00281   /*view interface*/
00282   kateregistertable(m_interpreter,katelua_viewtable,"view");
00283 
00284   /*open script*/
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 //END
00354 
00355 //BEGIN KateLUAIndentScriptManager
00356 KateLUAIndentScriptManager::KateLUAIndentScriptManager():KateIndentScriptManagerAbstract()
00357 {
00358   collectScripts();
00359 }
00360 
00361 KateLUAIndentScriptManager::~KateLUAIndentScriptManager ()
00362 {
00363 }
00364 
00365 void KateLUAIndentScriptManager::collectScripts (bool force)
00366 {
00367 // If there's something in myModeList the Mode List was already built so, don't do it again
00368   if (!m_scripts.isEmpty())
00369     return;
00370 
00371   kdDebug()<<"================================================="<<endl<<"Trying to find Lua scripts"<<endl
00372       <<"================================================="<<endl;
00373 
00374   // We'll store the scripts list in this config
00375   KConfig config("katepartluaindentscriptrc", false, false);
00376 #if 0
00377   // figure out if the kate install is too new
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   // Let's get a list of all the .js files
00387   QStringList list = KGlobal::dirs()->findAllResources("data","katepart/scripts/indent/*.lua",false,true);
00388 
00389   // Let's iterate through the list and build the Mode List
00390   for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
00391   {
00392     // Each file has a group ed:
00393     QString Group="Cache "+ *it;
00394 
00395     // Let's go to this group
00396     config.setGroup(Group);
00397 
00398     // stat the file
00399     struct stat sbuf;
00400     memset (&sbuf, 0, sizeof(sbuf));
00401     stat(QFile::encodeName(*it), &sbuf);
00402     kdDebug()<<"Lua script file:"<<(*it)<<endl;
00403     // If the group exist and we're not forced to read the .js file, let's build myModeList for katepartjscriptrc
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,&copyright,&version);
00435         /*save the information for retrieval*/
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   // Syncronize with the file katepartjscriptrc
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   // here the real parsing begins
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         //kdDebug(13050)<<"block content line:"<<bl<<endl<<bl.length()<<" "<<bl.isEmpty()<<endl;
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 //END
00527 
00528 #endif
00529 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Home | KDE Accessibility Home | Description of Access Keys