12 #include "text-motions.h"
15 #define VIS_PATH "/usr/local/share/vis"
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; }
35 static void stack_dump_entry(lua_State
*L
, int i
) {
36 int t
= lua_type(L
, i
);
42 printf(lua_toboolean(L
, i
) ? "true" : "false");
44 case LUA_TLIGHTUSERDATA
:
45 printf("lightuserdata(%p)", lua_touserdata(L
, i
));
48 printf("%g", lua_tonumber(L
, i
));
51 printf("`%s'", lua_tostring(L
, i
));
55 lua_pushnil(L
); /* first key */
56 while (lua_next(L
, i
> 0 ? i
: i
- 1)) {
57 stack_dump_entry(L
, -2);
59 stack_dump_entry(L
, -1);
61 lua_pop(L
, 1); /* remove value, keep key */
66 printf("userdata(%p)", lua_touserdata(L
, i
));
68 default: /* other values */
69 printf("%s", lua_typename(L
, t
));
74 static void stack_dump(lua_State
*L
, const char *format
, ...) {
79 int top
= lua_gettop(L
);
80 for (int i
= 1; i
<= top
; i
++) {
82 stack_dump_entry(L
, i
);
91 static int error_function(lua_State
*L
) {
92 Vis
*vis
= lua_touserdata(L
, lua_upvalueindex(1));
94 const char *msg
= lua_tostring(L
, 1);
96 luaL_traceback(L
, L
, msg
, 1);
97 msg
= lua_tolstring(L
, 1, &len
);
98 vis_message_show(vis
, msg
);
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);
108 int ret
= lua_pcall(L
, nargs
, nresults
, msgh
);
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
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
)
123 lua_getfield(L
, LUA_REGISTRYINDEX
, "vis.functions");
124 lua_pushlightuserdata(L
, (void*)addr
);
125 lua_pushvalue(L
, -3);
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
);
137 if (!lua_isfunction(L
, -1)) {
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);
149 lua_setuservalue(L
, -2);
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
);
159 if (lua_isnil(L
, -1)) {
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);
178 /* invalidates an object reference
180 * registry["vis.objects"][addr] = nil
182 static void obj_ref_free(lua_State
*L
, void *addr
) {
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
) {
192 void **handle
= (void**)obj_ref_get(L
, addr
, type
);
194 handle
= obj_new(L
, sizeof(addr
), type
);
195 obj_ref_set(L
, addr
);
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
))
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
);
217 static int index_common(lua_State
*L
) {
218 lua_getmetatable(L
, 1);
221 if (lua_isnil(L
, -1)) {
222 lua_getuservalue(L
, 1);
229 static int newindex_common(lua_State
*L
) {
230 lua_getuservalue(L
, 1);
237 static void pushrange(lua_State
*L
, Filerange
*r
) {
238 if (!text_range_valid(r
)) {
242 lua_createtable(L
, 0, 2);
243 lua_pushstring(L
, "start");
244 lua_pushunsigned(L
, r
->start
);
246 lua_pushstring(L
, "finish");
247 lua_pushunsigned(L
, r
->end
);
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);
257 lua_getfield(L
, index
, "finish");
258 range
.end
= luaL_checkunsigned(L
, -1);
261 range
.start
= luaL_checkunsigned(L
, index
);
262 range
.end
= range
.start
+ luaL_checkunsigned(L
, index
+1);
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
))
275 static int windows_iter(lua_State
*L
);
277 static int windows(lua_State
*L
) {
278 Vis
*vis
= obj_ref_check(L
, 1, "vis");
283 Win
**handle
= lua_newuserdata(L
, sizeof *handle
);
284 *handle
= vis
->windows
;
285 lua_pushcclosure(L
, windows_iter
, 1);
289 static int windows_iter(lua_State
*L
) {
290 Win
**handle
= lua_touserdata(L
, lua_upvalueindex(1));
293 Win
*win
= obj_ref_new(L
, *handle
, "vis.window");
300 static int files_iter(lua_State
*L
);
302 static int files(lua_State
*L
) {
303 Vis
*vis
= obj_ref_check(L
, 1, "vis");
308 File
**handle
= lua_newuserdata(L
, sizeof *handle
);
309 *handle
= vis
->files
;
310 lua_pushcclosure(L
, files_iter
, 1);
314 static int files_iter(lua_State
*L
) {
315 File
**handle
= lua_touserdata(L
, lua_upvalueindex(1));
318 File
*file
= obj_ref_new(L
, *handle
, "vis.file");
321 *handle
= file
->next
;
325 static int command(lua_State
*L
) {
326 Vis
*vis
= obj_ref_check(L
, 1, "vis");
331 const char *cmd
= luaL_checkstring(L
, 2);
332 bool ret
= vis_cmd(vis
, cmd
);
333 lua_pushboolean(L
, ret
);
337 static int info(lua_State
*L
) {
338 Vis
*vis
= obj_ref_check(L
, 1, "vis");
340 const char *msg
= luaL_checkstring(L
, 2);
341 vis_info_show(vis
, "%s", msg
);
343 lua_pushboolean(L
, vis
!= NULL
);
347 static int message(lua_State
*L
) {
348 Vis
*vis
= obj_ref_check(L
, 1, "vis");
350 const char *msg
= luaL_checkstring(L
, 2);
351 vis_message_show(vis
, msg
);
353 lua_pushboolean(L
, vis
!= NULL
);
357 static int open(lua_State
*L
) {
358 Vis
*vis
= obj_ref_check(L
, 1, "vis");
363 const char *name
= luaL_checkstring(L
, 2);
365 if (vis_window_new(vis
, name
))
366 file
= obj_ref_new(L
, vis
->win
->file
, "vis.file");
372 static int map(lua_State
*L
) {
373 Vis
*vis
= obj_ref_check(L
, 1, "vis");
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))
386 if (!(binding
= calloc(1, sizeof *binding
)) || !(action
= calloc(1, sizeof *action
)))
389 /* store reference to function in the registry */
391 const void *func
= func_ref_new(L
);
395 *action
= (KeyAction
){
404 binding
->action
= action
;
406 char *lhs
= strdup(key
), *next
= lhs
;
409 next
= (char*)vis_keys_next(vis
, next
);
414 vis_mode_unmap(vis
, mode
, lhs
);
420 if (!vis_mode_map(vis
, mode
, key
, binding
))
423 lua_pushboolean(L
, true);
428 lua_pushboolean(L
, false);
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
));
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"))
445 lua_pushunsigned(L
, pos
);
446 if (pcall(vis
, L
, 2, 1) != 0)
448 return luaL_checkunsigned(L
, -1);
451 static int motion_register(lua_State
*L
) {
454 Vis
*vis
= obj_ref_check(L
, 1, "vis");
455 if (!vis
|| !lua_isfunction(L
, 2) || !(func
= func_ref_new(L
)))
457 id
= vis_motion_register(vis
, 0, (void*)func
, motion_lua
);
459 lua_pushinteger(L
, id
);
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
));
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
) {
484 Vis
*vis
= obj_ref_check(L
, 1, "vis");
485 if (!vis
|| !lua_isfunction(L
, 2) || !(func
= func_ref_new(L
)))
487 id
= vis_textobject_register(vis
, 0, (void*)func
, textobject_lua
);
489 lua_pushinteger(L
, id
);
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
))
498 for (size_t i
= 0; argv
[i
]; i
++) {
499 lua_pushunsigned(L
, i
);
500 lua_pushstring(L
, argv
[i
]);
503 lua_pushboolean(L
, force
);
504 if (!obj_ref_new(L
, win
, "vis.window"))
507 cur
= view_cursors_primary_get(win
->view
);
508 if (!obj_ref_new(L
, cur
, "vis.window.cursor"))
511 if (pcall(vis
, L
, 5, 1) != 0)
513 return lua_toboolean(L
, -1);
516 static int command_register(lua_State
*L
) {
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
);
527 static int feedkeys(lua_State
*L
) {
528 Vis
*vis
= obj_ref_check(L
, 1, "vis");
529 const char *keys
= luaL_checkstring(L
, 2);
531 vis_keys_feed(vis
, keys
);
532 lua_pushboolean(L
, vis
!= NULL
);
536 static int vis_index(lua_State
*L
) {
537 Vis
*vis
= obj_ref_check(L
, 1, "vis");
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");
550 if (strcmp(key
, "mode") == 0) {
551 lua_pushunsigned(L
, vis
->mode
->id
);
555 if (strcmp(key
, "MODE_NORMAL") == 0) {
556 lua_pushunsigned(L
, VIS_MODE_NORMAL
);
560 if (strcmp(key
, "MODE_OPERATOR_PENDING") == 0) {
561 lua_pushunsigned(L
, VIS_MODE_OPERATOR_PENDING
);
565 if (strcmp(key
, "MODE_VISUAL") == 0) {
566 lua_pushunsigned(L
, VIS_MODE_VISUAL
);
570 if (strcmp(key
, "MODE_VISUAL_LINE") == 0) {
571 lua_pushunsigned(L
, VIS_MODE_VISUAL_LINE
);
575 if (strcmp(key
, "MODE_INSERT") == 0) {
576 lua_pushunsigned(L
, VIS_MODE_INSERT
);
580 if (strcmp(key
, "MODE_REPLACE") == 0) {
581 lua_pushunsigned(L
, VIS_MODE_REPLACE
);
586 return index_common(L
);
589 static int vis_newindex(lua_State
*L
) {
590 Vis
*vis
= obj_ref_check(L
, 1, "vis");
593 return newindex_common(L
);
596 static const struct luaL_Reg vis_lua
[] = {
598 { "windows", windows
},
599 { "command", command
},
601 { "message", message
},
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
},
615 static int window_index(lua_State
*L
) {
616 Win
*win
= obj_ref_check(L
, 1, "vis.window");
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");
629 if (strcmp(key
, "cursor") == 0) {
630 Cursor
*cur
= view_cursors_primary_get(win
->view
);
631 obj_ref_new(L
, cur
, "vis.window.cursor");
635 if (strcmp(key
, "cursors") == 0) {
636 obj_ref_new(L
, win
->view
, "vis.window.cursors");
640 if (strcmp(key
, "syntax") == 0) {
641 const char *syntax
= view_syntax_get(win
->view
);
643 lua_pushstring(L
, syntax
);
650 return index_common(L
);
653 static int window_newindex(lua_State
*L
) {
654 Win
*win
= obj_ref_check(L
, 1, "vis.window");
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
);
667 return newindex_common(L
);
670 static int window_cursors_iterator_next(lua_State
*L
) {
671 Cursor
**handle
= lua_touserdata(L
, lua_upvalueindex(1));
674 Cursor
*cur
= obj_ref_new(L
, *handle
, "vis.window.cursor");
677 *handle
= view_cursors_next(cur
);
681 static int window_cursors_iterator(lua_State
*L
) {
682 Win
*win
= obj_ref_check(L
, 1, "vis.window");
687 Cursor
**handle
= lua_newuserdata(L
, sizeof *handle
);
688 *handle
= view_cursors(win
->view
);
689 lua_pushcclosure(L
, window_cursors_iterator_next
, 1);
693 static const struct luaL_Reg window_funcs
[] = {
694 { "__index", window_index
},
695 { "__newindex", window_newindex
},
696 { "cursors_iterator", window_cursors_iterator
},
700 static int window_cursors_index(lua_State
*L
) {
701 View
*view
= obj_ref_check(L
, 1, "vis.window.cursors");
704 size_t index
= luaL_checkunsigned(L
, 2);
705 size_t count
= view_cursors_count(view
);
706 if (index
== 0 || index
> count
)
708 for (Cursor
*c
= view_cursors(view
); c
; c
= view_cursors_next(c
)) {
710 obj_ref_new(L
, c
, "vis.window.cursor");
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);
725 static const struct luaL_Reg window_cursors_funcs
[] = {
726 { "__index", window_cursors_index
},
727 { "__len", window_cursors_len
},
731 static int window_cursor_index(lua_State
*L
) {
732 Cursor
*cur
= obj_ref_check(L
, 1, "vis.window.cursor");
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
));
745 if (strcmp(key
, "line") == 0) {
746 lua_pushunsigned(L
, view_cursors_line(cur
));
750 if (strcmp(key
, "col") == 0) {
751 lua_pushunsigned(L
, view_cursors_col(cur
));
755 if (strcmp(key
, "number") == 0) {
756 lua_pushunsigned(L
, view_cursors_number(cur
)+1);
760 if (strcmp(key
, "selection") == 0) {
761 Filerange sel
= view_cursors_selection_get(cur
);
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");
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
);
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
);
787 view_cursors_selection_clear(cur
);
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");
797 size_t line
= luaL_checkunsigned(L
, 2);
798 size_t col
= luaL_checkunsigned(L
, 3);
799 view_cursors_place(cur
, line
, col
);
804 static const struct luaL_Reg window_cursor_funcs
[] = {
805 { "__index", window_cursor_index
},
806 { "__newindex", window_cursor_newindex
},
807 { "to", window_cursor_to
},
811 static int file_index(lua_State
*L
) {
812 File
*file
= obj_ref_check(L
, 1, "vis.file");
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
);
825 if (strcmp(key
, "lines") == 0) {
826 obj_ref_new(L
, file
->text
, "vis.file.text");
830 if (strcmp(key
, "newlines") == 0) {
831 switch (text_newline_type(file
->text
)) {
832 case TEXT_NEWLINE_NL
:
833 lua_pushstring(L
, "nl");
835 case TEXT_NEWLINE_CRNL
:
836 lua_pushstring(L
, "crnl");
845 if (strcmp(key
, "size") == 0) {
846 lua_pushunsigned(L
, text_size(file
->text
));
850 if (strcmp(key
, "modified") == 0) {
851 lua_pushboolean(L
, text_modified(file
->text
));
856 return index_common(L
);
859 static int file_newindex(lua_State
*L
) {
860 File
*file
= obj_ref_check(L
, 1, "vis.file");
863 return newindex_common(L
);
866 static int file_insert(lua_State
*L
) {
867 File
*file
= obj_ref_check(L
, 1, "vis.file");
869 size_t pos
= luaL_checkunsigned(L
, 2);
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
);
876 lua_pushboolean(L
, false);
881 static int file_delete(lua_State
*L
) {
882 File
*file
= obj_ref_check(L
, 1, "vis.file");
884 Filerange range
= getrange(L
, 2);
885 lua_pushboolean(L
, text_delete_range(file
->text
, &range
));
887 lua_pushboolean(L
, false);
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);
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
))
910 size_t end
= text_line_end(file
->text
, *start
);
911 size_t len
= end
- *start
;
912 char *buf
= malloc(len
);
915 len
= text_bytes_get(file
->text
, *start
, len
, buf
);
916 lua_pushlstring(L
, buf
, len
);
918 *start
= text_line_next(file
->text
, end
);
922 static int file_content(lua_State
*L
) {
923 File
*file
= obj_ref_check(L
, 1, "vis.file");
926 Filerange range
= getrange(L
, 2);
927 if (!text_range_valid(&range
))
929 size_t len
= text_range_size(&range
);
930 char *data
= malloc(len
);
933 len
= text_bytes_get(file
->text
, range
.start
, len
, data
);
934 lua_pushlstring(L
, data
, len
);
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
},
952 static int file_lines_index(lua_State
*L
) {
953 Text
*txt
= obj_ref_check(L
, 1, "vis.file.text");
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
);
964 size
= text_bytes_get(txt
, start
, size
, data
);
965 lua_pushlstring(L
, data
, size
);
974 static int file_lines_newindex(lua_State
*L
) {
975 Text
*txt
= obj_ref_check(L
, 1, "vis.file.text");
978 size_t line
= luaL_checkunsigned(L
, 2);
980 const char *data
= luaL_checklstring(L
, 3, &size
);
982 text_insert(txt
, 0, data
, size
);
983 text_insert_newline(txt
, size
);
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
));
997 static int file_lines_len(lua_State
*L
) {
998 Text
*txt
= obj_ref_check(L
, 1, "vis.file.text");
1002 size_t size
= text_size(txt
);
1004 lines
= text_lineno_by_pos(txt
, size
);
1005 if (lines
> 1 && text_byte_get(txt
, size
-1, &lastchar
) && lastchar
== '\n')
1008 lua_pushunsigned(L
, lines
);
1012 static const struct luaL_Reg file_lines_funcs
[] = {
1013 { "__index", file_lines_index
},
1014 { "__newindex", file_lines_newindex
},
1015 { "__len", file_lines_len
},
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
);
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);
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);
1048 char *copy
= strdup(path
), *stripped
= calloc(1, strlen(path
)+2);
1049 if (!copy
|| !stripped
) {
1055 for (char *elem
= copy
, *stripped_elem
= stripped
, *next
; elem
; elem
= next
) {
1056 if ((next
= strstr(elem
, ";")))
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
);
1070 lua_pop(L
, 1); /* package */
1074 bool vis_lua_path_add(Vis
*vis
, const char *path
) {
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");
1085 lua_setfield(L
, -2, "path");
1086 lua_pop(L
, 1); /* package */
1090 const char *vis_lua_paths_get(Vis
*vis
) {
1091 lua_State
*L
= vis
->lua
;
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();
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());
1128 const char *xdg_config
= getenv("XDG_CONFIG_HOME");
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);
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 */
1147 lua_setfield(L
, LUA_REGISTRYINDEX
, "vis.objects");
1148 /* table in registry to store references to Lua functions */
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
);
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
);
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);
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
);
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)) {
1230 lua_pushstring(L
, name
);
1233 pcall(vis
, L
, 1, 0);
1236 /* package.loaded['themes/'..name] = nil
1237 * require 'themes/'..name */