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.
30 #include <sys/select.h>
31 #include <sys/types.h>
34 #include <sys/ioctl.h>
40 #include "text-util.h"
41 #include "text-motions.h"
42 #include "text-objects.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
) {
54 if (file
->refcount
> 1) {
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
);
64 file
->prev
->next
= file
->next
;
66 file
->next
->prev
= file
->prev
;
67 if (vis
->files
== file
)
68 vis
->files
= file
->next
;
72 static File
*file_new_text(Vis
*vis
, Text
*text
) {
73 File
*file
= calloc(1, sizeof(*file
));
77 file
->stat
= text_stat(text
);
79 vis
->files
->prev
= file
;
80 file
->next
= vis
->files
;
85 static File
*file_new(Vis
*vis
, const char *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) {
96 Text
*text
= text_load(filename
);
97 if (!text
&& filename
&& errno
== ENOENT
)
98 text
= text_load(NULL
);
102 File
*file
= file_new_text(vis
, text
);
109 file
->name
= strdup(filename
);
110 if (vis
->event
&& vis
->event
->file_open
)
111 vis
->event
->file_open(vis
, file
);
115 static File
*file_new_internal(Vis
*vis
, const char *filename
) {
116 File
*file
= file_new(vis
, filename
);
119 file
->internal
= true;
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
) {
155 for (Win
*other
= vis
->windows
; other
; other
= other
->next
) {
156 if (other
->parent
== win
)
157 other
->parent
= NULL
;
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
);
168 Win
*window_new_file(Vis
*vis
, File
*file
) {
169 Win
*win
= calloc(1, sizeof(Win
));
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
) {
182 view_tabwidth_set(win
->view
, vis
->tabwidth
);
184 vis
->windows
->prev
= win
;
185 win
->next
= vis
->windows
;
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
);
196 bool vis_window_reload(Win
*win
) {
197 const char *name
= win
->file
->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
;
206 file_free(win
->vis
, win
->file
);
209 win
->ui
->reload(win
->ui
, file
);
213 bool vis_window_split(Win
*original
) {
214 Win
*win
= window_new_file(original
->vis
, original
->file
);
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
));
232 void vis_window_next(Vis
*vis
) {
236 vis
->win
= vis
->win
->next
;
238 vis
->win
= vis
->windows
;
239 vis
->ui
->window_focus(vis
->win
->ui
);
242 void vis_window_prev(Vis
*vis
) {
246 vis
->win
= vis
->win
->prev
;
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
);
275 Win
*win
= window_new_file(vis
, file
);
277 file_free(vis
, file
);
281 vis_window_name(win
, filename
);
287 bool vis_window_closable(Win
*win
) {
288 if (!text_modified(win
->file
->text
))
290 return win
->file
->refcount
> 1;
293 void vis_window_close(Win
*win
) {
295 if (vis
->event
&& vis
->event
->win_close
)
296 vis
->event
->win_close(vis
, win
);
297 file_free(vis
, win
->file
);
299 win
->prev
->next
= win
->next
;
301 win
->next
->prev
= win
->prev
;
302 if (vis
->windows
== win
)
303 vis
->windows
= win
->next
;
305 vis
->win
= win
->next
? win
->next
: win
->prev
;
306 if (win
== vis
->message_window
)
307 vis
->message_window
= NULL
;
310 vis
->ui
->window_focus(vis
->win
->ui
);
314 Vis
*vis_new(Ui
*ui
, VisEvent
*event
) {
317 Vis
*vis
= calloc(1, sizeof(Vis
));
321 vis
->ui
->init(vis
->ui
, vis
);
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()))
329 if (!(vis
->command_file
= file_new_internal(vis
, NULL
)))
331 if (!(vis
->search_file
= file_new_internal(vis
, NULL
)))
333 vis
->mode_prev
= vis
->mode
= &vis_modes
[VIS_MODE_NORMAL
];
335 if (event
&& event
->vis_start
)
336 event
->vis_start(vis
);
337 vis
->ui
->start(vis
->ui
);
344 void vis_free(Vis
*vis
) {
347 if (vis
->event
&& vis
->event
->vis_quit
)
348 vis
->event
->vis_quit(vis
);
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
);
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
);
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
) {
408 vis
->actions
= map_new();
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
) {
422 ringbuf_invalidate(win
->jumplist
);
425 void action_do(Vis
*vis
, Action
*a
) {
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
;
446 reg
= &vis
->registers
[win
->file
->internal
? VIS_REG_PROMPT
: VIS_REG_DEFAULT
];
448 OperatorContext c
= {
452 .range
= text_range_empty(),
454 .linewise
= linewise
,
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
)
478 c
.range
.start
= start
;
482 c
.range
= text_range_new(start
, pos
);
487 if (a
->movement
->type
& CHARWISE
)
488 view_cursors_scroll_to(cursor
, pos
);
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
);
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
);
504 c
.range
.start
= c
.range
.end
= pos
;
505 for (int i
= 0; i
< count
; i
++) {
508 r
= a
->textobj
->txt(txt
, pos
);
510 r
= a
->textobj
->vis(vis
, txt
, pos
);
511 if (!text_range_valid(&r
))
513 if (a
->textobj
->type
& OUTER
) {
518 if (a
->textobj
->type
& SPLIT
)
521 c
.range
= text_range_union(&c
.range
, &r
);
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
);
541 size_t pos
= a
->op
->func(vis
, txt
, &c
);
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
)
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
);
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
]) {
581 vis_mode_switch(vis
, VIS_MODE_NORMAL
);
582 vis_cmd(vis
, a
->arg
.s
);
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
);
595 if (a
!= &vis
->action_prev
) {
598 a
->macro
= vis
->macro_operator
;
599 vis
->action_prev
= *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
, ...) {
617 vis
->ui
->die(vis
->ui
, msg
, ap
);
621 const char *vis_keys_next(Vis
*vis
, const char *keys
) {
623 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
624 const char *next
= 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
== '>')
631 const char *start
= keys
+ 1, *end
= start
;
632 while (*end
&& *end
!= '>')
634 if (end
> start
&& end
- start
- 1 < 64 && *end
== '>') {
636 memcpy(key
, start
, end
- start
);
637 key
[end
- start
] = '\0';
638 if (map_get(vis
->actions
, key
))
642 while (!ISUTF8(*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
;
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
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
];
671 for (; mode
&& !binding
&& !prefix
; mode
= mode
->parent
) {
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
);
684 if (binding
) { /* exact match */
685 if (binding
->action
) {
686 end
= (char*)binding
->action
->func(vis
, end
, &binding
->action
->arg
);
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? */
697 } else { /* no keybinding */
698 KeyAction
*action
= NULL
;
699 if (start
[0] == '<' && end
[-1] == '>') {
700 /* test for special editor key command */
703 action
= map_get(vis
->actions
, start
+1);
706 end
= (char*)action
->func(vis
, end
, &action
->arg
);
711 if (!action
&& vis
->mode
->input
)
712 vis
->mode
->input(vis
, start
, end
- start
);
718 buffer_put0(buf
, start
);
719 return input
+ (start
- keys
);
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
;
730 if (pos
< buf
->data
|| pos
> buf
->data
+ buf
->len
)
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
);
739 const char *vis_keys_push(Vis
*vis
, const char *input
) {
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
);
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
);
764 bool vis_signal_handler(Vis
*vis
, int signum
, const siginfo_t
*siginfo
, const void *context
) {
767 for (File
*file
= vis
->files
; file
; file
= file
->next
) {
768 if (text_sigbus(file
->text
, siginfo
->si_addr
))
769 file
->truncated
= true;
773 siglongjmp(vis
->sigbus_jmpbuf
, 1);
776 vis
->cancel_filter
= true;
782 static void vis_args(Vis
*vis
, int argc
, char *argv
[]) {
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]) {
789 end_of_options
= true;
792 vis_die(vis
, "vis %s, compiled " __DATE__
" " __TIME__
"\n", VERSION
);
797 vis_die(vis
, "Unknown command option: %s\n", argv
[i
]);
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
));
805 vis_prompt_cmd(vis
, cmd
);
811 if (!strcmp(argv
[argc
-1], "-")) {
812 if (!vis_window_new(vis
, NULL
))
813 vis_die(vis
, "Can not create empty buffer\n");
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
);
822 vis_die(vis
, "Can not read from stdin\n");
824 int fd
= open("/dev/tty", O_RDONLY
);
826 vis_die(vis
, "Can not reopen stdin\n");
827 dup2(fd
, STDIN_FILENO
);
829 } else if (!vis_window_new(vis
, NULL
)) {
830 vis_die(vis
, "Can not create empty buffer\n");
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
;
843 sigemptyset(&emptyset
);
846 vis
->exit_status
= EXIT_SUCCESS
;
848 sigsetjmp(vis
->sigbus_jmpbuf
, 1);
850 while (vis
->running
) {
853 FD_SET(STDIN_FILENO
, &fds
);
857 for (Win
*next
, *win
= vis
->windows
; win
; win
= next
) {
859 if (win
->file
->truncated
) {
861 name
= strdup(win
->file
->name
);
862 vis_window_close(win
);
866 vis_die(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
868 vis_info_show(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
874 idle
.tv_sec
= vis
->mode
->idle_timeout
;
875 int r
= pselect(1, &fds
, NULL
, NULL
, timeout
, &emptyset
);
876 if (r
== -1 && errno
== EINTR
)
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
)) {
886 vis
->mode
->idle(vis
);
891 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
892 termkey_advisereadable(termkey
);
895 while ((key
= getkey(vis
)))
896 vis_keys_push(vis
, key
);
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
];
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
)
926 vis
->recording
= macro
;
930 bool vis_macro_record_stop(Vis
*vis
) {
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
;
944 bool vis_macro_recording(Vis
*vis
) {
945 return vis
->recording
;
948 static void macro_replay(Vis
*vis
, const Macro
*macro
) {
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
)
960 macro_replay(vis
, macro
);
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
;
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
])
988 for (int i
= 0; i
< count
; i
++) {
990 macro_replay(vis
, macro
);
992 vis
->action_prev
= action_prev
;
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
)
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
)
1030 if (reg
< LENGTH(vis
->registers
))
1031 return register_get(vis
, &vis
->registers
[reg
], len
);
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);
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
++)
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
)
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
);
1078 len
= text_bytes_get(text
, begin
, len
, buf
);
1079 vis_insert_key(win
->vis
, buf
, len
);
1083 void vis_insert_nl(Vis
*vis
) {
1085 switch (text_newline_type(vis
->win
->file
->text
)) {
1086 case TEXT_NEWLINE_CRNL
:
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
) {
1112 Text
*vis_file_text(File
*file
) {
1116 const char *vis_file_name(File
*file
) {