~ubuntu-branches/debian/squeeze/geany-plugins/squeeze

« back to all changes in this revision

Viewing changes to geanylua/glspi_run.c

  • Committer: Bazaar Package Importer
  • Author(s): Chow Loong Jin
  • Date: 2009-07-10 22:56:41 UTC
  • Revision ID: james.westby@ubuntu.com-20090710225641-xc1126t7pq0jmpos
Tags: upstream-0.17.1
ImportĀ upstreamĀ versionĀ 0.17.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
/*
 
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.
 
5
 */
 
6
 
 
7
#define NEED_FAIL_ARG_TYPE
 
8
#include "glspi.h"
 
9
 
 
10
 
 
11
typedef gint (*KeyfileAssignFunc) (lua_State *L, GKeyFile*kf);
 
12
 
 
13
 
 
14
 
 
15
typedef void (*GsDlgRunHook) (gboolean running, gpointer user_data);
 
16
 
 
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);
 
27
/* menu functions */
 
28
void glspi_init_mnu_funcs(lua_State *L);
 
29
 
 
30
static KeyfileAssignFunc glspi_kfile_assign=NULL;
 
31
 
 
32
void glspi_init_kfile_module(lua_State *L, KeyfileAssignFunc *func);
 
33
 
 
34
 
 
35
 
 
36
/*
 
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
 
42
  where the dialog was.
 
43
*/
 
44
static void repaint_scintilla(void)
 
45
{
 
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);
 
50
        }
 
51
}
 
52
 
 
53
 
 
54
 
 
55
/* Internal yes-or-no question box (not used by scripts) */
 
56
static gboolean glspi_show_question(gchar*title, gchar*question, gboolean default_result)
 
57
{
 
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;}
 
74
        repaint_scintilla();
 
75
        return GTK_RESPONSE_YES==rv;
 
76
}
 
77
 
 
78
 
 
79
static gboolean glspi_goto_error(gchar *fn, gint line)
 
80
{
 
81
        GeanyDocument *doc=document_open_file(fn, FALSE, NULL, NULL);
 
82
        if (doc) {
 
83
                if (doc->is_valid) {
 
84
                        ScintillaObject*sci=doc->editor->sci;
 
85
                        if (sci) {
 
86
                                gint pos=sci_get_position_from_line(sci,line-1);
 
87
                                sci_set_current_position(sci,pos,TRUE);
 
88
                                return TRUE;
 
89
                        }
 
90
                }
 
91
        }
 
92
        return FALSE;
 
93
}
 
94
 
 
95
 
 
96
 
 
97
/*
 
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
 
104
  the offending line.
 
105
*/
 
106
static void glspi_script_error(gchar *script_file, const gchar *msg, gboolean need_name, gint line)
 
107
{
 
108
        GtkWidget *dialog;
 
109
        if (need_name) {
 
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);
 
115
        } else {
 
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);
 
124
        }
 
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);
 
128
        }
 
129
        gtk_widget_destroy(dialog);
 
130
}
 
131
 
 
132
 
 
133
 
 
134
 
 
135
 
 
136
typedef struct _StateInfo {
 
137
        lua_State *state;
 
138
        GString *source;
 
139
        gint line;
 
140
        GTimer*timer;
 
141
        gint counter;
 
142
        gdouble remaining;
 
143
        gdouble max;
 
144
  gboolean optimized;
 
145
} StateInfo;
 
146
 
 
147
static GSList *state_list=NULL;
 
148
 
 
149
 
 
150
static StateInfo*find_state(lua_State *L)
 
151
{
 
152
        GSList*p=state_list;
 
153
        for (p=state_list; p; p=p->next) {
 
154
                if ( p->data && ((StateInfo*)p->data)->state==L ) { return p->data; }
 
155
        }
 
156
        return NULL;
 
157
}
 
158
 
 
159
 
 
160
static gchar *glspi_get_error_info(lua_State* L, gint *line)
 
161
{
 
162
        StateInfo*si=find_state(L);
 
163
        if (si) {
 
164
                *line=si->line;
 
165
                if (si->source->str && *(si->source->str) )     {
 
166
                        return g_strdup(si->source->str);
 
167
                }
 
168
        } else { *line=-1; }
 
169
        return NULL;
 
170
}
 
171
 
 
172
 
 
173
 
 
174
static gint glspi_timeout(lua_State* L)
 
175
{
 
176
        if (( lua_gettop(L) > 0 ) && lua_isnumber(L,1)) {
 
177
                gint n=lua_tonumber(L,1);
 
178
                if (n>=0) {
 
179
                        StateInfo*si=find_state(L);
 
180
                        if (si) {
 
181
                                si->max=n;
 
182
                                si->remaining=n;
 
183
                        }
 
184
                } else { return FAIL_UNSIGNED_ARG(1); }
 
185
        } else { return FAIL_NUMERIC_ARG(1); }
 
186
        return 0;
 
187
}
 
