vis: use strncpy to copy into fixed sized buffer
[vis.git] / vis-lua.c
blob8c5e7e6ed85b4f95e2a5d41fc454bb47d7fe0eb2
1 /***
2 * Lua Extension API for the [Vis Editor](https://github.com/martanne/vis).
4 * *WARNING:* there is no stability guarantee at this time, the API might
5 * change without notice!
7 * This document might be out of date, run `make luadoc` to regenerate it.
9 * @module vis
10 * @author Marc André Tanner
11 * @license ISC
12 * @release RELEASE
14 #include <stddef.h>
15 #include <stdarg.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <limits.h>
19 #include <unistd.h>
20 #include <libgen.h>
21 #include <sys/types.h>
22 #include <pwd.h>
24 #include "vis-lua.h"
25 #include "vis-core.h"
26 #include "text-motions.h"
27 #include "util.h"
29 #ifndef VIS_PATH
30 #define VIS_PATH "/usr/local/share/vis"
31 #endif
33 #define VIS_LUA_TYPE_VIS "vis"
34 #define VIS_LUA_TYPE_FILE "file"
35 #define VIS_LUA_TYPE_TEXT "text"
36 #define VIS_LUA_TYPE_MARK "mark"
37 #define VIS_LUA_TYPE_MARKS "marks"
38 #define VIS_LUA_TYPE_WINDOW "window"
39 #define VIS_LUA_TYPE_SELECTION "selection"
40 #define VIS_LUA_TYPE_SELECTIONS "selections"
41 #define VIS_LUA_TYPE_UI "ui"
42 #define VIS_LUA_TYPE_REGISTERS "registers"
43 #define VIS_LUA_TYPE_KEYACTION "keyaction"
45 #ifndef DEBUG_LUA
46 #define DEBUG_LUA 0
47 #endif
49 #if DEBUG_LUA
50 #define debug(...) do { printf(__VA_ARGS__); fflush(stdout); } while (0)
51 #else
52 #define debug(...) do { } while (0)
53 #endif
55 static void window_status_update(Vis *vis, Win *win) {
56 char left_parts[4][255] = { "", "", "", "" };
57 char right_parts[4][32] = { "", "", "", "" };
58 char left[sizeof(left_parts)+LENGTH(left_parts)*8];
59 char right[sizeof(right_parts)+LENGTH(right_parts)*8];
60 char status[sizeof(left)+sizeof(right)+1];
61 size_t left_count = 0;
62 size_t right_count = 0;
64 View *view = win->view;
65 File *file = win->file;
66 Text *txt = file->text;
67 int width = vis_window_width_get(win);
68 enum UiOption options = view_options_get(view);
69 bool focused = vis->win == win;
70 const char *filename = file_name_get(file);
71 const char *mode = vis->mode->status;
73 if (focused && mode)
74 strcpy(left_parts[left_count++], mode);
76 snprintf(left_parts[left_count], sizeof(left_parts[left_count])-1, "%s%s%s",
77 filename ? filename : "[No Name]",
78 text_modified(txt) ? " [+]" : "",
79 vis_macro_recording(vis) ? " @": "");
80 left_count++;
82 int sel_count = view_selections_count(view);
83 if (sel_count > 1) {
84 Selection *s = view_selections_primary_get(view);
85 int sel_number = view_selections_number(s) + 1;
86 snprintf(right_parts[right_count], sizeof(right_parts[right_count])-1,
87 "%d/%d", sel_number, sel_count);
88 right_count++;
91 size_t size = text_size(txt);
92 size_t pos = view_cursor_get(view);
93 size_t percent = 0;
94 if (size > 0) {
95 double tmp = ((double)pos/(double)size)*100;
96 percent = (size_t)(tmp+1);
98 snprintf(right_parts[right_count], sizeof(right_parts[right_count])-1,
99 "%zu%%", percent);
100 right_count++;
102 if (!(options & UI_OPTION_LARGE_FILE)) {
103 Selection *sel = view_selections_primary_get(win->view);
104 size_t line = view_cursors_line(sel);
105 size_t col = view_cursors_col(sel);
106 if (col > UI_LARGE_FILE_LINE_SIZE) {
107 options |= UI_OPTION_LARGE_FILE;
108 view_options_set(win->view, options);
110 snprintf(right_parts[right_count], sizeof(right_parts[right_count])-1,
111 "%zu, %zu", line, col);
112 right_count++;
115 int left_len = snprintf(left, sizeof(left)-1, " %s%s%s%s%s%s%s",
116 left_parts[0],
117 left_parts[1][0] ? " » " : "",
118 left_parts[1],
119 left_parts[2][0] ? " » " : "",
120 left_parts[2],
121 left_parts[3][0] ? " » " : "",
122 left_parts[3]);
124 int right_len = snprintf(right, sizeof(right)-1, "%s%s%s%s%s%s%s ",
125 right_parts[0],
126 right_parts[1][0] ? " « " : "",
127 right_parts[1],
128 right_parts[2][0] ? " « " : "",
129 right_parts[2],
130 right_parts[3][0] ? " « " : "",
131 right_parts[3]);
133 if (left_len < 0 || right_len < 0)
134 return;
135 int left_width = text_string_width(left, left_len);
136 int right_width = text_string_width(right, right_len);
138 int spaces = width - left_width - right_width;
139 if (spaces < 1)
140 spaces = 1;
142 snprintf(status, sizeof(status)-1, "%s%*s%s", left, spaces, " ", right);
143 vis_window_status(win, status);
146 #if !CONFIG_LUA
148 bool vis_lua_path_add(Vis *vis, const char *path) { return true; }
149 bool vis_lua_paths_get(Vis *vis, char **lpath, char **cpath) { return false; }
150 void vis_lua_init(Vis *vis) { }
151 void vis_lua_start(Vis *vis) { }
152 void vis_lua_quit(Vis *vis) { }
153 void vis_lua_file_open(Vis *vis, File *file) { }
154 bool vis_lua_file_save_pre(Vis *vis, File *file, const char *path) { return true; }
155 void vis_lua_file_save_post(Vis *vis, File *file, const char *path) { }
156 void vis_lua_file_close(Vis *vis, File *file) { }
157 void vis_lua_win_open(Vis *vis, Win *win) { }
158 void vis_lua_win_close(Vis *vis, Win *win) { }
159 void vis_lua_win_highlight(Vis *vis, Win *win) { }
160 void vis_lua_win_status(Vis *vis, Win *win) { window_status_update(vis, win); }
162 #else
164 #if DEBUG_LUA
165 static void stack_dump_entry(lua_State *L, int i) {
166 int t = lua_type(L, i);
167 switch (t) {
168 case LUA_TNIL:
169 printf("nil");
170 break;
171 case LUA_TBOOLEAN:
172 printf(lua_toboolean(L, i) ? "true" : "false");
173 break;
174 case LUA_TLIGHTUSERDATA:
175 printf("lightuserdata(%p)", lua_touserdata(L, i));
176 break;
177 case LUA_TNUMBER:
178 printf("%g", lua_tonumber(L, i));
179 break;
180 case LUA_TSTRING:
181 printf("`%s'", lua_tostring(L, i));
182 break;
183 case LUA_TTABLE:
184 printf("table[");
185 lua_pushnil(L); /* first key */
186 while (lua_next(L, i > 0 ? i : i - 1)) {
187 stack_dump_entry(L, -2);
188 printf("=");
189 stack_dump_entry(L, -1);
190 printf(",");
191 lua_pop(L, 1); /* remove value, keep key */
193 printf("]");
194 break;
195 case LUA_TUSERDATA:
196 printf("userdata(%p)", lua_touserdata(L, i));
197 break;
198 default: /* other values */
199 printf("%s", lua_typename(L, t));
200 break;
204 static void stack_dump(lua_State *L, const char *format, ...) {
205 va_list ap;
206 va_start(ap, format);
207 vprintf(format, ap);
208 va_end(ap);
209 int top = lua_gettop(L);
210 for (int i = 1; i <= top; i++) {
211 printf("%d: ", i);
212 stack_dump_entry(L, i);
213 printf("\n");
215 printf("\n\n");
216 fflush(stdout);
219 #endif
221 static int panic_handler(lua_State *L) {
222 void *ud = NULL;
223 lua_getallocf(L, &ud);
224 if (ud) {
225 Vis *vis = ud;
226 vis->lua = NULL;
227 const char *msg = NULL;
228 if (lua_type(L, -1) == LUA_TSTRING)
229 msg = lua_tostring(L, -1);
230 vis_info_show(vis, "Fatal Lua error: %s", msg ? msg : "unknown reason");
231 lua_close(L);
232 if (vis->running)
233 siglongjmp(vis->sigbus_jmpbuf, 1);
235 return 0;
238 static int error_handler(lua_State *L) {
239 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
240 if (vis->errorhandler)
241 return 1;
242 vis->errorhandler = true;
243 size_t len;
244 const char *msg = lua_tostring(L, 1);
245 if (msg)
246 luaL_traceback(L, L, msg, 1);
247 msg = lua_tolstring(L, 1, &len);
248 vis_message_show(vis, msg);
249 vis->errorhandler = false;
250 return 1;
253 static int pcall(Vis *vis, lua_State *L, int nargs, int nresults) {
254 /* insert a custom error function below all arguments */
255 int msgh = lua_gettop(L) - nargs;
256 lua_pushlightuserdata(L, vis);
257 lua_pushcclosure(L, error_handler, 1);
258 lua_insert(L, msgh);
259 int ret = lua_pcall(L, nargs, nresults, msgh);
260 lua_remove(L, msgh);
261 return ret;
264 /* expects a lua function at stack position `narg` and stores a
265 * reference to it in the registry. The return value can be used
266 * to look it up.
268 * registry["vis.functions"][(void*)(function)] = function
270 static const void *func_ref_new(lua_State *L, int narg) {
271 const void *addr = lua_topointer(L, narg);
272 if (!lua_isfunction(L, narg) || !addr)
273 luaL_argerror(L, narg, "function expected");
274 lua_getfield(L, LUA_REGISTRYINDEX, "vis.functions");
275 lua_pushlightuserdata(L, (void*)addr);
276 lua_pushvalue(L, narg);
277 lua_settable(L, -3);
278 lua_pop(L, 1);
279 return addr;
282 /* retrieve function from registry and place it at the top of the stack */
283 static bool func_ref_get(lua_State *L, const void *addr) {
284 if (!addr)
285 return false;
286 lua_getfield(L, LUA_REGISTRYINDEX, "vis.functions");
287 lua_pushlightuserdata(L, (void*)addr);
288 lua_gettable(L, -2);
289 lua_remove(L, -2);
290 if (!lua_isfunction(L, -1)) {
291 lua_pop(L, 1);
292 return false;
294 return true;
297 /* creates a new metatable for a given type and stores a mapping:
299 * registry["vis.types"][metatable] = type
301 * leaves the metatable at the top of the stack.
303 static void obj_type_new(lua_State *L, const char *type) {
304 luaL_newmetatable(L, type);
305 lua_getglobal(L, "vis");
306 if (!lua_isnil(L, -1)) {
307 lua_getfield(L, -1, "types");
308 lua_pushvalue(L, -3);
309 lua_setfield(L, -2, type);
310 lua_pop(L, 1);
312 lua_pop(L, 1);
313 lua_getfield(L, LUA_REGISTRYINDEX, "vis.types");
314 lua_pushvalue(L, -2);
315 lua_pushstring(L, type);
316 lua_settable(L, -3);
317 lua_pop(L, 1);
320 /* get type of userdatum at the top of the stack:
322 * return registry["vis.types"][getmetatable(userdata)]
324 const char *obj_type_get(lua_State *L) {
325 if (lua_isnil(L, -1))
326 return "nil";
327 lua_getfield(L, LUA_REGISTRYINDEX, "vis.types");
328 lua_getmetatable(L, -2);
329 lua_gettable(L, -2);
330 // XXX: in theory string might become invalid when poped from stack
331 const char *type = lua_tostring(L, -1);
332 lua_pop(L, 2);
333 return type;
336 static void *obj_new(lua_State *L, size_t size, const char *type) {
337 void *obj = lua_newuserdata(L, size);
338 luaL_getmetatable(L, type);
339 lua_setmetatable(L, -2);
340 lua_newtable(L);
341 lua_setuservalue(L, -2);
342 return obj;
345 /* returns registry["vis.objects"][addr] if it is of correct type */
346 static void *obj_ref_get(lua_State *L, void *addr, const char *type) {
347 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
348 lua_pushlightuserdata(L, addr);
349 lua_gettable(L, -2);
350 lua_remove(L, -2);
351 if (lua_isnil(L, -1)) {
352 debug("get: vis.objects[%p] = nil\n", addr);
353 lua_pop(L, 1);
354 return NULL;
356 if (DEBUG_LUA) {
357 const char *actual_type = obj_type_get(L);
358 if (strcmp(type, actual_type) != 0)
359 debug("get: vis.objects[%p] = %s (BUG: expected %s)\n", addr, actual_type, type);
360 void **handle = luaL_checkudata(L, -1, type);
361 if (!handle)
362 debug("get: vis.objects[%p] = %s (BUG: invalid handle)\n", addr, type);
363 else if (*handle != addr)
364 debug("get: vis.objects[%p] = %s (BUG: handle mismatch %p)\n", addr, type, *handle);
366 return luaL_checkudata(L, -1, type);
369 /* expects a userdatum at the top of the stack and sets
371 * registry["vis.objects"][addr] = userdata
373 static void obj_ref_set(lua_State *L, void *addr) {
374 //debug("set: vis.objects[%p] = %s\n", addr, obj_type_get(L));
375 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
376 lua_pushlightuserdata(L, addr);
377 lua_pushvalue(L, -3);
378 lua_settable(L, -3);
379 lua_pop(L, 1);
382 /* invalidates an object reference
384 * registry["vis.objects"][addr] = nil
386 static void obj_ref_free(lua_State *L, void *addr) {
387 if (DEBUG_LUA) {
388 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
389 lua_pushlightuserdata(L, addr);
390 lua_gettable(L, -2);
391 lua_remove(L, -2);
392 if (lua_isnil(L, -1))
393 debug("free-unused: %p\n", addr);
394 else
395 debug("free: vis.objects[%p] = %s\n", addr, obj_type_get(L));
396 lua_pop(L, 1);
398 lua_pushnil(L);
399 obj_ref_set(L, addr);
402 /* creates a new object reference of given type if it does not already exist in the registry:
404 * if (registry["vis.types"][metatable(registry["vis.objects"][addr])] != type) {
405 * // XXX: should not happen
406 * registry["vis.objects"][addr] = new_obj(addr, type)
408 * return registry["vis.objects"][addr];
410 static void *obj_ref_new(lua_State *L, void *addr, const char *type) {
411 if (!addr) {
412 lua_pushnil(L);
413 return NULL;
415 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
416 lua_pushlightuserdata(L, addr);
417 lua_gettable(L, -2);
418 lua_remove(L, -2);
419 const char *old_type = obj_type_get(L);
420 if (strcmp(type, old_type) == 0) {
421 debug("new: vis.objects[%p] = %s (returning existing object)\n", addr, old_type);
422 void **handle = luaL_checkudata(L, -1, type);
423 if (!handle)
424 debug("new: vis.objects[%p] = %s (BUG: invalid handle)\n", addr, old_type);
425 else if (*handle != addr)
426 debug("new: vis.objects[%p] = %s (BUG: handle mismatch %p)\n", addr, old_type, *handle);
427 return addr;
429 if (!lua_isnil(L, -1))
430 debug("new: vis.objects[%p] = %s (WARNING: changing object type from %s)\n", addr, type, old_type);
431 else
432 debug("new: vis.objects[%p] = %s (creating new object)\n", addr, type);
433 lua_pop(L, 1);
434 void **handle = obj_new(L, sizeof(addr), type);
435 obj_ref_set(L, addr);
436 *handle = addr;
437 return addr;
440 /* retrieve object stored in reference at stack location `idx' */
441 static void *obj_ref_check_get(lua_State *L, int idx, const char *type) {
442 void **addr = luaL_checkudata(L, idx, type);
443 if (!obj_ref_get(L, *addr, type))
444 return NULL;
445 return *addr;
448 /* (type) check validity of object reference at stack location `idx' */
449 static void *obj_ref_check(lua_State *L, int idx, const char *type) {
450 void *obj = obj_ref_check_get(L, idx, type);
451 if (obj)
452 lua_pop(L, 1);
453 else
454 luaL_argerror(L, idx, "invalid object reference");
455 return obj;
458 static void *obj_ref_check_containerof(lua_State *L, int idx, const char *type, size_t offset) {
459 void *obj = obj_ref_check(L, idx, type);
460 return obj ? ((char*)obj-offset) : obj;
463 static void *obj_lightref_new(lua_State *L, void *addr, const char *type) {
464 if (!addr)
465 return NULL;
466 void **handle = obj_new(L, sizeof(addr), type);
467 *handle = addr;
468 return addr;
471 static void *obj_lightref_check(lua_State *L, int idx, const char *type) {
472 void **addr = luaL_checkudata(L, idx, type);
473 return *addr;
476 static int index_common(lua_State *L) {
477 lua_getmetatable(L, 1);
478 lua_pushvalue(L, 2);
479 lua_gettable(L, -2);
480 if (lua_isnil(L, -1)) {
481 lua_getuservalue(L, 1);
482 lua_pushvalue(L, 2);
483 lua_gettable(L, -2);
485 return 1;
488 static int newindex_common(lua_State *L) {
489 lua_getuservalue(L, 1);
490 lua_pushvalue(L, 2);
491 lua_pushvalue(L, 3);
492 lua_settable(L, -3);
493 return 0;
496 static size_t getpos(lua_State *L, int narg) {
497 return lua_tounsigned(L, narg);
500 static size_t checkpos(lua_State *L, int narg) {
501 lua_Number n = luaL_checknumber(L, narg);
502 if (n >= 0 && n <= SIZE_MAX && n == (size_t)n)
503 return n;
504 return luaL_argerror(L, narg, "expected position, got number");
507 static void pushpos(lua_State *L, size_t pos) {
508 if (pos == EPOS)
509 lua_pushnil(L);
510 else
511 lua_pushunsigned(L, pos);
514 static void pushrange(lua_State *L, Filerange *r) {
515 if (!r || !text_range_valid(r)) {
516 lua_pushnil(L);
517 return;
519 lua_createtable(L, 0, 2);
520 lua_pushstring(L, "start");
521 lua_pushunsigned(L, r->start);
522 lua_settable(L, -3);
523 lua_pushstring(L, "finish");
524 lua_pushunsigned(L, r->end);
525 lua_settable(L, -3);
528 static Filerange getrange(lua_State *L, int index) {
529 Filerange range = text_range_empty();
530 if (lua_istable(L, index)) {
531 lua_getfield(L, index, "start");
532 range.start = checkpos(L, -1);
533 lua_pop(L, 1);
534 lua_getfield(L, index, "finish");
535 range.end = checkpos(L, -1);
536 lua_pop(L, 1);
537 } else {
538 range.start = checkpos(L, index);
539 range.end = range.start + checkpos(L, index+1);
541 return range;
544 static const char *keymapping(Vis *vis, const char *keys, const Arg *arg) {
545 lua_State *L = vis->lua;
546 if (!func_ref_get(L, arg->v))
547 return keys;
548 lua_pushstring(L, keys);
549 if (pcall(vis, L, 1, 1) != 0)
550 return keys;
551 if (lua_type(L, -1) != LUA_TNUMBER)
552 return keys; /* invalid or no return value, assume zero */
553 lua_Number number = lua_tonumber(L, -1);
554 lua_Integer integer = lua_tointeger(L, -1);
555 if (number != integer)
556 return keys;
557 if (integer < 0)
558 return NULL; /* need more input */
559 size_t len = integer;
560 size_t max = strlen(keys);
561 return (len <= max) ? keys+len : keys;
564 /***
565 * The main editor object.
566 * @type Vis
569 /***
570 * Version information.
571 * @tfield string VERSION
572 * version information in `git describe` format, same as reported by `vis -v`.
574 /***
575 * Lua API object types
576 * @field types meta tables of userdata objects used for type checking
577 * @local
579 /***
580 * User interface.
581 * @tfield Ui ui the user interface being used
583 /***
584 * Mode constants.
585 * @tfield modes modes
587 /***
588 * Events.
589 * @tfield events events
591 /***
592 * Registers.
593 * @field registers array to access the register by single letter name
595 /***
596 * Scintillua lexer module.
597 * @field lexers might be `nil` if module is not found
599 /***
600 * LPeg lexer module.
601 * @field lpeg might be `nil` if module is not found
603 /***
604 * Current count.
605 * @tfield int count the specified count for the current command or `nil` if none was given
608 /***
609 * Create an iterator over all windows.
610 * @function windows
611 * @return the new iterator
612 * @see win
613 * @usage
614 * for win in vis:windows() do
615 * -- do something with win
616 * end
618 static int windows_iter(lua_State *L);
619 static int windows(lua_State *L) {
620 Vis *vis = obj_ref_check(L, 1, "vis");
621 Win **handle = lua_newuserdata(L, sizeof *handle);
622 *handle = vis->windows;
623 lua_pushcclosure(L, windows_iter, 1);
624 return 1;
627 static int windows_iter(lua_State *L) {
628 Win **handle = lua_touserdata(L, lua_upvalueindex(1));
629 if (!*handle)
630 return 0;
631 Win *win = obj_ref_new(L, *handle, VIS_LUA_TYPE_WINDOW);
632 if (win)
633 *handle = win->next;
634 return 1;
637 /***
638 * Create an iterator over all files.
639 * @function files
640 * @return the new iterator
641 * @usage
642 * for file in vis:files() do
643 * -- do something with file
644 * end
646 static int files_iter(lua_State *L);
647 static int files(lua_State *L) {
648 Vis *vis = obj_ref_check(L, 1, "vis");
649 File **handle = lua_newuserdata(L, sizeof *handle);
650 *handle = vis->files;
651 lua_pushcclosure(L, files_iter, 1);
652 return 1;
655 static int files_iter(lua_State *L) {
656 File **handle = lua_touserdata(L, lua_upvalueindex(1));
657 if (!*handle)
658 return 0;
659 File *file = obj_ref_new(L, *handle, VIS_LUA_TYPE_FILE);
660 if (file)
661 *handle = file->next;
662 return 1;
665 /***
666 * Create an iterator over all mark names.
667 * @function mark_names
668 * @return the new iterator
669 * @usage
670 * local marks = vis.win.marks
671 * for name in vis:mark_names() do
672 * local mark = marks[name]
673 * for i = 1, #mark do
674 * -- do somthing with: name, mark[i].start, mark[i].finish
675 * end
676 * end
678 static int mark_names_iter(lua_State *L);
679 static int mark_names(lua_State *L) {
680 enum VisMark *handle = lua_newuserdata(L, sizeof *handle);
681 *handle = 0;
682 lua_pushcclosure(L, mark_names_iter, 1);
683 return 1;
686 static int mark_names_iter(lua_State *L) {
687 char mark = '\0';
688 enum VisMark *handle = lua_touserdata(L, lua_upvalueindex(1));
689 if (*handle < LENGTH(vis_marks))
690 mark = vis_marks[*handle].name;
691 else if (VIS_MARK_a <= *handle && *handle <= VIS_MARK_z)
692 mark = 'a' + *handle - VIS_MARK_a;
693 if (mark) {
694 char name[2] = { mark, '\0' };
695 lua_pushstring(L, name);
696 (*handle)++;
697 return 1;
699 return 0;
702 /***
703 * Create an iterator over all register names.
704 * @function register_names
705 * @return the new iterator
706 * @usage
707 * for name in vis:register_names() do
708 * local reg = vis.registers[name]
709 * for i = 1, #reg do
710 * -- do something with register value reg[i]
711 * end
712 * end
714 static int register_names_iter(lua_State *L);
715 static int register_names(lua_State *L) {
716 enum VisRegister *handle = lua_newuserdata(L, sizeof *handle);
717 *handle = 0;
718 lua_pushcclosure(L, register_names_iter, 1);
719 return 1;
722 static int register_names_iter(lua_State *L) {
723 char reg = '\0';
724 enum VisRegister *handle = lua_touserdata(L, lua_upvalueindex(1));
725 if (*handle < LENGTH(vis_registers))
726 reg = vis_registers[*handle].name;
727 else if (VIS_REG_a <= *handle && *handle <= VIS_REG_z)
728 reg = 'a' + *handle - VIS_REG_a;
729 if (reg) {
730 char name[2] = { reg, '\0' };
731 lua_pushstring(L, name);
732 (*handle)++;
733 return 1;
735 return 0;
738 /***
739 * Execute a `:`-command.
740 * @function command
741 * @tparam string command the command to execute
742 * @treturn bool whether the command succeeded
743 * @usage
744 * vis:command("set number")
746 static int command(lua_State *L) {
747 Vis *vis = obj_ref_check(L, 1, "vis");
748 const char *cmd = luaL_checkstring(L, 2);
749 bool ret = vis_cmd(vis, cmd);
750 lua_pushboolean(L, ret);
751 return 1;
754 /***
755 * Display a short message.
757 * The single line message will be displayed at the bottom of
758 * the scren and automatically hidden once a key is pressed.
760 * @function info
761 * @tparam string message the message to display
763 static int info(lua_State *L) {
764 Vis *vis = obj_ref_check(L, 1, "vis");
765 const char *msg = luaL_checkstring(L, 2);
766 vis_info_show(vis, "%s", msg);
767 return 0;
770 /***
771 * Display a multi line message.
773 * Opens a new window and displays an arbitrarily long message.
775 * @function message
776 * @tparam string message the message to display
778 static int message(lua_State *L) {
779 Vis *vis = obj_ref_check(L, 1, "vis");
780 const char *msg = luaL_checkstring(L, 2);
781 vis_message_show(vis, msg);
782 return 0;
785 /***
786 * Register a Lua function as key action.
787 * @function action_register
788 * @tparam string name the name of the action, can be referred to in key bindings as `<name>` pseudo key
789 * @tparam Function func the lua function implementing the key action (see @{keyhandler})
790 * @tparam[opt] string help the single line help text as displayed in `:help`
791 * @treturn KeyAction action the registered key action
792 * @see Vis:map
793 * @see Window:map
795 static int action_register(lua_State *L) {
796 Vis *vis = obj_ref_check(L, 1, "vis");
797 const char *name = luaL_checkstring(L, 2);
798 const void *func = func_ref_new(L, 3);
799 const char *help = luaL_optstring(L, 4, NULL);
800 KeyAction *action = vis_action_new(vis, name, help, keymapping, (Arg){ .v = func });
801 if (!action)
802 goto err;
803 if (!vis_action_register(vis, action))
804 goto err;
805 obj_ref_new(L, action, VIS_LUA_TYPE_KEYACTION);
806 return 1;
807 err:
808 vis_action_free(vis, action);
809 lua_pushnil(L);
810 return 1;
813 static int keymap(lua_State *L, Vis *vis, Win *win) {
814 int mode = luaL_checkint(L, 2);
815 const char *key = luaL_checkstring(L, 3);
816 const char *help = luaL_optstring(L, 5, NULL);
817 KeyBinding *binding = vis_binding_new(vis);
818 if (!binding)
819 goto err;
820 if (lua_isstring(L, 4)) {
821 const char *alias = luaL_checkstring(L, 4);
822 if (!(binding->alias = strdup(alias)))
823 goto err;
824 } else if (lua_isfunction(L, 4)) {
825 const void *func = func_ref_new(L, 4);
826 if (!(binding->action = vis_action_new(vis, NULL, help, keymapping, (Arg){ .v = func })))
827 goto err;
828 } else if (lua_isuserdata(L, 4)) {
829 binding->action = obj_ref_check(L, 4, VIS_LUA_TYPE_KEYACTION);
832 if (win) {
833 if (!vis_window_mode_map(win, mode, true, key, binding))
834 goto err;
835 } else {
836 if (!vis_mode_map(vis, mode, true, key, binding))
837 goto err;
840 lua_pushboolean(L, true);
841 return 1;
842 err:
843 vis_binding_free(vis, binding);
844 lua_pushboolean(L, false);
845 return 1;
848 /***
849 * Map a key to a Lua function.
851 * Creates a new key mapping in a given mode.
853 * @function map
854 * @tparam int mode the mode to which the mapping should be added
855 * @tparam string key the key to map
856 * @tparam function func the Lua function to handle the key mapping (see @{keyhandler})
857 * @tparam[opt] string help the single line help text as displayed in `:help`
858 * @treturn bool whether the mapping was successfully established
859 * @see Window:map
860 * @usage
861 * vis:map(vis.modes.INSERT, "<C-k>", function(keys)
862 * if #keys < 2 then
863 * return -1 -- need more input
864 * end
865 * local digraph = keys:sub(1, 2)
866 * if digraph == "l*" then
867 * vis:feedkeys('λ')
868 * return 2 -- consume 2 bytes of input
869 * end
870 * end, "Insert digraph")
872 /***
873 * Setup a key alias.
875 * This is equivalent to `vis:command('map! mode key alias')`.
877 * Mappings are always recursive!
878 * @function map
879 * @tparam int mode the mode to which the mapping should be added
880 * @tparam string key the key to map
881 * @tparam string alias the key to map to
882 * @treturn bool whether the mapping was successfully established
883 * @see Window:map
884 * @usage
885 * vis:map(vis.modes.NORMAL, "j", "k")
887 /***
888 * Map a key to a key action.
890 * @function map
891 * @tparam int mode the mode to which the mapping should be added
892 * @tparam string key the key to map
893 * @param action the action to map
894 * @treturn bool whether the mapping was successfully established
895 * @see Window:map
896 * @usage
897 * local action = vis:action_register("info", function()
898 * vis:info("Mapping works!")
899 * end, "Info message help text")
900 * vis:map(vis.modes.NORMAL, "gh", action)
901 * vis:map(vis.modes.NORMAL, "gl", action)
903 static int map(lua_State *L) {
904 Vis *vis = obj_ref_check(L, 1, "vis");
905 return keymap(L, vis, NULL);
908 /***
909 * Unmap a global key binding.
911 * @function unmap
912 * @tparam int mode the mode from which the mapping should be removed
913 * @tparam string key the mapping to remove
914 * @treturn bool whether the mapping was successfully removed
915 * @see Window:unmap
917 static int keyunmap(lua_State *L, Vis *vis, Win *win) {
918 enum VisMode mode = luaL_checkint(L, 2);
919 const char *key = luaL_checkstring(L, 3);
920 bool ret;
921 if (!win)
922 ret = vis_mode_unmap(vis, mode, key);
923 else
924 ret = vis_window_mode_unmap(win, mode, key);
925 lua_pushboolean(L, ret);
926 return 1;
929 static int unmap(lua_State *L) {
930 Vis *vis = obj_ref_check(L, 1, "vis");
931 return keyunmap(L, vis, NULL);
934 /***
935 * Get all currently active mappings of a mode.
937 * @function mappings
938 * @tparam int mode the mode to query
939 * @treturn table the active mappings and their associated help texts
940 * @usage
941 * local bindings = vis:mappings(vis.modes.NORMAL)
942 * for key, help in pairs(bindings) do
943 * -- do something
944 * end
945 * @see Vis:map
947 static bool binding_collect(const char *key, void *value, void *ctx) {
948 lua_State *L = ctx;
949 KeyBinding *binding = value;
950 lua_getfield(L, -1, key);
951 bool new = lua_isnil(L, -1);
952 lua_pop(L, 1);
953 if (new) {
954 const char *help = binding->alias ? binding->alias : VIS_HELP_USE(binding->action->help);
955 lua_pushstring(L, help ? help : "");
956 lua_setfield(L, -2, key);
958 return true;
961 static int mappings(lua_State *L) {
962 Vis *vis = obj_ref_check(L, 1, "vis");
963 lua_newtable(L);
964 for (Mode *mode = mode_get(vis, luaL_checkint(L, 2)); mode; mode = mode->parent) {
965 if (!mode->bindings)
966 continue;
967 map_iterate(mode->bindings, binding_collect, vis->lua);
969 return 1;
972 /***
973 * Execute a motion.
975 * @function motion
976 * @tparam int id the id of the motion to execute
977 * @treturn bool whether the id was valid
978 * @local
980 static int motion(lua_State *L) {
981 Vis *vis = obj_ref_check(L, 1, "vis");
982 enum VisMotion id = luaL_checkunsigned(L, 2);
983 // TODO handle var args?
984 lua_pushboolean(L, vis && vis_motion(vis, id));
985 return 1;
988 static size_t motion_lua(Vis *vis, Win *win, void *data, size_t pos) {
989 lua_State *L = vis->lua;
990 if (!L || !func_ref_get(L, data) || !obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW))
991 return EPOS;
993 lua_pushunsigned(L, pos);
994 if (pcall(vis, L, 2, 1) != 0)
995 return EPOS;
996 return getpos(L, -1);
999 /***
1000 * Register a custom motion.
1002 * @function motion_register
1003 * @tparam function motion the Lua function implementing the motion
1004 * @treturn int the associated motion id, or `-1` on failure
1005 * @see motion, motion_new
1006 * @local
1007 * @usage
1008 * -- custom motion advancing to the next byte
1009 * local id = vis:motion_register(function(win, pos)
1010 * return pos+1
1011 * end)
1013 static int motion_register(lua_State *L) {
1014 Vis *vis = obj_ref_check(L, 1, "vis");
1015 const void *func = func_ref_new(L, 2);
1016 int id = vis_motion_register(vis, 0, (void*)func, motion_lua);
1017 lua_pushinteger(L, id);
1018 return 1;
1021 /***
1022 * Execute an operator.
1024 * @function operator
1025 * @tparam int id the id of the operator to execute
1026 * @treturn bool whether the id was valid
1027 * @local
1029 static int operator(lua_State *L) {
1030 Vis *vis = obj_ref_check(L, 1, "vis");
1031 enum VisOperator id = luaL_checkunsigned(L, 2);
1032 // TODO handle var args?
1033 lua_pushboolean(L, vis && vis_operator(vis, id));
1034 return 1;
1037 static size_t operator_lua(Vis *vis, Text *text, OperatorContext *c) {
1038 lua_State *L = vis->lua;
1039 if (!L || !func_ref_get(L, c->context))
1040 return EPOS;
1041 File *file = vis->files;
1042 while (file && (file->internal || file->text != text))
1043 file = file->next;
1044 if (!file || !obj_ref_new(L, file, VIS_LUA_TYPE_FILE))
1045 return EPOS;
1046 pushrange(L, &c->range);
1047 pushpos(L, c->pos);
1048 if (pcall(vis, L, 3, 1) != 0)
1049 return EPOS;
1050 return getpos(L, -1);
1053 /***
1054 * Register a custom operator.
1056 * @function operator_register
1057 * @tparam function operator the Lua function implementing the operator
1058 * @treturn int the associated operator id, or `-1` on failure
1059 * @see operator, operator_new
1060 * @local
1061 * @usage
1062 * -- custom operator replacing every 'a' with 'b'
1063 * local id = vis:operator_register(function(file, range, pos)
1064 * local data = file:content(range)
1065 * data = data:gsub("a", "b")
1066 * file:delete(range)
1067 * file:insert(range.start, data)
1068 * return range.start -- new cursor location
1069 * end)
1071 static int operator_register(lua_State *L) {
1072 Vis *vis = obj_ref_check(L, 1, "vis");
1073 const void *func = func_ref_new(L, 2);
1074 int id = vis_operator_register(vis, operator_lua, (void*)func);
1075 lua_pushinteger(L, id);
1076 return 1;
1079 /***
1080 * Execute a text object.
1082 * @function textobject
1083 * @tparam int id the id of the text object to execute
1084 * @treturn bool whether the id was valid
1085 * @see textobject_register, textobject_new
1086 * @local
1088 static int textobject(lua_State *L) {
1089 Vis *vis = obj_ref_check(L, 1, "vis");
1090 enum VisTextObject id = luaL_checkunsigned(L, 2);
1091 lua_pushboolean(L, vis_textobject(vis, id));
1092 return 1;
1095 static Filerange textobject_lua(Vis *vis, Win *win, void *data, size_t pos) {
1096 lua_State *L = vis->lua;
1097 if (!L || !func_ref_get(L, data) || !obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW))
1098 return text_range_empty();
1099 lua_pushunsigned(L, pos);
1100 if (pcall(vis, L, 2, 2) != 0 || lua_isnil(L, -1))
1101 return text_range_empty();
1102 return text_range_new(getpos(L, -2), getpos(L, -1));
1105 /***
1106 * Register a custom text object.
1108 * @function textobject_register
1109 * @tparam function textobject the Lua function implementing the text object
1110 * @treturn int the associated text object id, or `-1` on failure
1111 * @see textobject, textobject_new
1112 * @local
1113 * @usage
1114 * -- custom text object covering the next byte
1115 * local id = vis:textobject_register(function(win, pos)
1116 * return pos, pos+1
1117 * end)
1119 static int textobject_register(lua_State *L) {
1120 Vis *vis = obj_ref_check(L, 1, "vis");
1121 const void *func = func_ref_new(L, 2);
1122 int id = vis_textobject_register(vis, 0, (void*)func, textobject_lua);
1123 lua_pushinteger(L, id);
1124 return 1;
1127 static bool option_lua(Vis *vis, Win *win, void *context, bool toggle,
1128 enum VisOption flags, const char *name, Arg *value) {
1129 lua_State *L = vis->lua;
1130 if (!L || !func_ref_get(L, context))
1131 return false;
1132 if (flags & VIS_OPTION_TYPE_BOOL)
1133 lua_pushboolean(L, value->b);
1134 else if (flags & VIS_OPTION_TYPE_STRING)
1135 lua_pushstring(L, value->s);
1136 else if (flags & VIS_OPTION_TYPE_NUMBER)
1137 lua_pushnumber(L, value->i);
1138 else
1139 return false;
1140 lua_pushboolean(L, toggle);
1141 return pcall(vis, L, 2, 2) == 0 && (!lua_isboolean(L, -1) || lua_toboolean(L, -1));
1144 /***
1145 * Register a custom `:set` option.
1147 * @function option_register
1148 * @tparam string name the option name
1149 * @tparam string type the option type (`bool`, `string` or `number`)
1150 * @tparam function handler the Lua function being called when the option is changed
1151 * @tparam[opt] string help the single line help text as displayed in `:help`
1152 * @treturn bool whether the option was successfully registered
1153 * @usage
1154 * vis:option_register("foo", "bool", function(value, toogle)
1155 * if not vis.win then return false end
1156 * vis.win.foo = toogle and not vis.win.foo or value
1157 * vis:info("Option foo = " .. tostring(vis.win.foo))
1158 * return true
1159 * end, "Foo enables superpowers")
1161 static int option_register(lua_State *L) {
1162 Vis *vis = obj_ref_check(L, 1, "vis");
1163 const char *name = luaL_checkstring(L, 2);
1164 const char *type = luaL_checkstring(L, 3);
1165 const void *func = func_ref_new(L, 4);
1166 const char *help = luaL_optstring(L, 5, NULL);
1167 const char *names[] = { name, NULL };
1168 enum VisOption flags = 0;
1169 if (strcmp(type, "string") == 0)
1170 flags |= VIS_OPTION_TYPE_STRING;
1171 else if (strcmp(type, "number") == 0)
1172 flags |= VIS_OPTION_TYPE_NUMBER;
1173 else
1174 flags |= VIS_OPTION_TYPE_BOOL;
1175 bool ret = vis_option_register(vis, names, flags, option_lua, (void*)func, help);
1176 lua_pushboolean(L, ret);
1177 return 1;
1180 /***
1181 * Unregister a `:set` option.
1183 * @function option_unregister
1184 * @tparam string name the option name
1185 * @treturn bool whether the option was successfully unregistered
1187 static int option_unregister(lua_State *L) {
1188 Vis *vis = obj_ref_check(L, 1, "vis");
1189 const char *name = luaL_checkstring(L, 2);
1190 bool ret = vis_option_unregister(vis, name);
1191 lua_pushboolean(L, ret);
1192 return 1;
1195 static bool command_lua(Vis *vis, Win *win, void *data, bool force, const char *argv[], Selection *sel, Filerange *range) {
1196 lua_State *L = vis->lua;
1197 if (!L || !func_ref_get(L, data))
1198 return false;
1199 lua_newtable(L);
1200 for (size_t i = 0; argv[i]; i++) {
1201 lua_pushunsigned(L, i);
1202 lua_pushstring(L, argv[i]);
1203 lua_settable(L, -3);
1205 lua_pushboolean(L, force);
1206 if (!obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW))
1207 return false;
1208 if (!sel)
1209 sel = view_selections_primary_get(win->view);
1210 if (!obj_lightref_new(L, sel, VIS_LUA_TYPE_SELECTION))
1211 return false;
1212 pushrange(L, range);
1213 if (pcall(vis, L, 5, 1) != 0)
1214 return false;
1215 return lua_toboolean(L, -1);
1218 /***
1219 * Register a custom `:`-command.
1221 * @function command_register
1222 * @tparam string name the command name
1223 * @tparam function command the Lua function implementing the command
1224 * @tparam[opt] string help the single line help text as displayed in `:help`
1225 * @treturn bool whether the command has been successfully registered
1226 * @usage
1227 * vis:command_register("foo", function(argv, force, win, selection, range)
1228 * for i,arg in ipairs(argv) do
1229 * print(i..": "..arg)
1230 * end
1231 * print("was command forced with ! "..(force and "yes" or "no"))
1232 * print(win.file.name)
1233 * print(selection.pos)
1234 * print(range ~= nil and ('['..range.start..', '..range.finish..']') or "invalid range")
1235 * return true;
1236 * end)
1238 static int command_register(lua_State *L) {
1239 Vis *vis = obj_ref_check(L, 1, "vis");
1240 const char *name = luaL_checkstring(L, 2);
1241 const void *func = func_ref_new(L, 3);
1242 const char *help = luaL_optstring(L, 4, "");
1243 bool ret = vis_cmd_register(vis, name, help, (void*)func, command_lua);
1244 lua_pushboolean(L, ret);
1245 return 1;
1248 /***
1249 * Push keys to input queue and interpret them.
1251 * The keys are processed as if they were read from the keyboard.
1253 * @function feedkeys
1254 * @tparam string keys the keys to interpret
1256 static int feedkeys(lua_State *L) {
1257 Vis *vis = obj_ref_check(L, 1, "vis");
1258 const char *keys = luaL_checkstring(L, 2);
1259 vis_keys_feed(vis, keys);
1260 return 0;
1263 /***
1264 * Insert keys at all cursor positions of active window.
1266 * This function behaves as if the keys were entered in insert mode,
1267 * but in contrast to @{Vis:feedkeys} it bypasses the input queue,
1268 * meaning mappings do not apply and the keys will not be recorded in macros.
1270 * @function insert
1271 * @tparam string keys the keys to insert
1272 * @see Vis:feedkeys
1274 static int insert(lua_State *L) {
1275 Vis *vis = obj_ref_check(L, 1, "vis");
1276 size_t len;
1277 const char *keys = luaL_checklstring(L, 2, &len);
1278 vis_insert_key(vis, keys, len);
1279 return 0;
1282 /***
1283 * Replace keys at all cursor positions of active window.
1285 * This function behaves as if the keys were entered in replace mode,
1286 * but in contrast to @{Vis:feedkeys} it bypasses the input queue,
1287 * meaning mappings do not apply and the keys will not be recorded in macros.
1289 * @function replace
1290 * @tparam string keys the keys to insert
1291 * @see Vis:feedkeys
1293 static int replace(lua_State *L) {
1294 Vis *vis = obj_ref_check(L, 1, "vis");
1295 size_t len;
1296 const char *keys = luaL_checklstring(L, 2, &len);
1297 vis_replace_key(vis, keys, len);
1298 return 0;
1301 /***
1302 * Terminate editor process.
1304 * Termination happens upon the next iteration of the main event loop.
1305 * This means the calling Lua code will be executed further until it
1306 * eventually hands over control to the editor core. The exit status
1307 * of the most recent call is used.
1309 * All unsaved chanes will be lost!
1311 * @function exit
1312 * @tparam int code the exit status returned to the operating system
1314 static int exit_func(lua_State *L) {
1315 Vis *vis = obj_ref_check(L, 1, "vis");
1316 int code = luaL_checkint(L, 2);
1317 vis_exit(vis, code);
1318 return 0;
1321 /***
1322 * Pipe file range to external process and collect output.
1324 * The editor core will be blocked while the external process is running.
1326 * @function pipe
1327 * @tparam File file the file to which the range applies
1328 * @tparam Range range the range to pipe
1329 * @tparam string command the command to execute
1330 * @treturn int code the exit status of the executed command
1331 * @treturn string stdout the data written to stdout
1332 * @treturn string stderr the data written to stderr
1334 static int pipe_func(lua_State *L) {
1335 Vis *vis = obj_ref_check(L, 1, "vis");
1336 File *file = obj_ref_check(L, 2, VIS_LUA_TYPE_FILE);
1337 Filerange range = getrange(L, 3);
1338 const char *cmd = luaL_checkstring(L, 4);
1339 char *out = NULL, *err = NULL;
1340 int status = vis_pipe_collect(vis, file, &range, (const char*[]){ cmd, NULL }, &out, &err);
1341 lua_pushinteger(L, status);
1342 if (out)
1343 lua_pushstring(L, out);
1344 else
1345 lua_pushnil(L);
1346 free(out);
1347 if (err)
1348 lua_pushstring(L, err);
1349 else
1350 lua_pushnil(L);
1351 free(err);
1352 vis_draw(vis);
1353 return 3;
1355 /***
1356 * Currently active window.
1357 * @tfield Window win
1358 * @see windows
1360 /***
1361 * Currently active mode.
1362 * @tfield modes mode
1364 /***
1365 * Whether a macro is being recorded.
1366 * @tfield bool recording
1368 static int vis_index(lua_State *L) {
1369 Vis *vis = obj_ref_check(L, 1, "vis");
1371 if (lua_isstring(L, 2)) {
1372 const char *key = lua_tostring(L, 2);
1373 if (strcmp(key, "win") == 0) {
1374 if (vis->win)
1375 obj_ref_new(L, vis->win, VIS_LUA_TYPE_WINDOW);
1376 else
1377 lua_pushnil(L);
1378 return 1;
1381 if (strcmp(key, "mode") == 0) {
1382 lua_pushunsigned(L, vis->mode->id);
1383 return 1;
1386 if (strcmp(key, "recording") == 0) {
1387 lua_pushboolean(L, vis_macro_recording(vis));
1388 return 1;
1391 if (strcmp(key, "count") == 0) {
1392 int count = vis_count_get(vis);
1393 if (count == VIS_COUNT_UNKNOWN)
1394 lua_pushnil(L);
1395 else
1396 lua_pushunsigned(L, count);
1397 return 1;
1400 if (strcmp(key, "registers") == 0) {
1401 obj_ref_new(L, vis->ui, VIS_LUA_TYPE_REGISTERS);
1402 return 1;
1405 if (strcmp(key, "ui") == 0) {
1406 obj_ref_new(L, vis->ui, VIS_LUA_TYPE_UI);
1407 return 1;
1411 return index_common(L);
1414 static int vis_newindex(lua_State *L) {
1415 Vis *vis = obj_ref_check(L, 1, "vis");
1416 if (lua_isstring(L, 2)) {
1417 const char *key = lua_tostring(L, 2);
1418 if (strcmp(key, "mode") == 0) {
1419 enum VisMode mode = luaL_checkunsigned(L, 3);
1420 vis_mode_switch(vis, mode);
1421 return 0;
1424 if (strcmp(key, "count") == 0) {
1425 int count;
1426 if (lua_isnil(L, 3))
1427 count = VIS_COUNT_UNKNOWN;
1428 else
1429 count = luaL_checkunsigned(L, 3);
1430 vis_count_set(vis, count);
1431 return 0;
1434 if (strcmp(key, "win") == 0) {
1435 vis_window_focus(obj_ref_check(L, 3, VIS_LUA_TYPE_WINDOW));
1436 return 0;
1439 return newindex_common(L);
1442 static const struct luaL_Reg vis_lua[] = {
1443 { "files", files },
1444 { "windows", windows },
1445 { "mark_names", mark_names },
1446 { "register_names", register_names },
1447 { "command", command },
1448 { "info", info },
1449 { "message", message },
1450 { "map", map },
1451 { "unmap", unmap },
1452 { "mappings", mappings },
1453 { "operator", operator },
1454 { "operator_register", operator_register },
1455 { "motion", motion },
1456 { "motion_register", motion_register },
1457 { "textobject", textobject },
1458 { "textobject_register", textobject_register },
1459 { "option_register", option_register },
1460 { "option_unregister", option_unregister },
1461 { "command_register", command_register },
1462 { "feedkeys", feedkeys },
1463 { "insert", insert },
1464 { "replace", replace },
1465 { "action_register", action_register },
1466 { "exit", exit_func },
1467 { "pipe", pipe_func },
1468 { "__index", vis_index },
1469 { "__newindex", vis_newindex },
1470 { NULL, NULL },
1473 static const struct luaL_Reg ui_funcs[] = {
1474 { "__index", index_common },
1475 { NULL, NULL },
1478 static int registers_index(lua_State *L) {
1479 lua_newtable(L);
1480 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
1481 const char *symbol = luaL_checkstring(L, 2);
1482 if (strlen(symbol) != 1)
1483 return 1;
1484 enum VisRegister reg = vis_register_from(vis, symbol[0]);
1485 if (reg >= VIS_REG_INVALID)
1486 return 1;
1487 Array data = vis_register_get(vis, reg);
1488 for (size_t i = 0, len = array_length(&data); i < len; i++) {
1489 TextString *string = array_get(&data, i);
1490 lua_pushunsigned(L, i+1);
1491 lua_pushlstring(L, string->data, string->len);
1492 lua_settable(L, -3);
1494 array_release(&data);
1495 return 1;
1498 static int registers_newindex(lua_State *L) {
1499 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
1500 const char *symbol = luaL_checkstring(L, 2);
1501 if (strlen(symbol) != 1)
1502 return 0;
1503 enum VisRegister reg = vis_register_from(vis, symbol[0]);
1504 Array data;
1505 array_init_sized(&data, sizeof(TextString));
1507 if (lua_istable(L, 3)) {
1508 lua_pushnil(L);
1509 while (lua_next(L, 3)) {
1510 TextString string;
1511 string.data = luaL_checklstring(L, -1, &string.len);
1512 array_add(&data, &string);
1513 lua_pop(L, 1);
1517 vis_register_set(vis, reg, &data);
1518 array_release(&data);
1519 return 0;
1522 static int registers_len(lua_State *L) {
1523 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
1524 lua_pushunsigned(L, LENGTH(vis->registers));
1525 return 1;
1528 static const struct luaL_Reg registers_funcs[] = {
1529 { "__index", registers_index },
1530 { "__newindex", registers_newindex },
1531 { "__len", registers_len },
1532 { NULL, NULL },
1535 /***
1536 * A window object.
1537 * @type Window
1540 /***
1541 * Viewport currently being displayed.
1542 * @tfield Range viewport
1544 /***
1545 * The window width.
1546 * @tfield int width
1548 /***
1549 * The window height.
1550 * @tfield int height
1552 /***
1553 * The file being displayed in this window.
1554 * @tfield File file
1556 /***
1557 * The primary selection of this window.
1558 * @tfield Selection selection
1560 /***
1561 * The selections of this window.
1562 * @tfield Array(Selection) selections
1564 /***
1565 * Window marks.
1566 * Most of these marks are stored in the associated File object, meaning they
1567 * are the same in all windows displaying the same file.
1568 * @field marks array to access the marks of this window by single letter name
1569 * @see Vis:marks_names
1571 static int window_index(lua_State *L) {
1572 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1574 if (lua_isstring(L, 2)) {
1575 const char *key = lua_tostring(L, 2);
1577 if (strcmp(key, "viewport") == 0) {
1578 Filerange r = view_viewport_get(win->view);
1579 pushrange(L, &r);
1580 return 1;
1583 if (strcmp(key, "width") == 0) {
1584 lua_pushunsigned(L, vis_window_width_get(win));
1585 return 1;
1588 if (strcmp(key, "height") == 0) {
1589 lua_pushunsigned(L, vis_window_height_get(win));
1590 return 1;
1593 if (strcmp(key, "file") == 0) {
1594 obj_ref_new(L, win->file, VIS_LUA_TYPE_FILE);
1595 return 1;
1598 if (strcmp(key, "selection") == 0) {
1599 Selection *sel = view_selections_primary_get(win->view);
1600 obj_lightref_new(L, sel, VIS_LUA_TYPE_SELECTION);
1601 return 1;
1604 if (strcmp(key, "selections") == 0) {
1605 obj_ref_new(L, win->view, VIS_LUA_TYPE_SELECTIONS);
1606 return 1;
1609 if (strcmp(key, "marks") == 0) {
1610 obj_ref_new(L, &win->saved_selections, VIS_LUA_TYPE_MARKS);
1611 return 1;
1615 return index_common(L);
1618 static int window_selections_iterator_next(lua_State *L) {
1619 Selection **handle = lua_touserdata(L, lua_upvalueindex(1));
1620 if (!*handle)
1621 return 0;
1622 Selection *sel = obj_lightref_new(L, *handle, VIS_LUA_TYPE_SELECTION);
1623 if (!sel)
1624 return 0;
1625 *handle = view_selections_next(sel);
1626 return 1;
1629 /***
1630 * Create an iterator over all selections of this window.
1631 * @function selections_iterator
1632 * @return the new iterator
1634 static int window_selections_iterator(lua_State *L) {
1635 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1636 Selection **handle = lua_newuserdata(L, sizeof *handle);
1637 *handle = view_selections(win->view);
1638 lua_pushcclosure(L, window_selections_iterator_next, 1);
1639 return 1;
1642 /***
1643 * Set up a window local key mapping.
1644 * The function signatures are the same as for @{Vis:map}.
1645 * @function map
1646 * @param ...
1647 * @see Vis:map
1649 static int window_map(lua_State *L) {
1650 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1651 return keymap(L, win->vis, win);
1654 /***
1655 * Remove a window local key mapping.
1656 * The function signature is the same as for @{Vis:unmap}.
1657 * @function unmap
1658 * @param ...
1659 * @see Vis:unmap
1661 static int window_unmap(lua_State *L) {
1662 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1663 return keyunmap(L, win->vis, win);
1666 /***
1667 * Define a display style.
1668 * @function style_define
1669 * @tparam int id the style id to use
1670 * @tparam string style the style definition
1671 * @treturn bool whether the style definition has been successfully
1672 * associated with the given id
1673 * @see style
1674 * @usage
1675 * win:style_define(win.STYLE_DEFAULT, "fore:red")
1677 static int window_style_define(lua_State *L) {
1678 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1679 enum UiStyle id = luaL_checkunsigned(L, 2);
1680 const char *style = luaL_checkstring(L, 3);
1681 bool ret = view_style_define(win->view, id, style);
1682 lua_pushboolean(L, ret);
1683 return 1;
1686 /***
1687 * Style a window range.
1689 * The style will be cleared after every window redraw.
1690 * @function style
1691 * @tparam int id the display style as registered with @{style_define}
1692 * @tparam int start the absolute file position in bytes
1693 * @tparam int len the length in bytes to style
1694 * @see style_define
1695 * @usage
1696 * win:style(win.STYLE_DEFAULT, 0, 10)
1698 static int window_style(lua_State *L) {
1699 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1700 enum UiStyle style = luaL_checkunsigned(L, 2);
1701 size_t start = checkpos(L, 3);
1702 size_t end = checkpos(L, 4);
1703 view_style(win->view, style, start, end);
1704 return 0;
1707 /***
1708 * Set window status line.
1710 * @function status
1711 * @tparam string left the left aligned part of the status line
1712 * @tparam[opt] string right the right aligned part of the status line
1714 static int window_status(lua_State *L) {
1715 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1716 char status[1024] = "";
1717 int width = vis_window_width_get(win);
1718 const char *left = luaL_checkstring(L, 2);
1719 const char *right = luaL_optstring(L, 3, "");
1720 int left_width = text_string_width(left, strlen(left));
1721 int right_width = text_string_width(right, strlen(right));
1722 int spaces = width - left_width - right_width;
1723 if (spaces < 1)
1724 spaces = 1;
1725 snprintf(status, sizeof(status)-1, "%s%*s%s", left, spaces, " ", right);
1726 vis_window_status(win, status);
1727 return 0;
1730 /***
1731 * Redraw window content.
1733 * @function draw
1735 static int window_draw(lua_State *L) {
1736 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1737 view_draw(win->view);
1738 return 0;
1741 static const struct luaL_Reg window_funcs[] = {
1742 { "__index", window_index },
1743 { "__newindex", newindex_common },
1744 { "selections_iterator", window_selections_iterator },
1745 { "map", window_map },
1746 { "unmap", window_unmap },
1747 { "style_define", window_style_define },
1748 { "style", window_style },
1749 { "status", window_status },
1750 { "draw", window_draw },
1751 { NULL, NULL },
1754 static int window_selections_index(lua_State *L) {
1755 View *view = obj_ref_check(L, 1, VIS_LUA_TYPE_SELECTIONS);
1756 size_t index = luaL_checkunsigned(L, 2);
1757 size_t count = view_selections_count(view);
1758 if (index == 0 || index > count)
1759 goto err;
1760 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
1761 if (!--index) {
1762 obj_lightref_new(L, s, VIS_LUA_TYPE_SELECTION);
1763 return 1;
1766 err:
1767 lua_pushnil(L);
1768 return 1;
1771 static int window_selections_len(lua_State *L) {
1772 View *view = obj_ref_check(L, 1, VIS_LUA_TYPE_SELECTIONS);
1773 lua_pushunsigned(L, view_selections_count(view));
1774 return 1;
1777 static const struct luaL_Reg window_selections_funcs[] = {
1778 { "__index", window_selections_index },
1779 { "__len", window_selections_len },
1780 { NULL, NULL },
1783 /***
1784 * A selection object.
1786 * A selection is a non-empty, directed range with two endpoints called
1787 * *cursor* and *anchor*. A selection can be anchored in which case
1788 * the anchor remains fixed while only the position of the cursor is
1789 * adjusted. For non-anchored selections both endpoints are updated. A
1790 * singleton selection covers one character on which both cursor and
1791 * anchor reside. There always exists a primary selection which remains
1792 * visible (i.e. changes to its position will adjust the viewport).
1794 * The range covered by a selection is represented as an interval whose
1795 * endpoints are absolute byte offsets from the start of the file.
1796 * Valid addresses are within the closed interval `[0, file.size]`.
1798 * Selections are currently implemented using character marks into
1799 * the underlying persistent
1800 * [text management data structure](https://github.com/martanne/vis/wiki/Text-management-using-a-piece-chain).
1802 * This has a few consequences you should be aware of:
1804 * - A selection becomes invalid when the delimiting boundaries of the underlying
1805 * text it is referencing is deleted:
1807 * -- leaves selection in an invalid state
1808 * win.file:delete(win.selection.pos, 1)
1809 * assert(win.selection.pos == nil)
1811 * Like a regular mark it will become valid again when the text is reverted
1812 * to the state before the deletion.
1814 * - Inserts after the selection position (`> selection.pos`) will not affect the
1815 * selection postion.
1817 * local pos = win.selection.pos
1818 * win.file:insert(pos+1, "-")
1819 * assert(win.selection.pos == pos)
1821 * - Non-cached inserts before the selection position (`<= selection.pos`) will
1822 * affect the mark and adjust the selection postion by the number of bytes
1823 * which were inserted.
1825 * local pos = win.selection.pos
1826 * win.file:insert(pos, "-")
1827 * assert(win.selection.pos == pos+1)
1829 * - Cached inserts before the selection position (`<= selection.pos`) will
1830 * not affect the selection position because the underlying text is replaced
1831 * inplace.
1833 * For these reasons it is generally recommended to update the selection position
1834 * after a modification. The general procedure amounts to:
1836 * 1. Read out the current selection position
1837 * 2. Perform text modifications
1838 * 3. Update the selection postion
1840 * This is what @{Vis:insert} and @{Vis:replace} do internally.
1842 * @type Selection
1843 * @usage
1844 * local data = "new text"
1845 * local pos = win.selection.pos
1846 * win.file:insert(pos, data)
1847 * win.selection.pos = pos + #data
1850 /***
1851 * The zero based byte position in the file.
1853 * Might be `nil` if the selection is in an invalid state.
1854 * Setting this field will move the cursor endpoint of the
1855 * selection to the given position.
1856 * @tfield int pos
1858 /***
1859 * The 1-based line the cursor of this selection resides on.
1861 * @tfield int line
1862 * @see to
1864 /***
1865 * The 1-based column position the cursor of this selection resides on.
1866 * @tfield int col
1867 * @see to
1869 /***
1870 * The 1-based selection index.
1871 * @tfield int number
1873 /***
1874 * The range covered by this selection.
1875 * @tfield Range range
1877 /***
1878 * Whether this selection is anchored.
1879 * @tfield bool anchored
1881 static int window_selection_index(lua_State *L) {
1882 Selection *sel = obj_lightref_check(L, 1, VIS_LUA_TYPE_SELECTION);
1883 if (!sel) {
1884 lua_pushnil(L);
1885 return 1;
1888 if (lua_isstring(L, 2)) {
1889 const char *key = lua_tostring(L, 2);
1890 if (strcmp(key, "pos") == 0) {
1891 pushpos(L, view_cursors_pos(sel));
1892 return 1;
1895 if (strcmp(key, "line") == 0) {
1896 lua_pushunsigned(L, view_cursors_line(sel));
1897 return 1;
1900 if (strcmp(key, "col") == 0) {
1901 lua_pushunsigned(L, view_cursors_col(sel));
1902 return 1;
1905 if (strcmp(key, "number") == 0) {
1906 lua_pushunsigned(L, view_selections_number(sel)+1);
1907 return 1;
1910 if (strcmp(key, "range") == 0) {
1911 Filerange range = view_selections_get(sel);
1912 pushrange(L, &range);
1913 return 1;
1916 if (strcmp(key, "anchored") == 0) {
1917 lua_pushboolean(L, view_selections_anchored(sel));
1918 return 1;
1923 return index_common(L);
1926 static int window_selection_newindex(lua_State *L) {
1927 Selection *sel = obj_lightref_check(L, 1, VIS_LUA_TYPE_SELECTION);
1928 if (!sel)
1929 return 0;
1930 if (lua_isstring(L, 2)) {
1931 const char *key = lua_tostring(L, 2);
1932 if (strcmp(key, "pos") == 0) {
1933 size_t pos = checkpos(L, 3);
1934 view_cursors_to(sel, pos);
1935 return 0;
1938 if (strcmp(key, "range") == 0) {
1939 Filerange range = getrange(L, 3);
1940 if (text_range_valid(&range)) {
1941 view_selections_set(sel, &range);
1942 view_selections_anchor(sel, true);
1943 } else {
1944 view_selection_clear(sel);
1946 return 0;
1949 if (strcmp(key, "anchored") == 0) {
1950 view_selections_anchor(sel, lua_toboolean(L, 3));
1951 return 0;
1954 return newindex_common(L);
1957 /***
1958 * Move cursor of selection.
1959 * @function to
1960 * @tparam int line the 1-based line number
1961 * @tparam int col the 1-based column number
1963 static int window_selection_to(lua_State *L) {
1964 Selection *sel = obj_lightref_check(L, 1, VIS_LUA_TYPE_SELECTION);
1965 if (sel) {
1966 size_t line = checkpos(L, 2);
1967 size_t col = checkpos(L, 3);
1968 view_cursors_place(sel, line, col);
1970 return 0;
1973 static const struct luaL_Reg window_selection_funcs[] = {
1974 { "__index", window_selection_index },
1975 { "__newindex", window_selection_newindex },
1976 { "to", window_selection_to },
1977 { NULL, NULL },
1980 /***
1981 * A file object.
1982 * @type File
1984 /***
1985 * File name.
1986 * @tfield string name the file name relative to current working directory or `nil` if not yet named
1988 /***
1989 * File path.
1990 * @tfield string path the absolute file path or `nil` if not yet named
1992 /***
1993 * File content by logical lines.
1995 * Assigning to array element `0` (`#lines+1`) will insert a new line at
1996 * the beginning (end) of the file.
1997 * @tfield Array(string) lines the file content accessible as 1-based array
1998 * @see content
1999 * @usage
2000 * local lines = vis.win.file.lines
2001 * for i=1, #lines do
2002 * lines[i] = i .. ": " .. lines[i]
2003 * end
2005 /***
2006 * File size in bytes.
2007 * @tfield int size the current file size in bytes
2009 /***
2010 * File state.
2011 * @tfield bool modified whether the file contains unsaved changes
2013 static int file_index(lua_State *L) {
2014 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2016 if (lua_isstring(L, 2)) {
2017 const char *key = lua_tostring(L, 2);
2018 if (strcmp(key, "name") == 0) {
2019 lua_pushstring(L, file_name_get(file));
2020 return 1;
2023 if (strcmp(key, "path") == 0) {
2024 lua_pushstring(L, file->name);
2025 return 1;
2028 if (strcmp(key, "lines") == 0) {
2029 obj_ref_new(L, file->text, VIS_LUA_TYPE_TEXT);
2030 return 1;
2033 if (strcmp(key, "size") == 0) {
2034 lua_pushunsigned(L, text_size(file->text));
2035 return 1;
2038 if (strcmp(key, "modified") == 0) {
2039 lua_pushboolean(L, text_modified(file->text));
2040 return 1;
2044 return index_common(L);
2047 /***
2048 * Insert data at position.
2049 * @function insert
2050 * @tparam int pos the 0-based file position in bytes
2051 * @tparam string data the data to insert
2052 * @treturn bool whether the file content was successfully changed
2054 static int file_insert(lua_State *L) {
2055 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2056 size_t pos = checkpos(L, 2);
2057 size_t len;
2058 luaL_checkstring(L, 3);
2059 const char *data = lua_tolstring(L, 3, &len);
2060 lua_pushboolean(L, text_insert(file->text, pos, data, len));
2061 return 1;
2064 /***
2065 * Delete data at position.
2067 * @function delete
2068 * @tparam int pos the 0-based file position in bytes
2069 * @tparam int len the length in bytes to delete
2070 * @treturn bool whether the file content was successfully changed
2072 /***
2073 * Delete file range.
2075 * @function delete
2076 * @tparam Range range the range to delete
2077 * @treturn bool whether the file content was successfully changed
2079 static int file_delete(lua_State *L) {
2080 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2081 Filerange range = getrange(L, 2);
2082 lua_pushboolean(L, text_delete_range(file->text, &range));
2083 return 1;
2086 /***
2087 * Create an iterator over all lines of the file.
2089 * For large files this is probably faster than @{lines}.
2090 * @function lines_iterator
2091 * @return the new iterator
2092 * @see lines
2093 * @usage
2094 * for line in file:lines_iterator() do
2095 * -- do something with line
2096 * end
2098 static int file_lines_iterator_it(lua_State *L);
2099 static int file_lines_iterator(lua_State *L) {
2100 /* need to check second parameter first, because obj_ref_check_get
2101 * modifies the stack */
2102 size_t line = luaL_optunsigned(L, 2, 1);
2103 File *file = obj_ref_check_get(L, 1, VIS_LUA_TYPE_FILE);
2104 size_t *pos = lua_newuserdata(L, sizeof *pos);
2105 *pos = text_pos_by_lineno(file->text, line);
2106 lua_pushcclosure(L, file_lines_iterator_it, 2);
2107 return 1;
2110 static int file_lines_iterator_it(lua_State *L) {
2111 File *file = *(File**)lua_touserdata(L, lua_upvalueindex(1));
2112 size_t *start = lua_touserdata(L, lua_upvalueindex(2));
2113 if (*start == text_size(file->text))
2114 return 0;
2115 size_t end = text_line_end(file->text, *start);
2116 size_t len = end - *start;
2117 char *buf = malloc(len);
2118 if (!buf && len)
2119 return 0;
2120 len = text_bytes_get(file->text, *start, len, buf);
2121 lua_pushlstring(L, buf, len);
2122 free(buf);
2123 *start = text_line_next(file->text, end);
2124 return 1;
2127 /***
2128 * Get file content of position and length.
2130 * @function content
2131 * @tparam int pos the 0-based file position in bytes
2132 * @tparam int len the length in bytes to read
2133 * @treturn string the file content corresponding to the range
2134 * @see lines
2135 * @usage
2136 * local file = vis.win.file
2137 * local text = file:content(0, file.size)
2139 /***
2140 * Get file content of range.
2142 * @function content
2143 * @tparam Range range the range to read
2144 * @treturn string the file content corresponding to the range
2146 static int file_content(lua_State *L) {
2147 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2148 Filerange range = getrange(L, 2);
2149 if (!text_range_valid(&range))
2150 goto err;
2151 size_t len = text_range_size(&range);
2152 char *data = malloc(len);
2153 if (!data)
2154 goto err;
2155 len = text_bytes_get(file->text, range.start, len, data);
2156 lua_pushlstring(L, data, len);
2157 free(data);
2158 return 1;
2159 err:
2160 lua_pushnil(L);
2161 return 1;
2164 /***
2165 * Set mark.
2166 * @function mark_set
2167 * @tparam int pos the position to set the mark to, must be in [0, file.size]
2168 * @treturn Mark mark the mark which can be looked up later
2170 static int file_mark_set(lua_State *L) {
2171 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2172 size_t pos = checkpos(L, 2);
2173 Mark mark = text_mark_set(file->text, pos);
2174 if (mark)
2175 obj_lightref_new(L, (void*)mark, VIS_LUA_TYPE_MARK);
2176 else
2177 lua_pushnil(L);
2178 return 1;
2181 /***
2182 * Get position of mark.
2183 * @function mark_get
2184 * @tparam Mark mark the mark to look up
2185 * @treturn int pos the position of the mark, or `nil` if invalid
2187 static int file_mark_get(lua_State *L) {
2188 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2189 Mark mark = (Mark)obj_lightref_check(L, 2, VIS_LUA_TYPE_MARK);
2190 size_t pos = text_mark_get(file->text, mark);
2191 if (pos == EPOS)
2192 lua_pushnil(L);
2193 else
2194 lua_pushunsigned(L, pos);
2195 return 1;
2198 /***
2199 * Word text object.
2201 * @function text_object_word
2202 * @tparam int pos the position which must be part of the word
2203 * @treturn Range range the range
2206 static int file_text_object(lua_State *L) {
2207 Filerange range = text_range_empty();
2208 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2209 size_t pos = checkpos(L, 2);
2210 size_t idx = lua_tointeger(L, lua_upvalueindex(1));
2211 if (idx < LENGTH(vis_textobjects)) {
2212 const TextObject *txtobj = &vis_textobjects[idx];
2213 if (txtobj->txt)
2214 range = txtobj->txt(file->text, pos);
2216 pushrange(L, &range);
2217 return 1;
2220 static const struct luaL_Reg file_funcs[] = {
2221 { "__index", file_index },
2222 { "__newindex", newindex_common },
2223 { "insert", file_insert },
2224 { "delete", file_delete },
2225 { "lines_iterator", file_lines_iterator },
2226 { "content", file_content },
2227 { "mark_set", file_mark_set },
2228 { "mark_get", file_mark_get },
2229 { NULL, NULL },
2232 static int file_lines_index(lua_State *L) {
2233 Text *txt = obj_ref_check(L, 1, VIS_LUA_TYPE_TEXT);
2234 size_t line = luaL_checkunsigned(L, 2);
2235 size_t start = text_pos_by_lineno(txt, line);
2236 size_t end = text_line_end(txt, start);
2237 if (start != EPOS && end != EPOS) {
2238 size_t size = end - start;
2239 char *data = malloc(size);
2240 if (!data && size)
2241 goto err;
2242 size = text_bytes_get(txt, start, size, data);
2243 lua_pushlstring(L, data, size);
2244 free(data);
2245 return 1;
2247 err:
2248 lua_pushnil(L);
2249 return 1;
2252 static int file_lines_newindex(lua_State *L) {
2253 Text *txt = obj_ref_check(L, 1, VIS_LUA_TYPE_TEXT);
2254 size_t line = luaL_checkunsigned(L, 2);
2255 size_t size;
2256 const char *data = luaL_checklstring(L, 3, &size);
2257 if (line == 0) {
2258 text_insert(txt, 0, data, size);
2259 text_insert(txt, size, "\n", 1);
2260 return 0;
2262 size_t start = text_pos_by_lineno(txt, line);
2263 size_t end = text_line_end(txt, start);
2264 if (start != EPOS && end != EPOS) {
2265 text_delete(txt, start, end - start);
2266 text_insert(txt, start, data, size);
2267 if (text_size(txt) == start + size)
2268 text_insert(txt, text_size(txt), "\n", 1);
2270 return 0;
2273 static int file_lines_len(lua_State *L) {
2274 Text *txt = obj_ref_check(L, 1, VIS_LUA_TYPE_TEXT);
2275 size_t lines = 0;
2276 char lastchar;
2277 size_t size = text_size(txt);
2278 if (size > 0)
2279 lines = text_lineno_by_pos(txt, size);
2280 if (lines > 1 && text_byte_get(txt, size-1, &lastchar) && lastchar == '\n')
2281 lines--;
2282 lua_pushunsigned(L, lines);
2283 return 1;
2286 static const struct luaL_Reg file_lines_funcs[] = {
2287 { "__index", file_lines_index },
2288 { "__newindex", file_lines_newindex },
2289 { "__len", file_lines_len },
2290 { NULL, NULL },
2293 static int window_marks_index(lua_State *L) {
2294 lua_newtable(L);
2295 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
2296 Win *win = obj_ref_check_containerof(L, 1, VIS_LUA_TYPE_MARKS, offsetof(Win, saved_selections));
2297 if (!win)
2298 return 1;
2299 const char *symbol = luaL_checkstring(L, 2);
2300 if (strlen(symbol) != 1)
2301 return 1;
2302 enum VisMark mark = vis_mark_from(vis, symbol[0]);
2303 if (mark == VIS_MARK_INVALID)
2304 return 1;
2306 Array arr = vis_mark_get(win, mark);
2307 for (size_t i = 0, len = array_length(&arr); i < len; i++) {
2308 Filerange *range = array_get(&arr, i);
2309 lua_pushunsigned(L, i+1);
2310 pushrange(L, range);
2311 lua_settable(L, -3);
2313 array_release(&arr);
2314 return 1;
2317 static int window_marks_newindex(lua_State *L) {
2318 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
2319 Win *win = obj_ref_check_containerof(L, 1, VIS_LUA_TYPE_MARKS, offsetof(Win, saved_selections));
2320 if (!win)
2321 return 0;
2322 const char *symbol = luaL_checkstring(L, 2);
2323 if (strlen(symbol) != 1)
2324 return 0;
2325 enum VisMark mark = vis_mark_from(vis, symbol[0]);
2326 if (mark == VIS_MARK_INVALID)
2327 return 0;
2329 Array ranges;
2330 array_init_sized(&ranges, sizeof(Filerange));
2332 if (lua_istable(L, 3)) {
2333 lua_pushnil(L);
2334 while (lua_next(L, 3)) {
2335 Filerange range = getrange(L, -1);
2336 if (text_range_valid(&range))
2337 array_add(&ranges, &range);
2338 lua_pop(L, 1);
2342 vis_mark_set(win, mark, &ranges);
2343 array_release(&ranges);
2344 return 0;
2347 static int window_marks_len(lua_State *L) {
2348 lua_pushunsigned(L, VIS_MARK_INVALID);
2349 return 1;
2352 static const struct luaL_Reg window_marks_funcs[] = {
2353 { "__index", window_marks_index },
2354 { "__newindex", window_marks_newindex },
2355 { "__len", window_marks_len },
2356 { NULL, NULL },
2359 /***
2360 * The user interface.
2362 * @type Ui
2364 /***
2365 * Number of available colors.
2366 * @tfield int colors
2369 /***
2370 * A file range.
2372 * For a valid range `start <= finish` holds.
2373 * An invalid range is represented as `nil`.
2374 * @type Range
2376 /***
2377 * The being of the range.
2378 * @tfield int start
2380 /***
2381 * The end of the range.
2382 * @tfield int finish
2385 /***
2386 * Modes.
2387 * @section Modes
2390 /***
2391 * Mode constants.
2392 * @table modes
2393 * @tfield int NORMAL
2394 * @tfield int OPERATOR_PENDING
2395 * @tfield int INSERT
2396 * @tfield int REPLACE
2397 * @tfield int VISUAL
2398 * @tfield int VISUAL_LINE
2399 * @see Vis:map
2400 * @see Window:map
2403 /***
2404 * Key Handling.
2406 * This section describes the contract between the editor core and Lua
2407 * key handling functions mapped to symbolic keys using either @{Vis:map}
2408 * or @{Window:map}.
2410 * @section Key_Handling
2413 /***
2414 * Example of a key handling function.
2416 * The keyhandler is invoked with the pending content of the input queue
2417 * given as argument. This might be the empty string if no further input
2418 * is available.
2420 * The function is expected to return the number of *bytes* it has
2421 * consumed from the passed input keys. A negative return value is
2422 * interpreted as an indication that not enough input was available. The
2423 * function will be called again once the user has provided more input. A
2424 * missing return value (i.e. `nil`) is interpreted as zero, meaning
2425 * no further input was consumed but the function completed successfully.
2427 * @function keyhandler
2428 * @tparam string keys the keys following the mapping
2429 * @treturn int the number of *bytes* being consumed by the function (see above)
2430 * @see Vis:action_register
2431 * @see Vis:map
2432 * @see Window:map
2433 * @usage
2434 * vis:map(vis.modes.INSERT, "<C-k>", function(keys)
2435 * if #keys < 2 then
2436 * return -1 -- need more input
2437 * end
2438 * local digraph = keys:sub(1, 2)
2439 * if digraph == "l*" then
2440 * vis:feedkeys('λ')
2441 * return 2 -- consume 2 bytes of input
2442 * end
2443 * end, "Insert digraph")
2446 /***
2447 * Core Events.
2449 * These events are invoked from the editor core.
2450 * The following functions are invoked if they are registered in the
2451 * `vis.events` table. Users scripts should generally use the [Events](#events)
2452 * mechanism instead which multiplexes these core events.
2454 * @section Core_Events
2457 static void vis_lua_event_get(lua_State *L, const char *name) {
2458 lua_getglobal(L, "vis");
2459 lua_getfield(L, -1, "events");
2460 if (lua_istable(L, -1)) {
2461 lua_getfield(L, -1, name);
2463 lua_remove(L, -2);
2466 static void vis_lua_event_call(Vis *vis, const char *name) {
2467 lua_State *L = vis->lua;
2468 vis_lua_event_get(L, name);
2469 if (lua_isfunction(L, -1))
2470 pcall(vis, L, 0, 0);
2471 lua_pop(L, 1);
2474 static bool vis_lua_path_strip(Vis *vis) {
2475 lua_State *L = vis->lua;
2476 lua_getglobal(L, "package");
2478 for (const char **var = (const char*[]){ "path", "cpath", NULL }; *var; var++) {
2480 lua_getfield(L, -1, *var);
2481 const char *path = lua_tostring(L, -1);
2482 lua_pop(L, 1);
2483 if (!path)
2484 return false;
2486 char *copy = strdup(path), *stripped = calloc(1, strlen(path)+2);
2487 if (!copy || !stripped) {
2488 free(copy);
2489 free(stripped);
2490 return false;
2493 for (char *elem = copy, *stripped_elem = stripped, *next; elem; elem = next) {
2494 if ((next = strstr(elem, ";")))
2495 *next++ = '\0';
2496 if (strstr(elem, "./"))
2497 continue; /* skip relative path entries */
2498 stripped_elem += sprintf(stripped_elem, "%s;", elem);
2501 lua_pushstring(L, stripped);
2502 lua_setfield(L, -2, *var);
2504 free(copy);
2505 free(stripped);
2508 lua_pop(L, 1); /* package */
2509 return true;
2512 bool vis_lua_path_add(Vis *vis, const char *path) {
2513 lua_State *L = vis->lua;
2514 if (!L || !path)
2515 return false;
2516 lua_getglobal(L, "package");
2517 lua_pushstring(L, path);
2518 lua_pushstring(L, "/?.lua;");
2519 lua_getfield(L, -3, "path");
2520 lua_concat(L, 3);
2521 lua_setfield(L, -2, "path");
2522 lua_pop(L, 1); /* package */
2523 return true;
2526 bool vis_lua_paths_get(Vis *vis, char **lpath, char **cpath) {
2527 lua_State *L = vis->lua;
2528 if (!L)
2529 return false;
2530 const char *s;
2531 lua_getglobal(L, "package");
2532 lua_getfield(L, -1, "path");
2533 s = lua_tostring(L, -1);
2534 *lpath = s ? strdup(s) : NULL;
2535 lua_getfield(L, -2, "cpath");
2536 s = lua_tostring(L, -1);
2537 *cpath = s ? strdup(s) : NULL;
2538 return true;
2541 static bool package_exist(Vis *vis, lua_State *L, const char *name) {
2542 const char lua[] =
2543 "local name = ...\n"
2544 "for _, searcher in ipairs(package.searchers or package.loaders) do\n"
2545 "local loader = searcher(name)\n"
2546 "if type(loader) == 'function' then\n"
2547 "return true\n"
2548 "end\n"
2549 "end\n"
2550 "return false\n";
2551 if (luaL_loadstring(L, lua) != LUA_OK)
2552 return false;
2553 lua_pushstring(L, name);
2554 /* an error indicates package exists */
2555 bool ret = lua_pcall(L, 1, 1, 0) != LUA_OK || lua_toboolean(L, -1);
2556 lua_pop(L, 1);
2557 return ret;
2560 static void *alloc_lua(void *ud, void *ptr, size_t osize, size_t nsize) {
2561 if (nsize == 0) {
2562 free(ptr);
2563 return NULL;
2564 } else {
2565 return realloc(ptr, nsize);
2569 /***
2570 * Editor initialization completed.
2571 * This event is emitted immediately after `visrc.lua` has been sourced, but
2572 * before any other events have occured, in particular the command line arguments
2573 * have not yet been processed.
2575 * Can be used to set *global* configuration options.
2576 * @function init
2578 void vis_lua_init(Vis *vis) {
2579 lua_State *L = lua_newstate(alloc_lua, vis);
2580 if (!L)
2581 return;
2582 vis->lua = L;
2583 lua_atpanic(L, &panic_handler);
2585 luaL_openlibs(L);
2587 #if CONFIG_LPEG
2588 extern int luaopen_lpeg(lua_State *L);
2589 lua_getglobal(L, "package");
2590 lua_getfield(L, -1, "preload");
2591 lua_pushcfunction(L, luaopen_lpeg);
2592 lua_setfield(L, -2, "lpeg");
2593 lua_pop(L, 2);
2594 #endif
2596 /* remove any relative paths from lua's default package.path */
2597 vis_lua_path_strip(vis);
2599 /* extends lua's package.path with:
2600 * - $VIS_PATH
2601 * - ./lua (relative path to the binary location)
2602 * - $XDG_CONFIG_HOME/vis (defaulting to $HOME/.config/vis)
2603 * - /etc/vis (for system-wide configuration provided by administrator)
2604 * - /usr/(local/)?share/vis (or whatever is specified during ./configure)
2605 * - package.path (standard lua search path)
2607 char path[PATH_MAX];
2609 vis_lua_path_add(vis, VIS_PATH);
2611 /* try to get users home directory */
2612 const char *home = getenv("HOME");
2613 if (!home || !*home) {
2614 struct passwd *pw = getpwuid(getuid());
2615 if (pw)
2616 home = pw->pw_dir;
2619 vis_lua_path_add(vis, "/etc/vis");
2621 const char *xdg_config = getenv("XDG_CONFIG_HOME");
2622 if (xdg_config) {
2623 snprintf(path, sizeof path, "%s/vis", xdg_config);
2624 vis_lua_path_add(vis, path);
2625 } else if (home && *home) {
2626 snprintf(path, sizeof path, "%s/.config/vis", home);
2627 vis_lua_path_add(vis, path);
2630 ssize_t len = readlink("/proc/self/exe", path, sizeof(path)-1);
2631 if (len > 0) {
2632 path[len] = '\0';
2633 /* some idotic dirname(3) implementations return pointers to statically
2634 * allocated memory, hence we use memmove to copy it back */
2635 char *dir = dirname(path);
2636 if (dir) {
2637 size_t len = strlen(dir)+1;
2638 if (len < sizeof(path) - sizeof("/lua")) {
2639 memmove(path, dir, len);
2640 strcat(path, "/lua");
2641 vis_lua_path_add(vis, path);
2646 vis_lua_path_add(vis, getenv("VIS_PATH"));
2648 /* table in registry to lookup object type, stores metatable -> type mapping */
2649 lua_newtable(L);
2650 lua_setfield(L, LUA_REGISTRYINDEX, "vis.types");
2651 /* table in registry to track lifetimes of C objects */
2652 lua_newtable(L);
2653 lua_setfield(L, LUA_REGISTRYINDEX, "vis.objects");
2654 /* table in registry to store references to Lua functions */
2655 lua_newtable(L);
2656 lua_setfield(L, LUA_REGISTRYINDEX, "vis.functions");
2657 /* metatable used to type check user data */
2658 obj_type_new(L, VIS_LUA_TYPE_VIS);
2659 luaL_setfuncs(L, vis_lua, 0);
2660 lua_newtable(L);
2661 lua_setfield(L, -2, "types");
2662 /* create reference to main vis object, such that the further
2663 * calls to obj_type_new can register the type meta tables in
2664 * vis.types[name] */
2665 obj_ref_new(L, vis, "vis");
2666 lua_setglobal(L, "vis");
2668 obj_type_new(L, VIS_LUA_TYPE_FILE);
2670 const struct {
2671 enum VisTextObject id;
2672 const char *name;
2673 } textobjects[] = {
2674 { VIS_TEXTOBJECT_INNER_WORD, "text_object_word" },
2677 for (size_t i = 0; i < LENGTH(textobjects); i++) {
2678 lua_pushunsigned(L, textobjects[i].id);
2679 lua_pushcclosure(L, file_text_object, 1);
2680 lua_setfield(L, -2, textobjects[i].name);
2683 luaL_setfuncs(L, file_funcs, 0);
2685 obj_type_new(L, VIS_LUA_TYPE_TEXT);
2686 luaL_setfuncs(L, file_lines_funcs, 0);
2687 obj_type_new(L, VIS_LUA_TYPE_WINDOW);
2688 luaL_setfuncs(L, window_funcs, 0);
2690 const struct {
2691 enum UiStyle id;
2692 const char *name;
2693 } styles[] = {
2694 { UI_STYLE_DEFAULT, "STYLE_DEFAULT" },
2695 { UI_STYLE_CURSOR, "STYLE_CURSOR" },
2696 { UI_STYLE_CURSOR_PRIMARY, "STYLE_CURSOR_PRIMARY" },
2697 { UI_STYLE_CURSOR_LINE, "STYLE_CURSOR_LINE" },
2698 { UI_STYLE_SELECTION, "STYLE_SELECTION" },
2699 { UI_STYLE_LINENUMBER, "STYLE_LINENUMBER" },
2700 { UI_STYLE_LINENUMBER_CURSOR, "STYLE_LINENUMBER_CURSOR" },
2701 { UI_STYLE_COLOR_COLUMN, "STYLE_COLOR_COLUMN" },
2702 { UI_STYLE_STATUS, "STYLE_STATUS" },
2703 { UI_STYLE_STATUS_FOCUSED, "STYLE_STATUS_FOCUSED" },
2704 { UI_STYLE_SEPARATOR, "STYLE_SEPARATOR" },
2705 { UI_STYLE_INFO, "STYLE_INFO" },
2706 { UI_STYLE_EOF, "STYLE_EOF" },
2709 for (size_t i = 0; i < LENGTH(styles); i++) {
2710 lua_pushunsigned(L, styles[i].id);
2711 lua_setfield(L, -2, styles[i].name);
2714 obj_type_new(L, VIS_LUA_TYPE_MARK);
2715 obj_type_new(L, VIS_LUA_TYPE_MARKS);
2716 lua_pushlightuserdata(L, vis);
2717 luaL_setfuncs(L, window_marks_funcs, 1);
2719 obj_type_new(L, VIS_LUA_TYPE_SELECTION);
2720 luaL_setfuncs(L, window_selection_funcs, 0);
2721 obj_type_new(L, VIS_LUA_TYPE_SELECTIONS);
2722 luaL_setfuncs(L, window_selections_funcs, 0);
2724 obj_type_new(L, VIS_LUA_TYPE_UI);
2725 luaL_setfuncs(L, ui_funcs, 0);
2726 lua_pushunsigned(L, vis->ui->colors(vis->ui));
2727 lua_setfield(L, -2, "colors");
2729 obj_type_new(L, VIS_LUA_TYPE_REGISTERS);
2730 lua_pushlightuserdata(L, vis);
2731 luaL_setfuncs(L, registers_funcs, 1);
2733 obj_type_new(L, VIS_LUA_TYPE_KEYACTION);
2735 lua_getglobal(L, "vis");
2736 lua_getmetatable(L, -1);
2738 lua_pushstring(L, VERSION);
2739 lua_setfield(L, -2, "VERSION");
2741 lua_newtable(L);
2743 static const struct {
2744 enum VisMode id;
2745 const char *name;
2746 } modes[] = {
2747 { VIS_MODE_NORMAL, "NORMAL" },
2748 { VIS_MODE_OPERATOR_PENDING, "OPERATOR_PENDING" },
2749 { VIS_MODE_VISUAL, "VISUAL" },
2750 { VIS_MODE_VISUAL_LINE, "VISUAL_LINE" },
2751 { VIS_MODE_INSERT, "INSERT" },
2752 { VIS_MODE_REPLACE, "REPLACE" },
2755 for (size_t i = 0; i < LENGTH(modes); i++) {
2756 lua_pushunsigned(L, modes[i].id);
2757 lua_setfield(L, -2, modes[i].name);
2760 lua_setfield(L, -2, "modes");
2762 if (!package_exist(vis, L, "visrc")) {
2763 vis_info_show(vis, "WARNING: failed to load visrc.lua");
2764 } else {
2765 lua_getglobal(L, "require");
2766 lua_pushstring(L, "visrc");
2767 pcall(vis, L, 1, 0);
2768 vis_lua_event_call(vis, "init");
2772 /***
2773 * Editor startup completed.
2774 * This event is emitted immediately before the main loop starts.
2775 * At this point all files are loaded and corresponding windows are created.
2776 * We are about to process interactive keyboard input.
2777 * @function start
2779 void vis_lua_start(Vis *vis) {
2780 vis_lua_event_call(vis, "start");
2784 * Editor is about to terminate.
2785 * @function quit
2787 void vis_lua_quit(Vis *vis) {
2788 if (!vis->lua)
2789 return;
2790 vis_lua_event_call(vis, "quit");
2791 lua_close(vis->lua);
2792 vis->lua = NULL;
2795 /***
2796 * Input key event in either input or replace mode.
2797 * @function input
2798 * @tparam string key
2799 * @treturn bool whether the key was cosumed or not
2801 static bool vis_lua_input(Vis *vis, const char *key, size_t len) {
2802 lua_State *L = vis->lua;
2803 if (!L || vis->win->file->internal)
2804 return false;
2805 bool ret = false;
2806 vis_lua_event_get(L, "input");
2807 if (lua_isfunction(L, -1)) {
2808 lua_pushlstring(L, key, len);
2809 if (pcall(vis, L, 1, 1) == 0) {
2810 ret = lua_isboolean(L, -1) && lua_toboolean(L, -1);
2811 lua_pop(L, 1);
2814 lua_pop(L, 1);
2815 return ret;
2818 void vis_lua_mode_insert_input(Vis *vis, const char *key, size_t len) {
2819 if (!vis_lua_input(vis, key, len))
2820 vis_insert_key(vis, key, len);
2823 void vis_lua_mode_replace_input(Vis *vis, const char *key, size_t len) {
2824 if (!vis_lua_input(vis, key, len))
2825 vis_replace_key(vis, key, len);
2828 /***
2829 * File open.
2830 * @function file_open
2831 * @tparam File file the file to be opened
2833 void vis_lua_file_open(Vis *vis, File *file) {
2834 debug("event: file-open: %s %p %p\n", file->name ? file->name : "unnamed", (void*)file, (void*)file->text);
2835 lua_State *L = vis->lua;
2836 if (!L)
2837 return;
2838 vis_lua_event_get(L, "file_open");
2839 if (lua_isfunction(L, -1)) {
2840 obj_ref_new(L, file, VIS_LUA_TYPE_FILE);
2841 pcall(vis, L, 1, 0);
2843 lua_pop(L, 1);
2846 /***
2847 * File pre save.
2848 * Triggered *before* the file is being written.
2849 * @function file_save_pre
2850 * @tparam File file the file being written
2851 * @tparam string path the absolute path to which the file will be written, `nil` if standard output
2852 * @treturn bool whether the write operation should be proceeded
2854 bool vis_lua_file_save_pre(Vis *vis, File *file, const char *path) {
2855 lua_State *L = vis->lua;
2856 if (!L)
2857 return true;
2858 vis_lua_event_get(L, "file_save_pre");
2859 if (lua_isfunction(L, -1)) {
2860 obj_ref_new(L, file, VIS_LUA_TYPE_FILE);
2861 lua_pushstring(L, path);
2862 if (pcall(vis, L, 2, 1) != 0)
2863 return false;
2864 return !lua_isboolean(L, -1) || lua_toboolean(L, -1);
2866 lua_pop(L, 1);
2867 return true;
2870 /***
2871 * File post save.
2872 * Triggered *after* a successfull write operation.
2873 * @function file_save_post
2874 * @tparam File file the file which was written
2875 * @tparam string path the absolute path to which it was written, `nil` if standard output
2877 void vis_lua_file_save_post(Vis *vis, File *file, const char *path) {
2878 lua_State *L = vis->lua;
2879 if (!L)
2880 return;
2881 vis_lua_event_get(L, "file_save_post");
2882 if (lua_isfunction(L, -1)) {
2883 obj_ref_new(L, file, VIS_LUA_TYPE_FILE);
2884 lua_pushstring(L, path);
2885 pcall(vis, L, 2, 0);
2887 lua_pop(L, 1);
2890 /***
2891 * File close.
2892 * The last window displaying the file has been closed.
2893 * @function file_close
2894 * @tparam File file the file being closed
2896 void vis_lua_file_close(Vis *vis, File *file) {
2897 debug("event: file-close: %s %p %p\n", file->name ? file->name : "unnamed", (void*)file, (void*)file->text);
2898 lua_State *L = vis->lua;
2899 if (!L)
2900 return;
2901 vis_lua_event_get(L, "file_close");
2902 if (lua_isfunction(L, -1)) {
2903 obj_ref_new(L, file, VIS_LUA_TYPE_FILE);
2904 pcall(vis, L, 1, 0);
2906 obj_ref_free(L, file->marks);
2907 obj_ref_free(L, file->text);
2908 obj_ref_free(L, file);
2909 lua_pop(L, 1);
2912 /***
2913 * Window open.
2914 * A new window has been created.
2915 * @function win_open
2916 * @tparam Window win the window being opened
2918 void vis_lua_win_open(Vis *vis, Win *win) {
2919 debug("event: win-open: %s %p %p\n", win->file->name ? win->file->name : "unnamed", (void*)win, (void*)win->view);
2920 lua_State *L = vis->lua;
2921 if (!L)
2922 return;
2923 vis_lua_event_get(L, "win_open");
2924 if (lua_isfunction(L, -1)) {
2925 obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW);
2926 pcall(vis, L, 1, 0);
2928 lua_pop(L, 1);
2931 /***
2932 * Window close.
2933 * An window is being closed.
2934 * @function win_close
2935 * @tparam Window win the window being closed
2937 void vis_lua_win_close(Vis *vis, Win *win) {
2938 debug("event: win-close: %s %p %p\n", win->file->name ? win->file->name : "unnamed", (void*)win, (void*)win->view);
2939 lua_State *L = vis->lua;
2940 if (!L)
2941 return;
2942 vis_lua_event_get(L, "win_close");
2943 if (lua_isfunction(L, -1)) {
2944 obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW);
2945 pcall(vis, L, 1, 0);
2947 obj_ref_free(L, win->view);
2948 obj_ref_free(L, win);
2949 lua_pop(L, 1);
2953 * Window highlight.
2954 * The window has been redrawn and the syntax highlighting needs to be performed.
2955 * @function win_highlight
2956 * @tparam Window win the window being redrawn
2957 * @see style
2959 void vis_lua_win_highlight(Vis *vis, Win *win) {
2960 lua_State *L = vis->lua;
2961 if (!L)
2962 return;
2963 vis_lua_event_get(L, "win_highlight");
2964 if (lua_isfunction(L, -1)) {
2965 obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW);
2966 pcall(vis, L, 1, 0);
2968 lua_pop(L, 1);
2971 /***
2972 * Window status bar redraw.
2973 * @function win_status
2974 * @tparam Window win the affected window
2975 * @see status
2977 void vis_lua_win_status(Vis *vis, Win *win) {
2978 lua_State *L = vis->lua;
2979 if (!L || win->file->internal) {
2980 window_status_update(vis, win);
2981 return;
2983 vis_lua_event_get(L, "win_status");
2984 if (lua_isfunction(L, -1)) {
2985 obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW);
2986 pcall(vis, L, 1, 0);
2987 } else {
2988 window_status_update(vis, win);
2990 lua_pop(L, 1);
2993 #endif