lexer: sort list of file extensions
[vis.git] / vis-lua.c
blob9a2642f8e0ede25b18be685b031a01ed0109d634
1 #include <stdarg.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <limits.h>
5 #include <unistd.h>
6 #include <libgen.h>
7 #include <sys/types.h>
8 #include <pwd.h>
10 #include "vis-lua.h"
11 #include "vis-core.h"
12 #include "text-motions.h"
14 #ifndef VIS_PATH
15 #define VIS_PATH "/usr/local/share/vis"
16 #endif
18 #if !CONFIG_LUA
20 bool vis_lua_path_add(Vis *vis, const char *path) { return true; }
21 const char *vis_lua_paths_get(Vis *vis) { return NULL; }
22 void vis_lua_init(Vis *vis) { }
23 void vis_lua_start(Vis *vis) { }
24 void vis_lua_quit(Vis *vis) { }
25 void vis_lua_file_open(Vis *vis, File *file) { }
26 void vis_lua_file_save(Vis *vis, File *file) { }
27 void vis_lua_file_close(Vis *vis, File *file) { }
28 void vis_lua_win_open(Vis *vis, Win *win) { }
29 void vis_lua_win_close(Vis *vis, Win *win) { }
30 bool vis_theme_load(Vis *vis, const char *name) { return true; }
32 #else
34 #if 0
35 static void stack_dump_entry(lua_State *L, int i) {
36 int t = lua_type(L, i);
37 switch (t) {
38 case LUA_TNIL:
39 printf("nil");
40 break;
41 case LUA_TBOOLEAN:
42 printf(lua_toboolean(L, i) ? "true" : "false");
43 break;
44 case LUA_TLIGHTUSERDATA:
45 printf("lightuserdata(%p)", lua_touserdata(L, i));
46 break;
47 case LUA_TNUMBER:
48 printf("%g", lua_tonumber(L, i));
49 break;
50 case LUA_TSTRING:
51 printf("`%s'", lua_tostring(L, i));
52 break;
53 case LUA_TTABLE:
54 printf("table[");
55 lua_pushnil(L); /* first key */
56 while (lua_next(L, i > 0 ? i : i - 1)) {
57 stack_dump_entry(L, -2);
58 printf("=");
59 stack_dump_entry(L, -1);
60 printf(",");
61 lua_pop(L, 1); /* remove value, keep key */
63 printf("]");
64 break;
65 case LUA_TUSERDATA:
66 printf("userdata(%p)", lua_touserdata(L, i));
67 break;
68 default: /* other values */
69 printf("%s", lua_typename(L, t));
70 break;
74 static void stack_dump(lua_State *L, const char *format, ...) {
75 va_list ap;
76 va_start(ap, format);
77 vprintf(format, ap);
78 va_end(ap);
79 int top = lua_gettop(L);
80 for (int i = 1; i <= top; i++) {
81 printf("%d: ", i);
82 stack_dump_entry(L, i);
83 printf("\n");
85 printf("\n\n");
86 fflush(stdout);
89 #endif
91 static int error_function(lua_State *L) {
92 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
93 size_t len;
94 const char *msg = lua_tostring(L, 1);
95 if (msg)
96 luaL_traceback(L, L, msg, 1);
97 msg = lua_tolstring(L, 1, &len);
98 vis_message_show(vis, msg);
99 return 1;
102 static int pcall(Vis *vis, lua_State *L, int nargs, int nresults) {
103 /* insert a custom error function below all arguments */
104 int msgh = lua_gettop(L) - nargs;
105 lua_pushlightuserdata(L, vis);
106 lua_pushcclosure(L, error_function, 1);
107 lua_insert(L, msgh);
108 int ret = lua_pcall(L, nargs, nresults, msgh);
109 lua_remove(L, msgh);
110 return ret;
113 /* expects a lua function at the top of the stack and stores a
114 * reference to it in the registry. The return value can be used
115 * to look it up.
117 * registry["vis.functions"][(void*)(function)] = function
119 static const void *func_ref_new(lua_State *L) {
120 const void *addr = lua_topointer(L, -1);
121 if (!lua_isfunction(L, -1) || !addr)
122 return NULL;
123 lua_getfield(L, LUA_REGISTRYINDEX, "vis.functions");
124 lua_pushlightuserdata(L, (void*)addr);
125 lua_pushvalue(L, -3);
126 lua_settable(L, -3);
127 lua_pop(L, 1);
128 return addr;
131 /* retrieve function from registry and place it at the top of the stack */
132 static bool func_ref_get(lua_State *L, const void *addr) {
133 lua_getfield(L, LUA_REGISTRYINDEX, "vis.functions");
134 lua_pushlightuserdata(L, (void*)addr);
135 lua_gettable(L, -2);
136 lua_remove(L, -2);
137 if (!lua_isfunction(L, -1)) {
138 lua_pop(L, 1);
139 return false;
141 return true;
144 static void *obj_new(lua_State *L, size_t size, const char *type) {
145 void *obj = lua_newuserdata(L, size);
146 luaL_getmetatable(L, type);
147 lua_setmetatable(L, -2);
148 lua_newtable(L);
149 lua_setuservalue(L, -2);
150 return obj;
153 /* returns registry["vis.objects"][addr] if it is of correct type */
154 static void *obj_ref_get(lua_State *L, void *addr, const char *type) {
155 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
156 lua_pushlightuserdata(L, addr);
157 lua_gettable(L, -2);
158 lua_remove(L, -2);
159 if (lua_isnil(L, -1)) {
160 lua_pop(L, 1);
161 return NULL;
163 return luaL_checkudata(L, -1, type);
166 /* expects a userdatum at the top of the stack and sets
168 * registry["vis.objects"][addr] = userdata
170 static void obj_ref_set(lua_State *L, void *addr) {
171 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
172 lua_pushlightuserdata(L, addr);
173 lua_pushvalue(L, -3);
174 lua_settable(L, -3);
175 lua_pop(L, 1);
178 /* invalidates an object reference
180 * registry["vis.objects"][addr] = nil
182 static void obj_ref_free(lua_State *L, void *addr) {
183 lua_pushnil(L);
184 obj_ref_set(L, addr);
187 /* creates a new object reference of given type if it does not
188 * already exist in the registry */
189 static void *obj_ref_new(lua_State *L, void *addr, const char *type) {
190 if (!addr)
191 return NULL;
192 void **handle = (void**)obj_ref_get(L, addr, type);
193 if (!handle) {
194 handle = obj_new(L, sizeof(addr), type);
195 obj_ref_set(L, addr);
196 *handle = addr;
198 return *handle;
201 /* retrieve object stored in reference at stack location `idx' */
202 static void *obj_ref_check_get(lua_State *L, int idx, const char *type) {
203 void **addr = luaL_checkudata(L, idx, type);
204 if (!obj_ref_get(L, *addr, type))
205 return NULL;
206 return *addr;
209 /* (type) check validity of object reference at stack location `idx' */
210 static void *obj_ref_check(lua_State *L, int idx, const char *type) {
211 void *obj = obj_ref_check_get(L, idx, type);
212 if (obj)
213 lua_pop(L, 1);
214 return obj;
217 static int index_common(lua_State *L) {
218 lua_getmetatable(L, 1);
219 lua_pushvalue(L, 2);
220 lua_gettable(L, -2);
221 if (lua_isnil(L, -1)) {
222 lua_getuservalue(L, 1);
223 lua_pushvalue(L, 2);
224 lua_gettable(L, -2);
226 return 1;
229 static int newindex_common(lua_State *L) {
230 lua_getuservalue(L, 1);
231 lua_pushvalue(L, 2);
232 lua_pushvalue(L, 3);
233 lua_settable(L, -3);
234 return 0;
237 static void pushrange(lua_State *L, Filerange *r) {
238 if (!text_range_valid(r)) {
239 lua_pushnil(L);
240 return;
242 lua_createtable(L, 0, 2);
243 lua_pushstring(L, "start");
244 lua_pushunsigned(L, r->start);
245 lua_settable(L, -3);
246 lua_pushstring(L, "finish");
247 lua_pushunsigned(L, r->end);
248 lua_settable(L, -3);
251 static Filerange getrange(lua_State *L, int index) {
252 Filerange range = text_range_empty();
253 if (lua_istable(L, index)) {
254 lua_getfield(L, index, "start");
255 range.start = luaL_checkunsigned(L, -1);
256 lua_pop(L, 1);
257 lua_getfield(L, index, "finish");
258 range.end = luaL_checkunsigned(L, -1);
259 lua_pop(L, 1);
260 } else {
261 range.start = luaL_checkunsigned(L, index);
262 range.end = range.start + luaL_checkunsigned(L, index+1);
264 return range;
267 static const char *keymapping(Vis *vis, const char *keys, const Arg *arg) {
268 lua_State *L = vis->lua;
269 if (!func_ref_get(L, arg->v))
270 return keys;
271 pcall(vis, L, 0, 0);
272 return keys;
275 static int windows_iter(lua_State *L);
277 static int windows(lua_State *L) {
278 Vis *vis = obj_ref_check(L, 1, "vis");
279 if (!vis) {
280 lua_pushnil(L);
281 return 1;
283 Win **handle = lua_newuserdata(L, sizeof *handle);
284 *handle = vis->windows;
285 lua_pushcclosure(L, windows_iter, 1);
286 return 1;
289 static int windows_iter(lua_State *L) {
290 Win **handle = lua_touserdata(L, lua_upvalueindex(1));
291 if (!*handle)
292 return 0;
293 Win *win = obj_ref_new(L, *handle, "vis.window");
294 if (!win)
295 return 0;
296 *handle = win->next;
297 return 1;
300 static int files_iter(lua_State *L);
302 static int files(lua_State *L) {
303 Vis *vis = obj_ref_check(L, 1, "vis");
304 if (!vis) {
305 lua_pushnil(L);
306 return 1;
308 File **handle = lua_newuserdata(L, sizeof *handle);
309 *handle = vis->files;
310 lua_pushcclosure(L, files_iter, 1);
311 return 1;
314 static int files_iter(lua_State *L) {
315 File **handle = lua_touserdata(L, lua_upvalueindex(1));
316 if (!*handle)
317 return 0;
318 File *file = obj_ref_new(L, *handle, "vis.file");
319 if (!file)
320 return 0;
321 *handle = file->next;
322 return 1;
325 static int command(lua_State *L) {
326 Vis *vis = obj_ref_check(L, 1, "vis");
327 if (!vis) {
328 lua_pushnil(L);
329 return 1;
331 const char *cmd = luaL_checkstring(L, 2);
332 bool ret = vis_cmd(vis, cmd);
333 lua_pushboolean(L, ret);
334 return 1;
337 static int info(lua_State *L) {
338 Vis *vis = obj_ref_check(L, 1, "vis");
339 if (vis) {
340 const char *msg = luaL_checkstring(L, 2);
341 vis_info_show(vis, "%s", msg);
343 lua_pushboolean(L, vis != NULL);
344 return 1;
347 static int message(lua_State *L) {
348 Vis *vis = obj_ref_check(L, 1, "vis");
349 if (vis) {
350 const char *msg = luaL_checkstring(L, 2);
351 vis_message_show(vis, msg);
353 lua_pushboolean(L, vis != NULL);
354 return 1;
357 static int open(lua_State *L) {
358 Vis *vis = obj_ref_check(L, 1, "vis");
359 if (!vis) {
360 lua_pushnil(L);
361 return 1;
363 const char *name = luaL_checkstring(L, 2);
364 File *file = NULL;
365 if (vis_window_new(vis, name))
366 file = obj_ref_new(L, vis->win->file, "vis.file");
367 if (!file)
368 lua_pushnil(L);
369 return 1;
372 static int map(lua_State *L) {
373 Vis *vis = obj_ref_check(L, 1, "vis");
374 if (!vis) {
375 lua_pushnil(L);
376 return 1;
378 KeyBinding *binding = NULL;
379 KeyAction *action = NULL;
381 int mode = luaL_checkint(L, 2);
382 const char *key = luaL_checkstring(L, 3);
384 if (!key || !lua_isfunction(L, 4))
385 goto err;
386 if (!(binding = calloc(1, sizeof *binding)) || !(action = calloc(1, sizeof *action)))
387 goto err;
389 /* store reference to function in the registry */
390 lua_pushvalue(L, 4);
391 const void *func = func_ref_new(L);
392 if (!func)
393 goto err;
395 *action = (KeyAction){
396 .name = NULL,
397 .help = NULL,
398 .func = keymapping,
399 .arg = (const Arg){
400 .v = func,
404 binding->action = action;
406 char *lhs = strdup(key), *next = lhs;
407 while (next) {
408 char tmp;
409 next = (char*)vis_keys_next(vis, next);
410 if (next) {
411 tmp = *next;
412 *next = '\0';
414 vis_mode_unmap(vis, mode, lhs);
415 if (next)
416 *next = tmp;
418 free(lhs);
420 if (!vis_mode_map(vis, mode, key, binding))
421 goto err;
423 lua_pushboolean(L, true);
424 return 1;
425 err:
426 free(binding);
427 free(action);
428 lua_pushboolean(L, false);
429 return 1;
432 static int motion(lua_State *L) {
433 Vis *vis = obj_ref_check(L, 1, "vis");
434 enum VisMotion id = luaL_checkunsigned(L, 2);
435 // TODO handle var args?
436 lua_pushboolean(L, vis && vis_motion(vis, id));
437 return 1;
440 static size_t motion_lua(Vis *vis, Win *win, void *data, size_t pos) {
441 lua_State *L = vis->lua;
442 if (!func_ref_get(L, data) || !obj_ref_new(L, win, "vis.window"))
443 return EPOS;
445 lua_pushunsigned(L, pos);
446 if (pcall(vis, L, 2, 1) != 0)
447 return EPOS;
448 return luaL_checkunsigned(L, -1);
451 static int motion_register(lua_State *L) {
452 int id = -1;
453 const void *func;
454 Vis *vis = obj_ref_check(L, 1, "vis");
455 if (!vis || !lua_isfunction(L, 2) || !(func = func_ref_new(L)))
456 goto err;
457 id = vis_motion_register(vis, 0, (void*)func, motion_lua);
458 err:
459 lua_pushinteger(L, id);
460 return 1;
463 static int textobject(lua_State *L) {
464 Vis *vis = obj_ref_check(L, 1, "vis");
465 enum VisTextObject id = luaL_checkunsigned(L, 2);
466 lua_pushboolean(L, vis && vis_textobject(vis, id));
467 return 1;
471 static Filerange textobject_lua(Vis *vis, Win *win, void *data, size_t pos) {
472 lua_State *L = vis->lua;
473 if (!func_ref_get(L, data) || !obj_ref_new(L, win, "vis.window"))
474 return text_range_empty();
475 lua_pushunsigned(L, pos);
476 if (pcall(vis, L, 2, 2) != 0)
477 return text_range_empty();
478 return text_range_new(luaL_checkunsigned(L, -2), luaL_checkunsigned(L, -1));
481 static int textobject_register(lua_State *L) {
482 int id = -1;
483 const void *func;
484 Vis *vis = obj_ref_check(L, 1, "vis");
485 if (!vis || !lua_isfunction(L, 2) || !(func = func_ref_new(L)))
486 goto err;
487 id = vis_textobject_register(vis, 0, (void*)func, textobject_lua);
488 err:
489 lua_pushinteger(L, id);
490 return 1;
493 static bool command_lua(Vis *vis, Win *win, void *data, bool force, const char *argv[], Cursor *cur, Filerange *range) {
494 lua_State *L = vis->lua;
495 if (!func_ref_get(L, data))
496 return false;
497 lua_newtable(L);
498 for (size_t i = 0; argv[i]; i++) {
499 lua_pushunsigned(L, i);
500 lua_pushstring(L, argv[i]);
501 lua_settable(L, -3);
503 lua_pushboolean(L, force);
504 if (!obj_ref_new(L, win, "vis.window"))
505 return false;
506 if (!cur)
507 cur = view_cursors_primary_get(win->view);
508 if (!obj_ref_new(L, cur, "vis.window.cursor"))
509 return false;
510 pushrange(L, range);
511 if (pcall(vis, L, 5, 1) != 0)
512 return false;
513 return lua_toboolean(L, -1);
516 static int command_register(lua_State *L) {
517 bool ret = false;
518 const void *func;
519 Vis *vis = obj_ref_check(L, 1, "vis");
520 const char *name = luaL_checkstring(L, 2);
521 if (vis && lua_isfunction(L, 3) && (func = func_ref_new(L)))
522 ret = vis_cmd_register(vis, name, (void*)func, command_lua);
523 lua_pushboolean(L, ret);
524 return 1;
527 static int feedkeys(lua_State *L) {
528 Vis *vis = obj_ref_check(L, 1, "vis");
529 const char *keys = luaL_checkstring(L, 2);
530 if (vis)
531 vis_keys_feed(vis, keys);
532 lua_pushboolean(L, vis != NULL);
533 return 1;
536 static int vis_index(lua_State *L) {
537 Vis *vis = obj_ref_check(L, 1, "vis");
538 if (!vis) {
539 lua_pushnil(L);
540 return 1;
543 if (lua_isstring(L, 2)) {
544 const char *key = lua_tostring(L, 2);
545 if (strcmp(key, "win") == 0) {
546 obj_ref_new(L, vis->win, "vis.window");
547 return 1;
550 if (strcmp(key, "mode") == 0) {
551 lua_pushunsigned(L, vis->mode->id);
552 return 1;
555 if (strcmp(key, "MODE_NORMAL") == 0) {
556 lua_pushunsigned(L, VIS_MODE_NORMAL);
557 return 1;
560 if (strcmp(key, "MODE_OPERATOR_PENDING") == 0) {
561 lua_pushunsigned(L, VIS_MODE_OPERATOR_PENDING);
562 return 1;
565 if (strcmp(key, "MODE_VISUAL") == 0) {
566 lua_pushunsigned(L, VIS_MODE_VISUAL);
567 return 1;
570 if (strcmp(key, "MODE_VISUAL_LINE") == 0) {
571 lua_pushunsigned(L, VIS_MODE_VISUAL_LINE);
572 return 1;
575 if (strcmp(key, "MODE_INSERT") == 0) {
576 lua_pushunsigned(L, VIS_MODE_INSERT);
577 return 1;
580 if (strcmp(key, "MODE_REPLACE") == 0) {
581 lua_pushunsigned(L, VIS_MODE_REPLACE);
582 return 1;
586 return index_common(L);
589 static int vis_newindex(lua_State *L) {
590 Vis *vis = obj_ref_check(L, 1, "vis");
591 if (!vis)
592 return 0;
593 return newindex_common(L);
596 static const struct luaL_Reg vis_lua[] = {
597 { "files", files },
598 { "windows", windows },
599 { "command", command },
600 { "info", info },
601 { "message", message },
602 { "open", open },
603 { "map", map },
604 { "motion", motion },
605 { "motion_register", motion_register },
606 { "textobject", textobject },
607 { "textobject_register", textobject_register },
608 { "command_register", command_register },
609 { "feedkeys", feedkeys },
610 { "__index", vis_index },
611 { "__newindex", vis_newindex },
612 { NULL, NULL },
615 static int window_index(lua_State *L) {
616 Win *win = obj_ref_check(L, 1, "vis.window");
617 if (!win) {
618 lua_pushnil(L);
619 return 1;
622 if (lua_isstring(L, 2)) {
623 const char *key = lua_tostring(L, 2);
624 if (strcmp(key, "file") == 0) {
625 obj_ref_new(L, win->file, "vis.file");
626 return 1;
629 if (strcmp(key, "cursor") == 0) {
630 Cursor *cur = view_cursors_primary_get(win->view);
631 obj_ref_new(L, cur, "vis.window.cursor");
632 return 1;
635 if (strcmp(key, "cursors") == 0) {
636 obj_ref_new(L, win->view, "vis.window.cursors");
637 return 1;
640 if (strcmp(key, "syntax") == 0) {
641 const char *syntax = view_syntax_get(win->view);
642 if (syntax)
643 lua_pushstring(L, syntax);
644 else
645 lua_pushnil(L);
646 return 1;
650 return index_common(L);
653 static int window_newindex(lua_State *L) {
654 Win *win = obj_ref_check(L, 1, "vis.window");
655 if (!win)
656 return 0;
657 if (lua_isstring(L, 2)) {
658 const char *key = lua_tostring(L, 2);
659 if (strcmp(key, "syntax") == 0) {
660 const char *syntax = NULL;
661 if (!lua_isnil(L, 3))
662 syntax = luaL_checkstring(L, 3);
663 view_syntax_set(win->view, syntax);
664 return 0;
667 return newindex_common(L);
670 static int window_cursors_iterator_next(lua_State *L) {
671 Cursor **handle = lua_touserdata(L, lua_upvalueindex(1));
672 if (!*handle)
673 return 0;
674 Cursor *cur = obj_ref_new(L, *handle, "vis.window.cursor");
675 if (!cur)
676 return 0;
677 *handle = view_cursors_next(cur);
678 return 1;
681 static int window_cursors_iterator(lua_State *L) {
682 Win *win = obj_ref_check(L, 1, "vis.window");
683 if (!win) {
684 lua_pushnil(L);
685 return 1;
687 Cursor **handle = lua_newuserdata(L, sizeof *handle);
688 *handle = view_cursors(win->view);
689 lua_pushcclosure(L, window_cursors_iterator_next, 1);
690 return 1;
693 static const struct luaL_Reg window_funcs[] = {
694 { "__index", window_index },
695 { "__newindex", window_newindex },
696 { "cursors_iterator", window_cursors_iterator },
697 { NULL, NULL },
700 static int window_cursors_index(lua_State *L) {
701 View *view = obj_ref_check(L, 1, "vis.window.cursors");
702 if (!view)
703 goto err;
704 size_t index = luaL_checkunsigned(L, 2);
705 size_t count = view_cursors_count(view);
706 if (index == 0 || index > count)
707 goto err;
708 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
709 if (!--index) {
710 obj_ref_new(L, c, "vis.window.cursor");
711 return 1;
714 err:
715 lua_pushnil(L);
716 return 1;
719 static int window_cursors_len(lua_State *L) {
720 View *view = obj_ref_check(L, 1, "vis.window.cursors");
721 lua_pushunsigned(L, view ? view_cursors_count(view) : 0);
722 return 1;
725 static const struct luaL_Reg window_cursors_funcs[] = {
726 { "__index", window_cursors_index },
727 { "__len", window_cursors_len },
728 { NULL, NULL },
731 static int window_cursor_index(lua_State *L) {
732 Cursor *cur = obj_ref_check(L, 1, "vis.window.cursor");
733 if (!cur) {
734 lua_pushnil(L);
735 return 1;
738 if (lua_isstring(L, 2)) {
739 const char *key = lua_tostring(L, 2);
740 if (strcmp(key, "pos") == 0) {
741 lua_pushunsigned(L, view_cursors_pos(cur));
742 return 1;
745 if (strcmp(key, "line") == 0) {
746 lua_pushunsigned(L, view_cursors_line(cur));
747 return 1;
750 if (strcmp(key, "col") == 0) {
751 lua_pushunsigned(L, view_cursors_col(cur));
752 return 1;
755 if (strcmp(key, "number") == 0) {
756 lua_pushunsigned(L, view_cursors_number(cur)+1);
757 return 1;
760 if (strcmp(key, "selection") == 0) {
761 Filerange sel = view_cursors_selection_get(cur);
762 pushrange(L, &sel);
763 return 1;
767 return index_common(L);
770 static int window_cursor_newindex(lua_State *L) {
771 Cursor *cur = obj_ref_check(L, 1, "vis.window.cursor");
772 if (!cur)
773 return 0;
774 if (lua_isstring(L, 2)) {
775 const char *key = lua_tostring(L, 2);
776 if (strcmp(key, "pos") == 0) {
777 size_t pos = luaL_checkunsigned(L, 3);
778 view_cursors_to(cur, pos);
779 return 0;
782 if (strcmp(key, "selection") == 0) {
783 Filerange sel = getrange(L, 3);
784 if (text_range_valid(&sel))
785 view_cursors_selection_set(cur, &sel);
786 else
787 view_cursors_selection_clear(cur);
788 return 0;
791 return newindex_common(L);
794 static int window_cursor_to(lua_State *L) {
795 Cursor *cur = obj_ref_check(L, 1, "vis.window.cursor");
796 if (cur) {
797 size_t line = luaL_checkunsigned(L, 2);
798 size_t col = luaL_checkunsigned(L, 3);
799 view_cursors_place(cur, line, col);
801 return 0;
804 static const struct luaL_Reg window_cursor_funcs[] = {
805 { "__index", window_cursor_index },
806 { "__newindex", window_cursor_newindex },
807 { "to", window_cursor_to },
808 { NULL, NULL },
811 static int file_index(lua_State *L) {
812 File *file = obj_ref_check(L, 1, "vis.file");
813 if (!file) {
814 lua_pushnil(L);
815 return 1;
818 if (lua_isstring(L, 2)) {
819 const char *key = lua_tostring(L, 2);
820 if (strcmp(key, "name") == 0) {
821 lua_pushstring(L, file->name);
822 return 1;
825 if (strcmp(key, "lines") == 0) {
826 obj_ref_new(L, file->text, "vis.file.text");
827 return 1;
830 if (strcmp(key, "newlines") == 0) {
831 switch (text_newline_type(file->text)) {
832 case TEXT_NEWLINE_NL:
833 lua_pushstring(L, "nl");
834 break;
835 case TEXT_NEWLINE_CRNL:
836 lua_pushstring(L, "crnl");
837 break;
838 default:
839 lua_pushnil(L);
840 break;
842 return 1;
845 if (strcmp(key, "size") == 0) {
846 lua_pushunsigned(L, text_size(file->text));
847 return 1;
850 if (strcmp(key, "modified") == 0) {
851 lua_pushboolean(L, text_modified(file->text));
852 return 1;
856 return index_common(L);
859 static int file_newindex(lua_State *L) {
860 File *file = obj_ref_check(L, 1, "vis.file");
861 if (!file)
862 return 0;
863 return newindex_common(L);
866 static int file_insert(lua_State *L) {
867 File *file = obj_ref_check(L, 1, "vis.file");
868 if (file) {
869 size_t pos = luaL_checkunsigned(L, 2);
870 size_t len;
871 luaL_checkstring(L, 3);
872 const char *data = lua_tolstring(L, 3, &len);
873 bool ret = text_insert(file->text, pos, data, len);
874 lua_pushboolean(L, ret);
875 } else {
876 lua_pushboolean(L, false);
878 return 1;
881 static int file_delete(lua_State *L) {
882 File *file = obj_ref_check(L, 1, "vis.file");
883 if (file) {
884 Filerange range = getrange(L, 2);
885 lua_pushboolean(L, text_delete_range(file->text, &range));
886 } else {
887 lua_pushboolean(L, false);
889 return 1;
892 static int file_lines_iterator_it(lua_State *L);
894 static int file_lines_iterator(lua_State *L) {
895 /* need to check second parameter first, because obj_ref_check_get
896 * modifies the stack */
897 size_t line = luaL_optunsigned(L, 2, 1);
898 File *file = obj_ref_check_get(L, 1, "vis.file");
899 size_t *pos = lua_newuserdata(L, sizeof *pos);
900 *pos = text_pos_by_lineno(file->text, line);
901 lua_pushcclosure(L, file_lines_iterator_it, 2);
902 return 1;
905 static int file_lines_iterator_it(lua_State *L) {
906 File *file = *(File**)lua_touserdata(L, lua_upvalueindex(1));
907 size_t *start = lua_touserdata(L, lua_upvalueindex(2));
908 if (*start == text_size(file->text))
909 return 0;
910 size_t end = text_line_end(file->text, *start);
911 size_t len = end - *start;
912 char *buf = malloc(len);
913 if (!buf && len)
914 return 0;
915 len = text_bytes_get(file->text, *start, len, buf);
916 lua_pushlstring(L, buf, len);
917 free(buf);
918 *start = text_line_next(file->text, end);
919 return 1;
922 static int file_content(lua_State *L) {
923 File *file = obj_ref_check(L, 1, "vis.file");
924 if (!file)
925 goto err;
926 Filerange range = getrange(L, 2);
927 if (!text_range_valid(&range))
928 goto err;
929 size_t len = text_range_size(&range);
930 char *data = malloc(len);
931 if (!data)
932 goto err;
933 len = text_bytes_get(file->text, range.start, len, data);
934 lua_pushlstring(L, data, len);
935 free(data);
936 return 1;
937 err:
938 lua_pushnil(L);
939 return 1;
942 static const struct luaL_Reg file_funcs[] = {
943 { "__index", file_index },
944 { "__newindex", file_newindex },
945 { "insert", file_insert },
946 { "delete", file_delete },
947 { "lines_iterator", file_lines_iterator },
948 { "content", file_content },
949 { NULL, NULL },
952 static int file_lines_index(lua_State *L) {
953 Text *txt = obj_ref_check(L, 1, "vis.file.text");
954 if (!txt)
955 goto err;
956 size_t line = luaL_checkunsigned(L, 2);
957 size_t start = text_pos_by_lineno(txt, line);
958 size_t end = text_line_end(txt, start);
959 if (start != EPOS && end != EPOS) {
960 size_t size = end - start;
961 char *data = malloc(size);
962 if (!data && size)
963 goto err;
964 size = text_bytes_get(txt, start, size, data);
965 lua_pushlstring(L, data, size);
966 free(data);
967 return 1;
969 err:
970 lua_pushnil(L);
971 return 1;
974 static int file_lines_newindex(lua_State *L) {
975 Text *txt = obj_ref_check(L, 1, "vis.file.text");
976 if (!txt)
977 return 0;
978 size_t line = luaL_checkunsigned(L, 2);
979 size_t size;
980 const char *data = luaL_checklstring(L, 3, &size);
981 if (line == 0) {
982 text_insert(txt, 0, data, size);
983 text_insert_newline(txt, size);
984 return 0;
986 size_t start = text_pos_by_lineno(txt, line);
987 size_t end = text_line_end(txt, start);
988 if (start != EPOS && end != EPOS) {
989 text_delete(txt, start, end - start);
990 text_insert(txt, start, data, size);
991 if (text_size(txt) == start + size)
992 text_insert_newline(txt, text_size(txt));
994 return 0;
997 static int file_lines_len(lua_State *L) {
998 Text *txt = obj_ref_check(L, 1, "vis.file.text");
999 size_t lines = 0;
1000 if (txt) {
1001 char lastchar;
1002 size_t size = text_size(txt);
1003 if (size > 0)
1004 lines = text_lineno_by_pos(txt, size);
1005 if (lines > 1 && text_byte_get(txt, size-1, &lastchar) && lastchar == '\n')
1006 lines--;
1008 lua_pushunsigned(L, lines);
1009 return 1;
1012 static const struct luaL_Reg file_lines_funcs[] = {
1013 { "__index", file_lines_index },
1014 { "__newindex", file_lines_newindex },
1015 { "__len", file_lines_len },
1016 { NULL, NULL },
1019 static void vis_lua_event_get(lua_State *L, const char *name) {
1020 lua_getglobal(L, "vis");
1021 lua_getfield(L, -1, "events");
1022 if (lua_istable(L, -1)) {
1023 lua_getfield(L, -1, name);
1025 lua_remove(L, -2);
1028 static void vis_lua_event_call(Vis *vis, const char *name) {
1029 lua_State *L = vis->lua;
1030 vis_lua_event_get(L, name);
1031 if (lua_isfunction(L, -1))
1032 pcall(vis, L, 0, 0);
1033 lua_pop(L, 1);
1036 static bool vis_lua_path_strip(Vis *vis) {
1037 lua_State *L = vis->lua;
1038 lua_getglobal(L, "package");
1040 for (const char **var = (const char*[]){ "path", "cpath", NULL }; *var; var++) {
1042 lua_getfield(L, -1, *var);
1043 const char *path = lua_tostring(L, -1);
1044 lua_pop(L, 1);
1045 if (!path)
1046 return false;
1048 char *copy = strdup(path), *stripped = calloc(1, strlen(path)+2);
1049 if (!copy || !stripped) {
1050 free(copy);
1051 free(stripped);
1052 return false;
1055 for (char *elem = copy, *stripped_elem = stripped, *next; elem; elem = next) {
1056 if ((next = strstr(elem, ";")))
1057 *next++ = '\0';
1058 if (strstr(elem, "./"))
1059 continue; /* skip relative path entries */
1060 stripped_elem += sprintf(stripped_elem, "%s;", elem);
1063 lua_pushstring(L, stripped);
1064 lua_setfield(L, -2, *var);
1066 free(copy);
1067 free(stripped);
1070 lua_pop(L, 1); /* package */
1071 return true;
1074 bool vis_lua_path_add(Vis *vis, const char *path) {
1075 if (!path)
1076 return false;
1077 lua_State *L = vis->lua;
1078 lua_getglobal(L, "package");
1079 lua_pushstring(L, path);
1080 lua_pushstring(L, "/?.lua;");
1081 lua_pushstring(L, path);
1082 lua_pushstring(L, "/lexers/?.lua;");
1083 lua_getfield(L, -5, "path");
1084 lua_concat(L, 5);
1085 lua_setfield(L, -2, "path");
1086 lua_pop(L, 1); /* package */
1087 return true;
1090 const char *vis_lua_paths_get(Vis *vis) {
1091 lua_State *L = vis->lua;
1092 if (!L)
1093 return NULL;
1094 lua_getglobal(L, "package");
1095 lua_getfield(L, -1, "path");
1096 return lua_tostring(L, -1);
1099 void vis_lua_init(Vis *vis) {
1100 lua_State *L = luaL_newstate();
1101 if (!L)
1102 return;
1103 vis->lua = L;
1104 luaL_openlibs(L);
1106 /* remove any relative paths from lua's default package.path */
1107 vis_lua_path_strip(vis);
1109 /* extends lua's package.path with:
1110 * - $VIS_PATH/{,lexers}
1111 * - {,lexers} relative to the binary location
1112 * - $XDG_CONFIG_HOME/vis/{,lexers} (defaulting to $HOME/.config/vis/{,lexers})
1113 * - /usr/local/share/vis/{,lexers} (or whatever is specified during ./configure)
1114 * - package.path (standard lua search path)
1116 char path[PATH_MAX];
1118 vis_lua_path_add(vis, VIS_PATH);
1120 /* try to get users home directory */
1121 const char *home = getenv("HOME");
1122 if (!home || !*home) {
1123 struct passwd *pw = getpwuid(getuid());
1124 if (pw)
1125 home = pw->pw_dir;
1128 const char *xdg_config = getenv("XDG_CONFIG_HOME");
1129 if (xdg_config) {
1130 snprintf(path, sizeof path, "%s/vis", xdg_config);
1131 vis_lua_path_add(vis, path);
1132 } else if (home && *home) {
1133 snprintf(path, sizeof path, "%s/.config/vis", home);
1134 vis_lua_path_add(vis, path);
1137 ssize_t len = readlink("/proc/self/exe", path, sizeof(path)-1);
1138 if (len > 0) {
1139 path[len] = '\0';
1140 vis_lua_path_add(vis, dirname(path));
1143 vis_lua_path_add(vis, getenv("VIS_PATH"));
1145 /* table in registry to track lifetimes of C objects */
1146 lua_newtable(L);
1147 lua_setfield(L, LUA_REGISTRYINDEX, "vis.objects");
1148 /* table in registry to store references to Lua functions */
1149 lua_newtable(L);
1150 lua_setfield(L, LUA_REGISTRYINDEX, "vis.functions");
1151 /* metatable used to type check user data */
1152 luaL_newmetatable(L, "vis.file");
1153 luaL_setfuncs(L, file_funcs, 0);
1154 luaL_newmetatable(L, "vis.file.text");
1155 luaL_setfuncs(L, file_lines_funcs, 0);
1156 luaL_newmetatable(L, "vis.window");
1157 luaL_setfuncs(L, window_funcs, 0);
1158 luaL_newmetatable(L, "vis.window.cursor");
1159 luaL_setfuncs(L, window_cursor_funcs, 0);
1160 luaL_newmetatable(L, "vis.window.cursors");
1161 luaL_setfuncs(L, window_cursors_funcs, 0);
1162 /* vis module table with up value as the C pointer */
1163 luaL_newmetatable(L, "vis");
1164 luaL_setfuncs(L, vis_lua, 0);
1165 obj_ref_new(L, vis, "vis");
1166 lua_setglobal(L, "vis");
1168 lua_getglobal(L, "require");
1169 lua_pushstring(L, "visrc");
1170 pcall(vis, L, 1, 0);
1173 void vis_lua_start(Vis *vis) {
1174 vis_lua_event_call(vis, "start");
1177 void vis_lua_quit(Vis *vis) {
1178 vis_lua_event_call(vis, "quit");
1179 lua_close(vis->lua);
1180 vis->lua = NULL;
1183 void vis_lua_file_open(Vis *vis, File *file) {
1187 void vis_lua_file_save(Vis *vis, File *file) {
1191 void vis_lua_file_close(Vis *vis, File *file) {
1192 lua_State *L = vis->lua;
1193 vis_lua_event_get(L, "file_close");
1194 if (lua_isfunction(L, -1)) {
1195 obj_ref_new(L, file, "vis.file");
1196 pcall(vis, L, 1, 0);
1198 obj_ref_free(L, file->text);
1199 obj_ref_free(L, file);
1200 lua_pop(L, 1);
1203 void vis_lua_win_open(Vis *vis, Win *win) {
1204 lua_State *L = vis->lua;
1205 vis_lua_event_get(L, "win_open");
1206 if (lua_isfunction(L, -1)) {
1207 obj_ref_new(L, win, "vis.window");
1208 pcall(vis, L, 1, 0);
1210 lua_pop(L, 1);
1213 void vis_lua_win_close(Vis *vis, Win *win) {
1214 lua_State *L = vis->lua;
1215 vis_lua_event_get(L, "win_close");
1216 if (lua_isfunction(L, -1)) {
1217 obj_ref_new(L, win, "vis.window");
1218 pcall(vis, L, 1, 0);
1220 obj_ref_free(L, win->view);
1221 obj_ref_free(L, win);
1222 lua_pop(L, 1);
1225 bool vis_theme_load(Vis *vis, const char *name) {
1226 lua_State *L = vis->lua;
1227 vis_lua_event_get(L, "theme_change");
1228 if (lua_isfunction(L, -1)) {
1229 if (name)
1230 lua_pushstring(L, name);
1231 else
1232 lua_pushnil(L);
1233 pcall(vis, L, 1, 0);
1235 lua_pop(L, 1);
1236 /* package.loaded['themes/'..name] = nil
1237 * require 'themes/'..name */
1238 return true;
1241 #endif