vis: implement gn and gN text objects
[vis.git] / vis.c
blob4c5124a9490879258c070c2b7b59f959426459dd
1 /*
2 * Copyright (c) 2014-2015 Marc André Tanner <mat at brain-dump.org>
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 #include <locale.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <string.h>
20 #include <strings.h>
21 #include <signal.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <limits.h>
27 #include <ctype.h>
28 #include <time.h>
29 #include <regex.h>
30 #include <sys/select.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <sys/stat.h>
34 #include <sys/ioctl.h>
35 #include <sys/mman.h>
36 #include <pwd.h>
37 #include <termkey.h>
39 #include "vis.h"
40 #include "text-util.h"
41 #include "text-motions.h"
42 #include "text-objects.h"
43 #include "util.h"
44 #include "vis-core.h"
46 static Macro *macro_get(Vis *vis, enum VisMacro m);
47 static void macro_replay(Vis *vis, const Macro *macro);
49 /** window / file handling */
51 static void file_free(Vis *vis, File *file) {
52 if (!file)
53 return;
54 if (file->refcount > 1) {
55 --file->refcount;
56 return;
58 if (vis->event && vis->event->file_close)
59 vis->event->file_close(vis, file);
60 text_free(file->text);
61 free((char*)file->name);
63 if (file->prev)
64 file->prev->next = file->next;
65 if (file->next)
66 file->next->prev = file->prev;
67 if (vis->files == file)
68 vis->files = file->next;
69 free(file);
72 static File *file_new_text(Vis *vis, Text *text) {
73 File *file = calloc(1, sizeof(*file));
74 if (!file)
75 return NULL;
76 file->text = text;
77 file->stat = text_stat(text);
78 if (vis->files)
79 vis->files->prev = file;
80 file->next = vis->files;
81 vis->files = file;
82 return file;
85 static File *file_new(Vis *vis, const char *filename) {
86 if (filename) {
87 /* try to detect whether the same file is already open in another window
88 * TODO: do this based on inodes */
89 for (File *file = vis->files; file; file = file->next) {
90 if (file->name && strcmp(file->name, filename) == 0) {
91 return file;
96 Text *text = text_load(filename);
97 if (!text && filename && errno == ENOENT)
98 text = text_load(NULL);
99 if (!text)
100 return NULL;
102 File *file = file_new_text(vis, text);
103 if (!file) {
104 text_free(text);
105 return NULL;
108 if (filename)
109 file->name = strdup(filename);
110 if (vis->event && vis->event->file_open)
111 vis->event->file_open(vis, file);
112 return file;
115 static File *file_new_internal(Vis *vis, const char *filename) {
116 File *file = file_new(vis, filename);
117 if (file) {
118 file->refcount = 1;
119 file->internal = true;
121 return file;
124 void vis_window_name(Win *win, const char *filename) {
125 File *file = win->file;
126 if (filename != file->name) {
127 free((char*)file->name);
128 file->name = filename ? strdup(filename) : NULL;
132 static void windows_invalidate(Vis *vis, size_t start, size_t end) {
133 for (Win *win = vis->windows; win; win = win->next) {
134 if (vis->win != win && vis->win->file == win->file) {
135 Filerange view = view_viewport_get(win->view);
136 if ((view.start <= start && start <= view.end) ||
137 (view.start <= end && end <= view.end))
138 view_draw(win->view);
141 view_draw(vis->win->view);
144 void window_selection_save(Win *win) {
145 File *file = win->file;
146 Filerange sel = view_cursors_selection_get(view_cursors(win->view));
147 file->marks[VIS_MARK_SELECTION_START] = text_mark_set(file->text, sel.start);
148 file->marks[VIS_MARK_SELECTION_END] = text_mark_set(file->text, sel.end);
151 static void window_free(Win *win) {
152 if (!win)
153 return;
154 Vis *vis = win->vis;
155 for (Win *other = vis->windows; other; other = other->next) {
156 if (other->parent == win)
157 other->parent = NULL;
159 if (vis->ui)
160 vis->ui->window_free(win->ui);
161 view_free(win->view);
162 for (size_t i = 0; i < LENGTH(win->modes); i++)
163 map_free(win->modes[i].bindings);
164 ringbuf_free(win->jumplist);
165 free(win);
168 Win *window_new_file(Vis *vis, File *file) {
169 Win *win = calloc(1, sizeof(Win));
170 if (!win)
171 return NULL;
172 win->vis = vis;
173 win->file = file;
174 win->jumplist = ringbuf_alloc(31);
175 win->view = view_new(file->text, vis->lua);
176 win->ui = vis->ui->window_new(vis->ui, win->view, file, UI_OPTION_STATUSBAR);
177 if (!win->jumplist || !win->view || !win->ui) {
178 window_free(win);
179 return NULL;
181 file->refcount++;
182 view_tabwidth_set(win->view, vis->tabwidth);
183 if (vis->windows)
184 vis->windows->prev = win;
185 win->next = vis->windows;
186 vis->windows = win;
187 vis->win = win;
188 vis->ui->window_focus(win->ui);
189 for (size_t i = 0; i < LENGTH(win->modes); i++)
190 win->modes[i].parent = &vis_modes[i];
191 if (vis->event && vis->event->win_open)
192 vis->event->win_open(vis, win);
193 return win;
196 bool vis_window_reload(Win *win) {
197 const char *name = win->file->name;
198 if (!name)
199 return false; /* can't reload unsaved file */
200 /* temporarily unset file name, otherwise file_new returns the same File */
201 win->file->name = NULL;
202 File *file = file_new(win->vis, name);
203 win->file->name = name;
204 if (!file)
205 return false;
206 file_free(win->vis, win->file);
207 file->refcount = 1;
208 win->file = file;
209 win->ui->reload(win->ui, file);
210 return true;
213 bool vis_window_split(Win *original) {
214 Win *win = window_new_file(original->vis, original->file);
215 if (!win)
216 return false;
217 for (size_t i = 0; i < LENGTH(win->modes); i++) {
218 if (original->modes[i].bindings)
219 win->modes[i].bindings = map_new();
220 if (win->modes[i].bindings)
221 map_copy(win->modes[i].bindings, original->modes[i].bindings);
223 win->file = original->file;
224 win->file->refcount++;
225 view_syntax_set(win->view, view_syntax_get(original->view));
226 view_options_set(win->view, view_options_get(original->view));
227 view_cursor_to(win->view, view_cursor_get(original->view));
228 vis_draw(win->vis);
229 return true;
232 void vis_window_next(Vis *vis) {
233 Win *sel = vis->win;
234 if (!sel)
235 return;
236 vis->win = vis->win->next;
237 if (!vis->win)
238 vis->win = vis->windows;
239 vis->ui->window_focus(vis->win->ui);
242 void vis_window_prev(Vis *vis) {
243 Win *sel = vis->win;
244 if (!sel)
245 return;
246 vis->win = vis->win->prev;
247 if (!vis->win)
248 for (vis->win = vis->windows; vis->win->next; vis->win = vis->win->next);
249 vis->ui->window_focus(vis->win->ui);
252 void vis_draw(Vis *vis) {
253 vis->ui->draw(vis->ui);
256 void vis_redraw(Vis *vis) {
257 vis->ui->redraw(vis->ui);
260 void vis_update(Vis *vis) {
261 for (Win *win = vis->windows; win; win = win->next)
262 view_update(win->view);
263 view_update(vis->win->view);
264 vis->ui->update(vis->ui);
267 void vis_suspend(Vis *vis) {
268 vis->ui->suspend(vis->ui);
271 bool vis_window_new(Vis *vis, const char *filename) {
272 File *file = file_new(vis, filename);
273 if (!file)
274 return false;
275 Win *win = window_new_file(vis, file);
276 if (!win) {
277 file_free(vis, file);
278 return false;
281 vis_window_name(win, filename);
282 vis_draw(vis);
284 return true;
287 bool vis_window_closable(Win *win) {
288 if (!text_modified(win->file->text))
289 return true;
290 return win->file->refcount > 1;
293 void vis_window_close(Win *win) {
294 Vis *vis = win->vis;
295 if (vis->event && vis->event->win_close)
296 vis->event->win_close(vis, win);
297 file_free(vis, win->file);
298 if (win->prev)
299 win->prev->next = win->next;
300 if (win->next)
301 win->next->prev = win->prev;
302 if (vis->windows == win)
303 vis->windows = win->next;
304 if (vis->win == win)
305 vis->win = win->next ? win->next : win->prev;
306 if (win == vis->message_window)
307 vis->message_window = NULL;
308 window_free(win);
309 if (vis->win)
310 vis->ui->window_focus(vis->win->ui);
311 vis_draw(vis);
314 Vis *vis_new(Ui *ui, VisEvent *event) {
315 if (!ui)
316 return NULL;
317 Vis *vis = calloc(1, sizeof(Vis));
318 if (!vis)
319 return NULL;
320 vis->ui = ui;
321 vis->ui->init(vis->ui, vis);
322 vis->tabwidth = 8;
323 vis->expandtab = false;
324 vis->registers[VIS_REG_BLACKHOLE].type = REGISTER_BLACKHOLE;
325 vis->registers[VIS_REG_CLIPBOARD].type = REGISTER_CLIPBOARD;
326 action_reset(&vis->action);
327 if (!(vis->search_pattern = text_regex_new()))
328 goto err;
329 if (!(vis->command_file = file_new_internal(vis, NULL)))
330 goto err;
331 if (!(vis->search_file = file_new_internal(vis, NULL)))
332 goto err;
333 vis->mode_prev = vis->mode = &vis_modes[VIS_MODE_NORMAL];
334 vis->event = event;
335 if (event && event->vis_start)
336 event->vis_start(vis);
337 vis->ui->start(vis->ui);
338 return vis;
339 err:
340 vis_free(vis);
341 return NULL;
344 void vis_free(Vis *vis) {
345 if (!vis)
346 return;
347 if (vis->event && vis->event->vis_quit)
348 vis->event->vis_quit(vis);
349 vis->event = NULL;
350 while (vis->windows)
351 vis_window_close(vis->windows);
352 file_free(vis, vis->command_file);
353 file_free(vis, vis->search_file);
354 text_regex_free(vis->search_pattern);
355 for (int i = 0; i < LENGTH(vis->registers); i++)
356 register_release(&vis->registers[i]);
357 for (int i = 0; i < LENGTH(vis->macros); i++)
358 macro_release(&vis->macros[i]);
359 vis->ui->free(vis->ui);
360 map_free(vis->cmds);
361 map_free(vis->options);
362 map_free(vis->actions);
363 buffer_release(&vis->input_queue);
364 for (int i = 0; i < VIS_MODE_INVALID; i++)
365 map_free(vis_modes[i].bindings);
366 free(vis);
369 void vis_insert(Vis *vis, size_t pos, const char *data, size_t len) {
370 text_insert(vis->win->file->text, pos, data, len);
371 windows_invalidate(vis, pos, pos + len);
374 void vis_insert_key(Vis *vis, const char *data, size_t len) {
375 for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c)) {
376 size_t pos = view_cursors_pos(c);
377 vis_insert(vis, pos, data, len);
378 view_cursors_scroll_to(c, pos + len);
382 void vis_replace(Vis *vis, size_t pos, const char *data, size_t len) {
383 Text *txt = vis->win->file->text;
384 Iterator it = text_iterator_get(txt, pos);
385 int chars = text_char_count(data, len);
386 for (char c; chars-- > 0 && text_iterator_byte_get(&it, &c) && c != '\r' && c != '\n'; )
387 text_iterator_char_next(&it, NULL);
389 text_delete(txt, pos, it.pos - pos);
390 vis_insert(vis, pos, data, len);
393 void vis_replace_key(Vis *vis, const char *data, size_t len) {
394 for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c)) {
395 size_t pos = view_cursors_pos(c);
396 vis_replace(vis, pos, data, len);
397 view_cursors_scroll_to(c, pos + len);
401 void vis_delete(Vis *vis, size_t pos, size_t len) {
402 text_delete(vis->win->file->text, pos, len);
403 windows_invalidate(vis, pos, pos + len);
406 bool vis_action_register(Vis *vis, KeyAction *action) {
407 if (!vis->actions)
408 vis->actions = map_new();
409 if (!vis->actions)
410 return false;
411 return map_put(vis->actions, action->name, action);
414 static void window_jumplist_add(Win *win, size_t pos) {
415 Mark mark = text_mark_set(win->file->text, pos);
416 if (mark && win->jumplist)
417 ringbuf_add(win->jumplist, mark);
420 static void window_jumplist_invalidate(Win *win) {
421 if (win->jumplist)
422 ringbuf_invalidate(win->jumplist);
425 void action_do(Vis *vis, Action *a) {
426 Win *win = vis->win;
427 Text *txt = win->file->text;
428 View *view = win->view;
430 if (a->op == &vis_operators[VIS_OP_FILTER] && !vis->mode->visual)
431 vis_mode_switch(vis, VIS_MODE_VISUAL_LINE);
433 int count = MAX(a->count, 1);
434 bool repeatable = a->op && !vis->macro_operator;
435 bool multiple_cursors = view_cursors_count(view) > 1;
436 bool linewise = !(a->type & CHARWISE) && (
437 a->type & LINEWISE || (a->movement && a->movement->type & LINEWISE) ||
438 vis->mode == &vis_modes[VIS_MODE_VISUAL_LINE]);
440 for (Cursor *cursor = view_cursors(view), *next; cursor; cursor = next) {
442 next = view_cursors_next(cursor);
443 size_t pos = view_cursors_pos(cursor);
444 Register *reg = multiple_cursors ? view_cursors_register(cursor) : a->reg;
445 if (!reg)
446 reg = &vis->registers[win->file->internal ? VIS_REG_PROMPT : VIS_REG_DEFAULT];
448 OperatorContext c = {
449 .count = count,
450 .pos = pos,
451 .newpos = EPOS,
452 .range = text_range_empty(),
453 .reg = reg,
454 .linewise = linewise,
455 .arg = &a->arg,
458 if (a->movement) {
459 size_t start = pos;
460 for (int i = 0; i < count; i++) {
461 if (a->movement->txt)
462 pos = a->movement->txt(txt, pos);
463 else if (a->movement->cur)
464 pos = a->movement->cur(cursor);
465 else if (a->movement->file)
466 pos = a->movement->file(vis, vis->win->file, pos);
467 else if (a->movement->vis)
468 pos = a->movement->vis(vis, txt, pos);
469 else if (a->movement->view)
470 pos = a->movement->view(vis, view);
471 else if (a->movement->win)
472 pos = a->movement->win(vis, win, pos);
473 if (pos == EPOS || a->movement->type & IDEMPOTENT)
474 break;
477 if (pos == EPOS) {
478 c.range.start = start;
479 c.range.end = start;
480 pos = start;
481 } else {
482 c.range = text_range_new(start, pos);
483 c.newpos = pos;
486 if (!a->op) {
487 if (a->movement->type & CHARWISE)
488 view_cursors_scroll_to(cursor, pos);
489 else
490 view_cursors_to(cursor, pos);
491 if (vis->mode->visual)
492 c.range = view_cursors_selection_get(cursor);
493 if (a->movement->type & JUMP)
494 window_jumplist_add(win, pos);
495 else
496 window_jumplist_invalidate(win);
497 } else if (a->movement->type & INCLUSIVE) {
498 c.range.end = text_char_next(txt, c.range.end);
500 } else if (a->textobj) {
501 if (vis->mode->visual)
502 c.range = view_cursors_selection_get(cursor);
503 else
504 c.range.start = c.range.end = pos;
505 for (int i = 0; i < count; i++) {
506 Filerange r;
507 if (a->textobj->txt)
508 r = a->textobj->txt(txt, pos);
509 else
510 r = a->textobj->vis(vis, txt, pos);
511 if (!text_range_valid(&r))
512 break;
513 if (a->textobj->type & OUTER) {
514 r.start--;
515 r.end++;
518 if (a->textobj->type & SPLIT)
519 c.range = r;
520 else
521 c.range = text_range_union(&c.range, &r);
523 if (i < count - 1)
524 pos = c.range.end + 1;
526 } else if (vis->mode->visual) {
527 c.range = view_cursors_selection_get(cursor);
528 if (!text_range_valid(&c.range))
529 c.range.start = c.range.end = pos;
532 if (linewise && vis->mode != &vis_modes[VIS_MODE_VISUAL])
533 c.range = text_range_linewise(txt, &c.range);
534 if (vis->mode->visual) {
535 view_cursors_selection_set(cursor, &c.range);
536 if (vis->mode == &vis_modes[VIS_MODE_VISUAL] || a->textobj)
537 view_cursors_selection_sync(cursor);
540 if (a->op) {
541 size_t pos = a->op->func(vis, txt, &c);
542 if (pos == EPOS) {
543 view_cursors_dispose(cursor);
544 } else if (pos <= text_size(txt)) {
545 /* moving the cursor will affect the selection.
546 * because we want to be able to later restore
547 * the old selection we update it again before
548 * leaving visual mode.
550 Filerange sel = view_cursors_selection_get(cursor);
551 view_cursors_to(cursor, pos);
552 if (vis->mode->visual) {
553 if (sel.start == EPOS && sel.end == EPOS)
554 sel = c.range;
555 else if (sel.start == EPOS)
556 sel = text_range_new(c.range.start, sel.end);
557 else if (sel.end == EPOS)
558 sel = text_range_new(c.range.start, sel.start);
559 if (vis->mode == &vis_modes[VIS_MODE_VISUAL_LINE])
560 sel = text_range_linewise(txt, &sel);
561 view_cursors_selection_set(cursor, &sel);
567 if (a->op) {
568 /* we do not support visual repeat, still do something resonable */
569 if (vis->mode->visual && !a->movement && !a->textobj)
570 a->movement = &vis_motions[VIS_MOVE_NOP];
572 /* operator implementations must not change the mode,
573 * they might get called multiple times (once for every cursor)
575 if (a->op == &vis_operators[VIS_OP_INSERT] || a->op == &vis_operators[VIS_OP_CHANGE]) {
576 vis_mode_switch(vis, VIS_MODE_INSERT);
577 } else if (a->op == &vis_operators[VIS_OP_REPLACE]) {
578 vis_mode_switch(vis, VIS_MODE_REPLACE);
579 } else if (a->op == &vis_operators[VIS_OP_FILTER]) {
580 if (a->arg.s) {
581 vis_mode_switch(vis, VIS_MODE_NORMAL);
582 vis_cmd(vis, a->arg.s);
583 } else {
584 vis_prompt_show(vis, ":'<,'>!");
586 } else if (vis->mode == &vis_modes[VIS_MODE_OPERATOR_PENDING]) {
587 mode_set(vis, vis->mode_prev);
588 } else if (vis->mode->visual) {
589 vis_mode_switch(vis, VIS_MODE_NORMAL);
591 text_snapshot(txt);
592 vis_draw(vis);
595 if (a != &vis->action_prev) {
596 if (repeatable) {
597 if (!a->macro)
598 a->macro = vis->macro_operator;
599 vis->action_prev = *a;
601 action_reset(a);
605 void action_reset(Action *a) {
606 memset(a, 0, sizeof(*a));
607 a->count = VIS_COUNT_UNKNOWN;
610 void vis_cancel(Vis *vis) {
611 action_reset(&vis->action);
614 void vis_die(Vis *vis, const char *msg, ...) {
615 va_list ap;
616 va_start(ap, msg);
617 vis->ui->die(vis->ui, msg, ap);
618 va_end(ap);
621 const char *vis_keys_next(Vis *vis, const char *keys) {
622 TermKeyKey key;
623 TermKey *termkey = vis->ui->termkey_get(vis->ui);
624 const char *next = NULL;
625 if (!keys)
626 return NULL;
627 /* first try to parse a special key of the form <Key> */
628 if (*keys == '<' && (next = termkey_strpkey(termkey, keys+1, &key, TERMKEY_FORMAT_VIM)) && *next == '>')
629 return next+1;
630 if (*keys == '<') {
631 const char *start = keys + 1, *end = start;
632 while (*end && *end != '>')
633 end++;
634 if (end > start && end - start - 1 < 64 && *end == '>') {
635 char key[64];
636 memcpy(key, start, end - start);
637 key[end - start] = '\0';
638 if (map_get(vis->actions, key))
639 return end + 1;
642 while (!ISUTF8(*keys))
643 keys++;
644 return termkey_strpkey(termkey, keys, &key, TERMKEY_FORMAT_VIM);
647 static const char *vis_keys_raw(Vis *vis, Buffer *buf, const char *input) {
648 char *keys = buf->data, *start = keys, *cur = keys, *end;
649 bool prefix = false;
650 KeyBinding *binding = NULL;
652 while (cur && *cur) {
654 if (!(end = (char*)vis_keys_next(vis, cur)))
655 goto err; // XXX: can't parse key this should never happen
657 char tmp = *end;
658 *end = '\0';
659 prefix = false;
660 binding = NULL;
662 Mode *mode = vis->mode;
663 for (size_t i = 0; i < LENGTH(vis->win->modes); i++) {
664 if (mode == &vis_modes[i]) {
665 if (vis->win->modes[i].bindings)
666 mode = &vis->win->modes[i];
667 break;
671 for (; mode && !binding && !prefix; mode = mode->parent) {
672 if (!mode->bindings)
673 continue;
674 binding = map_get(mode->bindings, start);
675 /* "<" is never treated as a prefix because it is used to denote
676 * special key symbols */
677 if (strcmp(cur, "<"))
678 prefix = !binding && map_contains(mode->bindings, start);
681 *end = tmp;
682 vis->keys = buf;
684 if (binding) { /* exact match */
685 if (binding->action) {
686 end = (char*)binding->action->func(vis, end, &binding->action->arg);
687 if (!end)
688 break;
689 start = cur = end;
690 } else if (binding->alias) {
691 buffer_put0(buf, end);
692 buffer_prepend0(buf, binding->alias);
693 start = cur = buf->data;
695 } else if (prefix) { /* incomplete key binding? */
696 cur = end;
697 } else { /* no keybinding */
698 KeyAction *action = NULL;
699 if (start[0] == '<' && end[-1] == '>') {
700 /* test for special editor key command */
701 char tmp = end[-1];
702 end[-1] = '\0';
703 action = map_get(vis->actions, start+1);
704 end[-1] = tmp;
705 if (action) {
706 end = (char*)action->func(vis, end, &action->arg);
707 if (!end)
708 break;
711 if (!action && vis->mode->input)
712 vis->mode->input(vis, start, end - start);
713 start = cur = end;
717 vis->keys = NULL;
718 buffer_put0(buf, start);
719 return input + (start - keys);
720 err:
721 vis->keys = NULL;
722 buffer_truncate(buf);
723 return input + strlen(input);
726 bool vis_keys_inject(Vis *vis, const char *pos, const char *input) {
727 Buffer *buf = vis->keys;
728 if (!buf)
729 return false;
730 if (pos < buf->data || pos > buf->data + buf->len)
731 return false;
732 size_t off = pos - buf->data;
733 buffer_insert0(buf, off, input);
734 if (vis->macro_operator)
735 macro_append(vis->macro_operator, input);
736 return true;
739 const char *vis_keys_push(Vis *vis, const char *input) {
740 if (!input)
741 return NULL;
743 if (vis->recording)
744 macro_append(vis->recording, input);
745 if (vis->macro_operator)
746 macro_append(vis->macro_operator, input);
748 if (!buffer_append0(&vis->input_queue, input)) {
749 buffer_truncate(&vis->input_queue);
750 return NULL;
753 return vis_keys_raw(vis, &vis->input_queue, input);
756 static const char *getkey(Vis *vis) {
757 const char *key = vis->ui->getkey(vis->ui);
758 if (!key)
759 return NULL;
760 vis_info_hide(vis);
761 return key;
764 bool vis_signal_handler(Vis *vis, int signum, const siginfo_t *siginfo, const void *context) {
765 switch (signum) {
766 case SIGBUS:
767 for (File *file = vis->files; file; file = file->next) {
768 if (text_sigbus(file->text, siginfo->si_addr))
769 file->truncated = true;
771 vis->sigbus = true;
772 if (vis->running)
773 siglongjmp(vis->sigbus_jmpbuf, 1);
774 return true;
775 case SIGINT:
776 vis->cancel_filter = true;
777 return true;
779 return false;
782 static void vis_args(Vis *vis, int argc, char *argv[]) {
783 char *cmd = NULL;
784 bool end_of_options = false;
785 for (int i = 1; i < argc; i++) {
786 if (argv[i][0] == '-' && !end_of_options) {
787 switch (argv[i][1]) {
788 case '-':
789 end_of_options = true;
790 break;
791 case 'v':
792 vis_die(vis, "vis %s, compiled " __DATE__ " " __TIME__ "\n", VERSION);
793 break;
794 case '\0':
795 break;
796 default:
797 vis_die(vis, "Unknown command option: %s\n", argv[i]);
798 break;
800 } else if (argv[i][0] == '+') {
801 cmd = argv[i] + (argv[i][1] == '/' || argv[i][1] == '?');
802 } else if (!vis_window_new(vis, argv[i])) {
803 vis_die(vis, "Can not load `%s': %s\n", argv[i], strerror(errno));
804 } else if (cmd) {
805 vis_prompt_cmd(vis, cmd);
806 cmd = NULL;
810 if (!vis->windows) {
811 if (!strcmp(argv[argc-1], "-")) {
812 if (!vis_window_new(vis, NULL))
813 vis_die(vis, "Can not create empty buffer\n");
814 ssize_t len = 0;
815 char buf[PIPE_BUF];
816 File *file = vis->win->file;
817 Text *txt = file->text;
818 file->is_stdin = true;
819 while ((len = read(STDIN_FILENO, buf, sizeof buf)) > 0)
820 text_insert(txt, text_size(txt), buf, len);
821 if (len == -1)
822 vis_die(vis, "Can not read from stdin\n");
823 text_snapshot(txt);
824 int fd = open("/dev/tty", O_RDONLY);
825 if (fd == -1)
826 vis_die(vis, "Can not reopen stdin\n");
827 dup2(fd, STDIN_FILENO);
828 close(fd);
829 } else if (!vis_window_new(vis, NULL)) {
830 vis_die(vis, "Can not create empty buffer\n");
832 if (cmd)
833 vis_prompt_cmd(vis, cmd);
837 int vis_run(Vis *vis, int argc, char *argv[]) {
838 vis_args(vis, argc, argv);
840 struct timespec idle = { .tv_nsec = 0 }, *timeout = NULL;
842 sigset_t emptyset;
843 sigemptyset(&emptyset);
844 vis_draw(vis);
845 vis->running = true;
846 vis->exit_status = EXIT_SUCCESS;
848 sigsetjmp(vis->sigbus_jmpbuf, 1);
850 while (vis->running) {
851 fd_set fds;
852 FD_ZERO(&fds);
853 FD_SET(STDIN_FILENO, &fds);
855 if (vis->sigbus) {
856 char *name = NULL;
857 for (Win *next, *win = vis->windows; win; win = next) {
858 next = win->next;
859 if (win->file->truncated) {
860 free(name);
861 name = strdup(win->file->name);
862 vis_window_close(win);
865 if (!vis->windows)
866 vis_die(vis, "WARNING: file `%s' truncated!\n", name ? name : "-");
867 else
868 vis_info_show(vis, "WARNING: file `%s' truncated!\n", name ? name : "-");
869 vis->sigbus = false;
870 free(name);
873 vis_update(vis);
874 idle.tv_sec = vis->mode->idle_timeout;
875 int r = pselect(1, &fds, NULL, NULL, timeout, &emptyset);
876 if (r == -1 && errno == EINTR)
877 continue;
879 if (r < 0) {
880 /* TODO save all pending changes to a ~suffixed file */
881 vis_die(vis, "Error in mainloop: %s\n", strerror(errno));
884 if (!FD_ISSET(STDIN_FILENO, &fds)) {
885 if (vis->mode->idle)
886 vis->mode->idle(vis);
887 timeout = NULL;
888 continue;
891 TermKey *termkey = vis->ui->termkey_get(vis->ui);
892 termkey_advisereadable(termkey);
893 const char *key;
895 while ((key = getkey(vis)))
896 vis_keys_push(vis, key);
898 if (vis->mode->idle)
899 timeout = &idle;
901 return vis->exit_status;
904 static Macro *macro_get(Vis *vis, enum VisMacro m) {
905 if (m == VIS_MACRO_LAST_RECORDED)
906 return vis->last_recording;
907 if (m < LENGTH(vis->macros))
908 return &vis->macros[m];
909 return NULL;
912 void macro_operator_record(Vis *vis) {
913 vis->macro_operator = macro_get(vis, VIS_MACRO_OPERATOR);
914 macro_reset(vis->macro_operator);
917 void macro_operator_stop(Vis *vis) {
918 vis->macro_operator = NULL;
921 bool vis_macro_record(Vis *vis, enum VisMacro id) {
922 Macro *macro = macro_get(vis, id);
923 if (vis->recording || !macro)
924 return false;
925 macro_reset(macro);
926 vis->recording = macro;
927 return true;
930 bool vis_macro_record_stop(Vis *vis) {
931 if (!vis->recording)
932 return false;
933 /* XXX: hack to remove last recorded key, otherwise upon replay
934 * we would start another recording */
935 if (vis->recording->len > 1) {
936 vis->recording->len--;
937 vis->recording->data[vis->recording->len-1] = '\0';
939 vis->last_recording = vis->recording;
940 vis->recording = NULL;
941 return true;
944 bool vis_macro_recording(Vis *vis) {
945 return vis->recording;
948 static void macro_replay(Vis *vis, const Macro *macro) {
949 Buffer buf;
950 buffer_init(&buf);
951 buffer_put(&buf, macro->data, macro->len);
952 vis_keys_raw(vis, &buf, macro->data);
953 buffer_release(&buf);
956 bool vis_macro_replay(Vis *vis, enum VisMacro id) {
957 Macro *macro = macro_get(vis, id);
958 if (!macro || macro == vis->recording)
959 return false;
960 macro_replay(vis, macro);
961 return true;
964 void vis_repeat(Vis *vis) {
965 int count = vis->action.count;
966 Macro *macro_operator = macro_get(vis, VIS_MACRO_OPERATOR);
967 Macro *macro_repeat = macro_get(vis, VIS_MACRO_REPEAT);
968 const Macro *macro = vis->action_prev.macro;
969 if (macro == macro_operator) {
970 buffer_put(macro_repeat, macro_operator->data, macro_operator->len);
971 macro = macro_repeat;
972 vis->action_prev.macro = macro;
974 if (count != VIS_COUNT_UNKNOWN)
975 vis->action_prev.count = count;
976 count = vis->action_prev.count;
977 /* for some operators count should be applied only to the macro not the motion */
978 if (vis->action_prev.op == &vis_operators[VIS_OP_INSERT] || vis->action_prev.op == &vis_operators[VIS_OP_REPLACE])
979 vis->action_prev.count = 1;
980 action_do(vis, &vis->action_prev);
981 vis->action_prev.count = count;
982 if (macro) {
983 Mode *mode = vis->mode;
984 Action action_prev = vis->action_prev;
985 count = action_prev.count;
986 if (count < 1 || action_prev.op == &vis_operators[VIS_OP_CHANGE] || action_prev.op == &vis_operators[VIS_OP_FILTER])
987 count = 1;
988 for (int i = 0; i < count; i++) {
989 mode_set(vis, mode);
990 macro_replay(vis, macro);
992 vis->action_prev = action_prev;
994 vis_cancel(vis);
997 void vis_mark_set(Vis *vis, enum VisMark mark, size_t pos) {
998 File *file = vis->win->file;
999 if (mark < LENGTH(file->marks))
1000 file->marks[mark] = text_mark_set(file->text, pos);
1003 int vis_count_get(Vis *vis) {
1004 return vis->action.count;
1007 int vis_count_get_default(Vis *vis, int def) {
1008 if (vis->action.count == VIS_COUNT_UNKNOWN)
1009 return def;
1010 return vis->action.count;
1013 void vis_count_set(Vis *vis, int count) {
1014 vis->action.count = (count >= 0 ? count : VIS_COUNT_UNKNOWN);
1017 void vis_register_set(Vis *vis, enum VisRegister reg) {
1018 if (reg >= VIS_REG_A && reg <= VIS_REG_Z) {
1019 vis->action.reg = &vis->registers[reg - VIS_REG_A];
1020 vis->action.reg->append = true;
1021 } else if (reg < LENGTH(vis->registers)) {
1022 vis->action.reg = &vis->registers[reg];
1023 vis->action.reg->append = false;
1027 const char *vis_register_get(Vis *vis, enum VisRegister reg, size_t *len) {
1028 if (reg >= VIS_REG_A && reg <= VIS_REG_Z)
1029 reg -= VIS_REG_A;
1030 if (reg < LENGTH(vis->registers))
1031 return register_get(vis, &vis->registers[reg], len);
1032 *len = 0;
1033 return NULL;
1036 void vis_exit(Vis *vis, int status) {
1037 vis->running = false;
1038 vis->exit_status = status;
1041 const char *vis_mode_status(Vis *vis) {
1042 return vis->mode->status;
1045 void vis_insert_tab(Vis *vis) {
1046 if (!vis->expandtab) {
1047 vis_insert_key(vis, "\t", 1);
1048 return;
1050 char spaces[9];
1051 int tabwidth = MIN(vis->tabwidth, LENGTH(spaces) - 1);
1052 for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c)) {
1053 size_t pos = view_cursors_pos(c);
1054 /* FIXME: this does not take double width characters into account */
1055 int chars = text_line_char_get(vis->win->file->text, pos);
1056 int count = tabwidth - (chars % tabwidth);
1057 for (int i = 0; i < count; i++)
1058 spaces[i] = ' ';
1059 spaces[count] = '\0';
1060 vis_insert(vis, pos, spaces, count);
1061 view_cursors_scroll_to(c, pos + count);
1065 static void copy_indent_from_previous_line(Win *win) {
1066 View *view = win->view;
1067 Text *text = win->file->text;
1068 size_t pos = view_cursor_get(view);
1069 size_t prev_line = text_line_prev(text, pos);
1070 if (pos == prev_line)
1071 return;
1072 size_t begin = text_line_begin(text, prev_line);
1073 size_t start = text_line_start(text, begin);
1074 size_t len = start-begin;
1075 char *buf = malloc(len);
1076 if (!buf)
1077 return;
1078 len = text_bytes_get(text, begin, len, buf);
1079 vis_insert_key(win->vis, buf, len);
1080 free(buf);
1083 void vis_insert_nl(Vis *vis) {
1084 const char *nl;
1085 switch (text_newline_type(vis->win->file->text)) {
1086 case TEXT_NEWLINE_CRNL:
1087 nl = "\r\n";
1088 break;
1089 default:
1090 nl = "\n";
1091 break;
1094 vis_insert_key(vis, nl, strlen(nl));
1096 if (vis->autoindent)
1097 copy_indent_from_previous_line(vis->win);
1100 Text *vis_text(Vis *vis) {
1101 return vis->win->file->text;
1104 View *vis_view(Vis *vis) {
1105 return vis->win->view;
1108 Win *vis_window(Vis *vis) {
1109 return vis->win;
1112 Text *vis_file_text(File *file) {
1113 return file->text;
1116 const char *vis_file_name(File *file) {
1117 return file->name;