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
);
109 /** window / file handling */
111 static void file_free(Vis
*vis
, File
*file
) {
114 if (file
->refcount
> 1) {
118 vis_event_emit(vis
, VIS_EVENT_FILE_CLOSE
, file
);
119 for (size_t i
= 0; i
< LENGTH(file
->marks
); i
++)
120 mark_release(&file
->marks
[i
]);
121 text_free(file
->text
);
122 free((char*)file
->name
);
125 file
->prev
->next
= file
->next
;
127 file
->next
->prev
= file
->prev
;
128 if (vis
->files
== file
)
129 vis
->files
= file
->next
;
133 static File
*file_new_text(Vis
*vis
, Text
*text
) {
134 File
*file
= calloc(1, sizeof(*file
));
139 file
->stat
= text_stat(text
);
140 for (size_t i
= 0; i
< LENGTH(file
->marks
); i
++)
141 mark_init(&file
->marks
[i
]);
143 vis
->files
->prev
= file
;
144 file
->next
= vis
->files
;
149 char *absolute_path(const char *name
) {
152 char *copy1
= strdup(name
);
153 char *copy2
= strdup(name
);
154 char *path_absolute
= NULL
;
155 char path_normalized
[PATH_MAX
] = "";
157 if (!copy1
|| !copy2
)
160 char *dir
= dirname(copy1
);
161 char *base
= basename(copy2
);
162 if (!(path_absolute
= realpath(dir
, NULL
)))
164 if (strcmp(path_absolute
, "/") == 0)
165 path_absolute
[0] = '\0';
167 snprintf(path_normalized
, sizeof(path_normalized
), "%s/%s",
168 path_absolute
, base
);
173 return path_normalized
[0] ? strdup(path_normalized
) : NULL
;
176 static File
*file_new(Vis
*vis
, const char *name
) {
177 char *name_absolute
= NULL
;
179 if (!(name_absolute
= absolute_path(name
)))
181 File
*existing
= NULL
;
182 /* try to detect whether the same file is already open in another window
183 * TODO: do this based on inodes */
184 for (File
*file
= vis
->files
; file
; file
= file
->next
) {
185 if (file
->name
&& strcmp(file
->name
, name_absolute
) == 0) {
197 Text
*text
= text_load_method(name
, vis
->load_method
);
198 if (!text
&& name
&& errno
== ENOENT
)
199 text
= text_load(NULL
);
202 if (!(file
= file_new_text(vis
, text
)))
204 file
->name
= name_absolute
;
205 vis_event_emit(vis
, VIS_EVENT_FILE_OPEN
, file
);
210 file_free(vis
, file
);
214 static File
*file_new_internal(Vis
*vis
, const char *filename
) {
215 File
*file
= file_new(vis
, filename
);
218 file
->internal
= true;
223 void file_name_set(File
*file
, const char *name
) {
224 if (name
== file
->name
)
226 free((char*)file
->name
);
227 file
->name
= absolute_path(name
);
230 const char *file_name_get(File
*file
) {
231 /* TODO: calculate path relative to working directory, cache result */
235 if (!getcwd(cwd
, sizeof cwd
))
237 const char *path
= strstr(file
->name
, cwd
);
238 if (path
!= file
->name
)
240 size_t cwdlen
= strlen(cwd
);
241 return file
->name
[cwdlen
] == '/' ? file
->name
+cwdlen
+1 : file
->name
;
244 void vis_window_status(Win
*win
, const char *status
) {
245 win
->ui
->status(win
->ui
, status
);
248 void window_selection_save(Win
*win
) {
250 View
*view
= win
->view
;
251 Array sel
= view_selections_get_all(view
);
252 vis_mark_set(win
, VIS_MARK_SELECTION
, &sel
);
254 vis_jumplist_save(vis
);
258 static void window_free(Win
*win
) {
262 for (Win
*other
= vis
->windows
; other
; other
= other
->next
) {
263 if (other
->parent
== win
)
264 other
->parent
= NULL
;
267 vis
->ui
->window_free(win
->ui
);
268 view_free(win
->view
);
269 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++)
270 map_free(win
->modes
[i
].bindings
);
271 marklist_release(&win
->jumplist
);
272 mark_release(&win
->saved_selections
);
276 static void window_draw_colorcolumn(Win
*win
) {
277 View
*view
= win
->view
;
278 int cc
= view_colorcolumn_get(view
);
281 CellStyle style
= win
->ui
->style_get(win
->ui
, UI_STYLE_COLOR_COLUMN
);
283 int line_cols
= 0; /* Track the number of columns we've passed on each line */
284 bool line_cc_set
= false; /* Has the colorcolumn attribute been set for this line yet */
285 int width
= view_width_get(view
);
287 for (Line
*l
= view_lines_first(view
); l
; l
= l
->next
) {
288 if (l
->lineno
!= lineno
) {
291 if (!(lineno
= l
->lineno
))
297 /* This screen line contains the cell we want to highlight */
298 if (cc
<= line_cols
+ width
) {
299 CellStyle
*orig
= &l
->cells
[cc
- 1 - line_cols
].style
;
300 orig
->attr
= style
.attr
;
301 orig
->fg
= is_default_color(style
.fg
) ? orig
->fg
: style
.fg
;
302 orig
->bg
= is_default_color(style
.bg
) ? orig
->bg
: style
.bg
;
310 static void window_draw_cursorline(Win
*win
) {
312 View
*view
= win
->view
;
313 enum UiOption options
= view_options_get(view
);
314 if (!(options
& UI_OPTION_CURSOR_LINE
))
316 if (vis
->mode
->visual
|| vis
->win
!= win
)
318 if (view_selections_count(view
) > 1)
321 int width
= view_width_get(view
);
322 CellStyle style
= win
->ui
->style_get(win
->ui
, UI_STYLE_CURSOR_LINE
);
323 Selection
*sel
= view_selections_primary_get(view
);
324 size_t lineno
= view_cursors_line_get(sel
)->lineno
;
325 for (Line
*l
= view_lines_first(view
); l
; l
= l
->next
) {
326 if (l
->lineno
== lineno
) {
327 for (int x
= 0; x
< width
; x
++) {
328 l
->cells
[x
].style
.attr
|= style
.attr
;
329 l
->cells
[x
].style
.bg
= style
.bg
;
331 } else if (l
->lineno
> lineno
) {
337 static void window_draw_selection(View
*view
, Selection
*cur
, CellStyle
*style
) {
338 Filerange sel
= view_selections_get(cur
);
339 if (!text_range_valid(&sel
))
341 Line
*start_line
; int start_col
;
342 Line
*end_line
; int end_col
;
343 view_coord_get(view
, sel
.start
, &start_line
, NULL
, &start_col
);
344 view_coord_get(view
, sel
.end
, &end_line
, NULL
, &end_col
);
345 if (!start_line
&& !end_line
)
348 start_line
= view_lines_first(view
);
352 end_line
= view_lines_last(view
);
353 end_col
= end_line
->width
;
355 for (Line
*l
= start_line
; l
!= end_line
->next
; l
= l
->next
) {
356 int col
= (l
== start_line
) ? start_col
: 0;
357 int end
= (l
== end_line
) ? end_col
: l
->width
;
359 if (cell_color_equal(l
->cells
[col
].style
.fg
, style
->bg
)) {
360 CellStyle old
= l
->cells
[col
].style
;
361 if (!cell_color_equal(old
.fg
, old
.bg
)) {
362 l
->cells
[col
].style
.fg
= old
.bg
;
363 l
->cells
[col
].style
.bg
= old
.fg
;
365 l
->cells
[col
].style
.attr
= style
->attr
;
368 l
->cells
[col
].style
.bg
= style
->bg
;
375 static void window_draw_cursor_matching(Win
*win
, Selection
*cur
, CellStyle
*style
) {
376 if (win
->vis
->mode
->visual
)
378 Line
*line_match
; int col_match
;
379 size_t pos
= view_cursors_pos(cur
);
380 Filerange limits
= view_viewport_get(win
->view
);
381 size_t pos_match
= text_bracket_match_symbol(win
->file
->text
, pos
, "(){}[]\"'`", &limits
);
382 if (pos
== pos_match
)
384 if (!view_coord_get(win
->view
, pos_match
, &line_match
, NULL
, &col_match
))
386 if (cell_color_equal(line_match
->cells
[col_match
].style
.fg
, style
->fg
)) {
387 CellStyle old
= line_match
->cells
[col_match
].style
;
388 line_match
->cells
[col_match
].style
.fg
= old
.bg
;
389 line_match
->cells
[col_match
].style
.bg
= old
.fg
;
391 line_match
->cells
[col_match
].style
.bg
= style
->bg
;
395 static void window_draw_cursor(Win
*win
, Selection
*cur
, CellStyle
*style
, CellStyle
*sel_style
) {
396 if (win
->vis
->win
!= win
)
398 Line
*line
= view_cursors_line_get(cur
);
399 int col
= view_cursors_cell_get(cur
);
400 if (!line
|| col
== -1)
402 line
->cells
[col
].style
= *style
;
403 window_draw_cursor_matching(win
, cur
, sel_style
);
407 static void window_draw_selections(Win
*win
) {
408 View
*view
= win
->view
;
409 Filerange viewport
= view_viewport_get(view
);
410 Selection
*sel
= view_selections_primary_get(view
);
411 CellStyle style_cursor
= win
->ui
->style_get(win
->ui
, UI_STYLE_CURSOR
);
412 CellStyle style_cursor_primary
= win
->ui
->style_get(win
->ui
, UI_STYLE_CURSOR_PRIMARY
);
413 CellStyle style_selection
= win
->ui
->style_get(win
->ui
, UI_STYLE_SELECTION
);
414 for (Selection
*s
= view_selections_prev(sel
); s
; s
= view_selections_prev(s
)) {
415 window_draw_selection(win
->view
, s
, &style_selection
);
416 size_t pos
= view_cursors_pos(s
);
417 if (pos
< viewport
.start
)
419 window_draw_cursor(win
, s
, &style_cursor
, &style_selection
);
421 window_draw_selection(win
->view
, sel
, &style_selection
);
422 window_draw_cursor(win
, sel
, &style_cursor_primary
, &style_selection
);
423 for (Selection
*s
= view_selections_next(sel
); s
; s
= view_selections_next(s
)) {
424 window_draw_selection(win
->view
, s
, &style_selection
);
425 size_t pos
= view_cursors_pos(s
);
426 if (pos
> viewport
.end
)
428 window_draw_cursor(win
, s
, &style_cursor
, &style_selection
);
432 static void window_draw_eof(Win
*win
) {
433 View
*view
= win
->view
;
434 if (view_width_get(view
) == 0)
436 CellStyle style
= win
->ui
->style_get(win
->ui
, UI_STYLE_EOF
);
437 for (Line
*l
= view_lines_last(view
)->next
; l
; l
= l
->next
) {
438 strncpy(l
->cells
[0].data
, view_symbol_eof_get(view
), sizeof(l
->cells
[0].data
)-1);
439 l
->cells
[0].style
= style
;
443 void vis_window_draw(Win
*win
) {
444 if (!win
->ui
|| !view_update(win
->view
))
447 vis_event_emit(vis
, VIS_EVENT_WIN_HIGHLIGHT
, win
);
449 window_draw_colorcolumn(win
);
450 window_draw_cursorline(win
);
451 if (!vis
->win
|| vis
->win
== win
|| vis
->win
->parent
== win
)
452 window_draw_selections(win
);
453 window_draw_eof(win
);
455 vis_event_emit(vis
, VIS_EVENT_WIN_STATUS
, win
);
459 void vis_window_invalidate(Win
*win
) {
460 for (Win
*w
= win
->vis
->windows
; w
; w
= w
->next
) {
461 if (w
->file
== win
->file
)
466 Win
*window_new_file(Vis
*vis
, File
*file
, enum UiOption options
) {
467 Win
*win
= calloc(1, sizeof(Win
));
472 win
->view
= view_new(file
->text
);
473 win
->ui
= vis
->ui
->window_new(vis
->ui
, win
, options
);
474 if (!win
->view
|| !win
->ui
) {
478 marklist_init(&win
->jumplist
, 32);
479 mark_init(&win
->saved_selections
);
481 view_options_set(win
->view
, view_options_get(win
->view
));
482 view_tabwidth_set(win
->view
, vis
->tabwidth
);
485 vis
->windows
->prev
= win
;
486 win
->next
= vis
->windows
;
489 vis
->ui
->window_focus(win
->ui
);
490 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++)
491 win
->modes
[i
].parent
= &vis_modes
[i
];
492 vis_event_emit(vis
, VIS_EVENT_WIN_OPEN
, win
);
496 bool vis_window_reload(Win
*win
) {
497 const char *name
= win
->file
->name
;
499 return false; /* can't reload unsaved file */
500 /* temporarily unset file name, otherwise file_new returns the same File */
501 win
->file
->name
= NULL
;
502 File
*file
= file_new(win
->vis
, name
);
503 win
->file
->name
= name
;
506 file_free(win
->vis
, win
->file
);
509 view_reload(win
->view
, file
->text
);
513 bool vis_window_split(Win
*original
) {
514 Win
*win
= window_new_file(original
->vis
, original
->file
, UI_OPTION_STATUSBAR
);
517 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++) {
518 if (original
->modes
[i
].bindings
)
519 win
->modes
[i
].bindings
= map_new();
520 if (win
->modes
[i
].bindings
)
521 map_copy(win
->modes
[i
].bindings
, original
->modes
[i
].bindings
);
523 win
->file
= original
->file
;
524 view_options_set(win
->view
, view_options_get(original
->view
));
525 view_cursor_to(win
->view
, view_cursor_get(original
->view
));
529 void vis_window_focus(Win
*win
) {
534 vis
->ui
->window_focus(win
->ui
);
537 void vis_window_next(Vis
*vis
) {
541 vis_window_focus(sel
->next
? sel
->next
: vis
->windows
);
544 void vis_window_prev(Vis
*vis
) {
550 for (sel
= vis
->windows
; sel
->next
; sel
= sel
->next
);
551 vis_window_focus(sel
);
554 int vis_window_width_get(const Win
*win
) {
555 return win
->ui
->window_width(win
->ui
);
558 int vis_window_height_get(const Win
*win
) {
559 return win
->ui
->window_height(win
->ui
);
562 void vis_draw(Vis
*vis
) {
563 for (Win
*win
= vis
->windows
; win
; win
= win
->next
)
564 view_draw(win
->view
);
567 void vis_redraw(Vis
*vis
) {
568 vis
->ui
->redraw(vis
->ui
);
572 void vis_update(Vis
*vis
) {
573 vis
->ui
->draw(vis
->ui
);
576 void vis_suspend(Vis
*vis
) {
577 vis
->ui
->suspend(vis
->ui
);
580 void vis_resume(Vis
*vis
) {
581 vis
->ui
->resume(vis
->ui
);
584 bool vis_window_new(Vis
*vis
, const char *filename
) {
585 File
*file
= file_new(vis
, filename
);
588 Win
*win
= window_new_file(vis
, file
, UI_OPTION_STATUSBAR
|UI_OPTION_SYMBOL_EOF
);
590 file_free(vis
, file
);
597 bool vis_window_new_fd(Vis
*vis
, int fd
) {
600 if (!vis_window_new(vis
, NULL
))
602 vis
->win
->file
->fd
= fd
;
606 bool vis_window_closable(Win
*win
) {
607 if (!win
|| !text_modified(win
->file
->text
))
609 return win
->file
->refcount
> 1;
612 void vis_window_swap(Win
*a
, Win
*b
) {
613 if (a
== b
|| !a
|| !b
)
630 if (vis
->windows
== a
)
632 else if (vis
->windows
== b
)
634 vis
->ui
->window_swap(a
->ui
, b
->ui
);
637 else if (vis
->win
== b
)
641 void vis_window_close(Win
*win
) {
645 vis_event_emit(vis
, VIS_EVENT_WIN_CLOSE
, win
);
646 file_free(vis
, win
->file
);
648 win
->prev
->next
= win
->next
;
650 win
->next
->prev
= win
->prev
;
651 if (vis
->windows
== win
)
652 vis
->windows
= win
->next
;
654 vis
->win
= win
->next
? win
->next
: win
->prev
;
655 if (win
== vis
->message_window
)
656 vis
->message_window
= NULL
;
659 vis
->ui
->window_focus(vis
->win
->ui
);
663 Vis
*vis_new(Ui
*ui
, VisEvent
*event
) {
666 Vis
*vis
= calloc(1, sizeof(Vis
));
669 vis
->exit_status
= -1;
672 vis
->expandtab
= false;
673 vis
->change_colors
= true;
674 for (size_t i
= 0; i
< LENGTH(vis
->registers
); i
++)
675 register_init(&vis
->registers
[i
]);
676 vis
->registers
[VIS_REG_BLACKHOLE
].type
= REGISTER_BLACKHOLE
;
677 vis
->registers
[VIS_REG_CLIPBOARD
].type
= REGISTER_CLIPBOARD
;
678 vis
->registers
[VIS_REG_NUMBER
].type
= REGISTER_NUMBER
;
679 array_init(&vis
->operators
);
680 array_init(&vis
->motions
);
681 array_init(&vis
->textobjects
);
682 array_init(&vis
->bindings
);
683 array_init(&vis
->actions_user
);
684 action_reset(&vis
->action
);
685 buffer_init(&vis
->input_queue
);
686 if (!(vis
->command_file
= file_new_internal(vis
, NULL
)))
688 if (!(vis
->search_file
= file_new_internal(vis
, NULL
)))
690 if (!(vis
->error_file
= file_new_internal(vis
, NULL
)))
692 if (!(vis
->actions
= map_new()))
694 if (!(vis
->keymap
= map_new()))
699 char *shell
= getenv("SHELL");
700 if ((!shell
|| !*shell
) && (pw
= getpwuid(getuid())))
701 shell
= pw
->pw_shell
;
702 if (!shell
|| !*shell
)
704 if (!(vis
->shell
= strdup(shell
)))
706 vis
->mode_prev
= vis
->mode
= &vis_modes
[VIS_MODE_NORMAL
];
709 if (event
->mode_insert_input
)
710 vis_modes
[VIS_MODE_INSERT
].input
= event
->mode_insert_input
;
711 if (event
->mode_replace_input
)
712 vis_modes
[VIS_MODE_REPLACE
].input
= event
->mode_replace_input
;
720 void vis_free(Vis
*vis
) {
723 vis_event_emit(vis
, VIS_EVENT_QUIT
);
726 vis_window_close(vis
->windows
);
727 file_free(vis
, vis
->command_file
);
728 file_free(vis
, vis
->search_file
);
729 file_free(vis
, vis
->error_file
);
730 for (int i
= 0; i
< LENGTH(vis
->registers
); i
++)
731 register_release(&vis
->registers
[i
]);
732 vis
->ui
->free(vis
->ui
);
735 while (map_first(vis
->usercmds
, &name
) && vis_cmd_unregister(vis
, name
));
737 map_free(vis
->usercmds
);
741 while (map_first(vis
->options
, &name
) && vis_option_unregister(vis
, name
));
743 map_free(vis
->options
);
744 map_free(vis
->actions
);
745 map_free(vis
->keymap
);
746 buffer_release(&vis
->input_queue
);
747 for (int i
= 0; i
< VIS_MODE_INVALID
; i
++)
748 map_free(vis_modes
[i
].bindings
);
749 array_release_full(&vis
->operators
);
750 array_release_full(&vis
->motions
);
751 array_release_full(&vis
->textobjects
);
752 while (array_length(&vis
->bindings
))
753 vis_binding_free(vis
, array_get_ptr(&vis
->bindings
, 0));
754 array_release(&vis
->bindings
);
755 while (array_length(&vis
->actions_user
))
756 vis_action_free(vis
, array_get_ptr(&vis
->actions_user
, 0));
757 array_release(&vis
->actions_user
);
762 void vis_insert(Vis
*vis
, size_t pos
, const char *data
, size_t len
) {
766 text_insert(win
->file
->text
, pos
, data
, len
);
767 vis_window_invalidate(win
);
770 void vis_insert_key(Vis
*vis
, const char *data
, size_t len
) {
774 for (Selection
*s
= view_selections(win
->view
); s
; s
= view_selections_next(s
)) {
775 size_t pos
= view_cursors_pos(s
);
776 vis_insert(vis
, pos
, data
, len
);
777 view_cursors_scroll_to(s
, pos
+ len
);
781 void vis_replace(Vis
*vis
, size_t pos
, const char *data
, size_t len
) {
785 Text
*txt
= win
->file
->text
;
786 Iterator it
= text_iterator_get(txt
, pos
);
787 int chars
= text_char_count(data
, len
);
788 for (char c
; chars
-- > 0 && text_iterator_byte_get(&it
, &c
) && c
!= '\n'; )
789 text_iterator_char_next(&it
, NULL
);
791 text_delete(txt
, pos
, it
.pos
- pos
);
792 vis_insert(vis
, pos
, data
, len
);
795 void vis_replace_key(Vis
*vis
, const char *data
, size_t len
) {
799 for (Selection
*s
= view_selections(win
->view
); s
; s
= view_selections_next(s
)) {
800 size_t pos
= view_cursors_pos(s
);
801 vis_replace(vis
, pos
, data
, len
);
802 view_cursors_scroll_to(s
, pos
+ len
);
806 void vis_delete(Vis
*vis
, size_t pos
, size_t len
) {
810 text_delete(win
->file
->text
, pos
, len
);
811 vis_window_invalidate(win
);
814 bool vis_action_register(Vis
*vis
, const KeyAction
*action
) {
815 return map_put(vis
->actions
, action
->name
, action
);
818 bool vis_keymap_add(Vis
*vis
, const char *key
, const char *mapping
) {
819 return map_put(vis
->keymap
, key
, mapping
);
822 void vis_keymap_disable(Vis
*vis
) {
823 vis
->keymap_disabled
= true;
826 void vis_interrupt(Vis
*vis
) {
827 vis
->interrupted
= true;
830 bool vis_interrupt_requested(Vis
*vis
) {
831 return vis
->interrupted
;
834 void vis_do(Vis
*vis
) {
838 File
*file
= win
->file
;
839 Text
*txt
= file
->text
;
840 View
*view
= win
->view
;
841 Action
*a
= &vis
->action
;
843 int count
= MAX(a
->count
, 1);
844 if (a
->op
== &vis_operators
[VIS_OP_MODESWITCH
])
845 count
= 1; /* count should apply to inserted text not motion */
846 bool repeatable
= a
->op
&& !vis
->macro_operator
&& !vis
->win
->parent
;
847 bool multiple_cursors
= view_selections_count(view
) > 1;
849 bool linewise
= !(a
->type
& CHARWISE
) && (
850 a
->type
& LINEWISE
|| (a
->movement
&& a
->movement
->type
& LINEWISE
) ||
851 vis
->mode
== &vis_modes
[VIS_MODE_VISUAL_LINE
]);
853 Register
*reg
= a
->reg
;
854 size_t reg_slot
= multiple_cursors
? EPOS
: 0;
855 size_t last_reg_slot
= reg_slot
;
857 reg
= &vis
->registers
[file
->internal
? VIS_REG_PROMPT
: VIS_REG_DEFAULT
];
858 if (a
->op
== &vis_operators
[VIS_OP_PUT_AFTER
] && multiple_cursors
&& vis_register_count(vis
, reg
) == 1)
861 if (vis
->mode
->visual
&& a
->op
)
862 window_selection_save(win
);
864 for (Selection
*sel
= view_selections(view
), *next
; sel
; sel
= next
) {
865 if (vis
->interrupted
)
868 next
= view_selections_next(sel
);
870 size_t pos
= view_cursors_pos(sel
);
872 if (!view_selections_dispose(sel
))
873 view_cursors_to(sel
, 0);
877 OperatorContext c
= {
881 .range
= text_range_empty(),
883 .reg_slot
= reg_slot
== EPOS
? (size_t)view_selections_number(sel
) : reg_slot
,
884 .linewise
= linewise
,
886 .context
= a
->op
? a
->op
->context
: NULL
,
889 last_reg_slot
= c
.reg_slot
;
894 for (int i
= 0; i
< count
; i
++) {
895 size_t pos_prev
= pos
;
896 if (a
->movement
->txt
)
897 pos
= a
->movement
->txt(txt
, pos
);
898 else if (a
->movement
->cur
)
899 pos
= a
->movement
->cur(sel
);
900 else if (a
->movement
->file
)
901 pos
= a
->movement
->file(vis
, file
, sel
);
902 else if (a
->movement
->vis
)
903 pos
= a
->movement
->vis(vis
, txt
, pos
);
904 else if (a
->movement
->view
)
905 pos
= a
->movement
->view(vis
, view
);
906 else if (a
->movement
->win
)
907 pos
= a
->movement
->win(vis
, win
, pos
);
908 else if (a
->movement
->user
)
909 pos
= a
->movement
->user(vis
, win
, a
->movement
->data
, pos
);
910 if (pos
== EPOS
|| a
->movement
->type
& IDEMPOTENT
|| pos
== pos_prev
) {
911 err
= a
->movement
->type
& COUNT_EXACT
;
922 c
.range
.start
= start
;
926 c
.range
= text_range_new(start
, pos
);
931 if (a
->movement
->type
& CHARWISE
)
932 view_cursors_scroll_to(sel
, pos
);
934 view_cursors_to(sel
, pos
);
935 if (vis
->mode
->visual
)
936 c
.range
= view_selections_get(sel
);
937 } else if (a
->movement
->type
& INCLUSIVE
&& c
.range
.end
> start
) {
938 c
.range
.end
= text_char_next(txt
, c
.range
.end
);
939 } else if (linewise
&& (a
->movement
->type
& LINEWISE_INCLUSIVE
)) {
940 c
.range
.end
= text_char_next(txt
, c
.range
.end
);
942 } else if (a
->textobj
) {
943 if (vis
->mode
->visual
)
944 c
.range
= view_selections_get(sel
);
946 c
.range
.start
= c
.range
.end
= pos
;
947 for (int i
= 0; i
< count
; i
++) {
948 Filerange r
= text_range_empty();
950 r
= a
->textobj
->txt(txt
, pos
);
951 else if (a
->textobj
->vis
)
952 r
= a
->textobj
->vis(vis
, txt
, pos
);
953 else if (a
->textobj
->user
)
954 r
= a
->textobj
->user(vis
, win
, a
->textobj
->data
, pos
);
955 if (!text_range_valid(&r
))
957 if (a
->textobj
->type
& TEXTOBJECT_DELIMITED_OUTER
) {
962 if (vis
->mode
->visual
|| (i
> 0 && !(a
->textobj
->type
& TEXTOBJECT_NON_CONTIGUOUS
)))
963 c
.range
= text_range_union(&c
.range
, &r
);
968 if (a
->textobj
->type
& TEXTOBJECT_EXTEND_BACKWARD
) {
970 if ((a
->textobj
->type
& TEXTOBJECT_DELIMITED_INNER
) && pos
> 0)
974 if (a
->textobj
->type
& TEXTOBJECT_DELIMITED_INNER
)
979 } else if (vis
->mode
->visual
) {
980 c
.range
= view_selections_get(sel
);
981 if (!text_range_valid(&c
.range
))
982 c
.range
.start
= c
.range
.end
= pos
;
985 if (linewise
&& vis
->mode
!= &vis_modes
[VIS_MODE_VISUAL
])
986 c
.range
= text_range_linewise(txt
, &c
.range
);
987 if (vis
->mode
->visual
) {
988 view_selections_set(sel
, &c
.range
);
989 view_selections_anchor(sel
, true);
993 size_t pos
= a
->op
->func(vis
, txt
, &c
);
995 view_selections_dispose(sel
);
996 } else if (pos
<= text_size(txt
)) {
997 view_selection_clear(sel
);
998 view_cursors_to(sel
, pos
);
1003 view_selections_normalize(view
);
1004 if (a
->movement
&& (a
->movement
->type
& JUMP
))
1005 vis_jumplist_save(vis
);
1009 if (a
->op
== &vis_operators
[VIS_OP_YANK
] ||
1010 a
->op
== &vis_operators
[VIS_OP_DELETE
] ||
1011 a
->op
== &vis_operators
[VIS_OP_CHANGE
] ||
1012 a
->op
== &vis_operators
[VIS_OP_REPLACE
]) {
1013 register_resize(reg
, last_reg_slot
+1);
1016 /* we do not support visual repeat, still do something resonable */
1017 if (vis
->mode
->visual
&& !a
->movement
&& !a
->textobj
)
1018 a
->movement
= &vis_motions
[VIS_MOVE_NOP
];
1020 /* operator implementations must not change the mode,
1021 * they might get called multiple times (once for every cursor)
1023 if (a
->op
== &vis_operators
[VIS_OP_CHANGE
]) {
1024 vis_mode_switch(vis
, VIS_MODE_INSERT
);
1025 } else if (a
->op
== &vis_operators
[VIS_OP_MODESWITCH
]) {
1026 vis_mode_switch(vis
, a
->mode
);
1027 } else if (vis
->mode
== &vis_modes
[VIS_MODE_OPERATOR_PENDING
]) {
1028 mode_set(vis
, vis
->mode_prev
);
1029 } else if (vis
->mode
->visual
) {
1030 vis_mode_switch(vis
, VIS_MODE_NORMAL
);
1033 if (vis
->mode
== &vis_modes
[VIS_MODE_NORMAL
])
1034 vis_file_snapshot(vis
, file
);
1038 if (a
!= &vis
->action_prev
) {
1041 a
->macro
= vis
->macro_operator
;
1042 vis
->action_prev
= *a
;
1048 void action_reset(Action
*a
) {
1049 memset(a
, 0, sizeof(*a
));
1050 a
->count
= VIS_COUNT_UNKNOWN
;
1053 void vis_cancel(Vis
*vis
) {
1054 action_reset(&vis
->action
);
1057 void vis_die(Vis
*vis
, const char *msg
, ...) {
1060 vis
->ui
->die(vis
->ui
, msg
, ap
);
1064 const char *vis_keys_next(Vis
*vis
, const char *keys
) {
1065 if (!keys
|| !*keys
)
1068 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1069 const char *next
= NULL
;
1070 /* first try to parse a special key of the form <Key> */
1071 if (*keys
== '<' && keys
[1] && (next
= termkey_strpkey(termkey
, keys
+1, &key
, TERMKEY_FORMAT_VIM
)) && *next
== '>')
1073 if (strncmp(keys
, "<vis-", 5) == 0) {
1074 const char *start
= keys
+ 1, *end
= start
;
1075 while (*end
&& *end
!= '>')
1077 if (end
> start
&& end
- start
- 1 < VIS_KEY_LENGTH_MAX
&& *end
== '>') {
1078 char key
[VIS_KEY_LENGTH_MAX
];
1079 memcpy(key
, start
, end
- start
);
1080 key
[end
- start
] = '\0';
1081 if (map_get(vis
->actions
, key
))
1087 while (!ISUTF8(*keys
))
1092 long vis_keys_codepoint(Vis
*vis
, const char *keys
) {
1093 long codepoint
= -1;
1096 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1100 if (keys
[0] == '<' && !keys
[1])
1103 if (keys
[0] == '<' && (next
= termkey_strpkey(termkey
, keys
+1, &key
, TERMKEY_FORMAT_VIM
)) && *next
== '>')
1104 codepoint
= (key
.type
== TERMKEY_TYPE_UNICODE
) ? key
.code
.codepoint
: -1;
1105 else if ((next
= termkey_strpkey(termkey
, keys
, &key
, TERMKEY_FORMAT_VIM
)))
1106 codepoint
= (key
.type
== TERMKEY_TYPE_UNICODE
) ? key
.code
.codepoint
: -1;
1108 if (codepoint
!= -1) {
1109 if (key
.modifiers
== TERMKEY_KEYMOD_CTRL
)
1114 if (!next
|| key
.type
!= TERMKEY_TYPE_KEYSYM
)
1117 const int keysym
[] = {
1118 TERMKEY_SYM_ENTER
, '\n',
1119 TERMKEY_SYM_TAB
, '\t',
1120 TERMKEY_SYM_BACKSPACE
, '\b',
1121 TERMKEY_SYM_ESCAPE
, 0x1b,
1122 TERMKEY_SYM_DELETE
, 0x7f,
1126 for (const int *k
= keysym
; k
[0]; k
+= 2) {
1127 if (key
.code
.sym
== k
[0])
1134 bool vis_keys_utf8(Vis
*vis
, const char *keys
, char utf8
[static UTFmax
+1]) {
1135 Rune rune
= vis_keys_codepoint(vis
, keys
);
1136 if (rune
== (Rune
)-1)
1138 size_t len
= runetochar(utf8
, &rune
);
1145 size_t len
; // length of the prefix
1146 int count
; // how many bindings can complete this prefix
1147 bool angle_bracket
; // does the prefix end with '<'
1150 static bool isprefix(const char *key
, void *value
, void *data
) {
1151 PrefixCompletion
*completion
= data
;
1152 if (!completion
->angle_bracket
) {
1153 completion
->count
++;
1155 const char *start
= key
+ completion
->len
;
1156 const char *end
= vis_keys_next(completion
->vis
, start
);
1157 if (end
&& start
+ 1 == end
)
1158 completion
->count
++;
1160 return completion
->count
== 1;
1163 static void vis_keys_process(Vis
*vis
, size_t pos
) {
1164 Buffer
*buf
= &vis
->input_queue
;
1165 char *keys
= buf
->data
+ pos
, *start
= keys
, *cur
= keys
, *end
= keys
, *binding_end
= keys
;;
1166 bool prefix
= false;
1167 KeyBinding
*binding
= NULL
;
1169 while (cur
&& *cur
) {
1171 if (!(end
= (char*)vis_keys_next(vis
, cur
))) {
1172 buffer_remove(buf
, keys
- buf
->data
, strlen(keys
));
1180 for (Mode
*global_mode
= vis
->mode
; global_mode
&& !prefix
; global_mode
= global_mode
->parent
) {
1181 for (int global
= 0; global
< 2 && !prefix
; global
++) {
1182 Mode
*mode
= (global
|| !vis
->win
) ?
1184 &vis
->win
->modes
[global_mode
->id
];
1185 if (!mode
->bindings
)
1187 /* keep track of longest matching binding */
1188 KeyBinding
*match
= map_get(mode
->bindings
, start
);
1189 if (match
&& end
> binding_end
) {
1194 const Map
*pmap
= map_prefix(mode
->bindings
, start
);
1195 PrefixCompletion completions
= {
1199 .angle_bracket
= !strcmp(cur
, "<"),
1201 map_iterate(pmap
, isprefix
, &completions
);
1203 prefix
= (!match
&& completions
.count
> 0) ||
1204 ( match
&& completions
.count
> 1);
1211 /* input sofar is ambigious, wait for more */
1214 } else if (binding
) { /* exact match */
1215 if (binding
->action
) {
1216 size_t len
= binding_end
- start
;
1217 strcpy(vis
->key_prev
, vis
->key_current
);
1218 strncpy(vis
->key_current
, start
, len
);
1219 vis
->key_current
[len
] = '\0';
1220 end
= (char*)binding
->action
->func(vis
, binding_end
, &binding
->action
->arg
);
1226 } else if (binding
->alias
) {
1227 buffer_remove(buf
, start
- buf
->data
, binding_end
- start
);
1228 buffer_insert0(buf
, start
- buf
->data
, binding
->alias
);
1232 binding_end
= start
;
1233 } else { /* no keybinding */
1234 KeyAction
*action
= NULL
;
1235 if (start
[0] == '<' && end
[-1] == '>') {
1236 /* test for special editor key command */
1239 action
= map_get(vis
->actions
, start
+1);
1242 size_t len
= end
- start
;
1243 strcpy(vis
->key_prev
, vis
->key_current
);
1244 strncpy(vis
->key_current
, start
, len
);
1245 vis
->key_current
[len
] = '\0';
1246 end
= (char*)action
->func(vis
, end
, &action
->arg
);
1253 if (!action
&& vis
->mode
->input
) {
1254 end
= (char*)vis_keys_next(vis
, start
);
1255 vis
->mode
->input(vis
, start
, end
- start
);
1261 buffer_remove(buf
, keys
- buf
->data
, end
- keys
);
1264 void vis_keys_feed(Vis
*vis
, const char *input
) {
1269 if (!macro_append(¯o
, input
))
1271 /* use internal function, to keep Lua based tests which use undo points working */
1272 macro_replay_internal(vis
, ¯o
);
1273 macro_release(¯o
);
1276 static void vis_keys_push(Vis
*vis
, const char *input
, size_t pos
, bool record
) {
1279 if (record
&& vis
->recording
)
1280 macro_append(vis
->recording
, input
);
1281 if (vis
->macro_operator
)
1282 macro_append(vis
->macro_operator
, input
);
1283 if (buffer_append0(&vis
->input_queue
, input
))
1284 vis_keys_process(vis
, pos
);
1287 static const char *getkey(Vis
*vis
) {
1288 TermKeyKey key
= { 0 };
1289 if (!vis
->ui
->getkey(vis
->ui
, &key
))
1292 bool use_keymap
= vis
->mode
->id
!= VIS_MODE_INSERT
&&
1293 vis
->mode
->id
!= VIS_MODE_REPLACE
&&
1294 !vis
->keymap_disabled
;
1295 vis
->keymap_disabled
= false;
1296 if (key
.type
== TERMKEY_TYPE_UNICODE
&& use_keymap
) {
1297 const char *mapped
= map_get(vis
->keymap
, key
.utf8
);
1299 size_t len
= strlen(mapped
)+1;
1300 if (len
<= sizeof(key
.utf8
))
1301 memcpy(key
.utf8
, mapped
, len
);
1305 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1306 termkey_strfkey(termkey
, vis
->key
, sizeof(vis
->key
), &key
, TERMKEY_FORMAT_VIM
);
1310 bool vis_signal_handler(Vis
*vis
, int signum
, const siginfo_t
*siginfo
, const void *context
) {
1313 for (File
*file
= vis
->files
; file
; file
= file
->next
) {
1314 if (text_mmaped(file
->text
, siginfo
->si_addr
))
1315 file
->truncated
= true;
1319 siglongjmp(vis
->sigbus_jmpbuf
, 1);
1322 vis
->interrupted
= true;
1328 vis
->need_resize
= true;
1332 vis
->terminate
= true;
1338 int vis_run(Vis
*vis
) {
1340 return EXIT_SUCCESS
;
1341 if (vis
->exit_status
!= -1)
1342 return vis
->exit_status
;
1343 vis
->running
= true;
1345 vis_event_emit(vis
, VIS_EVENT_START
);
1347 struct timespec idle
= { .tv_nsec
= 0 }, *timeout
= NULL
;
1350 sigemptyset(&emptyset
);
1352 vis
->exit_status
= EXIT_SUCCESS
;
1354 sigsetjmp(vis
->sigbus_jmpbuf
, 1);
1356 while (vis
->running
) {
1359 FD_SET(STDIN_FILENO
, &fds
);
1363 for (Win
*next
, *win
= vis
->windows
; win
; win
= next
) {
1365 if (win
->file
->truncated
) {
1367 name
= strdup(win
->file
->name
);
1368 vis_window_close(win
);
1372 vis_die(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
1374 vis_info_show(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
1375 vis
->sigbus
= false;
1380 vis_die(vis
, "Killed by SIGTERM\n");
1381 if (vis
->interrupted
) {
1382 vis
->interrupted
= false;
1383 vis_keys_feed(vis
, "<C-c>");
1388 vis
->resume
= false;
1391 if (vis
->need_resize
) {
1392 vis
->ui
->resize(vis
->ui
);
1393 vis
->need_resize
= false;
1397 idle
.tv_sec
= vis
->mode
->idle_timeout
;
1398 int r
= pselect(1, &fds
, NULL
, NULL
, timeout
, &emptyset
);
1399 if (r
== -1 && errno
== EINTR
)
1403 /* TODO save all pending changes to a ~suffixed file */
1404 vis_die(vis
, "Error in mainloop: %s\n", strerror(errno
));
1407 if (!FD_ISSET(STDIN_FILENO
, &fds
)) {
1408 if (vis
->mode
->idle
)
1409 vis
->mode
->idle(vis
);
1414 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1415 termkey_advisereadable(termkey
);
1418 while ((key
= getkey(vis
)))
1419 vis_keys_push(vis
, key
, 0, true);
1421 if (vis
->mode
->idle
)
1424 return vis
->exit_status
;
1427 Macro
*macro_get(Vis
*vis
, enum VisRegister id
) {
1428 if (id
== VIS_MACRO_LAST_RECORDED
)
1429 return vis
->last_recording
;
1430 if (VIS_REG_A
<= id
&& id
<= VIS_REG_Z
)
1432 if (id
< LENGTH(vis
->registers
))
1433 return array_get(&vis
->registers
[id
].values
, 0);
1437 void macro_operator_record(Vis
*vis
) {
1438 if (vis
->macro_operator
)
1440 vis
->macro_operator
= macro_get(vis
, VIS_MACRO_OPERATOR
);
1441 macro_reset(vis
->macro_operator
);
1444 void macro_operator_stop(Vis
*vis
) {
1445 if (!vis
->macro_operator
)
1447 Macro
*dot
= macro_get(vis
, VIS_REG_DOT
);
1448 buffer_put(dot
, vis
->macro_operator
->data
, vis
->macro_operator
->len
);
1449 vis
->action_prev
.macro
= dot
;
1450 vis
->macro_operator
= NULL
;
1453 bool vis_macro_record(Vis
*vis
, enum VisRegister id
) {
1454 Macro
*macro
= macro_get(vis
, id
);
1455 if (vis
->recording
|| !macro
)
1457 if (!(VIS_REG_A
<= id
&& id
<= VIS_REG_Z
))
1459 vis
->recording
= macro
;
1460 vis_event_emit(vis
, VIS_EVENT_WIN_STATUS
, vis
->win
);
1464 bool vis_macro_record_stop(Vis
*vis
) {
1465 if (!vis
->recording
)
1467 /* XXX: hack to remove last recorded key, otherwise upon replay
1468 * we would start another recording */
1469 if (vis
->recording
->len
> 1) {
1470 vis
->recording
->len
--;
1471 vis
->recording
->data
[vis
->recording
->len
-1] = '\0';
1473 vis
->last_recording
= vis
->recording
;
1474 vis
->recording
= NULL
;
1475 vis_event_emit(vis
, VIS_EVENT_WIN_STATUS
, vis
->win
);
1479 bool vis_macro_recording(Vis
*vis
) {
1480 return vis
->recording
;
1483 static void macro_replay(Vis
*vis
, const Macro
*macro
) {
1484 const Macro
*replaying
= vis
->replaying
;
1485 vis
->replaying
= macro
;
1486 macro_replay_internal(vis
, macro
);
1487 vis
->replaying
= replaying
;
1490 static void macro_replay_internal(Vis
*vis
, const Macro
*macro
) {
1491 size_t pos
= buffer_length0(&vis
->input_queue
);
1492 for (char *key
= macro
->data
, *next
; key
; key
= next
) {
1494 next
= (char*)vis_keys_next(vis
, key
);
1500 vis_keys_push(vis
, key
, pos
, false);
1507 bool vis_macro_replay(Vis
*vis
, enum VisRegister id
) {
1508 if (id
== VIS_REG_SEARCH
)
1509 return vis_motion(vis
, VIS_MOVE_SEARCH_REPEAT_FORWARD
);
1510 if (id
== VIS_REG_COMMAND
) {
1511 const char *cmd
= register_get(vis
, &vis
->registers
[id
], NULL
);
1512 return vis_cmd(vis
, cmd
);
1515 Macro
*macro
= macro_get(vis
, id
);
1516 if (!macro
|| macro
== vis
->recording
)
1518 int count
= vis_count_get_default(vis
, 1);
1520 for (int i
= 0; i
< count
; i
++)
1521 macro_replay(vis
, macro
);
1522 Win
*win
= vis
->win
;
1524 vis_file_snapshot(vis
, win
->file
);
1528 void vis_repeat(Vis
*vis
) {
1529 const Macro
*macro
= vis
->action_prev
.macro
;
1530 int count
= vis
->action
.count
;
1531 if (count
!= VIS_COUNT_UNKNOWN
)
1532 vis
->action_prev
.count
= count
;
1534 count
= vis
->action_prev
.count
;
1535 vis
->action
= vis
->action_prev
;
1536 vis_mode_switch(vis
, VIS_MODE_OPERATOR_PENDING
);
1539 Mode
*mode
= vis
->mode
;
1540 Action action_prev
= vis
->action_prev
;
1541 if (count
< 1 || action_prev
.op
== &vis_operators
[VIS_OP_CHANGE
])
1543 if (vis
->action_prev
.op
== &vis_operators
[VIS_OP_MODESWITCH
])
1544 vis
->action_prev
.count
= 1;
1545 for (int i
= 0; i
< count
; i
++) {
1546 if (vis
->interrupted
)
1548 mode_set(vis
, mode
);
1549 macro_replay(vis
, macro
);
1551 vis
->action_prev
= action_prev
;
1554 Win
*win
= vis
->win
;
1556 vis_file_snapshot(vis
, win
->file
);
1559 int vis_count_get(Vis
*vis
) {
1560 return vis
->action
.count
;
1563 int vis_count_get_default(Vis
*vis
, int def
) {
1564 if (vis
->action
.count
== VIS_COUNT_UNKNOWN
)
1566 return vis
->action
.count
;
1569 void vis_count_set(Vis
*vis
, int count
) {
1570 vis
->action
.count
= (count
>= 0 ? count
: VIS_COUNT_UNKNOWN
);
1573 VisCountIterator
vis_count_iterator_get(Vis
*vis
, int def
) {
1574 return (VisCountIterator
) {
1577 .count
= vis_count_get_default(vis
, def
),
1581 VisCountIterator
vis_count_iterator_init(Vis
*vis
, int count
) {
1582 return (VisCountIterator
) {
1589 bool vis_count_iterator_next(VisCountIterator
*it
) {
1590 if (it
->vis
->interrupted
)
1592 return it
->iteration
++ < it
->count
;
1595 void vis_exit(Vis
*vis
, int status
) {
1596 vis
->running
= false;
1597 vis
->exit_status
= status
;
1600 void vis_insert_tab(Vis
*vis
) {
1601 Win
*win
= vis
->win
;
1604 if (!vis
->expandtab
) {
1605 vis_insert_key(vis
, "\t", 1);
1609 int tabwidth
= MIN(vis
->tabwidth
, LENGTH(spaces
) - 1);
1610 for (Selection
*s
= view_selections(win
->view
); s
; s
= view_selections_next(s
)) {
1611 size_t pos
= view_cursors_pos(s
);
1612 int width
= text_line_width_get(win
->file
->text
, pos
);
1613 int count
= tabwidth
- (width
% tabwidth
);
1614 for (int i
= 0; i
< count
; i
++)
1616 spaces
[count
] = '\0';
1617 vis_insert(vis
, pos
, spaces
, count
);
1618 view_cursors_scroll_to(s
, pos
+ count
);
1622 size_t vis_text_insert_nl(Vis
*vis
, Text
*txt
, size_t pos
) {
1623 size_t indent_len
= 0;
1624 char byte
, *indent
= NULL
;
1625 /* insert second newline at end of file, except if there is already one */
1626 bool eof
= pos
== text_size(txt
);
1627 bool nl2
= eof
&& !(pos
> 0 && text_byte_get(txt
, pos
-1, &byte
) && byte
== '\n');
1629 if (vis
->autoindent
) {
1630 /* copy leading white space of current line */
1631 size_t begin
= text_line_begin(txt
, pos
);
1632 size_t start
= text_line_start(txt
, begin
);
1633 size_t end
= text_line_end(txt
, start
);
1636 indent_len
= start
>= begin
? start
-begin
: 0;
1640 indent
= malloc(indent_len
+1);
1642 indent_len
= text_bytes_get(txt
, begin
, indent_len
, indent
);
1646 text_insert(txt
, pos
, "\n", 1);
1649 text_insert(txt
, text_size(txt
), "\n", 1);
1651 pos
--; /* place cursor before, not after nl */
1656 text_insert(txt
, pos
, indent
, indent_len
);
1658 return pos
+ indent_len
;
1661 void vis_insert_nl(Vis
*vis
) {
1662 Win
*win
= vis
->win
;
1665 View
*view
= win
->view
;
1666 Text
*txt
= win
->file
->text
;
1667 for (Selection
*s
= view_selections(view
); s
; s
= view_selections_next(s
)) {
1668 size_t pos
= view_cursors_pos(s
);
1669 size_t newpos
= vis_text_insert_nl(vis
, txt
, pos
);
1670 /* This is a bit of a hack to fix cursor positioning when
1671 * inserting a new line at the start of the view port.
1672 * It has the effect of reseting the mark used by the view
1673 * code to keep track of the start of the visible region.
1675 view_cursors_to(s
, pos
);
1676 view_cursors_to(s
, newpos
);
1678 vis_window_invalidate(win
);
1681 Regex
*vis_regex(Vis
*vis
, const char *pattern
) {
1682 if (!pattern
&& !(pattern
= register_get(vis
, &vis
->registers
[VIS_REG_SEARCH
], NULL
)))
1684 Regex
*regex
= text_regex_new();
1687 if (text_regex_compile(regex
, pattern
, REG_EXTENDED
|REG_NEWLINE
) != 0) {
1688 text_regex_free(regex
);
1691 register_put0(vis
, &vis
->registers
[VIS_REG_SEARCH
], pattern
);
1695 int vis_pipe(Vis
*vis
, File
*file
, Filerange
*range
, const char *argv
[],
1696 void *stdout_context
, ssize_t (*read_stdout
)(void *stdout_context
, char *data
, size_t len
),
1697 void *stderr_context
, ssize_t (*read_stderr
)(void *stderr_context
, char *data
, size_t len
)) {
1699 /* if an invalid range was given, stdin (i.e. key board input) is passed
1700 * through the external command. */
1701 Text
*text
= file
->text
;
1702 int pin
[2], pout
[2], perr
[2], status
= -1;
1703 bool interactive
= !text_range_valid(range
);
1704 Filerange rout
= interactive
? text_range_new(0, 0) : *range
;
1706 if (pipe(pin
) == -1)
1708 if (pipe(pout
) == -1) {
1714 if (pipe(perr
) == -1) {
1722 vis
->ui
->terminal_save(vis
->ui
);
1732 vis_info_show(vis
, "fork failure: %s", strerror(errno
));
1734 } else if (pid
== 0) { /* child i.e filter */
1735 sigset_t sigterm_mask
;
1736 sigemptyset(&sigterm_mask
);
1737 sigaddset(&sigterm_mask
, SIGTERM
);
1738 if (sigprocmask(SIG_UNBLOCK
, &sigterm_mask
, NULL
) == -1) {
1739 fprintf(stderr
, "failed to reset signal mask");
1743 int null
= open("/dev/null", O_RDWR
);
1745 fprintf(stderr
, "failed to open /dev/null");
1750 /* If we have nothing to write, let stdin point to
1751 * /dev/null instead of a pipe which is immediately
1752 * closed. Some programs behave differently when used
1755 if (text_range_size(range
) == 0)
1756 dup2(null
, STDIN_FILENO
);
1758 dup2(pin
[0], STDIN_FILENO
);
1764 dup2(STDERR_FILENO
, STDOUT_FILENO
);
1765 /* For some reason the first byte written by the
1766 * interactive application is not being displayed.
1767 * It probably has something to do with the terminal
1768 * state change. By writing a dummy byte ourself we
1769 * ensure that the complete output is visible.
1771 while(write(STDOUT_FILENO
, " ", 1) == -1 && errno
== EINTR
);
1772 } else if (read_stdout
) {
1773 dup2(pout
[1], STDOUT_FILENO
);
1775 dup2(null
, STDOUT_FILENO
);
1781 dup2(perr
[1], STDERR_FILENO
);
1783 dup2(null
, STDERR_FILENO
);
1790 char *name
= strrchr(file
->name
, '/');
1791 setenv("vis_filepath", file
->name
, 1);
1792 setenv("vis_filename", name
? name
+1 : file
->name
, 1);
1796 execlp(vis
->shell
, vis
->shell
, "-c", argv
[0], (char*)NULL
);
1798 execvp(argv
[0], (char* const*)argv
);
1799 fprintf(stderr
, "exec failure: %s", strerror(errno
));
1803 vis
->interrupted
= false;
1809 if (fcntl(pout
[0], F_SETFL
, O_NONBLOCK
) == -1 ||
1810 fcntl(perr
[0], F_SETFL
, O_NONBLOCK
) == -1)
1816 if (vis
->interrupted
) {
1824 FD_SET(pin
[1], &wfds
);
1826 FD_SET(pout
[0], &rfds
);
1828 FD_SET(perr
[0], &rfds
);
1830 if (select(FD_SETSIZE
, &rfds
, &wfds
, NULL
, NULL
) == -1) {
1833 vis_info_show(vis
, "Select failure");
1837 if (pin
[1] != -1 && FD_ISSET(pin
[1], &wfds
)) {
1838 Filerange junk
= rout
;
1839 if (junk
.end
> junk
.start
+ PIPE_BUF
)
1840 junk
.end
= junk
.start
+ PIPE_BUF
;
1841 ssize_t len
= text_write_range(text
, &junk
, pin
[1]);
1844 if (text_range_size(&rout
) == 0) {
1852 vis_info_show(vis
, "Error writing to external command");
1856 if (pout
[0] != -1 && FD_ISSET(pout
[0], &rfds
)) {
1858 ssize_t len
= read(pout
[0], buf
, sizeof buf
);
1861 (*read_stdout
)(stdout_context
, buf
, len
);
1862 } else if (len
== 0) {
1865 } else if (errno
!= EINTR
&& errno
!= EWOULDBLOCK
) {
1866 vis_info_show(vis
, "Error reading from filter stdout");
1872 if (perr
[0] != -1 && FD_ISSET(perr
[0], &rfds
)) {
1874 ssize_t len
= read(perr
[0], buf
, sizeof buf
);
1877 (*read_stderr
)(stderr_context
, buf
, len
);
1878 } else if (len
== 0) {
1881 } else if (errno
!= EINTR
&& errno
!= EWOULDBLOCK
) {
1882 vis_info_show(vis
, "Error reading from filter stderr");
1888 } while (pin
[1] != -1 || pout
[0] != -1 || perr
[0] != -1);
1899 if (vis
->interrupted
)
1901 pid_t died
= waitpid(pid
, &status
, 0);
1902 if ((died
== -1 && errno
== ECHILD
) || pid
== died
)
1906 /* clear any pending SIGTERM */
1907 struct sigaction sigterm_ignore
, sigterm_old
;
1908 sigterm_ignore
.sa_handler
= SIG_IGN
;
1909 sigterm_ignore
.sa_flags
= 0;
1910 sigemptyset(&sigterm_ignore
.sa_mask
);
1912 sigaction(SIGTERM
, &sigterm_ignore
, &sigterm_old
);
1913 sigaction(SIGTERM
, &sigterm_old
, NULL
);
1915 vis
->interrupted
= false;
1916 vis
->ui
->terminal_restore(vis
->ui
);
1921 static ssize_t
read_buffer(void *context
, char *data
, size_t len
) {
1922 buffer_append(context
, data
, len
);
1926 int vis_pipe_collect(Vis
*vis
, File
*file
, Filerange
*range
, const char *argv
[], char **out
, char **err
) {
1927 Buffer bufout
, buferr
;
1928 buffer_init(&bufout
);
1929 buffer_init(&buferr
);
1930 int status
= vis_pipe(vis
, file
, range
, argv
,
1931 &bufout
, out
? read_buffer
: NULL
,
1932 &buferr
, err
? read_buffer
: NULL
);
1933 buffer_terminate(&bufout
);
1934 buffer_terminate(&buferr
);
1936 *out
= buffer_move(&bufout
);
1938 *err
= buffer_move(&buferr
);
1939 buffer_release(&bufout
);
1940 buffer_release(&buferr
);
1944 bool vis_cmd(Vis
*vis
, const char *cmdline
) {
1947 while (*cmdline
== ':')
1949 size_t len
= strlen(cmdline
);
1950 char *line
= malloc(len
+2);
1953 strncpy(line
, cmdline
, len
+1);
1955 for (char *end
= line
+ len
- 1; end
>= line
&& isspace((unsigned char)*end
); end
--)
1958 enum SamError err
= sam_cmd(vis
, line
);
1959 if (err
!= SAM_ERR_OK
)
1960 vis_info_show(vis
, "%s", sam_error(err
));
1962 return err
== SAM_ERR_OK
;
1965 void vis_file_snapshot(Vis
*vis
, File
*file
) {
1966 if (!vis
->replaying
)
1967 text_snapshot(file
->text
);
1970 Text
*vis_text(Vis
*vis
) {
1971 Win
*win
= vis
->win
;
1972 return win
? win
->file
->text
: NULL
;
1975 View
*vis_view(Vis
*vis
) {
1976 Win
*win
= vis
->win
;
1977 return win
? win
->view
: NULL
;
1980 Win
*vis_window(Vis
*vis
) {
1984 bool vis_get_autoindent(const Vis
*vis
) {
1985 return vis
->autoindent
;