3
* glspi_run.c - This file is part of the Lua scripting plugin for the Geany IDE
4
* See the file "geanylua.c" for copyright information.
7
#define NEED_FAIL_ARG_TYPE
11
typedef gint (*KeyfileAssignFunc) (lua_State *L, GKeyFile*kf);
15
typedef void (*GsDlgRunHook) (gboolean running, gpointer user_data);
17
/* custom dialogs module */
18
extern void glspi_init_gsdlg_module(lua_State *L, GsDlgRunHook hook, GtkWindow *toplevel);
19
/* editor functions */
20
extern void glspi_init_sci_funcs(lua_State *L);
21
/* document functions */
22
extern void glspi_init_doc_funcs(lua_State *L);
23
/* basic dialog box functions */
24
extern void glspi_init_dlg_funcs(lua_State *L, GsDlgRunHook hook);
25
/* application functions */
26
extern void glspi_init_app_funcs(lua_State *L, gchar*script_dir);
28
void glspi_init_mnu_funcs(lua_State *L);
30
static KeyfileAssignFunc glspi_kfile_assign=NULL;
32
void glspi_init_kfile_module(lua_State *L, KeyfileAssignFunc *func);
37
If a script gets caught in a tight loop and the timeout expires,
38
and the user confirms they want to keep waiting, for some reason
39
the normal methods for repainting the window don't work in the
40
editor window, which makes it appear as if the dialog is still
41
active. So we need to tell scintilla to paint over the spot
44
static void repaint_scintilla(void)
46
GeanyDocument* doc=document_get_current();
47
if ( doc && doc->is_valid ) {
48
gdk_window_invalidate_rect(GTK_WIDGET(doc->editor->sci)->window, NULL, TRUE);
49
gdk_window_process_updates(GTK_WIDGET(doc->editor->sci)->window, TRUE);
55
/* Internal yes-or-no question box (not used by scripts) */
56
static gboolean glspi_show_question(gchar*title, gchar*question, gboolean default_result)
58
GtkWidget *dialog, *yes_btn, *no_btn;
59
GtkResponseType dv, rv;
60
dv=default_result?GTK_RESPONSE_YES:GTK_RESPONSE_NO;
61
dialog=gtk_message_dialog_new(GTK_WINDOW(main_widgets->window),
62
GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_MODAL,
63
GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", title);
64
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", question);
65
yes_btn=gtk_dialog_add_button(GTK_DIALOG(dialog),
66
GTK_STOCK_YES, GTK_RESPONSE_YES);
67
no_btn=gtk_dialog_add_button(GTK_DIALOG(dialog),
68
GTK_STOCK_NO, GTK_RESPONSE_NO);
69
gtk_widget_grab_default(dv==GTK_RESPONSE_YES?yes_btn:no_btn);
70
gtk_window_set_title(GTK_WINDOW(dialog), DEFAULT_BANNER);
71
rv=gtk_dialog_run(GTK_DIALOG(dialog));
72
gtk_widget_destroy(dialog);
73
if ((rv!=GTK_RESPONSE_YES)&&(rv!=GTK_RESPONSE_NO)) {rv=dv;}
75
return GTK_RESPONSE_YES==rv;
79
static gboolean glspi_goto_error(gchar *fn, gint line)
81
GeanyDocument *doc=document_open_file(fn, FALSE, NULL, NULL);
84
ScintillaObject*sci=doc->editor->sci;
86
gint pos=sci_get_position_from_line(sci,line-1);
87
sci_set_current_position(sci,pos,TRUE);
98
Display a message box showing any script error...
99
Depending on the type of error, Lua will sometimes prepend the filename
100
to the message. If need_name is TRUE then we assume that Lua didn't add
101
the filename, so we prepend it ourself. If need_name is FALSE, then the
102
error message likely contains a filename *and* a line number, so we
103
give the user an option to automatically open the file and scroll to
106
static void glspi_script_error(gchar *script_file, const gchar *msg, gboolean need_name, gint line)
110
dialog=gtk_message_dialog_new(GTK_WINDOW(main_widgets->window),
111
GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_MODAL,
112
GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Lua script error:"));
113
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
114
"%s:\n%s", script_file, msg);
116
GtkWidget *open_btn, *cancel_btn;
117
dialog=gtk_message_dialog_new(GTK_WINDOW(main_widgets->window),
118
GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_MODAL,
119
GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, _("Lua script error:"));
120
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", msg);
121
cancel_btn=gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
122
open_btn=gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT);
123
gtk_widget_grab_default(open_btn);
125
gtk_window_set_title(GTK_WINDOW(dialog), DEFAULT_BANNER);
126
if ( (gtk_dialog_run(GTK_DIALOG(dialog))==GTK_RESPONSE_ACCEPT) && !need_name) {
127
glspi_goto_error(script_file, line);
129
gtk_widget_destroy(dialog);
136
typedef struct _StateInfo {
147
static GSList *state_list=NULL;
150
static StateInfo*find_state(lua_State *L)
153
for (p=state_list; p; p=p->next) {
154
if ( p->data && ((StateInfo*)p->data)->state==L ) { return p->data; }
160
static gchar *glspi_get_error_info(lua_State* L, gint *line)
162
StateInfo*si=find_state(L);
165
if (si->source->str && *(si->source->str) ) {
166
return g_strdup(si->source->str);
174
static gint glspi_timeout(lua_State* L)
176
if (( lua_gettop(L) > 0 ) && lua_isnumber(L,1)) {
177
gint n=lua_tonumber(L,1);
179
StateInfo*si=find_state(L);
184
} else { return FAIL_UNSIGNED_ARG(1); }
185
} else { return FAIL_NUMERIC_ARG(1); }
191
static gint glspi_yield(lua_State* L)
193
while (gtk_events_pending()) { gtk_main_iteration(); }
198
static gint glspi_optimize(lua_State* L)
200
StateInfo*si=find_state(L);
201
if (si) { si->optimized=TRUE; }
206
/* Lua debug hook callback */
207
static void debug_hook(lua_State *L, lua_Debug *ar)
209
StateInfo*si=find_state(L);
210
if (si && !si->optimized) {
211
if (lua_getinfo(L,"Sl",ar)) {
212
if (ar->source && (ar->source[0]=='@') && strcmp(si->source->str, ar->source+1)) {
213
g_string_assign(si->source, ar->source+1);
215
si->line=ar->currentline;
218
if (si->timer && si->max && (g_timer_elapsed(si->timer,NULL)>si->remaining)) {
219
if ( glspi_show_question(_("Script timeout"), _(
220
"A Lua script seems to be taking excessive time to complete.\n"
221
"Do you want to continue waiting?"
224
si->remaining=si->max;
225
g_timer_start(si->timer);
228
lua_pushstring(L, _("Script timeout exceeded."));
233
if (si->counter > 100000) {
234
gdk_window_invalidate_rect(main_widgets->window->window, NULL, TRUE);
235
gdk_window_process_updates(main_widgets->window->window, TRUE);
237
} else si->counter++;
244
Pause the run timer, while dialogs are displayed. Note that we
245
purposely add 1/10 of a second to our elapsed time here.
246
That should not even be noticeable for most scripts, but
247
it helps us time-out faster for dialogs caught in a loop.
250
static void glspi_pause_timer(gboolean pause, gpointer user_data)
252
StateInfo*si=find_state((lua_State*)user_data);
253
if (si && si->timer) {
255
si->remaining -= g_timer_elapsed(si->timer,NULL) + 0.10;
256
if ( si->remaining < 0 ) si->remaining = 0;
257
g_timer_stop(si->timer);
259
g_timer_start(si->timer);
267
static lua_State *glspi_state_new(void)
269
lua_State *L = luaL_newstate();
270
StateInfo*si=g_new0(StateInfo,1);
273
si->timer=g_timer_new();
274
si->max=DEFAULT_MAX_EXEC_TIME;
275
si->remaining=DEFAULT_MAX_EXEC_TIME;
276
si->source=g_string_new("");
279
state_list=g_slist_append(state_list,si);
280
lua_sethook(L,debug_hook,LUA_MASKLINE,1);
285
static void glspi_state_done(lua_State *L)
287
StateInfo*si=find_state(L);
290
g_timer_destroy(si->timer);
294
g_string_free(si->source, TRUE);
296
state_list=g_slist_remove(state_list,si);
304
static const struct luaL_reg glspi_timer_funcs[] = {
305
{"timeout", glspi_timeout},
306
{"yield", glspi_yield},
307
{"optimize", glspi_optimize},
315
/* Catch and report script errors */
316
static gint glspi_traceback(lua_State *L)
318
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
319
if (!lua_istable(L, -1)) {
323
lua_getfield(L, -1, "traceback");
324
if (!lua_isfunction(L, -1)) {
329
lua_pushinteger(L, 2);
336
The set_*_token functions assign default values for module-level variables
339
static void set_string_token(lua_State *L, gchar*name, gchar*value)
341
lua_getglobal(L, LUA_MODULE_NAME);
342
if (lua_istable(L, -1)) {
343
lua_pushstring(L,name);
344
lua_pushstring(L,value);
347
g_printerr("*** %s: Failed to set value for %s\n", PLUGIN_NAME, name);
353
static void set_numeric_token(lua_State *L, gchar*name, gint value)
355
lua_getglobal(L, LUA_MODULE_NAME);
356
if (lua_istable(L, -1)) {
357
lua_pushstring(L,name);
358
push_number(L,value);
361
g_printerr("*** %s: Failed to set value for %s\n", PLUGIN_NAME, name);
367
static void set_boolean_token(lua_State *L, gchar*name, gboolean value)
369
lua_getglobal(L, LUA_MODULE_NAME);
370
if (lua_istable(L, -1)) {
371
lua_pushstring(L,name);
372
lua_pushboolean(L,value);
375
g_printerr("*** %s: Failed to set value for %s\n", PLUGIN_NAME, name);
381
static void set_keyfile_token(lua_State *L, gchar*name, GKeyFile* value)
383
if (!value) {return;}
384
lua_getglobal(L, LUA_MODULE_NAME);
385
if (lua_istable(L, -1)) {
386
lua_pushstring(L,name);
387
glspi_kfile_assign(L, value);
390
g_printerr("*** %s: Failed to set value for %s\n", PLUGIN_NAME, name);
396
static void show_error(lua_State *L, gchar *script_file)
399
gchar *fn = glspi_get_error_info(L, &line);
400
if (!lua_isnil(L, -1)) {
402
msg = lua_tostring(L, -1);
404
msg = _("(error object is not a string)");
406
glspi_script_error(fn?fn:script_file, msg, FALSE, line);
409
glspi_script_error(fn?fn:script_file, _("Unknown Error inside script."), FALSE, line);
416
static gint glspi_init_module(lua_State *L, gchar *script_file, gint caller, GKeyFile*proj, gchar*script_dir)
418
luaL_openlib(L, LUA_MODULE_NAME, glspi_timer_funcs, 0);
419
glspi_init_sci_funcs(L);
420
glspi_init_doc_funcs(L);
421
glspi_init_mnu_funcs(L);
422
glspi_init_dlg_funcs(L, glspi_pause_timer);
423
glspi_init_app_funcs(L,script_dir);
424
set_string_token(L,tokenWordChars,GEANY_WORDCHARS);
425
set_string_token(L,tokenBanner,DEFAULT_BANNER);
426
set_string_token(L,tokenDirSep, G_DIR_SEPARATOR_S);
427
set_boolean_token(L,tokenRectSel,FALSE);
428
set_numeric_token(L,tokenCaller, caller);
429
glspi_init_gsdlg_module(L,glspi_pause_timer, geany_data?GTK_WINDOW(main_widgets->window):NULL);
430
glspi_init_kfile_module(L,&glspi_kfile_assign);
431
set_keyfile_token(L,tokenProject, proj);
432
set_string_token(L,tokenScript,script_file);
439
Function to load this module into the standalone lua interpreter.
440
The only reason you would ever want to do this is to re-generate
441
the "keywords.list" file from the command line.
442
See the file "util/keywords.lua" for more info.
445
gint luaopen_libgeanylua(lua_State *L)
447
return glspi_init_module(L, "", 0, NULL, NULL);
452
/* Load and run the script */
453
void glspi_run_script(gchar *script_file, gint caller, GKeyFile*proj, gchar *script_dir)
456
lua_State *L = glspi_state_new();
457
glspi_init_module(L, script_file, caller,proj,script_dir);
459
while (gtk_events_pending()) { gtk_main_iteration(); }
461
status = luaL_loadfile(L, script_file);
464
gint base = lua_gettop(L); /* function index */
465
lua_pushcfunction(L, glspi_traceback); /* push traceback function */
466
lua_insert(L, base); /* put it under chunk and args */
467
status = lua_pcall(L, 0, 0, base);
468
lua_remove(L, base); /* remove traceback function */
470
status = lua_pcall(L, 0, 0, 0);
472
lua_gc(L, LUA_GCCOLLECT, 0); /* force garbage collection if error */
473
show_error(L, script_file);
478
show_error(L, script_file);
481
glspi_script_error(script_file, _("Out of memory."), TRUE, -1);
484
glspi_script_error(script_file, _("Failed to open script file."), TRUE, -1);
487
glspi_script_error(script_file, _("Unknown error while loading script file."), TRUE, -1);