15 #include <sys/select.h>
16 #include <sys/types.h>
19 #include <sys/ioctl.h>
25 #include "text-util.h"
26 #include "text-motions.h"
27 #include "text-objects.h"
31 /* enable large file optimization for files larger than: */
32 #define LARGE_FILE (1 << 25)
34 static Macro
*macro_get(Vis
*vis
, enum VisRegister
);
35 static void macro_replay(Vis
*vis
, const Macro
*macro
);
37 /** window / file handling */
39 static void file_free(Vis
*vis
, File
*file
) {
42 if (file
->refcount
> 1) {
46 if (vis
->event
&& vis
->event
->file_close
)
47 vis
->event
->file_close(vis
, file
);
48 text_free(file
->text
);
49 free((char*)file
->name
);
52 file
->prev
->next
= file
->next
;
54 file
->next
->prev
= file
->prev
;
55 if (vis
->files
== file
)
56 vis
->files
= file
->next
;
60 static File
*file_new_text(Vis
*vis
, Text
*text
) {
61 File
*file
= calloc(1, sizeof(*file
));
65 file
->stat
= text_stat(text
);
67 vis
->files
->prev
= file
;
68 file
->next
= vis
->files
;
73 static File
*file_new(Vis
*vis
, const char *filename
) {
75 /* try to detect whether the same file is already open in another window
76 * TODO: do this based on inodes */
77 for (File
*file
= vis
->files
; file
; file
= file
->next
) {
78 if (file
->name
&& strcmp(file
->name
, filename
) == 0) {
84 Text
*text
= text_load(filename
);
85 if (!text
&& filename
&& errno
== ENOENT
)
86 text
= text_load(NULL
);
90 File
*file
= file_new_text(vis
, text
);
97 file
->name
= strdup(filename
);
98 if (vis
->event
&& vis
->event
->file_open
)
99 vis
->event
->file_open(vis
, file
);
103 static File
*file_new_internal(Vis
*vis
, const char *filename
) {
104 File
*file
= file_new(vis
, filename
);
107 file
->internal
= true;
112 void vis_window_name(Win
*win
, const char *filename
) {
113 File
*file
= win
->file
;
114 if (filename
!= file
->name
) {
115 free((char*)file
->name
);
116 file
->name
= filename
? strdup(filename
) : NULL
;
120 static void windows_invalidate(Vis
*vis
, size_t start
, size_t end
) {
121 for (Win
*win
= vis
->windows
; win
; win
= win
->next
) {
122 if (vis
->win
!= win
&& vis
->win
->file
== win
->file
) {
123 Filerange view
= view_viewport_get(win
->view
);
124 if ((view
.start
<= start
&& start
<= view
.end
) ||
125 (view
.start
<= end
&& end
<= view
.end
))
126 view_draw(win
->view
);
129 view_draw(vis
->win
->view
);
132 void window_selection_save(Win
*win
) {
133 File
*file
= win
->file
;
134 Filerange sel
= view_cursors_selection_get(view_cursors(win
->view
));
135 file
->marks
[VIS_MARK_SELECTION_START
] = text_mark_set(file
->text
, sel
.start
);
136 file
->marks
[VIS_MARK_SELECTION_END
] = text_mark_set(file
->text
, sel
.end
);
139 static void window_free(Win
*win
) {
143 for (Win
*other
= vis
->windows
; other
; other
= other
->next
) {
144 if (other
->parent
== win
)
145 other
->parent
= NULL
;
148 vis
->ui
->window_free(win
->ui
);
149 view_free(win
->view
);
150 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++)
151 map_free(win
->modes
[i
].bindings
);
152 ringbuf_free(win
->jumplist
);
156 Win
*window_new_file(Vis
*vis
, File
*file
) {
157 Win
*win
= calloc(1, sizeof(Win
));
162 win
->jumplist
= ringbuf_alloc(31);
163 win
->view
= view_new(file
->text
, vis
->lua
);
164 win
->ui
= vis
->ui
->window_new(vis
->ui
, win
->view
, file
, UI_OPTION_STATUSBAR
);
165 if (!win
->jumplist
|| !win
->view
|| !win
->ui
) {
170 view_tabwidth_set(win
->view
, vis
->tabwidth
);
172 if (text_size(file
->text
) > LARGE_FILE
) {
173 enum UiOption opt
= view_options_get(win
->view
);
174 opt
|= UI_OPTION_LARGE_FILE
;
175 opt
&= ~UI_OPTION_LINE_NUMBERS_ABSOLUTE
;
176 view_options_set(win
->view
, opt
);
180 vis
->windows
->prev
= win
;
181 win
->next
= vis
->windows
;
184 vis
->ui
->window_focus(win
->ui
);
185 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++)
186 win
->modes
[i
].parent
= &vis_modes
[i
];
187 if (vis
->event
&& vis
->event
->win_open
)
188 vis
->event
->win_open(vis
, win
);
192 bool vis_window_reload(Win
*win
) {
193 const char *name
= win
->file
->name
;
195 return false; /* can't reload unsaved file */
196 /* temporarily unset file name, otherwise file_new returns the same File */
197 win
->file
->name
= NULL
;
198 File
*file
= file_new(win
->vis
, name
);
199 win
->file
->name
= name
;
202 file_free(win
->vis
, win
->file
);
205 win
->ui
->reload(win
->ui
, file
);
209 bool vis_window_split(Win
*original
) {
210 Win
*win
= window_new_file(original
->vis
, original
->file
);
213 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++) {
214 if (original
->modes
[i
].bindings
)
215 win
->modes
[i
].bindings
= map_new();
216 if (win
->modes
[i
].bindings
)
217 map_copy(win
->modes
[i
].bindings
, original
->modes
[i
].bindings
);
219 win
->file
= original
->file
;
220 win
->file
->refcount
++;
221 view_syntax_set(win
->view
, view_syntax_get(original
->view
));
222 view_options_set(win
->view
, view_options_get(original
->view
));
223 view_cursor_to(win
->view
, view_cursor_get(original
->view
));
228 void vis_window_next(Vis
*vis
) {
232 vis
->win
= vis
->win
->next
;
234 vis
->win
= vis
->windows
;
235 vis
->ui
->window_focus(vis
->win
->ui
);
238 void vis_window_prev(Vis
*vis
) {
242 vis
->win
= vis
->win
->prev
;
244 for (vis
->win
= vis
->windows
; vis
->win
->next
; vis
->win
= vis
->win
->next
);
245 vis
->ui
->window_focus(vis
->win
->ui
);
248 void vis_draw(Vis
*vis
) {
249 vis
->ui
->draw(vis
->ui
);
252 void vis_redraw(Vis
*vis
) {
253 vis
->ui
->redraw(vis
->ui
);
256 void vis_update(Vis
*vis
) {
257 for (Win
*win
= vis
->windows
; win
; win
= win
->next
)
258 view_update(win
->view
);
259 view_update(vis
->win
->view
);
260 vis
->ui
->update(vis
->ui
);
263 void vis_suspend(Vis
*vis
) {
264 vis
->ui
->suspend(vis
->ui
);
267 bool vis_window_new(Vis
*vis
, const char *filename
) {
268 File
*file
= file_new(vis
, filename
);
271 Win
*win
= window_new_file(vis
, file
);
273 file_free(vis
, file
);
277 vis_window_name(win
, filename
);
283 bool vis_window_closable(Win
*win
) {
284 if (!text_modified(win
->file
->text
))
286 return win
->file
->refcount
> 1;
289 void vis_window_close(Win
*win
) {
291 if (vis
->event
&& vis
->event
->win_close
)
292 vis
->event
->win_close(vis
, win
);
293 file_free(vis
, win
->file
);
295 win
->prev
->next
= win
->next
;
297 win
->next
->prev
= win
->prev
;
298 if (vis
->windows
== win
)
299 vis
->windows
= win
->next
;
301 vis
->win
= win
->next
? win
->next
: win
->prev
;
302 if (win
== vis
->message_window
)
303 vis
->message_window
= NULL
;
306 vis
->ui
->window_focus(vis
->win
->ui
);
310 Vis
*vis_new(Ui
*ui
, VisEvent
*event
) {
313 Vis
*vis
= calloc(1, sizeof(Vis
));
317 vis
->ui
->init(vis
->ui
, vis
);
319 vis
->expandtab
= false;
320 vis
->registers
[VIS_REG_BLACKHOLE
].type
= REGISTER_BLACKHOLE
;
321 vis
->registers
[VIS_REG_CLIPBOARD
].type
= REGISTER_CLIPBOARD
;
322 action_reset(&vis
->action
);
323 if (!(vis
->command_file
= file_new_internal(vis
, NULL
)))
325 if (!(vis
->search_file
= file_new_internal(vis
, NULL
)))
327 if (!(vis
->keymap
= map_new()))
329 vis
->mode_prev
= vis
->mode
= &vis_modes
[VIS_MODE_NORMAL
];
331 if (event
&& event
->vis_start
)
332 event
->vis_start(vis
);
333 vis
->ui
->start(vis
->ui
);
340 void vis_free(Vis
*vis
) {
343 if (vis
->event
&& vis
->event
->vis_quit
)
344 vis
->event
->vis_quit(vis
);
347 vis_window_close(vis
->windows
);
348 file_free(vis
, vis
->command_file
);
349 file_free(vis
, vis
->search_file
);
350 for (int i
= 0; i
< LENGTH(vis
->registers
); i
++)
351 register_release(&vis
->registers
[i
]);
352 vis
->ui
->free(vis
->ui
);
354 map_free(vis
->options
);
355 map_free(vis
->actions
);
356 map_free(vis
->keymap
);
357 buffer_release(&vis
->input_queue
);
358 for (int i
= 0; i
< VIS_MODE_INVALID
; i
++)
359 map_free(vis_modes
[i
].bindings
);
360 array_release_full(&vis
->motions
);
361 array_release_full(&vis
->textobjects
);
365 void vis_insert(Vis
*vis
, size_t pos
, const char *data
, size_t len
) {
366 text_insert(vis
->win
->file
->text
, pos
, data
, len
);
367 windows_invalidate(vis
, pos
, pos
+ len
);
370 void vis_insert_key(Vis
*vis
, const char *data
, size_t len
) {
371 for (Cursor
*c
= view_cursors(vis
->win
->view
); c
; c
= view_cursors_next(c
)) {
372 size_t pos
= view_cursors_pos(c
);
373 vis_insert(vis
, pos
, data
, len
);
374 view_cursors_scroll_to(c
, pos
+ len
);
378 void vis_replace(Vis
*vis
, size_t pos
, const char *data
, size_t len
) {
379 Text
*txt
= vis
->win
->file
->text
;
380 Iterator it
= text_iterator_get(txt
, pos
);
381 int chars
= text_char_count(data
, len
);
382 for (char c
; chars
-- > 0 && text_iterator_byte_get(&it
, &c
) && c
!= '\r' && c
!= '\n'; )
383 text_iterator_char_next(&it
, NULL
);
385 text_delete(txt
, pos
, it
.pos
- pos
);
386 vis_insert(vis
, pos
, data
, len
);
389 void vis_replace_key(Vis
*vis
, const char *data
, size_t len
) {
390 for (Cursor
*c
= view_cursors(vis
->win
->view
); c
; c
= view_cursors_next(c
)) {
391 size_t pos
= view_cursors_pos(c
);
392 vis_replace(vis
, pos
, data
, len
);
393 view_cursors_scroll_to(c
, pos
+ len
);
397 void vis_delete(Vis
*vis
, size_t pos
, size_t len
) {
398 text_delete(vis
->win
->file
->text
, pos
, len
);
399 windows_invalidate(vis
, pos
, pos
+ len
);
402 bool vis_action_register(Vis
*vis
, const KeyAction
*action
) {
404 vis
->actions
= map_new();
407 return map_put(vis
->actions
, action
->name
, action
);
410 bool vis_keymap_add(Vis
*vis
, const char *key
, const char *mapping
) {
411 return map_put(vis
->keymap
, key
, mapping
);
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 else if (a
->movement
->user
)
474 pos
= a
->movement
->user(vis
, win
, a
->movement
->data
, pos
);
475 if (pos
== EPOS
|| a
->movement
->type
& IDEMPOTENT
)
480 c
.range
.start
= start
;
484 c
.range
= text_range_new(start
, pos
);
489 if (a
->movement
->type
& CHARWISE
)
490 view_cursors_scroll_to(cursor
, pos
);
492 view_cursors_to(cursor
, pos
);
493 if (vis
->mode
->visual
)
494 c
.range
= view_cursors_selection_get(cursor
);
495 if (a
->movement
->type
& JUMP
)
496 window_jumplist_add(win
, pos
);
498 window_jumplist_invalidate(win
);
499 } else if (a
->movement
->type
& INCLUSIVE
) {
500 c
.range
.end
= text_char_next(txt
, c
.range
.end
);
502 } else if (a
->textobj
) {
503 if (vis
->mode
->visual
)
504 c
.range
= view_cursors_selection_get(cursor
);
506 c
.range
.start
= c
.range
.end
= pos
;
507 for (int i
= 0; i
< count
; i
++) {
508 Filerange r
= text_range_empty();
510 r
= a
->textobj
->txt(txt
, pos
);
511 else if (a
->textobj
->vis
)
512 r
= a
->textobj
->vis(vis
, txt
, pos
);
513 else if (a
->textobj
->user
)
514 r
= a
->textobj
->user(vis
, win
, a
->textobj
->data
, pos
);
515 if (!text_range_valid(&r
))
517 if (a
->textobj
->type
& OUTER
) {
522 if (a
->textobj
->type
& SPLIT
)
525 c
.range
= text_range_union(&c
.range
, &r
);
528 pos
= c
.range
.end
+ 1;
530 } else if (vis
->mode
->visual
) {
531 c
.range
= view_cursors_selection_get(cursor
);
532 if (!text_range_valid(&c
.range
))
533 c
.range
.start
= c
.range
.end
= pos
;
536 if (linewise
&& vis
->mode
!= &vis_modes
[VIS_MODE_VISUAL
])
537 c
.range
= text_range_linewise(txt
, &c
.range
);
538 if (vis
->mode
->visual
) {
539 view_cursors_selection_set(cursor
, &c
.range
);
540 if (vis
->mode
== &vis_modes
[VIS_MODE_VISUAL
] || a
->textobj
)
541 view_cursors_selection_sync(cursor
);
545 size_t pos
= a
->op
->func(vis
, txt
, &c
);
547 view_cursors_dispose(cursor
);
548 } else if (pos
<= text_size(txt
)) {
549 /* moving the cursor will affect the selection.
550 * because we want to be able to later restore
551 * the old selection we update it again before
552 * leaving visual mode.
554 Filerange sel
= view_cursors_selection_get(cursor
);
555 view_cursors_to(cursor
, pos
);
556 if (vis
->mode
->visual
) {
557 if (sel
.start
== EPOS
&& sel
.end
== EPOS
)
559 else if (sel
.start
== EPOS
)
560 sel
= text_range_new(c
.range
.start
, sel
.end
);
561 else if (sel
.end
== EPOS
)
562 sel
= text_range_new(c
.range
.start
, sel
.start
);
563 if (vis
->mode
== &vis_modes
[VIS_MODE_VISUAL_LINE
])
564 sel
= text_range_linewise(txt
, &sel
);
565 view_cursors_selection_set(cursor
, &sel
);
572 /* we do not support visual repeat, still do something resonable */
573 if (vis
->mode
->visual
&& !a
->movement
&& !a
->textobj
)
574 a
->movement
= &vis_motions
[VIS_MOVE_NOP
];
576 /* operator implementations must not change the mode,
577 * they might get called multiple times (once for every cursor)
579 if (a
->op
== &vis_operators
[VIS_OP_INSERT
] || a
->op
== &vis_operators
[VIS_OP_CHANGE
]) {
580 vis_mode_switch(vis
, VIS_MODE_INSERT
);
581 } else if (a
->op
== &vis_operators
[VIS_OP_REPLACE
]) {
582 vis_mode_switch(vis
, VIS_MODE_REPLACE
);
583 } else if (a
->op
== &vis_operators
[VIS_OP_FILTER
]) {
585 vis_mode_switch(vis
, VIS_MODE_NORMAL
);
586 vis_cmd(vis
, a
->arg
.s
);
588 vis_prompt_show(vis
, ":'<,'>!");
590 } else if (vis
->mode
== &vis_modes
[VIS_MODE_OPERATOR_PENDING
]) {
591 mode_set(vis
, vis
->mode_prev
);
592 } else if (vis
->mode
->visual
) {
593 vis_mode_switch(vis
, VIS_MODE_NORMAL
);
599 if (a
!= &vis
->action_prev
) {
602 a
->macro
= vis
->macro_operator
;
603 vis
->action_prev
= *a
;
609 void action_reset(Action
*a
) {
610 memset(a
, 0, sizeof(*a
));
611 a
->count
= VIS_COUNT_UNKNOWN
;
614 void vis_cancel(Vis
*vis
) {
615 action_reset(&vis
->action
);
618 void vis_die(Vis
*vis
, const char *msg
, ...) {
621 vis
->ui
->die(vis
->ui
, msg
, ap
);
625 const char *vis_keys_next(Vis
*vis
, const char *keys
) {
627 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
628 const char *next
= NULL
;
631 /* first try to parse a special key of the form <Key> */
632 if (*keys
== '<' && (next
= termkey_strpkey(termkey
, keys
+1, &key
, TERMKEY_FORMAT_VIM
)) && *next
== '>')
635 const char *start
= keys
+ 1, *end
= start
;
636 while (*end
&& *end
!= '>')
638 if (end
> start
&& end
- start
- 1 < 64 && *end
== '>') {
640 memcpy(key
, start
, end
- start
);
641 key
[end
- start
] = '\0';
642 if (map_get(vis
->actions
, key
))
646 while (!ISUTF8(*keys
))
648 return termkey_strpkey(termkey
, keys
, &key
, TERMKEY_FORMAT_VIM
);
651 static const char *vis_keys_raw(Vis
*vis
, Buffer
*buf
, const char *input
) {
652 char *keys
= buf
->data
, *start
= keys
, *cur
= keys
, *end
;
654 KeyBinding
*binding
= NULL
;
656 while (cur
&& *cur
) {
658 if (!(end
= (char*)vis_keys_next(vis
, cur
)))
659 goto err
; // XXX: can't parse key this should never happen
666 for (Mode
*mode
= vis
->mode
; mode
&& !binding
&& !prefix
; mode
= mode
->parent
) {
667 for (int global
= 0; global
< 2 && !binding
&& !prefix
; global
++) {
668 Mode
*mode_local
= global
? mode
: &vis
->win
->modes
[mode
->id
];
669 if (!mode_local
->bindings
)
671 binding
= map_get(mode_local
->bindings
, start
);
672 /* "<" is never treated as a prefix because it is used to denote
673 * special key symbols */
674 if (strcmp(cur
, "<"))
675 prefix
= !binding
&& map_contains(mode_local
->bindings
, start
);
682 if (binding
) { /* exact match */
683 if (binding
->action
) {
684 end
= (char*)binding
->action
->func(vis
, end
, &binding
->action
->arg
);
688 } else if (binding
->alias
) {
689 buffer_put0(buf
, end
);
690 buffer_prepend0(buf
, binding
->alias
);
691 start
= cur
= buf
->data
;
693 } else if (prefix
) { /* incomplete key binding? */
695 } else { /* no keybinding */
696 KeyAction
*action
= NULL
;
697 if (start
[0] == '<' && end
[-1] == '>') {
698 /* test for special editor key command */
701 action
= map_get(vis
->actions
, start
+1);
704 end
= (char*)action
->func(vis
, end
, &action
->arg
);
709 if (!action
&& vis
->mode
->input
)
710 vis
->mode
->input(vis
, start
, end
- start
);
716 buffer_put0(buf
, start
);
717 return input
+ (start
- keys
);
720 buffer_truncate(buf
);
721 return input
+ strlen(input
);
724 bool vis_keys_inject(Vis
*vis
, const char *pos
, const char *input
) {
725 Buffer
*buf
= vis
->keys
;
728 if (pos
< buf
->data
|| pos
> buf
->data
+ buf
->len
)
730 size_t off
= pos
- buf
->data
;
731 buffer_insert0(buf
, off
, input
);
732 if (vis
->macro_operator
)
733 macro_append(vis
->macro_operator
, input
);
737 const char *vis_keys_push(Vis
*vis
, const char *input
) {
742 macro_append(vis
->recording
, input
);
743 if (vis
->macro_operator
)
744 macro_append(vis
->macro_operator
, input
);
746 if (!buffer_append0(&vis
->input_queue
, input
)) {
747 buffer_truncate(&vis
->input_queue
);
751 return vis_keys_raw(vis
, &vis
->input_queue
, input
);
754 static const char *getkey(Vis
*vis
) {
755 const char *key
= vis
->ui
->getkey(vis
->ui
);
759 if (!vis
->mode
->input
) {
760 const char *mapped
= map_get(vis
->keymap
, key
);
767 bool vis_signal_handler(Vis
*vis
, int signum
, const siginfo_t
*siginfo
, const void *context
) {
770 for (File
*file
= vis
->files
; file
; file
= file
->next
) {
771 if (text_sigbus(file
->text
, siginfo
->si_addr
))
772 file
->truncated
= true;
776 siglongjmp(vis
->sigbus_jmpbuf
, 1);
779 vis
->cancel_filter
= true;
785 static void vis_args(Vis
*vis
, int argc
, char *argv
[]) {
787 bool end_of_options
= false;
788 for (int i
= 1; i
< argc
; i
++) {
789 if (argv
[i
][0] == '-' && !end_of_options
) {
790 switch (argv
[i
][1]) {
792 end_of_options
= true;
795 vis_die(vis
, "vis %s, compiled " __DATE__
" " __TIME__
"\n", VERSION
);
800 vis_die(vis
, "Unknown command option: %s\n", argv
[i
]);
803 } else if (argv
[i
][0] == '+') {
804 cmd
= argv
[i
] + (argv
[i
][1] == '/' || argv
[i
][1] == '?');
805 } else if (!vis_window_new(vis
, argv
[i
])) {
806 vis_die(vis
, "Can not load `%s': %s\n", argv
[i
], strerror(errno
));
808 vis_prompt_cmd(vis
, cmd
);
814 if (!strcmp(argv
[argc
-1], "-")) {
815 if (!vis_window_new(vis
, NULL
))
816 vis_die(vis
, "Can not create empty buffer\n");
819 File
*file
= vis
->win
->file
;
820 Text
*txt
= file
->text
;
821 file
->is_stdin
= true;
822 while ((len
= read(STDIN_FILENO
, buf
, sizeof buf
)) > 0)
823 text_insert(txt
, text_size(txt
), buf
, len
);
825 vis_die(vis
, "Can not read from stdin\n");
827 int fd
= open("/dev/tty", O_RDONLY
);
829 vis_die(vis
, "Can not reopen stdin\n");
830 dup2(fd
, STDIN_FILENO
);
832 } else if (!vis_window_new(vis
, NULL
)) {
833 vis_die(vis
, "Can not create empty buffer\n");
836 vis_prompt_cmd(vis
, cmd
);
840 int vis_run(Vis
*vis
, int argc
, char *argv
[]) {
841 vis_args(vis
, argc
, argv
);
843 struct timespec idle
= { .tv_nsec
= 0 }, *timeout
= NULL
;
846 sigemptyset(&emptyset
);
849 vis
->exit_status
= EXIT_SUCCESS
;
851 sigsetjmp(vis
->sigbus_jmpbuf
, 1);
853 while (vis
->running
) {
856 FD_SET(STDIN_FILENO
, &fds
);
860 for (Win
*next
, *win
= vis
->windows
; win
; win
= next
) {
862 if (win
->file
->truncated
) {
864 name
= strdup(win
->file
->name
);
865 vis_window_close(win
);
869 vis_die(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
871 vis_info_show(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
877 idle
.tv_sec
= vis
->mode
->idle_timeout
;
878 int r
= pselect(1, &fds
, NULL
, NULL
, timeout
, &emptyset
);
879 if (r
== -1 && errno
== EINTR
)
883 /* TODO save all pending changes to a ~suffixed file */
884 vis_die(vis
, "Error in mainloop: %s\n", strerror(errno
));
887 if (!FD_ISSET(STDIN_FILENO
, &fds
)) {
889 vis
->mode
->idle(vis
);
894 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
895 termkey_advisereadable(termkey
);
898 while ((key
= getkey(vis
)))
899 vis_keys_push(vis
, key
);
904 return vis
->exit_status
;
907 static Macro
*macro_get(Vis
*vis
, enum VisRegister id
) {
908 if (id
== VIS_MACRO_LAST_RECORDED
)
909 return vis
->last_recording
;
910 if (VIS_REG_A
<= id
&& id
<= VIS_REG_Z
)
912 if (id
< LENGTH(vis
->registers
))
913 return &vis
->registers
[id
].buf
;
917 void macro_operator_record(Vis
*vis
) {
918 vis
->macro_operator
= macro_get(vis
, VIS_MACRO_OPERATOR
);
919 macro_reset(vis
->macro_operator
);
922 void macro_operator_stop(Vis
*vis
) {
923 vis
->macro_operator
= NULL
;
926 bool vis_macro_record(Vis
*vis
, enum VisRegister id
) {
927 Macro
*macro
= macro_get(vis
, id
);
928 if (vis
->recording
|| !macro
)
930 if (!(VIS_REG_A
<= id
&& id
<= VIS_REG_Z
))
932 vis
->recording
= macro
;
936 bool vis_macro_record_stop(Vis
*vis
) {
939 /* XXX: hack to remove last recorded key, otherwise upon replay
940 * we would start another recording */
941 if (vis
->recording
->len
> 1) {
942 vis
->recording
->len
--;
943 vis
->recording
->data
[vis
->recording
->len
-1] = '\0';
945 vis
->last_recording
= vis
->recording
;
946 vis
->recording
= NULL
;
950 bool vis_macro_recording(Vis
*vis
) {
951 return vis
->recording
;
954 static void macro_replay(Vis
*vis
, const Macro
*macro
) {
957 buffer_put(&buf
, macro
->data
, macro
->len
);
958 buffer_append(&buf
, "\0", 1);
959 vis_keys_raw(vis
, &buf
, macro
->data
);
960 buffer_release(&buf
);
963 bool vis_macro_replay(Vis
*vis
, enum VisRegister id
) {
964 if (id
== VIS_REG_SEARCH
)
965 return vis_motion(vis
, VIS_MOVE_SEARCH_NEXT
);
966 if (id
== VIS_REG_COMMAND
) {
967 const char *cmd
= register_get(vis
, &vis
->registers
[id
], NULL
);
968 return vis_cmd(vis
, cmd
);
971 Macro
*macro
= macro_get(vis
, id
);
972 if (!macro
|| macro
== vis
->recording
)
974 macro_replay(vis
, macro
);
978 void vis_repeat(Vis
*vis
) {
979 int count
= vis
->action
.count
;
980 Macro
*macro_operator
= macro_get(vis
, VIS_MACRO_OPERATOR
);
981 Macro
*macro_repeat
= macro_get(vis
, VIS_MACRO_REPEAT
);
982 const Macro
*macro
= vis
->action_prev
.macro
;
983 if (macro
== macro_operator
) {
984 buffer_put(macro_repeat
, macro_operator
->data
, macro_operator
->len
);
985 macro
= macro_repeat
;
986 vis
->action_prev
.macro
= macro
;
988 if (count
!= VIS_COUNT_UNKNOWN
)
989 vis
->action_prev
.count
= count
;
990 count
= vis
->action_prev
.count
;
991 /* for some operators count should be applied only to the macro not the motion */
992 if (vis
->action_prev
.op
== &vis_operators
[VIS_OP_INSERT
] || vis
->action_prev
.op
== &vis_operators
[VIS_OP_REPLACE
])
993 vis
->action_prev
.count
= 1;
994 action_do(vis
, &vis
->action_prev
);
995 vis
->action_prev
.count
= count
;
997 Mode
*mode
= vis
->mode
;
998 Action action_prev
= vis
->action_prev
;
999 count
= action_prev
.count
;
1000 if (count
< 1 || action_prev
.op
== &vis_operators
[VIS_OP_CHANGE
] || action_prev
.op
== &vis_operators
[VIS_OP_FILTER
])
1002 for (int i
= 0; i
< count
; i
++) {
1003 mode_set(vis
, mode
);
1004 macro_replay(vis
, macro
);
1006 vis
->action_prev
= action_prev
;
1011 void vis_mark_set(Vis
*vis
, enum VisMark mark
, size_t pos
) {
1012 File
*file
= vis
->win
->file
;
1013 if (mark
< LENGTH(file
->marks
))
1014 file
->marks
[mark
] = text_mark_set(file
->text
, pos
);
1017 int vis_count_get(Vis
*vis
) {
1018 return vis
->action
.count
;
1021 int vis_count_get_default(Vis
*vis
, int def
) {
1022 if (vis
->action
.count
== VIS_COUNT_UNKNOWN
)
1024 return vis
->action
.count
;
1027 void vis_count_set(Vis
*vis
, int count
) {
1028 vis
->action
.count
= (count
>= 0 ? count
: VIS_COUNT_UNKNOWN
);
1031 void vis_register_set(Vis
*vis
, enum VisRegister reg
) {
1032 if (reg
>= VIS_REG_A
&& reg
<= VIS_REG_Z
) {
1033 vis
->action
.reg
= &vis
->registers
[reg
- VIS_REG_A
];
1034 vis
->action
.reg
->append
= true;
1035 } else if (reg
< LENGTH(vis
->registers
)) {
1036 vis
->action
.reg
= &vis
->registers
[reg
];
1037 vis
->action
.reg
->append
= false;
1041 const char *vis_register_get(Vis
*vis
, enum VisRegister reg
, size_t *len
) {
1042 if (VIS_REG_A
<= reg
&& reg
<= VIS_REG_Z
)
1044 if (reg
< LENGTH(vis
->registers
))
1045 return register_get(vis
, &vis
->registers
[reg
], len
);
1050 void vis_exit(Vis
*vis
, int status
) {
1051 vis
->running
= false;
1052 vis
->exit_status
= status
;
1055 const char *vis_mode_status(Vis
*vis
) {
1056 return vis
->mode
->status
;
1059 void vis_insert_tab(Vis
*vis
) {
1060 if (!vis
->expandtab
) {
1061 vis_insert_key(vis
, "\t", 1);
1065 int tabwidth
= MIN(vis
->tabwidth
, LENGTH(spaces
) - 1);
1066 for (Cursor
*c
= view_cursors(vis
->win
->view
); c
; c
= view_cursors_next(c
)) {
1067 size_t pos
= view_cursors_pos(c
);
1068 int width
= text_line_width_get(vis
->win
->file
->text
, pos
);
1069 int count
= tabwidth
- (width
% tabwidth
);
1070 for (int i
= 0; i
< count
; i
++)
1072 spaces
[count
] = '\0';
1073 vis_insert(vis
, pos
, spaces
, count
);
1074 view_cursors_scroll_to(c
, pos
+ count
);
1078 static void copy_indent_from_previous_line(Win
*win
) {
1079 View
*view
= win
->view
;
1080 Text
*text
= win
->file
->text
;
1081 size_t pos
= view_cursor_get(view
);
1082 size_t prev_line
= text_line_prev(text
, pos
);
1083 if (pos
== prev_line
)
1085 size_t begin
= text_line_begin(text
, prev_line
);
1086 size_t start
= text_line_start(text
, begin
);
1087 size_t len
= start
-begin
;
1088 char *buf
= malloc(len
);
1091 len
= text_bytes_get(text
, begin
, len
, buf
);
1092 vis_insert_key(win
->vis
, buf
, len
);
1096 void vis_insert_nl(Vis
*vis
) {
1097 const char *nl
= text_newline_char(vis
->win
->file
->text
);
1098 vis_insert_key(vis
, nl
, strlen(nl
));
1100 if (vis
->autoindent
)
1101 copy_indent_from_previous_line(vis
->win
);
1104 Regex
*vis_regex(Vis
*vis
, const char *pattern
) {
1105 if (!pattern
&& !(pattern
= register_get(vis
, &vis
->registers
[VIS_REG_SEARCH
], NULL
)))
1107 Regex
*regex
= text_regex_new();
1110 if (text_regex_compile(regex
, pattern
, REG_EXTENDED
|REG_NEWLINE
) != 0) {
1111 text_regex_free(regex
);
1114 register_put0(vis
, &vis
->registers
[VIS_REG_SEARCH
], pattern
);
1118 Text
*vis_text(Vis
*vis
) {
1119 return vis
->win
->file
->text
;
1122 View
*vis_view(Vis
*vis
) {
1123 return vis
->win
->view
;
1126 Win
*vis_window(Vis
*vis
) {
1130 Text
*vis_file_text(File
*file
) {
1134 const char *vis_file_name(File
*file
) {