188
 
 
189
 
 
190
 
 
191
static gint glspi_yield(lua_State* L)
 
192
{
 
193
        while (gtk_events_pending()) { gtk_main_iteration(); }
 
194
        return 0;
 
195
}
 
196
 
 
197
 
 
198
static gint glspi_optimize(lua_State* L)
 
199
{
 
200
        StateInfo*si=find_state(L);
 
201
        if (si) { si->optimized=TRUE; }
 
202
        return 0;
 
203
}
 
204
 
 
205
 
 
206
/* Lua debug hook callback */
 
207
static void debug_hook(lua_State *L, lua_Debug *ar)
 
208
{
 
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);
 
214
                        }
 
215
                        si->line=ar->currentline;
 
216
                }
 
217
                if (si->timer) {
 
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?"
 
222
                                ), FALSE) )
 
223
                                {
 
224
                                        si->remaining=si->max;
 
225
                                        g_timer_start(si->timer);
 
226
                                } else
 
227
                                {
 
228
                                        lua_pushstring(L, _("Script timeout exceeded."));
 
229
                                        lua_error(L);
 
230
                                }
 
231
                        }
 
232
                }
 
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);
 
236
                        si->counter=0;
 
237
                } else si->counter++;
 
238
        }
 
239
}
 
240
 
 
241
 
 
242
 
 
243
/*
 
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.
 
248
*/
 
249
 
 
250
static void glspi_pause_timer(gboolean pause, gpointer user_data)
 
251
{
 
252
        StateInfo*si=find_state((lua_State*)user_data);
 
253
        if (si && si->timer) {
 
254
                if (pause) {
 
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);
 
258
                } else {
 
259
                        g_timer_start(si->timer);
 
260
                }
 
261
        }
 
262
}
 
263
 
 
264
 
 
265
 
 
266
 
 
267
static lua_State *glspi_state_new(void)
 
268
{
 
269
        lua_State *L = luaL_newstate();
 
270
        StateInfo*si=g_new0(StateInfo,1);
 
271
        luaL_openlibs(L);
 
272
        si->state=L;
 
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("");
 
277
        si->line=-1;
 
278
        si->counter=0;
 
279
        state_list=g_slist_append(state_list,si);
 
280
        lua_sethook(L,debug_hook,LUA_MASKLINE,1);
 
281
        return L;
 
282
}
 
283
 
 
284
 
 
285
static void glspi_state_done(lua_State *L)
 
286
{
 
287
        StateInfo*si=find_state(L);
 
288
        if (si) {
 
289
                if (si->timer) {
 
290
                        g_timer_destroy(si->timer);
 
291
                        si->timer=NULL;
 
292
                }
 
293
                if (si->source) {
 
294
                        g_string_free(si->source, TRUE);
 
295
                }
 
296
                state_list=g_slist_remove(state_list,si);
 
297
                g_free(si);
 
298
        }
 
299
        lua_close(L);
 
300
}
 
301
 
 
302
 
 
303
 
 
304
static const struct luaL_reg glspi_timer_funcs[] = {
 
305
        {"timeout",  glspi_timeout},
 
306
        {"yield",    glspi_yield},
 
307
        {"optimize", glspi_optimize},
 
308
        {NULL,NULL}
 
309
};
 
310
 
 
311
 
 
312
 
 
313
 
 
314
 
 
315
/* Catch and report script errors */
 
316
static gint glspi_traceback(lua_State *L)
 
317
{
 
318
        lua_getfield(L, LUA_GLOBALSINDEX, "debug");
 
319
        if (!lua_istable(L, -1)) {
 
320
                lua_pop(L, 1);
 
321
                return 1;
 
322
        }
 
323
        lua_getfield(L, -1, "traceback");
 
324
        if (!lua_isfunction(L, -1)) {
 
325
                lua_pop(L, 2);
 
326
                return 1;
 
327
        }
 
328
        lua_pushvalue(L, 1);
 
329
        lua_pushinteger(L, 2);
 
330
        lua_call(L, 2, 1);
 
331
 
 
332
        return 1;
 
333
}
 
334
 
 
335
/*
 
336
  The set_*_token functions assign default values for module-level variables
 
337
*/
 
338
 
 
339
static void set_string_token(lua_State *L, gchar*name, gchar*value)
 
