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 /* enable large file optimization for files larger than: */
47 #define LARGE_FILE (1 << 25)
49 static Macro
*macro_get(Vis
*vis
, enum VisMacro m
);
50 static void macro_replay(Vis
*vis
, const Macro
*macro
);
52 /** window / file handling */
54 static void file_free(Vis
*vis
, File
*file
) {
57 if (file
->refcount
> 1) {
61 if (vis
->event
&& vis
->event
->file_close
)
62 vis
->event
->file_close(vis
, file
);
63 text_free(file
->text
);
64 free((char*)file
->name
);
67 file
->prev
->next
= file
->next
;
69 file
->next
->prev
= file
->prev
;
70 if (vis
->files
== file
)
71 vis
->files
= file
->next
;
75 static File
*file_new_text(Vis
*vis
, Text
*text
) {
76 File
*file
= calloc(1, sizeof(*file
));
80 file
->stat
= text_stat(text
);
82 vis
->files
->prev
= file
;
83 file
->next
= vis
->files
;
88 static File
*file_new(Vis
*vis
, const char *filename
) {
90 /* try to detect whether the same file is already open in another window
91 * TODO: do this based on inodes */
92 for (File
*file
= vis
->files
; file
; file
= file
->next
) {
93 if (file
->name
&& strcmp(file
->name
, filename
) == 0) {
99 Text
*text
= text_load(filename
);
100 if (!text
&& filename
&& errno
== ENOENT
)
101 text
= text_load(NULL
);
105 File
*file
= file_new_text(vis
, text
);
112 file
->name
= strdup(filename
);
113 if (vis
->event
&& vis
->event
->file_open
)
114 vis
->event
->file_open(vis
, file
);
118 static File
*file_new_internal(Vis
*vis
, const char *filename
) {
119 File
*file
= file_new(vis
, filename
);
122 file
->internal
= true;
127 void vis_window_name(Win
*win
, const char *filename
) {
128 File
*file
= win
->file
;
129 if (filename
!= file
->name
) {
130 free((char*)file
->name
);
131 file
->name
= filename
? strdup(filename
) : NULL
;
135 static void windows_invalidate(Vis
*vis
, size_t start
, size_t end
) {
136 for (Win
*win
= vis
->windows
; win
; win
= win
->next
) {
137 if (vis
->win
!= win
&& vis
->win
->file
== win
->file
) {
138 Filerange view
= view_viewport_get(win
->view
);
139 if ((view
.start
<= start
&& start
<= view
.end
) ||
140 (view
.start
<= end
&& end
<= view
.end
))
141 view_draw(win
->view
);
144 view_draw(vis
->win
->view
);
147 void window_selection_save(Win
*win
) {
148 File
*file
= win
->file
;
149 Filerange sel
= view_cursors_selection_get(view_cursors(win
->view
));
150 file
->marks
[VIS_MARK_SELECTION_START
] = text_mark_set(file
->text
, sel
.start
);
151 file
->marks
[VIS_MARK_SELECTION_END
] = text_mark_set(file
->text
, sel
.end
);
154 static void window_free(Win
*win
) {
158 for (Win
*other
= vis
->windows
; other
; other
= other
->next
) {
159 if (other
->parent
== win
)
160 other
->parent
= NULL
;
163 vis
->ui
->window_free(win
->ui
);
164 view_free(win
->view
);
165 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++)
166 map_free(win
->modes
[i
].bindings
);
167 ringbuf_free(win
->jumplist
);
171 Win
*window_new_file(Vis
*vis
, File
*file
) {
172 Win
*win
= calloc(1, sizeof(Win
));
177 win
->jumplist
= ringbuf_alloc(31);
178 win
->view
= view_new(file
->text
, vis
->lua
);
179 win
->ui
= vis
->ui
->window_new(vis
->ui
, win
->view
, file
, UI_OPTION_STATUSBAR
);
180 if (!win
->jumplist
|| !win
->view
|| !win
->ui
) {
185 view_tabwidth_set(win
->view
, vis
->tabwidth
);
187 if (text_size(file
->text
) > LARGE_FILE
) {
188 enum UiOption opt
= view_options_get(win
->view
);
189 opt
|= UI_OPTION_LARGE_FILE
;
190 opt
&= ~UI_OPTION_LINE_NUMBERS_ABSOLUTE
;
191 view_options_set(win
->view
, opt
);
195 vis
->windows
->prev
= win
;
196 win
->next
= vis
->windows
;
199 vis
->ui
->window_focus(win
->ui
);
200 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++)
201 win
->modes
[i
].parent
= &vis_modes
[i
];
202 if (vis
->event
&& vis
->event
->win_open
)
203 vis
->event
->win_open(vis
, win
);
207 bool vis_window_reload(Win
*win
) {
208 const char *name
= win
->file
->name
;
210 return false; /* can't reload unsaved file */
211 /* temporarily unset file name, otherwise file_new returns the same File */
212 win
->file
->name
= NULL
;
213 File
*file
= file_new(win
->vis
, name
);
214 win
->file
->name
= name
;
217 file_free(win
->vis
, win
->file
);
220 win
->ui
->reload(win
->ui
, file
);
224 bool vis_window_split(Win
*original
) {
225 Win
*win
= window_new_file(original
->vis
, original
->file
);
228 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++) {
229 if (original
->modes
[i
].bindings
)
230 win
->modes
[i
].bindings
= map_new();
231 if (win
->modes
[i
].bindings
)
232 map_copy(win
->modes
[i
].bindings
, original
->modes
[i
].bindings
);
234 win
->file
= original
->file
;
235 win
->file
->refcount
++;
236 view_syntax_set(win
->view
, view_syntax_get(original
->view
));
237 view_options_set(win
->view
, view_options_get(original
->view
));
238 view_cursor_to(win
->view
, view_cursor_get(original
->view
));
243 void vis_window_next(Vis
*vis
) {
247 vis
->win
= vis
->win
->next
;
249 vis
->win
= vis
->windows
;
250 vis
->ui
->window_focus(vis
->win
->ui
);
253 void vis_window_prev(Vis
*vis
) {
257 vis
->win
= vis
->win
->prev
;
259 for (vis
->win
= vis
->windows
; vis
->win
->next
; vis
->win
= vis
->win
->next
);
260 vis
->ui
->window_focus(vis
->win
->ui
);
263 void vis_draw(Vis
*vis
) {
264 vis
->ui
->draw(vis
->ui
);
267 void vis_redraw(Vis
*vis
) {
268 vis
->ui
->redraw(vis
->ui
);
271 void vis_update(Vis
*vis
) {
272 for (Win
*win
= vis
->windows
; win
; win
= win
->next
)
273 view_update(win
->view
);
274 view_update(vis
->win
->view
);
275 vis
->ui
->update(vis
->ui
);
278 void vis_suspend(Vis
*vis
) {
279 vis
->ui
->suspend(vis
->ui
);
282 bool vis_window_new(Vis
*vis
, const char *filename
) {
283 File
*file
= file_new(vis
, filename
);
286 Win
*win
= window_new_file(vis
, file
);
288 file_free(vis
, file
);
292 vis_window_name(win
, filename
);
298 bool vis_window_closable(Win
*win
) {
299 if (!text_modified(win
->file
->text
))
301 return win
->file
->refcount
> 1;
304 void vis_window_close(Win
*win
) {
306 if (vis
->event
&& vis
->event
->win_close
)
307 vis
->event
->win_close(vis
, win
);
308 file_free(vis
, win
->file
);
310 win
->prev
->next
= win
->next
;
312 win
->next
->prev
= win
->prev
;
313 if (vis
->windows
== win
)
314 vis
->windows
= win
->next
;
316 vis
->win
= win
->next
? win
->next
: win
->prev
;
317 if (win
== vis
->message_window
)
318 vis
->message_window
= NULL
;
321 vis
->ui
->window_focus(vis
->win
->ui
);
325 Vis
*vis_new(Ui
*ui
, VisEvent
*event
) {
328 Vis
*vis
= calloc(1, sizeof(Vis
));
332 vis
->ui
->init(vis
->ui
, vis
);
334 vis
->expandtab
= false;
335 vis
->registers
[VIS_REG_BLACKHOLE
].type
= REGISTER_BLACKHOLE
;
336 vis
->registers
[VIS_REG_CLIPBOARD
].type
= REGISTER_CLIPBOARD
;
337 action_reset(&vis
->action
);
338 if (!(vis
->search_pattern
= text_regex_new()))
340 if (!(vis
->command_file
= file_new_internal(vis
, NULL
)))
342 if (!(vis
->search_file
= file_new_internal(vis
, NULL
)))
344 vis
->mode_prev
= vis
->mode
= &vis_modes
[VIS_MODE_NORMAL
];
346 if (event
&& event
->vis_start
)
347 event
->vis_start(vis
);
348 vis
->ui
->start(vis
->ui
);
355 void vis_free(Vis
*vis
) {
358 if (vis
->event
&& vis
->event
->vis_quit
)
359 vis
->event
->vis_quit(vis
);
362 vis_window_close(vis
->windows
);
363 file_free(vis
, vis
->command_file
);
364 file_free(vis
, vis
->search_file
);
365 text_regex_free(vis
->search_pattern
);
366 for (int i
= 0; i
< LENGTH(vis
->registers
); i
++)
367 register_release(&vis
->registers
[i
]);
368 for (int i
= 0; i
< LENGTH(vis
->macros
); i
++)
369 macro_release(&vis
->macros
[i
]);
370 vis
->ui
->free(vis
->ui
);
372 map_free(vis
->options
);
373 map_free(vis
->actions
);
374 buffer_release(&vis
->input_queue
);
375 for (int i
= 0; i
< VIS_MODE_INVALID
; i
++)
376 map_free(vis_modes
[i
].bindings
);
380 void vis_insert(Vis
*vis
, size_t pos
, const char *data
, size_t len
) {
381 text_insert(vis
->win
->file
->text
, pos
, data
, len
);
382 windows_invalidate(vis
, pos
, pos
+ len
);
385 void vis_insert_key(Vis
*vis
, const char *data
, size_t len
) {
386 for (Cursor
*c
= view_cursors(vis
->win
->view
); c
; c
= view_cursors_next(c
)) {
387 size_t pos
= view_cursors_pos(c
);
388 vis_insert(vis
, pos
, data
, len
);
389 view_cursors_scroll_to(c
, pos
+ len
);
393 void vis_replace(Vis
*vis
, size_t pos
, const char *data
, size_t len
) {
394 Text
*txt
= vis
->win
->file
->text
;
395 Iterator it
= text_iterator_get(txt
, pos
);
396 int chars
= text_char_count(data
, len
);
397 for (char c
; chars
-- > 0 && text_iterator_byte_get(&it
, &c
) && c
!= '\r' && c
!= '\n'; )
398 text_iterator_char_next(&it
, NULL
);
400 text_delete(txt
, pos
, it
.pos
- pos
);
401 vis_insert(vis
, pos
, data
, len
);
404 void vis_replace_key(Vis
*vis
, const char *data
, size_t len
) {
405 for (Cursor
*c
= view_cursors(vis
->win
->view
); c
; c
= view_cursors_next(c
)) {
406 size_t pos
= view_cursors_pos(c
);
407 vis_replace(vis
, pos
, data
, len
);
408 view_cursors_scroll_to(c
, pos
+ len
);
412 void vis_delete(Vis
*vis
, size_t pos
, size_t len
) {
413 text_delete(vis
->win
->file
->text
, pos
, len
);
414 windows_invalidate(vis
, pos
, pos
+ len
);
417 bool vis_action_register(Vis
*vis
, const KeyAction
*action
) {
419 vis
->actions
= map_new();
422 return map_put(vis
->actions
, action
->name
, action
);
425 static void window_jumplist_add(Win
*win
, size_t pos
) {
426 Mark mark
= text_mark_set(win
->file
->text
, pos
);
427 if (mark
&& win
->jumplist
)
428 ringbuf_add(win
->jumplist
, mark
);
431 static void window_jumplist_invalidate(Win
*win
) {
433 ringbuf_invalidate(win
->jumplist
);
436 void action_do(Vis
*vis
, Action
*a
) {
438 Text
*txt
= win
->file
->text
;
439 View
*view
= win
->view
;
441 if (a
->op
== &vis_operators
[VIS_OP_FILTER
] && !vis
->mode
->visual
)
442 vis_mode_switch(vis
, VIS_MODE_VISUAL_LINE
);
444 int count
= MAX(a
->count
, 1);
445 bool repeatable
= a
->op
&& !vis
->macro_operator
;
446 bool multiple_cursors
= view_cursors_count(view
) > 1;
447 bool linewise
= !(a
->type
& CHARWISE
) && (
448 a
->type
& LINEWISE
|| (a
->movement
&& a
->movement
->type
& LINEWISE
) ||
449 vis
->mode
== &vis_modes
[VIS_MODE_VISUAL_LINE
]);
451 for (Cursor
*cursor
= view_cursors(view
), *next
; cursor
; cursor
= next
) {
453 next
= view_cursors_next(cursor
);
454 size_t pos
= view_cursors_pos(cursor
);
455 Register
*reg
= multiple_cursors
? view_cursors_register(cursor
) : a
->reg
;
457 reg
= &vis
->registers
[win
->file
->internal
? VIS_REG_PROMPT
: VIS_REG_DEFAULT
];
459 OperatorContext c
= {
463 .range
= text_range_empty(),
465 .linewise
= linewise
,
471 for (int i
= 0; i
< count
; i
++) {
472 if (a
->movement
->txt
)
473 pos
= a
->movement
->txt(txt
, pos
);
474 else if (a
->movement
->cur
)
475 pos
= a
->movement
->cur(cursor
);
476 else if (a
->movement
->file
)
477 pos
= a
->movement
->file(vis
, vis
->win
->file
, pos
);
478 else if (a
->movement
->vis
)
479 pos
= a
->movement
->vis(vis
, txt
, pos
);
480 else if (a
->movement
->view
)
481 pos
= a
->movement
->view(vis
, view
);
482 else if (a
->movement
->win
)
483 pos
= a
->movement
->win(vis
, win
, pos
);
484 if (pos
== EPOS
|| a
->movement
->type
& IDEMPOTENT
)
489 c
.range
.start
= start
;
493 c
.range
= text_range_new(start
, pos
);
498 if (a
->movement
->type
& CHARWISE
)
499 view_cursors_scroll_to(cursor
, pos
);
501 view_cursors_to(cursor
, pos
);
502 if (vis
->mode
->visual
)
503 c
.range
= view_cursors_selection_get(cursor
);
504 if (a
->movement
->type
& JUMP
)
505 window_jumplist_add(win
, pos
);
507 window_jumplist_invalidate(win
);
508 } else if (a
->movement
->type
& INCLUSIVE
) {
509 c
.range
.end
= text_char_next(txt
, c
.range
.end
);
511 } else if (a
->textobj
) {
512 if (vis
->mode
->visual
)
513 c
.range
= view_cursors_selection_get(cursor
);
515 c
.range
.start
= c
.range
.end
= pos
;
516 for (int i
= 0; i
< count
; i
++) {
519 r
= a
->textobj
->txt(txt
, pos
);
521 r
= a
->textobj
->vis(vis
, txt
, pos
);
522 if (!text_range_valid(&r
))
524 if (a
->textobj
->type
& OUTER
) {
529 if (a
->textobj
->type
& SPLIT
)
532 c
.range
= text_range_union(&c
.range
, &r
);
535 pos
= c
.range
.end
+ 1;
537 } else if (vis
->mode
->visual
) {
538 c
.range
= view_cursors_selection_get(cursor
);
539 if (!text_range_valid(&c
.range
))
540 c
.range
.start
= c
.range
.end
= pos
;
543 if (linewise
&& vis
->mode
!= &vis_modes
[VIS_MODE_VISUAL
])
544 c
.range
= text_range_linewise(txt
, &c
.range
);
545 if (vis
->mode
->visual
) {
546 view_cursors_selection_set(cursor
, &c
.range
);
547 if (vis
->mode
== &vis_modes
[VIS_MODE_VISUAL
] || a
->textobj
)
548 view_cursors_selection_sync(cursor
);
552 size_t pos
= a
->op
->func(vis
, txt
, &c
);
554 view_cursors_dispose(cursor
);
555 } else if (pos
<= text_size(txt
)) {
556 /* moving the cursor will affect the selection.
557 * because we want to be able to later restore
558 * the old selection we update it again before
559 * leaving visual mode.
561 Filerange sel
= view_cursors_selection_get(cursor
);
562 view_cursors_to(cursor
, pos
);
563 if (vis
->mode
->visual
) {
564 if (sel
.start
== EPOS
&& sel
.end
== EPOS
)
566 else if (sel
.start
== EPOS
)
567 sel
= text_range_new(c
.range
.start
, sel
.end
);
568 else if (sel
.end
== EPOS
)
569 sel
= text_range_new(c
.range
.start
, sel
.start
);
570 if (vis
->mode
== &vis_modes
[VIS_MODE_VISUAL_LINE
])
571 sel
= text_range_linewise(txt
, &sel
);
572 view_cursors_selection_set(cursor
, &sel
);
579 /* we do not support visual repeat, still do something resonable */
580 if (vis
->mode
->visual
&& !a
->movement
&& !a
->textobj
)
581 a
->movement
= &vis_motions
[VIS_MOVE_NOP
];
583 /* operator implementations must not change the mode,
584 * they might get called multiple times (once for every cursor)
586 if (a
->op
== &vis_operators
[VIS_OP_INSERT
] || a
->op
== &vis_operators
[VIS_OP_CHANGE
]) {
587 vis_mode_switch(vis
, VIS_MODE_INSERT
);
588 } else if (a
->op
== &vis_operators
[VIS_OP_REPLACE
]) {
589 vis_mode_switch(vis
, VIS_MODE_REPLACE
);
590 } else if (a
->op
== &vis_operators
[VIS_OP_FILTER
]) {
592 vis_mode_switch(vis
, VIS_MODE_NORMAL
);
593 vis_cmd(vis
, a
->arg
.s
);
595 vis_prompt_show(vis
, ":'<,'>!");
597 } else if (vis
->mode
== &vis_modes
[VIS_MODE_OPERATOR_PENDING
]) {
598 mode_set(vis
, vis
->mode_prev
);
599 } else if (vis
->mode
->visual
) {
600 vis_mode_switch(vis
, VIS_MODE_NORMAL
);
606 if (a
!= &vis
->action_prev
) {
609 a
->macro
= vis
->macro_operator
;
610 vis
->action_prev
= *a
;
616 void action_reset(Action
*a
) {
617 memset(a
, 0, sizeof(*a
));
618 a
->count
= VIS_COUNT_UNKNOWN
;
621 void vis_cancel(Vis
*vis
) {
622 action_reset(&vis
->action
);
625 void vis_die(Vis
*vis
, const char *msg
, ...) {
628 vis
->ui
->die(vis
->ui
, msg
, ap
);
632 const char *vis_keys_next(Vis
*vis
, const char *keys
) {
634 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
635 const char *next
= NULL
;
638 /* first try to parse a special key of the form <Key> */
639 if (*keys
== '<' && (next
= termkey_strpkey(termkey
, keys
+1, &key
, TERMKEY_FORMAT_VIM
)) && *next
== '>')
642 const char *start
= keys
+ 1, *end
= start
;
643 while (*end
&& *end
!= '>')
645 if (end
> start
&& end
- start
- 1 < 64 && *end
== '>') {
647 memcpy(key
, start
, end
- start
);
648 key
[end
- start
] = '\0';
649 if (map_get(vis
->actions
, key
))
653 while (!ISUTF8(*keys
))
655 return termkey_strpkey(termkey
, keys
, &key
, TERMKEY_FORMAT_VIM
);
658 static const char *vis_keys_raw(Vis
*vis
, Buffer
*buf
, const char *input
) {
659 char *keys
= buf
->data
, *start
= keys
, *cur
= keys
, *end
;
661 KeyBinding
*binding
= NULL
;
663 while (cur
&& *cur
) {
665 if (!(end
= (char*)vis_keys_next(vis
, cur
)))
666 goto err
; // XXX: can't parse key this should never happen
673 for (Mode
*mode
= vis
->mode
; mode
&& !binding
&& !prefix
; mode
= mode
->parent
) {
674 for (int global
= 0; global
< 2 && !binding
&& !prefix
; global
++) {
675 Mode
*mode_local
= global
? mode
: &vis
->win
->modes
[mode
->id
];
676 if (!mode_local
->bindings
)
678 binding
= map_get(mode_local
->bindings
, start
);
679 /* "<" is never treated as a prefix because it is used to denote
680 * special key symbols */
681 if (strcmp(cur
, "<"))
682 prefix
= !binding
&& map_contains(mode_local
->bindings
, start
);
689 if (binding
) { /* exact match */
690 if (binding
->action
) {
691 end
= (char*)binding
->action
->func(vis
, end
, &binding
->action
->arg
);
695 } else if (binding
->alias
) {
696 buffer_put0(buf
, end
);
697 buffer_prepend0(buf
, binding
->alias
);
698 start
= cur
= buf
->data
;
700 } else if (prefix
) { /* incomplete key binding? */
702 } else { /* no keybinding */
703 KeyAction
*action
= NULL
;
704 if (start
[0] == '<' && end
[-1] == '>') {
705 /* test for special editor key command */
708 action
= map_get(vis
->actions
, start
+1);
711 end
= (char*)action
->func(vis
, end
, &action
->arg
);
716 if (!action
&& vis
->mode
->input
)
717 vis
->mode
->input(vis
, start
, end
- start
);
723 buffer_put0(buf
, start
);
724 return input
+ (start
- keys
);
727 buffer_truncate(buf
);
728 return input
+ strlen(input
);
731 bool vis_keys_inject(Vis
*vis
, const char *pos
, const char *input
) {
732 Buffer
*buf
= vis
->keys
;
735 if (pos
< buf
->data
|| pos
> buf
->data
+ buf
->len
)
737 size_t off
= pos
- buf
->data
;
738 buffer_insert0(buf
, off
, input
);
739 if (vis
->macro_operator
)
740 macro_append(vis
->macro_operator
, input
);
744 const char *vis_keys_push(Vis
*vis
, const char *input
) {
749 macro_append(vis
->recording
, input
);
750 if (vis
->macro_operator
)
751 macro_append(vis
->macro_operator
, input
);
753 if (!buffer_append0(&vis
->input_queue
, input
)) {
754 buffer_truncate(&vis
->input_queue
);
758 return vis_keys_raw(vis
, &vis
->input_queue
, input
);
761 static const char *getkey(Vis
*vis
) {
762 const char *key
= vis
->ui
->getkey(vis
->ui
);
769 bool vis_signal_handler(Vis
*vis
, int signum
, const siginfo_t
*siginfo
, const void *context
) {
772 for (File
*file
= vis
->files
; file
; file
= file
->next
) {
773 if (text_sigbus(file
->text
, siginfo
->si_addr
))
774 file
->truncated
= true;
778 siglongjmp(vis
->sigbus_jmpbuf
, 1);
781 vis
->cancel_filter
= true;
787 static void vis_args(Vis
*vis
, int argc
, char *argv
[]) {
789 bool end_of_options
= false;
790 for (int i
= 1; i
< argc
; i
++) {
791 if (argv
[i
][0] == '-' && !end_of_options
) {
792 switch (argv
[i
][1]) {
794 end_of_options
= true;
797 vis_die(vis
, "vis %s, compiled " __DATE__
" " __TIME__
"\n", VERSION
);
802 vis_die(vis
, "Unknown command option: %s\n", argv
[i
]);
805 } else if (argv
[i
][0] == '+') {
806 cmd
= argv
[i
] + (argv
[i
][1] == '/' || argv
[i
][1] == '?');
807 } else if (!vis_window_new(vis
, argv
[i
])) {
808 vis_die(vis
, "Can not load `%s': %s\n", argv
[i
], strerror(errno
));
810 vis_prompt_cmd(vis
, cmd
);
816 if (!strcmp(argv
[argc
-1], "-")) {
817 if (!vis_window_new(vis
, NULL
))
818 vis_die(vis
, "Can not create empty buffer\n");
821 File
*file
= vis
->win
->file
;
822 Text
*txt
= file
->text
;
823 file
->is_stdin
= true;
824 while ((len
= read(STDIN_FILENO
, buf
, sizeof buf
)) > 0)
825 text_insert(txt
, text_size(txt
), buf
, len
);
827 vis_die(vis
, "Can not read from stdin\n");
829 int fd
= open("/dev/tty", O_RDONLY
);
831 vis_die(vis
, "Can not reopen stdin\n");
832 dup2(fd
, STDIN_FILENO
);
834 } else if (!vis_window_new(vis
, NULL
)) {
835 vis_die(vis
, "Can not create empty buffer\n");
838 vis_prompt_cmd(vis
, cmd
);
842 int vis_run(Vis
*vis
, int argc
, char *argv
[]) {
843 vis_args(vis
, argc
, argv
);
845 struct timespec idle
= { .tv_nsec
= 0 }, *timeout
= NULL
;
848 sigemptyset(&emptyset
);
851 vis
->exit_status
= EXIT_SUCCESS
;
853 sigsetjmp(vis
->sigbus_jmpbuf
, 1);
855 while (vis
->running
) {
858 FD_SET(STDIN_FILENO
, &fds
);
862 for (Win
*next
, *win
= vis
->windows
; win
; win
= next
) {
864 if (win
->file
->truncated
) {
866 name
= strdup(win
->file
->name
);
867 vis_window_close(win
);
871 vis_die(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
873 vis_info_show(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
879 idle
.tv_sec
= vis
->mode
->idle_timeout
;
880 int r
= pselect(1, &fds
, NULL
, NULL
, timeout
, &emptyset
);
881 if (r
== -1 && errno
== EINTR
)
885 /* TODO save all pending changes to a ~suffixed file */
886 vis_die(vis
, "Error in mainloop: %s\n", strerror(errno
));
889 if (!FD_ISSET(STDIN_FILENO
, &fds
)) {
891 vis
->mode
->idle(vis
);
896 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
897 termkey_advisereadable(termkey
);
900 while ((key
= getkey(vis
)))
901 vis_keys_push(vis
, key
);
906 return vis
->exit_status
;
909 static Macro
*macro_get(Vis
*vis
, enum VisMacro m
) {
910 if (m
== VIS_MACRO_LAST_RECORDED
)
911 return vis
->last_recording
;
912 if (m
< LENGTH(vis
->macros
))
913 return &vis
->macros
[m
];
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 VisMacro id
) {
927 Macro
*macro
= macro_get(vis
, id
);
928 if (vis
->recording
|| !macro
)
931 vis
->recording
= macro
;
935 bool vis_macro_record_stop(Vis
*vis
) {
938 /* XXX: hack to remove last recorded key, otherwise upon replay
939 * we would start another recording */
940 if (vis
->recording
->len
> 1) {
941 vis
->recording
->len
--;
942 vis
->recording
->data
[vis
->recording
->len
-1] = '\0';
944 vis
->last_recording
= vis
->recording
;
945 vis
->recording
= NULL
;
949 bool vis_macro_recording(Vis
*vis
) {
950 return vis
->recording
;
953 static void macro_replay(Vis
*vis
, const Macro
*macro
) {
956 buffer_put(&buf
, macro
->data
, macro
->len
);
957 vis_keys_raw(vis
, &buf
, macro
->data
);
958 buffer_release(&buf
);
961 bool vis_macro_replay(Vis
*vis
, enum VisMacro id
) {
962 Macro
*macro
= macro_get(vis
, id
);
963 if (!macro
|| macro
== vis
->recording
)
965 macro_replay(vis
, macro
);
969 void vis_repeat(Vis
*vis
) {
970 int count
= vis
->action
.count
;
971 Macro
*macro_operator
= macro_get(vis
, VIS_MACRO_OPERATOR
);
972 Macro
*macro_repeat
= macro_get(vis
, VIS_MACRO_REPEAT
);
973 const Macro
*macro
= vis
->action_prev
.macro
;
974 if (macro
== macro_operator
) {
975 buffer_put(macro_repeat
, macro_operator
->data
, macro_operator
->len
);
976 macro
= macro_repeat
;
977 vis
->action_prev
.macro
= macro
;
979 if (count
!= VIS_COUNT_UNKNOWN
)
980 vis
->action_prev
.count
= count
;
981 count
= vis
->action_prev
.count
;
982 /* for some operators count should be applied only to the macro not the motion */
983 if (vis
->action_prev
.op
== &vis_operators
[VIS_OP_INSERT
] || vis
->action_prev
.op
== &vis_operators
[VIS_OP_REPLACE
])
984 vis
->action_prev
.count
= 1;
985 action_do(vis
, &vis
->action_prev
);
986 vis
->action_prev
.count
= count
;
988 Mode
*mode
= vis
->mode
;
989 Action action_prev
= vis
->action_prev
;
990 count
= action_prev
.count
;
991 if (count
< 1 || action_prev
.op
== &vis_operators
[VIS_OP_CHANGE
] || action_prev
.op
== &vis_operators
[VIS_OP_FILTER
])
993 for (int i
= 0; i
< count
; i
++) {
995 macro_replay(vis
, macro
);
997 vis
->action_prev
= action_prev
;
1002 void vis_mark_set(Vis
*vis
, enum VisMark mark
, size_t pos
) {
1003 File
*file
= vis
->win
->file
;
1004 if (mark
< LENGTH(file
->marks
))
1005 file
->marks
[mark
] = text_mark_set(file
->text
, pos
);
1008 int vis_count_get(Vis
*vis
) {
1009 return vis
->action
.count
;
1012 int vis_count_get_default(Vis
*vis
, int def
) {
1013 if (vis
->action
.count
== VIS_COUNT_UNKNOWN
)
1015 return vis
->action
.count
;
1018 void vis_count_set(Vis
*vis
, int count
) {
1019 vis
->action
.count
= (count
>= 0 ? count
: VIS_COUNT_UNKNOWN
);
1022 void vis_register_set(Vis
*vis
, enum VisRegister reg
) {
1023 if (reg
>= VIS_REG_A
&& reg
<= VIS_REG_Z
) {
1024 vis
->action
.reg
= &vis
->registers
[reg
- VIS_REG_A
];
1025 vis
->action
.reg
->append
= true;
1026 } else if (reg
< LENGTH(vis
->registers
)) {
1027 vis
->action
.reg
= &vis
->registers
[reg
];
1028 vis
->action
.reg
->append
= false;
1032 const char *vis_register_get(Vis
*vis
, enum VisRegister reg
, size_t *len
) {
1033 if (reg
>= VIS_REG_A
&& reg
<= VIS_REG_Z
)
1035 if (reg
< LENGTH(vis
->registers
))
1036 return register_get(vis
, &vis
->registers
[reg
], len
);
1041 void vis_exit(Vis
*vis
, int status
) {
1042 vis
->running
= false;
1043 vis
->exit_status
= status
;
1046 const char *vis_mode_status(Vis
*vis
) {
1047 return vis
->mode
->status
;
1050 void vis_insert_tab(Vis
*vis
) {
1051 if (!vis
->expandtab
) {
1052 vis_insert_key(vis
, "\t", 1);
1056 int tabwidth
= MIN(vis
->tabwidth
, LENGTH(spaces
) - 1);
1057 for (Cursor
*c
= view_cursors(vis
->win
->view
); c
; c
= view_cursors_next(c
)) {
1058 size_t pos
= view_cursors_pos(c
);
1059 /* FIXME: this does not take double width characters into account */
1060 int chars
= text_line_char_get(vis
->win
->file
->text
, pos
);
1061 int count
= tabwidth
- (chars
% tabwidth
);
1062 for (int i
= 0; i
< count
; i
++)
1064 spaces
[count
] = '\0';
1065 vis_insert(vis
, pos
, spaces
, count
);
1066 view_cursors_scroll_to(c
, pos
+ count
);
1070 static void copy_indent_from_previous_line(Win
*win
) {
1071 View
*view
= win
->view
;
1072 Text
*text
= win
->file
->text
;
1073 size_t pos
= view_cursor_get(view
);
1074 size_t prev_line
= text_line_prev(text
, pos
);
1075 if (pos
== prev_line
)
1077 size_t begin
= text_line_begin(text
, prev_line
);
1078 size_t start
= text_line_start(text
, begin
);
1079 size_t len
= start
-begin
;
1080 char *buf
= malloc(len
);
1083 len
= text_bytes_get(text
, begin
, len
, buf
);
1084 vis_insert_key(win
->vis
, buf
, len
);
1088 void vis_insert_nl(Vis
*vis
) {
1090 switch (text_newline_type(vis
->win
->file
->text
)) {
1091 case TEXT_NEWLINE_CRNL
:
1099 vis_insert_key(vis
, nl
, strlen(nl
));
1101 if (vis
->autoindent
)
1102 copy_indent_from_previous_line(vis
->win
);
1105 Text
*vis_text(Vis
*vis
) {
1106 return vis
->win
->file
->text
;
1109 View
*vis_view(Vis
*vis
) {
1110 return vis
->win
->view
;
1113 Win
*vis_window(Vis
*vis
) {
1117 Text
*vis_file_text(File
*file
) {
1121 const char *vis_file_name(File
*file
) {