vis
a vi-like editor based on Plan 9's structural regular expressions
git clone https://9o.is/git/vis.git
vis.c
(50271B)
1 #include <stdlib.h>
2 #include <unistd.h>
3 #include <string.h>
4 #include <strings.h>
5 #include <signal.h>
6 #include <stdarg.h>
7 #include <stdio.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <limits.h>
11 #include <ctype.h>
12 #include <time.h>
13 #include <sys/select.h>
14 #include <sys/types.h>
15 #include <sys/wait.h>
16 #include <sys/stat.h>
17 #include <sys/ioctl.h>
18 #include <sys/mman.h>
19 #include <pwd.h>
20 #include <libgen.h>
21 #include <termkey.h>
22
23 #include "vis.h"
24 #include "text-util.h"
25 #include "text-motions.h"
26 #include "text-objects.h"
27 #include "util.h"
28 #include "vis-core.h"
29 #include "sam.h"
30 #include "ui.h"
31 #include "vis-subprocess.h"
32
33
34 static void macro_replay(Vis *vis, const Macro *macro);
35 static void macro_replay_internal(Vis *vis, const Macro *macro);
36 static void vis_keys_push(Vis *vis, const char *input, size_t pos, bool record);
37
38 /** window / file handling */
39
40 static void file_free(Vis *vis, File *file) {
41 if (!file)
42 return;
43 if (file->refcount > 1) {
44 --file->refcount;
45 return;
46 }
47 vis_event_emit(vis, VIS_EVENT_FILE_CLOSE, file);
48 for (size_t i = 0; i < LENGTH(file->marks); i++)
49 mark_release(&file->marks[i]);
50 text_free(file->text);
51 free((char*)file->name);
52
53 if (file->prev)
54 file->prev->next = file->next;
55 if (file->next)
56 file->next->prev = file->prev;
57 if (vis->files == file)
58 vis->files = file->next;
59 free(file);
60 }
61
62 static File *file_new_text(Vis *vis, Text *text) {
63 File *file = calloc(1, sizeof(*file));
64 if (!file)
65 return NULL;
66 file->fd = -1;
67 file->text = text;
68 file->stat = text_stat(text);
69 for (size_t i = 0; i < LENGTH(file->marks); i++)
70 mark_init(&file->marks[i]);
71 if (vis->files)
72 vis->files->prev = file;
73 file->next = vis->files;
74 vis->files = file;
75 return file;
76 }
77
78 char *absolute_path(const char *name) {
79 if (!name)
80 return NULL;
81 char *copy1 = strdup(name);
82 char *copy2 = strdup(name);
83 char *path_absolute = NULL;
84 char path_normalized[PATH_MAX] = "";
85
86 if (!copy1 || !copy2)
87 goto err;
88
89 char *dir = dirname(copy1);
90 char *base = basename(copy2);
91 if (!(path_absolute = realpath(dir, NULL)))
92 goto err;
93 if (strcmp(path_absolute, "/") == 0)
94 path_absolute[0] = '\0';
95
96 snprintf(path_normalized, sizeof(path_normalized), "%s/%s",
97 path_absolute, base);
98 err:
99 free(copy1);
100 free(copy2);
101 free(path_absolute);
102 return path_normalized[0] ? strdup(path_normalized) : NULL;
103 }
104
105 static File *file_new(Vis *vis, const char *name, bool internal) {
106 char *name_absolute = NULL;
107 bool cmp_names = 0;
108 struct stat new;
109
110 if (name) {
111 if (!(name_absolute = absolute_path(name)))
112 return NULL;
113
114 if (stat(name_absolute, &new)) {
115 if (errno != ENOENT) {
116 free(name_absolute);
117 return NULL;
118 }
119 cmp_names = 1;
120 }
121
122 File *existing = NULL;
123 /* try to detect whether the same file is already open in another window */
124 for (File *file = vis->files; file; file = file->next) {
125 if (file->name) {
126 if ((cmp_names && strcmp(file->name, name_absolute) == 0) ||
127 (file->stat.st_dev == new.st_dev && file->stat.st_ino == new.st_ino)) {
128 existing = file;
129 break;
130 }
131 }
132 }
133 if (existing) {
134 free(name_absolute);
135 return existing;
136 }
137 }
138
139 File *file = NULL;
140 Text *text = text_load_method(name, vis->load_method);
141 if (!text && name && errno == ENOENT)
142 text = text_load(NULL);
143 if (!text)
144 goto err;
145 if (!(file = file_new_text(vis, text)))
146 goto err;
147 file->name = name_absolute;
148 file->internal = internal;
149 if (!internal)
150 vis_event_emit(vis, VIS_EVENT_FILE_OPEN, file);
151 return file;
152 err:
153 free(name_absolute);
154 text_free(text);
155 file_free(vis, file);
156 return NULL;
157 }
158
159 static File *file_new_internal(Vis *vis, const char *filename) {
160 File *file = file_new(vis, filename, true);
161 if (file)
162 file->refcount = 1;
163 return file;
164 }
165
166 void file_name_set(File *file, const char *name) {
167 if (name == file->name)
168 return;
169 free((char*)file->name);
170 file->name = absolute_path(name);
171 }
172
173 const char *file_name_get(File *file) {
174 /* TODO: calculate path relative to working directory, cache result */
175 if (!file->name)
176 return NULL;
177 char cwd[PATH_MAX];
178 if (!getcwd(cwd, sizeof cwd))
179 return file->name;
180 const char *path = strstr(file->name, cwd);
181 if (path != file->name)
182 return file->name;
183 size_t cwdlen = strlen(cwd);
184 return file->name[cwdlen] == '/' ? file->name+cwdlen+1 : file->name;
185 }
186
187 void window_selection_save(Win *win) {
188 Vis *vis = win->vis;
189 Array sel = view_selections_get_all(&win->view);
190 vis_mark_set(win, VIS_MARK_SELECTION, &sel);
191 array_release(&sel);
192 vis_jumplist_save(vis);
193 }
194
195
196 static void window_free(Win *win) {
197 if (!win)
198 return;
199 Vis *vis = win->vis;
200 for (Win *other = vis->windows; other; other = other->next) {
201 if (other->parent == win)
202 other->parent = NULL;
203 }
204 ui_window_release(&vis->ui, win);
205 view_free(&win->view);
206 for (size_t i = 0; i < LENGTH(win->modes); i++)
207 map_free(win->modes[i].bindings);
208 marklist_release(&win->jumplist);
209 mark_release(&win->saved_selections);
210 free(win);
211 }
212
213 static void window_draw_colorcolumn(Win *win) {
214 int cc = win->view.colorcolumn;
215 if (cc <= 0)
216 return;
217 size_t lineno = 0;
218 int line_cols = 0; /* Track the number of columns we've passed on each line */
219 bool line_cc_set = false; /* Has the colorcolumn attribute been set for this line yet */
220 int width = win->view.width;
221
222 for (Line *l = win->view.topline; l; l = l->next) {
223 if (l->lineno != lineno) {
224 line_cols = 0;
225 line_cc_set = false;
226 if (!(lineno = l->lineno))
227 break;
228 }
229 if (line_cc_set)
230 continue;
231
232 /* This screen line contains the cell we want to highlight */
233 if (cc <= line_cols + width) {
234 ui_window_style_set(&win->vis->ui, win->id, &l->cells[cc - 1 - line_cols], UI_STYLE_COLOR_COLUMN, false);
235 line_cc_set = true;
236 } else {
237 line_cols += width;
238 }
239 }
240 }
241
242 static void window_draw_cursorline(Win *win) {
243 Vis *vis = win->vis;
244 enum UiOption options = win->options;
245 if (!(options & UI_OPTION_CURSOR_LINE))
246 return;
247 if (vis->mode->visual || vis->win != win)
248 return;
249 if (win->view.selection_count > 1)
250 return;
251
252 int width = win->view.width;
253 Selection *sel = view_selections_primary_get(&win->view);
254 size_t lineno = sel->line->lineno;
255 for (Line *l = win->view.topline; l; l = l->next) {
256 if (l->lineno == lineno) {
257 for (int x = 0; x < width; x++)
258 ui_window_style_set(&vis->ui, win->id, &l->cells[x], UI_STYLE_CURSOR_LINE, true);
259 } else if (l->lineno > lineno) {
260 break;
261 }
262 }
263 }
264
265 static void window_draw_selection(Win *win, Selection *cur) {
266 View *view = &win->view;
267 Filerange sel = view_selections_get(cur);
268 if (!text_range_valid(&sel))
269 return;
270 Line *start_line; int start_col;
271 Line *end_line; int end_col;
272 view_coord_get(view, sel.start, &start_line, NULL, &start_col);
273 view_coord_get(view, sel.end, &end_line, NULL, &end_col);
274 if (!start_line && !end_line)
275 return;
276 if (!start_line) {
277 start_line = view->topline;
278 start_col = 0;
279 }
280 if (!end_line) {
281 end_line = view->lastline;
282 end_col = end_line->width;
283 }
284 for (Line *l = start_line; l != end_line->next; l = l->next) {
285 int col = (l == start_line) ? start_col : 0;
286 int end = (l == end_line) ? end_col : l->width;
287 while (col < end)
288 ui_window_style_set(&win->vis->ui, win->id, &l->cells[col++], UI_STYLE_SELECTION, false);
289 }
290 }
291
292 static void window_draw_cursor_matching(Win *win, Selection *cur) {
293 if (win->vis->mode->visual)
294 return;
295 Line *line_match; int col_match;
296 size_t pos = view_cursors_pos(cur);
297 Filerange limits = VIEW_VIEWPORT_GET(win->view);
298 size_t pos_match = text_bracket_match_symbol(win->file->text, pos, "(){}[]\"'`", &limits);
299 if (pos == pos_match)
300 return;
301 if (!view_coord_get(&win->view, pos_match, &line_match, NULL, &col_match))
302 return;
303 ui_window_style_set(&win->vis->ui, win->id, &line_match->cells[col_match], UI_STYLE_SELECTION, false);
304 }
305
306 static void window_draw_cursor(Win *win, Selection *cur) {
307 if (win->vis->win != win)
308 return;
309 Line *line = cur->line;
310 if (!line)
311 return;
312 Selection *primary = view_selections_primary_get(&win->view);
313 ui_window_style_set(&win->vis->ui, win->id, &line->cells[cur->col], primary == cur ? UI_STYLE_CURSOR_PRIMARY : UI_STYLE_CURSOR, false);
314 window_draw_cursor_matching(win, cur);
315 return;
316 }
317
318 static void window_draw_selections(Win *win) {
319 Filerange viewport = VIEW_VIEWPORT_GET(win->view);
320 Selection *sel = view_selections_primary_get(&win->view);
321 for (Selection *s = view_selections_prev(sel); s; s = view_selections_prev(s)) {
322 window_draw_selection(win, s);
323 size_t pos = view_cursors_pos(s);
324 if (pos < viewport.start)
325 break;
326 window_draw_cursor(win, s);
327 }
328 window_draw_selection(win, sel);
329 window_draw_cursor(win, sel);
330 for (Selection *s = view_selections_next(sel); s; s = view_selections_next(s)) {
331 window_draw_selection(win, s);
332 size_t pos = view_cursors_pos(s);
333 if (pos > viewport.end)
334 break;
335 window_draw_cursor(win, s);
336 }
337 }
338
339 static void window_draw_eof(Win *win) {
340 View *view = &win->view;
341 if (view->width == 0)
342 return;
343 for (Line *l = view->lastline->next; l; l = l->next) {
344 strncpy(l->cells[0].data, view->symbols[SYNTAX_SYMBOL_EOF], sizeof(l->cells[0].data)-1);
345 ui_window_style_set(&win->vis->ui, win->id, l->cells, UI_STYLE_EOF, false);
346 }
347 }
348
349 void vis_window_draw(Win *win) {
350 if (!view_update(&win->view))
351 return;
352 Vis *vis = win->vis;
353 vis_event_emit(vis, VIS_EVENT_WIN_HIGHLIGHT, win);
354
355 window_draw_colorcolumn(win);
356 window_draw_cursorline(win);
357 if (!vis->win || vis->win == win || vis->win->parent == win)
358 window_draw_selections(win);
359 window_draw_eof(win);
360
361 vis_event_emit(vis, VIS_EVENT_WIN_STATUS, win);
362 }
363
364
365 void vis_window_invalidate(Win *win) {
366 for (Win *w = win->vis->windows; w; w = w->next) {
367 if (w->file == win->file)
368 view_draw(&w->view);
369 }
370 }
371
372 Win *window_new_file(Vis *vis, File *file, enum UiOption options) {
373 Win *win = calloc(1, sizeof(Win));
374 if (!win)
375 return NULL;
376 win->vis = vis;
377 win->file = file;
378 if (!view_init(win, file->text)) {
379 free(win);
380 return NULL;
381 }
382 win->expandtab = false;
383 if (!ui_window_init(&vis->ui, win, options)) {
384 window_free(win);
385 return NULL;
386 }
387 marklist_init(&win->jumplist, 32);
388 mark_init(&win->saved_selections);
389 file->refcount++;
390 win_options_set(win, win->options);
391
392 if (vis->windows)
393 vis->windows->prev = win;
394 win->next = vis->windows;
395 vis->windows = win;
396 vis->win = win;
397 ui_window_focus(win);
398 for (size_t i = 0; i < LENGTH(win->modes); i++)
399 win->modes[i].parent = &vis_modes[i];
400 vis_event_emit(vis, VIS_EVENT_WIN_OPEN, win);
401 return win;
402 }
403
404 bool vis_window_reload(Win *win) {
405 const char *name = win->file->name;
406 if (!name)
407 return false; /* can't reload unsaved file */
408 /* temporarily unset file name, otherwise file_new returns the same File */
409 win->file->name = NULL;
410 File *file = file_new(win->vis, name, false);
411 win->file->name = name;
412 if (!file)
413 return false;
414 file_free(win->vis, win->file);
415 file->refcount = 1;
416 win->file = file;
417 view_reload(&win->view, file->text);
418 return true;
419 }
420
421 bool vis_window_change_file(Win *win, const char* filename) {
422 File *file = file_new(win->vis, filename, false);
423 if (!file)
424 return false;
425 file->refcount++;
426 if (win->file)
427 file_free(win->vis, win->file);
428 win->file = file;
429 view_reload(&win->view, file->text);
430 return true;
431 }
432
433 bool vis_window_split(Win *original) {
434 original->vis->ui.doupdate = false;
435 Win *win = window_new_file(original->vis, original->file, UI_OPTION_STATUSBAR);
436 if (!win)
437 return false;
438 for (size_t i = 0; i < LENGTH(win->modes); i++) {
439 if (original->modes[i].bindings)
440 win->modes[i].bindings = map_new();
441 if (win->modes[i].bindings)
442 map_copy(win->modes[i].bindings, original->modes[i].bindings);
443 }
444 win->file = original->file;
445 win_options_set(win, original->options);
446 view_cursors_to(win->view.selection, view_cursor_get(&original->view));
447 win->vis->ui.doupdate = true;
448 return true;
449 }
450
451 void vis_window_focus(Win *win) {
452 if (!win)
453 return;
454 Vis *vis = win->vis;
455 vis->win = win;
456 ui_window_focus(win);
457 }
458
459 void vis_window_next(Vis *vis) {
460 Win *sel = vis->win;
461 if (!sel)
462 return;
463 vis_window_focus(sel->next ? sel->next : vis->windows);
464 vis_window_invalidate(sel);
465 }
466
467 void vis_window_prev(Vis *vis) {
468 Win *sel = vis->win;
469 if (!sel)
470 return;
471 Win *prev = sel->prev;
472 if (!prev)
473 for (prev = vis->windows; prev->next; prev = prev->next);
474 vis_window_focus(prev);
475 vis_window_invalidate(sel);
476 }
477
478 void vis_draw(Vis *vis) {
479 for (Win *win = vis->windows; win; win = win->next)
480 view_draw(&win->view);
481 }
482
483 void vis_redraw(Vis *vis) {
484 ui_redraw(&vis->ui);
485 ui_draw(&vis->ui);
486 }
487
488 bool vis_window_new(Vis *vis, const char *filename) {
489 File *file = file_new(vis, filename, false);
490 if (!file)
491 return false;
492 vis->ui.doupdate = false;
493 Win *win = window_new_file(vis, file, UI_OPTION_STATUSBAR|UI_OPTION_SYMBOL_EOF);
494 if (!win) {
495 file_free(vis, file);
496 return false;
497 }
498 vis->ui.doupdate = true;
499
500 return true;
501 }
502
503 bool vis_window_new_fd(Vis *vis, int fd) {
504 if (fd == -1)
505 return false;
506 if (!vis_window_new(vis, NULL))
507 return false;
508 vis->win->file->fd = fd;
509 return true;
510 }
511
512 bool vis_window_closable(Win *win) {
513 if (!win || !text_modified(win->file->text))
514 return true;
515 return win->file->refcount > 1;
516 }
517
518 void vis_window_swap(Win *a, Win *b) {
519 if (a == b || !a || !b)
520 return;
521 Vis *vis = a->vis;
522 Win *tmp = a->next;
523 a->next = b->next;
524 b->next = tmp;
525 if (a->next)
526 a->next->prev = a;
527 if (b->next)
528 b->next->prev = b;
529 tmp = a->prev;
530 a->prev = b->prev;
531 b->prev = tmp;
532 if (a->prev)
533 a->prev->next = a;
534 if (b->prev)
535 b->prev->next = b;
536 if (vis->windows == a)
537 vis->windows = b;
538 else if (vis->windows == b)
539 vis->windows = a;
540 ui_window_swap(a, b);
541 if (vis->win == a)
542 vis_window_focus(b);
543 else if (vis->win == b)
544 vis_window_focus(a);
545 }
546
547 void vis_window_close(Win *win) {
548 if (!win)
549 return;
550 Vis *vis = win->vis;
551 vis_event_emit(vis, VIS_EVENT_WIN_CLOSE, win);
552 file_free(vis, win->file);
553 if (win->prev)
554 win->prev->next = win->next;
555 if (win->next)
556 win->next->prev = win->prev;
557 if (vis->windows == win)
558 vis->windows = win->next;
559 if (vis->win == win)
560 vis->win = win->next ? win->next : win->prev;
561 if (win == vis->message_window)
562 vis->message_window = NULL;
563 window_free(win);
564 if (vis->win)
565 ui_window_focus(vis->win);
566 vis_draw(vis);
567 }
568
569 Vis *vis_new(void) {
570 Vis *vis = calloc(1, sizeof(Vis));
571 if (!vis)
572 return NULL;
573 vis->exit_status = -1;
574 if (!ui_terminal_init(&vis->ui)) {
575 free(vis);
576 return NULL;
577 }
578 ui_init(&vis->ui, vis);
579 vis->change_colors = true;
580 for (size_t i = 0; i < LENGTH(vis->registers); i++)
581 register_init(&vis->registers[i]);
582 vis->registers[VIS_REG_BLACKHOLE].type = REGISTER_BLACKHOLE;
583 vis->registers[VIS_REG_CLIPBOARD].type = REGISTER_CLIPBOARD;
584 vis->registers[VIS_REG_PRIMARY].type = REGISTER_CLIPBOARD;
585 vis->registers[VIS_REG_NUMBER].type = REGISTER_NUMBER;
586 array_init(&vis->operators);
587 array_init(&vis->motions);
588 array_init(&vis->textobjects);
589 array_init(&vis->bindings);
590 array_init(&vis->actions_user);
591 action_reset(&vis->action);
592 vis->input_queue = (Buffer){0};
593 if (!(vis->command_file = file_new_internal(vis, NULL)))
594 goto err;
595 if (!(vis->search_file = file_new_internal(vis, NULL)))
596 goto err;
597 if (!(vis->error_file = file_new_internal(vis, NULL)))
598 goto err;
599 if (!(vis->actions = map_new()))
600 goto err;
601 if (!(vis->keymap = map_new()))
602 goto err;
603 if (!sam_init(vis))
604 goto err;
605 struct passwd *pw;
606 char *shell = getenv("SHELL");
607 if ((!shell || !*shell) && (pw = getpwuid(getuid())))
608 shell = pw->pw_shell;
609 if (!shell || !*shell)
610 shell = "/bin/sh";
611 if (!(vis->shell = strdup(shell)))
612 goto err;
613 vis->mode_prev = vis->mode = &vis_modes[VIS_MODE_NORMAL];
614 vis_modes[VIS_MODE_INSERT].input = vis_event_mode_insert_input;
615 vis_modes[VIS_MODE_REPLACE].input = vis_event_mode_replace_input;
616 return vis;
617 err:
618 vis_free(vis);
619 return NULL;
620 }
621
622 void vis_free(Vis *vis) {
623 if (!vis)
624 return;
625 while (vis->windows)
626 vis_window_close(vis->windows);
627 vis_event_emit(vis, VIS_EVENT_QUIT);
628 vis_process_waitall(vis);
629 file_free(vis, vis->command_file);
630 file_free(vis, vis->search_file);
631 file_free(vis, vis->error_file);
632 for (int i = 0; i < LENGTH(vis->registers); i++)
633 register_release(&vis->registers[i]);
634 ui_terminal_free(&vis->ui);
635 if (vis->usercmds) {
636 const char *name;
637 while (map_first(vis->usercmds, &name) && vis_cmd_unregister(vis, name));
638 }
639 map_free(vis->usercmds);
640 map_free(vis->cmds);
641 if (vis->options) {
642 const char *name;
643 while (map_first(vis->options, &name) && vis_option_unregister(vis, name));
644 }
645 map_free(vis->options);
646 map_free(vis->actions);
647 map_free(vis->keymap);
648 buffer_release(&vis->input_queue);
649 for (int i = 0; i < VIS_MODE_INVALID; i++)
650 map_free(vis_modes[i].bindings);
651 array_release_full(&vis->operators);
652 array_release_full(&vis->motions);
653 array_release_full(&vis->textobjects);
654 while (vis->bindings.len)
655 vis_binding_free(vis, array_get_ptr(&vis->bindings, 0));
656 array_release(&vis->bindings);
657 while (vis->actions_user.len)
658 vis_action_free(vis, array_get_ptr(&vis->actions_user, 0));
659 array_release(&vis->actions_user);
660 free(vis->shell);
661 free(vis);
662 }
663
664 void vis_insert(Vis *vis, size_t pos, const char *data, size_t len) {
665 Win *win = vis->win;
666 if (!win)
667 return;
668 text_insert(win->file->text, pos, data, len);
669 vis_window_invalidate(win);
670 }
671
672 void vis_insert_key(Vis *vis, const char *data, size_t len) {
673 Win *win = vis->win;
674 if (!win)
675 return;
676 for (Selection *s = view_selections(&win->view); s; s = view_selections_next(s)) {
677 size_t pos = view_cursors_pos(s);
678 vis_insert(vis, pos, data, len);
679 view_cursors_scroll_to(s, pos + len);
680 }
681 }
682
683 void vis_replace(Vis *vis, size_t pos, const char *data, size_t len) {
684 Win *win = vis->win;
685 if (!win)
686 return;
687 Text *txt = win->file->text;
688 Iterator it = text_iterator_get(txt, pos);
689 int chars = text_char_count(data, len);
690 for (char c; chars-- > 0 && text_iterator_byte_get(&it, &c) && c != '\n'; )
691 text_iterator_char_next(&it, NULL);
692
693 text_delete(txt, pos, it.pos - pos);
694 vis_insert(vis, pos, data, len);
695 }
696
697 void vis_replace_key(Vis *vis, const char *data, size_t len) {
698 Win *win = vis->win;
699 if (!win)
700 return;
701 for (Selection *s = view_selections(&win->view); s; s = view_selections_next(s)) {
702 size_t pos = view_cursors_pos(s);
703 vis_replace(vis, pos, data, len);
704 view_cursors_scroll_to(s, pos + len);
705 }
706 }
707
708 void vis_delete(Vis *vis, size_t pos, size_t len) {
709 Win *win = vis->win;
710 if (!win)
711 return;
712 text_delete(win->file->text, pos, len);
713 vis_window_invalidate(win);
714 }
715
716 bool vis_action_register(Vis *vis, const KeyAction *action) {
717 return map_put(vis->actions, action->name, action);
718 }
719
720 bool vis_keymap_add(Vis *vis, const char *key, const char *mapping) {
721 return map_put(vis->keymap, key, mapping);
722 }
723
724 void vis_keymap_disable(Vis *vis) {
725 vis->keymap_disabled = true;
726 }
727
728 void vis_interrupt(Vis *vis) {
729 vis->interrupted = true;
730 }
731
732 bool vis_interrupt_requested(Vis *vis) {
733 return vis->interrupted;
734 }
735
736 void vis_do(Vis *vis) {
737 Win *win = vis->win;
738 if (!win)
739 return;
740 File *file = win->file;
741 Text *txt = file->text;
742 View *view = &win->view;
743 Action *a = &vis->action;
744
745 int count = MAX(a->count, 1);
746 if (a->op == &vis_operators[VIS_OP_MODESWITCH])
747 count = 1; /* count should apply to inserted text not motion */
748 bool repeatable = a->op && !vis->macro_operator && !vis->win->parent;
749 bool multiple_cursors = view->selection_count > 1;
750
751 bool linewise = !(a->type & CHARWISE) && (
752 a->type & LINEWISE || (a->movement && a->movement->type & LINEWISE) ||
753 vis->mode == &vis_modes[VIS_MODE_VISUAL_LINE]);
754
755 Register *reg = a->reg;
756 size_t reg_slot = multiple_cursors ? EPOS : 0;
757 size_t last_reg_slot = reg_slot;
758 if (!reg)
759 reg = &vis->registers[file->internal ? VIS_REG_PROMPT : VIS_REG_DEFAULT];
760 if (a->op == &vis_operators[VIS_OP_PUT_AFTER] && multiple_cursors && vis_register_count(vis, reg) == 1)
761 reg_slot = 0;
762
763 if (vis->mode->visual && a->op)
764 window_selection_save(win);
765
766 for (Selection *sel = view_selections(view), *next; sel; sel = next) {
767 if (vis->interrupted)
768 break;
769
770 next = view_selections_next(sel);
771
772 size_t pos = view_cursors_pos(sel);
773 if (pos == EPOS) {
774 if (!view_selections_dispose(sel))
775 view_cursors_to(sel, 0);
776 continue;
777 }
778
779 OperatorContext c = {
780 .count = count,
781 .pos = pos,
782 .newpos = EPOS,
783 .range = text_range_empty(),
784 .reg = reg,
785 .reg_slot = reg_slot == EPOS ? (size_t)view_selections_number(sel) : reg_slot,
786 .linewise = linewise,
787 .arg = &a->arg,
788 .context = a->op ? a->op->context : NULL,
789 };
790
791 last_reg_slot = c.reg_slot;
792
793 bool err = false;
794 if (a->movement) {
795 size_t start = pos;
796 for (int i = 0; i < count; i++) {
797 size_t pos_prev = pos;
798 if (a->movement->txt)
799 pos = a->movement->txt(txt, pos);
800 else if (a->movement->cur)
801 pos = a->movement->cur(sel);
802 else if (a->movement->file)
803 pos = a->movement->file(vis, file, sel);
804 else if (a->movement->vis)
805 pos = a->movement->vis(vis, txt, pos);
806 else if (a->movement->view)
807 pos = a->movement->view(vis, view);
808 else if (a->movement->win)
809 pos = a->movement->win(vis, win, pos);
810 else if (a->movement->user)
811 pos = a->movement->user(vis, win, a->movement->data, pos);
812 if (pos == EPOS || a->movement->type & IDEMPOTENT || pos == pos_prev) {
813 err = a->movement->type & COUNT_EXACT;
814 break;
815 }
816 }
817
818 if (err) {
819 repeatable = false;
820 continue; // break?
821 }
822
823 if (pos == EPOS) {
824 c.range.start = start;
825 c.range.end = start;
826 pos = start;
827 } else {
828 c.range = text_range_new(start, pos);
829 c.newpos = pos;
830 }
831
832 if (!a->op) {
833 if (a->movement->type & CHARWISE)
834 view_cursors_scroll_to(sel, pos);
835 else
836 view_cursors_to(sel, pos);
837 if (vis->mode->visual)
838 c.range = view_selections_get(sel);
839 } else if (a->movement->type & INCLUSIVE && c.range.end > start) {
840 c.range.end = text_char_next(txt, c.range.end);
841 } else if (linewise && (a->movement->type & LINEWISE_INCLUSIVE)) {
842 c.range.end = text_char_next(txt, c.range.end);
843 }
844 } else if (a->textobj) {
845 if (vis->mode->visual)
846 c.range = view_selections_get(sel);
847 else
848 c.range.start = c.range.end = pos;
849 for (int i = 0; i < count; i++) {
850 Filerange r = text_range_empty();
851 if (a->textobj->txt)
852 r = a->textobj->txt(txt, pos);
853 else if (a->textobj->vis)
854 r = a->textobj->vis(vis, txt, pos);
855 else if (a->textobj->user)
856 r = a->textobj->user(vis, win, a->textobj->data, pos);
857 if (!text_range_valid(&r))
858 break;
859 if (a->textobj->type & TEXTOBJECT_DELIMITED_OUTER) {
860 r.start--;
861 r.end++;
862 } else if (linewise && (a->textobj->type & TEXTOBJECT_DELIMITED_INNER)) {
863 r.start = text_line_next(txt, r.start);
864 r.end = text_line_prev(txt, r.end);
865 }
866
867 if (vis->mode->visual || (i > 0 && !(a->textobj->type & TEXTOBJECT_NON_CONTIGUOUS)))
868 c.range = text_range_union(&c.range, &r);
869 else
870 c.range = r;
871
872 if (i < count - 1) {
873 if (a->textobj->type & TEXTOBJECT_EXTEND_BACKWARD) {
874 pos = c.range.start;
875 if ((a->textobj->type & TEXTOBJECT_DELIMITED_INNER) && pos > 0)
876 pos--;
877 } else {
878 pos = c.range.end;
879 if (a->textobj->type & TEXTOBJECT_DELIMITED_INNER)
880 pos++;
881 }
882 }
883 }
884 } else if (vis->mode->visual) {
885 c.range = view_selections_get(sel);
886 if (!text_range_valid(&c.range))
887 c.range.start = c.range.end = pos;
888 }
889
890 if (linewise && vis->mode != &vis_modes[VIS_MODE_VISUAL])
891 c.range = text_range_linewise(txt, &c.range);
892 if (vis->mode->visual) {
893 view_selections_set(sel, &c.range);
894 sel->anchored = true;
895 }
896
897 if (a->op) {
898 size_t pos = a->op->func(vis, txt, &c);
899 if (pos == EPOS) {
900 view_selections_dispose(sel);
901 } else if (pos <= text_size(txt)) {
902 view_selection_clear(sel);
903 view_cursors_to(sel, pos);
904 }
905 }
906 }
907
908 view_selections_normalize(view);
909 if (a->movement && (a->movement->type & JUMP))
910 vis_jumplist_save(vis);
911
912 if (a->op) {
913
914 if (a->op == &vis_operators[VIS_OP_YANK] ||
915 a->op == &vis_operators[VIS_OP_DELETE] ||
916 a->op == &vis_operators[VIS_OP_CHANGE] ||
917 a->op == &vis_operators[VIS_OP_REPLACE]) {
918 register_resize(reg, last_reg_slot+1);
919 }
920
921 /* we do not support visual repeat, still do something reasonable */
922 if (vis->mode->visual && !a->movement && !a->textobj)
923 a->movement = &vis_motions[VIS_MOVE_NOP];
924
925 /* operator implementations must not change the mode,
926 * they might get called multiple times (once for every cursor)
927 */
928 if (a->op == &vis_operators[VIS_OP_CHANGE]) {
929 vis_mode_switch(vis, VIS_MODE_INSERT);
930 } else if (a->op == &vis_operators[VIS_OP_MODESWITCH]) {
931 vis_mode_switch(vis, a->mode);
932 } else if (vis->mode == &vis_modes[VIS_MODE_OPERATOR_PENDING]) {
933 mode_set(vis, vis->mode_prev);
934 } else if (vis->mode->visual) {
935 vis_mode_switch(vis, VIS_MODE_NORMAL);
936 }
937
938 if (vis->mode == &vis_modes[VIS_MODE_NORMAL])
939 vis_file_snapshot(vis, file);
940 vis_draw(vis);
941 }
942
943 if (a != &vis->action_prev) {
944 if (repeatable) {
945 if (!a->macro)
946 a->macro = vis->macro_operator;
947 vis->action_prev = *a;
948 }
949 action_reset(a);
950 }
951 }
952
953 void action_reset(Action *a) {
954 memset(a, 0, sizeof(*a));
955 a->count = VIS_COUNT_UNKNOWN;
956 }
957
958 void vis_cancel(Vis *vis) {
959 action_reset(&vis->action);
960 }
961
962 void vis_die(Vis *vis, const char *msg, ...) {
963 va_list ap;
964 va_start(ap, msg);
965 ui_die(&vis->ui, msg, ap);
966 va_end(ap);
967 }
968
969 const char *vis_keys_next(Vis *vis, const char *keys) {
970 if (!keys || !*keys)
971 return NULL;
972 TermKeyKey key;
973 TermKey *termkey = vis->ui.termkey;
974 const char *next = NULL;
975 /* first try to parse a special key of the form <Key> */
976 if (*keys == '<' && keys[1] && (next = termkey_strpkey(termkey, keys+1, &key, TERMKEY_FORMAT_VIM)) && *next == '>')
977 return next+1;
978 if (strncmp(keys, "<vis-", 5) == 0) {
979 const char *start = keys + 1, *end = start;
980 while (*end && *end != '>')
981 end++;
982 if (end > start && end - start - 1 < VIS_KEY_LENGTH_MAX && *end == '>') {
983 char key[VIS_KEY_LENGTH_MAX];
984 memcpy(key, start, end - start);
985 key[end - start] = '\0';
986 if (map_get(vis->actions, key))
987 return end + 1;
988 }
989 }
990 if (ISUTF8(*keys))
991 keys++;
992 while (!ISUTF8(*keys))
993 keys++;
994 return keys;
995 }
996
997 long vis_keys_codepoint(Vis *vis, const char *keys) {
998 long codepoint = -1;
999 const char *next;
1000 TermKeyKey key;
1001 TermKey *termkey = vis->ui.termkey;
1002
1003 if (!keys[0])
1004 return -1;
1005 if (keys[0] == '<' && !keys[1])
1006 return '<';
1007
1008 if (keys[0] == '<' && (next = termkey_strpkey(termkey, keys+1, &key, TERMKEY_FORMAT_VIM)) && *next == '>')
1009 codepoint = (key.type == TERMKEY_TYPE_UNICODE) ? key.code.codepoint : -1;
1010 else if ((next = termkey_strpkey(termkey, keys, &key, TERMKEY_FORMAT_VIM)))
1011 codepoint = (key.type == TERMKEY_TYPE_UNICODE) ? key.code.codepoint : -1;
1012
1013 if (codepoint != -1) {
1014 if (key.modifiers == TERMKEY_KEYMOD_CTRL)
1015 codepoint &= 0x1f;
1016 return codepoint;
1017 }
1018
1019 if (!next || key.type != TERMKEY_TYPE_KEYSYM)
1020 return -1;
1021
1022 const int keysym[] = {
1023 TERMKEY_SYM_ENTER, '\n',
1024 TERMKEY_SYM_TAB, '\t',
1025 TERMKEY_SYM_BACKSPACE, '\b',
1026 TERMKEY_SYM_ESCAPE, 0x1b,
1027 TERMKEY_SYM_DELETE, 0x7f,
1028 0,
1029 };
1030
1031 for (const int *k = keysym; k[0]; k += 2) {
1032 if (key.code.sym == k[0])
1033 return k[1];
1034 }
1035
1036 return -1;
1037 }
1038
1039 bool vis_keys_utf8(Vis *vis, const char *keys, char utf8[static UTFmax+1]) {
1040 Rune rune = vis_keys_codepoint(vis, keys);
1041 if (rune == (Rune)-1)
1042 return false;
1043 size_t len = runetochar(utf8, &rune);
1044 utf8[len] = '\0';
1045 return true;
1046 }
1047
1048 typedef struct {
1049 Vis *vis;
1050 size_t len; // length of the prefix
1051 int count; // how many bindings can complete this prefix
1052 bool angle_bracket; // does the prefix end with '<'
1053 } PrefixCompletion;
1054
1055 static bool isprefix(const char *key, void *value, void *data) {
1056 PrefixCompletion *completion = data;
1057 if (!completion->angle_bracket) {
1058 completion->count++;
1059 } else {
1060 const char *start = key + completion->len;
1061 const char *end = vis_keys_next(completion->vis, start);
1062 if (end && start + 1 == end)
1063 completion->count++;
1064 }
1065 return completion->count == 1;
1066 }
1067
1068 static void vis_keys_process(Vis *vis, size_t pos) {
1069 Buffer *buf = &vis->input_queue;
1070 char *keys = buf->data + pos, *start = keys, *cur = keys, *end = keys, *binding_end = keys;;
1071 bool prefix = false;
1072 KeyBinding *binding = NULL;
1073
1074 while (cur && *cur) {
1075
1076 if (!(end = (char*)vis_keys_next(vis, cur))) {
1077 buffer_remove(buf, keys - buf->data, strlen(keys));
1078 return;
1079 }
1080
1081 char tmp = *end;
1082 *end = '\0';
1083 prefix = false;
1084
1085 for (Mode *global_mode = vis->mode; global_mode && !prefix; global_mode = global_mode->parent) {
1086 for (int global = 0; global < 2 && !prefix; global++) {
1087 Mode *mode = (global || !vis->win) ?
1088 global_mode :
1089 &vis->win->modes[global_mode->id];
1090 if (!mode->bindings)
1091 continue;
1092 /* keep track of longest matching binding */
1093 KeyBinding *match = map_get(mode->bindings, start);
1094 if (match && end > binding_end) {
1095 binding = match;
1096 binding_end = end;
1097 }
1098
1099 const Map *pmap = map_prefix(mode->bindings, start);
1100 PrefixCompletion completions = {
1101 .vis = vis,
1102 .len = cur - start,
1103 .count = 0,
1104 .angle_bracket = !strcmp(cur, "<"),
1105 };
1106 map_iterate(pmap, isprefix, &completions);
1107
1108 prefix = (!match && completions.count > 0) ||
1109 ( match && completions.count > 1);
1110 }
1111 }
1112
1113 *end = tmp;
1114
1115 if (prefix) {
1116 /* input so far is ambiguous, wait for more */
1117 cur = end;
1118 end = start;
1119 } else if (binding) { /* exact match */
1120 if (binding->action) {
1121 size_t len = binding_end - start;
1122 strcpy(vis->key_prev, vis->key_current);
1123 strncpy(vis->key_current, start, len);
1124 vis->key_current[len] = '\0';
1125 end = (char*)binding->action->func(vis, binding_end, &binding->action->arg);
1126 if (!end) {
1127 end = start;
1128 break;
1129 }
1130 start = cur = end;
1131 } else if (binding->alias) {
1132 buffer_remove(buf, start - buf->data, binding_end - start);
1133 buffer_insert0(buf, start - buf->data, binding->alias);
1134 cur = end = start;
1135 }
1136 binding = NULL;
1137 binding_end = start;
1138 } else { /* no keybinding */
1139 KeyAction *action = NULL;
1140 if (start[0] == '<' && end[-1] == '>') {
1141 /* test for special editor key command */
1142 char tmp = end[-1];
1143 end[-1] = '\0';
1144 action = map_get(vis->actions, start+1);
1145 end[-1] = tmp;
1146 if (action) {
1147 size_t len = end - start;
1148 strcpy(vis->key_prev, vis->key_current);
1149 strncpy(vis->key_current, start, len);
1150 vis->key_current[len] = '\0';
1151 end = (char*)action->func(vis, end, &action->arg);
1152 if (!end) {
1153 end = start;
1154 break;
1155 }
1156 }
1157 }
1158 if (!action && vis->mode->input) {
1159 end = (char*)vis_keys_next(vis, start);
1160 vis->mode->input(vis, start, end - start);
1161 }
1162 start = cur = end;
1163 }
1164 }
1165
1166 buffer_remove(buf, keys - buf->data, end - keys);
1167 }
1168
1169 void vis_keys_feed(Vis *vis, const char *input) {
1170 if (!input)
1171 return;
1172 Macro macro = {0};
1173 if (!macro_append(¯o, input))
1174 return;
1175 /* use internal function, to keep Lua based tests which use undo points working */
1176 macro_replay_internal(vis, ¯o);
1177 macro_release(¯o);
1178 }
1179
1180 static void vis_keys_push(Vis *vis, const char *input, size_t pos, bool record) {
1181 if (!input)
1182 return;
1183 if (record && vis->recording)
1184 macro_append(vis->recording, input);
1185 if (vis->macro_operator)
1186 macro_append(vis->macro_operator, input);
1187 if (buffer_append0(&vis->input_queue, input))
1188 vis_keys_process(vis, pos);
1189 }
1190
1191 static const char *getkey(Vis *vis) {
1192 TermKeyKey key = { 0 };
1193 if (!ui_getkey(&vis->ui, &key))
1194 return NULL;
1195 ui_info_hide(&vis->ui);
1196 bool use_keymap = vis->mode->id != VIS_MODE_INSERT &&
1197 vis->mode->id != VIS_MODE_REPLACE &&
1198 !vis->keymap_disabled;
1199 vis->keymap_disabled = false;
1200 if (key.type == TERMKEY_TYPE_UNICODE && use_keymap) {
1201 const char *mapped = map_get(vis->keymap, key.utf8);
1202 if (mapped) {
1203 size_t len = strlen(mapped)+1;
1204 if (len <= sizeof(key.utf8))
1205 memcpy(key.utf8, mapped, len);
1206 }
1207 }
1208
1209 TermKey *termkey = vis->ui.termkey;
1210 if (key.type == TERMKEY_TYPE_UNKNOWN_CSI) {
1211 long args[18];
1212 size_t nargs;
1213 unsigned long cmd;
1214 if (termkey_interpret_csi(termkey, &key, &args[2], &nargs, &cmd) == TERMKEY_RES_KEY) {
1215 args[0] = (long)cmd;
1216 args[1] = nargs;
1217 vis_event_emit(vis, VIS_EVENT_TERM_CSI, args);
1218 }
1219 return getkey(vis);
1220 }
1221 termkey_strfkey(termkey, vis->key, sizeof(vis->key), &key, TERMKEY_FORMAT_VIM);
1222 return vis->key;
1223 }
1224
1225 bool vis_signal_handler(Vis *vis, int signum, const siginfo_t *siginfo, const void *context) {
1226 switch (signum) {
1227 case SIGBUS:
1228 for (File *file = vis->files; file; file = file->next) {
1229 if (text_mmaped(file->text, siginfo->si_addr))
1230 file->truncated = true;
1231 }
1232 vis->sigbus = true;
1233 if (vis->running)
1234 siglongjmp(vis->sigbus_jmpbuf, 1);
1235 return true;
1236 case SIGINT:
1237 vis->interrupted = true;
1238 return true;
1239 case SIGCONT:
1240 vis->resume = true;
1241 /* fall through */
1242 case SIGWINCH:
1243 vis->need_resize = true;
1244 return true;
1245 case SIGTERM:
1246 case SIGHUP:
1247 vis->terminate = true;
1248 return true;
1249 }
1250 return false;
1251 }
1252
1253 int vis_run(Vis *vis) {
1254 if (!vis->windows)
1255 return EXIT_SUCCESS;
1256 if (vis->exit_status != -1)
1257 return vis->exit_status;
1258 vis->running = true;
1259
1260 vis_event_emit(vis, VIS_EVENT_START);
1261
1262 struct timespec idle = { .tv_nsec = 0 }, *timeout = NULL;
1263
1264 sigset_t emptyset;
1265 sigemptyset(&emptyset);
1266 vis_draw(vis);
1267 vis->exit_status = EXIT_SUCCESS;
1268
1269 sigsetjmp(vis->sigbus_jmpbuf, 1);
1270
1271 while (vis->running) {
1272 fd_set fds;
1273 FD_ZERO(&fds);
1274 FD_SET(STDIN_FILENO, &fds);
1275
1276 if (vis->sigbus) {
1277 char *name = NULL;
1278 for (Win *next, *win = vis->windows; win; win = next) {
1279 next = win->next;
1280 if (win->file->truncated) {
1281 free(name);
1282 name = strdup(win->file->name);
1283 vis_window_close(win);
1284 }
1285 }
1286 if (!vis->windows)
1287 vis_die(vis, "WARNING: file `%s' truncated!\n", name ? name : "-");
1288 else
1289 vis_info_show(vis, "WARNING: file `%s' truncated!\n", name ? name : "-");
1290 vis->sigbus = false;
1291 free(name);
1292 }
1293
1294 if (vis->terminate)
1295 vis_die(vis, "Killed by SIGTERM\n");
1296 if (vis->interrupted) {
1297 vis->interrupted = false;
1298 vis_keys_push(vis, "<C-c>", 0, true);
1299 continue;
1300 }
1301
1302 if (vis->resume) {
1303 ui_terminal_resume(&vis->ui);
1304 vis->resume = false;
1305 }
1306
1307 if (vis->need_resize) {
1308 ui_resize(&vis->ui);
1309 vis->need_resize = false;
1310 }
1311
1312 ui_draw(&vis->ui);
1313 idle.tv_sec = vis->mode->idle_timeout;
1314 int r = pselect(vis_process_before_tick(&fds) + 1, &fds, NULL, NULL,
1315 timeout, &emptyset);
1316 if (r == -1 && errno == EINTR)
1317 continue;
1318
1319 if (r < 0) {
1320 /* TODO save all pending changes to a ~suffixed file */
1321 vis_die(vis, "Error in mainloop: %s\n", strerror(errno));
1322 }
1323 vis_process_tick(vis, &fds);
1324
1325 if (!FD_ISSET(STDIN_FILENO, &fds)) {
1326 if (vis->mode->idle)
1327 vis->mode->idle(vis);
1328 timeout = NULL;
1329 continue;
1330 }
1331
1332 termkey_advisereadable(vis->ui.termkey);
1333 const char *key;
1334
1335 while ((key = getkey(vis)))
1336 vis_keys_push(vis, key, 0, true);
1337
1338 if (vis->mode->idle)
1339 timeout = &idle;
1340 }
1341 return vis->exit_status;
1342 }
1343
1344 Macro *macro_get(Vis *vis, enum VisRegister id) {
1345 if (id == VIS_MACRO_LAST_RECORDED)
1346 return vis->last_recording;
1347 if (VIS_REG_A <= id && id <= VIS_REG_Z)
1348 id -= VIS_REG_A;
1349 if (id < LENGTH(vis->registers))
1350 return array_get(&vis->registers[id].values, 0);
1351 return NULL;
1352 }
1353
1354 void macro_operator_record(Vis *vis) {
1355 if (vis->macro_operator)
1356 return;
1357 vis->macro_operator = macro_get(vis, VIS_MACRO_OPERATOR);
1358 vis->macro_operator->len = 0;
1359 }
1360
1361 void macro_operator_stop(Vis *vis) {
1362 if (!vis->macro_operator)
1363 return;
1364 Macro *dot = macro_get(vis, VIS_REG_DOT);
1365 buffer_put(dot, vis->macro_operator->data, vis->macro_operator->len);
1366 vis->action_prev.macro = dot;
1367 vis->macro_operator = NULL;
1368 }
1369
1370 bool vis_macro_record(Vis *vis, enum VisRegister id) {
1371 Macro *macro = macro_get(vis, id);
1372 if (vis->recording || !macro)
1373 return false;
1374 if (!(VIS_REG_A <= id && id <= VIS_REG_Z))
1375 macro->len = 0;
1376 vis->recording = macro;
1377 vis_event_emit(vis, VIS_EVENT_WIN_STATUS, vis->win);
1378 return true;
1379 }
1380
1381 bool vis_macro_record_stop(Vis *vis) {
1382 if (!vis->recording)
1383 return false;
1384 /* XXX: hack to remove last recorded key, otherwise upon replay
1385 * we would start another recording */
1386 if (vis->recording->len > 1) {
1387 vis->recording->len--;
1388 vis->recording->data[vis->recording->len-1] = '\0';
1389 }
1390 vis->last_recording = vis->recording;
1391 vis->recording = NULL;
1392 vis_event_emit(vis, VIS_EVENT_WIN_STATUS, vis->win);
1393 return true;
1394 }
1395
1396 bool vis_macro_recording(Vis *vis) {
1397 return vis->recording;
1398 }
1399
1400 static void macro_replay(Vis *vis, const Macro *macro) {
1401 const Macro *replaying = vis->replaying;
1402 vis->replaying = macro;
1403 macro_replay_internal(vis, macro);
1404 vis->replaying = replaying;
1405 }
1406
1407 static void macro_replay_internal(Vis *vis, const Macro *macro) {
1408 size_t pos = buffer_length0(&vis->input_queue);
1409 for (char *key = macro->data, *next; key; key = next) {
1410 char tmp;
1411 next = (char*)vis_keys_next(vis, key);
1412 if (next) {
1413 tmp = *next;
1414 *next = '\0';
1415 }
1416
1417 vis_keys_push(vis, key, pos, false);
1418
1419 if (next)
1420 *next = tmp;
1421 }
1422 }
1423
1424 bool vis_macro_replay(Vis *vis, enum VisRegister id) {
1425 if (id == VIS_REG_SEARCH)
1426 return vis_motion(vis, VIS_MOVE_SEARCH_REPEAT_FORWARD);
1427 if (id == VIS_REG_COMMAND) {
1428 const char *cmd = register_get(vis, &vis->registers[id], NULL);
1429 return vis_cmd(vis, cmd);
1430 }
1431
1432 Macro *macro = macro_get(vis, id);
1433 if (!macro || macro == vis->recording)
1434 return false;
1435 int count = VIS_COUNT_DEFAULT(vis->action.count, 1);
1436 vis_cancel(vis);
1437 for (int i = 0; i < count; i++)
1438 macro_replay(vis, macro);
1439 Win *win = vis->win;
1440 if (win)
1441 vis_file_snapshot(vis, win->file);
1442 return true;
1443 }
1444
1445 void vis_repeat(Vis *vis) {
1446 const Macro *macro = vis->action_prev.macro;
1447 int count = vis->action.count;
1448 if (count != VIS_COUNT_UNKNOWN)
1449 vis->action_prev.count = count;
1450 else
1451 count = vis->action_prev.count;
1452 vis->action = vis->action_prev;
1453 vis_mode_switch(vis, VIS_MODE_OPERATOR_PENDING);
1454 vis_do(vis);
1455 if (macro) {
1456 Mode *mode = vis->mode;
1457 Action action_prev = vis->action_prev;
1458 if (count < 1 || action_prev.op == &vis_operators[VIS_OP_CHANGE])
1459 count = 1;
1460 if (vis->action_prev.op == &vis_operators[VIS_OP_MODESWITCH])
1461 vis->action_prev.count = 1;
1462 for (int i = 0; i < count; i++) {
1463 if (vis->interrupted)
1464 break;
1465 mode_set(vis, mode);
1466 macro_replay(vis, macro);
1467 }
1468 vis->action_prev = action_prev;
1469 }
1470 vis_cancel(vis);
1471 Win *win = vis->win;
1472 if (win)
1473 vis_file_snapshot(vis, win->file);
1474 }
1475
1476 VisCountIterator vis_count_iterator_get(Vis *vis, int def) {
1477 return (VisCountIterator) {
1478 .vis = vis,
1479 .iteration = 0,
1480 .count = VIS_COUNT_DEFAULT(vis->action.count, def),
1481 };
1482 }
1483
1484 VisCountIterator vis_count_iterator_init(Vis *vis, int count) {
1485 return (VisCountIterator) {
1486 .vis = vis,
1487 .iteration = 0,
1488 .count = count,
1489 };
1490 }
1491
1492 bool vis_count_iterator_next(VisCountIterator *it) {
1493 if (it->vis->interrupted)
1494 return false;
1495 return it->iteration++ < it->count;
1496 }
1497
1498 void vis_exit(Vis *vis, int status) {
1499 vis->running = false;
1500 vis->exit_status = status;
1501 }
1502
1503 void vis_insert_tab(Vis *vis) {
1504 Win *win = vis->win;
1505 if (!win)
1506 return;
1507 if (!win->expandtab) {
1508 vis_insert_key(vis, "\t", 1);
1509 return;
1510 }
1511 char spaces[9];
1512 int tabwidth = MIN(vis->win->view.tabwidth, LENGTH(spaces) - 1);
1513 for (Selection *s = view_selections(&win->view); s; s = view_selections_next(s)) {
1514 size_t pos = view_cursors_pos(s);
1515 int width = text_line_width_get(win->file->text, pos);
1516 int count = tabwidth - (width % tabwidth);
1517 for (int i = 0; i < count; i++)
1518 spaces[i] = ' ';
1519 spaces[count] = '\0';
1520 vis_insert(vis, pos, spaces, count);
1521 view_cursors_scroll_to(s, pos + count);
1522 }
1523 }
1524
1525 size_t vis_text_insert_nl(Vis *vis, Text *txt, size_t pos) {
1526 size_t indent_len = 0;
1527 char byte, *indent = NULL;
1528 /* insert second newline at end of file, except if there is already one */
1529 bool eof = pos == text_size(txt);
1530 bool nl2 = eof && !(pos > 0 && text_byte_get(txt, pos-1, &byte) && byte == '\n');
1531
1532 if (vis->autoindent) {
1533 /* copy leading white space of current line */
1534 size_t begin = text_line_begin(txt, pos);
1535 size_t start = text_line_start(txt, begin);
1536 size_t end = text_line_end(txt, start);
1537 if (start > pos)
1538 start = pos;
1539 indent_len = start >= begin ? start-begin : 0;
1540 if (start == end) {
1541 pos = begin;
1542 } else {
1543 indent = malloc(indent_len+1);
1544 if (indent)
1545 indent_len = text_bytes_get(txt, begin, indent_len, indent);
1546 }
1547 }
1548
1549 text_insert(txt, pos, "\n", 1);
1550 if (eof) {
1551 if (nl2)
1552 text_insert(txt, text_size(txt), "\n", 1);
1553 else
1554 pos--; /* place cursor before, not after nl */
1555 }
1556 pos++;
1557
1558 if (indent)
1559 text_insert(txt, pos, indent, indent_len);
1560 free(indent);
1561 return pos + indent_len;
1562 }
1563
1564 void vis_insert_nl(Vis *vis) {
1565 Win *win = vis->win;
1566 if (!win)
1567 return;
1568 Text *txt = win->file->text;
1569 for (Selection *s = view_selections(&win->view); s; s = view_selections_next(s)) {
1570 size_t pos = view_cursors_pos(s);
1571 size_t newpos = vis_text_insert_nl(vis, txt, pos);
1572 /* This is a bit of a hack to fix cursor positioning when
1573 * inserting a new line at the start of the view port.
1574 * It has the effect of resetting the mark used by the view
1575 * code to keep track of the start of the visible region.
1576 */
1577 view_cursors_to(s, pos);
1578 view_cursors_to(s, newpos);
1579 }
1580 vis_window_invalidate(win);
1581 }
1582
1583 Regex *vis_regex(Vis *vis, const char *pattern) {
1584 if (!pattern && !(pattern = register_get(vis, &vis->registers[VIS_REG_SEARCH], NULL)))
1585 return NULL;
1586 Regex *regex = text_regex_new();
1587 if (!regex)
1588 return NULL;
1589 int cflags = REG_EXTENDED|REG_NEWLINE|(REG_ICASE*vis->ignorecase);
1590 if (text_regex_compile(regex, pattern, cflags) != 0) {
1591 text_regex_free(regex);
1592 return NULL;
1593 }
1594 register_put0(vis, &vis->registers[VIS_REG_SEARCH], pattern);
1595 return regex;
1596 }
1597
1598 static int _vis_pipe(Vis *vis, File *file, Filerange *range, const char* buf, const char *argv[],
1599 void *stdout_context, ssize_t (*read_stdout)(void *stdout_context, char *data, size_t len),
1600 void *stderr_context, ssize_t (*read_stderr)(void *stderr_context, char *data, size_t len),
1601 bool fullscreen) {
1602
1603 /* if an invalid range was given, stdin (i.e. key board input) is passed
1604 * through the external command. */
1605 Text *text = file != NULL ? file->text : NULL;
1606 int pin[2], pout[2], perr[2], status = -1;
1607 bool interactive = buf == NULL && (range == NULL || !text_range_valid(range));
1608 Filerange rout = (interactive || buf != NULL) ? text_range_new(0, 0) : *range;
1609
1610 if (pipe(pin) == -1)
1611 return -1;
1612 if (pipe(pout) == -1) {
1613 close(pin[0]);
1614 close(pin[1]);
1615 return -1;
1616 }
1617
1618 if (pipe(perr) == -1) {
1619 close(pin[0]);
1620 close(pin[1]);
1621 close(pout[0]);
1622 close(pout[1]);
1623 return -1;
1624 }
1625
1626 if (fullscreen)
1627 ui_terminal_save(&vis->ui, fullscreen);
1628 pid_t pid = fork();
1629
1630 if (pid == -1) {
1631 close(pin[0]);
1632 close(pin[1]);
1633 close(pout[0]);
1634 close(pout[1]);
1635 close(perr[0]);
1636 close(perr[1]);
1637 vis_info_show(vis, "fork failure: %s", strerror(errno));
1638 return -1;
1639 } else if (pid == 0) { /* child i.e filter */
1640 sigset_t sigterm_mask;
1641 sigemptyset(&sigterm_mask);
1642 sigaddset(&sigterm_mask, SIGTERM);
1643 if (sigprocmask(SIG_UNBLOCK, &sigterm_mask, NULL) == -1) {
1644 fprintf(stderr, "failed to reset signal mask");
1645 exit(EXIT_FAILURE);
1646 }
1647
1648 int null = open("/dev/null", O_RDWR);
1649 if (null == -1) {
1650 fprintf(stderr, "failed to open /dev/null");
1651 exit(EXIT_FAILURE);
1652 }
1653
1654 if (!interactive) {
1655 /* If we have nothing to write, let stdin point to
1656 * /dev/null instead of a pipe which is immediately
1657 * closed. Some programs behave differently when used
1658 * in a pipeline.
1659 */
1660 if (range && text_range_size(range) == 0)
1661 dup2(null, STDIN_FILENO);
1662 else
1663 dup2(pin[0], STDIN_FILENO);
1664 }
1665
1666 close(pin[0]);
1667 close(pin[1]);
1668 if (interactive) {
1669 dup2(STDERR_FILENO, STDOUT_FILENO);
1670 /* For some reason the first byte written by the
1671 * interactive application is not being displayed.
1672 * It probably has something to do with the terminal
1673 * state change. By writing a dummy byte ourself we
1674 * ensure that the complete output is visible.
1675 */
1676 while(write(STDOUT_FILENO, " ", 1) == -1 && errno == EINTR);
1677 } else if (read_stdout) {
1678 dup2(pout[1], STDOUT_FILENO);
1679 } else {
1680 dup2(null, STDOUT_FILENO);
1681 }
1682 close(pout[1]);
1683 close(pout[0]);
1684 if (!interactive) {
1685 if (read_stderr)
1686 dup2(perr[1], STDERR_FILENO);
1687 else
1688 dup2(null, STDERR_FILENO);
1689 }
1690 close(perr[0]);
1691 close(perr[1]);
1692 close(null);
1693
1694 if (file != NULL && file->name) {
1695 char *name = strrchr(file->name, '/');
1696 setenv("vis_filepath", file->name, 1);
1697 setenv("vis_filename", name ? name+1 : file->name, 1);
1698 }
1699
1700 if (!argv[1])
1701 execlp(vis->shell, vis->shell, "-c", argv[0], (char*)NULL);
1702 else
1703 execvp(argv[0], (char* const*)argv);
1704 fprintf(stderr, "exec failure: %s", strerror(errno));
1705 exit(EXIT_FAILURE);
1706 }
1707
1708 vis->interrupted = false;
1709
1710 close(pin[0]);
1711 close(pout[1]);
1712 close(perr[1]);
1713
1714 if (fcntl(pout[0], F_SETFL, O_NONBLOCK) == -1 ||
1715 fcntl(perr[0], F_SETFL, O_NONBLOCK) == -1)
1716 goto err;
1717
1718 fd_set rfds, wfds;
1719
1720 do {
1721 if (vis->interrupted) {
1722 kill(0, SIGTERM);
1723 break;
1724 }
1725
1726 FD_ZERO(&rfds);
1727 FD_ZERO(&wfds);
1728 if (pin[1] != -1)
1729 FD_SET(pin[1], &wfds);
1730 if (pout[0] != -1)
1731 FD_SET(pout[0], &rfds);
1732 if (perr[0] != -1)
1733 FD_SET(perr[0], &rfds);
1734
1735 if (select(FD_SETSIZE, &rfds, &wfds, NULL, NULL) == -1) {
1736 if (errno == EINTR)
1737 continue;
1738 vis_info_show(vis, "Select failure");
1739 break;
1740 }
1741
1742 if (pin[1] != -1 && FD_ISSET(pin[1], &wfds)) {
1743 ssize_t written = 0;
1744 Filerange junk = rout;
1745 if (text_range_size(&rout)) {
1746 if (junk.end > junk.start + PIPE_BUF)
1747 junk.end = junk.start + PIPE_BUF;
1748 written = text_write_range(text, &junk, pin[1]);
1749 if (written > 0) {
1750 rout.start += written;
1751 if (text_range_size(&rout) == 0) {
1752 close(pin[1]);
1753 pin[1] = -1;
1754 }
1755 }
1756 } else if (buf != NULL) {
1757 size_t len = strlen(buf);
1758 if (len > 0) {
1759 if (len > PIPE_BUF)
1760 len = PIPE_BUF;
1761
1762 written = write_all(pin[1], buf, len);
1763 if (written > 0) {
1764 buf += written;
1765 }
1766 }
1767 }
1768
1769 if (written <= 0) {
1770 close(pin[1]);
1771 pin[1] = -1;
1772 if (written == -1)
1773 vis_info_show(vis, "Error writing to external command");
1774 }
1775 }
1776
1777 if (pout[0] != -1 && FD_ISSET(pout[0], &rfds)) {
1778 char buf[BUFSIZ];
1779 ssize_t len = read(pout[0], buf, sizeof buf);
1780 if (len > 0) {
1781 if (read_stdout)
1782 (*read_stdout)(stdout_context, buf, len);
1783 } else if (len == 0) {
1784 close(pout[0]);
1785 pout[0] = -1;
1786 } else if (errno != EINTR && errno != EWOULDBLOCK) {
1787 vis_info_show(vis, "Error reading from filter stdout");
1788 close(pout[0]);
1789 pout[0] = -1;
1790 }
1791 }
1792
1793 if (perr[0] != -1 && FD_ISSET(perr[0], &rfds)) {
1794 char buf[BUFSIZ];
1795 ssize_t len = read(perr[0], buf, sizeof buf);
1796 if (len > 0) {
1797 if (read_stderr)
1798 (*read_stderr)(stderr_context, buf, len);
1799 } else if (len == 0) {
1800 close(perr[0]);
1801 perr[0] = -1;
1802 } else if (errno != EINTR && errno != EWOULDBLOCK) {
1803 vis_info_show(vis, "Error reading from filter stderr");
1804 close(perr[0]);
1805 perr[0] = -1;
1806 }
1807 }
1808
1809 } while (pin[1] != -1 || pout[0] != -1 || perr[0] != -1);
1810
1811 err:
1812 if (pin[1] != -1)
1813 close(pin[1]);
1814 if (pout[0] != -1)
1815 close(pout[0]);
1816 if (perr[0] != -1)
1817 close(perr[0]);
1818
1819 for (;;) {
1820 if (vis->interrupted)
1821 kill(0, SIGTERM);
1822 pid_t died = waitpid(pid, &status, 0);
1823 if ((died == -1 && errno == ECHILD) || pid == died)
1824 break;
1825 }
1826
1827 /* clear any pending SIGTERM */
1828 struct sigaction sigterm_ignore, sigterm_old;
1829 sigterm_ignore.sa_handler = SIG_IGN;
1830 sigterm_ignore.sa_flags = 0;
1831 sigemptyset(&sigterm_ignore.sa_mask);
1832
1833 sigaction(SIGTERM, &sigterm_ignore, &sigterm_old);
1834 sigaction(SIGTERM, &sigterm_old, NULL);
1835
1836 vis->interrupted = false;
1837 if (fullscreen)
1838 ui_terminal_restore(&vis->ui);
1839
1840 if (WIFEXITED(status))
1841 return WEXITSTATUS(status);
1842
1843 return -1;
1844 }
1845
1846 int vis_pipe(Vis *vis, File *file, Filerange *range, const char *argv[],
1847 void *stdout_context, ssize_t (*read_stdout)(void *stdout_context, char *data, size_t len),
1848 void *stderr_context, ssize_t (*read_stderr)(void *stderr_context, char *data, size_t len),
1849 bool fullscreen) {
1850 return _vis_pipe(vis, file, range, NULL, argv, stdout_context, read_stdout, stderr_context, read_stderr, fullscreen);
1851 }
1852
1853 int vis_pipe_buf(Vis *vis, const char* buf, const char *argv[],
1854 void *stdout_context, ssize_t (*read_stdout)(void *stdout_context, char *data, size_t len),
1855 void *stderr_context, ssize_t (*read_stderr)(void *stderr_context, char *data, size_t len),
1856 bool fullscreen) {
1857 return _vis_pipe(vis, NULL, NULL, buf, argv, stdout_context, read_stdout, stderr_context, read_stderr, fullscreen);
1858 }
1859
1860 static int _vis_pipe_collect(Vis *vis, File *file, Filerange *range, const char* buf, const char *argv[], char **out, char **err, bool fullscreen) {
1861 Buffer bufout = {0}, buferr = {0};
1862 int status = _vis_pipe(vis, file, range, buf, argv,
1863 &bufout, out ? read_into_buffer : NULL,
1864 &buferr, err ? read_into_buffer : NULL,
1865 fullscreen);
1866 buffer_terminate(&bufout);
1867 buffer_terminate(&buferr);
1868 if (out) *out = bufout.data;
1869 if (err) *err = buferr.data;
1870 return status;
1871 }
1872
1873 int vis_pipe_collect(Vis *vis, File *file, Filerange *range, const char *argv[], char **out, char **err, bool fullscreen) {
1874 return _vis_pipe_collect(vis, file, range, NULL, argv, out, err, fullscreen);
1875 }
1876
1877 int vis_pipe_buf_collect(Vis *vis, const char* buf, const char *argv[], char **out, char **err, bool fullscreen) {
1878 return _vis_pipe_collect(vis, NULL, NULL, buf, argv, out, err, fullscreen);
1879 }
1880
1881 bool vis_cmd(Vis *vis, const char *cmdline) {
1882 if (!cmdline)
1883 return true;
1884 while (*cmdline == ':')
1885 cmdline++;
1886 char *line = strdup(cmdline);
1887 if (!line)
1888 return false;
1889
1890 size_t len = strlen(line);
1891 while (len > 0 && isspace((unsigned char)line[len-1]))
1892 len--;
1893 line[len] = '\0';
1894
1895 enum SamError err = sam_cmd(vis, line);
1896 if (err != SAM_ERR_OK)
1897 vis_info_show(vis, "%s", sam_error(err));
1898 free(line);
1899 return err == SAM_ERR_OK;
1900 }
1901
1902 void vis_file_snapshot(Vis *vis, File *file) {
1903 if (!vis->replaying)
1904 text_snapshot(file->text);
1905 }
1906
1907 Text *vis_text(Vis *vis) {
1908 Win *win = vis->win;
1909 return win ? win->file->text : NULL;
1910 }
1911
1912 View *vis_view(Vis *vis) {
1913 Win *win = vis->win;
1914 return win ? &win->view : NULL;
1915 }