TDE core libraries
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

529 lines
17KB

  1. /* This file is part of the KDE libraries
  2. Copyright (C) 2005 Joseph Wenninger <jowenn@kde.org>
  3. This library is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Library General Public
  5. License version 2 as published by the Free Software Foundation.
  6. This library is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  9. Library General Public License for more details.
  10. You should have received a copy of the GNU Library General Public License
  11. along with this library; see the file COPYING.LIB. If not, write to
  12. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  13. Boston, MA 02110-1301, USA.
  14. */
  15. #include "config.h"
  16. #ifdef HAVE_LUA
  17. #include "kateluaindentscript.h"
  18. #include "katedocument.h"
  19. #include "kateview.h"
  20. #include <sys/types.h>
  21. #include <sys/stat.h>
  22. #include <unistd.h>
  23. #include <tqfile.h>
  24. #include <tqfileinfo.h>
  25. #include <kstandarddirs.h>
  26. #include <tdeconfig.h>
  27. #include <tdeglobal.h>
  28. #include <tdelocale.h>
  29. extern "C" {
  30. #include <lua.h>
  31. #include <lualib.h>
  32. }
  33. #define ONCHAR 1
  34. #define ONNEWLINE 2
  35. #define ONCHARSTR "kateonchar"
  36. #define ONNEWLINESTR "kateonnewline"
  37. #define katelua_registerFunc(n,f,t) \
  38. (lua_pushstring(m_interpreter, n), \
  39. lua_pushcfunction(m_interpreter, f), \
  40. lua_settable(m_interpreter, t))
  41. #define katelua_registerNumConst(n,v,t) \
  42. (lua_pushstring(m_interpreter, n), \
  43. lua_pushnumber(m_interpreter, v), \
  44. lua_settable(m_interpreter, t))
  45. //BEGIN temporary, try to use registry later
  46. static KateDocument *katelua_doc;
  47. static Kate::View *katelua_view;
  48. //END
  49. //BEGIN STATIC BINDING FUNCTIONS
  50. typedef struct KATELUA_FUNCTIONS {
  51. char *name;
  52. lua_CFunction func;
  53. } KATELUA_FUNCTIONS;
  54. static int katelua_katedebug(lua_State *L) {
  55. int n=lua_gettop(L);
  56. for (int i=1;i<=n;i++) {
  57. if (lua_isnil(L,i)) kdDebug()<<"NIL VALUE"<<endl;
  58. else if (lua_isstring(L,i)) kdDebug()<<lua_tostring(L,i)<<endl;
  59. else if (lua_isboolean(L,i)) kdDebug()<<(bool)lua_toboolean(L,i)<<endl;
  60. else if (lua_isnumber(L,i)) kdDebug()<<lua_tonumber(L,i)<<endl;
  61. else kdDebug()<<"Invalid type for katedebug:"<<lua_type(L,i)<<endl;
  62. }
  63. return 0;
  64. }
  65. static int katelua_indenter_register(lua_State *L) {
  66. int n=lua_gettop(L);
  67. if (n!=2) {
  68. lua_pushstring(L,i18n("indenter.register requires 2 parameters (event id, function to call)").utf8().data());
  69. lua_error(L);
  70. }
  71. if ( (!lua_isfunction(L,2)) || (!lua_isnumber(L,1)))
  72. {
  73. /*if (lua_isnumber(L,1)) kdDebug()<<"A"<<endl;
  74. if (lua_isfunction(L,2)) kdDebug()<<"B"<<endl;
  75. kdDebug()<<lua_type(L,2)<<endl;*/
  76. lua_pushstring(L,i18n("indenter.register requires 2 parameters (event id (number), function to call (function))").utf8().data());
  77. lua_error(L);
  78. }
  79. switch ((int)lua_tonumber(L,1))
  80. {
  81. case ONCHAR:
  82. lua_pushstring(L,ONCHARSTR);
  83. lua_pushstring(L,ONCHARSTR);
  84. break;
  85. case ONNEWLINE:
  86. lua_pushstring(L,ONNEWLINESTR);
  87. lua_pushstring(L,ONNEWLINESTR);
  88. break;
  89. default:
  90. lua_pushstring(L,i18n("indenter.register:invalid event id").utf8().data());
  91. lua_error(L);
  92. }
  93. lua_gettable(L,LUA_REGISTRYINDEX);
  94. if (!lua_isnil(L,lua_gettop(L))) {
  95. lua_pushstring(L,i18n("indenter.register:there is already a function set for given").utf8().data());
  96. lua_error(L);
  97. }
  98. lua_pop(L,1);
  99. lua_pushvalue(L,2);
  100. lua_settable(L,LUA_REGISTRYINDEX);
  101. kdDebug()<<"katelua_indenter_register: Success"<<endl;
  102. return 0;
  103. }
  104. static int katelua_document_textline(lua_State *L) {
  105. if (lua_gettop(L)!=1) {
  106. lua_pushstring(L,i18n("document.textLine:One parameter (line number) required").utf8().data());
  107. lua_error(L);
  108. }
  109. if (!lua_isnumber(L,1)) {
  110. lua_pushstring(L,i18n("document.textLine:One parameter (line number) required (number)").utf8().data());
  111. lua_error(L);
  112. }
  113. lua_pushstring(L,katelua_doc->textLine((uint)lua_tonumber(L,1)).utf8().data());
  114. return 1;
  115. }
  116. static int katelua_document_removeText(lua_State *L) {
  117. if (lua_gettop(L)!=4) {
  118. lua_pushstring(L,i18n("document.removeText:Four parameters needed (start line, start col,end line, end col)").utf8().data());
  119. lua_error(L);
  120. }
  121. if ((!lua_isnumber(L,1)) || (!lua_isnumber(L,2)) ||(!lua_isnumber(L,3)) || (!lua_isnumber(L,4))) {
  122. lua_pushstring(L,i18n("document.removeText:Four parameters needed (start line, start col,end line, end col) (4x number)").utf8().data());
  123. lua_error(L);
  124. }
  125. 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)));
  126. return 1;
  127. }
  128. static int katelua_document_insertText(lua_State *L) {
  129. if (lua_gettop(L)!=3) {
  130. lua_pushstring(L,i18n("document.insertText:Three parameters needed (line,col,text)").utf8().data());
  131. lua_error(L);
  132. }
  133. if ((!lua_isnumber(L,1)) || (!lua_isnumber(L,2)) ||(!lua_isstring(L,3)) ) {
  134. lua_pushstring(L,i18n("document.removeText:Three parameters needed (line,col,text) (number,number,string)").utf8().data());
  135. lua_error(L);
  136. }
  137. lua_pushboolean(L,katelua_doc->insertText((uint)lua_tonumber(L,1),(uint)lua_tonumber(L,2),TQString::fromUtf8(lua_tostring(L,3))));
  138. return 1;
  139. }
  140. static int katelua_view_cursorline(lua_State *L) {
  141. lua_pushnumber(L,katelua_view->cursorLine());
  142. return 1;
  143. }
  144. static int katelua_view_cursorcolumn(lua_State *L) {
  145. lua_pushnumber(L,katelua_view->cursorColumn());
  146. return 1;
  147. }
  148. static int katelua_view_cursorposition(lua_State *L) {
  149. lua_pushnumber(L,katelua_view->cursorLine());
  150. lua_pushnumber(L,katelua_view->cursorColumn());
  151. return 2;
  152. }
  153. static int katelua_view_setcursorpositionreal(lua_State *L) {
  154. return 0;
  155. }
  156. static const struct KATELUA_FUNCTIONS katelua_documenttable[4]= {
  157. {"textLine",katelua_document_textline},
  158. {"removeText",katelua_document_removeText},
  159. {"insertText",katelua_document_insertText},
  160. {0,0}
  161. };
  162. static const struct KATELUA_FUNCTIONS katelua_viewtable[5]= {
  163. {"cursorLine",katelua_view_cursorline},
  164. {"cursorColumn",katelua_view_cursorcolumn},
  165. {"cursorPosition",katelua_view_cursorposition},
  166. {"setCursorPositionReal",katelua_view_setcursorpositionreal},
  167. {0,0}
  168. };
  169. static void kateregistertable(lua_State* m_interpreter,const KATELUA_FUNCTIONS funcs[],char * tablename) {
  170. lua_newtable(m_interpreter);
  171. int table=lua_gettop(m_interpreter);
  172. for (uint i=0;funcs[i].name!=0;i++)
  173. {
  174. katelua_registerFunc(funcs[i].name,funcs[i].func,table);
  175. }
  176. lua_pushstring(m_interpreter,tablename);
  177. lua_pushvalue(m_interpreter,table);
  178. lua_settable(m_interpreter,LUA_GLOBALSINDEX);
  179. lua_pop(m_interpreter,1);
  180. }
  181. //END STATIC BINDING FUNCTIONS
  182. //BEGIN KateLUAIndentScriptImpl
  183. KateLUAIndentScriptImpl::KateLUAIndentScriptImpl(const TQString& internalName,
  184. const TQString &filePath, const TQString &niceName,
  185. const TQString &copyright, double version):
  186. KateIndentScriptImplAbstract(internalName,filePath,niceName,copyright,version),m_interpreter(0)/*,m_indenter(0)*/
  187. {
  188. }
  189. KateLUAIndentScriptImpl::~KateLUAIndentScriptImpl()
  190. {
  191. deleteInterpreter();
  192. }
  193. void KateLUAIndentScriptImpl::decRef()
  194. {
  195. KateIndentScriptImplAbstract::decRef();
  196. if (refCount()==0)
  197. {
  198. deleteInterpreter();
  199. }
  200. }
  201. void KateLUAIndentScriptImpl::deleteInterpreter()
  202. {
  203. if (m_interpreter)
  204. {
  205. lua_close(m_interpreter);
  206. m_interpreter=0;
  207. }
  208. }
  209. bool KateLUAIndentScriptImpl::setupInterpreter(TQString &errorMsg)
  210. {
  211. if (m_interpreter) return true;
  212. m_interpreter=lua_open();
  213. if (!m_interpreter)
  214. {
  215. errorMsg=i18n("LUA interpreter could not be initialized");
  216. return false;
  217. }
  218. luaopen_base(m_interpreter);
  219. luaopen_string( m_interpreter );
  220. luaopen_table( m_interpreter );
  221. luaopen_math( m_interpreter );
  222. luaopen_io( m_interpreter );
  223. luaopen_debug( m_interpreter );
  224. /*indenter callback setup table*/
  225. lua_newtable(m_interpreter);
  226. int indentertable=lua_gettop(m_interpreter);
  227. katelua_registerFunc("register",katelua_indenter_register,indentertable);
  228. katelua_registerNumConst("OnChar",ONCHAR,indentertable);
  229. katelua_registerNumConst("OnNewline",ONNEWLINE,indentertable);
  230. lua_pushstring(m_interpreter,"indenter");
  231. lua_pushvalue(m_interpreter,indentertable);
  232. lua_settable(m_interpreter,LUA_GLOBALSINDEX);
  233. lua_pop(m_interpreter,1);
  234. /*debug*/
  235. katelua_registerFunc("katedebug",katelua_katedebug,LUA_GLOBALSINDEX);
  236. /*document interface*/
  237. kateregistertable(m_interpreter,katelua_documenttable,"document");
  238. /*view interface*/
  239. kateregistertable(m_interpreter,katelua_viewtable,"view");
  240. /*open script*/
  241. lua_pushstring(m_interpreter,"dofile");
  242. lua_gettable(m_interpreter,LUA_GLOBALSINDEX);
  243. TQCString fn=TQFile::encodeName(filePath());
  244. lua_pushstring(m_interpreter,fn.data());
  245. int execresult=lua_pcall(m_interpreter,1,1,0);
  246. if (execresult==0) {
  247. kdDebug()<<"Lua script has been loaded successfully. Lua interpreter version:"<<lua_version()<<endl;
  248. return true;
  249. } else {
  250. errorMsg=i18n("Lua indenting script had errors: %1").arg(lua_tostring(m_interpreter,lua_gettop(m_interpreter)));
  251. kdDebug()<<errorMsg<<endl;
  252. deleteInterpreter();
  253. return false;
  254. }
  255. }
  256. bool KateLUAIndentScriptImpl::processChar(Kate::View *view, TQChar c, TQString &errorMsg )
  257. {
  258. if (!setupInterpreter(errorMsg)) return false;
  259. katelua_doc=((KateView*)view)->doc();
  260. katelua_view=view;
  261. int oldtop=lua_gettop(m_interpreter);
  262. lua_pushstring(m_interpreter,ONCHARSTR);
  263. lua_gettable(m_interpreter,LUA_REGISTRYINDEX);
  264. bool result=true;
  265. if (!lua_isnil(m_interpreter,lua_gettop(m_interpreter)))
  266. {
  267. lua_pushstring(m_interpreter,TQString(c).utf8().data());
  268. if (lua_pcall(m_interpreter,1,0,0)!=0)
  269. {
  270. errorMsg=i18n("Lua indenting script had errors: %1").arg(lua_tostring(m_interpreter,lua_gettop(m_interpreter)));
  271. kdDebug()<<errorMsg<<endl;
  272. result=false;
  273. }
  274. }
  275. lua_settop(m_interpreter,oldtop);
  276. return result;
  277. }
  278. bool KateLUAIndentScriptImpl::processLine(Kate::View *view, const KateDocCursor &line, TQString &errorMsg )
  279. {
  280. if (!setupInterpreter(errorMsg)) return false;
  281. return true;
  282. }
  283. bool KateLUAIndentScriptImpl::processNewline( class Kate::View *view, const KateDocCursor &begin, bool needcontinue, TQString &errorMsg )
  284. {
  285. if (!setupInterpreter(errorMsg)) return false;
  286. katelua_doc=((KateView*)view)->doc();
  287. katelua_view=view;
  288. int oldtop=lua_gettop(m_interpreter);
  289. lua_pushstring(m_interpreter,ONNEWLINESTR);
  290. lua_gettable(m_interpreter,LUA_REGISTRYINDEX);
  291. bool result=true;
  292. if (!lua_isnil(m_interpreter,lua_gettop(m_interpreter)))
  293. {
  294. if (lua_pcall(m_interpreter,0,0,0)!=0)
  295. {
  296. errorMsg=i18n("Lua indenting script had errors: %1").arg(lua_tostring(m_interpreter,lua_gettop(m_interpreter)));
  297. kdDebug()<<errorMsg<<endl;
  298. result=false;
  299. }
  300. }
  301. lua_settop(m_interpreter,oldtop);
  302. return result;
  303. }
  304. //END
  305. //BEGIN KateLUAIndentScriptManager
  306. KateLUAIndentScriptManager::KateLUAIndentScriptManager():KateIndentScriptManagerAbstract()
  307. {
  308. collectScripts();
  309. }
  310. KateLUAIndentScriptManager::~KateLUAIndentScriptManager ()
  311. {
  312. }
  313. void KateLUAIndentScriptManager::collectScripts (bool force)
  314. {
  315. // If there's something in myModeList the Mode List was already built so, don't do it again
  316. if (!m_scripts.isEmpty())
  317. return;
  318. kdDebug()<<"================================================="<<endl<<"Trying to find Lua scripts"<<endl
  319. <<"================================================="<<endl;
  320. // We'll store the scripts list in this config
  321. TDEConfig config("katepartluaindentscriptrc", false, false);
  322. #if 0
  323. // figure out if the kate install is too new
  324. config.setGroup ("General");
  325. if (config.readNumEntry ("Version") > config.readNumEntry ("CachedVersion"))
  326. {
  327. config.writeEntry ("CachedVersion", config.readNumEntry ("Version"));
  328. force = true;
  329. }
  330. #endif
  331. // Let's get a list of all the .js files
  332. TQStringList list = TDEGlobal::dirs()->findAllResources("data","katepart/scripts/indent/*.lua",false,true);
  333. // Let's iterate through the list and build the Mode List
  334. for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it )
  335. {
  336. // Each file has a group ed:
  337. TQString Group="Cache "+ *it;
  338. // Let's go to this group
  339. config.setGroup(Group);
  340. // stat the file
  341. struct stat sbuf;
  342. memset (&sbuf, 0, sizeof(sbuf));
  343. stat(TQFile::encodeName(*it), &sbuf);
  344. kdDebug()<<"Lua script file:"<<(*it)<<endl;
  345. // If the group exist and we're not forced to read the .js file, let's build myModeList for katepartjscriptrc
  346. bool readnew=false;
  347. if (!force && config.hasGroup(Group) && (sbuf.st_mtime == config.readNumEntry("lastModified")))
  348. {
  349. config.setGroup(Group);
  350. TQString filePath=*it;
  351. TQString internalName=config.readEntry("internlName","KATE-ERROR");
  352. if (internalName=="KATE-ERROR") readnew=true;
  353. else
  354. {
  355. TQString niceName=config.readEntry("niceName",internalName);
  356. TQString copyright=config.readEntry("copyright",i18n("(Unknown)"));
  357. double version=config.readDoubleNumEntry("version",0.0);
  358. KateLUAIndentScriptImpl *s=new KateLUAIndentScriptImpl(
  359. internalName,filePath,niceName,copyright,version);
  360. m_scripts.insert (internalName, s);
  361. }
  362. }
  363. else readnew=true;
  364. if (readnew)
  365. {
  366. TQFileInfo fi (*it);
  367. if (m_scripts[fi.baseName()])
  368. continue;
  369. TQString internalName=fi.baseName();
  370. TQString filePath=*it;
  371. TQString niceName=internalName;
  372. TQString copyright=i18n("(Unknown)");
  373. double version=0.0;
  374. parseScriptHeader(filePath,&niceName,&copyright,&version);
  375. /*save the information for retrieval*/
  376. config.setGroup(Group);
  377. config.writeEntry("lastModified",sbuf.st_mtime);
  378. config.writeEntry("internalName",internalName);
  379. config.writeEntry("niceName",niceName);
  380. config.writeEntry("copyright",copyright);
  381. config.writeEntry("version",version);
  382. KateLUAIndentScriptImpl *s=new KateLUAIndentScriptImpl(
  383. internalName,filePath,niceName,copyright,version);
  384. m_scripts.insert (internalName, s);
  385. }
  386. }
  387. // Syncronize with the file katepartjscriptrc
  388. config.sync();
  389. }
  390. KateIndentScript KateLUAIndentScriptManager::script(const TQString &scriptname) {
  391. KateLUAIndentScriptImpl *s=m_scripts[scriptname];
  392. kdDebug(13050)<<scriptname<<"=="<<s<<endl;
  393. return KateIndentScript(s);
  394. }
  395. void KateLUAIndentScriptManager::parseScriptHeader(const TQString &filePath,
  396. TQString *niceName,TQString *copyright,double *version)
  397. {
  398. #if 0
  399. TQFile f(TQFile::encodeName(filePath));
  400. if (!f.open(IO_ReadOnly) ) {
  401. kdDebug(13050)<<"Header could not be parsed, because file could not be opened"<<endl;
  402. return;
  403. }
  404. TQTextStream st(&f);
  405. st.setEncoding (TQTextStream::UnicodeUTF8);
  406. if (!st.readLine().upper().startsWith("/**KATE")) {
  407. kdDebug(13050)<<"No header found"<<endl;
  408. f.close();
  409. return;
  410. }
  411. // here the real parsing begins
  412. kdDebug(13050)<<"Parsing indent script header"<<endl;
  413. enum {NOTHING=0,COPYRIGHT=1} currentState=NOTHING;
  414. TQString line;
  415. TQString tmpblockdata="";
  416. TQRegExp endExpr("[\\s\\t]*\\*\\*\\/[\\s\\t]*$");
  417. TQRegExp keyValue("[\\s\\t]*\\*\\s*(.+):(.*)$");
  418. TQRegExp blockContent("[\\s\\t]*\\*(.*)$");
  419. while ((line=st.readLine())!=TQString::null) {
  420. if (endExpr.exactMatch(line)) {
  421. kdDebug(13050)<<"end of config block"<<endl;
  422. if (currentState==NOTHING) break;
  423. if (currentState==COPYRIGHT) {
  424. *copyright=tmpblockdata;
  425. break;
  426. }
  427. Q_ASSERT(0);
  428. }
  429. if (currentState==NOTHING)
  430. {
  431. if (keyValue.exactMatch(line)) {
  432. TQStringList sl=keyValue.capturedTexts();
  433. kdDebug(13050)<<"key:"<<sl[1]<<endl<<"value:"<<sl[2]<<endl;
  434. kdDebug(13050)<<"key-length:"<<sl[1].length()<<endl<<"value-length:"<<sl[2].length()<<endl;
  435. TQString key=sl[1];
  436. TQString value=sl[2];
  437. if (key=="NAME") (*niceName)=value.stripWhiteSpace();
  438. else if (key=="VERSION") (*version)=value.stripWhiteSpace().toDouble(0);
  439. else if (key=="COPYRIGHT")
  440. {
  441. tmpblockdata="";
  442. if (value.stripWhiteSpace().length()>0) tmpblockdata=value;
  443. currentState=COPYRIGHT;
  444. } else kdDebug(13050)<<"ignoring key"<<endl;
  445. }
  446. } else {
  447. if (blockContent.exactMatch(line))
  448. {
  449. TQString bl=blockContent.capturedTexts()[1];
  450. //kdDebug(13050)<<"block content line:"<<bl<<endl<<bl.length()<<" "<<bl.isEmpty()<<endl;
  451. if (bl.isEmpty())
  452. {
  453. (*copyright)=tmpblockdata;
  454. kdDebug(13050)<<"Copyright block:"<<endl<<(*copyright)<<endl;
  455. currentState=NOTHING;
  456. } else tmpblockdata=tmpblockdata+"\n"+bl;
  457. }
  458. }
  459. }
  460. f.close();
  461. #endif
  462. }
  463. //END
  464. #endif
  465. // kate: space-indent on; indent-width 2; replace-tabs on;