340
{
 
341
        lua_getglobal(L, LUA_MODULE_NAME);
 
342
        if (lua_istable(L, -1)) {
 
343
                lua_pushstring(L,name);
 
344
                lua_pushstring(L,value);
 
345
                lua_settable(L, -3);
 
346
        } else {
 
347
                g_printerr("*** %s: Failed to set value for %s\n", PLUGIN_NAME, name);
 
348
        }
 
349
}
 
350
 
 
351
 
 
352
 
 
353
static void set_numeric_token(lua_State *L, gchar*name, gint value)
 
354
{
 
355
        lua_getglobal(L, LUA_MODULE_NAME);
 
356
        if (lua_istable(L, -1)) {
 
357
                lua_pushstring(L,name);
 
358
                push_number(L,value);
 
359
                lua_settable(L, -3);
 
360
        } else {
 
361
                g_printerr("*** %s: Failed to set value for %s\n", PLUGIN_NAME, name);
 
362
        }
 
363
}
 
364
 
 
365
 
 
366
 
 
367
static void set_boolean_token(lua_State *L, gchar*name, gboolean value)
 
368
{
 
369
        lua_getglobal(L, LUA_MODULE_NAME);
 
370
        if (lua_istable(L, -1)) {
 
371
                lua_pushstring(L,name);
 
372
                lua_pushboolean(L,value);
 
373
                lua_settable(L, -3);
 
374
        } else {
 
375
                g_printerr("*** %s: Failed to set value for %s\n", PLUGIN_NAME, name);
 
376
        }
 
377
}
 
378
 
 
379
 
 
380
 
 
381
static void set_keyfile_token(lua_State *L, gchar*name, GKeyFile* value)
 
382
{
 
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);
 
388
                lua_settable(L, -3);
 
389
        } else {
 
390
                g_printerr("*** %s: Failed to set value for %s\n", PLUGIN_NAME, name);
 
391
        }
 
392
}
 
393
 
 
394
 
 
395
 
 
396
static void show_error(lua_State *L, gchar *script_file)
 
397
{
 
398
        gint line=-1;
 
399
        gchar *fn = glspi_get_error_info(L, &line);
 
400
        if (!lua_isnil(L, -1)) {
 
401
                const gchar *msg;
 
402
                msg = lua_tostring(L, -1);
 
403
                if (msg == NULL) {
 
404
                        msg = _("(error object is not a string)");
 
405
                }
 
406
                glspi_script_error(fn?fn:script_file, msg, FALSE, line);
 
407
                lua_pop(L, 1);
 
408
        } else {
 
409
                glspi_script_error(fn?fn:script_file, _("Unknown Error inside script."), FALSE, line);
 
410
        }
 
411
        if (fn) g_free(fn);
 
412
}
 
413
 
 
414
 
 
415
 
 
416
static gint glspi_init_module(lua_State *L, gchar *script_file, gint caller, GKeyFile*proj, gchar*script_dir)
 
417
{
 
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);
 
433
        return 0;
 
434
}
 
435
 
 
436
 
 
437
 
 
438
/*
 
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.
 
443
*/
 
444
PLUGIN_EXPORT
 
445
gint luaopen_libgeanylua(lua_State *L)
 
446
{
 
447
        return glspi_init_module(L, "", 0, NULL, NULL);
 
448
}
 
449
 
 
450
 
 
451
 
 
452
/* Load and run the script */
 
453
void glspi_run_script(gchar *script_file, gint caller, GKeyFile*proj, gchar *script_dir)
 
454
{
 
455
        gint status;
 
456
        lua_State *L = glspi_state_new();
 
457
        glspi_init_module(L, script_file, caller,proj,script_dir);
 
458
#if 0
 
459
        while (gtk_events_pending()) { gtk_main_iteration(); }
 
460
#endif
 
461
        status = luaL_loadfile(L, script_file);
 
462
        switch (status) {
 
463
        case 0: {
 
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 */
 
469
                if (0 == status) {
 
470
                        status = lua_pcall(L, 0, 0, 0);
 
471
                } else {
 
472
                        lua_gc(L, LUA_GCCOLLECT, 0); /* force garbage collection if error */
 
473
                        show_error(L, script_file);
 
474
                }
 
475
                break;
 
476
        }
 
477
        case LUA_ERRSYNTAX:
 
478
                show_error(L, script_file);
 
479
                break;
 
480
        case LUA_ERRMEM:
 
481
                glspi_script_error(script_file, _("Out of memory."), TRUE, -1);
 
482
                break;
 
483
        case LUA_ERRFILE:
 
484
                glspi_script_error(script_file, _("Failed to open script file."), TRUE, -1);
 
485
                break;
 
486
        default:
 
487
                glspi_script_error(script_file, _("Unknown error while loading script file."), TRUE, -1);
 
488
        }
 
489
        glspi_state_done(L);
 
490
}
 
491