9 #include "text-motions.h"
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; }
25 static void stack_dump_entry(lua_State
*L
, int i
) {
26 int t
= lua_type(L
, i
);
32 printf(lua_toboolean(L
, i
) ? "true" : "false");
34 case LUA_TLIGHTUSERDATA
:
35 printf("lightuserdata(%p)", lua_touserdata(L
, i
));
38 printf("%g", lua_tonumber(L
, i
));
41 printf("`%s'", lua_tostring(L
, i
));
45 lua_pushnil(L
); /* first key */
46 while (lua_next(L
, i
> 0 ? i
: i
- 1)) {
47 stack_dump_entry(L
, -2);
49 stack_dump_entry(L
, -1);
51 lua_pop(L
, 1); /* remove value, keep key */
56 printf("userdata(%p)", lua_touserdata(L
, i
));
58 default: /* other values */
59 printf("%s", lua_typename(L
, t
));
64 static void stack_dump(lua_State
*L
, const char *format
, ...) {
69 int top
= lua_gettop(L
);
70 for (int i
= 1; i
<= top
; i
++) {
72 stack_dump_entry(L
, i
);
81 static void *obj_new(lua_State
*L
, size_t size
, const char *type
) {
82 void *obj
= lua_newuserdata(L
, size
);
83 luaL_getmetatable(L
, type
);
84 lua_setmetatable(L
, -2);
86 lua_setuservalue(L
, -2);
90 /* returns registry["vis.objects"][addr] if it is of correct type */
91 static void *obj_ref_get(lua_State
*L
, void *addr
, const char *type
) {
92 lua_getfield(L
, LUA_REGISTRYINDEX
, "vis.objects");
93 lua_pushlightuserdata(L
, addr
);
96 if (lua_isnil(L
, -1)) {
100 return luaL_checkudata(L
, -1, type
);
103 /* expects a userdatum at the top of the stack and sets
105 * registry["vis.objects"][addr] = userdata
107 static void obj_ref_set(lua_State
*L
, void *addr
) {
108 lua_getfield(L
, LUA_REGISTRYINDEX
, "vis.objects");
109 lua_pushlightuserdata(L
, addr
);
110 lua_pushvalue(L
, -3);
115 /* invalidates an object reference
117 * registry["vis.objects"][addr] = nil
119 static void obj_ref_free(lua_State
*L
, void *addr
) {
121 obj_ref_set(L
, addr
);
124 /* creates a new object reference of given type if it does not
125 * already exist in the registry */
126 static void *obj_ref_new(lua_State
*L
, void *addr
, const char *type
) {
129 void **handle
= (void**)obj_ref_get(L
, addr
, type
);
131 handle
= obj_new(L
, sizeof(addr
), type
);
132 obj_ref_set(L
, addr
);
138 /* retrieve object stored in reference at stack location `idx' */
139 static void *obj_ref_check_get(lua_State
*L
, int idx
, const char *type
) {
140 void **addr
= luaL_checkudata(L
, idx
, type
);
141 if (!obj_ref_get(L
, *addr
, type
))
146 /* (type) check validity of object reference at stack location `idx' */
147 static void *obj_ref_check(lua_State
*L
, int idx
, const char *type
) {
148 void *obj
= obj_ref_check_get(L
, idx
, type
);
154 static int index_common(lua_State
*L
) {
155 lua_getmetatable(L
, 1);
158 if (lua_isnil(L
, -1)) {
159 lua_getuservalue(L
, 1);
166 static int newindex_common(lua_State
*L
) {
167 lua_getuservalue(L
, 1);
174 static int windows_iter(lua_State
*L
);
176 static int windows(lua_State
*L
) {
177 Vis
*vis
= lua_touserdata(L
, lua_upvalueindex(1));
178 Win
**handle
= lua_newuserdata(L
, sizeof *handle
);
179 *handle
= vis
->windows
;
180 lua_pushcclosure(L
, windows_iter
, 1);
184 static int windows_iter(lua_State
*L
) {
185 Win
**handle
= lua_touserdata(L
, lua_upvalueindex(1));
188 Win
*win
= obj_ref_new(L
, *handle
, "vis.window");
195 static int files_iter(lua_State
*L
);
197 static int files(lua_State
*L
) {
198 Vis
*vis
= lua_touserdata(L
, lua_upvalueindex(1));
199 File
**handle
= lua_newuserdata(L
, sizeof *handle
);
200 *handle
= vis
->files
;
201 lua_pushcclosure(L
, files_iter
, 1);
205 static int files_iter(lua_State
*L
) {
206 File
**handle
= lua_touserdata(L
, lua_upvalueindex(1));
209 File
*file
= obj_ref_new(L
, *handle
, "vis.file");
212 *handle
= file
->next
;
216 static int command(lua_State
*L
) {
217 Vis
*vis
= lua_touserdata(L
, lua_upvalueindex(1));
218 const char *cmd
= luaL_checkstring(L
, 1);
219 bool ret
= vis_cmd(vis
, cmd
);
220 lua_pushboolean(L
, ret
);
224 static int info(lua_State
*L
) {
225 Vis
*vis
= lua_touserdata(L
, lua_upvalueindex(1));
226 const char *msg
= luaL_checkstring(L
, 1);
227 vis_info_show(vis
, "%s", msg
);
231 static int open(lua_State
*L
) {
232 Vis
*vis
= lua_touserdata(L
, lua_upvalueindex(1));
233 const char *name
= luaL_checkstring(L
, 1);
235 if (vis_window_new(vis
, name
))
236 file
= obj_ref_new(L
, vis
->win
->file
, "vis.file");
242 static const struct luaL_Reg vis_lua
[] = {
244 { "windows", windows
},
245 { "command", command
},
251 static int window_index(lua_State
*L
) {
252 Win
*win
= obj_ref_check(L
, 1, "vis.window");
258 if (lua_isstring(L
, 2)) {
259 const char *key
= lua_tostring(L
, 2);
260 if (strcmp(key
, "file") == 0) {
261 obj_ref_new(L
, win
->file
, "vis.file");
265 if (strcmp(key
, "cursor") == 0) {
266 obj_ref_new(L
, win
->view
, "vis.window.cursor");
271 return index_common(L
);
274 static int window_newindex(lua_State
*L
) {
275 Win
*win
= obj_ref_check(L
, 1, "vis.window");
278 return newindex_common(L
);
281 static const struct luaL_Reg window_funcs
[] = {
282 { "__index", window_index
},
283 { "__newindex", window_newindex
},
287 static int window_cursor_index(lua_State
*L
) {
288 View
*view
= obj_ref_check(L
, 1, "vis.window.cursor");
294 if (lua_isstring(L
, 2)) {
295 const char *key
= lua_tostring(L
, 2);
296 if (strcmp(key
, "pos") == 0) {
297 lua_pushunsigned(L
, view_cursor_get(view
));
301 if (strcmp(key
, "line") == 0) {
302 lua_pushunsigned(L
, view_cursor_getpos(view
).line
);
306 if (strcmp(key
, "col") == 0) {
307 lua_pushunsigned(L
, view_cursor_getpos(view
).col
);
312 return index_common(L
);
315 static int window_cursor_newindex(lua_State
*L
) {
316 View
*view
= obj_ref_check(L
, 1, "vis.window.cursor");
319 if (lua_isstring(L
, 2)) {
320 const char *key
= lua_tostring(L
, 2);
321 if (strcmp(key
, "pos") == 0) {
322 size_t pos
= luaL_checkunsigned(L
, 3);
323 view_cursor_to(view
, pos
);
327 return newindex_common(L
);
330 static const struct luaL_Reg window_cursor_funcs
[] = {
331 { "__index", window_cursor_index
},
332 { "__newindex", window_cursor_newindex
},
336 static int file_index(lua_State
*L
) {
337 File
*file
= obj_ref_check(L
, 1, "vis.file");
343 if (lua_isstring(L
, 2)) {
344 const char *key
= lua_tostring(L
, 2);
345 if (strcmp(key
, "name") == 0) {
346 lua_pushstring(L
, file
->name
);
349 if (strcmp(key
, "lines") == 0) {
350 obj_ref_new(L
, file
->text
, "vis.file.text");
355 return index_common(L
);
358 static int file_newindex(lua_State
*L
) {
359 File
*file
= obj_ref_check(L
, 1, "vis.file");
362 return newindex_common(L
);
365 static int file_insert(lua_State
*L
) {
366 File
*file
= obj_ref_check(L
, 1, "vis.file");
368 size_t pos
= luaL_checkunsigned(L
, 2);
370 luaL_checkstring(L
, 3);
371 const char *data
= lua_tolstring(L
, 3, &len
);
372 bool ret
= text_insert(file
->text
, pos
, data
, len
);
373 lua_pushboolean(L
, ret
);
375 lua_pushboolean(L
, false);
380 static int file_delete(lua_State
*L
) {
381 File
*file
= obj_ref_check(L
, 1, "vis.file");
383 size_t pos
= luaL_checkunsigned(L
, 2);
384 size_t len
= luaL_checkunsigned(L
, 3);
385 bool ret
= text_delete(file
->text
, pos
, len
);
386 lua_pushboolean(L
, ret
);
388 lua_pushboolean(L
, false);
393 static int file_lines_iterator_it(lua_State
*L
);
395 static int file_lines_iterator(lua_State
*L
) {
396 /* need to check second parameter first, because obj_ref_check_get
397 * modifies the stack */
398 size_t line
= luaL_optunsigned(L
, 2, 1);
399 File
*file
= obj_ref_check_get(L
, 1, "vis.file");
400 size_t *pos
= lua_newuserdata(L
, sizeof *pos
);
401 *pos
= text_pos_by_lineno(file
->text
, line
);
402 lua_pushcclosure(L
, file_lines_iterator_it
, 2);
406 static int file_lines_iterator_it(lua_State
*L
) {
407 File
*file
= *(File
**)lua_touserdata(L
, lua_upvalueindex(1));
408 size_t *start
= lua_touserdata(L
, lua_upvalueindex(2));
409 if (*start
== text_size(file
->text
))
411 size_t end
= text_line_end(file
->text
, *start
);
412 size_t len
= end
- *start
;
413 char *buf
= malloc(len
);
416 len
= text_bytes_get(file
->text
, *start
, len
, buf
);
417 lua_pushlstring(L
, buf
, len
);
419 *start
= text_line_next(file
->text
, end
);
423 static const struct luaL_Reg file_funcs
[] = {
424 { "__index", file_index
},
425 { "__newindex", file_newindex
},
426 { "insert", file_insert
},
427 { "delete", file_delete
},
428 { "lines_iterator", file_lines_iterator
},
432 static int file_lines_index(lua_State
*L
) {
433 Text
*txt
= obj_ref_check(L
, 1, "vis.file.text");
436 size_t line
= luaL_checkunsigned(L
, 2);
437 size_t start
= text_pos_by_lineno(txt
, line
);
438 size_t end
= text_line_end(txt
, start
);
439 if (start
!= EPOS
&& end
!= EPOS
) {
440 size_t size
= end
- start
;
441 char *data
= malloc(size
);
444 size
= text_bytes_get(txt
, start
, size
, data
);
445 lua_pushlstring(L
, data
, size
);
454 static int file_lines_newindex(lua_State
*L
) {
455 Text
*txt
= obj_ref_check(L
, 1, "vis.file.text");
458 size_t line
= luaL_checkunsigned(L
, 2);
460 const char *data
= luaL_checklstring(L
, 3, &size
);
462 text_insert(txt
, 0, data
, size
);
463 text_insert_newline(txt
, size
);
466 size_t start
= text_pos_by_lineno(txt
, line
);
467 size_t end
= text_line_end(txt
, start
);
468 if (start
!= EPOS
&& end
!= EPOS
) {
469 text_delete(txt
, start
, end
- start
);
470 text_insert(txt
, start
, data
, size
);
471 if (text_size(txt
) == start
+ size
)
472 text_insert_newline(txt
, text_size(txt
));
477 static int file_lines_len(lua_State
*L
) {
478 Text
*txt
= obj_ref_check(L
, 1, "vis.file.text");
482 size_t size
= text_size(txt
);
484 lines
= text_lineno_by_pos(txt
, size
);
485 if (lines
> 1 && text_byte_get(txt
, size
-1, &lastchar
) && lastchar
== '\n')
488 lua_pushunsigned(L
, lines
);
492 static const struct luaL_Reg file_lines_funcs
[] = {
493 { "__index", file_lines_index
},
494 { "__newindex", file_lines_newindex
},
495 { "__len", file_lines_len
},
499 static void vis_lua_event(Vis
*vis
, const char *name
) {
500 lua_State
*L
= vis
->lua
;
501 lua_getglobal(L
, "vis");
502 lua_getfield(L
, -1, "events");
503 if (lua_istable(L
, -1)) {
504 lua_getfield(L
, -1, name
);
509 void vis_lua_start(Vis
*vis
) {
510 lua_State
*L
= luaL_newstate();
517 /* extends lua's package.path with:
518 * - $VIS_PATH/{,lexers}
519 * - $XDG_CONFIG_HOME/vis/{,lexers} (defaulting to $HOME/.config/vis/{,lexers})
520 * - /usr/local/share/vis/{,lexers}
521 * - /usr/share/vis/{,lexers}
522 * - package.path (standard lua search path)
525 lua_getglobal(L
, "package");
527 const char *vis_path
= getenv("VIS_PATH");
529 lua_pushstring(L
, vis_path
);
530 lua_pushstring(L
, "/?.lua;");
531 lua_pushstring(L
, vis_path
);
532 lua_pushstring(L
, "/lexers/?.lua;");
537 /* try to get users home directory */
538 const char *home
= getenv("HOME");
539 if (!home
|| !*home
) {
540 struct passwd
*pw
= getpwuid(getuid());
545 const char *xdg_config
= getenv("XDG_CONFIG_HOME");
547 lua_pushstring(L
, xdg_config
);
548 lua_pushstring(L
, "/vis/?.lua;");
549 lua_pushstring(L
, xdg_config
);
550 lua_pushstring(L
, "/vis/lexers/?.lua;");
553 } else if (home
&& *home
) {
554 lua_pushstring(L
, home
);
555 lua_pushstring(L
, "/.config/vis/?.lua;");
556 lua_pushstring(L
, home
);
557 lua_pushstring(L
, "/.config/vis/lexers/?.lua;");
562 lua_pushstring(L
, "/usr/local/share/vis/?.lua;/usr/local/share/vis/lexers/?.lua;");
563 lua_pushstring(L
, "/usr/share/vis/?.lua;/usr/share/vis/lexers/?.lua;");
564 lua_getfield(L
, -paths
, "path");
565 lua_concat(L
, paths
);
566 lua_setfield(L
, -2, "path");
567 lua_pop(L
, 1); /* package */
569 /* table in registry to track lifetimes of C objects */
571 lua_setfield(L
, LUA_REGISTRYINDEX
, "vis.objects");
572 /* metatable used to type check user data */
573 luaL_newmetatable(L
, "vis.file");
574 luaL_setfuncs(L
, file_funcs
, 0);
575 luaL_newmetatable(L
, "vis.file.text");
576 luaL_setfuncs(L
, file_lines_funcs
, 0);
577 luaL_newmetatable(L
, "vis.window");
578 luaL_setfuncs(L
, window_funcs
, 0);
579 luaL_newmetatable(L
, "vis.window.cursor");
580 luaL_setfuncs(L
, window_cursor_funcs
, 0);
581 /* vis module table with up value as the C pointer */
582 luaL_newlibtable(L
, vis_lua
);
583 lua_pushlightuserdata(L
, vis
);
584 luaL_setfuncs(L
, vis_lua
, 1);
585 lua_setglobal(L
, "vis");
587 lua_getglobal(L
, "require");
588 lua_pushstring(L
, "visrc");
589 lua_pcall(L
, 1, 0, 0);
590 vis_lua_event(vis
, "start");
591 if (lua_isfunction(L
, -1))
592 lua_pcall(L
, 0, 0, 0);
596 void vis_lua_quit(Vis
*vis
) {
597 lua_State
*L
= vis
->lua
;
600 vis_lua_event(vis
, "quit");
601 if (lua_isfunction(L
, -1))
602 lua_pcall(L
, 0, 0, 0);
607 void vis_lua_file_open(Vis
*vis
, File
*file
) {
611 void vis_lua_file_save(Vis
*vis
, File
*file
) {
615 void vis_lua_file_close(Vis
*vis
, File
*file
) {
616 lua_State
*L
= vis
->lua
;
617 vis_lua_event(vis
, "file_close");
618 if (lua_isfunction(L
, -1)) {
619 obj_ref_new(L
, file
, "vis.file");
620 lua_pcall(L
, 1, 0, 0);
622 obj_ref_free(L
, file
->text
);
623 obj_ref_free(L
, file
);
627 void vis_lua_win_open(Vis
*vis
, Win
*win
) {
628 lua_State
*L
= vis
->lua
;
629 vis_lua_event(vis
, "win_open");
630 if (lua_isfunction(L
, -1)) {
631 obj_ref_new(L
, win
, "vis.window");
632 lua_pcall(L
, 1, 0, 0);
637 void vis_lua_win_close(Vis
*vis
, Win
*win
) {
638 lua_State
*L
= vis
->lua
;
639 vis_lua_event(vis
, "win_close");
640 if (lua_isfunction(L
, -1)) {
641 obj_ref_new(L
, win
, "vis.window");
642 lua_pcall(L
, 1, 0, 0);
644 obj_ref_free(L
, win
->view
);
645 obj_ref_free(L
, win
);
649 bool vis_theme_load(Vis
*vis
, const char *name
) {
650 lua_State
*L
= vis
->lua
;
653 /* package.loaded['themes/'..name] = nil
654 * require 'themes/'..name */
655 lua_pushstring(L
, "themes/");
656 lua_pushstring(L
, name
);
658 lua_getglobal(L
, "package");
659 lua_getfield(L
, -1, "loaded");
660 lua_pushvalue(L
, -3);
664 lua_getglobal(L
, "require");
665 lua_pushvalue(L
, -2);
666 if (lua_pcall(L
, 1, 0, 0))
668 for (Win
*win
= vis
->windows
; win
; win
= win
->next
)
669 view_syntax_set(win
->view
, view_syntax_get(win
->view
));