diff options
Diffstat (limited to 'kate/part/kateluaindentscript.cpp')
-rw-r--r-- | kate/part/kateluaindentscript.cpp | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/kate/part/kateluaindentscript.cpp b/kate/part/kateluaindentscript.cpp new file mode 100644 index 000000000..a8872c0e8 --- /dev/null +++ b/kate/part/kateluaindentscript.cpp @@ -0,0 +1,528 @@ +/* This file is part of the KDE libraries + Copyright (C) 2005 Joseph Wenninger <jowenn@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#ifdef HAVE_LUA + +#include "kateluaindentscript.h" +#include "katedocument.h" +#include "kateview.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <qfile.h> +#include <qfileinfo.h> +#include <kstandarddirs.h> + +#include <kconfig.h> +#include <kglobal.h> +#include <klocale.h> + +extern "C" { +#include <lua.h> +#include <lualib.h> +} + +#define ONCHAR 1 +#define ONNEWLINE 2 +#define ONCHARSTR "kateonchar" +#define ONNEWLINESTR "kateonnewline" + +#define katelua_registerFunc(n,f,t) \ + (lua_pushstring(m_interpreter, n), \ + lua_pushcfunction(m_interpreter, f), \ + lua_settable(m_interpreter, t)) + +#define katelua_registerNumConst(n,v,t) \ + (lua_pushstring(m_interpreter, n), \ + lua_pushnumber(m_interpreter, v), \ + lua_settable(m_interpreter, t)) + +//BEGIN temporary, try to use registry later +static KateDocument *katelua_doc; +static Kate::View *katelua_view; +//END + + + +//BEGIN STATIC BINDING FUNCTIONS +typedef struct KATELUA_FUNCTIONS { + char *name; + lua_CFunction func; +} KATELUA_FUNCTIONS; + +static int katelua_katedebug(lua_State *L) { + int n=lua_gettop(L); + for (int i=1;i<=n;i++) { + if (lua_isnil(L,i)) kdDebug()<<"NIL VALUE"<<endl; + else if (lua_isstring(L,i)) kdDebug()<<lua_tostring(L,i)<<endl; + else if (lua_isboolean(L,i)) kdDebug()<<(bool)lua_toboolean(L,i)<<endl; + else if (lua_isnumber(L,i)) kdDebug()<<lua_tonumber(L,i)<<endl; + else kdDebug()<<"Invalid type for katedebug:"<<lua_type(L,i)<<endl; + } + return 0; +} + +static int katelua_indenter_register(lua_State *L) { + int n=lua_gettop(L); + if (n!=2) { + lua_pushstring(L,i18n("indenter.register requires 2 parameters (event id, function to call)").utf8().data()); + lua_error(L); + } + if ( (!lua_isfunction(L,2)) || (!lua_isnumber(L,1))) + { + /*if (lua_isnumber(L,1)) kdDebug()<<"A"<<endl; + if (lua_isfunction(L,2)) kdDebug()<<"B"<<endl; + kdDebug()<<lua_type(L,2)<<endl;*/ + lua_pushstring(L,i18n("indenter.register requires 2 parameters (event id (number), function to call (function))").utf8().data()); + lua_error(L); + } + switch ((int)lua_tonumber(L,1)) + { + case ONCHAR: + lua_pushstring(L,ONCHARSTR); + lua_pushstring(L,ONCHARSTR); + break; + case ONNEWLINE: + lua_pushstring(L,ONNEWLINESTR); + lua_pushstring(L,ONNEWLINESTR); + break; + default: + lua_pushstring(L,i18n("indenter.register:invalid event id").utf8().data()); + lua_error(L); + } + lua_gettable(L,LUA_REGISTRYINDEX); + if (!lua_isnil(L,lua_gettop(L))) { + lua_pushstring(L,i18n("indenter.register:there is already a function set for given").utf8().data()); + lua_error(L); + } + lua_pop(L,1); + lua_pushvalue(L,2); + lua_settable(L,LUA_REGISTRYINDEX); + kdDebug()<<"katelua_indenter_register: Success"<<endl; + return 0; +} + + +static int katelua_document_textline(lua_State *L) { + if (lua_gettop(L)!=1) { + lua_pushstring(L,i18n("document.textLine:One parameter (line number) required").utf8().data()); + lua_error(L); + } + if (!lua_isnumber(L,1)) { + lua_pushstring(L,i18n("document.textLine:One parameter (line number) required (number)").utf8().data()); + lua_error(L); + } + lua_pushstring(L,katelua_doc->textLine((uint)lua_tonumber(L,1)).utf8().data()); + return 1; +} + +static int katelua_document_removeText(lua_State *L) { + if (lua_gettop(L)!=4) { + lua_pushstring(L,i18n("document.removeText:Four parameters needed (start line, start col,end line, end col)").utf8().data()); + lua_error(L); + } + if ((!lua_isnumber(L,1)) || (!lua_isnumber(L,2)) ||(!lua_isnumber(L,3)) || (!lua_isnumber(L,4))) { + lua_pushstring(L,i18n("document.removeText:Four parameters needed (start line, start col,end line, end col) (4x number)").utf8().data()); + lua_error(L); + } + 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))); + return 1; +} + +static int katelua_document_insertText(lua_State *L) { + if (lua_gettop(L)!=3) { + lua_pushstring(L,i18n("document.insertText:Three parameters needed (line,col,text)").utf8().data()); + lua_error(L); + } + if ((!lua_isnumber(L,1)) || (!lua_isnumber(L,2)) ||(!lua_isstring(L,3)) ) { + lua_pushstring(L,i18n("document.removeText:Three parameters needed (line,col,text) (number,number,string)").utf8().data()); + lua_error(L); + } + lua_pushboolean(L,katelua_doc->insertText((uint)lua_tonumber(L,1),(uint)lua_tonumber(L,2),QString::fromUtf8(lua_tostring(L,3)))); + return 1; +} + +static int katelua_view_cursorline(lua_State *L) { + lua_pushnumber(L,katelua_view->cursorLine()); + return 1; +} +static int katelua_view_cursorcolumn(lua_State *L) { + lua_pushnumber(L,katelua_view->cursorColumn()); + return 1; +} +static int katelua_view_cursorposition(lua_State *L) { + lua_pushnumber(L,katelua_view->cursorLine()); + lua_pushnumber(L,katelua_view->cursorColumn()); + return 2; + +} +static int katelua_view_setcursorpositionreal(lua_State *L) { + return 0; +} + +static const struct KATELUA_FUNCTIONS katelua_documenttable[4]= { +{"textLine",katelua_document_textline}, +{"removeText",katelua_document_removeText}, +{"insertText",katelua_document_insertText}, +{0,0} +}; + +static const struct KATELUA_FUNCTIONS katelua_viewtable[5]= { +{"cursorLine",katelua_view_cursorline}, +{"cursorColumn",katelua_view_cursorcolumn}, +{"cursorPosition",katelua_view_cursorposition}, +{"setCursorPositionReal",katelua_view_setcursorpositionreal}, +{0,0} +}; + +static void kateregistertable(lua_State* m_interpreter,const KATELUA_FUNCTIONS funcs[],char * tablename) { + lua_newtable(m_interpreter); + int table=lua_gettop(m_interpreter); + for (uint i=0;funcs[i].name!=0;i++) + { + katelua_registerFunc(funcs[i].name,funcs[i].func,table); + } + + lua_pushstring(m_interpreter,tablename); + lua_pushvalue(m_interpreter,table); + lua_settable(m_interpreter,LUA_GLOBALSINDEX); + lua_pop(m_interpreter,1); + +} + +//END STATIC BINDING FUNCTIONS + + +//BEGIN KateLUAIndentScriptImpl +KateLUAIndentScriptImpl::KateLUAIndentScriptImpl(const QString& internalName, + const QString &filePath, const QString &niceName, + const QString ©right, double version): + KateIndentScriptImplAbstract(internalName,filePath,niceName,copyright,version),m_interpreter(0)/*,m_indenter(0)*/ +{ +} + + +KateLUAIndentScriptImpl::~KateLUAIndentScriptImpl() +{ + deleteInterpreter(); +} + +void KateLUAIndentScriptImpl::decRef() +{ + KateIndentScriptImplAbstract::decRef(); + if (refCount()==0) + { + deleteInterpreter(); + } +} + +void KateLUAIndentScriptImpl::deleteInterpreter() +{ + if (m_interpreter) + { + lua_close(m_interpreter); + m_interpreter=0; + } +} + +bool KateLUAIndentScriptImpl::setupInterpreter(QString &errorMsg) +{ + if (m_interpreter) return true; + m_interpreter=lua_open(); + + if (!m_interpreter) + { + errorMsg=i18n("LUA interpreter could not be initialized"); + return false; + } + luaopen_base(m_interpreter); + luaopen_string( m_interpreter ); + luaopen_table( m_interpreter ); + luaopen_math( m_interpreter ); + luaopen_io( m_interpreter ); + luaopen_debug( m_interpreter ); + + + /*indenter callback setup table*/ + lua_newtable(m_interpreter); + int indentertable=lua_gettop(m_interpreter); + katelua_registerFunc("register",katelua_indenter_register,indentertable); + katelua_registerNumConst("OnChar",ONCHAR,indentertable); + katelua_registerNumConst("OnNewline",ONNEWLINE,indentertable); + lua_pushstring(m_interpreter,"indenter"); + lua_pushvalue(m_interpreter,indentertable); + lua_settable(m_interpreter,LUA_GLOBALSINDEX); + lua_pop(m_interpreter,1); + + /*debug*/ + katelua_registerFunc("katedebug",katelua_katedebug,LUA_GLOBALSINDEX); + + /*document interface*/ + kateregistertable(m_interpreter,katelua_documenttable,"document"); + /*view interface*/ + kateregistertable(m_interpreter,katelua_viewtable,"view"); + + /*open script*/ + lua_pushstring(m_interpreter,"dofile"); + lua_gettable(m_interpreter,LUA_GLOBALSINDEX); + QCString fn=QFile::encodeName(filePath()); + lua_pushstring(m_interpreter,fn.data()); + int execresult=lua_pcall(m_interpreter,1,1,0); + if (execresult==0) { + kdDebug()<<"Lua script has been loaded successfully. Lua interpreter version:"<<lua_version()<<endl; + return true; + } else { + errorMsg=i18n("Lua indenting script had errors: %1").arg(lua_tostring(m_interpreter,lua_gettop(m_interpreter))); + kdDebug()<<errorMsg<<endl; + deleteInterpreter(); + + return false; + } +} + + +bool KateLUAIndentScriptImpl::processChar(Kate::View *view, QChar c, QString &errorMsg ) +{ + if (!setupInterpreter(errorMsg)) return false; + katelua_doc=((KateView*)view)->doc(); + katelua_view=view; + int oldtop=lua_gettop(m_interpreter); + lua_pushstring(m_interpreter,ONCHARSTR); + lua_gettable(m_interpreter,LUA_REGISTRYINDEX); + bool result=true; + if (!lua_isnil(m_interpreter,lua_gettop(m_interpreter))) + { + lua_pushstring(m_interpreter,QString(c).utf8().data()); + if (lua_pcall(m_interpreter,1,0,0)!=0) + { + errorMsg=i18n("Lua indenting script had errors: %1").arg(lua_tostring(m_interpreter,lua_gettop(m_interpreter))); + kdDebug()<<errorMsg<<endl; + result=false; + } + } + lua_settop(m_interpreter,oldtop); + return result; +} + +bool KateLUAIndentScriptImpl::processLine(Kate::View *view, const KateDocCursor &line, QString &errorMsg ) +{ + if (!setupInterpreter(errorMsg)) return false; + return true; +} + +bool KateLUAIndentScriptImpl::processNewline( class Kate::View *view, const KateDocCursor &begin, bool needcontinue, QString &errorMsg ) +{ + if (!setupInterpreter(errorMsg)) return false; + katelua_doc=((KateView*)view)->doc(); + katelua_view=view; + int oldtop=lua_gettop(m_interpreter); + lua_pushstring(m_interpreter,ONNEWLINESTR); + lua_gettable(m_interpreter,LUA_REGISTRYINDEX); + bool result=true; + if (!lua_isnil(m_interpreter,lua_gettop(m_interpreter))) + { + if (lua_pcall(m_interpreter,0,0,0)!=0) + { + errorMsg=i18n("Lua indenting script had errors: %1").arg(lua_tostring(m_interpreter,lua_gettop(m_interpreter))); + kdDebug()<<errorMsg<<endl; + result=false; + } + } + lua_settop(m_interpreter,oldtop); + return result; +} +//END + +//BEGIN KateLUAIndentScriptManager +KateLUAIndentScriptManager::KateLUAIndentScriptManager():KateIndentScriptManagerAbstract() +{ + collectScripts(); +} + +KateLUAIndentScriptManager::~KateLUAIndentScriptManager () +{ +} + +void KateLUAIndentScriptManager::collectScripts (bool force) +{ +// If there's something in myModeList the Mode List was already built so, don't do it again + if (!m_scripts.isEmpty()) + return; + + kdDebug()<<"================================================="<<endl<<"Trying to find Lua scripts"<<endl + <<"================================================="<<endl; + + // We'll store the scripts list in this config + KConfig config("katepartluaindentscriptrc", false, false); +#if 0 + // figure out if the kate install is too new + config.setGroup ("General"); + if (config.readNumEntry ("Version") > config.readNumEntry ("CachedVersion")) + { + config.writeEntry ("CachedVersion", config.readNumEntry ("Version")); + force = true; + } +#endif + + // Let's get a list of all the .js files + QStringList list = KGlobal::dirs()->findAllResources("data","katepart/scripts/indent/*.lua",false,true); + + // Let's iterate through the list and build the Mode List + for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) + { + // Each file has a group ed: + QString Group="Cache "+ *it; + + // Let's go to this group + config.setGroup(Group); + + // stat the file + struct stat sbuf; + memset (&sbuf, 0, sizeof(sbuf)); + stat(QFile::encodeName(*it), &sbuf); + kdDebug()<<"Lua script file:"<<(*it)<<endl; + // If the group exist and we're not forced to read the .js file, let's build myModeList for katepartjscriptrc + bool readnew=false; + if (!force && config.hasGroup(Group) && (sbuf.st_mtime == config.readNumEntry("lastModified"))) + { + config.setGroup(Group); + QString filePath=*it; + QString internalName=config.readEntry("internlName","KATE-ERROR"); + if (internalName=="KATE-ERROR") readnew=true; + else + { + QString niceName=config.readEntry("niceName",internalName); + QString copyright=config.readEntry("copyright",i18n("(Unknown)")); + double version=config.readDoubleNumEntry("version",0.0); + KateLUAIndentScriptImpl *s=new KateLUAIndentScriptImpl( + internalName,filePath,niceName,copyright,version); + m_scripts.insert (internalName, s); + } + } + else readnew=true; + if (readnew) + { + QFileInfo fi (*it); + + if (m_scripts[fi.baseName()]) + continue; + + QString internalName=fi.baseName(); + QString filePath=*it; + QString niceName=internalName; + QString copyright=i18n("(Unknown)"); + double version=0.0; + parseScriptHeader(filePath,&niceName,©right,&version); + /*save the information for retrieval*/ + config.setGroup(Group); + config.writeEntry("lastModified",sbuf.st_mtime); + config.writeEntry("internalName",internalName); + config.writeEntry("niceName",niceName); + config.writeEntry("copyright",copyright); + config.writeEntry("version",version); + KateLUAIndentScriptImpl *s=new KateLUAIndentScriptImpl( + internalName,filePath,niceName,copyright,version); + m_scripts.insert (internalName, s); + } + } + + // Syncronize with the file katepartjscriptrc + config.sync(); +} + +KateIndentScript KateLUAIndentScriptManager::script(const QString &scriptname) { + KateLUAIndentScriptImpl *s=m_scripts[scriptname]; + kdDebug(13050)<<scriptname<<"=="<<s<<endl; + return KateIndentScript(s); +} + +void KateLUAIndentScriptManager::parseScriptHeader(const QString &filePath, + QString *niceName,QString *copyright,double *version) +{ +#if 0 + QFile f(QFile::encodeName(filePath)); + if (!f.open(IO_ReadOnly) ) { + kdDebug(13050)<<"Header could not be parsed, because file could not be opened"<<endl; + return; + } + QTextStream st(&f); + st.setEncoding (QTextStream::UnicodeUTF8); + if (!st.readLine().upper().startsWith("/**KATE")) { + kdDebug(13050)<<"No header found"<<endl; + f.close(); + return; + } + // here the real parsing begins + kdDebug(13050)<<"Parsing indent script header"<<endl; + enum {NOTHING=0,COPYRIGHT=1} currentState=NOTHING; + QString line; + QString tmpblockdata=""; + QRegExp endExpr("[\\s\\t]*\\*\\*\\/[\\s\\t]*$"); + QRegExp keyValue("[\\s\\t]*\\*\\s*(.+):(.*)$"); + QRegExp blockContent("[\\s\\t]*\\*(.*)$"); + while ((line=st.readLine())!=QString::null) { + if (endExpr.exactMatch(line)) { + kdDebug(13050)<<"end of config block"<<endl; + if (currentState==NOTHING) break; + if (currentState==COPYRIGHT) { + *copyright=tmpblockdata; + break; + } + Q_ASSERT(0); + } + if (currentState==NOTHING) + { + if (keyValue.exactMatch(line)) { + QStringList sl=keyValue.capturedTexts(); + kdDebug(13050)<<"key:"<<sl[1]<<endl<<"value:"<<sl[2]<<endl; + kdDebug(13050)<<"key-length:"<<sl[1].length()<<endl<<"value-length:"<<sl[2].length()<<endl; + QString key=sl[1]; + QString value=sl[2]; + if (key=="NAME") (*niceName)=value.stripWhiteSpace(); + else if (key=="VERSION") (*version)=value.stripWhiteSpace().toDouble(0); + else if (key=="COPYRIGHT") + { + tmpblockdata=""; + if (value.stripWhiteSpace().length()>0) tmpblockdata=value; + currentState=COPYRIGHT; + } else kdDebug(13050)<<"ignoring key"<<endl; + } + } else { + if (blockContent.exactMatch(line)) + { + QString bl=blockContent.capturedTexts()[1]; + //kdDebug(13050)<<"block content line:"<<bl<<endl<<bl.length()<<" "<<bl.isEmpty()<<endl; + if (bl.isEmpty()) + { + (*copyright)=tmpblockdata; + kdDebug(13050)<<"Copyright block:"<<endl<<(*copyright)<<endl; + currentState=NOTHING; + } else tmpblockdata=tmpblockdata+"\n"+bl; + } + } + } + f.close(); +#endif +} +//END + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; |