vis-lua: allow to set window.syntax = nil
[vis.git] / vis-lua.c
blob35dbe9560d14e638c261834910eb70ab704bb4c2
1 #include <stdarg.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <pwd.h>
7 #include "vis-lua.h"
8 #include "vis-core.h"
9 #include "text-motions.h"
11 #if !CONFIG_LUA
13 void vis_lua_start(Vis *vis) { }
14 void vis_lua_quit(Vis *vis) { }
15 void vis_lua_file_open(Vis *vis, File *file) { }
16 void vis_lua_file_save(Vis *vis, File *file) { }
17 void vis_lua_file_close(Vis *vis, File *file) { }
18 void vis_lua_win_open(Vis *vis, Win *win) { }
19 void vis_lua_win_close(Vis *vis, Win *win) { }
20 bool vis_theme_load(Vis *vis, const char *name) { return true; }
22 #else
24 #if 0
25 static void stack_dump_entry(lua_State *L, int i) {
26 int t = lua_type(L, i);
27 switch (t) {
28 case LUA_TNIL:
29 printf("nil");
30 break;
31 case LUA_TBOOLEAN:
32 printf(lua_toboolean(L, i) ? "true" : "false");
33 break;
34 case LUA_TLIGHTUSERDATA:
35 printf("lightuserdata(%p)", lua_touserdata(L, i));
36 break;
37 case LUA_TNUMBER:
38 printf("%g", lua_tonumber(L, i));
39 break;
40 case LUA_TSTRING:
41 printf("`%s'", lua_tostring(L, i));
42 break;
43 case LUA_TTABLE:
44 printf("table[");
45 lua_pushnil(L); /* first key */
46 while (lua_next(L, i > 0 ? i : i - 1)) {
47 stack_dump_entry(L, -2);
48 printf("=");
49 stack_dump_entry(L, -1);
50 printf(",");
51 lua_pop(L, 1); /* remove value, keep key */
53 printf("]");
54 break;
55 case LUA_TUSERDATA:
56 printf("userdata(%p)", lua_touserdata(L, i));
57 break;
58 default: /* other values */
59 printf("%s", lua_typename(L, t));
60 break;
64 static void stack_dump(lua_State *L, const char *format, ...) {
65 va_list ap;
66 va_start(ap, format);
67 vprintf(format, ap);
68 va_end(ap);
69 int top = lua_gettop(L);
70 for (int i = 1; i <= top; i++) {
71 printf("%d: ", i);
72 stack_dump_entry(L, i);
73 printf("\n");
75 printf("\n\n");
76 fflush(stdout);
79 #endif
81 static int error_function(lua_State *L) {
82 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
83 size_t len;
84 const char *msg = lua_tostring(L, 1);
85 if (msg)
86 luaL_traceback(L, L, msg, 1);
87 msg = lua_tolstring(L, 1, &len);
88 vis_message_show(vis, msg);
89 return 1;
92 static int pcall(Vis *vis, lua_State *L, int nargs, int nresults) {
93 /* insert a custom error function below all arguments */
94 int msgh = lua_gettop(L) - nargs;
95 lua_pushlightuserdata(L, vis);
96 lua_pushcclosure(L, error_function, 1);
97 lua_insert(L, msgh);
98 int ret = lua_pcall(L, nargs, nresults, msgh);
99 lua_remove(L, msgh);
100 return ret;
103 /* expects a lua function at the top of the stack and stores a
104 * reference to it in the registry. The return value can be used
105 * to look it up.
107 * registry["vis.functions"][(void*)(function)] = function
109 static const void *func_ref_new(lua_State *L) {
110 const void *addr = lua_topointer(L, -1);
111 if (!lua_isfunction(L, -1) || !addr)
112 return NULL;
113 lua_getfield(L, LUA_REGISTRYINDEX, "vis.functions");
114 lua_pushlightuserdata(L, (void*)addr);
115 lua_pushvalue(L, -3);
116 lua_settable(L, -3);
117 lua_pop(L, 1);
118 return addr;
121 /* retrieve function from registry and place it at the top of the stack */
122 static bool func_ref_get(lua_State *L, const void *addr) {
123 lua_getfield(L, LUA_REGISTRYINDEX, "vis.functions");
124 lua_pushlightuserdata(L, (void*)addr);
125 lua_gettable(L, -2);
126 lua_remove(L, -2);
127 if (!lua_isfunction(L, -1)) {
128 lua_pop(L, 1);
129 return false;
131 return true;
134 static void *obj_new(lua_State *L, size_t size, const char *type) {
135 void *obj = lua_newuserdata(L, size);
136 luaL_getmetatable(L, type);
137 lua_setmetatable(L, -2);
138 lua_newtable(L);
139 lua_setuservalue(L, -2);
140 return obj;
143 /* returns registry["vis.objects"][addr] if it is of correct type */
144 static void *obj_ref_get(lua_State *L, void *addr, const char *type) {
145 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
146 lua_pushlightuserdata(L, addr);
147 lua_gettable(L, -2);
148 lua_remove(L, -2);
149 if (lua_isnil(L, -1)) {
150 lua_pop(L, 1);
151 return NULL;
153 return luaL_checkudata(L, -1, type);
156 /* expects a userdatum at the top of the stack and sets
158 * registry["vis.objects"][addr] = userdata
160 static void obj_ref_set(lua_State *L, void *addr) {
161 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
162 lua_pushlightuserdata(L, addr);
163 lua_pushvalue(L, -3);
164 lua_settable(L, -3);
165 lua_pop(L, 1);
168 /* invalidates an object reference
170 * registry["vis.objects"][addr] = nil
172 static void obj_ref_free(lua_State *L, void *addr) {
173 lua_pushnil(L);
174 obj_ref_set(L, addr);
177 /* creates a new object reference of given type if it does not
178 * already exist in the registry */
179 static void *obj_ref_new(lua_State *L, void *addr, const char *type) {
180 if (!addr)
181 return NULL;
182 void **handle = (void**)obj_ref_get(L, addr, type);
183 if (!handle) {
184 handle = obj_new(L, sizeof(addr), type);
185 obj_ref_set(L, addr);
186 *handle = addr;
188 return *handle;
191 /* retrieve object stored in reference at stack location `idx' */
192 static void *obj_ref_check_get(lua_State *L, int idx, const char *type) {
193 void **addr = luaL_checkudata(L, idx, type);
194 if (!obj_ref_get(L, *addr, type))
195 return NULL;
196 return *addr;
199 /* (type) check validity of object reference at stack location `idx' */
200 static void *obj_ref_check(lua_State *L, int idx, const char *type) {
201 void *obj = obj_ref_check_get(L, idx, type);
202 if (obj)
203 lua_pop(L, 1);
204 return obj;
207 static int index_common(lua_State *L) {
208 lua_getmetatable(L, 1);
209 lua_pushvalue(L, 2);
210 lua_gettable(L, -2);
211 if (lua_isnil(L, -1)) {
212 lua_getuservalue(L, 1);
213 lua_pushvalue(L, 2);
214 lua_gettable(L, -2);
216 return 1;
219 static int newindex_common(lua_State *L) {
220 lua_getuservalue(L, 1);
221 lua_pushvalue(L, 2);
222 lua_pushvalue(L, 3);
223 lua_settable(L, -3);
224 return 0;
227 static const char *keymapping(Vis *vis, const char *keys, const Arg *arg) {
228 lua_State *L = vis->lua;
229 if (!func_ref_get(L, arg->v))
230 return keys;
231 pcall(vis, L, 0, 0);
232 return keys;
235 static int windows_iter(lua_State *L);
237 static int windows(lua_State *L) {
238 Vis *vis = obj_ref_check(L, 1, "vis");
239 if (!vis) {
240 lua_pushnil(L);
241 return 1;
243 Win **handle = lua_newuserdata(L, sizeof *handle);
244 *handle = vis->windows;
245 lua_pushcclosure(L, windows_iter, 1);
246 return 1;
249 static int windows_iter(lua_State *L) {
250 Win **handle = lua_touserdata(L, lua_upvalueindex(1));
251 if (!*handle)
252 return 0;
253 Win *win = obj_ref_new(L, *handle, "vis.window");
254 if (!win)
255 return 0;
256 *handle = win->next;
257 return 1;
260 static int files_iter(lua_State *L);
262 static int files(lua_State *L) {
263 Vis *vis = obj_ref_check(L, 1, "vis");
264 if (!vis) {
265 lua_pushnil(L);
266 return 1;
268 File **handle = lua_newuserdata(L, sizeof *handle);
269 *handle = vis->files;
270 lua_pushcclosure(L, files_iter, 1);
271 return 1;
274 static int files_iter(lua_State *L) {
275 File **handle = lua_touserdata(L, lua_upvalueindex(1));
276 if (!*handle)
277 return 0;
278 File *file = obj_ref_new(L, *handle, "vis.file");
279 if (!file)
280 return 0;
281 *handle = file->next;
282 return 1;
285 static int command(lua_State *L) {
286 Vis *vis = obj_ref_check(L, 1, "vis");
287 if (!vis) {
288 lua_pushnil(L);
289 return 1;
291 const char *cmd = luaL_checkstring(L, 2);
292 bool ret = vis_cmd(vis, cmd);
293 lua_pushboolean(L, ret);
294 return 1;
297 static int info(lua_State *L) {
298 Vis *vis = obj_ref_check(L, 1, "vis");
299 if (!vis) {
300 lua_pushnil(L);
301 return 1;
303 const char *msg = luaL_checkstring(L, 2);
304 vis_info_show(vis, "%s", msg);
305 return 0;
308 static int open(lua_State *L) {
309 Vis *vis = obj_ref_check(L, 1, "vis");
310 if (!vis) {
311 lua_pushnil(L);
312 return 1;
314 const char *name = luaL_checkstring(L, 2);
315 File *file = NULL;
316 if (vis_window_new(vis, name))
317 file = obj_ref_new(L, vis->win->file, "vis.file");
318 if (!file)
319 lua_pushnil(L);
320 return 1;
323 static int map(lua_State *L) {
324 Vis *vis = obj_ref_check(L, 1, "vis");
325 if (!vis) {
326 lua_pushnil(L);
327 return 1;
329 KeyBinding *binding = NULL;
330 KeyAction *action = NULL;
332 int mode = luaL_checkint(L, 2);
333 const char *key = luaL_checkstring(L, 3);
335 if (!key || !lua_isfunction(L, 4))
336 goto err;
337 if (!(binding = calloc(1, sizeof *binding)) || !(action = calloc(1, sizeof *action)))
338 goto err;
340 /* store reference to function in the registry */
341 lua_pushvalue(L, 4);
342 const void *func = func_ref_new(L);
343 if (!func)
344 goto err;
346 *action = (KeyAction){
347 .name = NULL,
348 .help = NULL,
349 .func = keymapping,
350 .arg = (const Arg){
351 .v = func,
355 binding->action = action;
357 if (!vis_mode_map(vis, mode, key, binding))
358 goto err;
360 lua_pushboolean(L, true);
361 return 1;
362 err:
363 free(binding);
364 free(action);
365 lua_pushboolean(L, false);
366 return 1;
369 static int motion(lua_State *L) {
370 Vis *vis = obj_ref_check(L, 1, "vis");
371 enum VisMotion id = luaL_checkunsigned(L, 2);
372 // TODO handle var args?
373 lua_pushboolean(L, vis && vis_motion(vis, id));
374 return 1;
377 static size_t motion_lua(Vis *vis, Win *win, void *data, size_t pos) {
378 lua_State *L = vis->lua;
379 if (!func_ref_get(L, data) || !obj_ref_new(L, win, "vis.window"))
380 return EPOS;
382 lua_pushunsigned(L, pos);
383 if (pcall(vis, L, 2, 1) != 0)
384 return EPOS;
385 return luaL_checkunsigned(L, -1);
388 static int motion_register(lua_State *L) {
389 int id = -1;
390 const void *func;
391 Vis *vis = obj_ref_check(L, 1, "vis");
392 if (!vis || !lua_isfunction(L, 2) || !(func = func_ref_new(L)))
393 goto err;
394 id = vis_motion_register(vis, 0, (void*)func, motion_lua);
395 err:
396 lua_pushinteger(L, id);
397 return 1;
400 static int textobject(lua_State *L) {
401 Vis *vis = obj_ref_check(L, 1, "vis");
402 enum VisTextObject id = luaL_checkunsigned(L, 2);
403 lua_pushboolean(L, vis && vis_textobject(vis, id));
404 return 1;
408 static Filerange textobject_lua(Vis *vis, Win *win, void *data, size_t pos) {
409 lua_State *L = vis->lua;
410 if (!func_ref_get(L, data) || !obj_ref_new(L, win, "vis.window"))
411 return text_range_empty();
412 lua_pushunsigned(L, pos);
413 if (pcall(vis, L, 2, 2) != 0)
414 return text_range_empty();
415 return text_range_new(luaL_checkunsigned(L, -2), luaL_checkunsigned(L, -1));
418 static int textobject_register(lua_State *L) {
419 int id = -1;
420 const void *func;
421 Vis *vis = obj_ref_check(L, 1, "vis");
422 if (!vis || !lua_isfunction(L, 2) || !(func = func_ref_new(L)))
423 goto err;
424 id = vis_textobject_register(vis, 0, (void*)func, textobject_lua);
425 err:
426 lua_pushinteger(L, id);
427 return 1;
430 static int vis_index(lua_State *L) {
431 Vis *vis = obj_ref_check(L, 1, "vis");
432 if (!vis) {
433 lua_pushnil(L);
434 return 1;
437 if (lua_isstring(L, 2)) {
438 const char *key = lua_tostring(L, 2);
439 if (strcmp(key, "win") == 0) {
440 obj_ref_new(L, vis->windows, "vis.window");
441 return 1;
444 if (strcmp(key, "MODE_NORMAL") == 0) {
445 lua_pushunsigned(L, VIS_MODE_NORMAL);
446 return 1;
449 if (strcmp(key, "MODE_OPERATOR_PENDING") == 0) {
450 lua_pushunsigned(L, VIS_MODE_OPERATOR_PENDING);
451 return 1;
454 if (strcmp(key, "MODE_VISUAL") == 0) {
455 lua_pushunsigned(L, VIS_MODE_VISUAL);
456 return 1;
459 if (strcmp(key, "MODE_VISUAL_LINE") == 0) {
460 lua_pushunsigned(L, VIS_MODE_VISUAL_LINE);
461 return 1;
464 if (strcmp(key, "MODE_INSERT") == 0) {
465 lua_pushunsigned(L, VIS_MODE_INSERT);
466 return 1;
469 if (strcmp(key, "MODE_REPLACE") == 0) {
470 lua_pushunsigned(L, VIS_MODE_REPLACE);
471 return 1;
475 return index_common(L);
478 static int vis_newindex(lua_State *L) {
479 Vis *vis = obj_ref_check(L, 1, "vis");
480 if (!vis)
481 return 0;
482 return newindex_common(L);
485 static const struct luaL_Reg vis_lua[] = {
486 { "files", files },
487 { "windows", windows },
488 { "command", command },
489 { "info", info },
490 { "open", open },
491 { "map", map },
492 { "motion", motion },
493 { "motion_register", motion_register },
494 { "textobject", textobject },
495 { "textobject_register", textobject_register },
496 { "__index", vis_index },
497 { "__newindex", vis_newindex },
498 { NULL, NULL },
501 static int window_index(lua_State *L) {
502 Win *win = obj_ref_check(L, 1, "vis.window");
503 if (!win) {
504 lua_pushnil(L);
505 return 1;
508 if (lua_isstring(L, 2)) {
509 const char *key = lua_tostring(L, 2);
510 if (strcmp(key, "file") == 0) {
511 obj_ref_new(L, win->file, "vis.file");
512 return 1;
515 if (strcmp(key, "cursor") == 0) {
516 obj_ref_new(L, win->view, "vis.window.cursor");
517 return 1;
520 if (strcmp(key, "syntax") == 0) {
521 const char *syntax = view_syntax_get(win->view);
522 if (syntax)
523 lua_pushstring(L, syntax);
524 else
525 lua_pushnil(L);
526 return 1;
530 return index_common(L);
533 static int window_newindex(lua_State *L) {
534 Win *win = obj_ref_check(L, 1, "vis.window");
535 if (!win)
536 return 0;
537 if (lua_isstring(L, 2)) {
538 const char *key = lua_tostring(L, 2);
539 if (strcmp(key, "syntax") == 0) {
540 const char *syntax = NULL;
541 if (!lua_isnil(L, 3))
542 syntax = luaL_checkstring(L, 3);
543 view_syntax_set(win->view, syntax);
544 return 0;
547 return newindex_common(L);
550 static const struct luaL_Reg window_funcs[] = {
551 { "__index", window_index },
552 { "__newindex", window_newindex },
553 { NULL, NULL },
556 static int window_cursor_index(lua_State *L) {
557 View *view = obj_ref_check(L, 1, "vis.window.cursor");
558 if (!view) {
559 lua_pushnil(L);
560 return 1;
563 if (lua_isstring(L, 2)) {
564 Text *txt = view_text(view);
565 const char *key = lua_tostring(L, 2);
566 if (strcmp(key, "pos") == 0) {
567 lua_pushunsigned(L, view_cursor_get(view));
568 return 1;
571 if (strcmp(key, "line") == 0) {
572 size_t pos = view_cursor_get(view);
573 size_t line = text_lineno_by_pos(txt, pos);
574 lua_pushunsigned(L, line);
575 return 1;
578 if (strcmp(key, "col") == 0) {
579 size_t pos = view_cursor_get(view);
580 int col = text_line_char_get(txt, pos);
581 lua_pushunsigned(L, col);
582 return 1;
586 return index_common(L);
589 static int window_cursor_newindex(lua_State *L) {
590 View *view = obj_ref_check(L, 1, "vis.window.cursor");
591 if (!view)
592 return 0;
593 if (lua_isstring(L, 2)) {
594 const char *key = lua_tostring(L, 2);
595 if (strcmp(key, "pos") == 0) {
596 size_t pos = luaL_checkunsigned(L, 3);
597 view_cursor_to(view, pos);
598 return 0;
601 return newindex_common(L);
604 static int window_cursor_to(lua_State *L) {
605 View *view = obj_ref_check(L, 1, "vis.window.cursor");
606 if (view) {
607 Text *txt = view_text(view);
608 size_t line = luaL_checkunsigned(L, 2);
609 size_t col = luaL_checkunsigned(L, 3);
610 size_t pos = text_pos_by_lineno(txt, line);
611 pos = text_line_char_set(txt, pos, col);
612 view_cursor_to(view, pos);
614 return 0;
617 static const struct luaL_Reg window_cursor_funcs[] = {
618 { "__index", window_cursor_index },
619 { "__newindex", window_cursor_newindex },
620 { "to", window_cursor_to },
621 { NULL, NULL },
624 static int file_index(lua_State *L) {
625 File *file = obj_ref_check(L, 1, "vis.file");
626 if (!file) {
627 lua_pushnil(L);
628 return 1;
631 if (lua_isstring(L, 2)) {
632 const char *key = lua_tostring(L, 2);
633 if (strcmp(key, "name") == 0) {
634 lua_pushstring(L, file->name);
635 return 1;
637 if (strcmp(key, "lines") == 0) {
638 obj_ref_new(L, file->text, "vis.file.text");
639 return 1;
643 return index_common(L);
646 static int file_newindex(lua_State *L) {
647 File *file = obj_ref_check(L, 1, "vis.file");
648 if (!file)
649 return 0;
650 return newindex_common(L);
653 static int file_insert(lua_State *L) {
654 File *file = obj_ref_check(L, 1, "vis.file");
655 if (file) {
656 size_t pos = luaL_checkunsigned(L, 2);
657 size_t len;
658 luaL_checkstring(L, 3);
659 const char *data = lua_tolstring(L, 3, &len);
660 bool ret = text_insert(file->text, pos, data, len);
661 lua_pushboolean(L, ret);
662 } else {
663 lua_pushboolean(L, false);
665 return 1;
668 static int file_delete(lua_State *L) {
669 File *file = obj_ref_check(L, 1, "vis.file");
670 if (file) {
671 size_t pos = luaL_checkunsigned(L, 2);
672 size_t len = luaL_checkunsigned(L, 3);
673 bool ret = text_delete(file->text, pos, len);
674 lua_pushboolean(L, ret);
675 } else {
676 lua_pushboolean(L, false);
678 return 1;
681 static int file_lines_iterator_it(lua_State *L);
683 static int file_lines_iterator(lua_State *L) {
684 /* need to check second parameter first, because obj_ref_check_get
685 * modifies the stack */
686 size_t line = luaL_optunsigned(L, 2, 1);
687 File *file = obj_ref_check_get(L, 1, "vis.file");
688 size_t *pos = lua_newuserdata(L, sizeof *pos);
689 *pos = text_pos_by_lineno(file->text, line);
690 lua_pushcclosure(L, file_lines_iterator_it, 2);
691 return 1;
694 static int file_lines_iterator_it(lua_State *L) {
695 File *file = *(File**)lua_touserdata(L, lua_upvalueindex(1));
696 size_t *start = lua_touserdata(L, lua_upvalueindex(2));
697 if (*start == text_size(file->text))
698 return 0;
699 size_t end = text_line_end(file->text, *start);
700 size_t len = end - *start;
701 char *buf = malloc(len);
702 if (!buf && len)
703 return 0;
704 len = text_bytes_get(file->text, *start, len, buf);
705 lua_pushlstring(L, buf, len);
706 free(buf);
707 *start = text_line_next(file->text, end);
708 return 1;
711 static int file_content(lua_State *L) {
712 File *file = obj_ref_check(L, 1, "vis.file");
713 if (!file)
714 goto err;
715 size_t pos = luaL_checkunsigned(L, 2);
716 size_t len = luaL_checkunsigned(L, 3);
717 char *data = malloc(len);
718 if (!data)
719 goto err;
720 len = text_bytes_get(file->text, pos, len, data);
721 lua_pushlstring(L, data, len);
722 free(data);
723 return 1;
724 err:
725 lua_pushnil(L);
726 return 1;
729 static const struct luaL_Reg file_funcs[] = {
730 { "__index", file_index },
731 { "__newindex", file_newindex },
732 { "insert", file_insert },
733 { "delete", file_delete },
734 { "lines_iterator", file_lines_iterator },
735 { "content", file_content },
736 { NULL, NULL },
739 static int file_lines_index(lua_State *L) {
740 Text *txt = obj_ref_check(L, 1, "vis.file.text");
741 if (!txt)
742 goto err;
743 size_t line = luaL_checkunsigned(L, 2);
744 size_t start = text_pos_by_lineno(txt, line);
745 size_t end = text_line_end(txt, start);
746 if (start != EPOS && end != EPOS) {
747 size_t size = end - start;
748 char *data = malloc(size);
749 if (!data && size)
750 goto err;
751 size = text_bytes_get(txt, start, size, data);
752 lua_pushlstring(L, data, size);
753 free(data);
754 return 1;
756 err:
757 lua_pushnil(L);
758 return 1;
761 static int file_lines_newindex(lua_State *L) {
762 Text *txt = obj_ref_check(L, 1, "vis.file.text");
763 if (!txt)
764 return 0;
765 size_t line = luaL_checkunsigned(L, 2);
766 size_t size;
767 const char *data = luaL_checklstring(L, 3, &size);
768 if (line == 0) {
769 text_insert(txt, 0, data, size);
770 text_insert_newline(txt, size);
771 return 0;
773 size_t start = text_pos_by_lineno(txt, line);
774 size_t end = text_line_end(txt, start);
775 if (start != EPOS && end != EPOS) {
776 text_delete(txt, start, end - start);
777 text_insert(txt, start, data, size);
778 if (text_size(txt) == start + size)
779 text_insert_newline(txt, text_size(txt));
781 return 0;
784 static int file_lines_len(lua_State *L) {
785 Text *txt = obj_ref_check(L, 1, "vis.file.text");
786 size_t lines = 0;
787 if (txt) {
788 char lastchar;
789 size_t size = text_size(txt);
790 if (size > 0)
791 lines = text_lineno_by_pos(txt, size);
792 if (lines > 1 && text_byte_get(txt, size-1, &lastchar) && lastchar == '\n')
793 lines--;
795 lua_pushunsigned(L, lines);
796 return 1;
799 static const struct luaL_Reg file_lines_funcs[] = {
800 { "__index", file_lines_index },
801 { "__newindex", file_lines_newindex },
802 { "__len", file_lines_len },
803 { NULL, NULL },
806 static void vis_lua_event(Vis *vis, const char *name) {
807 lua_State *L = vis->lua;
808 lua_getglobal(L, "vis");
809 lua_getfield(L, -1, "events");
810 if (lua_istable(L, -1)) {
811 lua_getfield(L, -1, name);
813 lua_remove(L, -2);
816 void vis_lua_start(Vis *vis) {
817 lua_State *L = luaL_newstate();
818 if (!L)
819 return;
820 vis->lua = L;
821 luaL_openlibs(L);
824 /* extends lua's package.path with:
825 * - $VIS_PATH/{,lexers}
826 * - $XDG_CONFIG_HOME/vis/{,lexers} (defaulting to $HOME/.config/vis/{,lexers})
827 * - /usr/local/share/vis/{,lexers}
828 * - /usr/share/vis/{,lexers}
829 * - package.path (standard lua search path)
831 int paths = 3;
832 lua_getglobal(L, "package");
834 const char *vis_path = getenv("VIS_PATH");
835 if (vis_path) {
836 lua_pushstring(L, vis_path);
837 lua_pushstring(L, "/?.lua;");
838 lua_pushstring(L, vis_path);
839 lua_pushstring(L, "/lexers/?.lua;");
840 lua_concat(L, 4);
841 paths++;
844 /* try to get users home directory */
845 const char *home = getenv("HOME");
846 if (!home || !*home) {
847 struct passwd *pw = getpwuid(getuid());
848 if (pw)
849 home = pw->pw_dir;
852 const char *xdg_config = getenv("XDG_CONFIG_HOME");
853 if (xdg_config) {
854 lua_pushstring(L, xdg_config);
855 lua_pushstring(L, "/vis/?.lua;");
856 lua_pushstring(L, xdg_config);
857 lua_pushstring(L, "/vis/lexers/?.lua;");
858 lua_concat(L, 4);
859 paths++;
860 } else if (home && *home) {
861 lua_pushstring(L, home);
862 lua_pushstring(L, "/.config/vis/?.lua;");
863 lua_pushstring(L, home);
864 lua_pushstring(L, "/.config/vis/lexers/?.lua;");
865 lua_concat(L, 4);
866 paths++;
869 lua_pushstring(L, "/usr/local/share/vis/?.lua;/usr/local/share/vis/lexers/?.lua;");
870 lua_pushstring(L, "/usr/share/vis/?.lua;/usr/share/vis/lexers/?.lua;");
871 lua_getfield(L, -paths, "path");
872 lua_concat(L, paths);
873 lua_setfield(L, -2, "path");
874 lua_pop(L, 1); /* package */
876 /* table in registry to track lifetimes of C objects */
877 lua_newtable(L);
878 lua_setfield(L, LUA_REGISTRYINDEX, "vis.objects");
879 /* table in registry to store references to Lua functions */
880 lua_newtable(L);
881 lua_setfield(L, LUA_REGISTRYINDEX, "vis.functions");
882 /* metatable used to type check user data */
883 luaL_newmetatable(L, "vis.file");
884 luaL_setfuncs(L, file_funcs, 0);
885 luaL_newmetatable(L, "vis.file.text");
886 luaL_setfuncs(L, file_lines_funcs, 0);
887 luaL_newmetatable(L, "vis.window");
888 luaL_setfuncs(L, window_funcs, 0);
889 luaL_newmetatable(L, "vis.window.cursor");
890 luaL_setfuncs(L, window_cursor_funcs, 0);
891 /* vis module table with up value as the C pointer */
892 luaL_newmetatable(L, "vis");
893 luaL_setfuncs(L, vis_lua, 0);
894 obj_ref_new(L, vis, "vis");
895 lua_setglobal(L, "vis");
897 lua_getglobal(L, "require");
898 lua_pushstring(L, "visrc");
899 pcall(vis, L, 1, 0);
900 vis_lua_event(vis, "start");
901 if (lua_isfunction(L, -1))
902 pcall(vis, L, 0, 0);
903 lua_pop(L, 1);
906 void vis_lua_quit(Vis *vis) {
907 lua_State *L = vis->lua;
908 if (!L)
909 return;
910 vis_lua_event(vis, "quit");
911 if (lua_isfunction(L, -1))
912 pcall(vis, L, 0, 0);
913 lua_pop(L, 1);
914 lua_close(L);
917 void vis_lua_file_open(Vis *vis, File *file) {
921 void vis_lua_file_save(Vis *vis, File *file) {
925 void vis_lua_file_close(Vis *vis, File *file) {
926 lua_State *L = vis->lua;
927 vis_lua_event(vis, "file_close");
928 if (lua_isfunction(L, -1)) {
929 obj_ref_new(L, file, "vis.file");
930 pcall(vis, L, 1, 0);
932 obj_ref_free(L, file->text);
933 obj_ref_free(L, file);
934 lua_pop(L, 1);
937 void vis_lua_win_open(Vis *vis, Win *win) {
938 lua_State *L = vis->lua;
939 vis_lua_event(vis, "win_open");
940 if (lua_isfunction(L, -1)) {
941 obj_ref_new(L, win, "vis.window");
942 pcall(vis, L, 1, 0);
944 lua_pop(L, 1);
947 void vis_lua_win_close(Vis *vis, Win *win) {
948 lua_State *L = vis->lua;
949 vis_lua_event(vis, "win_close");
950 if (lua_isfunction(L, -1)) {
951 obj_ref_new(L, win, "vis.window");
952 pcall(vis, L, 1, 0);
954 obj_ref_free(L, win->view);
955 obj_ref_free(L, win);
956 lua_pop(L, 1);
959 bool vis_theme_load(Vis *vis, const char *name) {
960 lua_State *L = vis->lua;
961 if (!L)
962 return false;
963 /* package.loaded['themes/'..name] = nil
964 * require 'themes/'..name */
965 lua_pushstring(L, "themes/");
966 lua_pushstring(L, name);
967 lua_concat(L, 2);
968 lua_getglobal(L, "package");
969 lua_getfield(L, -1, "loaded");
970 lua_pushvalue(L, -3);
971 lua_pushnil(L);
972 lua_settable(L, -3);
973 lua_pop(L, 2);
974 lua_getglobal(L, "require");
975 lua_pushvalue(L, -2);
976 if (pcall(vis, L, 1, 0))
977 return false;
978 for (Win *win = vis->windows; win; win = win->next)
979 view_syntax_set(win->view, view_syntax_get(win->view));
980 return true;
983 #endif