13 #include <sys/select.h>
14 #include <sys/types.h>
17 #include <sys/ioctl.h>
24 #include "text-util.h"
25 #include "text-motions.h"
26 #include "text-objects.h"
33 static void macro_replay(Vis
*vis
, const Macro
*macro
);
34 static void macro_replay_internal(Vis
*vis
, const Macro
*macro
);
35 static void vis_keys_push(Vis
*vis
, const char *input
, size_t pos
, bool record
);
37 bool vis_event_emit(Vis
*vis
, enum VisEvents id
, ...) {
41 if (!vis
->initialized
) {
42 vis
->initialized
= true;
43 vis
->ui
->init(vis
->ui
, vis
);
45 vis
->event
->init(vis
);
56 if (vis
->event
->start
)
57 vis
->event
->start(vis
);
59 case VIS_EVENT_FILE_OPEN
:
60 case VIS_EVENT_FILE_SAVE_PRE
:
61 case VIS_EVENT_FILE_SAVE_POST
:
62 case VIS_EVENT_FILE_CLOSE
:
64 File
*file
= va_arg(ap
, File
*);
67 if (id
== VIS_EVENT_FILE_OPEN
&& vis
->event
->file_open
) {
68 vis
->event
->file_open(vis
, file
);
69 } else if (id
== VIS_EVENT_FILE_SAVE_PRE
&& vis
->event
->file_save_pre
) {
70 const char *path
= va_arg(ap
, const char*);
71 ret
= vis
->event
->file_save_pre(vis
, file
, path
);
72 } else if (id
== VIS_EVENT_FILE_SAVE_POST
&& vis
->event
->file_save_post
) {
73 const char *path
= va_arg(ap
, const char*);
74 vis
->event
->file_save_post(vis
, file
, path
);
75 } else if (id
== VIS_EVENT_FILE_CLOSE
&& vis
->event
->file_close
) {
76 vis
->event
->file_close(vis
, file
);
80 case VIS_EVENT_WIN_OPEN
:
81 case VIS_EVENT_WIN_CLOSE
:
82 case VIS_EVENT_WIN_HIGHLIGHT
:
83 case VIS_EVENT_WIN_STATUS
:
85 Win
*win
= va_arg(ap
, Win
*);
86 if (win
->file
->internal
&& id
!= VIS_EVENT_WIN_STATUS
)
88 if (vis
->event
->win_open
&& id
== VIS_EVENT_WIN_OPEN
) {
89 vis
->event
->win_open(vis
, win
);
90 } else if (vis
->event
->win_close
&& id
== VIS_EVENT_WIN_CLOSE
) {
91 vis
->event
->win_close(vis
, win
);
92 } else if (vis
->event
->win_highlight
&& id
== VIS_EVENT_WIN_HIGHLIGHT
) {
93 vis
->event
->win_highlight(vis
, win
);
94 } else if (vis
->event
->win_status
&& id
== VIS_EVENT_WIN_STATUS
) {
95 vis
->event
->win_status(vis
, win
);
100 if (vis
->event
->quit
)
101 vis
->event
->quit(vis
);
103 case VIS_EVENT_TERM_CSI
:
104 if (vis
->event
->term_csi
)
105 vis
->event
->term_csi(vis
, va_arg(ap
, const long *));
113 /** window / file handling */
115 static void file_free(Vis
*vis
, File
*file
) {
118 if (file
->refcount
> 1) {
122 vis_event_emit(vis
, VIS_EVENT_FILE_CLOSE
, file
);
123 for (size_t i
= 0; i
< LENGTH(file
->marks
); i
++)
124 mark_release(&file
->marks
[i
]);
125 text_free(file
->text
);
126 free((char*)file
->name
);
129 file
->prev
->next
= file
->next
;
131 file
->next
->prev
= file
->prev
;
132 if (vis
->files
== file
)
133 vis
->files
= file
->next
;
137 static File
*file_new_text(Vis
*vis
, Text
*text
) {
138 File
*file
= calloc(1, sizeof(*file
));
143 file
->stat
= text_stat(text
);
144 for (size_t i
= 0; i
< LENGTH(file
->marks
); i
++)
145 mark_init(&file
->marks
[i
]);
147 vis
->files
->prev
= file
;
148 file
->next
= vis
->files
;
153 char *absolute_path(const char *name
) {
156 char *copy1
= strdup(name
);
157 char *copy2
= strdup(name
);
158 char *path_absolute
= NULL
;
159 char path_normalized
[PATH_MAX
] = "";
161 if (!copy1
|| !copy2
)
164 char *dir
= dirname(copy1
);
165 char *base
= basename(copy2
);
166 if (!(path_absolute
= realpath(dir
, NULL
)))
168 if (strcmp(path_absolute
, "/") == 0)
169 path_absolute
[0] = '\0';
171 snprintf(path_normalized
, sizeof(path_normalized
), "%s/%s",
172 path_absolute
, base
);
177 return path_normalized
[0] ? strdup(path_normalized
) : NULL
;
180 static File
*file_new(Vis
*vis
, const char *name
) {
181 char *name_absolute
= NULL
;
183 if (!(name_absolute
= absolute_path(name
)))
185 File
*existing
= NULL
;
186 /* try to detect whether the same file is already open in another window
187 * TODO: do this based on inodes */
188 for (File
*file
= vis
->files
; file
; file
= file
->next
) {
189 if (file
->name
&& strcmp(file
->name
, name_absolute
) == 0) {
201 Text
*text
= text_load_method(name
, vis
->load_method
);
202 if (!text
&& name
&& errno
== ENOENT
)
203 text
= text_load(NULL
);
206 if (!(file
= file_new_text(vis
, text
)))
208 file
->name
= name_absolute
;
209 vis_event_emit(vis
, VIS_EVENT_FILE_OPEN
, file
);
214 file_free(vis
, file
);
218 static File
*file_new_internal(Vis
*vis
, const char *filename
) {
219 File
*file
= file_new(vis
, filename
);
222 file
->internal
= true;
227 void file_name_set(File
*file
, const char *name
) {
228 if (name
== file
->name
)
230 free((char*)file
->name
);
231 file
->name
= absolute_path(name
);
234 const char *file_name_get(File
*file
) {
235 /* TODO: calculate path relative to working directory, cache result */
239 if (!getcwd(cwd
, sizeof cwd
))
241 const char *path
= strstr(file
->name
, cwd
);
242 if (path
!= file
->name
)
244 size_t cwdlen
= strlen(cwd
);
245 return file
->name
[cwdlen
] == '/' ? file
->name
+cwdlen
+1 : file
->name
;
248 void vis_window_status(Win
*win
, const char *status
) {
249 win
->ui
->status(win
->ui
, status
);
252 void window_selection_save(Win
*win
) {
254 View
*view
= win
->view
;
255 Array sel
= view_selections_get_all(view
);
256 vis_mark_set(win
, VIS_MARK_SELECTION
, &sel
);
258 vis_jumplist_save(vis
);
262 static void window_free(Win
*win
) {
266 for (Win
*other
= vis
->windows
; other
; other
= other
->next
) {
267 if (other
->parent
== win
)
268 other
->parent
= NULL
;
271 vis
->ui
->window_free(win
->ui
);
272 view_free(win
->view
);
273 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++)
274 map_free(win
->modes
[i
].bindings
);
275 marklist_release(&win
->jumplist
);
276 mark_release(&win
->saved_selections
);
280 static void window_draw_colorcolumn(Win
*win
) {
281 View
*view
= win
->view
;
282 int cc
= view_colorcolumn_get(view
);
285 CellStyle style
= win
->ui
->style_get(win
->ui
, UI_STYLE_COLOR_COLUMN
);
287 int line_cols
= 0; /* Track the number of columns we've passed on each line */
288 bool line_cc_set
= false; /* Has the colorcolumn attribute been set for this line yet */
289 int width
= view_width_get(view
);
291 for (Line
*l
= view_lines_first(view
); l
; l
= l
->next
) {
292 if (l
->lineno
!= lineno
) {
295 if (!(lineno
= l
->lineno
))
301 /* This screen line contains the cell we want to highlight */
302 if (cc
<= line_cols
+ width
) {
303 CellStyle
*orig
= &l
->cells
[cc
- 1 - line_cols
].style
;
304 orig
->attr
= style
.attr
;
305 orig
->fg
= is_default_color(style
.fg
) ? orig
->fg
: style
.fg
;
306 orig
->bg
= is_default_color(style
.bg
) ? orig
->bg
: style
.bg
;
314 static void window_draw_cursorline(Win
*win
) {
316 View
*view
= win
->view
;
317 enum UiOption options
= view_options_get(view
);
318 if (!(options
& UI_OPTION_CURSOR_LINE
))
320 if (vis
->mode
->visual
|| vis
->win
!= win
)
322 if (view_selections_count(view
) > 1)
325 int width
= view_width_get(view
);
326 CellStyle style
= win
->ui
->style_get(win
->ui
, UI_STYLE_CURSOR_LINE
);
327 Selection
*sel
= view_selections_primary_get(view
);
328 size_t lineno
= view_cursors_line_get(sel
)->lineno
;
329 for (Line
*l
= view_lines_first(view
); l
; l
= l
->next
) {
330 if (l
->lineno
== lineno
) {
331 for (int x
= 0; x
< width
; x
++) {
332 l
->cells
[x
].style
.attr
|= style
.attr
;
333 l
->cells
[x
].style
.bg
= style
.bg
;
335 } else if (l
->lineno
> lineno
) {
341 static void window_draw_selection(View
*view
, Selection
*cur
, CellStyle
*style
) {
342 Filerange sel
= view_selections_get(cur
);
343 if (!text_range_valid(&sel
))
345 Line
*start_line
; int start_col
;
346 Line
*end_line
; int end_col
;
347 view_coord_get(view
, sel
.start
, &start_line
, NULL
, &start_col
);
348 view_coord_get(view
, sel
.end
, &end_line
, NULL
, &end_col
);
349 if (!start_line
&& !end_line
)
352 start_line
= view_lines_first(view
);
356 end_line
= view_lines_last(view
);
357 end_col
= end_line
->width
;
359 for (Line
*l
= start_line
; l
!= end_line
->next
; l
= l
->next
) {
360 int col
= (l
== start_line
) ? start_col
: 0;
361 int end
= (l
== end_line
) ? end_col
: l
->width
;
363 if (cell_color_equal(l
->cells
[col
].style
.fg
, style
->bg
)) {
364 CellStyle old
= l
->cells
[col
].style
;
365 if (!cell_color_equal(old
.fg
, old
.bg
)) {
366 l
->cells
[col
].style
.fg
= old
.bg
;
367 l
->cells
[col
].style
.bg
= old
.fg
;
369 l
->cells
[col
].style
.attr
= style
->attr
;
372 l
->cells
[col
].style
.bg
= style
->bg
;
379 static void window_draw_cursor_matching(Win
*win
, Selection
*cur
, CellStyle
*style
) {
380 if (win
->vis
->mode
->visual
)
382 Line
*line_match
; int col_match
;
383 size_t pos
= view_cursors_pos(cur
);
384 Filerange limits
= view_viewport_get(win
->view
);
385 size_t pos_match
= text_bracket_match_symbol(win
->file
->text
, pos
, "(){}[]\"'`", &limits
);
386 if (pos
== pos_match
)
388 if (!view_coord_get(win
->view
, pos_match
, &line_match
, NULL
, &col_match
))
390 if (cell_color_equal(line_match
->cells
[col_match
].style
.fg
, style
->fg
)) {
391 CellStyle old
= line_match
->cells
[col_match
].style
;
392 line_match
->cells
[col_match
].style
.fg
= old
.bg
;
393 line_match
->cells
[col_match
].style
.bg
= old
.fg
;
395 line_match
->cells
[col_match
].style
.bg
= style
->bg
;
399 static void window_draw_cursor(Win
*win
, Selection
*cur
, CellStyle
*style
, CellStyle
*sel_style
) {
400 if (win
->vis
->win
!= win
)
402 Line
*line
= view_cursors_line_get(cur
);
403 int col
= view_cursors_cell_get(cur
);
404 if (!line
|| col
== -1)
406 line
->cells
[col
].style
= *style
;
407 window_draw_cursor_matching(win
, cur
, sel_style
);
411 static void window_draw_selections(Win
*win
) {
412 View
*view
= win
->view
;
413 Filerange viewport
= view_viewport_get(view
);
414 Selection
*sel
= view_selections_primary_get(view
);
415 CellStyle style_cursor
= win
->ui
->style_get(win
->ui
, UI_STYLE_CURSOR
);
416 CellStyle style_cursor_primary
= win
->ui
->style_get(win
->ui
, UI_STYLE_CURSOR_PRIMARY
);
417 CellStyle style_selection
= win
->ui
->style_get(win
->ui
, UI_STYLE_SELECTION
);
418 for (Selection
*s
= view_selections_prev(sel
); s
; s
= view_selections_prev(s
)) {
419 window_draw_selection(win
->view
, s
, &style_selection
);
420 size_t pos
= view_cursors_pos(s
);
421 if (pos
< viewport
.start
)
423 window_draw_cursor(win
, s
, &style_cursor
, &style_selection
);
425 window_draw_selection(win
->view
, sel
, &style_selection
);
426 window_draw_cursor(win
, sel
, &style_cursor_primary
, &style_selection
);
427 for (Selection
*s
= view_selections_next(sel
); s
; s
= view_selections_next(s
)) {
428 window_draw_selection(win
->view
, s
, &style_selection
);
429 size_t pos
= view_cursors_pos(s
);
430 if (pos
> viewport
.end
)
432 window_draw_cursor(win
, s
, &style_cursor
, &style_selection
);
436 static void window_draw_eof(Win
*win
) {
437 View
*view
= win
->view
;
438 if (view_width_get(view
) == 0)
440 CellStyle style
= win
->ui
->style_get(win
->ui
, UI_STYLE_EOF
);
441 for (Line
*l
= view_lines_last(view
)->next
; l
; l
= l
->next
) {
442 strncpy(l
->cells
[0].data
, view_symbol_eof_get(view
), sizeof(l
->cells
[0].data
)-1);
443 l
->cells
[0].style
= style
;
447 void vis_window_draw(Win
*win
) {
448 if (!win
->ui
|| !view_update(win
->view
))
451 vis_event_emit(vis
, VIS_EVENT_WIN_HIGHLIGHT
, win
);
453 window_draw_colorcolumn(win
);
454 window_draw_cursorline(win
);
455 if (!vis
->win
|| vis
->win
== win
|| vis
->win
->parent
== win
)
456 window_draw_selections(win
);
457 window_draw_eof(win
);
459 vis_event_emit(vis
, VIS_EVENT_WIN_STATUS
, win
);
463 void vis_window_invalidate(Win
*win
) {
464 for (Win
*w
= win
->vis
->windows
; w
; w
= w
->next
) {
465 if (w
->file
== win
->file
)
470 Win
*window_new_file(Vis
*vis
, File
*file
, enum UiOption options
) {
471 Win
*win
= calloc(1, sizeof(Win
));
476 win
->view
= view_new(file
->text
);
477 win
->ui
= vis
->ui
->window_new(vis
->ui
, win
, options
);
478 if (!win
->view
|| !win
->ui
) {
482 marklist_init(&win
->jumplist
, 32);
483 mark_init(&win
->saved_selections
);
485 view_options_set(win
->view
, view_options_get(win
->view
));
486 view_tabwidth_set(win
->view
, vis
->tabwidth
);
489 vis
->windows
->prev
= win
;
490 win
->next
= vis
->windows
;
493 vis
->ui
->window_focus(win
->ui
);
494 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++)
495 win
->modes
[i
].parent
= &vis_modes
[i
];
496 vis_event_emit(vis
, VIS_EVENT_WIN_OPEN
, win
);
500 bool vis_window_reload(Win
*win
) {
501 const char *name
= win
->file
->name
;
503 return false; /* can't reload unsaved file */
504 /* temporarily unset file name, otherwise file_new returns the same File */
505 win
->file
->name
= NULL
;
506 File
*file
= file_new(win
->vis
, name
);
507 win
->file
->name
= name
;
510 file_free(win
->vis
, win
->file
);
513 view_reload(win
->view
, file
->text
);
517 bool vis_window_split(Win
*original
) {
518 Win
*win
= window_new_file(original
->vis
, original
->file
, UI_OPTION_STATUSBAR
);
521 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++) {
522 if (original
->modes
[i
].bindings
)
523 win
->modes
[i
].bindings
= map_new();
524 if (win
->modes
[i
].bindings
)
525 map_copy(win
->modes
[i
].bindings
, original
->modes
[i
].bindings
);
527 win
->file
= original
->file
;
528 view_options_set(win
->view
, view_options_get(original
->view
));
529 view_cursor_to(win
->view
, view_cursor_get(original
->view
));
533 void vis_window_focus(Win
*win
) {
538 vis
->ui
->window_focus(win
->ui
);
541 void vis_window_next(Vis
*vis
) {
545 vis_window_focus(sel
->next
? sel
->next
: vis
->windows
);
548 void vis_window_prev(Vis
*vis
) {
554 for (sel
= vis
->windows
; sel
->next
; sel
= sel
->next
);
555 vis_window_focus(sel
);
558 int vis_window_width_get(const Win
*win
) {
559 return win
->ui
->window_width(win
->ui
);
562 int vis_window_height_get(const Win
*win
) {
563 return win
->ui
->window_height(win
->ui
);
566 void vis_draw(Vis
*vis
) {
567 for (Win
*win
= vis
->windows
; win
; win
= win
->next
)
568 view_draw(win
->view
);
571 void vis_redraw(Vis
*vis
) {
572 vis
->ui
->redraw(vis
->ui
);
576 void vis_update(Vis
*vis
) {
577 vis
->ui
->draw(vis
->ui
);
580 void vis_suspend(Vis
*vis
) {
581 vis
->ui
->suspend(vis
->ui
);
584 void vis_resume(Vis
*vis
) {
585 vis
->ui
->resume(vis
->ui
);
588 bool vis_window_new(Vis
*vis
, const char *filename
) {
589 File
*file
= file_new(vis
, filename
);
592 Win
*win
= window_new_file(vis
, file
, UI_OPTION_STATUSBAR
|UI_OPTION_SYMBOL_EOF
);
594 file_free(vis
, file
);
601 bool vis_window_new_fd(Vis
*vis
, int fd
) {
604 if (!vis_window_new(vis
, NULL
))
606 vis
->win
->file
->fd
= fd
;
610 bool vis_window_closable(Win
*win
) {
611 if (!win
|| !text_modified(win
->file
->text
))
613 return win
->file
->refcount
> 1;
616 void vis_window_swap(Win
*a
, Win
*b
) {
617 if (a
== b
|| !a
|| !b
)
634 if (vis
->windows
== a
)
636 else if (vis
->windows
== b
)
638 vis
->ui
->window_swap(a
->ui
, b
->ui
);
641 else if (vis
->win
== b
)
645 void vis_window_close(Win
*win
) {
649 vis_event_emit(vis
, VIS_EVENT_WIN_CLOSE
, win
);
650 file_free(vis
, win
->file
);
652 win
->prev
->next
= win
->next
;
654 win
->next
->prev
= win
->prev
;
655 if (vis
->windows
== win
)
656 vis
->windows
= win
->next
;
658 vis
->win
= win
->next
? win
->next
: win
->prev
;
659 if (win
== vis
->message_window
)
660 vis
->message_window
= NULL
;
663 vis
->ui
->window_focus(vis
->win
->ui
);
667 Vis
*vis_new(Ui
*ui
, VisEvent
*event
) {
670 Vis
*vis
= calloc(1, sizeof(Vis
));
673 vis
->exit_status
= -1;
676 vis
->expandtab
= false;
677 vis
->change_colors
= true;
678 for (size_t i
= 0; i
< LENGTH(vis
->registers
); i
++)
679 register_init(&vis
->registers
[i
]);
680 vis
->registers
[VIS_REG_BLACKHOLE
].type
= REGISTER_BLACKHOLE
;
681 vis
->registers
[VIS_REG_CLIPBOARD
].type
= REGISTER_CLIPBOARD
;
682 vis
->registers
[VIS_REG_PRIMARY
].type
= REGISTER_CLIPBOARD
;
683 vis
->registers
[VIS_REG_NUMBER
].type
= REGISTER_NUMBER
;
684 array_init(&vis
->operators
);
685 array_init(&vis
->motions
);
686 array_init(&vis
->textobjects
);
687 array_init(&vis
->bindings
);
688 array_init(&vis
->actions_user
);
689 action_reset(&vis
->action
);
690 buffer_init(&vis
->input_queue
);
691 if (!(vis
->command_file
= file_new_internal(vis
, NULL
)))
693 if (!(vis
->search_file
= file_new_internal(vis
, NULL
)))
695 if (!(vis
->error_file
= file_new_internal(vis
, NULL
)))
697 if (!(vis
->actions
= map_new()))
699 if (!(vis
->keymap
= map_new()))
704 char *shell
= getenv("SHELL");
705 if ((!shell
|| !*shell
) && (pw
= getpwuid(getuid())))
706 shell
= pw
->pw_shell
;
707 if (!shell
|| !*shell
)
709 if (!(vis
->shell
= strdup(shell
)))
711 vis
->mode_prev
= vis
->mode
= &vis_modes
[VIS_MODE_NORMAL
];
714 if (event
->mode_insert_input
)
715 vis_modes
[VIS_MODE_INSERT
].input
= event
->mode_insert_input
;
716 if (event
->mode_replace_input
)
717 vis_modes
[VIS_MODE_REPLACE
].input
= event
->mode_replace_input
;
725 void vis_free(Vis
*vis
) {
728 vis_event_emit(vis
, VIS_EVENT_QUIT
);
731 vis_window_close(vis
->windows
);
732 file_free(vis
, vis
->command_file
);
733 file_free(vis
, vis
->search_file
);
734 file_free(vis
, vis
->error_file
);
735 for (int i
= 0; i
< LENGTH(vis
->registers
); i
++)
736 register_release(&vis
->registers
[i
]);
737 vis
->ui
->free(vis
->ui
);
740 while (map_first(vis
->usercmds
, &name
) && vis_cmd_unregister(vis
, name
));
742 map_free(vis
->usercmds
);
746 while (map_first(vis
->options
, &name
) && vis_option_unregister(vis
, name
));
748 map_free(vis
->options
);
749 map_free(vis
->actions
);
750 map_free(vis
->keymap
);
751 buffer_release(&vis
->input_queue
);
752 for (int i
= 0; i
< VIS_MODE_INVALID
; i
++)
753 map_free(vis_modes
[i
].bindings
);
754 array_release_full(&vis
->operators
);
755 array_release_full(&vis
->motions
);
756 array_release_full(&vis
->textobjects
);
757 while (array_length(&vis
->bindings
))
758 vis_binding_free(vis
, array_get_ptr(&vis
->bindings
, 0));
759 array_release(&vis
->bindings
);
760 while (array_length(&vis
->actions_user
))
761 vis_action_free(vis
, array_get_ptr(&vis
->actions_user
, 0));
762 array_release(&vis
->actions_user
);
767 void vis_insert(Vis
*vis
, size_t pos
, const char *data
, size_t len
) {
771 text_insert(win
->file
->text
, pos
, data
, len
);
772 vis_window_invalidate(win
);
775 void vis_insert_key(Vis
*vis
, const char *data
, size_t len
) {
779 for (Selection
*s
= view_selections(win
->view
); s
; s
= view_selections_next(s
)) {
780 size_t pos
= view_cursors_pos(s
);
781 vis_insert(vis
, pos
, data
, len
);
782 view_cursors_scroll_to(s
, pos
+ len
);
786 void vis_replace(Vis
*vis
, size_t pos
, const char *data
, size_t len
) {
790 Text
*txt
= win
->file
->text
;
791 Iterator it
= text_iterator_get(txt
, pos
);
792 int chars
= text_char_count(data
, len
);
793 for (char c
; chars
-- > 0 && text_iterator_byte_get(&it
, &c
) && c
!= '\n'; )
794 text_iterator_char_next(&it
, NULL
);
796 text_delete(txt
, pos
, it
.pos
- pos
);
797 vis_insert(vis
, pos
, data
, len
);
800 void vis_replace_key(Vis
*vis
, const char *data
, size_t len
) {
804 for (Selection
*s
= view_selections(win
->view
); s
; s
= view_selections_next(s
)) {
805 size_t pos
= view_cursors_pos(s
);
806 vis_replace(vis
, pos
, data
, len
);
807 view_cursors_scroll_to(s
, pos
+ len
);
811 void vis_delete(Vis
*vis
, size_t pos
, size_t len
) {
815 text_delete(win
->file
->text
, pos
, len
);
816 vis_window_invalidate(win
);
819 bool vis_action_register(Vis
*vis
, const KeyAction
*action
) {
820 return map_put(vis
->actions
, action
->name
, action
);
823 bool vis_keymap_add(Vis
*vis
, const char *key
, const char *mapping
) {
824 return map_put(vis
->keymap
, key
, mapping
);
827 void vis_keymap_disable(Vis
*vis
) {
828 vis
->keymap_disabled
= true;
831 void vis_interrupt(Vis
*vis
) {
832 vis
->interrupted
= true;
835 bool vis_interrupt_requested(Vis
*vis
) {
836 return vis
->interrupted
;
839 void vis_do(Vis
*vis
) {
843 File
*file
= win
->file
;
844 Text
*txt
= file
->text
;
845 View
*view
= win
->view
;
846 Action
*a
= &vis
->action
;
848 int count
= MAX(a
->count
, 1);
849 if (a
->op
== &vis_operators
[VIS_OP_MODESWITCH
])
850 count
= 1; /* count should apply to inserted text not motion */
851 bool repeatable
= a
->op
&& !vis
->macro_operator
&& !vis
->win
->parent
;
852 bool multiple_cursors
= view_selections_count(view
) > 1;
854 bool linewise
= !(a
->type
& CHARWISE
) && (
855 a
->type
& LINEWISE
|| (a
->movement
&& a
->movement
->type
& LINEWISE
) ||
856 vis
->mode
== &vis_modes
[VIS_MODE_VISUAL_LINE
]);
858 Register
*reg
= a
->reg
;
859 size_t reg_slot
= multiple_cursors
? EPOS
: 0;
860 size_t last_reg_slot
= reg_slot
;
862 reg
= &vis
->registers
[file
->internal
? VIS_REG_PROMPT
: VIS_REG_DEFAULT
];
863 if (a
->op
== &vis_operators
[VIS_OP_PUT_AFTER
] && multiple_cursors
&& vis_register_count(vis
, reg
) == 1)
866 if (vis
->mode
->visual
&& a
->op
)
867 window_selection_save(win
);
869 for (Selection
*sel
= view_selections(view
), *next
; sel
; sel
= next
) {
870 if (vis
->interrupted
)
873 next
= view_selections_next(sel
);
875 size_t pos
= view_cursors_pos(sel
);
877 if (!view_selections_dispose(sel
))
878 view_cursors_to(sel
, 0);
882 OperatorContext c
= {
886 .range
= text_range_empty(),
888 .reg_slot
= reg_slot
== EPOS
? (size_t)view_selections_number(sel
) : reg_slot
,
889 .linewise
= linewise
,
891 .context
= a
->op
? a
->op
->context
: NULL
,
894 last_reg_slot
= c
.reg_slot
;
899 for (int i
= 0; i
< count
; i
++) {
900 size_t pos_prev
= pos
;
901 if (a
->movement
->txt
)
902 pos
= a
->movement
->txt(txt
, pos
);
903 else if (a
->movement
->cur
)
904 pos
= a
->movement
->cur(sel
);
905 else if (a
->movement
->file
)
906 pos
= a
->movement
->file(vis
, file
, sel
);
907 else if (a
->movement
->vis
)
908 pos
= a
->movement
->vis(vis
, txt
, pos
);
909 else if (a
->movement
->view
)
910 pos
= a
->movement
->view(vis
, view
);
911 else if (a
->movement
->win
)
912 pos
= a
->movement
->win(vis
, win
, pos
);
913 else if (a
->movement
->user
)
914 pos
= a
->movement
->user(vis
, win
, a
->movement
->data
, pos
);
915 if (pos
== EPOS
|| a
->movement
->type
& IDEMPOTENT
|| pos
== pos_prev
) {
916 err
= a
->movement
->type
& COUNT_EXACT
;
927 c
.range
.start
= start
;
931 c
.range
= text_range_new(start
, pos
);
936 if (a
->movement
->type
& CHARWISE
)
937 view_cursors_scroll_to(sel
, pos
);
939 view_cursors_to(sel
, pos
);
940 if (vis
->mode
->visual
)
941 c
.range
= view_selections_get(sel
);
942 } else if (a
->movement
->type
& INCLUSIVE
&& c
.range
.end
> start
) {
943 c
.range
.end
= text_char_next(txt
, c
.range
.end
);
944 } else if (linewise
&& (a
->movement
->type
& LINEWISE_INCLUSIVE
)) {
945 c
.range
.end
= text_char_next(txt
, c
.range
.end
);
947 } else if (a
->textobj
) {
948 if (vis
->mode
->visual
)
949 c
.range
= view_selections_get(sel
);
951 c
.range
.start
= c
.range
.end
= pos
;
952 for (int i
= 0; i
< count
; i
++) {
953 Filerange r
= text_range_empty();
955 r
= a
->textobj
->txt(txt
, pos
);
956 else if (a
->textobj
->vis
)
957 r
= a
->textobj
->vis(vis
, txt
, pos
);
958 else if (a
->textobj
->user
)
959 r
= a
->textobj
->user(vis
, win
, a
->textobj
->data
, pos
);
960 if (!text_range_valid(&r
))
962 if (a
->textobj
->type
& TEXTOBJECT_DELIMITED_OUTER
) {
967 if (vis
->mode
->visual
|| (i
> 0 && !(a
->textobj
->type
& TEXTOBJECT_NON_CONTIGUOUS
)))
968 c
.range
= text_range_union(&c
.range
, &r
);
973 if (a
->textobj
->type
& TEXTOBJECT_EXTEND_BACKWARD
) {
975 if ((a
->textobj
->type
& TEXTOBJECT_DELIMITED_INNER
) && pos
> 0)
979 if (a
->textobj
->type
& TEXTOBJECT_DELIMITED_INNER
)
984 } else if (vis
->mode
->visual
) {
985 c
.range
= view_selections_get(sel
);
986 if (!text_range_valid(&c
.range
))
987 c
.range
.start
= c
.range
.end
= pos
;
990 if (linewise
&& vis
->mode
!= &vis_modes
[VIS_MODE_VISUAL
])
991 c
.range
= text_range_linewise(txt
, &c
.range
);
992 if (vis
->mode
->visual
) {
993 view_selections_set(sel
, &c
.range
);
994 view_selections_anchor(sel
, true);
998 size_t pos
= a
->op
->func(vis
, txt
, &c
);
1000 view_selections_dispose(sel
);
1001 } else if (pos
<= text_size(txt
)) {
1002 view_selection_clear(sel
);
1003 view_cursors_to(sel
, pos
);
1008 view_selections_normalize(view
);
1009 if (a
->movement
&& (a
->movement
->type
& JUMP
))
1010 vis_jumplist_save(vis
);
1014 if (a
->op
== &vis_operators
[VIS_OP_YANK
] ||
1015 a
->op
== &vis_operators
[VIS_OP_DELETE
] ||
1016 a
->op
== &vis_operators
[VIS_OP_CHANGE
] ||
1017 a
->op
== &vis_operators
[VIS_OP_REPLACE
]) {
1018 register_resize(reg
, last_reg_slot
+1);
1021 /* we do not support visual repeat, still do something resonable */
1022 if (vis
->mode
->visual
&& !a
->movement
&& !a
->textobj
)
1023 a
->movement
= &vis_motions
[VIS_MOVE_NOP
];
1025 /* operator implementations must not change the mode,
1026 * they might get called multiple times (once for every cursor)
1028 if (a
->op
== &vis_operators
[VIS_OP_CHANGE
]) {
1029 vis_mode_switch(vis
, VIS_MODE_INSERT
);
1030 } else if (a
->op
== &vis_operators
[VIS_OP_MODESWITCH
]) {
1031 vis_mode_switch(vis
, a
->mode
);
1032 } else if (vis
->mode
== &vis_modes
[VIS_MODE_OPERATOR_PENDING
]) {
1033 mode_set(vis
, vis
->mode_prev
);
1034 } else if (vis
->mode
->visual
) {
1035 vis_mode_switch(vis
, VIS_MODE_NORMAL
);
1038 if (vis
->mode
== &vis_modes
[VIS_MODE_NORMAL
])
1039 vis_file_snapshot(vis
, file
);
1043 if (a
!= &vis
->action_prev
) {
1046 a
->macro
= vis
->macro_operator
;
1047 vis
->action_prev
= *a
;
1053 void action_reset(Action
*a
) {
1054 memset(a
, 0, sizeof(*a
));
1055 a
->count
= VIS_COUNT_UNKNOWN
;
1058 void vis_cancel(Vis
*vis
) {
1059 action_reset(&vis
->action
);
1062 void vis_die(Vis
*vis
, const char *msg
, ...) {
1065 vis
->ui
->die(vis
->ui
, msg
, ap
);
1069 const char *vis_keys_next(Vis
*vis
, const char *keys
) {
1070 if (!keys
|| !*keys
)
1073 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1074 const char *next
= NULL
;
1075 /* first try to parse a special key of the form <Key> */
1076 if (*keys
== '<' && keys
[1] && (next
= termkey_strpkey(termkey
, keys
+1, &key
, TERMKEY_FORMAT_VIM
)) && *next
== '>')
1078 if (strncmp(keys
, "<vis-", 5) == 0) {
1079 const char *start
= keys
+ 1, *end
= start
;
1080 while (*end
&& *end
!= '>')
1082 if (end
> start
&& end
- start
- 1 < VIS_KEY_LENGTH_MAX
&& *end
== '>') {
1083 char key
[VIS_KEY_LENGTH_MAX
];
1084 memcpy(key
, start
, end
- start
);
1085 key
[end
- start
] = '\0';
1086 if (map_get(vis
->actions
, key
))
1092 while (!ISUTF8(*keys
))
1097 long vis_keys_codepoint(Vis
*vis
, const char *keys
) {
1098 long codepoint
= -1;
1101 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1105 if (keys
[0] == '<' && !keys
[1])
1108 if (keys
[0] == '<' && (next
= termkey_strpkey(termkey
, keys
+1, &key
, TERMKEY_FORMAT_VIM
)) && *next
== '>')
1109 codepoint
= (key
.type
== TERMKEY_TYPE_UNICODE
) ? key
.code
.codepoint
: -1;
1110 else if ((next
= termkey_strpkey(termkey
, keys
, &key
, TERMKEY_FORMAT_VIM
)))
1111 codepoint
= (key
.type
== TERMKEY_TYPE_UNICODE
) ? key
.code
.codepoint
: -1;
1113 if (codepoint
!= -1) {
1114 if (key
.modifiers
== TERMKEY_KEYMOD_CTRL
)
1119 if (!next
|| key
.type
!= TERMKEY_TYPE_KEYSYM
)
1122 const int keysym
[] = {
1123 TERMKEY_SYM_ENTER
, '\n',
1124 TERMKEY_SYM_TAB
, '\t',
1125 TERMKEY_SYM_BACKSPACE
, '\b',
1126 TERMKEY_SYM_ESCAPE
, 0x1b,
1127 TERMKEY_SYM_DELETE
, 0x7f,
1131 for (const int *k
= keysym
; k
[0]; k
+= 2) {
1132 if (key
.code
.sym
== k
[0])
1139 bool vis_keys_utf8(Vis
*vis
, const char *keys
, char utf8
[static UTFmax
+1]) {
1140 Rune rune
= vis_keys_codepoint(vis
, keys
);
1141 if (rune
== (Rune
)-1)
1143 size_t len
= runetochar(utf8
, &rune
);
1150 size_t len
; // length of the prefix
1151 int count
; // how many bindings can complete this prefix
1152 bool angle_bracket
; // does the prefix end with '<'
1155 static bool isprefix(const char *key
, void *value
, void *data
) {
1156 PrefixCompletion
*completion
= data
;
1157 if (!completion
->angle_bracket
) {
1158 completion
->count
++;
1160 const char *start
= key
+ completion
->len
;
1161 const char *end
= vis_keys_next(completion
->vis
, start
);
1162 if (end
&& start
+ 1 == end
)
1163 completion
->count
++;
1165 return completion
->count
== 1;
1168 static void vis_keys_process(Vis
*vis
, size_t pos
) {
1169 Buffer
*buf
= &vis
->input_queue
;
1170 char *keys
= buf
->data
+ pos
, *start
= keys
, *cur
= keys
, *end
= keys
, *binding_end
= keys
;;
1171 bool prefix
= false;
1172 KeyBinding
*binding
= NULL
;
1174 while (cur
&& *cur
) {
1176 if (!(end
= (char*)vis_keys_next(vis
, cur
))) {
1177 buffer_remove(buf
, keys
- buf
->data
, strlen(keys
));
1185 for (Mode
*global_mode
= vis
->mode
; global_mode
&& !prefix
; global_mode
= global_mode
->parent
) {
1186 for (int global
= 0; global
< 2 && !prefix
; global
++) {
1187 Mode
*mode
= (global
|| !vis
->win
) ?
1189 &vis
->win
->modes
[global_mode
->id
];
1190 if (!mode
->bindings
)
1192 /* keep track of longest matching binding */
1193 KeyBinding
*match
= map_get(mode
->bindings
, start
);
1194 if (match
&& end
> binding_end
) {
1199 const Map
*pmap
= map_prefix(mode
->bindings
, start
);
1200 PrefixCompletion completions
= {
1204 .angle_bracket
= !strcmp(cur
, "<"),
1206 map_iterate(pmap
, isprefix
, &completions
);
1208 prefix
= (!match
&& completions
.count
> 0) ||
1209 ( match
&& completions
.count
> 1);
1216 /* input sofar is ambigious, wait for more */
1219 } else if (binding
) { /* exact match */
1220 if (binding
->action
) {
1221 size_t len
= binding_end
- start
;
1222 strcpy(vis
->key_prev
, vis
->key_current
);
1223 strncpy(vis
->key_current
, start
, len
);
1224 vis
->key_current
[len
] = '\0';
1225 end
= (char*)binding
->action
->func(vis
, binding_end
, &binding
->action
->arg
);
1231 } else if (binding
->alias
) {
1232 buffer_remove(buf
, start
- buf
->data
, binding_end
- start
);
1233 buffer_insert0(buf
, start
- buf
->data
, binding
->alias
);
1237 binding_end
= start
;
1238 } else { /* no keybinding */
1239 KeyAction
*action
= NULL
;
1240 if (start
[0] == '<' && end
[-1] == '>') {
1241 /* test for special editor key command */
1244 action
= map_get(vis
->actions
, start
+1);
1247 size_t len
= end
- start
;
1248 strcpy(vis
->key_prev
, vis
->key_current
);
1249 strncpy(vis
->key_current
, start
, len
);
1250 vis
->key_current
[len
] = '\0';
1251 end
= (char*)action
->func(vis
, end
, &action
->arg
);
1258 if (!action
&& vis
->mode
->input
) {
1259 end
= (char*)vis_keys_next(vis
, start
);
1260 vis
->mode
->input(vis
, start
, end
- start
);
1266 buffer_remove(buf
, keys
- buf
->data
, end
- keys
);
1269 void vis_keys_feed(Vis
*vis
, const char *input
) {
1274 if (!macro_append(¯o
, input
))
1276 /* use internal function, to keep Lua based tests which use undo points working */
1277 macro_replay_internal(vis
, ¯o
);
1278 macro_release(¯o
);
1281 static void vis_keys_push(Vis
*vis
, const char *input
, size_t pos
, bool record
) {
1284 if (record
&& vis
->recording
)
1285 macro_append(vis
->recording
, input
);
1286 if (vis
->macro_operator
)
1287 macro_append(vis
->macro_operator
, input
);
1288 if (buffer_append0(&vis
->input_queue
, input
))
1289 vis_keys_process(vis
, pos
);
1292 static const char *getkey(Vis
*vis
) {
1293 TermKeyKey key
= { 0 };
1294 if (!vis
->ui
->getkey(vis
->ui
, &key
))
1297 bool use_keymap
= vis
->mode
->id
!= VIS_MODE_INSERT
&&
1298 vis
->mode
->id
!= VIS_MODE_REPLACE
&&
1299 !vis
->keymap_disabled
;
1300 vis
->keymap_disabled
= false;
1301 if (key
.type
== TERMKEY_TYPE_UNICODE
&& use_keymap
) {
1302 const char *mapped
= map_get(vis
->keymap
, key
.utf8
);
1304 size_t len
= strlen(mapped
)+1;
1305 if (len
<= sizeof(key
.utf8
))
1306 memcpy(key
.utf8
, mapped
, len
);
1310 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1311 if (key
.type
== TERMKEY_TYPE_UNKNOWN_CSI
) {
1315 if (termkey_interpret_csi(termkey
, &key
, &args
[2], &nargs
, &cmd
) == TERMKEY_RES_KEY
) {
1316 args
[0] = (long)cmd
;
1318 vis_event_emit(vis
, VIS_EVENT_TERM_CSI
, args
);
1322 termkey_strfkey(termkey
, vis
->key
, sizeof(vis
->key
), &key
, TERMKEY_FORMAT_VIM
);
1326 bool vis_signal_handler(Vis
*vis
, int signum
, const siginfo_t
*siginfo
, const void *context
) {
1329 for (File
*file
= vis
->files
; file
; file
= file
->next
) {
1330 if (text_mmaped(file
->text
, siginfo
->si_addr
))
1331 file
->truncated
= true;
1335 siglongjmp(vis
->sigbus_jmpbuf
, 1);
1338 vis
->interrupted
= true;
1344 vis
->need_resize
= true;
1348 vis
->terminate
= true;
1354 int vis_run(Vis
*vis
) {
1356 return EXIT_SUCCESS
;
1357 if (vis
->exit_status
!= -1)
1358 return vis
->exit_status
;
1359 vis
->running
= true;
1361 vis_event_emit(vis
, VIS_EVENT_START
);
1363 struct timespec idle
= { .tv_nsec
= 0 }, *timeout
= NULL
;
1366 sigemptyset(&emptyset
);
1368 vis
->exit_status
= EXIT_SUCCESS
;
1370 sigsetjmp(vis
->sigbus_jmpbuf
, 1);
1372 while (vis
->running
) {
1375 FD_SET(STDIN_FILENO
, &fds
);
1379 for (Win
*next
, *win
= vis
->windows
; win
; win
= next
) {
1381 if (win
->file
->truncated
) {
1383 name
= strdup(win
->file
->name
);
1384 vis_window_close(win
);
1388 vis_die(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
1390 vis_info_show(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
1391 vis
->sigbus
= false;
1396 vis_die(vis
, "Killed by SIGTERM\n");
1397 if (vis
->interrupted
) {
1398 vis
->interrupted
= false;
1399 vis_keys_feed(vis
, "<C-c>");
1404 vis
->resume
= false;
1407 if (vis
->need_resize
) {
1408 vis
->ui
->resize(vis
->ui
);
1409 vis
->need_resize
= false;
1413 idle
.tv_sec
= vis
->mode
->idle_timeout
;
1414 int r
= pselect(1, &fds
, NULL
, NULL
, timeout
, &emptyset
);
1415 if (r
== -1 && errno
== EINTR
)
1419 /* TODO save all pending changes to a ~suffixed file */
1420 vis_die(vis
, "Error in mainloop: %s\n", strerror(errno
));
1423 if (!FD_ISSET(STDIN_FILENO
, &fds
)) {
1424 if (vis
->mode
->idle
)
1425 vis
->mode
->idle(vis
);
1430 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1431 termkey_advisereadable(termkey
);
1434 while ((key
= getkey(vis
)))
1435 vis_keys_push(vis
, key
, 0, true);
1437 if (vis
->mode
->idle
)
1440 return vis
->exit_status
;
1443 Macro
*macro_get(Vis
*vis
, enum VisRegister id
) {
1444 if (id
== VIS_MACRO_LAST_RECORDED
)
1445 return vis
->last_recording
;
1446 if (VIS_REG_A
<= id
&& id
<= VIS_REG_Z
)
1448 if (id
< LENGTH(vis
->registers
))
1449 return array_get(&vis
->registers
[id
].values
, 0);
1453 void macro_operator_record(Vis
*vis
) {
1454 if (vis
->macro_operator
)
1456 vis
->macro_operator
= macro_get(vis
, VIS_MACRO_OPERATOR
);
1457 macro_reset(vis
->macro_operator
);
1460 void macro_operator_stop(Vis
*vis
) {
1461 if (!vis
->macro_operator
)
1463 Macro
*dot
= macro_get(vis
, VIS_REG_DOT
);
1464 buffer_put(dot
, vis
->macro_operator
->data
, vis
->macro_operator
->len
);
1465 vis
->action_prev
.macro
= dot
;
1466 vis
->macro_operator
= NULL
;
1469 bool vis_macro_record(Vis
*vis
, enum VisRegister id
) {
1470 Macro
*macro
= macro_get(vis
, id
);
1471 if (vis
->recording
|| !macro
)
1473 if (!(VIS_REG_A
<= id
&& id
<= VIS_REG_Z
))
1475 vis
->recording
= macro
;
1476 vis_event_emit(vis
, VIS_EVENT_WIN_STATUS
, vis
->win
);
1480 bool vis_macro_record_stop(Vis
*vis
) {
1481 if (!vis
->recording
)
1483 /* XXX: hack to remove last recorded key, otherwise upon replay
1484 * we would start another recording */
1485 if (vis
->recording
->len
> 1) {
1486 vis
->recording
->len
--;
1487 vis
->recording
->data
[vis
->recording
->len
-1] = '\0';
1489 vis
->last_recording
= vis
->recording
;
1490 vis
->recording
= NULL
;
1491 vis_event_emit(vis
, VIS_EVENT_WIN_STATUS
, vis
->win
);
1495 bool vis_macro_recording(Vis
*vis
) {
1496 return vis
->recording
;
1499 static void macro_replay(Vis
*vis
, const Macro
*macro
) {
1500 const Macro
*replaying
= vis
->replaying
;
1501 vis
->replaying
= macro
;
1502 macro_replay_internal(vis
, macro
);
1503 vis
->replaying
= replaying
;
1506 static void macro_replay_internal(Vis
*vis
, const Macro
*macro
) {
1507 size_t pos
= buffer_length0(&vis
->input_queue
);
1508 for (char *key
= macro
->data
, *next
; key
; key
= next
) {
1510 next
= (char*)vis_keys_next(vis
, key
);
1516 vis_keys_push(vis
, key
, pos
, false);
1523 bool vis_macro_replay(Vis
*vis
, enum VisRegister id
) {
1524 if (id
== VIS_REG_SEARCH
)
1525 return vis_motion(vis
, VIS_MOVE_SEARCH_REPEAT_FORWARD
);
1526 if (id
== VIS_REG_COMMAND
) {
1527 const char *cmd
= register_get(vis
, &vis
->registers
[id
], NULL
);
1528 return vis_cmd(vis
, cmd
);
1531 Macro
*macro
= macro_get(vis
, id
);
1532 if (!macro
|| macro
== vis
->recording
)
1534 int count
= vis_count_get_default(vis
, 1);
1536 for (int i
= 0; i
< count
; i
++)
1537 macro_replay(vis
, macro
);
1538 Win
*win
= vis
->win
;
1540 vis_file_snapshot(vis
, win
->file
);
1544 void vis_repeat(Vis
*vis
) {
1545 const Macro
*macro
= vis
->action_prev
.macro
;
1546 int count
= vis
->action
.count
;
1547 if (count
!= VIS_COUNT_UNKNOWN
)
1548 vis
->action_prev
.count
= count
;
1550 count
= vis
->action_prev
.count
;
1551 vis
->action
= vis
->action_prev
;
1552 vis_mode_switch(vis
, VIS_MODE_OPERATOR_PENDING
);
1555 Mode
*mode
= vis
->mode
;
1556 Action action_prev
= vis
->action_prev
;
1557 if (count
< 1 || action_prev
.op
== &vis_operators
[VIS_OP_CHANGE
])
1559 if (vis
->action_prev
.op
== &vis_operators
[VIS_OP_MODESWITCH
])
1560 vis
->action_prev
.count
= 1;
1561 for (int i
= 0; i
< count
; i
++) {
1562 if (vis
->interrupted
)
1564 mode_set(vis
, mode
);
1565 macro_replay(vis
, macro
);
1567 vis
->action_prev
= action_prev
;
1570 Win
*win
= vis
->win
;
1572 vis_file_snapshot(vis
, win
->file
);
1575 int vis_count_get(Vis
*vis
) {
1576 return vis
->action
.count
;
1579 int vis_count_get_default(Vis
*vis
, int def
) {
1580 if (vis
->action
.count
== VIS_COUNT_UNKNOWN
)
1582 return vis
->action
.count
;
1585 void vis_count_set(Vis
*vis
, int count
) {
1586 vis
->action
.count
= (count
>= 0 ? count
: VIS_COUNT_UNKNOWN
);
1589 VisCountIterator
vis_count_iterator_get(Vis
*vis
, int def
) {
1590 return (VisCountIterator
) {
1593 .count
= vis_count_get_default(vis
, def
),
1597 VisCountIterator
vis_count_iterator_init(Vis
*vis
, int count
) {
1598 return (VisCountIterator
) {
1605 bool vis_count_iterator_next(VisCountIterator
*it
) {
1606 if (it
->vis
->interrupted
)
1608 return it
->iteration
++ < it
->count
;
1611 void vis_exit(Vis
*vis
, int status
) {
1612 vis
->running
= false;
1613 vis
->exit_status
= status
;
1616 void vis_insert_tab(Vis
*vis
) {
1617 Win
*win
= vis
->win
;
1620 if (!vis
->expandtab
) {
1621 vis_insert_key(vis
, "\t", 1);
1625 int tabwidth
= MIN(vis
->tabwidth
, LENGTH(spaces
) - 1);
1626 for (Selection
*s
= view_selections(win
->view
); s
; s
= view_selections_next(s
)) {
1627 size_t pos
= view_cursors_pos(s
);
1628 int width
= text_line_width_get(win
->file
->text
, pos
);
1629 int count
= tabwidth
- (width
% tabwidth
);
1630 for (int i
= 0; i
< count
; i
++)
1632 spaces
[count
] = '\0';
1633 vis_insert(vis
, pos
, spaces
, count
);
1634 view_cursors_scroll_to(s
, pos
+ count
);
1638 size_t vis_text_insert_nl(Vis
*vis
, Text
*txt
, size_t pos
) {
1639 size_t indent_len
= 0;
1640 char byte
, *indent
= NULL
;
1641 /* insert second newline at end of file, except if there is already one */
1642 bool eof
= pos
== text_size(txt
);
1643 bool nl2
= eof
&& !(pos
> 0 && text_byte_get(txt
, pos
-1, &byte
) && byte
== '\n');
1645 if (vis
->autoindent
) {
1646 /* copy leading white space of current line */
1647 size_t begin
= text_line_begin(txt
, pos
);
1648 size_t start
= text_line_start(txt
, begin
);
1649 size_t end
= text_line_end(txt
, start
);
1652 indent_len
= start
>= begin
? start
-begin
: 0;
1656 indent
= malloc(indent_len
+1);
1658 indent_len
= text_bytes_get(txt
, begin
, indent_len
, indent
);
1662 text_insert(txt
, pos
, "\n", 1);
1665 text_insert(txt
, text_size(txt
), "\n", 1);
1667 pos
--; /* place cursor before, not after nl */
1672 text_insert(txt
, pos
, indent
, indent_len
);
1674 return pos
+ indent_len
;
1677 void vis_insert_nl(Vis
*vis
) {
1678 Win
*win
= vis
->win
;
1681 View
*view
= win
->view
;
1682 Text
*txt
= win
->file
->text
;
1683 for (Selection
*s
= view_selections(view
); s
; s
= view_selections_next(s
)) {
1684 size_t pos
= view_cursors_pos(s
);
1685 size_t newpos
= vis_text_insert_nl(vis
, txt
, pos
);
1686 /* This is a bit of a hack to fix cursor positioning when
1687 * inserting a new line at the start of the view port.
1688 * It has the effect of reseting the mark used by the view
1689 * code to keep track of the start of the visible region.
1691 view_cursors_to(s
, pos
);
1692 view_cursors_to(s
, newpos
);
1694 vis_window_invalidate(win
);
1697 Regex
*vis_regex(Vis
*vis
, const char *pattern
) {
1698 if (!pattern
&& !(pattern
= register_get(vis
, &vis
->registers
[VIS_REG_SEARCH
], NULL
)))
1700 Regex
*regex
= text_regex_new();
1703 int cflags
= REG_EXTENDED
|REG_NEWLINE
|(REG_ICASE
*vis
->ignorecase
);
1704 if (text_regex_compile(regex
, pattern
, cflags
) != 0) {
1705 text_regex_free(regex
);
1708 register_put0(vis
, &vis
->registers
[VIS_REG_SEARCH
], pattern
);
1712 int vis_pipe(Vis
*vis
, File
*file
, Filerange
*range
, const char *argv
[],
1713 void *stdout_context
, ssize_t (*read_stdout
)(void *stdout_context
, char *data
, size_t len
),
1714 void *stderr_context
, ssize_t (*read_stderr
)(void *stderr_context
, char *data
, size_t len
)) {
1716 /* if an invalid range was given, stdin (i.e. key board input) is passed
1717 * through the external command. */
1718 Text
*text
= file
->text
;
1719 int pin
[2], pout
[2], perr
[2], status
= -1;
1720 bool interactive
= !text_range_valid(range
);
1721 Filerange rout
= interactive
? text_range_new(0, 0) : *range
;
1723 if (pipe(pin
) == -1)
1725 if (pipe(pout
) == -1) {
1731 if (pipe(perr
) == -1) {
1739 vis
->ui
->terminal_save(vis
->ui
);
1749 vis_info_show(vis
, "fork failure: %s", strerror(errno
));
1751 } else if (pid
== 0) { /* child i.e filter */
1752 sigset_t sigterm_mask
;
1753 sigemptyset(&sigterm_mask
);
1754 sigaddset(&sigterm_mask
, SIGTERM
);
1755 if (sigprocmask(SIG_UNBLOCK
, &sigterm_mask
, NULL
) == -1) {
1756 fprintf(stderr
, "failed to reset signal mask");
1760 int null
= open("/dev/null", O_RDWR
);
1762 fprintf(stderr
, "failed to open /dev/null");
1767 /* If we have nothing to write, let stdin point to
1768 * /dev/null instead of a pipe which is immediately
1769 * closed. Some programs behave differently when used
1772 if (text_range_size(range
) == 0)
1773 dup2(null
, STDIN_FILENO
);
1775 dup2(pin
[0], STDIN_FILENO
);
1781 dup2(STDERR_FILENO
, STDOUT_FILENO
);
1782 /* For some reason the first byte written by the
1783 * interactive application is not being displayed.
1784 * It probably has something to do with the terminal
1785 * state change. By writing a dummy byte ourself we
1786 * ensure that the complete output is visible.
1788 while(write(STDOUT_FILENO
, " ", 1) == -1 && errno
== EINTR
);
1789 } else if (read_stdout
) {
1790 dup2(pout
[1], STDOUT_FILENO
);
1792 dup2(null
, STDOUT_FILENO
);
1798 dup2(perr
[1], STDERR_FILENO
);
1800 dup2(null
, STDERR_FILENO
);
1807 char *name
= strrchr(file
->name
, '/');
1808 setenv("vis_filepath", file
->name
, 1);
1809 setenv("vis_filename", name
? name
+1 : file
->name
, 1);
1813 execlp(vis
->shell
, vis
->shell
, "-c", argv
[0], (char*)NULL
);
1815 execvp(argv
[0], (char* const*)argv
);
1816 fprintf(stderr
, "exec failure: %s", strerror(errno
));
1820 vis
->interrupted
= false;
1826 if (fcntl(pout
[0], F_SETFL
, O_NONBLOCK
) == -1 ||
1827 fcntl(perr
[0], F_SETFL
, O_NONBLOCK
) == -1)
1833 if (vis
->interrupted
) {
1841 FD_SET(pin
[1], &wfds
);
1843 FD_SET(pout
[0], &rfds
);
1845 FD_SET(perr
[0], &rfds
);
1847 if (select(FD_SETSIZE
, &rfds
, &wfds
, NULL
, NULL
) == -1) {
1850 vis_info_show(vis
, "Select failure");
1854 if (pin
[1] != -1 && FD_ISSET(pin
[1], &wfds
)) {
1855 Filerange junk
= rout
;
1856 if (junk
.end
> junk
.start
+ PIPE_BUF
)
1857 junk
.end
= junk
.start
+ PIPE_BUF
;
1858 ssize_t len
= text_write_range(text
, &junk
, pin
[1]);
1861 if (text_range_size(&rout
) == 0) {
1869 vis_info_show(vis
, "Error writing to external command");
1873 if (pout
[0] != -1 && FD_ISSET(pout
[0], &rfds
)) {
1875 ssize_t len
= read(pout
[0], buf
, sizeof buf
);
1878 (*read_stdout
)(stdout_context
, buf
, len
);
1879 } else if (len
== 0) {
1882 } else if (errno
!= EINTR
&& errno
!= EWOULDBLOCK
) {
1883 vis_info_show(vis
, "Error reading from filter stdout");
1889 if (perr
[0] != -1 && FD_ISSET(perr
[0], &rfds
)) {
1891 ssize_t len
= read(perr
[0], buf
, sizeof buf
);
1894 (*read_stderr
)(stderr_context
, buf
, len
);
1895 } else if (len
== 0) {
1898 } else if (errno
!= EINTR
&& errno
!= EWOULDBLOCK
) {
1899 vis_info_show(vis
, "Error reading from filter stderr");
1905 } while (pin
[1] != -1 || pout
[0] != -1 || perr
[0] != -1);
1916 if (vis
->interrupted
)
1918 pid_t died
= waitpid(pid
, &status
, 0);
1919 if ((died
== -1 && errno
== ECHILD
) || pid
== died
)
1923 /* clear any pending SIGTERM */
1924 struct sigaction sigterm_ignore
, sigterm_old
;
1925 sigterm_ignore
.sa_handler
= SIG_IGN
;
1926 sigterm_ignore
.sa_flags
= 0;
1927 sigemptyset(&sigterm_ignore
.sa_mask
);
1929 sigaction(SIGTERM
, &sigterm_ignore
, &sigterm_old
);
1930 sigaction(SIGTERM
, &sigterm_old
, NULL
);
1932 vis
->interrupted
= false;
1933 vis
->ui
->terminal_restore(vis
->ui
);
1938 static ssize_t
read_buffer(void *context
, char *data
, size_t len
) {
1939 buffer_append(context
, data
, len
);
1943 int vis_pipe_collect(Vis
*vis
, File
*file
, Filerange
*range
, const char *argv
[], char **out
, char **err
) {
1944 Buffer bufout
, buferr
;
1945 buffer_init(&bufout
);
1946 buffer_init(&buferr
);
1947 int status
= vis_pipe(vis
, file
, range
, argv
,
1948 &bufout
, out
? read_buffer
: NULL
,
1949 &buferr
, err
? read_buffer
: NULL
);
1950 buffer_terminate(&bufout
);
1951 buffer_terminate(&buferr
);
1953 *out
= buffer_move(&bufout
);
1955 *err
= buffer_move(&buferr
);
1956 buffer_release(&bufout
);
1957 buffer_release(&buferr
);
1961 bool vis_cmd(Vis
*vis
, const char *cmdline
) {
1964 while (*cmdline
== ':')
1966 char *line
= strdup(cmdline
);
1970 size_t len
= strlen(line
);
1971 while (len
> 0 && isspace((unsigned char)line
[len
-1]))
1975 enum SamError err
= sam_cmd(vis
, line
);
1976 if (err
!= SAM_ERR_OK
)
1977 vis_info_show(vis
, "%s", sam_error(err
));
1979 return err
== SAM_ERR_OK
;
1982 void vis_file_snapshot(Vis
*vis
, File
*file
) {
1983 if (!vis
->replaying
)
1984 text_snapshot(file
->text
);
1987 Text
*vis_text(Vis
*vis
) {
1988 Win
*win
= vis
->win
;
1989 return win
? win
->file
->text
: NULL
;
1992 View
*vis_view(Vis
*vis
) {
1993 Win
*win
= vis
->win
;
1994 return win
? win
->view
: NULL
;
1997 Win
*vis_window(Vis
*vis
) {
2001 bool vis_get_autoindent(const Vis
*vis
) {
2002 return vis
->autoindent
;