vis: make paragraph textobjects linewise
[vis/gkirilov.git] / vis-lua.c
blobe8421355c9f84ebc71e16543bbae96359add4813
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_WIN_OPTS "winoptions"
35 #define VIS_LUA_TYPE_VIS_OPTS "visoptions"
36 #define VIS_LUA_TYPE_FILE "file"
37 #define VIS_LUA_TYPE_TEXT "text"
38 #define VIS_LUA_TYPE_MARK "mark"
39 #define VIS_LUA_TYPE_MARKS "marks"
40 #define VIS_LUA_TYPE_WINDOW "window"
41 #define VIS_LUA_TYPE_SELECTION "selection"
42 #define VIS_LUA_TYPE_SELECTIONS "selections"
43 #define VIS_LUA_TYPE_UI "ui"
44 #define VIS_LUA_TYPE_REGISTERS "registers"
45 #define VIS_LUA_TYPE_KEYACTION "keyaction"
47 #ifndef DEBUG_LUA
48 #define DEBUG_LUA 0
49 #endif
51 #if DEBUG_LUA
52 #define debug(...) do { printf(__VA_ARGS__); fflush(stdout); } while (0)
53 #else
54 #define debug(...) do { } while (0)
55 #endif
57 static void window_status_update(Vis *vis, Win *win) {
58 char left_parts[4][255] = { "", "", "", "" };
59 char right_parts[4][32] = { "", "", "", "" };
60 char left[sizeof(left_parts)+LENGTH(left_parts)*8];
61 char right[sizeof(right_parts)+LENGTH(right_parts)*8];
62 char status[sizeof(left)+sizeof(right)+1];
63 size_t left_count = 0;
64 size_t right_count = 0;
66 View *view = win->view;
67 File *file = win->file;
68 Text *txt = file->text;
69 int width = vis_window_width_get(win);
70 enum UiOption options = view_options_get(view);
71 bool focused = vis->win == win;
72 const char *filename = file_name_get(file);
73 const char *mode = vis->mode->status;
75 if (focused && mode)
76 strcpy(left_parts[left_count++], mode);
78 snprintf(left_parts[left_count++], sizeof(left_parts[0]), "%s%s%s",
79 filename ? filename : "[No Name]",
80 text_modified(txt) ? " [+]" : "",
81 vis_macro_recording(vis) ? " @": "");
83 int count = vis_count_get(vis);
84 const char *keys = buffer_content0(&vis->input_queue);
85 if (keys && keys[0])
86 snprintf(right_parts[right_count++], sizeof(right_parts[0]), "%s", keys);
87 else if (count != VIS_COUNT_UNKNOWN)
88 snprintf(right_parts[right_count++], sizeof(right_parts[0]), "%d", count);
90 int sel_count = view_selections_count(view);
91 if (sel_count > 1) {
92 Selection *s = view_selections_primary_get(view);
93 int sel_number = view_selections_number(s) + 1;
94 snprintf(right_parts[right_count++], sizeof(right_parts[0]),
95 "%d/%d", sel_number, sel_count);
98 size_t size = text_size(txt);
99 size_t pos = view_cursor_get(view);
100 size_t percent = 0;
101 if (size > 0) {
102 double tmp = ((double)pos/(double)size)*100;
103 percent = (size_t)(tmp+1);
105 snprintf(right_parts[right_count++], sizeof(right_parts[0]),
106 "%zu%%", percent);
108 if (!(options & UI_OPTION_LARGE_FILE)) {
109 Selection *sel = view_selections_primary_get(win->view);
110 size_t line = view_cursors_line(sel);
111 size_t col = view_cursors_col(sel);
112 if (col > UI_LARGE_FILE_LINE_SIZE) {
113 options |= UI_OPTION_LARGE_FILE;
114 view_options_set(win->view, options);
116 snprintf(right_parts[right_count++], sizeof(right_parts[0]),
117 "%zu, %zu", line, col);
120 int left_len = snprintf(left, sizeof(left), " %s%s%s%s%s%s%s",
121 left_parts[0],
122 left_parts[1][0] ? " » " : "",
123 left_parts[1],
124 left_parts[2][0] ? " » " : "",
125 left_parts[2],
126 left_parts[3][0] ? " » " : "",
127 left_parts[3]);
129 int right_len = snprintf(right, sizeof(right), "%s%s%s%s%s%s%s ",
130 right_parts[0],
131 right_parts[1][0] ? " « " : "",
132 right_parts[1],
133 right_parts[2][0] ? " « " : "",
134 right_parts[2],
135 right_parts[3][0] ? " « " : "",
136 right_parts[3]);
138 if (left_len < 0 || right_len < 0)
139 return;
140 int left_width = text_string_width(left, left_len);
141 int right_width = text_string_width(right, right_len);
143 int spaces = width - left_width - right_width;
144 if (spaces < 1)
145 spaces = 1;
147 snprintf(status, sizeof(status), "%s%*s%s", left, spaces, " ", right);
148 vis_window_status(win, status);
151 #if !CONFIG_LUA
153 bool vis_lua_path_add(Vis *vis, const char *path) { return true; }
154 bool vis_lua_paths_get(Vis *vis, char **lpath, char **cpath) { return false; }
155 void vis_lua_init(Vis *vis) { }
156 void vis_lua_start(Vis *vis) { }
157 void vis_lua_quit(Vis *vis) { }
158 void vis_lua_file_open(Vis *vis, File *file) { }
159 bool vis_lua_file_save_pre(Vis *vis, File *file, const char *path) { return true; }
160 void vis_lua_file_save_post(Vis *vis, File *file, const char *path) { }
161 void vis_lua_file_close(Vis *vis, File *file) { }
162 void vis_lua_win_open(Vis *vis, Win *win) { }
163 void vis_lua_win_close(Vis *vis, Win *win) { }
164 void vis_lua_win_highlight(Vis *vis, Win *win) { }
165 void vis_lua_win_status(Vis *vis, Win *win) { window_status_update(vis, win); }
166 void vis_lua_term_csi(Vis *vis, const long *csi) { }
167 void vis_lua_process_response(Vis *vis, const char *name,
168 char *buffer, size_t len, ResponseType rtype) { }
169 void vis_lua_ui_draw(Vis *vis) { }
171 #else
173 #if DEBUG_LUA
174 static void stack_dump_entry(lua_State *L, int i) {
175 int t = lua_type(L, i);
176 switch (t) {
177 case LUA_TNIL:
178 printf("nil");
179 break;
180 case LUA_TBOOLEAN:
181 printf(lua_toboolean(L, i) ? "true" : "false");
182 break;
183 case LUA_TLIGHTUSERDATA:
184 printf("lightuserdata(%p)", lua_touserdata(L, i));
185 break;
186 case LUA_TNUMBER:
187 printf("%g", lua_tonumber(L, i));
188 break;
189 case LUA_TSTRING:
190 printf("`%s'", lua_tostring(L, i));
191 break;
192 case LUA_TTABLE:
193 printf("table[");
194 lua_pushnil(L); /* first key */
195 while (lua_next(L, i > 0 ? i : i - 1)) {
196 stack_dump_entry(L, -2);
197 printf("=");
198 stack_dump_entry(L, -1);
199 printf(",");
200 lua_pop(L, 1); /* remove value, keep key */
202 printf("]");
203 break;
204 case LUA_TUSERDATA:
205 printf("userdata(%p)", lua_touserdata(L, i));
206 break;
207 default: /* other values */
208 printf("%s", lua_typename(L, t));
209 break;
213 static void stack_dump(lua_State *L, const char *format, ...) {
214 va_list ap;
215 va_start(ap, format);
216 vprintf(format, ap);
217 va_end(ap);
218 int top = lua_gettop(L);
219 for (int i = 1; i <= top; i++) {
220 printf("%d: ", i);
221 stack_dump_entry(L, i);
222 printf("\n");
224 printf("\n\n");
225 fflush(stdout);
228 #endif
230 static int panic_handler(lua_State *L) {
231 void *ud = NULL;
232 lua_getallocf(L, &ud);
233 if (ud) {
234 Vis *vis = ud;
235 vis->lua = NULL;
236 const char *msg = NULL;
237 if (lua_type(L, -1) == LUA_TSTRING)
238 msg = lua_tostring(L, -1);
239 vis_info_show(vis, "Fatal Lua error: %s", msg ? msg : "unknown reason");
240 lua_close(L);
241 if (vis->running)
242 siglongjmp(vis->sigbus_jmpbuf, 1);
244 return 0;
247 static int error_handler(lua_State *L) {
248 const char *msg = lua_tostring(L, 1);
249 luaL_traceback(L, L, msg ? msg : NULL, 1);
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_pushcclosure(L, error_handler, 0);
257 lua_insert(L, msgh);
258 int ret = lua_pcall(L, nargs, nresults, msgh);
259 lua_remove(L, msgh);
260 if (ret) {
261 const char *msg = lua_tostring(L, -1);
262 if (msg) {
263 if (strstr(msg, "C stack overflow")) {
264 vis->ui->terminal_save(vis->ui, false);
265 fprintf(stderr, "%s", msg);
266 vis->ui->terminal_restore(vis->ui);
267 } else {
268 vis_message_show(vis, msg);
272 return ret;
275 /* expects a lua function at stack position `narg` and stores a
276 * reference to it in the registry. The return value can be used
277 * to look it up.
279 * registry["vis.functions"][(void*)(function)] = function
281 static const void *func_ref_new(lua_State *L, int narg) {
282 const void *addr = lua_topointer(L, narg);
283 if (!lua_isfunction(L, narg) || !addr)
284 luaL_argerror(L, narg, "function expected");
285 lua_getfield(L, LUA_REGISTRYINDEX, "vis.functions");
286 lua_pushlightuserdata(L, (void*)addr);
287 lua_pushvalue(L, narg);
288 lua_settable(L, -3);
289 lua_pop(L, 1);
290 return addr;
293 /* retrieve function from registry and place it at the top of the stack */
294 static bool func_ref_get(lua_State *L, const void *addr) {
295 if (!addr)
296 return false;
297 lua_getfield(L, LUA_REGISTRYINDEX, "vis.functions");
298 lua_pushlightuserdata(L, (void*)addr);
299 lua_gettable(L, -2);
300 lua_remove(L, -2);
301 if (!lua_isfunction(L, -1)) {
302 lua_pop(L, 1);
303 return false;
305 return true;
308 /* creates a new metatable for a given type and stores a mapping:
310 * registry["vis.types"][metatable] = type
312 * leaves the metatable at the top of the stack.
314 static void obj_type_new(lua_State *L, const char *type) {
315 luaL_newmetatable(L, type);
316 lua_getglobal(L, "vis");
317 if (!lua_isnil(L, -1)) {
318 lua_getfield(L, -1, "types");
319 lua_pushvalue(L, -3);
320 lua_setfield(L, -2, type);
321 lua_pop(L, 1);
323 lua_pop(L, 1);
324 lua_getfield(L, LUA_REGISTRYINDEX, "vis.types");
325 lua_pushvalue(L, -2);
326 lua_pushstring(L, type);
327 lua_settable(L, -3);
328 lua_pop(L, 1);
331 /* get type of userdatum at the top of the stack:
333 * return registry["vis.types"][getmetatable(userdata)]
335 const char *obj_type_get(lua_State *L) {
336 if (lua_isnil(L, -1))
337 return "nil";
338 lua_getfield(L, LUA_REGISTRYINDEX, "vis.types");
339 lua_getmetatable(L, -2);
340 lua_gettable(L, -2);
341 // XXX: in theory string might become invalid when popped from stack
342 const char *type = lua_tostring(L, -1);
343 lua_pop(L, 2);
344 return type;
347 static void *obj_new(lua_State *L, size_t size, const char *type) {
348 void *obj = lua_newuserdata(L, size);
349 luaL_getmetatable(L, type);
350 lua_setmetatable(L, -2);
351 lua_newtable(L);
352 lua_setuservalue(L, -2);
353 return obj;
356 /* returns registry["vis.objects"][addr] if it is of correct type */
357 static void *obj_ref_get(lua_State *L, void *addr, const char *type) {
358 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
359 lua_pushlightuserdata(L, addr);
360 lua_gettable(L, -2);
361 lua_remove(L, -2);
362 if (lua_isnil(L, -1)) {
363 debug("get: vis.objects[%p] = nil\n", addr);
364 lua_pop(L, 1);
365 return NULL;
367 if (DEBUG_LUA) {
368 const char *actual_type = obj_type_get(L);
369 if (strcmp(type, actual_type) != 0)
370 debug("get: vis.objects[%p] = %s (BUG: expected %s)\n", addr, actual_type, type);
371 void **handle = luaL_checkudata(L, -1, type);
372 if (!handle)
373 debug("get: vis.objects[%p] = %s (BUG: invalid handle)\n", addr, type);
374 else if (*handle != addr)
375 debug("get: vis.objects[%p] = %s (BUG: handle mismatch %p)\n", addr, type, *handle);
377 /* verify that obj is correct type then unmodify the stack */
378 luaL_checkudata(L, -1, type);
379 lua_pop(L, 1);
380 return addr;
383 /* expects a userdatum at the top of the stack and sets
385 * registry["vis.objects"][addr] = userdata
387 static void obj_ref_set(lua_State *L, void *addr) {
388 //debug("set: vis.objects[%p] = %s\n", addr, obj_type_get(L));
389 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
390 lua_pushlightuserdata(L, addr);
391 lua_pushvalue(L, -3);
392 lua_settable(L, -3);
393 lua_pop(L, 1);
396 /* invalidates an object reference
398 * registry["vis.objects"][addr] = nil
400 static void obj_ref_free(lua_State *L, void *addr) {
401 if (DEBUG_LUA) {
402 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
403 lua_pushlightuserdata(L, addr);
404 lua_gettable(L, -2);
405 lua_remove(L, -2);
406 if (lua_isnil(L, -1))
407 debug("free-unused: %p\n", addr);
408 else
409 debug("free: vis.objects[%p] = %s\n", addr, obj_type_get(L));
410 lua_pop(L, 1);
412 lua_pushnil(L);
413 obj_ref_set(L, addr);
416 /* creates a new object reference of given type if it does not already exist in the registry:
418 * if (registry["vis.types"][metatable(registry["vis.objects"][addr])] != type) {
419 * // XXX: should not happen
420 * registry["vis.objects"][addr] = new_obj(addr, type)
422 * return registry["vis.objects"][addr];
424 static void *obj_ref_new(lua_State *L, void *addr, const char *type) {
425 if (!addr) {
426 lua_pushnil(L);
427 return NULL;
429 lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
430 lua_pushlightuserdata(L, addr);
431 lua_gettable(L, -2);
432 lua_remove(L, -2);
433 const char *old_type = obj_type_get(L);
434 if (strcmp(type, old_type) == 0) {
435 debug("new: vis.objects[%p] = %s (returning existing object)\n", addr, old_type);
436 void **handle = luaL_checkudata(L, -1, type);
437 if (!handle)
438 debug("new: vis.objects[%p] = %s (BUG: invalid handle)\n", addr, old_type);
439 else if (*handle != addr)
440 debug("new: vis.objects[%p] = %s (BUG: handle mismatch %p)\n", addr, old_type, *handle);
441 return addr;
443 if (!lua_isnil(L, -1))
444 debug("new: vis.objects[%p] = %s (WARNING: changing object type from %s)\n", addr, type, old_type);
445 else
446 debug("new: vis.objects[%p] = %s (creating new object)\n", addr, type);
447 lua_pop(L, 1);
448 void **handle = obj_new(L, sizeof(addr), type);
449 obj_ref_set(L, addr);
450 *handle = addr;
451 return addr;
454 /* (type) check validity of object reference at stack location `idx' and retrieve it */
455 static void *obj_ref_check(lua_State *L, int idx, const char *type) {
456 void **addr = luaL_checkudata(L, idx, type);
457 if (!obj_ref_get(L, *addr, type))
458 luaL_argerror(L, idx, "invalid object reference");
459 return *addr;
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 /* on most systems SIZE_MAX can't be represented in lua_Number.
507 * using < avoids undefined behaviour when n == SIZE_MAX+1
508 * which can be represented in lua_Number
510 if (n >= 0 && n < (lua_Number)SIZE_MAX && n == (size_t)n)
511 return n;
512 return luaL_argerror(L, narg, "expected position, got number");
515 static void pushpos(lua_State *L, size_t pos) {
516 if (pos == EPOS)
517 lua_pushnil(L);
518 else
519 lua_pushunsigned(L, pos);
522 static void pushrange(lua_State *L, Filerange *r) {
523 if (!r || !text_range_valid(r)) {
524 lua_pushnil(L);
525 return;
527 lua_createtable(L, 0, 2);
528 lua_pushstring(L, "start");
529 lua_pushunsigned(L, r->start);
530 lua_settable(L, -3);
531 lua_pushstring(L, "finish");
532 lua_pushunsigned(L, r->end);
533 lua_settable(L, -3);
536 static Filerange getrange(lua_State *L, int index) {
537 Filerange range = text_range_empty();
538 if (lua_istable(L, index)) {
539 lua_getfield(L, index, "start");
540 range.start = checkpos(L, -1);
541 lua_pop(L, 1);
542 lua_getfield(L, index, "finish");
543 range.end = checkpos(L, -1);
544 lua_pop(L, 1);
545 } else {
546 range.start = checkpos(L, index);
547 range.end = range.start + checkpos(L, index+1);
549 return range;
552 static const char *keymapping(Vis *vis, const char *keys, const Arg *arg) {
553 lua_State *L = vis->lua;
554 if (!func_ref_get(L, arg->v))
555 return keys;
556 lua_pushstring(L, keys);
557 if (pcall(vis, L, 1, 1) != 0)
558 return keys;
559 if (lua_type(L, -1) != LUA_TNUMBER)
560 return keys; /* invalid or no return value, assume zero */
561 lua_Number number = lua_tonumber(L, -1);
562 lua_Integer integer = lua_tointeger(L, -1);
563 if (number != integer)
564 return keys;
565 if (integer < 0)
566 return NULL; /* need more input */
567 size_t len = integer;
568 size_t max = strlen(keys);
569 return (len <= max) ? keys+len : keys;
572 /***
573 * The main editor object.
574 * @type Vis
577 /***
578 * Version information.
579 * @tfield string VERSION
580 * version information in `git describe` format, same as reported by `vis -v`.
582 /***
583 * Lua API object types
584 * @field types meta tables of userdata objects used for type checking
585 * @local
587 /***
588 * User interface.
589 * @tfield Ui ui the user interface being used
591 /***
592 * Mode constants.
593 * @tfield modes modes
595 /***
596 * Events.
597 * @tfield events events
599 /***
600 * Registers.
601 * @field registers array to access the register by single letter name
603 /***
604 * Scintillua lexer module.
605 * @field lexers might be `nil` if module is not found
607 /***
608 * LPeg lexer module.
609 * @field lpeg might be `nil` if module is not found
611 /***
612 * Current count.
613 * @tfield int count the specified count for the current command or `nil` if none was given
616 /***
617 * Create an iterator over all windows.
618 * @function windows
619 * @return the new iterator
620 * @see win
621 * @usage
622 * for win in vis:windows() do
623 * -- do something with win
624 * end
626 static int windows_iter(lua_State *L);
627 static int windows(lua_State *L) {
628 Vis *vis = obj_ref_check(L, 1, "vis");
629 Win **handle = lua_newuserdata(L, sizeof *handle), *next;
630 for (next = vis->windows; next && next->file->internal; next = next->next);
631 *handle = next;
632 lua_pushcclosure(L, windows_iter, 1);
633 return 1;
636 static int windows_iter(lua_State *L) {
637 Win **handle = lua_touserdata(L, lua_upvalueindex(1));
638 if (!*handle)
639 return 0;
640 Win *win = obj_ref_new(L, *handle, VIS_LUA_TYPE_WINDOW), *next;
641 if (win) {
642 for (next = win->next; next && next->file->internal; next = next->next);
643 *handle = next;
645 return 1;
648 /***
649 * Create an iterator over all files.
650 * @function files
651 * @return the new iterator
652 * @usage
653 * for file in vis:files() do
654 * -- do something with file
655 * end
657 static int files_iter(lua_State *L);
658 static int files(lua_State *L) {
659 Vis *vis = obj_ref_check(L, 1, "vis");
660 File **handle = lua_newuserdata(L, sizeof *handle);
661 *handle = vis->files;
662 lua_pushcclosure(L, files_iter, 1);
663 return 1;
666 static int files_iter(lua_State *L) {
667 File **handle = lua_touserdata(L, lua_upvalueindex(1));
668 if (!*handle)
669 return 0;
670 File *file = obj_ref_new(L, *handle, VIS_LUA_TYPE_FILE);
671 if (file)
672 *handle = file->next;
673 return 1;
676 /***
677 * Create an iterator over all mark names.
678 * @function mark_names
679 * @return the new iterator
680 * @usage
681 * local marks = vis.win.marks
682 * for name in vis:mark_names() do
683 * local mark = marks[name]
684 * for i = 1, #mark do
685 * -- do something with: name, mark[i].start, mark[i].finish
686 * end
687 * end
689 static int mark_names_iter(lua_State *L);
690 static int mark_names(lua_State *L) {
691 Vis *vis = obj_ref_check(L, 1, "vis");
692 lua_pushlightuserdata(L, vis);
693 enum VisMark *handle = lua_newuserdata(L, sizeof *handle);
694 *handle = 0;
695 lua_pushcclosure(L, mark_names_iter, 2);
696 return 1;
699 static int mark_names_iter(lua_State *L) {
700 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
701 enum VisMark *handle = lua_touserdata(L, lua_upvalueindex(2));
702 char mark = vis_mark_to(vis, *handle);
703 if (mark) {
704 lua_pushlstring(L, &mark, 1);
705 (*handle)++;
706 return 1;
708 return 0;
711 /***
712 * Create an iterator over all register names.
713 * @function register_names
714 * @return the new iterator
715 * @usage
716 * for name in vis:register_names() do
717 * local reg = vis.registers[name]
718 * for i = 1, #reg do
719 * -- do something with register value reg[i]
720 * end
721 * end
723 static int register_names_iter(lua_State *L);
724 static int register_names(lua_State *L) {
725 Vis *vis = obj_ref_check(L, 1, "vis");
726 lua_pushlightuserdata(L, vis);
727 enum VisRegister *handle = lua_newuserdata(L, sizeof *handle);
728 *handle = 0;
729 lua_pushcclosure(L, register_names_iter, 2);
730 return 1;
733 static int register_names_iter(lua_State *L) {
734 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
735 enum VisRegister *handle = lua_touserdata(L, lua_upvalueindex(2));
736 char reg = vis_register_to(vis, *handle);
737 if (reg) {
738 lua_pushlstring(L, &reg, 1);
739 (*handle)++;
740 return 1;
742 return 0;
745 /***
746 * Execute a `:`-command.
747 * @function command
748 * @tparam string command the command to execute
749 * @treturn bool whether the command succeeded
750 * @usage
751 * vis:command("set number")
753 static int command(lua_State *L) {
754 Vis *vis = obj_ref_check(L, 1, "vis");
755 const char *cmd = luaL_checkstring(L, 2);
756 bool ret = vis_cmd(vis, cmd);
757 lua_pushboolean(L, ret);
758 return 1;
761 /***
762 * Display a short message.
764 * The single line message will be displayed at the bottom of
765 * the screen and automatically hidden once a key is pressed.
767 * @function info
768 * @tparam string message the message to display
770 static int info(lua_State *L) {
771 Vis *vis = obj_ref_check(L, 1, "vis");
772 const char *msg = luaL_checkstring(L, 2);
773 vis_info_show(vis, "%s", msg);
774 return 0;
777 /***
778 * Display a multi line message.
780 * Opens a new window and displays an arbitrarily long message.
782 * @function message
783 * @tparam string message the message to display
785 static int message(lua_State *L) {
786 Vis *vis = obj_ref_check(L, 1, "vis");
787 const char *msg = luaL_checkstring(L, 2);
788 vis_message_show(vis, msg);
789 return 0;
792 /***
793 * Register a Lua function as key action.
794 * @function action_register
795 * @tparam string name the name of the action, can be referred to in key bindings as `<name>` pseudo key
796 * @tparam Function func the lua function implementing the key action (see @{keyhandler})
797 * @tparam[opt] string help the single line help text as displayed in `:help`
798 * @treturn KeyAction action the registered key action
799 * @see Vis:map
800 * @see Window:map
802 static int action_register(lua_State *L) {
803 Vis *vis = obj_ref_check(L, 1, "vis");
804 const char *name = luaL_checkstring(L, 2);
805 const void *func = func_ref_new(L, 3);
806 const char *help = luaL_optstring(L, 4, NULL);
807 KeyAction *action = vis_action_new(vis, name, help, keymapping, (Arg){ .v = func });
808 if (!action)
809 goto err;
810 if (!vis_action_register(vis, action))
811 goto err;
812 obj_ref_new(L, action, VIS_LUA_TYPE_KEYACTION);
813 return 1;
814 err:
815 vis_action_free(vis, action);
816 lua_pushnil(L);
817 return 1;
820 static int keymap(lua_State *L, Vis *vis, Win *win) {
821 int mode = luaL_checkint(L, 2);
822 const char *key = luaL_checkstring(L, 3);
823 const char *help = luaL_optstring(L, 5, NULL);
824 KeyBinding *binding = vis_binding_new(vis);
825 if (!binding)
826 goto err;
827 if (lua_isstring(L, 4)) {
828 const char *alias = luaL_checkstring(L, 4);
829 if (!(binding->alias = strdup(alias)))
830 goto err;
831 } else if (lua_isfunction(L, 4)) {
832 const void *func = func_ref_new(L, 4);
833 if (!(binding->action = vis_action_new(vis, NULL, help, keymapping, (Arg){ .v = func })))
834 goto err;
835 } else if (lua_isuserdata(L, 4)) {
836 binding->action = obj_ref_check(L, 4, VIS_LUA_TYPE_KEYACTION);
837 } else {
838 goto err;
841 if (win) {
842 if (!vis_window_mode_map(win, mode, true, key, binding))
843 goto err;
844 } else {
845 if (!vis_mode_map(vis, mode, true, key, binding))
846 goto err;
849 lua_pushboolean(L, true);
850 return 1;
851 err:
852 vis_binding_free(vis, binding);
853 lua_pushboolean(L, false);
854 return 1;
857 /***
858 * Map a key to a Lua function.
860 * Creates a new key mapping in a given mode.
862 * @function map
863 * @tparam int mode the mode to which the mapping should be added
864 * @tparam string key the key to map
865 * @tparam function func the Lua function to handle the key mapping (see @{keyhandler})
866 * @tparam[opt] string help the single line help text as displayed in `:help`
867 * @treturn bool whether the mapping was successfully established
868 * @see Window:map
869 * @usage
870 * vis:map(vis.modes.INSERT, "<C-k>", function(keys)
871 * if #keys < 2 then
872 * return -1 -- need more input
873 * end
874 * local digraph = keys:sub(1, 2)
875 * if digraph == "l*" then
876 * vis:feedkeys('λ')
877 * return 2 -- consume 2 bytes of input
878 * end
879 * end, "Insert digraph")
881 /***
882 * Setup a key alias.
884 * This is equivalent to `vis:command('map! mode key alias')`.
886 * Mappings are always recursive!
887 * @function map
888 * @tparam int mode the mode to which the mapping should be added
889 * @tparam string key the key to map
890 * @tparam string alias the key to map to
891 * @treturn bool whether the mapping was successfully established
892 * @see Window:map
893 * @usage
894 * vis:map(vis.modes.NORMAL, "j", "k")
896 /***
897 * Map a key to a key action.
899 * @function map
900 * @tparam int mode the mode to which the mapping should be added
901 * @tparam string key the key to map
902 * @param action the action to map
903 * @treturn bool whether the mapping was successfully established
904 * @see Window:map
905 * @usage
906 * local action = vis:action_register("info", function()
907 * vis:info("Mapping works!")
908 * end, "Info message help text")
909 * vis:map(vis.modes.NORMAL, "gh", action)
910 * vis:map(vis.modes.NORMAL, "gl", action)
912 static int map(lua_State *L) {
913 Vis *vis = obj_ref_check(L, 1, "vis");
914 return keymap(L, vis, NULL);
917 /***
918 * Unmap a global key binding.
920 * @function unmap
921 * @tparam int mode the mode from which the mapping should be removed
922 * @tparam string key the mapping to remove
923 * @treturn bool whether the mapping was successfully removed
924 * @see Window:unmap
926 static int keyunmap(lua_State *L, Vis *vis, Win *win) {
927 enum VisMode mode = luaL_checkint(L, 2);
928 const char *key = luaL_checkstring(L, 3);
929 bool ret;
930 if (!win)
931 ret = vis_mode_unmap(vis, mode, key);
932 else
933 ret = vis_window_mode_unmap(win, mode, key);
934 lua_pushboolean(L, ret);
935 return 1;
938 static int unmap(lua_State *L) {
939 Vis *vis = obj_ref_check(L, 1, "vis");
940 return keyunmap(L, vis, NULL);
943 /***
944 * Get all currently active mappings of a mode.
946 * @function mappings
947 * @tparam int mode the mode to query
948 * @treturn table the active mappings and their associated help texts
949 * @usage
950 * local bindings = vis:mappings(vis.modes.NORMAL)
951 * for key, help in pairs(bindings) do
952 * -- do something
953 * end
954 * @see Vis:map
956 static bool binding_collect(const char *key, void *value, void *ctx) {
957 lua_State *L = ctx;
958 KeyBinding *binding = value;
959 lua_getfield(L, -1, key);
960 bool new = lua_isnil(L, -1);
961 lua_pop(L, 1);
962 if (new) {
963 const char *help = binding->alias ? binding->alias : VIS_HELP_USE(binding->action->help);
964 lua_pushstring(L, help ? help : "");
965 lua_setfield(L, -2, key);
967 return true;
970 static int mappings(lua_State *L) {
971 Vis *vis = obj_ref_check(L, 1, "vis");
972 lua_newtable(L);
973 for (Mode *mode = mode_get(vis, luaL_checkint(L, 2)); mode; mode = mode->parent) {
974 if (!mode->bindings)
975 continue;
976 map_iterate(mode->bindings, binding_collect, vis->lua);
978 return 1;
981 /***
982 * Execute a motion.
984 * @function motion
985 * @tparam int id the id of the motion to execute
986 * @treturn bool whether the id was valid
987 * @local
989 static int motion(lua_State *L) {
990 Vis *vis = obj_ref_check(L, 1, "vis");
991 enum VisMotion id = luaL_checkunsigned(L, 2);
992 // TODO handle var args?
993 lua_pushboolean(L, vis && vis_motion(vis, id));
994 return 1;
997 static size_t motion_lua(Vis *vis, Win *win, void *data, size_t pos) {
998 lua_State *L = vis->lua;
999 if (!L || !func_ref_get(L, data) || !obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW))
1000 return EPOS;
1002 lua_pushunsigned(L, pos);
1003 if (pcall(vis, L, 2, 1) != 0)
1004 return EPOS;
1005 return getpos(L, -1);
1008 /***
1009 * Register a custom motion.
1011 * @function motion_register
1012 * @tparam function motion the Lua function implementing the motion
1013 * @treturn int the associated motion id, or `-1` on failure
1014 * @see motion, motion_new
1015 * @local
1016 * @usage
1017 * -- custom motion advancing to the next byte
1018 * local id = vis:motion_register(function(win, pos)
1019 * return pos+1
1020 * end)
1022 static int motion_register(lua_State *L) {
1023 Vis *vis = obj_ref_check(L, 1, "vis");
1024 const void *func = func_ref_new(L, 2);
1025 int id = vis_motion_register(vis, (void*)func, motion_lua);
1026 lua_pushinteger(L, id);
1027 return 1;
1030 /***
1031 * Execute an operator.
1033 * @function operator
1034 * @tparam int id the id of the operator to execute
1035 * @treturn bool whether the id was valid
1036 * @local
1038 static int operator(lua_State *L) {
1039 Vis *vis = obj_ref_check(L, 1, "vis");
1040 enum VisOperator id = luaL_checkunsigned(L, 2);
1041 // TODO handle var args?
1042 lua_pushboolean(L, vis && vis_operator(vis, id));
1043 return 1;
1046 static size_t operator_lua(Vis *vis, Text *text, OperatorContext *c) {
1047 lua_State *L = vis->lua;
1048 if (!L || !func_ref_get(L, c->context))
1049 return EPOS;
1050 File *file = vis->files;
1051 while (file && (file->internal || file->text != text))
1052 file = file->next;
1053 if (!file || !obj_ref_new(L, file, VIS_LUA_TYPE_FILE))
1054 return EPOS;
1055 pushrange(L, &c->range);
1056 pushpos(L, c->pos);
1057 if (pcall(vis, L, 3, 1) != 0)
1058 return EPOS;
1059 return getpos(L, -1);
1062 /***
1063 * Register a custom operator.
1065 * @function operator_register
1066 * @tparam function operator the Lua function implementing the operator
1067 * @treturn int the associated operator id, or `-1` on failure
1068 * @see operator, operator_new
1069 * @local
1070 * @usage
1071 * -- custom operator replacing every 'a' with 'b'
1072 * local id = vis:operator_register(function(file, range, pos)
1073 * local data = file:content(range)
1074 * data = data:gsub("a", "b")
1075 * file:delete(range)
1076 * file:insert(range.start, data)
1077 * return range.start -- new cursor location
1078 * end)
1080 static int operator_register(lua_State *L) {
1081 Vis *vis = obj_ref_check(L, 1, "vis");
1082 const void *func = func_ref_new(L, 2);
1083 int id = vis_operator_register(vis, operator_lua, (void*)func);
1084 lua_pushinteger(L, id);
1085 return 1;
1088 /***
1089 * Execute a text object.
1091 * @function textobject
1092 * @tparam int id the id of the text object to execute
1093 * @treturn bool whether the id was valid
1094 * @see textobject_register, textobject_new
1095 * @local
1097 static int textobject(lua_State *L) {
1098 Vis *vis = obj_ref_check(L, 1, "vis");
1099 enum VisTextObject id = luaL_checkunsigned(L, 2);
1100 lua_pushboolean(L, vis_textobject(vis, id));
1101 return 1;
1104 static Filerange textobject_lua(Vis *vis, Win *win, void *data, size_t pos) {
1105 lua_State *L = vis->lua;
1106 if (!L || !func_ref_get(L, data) || !obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW))
1107 return text_range_empty();
1108 lua_pushunsigned(L, pos);
1109 if (pcall(vis, L, 2, 2) != 0 || lua_isnil(L, -1))
1110 return text_range_empty();
1111 return text_range_new(getpos(L, -2), getpos(L, -1));
1114 /***
1115 * Register a custom text object.
1117 * @function textobject_register
1118 * @tparam function textobject the Lua function implementing the text object
1119 * @treturn int the associated text object id, or `-1` on failure
1120 * @see textobject, textobject_new
1121 * @local
1122 * @usage
1123 * -- custom text object covering the next byte
1124 * local id = vis:textobject_register(function(win, pos)
1125 * return pos, pos+1
1126 * end)
1128 static int textobject_register(lua_State *L) {
1129 Vis *vis = obj_ref_check(L, 1, "vis");
1130 const void *func = func_ref_new(L, 2);
1131 int id = vis_textobject_register(vis, 0, (void*)func, textobject_lua);
1132 lua_pushinteger(L, id);
1133 return 1;
1136 static bool option_lua(Vis *vis, Win *win, void *context, bool toggle,
1137 enum VisOption flags, const char *name, Arg *value) {
1138 lua_State *L = vis->lua;
1139 if (!L || !func_ref_get(L, context))
1140 return false;
1141 if (flags & VIS_OPTION_TYPE_BOOL)
1142 lua_pushboolean(L, value->b);
1143 else if (flags & VIS_OPTION_TYPE_STRING)
1144 lua_pushstring(L, value->s);
1145 else if (flags & VIS_OPTION_TYPE_NUMBER)
1146 lua_pushnumber(L, value->i);
1147 else
1148 return false;
1149 lua_pushboolean(L, toggle);
1150 return pcall(vis, L, 2, 2) == 0 && (!lua_isboolean(L, -1) || lua_toboolean(L, -1));
1153 /***
1154 * Register a custom `:set` option.
1156 * @function option_register
1157 * @tparam string name the option name
1158 * @tparam string type the option type (`bool`, `string` or `number`)
1159 * @tparam function handler the Lua function being called when the option is changed
1160 * @tparam[opt] string help the single line help text as displayed in `:help`
1161 * @treturn bool whether the option was successfully registered
1162 * @usage
1163 * vis:option_register("foo", "bool", function(value, toggle)
1164 * if not vis.win then return false end
1165 * vis.win.foo = toggle and not vis.win.foo or value
1166 * vis:info("Option foo = " .. tostring(vis.win.foo))
1167 * return true
1168 * end, "Foo enables superpowers")
1170 static int option_register(lua_State *L) {
1171 Vis *vis = obj_ref_check(L, 1, "vis");
1172 const char *name = luaL_checkstring(L, 2);
1173 const char *type = luaL_checkstring(L, 3);
1174 const void *func = func_ref_new(L, 4);
1175 const char *help = luaL_optstring(L, 5, NULL);
1176 const char *names[] = { name, NULL };
1177 enum VisOption flags = 0;
1178 if (strcmp(type, "string") == 0)
1179 flags |= VIS_OPTION_TYPE_STRING;
1180 else if (strcmp(type, "number") == 0)
1181 flags |= VIS_OPTION_TYPE_NUMBER;
1182 else
1183 flags |= VIS_OPTION_TYPE_BOOL;
1184 bool ret = vis_option_register(vis, names, flags, option_lua, (void*)func, help);
1185 lua_pushboolean(L, ret);
1186 return 1;
1189 /***
1190 * Unregister a `:set` option.
1192 * @function option_unregister
1193 * @tparam string name the option name
1194 * @treturn bool whether the option was successfully unregistered
1196 static int option_unregister(lua_State *L) {
1197 Vis *vis = obj_ref_check(L, 1, "vis");
1198 const char *name = luaL_checkstring(L, 2);
1199 bool ret = vis_option_unregister(vis, name);
1200 lua_pushboolean(L, ret);
1201 return 1;
1204 static bool command_lua(Vis *vis, Win *win, void *data, bool force, const char *argv[], Selection *sel, Filerange *range) {
1205 lua_State *L = vis->lua;
1206 if (!L || !func_ref_get(L, data))
1207 return false;
1208 lua_newtable(L);
1209 for (size_t i = 0; argv[i]; i++) {
1210 lua_pushunsigned(L, i);
1211 lua_pushstring(L, argv[i]);
1212 lua_settable(L, -3);
1214 lua_pushboolean(L, force);
1215 if (!obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW))
1216 return false;
1217 if (!sel)
1218 sel = view_selections_primary_get(win->view);
1219 if (!obj_lightref_new(L, sel, VIS_LUA_TYPE_SELECTION))
1220 return false;
1221 pushrange(L, range);
1222 if (pcall(vis, L, 5, 1) != 0)
1223 return false;
1224 return lua_toboolean(L, -1);
1227 /***
1228 * Register a custom `:`-command.
1230 * @function command_register
1231 * @tparam string name the command name
1232 * @tparam function command the Lua function implementing the command
1233 * @tparam[opt] string help the single line help text as displayed in `:help`
1234 * @treturn bool whether the command has been successfully registered
1235 * @usage
1236 * vis:command_register("foo", function(argv, force, win, selection, range)
1237 * for i,arg in ipairs(argv) do
1238 * print(i..": "..arg)
1239 * end
1240 * print("was command forced with ! "..(force and "yes" or "no"))
1241 * print(win.file.name)
1242 * print(selection.pos)
1243 * print(range ~= nil and ('['..range.start..', '..range.finish..']') or "invalid range")
1244 * return true;
1245 * end)
1247 static int command_register(lua_State *L) {
1248 Vis *vis = obj_ref_check(L, 1, "vis");
1249 const char *name = luaL_checkstring(L, 2);
1250 const void *func = func_ref_new(L, 3);
1251 const char *help = luaL_optstring(L, 4, "");
1252 bool ret = vis_cmd_register(vis, name, help, (void*)func, command_lua);
1253 lua_pushboolean(L, ret);
1254 return 1;
1257 /***
1258 * Push keys to input queue and interpret them.
1260 * The keys are processed as if they were read from the keyboard.
1262 * @function feedkeys
1263 * @tparam string keys the keys to interpret
1265 static int feedkeys(lua_State *L) {
1266 Vis *vis = obj_ref_check(L, 1, "vis");
1267 const char *keys = luaL_checkstring(L, 2);
1268 vis_keys_feed(vis, keys);
1269 return 0;
1272 /***
1273 * Insert keys at all cursor positions of active window.
1275 * This function behaves as if the keys were entered in insert mode,
1276 * but in contrast to @{Vis:feedkeys} it bypasses the input queue,
1277 * meaning mappings do not apply and the keys will not be recorded in macros.
1279 * @function insert
1280 * @tparam string keys the keys to insert
1281 * @see Vis:feedkeys
1283 static int insert(lua_State *L) {
1284 Vis *vis = obj_ref_check(L, 1, "vis");
1285 size_t len;
1286 const char *keys = luaL_checklstring(L, 2, &len);
1287 vis_insert_key(vis, keys, len);
1288 return 0;
1291 /***
1292 * Replace keys at all cursor positions of active window.
1294 * This function behaves as if the keys were entered in replace mode,
1295 * but in contrast to @{Vis:feedkeys} it bypasses the input queue,
1296 * meaning mappings do not apply and the keys will not be recorded in macros.
1298 * @function replace
1299 * @tparam string keys the keys to insert
1300 * @see Vis:feedkeys
1302 static int replace(lua_State *L) {
1303 Vis *vis = obj_ref_check(L, 1, "vis");
1304 size_t len;
1305 const char *keys = luaL_checklstring(L, 2, &len);
1306 vis_replace_key(vis, keys, len);
1307 return 0;
1310 /***
1311 * Terminate editor process.
1313 * Termination happens upon the next iteration of the main event loop.
1314 * This means the calling Lua code will be executed further until it
1315 * eventually hands over control to the editor core. The exit status
1316 * of the most recent call is used.
1318 * All unsaved changes will be lost!
1320 * @function exit
1321 * @tparam int code the exit status returned to the operating system
1323 static int exit_func(lua_State *L) {
1324 Vis *vis = obj_ref_check(L, 1, "vis");
1325 int code = luaL_checkint(L, 2);
1326 vis_exit(vis, code);
1327 return 0;
1330 /***
1331 * Pipe file range to external process and collect output.
1333 * The editor core will be blocked while the external process is running.
1334 * File and Range can be omitted or nil to indicate empty input.
1336 * @function pipe
1337 * @tparam[opt] File file the file to which the range applies
1338 * @tparam[opt] Range range the range to pipe
1339 * @tparam string command the command to execute
1340 * @tparam[opt] bool fullscreen whether command is a fullscreen program (e.g. curses based)
1341 * @treturn int code the exit status of the executed command
1342 * @treturn string stdout the data written to stdout
1343 * @treturn string stderr the data written to stderr
1345 static int pipe_func(lua_State *L) {
1346 Vis *vis = obj_ref_check(L, 1, "vis");
1347 int cmd_idx = 4;
1348 char *out = NULL, *err = NULL;
1349 File *file = vis->win ? vis->win->file : NULL;
1350 Filerange range = text_range_new(0, 0);
1351 if (lua_gettop(L) <= 3) {
1352 cmd_idx = 2;
1353 } else if (!(lua_isnil(L, 2) && lua_isnil(L, 3))) {
1354 file = obj_ref_check(L, 2, VIS_LUA_TYPE_FILE);
1355 range = getrange(L, 3);
1357 const char *cmd = luaL_checkstring(L, cmd_idx);
1358 bool fullscreen = lua_isboolean(L, cmd_idx + 1) && lua_toboolean(L, cmd_idx + 1);
1360 if (!file)
1361 return luaL_error(L, "vis:pipe(cmd = '%s'): win not open, file can't be nil", cmd);
1363 int status = vis_pipe_collect(vis, file, &range, (const char*[]){ cmd, NULL }, &out, &err, fullscreen);
1364 lua_pushinteger(L, status);
1365 if (out)
1366 lua_pushstring(L, out);
1367 else
1368 lua_pushnil(L);
1369 free(out);
1370 if (err)
1371 lua_pushstring(L, err);
1372 else
1373 lua_pushnil(L);
1374 free(err);
1375 vis_draw(vis);
1376 return 3;
1379 /***
1380 * Redraw complete user interface.
1382 * Will trigger redraw events, make sure to avoid recursive events.
1384 * @function redraw
1386 static int redraw(lua_State *L) {
1387 Vis *vis = obj_ref_check(L, 1, "vis");
1388 vis_redraw(vis);
1389 return 0;
1391 /***
1392 * Closes a stream returned by @{Vis:communicate}.
1394 * @function close
1395 * @tparam io.file inputfd the stream to be closed
1396 * @treturn bool identical to @{io.close}
1398 static int close_subprocess(lua_State *L) {
1399 luaL_Stream *file = luaL_checkudata(L, -1, "FILE*");
1400 int result = fclose(file->f);
1401 if (result == 0) {
1402 file->f = NULL;
1403 file->closef = NULL;
1405 return luaL_fileresult(L, result == 0, NULL);
1407 /***
1408 * Open new process and return its input stream (stdin).
1409 * If the stream is closed (by calling the close method or by being removed by a garbage collector)
1410 * the spawned process will be killed by SIGTERM.
1411 * When the process will quit or will output anything to stdout or stderr,
1412 * the @{process_response} event will be fired.
1414 * The editor core won't be blocked while the external process is running.
1416 * @function communicate
1417 * @tparam string name the name of subprocess (to distinguish processes in the @{process_response} event)
1418 * @tparam string command the command to execute
1419 * @return the file handle to write data to the process, in case of error the return values are equivalent to @{io.open} error values.
1421 static int communicate_func(lua_State *L) {
1423 typedef struct {
1424 /* Lua stream structure for the process input stream */
1425 luaL_Stream stream;
1426 Process *handler;
1427 } ProcessStream;
1429 Vis *vis = obj_ref_check(L, 1, "vis");
1430 const char *name = luaL_checkstring(L, 2);
1431 const char *cmd = luaL_checkstring(L, 3);
1432 ProcessStream *inputfd = (ProcessStream *)lua_newuserdata(L, sizeof(ProcessStream));
1433 luaL_setmetatable(L, LUA_FILEHANDLE);
1434 inputfd->handler = vis_process_communicate(vis, name, cmd, &(inputfd->stream.closef));
1435 if (inputfd->handler) {
1436 inputfd->stream.f = fdopen(inputfd->handler->inpfd, "w");
1437 inputfd->stream.closef = &close_subprocess;
1439 return inputfd->stream.f ? 1 : luaL_fileresult(L, 0, name);
1441 /***
1442 * Currently active window.
1443 * @tfield Window win
1444 * @see windows
1446 /***
1447 * Currently active mode.
1448 * @tfield modes mode
1450 /***
1451 * Whether a macro is being recorded.
1452 * @tfield bool recording
1454 /***
1455 * Currently unconsumed keys in the input queue.
1456 * @tfield string input_queue
1458 /***
1459 * Register name in use.
1460 * @tfield string register
1462 /***
1463 * Mark name in use.
1464 * @tfield string mark
1466 static int vis_index(lua_State *L) {
1467 Vis *vis = obj_ref_check(L, 1, "vis");
1469 if (lua_isstring(L, 2)) {
1470 const char *key = lua_tostring(L, 2);
1471 if (strcmp(key, "win") == 0) {
1472 if (vis->win)
1473 obj_ref_new(L, vis->win, VIS_LUA_TYPE_WINDOW);
1474 else
1475 lua_pushnil(L);
1476 return 1;
1479 if (strcmp(key, "mode") == 0) {
1480 lua_pushunsigned(L, vis->mode->id);
1481 return 1;
1484 if (strcmp(key, "smartcase") == 0) {
1485 lua_pushboolean(L, vis->smartcase);
1486 return 1;
1489 if (strcmp(key, "literal") == 0) {
1490 lua_pushboolean(L, vis->literal);
1491 return 1;
1494 if (strcmp(key, "input_queue") == 0) {
1495 lua_pushstring(L, buffer_content0(&vis->input_queue));
1496 return 1;
1499 if (strcmp(key, "recording") == 0) {
1500 lua_pushboolean(L, vis_macro_recording(vis));
1501 return 1;
1504 if (strcmp(key, "count") == 0) {
1505 int count = vis_count_get(vis);
1506 if (count == VIS_COUNT_UNKNOWN)
1507 lua_pushnil(L);
1508 else
1509 lua_pushunsigned(L, count);
1510 return 1;
1513 if (strcmp(key, "register") == 0) {
1514 char name = vis_register_to(vis, vis_register_used(vis));
1515 lua_pushlstring(L, &name, 1);
1516 return 1;
1519 if (strcmp(key, "registers") == 0) {
1520 obj_ref_new(L, vis->ui, VIS_LUA_TYPE_REGISTERS);
1521 return 1;
1524 if (strcmp(key, "mark") == 0) {
1525 char name = vis_mark_to(vis, vis_mark_used(vis));
1526 lua_pushlstring(L, &name, 1);
1527 return 1;
1530 if (strcmp(key, "options") == 0) {
1531 obj_ref_new(L, &vis->options, VIS_LUA_TYPE_VIS_OPTS);
1532 return 1;
1535 if (strcmp(key, "ui") == 0) {
1536 obj_ref_new(L, vis->ui, VIS_LUA_TYPE_UI);
1537 return 1;
1541 return index_common(L);
1544 static int vis_options_assign(Vis *vis, lua_State *L, const char *key, int next) {
1545 if (strcmp(key, "autoindent") == 0 || strcmp(key, "ai") == 0) {
1546 vis->autoindent = lua_toboolean(L, next);
1547 } else if (strcmp(key, "changecolors") == 0) {
1548 vis->change_colors = lua_toboolean(L, next);
1549 } else if (strcmp(key, "escdelay") == 0) {
1550 TermKey *tk = vis->ui->termkey_get(vis->ui);
1551 termkey_set_waittime(tk, luaL_checkint(L, next));
1552 } else if (strcmp(key, "smartcase") == 0 || strcmp(key, "scs") == 0) {
1553 vis->smartcase = lua_toboolean(L, next);
1554 } else if (strcmp(key, "literal") == 0) {
1555 vis->literal = lua_toboolean(L, next);
1556 } else if (strcmp(key, "loadmethod") == 0) {
1557 if (!lua_isstring(L, next))
1558 return newindex_common(L);
1559 const char *lm = lua_tostring(L, next);
1560 if (strcmp(lm, "auto") == 0)
1561 vis->load_method = TEXT_LOAD_AUTO;
1562 else if (strcmp(lm, "read") == 0)
1563 vis->load_method = TEXT_LOAD_READ;
1564 else if (strcmp(lm, "mmap") == 0)
1565 vis->load_method = TEXT_LOAD_MMAP;
1566 } else if (strcmp(key, "shell") == 0) {
1567 if (!lua_isstring(L, next))
1568 return newindex_common(L);
1569 vis_shell_set(vis, lua_tostring(L, next));
1571 return 0;
1574 static int vis_newindex(lua_State *L) {
1575 Vis *vis = obj_ref_check(L, 1, "vis");
1576 if (lua_isstring(L, 2)) {
1577 const char *key = lua_tostring(L, 2);
1578 if (strcmp(key, "mode") == 0) {
1579 enum VisMode mode = luaL_checkunsigned(L, 3);
1580 vis_mode_switch(vis, mode);
1581 return 0;
1584 if (strcmp(key, "count") == 0) {
1585 int count;
1586 if (lua_isnil(L, 3))
1587 count = VIS_COUNT_UNKNOWN;
1588 else
1589 count = luaL_checkunsigned(L, 3);
1590 vis_count_set(vis, count);
1591 return 0;
1594 if (strcmp(key, "win") == 0) {
1595 vis_window_focus(obj_ref_check(L, 3, VIS_LUA_TYPE_WINDOW));
1596 return 0;
1599 if (strcmp(key, "register") == 0) {
1600 const char *name = luaL_checkstring(L, 3);
1601 if (strlen(name) == 1)
1602 vis_register(vis, vis_register_from(vis, name[0]));
1603 return 0;
1606 if (strcmp(key, "mark") == 0) {
1607 const char *name = luaL_checkstring(L, 3);
1608 if (strlen(name) == 1)
1609 vis_mark(vis, vis_mark_from(vis, name[0]));
1610 return 0;
1613 if (strcmp(key, "options") == 0 && lua_istable(L, 3)) {
1614 int ret = 0;
1615 /* since we don't know which keys are in the table we push
1616 * a nil then use lua_next() to remove it and push the
1617 * table's key-value pairs to the stack. these can then be
1618 * used to assign options
1620 lua_pushnil(L);
1621 while (lua_next(L, 3)) {
1622 if (lua_isstring(L, 4))
1623 ret += vis_options_assign(vis, L, lua_tostring(L, 4), 5);
1624 else
1625 ret += newindex_common(L);
1626 lua_pop(L, 1);
1628 lua_pop(L, 1);
1629 return ret;
1632 return newindex_common(L);
1635 static const struct luaL_Reg vis_lua[] = {
1636 { "files", files },
1637 { "windows", windows },
1638 { "mark_names", mark_names },
1639 { "register_names", register_names },
1640 { "command", command },
1641 { "info", info },
1642 { "message", message },
1643 { "map", map },
1644 { "unmap", unmap },
1645 { "mappings", mappings },
1646 { "operator", operator },
1647 { "operator_register", operator_register },
1648 { "motion", motion },
1649 { "motion_register", motion_register },
1650 { "textobject", textobject },
1651 { "textobject_register", textobject_register },
1652 { "option_register", option_register },
1653 { "option_unregister", option_unregister },
1654 { "command_register", command_register },
1655 { "feedkeys", feedkeys },
1656 { "insert", insert },
1657 { "replace", replace },
1658 { "action_register", action_register },
1659 { "exit", exit_func },
1660 { "pipe", pipe_func },
1661 { "redraw", redraw },
1662 { "communicate", communicate_func },
1663 { "__index", vis_index },
1664 { "__newindex", vis_newindex },
1665 { NULL, NULL },
1668 /***
1669 * Vis Options
1670 * @table options
1671 * @tfield[opt=false] boolean autoindent {ai}
1672 * @tfield[opt=false] boolean changecolors
1673 * @tfield[opt=50] int escdelay
1674 * @tfield[opt=false] boolean smartcase {scs}
1675 * @tfield[opt=false] boolean literal
1676 * @tfield[opt="auto"] string loadmethod `"auto"`, `"read"`, or `"mmap"`.
1677 * @tfield[opt="/bin/sh"] string shell
1678 * @see Window.options
1681 static int vis_options_index(lua_State *L) {
1682 Vis *vis = obj_ref_check_containerof(L, 1, VIS_LUA_TYPE_VIS_OPTS, offsetof(Vis, options));
1683 if (!vis)
1684 return -1;
1685 if (lua_isstring(L, 2)) {
1686 const char *key = lua_tostring(L, 2);
1687 if (strcmp(key, "autoindent") == 0 || strcmp(key, "ai") == 0) {
1688 lua_pushboolean(L, vis->autoindent);
1689 return 1;
1690 } else if (strcmp(key, "changecolors") == 0) {
1691 lua_pushboolean(L, vis->change_colors);
1692 return 1;
1693 } else if (strcmp(key, "escdelay") == 0) {
1694 TermKey *tk = vis->ui->termkey_get(vis->ui);
1695 lua_pushunsigned(L, termkey_get_waittime(tk));
1696 return 1;
1697 } else if (strcmp(key, "smartcase") == 0 || strcmp(key, "scs") == 0) {
1698 lua_pushboolean(L, vis->smartcase);
1699 return 1;
1700 } else if (strcmp(key, "literal") == 0) {
1701 lua_pushboolean(L, vis->literal);
1702 return 1;
1703 } else if (strcmp(key, "loadmethod") == 0) {
1704 switch (vis->load_method) {
1705 case TEXT_LOAD_AUTO:
1706 lua_pushstring(L, "auto");
1707 break;
1708 case TEXT_LOAD_READ:
1709 lua_pushstring(L, "read");
1710 break;
1711 case TEXT_LOAD_MMAP:
1712 lua_pushstring(L, "mmap");
1713 break;
1715 return 1;
1716 } else if (strcmp(key, "shell") == 0) {
1717 lua_pushstring(L, vis->shell);
1718 return 1;
1721 return index_common(L);
1724 static int vis_options_newindex(lua_State *L) {
1725 Vis *vis = obj_ref_check_containerof(L, 1, VIS_LUA_TYPE_VIS_OPTS, offsetof(Vis, options));
1726 if (!vis)
1727 return 0;
1728 if (lua_isstring(L, 2))
1729 return vis_options_assign(vis, L, lua_tostring(L, 2), 3);
1730 return newindex_common(L);
1733 static const struct luaL_Reg vis_option_funcs[] = {
1734 { "__index", vis_options_index },
1735 { "__newindex", vis_options_newindex},
1736 { NULL, NULL },
1739 /***
1740 * The user interface.
1742 * @type Ui
1744 /***
1745 * Number of available colors.
1746 * @tfield int colors
1748 /***
1749 * Current layout.
1750 * @tfield layouts layout current window layout.
1753 static int ui_index(lua_State *L) {
1754 Ui *ui = obj_ref_check(L, 1, VIS_LUA_TYPE_UI);
1756 if (lua_isstring(L, 2)) {
1757 const char *key = lua_tostring(L, 2);
1759 if (strcmp(key, "layout") == 0) {
1760 lua_pushunsigned(L, ui_layout_get(ui));
1761 return 1;
1765 return index_common(L);
1768 static int ui_newindex(lua_State *L) {
1769 Ui *ui = obj_ref_check(L, 1, VIS_LUA_TYPE_UI);
1771 if (lua_isstring(L, 2)) {
1772 const char *key = lua_tostring(L, 2);
1774 if (strcmp(key, "layout") == 0) {
1775 ui->arrange(ui, luaL_checkint(L, 3));
1776 return 0;
1779 return newindex_common(L);
1782 static const struct luaL_Reg ui_funcs[] = {
1783 { "__index", ui_index },
1784 { "__newindex", ui_newindex },
1785 { NULL, NULL },
1788 static int registers_index(lua_State *L) {
1789 lua_newtable(L);
1790 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
1791 const char *symbol = luaL_checkstring(L, 2);
1792 if (strlen(symbol) != 1)
1793 return 1;
1794 enum VisRegister reg = vis_register_from(vis, symbol[0]);
1795 if (reg >= VIS_REG_INVALID)
1796 return 1;
1797 Array data = vis_register_get(vis, reg);
1798 for (size_t i = 0, len = array_length(&data); i < len; i++) {
1799 TextString *string = array_get(&data, i);
1800 lua_pushunsigned(L, i+1);
1801 lua_pushlstring(L, string->data, string->len);
1802 lua_settable(L, -3);
1804 array_release(&data);
1805 return 1;
1808 static int registers_newindex(lua_State *L) {
1809 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
1810 const char *symbol = luaL_checkstring(L, 2);
1811 if (strlen(symbol) != 1)
1812 return 0;
1813 enum VisRegister reg = vis_register_from(vis, symbol[0]);
1814 Array data;
1815 array_init_sized(&data, sizeof(TextString));
1817 if (lua_istable(L, 3)) {
1818 lua_pushnil(L);
1819 while (lua_next(L, 3)) {
1820 TextString string;
1821 string.data = luaL_checklstring(L, -1, &string.len);
1822 array_add(&data, &string);
1823 lua_pop(L, 1);
1827 vis_register_set(vis, reg, &data);
1828 if (symbol[0] == vis_registers[VIS_REG_SEARCH].name) {
1829 Text *txt = vis->search_file->text;
1830 size_t size = text_size(txt);
1831 size_t last_line = text_lineno_by_pos(txt, size);
1832 char lastchar;
1833 if (last_line > 1 && text_byte_get(txt, size-1, &lastchar) && lastchar == '\n')
1834 last_line--;
1835 size_t start = text_pos_by_lineno(txt, last_line);
1836 size_t end = text_line_end(txt, start);
1837 if (start != EPOS && end != EPOS) {
1838 size_t size = end - start;
1839 char *last_search = malloc(size);
1840 if (last_search && size) {
1841 size = text_bytes_get(txt, start, size, last_search);
1842 if (0 == strncmp(last_search+1, ((TextString *)array_get(&data, 0))->data, size-1)) {
1843 vis_regex(vis, last_search+1, vis->smartcase ? 1 : 0);
1846 free(last_search);
1849 array_release(&data);
1850 return 0;
1853 static int registers_len(lua_State *L) {
1854 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
1855 lua_pushunsigned(L, LENGTH(vis->registers));
1856 return 1;
1859 static const struct luaL_Reg registers_funcs[] = {
1860 { "__index", registers_index },
1861 { "__newindex", registers_newindex },
1862 { "__len", registers_len },
1863 { NULL, NULL },
1866 /***
1867 * A window object.
1868 * @type Window
1871 /***
1872 * Viewport currently being displayed.
1873 * Changing these values will not move the viewport.
1874 * @table viewport
1875 * @tfield Range bytes file bytes, from 0, at the start and end of the viewport
1876 * @tfield Range lines file lines, from 1, at the top and bottom of the viewport
1877 * @tfield int height lines in viewport, accounting for window decoration
1878 * @tfield int width columns in viewport, accounting for window decoration
1880 /***
1881 * The window width.
1882 * @tfield int width
1884 /***
1885 * The window height.
1886 * @tfield int height
1888 /***
1889 * The file being displayed in this window.
1890 * @tfield File file
1892 /***
1893 * The primary selection of this window.
1894 * @tfield Selection selection
1896 /***
1897 * The selections of this window.
1898 * @tfield Array(Selection) selections
1900 /***
1901 * Window marks.
1902 * Most of these marks are stored in the associated File object, meaning they
1903 * are the same in all windows displaying the same file.
1904 * @field marks array to access the marks of this window by single letter name
1905 * @see Vis:mark_names
1907 static int window_index(lua_State *L) {
1908 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
1910 if (lua_isstring(L, 2)) {
1911 const char *key = lua_tostring(L, 2);
1913 if (strcmp(key, "viewport") == 0) {
1914 Filerange b = view_viewport_get(win->view);
1915 Filerange l;
1916 l.start = view_lines_first(win->view)->lineno;
1917 l.end = view_lines_last(win->view)->lineno;
1919 lua_createtable(L, 0, 4);
1920 lua_pushstring(L, "bytes");
1921 pushrange(L, &b);
1922 lua_settable(L, -3);
1923 lua_pushstring(L, "lines");
1924 pushrange(L, &l);
1925 lua_settable(L, -3);
1926 lua_pushstring(L, "width");
1927 lua_pushunsigned(L, view_width_get(win->view));
1928 lua_settable(L, -3);
1929 lua_pushstring(L, "height");
1930 lua_pushunsigned(L, view_height_get(win->view));
1931 lua_settable(L, -3);
1932 return 1;
1935 if (strcmp(key, "width") == 0) {
1936 lua_pushunsigned(L, vis_window_width_get(win));
1937 return 1;
1940 if (strcmp(key, "height") == 0) {
1941 lua_pushunsigned(L, vis_window_height_get(win));
1942 return 1;
1945 if (strcmp(key, "file") == 0) {
1946 obj_ref_new(L, win->file, VIS_LUA_TYPE_FILE);
1947 return 1;
1950 if (strcmp(key, "selection") == 0) {
1951 Selection *sel = view_selections_primary_get(win->view);
1952 obj_lightref_new(L, sel, VIS_LUA_TYPE_SELECTION);
1953 return 1;
1956 if (strcmp(key, "selections") == 0) {
1957 obj_ref_new(L, win->view, VIS_LUA_TYPE_SELECTIONS);
1958 return 1;
1961 if (strcmp(key, "marks") == 0) {
1962 obj_ref_new(L, &win->saved_selections, VIS_LUA_TYPE_MARKS);
1963 return 1;
1965 if (strcmp(key, "options") == 0) {
1966 obj_ref_new(L, &win->view, VIS_LUA_TYPE_WIN_OPTS);
1967 return 1;
1971 return index_common(L);
1974 static int window_options_assign(Win *win, lua_State *L, const char *key, int next) {
1975 enum UiOption flags = view_options_get(win->view);
1976 if (strcmp(key, "breakat") == 0 || strcmp(key, "brk") == 0) {
1977 if (lua_isstring(L, next))
1978 view_breakat_set(win->view, lua_tostring(L, next));
1979 } else if (strcmp(key, "colorcolumn") == 0 || strcmp(key, "cc") == 0) {
1980 view_colorcolumn_set(win->view, luaL_checkint(L, next));
1981 } else if (strcmp(key, "cursorline") == 0 || strcmp(key, "cul") == 0) {
1982 if (lua_toboolean(L, next))
1983 flags |= UI_OPTION_CURSOR_LINE;
1984 else
1985 flags &= ~UI_OPTION_CURSOR_LINE;
1986 view_options_set(win->view, flags);
1987 } else if (strcmp(key, "numbers") == 0 || strcmp(key, "nu") == 0) {
1988 if (lua_toboolean(L, next))
1989 flags |= UI_OPTION_LINE_NUMBERS_ABSOLUTE;
1990 else
1991 flags &= ~UI_OPTION_LINE_NUMBERS_ABSOLUTE;
1992 view_options_set(win->view, flags);
1993 } else if (strcmp(key, "relativenumbers") == 0 || strcmp(key, "rnu") == 0) {
1994 if (lua_toboolean(L, next))
1995 flags |= UI_OPTION_LINE_NUMBERS_RELATIVE;
1996 else
1997 flags &= ~UI_OPTION_LINE_NUMBERS_RELATIVE;
1998 view_options_set(win->view, flags);
1999 } else if (strcmp(key, "showeof") == 0) {
2000 if (lua_toboolean(L, next))
2001 flags |= UI_OPTION_SYMBOL_EOF;
2002 else
2003 flags &= ~UI_OPTION_SYMBOL_EOF;
2004 view_options_set(win->view, flags);
2005 } else if (strcmp(key, "shownewlines") == 0) {
2006 if (lua_toboolean(L, next))
2007 flags |= UI_OPTION_SYMBOL_EOL;
2008 else
2009 flags &= ~UI_OPTION_SYMBOL_EOL;
2010 view_options_set(win->view, flags);
2011 } else if (strcmp(key, "showspaces") == 0) {
2012 if (lua_toboolean(L, next))
2013 flags |= UI_OPTION_SYMBOL_SPACE;
2014 else
2015 flags &= ~UI_OPTION_SYMBOL_SPACE;
2016 view_options_set(win->view, flags);
2017 } else if (strcmp(key, "showtabs") == 0) {
2018 if (lua_toboolean(L, next))
2019 flags |= UI_OPTION_SYMBOL_TAB;
2020 else
2021 flags &= ~UI_OPTION_SYMBOL_TAB;
2022 view_options_set(win->view, flags);
2023 } else if (strcmp(key, "statusbar") == 0) {
2024 if (lua_toboolean(L, next))
2025 flags |= UI_OPTION_STATUSBAR;
2026 else
2027 flags &= ~UI_OPTION_STATUSBAR;
2028 view_options_set(win->view, flags);
2029 } else if (strcmp(key, "wrapcolumn") == 0 || strcmp(key, "wc") == 0) {
2030 view_wrapcolumn_set(win->view, luaL_checkint(L, next));
2031 } else if (strcmp(key, "tabwidth") == 0 || strcmp(key, "tw") == 0) {
2032 view_tabwidth_set(win->view, luaL_checkint(L, next));
2033 } else if (strcmp(key, "expandtab") == 0 || strcmp(key, "et") == 0) {
2034 win->expandtab = lua_toboolean(L, next);
2036 return 0;
2039 static int window_newindex(lua_State *L) {
2040 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
2042 if (lua_isstring(L, 2)) {
2043 const char *key = lua_tostring(L, 2);
2044 if (strcmp(key, "options") == 0 && lua_istable(L, 3)) {
2045 int ret = 0;
2046 /* since we don't know which keys are in the table we push
2047 * a nil then use lua_next() to remove it and push the
2048 * table's key-value pairs to the stack. these can then be
2049 * used to assign options
2051 lua_pushnil(L);
2052 while (lua_next(L, 3)) {
2053 if (lua_isstring(L, 4))
2054 ret += window_options_assign(win, L, lua_tostring(L, 4), 5);
2055 else
2056 ret += newindex_common(L);
2057 lua_pop(L, 1);
2059 lua_pop(L, 1);
2060 return ret;
2064 return newindex_common(L);
2067 static int window_selections_iterator_next(lua_State *L) {
2068 Selection **handle = lua_touserdata(L, lua_upvalueindex(1));
2069 if (!*handle)
2070 return 0;
2071 Selection *sel = obj_lightref_new(L, *handle, VIS_LUA_TYPE_SELECTION);
2072 if (!sel)
2073 return 0;
2074 *handle = view_selections_next(sel);
2075 return 1;
2078 /***
2079 * Create an iterator over all selections of this window.
2080 * @function selections_iterator
2081 * @return the new iterator
2083 static int window_selections_iterator(lua_State *L) {
2084 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
2085 Selection **handle = lua_newuserdata(L, sizeof *handle);
2086 *handle = view_selections(win->view);
2087 lua_pushcclosure(L, window_selections_iterator_next, 1);
2088 return 1;
2091 /***
2092 * Set up a window local key mapping.
2093 * The function signatures are the same as for @{Vis:map}.
2094 * @function map
2095 * @param ...
2096 * @see Vis:map
2098 static int window_map(lua_State *L) {
2099 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
2100 return keymap(L, win->vis, win);
2103 /***
2104 * Remove a window local key mapping.
2105 * The function signature is the same as for @{Vis:unmap}.
2106 * @function unmap
2107 * @param ...
2108 * @see Vis:unmap
2110 static int window_unmap(lua_State *L) {
2111 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
2112 return keyunmap(L, win->vis, win);
2115 /***
2116 * Define a display style.
2117 * @function style_define
2118 * @tparam int id the style id to use
2119 * @tparam string style the style definition
2120 * @treturn bool whether the style definition has been successfully
2121 * associated with the given id
2122 * @see style
2123 * @usage
2124 * win:style_define(win.STYLE_DEFAULT, "fore:red")
2126 static int window_style_define(lua_State *L) {
2127 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
2128 enum UiStyle id = luaL_checkunsigned(L, 2);
2129 const char *style = luaL_checkstring(L, 3);
2130 bool ret = view_style_define(win->view, id, style);
2131 lua_pushboolean(L, ret);
2132 return 1;
2135 /***
2136 * Style a window range.
2138 * The style will be cleared after every window redraw.
2139 * @function style
2140 * @tparam int id the display style as registered with @{style_define}
2141 * @tparam int start the absolute file position in bytes
2142 * @tparam int finish the end position
2143 * @see style_define
2144 * @usage
2145 * win:style(win.STYLE_DEFAULT, 0, 10)
2147 static int window_style(lua_State *L) {
2148 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
2149 enum UiStyle style = luaL_checkunsigned(L, 2);
2150 size_t start = checkpos(L, 3);
2151 size_t end = checkpos(L, 4);
2152 view_style(win->view, style, start, end);
2153 return 0;
2156 /***
2157 * Style the single terminal cell at the given coordinates, relative to this window.
2159 * Completely independent of the file buffer, and can be used to style UI elements,
2160 * such as the status bar.
2161 * The style will be cleared after every window redraw.
2162 * @function style_pos
2163 * @tparam int id display style registered with @{style_define}
2164 * @tparam int x 0-based x coordinate within Win, where (0,0) is the top left corner
2165 * @tparam int y See above
2166 * @treturn bool false if the coordinates would be outside the window's dimensions
2167 * @see style_define
2168 * @usage
2169 * win:style_pos(win.STYLE_COLOR_COLUMN, 0, win.height - 1)
2170 * -- Styles the first character of the status bar (or the last line, if disabled)
2172 static int window_style_pos(lua_State *L) {
2173 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
2174 enum UiStyle style = luaL_checkunsigned(L, 2);
2175 size_t x = checkpos(L, 3);
2176 size_t y = checkpos(L, 4);
2177 bool ret = win->ui->style_set_pos(win->ui, (int)x, (int)y, style);
2178 lua_pushboolean(L, ret);
2179 return 1;
2182 /***
2183 * Set window status line.
2185 * @function status
2186 * @tparam string left the left aligned part of the status line
2187 * @tparam[opt] string right the right aligned part of the status line
2189 static int window_status(lua_State *L) {
2190 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
2191 char status[1024] = "";
2192 int width = vis_window_width_get(win);
2193 const char *left = luaL_checkstring(L, 2);
2194 const char *right = luaL_optstring(L, 3, "");
2195 int left_width = text_string_width(left, strlen(left));
2196 int right_width = text_string_width(right, strlen(right));
2197 int spaces = width - left_width - right_width;
2198 if (spaces < 1)
2199 spaces = 1;
2200 snprintf(status, sizeof(status)-1, "%s%*s%s", left, spaces, " ", right);
2201 vis_window_status(win, status);
2202 return 0;
2205 /***
2206 * Redraw window content.
2208 * @function draw
2210 static int window_draw(lua_State *L) {
2211 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
2212 view_draw(win->view);
2213 return 0;
2216 /***
2217 * Close window.
2219 * After a successful call the Window reference becomes invalid and
2220 * must no longer be used. Attempting to close the last window will
2221 * always fail.
2223 * @function close
2224 * @see exit
2225 * @tparam bool force whether unsaved changes should be discarded
2226 * @treturn bool whether the window was closed
2228 static int window_close(lua_State *L) {
2229 Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
2230 int count = 0;
2231 for (Win *w = win->vis->windows; w; w = w->next) {
2232 if (!w->file->internal)
2233 count++;
2235 bool force = lua_isboolean(L, 2) && lua_toboolean(L, 2);
2236 bool close = count > 1 && (force || vis_window_closable(win));
2237 if (close)
2238 vis_window_close(win);
2239 lua_pushboolean(L, close);
2240 return 1;
2243 static const struct luaL_Reg window_funcs[] = {
2244 { "__index", window_index },
2245 { "__newindex", window_newindex },
2246 { "selections_iterator", window_selections_iterator },
2247 { "map", window_map },
2248 { "unmap", window_unmap },
2249 { "style_define", window_style_define },
2250 { "style", window_style },
2251 { "style_pos", window_style_pos },
2252 { "status", window_status },
2253 { "draw", window_draw },
2254 { "close", window_close },
2255 { NULL, NULL },
2258 /***
2259 * Window Options
2260 * @table options
2261 * @tfield[opt=""] string breakat {brk}
2262 * @tfield[opt=0] int colorcolumn {cc}
2263 * @tfield[opt=false] boolean cursorline {cul}
2264 * @tfield[opt=false] boolean expandtab {et}
2265 * @tfield[opt=false] boolean numbers {nu}
2266 * @tfield[opt=false] boolean relativenumbers {rnu}
2267 * @tfield[opt=true] boolean showeof
2268 * @tfield[opt=false] boolean shownewlines
2269 * @tfield[opt=false] boolean showspaces
2270 * @tfield[opt=false] boolean showtabs
2271 * @tfield[opt=true] boolean statusbar
2272 * @tfield[opt=8] int tabwidth {tw}
2273 * @tfield[opt=0] int wrapcolumn {wc}
2274 * @see Vis.options
2277 static int window_options_index(lua_State *L) {
2278 Win *win = obj_ref_check_containerof(L, 1, VIS_LUA_TYPE_WIN_OPTS, offsetof(Win, view));
2279 if (!win)
2280 return -1;
2281 if (lua_isstring(L, 2)) {
2282 const char *key = lua_tostring(L, 2);
2283 if (strcmp(key, "breakat") == 0 || strcmp(key, "brk") == 0) {
2284 lua_pushstring(L, view_breakat_get(win->view));
2285 return 1;
2286 } else if (strcmp(key, "colorcolumn") == 0 || strcmp(key, "cc") == 0) {
2287 lua_pushunsigned(L, view_colorcolumn_get(win->view));
2288 return 1;
2289 } else if (strcmp(key, "cursorline") == 0 || strcmp(key, "cul") == 0) {
2290 lua_pushboolean(L, view_options_get(win->view) & UI_OPTION_CURSOR_LINE);
2291 return 1;
2292 } else if (strcmp(key, "expandtab") == 0 || strcmp(key, "et") == 0) {
2293 lua_pushboolean(L, win->expandtab);
2294 return 1;
2295 } else if (strcmp(key, "numbers") == 0 || strcmp(key, "nu") == 0) {
2296 lua_pushboolean(L, view_options_get(win->view) & UI_OPTION_LINE_NUMBERS_ABSOLUTE);
2297 return 1;
2298 } else if (strcmp(key, "relativenumbers") == 0 || strcmp(key, "rnu") == 0) {
2299 lua_pushboolean(L, view_options_get(win->view) & UI_OPTION_LINE_NUMBERS_RELATIVE);
2300 return 1;
2301 } else if (strcmp(key, "showeof") == 0) {
2302 lua_pushboolean(L, view_options_get(win->view) & UI_OPTION_SYMBOL_EOF);
2303 return 1;
2304 } else if (strcmp(key, "shownewlines") == 0) {
2305 lua_pushboolean(L, view_options_get(win->view) & UI_OPTION_SYMBOL_EOL);
2306 return 1;
2307 } else if (strcmp(key, "showspaces") == 0) {
2308 lua_pushboolean(L, view_options_get(win->view) & UI_OPTION_SYMBOL_SPACE);
2309 return 1;
2310 } else if (strcmp(key, "showtabs") == 0) {
2311 lua_pushboolean(L, view_options_get(win->view) & UI_OPTION_SYMBOL_TAB);
2312 return 1;
2313 } else if (strcmp(key, "statusbar") == 0) {
2314 lua_pushboolean(L, view_options_get(win->view) & UI_OPTION_STATUSBAR);
2315 return 1;
2316 } else if (strcmp(key, "tabwidth") == 0 || strcmp(key, "tw") == 0) {
2317 lua_pushinteger(L, view_tabwidth_get(win->view));
2318 return 1;
2319 } else if (strcmp(key, "wrapcolumn") == 0 || strcmp(key, "wc") == 0) {
2320 lua_pushunsigned(L, view_wrapcolumn_get(win->view));
2321 return 1;
2324 return index_common(L);
2327 static int window_options_newindex(lua_State *L) {
2328 Win *win = obj_ref_check_containerof(L, 1, VIS_LUA_TYPE_WIN_OPTS, offsetof(Win, view));
2329 if (!win)
2330 return 0;
2331 if (lua_isstring(L, 2))
2332 return window_options_assign(win, L, lua_tostring(L, 2), 3);
2333 return newindex_common(L);
2336 static const struct luaL_Reg window_option_funcs[] = {
2337 { "__index", window_options_index },
2338 { "__newindex", window_options_newindex},
2339 { NULL, NULL },
2342 static int window_selections_index(lua_State *L) {
2343 View *view = obj_ref_check(L, 1, VIS_LUA_TYPE_SELECTIONS);
2344 size_t index = luaL_checkunsigned(L, 2);
2345 size_t count = view_selections_count(view);
2346 if (index == 0 || index > count)
2347 goto err;
2348 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
2349 if (!--index) {
2350 obj_lightref_new(L, s, VIS_LUA_TYPE_SELECTION);
2351 return 1;
2354 err:
2355 lua_pushnil(L);
2356 return 1;
2359 static int window_selections_len(lua_State *L) {
2360 View *view = obj_ref_check(L, 1, VIS_LUA_TYPE_SELECTIONS);
2361 lua_pushunsigned(L, view_selections_count(view));
2362 return 1;
2365 static const struct luaL_Reg window_selections_funcs[] = {
2366 { "__index", window_selections_index },
2367 { "__len", window_selections_len },
2368 { NULL, NULL },
2371 /***
2372 * A selection object.
2374 * A selection is a non-empty, directed range with two endpoints called
2375 * *cursor* and *anchor*. A selection can be anchored in which case
2376 * the anchor remains fixed while only the position of the cursor is
2377 * adjusted. For non-anchored selections both endpoints are updated. A
2378 * singleton selection covers one character on which both cursor and
2379 * anchor reside. There always exists a primary selection which remains
2380 * visible (i.e. changes to its position will adjust the viewport).
2382 * The range covered by a selection is represented as an interval whose
2383 * endpoints are absolute byte offsets from the start of the file.
2384 * Valid addresses are within the closed interval `[0, file.size]`.
2386 * Selections are currently implemented using character marks into
2387 * the underlying persistent
2388 * [text management data structure](https://github.com/martanne/vis/wiki/Text-management-using-a-piece-chain).
2390 * This has a few consequences you should be aware of:
2392 * - A selection becomes invalid when the delimiting boundaries of the underlying
2393 * text it is referencing is deleted:
2395 * -- leaves selection in an invalid state
2396 * win.file:delete(win.selection.pos, 1)
2397 * assert(win.selection.pos == nil)
2399 * Like a regular mark it will become valid again when the text is reverted
2400 * to the state before the deletion.
2402 * - Inserts after the selection position (`> selection.pos`) will not affect the
2403 * selection position.
2405 * local pos = win.selection.pos
2406 * win.file:insert(pos+1, "-")
2407 * assert(win.selection.pos == pos)
2409 * - Non-cached inserts before the selection position (`<= selection.pos`) will
2410 * affect the mark and adjust the selection position by the number of bytes
2411 * which were inserted.
2413 * local pos = win.selection.pos
2414 * win.file:insert(pos, "-")
2415 * assert(win.selection.pos == pos+1)
2417 * - Cached inserts before the selection position (`<= selection.pos`) will
2418 * not affect the selection position because the underlying text is replaced
2419 * inplace.
2421 * For these reasons it is generally recommended to update the selection position
2422 * after a modification. The general procedure amounts to:
2424 * 1. Read out the current selection position
2425 * 2. Perform text modifications
2426 * 3. Update the selection position
2428 * This is what @{Vis:insert} and @{Vis:replace} do internally.
2430 * @type Selection
2431 * @usage
2432 * local data = "new text"
2433 * local pos = win.selection.pos
2434 * win.file:insert(pos, data)
2435 * win.selection.pos = pos + #data
2438 /***
2439 * The zero based byte position in the file.
2441 * Might be `nil` if the selection is in an invalid state.
2442 * Setting this field will move the cursor endpoint of the
2443 * selection to the given position.
2444 * @tfield int pos
2446 /***
2447 * The 1-based line the cursor of this selection resides on.
2449 * @tfield int line
2450 * @see to
2452 /***
2453 * The 1-based column position the cursor of this selection resides on.
2454 * @tfield int col
2455 * @see to
2457 /***
2458 * The 1-based selection index.
2459 * @tfield int number
2461 /***
2462 * The range covered by this selection.
2463 * @tfield Range range
2465 /***
2466 * Whether this selection is anchored.
2467 * @tfield bool anchored
2469 static int window_selection_index(lua_State *L) {
2470 Selection *sel = obj_lightref_check(L, 1, VIS_LUA_TYPE_SELECTION);
2471 if (!sel) {
2472 lua_pushnil(L);
2473 return 1;
2476 if (lua_isstring(L, 2)) {
2477 const char *key = lua_tostring(L, 2);
2478 if (strcmp(key, "pos") == 0) {
2479 pushpos(L, view_cursors_pos(sel));
2480 return 1;
2483 if (strcmp(key, "line") == 0) {
2484 lua_pushunsigned(L, view_cursors_line(sel));
2485 return 1;
2488 if (strcmp(key, "col") == 0) {
2489 lua_pushunsigned(L, view_cursors_col(sel));
2490 return 1;
2493 if (strcmp(key, "number") == 0) {
2494 lua_pushunsigned(L, view_selections_number(sel)+1);
2495 return 1;
2498 if (strcmp(key, "range") == 0) {
2499 Filerange range = view_selections_get(sel);
2500 pushrange(L, &range);
2501 return 1;
2504 if (strcmp(key, "anchored") == 0) {
2505 lua_pushboolean(L, view_selections_anchored(sel));
2506 return 1;
2511 return index_common(L);
2514 static int window_selection_newindex(lua_State *L) {
2515 Selection *sel = obj_lightref_check(L, 1, VIS_LUA_TYPE_SELECTION);
2516 if (!sel)
2517 return 0;
2518 if (lua_isstring(L, 2)) {
2519 const char *key = lua_tostring(L, 2);
2520 if (strcmp(key, "pos") == 0) {
2521 size_t pos = checkpos(L, 3);
2522 view_cursors_to(sel, pos);
2523 return 0;
2526 if (strcmp(key, "range") == 0) {
2527 Filerange range = getrange(L, 3);
2528 if (text_range_valid(&range)) {
2529 view_selections_set(sel, &range);
2530 view_selections_anchor(sel, true);
2531 } else {
2532 view_selection_clear(sel);
2534 return 0;
2537 if (strcmp(key, "anchored") == 0) {
2538 view_selections_anchor(sel, lua_toboolean(L, 3));
2539 return 0;
2542 return newindex_common(L);
2545 /***
2546 * Move cursor of selection.
2547 * @function to
2548 * @tparam int line the 1-based line number
2549 * @tparam int col the 1-based column number
2551 static int window_selection_to(lua_State *L) {
2552 Selection *sel = obj_lightref_check(L, 1, VIS_LUA_TYPE_SELECTION);
2553 if (sel) {
2554 size_t line = checkpos(L, 2);
2555 size_t col = checkpos(L, 3);
2556 view_cursors_place(sel, line, col);
2558 return 0;
2561 /***
2562 * Remove selection.
2563 * @function remove
2565 static int window_selection_remove(lua_State *L) {
2566 Selection *sel = obj_lightref_check(L, 1, VIS_LUA_TYPE_SELECTION);
2567 if (sel) {
2568 view_selections_dispose(sel);
2570 return 0;
2573 static const struct luaL_Reg window_selection_funcs[] = {
2574 { "__index", window_selection_index },
2575 { "__newindex", window_selection_newindex },
2576 { "to", window_selection_to },
2577 { "remove", window_selection_remove },
2578 { NULL, NULL },
2581 /***
2582 * A file object.
2583 * @type File
2585 /***
2586 * File name.
2587 * @tfield string name the file name relative to current working directory or `nil` if not yet named
2589 /***
2590 * File path.
2591 * @tfield string path the absolute file path or `nil` if not yet named
2593 /***
2594 * File content by logical lines.
2596 * Assigning to array element `0` (`#lines+1`) will insert a new line at
2597 * the beginning (end) of the file.
2598 * @tfield Array(string) lines the file content accessible as 1-based array
2599 * @see content
2600 * @usage
2601 * local lines = vis.win.file.lines
2602 * for i=1, #lines do
2603 * lines[i] = i .. ": " .. lines[i]
2604 * end
2606 /***
2607 * File save method
2608 * @tfield[opt="auto"] string savemethod `"auto"`, `"atomic"`, or `"inplace"`.
2610 /***
2611 * File size in bytes.
2612 * @tfield int size the current file size in bytes
2614 /***
2615 * File state.
2616 * @tfield bool modified whether the file contains unsaved changes
2618 /***
2619 * File permission.
2620 * @tfield int permission the file permission bits as of the most recent load/save
2622 static int file_index(lua_State *L) {
2623 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2625 if (lua_isstring(L, 2)) {
2626 const char *key = lua_tostring(L, 2);
2627 if (strcmp(key, "name") == 0) {
2628 lua_pushstring(L, file_name_get(file));
2629 return 1;
2632 if (strcmp(key, "path") == 0) {
2633 lua_pushstring(L, file->name);
2634 return 1;
2637 if (strcmp(key, "lines") == 0) {
2638 obj_ref_new(L, file->text, VIS_LUA_TYPE_TEXT);
2639 return 1;
2642 if (strcmp(key, "size") == 0) {
2643 lua_pushunsigned(L, text_size(file->text));
2644 return 1;
2647 if (strcmp(key, "modified") == 0) {
2648 lua_pushboolean(L, text_modified(file->text));
2649 return 1;
2652 if (strcmp(key, "permission") == 0) {
2653 struct stat stat = text_stat(file->text);
2654 lua_pushunsigned(L, stat.st_mode & 0777);
2655 return 1;
2658 if (strcmp(key, "savemethod") == 0) {
2659 switch (file->save_method) {
2660 case TEXT_SAVE_AUTO:
2661 lua_pushstring(L, "auto");
2662 break;
2663 case TEXT_SAVE_ATOMIC:
2664 lua_pushstring(L, "atomic");
2665 break;
2666 case TEXT_SAVE_INPLACE:
2667 lua_pushstring(L, "inplace");
2668 break;
2670 return 1;
2674 return index_common(L);
2677 static int file_newindex(lua_State *L) {
2678 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2680 if (lua_isstring(L, 2)) {
2681 const char *key = lua_tostring(L, 2);
2683 if (strcmp(key, "modified") == 0) {
2684 bool modified = lua_isboolean(L, 3) && lua_toboolean(L, 3);
2685 if (modified) {
2686 text_insert(file->text, 0, " ", 1);
2687 text_delete(file->text, 0, 1);
2688 } else {
2689 text_save(file->text, NULL);
2691 return 0;
2694 if (strcmp(key, "savemethod") == 0) {
2695 if (!lua_isstring(L, 3))
2696 return newindex_common(L);
2697 const char *sm = lua_tostring(L, 3);
2698 if (strcmp(sm, "auto") == 0)
2699 file->save_method = TEXT_SAVE_AUTO;
2700 else if (strcmp(sm, "atomic") == 0)
2701 file->save_method = TEXT_SAVE_ATOMIC;
2702 else if (strcmp(sm, "inplace") == 0)
2703 file->save_method = TEXT_SAVE_INPLACE;
2704 return 0;
2708 return newindex_common(L);
2711 /***
2712 * Insert data at position.
2713 * @function insert
2714 * @tparam int pos the 0-based file position in bytes
2715 * @tparam string data the data to insert
2716 * @treturn bool whether the file content was successfully changed
2718 static int file_insert(lua_State *L) {
2719 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2720 size_t pos = checkpos(L, 2);
2721 size_t len;
2722 luaL_checkstring(L, 3);
2723 const char *data = lua_tolstring(L, 3, &len);
2724 lua_pushboolean(L, text_insert(file->text, pos, data, len));
2725 return 1;
2728 /***
2729 * Delete data at position.
2731 * @function delete
2732 * @tparam int pos the 0-based file position in bytes
2733 * @tparam int len the length in bytes to delete
2734 * @treturn bool whether the file content was successfully changed
2736 /***
2737 * Delete file range.
2739 * @function delete
2740 * @tparam Range range the range to delete
2741 * @treturn bool whether the file content was successfully changed
2743 static int file_delete(lua_State *L) {
2744 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2745 Filerange range = getrange(L, 2);
2746 lua_pushboolean(L, text_delete_range(file->text, &range));
2747 return 1;
2750 /***
2751 * Create an iterator over all lines of the file.
2753 * For large files this is probably faster than @{lines}.
2754 * @function lines_iterator
2755 * @return the new iterator
2756 * @see lines
2757 * @usage
2758 * for line in file:lines_iterator() do
2759 * -- do something with line
2760 * end
2762 static int file_lines_iterator_it(lua_State *L);
2763 static int file_lines_iterator(lua_State *L) {
2764 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2765 size_t line = luaL_optunsigned(L, 2, 1);
2766 size_t *pos = lua_newuserdata(L, sizeof *pos);
2767 *pos = text_pos_by_lineno(file->text, line);
2768 lua_pushcclosure(L, file_lines_iterator_it, 2);
2769 return 1;
2772 static int file_lines_iterator_it(lua_State *L) {
2773 File *file = *(File**)lua_touserdata(L, lua_upvalueindex(1));
2774 size_t *start = lua_touserdata(L, lua_upvalueindex(2));
2775 if (*start == text_size(file->text))
2776 return 0;
2777 size_t end = text_line_end(file->text, *start);
2778 size_t len = end - *start;
2779 char *buf = lua_newuserdata(L, len);
2780 if (!buf && len)
2781 return 0;
2782 len = text_bytes_get(file->text, *start, len, buf);
2783 lua_pushlstring(L, buf, len);
2784 *start = text_line_next(file->text, end);
2785 return 1;
2788 /***
2789 * Get file content of position and length.
2791 * @function content
2792 * @tparam int pos the 0-based file position in bytes
2793 * @tparam int len the length in bytes to read
2794 * @treturn string the file content corresponding to the range
2795 * @see lines
2796 * @usage
2797 * local file = vis.win.file
2798 * local text = file:content(0, file.size)
2800 /***
2801 * Get file content of range.
2803 * @function content
2804 * @tparam Range range the range to read
2805 * @treturn string the file content corresponding to the range
2807 static int file_content(lua_State *L) {
2808 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2809 Filerange range = getrange(L, 2);
2810 if (!text_range_valid(&range))
2811 goto err;
2812 size_t len = text_range_size(&range);
2813 char *data = lua_newuserdata(L, len);
2814 if (!data)
2815 goto err;
2816 len = text_bytes_get(file->text, range.start, len, data);
2817 lua_pushlstring(L, data, len);
2818 return 1;
2819 err:
2820 lua_pushnil(L);
2821 return 1;
2824 /***
2825 * Set mark.
2826 * @function mark_set
2827 * @tparam int pos the position to set the mark to, must be in [0, file.size]
2828 * @treturn Mark mark the mark which can be looked up later
2830 static int file_mark_set(lua_State *L) {
2831 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2832 size_t pos = checkpos(L, 2);
2833 Mark mark = text_mark_set(file->text, pos);
2834 if (mark)
2835 obj_lightref_new(L, (void*)mark, VIS_LUA_TYPE_MARK);
2836 else
2837 lua_pushnil(L);
2838 return 1;
2841 /***
2842 * Get position of mark.
2843 * @function mark_get
2844 * @tparam Mark mark the mark to look up
2845 * @treturn int pos the position of the mark, or `nil` if invalid
2847 static int file_mark_get(lua_State *L) {
2848 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2849 Mark mark = (Mark)obj_lightref_check(L, 2, VIS_LUA_TYPE_MARK);
2850 size_t pos = text_mark_get(file->text, mark);
2851 if (pos == EPOS)
2852 lua_pushnil(L);
2853 else
2854 lua_pushunsigned(L, pos);
2855 return 1;
2858 /***
2859 * Word text object.
2861 * @function text_object_word
2862 * @tparam int pos the position which must be part of the word
2863 * @treturn Range range the range
2866 /***
2867 * WORD text object.
2869 * @function text_object_longword
2870 * @tparam int pos the position which must be part of the word
2871 * @treturn Range range the range
2874 static int file_text_object(lua_State *L) {
2875 Filerange range = text_range_empty();
2876 File *file = obj_ref_check(L, 1, VIS_LUA_TYPE_FILE);
2877 size_t pos = checkpos(L, 2);
2878 size_t idx = lua_tointeger(L, lua_upvalueindex(1));
2879 if (idx < LENGTH(vis_textobjects)) {
2880 const TextObject *txtobj = &vis_textobjects[idx];
2881 if (txtobj->txt)
2882 range = txtobj->txt(file->text, pos);
2884 pushrange(L, &range);
2885 return 1;
2888 static const struct luaL_Reg file_funcs[] = {
2889 { "__index", file_index },
2890 { "__newindex", file_newindex },
2891 { "insert", file_insert },
2892 { "delete", file_delete },
2893 { "lines_iterator", file_lines_iterator },
2894 { "content", file_content },
2895 { "mark_set", file_mark_set },
2896 { "mark_get", file_mark_get },
2897 { NULL, NULL },
2900 static int file_lines_index(lua_State *L) {
2901 Text *txt = obj_ref_check(L, 1, VIS_LUA_TYPE_TEXT);
2902 size_t line = luaL_checkunsigned(L, 2);
2903 size_t start = text_pos_by_lineno(txt, line);
2904 size_t end = text_line_end(txt, start);
2905 if (start != EPOS && end != EPOS) {
2906 size_t size = end - start;
2907 char *data = lua_newuserdata(L, size);
2908 if (!data && size)
2909 goto err;
2910 size = text_bytes_get(txt, start, size, data);
2911 lua_pushlstring(L, data, size);
2912 return 1;
2914 err:
2915 lua_pushnil(L);
2916 return 1;
2919 static int file_lines_newindex(lua_State *L) {
2920 Text *txt = obj_ref_check(L, 1, VIS_LUA_TYPE_TEXT);
2921 size_t line = luaL_checkunsigned(L, 2);
2922 size_t size;
2923 const char *data = luaL_checklstring(L, 3, &size);
2924 if (line == 0) {
2925 text_insert(txt, 0, data, size);
2926 text_insert(txt, size, "\n", 1);
2927 return 0;
2929 size_t start = text_pos_by_lineno(txt, line);
2930 size_t end = text_line_end(txt, start);
2931 if (start != EPOS && end != EPOS) {
2932 text_delete(txt, start, end - start);
2933 text_insert(txt, start, data, size);
2934 if (text_size(txt) == start + size)
2935 text_insert(txt, text_size(txt), "\n", 1);
2937 return 0;
2940 static int file_lines_len(lua_State *L) {
2941 Text *txt = obj_ref_check(L, 1, VIS_LUA_TYPE_TEXT);
2942 size_t lines = 0;
2943 char lastchar;
2944 size_t size = text_size(txt);
2945 if (size > 0)
2946 lines = text_lineno_by_pos(txt, size);
2947 if (lines > 1 && text_byte_get(txt, size-1, &lastchar) && lastchar == '\n')
2948 lines--;
2949 lua_pushunsigned(L, lines);
2950 return 1;
2953 static const struct luaL_Reg file_lines_funcs[] = {
2954 { "__index", file_lines_index },
2955 { "__newindex", file_lines_newindex },
2956 { "__len", file_lines_len },
2957 { NULL, NULL },
2960 static int window_marks_index(lua_State *L) {
2961 lua_newtable(L);
2962 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
2963 Win *win = obj_ref_check_containerof(L, 1, VIS_LUA_TYPE_MARKS, offsetof(Win, saved_selections));
2964 if (!win)
2965 return 1;
2966 const char *symbol = luaL_checkstring(L, 2);
2967 if (strlen(symbol) != 1)
2968 return 1;
2969 enum VisMark mark = vis_mark_from(vis, symbol[0]);
2970 if (mark == VIS_MARK_INVALID)
2971 return 1;
2973 Array arr = vis_mark_get(win, mark);
2974 for (size_t i = 0, len = array_length(&arr); i < len; i++) {
2975 Filerange *range = array_get(&arr, i);
2976 lua_pushunsigned(L, i+1);
2977 pushrange(L, range);
2978 lua_settable(L, -3);
2980 array_release(&arr);
2981 return 1;
2984 static int window_marks_newindex(lua_State *L) {
2985 Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
2986 Win *win = obj_ref_check_containerof(L, 1, VIS_LUA_TYPE_MARKS, offsetof(Win, saved_selections));
2987 if (!win)
2988 return 0;
2989 const char *symbol = luaL_checkstring(L, 2);
2990 if (strlen(symbol) != 1)
2991 return 0;
2992 enum VisMark mark = vis_mark_from(vis, symbol[0]);
2993 if (mark == VIS_MARK_INVALID)
2994 return 0;
2996 Array ranges;
2997 array_init_sized(&ranges, sizeof(Filerange));
2999 if (lua_istable(L, 3)) {
3000 lua_pushnil(L);
3001 while (lua_next(L, 3)) {
3002 Filerange range = getrange(L, -1);
3003 if (text_range_valid(&range))
3004 array_add(&ranges, &range);
3005 lua_pop(L, 1);
3009 vis_mark_set(win, mark, &ranges);
3010 array_release(&ranges);
3011 return 0;
3014 static int window_marks_len(lua_State *L) {
3015 lua_pushunsigned(L, VIS_MARK_INVALID);
3016 return 1;
3019 static const struct luaL_Reg window_marks_funcs[] = {
3020 { "__index", window_marks_index },
3021 { "__newindex", window_marks_newindex },
3022 { "__len", window_marks_len },
3023 { NULL, NULL },
3026 /***
3027 * A file range.
3029 * For a valid range `start <= finish` holds.
3030 * An invalid range is represented as `nil`.
3031 * @type Range
3033 /***
3034 * The beginning of the range.
3035 * @tfield int start
3037 /***
3038 * The end of the range.
3039 * @tfield int finish
3042 /***
3043 * Layouts.
3044 * @section Layouts
3047 /***
3048 * Layout Constants.
3049 * @table layouts
3050 * @tfield int HORIZONTAL
3051 * @tfield int VERTICAL
3054 /***
3055 * Modes.
3056 * @section Modes
3059 /***
3060 * Mode constants.
3061 * @table modes
3062 * @tfield int NORMAL
3063 * @tfield int OPERATOR_PENDING
3064 * @tfield int INSERT
3065 * @tfield int REPLACE
3066 * @tfield int VISUAL
3067 * @tfield int VISUAL_LINE
3068 * @see Vis:map
3069 * @see Window:map
3072 /***
3073 * Key Handling.
3075 * This section describes the contract between the editor core and Lua
3076 * key handling functions mapped to symbolic keys using either @{Vis:map}
3077 * or @{Window:map}.
3079 * @section Key_Handling
3082 /***
3083 * Example of a key handling function.
3085 * The keyhandler is invoked with the pending content of the input queue
3086 * given as argument. This might be the empty string if no further input
3087 * is available.
3089 * The function is expected to return the number of *bytes* it has
3090 * consumed from the passed input keys. A negative return value is
3091 * interpreted as an indication that not enough input was available. The
3092 * function will be called again once the user has provided more input. A
3093 * missing return value (i.e. `nil`) is interpreted as zero, meaning
3094 * no further input was consumed but the function completed successfully.
3096 * @function keyhandler
3097 * @tparam string keys the keys following the mapping
3098 * @treturn int the number of *bytes* being consumed by the function (see above)
3099 * @see Vis:action_register
3100 * @see Vis:map
3101 * @see Window:map
3102 * @usage
3103 * vis:map(vis.modes.INSERT, "<C-k>", function(keys)
3104 * if #keys < 2 then
3105 * return -1 -- need more input
3106 * end
3107 * local digraph = keys:sub(1, 2)
3108 * if digraph == "l*" then
3109 * vis:feedkeys('λ')
3110 * return 2 -- consume 2 bytes of input
3111 * end
3112 * end, "Insert digraph")
3115 /***
3116 * Core Events.
3118 * These events are invoked from the editor core.
3119 * The following functions are invoked if they are registered in the
3120 * `vis.events` table. Users scripts should generally use the [Events](#events)
3121 * mechanism instead which multiplexes these core events.
3123 * @section Core_Events
3126 static void vis_lua_event_get(lua_State *L, const char *name) {
3127 lua_getglobal(L, "vis");
3128 lua_getfield(L, -1, "events");
3129 if (lua_istable(L, -1)) {
3130 lua_getfield(L, -1, name);
3132 lua_remove(L, -2);
3135 static void vis_lua_event_call(Vis *vis, const char *name) {
3136 lua_State *L = vis->lua;
3137 vis_lua_event_get(L, name);
3138 if (lua_isfunction(L, -1))
3139 pcall(vis, L, 0, 0);
3140 lua_pop(L, 1);
3143 static bool vis_lua_path_strip(Vis *vis) {
3144 lua_State *L = vis->lua;
3145 lua_getglobal(L, "package");
3147 for (const char **var = (const char*[]){ "path", "cpath", NULL }; *var; var++) {
3149 lua_getfield(L, -1, *var);
3150 const char *path = lua_tostring(L, -1);
3151 lua_pop(L, 1);
3152 if (!path)
3153 return false;
3155 char *copy = strdup(path), *stripped = calloc(1, strlen(path)+2);
3156 if (!copy || !stripped) {
3157 free(copy);
3158 free(stripped);
3159 return false;
3162 for (char *elem = copy, *stripped_elem = stripped, *next; elem; elem = next) {
3163 if ((next = strstr(elem, ";")))
3164 *next++ = '\0';
3165 if (strstr(elem, "./"))
3166 continue; /* skip relative path entries */
3167 stripped_elem += sprintf(stripped_elem, "%s;", elem);
3170 lua_pushstring(L, stripped);
3171 lua_setfield(L, -2, *var);
3173 free(copy);
3174 free(stripped);
3177 lua_pop(L, 1); /* package */
3178 return true;
3181 bool vis_lua_path_add(Vis *vis, const char *path) {
3182 lua_State *L = vis->lua;
3183 if (!L || !path)
3184 return false;
3185 lua_getglobal(L, "package");
3186 lua_pushstring(L, path);
3187 lua_pushstring(L, "/?.lua;");
3188 lua_pushstring(L, path);
3189 lua_pushstring(L, "/?/init.lua;");
3190 lua_getfield(L, -5, "path");
3191 lua_concat(L, 5);
3192 lua_setfield(L, -2, "path");
3193 lua_pop(L, 1); /* package */
3194 return true;
3197 bool vis_lua_paths_get(Vis *vis, char **lpath, char **cpath) {
3198 lua_State *L = vis->lua;
3199 if (!L)
3200 return false;
3201 const char *s;
3202 lua_getglobal(L, "package");
3203 lua_getfield(L, -1, "path");
3204 s = lua_tostring(L, -1);
3205 *lpath = s ? strdup(s) : NULL;
3206 lua_getfield(L, -2, "cpath");
3207 s = lua_tostring(L, -1);
3208 *cpath = s ? strdup(s) : NULL;
3209 return true;
3212 static bool package_exist(Vis *vis, lua_State *L, const char *name) {
3213 const char lua[] =
3214 "local name = ...\n"
3215 "for _, searcher in ipairs(package.searchers or package.loaders) do\n"
3216 "local loader = searcher(name)\n"
3217 "if type(loader) == 'function' then\n"
3218 "return true\n"
3219 "end\n"
3220 "end\n"
3221 "return false\n";
3222 if (luaL_loadstring(L, lua) != LUA_OK)
3223 return false;
3224 lua_pushstring(L, name);
3225 /* an error indicates package exists */
3226 bool ret = lua_pcall(L, 1, 1, 0) != LUA_OK || lua_toboolean(L, -1);
3227 lua_pop(L, 1);
3228 return ret;
3231 static void *alloc_lua(void *ud, void *ptr, size_t osize, size_t nsize) {
3232 if (nsize == 0) {
3233 free(ptr);
3234 return NULL;
3235 } else {
3236 return realloc(ptr, nsize);
3240 /***
3241 * Editor initialization completed.
3242 * This event is emitted immediately after `visrc.lua` has been sourced, but
3243 * before any other events have occurred, in particular the command line arguments
3244 * have not yet been processed.
3246 * Can be used to set *global* configuration options.
3247 * @function init
3249 void vis_lua_init(Vis *vis) {
3250 lua_State *L = lua_newstate(alloc_lua, vis);
3251 if (!L)
3252 return;
3253 vis->lua = L;
3254 lua_atpanic(L, &panic_handler);
3256 luaL_openlibs(L);
3258 #if CONFIG_LPEG
3259 extern int luaopen_lpeg(lua_State *L);
3260 lua_getglobal(L, "package");
3261 lua_getfield(L, -1, "preload");
3262 lua_pushcfunction(L, luaopen_lpeg);
3263 lua_setfield(L, -2, "lpeg");
3264 lua_pop(L, 2);
3265 #endif
3267 /* remove any relative paths from lua's default package.path */
3268 vis_lua_path_strip(vis);
3270 /* extends lua's package.path with:
3271 * - $VIS_PATH
3272 * - ./lua (relative path to the binary location)
3273 * - $XDG_CONFIG_HOME/vis (defaulting to $HOME/.config/vis)
3274 * - /etc/vis (for system-wide configuration provided by administrator)
3275 * - /usr/(local/)?share/vis (or whatever is specified during ./configure)
3276 * - package.path (standard lua search path)
3278 char path[PATH_MAX];
3280 vis_lua_path_add(vis, VIS_PATH);
3282 /* try to get users home directory */
3283 const char *home = getenv("HOME");
3284 if (!home || !*home) {
3285 struct passwd *pw = getpwuid(getuid());
3286 if (pw)
3287 home = pw->pw_dir;
3290 vis_lua_path_add(vis, "/etc/vis");
3292 const char *xdg_config = getenv("XDG_CONFIG_HOME");
3293 if (xdg_config) {
3294 snprintf(path, sizeof path, "%s/vis", xdg_config);
3295 vis_lua_path_add(vis, path);
3296 } else if (home && *home) {
3297 snprintf(path, sizeof path, "%s/.config/vis", home);
3298 vis_lua_path_add(vis, path);
3301 ssize_t len = readlink("/proc/self/exe", path, sizeof(path)-1);
3302 if (len > 0) {
3303 path[len] = '\0';
3304 /* some idiotic dirname(3) implementations return pointers to statically
3305 * allocated memory, hence we use memmove to copy it back */
3306 char *dir = dirname(path);
3307 if (dir) {
3308 size_t len = strlen(dir)+1;
3309 if (len < sizeof(path) - sizeof("/lua")) {
3310 memmove(path, dir, len);
3311 strcat(path, "/lua");
3312 vis_lua_path_add(vis, path);
3317 vis_lua_path_add(vis, getenv("VIS_PATH"));
3319 /* table in registry to lookup object type, stores metatable -> type mapping */
3320 lua_newtable(L);
3321 lua_setfield(L, LUA_REGISTRYINDEX, "vis.types");
3322 /* table in registry to track lifetimes of C objects */
3323 lua_newtable(L);
3324 lua_setfield(L, LUA_REGISTRYINDEX, "vis.objects");
3325 /* table in registry to store references to Lua functions */
3326 lua_newtable(L);
3327 lua_setfield(L, LUA_REGISTRYINDEX, "vis.functions");
3328 /* metatable used to type check user data */
3329 obj_type_new(L, VIS_LUA_TYPE_VIS);
3330 luaL_setfuncs(L, vis_lua, 0);
3331 lua_newtable(L);
3332 lua_setfield(L, -2, "types");
3333 /* create reference to main vis object, such that the further
3334 * calls to obj_type_new can register the type meta tables in
3335 * vis.types[name] */
3336 obj_ref_new(L, vis, "vis");
3337 lua_setglobal(L, "vis");
3339 obj_type_new(L, VIS_LUA_TYPE_FILE);
3341 const struct {
3342 enum VisTextObject id;
3343 const char *name;
3344 } textobjects[] = {
3345 { VIS_TEXTOBJECT_INNER_WORD, "text_object_word" },
3346 { VIS_TEXTOBJECT_INNER_LONGWORD, "text_object_longword" },
3349 for (size_t i = 0; i < LENGTH(textobjects); i++) {
3350 lua_pushunsigned(L, textobjects[i].id);
3351 lua_pushcclosure(L, file_text_object, 1);
3352 lua_setfield(L, -2, textobjects[i].name);
3355 luaL_setfuncs(L, file_funcs, 0);
3357 obj_type_new(L, VIS_LUA_TYPE_TEXT);
3358 luaL_setfuncs(L, file_lines_funcs, 0);
3359 obj_type_new(L, VIS_LUA_TYPE_WINDOW);
3360 luaL_setfuncs(L, window_funcs, 0);
3362 const struct {
3363 enum UiStyle id;
3364 const char *name;
3365 } styles[] = {
3366 { UI_STYLE_LEXER_MAX, "STYLE_LEXER_MAX" },
3367 { UI_STYLE_DEFAULT, "STYLE_DEFAULT" },
3368 { UI_STYLE_CURSOR, "STYLE_CURSOR" },
3369 { UI_STYLE_CURSOR_PRIMARY, "STYLE_CURSOR_PRIMARY" },
3370 { UI_STYLE_CURSOR_LINE, "STYLE_CURSOR_LINE" },
3371 { UI_STYLE_SELECTION, "STYLE_SELECTION" },
3372 { UI_STYLE_LINENUMBER, "STYLE_LINENUMBER" },
3373 { UI_STYLE_LINENUMBER_CURSOR, "STYLE_LINENUMBER_CURSOR" },
3374 { UI_STYLE_COLOR_COLUMN, "STYLE_COLOR_COLUMN" },
3375 { UI_STYLE_STATUS, "STYLE_STATUS" },
3376 { UI_STYLE_STATUS_FOCUSED, "STYLE_STATUS_FOCUSED" },
3377 { UI_STYLE_SEPARATOR, "STYLE_SEPARATOR" },
3378 { UI_STYLE_INFO, "STYLE_INFO" },
3379 { UI_STYLE_EOF, "STYLE_EOF" },
3382 for (size_t i = 0; i < LENGTH(styles); i++) {
3383 lua_pushunsigned(L, styles[i].id);
3384 lua_setfield(L, -2, styles[i].name);
3387 obj_type_new(L, VIS_LUA_TYPE_WIN_OPTS);
3388 luaL_setfuncs(L, window_option_funcs, 0);
3390 obj_type_new(L, VIS_LUA_TYPE_MARK);
3391 obj_type_new(L, VIS_LUA_TYPE_MARKS);
3392 lua_pushlightuserdata(L, vis);
3393 luaL_setfuncs(L, window_marks_funcs, 1);
3395 obj_type_new(L, VIS_LUA_TYPE_SELECTION);
3396 luaL_setfuncs(L, window_selection_funcs, 0);
3397 obj_type_new(L, VIS_LUA_TYPE_SELECTIONS);
3398 luaL_setfuncs(L, window_selections_funcs, 0);
3400 obj_type_new(L, VIS_LUA_TYPE_UI);
3401 luaL_setfuncs(L, ui_funcs, 0);
3402 lua_pushunsigned(L, vis->ui->colors(vis->ui));
3403 lua_setfield(L, -2, "colors");
3404 lua_newtable(L);
3405 static const struct {
3406 enum UiLayout id;
3407 const char *name;
3408 } layouts[] = {
3409 { UI_LAYOUT_HORIZONTAL, "HORIZONTAL" },
3410 { UI_LAYOUT_VERTICAL, "VERTICAL" },
3412 for (size_t i = 0; i < LENGTH(layouts); i++) {
3413 lua_pushunsigned(L, layouts[i].id);
3414 lua_setfield(L, -2, layouts[i].name);
3416 lua_setfield(L, -2, "layouts");
3418 obj_type_new(L, VIS_LUA_TYPE_REGISTERS);
3419 lua_pushlightuserdata(L, vis);
3420 luaL_setfuncs(L, registers_funcs, 1);
3422 obj_type_new(L, VIS_LUA_TYPE_KEYACTION);
3424 lua_getglobal(L, "vis");
3425 lua_getmetatable(L, -1);
3427 lua_pushstring(L, VERSION);
3428 lua_setfield(L, -2, "VERSION");
3430 lua_newtable(L);
3431 static const struct {
3432 enum VisMode id;
3433 const char *name;
3434 } modes[] = {
3435 { VIS_MODE_NORMAL, "NORMAL" },
3436 { VIS_MODE_OPERATOR_PENDING, "OPERATOR_PENDING" },
3437 { VIS_MODE_VISUAL, "VISUAL" },
3438 { VIS_MODE_VISUAL_LINE, "VISUAL_LINE" },
3439 { VIS_MODE_INSERT, "INSERT" },
3440 { VIS_MODE_REPLACE, "REPLACE" },
3442 for (size_t i = 0; i < LENGTH(modes); i++) {
3443 lua_pushunsigned(L, modes[i].id);
3444 lua_setfield(L, -2, modes[i].name);
3446 lua_setfield(L, -2, "modes");
3448 obj_type_new(L, VIS_LUA_TYPE_VIS_OPTS);
3449 luaL_setfuncs(L, vis_option_funcs, 0);
3451 if (!package_exist(vis, L, "visrc")) {
3452 vis_info_show(vis, "WARNING: failed to load visrc.lua");
3453 } else {
3454 lua_getglobal(L, "require");
3455 lua_pushstring(L, "visrc");
3456 pcall(vis, L, 1, 0);
3457 vis_lua_event_call(vis, "init");
3461 /***
3462 * Editor startup completed.
3463 * This event is emitted immediately before the main loop starts.
3464 * At this point all files are loaded and corresponding windows are created.
3465 * We are about to process interactive keyboard input.
3466 * @function start
3468 void vis_lua_start(Vis *vis) {
3469 vis_lua_event_call(vis, "start");
3473 * Editor is about to terminate.
3474 * @function quit
3476 void vis_lua_quit(Vis *vis) {
3477 if (!vis->lua)
3478 return;
3479 vis_lua_event_call(vis, "quit");
3480 lua_close(vis->lua);
3481 vis->lua = NULL;
3484 /***
3485 * Input key event in either input or replace mode.
3486 * @function input
3487 * @tparam string key
3488 * @treturn bool whether the key was consumed or not
3490 static bool vis_lua_input(Vis *vis, const char *key, size_t len) {
3491 lua_State *L = vis->lua;
3492 if (!L || !vis->win || vis->win->file->internal)
3493 return false;
3494 bool ret = false;
3495 vis_lua_event_get(L, "input");
3496 if (lua_isfunction(L, -1)) {
3497 lua_pushlstring(L, key, len);
3498 if (pcall(vis, L, 1, 1) == 0) {
3499 ret = lua_isboolean(L, -1) && lua_toboolean(L, -1);
3500 lua_pop(L, 1);
3503 lua_pop(L, 1);
3504 return ret;
3507 void vis_lua_mode_insert_input(Vis *vis, const char *key, size_t len) {
3508 if (!vis_lua_input(vis, key, len))
3509 vis_insert_key(vis, key, len);
3512 void vis_lua_mode_replace_input(Vis *vis, const char *key, size_t len) {
3513 if (!vis_lua_input(vis, key, len))
3514 vis_replace_key(vis, key, len);
3517 /***
3518 * File open.
3519 * @function file_open
3520 * @tparam File file the file to be opened
3522 void vis_lua_file_open(Vis *vis, File *file) {
3523 debug("event: file-open: %s %p %p\n", file->name ? file->name : "unnamed", (void*)file, (void*)file->text);
3524 lua_State *L = vis->lua;
3525 if (!L)
3526 return;
3527 vis_lua_event_get(L, "file_open");
3528 if (lua_isfunction(L, -1)) {
3529 obj_ref_new(L, file, VIS_LUA_TYPE_FILE);
3530 pcall(vis, L, 1, 0);
3532 lua_pop(L, 1);
3535 /***
3536 * File pre save.
3537 * Triggered *before* the file is being written.
3538 * @function file_save_pre
3539 * @tparam File file the file being written
3540 * @tparam string path the absolute path to which the file will be written, `nil` if standard output
3541 * @treturn bool whether the write operation should be proceeded
3543 bool vis_lua_file_save_pre(Vis *vis, File *file, const char *path) {
3544 lua_State *L = vis->lua;
3545 if (!L)
3546 return true;
3547 vis_lua_event_get(L, "file_save_pre");
3548 if (lua_isfunction(L, -1)) {
3549 obj_ref_new(L, file, VIS_LUA_TYPE_FILE);
3550 lua_pushstring(L, path);
3551 if (pcall(vis, L, 2, 1) != 0)
3552 return false;
3553 return !lua_isboolean(L, -1) || lua_toboolean(L, -1);
3555 lua_pop(L, 1);
3556 return true;
3559 /***
3560 * File post save.
3561 * Triggered *after* a successful write operation.
3562 * @function file_save_post
3563 * @tparam File file the file which was written
3564 * @tparam string path the absolute path to which it was written, `nil` if standard output
3566 void vis_lua_file_save_post(Vis *vis, File *file, const char *path) {
3567 lua_State *L = vis->lua;
3568 if (!L)
3569 return;
3570 vis_lua_event_get(L, "file_save_post");
3571 if (lua_isfunction(L, -1)) {
3572 obj_ref_new(L, file, VIS_LUA_TYPE_FILE);
3573 lua_pushstring(L, path);
3574 pcall(vis, L, 2, 0);
3576 lua_pop(L, 1);
3579 /***
3580 * File close.
3581 * The last window displaying the file has been closed.
3582 * @function file_close
3583 * @tparam File file the file being closed
3585 void vis_lua_file_close(Vis *vis, File *file) {
3586 debug("event: file-close: %s %p %p\n", file->name ? file->name : "unnamed", (void*)file, (void*)file->text);
3587 lua_State *L = vis->lua;
3588 if (!L)
3589 return;
3590 vis_lua_event_get(L, "file_close");
3591 if (lua_isfunction(L, -1)) {
3592 obj_ref_new(L, file, VIS_LUA_TYPE_FILE);
3593 pcall(vis, L, 1, 0);
3595 obj_ref_free(L, file->marks);
3596 obj_ref_free(L, file->text);
3597 obj_ref_free(L, file);
3598 lua_pop(L, 1);
3601 /***
3602 * Window open.
3603 * A new window has been created.
3604 * @function win_open
3605 * @tparam Window win the window being opened
3607 void vis_lua_win_open(Vis *vis, Win *win) {
3608 debug("event: win-open: %s %p %p\n", win->file->name ? win->file->name : "unnamed", (void*)win, (void*)win->view);
3609 lua_State *L = vis->lua;
3610 if (!L)
3611 return;
3612 vis_lua_event_get(L, "win_open");
3613 if (lua_isfunction(L, -1)) {
3614 obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW);
3615 pcall(vis, L, 1, 0);
3617 lua_pop(L, 1);
3620 /***
3621 * Window close.
3622 * An window is being closed.
3623 * @function win_close
3624 * @tparam Window win the window being closed
3626 void vis_lua_win_close(Vis *vis, Win *win) {
3627 debug("event: win-close: %s %p %p\n", win->file->name ? win->file->name : "unnamed", (void*)win, (void*)win->view);
3628 lua_State *L = vis->lua;
3629 if (!L)
3630 return;
3631 vis_lua_event_get(L, "win_close");
3632 if (lua_isfunction(L, -1)) {
3633 obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW);
3634 pcall(vis, L, 1, 0);
3636 obj_ref_free(L, win->view);
3637 obj_ref_free(L, win);
3638 lua_pop(L, 1);
3642 * Window highlight.
3643 * The window has been redrawn and the syntax highlighting needs to be performed.
3644 * @function win_highlight
3645 * @tparam Window win the window being redrawn
3646 * @see style
3648 void vis_lua_win_highlight(Vis *vis, Win *win) {
3649 lua_State *L = vis->lua;
3650 if (!L)
3651 return;
3652 vis_lua_event_get(L, "win_highlight");
3653 if (lua_isfunction(L, -1)) {
3654 obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW);
3655 pcall(vis, L, 1, 0);
3657 lua_pop(L, 1);
3660 /***
3661 * Window status bar redraw.
3662 * @function win_status
3663 * @tparam Window win the affected window
3664 * @see status
3666 void vis_lua_win_status(Vis *vis, Win *win) {
3667 lua_State *L = vis->lua;
3668 if (!L || win->file->internal) {
3669 window_status_update(vis, win);
3670 return;
3672 vis_lua_event_get(L, "win_status");
3673 if (lua_isfunction(L, -1)) {
3674 obj_ref_new(L, win, VIS_LUA_TYPE_WINDOW);
3675 pcall(vis, L, 1, 0);
3676 } else {
3677 window_status_update(vis, win);
3679 lua_pop(L, 1);
3682 /***
3683 * CSI command received from terminal.
3684 * @function term_csi
3685 * @param List of CSI parameters
3687 void vis_lua_term_csi(Vis *vis, const long *csi) {
3688 lua_State *L = vis->lua;
3689 if (!L)
3690 return;
3691 vis_lua_event_get(L, "term_csi");
3692 if (lua_isfunction(L, -1)) {
3693 int nargs = csi[1];
3694 lua_pushinteger(L, csi[0]);
3695 for (int i = 0; i < nargs; i++)
3696 lua_pushinteger(L, csi[2 + i]);
3697 pcall(vis, L, 1 + nargs, 0);
3699 lua_pop(L, 1);
3701 /***
3702 * The response received from the process started via @{Vis:communicate}.
3703 * @function process_response
3704 * @tparam string name the name of process given to @{Vis:communicate}
3705 * @tparam string response_type can be "STDOUT" or "STDERR" if new output was received in corresponding channel, "SIGNAL" if the process was terminated by a signal or "EXIT" when the process terminated normally
3706 * @tparam int code the exit code number if response_type is "EXIT", or the signal number if response_type is "SIGNAL"
3707 * @tparam string buffer the available content sent by the process
3709 void vis_lua_process_response(Vis *vis, const char *name,
3710 char *buffer, size_t len, ResponseType rtype) {
3711 lua_State *L = vis->lua;
3712 if (!L) {
3713 return;
3715 vis_lua_event_get(L, "process_response");
3716 if (lua_isfunction(L, -1)) {
3717 lua_pushstring(L, name);
3718 switch (rtype) {
3719 case STDOUT: lua_pushstring(L, "STDOUT"); break;
3720 case STDERR: lua_pushstring(L, "STDERR"); break;
3721 case SIGNAL: lua_pushstring(L, "SIGNAL"); break;
3722 case EXIT: lua_pushstring(L, "EXIT"); break;
3724 switch (rtype) {
3725 case EXIT:
3726 case SIGNAL:
3727 lua_pushinteger(L, len);
3728 lua_pushnil(L);
3729 break;
3730 default:
3731 lua_pushnil(L);
3732 lua_pushlstring(L, buffer, len);
3734 pcall(vis, L, 4, 0);
3736 lua_pop(L, 1);
3739 /***
3740 * Emitted immediately before the UI is drawn to the screen.
3741 * Allows last-minute overrides to the styling of UI elements.
3743 * *WARNING:* This is emitted every screen draw!
3744 * Use sparingly and check for `nil` values!
3745 * @function ui_draw
3747 void vis_lua_ui_draw(Vis *vis) {
3748 vis_lua_event_call(vis, "ui_draw");
3751 #endif