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