vis-lua: provide file.permission property
[vis.git] / vis-lua.c
blob064234393c0678616c5b4c808ad682b9f73085bd
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[0]), "%s%s%s",
77 filename ? filename : "[No Name]",
78 text_modified(txt) ? " [+]" : "",
79 vis_macro_recording(vis) ? " @": "");
81 int count = vis_count_get(vis);
82 const char *keys = buffer_content0(&vis->input_queue);
83 if (keys && keys[0])
84 snprintf(right_parts[right_count++], sizeof(right_parts[0]), "%s", keys);
85 else if (count != VIS_COUNT_UNKNOWN)
86 snprintf(right_parts[right_count++], sizeof(right_parts[0]), "%d", count);
88 int sel_count = view_selections_count(view);
89 if (sel_count > 1) {
90 Selection *s = view_selections_primary_get(view);
91 int sel_number = view_selections_number(s) + 1;
92 snprintf(right_parts[right_count++], sizeof(right_parts[0]),
93 "%d/%d", sel_number, sel_count);
96 size_t size = text_size(txt);
97 size_t pos = view_cursor_get(view);
98 size_t percent = 0;
99 if (size > 0) {
100 double tmp = ((double)pos/(double)size)*100;
101 percent = (size_t)(tmp+1);
103 snprintf(right_parts[right_count++], sizeof(right_parts[0]),
104 "%zu%%", percent);
106 if (!(options & UI_OPTION_LARGE_FILE)) {
107 Selection *sel = view_selections_primary_get(win->view);
108 size_t line = view_cursors_line(sel);
109 size_t col = view_cursors_col(sel);
110 if (col > UI_LARGE_FILE_LINE_SIZE) {
111 options |= UI_OPTION_LARGE_FILE;
112 view_options_set(win->view, options);
114 snprintf(right_parts[right_count++], sizeof(right_parts[0]),
115 "%zu, %zu", line, col);
118 int left_len = snprintf(left, sizeof(left), " %s%s%s%s%s%s%s",
119 left_parts[0],
120 left_parts[1][0] ? " » " : "",
121 left_parts[1],
122 left_parts[2][0] ? " » " : "",
123 left_parts[2],
124 left_parts[3][0] ? " » " : "",
125 left_parts[3]);
127 int right_len = snprintf(right, sizeof(right), "%s%s%s%s%s%s%s ",
128 right_parts[0],
129 right_parts[1][0] ? " « " : "",
130 right_parts[1],
131 right_parts[2][0] ? " « " : "",
132 right_parts[2],
133 right_parts[3][0] ? " « " : "",
134 right_parts[3]);
136 if (left_len < 0 || right_len < 0)
137 return;
138 int left_width = text_string_width(left, left_len);
139 int right_width = text_string_width(right, right_len);
141 int spaces = width - left_width - right_width;
142 if (spaces < 1)
143 spaces = 1;
145 snprintf(status, sizeof(status), "%s%*s%s", left, spaces, " ", right);
146 vis_window_status(win, status);
149 #if !CONFIG_LUA
151 bool vis_lua_path_add(Vis *vis, const char *path) { return true; }
152 bool vis_lua_paths_get(Vis *vis, char **lpath, char **cpath) { return false; }
153 void vis_lua_init(Vis *vis) { }
154 void vis_lua_start(Vis *vis) { }
155 void vis_lua_quit(Vis *vis) { }
156 void vis_lua_file_open(Vis *vis, File *file) { }
157 bool vis_lua_file_save_pre(Vis *vis, File *file, const char *path) { return true; }
158 void vis_lua_file_save_post(Vis *vis, File *file, const char *path) { }
159 void vis_lua_file_close(Vis *vis, File *file) { }
160 void vis_lua_win_open(Vis *vis, Win *win) { }
161 void vis_lua_win_close(Vis *vis, Win *win) { }
162 void vis_lua_win_highlight(Vis *vis, Win *win) { }
163 void vis_lua_win_status(Vis *vis, Win *win) { window_status_update(vis, win); }
164 void vis_lua_term_csi(Vis *vis, const long *csi) { }
166 #else
168 #if DEBUG_LUA
169 static void stack_dump_entry(lua_State *L, int i) {
170 int t = lua_type(L, i);
171 switch (t) {
172 case LUA_TNIL:
173 printf("nil");
174 break;
175 case LUA_TBOOLEAN:
176 printf(lua_toboolean(L, i) ? "true" : "false");
177 break;
178 case LUA_TLIGHTUSERDATA:
179 printf("lightuserdata(%p)", lua_touserdata(L, i));
180 break;
181 case LUA_TNUMBER:
182 printf("%g", lua_tonumber(L, i));
183 break;
184 case LUA_TSTRING:
185 printf("`%s'", lua_tostring(L, i));
186 break;
187 case LUA_TTABLE:
188 printf("table[");
189 lua_pushnil(L); /* first key */
190 while (lua_next(L, i > 0 ? i : i - 1)) {
191 stack_dump_entry(L, -2);
192 printf("=");
193 stack_dump_entry(L, -1);
194 printf(",");
195 lua_pop(L, 1); /* remove value, keep key */
197 printf("]");
198 break;
199 case LUA_TUSERDATA:
200 printf("userdata(%p)", lua_touserdata(L, i));
201 break;
202 default: /* other values */
203 printf("%s", lua_typename(L, t));
204 break;
208 static void stack_dump(lua_State *L, const char *format, ...) {
209 va_list ap;
210 va_start(ap, format);
211 vprintf(format, ap);
212 va_end(ap);
213 int top = lua_gettop(L);
214 for (int i = 1; i <= top; i++) {
215 printf("%d: ", i);
216 stack_dump_entry(L, i);
217 printf("\n");
219 printf("\n\n");
220 fflush(stdout);
223 #endif
225 static int panic_handler(lua_State *L) {
226 void *ud = NULL;
227 lua_getallocf(L, &ud);
228 if (ud) {
229 Vis *vis = ud;
230 vis->lua = NULL;
231 const char *msg = NULL;
232 if (lua_type(L, -1) == LUA_TSTRING)
233 msg = lua_tostring(L, -1);
234 vis_info_show(vis, "Fatal Lua error: %s", msg ? msg : "unknown reason");
235 lua_close(L);
236 if (vis->running)
237 siglongjmp(vis->sigbus_jmpbuf, 1);
239 return 0;
242 static int error_handler(lua_State *L) {
243 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
244 if (vis->errorhandler)
245 return 1;
246 vis->errorhandler = true;
247 size_t len;
248 const char *msg = lua_tostring(L, 1);
249 if (msg)
250 luaL_traceback(L, L, msg, 1);
251 msg = lua_tolstring(L, 1, &len);
252 vis_message_show(vis, msg);
253 vis->errorhandler = false;
254 return 1;
257 static int pcall(Vis *vis, lua_State *L, int nargs, int nresults) {
258 /* insert a custom error function below all arguments */
259 int msgh = lua_gettop(L) - nargs;
260 lua_pushlightuserdata(L, vis);
261 lua_pushcclosure(L, error_handler, 1);
262 lua_insert(L, msgh);
263 int ret = lua_pcall(L, nargs, nresults, msgh);
264 lua_remove(L, msgh);
265 return ret;
268 /* expects a lua function at stack position `narg` and stores a
269 * reference to it in the registry. The return value can be used
270 * to look it up.
272 * registry["vis.functions"][(void*)(function)] = function
274 static const void *func_ref_new(lua_State *L, int narg) {
275 const void *addr = lua_topointer(L, narg);
276 if (!lua_isfunction(L, narg) || !addr)
277 luaL_argerror(L, narg, "function expected");
278 lua_getfield(L, LUA_REGISTRYINDEX, "vis.functions");
279 lua_pushlightuserdata(L, (void*)addr);
280 lua_pushvalue(L, narg);
281 lua_settable(L, -3);
282 lua_pop(L, 1);
283 return addr;
286 /* retrieve function from registry and place it at the top of the stack */
287 static bool func_ref_get(lua_State *L, const void *addr) {
288 if (!addr)
289 return false;
290 lua_getfield(L, LUA_REGISTRYINDEX, "vis.functions");
291 lua_pushlightuserdata(L, (void*)addr);
292 lua_gettable(L, -2);
293 lua_remove(L, -2);
294 if (!lua_isfunction(L, -1)) {
295 lua_pop(L, 1);
296 return false;
298 return true;
301 /* creates a new metatable for a given type and stores a mapping:
303 * registry["vis.types"][metatable] = type
305 * leaves the metatable at the top of the stack.
307 static void obj_type_new(lua_State *L, const char *type) {
308 luaL_newmetatable(L, type);
309 lua_getglobal(L, "vis");
310 if (!lua_isnil(L, -1)) {
311 lua_getfield(L, -1, "types");
312 lua_pushvalue(L, -3);
313 lua_setfield(L, -2, type);
314 lua_pop(L, 1);
316 lua_pop(L, 1);
317 lua_getfield(L, LUA_REGISTRYINDEX, "vis.types");
318 lua_pushvalue(L, -2);
319 lua_pushstring(L, type);
320 lua_settable(L, -3);
321 lua_pop(L, 1);
324 /* get type of userdatum at the top of the stack:
326 * return registry["vis.types"][getmetatable(userdata)]
328 const char *obj_type_get(lua_State *L) {
329 if (lua_isnil(L, -1))
330 return "nil";
331 lua_getfield(L, LUA_REGISTRYINDEX, "vis.types");
332 lua_getmetatable(L, -2);
333 lua_gettable(L, -2);
334 // XXX: in theory string might become invalid when popped from stack
335 const char *type = lua_tostring(L, -1);
336 lua_pop(L, 2);
337 return type;
340 static void *obj_new(lua_State *L, size_t size, const char *type) {
341 void *obj = lua_newuserdata(L, size);
342 luaL_getmetatable(L, type);
343 lua_setmetatable(L, -2);
344 lua_newtable(L);
345 lua_setuservalue(L, -2);
346 return obj;
349 /* returns registry["vis.objects"][addr] if it is of correct type */
350 static void *obj_ref_get(lua_State *L, void *addr, const char *type) {
351 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
352 lua_pushlightuserdata(L, addr);
353 lua_gettable(L, -2);
354 lua_remove(L, -2);
355 if (lua_isnil(L, -1)) {
356 debug("get: vis.objects[%p] = nil\n", addr);
357 lua_pop(L, 1);
358 return NULL;
360 if (DEBUG_LUA) {
361 const char *actual_type = obj_type_get(L);
362 if (strcmp(type, actual_type) != 0)
363 debug("get: vis.objects[%p] = %s (BUG: expected %s)\n", addr, actual_type, type);
364 void **handle = luaL_checkudata(L, -1, type);
365 if (!handle)
366 debug("get: vis.objects[%p] = %s (BUG: invalid handle)\n", addr, type);
367 else if (*handle != addr)
368 debug("get: vis.objects[%p] = %s (BUG: handle mismatch %p)\n", addr, type, *handle);
370 return luaL_checkudata(L, -1, type);
373 /* expects a userdatum at the top of the stack and sets
375 * registry["vis.objects"][addr] = userdata
377 static void obj_ref_set(lua_State *L, void *addr) {
378 //debug("set: vis.objects[%p] = %s\n", addr, obj_type_get(L));
379 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
380 lua_pushlightuserdata(L, addr);
381 lua_pushvalue(L, -3);
382 lua_settable(L, -3);
383 lua_pop(L, 1);
386 /* invalidates an object reference
388 * registry["vis.objects"][addr] = nil
390 static void obj_ref_free(lua_State *L, void *addr) {
391 if (DEBUG_LUA) {
392 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
393 lua_pushlightuserdata(L, addr);
394 lua_gettable(L, -2);
395 lua_remove(L, -2);
396 if (lua_isnil(L, -1))
397 debug("free-unused: %p\n", addr);
398 else
399 debug("free: vis.objects[%p] = %s\n", addr, obj_type_get(L));
400 lua_pop(L, 1);
402 lua_pushnil(L);
403 obj_ref_set(L, addr);
406 /* creates a new object reference of given type if it does not already exist in the registry:
408 * if (registry["vis.types"][metatable(registry["vis.objects"][addr])] != type) {
409 * // XXX: should not happen
410 * registry["vis.objects"][addr] = new_obj(addr, type)
412 * return registry["vis.objects"][addr];
414 static void *obj_ref_new(lua_State *L, void *addr, const char *type) {
415 if (!addr) {
416 lua_pushnil(L);
417 return NULL;
419 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
420 lua_pushlightuserdata(L, addr);
421 lua_gettable(L, -2);
422 lua_remove(L, -2);
423 const char *old_type = obj_type_get(L);
424 if (strcmp(type, old_type) == 0) {
425 debug("new: vis.objects[%p] = %s (returning existing object)\n", addr, old_type);
426 void **handle = luaL_checkudata(L, -1, type);
427 if (!handle)
428 debug("new: vis.objects[%p] = %s (BUG: invalid handle)\n", addr, old_type);
429 else if (*handle != addr)
430 debug("new: vis.objects[%p] = %s (BUG: handle mismatch %p)\n", addr, old_type, *handle);
431 return addr;
433 if (!lua_isnil(L, -1))
434 debug("new: vis.objects[%p] = %s (WARNING: changing object type from %s)\n", addr, type, old_type);
435 else
436 debug("new: vis.objects[%p] = %s (creating new object)\n", addr, type);
437 lua_pop(L, 1);
438 void **handle = obj_new(L, sizeof(addr), type);
439 obj_ref_set(L, addr);
440 *handle = addr;
441 return addr;
444 /* retrieve object stored in reference at stack location `idx' */
445 static void *obj_ref_check_get(lua_State *L, int idx, const char *type) {
446 void **addr = luaL_checkudata(L, idx, type);
447 if (!obj_ref_get(L, *addr, type))
448 return NULL;
449 return *addr;
452 /* (type) check validity of object reference at stack location `idx' */
453 static void *obj_ref_check(lua_State *L, int idx, const char *type) {
454 void *obj = obj_ref_check_get(L, idx, type);
455 if (obj)
456 lua_pop(L, 1);
457 else
458 luaL_argerror(L, idx, "invalid object reference");
459 return obj;
462 static void *obj_ref_check_containerof(lua_State *L, int idx, const char *type, size_t offset) {
463 void *obj = obj_ref_check(L, idx, type);
464 return obj ? ((char*)obj-offset) : obj;
467 static void *obj_lightref_new(lua_State *L, void *addr, const char *type) {
468 if (!addr)
469 return NULL;
470 void **handle = obj_new(L, sizeof(addr), type);
471 *handle = addr;
472 return addr;
475 static void *obj_lightref_check(lua_State *L, int idx, const char *type) {
476 void **addr = luaL_checkudata(L, idx, type);
477 return *addr;
480 static int index_common(lua_State *L) {
481 lua_getmetatable(L, 1);
482 lua_pushvalue(L, 2);
483 lua_gettable(L, -2);
484 if (lua_isnil(L, -1)) {
485 lua_getuservalue(L, 1);
486 lua_pushvalue(L, 2);
487 lua_gettable(L, -2);
489 return 1;
492 static int newindex_common(lua_State *L) {
493 lua_getuservalue(L, 1);
494 lua_pushvalue(L, 2);
495 lua_pushvalue(L, 3);
496 lua_settable(L, -3);
497 return 0;
500 static size_t getpos(lua_State *L, int narg) {
501 return lua_tounsigned(L, narg);
504 static size_t checkpos(lua_State *L, int narg) {
505 lua_Number n = luaL_checknumber(L, narg);
506 if (n >= 0 && n <= SIZE_MAX && n == (size_t)n)
507 return n;
508 return luaL_argerror(L, narg, "expected position, got number");
511 static void pushpos(lua_State *L, size_t pos) {
512 if (pos == EPOS)
513 lua_pushnil(L);
514 else
515 lua_pushunsigned(L, pos);
518 static void pushrange(lua_State *L, Filerange *r) {
519 if (!r || !text_range_valid(r)) {
520 lua_pushnil(L);
521 return;
523 lua_createtable(L, 0, 2);
524 lua_pushstring(L, "start");
525 lua_pushunsigned(L, r->start);
526 lua_settable(L, -3);
527 lua_pushstring(L, "finish");
528 lua_pushunsigned(L, r->end);
529 lua_settable(L, -3);
532 static Filerange getrange(lua_State *L, int index) {
533 Filerange range = text_range_empty();
534 if (lua_istable(L, index)) {
535 lua_getfield(L, index, "start");
536 range.start = checkpos(L, -1);
537 lua_pop(L, 1);
538 lua_getfield(L, index, "finish");
539 range.end = checkpos(L, -1);
540 lua_pop(L, 1);
541 } else {
542 range.start = checkpos(L, index);
543 range.end = range.start + checkpos(L, index+1);
545 return range;
548 static const char *keymapping(Vis *vis, const char *keys, const Arg *arg) {
549 lua_State *L = vis->lua;
550 if (!func_ref_get(L, arg->v))
551 return keys;
552 lua_pushstring(L, keys);
553 if (pcall(vis, L, 1, 1) != 0)
554 return keys;
555 if (lua_type(L, -1) != LUA_TNUMBER)
556 return keys; /* invalid or no return value, assume zero */
557 lua_Number number = lua_tonumber(L, -1);
558 lua_Integer integer = lua_tointeger(L, -1);
559 if (number != integer)
560 return keys;
561 if (integer < 0)
562 return NULL; /* need more input */
563 size_t len = integer;
564 size_t max = strlen(keys);
565 return (len <= max) ? keys+len : keys;
568 /***
569 * The main editor object.
570 * @type Vis
573 /***
574 * Version information.
575 * @tfield string VERSION
576 * version information in `git describe` format, same as reported by `vis -v`.
578 /***
579 * Lua API object types
580 * @field types meta tables of userdata objects used for type checking
581 * @local
583 /***
584 * User interface.
585 * @tfield Ui ui the user interface being used
587 /***
588 * Mode constants.
589 * @tfield modes modes
591 /***
592 * Events.
593 * @tfield events events
595 /***
596 * Registers.
597 * @field registers array to access the register by single letter name
599 /***
600 * Scintillua lexer module.
601 * @field lexers might be `nil` if module is not found
603 /***
604 * LPeg lexer module.
605 * @field lpeg might be `nil` if module is not found
607 /***
608 * Current count.
609 * @tfield int count the specified count for the current command or `nil` if none was given
612 /***
613 * Create an iterator over all windows.
614 * @function windows
615 * @return the new iterator
616 * @see win
617 * @usage
618 * for win in vis:windows() do
619 * -- do something with win
620 * end
622 static int windows_iter(lua_State *L);
623 static int windows(lua_State *L) {
624 Vis *vis = obj_ref_check(L, 1, "vis");
625 Win **handle = lua_newuserdata(L, sizeof *handle), *next;
626 for (next = vis->windows; next && next->file->internal; next = next->next);
627 *handle = next;
628 lua_pushcclosure(L, windows_iter, 1);
629 return 1;
632 static int windows_iter(lua_State *L) {
633 Win **handle = lua_touserdata(L, lua_upvalueindex(1));
634 if (!*handle)
635 return 0;
636 Win *win = obj_ref_new(L, *handle, VIS_LUA_TYPE_WINDOW), *next;
637 if (win) {
638 for (next = win->next; next && next->file->internal; next = next->next);
639 *handle = next;
641 return 1;
644 /***
645 * Create an iterator over all files.
646 * @function files
647 * @return the new iterator
648 * @usage
649 * for file in vis:files() do
650 * -- do something with file
651 * end
653 static int files_iter(lua_State *L);
654 static int files(lua_State *L) {
655 Vis *vis = obj_ref_check(L, 1, "vis");
656 File **handle = lua_newuserdata(L, sizeof *handle);
657 *handle = vis->files;
658 lua_pushcclosure(L, files_iter, 1);
659 return 1;
662 static int files_iter(lua_State *L) {
663 File **handle = lua_touserdata(L, lua_upvalueindex(1));
664 if (!*handle)
665 return 0;
666 File *file = obj_ref_new(L, *handle, VIS_LUA_TYPE_FILE);
667 if (file)
668 *handle = file->next;
669 return 1;
672 /***
673 * Create an iterator over all mark names.
674 * @function mark_names
675 * @return the new iterator
676 * @usage
677 * local marks = vis.win.marks
678 * for name in vis:mark_names() do
679 * local mark = marks[name]
680 * for i = 1, #mark do
681 * -- do somthing with: name, mark[i].start, mark[i].finish
682 * end
683 * end
685 static int mark_names_iter(lua_State *L);
686 static int mark_names(lua_State *L) {
687 Vis *vis = obj_ref_check(L, 1, "vis");
688 lua_pushlightuserdata(L, vis);
689 enum VisMark *handle = lua_newuserdata(L, sizeof *handle);
690 *handle = 0;
691 lua_pushcclosure(L, mark_names_iter, 2);
692 return 1;
695 static int mark_names_iter(lua_State *L) {
696 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
697 enum VisMark *handle = lua_touserdata(L, lua_upvalueindex(2));
698 char mark = vis_mark_to(vis, *handle);
699 if (mark) {
700 lua_pushlstring(L, &mark, 1);
701 (*handle)++;
702 return 1;
704 return 0;
707 /***
708 * Create an iterator over all register names.
709 * @function register_names
710 * @return the new iterator
711 * @usage
712 * for name in vis:register_names() do
713 * local reg = vis.registers[name]
714 * for i = 1, #reg do
715 * -- do something with register value reg[i]
716 * end
717 * end
719 static int register_names_iter(lua_State *L);
720 static int register_names(lua_State *L) {
721 Vis *vis = obj_ref_check(L, 1, "vis");
722 lua_pushlightuserdata(L, vis);
723 enum VisRegister *handle = lua_newuserdata(L, sizeof *handle);
724 *handle = 0;
725 lua_pushcclosure(L, register_names_iter, 2);
726 return 1;
729 static int register_names_iter(lua_State *L) {
730 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
731 enum VisRegister *handle = lua_touserdata(L, lua_upvalueindex(2));
732 char reg = vis_register_to(vis, *handle);
733 if (reg) {
734 lua_pushlstring(L, &reg, 1);
735 (*handle)++;
736 return 1;
738 return 0;
741 /***
742 * Execute a `:`-command.
743 * @function command
744 * @tparam string command the command to execute
745 * @treturn bool whether the command succeeded
746 * @usage
747 * vis:command("set number")
749 static int command(lua_State *L) {
750 Vis *vis = obj_ref_check(L, 1, "vis");
751 const char *cmd = luaL_checkstring(L, 2);
752 bool ret = vis_cmd(vis, cmd);
753 lua_pushboolean(L, ret);
754 return 1;
757 /***
758 * Display a short message.
760 * The single line message will be displayed at the bottom of
761 * the screen and automatically hidden once a key is pressed.
763 * @function info
764 * @tparam string message the message to display
766 static int info(lua_State *L) {
767 Vis *vis = obj_ref_check(L, 1, "vis");
768 const char *msg = luaL_checkstring(L, 2);
769 vis_info_show(vis, "%s", msg);
770 return 0;
773 /***
774 * Display a multi line message.
776 * Opens a new window and displays an arbitrarily long message.
778 * @function message
779 * @tparam string message the message to display
781 static int message(lua_State *L) {
782 Vis *vis = obj_ref_check(L, 1, "vis");
783 const char *msg = luaL_checkstring(L, 2);
784 vis_message_show(vis, msg);
785 return 0;
788 /***
789 * Register a Lua function as key action.
790 * @function action_register
791 * @tparam string name the name of the action, can be referred to in key bindings as `<name>` pseudo key
792 * @tparam Function func the lua function implementing the key action (see @{keyhandler})
793 * @tparam[opt] string help the single line help text as displayed in `:help`
794 * @treturn KeyAction action the registered key action
795 * @see Vis:map
796 * @see Window:map
798 static int action_register(lua_State *L) {
799 Vis *vis = obj_ref_check(L, 1, "vis");
800 const char *name = luaL_checkstring(L, 2);
801 const void *func = func_ref_new(L, 3);
802 const char *help = luaL_optstring(L, 4, NULL);
803 KeyAction *action = vis_action_new(vis, name, help, keymapping, (Arg){ .v = func });
804 if (!action)
805 goto err;
806 if (!vis_action_register(vis, action))
807 goto err;
808 obj_ref_new(L, action, VIS_LUA_TYPE_KEYACTION);
809 return 1;
810 err:
811 vis_action_free(vis, action);
812 lua_pushnil(L);
813 return 1;
816 static int keymap(lua_State *L, Vis *vis, Win *win) {
817 int mode = luaL_checkint(L, 2);
818 const char *key = luaL_checkstring(L, 3);
819 const char *help = luaL_optstring(L, 5, NULL);
820 KeyBinding *binding = vis_binding_new(vis);
821 if (!binding)
822 goto err;
823 if (lua_isstring(L, 4)) {
824 const char *alias = luaL_checkstring(L, 4);
825 if (!(binding->alias = strdup(alias)))
826 goto err;
827 } else if (lua_isfunction(L, 4)) {
828 const void *func = func_ref_new(L, 4);
829 if (!(binding->action = vis_action_new(vis, NULL, help, keymapping, (Arg){ .v = func })))
830 goto err;
831 } else if (lua_isuserdata(L, 4)) {
832 binding->action = obj_ref_check(L, 4, VIS_LUA_TYPE_KEYACTION);
835 if (win) {
836 if (!vis_window_mode_map(win, mode, true, key, binding))
837 goto err;
838 } else {
839 if (!vis_mode_map(vis, mode, true, key, binding))
840 goto err;
843 lua_pushboolean(L, true);
844 return 1;
845 err:
846 vis_binding_free(vis, binding);
847 lua_pushboolean(L, false);
848 return 1;
851 /***
852 * Map a key to a Lua function.
854 * Creates a new key mapping in a given mode.
856 * @function map
857 * @tparam int mode the mode to which the mapping should be added
858 * @tparam string key the key to map
859 * @tparam function func the Lua function to handle the key mapping (see @{keyhandler})
860 * @tparam[opt] string help the single line help text as displayed in `:help`
861 * @treturn bool whether the mapping was successfully established
862 * @see Window:map
863 * @usage
864 * vis:map(vis.modes.INSERT, "<C-k>", function(keys)
865 * if #keys < 2 then
866 * return -1 -- need more input
867 * end
868 * local digraph = keys:sub(1, 2)
869 * if digraph == "l*" then
870 * vis:feedkeys('λ')
871 * return 2 -- consume 2 bytes of input
872 * end
873 * end, "Insert digraph")
875 /***
876 * Setup a key alias.
878 * This is equivalent to `vis:command('map! mode key alias')`.
880 * Mappings are always recursive!
881 * @function map
882 * @tparam int mode the mode to which the mapping should be added
883 * @tparam string key the key to map
884 * @tparam string alias the key to map to
885 * @treturn bool whether the mapping was successfully established
886 * @see Window:map
887 * @usage
888 * vis:map(vis.modes.NORMAL, "j", "k")
890 /***
891 * Map a key to a key action.
893 * @function map
894 * @tparam int mode the mode to which the mapping should be added
895 * @tparam string key the key to map
896 * @param action the action to map
897 * @treturn bool whether the mapping was successfully established
898 * @see Window:map
899 * @usage
900 * local action = vis:action_register("info", function()
901 * vis:info("Mapping works!")
902 * end, "Info message help text")
903 * vis:map(vis.modes.NORMAL, "gh", action)
904 * vis:map(vis.modes.NORMAL, "gl", action)
906 static int map(lua_State *L) {
907 Vis *vis = obj_ref_check(L, 1, "vis");
908 return keymap(L, vis, NULL);
911 /***
912 * Unmap a global key binding.
914 * @function unmap
915 * @tparam int mode the mode from which the mapping should be removed
916 * @tparam string key the mapping to remove
917 * @treturn bool whether the mapping was successfully removed
918 * @see Window:unmap
920 static int keyunmap(lua_State *L, Vis *vis, Win *win) {
921 enum VisMode mode = luaL_checkint(L, 2);
922 const char *key = luaL_checkstring(L, 3);
923 bool ret;
924 if (!win)
925 ret = vis_mode_unmap(vis, mode, key);
926 else
927 ret = vis_window_mode_unmap(win, mode, key);
928 lua_pushboolean(L, ret);
929 return 1;
932 static int unmap(lua_State *L) {
933 Vis *vis = obj_ref_check(L, 1, "vis");
934 return keyunmap(L, vis, NULL);
937 /***
938 * Get all currently active mappings of a mode.
940 * @function mappings
941 * @tparam int mode the mode to query
942 * @treturn table the active mappings and their associated help texts
943 * @usage
944 * local bindings = vis:mappings(vis.modes.NORMAL)
945 * for key, help in pairs(bindings) do
946 * -- do something
947 * end
948 * @see Vis:map
950 static bool binding_collect(const char *key, void *value, void *ctx) {
951 lua_State *L = ctx;
952 KeyBinding *binding = value;
953 lua_getfield(L, -1, key);
954 bool new = lua_isnil(L, -1);
955 lua_pop(L, 1);
956 if (new) {
957 const char *help = binding->alias ? binding->alias : VIS_HELP_USE(binding->action->help);
958 lua_pushstring(L, help ? help : "");
959 lua_setfield(L, -2, key);
961 return true;
964 static int mappings(lua_State *L) {
965 Vis *vis = obj_ref_check(L, 1, "vis");
966 lua_newtable(L);
967 for (Mode *mode = mode_get(vis, luaL_checkint(L, 2)); mode; mode = mode->parent) {
968 if (!mode->bindings)
969 continue;
970 map_iterate(mode->bindings, binding_collect, vis->lua);
972 return 1;
975 /***
976 * Execute a motion.
978 * @function motion
979 * @tparam int id the id of the motion to execute
980 * @treturn bool whether the id was valid
981 * @local
983 static int motion(lua_State *L) {
984 Vis *vis = obj_ref_check(L, 1, "vis");
985 enum VisMotion id = luaL_checkunsigned(L, 2);
986 // TODO handle var args?
987 lua_pushboolean(L, vis && vis_motion(vis, id));
988 return 1;
991 static size_t motion_lua(Vis *vis, Win *win, void *data, size_t pos) {
992 lua_State *L = vis->lua;
993 if (!L || !func_ref_get(L, data) || !obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW))
994 return EPOS;
996 lua_pushunsigned(L, pos);
997 if (pcall(vis, L, 2, 1) != 0)
998 return EPOS;
999 return getpos(L, -1);
1002 /***
1003 * Register a custom motion.
1005 * @function motion_register
1006 * @tparam function motion the Lua function implementing the motion
1007 * @treturn int the associated motion id, or `-1` on failure
1008 * @see motion, motion_new
1009 * @local
1010 * @usage
1011 * -- custom motion advancing to the next byte
1012 * local id = vis:motion_register(function(win, pos)
1013 * return pos+1
1014 * end)
1016 static int motion_register(lua_State *L) {
1017 Vis *vis = obj_ref_check(L, 1, "vis");
1018 const void *func = func_ref_new(L, 2);
1019 int id = vis_motion_register(vis, (void*)func, motion_lua);
1020 lua_pushinteger(L, id);
1021 return 1;
1024 /***
1025 * Execute an operator.
1027 * @function operator
1028 * @tparam int id the id of the operator to execute
1029 * @treturn bool whether the id was valid
1030 * @local
1032 static int operator(lua_State *L) {
1033 Vis *vis = obj_ref_check(L, 1, "vis");
1034 enum VisOperator id = luaL_checkunsigned(L, 2);
1035 // TODO handle var args?
1036 lua_pushboolean(L, vis && vis_operator(vis, id));
1037 return 1;
1040 static size_t operator_lua(Vis *vis, Text *text, OperatorContext *c) {
1041 lua_State *L = vis->lua;
1042 if (!L || !func_ref_get(L, c->context))
1043 return EPOS;
1044 File *file = vis->files;
1045 while (file && (file->internal || file->text != text))
1046 file = file->next;
1047 if (!file || !obj_ref_new(L, file, VIS_LUA_TYPE_FILE))
1048 return EPOS;
1049 pushrange(L, &c->range);
1050 pushpos(L, c->pos);
1051 if (pcall(vis, L, 3, 1) != 0)
1052 return EPOS;
1053 return getpos(L, -1);
1056 /***
1057 * Register a custom operator.
1059 * @function operator_register
1060 * @tparam function operator the Lua function implementing the operator
1061 * @treturn int the associated operator id, or `-1` on failure
1062 * @see operator, operator_new
1063 * @local
1064 * @usage
1065 * -- custom operator replacing every 'a' with 'b'
1066 * local id = vis:operator_register(function(file, range, pos)
1067 * local data = file:content(range)
1068 * data = data:gsub("a", "b")
1069 * file:delete(range)
1070 * file:insert(range.start, data)
1071 * return range.start -- new cursor location
1072 * end)
1074 static int operator_register(lua_State *L) {
1075 Vis *vis = obj_ref_check(L, 1, "vis");
1076 const void *func = func_ref_new(L, 2);
1077 int id = vis_operator_register(vis, operator_lua, (void*)func);
1078 lua_pushinteger(L, id);
1079 return 1;
1082 /***
1083 * Execute a text object.
1085 * @function textobject
1086 * @tparam int id the id of the text object to execute
1087 * @treturn bool whether the id was valid
1088 * @see textobject_register, textobject_new
1089 * @local
1091 static int textobject(lua_State *L) {
1092 Vis *vis = obj_ref_check(L, 1, "vis");
1093 enum VisTextObject id = luaL_checkunsigned(L, 2);
1094 lua_pushboolean(L, vis_textobject(vis, id));
1095 return 1;
1098 static Filerange textobject_lua(Vis *vis, Win *win, void *data, size_t pos) {
1099 lua_State *L = vis->lua;
1100 if (!L || !func_ref_get(L, data) || !obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW))
1101 return text_range_empty();
1102 lua_pushunsigned(L, pos);
1103 if (pcall(vis, L, 2, 2) != 0 || lua_isnil(L, -1))
1104 return text_range_empty();
1105 return text_range_new(getpos(L, -2), getpos(L, -1));
1108 /***
1109 * Register a custom text object.
1111 * @function textobject_register
1112 * @tparam function textobject the Lua function implementing the text object
1113 * @treturn int the associated text object id, or `-1` on failure
1114 * @see textobject, textobject_new
1115 * @local
1116 * @usage
1117 * -- custom text object covering the next byte
1118 * local id = vis:textobject_register(function(win, pos)
1119 * return pos, pos+1
1120 * end)
1122 static int textobject_register(lua_State *L) {
1123 Vis *vis = obj_ref_check(L, 1, "vis");
1124 const void *func = func_ref_new(L, 2);
1125 int id = vis_textobject_register(vis, 0, (void*)func, textobject_lua);
1126 lua_pushinteger(L, id);
1127 return 1;
1130 static bool option_lua(Vis *vis, Win *win, void *context, bool toggle,
1131 enum VisOption flags, const char *name, Arg *value) {
1132 lua_State *L = vis->lua;
1133 if (!L || !func_ref_get(L, context))
1134 return false;
1135 if (flags & VIS_OPTION_TYPE_BOOL)
1136 lua_pushboolean(L, value->b);
1137 else if (flags & VIS_OPTION_TYPE_STRING)
1138 lua_pushstring(L, value->s);
1139 else if (flags & VIS_OPTION_TYPE_NUMBER)
1140 lua_pushnumber(L, value->i);
1141 else
1142 return false;
1143 lua_pushboolean(L, toggle);
1144 return pcall(vis, L, 2, 2) == 0 && (!lua_isboolean(L, -1) || lua_toboolean(L, -1));
1147 /***
1148 * Register a custom `:set` option.
1150 * @function option_register
1151 * @tparam string name the option name
1152 * @tparam string type the option type (`bool`, `string` or `number`)
1153 * @tparam function handler the Lua function being called when the option is changed
1154 * @tparam[opt] string help the single line help text as displayed in `:help`
1155 * @treturn bool whether the option was successfully registered
1156 * @usage
1157 * vis:option_register("foo", "bool", function(value, toogle)
1158 * if not vis.win then return false end
1159 * vis.win.foo = toogle and not vis.win.foo or value
1160 * vis:info("Option foo = " .. tostring(vis.win.foo))
1161 * return true
1162 * end, "Foo enables superpowers")
1164 static int option_register(lua_State *L) {
1165 Vis *vis = obj_ref_check(L, 1, "vis");
1166 const char *name = luaL_checkstring(L, 2);
1167 const char *type = luaL_checkstring(L, 3);
1168 const void *func = func_ref_new(L, 4);
1169 const char *help = luaL_optstring(L, 5, NULL);
1170 const char *names[] = { name, NULL };
1171 enum VisOption flags = 0;
1172 if (strcmp(type, "string") == 0)
1173 flags |= VIS_OPTION_TYPE_STRING;
1174 else if (strcmp(type, "number") == 0)
1175 flags |= VIS_OPTION_TYPE_NUMBER;
1176 else
1177 flags |= VIS_OPTION_TYPE_BOOL;
1178 bool ret = vis_option_register(vis, names, flags, option_lua, (void*)func, help);
1179 lua_pushboolean(L, ret);
1180 return 1;
1183 /***
1184 * Unregister a `:set` option.
1186 * @function option_unregister
1187 * @tparam string name the option name
1188 * @treturn bool whether the option was successfully unregistered
1190 static int option_unregister(lua_State *L) {
1191 Vis *vis = obj_ref_check(L, 1, "vis");
1192 const char *name = luaL_checkstring(L, 2);
1193 bool ret = vis_option_unregister(vis, name);
1194 lua_pushboolean(L, ret);
1195 return 1;
1198 static bool command_lua(Vis *vis, Win *win, void *data, bool force, const char *argv[], Selection *sel, Filerange *range) {
1199 lua_State *L = vis->lua;
1200 if (!L || !func_ref_get(L, data))
1201 return false;
1202 lua_newtable(L);
1203 for (size_t i = 0; argv[i]; i++) {
1204 lua_pushunsigned(L, i);
1205 lua_pushstring(L, argv[i]);
1206 lua_settable(L, -3);
1208 lua_pushboolean(L, force);
1209 if (!obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW))
1210 return false;
1211 if (!sel)
1212 sel = view_selections_primary_get(win->view);
1213 if (!obj_lightref_new(L, sel, VIS_LUA_TYPE_SELECTION))
1214 return false;
1215 pushrange(L, range);
1216 if (pcall(vis, L, 5, 1) != 0)
1217 return false;
1218 return lua_toboolean(L, -1);
1221 /***
1222 * Register a custom `:`-command.
1224 * @function command_register
1225 * @tparam string name the command name
1226 * @tparam function command the Lua function implementing the command
1227 * @tparam[opt] string help the single line help text as displayed in `:help`
1228 * @treturn bool whether the command has been successfully registered
1229 * @usage
1230 * vis:command_register("foo", function(argv, force, win, selection, range)
1231 * for i,arg in ipairs(argv) do
1232 * print(i..": "..arg)
1233 * end
1234 * print("was command forced with ! "..(force and "yes" or "no"))
1235 * print(win.file.name)
1236 * print(selection.pos)
1237 * print(range ~= nil and ('['..range.start..', '..range.finish..']') or "invalid range")
1238 * return true;
1239 * end)
1241 static int command_register(lua_State *L) {
1242 Vis *vis = obj_ref_check(L, 1, "vis");
1243 const char *name = luaL_checkstring(L, 2);
1244 const void *func = func_ref_new(L, 3);
1245 const char *help = luaL_optstring(L, 4, "");
1246 bool ret = vis_cmd_register(vis, name, help, (void*)func, command_lua);
1247 lua_pushboolean(L, ret);
1248 return 1;
1251 /***
1252 * Push keys to input queue and interpret them.
1254 * The keys are processed as if they were read from the keyboard.
1256 * @function feedkeys
1257 * @tparam string keys the keys to interpret
1259 static int feedkeys(lua_State *L) {
1260 Vis *vis = obj_ref_check(L, 1, "vis");
1261 const char *keys = luaL_checkstring(L, 2);
1262 vis_keys_feed(vis, keys);
1263 return 0;
1266 /***
1267 * Insert keys at all cursor positions of active window.
1269 * This function behaves as if the keys were entered in insert mode,
1270 * but in contrast to @{Vis:feedkeys} it bypasses the input queue,
1271 * meaning mappings do not apply and the keys will not be recorded in macros.
1273 * @function insert
1274 * @tparam string keys the keys to insert
1275 * @see Vis:feedkeys
1277 static int insert(lua_State *L) {
1278 Vis *vis = obj_ref_check(L, 1, "vis");
1279 size_t len;
1280 const char *keys = luaL_checklstring(L, 2, &len);
1281 vis_insert_key(vis, keys, len);
1282 return 0;
1285 /***
1286 * Replace keys at all cursor positions of active window.
1288 * This function behaves as if the keys were entered in replace mode,
1289 * but in contrast to @{Vis:feedkeys} it bypasses the input queue,
1290 * meaning mappings do not apply and the keys will not be recorded in macros.
1292 * @function replace
1293 * @tparam string keys the keys to insert
1294 * @see Vis:feedkeys
1296 static int replace(lua_State *L) {
1297 Vis *vis = obj_ref_check(L, 1, "vis");
1298 size_t len;
1299 const char *keys = luaL_checklstring(L, 2, &len);
1300 vis_replace_key(vis, keys, len);
1301 return 0;
1304 /***
1305 * Terminate editor process.
1307 * Termination happens upon the next iteration of the main event loop.
1308 * This means the calling Lua code will be executed further until it
1309 * eventually hands over control to the editor core. The exit status
1310 * of the most recent call is used.
1312 * All unsaved changes will be lost!
1314 * @function exit
1315 * @tparam int code the exit status returned to the operating system
1317 static int exit_func(lua_State *L) {
1318 Vis *vis = obj_ref_check(L, 1, "vis");
1319 int code = luaL_checkint(L, 2);
1320 vis_exit(vis, code);
1321 return 0;
1324 /***
1325 * Pipe file range to external process and collect output.
1327 * The editor core will be blocked while the external process is running.
1329 * @function pipe
1330 * @tparam File file the file to which the range applies
1331 * @tparam Range range the range to pipe
1332 * @tparam string command the command to execute
1333 * @treturn int code the exit status of the executed command
1334 * @treturn string stdout the data written to stdout
1335 * @treturn string stderr the data written to stderr
1337 static int pipe_func(lua_State *L) {
1338 Vis *vis = obj_ref_check(L, 1, "vis");
1339 File *file = obj_ref_check(L, 2, VIS_LUA_TYPE_FILE);
1340 Filerange range = getrange(L, 3);
1341 const char *cmd = luaL_checkstring(L, 4);
1342 char *out = NULL, *err = NULL;
1343 int status = vis_pipe_collect(vis, file, &range, (const char*[]){ cmd, NULL }, &out, &err);
1344 lua_pushinteger(L, status);
1345 if (out)
1346 lua_pushstring(L, out);
1347 else
1348 lua_pushnil(L);
1349 free(out);
1350 if (err)
1351 lua_pushstring(L, err);
1352 else
1353 lua_pushnil(L);
1354 free(err);
1355 vis_draw(vis);
1356 return 3;
1358 /***
1359 * Redraw complete user interface.
1361 * Will trigger redraw events, make sure to avoid recursive events.
1363 * @function redraw
1365 static int redraw(lua_State *L) {
1366 Vis *vis = obj_ref_check(L, 1, "vis");
1367 vis_redraw(vis);
1368 return 0;
1370 /***
1371 * Currently active window.
1372 * @tfield Window win
1373 * @see windows
1375 /***
1376 * Currently active mode.
1377 * @tfield modes mode
1379 /***
1380 * Whether a macro is being recorded.
1381 * @tfield bool recording
1383 /***
1384 * Currently unconsumed keys in the input queue.
1385 * @tfield string input_queue
1387 /***
1388 * Register name in use.
1389 * @tfield string register
1391 /***
1392 * Mark name in use.
1393 * @tfield string mark
1395 static int vis_index(lua_State *L) {
1396 Vis *vis = obj_ref_check(L, 1, "vis");
1398 if (lua_isstring(L, 2)) {
1399 const char *key = lua_tostring(L, 2);
1400 if (strcmp(key, "win") == 0) {
1401 if (vis->win)
1402 obj_ref_new(L, vis->win, VIS_LUA_TYPE_WINDOW);
1403 else
1404 lua_pushnil(L);
1405 return 1;
1408 if (strcmp(key, "mode") == 0) {
1409 lua_pushunsigned(L, vis->mode->id);
1410 return 1;
1413 if (strcmp(key, "input_queue") == 0) {
1414 lua_pushstring(L, buffer_content0(&vis->input_queue));
1415 return 1;
1418 if (strcmp(key, "recording") == 0) {
1419 lua_pushboolean(L, vis_macro_recording(vis));
1420 return 1;
1423 if (strcmp(key, "count") == 0) {
1424 int count = vis_count_get(vis);
1425 if (count == VIS_COUNT_UNKNOWN)
1426 lua_pushnil(L);
1427 else
1428 lua_pushunsigned(L, count);
1429 return 1;
1432 if (strcmp(key, "register") == 0) {
1433 char name = vis_register_to(vis, vis_register_used(vis));
1434 lua_pushlstring(L, &name, 1);
1435 return 1;
1438 if (strcmp(key, "registers") == 0) {
1439 obj_ref_new(L, vis->ui, VIS_LUA_TYPE_REGISTERS);
1440 return 1;
1443 if (strcmp(key, "mark") == 0) {
1444 char name = vis_mark_to(vis, vis_mark_used(vis));
1445 lua_pushlstring(L, &name, 1);
1446 return 1;
1449 if (strcmp(key, "ui") == 0) {
1450 obj_ref_new(L, vis->ui, VIS_LUA_TYPE_UI);
1451 return 1;
1455 return index_common(L);
1458 static int vis_newindex(lua_State *L) {
1459 Vis *vis = obj_ref_check(L, 1, "vis");
1460 if (lua_isstring(L, 2)) {
1461 const char *key = lua_tostring(L, 2);
1462 if (strcmp(key, "mode") == 0) {
1463 enum VisMode mode = luaL_checkunsigned(L, 3);
1464 vis_mode_switch(vis, mode);
1465 return 0;
1468 if (strcmp(key, "count") == 0) {
1469 int count;
1470 if (lua_isnil(L, 3))
1471 count = VIS_COUNT_UNKNOWN;
1472 else
1473 count = luaL_checkunsigned(L, 3);
1474 vis_count_set(vis, count);
1475 return 0;
1478 if (strcmp(key, "win") == 0) {
1479 vis_window_focus(obj_ref_check(L, 3, VIS_LUA_TYPE_WINDOW));
1480 return 0;
1483 if (strcmp(key, "register") == 0) {
1484 const char *name = luaL_checkstring(L, 3);
1485 if (strlen(name) == 1)
1486 vis_register(vis, vis_register_from(vis, name[0]));
1487 return 0;
1490 if (strcmp(key, "mark") == 0) {
1491 const char *name = luaL_checkstring(L, 3);
1492 if (strlen(name) == 1)
1493 vis_mark(vis, vis_mark_from(vis, name[0]));
1494 return 0;
1497 return newindex_common(L);
1500 static const struct luaL_Reg vis_lua[] = {
1501 { "files", files },
1502 { "windows", windows },
1503 { "mark_names", mark_names },
1504 { "register_names", register_names },
1505 { "command", command },
1506 { "info", info },
1507 { "message", message },
1508 { "map", map },
1509 { "unmap", unmap },
1510 { "mappings", mappings },
1511 { "operator", operator },
1512 { "operator_register", operator_register },
1513 { "motion", motion },
1514 { "motion_register", motion_register },
1515 { "textobject", textobject },
1516 { "textobject_register", textobject_register },
1517 { "option_register", option_register },
1518 { "option_unregister", option_unregister },
1519 { "command_register", command_register },
1520 { "feedkeys", feedkeys },
1521 { "insert", insert },
1522 { "replace", replace },
1523 { "action_register", action_register },
1524 { "exit", exit_func },
1525 { "pipe", pipe_func },
1526 { "redraw", redraw },
1527 { "__index", vis_index },
1528 { "__newindex", vis_newindex },
1529 { NULL, NULL },
1532 static const struct luaL_Reg ui_funcs[] = {
1533 { "__index", index_common },
1534 { NULL, NULL },
1537 static int registers_index(lua_State *L) {
1538 lua_newtable(L);
1539 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
1540 const char *symbol = luaL_checkstring(L, 2);
1541 if (strlen(symbol) != 1)
1542 return 1;
1543 enum VisRegister reg = vis_register_from(vis, symbol[0]);
1544 if (reg >= VIS_REG_INVALID)
1545 return 1;
1546 Array data = vis_register_get(vis, reg);
1547 for (size_t i = 0, len = array_length(&data); i < len; i++) {
1548 TextString *string = array_get(&data, i);
1549 lua_pushunsigned(L, i+1);
1550 lua_pushlstring(L, string->data, string->len);
1551 lua_settable(L, -3);
1553 array_release(&data);
1554 return 1;
1557 static int registers_newindex(lua_State *L) {
1558 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
1559 const char *symbol = luaL_checkstring(L, 2);
1560 if (strlen(symbol) != 1)
1561 return 0;
1562 enum VisRegister reg = vis_register_from(vis, symbol[0]);
1563 Array data;
1564 array_init_sized(&data, sizeof(TextString));
1566 if (lua_istable(L, 3)) {
1567 lua_pushnil(L);
1568 while (lua_next(L, 3)) {
1569 TextString string;
1570 string.data = luaL_checklstring(L, -1, &string.len);
1571 array_add(&data, &string);
1572 lua_pop(L, 1);
1576 vis_register_set(vis, reg, &data);
1577 array_release(&data);
1578 return 0;
1581 static int registers_len(lua_State *L) {
1582 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
1583 lua_pushunsigned(L, LENGTH(vis->registers));
1584 return 1;
1587 static const struct luaL_Reg registers_funcs[] = {
1588 { "__index", registers_index },
1589 { "__newindex", registers_newindex },
1590 { "__len", registers_len },
1591 { NULL, NULL },
1594 /***
1595 * A window object.
1596 * @type Window
1599 /***
1600 * Viewport currently being displayed.
1601 * @tfield Range viewport
1603 /***
1604 * The window width.
1605 * @tfield int width
1607 /***
1608 * The window height.
1609 * @tfield int height
1611 /***
1612 * The file being displayed in this window.
1613 * @tfield File file
1615 /***
1616 * The primary selection of this window.
1617 * @tfield Selection selection
1619 /***
1620 * The selections of this window.
1621 * @tfield Array(Selection) selections
1623 /***
1624 * Window marks.
1625 * Most of these marks are stored in the associated File object, meaning they
1626 * are the same in all windows displaying the same file.
1627 * @field marks array to access the marks of this window by single letter name
1628 * @see Vis:marks_names
1630 static int window_index(lua_State *L) {
1631 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1633 if (lua_isstring(L, 2)) {
1634 const char *key = lua_tostring(L, 2);
1636 if (strcmp(key, "viewport") == 0) {
1637 Filerange r = view_viewport_get(win->view);
1638 pushrange(L, &r);
1639 return 1;
1642 if (strcmp(key, "width") == 0) {
1643 lua_pushunsigned(L, vis_window_width_get(win));
1644 return 1;
1647 if (strcmp(key, "height") == 0) {
1648 lua_pushunsigned(L, vis_window_height_get(win));
1649 return 1;
1652 if (strcmp(key, "file") == 0) {
1653 obj_ref_new(L, win->file, VIS_LUA_TYPE_FILE);
1654 return 1;
1657 if (strcmp(key, "selection") == 0) {
1658 Selection *sel = view_selections_primary_get(win->view);
1659 obj_lightref_new(L, sel, VIS_LUA_TYPE_SELECTION);
1660 return 1;
1663 if (strcmp(key, "selections") == 0) {
1664 obj_ref_new(L, win->view, VIS_LUA_TYPE_SELECTIONS);
1665 return 1;
1668 if (strcmp(key, "marks") == 0) {
1669 obj_ref_new(L, &win->saved_selections, VIS_LUA_TYPE_MARKS);
1670 return 1;
1674 return index_common(L);
1677 static int window_selections_iterator_next(lua_State *L) {
1678 Selection **handle = lua_touserdata(L, lua_upvalueindex(1));
1679 if (!*handle)
1680 return 0;
1681 Selection *sel = obj_lightref_new(L, *handle, VIS_LUA_TYPE_SELECTION);
1682 if (!sel)
1683 return 0;
1684 *handle = view_selections_next(sel);
1685 return 1;
1688 /***
1689 * Create an iterator over all selections of this window.
1690 * @function selections_iterator
1691 * @return the new iterator
1693 static int window_selections_iterator(lua_State *L) {
1694 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1695 Selection **handle = lua_newuserdata(L, sizeof *handle);
1696 *handle = view_selections(win->view);
1697 lua_pushcclosure(L, window_selections_iterator_next, 1);
1698 return 1;
1701 /***
1702 * Set up a window local key mapping.
1703 * The function signatures are the same as for @{Vis:map}.
1704 * @function map
1705 * @param ...
1706 * @see Vis:map
1708 static int window_map(lua_State *L) {
1709 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1710 return keymap(L, win->vis, win);
1713 /***
1714 * Remove a window local key mapping.
1715 * The function signature is the same as for @{Vis:unmap}.
1716 * @function unmap
1717 * @param ...
1718 * @see Vis:unmap
1720 static int window_unmap(lua_State *L) {
1721 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1722 return keyunmap(L, win->vis, win);
1725 /***
1726 * Define a display style.
1727 * @function style_define
1728 * @tparam int id the style id to use
1729 * @tparam string style the style definition
1730 * @treturn bool whether the style definition has been successfully
1731 * associated with the given id
1732 * @see style
1733 * @usage
1734 * win:style_define(win.STYLE_DEFAULT, "fore:red")
1736 static int window_style_define(lua_State *L) {
1737 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1738 enum UiStyle id = luaL_checkunsigned(L, 2);
1739 const char *style = luaL_checkstring(L, 3);
1740 bool ret = view_style_define(win->view, id, style);
1741 lua_pushboolean(L, ret);
1742 return 1;
1745 /***
1746 * Style a window range.
1748 * The style will be cleared after every window redraw.
1749 * @function style
1750 * @tparam int id the display style as registered with @{style_define}
1751 * @tparam int start the absolute file position in bytes
1752 * @tparam int finish the end position
1753 * @see style_define
1754 * @usage
1755 * win:style(win.STYLE_DEFAULT, 0, 10)
1757 static int window_style(lua_State *L) {
1758 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1759 enum UiStyle style = luaL_checkunsigned(L, 2);
1760 size_t start = checkpos(L, 3);
1761 size_t end = checkpos(L, 4);
1762 view_style(win->view, style, start, end);
1763 return 0;
1766 /***
1767 * Set window status line.
1769 * @function status
1770 * @tparam string left the left aligned part of the status line
1771 * @tparam[opt] string right the right aligned part of the status line
1773 static int window_status(lua_State *L) {
1774 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1775 char status[1024] = "";
1776 int width = vis_window_width_get(win);
1777 const char *left = luaL_checkstring(L, 2);
1778 const char *right = luaL_optstring(L, 3, "");
1779 int left_width = text_string_width(left, strlen(left));
1780 int right_width = text_string_width(right, strlen(right));
1781 int spaces = width - left_width - right_width;
1782 if (spaces < 1)
1783 spaces = 1;
1784 snprintf(status, sizeof(status)-1, "%s%*s%s", left, spaces, " ", right);
1785 vis_window_status(win, status);
1786 return 0;
1789 /***
1790 * Redraw window content.
1792 * @function draw
1794 static int window_draw(lua_State *L) {
1795 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1796 view_draw(win->view);
1797 return 0;
1800 /***
1801 * Close window.
1803 * After a successful call the Window reference becomes invalid and
1804 * must no longer be used. Attempting to close the last window will
1805 * always fail.
1807 * @function close
1808 * @see exit
1809 * @tparam bool force whether unsaved changes should be discarded
1810 * @treturn bool whether the window was closed
1812 static int window_close(lua_State *L) {
1813 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1814 int count = 0;
1815 for (Win *w = win->vis->windows; w; w = w->next) {
1816 if (!w->file->internal)
1817 count++;
1819 bool force = lua_isboolean(L, 2) && lua_toboolean(L, 2);
1820 bool close = count > 1 && (force || vis_window_closable(win));
1821 if (close)
1822 vis_window_close(win);
1823 lua_pushboolean(L, close);
1824 return 1;
1827 static const struct luaL_Reg window_funcs[] = {
1828 { "__index", window_index },
1829 { "__newindex", newindex_common },
1830 { "selections_iterator", window_selections_iterator },
1831 { "map", window_map },
1832 { "unmap", window_unmap },
1833 { "style_define", window_style_define },
1834 { "style", window_style },
1835 { "status", window_status },
1836 { "draw", window_draw },
1837 { "close", window_close },
1838 { NULL, NULL },
1841 static int window_selections_index(lua_State *L) {
1842 View *view = obj_ref_check(L, 1, VIS_LUA_TYPE_SELECTIONS);
1843 size_t index = luaL_checkunsigned(L, 2);
1844 size_t count = view_selections_count(view);
1845 if (index == 0 || index > count)
1846 goto err;
1847 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
1848 if (!--index) {
1849 obj_lightref_new(L, s, VIS_LUA_TYPE_SELECTION);
1850 return 1;
1853 err:
1854 lua_pushnil(L);
1855 return 1;
1858 static int window_selections_len(lua_State *L) {
1859 View *view = obj_ref_check(L, 1, VIS_LUA_TYPE_SELECTIONS);
1860 lua_pushunsigned(L, view_selections_count(view));
1861 return 1;
1864 static const struct luaL_Reg window_selections_funcs[] = {
1865 { "__index", window_selections_index },
1866 { "__len", window_selections_len },
1867 { NULL, NULL },
1870 /***
1871 * A selection object.
1873 * A selection is a non-empty, directed range with two endpoints called
1874 * *cursor* and *anchor*. A selection can be anchored in which case
1875 * the anchor remains fixed while only the position of the cursor is
1876 * adjusted. For non-anchored selections both endpoints are updated. A
1877 * singleton selection covers one character on which both cursor and
1878 * anchor reside. There always exists a primary selection which remains
1879 * visible (i.e. changes to its position will adjust the viewport).
1881 * The range covered by a selection is represented as an interval whose
1882 * endpoints are absolute byte offsets from the start of the file.
1883 * Valid addresses are within the closed interval `[0, file.size]`.
1885 * Selections are currently implemented using character marks into
1886 * the underlying persistent
1887 * [text management data structure](https://github.com/martanne/vis/wiki/Text-management-using-a-piece-chain).
1889 * This has a few consequences you should be aware of:
1891 * - A selection becomes invalid when the delimiting boundaries of the underlying
1892 * text it is referencing is deleted:
1894 * -- leaves selection in an invalid state
1895 * win.file:delete(win.selection.pos, 1)
1896 * assert(win.selection.pos == nil)
1898 * Like a regular mark it will become valid again when the text is reverted
1899 * to the state before the deletion.
1901 * - Inserts after the selection position (`> selection.pos`) will not affect the
1902 * selection postion.
1904 * local pos = win.selection.pos
1905 * win.file:insert(pos+1, "-")
1906 * assert(win.selection.pos == pos)
1908 * - Non-cached inserts before the selection position (`<= selection.pos`) will
1909 * affect the mark and adjust the selection postion by the number of bytes
1910 * which were inserted.
1912 * local pos = win.selection.pos
1913 * win.file:insert(pos, "-")
1914 * assert(win.selection.pos == pos+1)
1916 * - Cached inserts before the selection position (`<= selection.pos`) will
1917 * not affect the selection position because the underlying text is replaced
1918 * inplace.
1920 * For these reasons it is generally recommended to update the selection position
1921 * after a modification. The general procedure amounts to:
1923 * 1. Read out the current selection position
1924 * 2. Perform text modifications
1925 * 3. Update the selection postion
1927 * This is what @{Vis:insert} and @{Vis:replace} do internally.
1929 * @type Selection
1930 * @usage
1931 * local data = "new text"
1932 * local pos = win.selection.pos
1933 * win.file:insert(pos, data)
1934 * win.selection.pos = pos + #data
1937 /***
1938 * The zero based byte position in the file.
1940 * Might be `nil` if the selection is in an invalid state.
1941 * Setting this field will move the cursor endpoint of the
1942 * selection to the given position.
1943 * @tfield int pos
1945 /***
1946 * The 1-based line the cursor of this selection resides on.
1948 * @tfield int line
1949 * @see to
1951 /***
1952 * The 1-based column position the cursor of this selection resides on.
1953 * @tfield int col
1954 * @see to
1956 /***
1957 * The 1-based selection index.
1958 * @tfield int number
1960 /***
1961 * The range covered by this selection.
1962 * @tfield Range range
1964 /***
1965 * Whether this selection is anchored.
1966 * @tfield bool anchored
1968 static int window_selection_index(lua_State *L) {
1969 Selection *sel = obj_lightref_check(L, 1, VIS_LUA_TYPE_SELECTION);
1970 if (!sel) {
1971 lua_pushnil(L);
1972 return 1;
1975 if (lua_isstring(L, 2)) {
1976 const char *key = lua_tostring(L, 2);
1977 if (strcmp(key, "pos") == 0) {
1978 pushpos(L, view_cursors_pos(sel));
1979 return 1;
1982 if (strcmp(key, "line") == 0) {
1983 lua_pushunsigned(L, view_cursors_line(sel));
1984 return 1;
1987 if (strcmp(key, "col") == 0) {
1988 lua_pushunsigned(L, view_cursors_col(sel));
1989 return 1;
1992 if (strcmp(key, "number") == 0) {
1993 lua_pushunsigned(L, view_selections_number(sel)+1);
1994 return 1;
1997 if (strcmp(key, "range") == 0) {
1998 Filerange range = view_selections_get(sel);
1999 pushrange(L, &range);
2000 return 1;
2003 if (strcmp(key, "anchored") == 0) {
2004 lua_pushboolean(L, view_selections_anchored(sel));
2005 return 1;
2010 return index_common(L);
2013 static int window_selection_newindex(lua_State *L) {
2014 Selection *sel = obj_lightref_check(L, 1, VIS_LUA_TYPE_SELECTION);
2015 if (!sel)
2016 return 0;
2017 if (lua_isstring(L, 2)) {
2018 const char *key = lua_tostring(L, 2);
2019 if (strcmp(key, "pos") == 0) {
2020 size_t pos = checkpos(L, 3);
2021 view_cursors_to(sel, pos);
2022 return 0;
2025 if (strcmp(key, "range") == 0) {
2026 Filerange range = getrange(L, 3);
2027 if (text_range_valid(&range)) {
2028 view_selections_set(sel, &range);
2029 view_selections_anchor(sel, true);
2030 } else {
2031 view_selection_clear(sel);
2033 return 0;
2036 if (strcmp(key, "anchored") == 0) {
2037 view_selections_anchor(sel, lua_toboolean(L, 3));
2038 return 0;
2041 return newindex_common(L);
2044 /***
2045 * Move cursor of selection.
2046 * @function to
2047 * @tparam int line the 1-based line number
2048 * @tparam int col the 1-based column number
2050 static int window_selection_to(lua_State *L) {
2051 Selection *sel = obj_lightref_check(L, 1, VIS_LUA_TYPE_SELECTION);
2052 if (sel) {
2053 size_t line = checkpos(L, 2);
2054 size_t col = checkpos(L, 3);
2055 view_cursors_place(sel, line, col);
2057 return 0;
2060 static const struct luaL_Reg window_selection_funcs[] = {
2061 { "__index", window_selection_index },
2062 { "__newindex", window_selection_newindex },
2063 { "to", window_selection_to },
2064 { NULL, NULL },
2067 /***
2068 * A file object.
2069 * @type File
2071 /***
2072 * File name.
2073 * @tfield string name the file name relative to current working directory or `nil` if not yet named
2075 /***
2076 * File path.
2077 * @tfield string path the absolute file path or `nil` if not yet named
2079 /***
2080 * File content by logical lines.
2082 * Assigning to array element `0` (`#lines+1`) will insert a new line at
2083 * the beginning (end) of the file.
2084 * @tfield Array(string) lines the file content accessible as 1-based array
2085 * @see content
2086 * @usage
2087 * local lines = vis.win.file.lines
2088 * for i=1, #lines do
2089 * lines[i] = i .. ": " .. lines[i]
2090 * end
2092 /***
2093 * File size in bytes.
2094 * @tfield int size the current file size in bytes
2096 /***
2097 * File state.
2098 * @tfield bool modified whether the file contains unsaved changes
2100 /***
2101 * File permission.
2102 * @tfield int permission the file permission bits as of the most recent load/save
2104 static int file_index(lua_State *L) {
2105 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2107 if (lua_isstring(L, 2)) {
2108 const char *key = lua_tostring(L, 2);
2109 if (strcmp(key, "name") == 0) {
2110 lua_pushstring(L, file_name_get(file));
2111 return 1;
2114 if (strcmp(key, "path") == 0) {
2115 lua_pushstring(L, file->name);
2116 return 1;
2119 if (strcmp(key, "lines") == 0) {
2120 obj_ref_new(L, file->text, VIS_LUA_TYPE_TEXT);
2121 return 1;
2124 if (strcmp(key, "size") == 0) {
2125 lua_pushunsigned(L, text_size(file->text));
2126 return 1;
2129 if (strcmp(key, "modified") == 0) {
2130 lua_pushboolean(L, text_modified(file->text));
2131 return 1;
2134 if (strcmp(key, "permission") == 0) {
2135 struct stat stat = text_stat(file->text);
2136 lua_pushunsigned(L, stat.st_mode & 0777);
2137 return 1;
2141 return index_common(L);
2144 static int file_newindex(lua_State *L) {
2145 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2147 if (lua_isstring(L, 2)) {
2148 const char *key = lua_tostring(L, 2);
2150 if (strcmp(key, "modified") == 0) {
2151 bool modified = lua_isboolean(L, 3) && lua_toboolean(L, 3);
2152 if (modified) {
2153 text_insert(file->text, 0, " ", 1);
2154 text_delete(file->text, 0, 1);
2155 } else {
2156 text_save(file->text, NULL);
2158 return 0;
2162 return newindex_common(L);
2165 /***
2166 * Insert data at position.
2167 * @function insert
2168 * @tparam int pos the 0-based file position in bytes
2169 * @tparam string data the data to insert
2170 * @treturn bool whether the file content was successfully changed
2172 static int file_insert(lua_State *L) {
2173 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2174 size_t pos = checkpos(L, 2);
2175 size_t len;
2176 luaL_checkstring(L, 3);
2177 const char *data = lua_tolstring(L, 3, &len);
2178 lua_pushboolean(L, text_insert(file->text, pos, data, len));
2179 return 1;
2182 /***
2183 * Delete data at position.
2185 * @function delete
2186 * @tparam int pos the 0-based file position in bytes
2187 * @tparam int len the length in bytes to delete
2188 * @treturn bool whether the file content was successfully changed
2190 /***
2191 * Delete file range.
2193 * @function delete
2194 * @tparam Range range the range to delete
2195 * @treturn bool whether the file content was successfully changed
2197 static int file_delete(lua_State *L) {
2198 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2199 Filerange range = getrange(L, 2);
2200 lua_pushboolean(L, text_delete_range(file->text, &range));
2201 return 1;
2204 /***
2205 * Create an iterator over all lines of the file.
2207 * For large files this is probably faster than @{lines}.
2208 * @function lines_iterator
2209 * @return the new iterator
2210 * @see lines
2211 * @usage
2212 * for line in file:lines_iterator() do
2213 * -- do something with line
2214 * end
2216 static int file_lines_iterator_it(lua_State *L);
2217 static int file_lines_iterator(lua_State *L) {
2218 /* need to check second parameter first, because obj_ref_check_get
2219 * modifies the stack */
2220 size_t line = luaL_optunsigned(L, 2, 1);
2221 File *file = obj_ref_check_get(L, 1, VIS_LUA_TYPE_FILE);
2222 size_t *pos = lua_newuserdata(L, sizeof *pos);
2223 *pos = text_pos_by_lineno(file->text, line);
2224 lua_pushcclosure(L, file_lines_iterator_it, 2);
2225 return 1;
2228 static int file_lines_iterator_it(lua_State *L) {
2229 File *file = *(File**)lua_touserdata(L, lua_upvalueindex(1));
2230 size_t *start = lua_touserdata(L, lua_upvalueindex(2));
2231 if (*start == text_size(file->text))
2232 return 0;
2233 size_t end = text_line_end(file->text, *start);
2234 size_t len = end - *start;
2235 char *buf = lua_newuserdata(L, len);
2236 if (!buf && len)
2237 return 0;
2238 len = text_bytes_get(file->text, *start, len, buf);
2239 lua_pushlstring(L, buf, len);
2240 *start = text_line_next(file->text, end);
2241 return 1;
2244 /***
2245 * Get file content of position and length.
2247 * @function content
2248 * @tparam int pos the 0-based file position in bytes
2249 * @tparam int len the length in bytes to read
2250 * @treturn string the file content corresponding to the range
2251 * @see lines
2252 * @usage
2253 * local file = vis.win.file
2254 * local text = file:content(0, file.size)
2256 /***
2257 * Get file content of range.
2259 * @function content
2260 * @tparam Range range the range to read
2261 * @treturn string the file content corresponding to the range
2263 static int file_content(lua_State *L) {
2264 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2265 Filerange range = getrange(L, 2);
2266 if (!text_range_valid(&range))
2267 goto err;
2268 size_t len = text_range_size(&range);
2269 char *data = lua_newuserdata(L, len);
2270 if (!data)
2271 goto err;
2272 len = text_bytes_get(file->text, range.start, len, data);
2273 lua_pushlstring(L, data, len);
2274 return 1;
2275 err:
2276 lua_pushnil(L);
2277 return 1;
2280 /***
2281 * Set mark.
2282 * @function mark_set
2283 * @tparam int pos the position to set the mark to, must be in [0, file.size]
2284 * @treturn Mark mark the mark which can be looked up later
2286 static int file_mark_set(lua_State *L) {
2287 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2288 size_t pos = checkpos(L, 2);
2289 Mark mark = text_mark_set(file->text, pos);
2290 if (mark)
2291 obj_lightref_new(L, (void*)mark, VIS_LUA_TYPE_MARK);
2292 else
2293 lua_pushnil(L);
2294 return 1;
2297 /***
2298 * Get position of mark.
2299 * @function mark_get
2300 * @tparam Mark mark the mark to look up
2301 * @treturn int pos the position of the mark, or `nil` if invalid
2303 static int file_mark_get(lua_State *L) {
2304 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2305 Mark mark = (Mark)obj_lightref_check(L, 2, VIS_LUA_TYPE_MARK);
2306 size_t pos = text_mark_get(file->text, mark);
2307 if (pos == EPOS)
2308 lua_pushnil(L);
2309 else
2310 lua_pushunsigned(L, pos);
2311 return 1;
2314 /***
2315 * Word text object.
2317 * @function text_object_word
2318 * @tparam int pos the position which must be part of the word
2319 * @treturn Range range the range
2322 /***
2323 * WORD text object.
2325 * @function text_object_longword
2326 * @tparam int pos the position which must be part of the word
2327 * @treturn Range range the range
2330 static int file_text_object(lua_State *L) {
2331 Filerange range = text_range_empty();
2332 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2333 size_t pos = checkpos(L, 2);
2334 size_t idx = lua_tointeger(L, lua_upvalueindex(1));
2335 if (idx < LENGTH(vis_textobjects)) {
2336 const TextObject *txtobj = &vis_textobjects[idx];
2337 if (txtobj->txt)
2338 range = txtobj->txt(file->text, pos);
2340 pushrange(L, &range);
2341 return 1;
2344 static const struct luaL_Reg file_funcs[] = {
2345 { "__index", file_index },
2346 { "__newindex", file_newindex },
2347 { "insert", file_insert },
2348 { "delete", file_delete },
2349 { "lines_iterator", file_lines_iterator },
2350 { "content", file_content },
2351 { "mark_set", file_mark_set },
2352 { "mark_get", file_mark_get },
2353 { NULL, NULL },
2356 static int file_lines_index(lua_State *L) {
2357 Text *txt = obj_ref_check(L, 1, VIS_LUA_TYPE_TEXT);
2358 size_t line = luaL_checkunsigned(L, 2);
2359 size_t start = text_pos_by_lineno(txt, line);
2360 size_t end = text_line_end(txt, start);
2361 if (start != EPOS && end != EPOS) {
2362 size_t size = end - start;
2363 char *data = lua_newuserdata(L, size);
2364 if (!data && size)
2365 goto err;
2366 size = text_bytes_get(txt, start, size, data);
2367 lua_pushlstring(L, data, size);
2368 return 1;
2370 err:
2371 lua_pushnil(L);
2372 return 1;
2375 static int file_lines_newindex(lua_State *L) {
2376 Text *txt = obj_ref_check(L, 1, VIS_LUA_TYPE_TEXT);
2377 size_t line = luaL_checkunsigned(L, 2);
2378 size_t size;
2379 const char *data = luaL_checklstring(L, 3, &size);
2380 if (line == 0) {
2381 text_insert(txt, 0, data, size);
2382 text_insert(txt, size, "\n", 1);
2383 return 0;
2385 size_t start = text_pos_by_lineno(txt, line);
2386 size_t end = text_line_end(txt, start);
2387 if (start != EPOS && end != EPOS) {
2388 text_delete(txt, start, end - start);
2389 text_insert(txt, start, data, size);
2390 if (text_size(txt) == start + size)
2391 text_insert(txt, text_size(txt), "\n", 1);
2393 return 0;
2396 static int file_lines_len(lua_State *L) {
2397 Text *txt = obj_ref_check(L, 1, VIS_LUA_TYPE_TEXT);
2398 size_t lines = 0;
2399 char lastchar;
2400 size_t size = text_size(txt);
2401 if (size > 0)
2402 lines = text_lineno_by_pos(txt, size);
2403 if (lines > 1 && text_byte_get(txt, size-1, &lastchar) && lastchar == '\n')
2404 lines--;
2405 lua_pushunsigned(L, lines);
2406 return 1;
2409 static const struct luaL_Reg file_lines_funcs[] = {
2410 { "__index", file_lines_index },
2411 { "__newindex", file_lines_newindex },
2412 { "__len", file_lines_len },
2413 { NULL, NULL },
2416 static int window_marks_index(lua_State *L) {
2417 lua_newtable(L);
2418 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
2419 Win *win = obj_ref_check_containerof(L, 1, VIS_LUA_TYPE_MARKS, offsetof(Win, saved_selections));
2420 if (!win)
2421 return 1;
2422 const char *symbol = luaL_checkstring(L, 2);
2423 if (strlen(symbol) != 1)
2424 return 1;
2425 enum VisMark mark = vis_mark_from(vis, symbol[0]);
2426 if (mark == VIS_MARK_INVALID)
2427 return 1;
2429 Array arr = vis_mark_get(win, mark);
2430 for (size_t i = 0, len = array_length(&arr); i < len; i++) {
2431 Filerange *range = array_get(&arr, i);
2432 lua_pushunsigned(L, i+1);
2433 pushrange(L, range);
2434 lua_settable(L, -3);
2436 array_release(&arr);
2437 return 1;
2440 static int window_marks_newindex(lua_State *L) {
2441 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
2442 Win *win = obj_ref_check_containerof(L, 1, VIS_LUA_TYPE_MARKS, offsetof(Win, saved_selections));
2443 if (!win)
2444 return 0;
2445 const char *symbol = luaL_checkstring(L, 2);
2446 if (strlen(symbol) != 1)
2447 return 0;
2448 enum VisMark mark = vis_mark_from(vis, symbol[0]);
2449 if (mark == VIS_MARK_INVALID)
2450 return 0;
2452 Array ranges;
2453 array_init_sized(&ranges, sizeof(Filerange));
2455 if (lua_istable(L, 3)) {
2456 lua_pushnil(L);
2457 while (lua_next(L, 3)) {
2458 Filerange range = getrange(L, -1);
2459 if (text_range_valid(&range))
2460 array_add(&ranges, &range);
2461 lua_pop(L, 1);
2465 vis_mark_set(win, mark, &ranges);
2466 array_release(&ranges);
2467 return 0;
2470 static int window_marks_len(lua_State *L) {
2471 lua_pushunsigned(L, VIS_MARK_INVALID);
2472 return 1;
2475 static const struct luaL_Reg window_marks_funcs[] = {
2476 { "__index", window_marks_index },
2477 { "__newindex", window_marks_newindex },
2478 { "__len", window_marks_len },
2479 { NULL, NULL },
2482 /***
2483 * The user interface.
2485 * @type Ui
2487 /***
2488 * Number of available colors.
2489 * @tfield int colors
2492 /***
2493 * A file range.
2495 * For a valid range `start <= finish` holds.
2496 * An invalid range is represented as `nil`.
2497 * @type Range
2499 /***
2500 * The beginning of the range.
2501 * @tfield int start
2503 /***
2504 * The end of the range.
2505 * @tfield int finish
2508 /***
2509 * Modes.
2510 * @section Modes
2513 /***
2514 * Mode constants.
2515 * @table modes
2516 * @tfield int NORMAL
2517 * @tfield int OPERATOR_PENDING
2518 * @tfield int INSERT
2519 * @tfield int REPLACE
2520 * @tfield int VISUAL
2521 * @tfield int VISUAL_LINE
2522 * @see Vis:map
2523 * @see Window:map
2526 /***
2527 * Key Handling.
2529 * This section describes the contract between the editor core and Lua
2530 * key handling functions mapped to symbolic keys using either @{Vis:map}
2531 * or @{Window:map}.
2533 * @section Key_Handling
2536 /***
2537 * Example of a key handling function.
2539 * The keyhandler is invoked with the pending content of the input queue
2540 * given as argument. This might be the empty string if no further input
2541 * is available.
2543 * The function is expected to return the number of *bytes* it has
2544 * consumed from the passed input keys. A negative return value is
2545 * interpreted as an indication that not enough input was available. The
2546 * function will be called again once the user has provided more input. A
2547 * missing return value (i.e. `nil`) is interpreted as zero, meaning
2548 * no further input was consumed but the function completed successfully.
2550 * @function keyhandler
2551 * @tparam string keys the keys following the mapping
2552 * @treturn int the number of *bytes* being consumed by the function (see above)
2553 * @see Vis:action_register
2554 * @see Vis:map
2555 * @see Window:map
2556 * @usage
2557 * vis:map(vis.modes.INSERT, "<C-k>", function(keys)
2558 * if #keys < 2 then
2559 * return -1 -- need more input
2560 * end
2561 * local digraph = keys:sub(1, 2)
2562 * if digraph == "l*" then
2563 * vis:feedkeys('λ')
2564 * return 2 -- consume 2 bytes of input
2565 * end
2566 * end, "Insert digraph")
2569 /***
2570 * Core Events.
2572 * These events are invoked from the editor core.
2573 * The following functions are invoked if they are registered in the
2574 * `vis.events` table. Users scripts should generally use the [Events](#events)
2575 * mechanism instead which multiplexes these core events.
2577 * @section Core_Events
2580 static void vis_lua_event_get(lua_State *L, const char *name) {
2581 lua_getglobal(L, "vis");
2582 lua_getfield(L, -1, "events");
2583 if (lua_istable(L, -1)) {
2584 lua_getfield(L, -1, name);
2586 lua_remove(L, -2);
2589 static void vis_lua_event_call(Vis *vis, const char *name) {
2590 lua_State *L = vis->lua;
2591 vis_lua_event_get(L, name);
2592 if (lua_isfunction(L, -1))
2593 pcall(vis, L, 0, 0);
2594 lua_pop(L, 1);
2597 static bool vis_lua_path_strip(Vis *vis) {
2598 lua_State *L = vis->lua;
2599 lua_getglobal(L, "package");
2601 for (const char **var = (const char*[]){ "path", "cpath", NULL }; *var; var++) {
2603 lua_getfield(L, -1, *var);
2604 const char *path = lua_tostring(L, -1);
2605 lua_pop(L, 1);
2606 if (!path)
2607 return false;
2609 char *copy = strdup(path), *stripped = calloc(1, strlen(path)+2);
2610 if (!copy || !stripped) {
2611 free(copy);
2612 free(stripped);
2613 return false;
2616 for (char *elem = copy, *stripped_elem = stripped, *next; elem; elem = next) {
2617 if ((next = strstr(elem, ";")))
2618 *next++ = '\0';
2619 if (strstr(elem, "./"))
2620 continue; /* skip relative path entries */
2621 stripped_elem += sprintf(stripped_elem, "%s;", elem);
2624 lua_pushstring(L, stripped);
2625 lua_setfield(L, -2, *var);
2627 free(copy);
2628 free(stripped);
2631 lua_pop(L, 1); /* package */
2632 return true;
2635 bool vis_lua_path_add(Vis *vis, const char *path) {
2636 lua_State *L = vis->lua;
2637 if (!L || !path)
2638 return false;
2639 lua_getglobal(L, "package");
2640 lua_pushstring(L, path);
2641 lua_pushstring(L, "/?.lua;");
2642 lua_pushstring(L, path);
2643 lua_pushstring(L, "/?/init.lua;");
2644 lua_getfield(L, -5, "path");
2645 lua_concat(L, 5);
2646 lua_setfield(L, -2, "path");
2647 lua_pop(L, 1); /* package */
2648 return true;
2651 bool vis_lua_paths_get(Vis *vis, char **lpath, char **cpath) {
2652 lua_State *L = vis->lua;
2653 if (!L)
2654 return false;
2655 const char *s;
2656 lua_getglobal(L, "package");
2657 lua_getfield(L, -1, "path");
2658 s = lua_tostring(L, -1);
2659 *lpath = s ? strdup(s) : NULL;
2660 lua_getfield(L, -2, "cpath");
2661 s = lua_tostring(L, -1);
2662 *cpath = s ? strdup(s) : NULL;
2663 return true;
2666 static bool package_exist(Vis *vis, lua_State *L, const char *name) {
2667 const char lua[] =
2668 "local name = ...\n"
2669 "for _, searcher in ipairs(package.searchers or package.loaders) do\n"
2670 "local loader = searcher(name)\n"
2671 "if type(loader) == 'function' then\n"
2672 "return true\n"
2673 "end\n"
2674 "end\n"
2675 "return false\n";
2676 if (luaL_loadstring(L, lua) != LUA_OK)
2677 return false;
2678 lua_pushstring(L, name);
2679 /* an error indicates package exists */
2680 bool ret = lua_pcall(L, 1, 1, 0) != LUA_OK || lua_toboolean(L, -1);
2681 lua_pop(L, 1);
2682 return ret;
2685 static void *alloc_lua(void *ud, void *ptr, size_t osize, size_t nsize) {
2686 if (nsize == 0) {
2687 free(ptr);
2688 return NULL;
2689 } else {
2690 return realloc(ptr, nsize);
2694 /***
2695 * Editor initialization completed.
2696 * This event is emitted immediately after `visrc.lua` has been sourced, but
2697 * before any other events have occured, in particular the command line arguments
2698 * have not yet been processed.
2700 * Can be used to set *global* configuration options.
2701 * @function init
2703 void vis_lua_init(Vis *vis) {
2704 lua_State *L = lua_newstate(alloc_lua, vis);
2705 if (!L)
2706 return;
2707 vis->lua = L;
2708 lua_atpanic(L, &panic_handler);
2710 luaL_openlibs(L);
2712 #if CONFIG_LPEG
2713 extern int luaopen_lpeg(lua_State *L);
2714 lua_getglobal(L, "package");
2715 lua_getfield(L, -1, "preload");
2716 lua_pushcfunction(L, luaopen_lpeg);
2717 lua_setfield(L, -2, "lpeg");
2718 lua_pop(L, 2);
2719 #endif
2721 /* remove any relative paths from lua's default package.path */
2722 vis_lua_path_strip(vis);
2724 /* extends lua's package.path with:
2725 * - $VIS_PATH
2726 * - ./lua (relative path to the binary location)
2727 * - $XDG_CONFIG_HOME/vis (defaulting to $HOME/.config/vis)
2728 * - /etc/vis (for system-wide configuration provided by administrator)
2729 * - /usr/(local/)?share/vis (or whatever is specified during ./configure)
2730 * - package.path (standard lua search path)
2732 char path[PATH_MAX];
2734 vis_lua_path_add(vis, VIS_PATH);
2736 /* try to get users home directory */
2737 const char *home = getenv("HOME");
2738 if (!home || !*home) {
2739 struct passwd *pw = getpwuid(getuid());
2740 if (pw)
2741 home = pw->pw_dir;
2744 vis_lua_path_add(vis, "/etc/vis");
2746 const char *xdg_config = getenv("XDG_CONFIG_HOME");
2747 if (xdg_config) {
2748 snprintf(path, sizeof path, "%s/vis", xdg_config);
2749 vis_lua_path_add(vis, path);
2750 } else if (home && *home) {
2751 snprintf(path, sizeof path, "%s/.config/vis", home);
2752 vis_lua_path_add(vis, path);
2755 ssize_t len = readlink("/proc/self/exe", path, sizeof(path)-1);
2756 if (len > 0) {
2757 path[len] = '\0';
2758 /* some idotic dirname(3) implementations return pointers to statically
2759 * allocated memory, hence we use memmove to copy it back */
2760 char *dir = dirname(path);
2761 if (dir) {
2762 size_t len = strlen(dir)+1;
2763 if (len < sizeof(path) - sizeof("/lua")) {
2764 memmove(path, dir, len);
2765 strcat(path, "/lua");
2766 vis_lua_path_add(vis, path);
2771 vis_lua_path_add(vis, getenv("VIS_PATH"));
2773 /* table in registry to lookup object type, stores metatable -> type mapping */
2774 lua_newtable(L);
2775 lua_setfield(L, LUA_REGISTRYINDEX, "vis.types");
2776 /* table in registry to track lifetimes of C objects */
2777 lua_newtable(L);
2778 lua_setfield(L, LUA_REGISTRYINDEX, "vis.objects");
2779 /* table in registry to store references to Lua functions */
2780 lua_newtable(L);
2781 lua_setfield(L, LUA_REGISTRYINDEX, "vis.functions");
2782 /* metatable used to type check user data */
2783 obj_type_new(L, VIS_LUA_TYPE_VIS);
2784 luaL_setfuncs(L, vis_lua, 0);
2785 lua_newtable(L);
2786 lua_setfield(L, -2, "types");
2787 /* create reference to main vis object, such that the further
2788 * calls to obj_type_new can register the type meta tables in
2789 * vis.types[name] */
2790 obj_ref_new(L, vis, "vis");
2791 lua_setglobal(L, "vis");
2793 obj_type_new(L, VIS_LUA_TYPE_FILE);
2795 const struct {
2796 enum VisTextObject id;
2797 const char *name;
2798 } textobjects[] = {
2799 { VIS_TEXTOBJECT_INNER_WORD, "text_object_word" },
2800 { VIS_TEXTOBJECT_INNER_LONGWORD, "text_object_longword" },
2803 for (size_t i = 0; i < LENGTH(textobjects); i++) {
2804 lua_pushunsigned(L, textobjects[i].id);
2805 lua_pushcclosure(L, file_text_object, 1);
2806 lua_setfield(L, -2, textobjects[i].name);
2809 luaL_setfuncs(L, file_funcs, 0);
2811 obj_type_new(L, VIS_LUA_TYPE_TEXT);
2812 luaL_setfuncs(L, file_lines_funcs, 0);
2813 obj_type_new(L, VIS_LUA_TYPE_WINDOW);
2814 luaL_setfuncs(L, window_funcs, 0);
2816 const struct {
2817 enum UiStyle id;
2818 const char *name;
2819 } styles[] = {
2820 { UI_STYLE_DEFAULT, "STYLE_DEFAULT" },
2821 { UI_STYLE_CURSOR, "STYLE_CURSOR" },
2822 { UI_STYLE_CURSOR_PRIMARY, "STYLE_CURSOR_PRIMARY" },
2823 { UI_STYLE_CURSOR_LINE, "STYLE_CURSOR_LINE" },
2824 { UI_STYLE_SELECTION, "STYLE_SELECTION" },
2825 { UI_STYLE_LINENUMBER, "STYLE_LINENUMBER" },
2826 { UI_STYLE_LINENUMBER_CURSOR, "STYLE_LINENUMBER_CURSOR" },
2827 { UI_STYLE_COLOR_COLUMN, "STYLE_COLOR_COLUMN" },
2828 { UI_STYLE_STATUS, "STYLE_STATUS" },
2829 { UI_STYLE_STATUS_FOCUSED, "STYLE_STATUS_FOCUSED" },
2830 { UI_STYLE_SEPARATOR, "STYLE_SEPARATOR" },
2831 { UI_STYLE_INFO, "STYLE_INFO" },
2832 { UI_STYLE_EOF, "STYLE_EOF" },
2835 for (size_t i = 0; i < LENGTH(styles); i++) {
2836 lua_pushunsigned(L, styles[i].id);
2837 lua_setfield(L, -2, styles[i].name);
2840 obj_type_new(L, VIS_LUA_TYPE_MARK);
2841 obj_type_new(L, VIS_LUA_TYPE_MARKS);
2842 lua_pushlightuserdata(L, vis);
2843 luaL_setfuncs(L, window_marks_funcs, 1);
2845 obj_type_new(L, VIS_LUA_TYPE_SELECTION);
2846 luaL_setfuncs(L, window_selection_funcs, 0);
2847 obj_type_new(L, VIS_LUA_TYPE_SELECTIONS);
2848 luaL_setfuncs(L, window_selections_funcs, 0);
2850 obj_type_new(L, VIS_LUA_TYPE_UI);
2851 luaL_setfuncs(L, ui_funcs, 0);
2852 lua_pushunsigned(L, vis->ui->colors(vis->ui));
2853 lua_setfield(L, -2, "colors");
2855 obj_type_new(L, VIS_LUA_TYPE_REGISTERS);
2856 lua_pushlightuserdata(L, vis);
2857 luaL_setfuncs(L, registers_funcs, 1);
2859 obj_type_new(L, VIS_LUA_TYPE_KEYACTION);
2861 lua_getglobal(L, "vis");
2862 lua_getmetatable(L, -1);
2864 lua_pushstring(L, VERSION);
2865 lua_setfield(L, -2, "VERSION");
2867 lua_newtable(L);
2869 static const struct {
2870 enum VisMode id;
2871 const char *name;
2872 } modes[] = {
2873 { VIS_MODE_NORMAL, "NORMAL" },
2874 { VIS_MODE_OPERATOR_PENDING, "OPERATOR_PENDING" },
2875 { VIS_MODE_VISUAL, "VISUAL" },
2876 { VIS_MODE_VISUAL_LINE, "VISUAL_LINE" },
2877 { VIS_MODE_INSERT, "INSERT" },
2878 { VIS_MODE_REPLACE, "REPLACE" },
2881 for (size_t i = 0; i < LENGTH(modes); i++) {
2882 lua_pushunsigned(L, modes[i].id);
2883 lua_setfield(L, -2, modes[i].name);
2886 lua_setfield(L, -2, "modes");
2888 if (!package_exist(vis, L, "visrc")) {
2889 vis_info_show(vis, "WARNING: failed to load visrc.lua");
2890 } else {
2891 lua_getglobal(L, "require");
2892 lua_pushstring(L, "visrc");
2893 pcall(vis, L, 1, 0);
2894 vis_lua_event_call(vis, "init");
2898 /***
2899 * Editor startup completed.
2900 * This event is emitted immediately before the main loop starts.
2901 * At this point all files are loaded and corresponding windows are created.
2902 * We are about to process interactive keyboard input.
2903 * @function start
2905 void vis_lua_start(Vis *vis) {
2906 vis_lua_event_call(vis, "start");
2910 * Editor is about to terminate.
2911 * @function quit
2913 void vis_lua_quit(Vis *vis) {
2914 if (!vis->lua)
2915 return;
2916 vis_lua_event_call(vis, "quit");
2917 lua_close(vis->lua);
2918 vis->lua = NULL;
2921 /***
2922 * Input key event in either input or replace mode.
2923 * @function input
2924 * @tparam string key
2925 * @treturn bool whether the key was consumed or not
2927 static bool vis_lua_input(Vis *vis, const char *key, size_t len) {
2928 lua_State *L = vis->lua;
2929 if (!L || !vis->win || vis->win->file->internal)
2930 return false;
2931 bool ret = false;
2932 vis_lua_event_get(L, "input");
2933 if (lua_isfunction(L, -1)) {
2934 lua_pushlstring(L, key, len);
2935 if (pcall(vis, L, 1, 1) == 0) {
2936 ret = lua_isboolean(L, -1) && lua_toboolean(L, -1);
2937 lua_pop(L, 1);
2940 lua_pop(L, 1);
2941 return ret;
2944 void vis_lua_mode_insert_input(Vis *vis, const char *key, size_t len) {
2945 if (!vis_lua_input(vis, key, len))
2946 vis_insert_key(vis, key, len);
2949 void vis_lua_mode_replace_input(Vis *vis, const char *key, size_t len) {
2950 if (!vis_lua_input(vis, key, len))
2951 vis_replace_key(vis, key, len);
2954 /***
2955 * File open.
2956 * @function file_open
2957 * @tparam File file the file to be opened
2959 void vis_lua_file_open(Vis *vis, File *file) {
2960 debug("event: file-open: %s %p %p\n", file->name ? file->name : "unnamed", (void*)file, (void*)file->text);
2961 lua_State *L = vis->lua;
2962 if (!L)
2963 return;
2964 vis_lua_event_get(L, "file_open");
2965 if (lua_isfunction(L, -1)) {
2966 obj_ref_new(L, file, VIS_LUA_TYPE_FILE);
2967 pcall(vis, L, 1, 0);
2969 lua_pop(L, 1);
2972 /***
2973 * File pre save.
2974 * Triggered *before* the file is being written.
2975 * @function file_save_pre
2976 * @tparam File file the file being written
2977 * @tparam string path the absolute path to which the file will be written, `nil` if standard output
2978 * @treturn bool whether the write operation should be proceeded
2980 bool vis_lua_file_save_pre(Vis *vis, File *file, const char *path) {
2981 lua_State *L = vis->lua;
2982 if (!L)
2983 return true;
2984 vis_lua_event_get(L, "file_save_pre");
2985 if (lua_isfunction(L, -1)) {
2986 obj_ref_new(L, file, VIS_LUA_TYPE_FILE);
2987 lua_pushstring(L, path);
2988 if (pcall(vis, L, 2, 1) != 0)
2989 return false;
2990 return !lua_isboolean(L, -1) || lua_toboolean(L, -1);
2992 lua_pop(L, 1);
2993 return true;
2996 /***
2997 * File post save.
2998 * Triggered *after* a successfull write operation.
2999 * @function file_save_post
3000 * @tparam File file the file which was written
3001 * @tparam string path the absolute path to which it was written, `nil` if standard output
3003 void vis_lua_file_save_post(Vis *vis, File *file, const char *path) {
3004 lua_State *L = vis->lua;
3005 if (!L)
3006 return;
3007 vis_lua_event_get(L, "file_save_post");
3008 if (lua_isfunction(L, -1)) {
3009 obj_ref_new(L, file, VIS_LUA_TYPE_FILE);
3010 lua_pushstring(L, path);
3011 pcall(vis, L, 2, 0);
3013 lua_pop(L, 1);
3016 /***
3017 * File close.
3018 * The last window displaying the file has been closed.
3019 * @function file_close
3020 * @tparam File file the file being closed
3022 void vis_lua_file_close(Vis *vis, File *file) {
3023 debug("event: file-close: %s %p %p\n", file->name ? file->name : "unnamed", (void*)file, (void*)file->text);
3024 lua_State *L = vis->lua;
3025 if (!L)
3026 return;
3027 vis_lua_event_get(L, "file_close");
3028 if (lua_isfunction(L, -1)) {
3029 obj_ref_new(L, file, VIS_LUA_TYPE_FILE);
3030 pcall(vis, L, 1, 0);
3032 obj_ref_free(L, file->marks);
3033 obj_ref_free(L, file->text);
3034 obj_ref_free(L, file);
3035 lua_pop(L, 1);
3038 /***
3039 * Window open.
3040 * A new window has been created.
3041 * @function win_open
3042 * @tparam Window win the window being opened
3044 void vis_lua_win_open(Vis *vis, Win *win) {
3045 debug("event: win-open: %s %p %p\n", win->file->name ? win->file->name : "unnamed", (void*)win, (void*)win->view);
3046 lua_State *L = vis->lua;
3047 if (!L)
3048 return;
3049 vis_lua_event_get(L, "win_open");
3050 if (lua_isfunction(L, -1)) {
3051 obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW);
3052 pcall(vis, L, 1, 0);
3054 lua_pop(L, 1);
3057 /***
3058 * Window close.
3059 * An window is being closed.
3060 * @function win_close
3061 * @tparam Window win the window being closed
3063 void vis_lua_win_close(Vis *vis, Win *win) {
3064 debug("event: win-close: %s %p %p\n", win->file->name ? win->file->name : "unnamed", (void*)win, (void*)win->view);
3065 lua_State *L = vis->lua;
3066 if (!L)
3067 return;
3068 vis_lua_event_get(L, "win_close");
3069 if (lua_isfunction(L, -1)) {
3070 obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW);
3071 pcall(vis, L, 1, 0);
3073 obj_ref_free(L, win->view);
3074 obj_ref_free(L, win);
3075 lua_pop(L, 1);
3079 * Window highlight.
3080 * The window has been redrawn and the syntax highlighting needs to be performed.
3081 * @function win_highlight
3082 * @tparam Window win the window being redrawn
3083 * @see style
3085 void vis_lua_win_highlight(Vis *vis, Win *win) {
3086 lua_State *L = vis->lua;
3087 if (!L)
3088 return;
3089 vis_lua_event_get(L, "win_highlight");
3090 if (lua_isfunction(L, -1)) {
3091 obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW);
3092 pcall(vis, L, 1, 0);
3094 lua_pop(L, 1);
3097 /***
3098 * Window status bar redraw.
3099 * @function win_status
3100 * @tparam Window win the affected window
3101 * @see status
3103 void vis_lua_win_status(Vis *vis, Win *win) {
3104 lua_State *L = vis->lua;
3105 if (!L || win->file->internal) {
3106 window_status_update(vis, win);
3107 return;
3109 vis_lua_event_get(L, "win_status");
3110 if (lua_isfunction(L, -1)) {
3111 obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW);
3112 pcall(vis, L, 1, 0);
3113 } else {
3114 window_status_update(vis, win);
3116 lua_pop(L, 1);
3119 /***
3120 * CSI command received from terminal.
3121 * @function term_csi
3122 * @param List of CSI parameters
3124 void vis_lua_term_csi(Vis *vis, const long *csi) {
3125 lua_State *L = vis->lua;
3126 if (!L)
3127 return;
3128 vis_lua_event_get(L, "term_csi");
3129 if (lua_isfunction(L, -1)) {
3130 int nargs = csi[1];
3131 lua_pushinteger(L, csi[0]);
3132 for (int i = 0; i < nargs; i++)
3133 lua_pushinteger(L, csi[2 + i]);
3134 pcall(vis, L, 1 + nargs, 0);
3136 lua_pop(L, 1);
3139 #endif