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"
31 static void macro_replay(Vis
*vis
, const Macro
*macro
);
32 static void macro_replay_internal(Vis
*vis
, const Macro
*macro
);
33 static void vis_keys_push(Vis
*vis
, const char *input
, size_t pos
, bool record
);
35 bool vis_event_emit(Vis
*vis
, enum VisEvents id
, ...) {
39 if (!vis
->initialized
) {
40 vis
->initialized
= true;
41 vis
->ui
->init(vis
->ui
, vis
);
43 vis
->event
->init(vis
);
54 if (vis
->event
->start
)
55 vis
->event
->start(vis
);
57 case VIS_EVENT_FILE_OPEN
:
58 case VIS_EVENT_FILE_SAVE_PRE
:
59 case VIS_EVENT_FILE_SAVE_POST
:
60 case VIS_EVENT_FILE_CLOSE
:
62 File
*file
= va_arg(ap
, File
*);
65 if (id
== VIS_EVENT_FILE_OPEN
&& vis
->event
->file_open
) {
66 vis
->event
->file_open(vis
, file
);
67 } else if (id
== VIS_EVENT_FILE_SAVE_PRE
&& vis
->event
->file_save_pre
) {
68 const char *path
= va_arg(ap
, const char*);
69 ret
= vis
->event
->file_save_pre(vis
, file
, path
);
70 } else if (id
== VIS_EVENT_FILE_SAVE_POST
&& vis
->event
->file_save_post
) {
71 const char *path
= va_arg(ap
, const char*);
72 vis
->event
->file_save_post(vis
, file
, path
);
73 } else if (id
== VIS_EVENT_FILE_CLOSE
&& vis
->event
->file_close
) {
74 vis
->event
->file_close(vis
, file
);
78 case VIS_EVENT_WIN_OPEN
:
79 case VIS_EVENT_WIN_CLOSE
:
80 case VIS_EVENT_WIN_HIGHLIGHT
:
81 case VIS_EVENT_WIN_STATUS
:
83 Win
*win
= va_arg(ap
, Win
*);
84 if (win
->file
->internal
&& id
!= VIS_EVENT_WIN_STATUS
)
86 if (vis
->event
->win_open
&& id
== VIS_EVENT_WIN_OPEN
) {
87 vis
->event
->win_open(vis
, win
);
88 } else if (vis
->event
->win_close
&& id
== VIS_EVENT_WIN_CLOSE
) {
89 vis
->event
->win_close(vis
, win
);
90 } else if (vis
->event
->win_highlight
&& id
== VIS_EVENT_WIN_HIGHLIGHT
) {
91 vis
->event
->win_highlight(vis
, win
);
92 } else if (vis
->event
->win_status
&& id
== VIS_EVENT_WIN_STATUS
) {
93 vis
->event
->win_status(vis
, win
);
99 vis
->event
->quit(vis
);
107 /** window / file handling */
109 static void file_free(Vis
*vis
, File
*file
) {
112 if (file
->refcount
> 1) {
116 vis_event_emit(vis
, VIS_EVENT_FILE_CLOSE
, file
);
117 for (size_t i
= 0; i
< LENGTH(file
->marks
); i
++)
118 mark_release(&file
->marks
[i
]);
119 text_free(file
->text
);
120 free((char*)file
->name
);
123 file
->prev
->next
= file
->next
;
125 file
->next
->prev
= file
->prev
;
126 if (vis
->files
== file
)
127 vis
->files
= file
->next
;
131 static File
*file_new_text(Vis
*vis
, Text
*text
) {
132 File
*file
= calloc(1, sizeof(*file
));
137 file
->stat
= text_stat(text
);
138 for (size_t i
= 0; i
< LENGTH(file
->marks
); i
++)
139 mark_init(&file
->marks
[i
]);
141 vis
->files
->prev
= file
;
142 file
->next
= vis
->files
;
147 static char *absolute_path(const char *name
) {
150 char *copy1
= strdup(name
);
151 char *copy2
= strdup(name
);
152 char *path_absolute
= NULL
;
153 char path_normalized
[PATH_MAX
] = "";
155 if (!copy1
|| !copy2
)
158 char *dir
= dirname(copy1
);
159 char *base
= basename(copy2
);
160 if (!(path_absolute
= realpath(dir
, NULL
)))
162 if (strcmp(path_absolute
, "/") == 0)
163 path_absolute
[0] = '\0';
165 snprintf(path_normalized
, sizeof(path_normalized
), "%s/%s",
166 path_absolute
, base
);
171 return path_normalized
[0] ? strdup(path_normalized
) : NULL
;
174 static File
*file_new(Vis
*vis
, const char *name
) {
175 char *name_absolute
= NULL
;
177 if (!(name_absolute
= absolute_path(name
)))
179 File
*existing
= NULL
;
180 /* try to detect whether the same file is already open in another window
181 * TODO: do this based on inodes */
182 for (File
*file
= vis
->files
; file
; file
= file
->next
) {
183 if (file
->name
&& strcmp(file
->name
, name_absolute
) == 0) {
195 Text
*text
= text_load(name
);
196 if (!text
&& name
&& errno
== ENOENT
)
197 text
= text_load(NULL
);
200 if (!(file
= file_new_text(vis
, text
)))
202 file
->name
= name_absolute
;
203 vis_event_emit(vis
, VIS_EVENT_FILE_OPEN
, file
);
208 file_free(vis
, file
);
212 static File
*file_new_internal(Vis
*vis
, const char *filename
) {
213 File
*file
= file_new(vis
, filename
);
216 file
->internal
= true;
221 void file_name_set(File
*file
, const char *name
) {
222 if (name
== file
->name
)
224 free((char*)file
->name
);
225 file
->name
= absolute_path(name
);
228 const char *file_name_get(File
*file
) {
229 /* TODO: calculate path relative to working directory, cache result */
233 if (!getcwd(cwd
, sizeof cwd
))
235 const char *path
= strstr(file
->name
, cwd
);
236 if (path
!= file
->name
)
238 size_t cwdlen
= strlen(cwd
);
239 return file
->name
[cwdlen
] == '/' ? file
->name
+cwdlen
+1 : file
->name
;
242 void vis_window_status(Win
*win
, const char *status
) {
243 win
->ui
->status(win
->ui
, status
);
246 void window_selection_save(Win
*win
) {
248 View
*view
= win
->view
;
249 Array sel
= view_selections_get_all(view
);
250 vis_mark_set(win
, VIS_MARK_SELECTION
, &sel
);
252 vis_jumplist_save(vis
);
256 static void window_free(Win
*win
) {
260 for (Win
*other
= vis
->windows
; other
; other
= other
->next
) {
261 if (other
->parent
== win
)
262 other
->parent
= NULL
;
265 vis
->ui
->window_free(win
->ui
);
266 view_free(win
->view
);
267 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++)
268 map_free(win
->modes
[i
].bindings
);
269 marklist_release(&win
->jumplist
);
270 mark_release(&win
->saved_selections
);
274 static void window_draw_colorcolumn(Win
*win
) {
275 View
*view
= win
->view
;
276 int cc
= view_colorcolumn_get(view
);
279 CellStyle style
= win
->ui
->style_get(win
->ui
, UI_STYLE_COLOR_COLUMN
);
281 int line_cols
= 0; /* Track the number of columns we've passed on each line */
282 bool line_cc_set
= false; /* Has the colorcolumn attribute been set for this line yet */
283 int width
= view_width_get(view
);
285 for (Line
*l
= view_lines_first(view
); l
; l
= l
->next
) {
286 if (l
->lineno
!= lineno
) {
296 /* This screen line contains the cell we want to highlight */
297 if (line_cols
>= cc
) {
298 l
->cells
[(cc
- 1) % width
].style
= style
;
304 static void window_draw_cursorline(Win
*win
) {
306 View
*view
= win
->view
;
307 enum UiOption options
= view_options_get(view
);
308 if (!(options
& UI_OPTION_CURSOR_LINE
))
310 if (vis
->mode
->visual
|| vis
->win
!= win
)
312 if (view_selections_count(view
) > 1)
315 int width
= view_width_get(view
);
316 CellStyle style
= win
->ui
->style_get(win
->ui
, UI_STYLE_CURSOR_LINE
);
317 Selection
*sel
= view_selections_primary_get(view
);
318 size_t lineno
= view_cursors_line_get(sel
)->lineno
;
319 for (Line
*l
= view_lines_first(view
); l
; l
= l
->next
) {
320 if (l
->lineno
== lineno
) {
321 for (int x
= 0; x
< width
; x
++) {
322 l
->cells
[x
].style
.attr
|= style
.attr
;
323 l
->cells
[x
].style
.bg
= style
.bg
;
325 } else if (l
->lineno
> lineno
) {
331 static void window_draw_selection(View
*view
, Selection
*cur
, CellStyle
*style
) {
332 Filerange sel
= view_selections_get(cur
);
333 if (!text_range_valid(&sel
))
335 Line
*start_line
; int start_col
;
336 Line
*end_line
; int end_col
;
337 view_coord_get(view
, sel
.start
, &start_line
, NULL
, &start_col
);
338 view_coord_get(view
, sel
.end
, &end_line
, NULL
, &end_col
);
339 if (!start_line
&& !end_line
)
342 start_line
= view_lines_first(view
);
346 end_line
= view_lines_last(view
);
347 end_col
= end_line
->width
;
349 for (Line
*l
= start_line
; l
!= end_line
->next
; l
= l
->next
) {
350 int col
= (l
== start_line
) ? start_col
: 0;
351 int end
= (l
== end_line
) ? end_col
: l
->width
;
353 if (cell_color_equal(l
->cells
[col
].style
.fg
, style
->bg
)) {
354 CellStyle old
= l
->cells
[col
].style
;
355 if (!cell_color_equal(old
.fg
, old
.bg
)) {
356 l
->cells
[col
].style
.fg
= old
.bg
;
357 l
->cells
[col
].style
.bg
= old
.fg
;
359 l
->cells
[col
].style
.attr
= style
->attr
;
362 l
->cells
[col
].style
.bg
= style
->bg
;
369 static void window_draw_cursor_matching(Win
*win
, Selection
*cur
, CellStyle
*style
) {
370 if (win
->vis
->mode
->visual
)
372 Line
*line_match
; int col_match
;
373 size_t pos
= view_cursors_pos(cur
);
374 size_t pos_match
= text_bracket_match_symbol(win
->file
->text
, pos
, "(){}[]\"'`");
375 if (pos
== pos_match
)
377 if (!view_coord_get(win
->view
, pos_match
, &line_match
, NULL
, &col_match
))
379 if (cell_color_equal(line_match
->cells
[col_match
].style
.fg
, style
->fg
)) {
380 CellStyle old
= line_match
->cells
[col_match
].style
;
381 line_match
->cells
[col_match
].style
.fg
= old
.bg
;
382 line_match
->cells
[col_match
].style
.bg
= old
.fg
;
384 line_match
->cells
[col_match
].style
.bg
= style
->bg
;
388 static void window_draw_cursor(Win
*win
, Selection
*cur
, CellStyle
*style
, CellStyle
*sel_style
) {
389 if (win
->vis
->win
!= win
)
391 Line
*line
= view_cursors_line_get(cur
);
392 int col
= view_cursors_cell_get(cur
);
393 if (!line
|| col
== -1)
395 line
->cells
[col
].style
= *style
;
396 window_draw_cursor_matching(win
, cur
, sel_style
);
400 static void window_draw_selections(Win
*win
) {
401 View
*view
= win
->view
;
402 Filerange viewport
= view_viewport_get(view
);
403 bool multiple_cursors
= view_selections_count(view
) > 1;
404 Selection
*sel
= view_selections_primary_get(view
);
405 CellStyle style_cursor
= win
->ui
->style_get(win
->ui
, UI_STYLE_CURSOR
);
406 CellStyle style_cursor_primary
= win
->ui
->style_get(win
->ui
, UI_STYLE_CURSOR_PRIMARY
);
407 CellStyle style_selection
= win
->ui
->style_get(win
->ui
, UI_STYLE_SELECTION
);
408 for (Selection
*s
= view_selections_prev(sel
); s
; s
= view_selections_prev(s
)) {
409 window_draw_selection(win
->view
, s
, &style_selection
);
410 size_t pos
= view_cursors_pos(s
);
411 if (pos
< viewport
.start
)
413 window_draw_cursor(win
, s
, &style_cursor
, &style_selection
);
415 window_draw_selection(win
->view
, sel
, &style_selection
);
416 window_draw_cursor(win
, sel
, multiple_cursors
? &style_cursor_primary
: &style_cursor
, &style_selection
);
417 for (Selection
*s
= view_selections_next(sel
); s
; s
= view_selections_next(s
)) {
418 window_draw_selection(win
->view
, s
, &style_selection
);
419 size_t pos
= view_cursors_pos(s
);
420 if (pos
> viewport
.end
)
422 window_draw_cursor(win
, s
, &style_cursor
, &style_selection
);
426 static void window_draw_eof(Win
*win
) {
427 View
*view
= win
->view
;
428 if (view_width_get(view
) == 0)
430 CellStyle style
= win
->ui
->style_get(win
->ui
, UI_STYLE_EOF
);
431 for (Line
*l
= view_lines_last(view
)->next
; l
; l
= l
->next
) {
432 strncpy(l
->cells
[0].data
, view_symbol_eof_get(view
), sizeof(l
->cells
[0].data
)-1);
433 l
->cells
[0].style
= style
;
437 void vis_window_draw(Win
*win
) {
438 if (!win
->ui
|| !view_update(win
->view
))
441 vis_event_emit(vis
, VIS_EVENT_WIN_HIGHLIGHT
, win
);
443 window_draw_colorcolumn(win
);
444 window_draw_cursorline(win
);
445 if (!vis
->win
|| vis
->win
== win
|| vis
->win
->parent
== win
)
446 window_draw_selections(win
);
447 window_draw_eof(win
);
449 vis_event_emit(vis
, VIS_EVENT_WIN_STATUS
, win
);
453 void vis_window_invalidate(Win
*win
) {
454 for (Win
*w
= win
->vis
->windows
; w
; w
= w
->next
) {
455 if (w
->file
== win
->file
)
460 Win
*window_new_file(Vis
*vis
, File
*file
, enum UiOption options
) {
461 Win
*win
= calloc(1, sizeof(Win
));
466 win
->view
= view_new(file
->text
);
467 win
->ui
= vis
->ui
->window_new(vis
->ui
, win
, options
);
468 if (!win
->view
|| !win
->ui
) {
472 marklist_init(&win
->jumplist
, 32);
473 mark_init(&win
->saved_selections
);
475 view_options_set(win
->view
, view_options_get(win
->view
));
476 view_tabwidth_set(win
->view
, vis
->tabwidth
);
479 vis
->windows
->prev
= win
;
480 win
->next
= vis
->windows
;
483 vis
->ui
->window_focus(win
->ui
);
484 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++)
485 win
->modes
[i
].parent
= &vis_modes
[i
];
486 vis_event_emit(vis
, VIS_EVENT_WIN_OPEN
, win
);
490 bool vis_window_reload(Win
*win
) {
491 const char *name
= win
->file
->name
;
493 return false; /* can't reload unsaved file */
494 /* temporarily unset file name, otherwise file_new returns the same File */
495 win
->file
->name
= NULL
;
496 File
*file
= file_new(win
->vis
, name
);
497 win
->file
->name
= name
;
500 file_free(win
->vis
, win
->file
);
503 view_reload(win
->view
, file
->text
);
507 bool vis_window_split(Win
*original
) {
508 Win
*win
= window_new_file(original
->vis
, original
->file
, UI_OPTION_STATUSBAR
);
511 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++) {
512 if (original
->modes
[i
].bindings
)
513 win
->modes
[i
].bindings
= map_new();
514 if (win
->modes
[i
].bindings
)
515 map_copy(win
->modes
[i
].bindings
, original
->modes
[i
].bindings
);
517 win
->file
= original
->file
;
518 view_options_set(win
->view
, view_options_get(original
->view
));
519 view_cursor_to(win
->view
, view_cursor_get(original
->view
));
523 void vis_window_focus(Win
*win
) {
528 vis
->ui
->window_focus(win
->ui
);
531 void vis_window_next(Vis
*vis
) {
535 vis_window_focus(sel
->next
? sel
->next
: vis
->windows
);
538 void vis_window_prev(Vis
*vis
) {
544 for (sel
= vis
->windows
; sel
->next
; sel
= sel
->next
);
545 vis_window_focus(sel
);
548 int vis_window_width_get(const Win
*win
) {
549 return win
->ui
->window_width(win
->ui
);
552 int vis_window_height_get(const Win
*win
) {
553 return win
->ui
->window_height(win
->ui
);
556 void vis_draw(Vis
*vis
) {
557 for (Win
*win
= vis
->windows
; win
; win
= win
->next
)
558 view_draw(win
->view
);
561 void vis_redraw(Vis
*vis
) {
562 vis
->ui
->redraw(vis
->ui
);
566 void vis_update(Vis
*vis
) {
567 vis
->ui
->draw(vis
->ui
);
570 void vis_suspend(Vis
*vis
) {
571 vis
->ui
->suspend(vis
->ui
);
574 void vis_resume(Vis
*vis
) {
575 vis
->ui
->resume(vis
->ui
);
578 bool vis_window_new(Vis
*vis
, const char *filename
) {
579 File
*file
= file_new(vis
, filename
);
582 Win
*win
= window_new_file(vis
, file
, UI_OPTION_STATUSBAR
|UI_OPTION_SYMBOL_EOF
);
584 file_free(vis
, file
);
591 bool vis_window_new_fd(Vis
*vis
, int fd
) {
594 if (!vis_window_new(vis
, NULL
))
596 vis
->win
->file
->fd
= fd
;
600 bool vis_window_closable(Win
*win
) {
601 if (!win
|| !text_modified(win
->file
->text
))
603 return win
->file
->refcount
> 1;
606 void vis_window_swap(Win
*a
, Win
*b
) {
607 if (a
== b
|| !a
|| !b
)
624 if (vis
->windows
== a
)
626 else if (vis
->windows
== b
)
628 vis
->ui
->window_swap(a
->ui
, b
->ui
);
631 else if (vis
->win
== b
)
635 void vis_window_close(Win
*win
) {
639 vis_event_emit(vis
, VIS_EVENT_WIN_CLOSE
, win
);
640 file_free(vis
, win
->file
);
642 win
->prev
->next
= win
->next
;
644 win
->next
->prev
= win
->prev
;
645 if (vis
->windows
== win
)
646 vis
->windows
= win
->next
;
648 vis
->win
= win
->next
? win
->next
: win
->prev
;
649 if (win
== vis
->message_window
)
650 vis
->message_window
= NULL
;
653 vis
->ui
->window_focus(vis
->win
->ui
);
657 Vis
*vis_new(Ui
*ui
, VisEvent
*event
) {
660 Vis
*vis
= calloc(1, sizeof(Vis
));
663 vis
->exit_status
= -1;
666 vis
->expandtab
= false;
667 vis
->change_colors
= true;
668 for (size_t i
= 0; i
< LENGTH(vis
->registers
); i
++)
669 register_init(&vis
->registers
[i
]);
670 vis
->registers
[VIS_REG_BLACKHOLE
].type
= REGISTER_BLACKHOLE
;
671 vis
->registers
[VIS_REG_CLIPBOARD
].type
= REGISTER_CLIPBOARD
;
672 vis
->registers
[VIS_REG_NUMBER
].type
= REGISTER_NUMBER
;
673 array_init(&vis
->operators
);
674 array_init(&vis
->motions
);
675 array_init(&vis
->textobjects
);
676 array_init(&vis
->bindings
);
677 array_init(&vis
->actions_user
);
678 action_reset(&vis
->action
);
679 buffer_init(&vis
->input_queue
);
680 if (!(vis
->command_file
= file_new_internal(vis
, NULL
)))
682 if (!(vis
->search_file
= file_new_internal(vis
, NULL
)))
684 if (!(vis
->error_file
= file_new_internal(vis
, NULL
)))
686 if (!(vis
->actions
= map_new()))
688 if (!(vis
->keymap
= map_new()))
693 char *shell
= getenv("SHELL");
694 if ((!shell
|| !*shell
) && (pw
= getpwuid(getuid())))
695 shell
= pw
->pw_shell
;
696 if (!shell
|| !*shell
)
698 if (!(vis
->shell
= strdup(shell
)))
700 vis
->mode_prev
= vis
->mode
= &vis_modes
[VIS_MODE_NORMAL
];
703 if (event
->mode_insert_input
)
704 vis_modes
[VIS_MODE_INSERT
].input
= event
->mode_insert_input
;
705 if (event
->mode_replace_input
)
706 vis_modes
[VIS_MODE_REPLACE
].input
= event
->mode_replace_input
;
714 void vis_free(Vis
*vis
) {
717 vis_event_emit(vis
, VIS_EVENT_QUIT
);
720 vis_window_close(vis
->windows
);
721 file_free(vis
, vis
->command_file
);
722 file_free(vis
, vis
->search_file
);
723 file_free(vis
, vis
->error_file
);
724 for (int i
= 0; i
< LENGTH(vis
->registers
); i
++)
725 register_release(&vis
->registers
[i
]);
726 vis
->ui
->free(vis
->ui
);
729 while (map_first(vis
->usercmds
, &name
) && vis_cmd_unregister(vis
, name
));
731 map_free(vis
->usercmds
);
735 while (map_first(vis
->options
, &name
) && vis_option_unregister(vis
, name
));
737 map_free(vis
->options
);
738 map_free(vis
->actions
);
739 map_free(vis
->keymap
);
740 buffer_release(&vis
->input_queue
);
741 for (int i
= 0; i
< VIS_MODE_INVALID
; i
++)
742 map_free(vis_modes
[i
].bindings
);
743 array_release_full(&vis
->operators
);
744 array_release_full(&vis
->motions
);
745 array_release_full(&vis
->textobjects
);
746 while (array_length(&vis
->bindings
))
747 vis_binding_free(vis
, array_get_ptr(&vis
->bindings
, 0));
748 array_release(&vis
->bindings
);
749 while (array_length(&vis
->actions_user
))
750 vis_action_free(vis
, array_get_ptr(&vis
->actions_user
, 0));
751 array_release(&vis
->actions_user
);
756 void vis_insert(Vis
*vis
, size_t pos
, const char *data
, size_t len
) {
757 text_insert(vis
->win
->file
->text
, pos
, data
, len
);
758 vis_window_invalidate(vis
->win
);
761 void vis_insert_key(Vis
*vis
, const char *data
, size_t len
) {
762 for (Selection
*s
= view_selections(vis
->win
->view
); s
; s
= view_selections_next(s
)) {
763 size_t pos
= view_cursors_pos(s
);
764 vis_insert(vis
, pos
, data
, len
);
765 view_cursors_scroll_to(s
, pos
+ len
);
769 void vis_replace(Vis
*vis
, size_t pos
, const char *data
, size_t len
) {
770 Text
*txt
= vis
->win
->file
->text
;
771 Iterator it
= text_iterator_get(txt
, pos
);
772 int chars
= text_char_count(data
, len
);
773 for (char c
; chars
-- > 0 && text_iterator_byte_get(&it
, &c
) && c
!= '\n'; )
774 text_iterator_char_next(&it
, NULL
);
776 text_delete(txt
, pos
, it
.pos
- pos
);
777 vis_insert(vis
, pos
, data
, len
);
780 void vis_replace_key(Vis
*vis
, const char *data
, size_t len
) {
781 for (Selection
*s
= view_selections(vis
->win
->view
); s
; s
= view_selections_next(s
)) {
782 size_t pos
= view_cursors_pos(s
);
783 vis_replace(vis
, pos
, data
, len
);
784 view_cursors_scroll_to(s
, pos
+ len
);
788 void vis_delete(Vis
*vis
, size_t pos
, size_t len
) {
789 text_delete(vis
->win
->file
->text
, pos
, len
);
790 vis_window_invalidate(vis
->win
);
793 bool vis_action_register(Vis
*vis
, const KeyAction
*action
) {
794 return map_put(vis
->actions
, action
->name
, action
);
797 bool vis_keymap_add(Vis
*vis
, const char *key
, const char *mapping
) {
798 return map_put(vis
->keymap
, key
, mapping
);
801 void vis_keymap_disable(Vis
*vis
) {
802 vis
->keymap_disabled
= true;
805 void vis_interrupt(Vis
*vis
) {
806 vis
->interrupted
= true;
809 bool vis_interrupt_requested(Vis
*vis
) {
810 return vis
->interrupted
;
813 void vis_do(Vis
*vis
) {
815 File
*file
= win
->file
;
816 Text
*txt
= file
->text
;
817 View
*view
= win
->view
;
818 Action
*a
= &vis
->action
;
820 int count
= MAX(a
->count
, 1);
821 if (a
->op
== &vis_operators
[VIS_OP_MODESWITCH
])
822 count
= 1; /* count should apply to inserted text not motion */
823 bool repeatable
= a
->op
&& !vis
->macro_operator
&& !vis
->win
->parent
;
824 bool multiple_cursors
= view_selections_count(view
) > 1;
825 bool linewise
= !(a
->type
& CHARWISE
) && (
826 a
->type
& LINEWISE
|| (a
->movement
&& a
->movement
->type
& LINEWISE
) ||
827 vis
->mode
== &vis_modes
[VIS_MODE_VISUAL_LINE
]);
830 Register
*reg
= a
->reg
;
831 size_t reg_slot
= multiple_cursors
? EPOS
: 0;
832 size_t last_reg_slot
= reg_slot
;
834 reg
= &vis
->registers
[file
->internal
? VIS_REG_PROMPT
: VIS_REG_DEFAULT
];
835 if (a
->op
== &vis_operators
[VIS_OP_PUT_AFTER
] && multiple_cursors
&& vis_register_count(vis
, reg
) == 1)
838 if (vis
->mode
->visual
&& a
->op
)
839 window_selection_save(win
);
841 for (Selection
*sel
= view_selections(view
), *next
; sel
; sel
= next
) {
842 if (vis
->interrupted
)
845 next
= view_selections_next(sel
);
847 size_t pos
= view_cursors_pos(sel
);
849 if (!view_selections_dispose(sel
))
850 view_cursors_to(sel
, 0);
854 OperatorContext c
= {
858 .range
= text_range_empty(),
860 .reg_slot
= reg_slot
== EPOS
? (size_t)view_selections_number(sel
) : reg_slot
,
861 .linewise
= linewise
,
863 .context
= a
->op
? a
->op
->context
: NULL
,
866 last_reg_slot
= c
.reg_slot
;
871 for (int i
= 0; i
< count
; i
++) {
872 size_t pos_prev
= pos
;
873 if (a
->movement
->txt
)
874 pos
= a
->movement
->txt(txt
, pos
);
875 else if (a
->movement
->cur
)
876 pos
= a
->movement
->cur(sel
);
877 else if (a
->movement
->file
)
878 pos
= a
->movement
->file(vis
, file
, sel
);
879 else if (a
->movement
->vis
)
880 pos
= a
->movement
->vis(vis
, txt
, pos
);
881 else if (a
->movement
->view
)
882 pos
= a
->movement
->view(vis
, view
);
883 else if (a
->movement
->win
)
884 pos
= a
->movement
->win(vis
, win
, pos
);
885 else if (a
->movement
->user
)
886 pos
= a
->movement
->user(vis
, win
, a
->movement
->data
, pos
);
887 if (pos
== EPOS
|| a
->movement
->type
& IDEMPOTENT
|| pos
== pos_prev
) {
888 err
= a
->movement
->type
& COUNT_EXACT
;
899 c
.range
.start
= start
;
903 c
.range
= text_range_new(start
, pos
);
908 if (a
->movement
->type
& CHARWISE
)
909 view_cursors_scroll_to(sel
, pos
);
911 view_cursors_to(sel
, pos
);
912 if (vis
->mode
->visual
)
913 c
.range
= view_selections_get(sel
);
914 } else if (a
->movement
->type
& INCLUSIVE
&& c
.range
.end
> start
) {
915 c
.range
.end
= text_char_next(txt
, c
.range
.end
);
916 } else if (linewise
&& (a
->movement
->type
& LINEWISE_INCLUSIVE
)) {
917 c
.range
.end
= text_char_next(txt
, c
.range
.end
);
919 } else if (a
->textobj
) {
920 if (vis
->mode
->visual
)
921 c
.range
= view_selections_get(sel
);
923 c
.range
.start
= c
.range
.end
= pos
;
924 for (int i
= 0; i
< count
; i
++) {
925 Filerange r
= text_range_empty();
927 r
= a
->textobj
->txt(txt
, pos
);
928 else if (a
->textobj
->vis
)
929 r
= a
->textobj
->vis(vis
, txt
, pos
);
930 else if (a
->textobj
->user
)
931 r
= a
->textobj
->user(vis
, win
, a
->textobj
->data
, pos
);
932 if (!text_range_valid(&r
))
934 if (a
->textobj
->type
& TEXTOBJECT_DELIMITED_OUTER
) {
939 if (vis
->mode
->visual
|| (i
> 0 && !(a
->textobj
->type
& TEXTOBJECT_NON_CONTIGUOUS
)))
940 c
.range
= text_range_union(&c
.range
, &r
);
945 if (a
->textobj
->type
& TEXTOBJECT_EXTEND_BACKWARD
) {
947 if ((a
->textobj
->type
& TEXTOBJECT_DELIMITED_INNER
) && pos
> 0)
951 if (a
->textobj
->type
& TEXTOBJECT_DELIMITED_INNER
)
956 } else if (vis
->mode
->visual
) {
957 c
.range
= view_selections_get(sel
);
958 if (!text_range_valid(&c
.range
))
959 c
.range
.start
= c
.range
.end
= pos
;
962 if (linewise
&& vis
->mode
!= &vis_modes
[VIS_MODE_VISUAL
])
963 c
.range
= text_range_linewise(txt
, &c
.range
);
964 if (vis
->mode
->visual
) {
965 view_selections_set(sel
, &c
.range
);
966 view_selections_anchor(sel
, true);
970 size_t pos
= a
->op
->func(vis
, txt
, &c
);
972 view_selections_dispose(sel
);
973 } else if (pos
<= text_size(txt
)) {
974 view_selection_clear(sel
);
975 view_cursors_to(sel
, pos
);
980 view_selections_normalize(view
);
981 if (a
->movement
&& (a
->movement
->type
& JUMP
))
982 vis_jumplist_save(vis
);
986 if (a
->op
== &vis_operators
[VIS_OP_YANK
] ||
987 a
->op
== &vis_operators
[VIS_OP_DELETE
] ||
988 a
->op
== &vis_operators
[VIS_OP_CHANGE
] ||
989 a
->op
== &vis_operators
[VIS_OP_REPLACE
]) {
990 register_resize(reg
, last_reg_slot
+1);
993 /* we do not support visual repeat, still do something resonable */
994 if (vis
->mode
->visual
&& !a
->movement
&& !a
->textobj
)
995 a
->movement
= &vis_motions
[VIS_MOVE_NOP
];
997 /* operator implementations must not change the mode,
998 * they might get called multiple times (once for every cursor)
1000 if (a
->op
== &vis_operators
[VIS_OP_CHANGE
]) {
1001 vis_mode_switch(vis
, VIS_MODE_INSERT
);
1002 } else if (a
->op
== &vis_operators
[VIS_OP_MODESWITCH
]) {
1003 vis_mode_switch(vis
, a
->mode
);
1004 } else if (vis
->mode
== &vis_modes
[VIS_MODE_OPERATOR_PENDING
]) {
1005 mode_set(vis
, vis
->mode_prev
);
1006 } else if (vis
->mode
->visual
) {
1007 vis_mode_switch(vis
, VIS_MODE_NORMAL
);
1010 if (vis
->mode
== &vis_modes
[VIS_MODE_NORMAL
])
1011 vis_file_snapshot(vis
, file
);
1015 if (a
!= &vis
->action_prev
) {
1018 a
->macro
= vis
->macro_operator
;
1019 vis
->action_prev
= *a
;
1025 void action_reset(Action
*a
) {
1026 memset(a
, 0, sizeof(*a
));
1027 a
->count
= VIS_COUNT_UNKNOWN
;
1030 void vis_cancel(Vis
*vis
) {
1031 action_reset(&vis
->action
);
1034 void vis_die(Vis
*vis
, const char *msg
, ...) {
1037 vis
->ui
->die(vis
->ui
, msg
, ap
);
1041 const char *vis_keys_next(Vis
*vis
, const char *keys
) {
1042 if (!keys
|| !*keys
)
1045 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1046 const char *next
= NULL
;
1047 /* first try to parse a special key of the form <Key> */
1048 if (*keys
== '<' && keys
[1] && (next
= termkey_strpkey(termkey
, keys
+1, &key
, TERMKEY_FORMAT_VIM
)) && *next
== '>')
1050 if (strncmp(keys
, "<vis-", 5) == 0) {
1051 const char *start
= keys
+ 1, *end
= start
;
1052 while (*end
&& *end
!= '>')
1054 if (end
> start
&& end
- start
- 1 < VIS_KEY_LENGTH_MAX
&& *end
== '>') {
1055 char key
[VIS_KEY_LENGTH_MAX
];
1056 memcpy(key
, start
, end
- start
);
1057 key
[end
- start
] = '\0';
1058 if (map_get(vis
->actions
, key
))
1064 while (!ISUTF8(*keys
))
1069 long vis_keys_codepoint(Vis
*vis
, const char *keys
) {
1070 long codepoint
= -1;
1073 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1077 if (keys
[0] == '<' && !keys
[1])
1080 if (keys
[0] == '<' && (next
= termkey_strpkey(termkey
, keys
+1, &key
, TERMKEY_FORMAT_VIM
)) && *next
== '>')
1081 codepoint
= (key
.type
== TERMKEY_TYPE_UNICODE
) ? key
.code
.codepoint
: -1;
1082 else if ((next
= termkey_strpkey(termkey
, keys
, &key
, TERMKEY_FORMAT_VIM
)))
1083 codepoint
= (key
.type
== TERMKEY_TYPE_UNICODE
) ? key
.code
.codepoint
: -1;
1085 if (codepoint
!= -1) {
1086 if (key
.modifiers
== TERMKEY_KEYMOD_CTRL
)
1091 if (!next
|| key
.type
!= TERMKEY_TYPE_KEYSYM
)
1094 const int keysym
[] = {
1095 TERMKEY_SYM_ENTER
, '\r',
1096 TERMKEY_SYM_TAB
, '\t',
1097 TERMKEY_SYM_BACKSPACE
, '\b',
1098 TERMKEY_SYM_ESCAPE
, 0x1b,
1099 TERMKEY_SYM_DELETE
, 0x7f,
1103 for (const int *k
= keysym
; k
[0]; k
+= 2) {
1104 if (key
.code
.sym
== k
[0])
1111 bool vis_keys_utf8(Vis
*vis
, const char *keys
, char utf8
[static UTFmax
+1]) {
1112 Rune rune
= vis_keys_codepoint(vis
, keys
);
1113 if (rune
== (Rune
)-1)
1115 size_t len
= runetochar(utf8
, &rune
);
1122 size_t len
; // length of the prefix
1123 int count
; // how many bindings can complete this prefix
1124 bool angle_bracket
; // does the prefix end with '<'
1127 static bool isprefix(const char *key
, void *value
, void *data
) {
1128 PrefixCompletion
*completion
= data
;
1129 if (!completion
->angle_bracket
) {
1130 completion
->count
++;
1132 const char *start
= key
+ completion
->len
;
1133 const char *end
= vis_keys_next(completion
->vis
, start
);
1134 if (end
&& start
+ 1 == end
)
1135 completion
->count
++;
1137 return completion
->count
== 1;
1140 static void vis_keys_process(Vis
*vis
, size_t pos
) {
1141 Buffer
*buf
= &vis
->input_queue
;
1142 char *keys
= buf
->data
+ pos
, *start
= keys
, *cur
= keys
, *end
= keys
, *binding_end
= keys
;;
1143 bool prefix
= false;
1144 KeyBinding
*binding
= NULL
;
1146 while (cur
&& *cur
) {
1148 if (!(end
= (char*)vis_keys_next(vis
, cur
))) {
1149 buffer_remove(buf
, keys
- buf
->data
, strlen(keys
));
1157 for (Mode
*global_mode
= vis
->mode
; global_mode
&& !prefix
; global_mode
= global_mode
->parent
) {
1158 for (int global
= 0; global
< 2 && !prefix
; global
++) {
1159 Mode
*mode
= (global
|| !vis
->win
) ?
1161 &vis
->win
->modes
[global_mode
->id
];
1162 if (!mode
->bindings
)
1164 /* keep track of longest matching binding */
1165 KeyBinding
*match
= map_get(mode
->bindings
, start
);
1166 if (match
&& end
> binding_end
) {
1171 const Map
*pmap
= map_prefix(mode
->bindings
, start
);
1172 PrefixCompletion completions
= {
1176 .angle_bracket
= !strcmp(cur
, "<"),
1178 map_iterate(pmap
, isprefix
, &completions
);
1180 prefix
= (!match
&& completions
.count
> 0) ||
1181 ( match
&& completions
.count
> 1);
1188 /* input sofar is ambigious, wait for more */
1191 } else if (binding
) { /* exact match */
1192 if (binding
->action
) {
1193 size_t len
= binding_end
- start
;
1194 strcpy(vis
->key_prev
, vis
->key_current
);
1195 strncpy(vis
->key_current
, start
, len
);
1196 vis
->key_current
[len
] = '\0';
1197 end
= (char*)binding
->action
->func(vis
, binding_end
, &binding
->action
->arg
);
1203 } else if (binding
->alias
) {
1204 buffer_remove(buf
, start
- buf
->data
, binding_end
- start
);
1205 buffer_insert0(buf
, start
- buf
->data
, binding
->alias
);
1209 binding_end
= start
;
1210 } else { /* no keybinding */
1211 KeyAction
*action
= NULL
;
1212 if (start
[0] == '<' && end
[-1] == '>') {
1213 /* test for special editor key command */
1216 action
= map_get(vis
->actions
, start
+1);
1219 size_t len
= end
- start
;
1220 strcpy(vis
->key_prev
, vis
->key_current
);
1221 strncpy(vis
->key_current
, start
, len
);
1222 vis
->key_current
[len
] = '\0';
1223 end
= (char*)action
->func(vis
, end
, &action
->arg
);
1230 if (!action
&& vis
->mode
->input
) {
1231 end
= (char*)vis_keys_next(vis
, start
);
1232 vis
->mode
->input(vis
, start
, end
- start
);
1238 buffer_remove(buf
, keys
- buf
->data
, end
- keys
);
1241 void vis_keys_feed(Vis
*vis
, const char *input
) {
1246 if (!macro_append(¯o
, input
))
1248 /* use internal function, to keep Lua based tests which use undo points working */
1249 macro_replay_internal(vis
, ¯o
);
1250 macro_release(¯o
);
1253 static void vis_keys_push(Vis
*vis
, const char *input
, size_t pos
, bool record
) {
1256 if (record
&& vis
->recording
)
1257 macro_append(vis
->recording
, input
);
1258 if (vis
->macro_operator
)
1259 macro_append(vis
->macro_operator
, input
);
1260 if (buffer_append0(&vis
->input_queue
, input
))
1261 vis_keys_process(vis
, pos
);
1264 static const char *getkey(Vis
*vis
) {
1265 TermKeyKey key
= { 0 };
1266 if (!vis
->ui
->getkey(vis
->ui
, &key
))
1269 bool use_keymap
= vis
->mode
->id
!= VIS_MODE_INSERT
&&
1270 vis
->mode
->id
!= VIS_MODE_REPLACE
&&
1271 !vis
->keymap_disabled
;
1272 vis
->keymap_disabled
= false;
1273 if (key
.type
== TERMKEY_TYPE_UNICODE
&& use_keymap
) {
1274 const char *mapped
= map_get(vis
->keymap
, key
.utf8
);
1276 size_t len
= strlen(mapped
)+1;
1277 if (len
<= sizeof(key
.utf8
))
1278 memcpy(key
.utf8
, mapped
, len
);
1282 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1283 termkey_strfkey(termkey
, vis
->key
, sizeof(vis
->key
), &key
, TERMKEY_FORMAT_VIM
);
1287 bool vis_signal_handler(Vis
*vis
, int signum
, const siginfo_t
*siginfo
, const void *context
) {
1290 for (File
*file
= vis
->files
; file
; file
= file
->next
) {
1291 if (text_mmaped(file
->text
, siginfo
->si_addr
))
1292 file
->truncated
= true;
1296 siglongjmp(vis
->sigbus_jmpbuf
, 1);
1299 vis
->interrupted
= true;
1305 vis
->need_resize
= true;
1309 vis
->terminate
= true;
1315 int vis_run(Vis
*vis
) {
1317 return EXIT_SUCCESS
;
1318 if (vis
->exit_status
!= -1)
1319 return vis
->exit_status
;
1320 vis
->running
= true;
1322 vis_event_emit(vis
, VIS_EVENT_START
);
1324 struct timespec idle
= { .tv_nsec
= 0 }, *timeout
= NULL
;
1327 sigemptyset(&emptyset
);
1329 vis
->exit_status
= EXIT_SUCCESS
;
1331 sigsetjmp(vis
->sigbus_jmpbuf
, 1);
1333 while (vis
->running
) {
1336 FD_SET(STDIN_FILENO
, &fds
);
1340 for (Win
*next
, *win
= vis
->windows
; win
; win
= next
) {
1342 if (win
->file
->truncated
) {
1344 name
= strdup(win
->file
->name
);
1345 vis_window_close(win
);
1349 vis_die(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
1351 vis_info_show(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
1352 vis
->sigbus
= false;
1357 vis_die(vis
, "Killed by SIGTERM\n");
1358 if (vis
->interrupted
) {
1359 vis
->interrupted
= false;
1360 vis_keys_feed(vis
, "<C-c>");
1365 vis
->resume
= false;
1368 if (vis
->need_resize
) {
1369 vis
->ui
->resize(vis
->ui
);
1370 vis
->need_resize
= false;
1374 idle
.tv_sec
= vis
->mode
->idle_timeout
;
1375 int r
= pselect(1, &fds
, NULL
, NULL
, timeout
, &emptyset
);
1376 if (r
== -1 && errno
== EINTR
)
1380 /* TODO save all pending changes to a ~suffixed file */
1381 vis_die(vis
, "Error in mainloop: %s\n", strerror(errno
));
1384 if (!FD_ISSET(STDIN_FILENO
, &fds
)) {
1385 if (vis
->mode
->idle
)
1386 vis
->mode
->idle(vis
);
1391 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1392 termkey_advisereadable(termkey
);
1395 while ((key
= getkey(vis
)))
1396 vis_keys_push(vis
, key
, 0, true);
1398 if (vis
->mode
->idle
)
1401 return vis
->exit_status
;
1404 Macro
*macro_get(Vis
*vis
, enum VisRegister id
) {
1405 if (id
== VIS_MACRO_LAST_RECORDED
)
1406 return vis
->last_recording
;
1407 if (VIS_REG_A
<= id
&& id
<= VIS_REG_Z
)
1409 if (id
< LENGTH(vis
->registers
))
1410 return array_get(&vis
->registers
[id
].values
, 0);
1414 void macro_operator_record(Vis
*vis
) {
1415 if (vis
->macro_operator
)
1417 vis
->macro_operator
= macro_get(vis
, VIS_MACRO_OPERATOR
);
1418 macro_reset(vis
->macro_operator
);
1421 void macro_operator_stop(Vis
*vis
) {
1422 if (!vis
->macro_operator
)
1424 Macro
*dot
= macro_get(vis
, VIS_REG_DOT
);
1425 buffer_put(dot
, vis
->macro_operator
->data
, vis
->macro_operator
->len
);
1426 vis
->action_prev
.macro
= dot
;
1427 vis
->macro_operator
= NULL
;
1430 bool vis_macro_record(Vis
*vis
, enum VisRegister id
) {
1431 Macro
*macro
= macro_get(vis
, id
);
1432 if (vis
->recording
|| !macro
)
1434 if (!(VIS_REG_A
<= id
&& id
<= VIS_REG_Z
))
1436 vis
->recording
= macro
;
1437 vis_event_emit(vis
, VIS_EVENT_WIN_STATUS
, vis
->win
);
1441 bool vis_macro_record_stop(Vis
*vis
) {
1442 if (!vis
->recording
)
1444 /* XXX: hack to remove last recorded key, otherwise upon replay
1445 * we would start another recording */
1446 if (vis
->recording
->len
> 1) {
1447 vis
->recording
->len
--;
1448 vis
->recording
->data
[vis
->recording
->len
-1] = '\0';
1450 vis
->last_recording
= vis
->recording
;
1451 vis
->recording
= NULL
;
1452 vis_event_emit(vis
, VIS_EVENT_WIN_STATUS
, vis
->win
);
1456 bool vis_macro_recording(Vis
*vis
) {
1457 return vis
->recording
;
1460 static void macro_replay(Vis
*vis
, const Macro
*macro
) {
1461 const Macro
*replaying
= vis
->replaying
;
1462 vis
->replaying
= macro
;
1463 macro_replay_internal(vis
, macro
);
1464 vis
->replaying
= replaying
;
1467 static void macro_replay_internal(Vis
*vis
, const Macro
*macro
) {
1468 size_t pos
= buffer_length0(&vis
->input_queue
);
1469 for (char *key
= macro
->data
, *next
; key
; key
= next
) {
1471 next
= (char*)vis_keys_next(vis
, key
);
1477 vis_keys_push(vis
, key
, pos
, false);
1484 bool vis_macro_replay(Vis
*vis
, enum VisRegister id
) {
1485 if (id
== VIS_REG_SEARCH
)
1486 return vis_motion(vis
, VIS_MOVE_SEARCH_REPEAT_FORWARD
);
1487 if (id
== VIS_REG_COMMAND
) {
1488 const char *cmd
= register_get(vis
, &vis
->registers
[id
], NULL
);
1489 return vis_cmd(vis
, cmd
);
1492 Macro
*macro
= macro_get(vis
, id
);
1493 if (!macro
|| macro
== vis
->recording
)
1495 int count
= vis_count_get_default(vis
, 1);
1497 for (int i
= 0; i
< count
; i
++)
1498 macro_replay(vis
, macro
);
1499 vis_file_snapshot(vis
, vis
->win
->file
);
1503 void vis_repeat(Vis
*vis
) {
1504 const Macro
*macro
= vis
->action_prev
.macro
;
1505 int count
= vis
->action
.count
;
1506 if (count
!= VIS_COUNT_UNKNOWN
)
1507 vis
->action_prev
.count
= count
;
1509 count
= vis
->action_prev
.count
;
1510 vis
->action
= vis
->action_prev
;
1513 Mode
*mode
= vis
->mode
;
1514 Action action_prev
= vis
->action_prev
;
1515 if (count
< 1 || action_prev
.op
== &vis_operators
[VIS_OP_CHANGE
])
1517 if (vis
->action_prev
.op
== &vis_operators
[VIS_OP_MODESWITCH
])
1518 vis
->action_prev
.count
= 1;
1519 for (int i
= 0; i
< count
; i
++) {
1520 if (vis
->interrupted
)
1522 mode_set(vis
, mode
);
1523 macro_replay(vis
, macro
);
1525 vis
->action_prev
= action_prev
;
1528 vis_file_snapshot(vis
, vis
->win
->file
);
1531 int vis_count_get(Vis
*vis
) {
1532 return vis
->action
.count
;
1535 int vis_count_get_default(Vis
*vis
, int def
) {
1536 if (vis
->action
.count
== VIS_COUNT_UNKNOWN
)
1538 return vis
->action
.count
;
1541 void vis_count_set(Vis
*vis
, int count
) {
1542 vis
->action
.count
= (count
>= 0 ? count
: VIS_COUNT_UNKNOWN
);
1545 VisCountIterator
vis_count_iterator_get(Vis
*vis
, int def
) {
1546 return (VisCountIterator
) {
1549 .count
= vis_count_get_default(vis
, def
),
1553 VisCountIterator
vis_count_iterator_init(Vis
*vis
, int count
) {
1554 return (VisCountIterator
) {
1561 bool vis_count_iterator_next(VisCountIterator
*it
) {
1562 if (it
->vis
->interrupted
)
1564 return it
->iteration
++ < it
->count
;
1567 void vis_exit(Vis
*vis
, int status
) {
1568 vis
->running
= false;
1569 vis
->exit_status
= status
;
1572 void vis_insert_tab(Vis
*vis
) {
1573 if (!vis
->expandtab
) {
1574 vis_insert_key(vis
, "\t", 1);
1578 int tabwidth
= MIN(vis
->tabwidth
, LENGTH(spaces
) - 1);
1579 for (Selection
*s
= view_selections(vis
->win
->view
); s
; s
= view_selections_next(s
)) {
1580 size_t pos
= view_cursors_pos(s
);
1581 int width
= text_line_width_get(vis
->win
->file
->text
, pos
);
1582 int count
= tabwidth
- (width
% tabwidth
);
1583 for (int i
= 0; i
< count
; i
++)
1585 spaces
[count
] = '\0';
1586 vis_insert(vis
, pos
, spaces
, count
);
1587 view_cursors_scroll_to(s
, pos
+ count
);
1591 size_t vis_text_insert_nl(Vis
*vis
, Text
*txt
, size_t pos
) {
1592 size_t indent_len
= 0;
1593 char byte
, *indent
= NULL
;
1594 /* insert second newline at end of file, except if there is already one */
1595 bool eof
= pos
== text_size(txt
);
1596 bool nl2
= eof
&& !(pos
> 0 && text_byte_get(txt
, pos
-1, &byte
) && byte
== '\n');
1598 if (vis
->autoindent
) {
1599 /* copy leading white space of current line */
1600 size_t begin
= text_line_begin(txt
, pos
);
1601 size_t start
= text_line_start(txt
, begin
);
1602 size_t end
= text_line_end(txt
, start
);
1605 indent_len
= start
>= begin
? start
-begin
: 0;
1609 indent
= malloc(indent_len
+1);
1611 indent_len
= text_bytes_get(txt
, begin
, indent_len
, indent
);
1615 text_insert(txt
, pos
, "\n", 1);
1618 text_insert(txt
, text_size(txt
), "\n", 1);
1620 pos
--; /* place cursor before, not after nl */
1625 text_insert(txt
, pos
, indent
, indent_len
);
1627 return pos
+ indent_len
;
1630 void vis_insert_nl(Vis
*vis
) {
1631 Win
*win
= vis
->win
;
1632 View
*view
= win
->view
;
1633 Text
*txt
= win
->file
->text
;
1634 for (Selection
*s
= view_selections(view
); s
; s
= view_selections_next(s
)) {
1635 size_t pos
= view_cursors_pos(s
);
1636 size_t newpos
= vis_text_insert_nl(vis
, txt
, pos
);
1637 /* This is a bit of a hack to fix cursor positioning when
1638 * inserting a new line at the start of the view port.
1639 * It has the effect of reseting the mark used by the view
1640 * code to keep track of the start of the visible region.
1642 view_cursors_to(s
, pos
);
1643 view_cursors_to(s
, newpos
);
1645 vis_window_invalidate(win
);
1648 Regex
*vis_regex(Vis
*vis
, const char *pattern
) {
1649 if (!pattern
&& !(pattern
= register_get(vis
, &vis
->registers
[VIS_REG_SEARCH
], NULL
)))
1651 Regex
*regex
= text_regex_new();
1654 if (text_regex_compile(regex
, pattern
, REG_EXTENDED
|REG_NEWLINE
) != 0) {
1655 text_regex_free(regex
);
1658 register_put0(vis
, &vis
->registers
[VIS_REG_SEARCH
], pattern
);
1662 int vis_pipe(Vis
*vis
, File
*file
, Filerange
*range
, const char *argv
[],
1663 void *stdout_context
, ssize_t (*read_stdout
)(void *stdout_context
, char *data
, size_t len
),
1664 void *stderr_context
, ssize_t (*read_stderr
)(void *stderr_context
, char *data
, size_t len
)) {
1666 /* if an invalid range was given, stdin (i.e. key board input) is passed
1667 * through the external command. */
1668 Text
*text
= file
->text
;
1669 int pin
[2], pout
[2], perr
[2], status
= -1;
1670 bool interactive
= !text_range_valid(range
);
1671 Filerange rout
= interactive
? text_range_new(0, 0) : *range
;
1673 if (pipe(pin
) == -1)
1675 if (pipe(pout
) == -1) {
1681 if (pipe(perr
) == -1) {
1689 vis
->ui
->terminal_save(vis
->ui
);
1699 vis_info_show(vis
, "fork failure: %s", strerror(errno
));
1701 } else if (pid
== 0) { /* child i.e filter */
1702 sigset_t sigterm_mask
;
1703 sigemptyset(&sigterm_mask
);
1704 sigaddset(&sigterm_mask
, SIGTERM
);
1705 if (sigprocmask(SIG_UNBLOCK
, &sigterm_mask
, NULL
) == -1) {
1706 fprintf(stderr
, "failed to reset signal mask");
1710 int null
= open("/dev/null", O_RDWR
);
1712 fprintf(stderr
, "failed to open /dev/null");
1717 /* If we have nothing to write, let stdin point to
1718 * /dev/null instead of a pipe which is immediately
1719 * closed. Some programs behave differently when used
1722 if (text_range_size(range
) == 0)
1723 dup2(null
, STDIN_FILENO
);
1725 dup2(pin
[0], STDIN_FILENO
);
1731 dup2(STDERR_FILENO
, STDOUT_FILENO
);
1732 /* For some reason the first byte written by the
1733 * interactive application is not being displayed.
1734 * It probably has something to do with the terminal
1735 * state change. By writing a dummy byte ourself we
1736 * ensure that the complete output is visible.
1738 while(write(STDOUT_FILENO
, " ", 1) == -1 && errno
== EINTR
);
1739 } else if (read_stdout
) {
1740 dup2(pout
[1], STDOUT_FILENO
);
1742 dup2(null
, STDOUT_FILENO
);
1748 dup2(perr
[1], STDERR_FILENO
);
1750 dup2(null
, STDERR_FILENO
);
1757 char *name
= strrchr(file
->name
, '/');
1758 setenv("vis_filepath", file
->name
, 1);
1759 setenv("vis_filename", name
? name
+1 : file
->name
, 1);
1763 execlp(vis
->shell
, vis
->shell
, "-c", argv
[0], (char*)NULL
);
1765 execvp(argv
[0], (char* const*)argv
);
1766 fprintf(stderr
, "exec failure: %s", strerror(errno
));
1770 vis
->interrupted
= false;
1776 if (fcntl(pout
[0], F_SETFL
, O_NONBLOCK
) == -1 ||
1777 fcntl(perr
[0], F_SETFL
, O_NONBLOCK
) == -1)
1783 if (vis
->interrupted
) {
1791 FD_SET(pin
[1], &wfds
);
1793 FD_SET(pout
[0], &rfds
);
1795 FD_SET(perr
[0], &rfds
);
1797 if (select(FD_SETSIZE
, &rfds
, &wfds
, NULL
, NULL
) == -1) {
1800 vis_info_show(vis
, "Select failure");
1804 if (pin
[1] != -1 && FD_ISSET(pin
[1], &wfds
)) {
1805 Filerange junk
= rout
;
1806 if (junk
.end
> junk
.start
+ PIPE_BUF
)
1807 junk
.end
= junk
.start
+ PIPE_BUF
;
1808 ssize_t len
= text_write_range(text
, &junk
, pin
[1]);
1811 if (text_range_size(&rout
) == 0) {
1819 vis_info_show(vis
, "Error writing to external command");
1823 if (pout
[0] != -1 && FD_ISSET(pout
[0], &rfds
)) {
1825 ssize_t len
= read(pout
[0], buf
, sizeof buf
);
1828 (*read_stdout
)(stdout_context
, buf
, len
);
1829 } else if (len
== 0) {
1832 } else if (errno
!= EINTR
&& errno
!= EWOULDBLOCK
) {
1833 vis_info_show(vis
, "Error reading from filter stdout");
1839 if (perr
[0] != -1 && FD_ISSET(perr
[0], &rfds
)) {
1841 ssize_t len
= read(perr
[0], buf
, sizeof buf
);
1844 (*read_stderr
)(stderr_context
, buf
, len
);
1845 } else if (len
== 0) {
1848 } else if (errno
!= EINTR
&& errno
!= EWOULDBLOCK
) {
1849 vis_info_show(vis
, "Error reading from filter stderr");
1855 } while (pin
[1] != -1 || pout
[0] != -1 || perr
[0] != -1);
1866 if (vis
->interrupted
)
1868 pid_t died
= waitpid(pid
, &status
, 0);
1869 if ((died
== -1 && errno
== ECHILD
) || pid
== died
)
1873 /* clear any pending SIGTERM */
1874 struct sigaction sigterm_ignore
, sigterm_old
;
1875 sigterm_ignore
.sa_handler
= SIG_IGN
;
1876 sigterm_ignore
.sa_flags
= 0;
1877 sigemptyset(&sigterm_ignore
.sa_mask
);
1879 sigaction(SIGTERM
, &sigterm_ignore
, &sigterm_old
);
1880 sigaction(SIGTERM
, &sigterm_old
, NULL
);
1882 vis
->interrupted
= false;
1883 vis
->ui
->terminal_restore(vis
->ui
);
1888 static ssize_t
read_buffer(void *context
, char *data
, size_t len
) {
1889 buffer_append(context
, data
, len
);
1893 int vis_pipe_collect(Vis
*vis
, File
*file
, Filerange
*range
, const char *argv
[], char **out
, char **err
) {
1894 Buffer bufout
, buferr
;
1895 buffer_init(&bufout
);
1896 buffer_init(&buferr
);
1897 int status
= vis_pipe(vis
, file
, range
, argv
,
1898 &bufout
, out
? read_buffer
: NULL
,
1899 &buferr
, err
? read_buffer
: NULL
);
1900 buffer_terminate(&bufout
);
1901 buffer_terminate(&buferr
);
1903 *out
= buffer_move(&bufout
);
1905 *err
= buffer_move(&buferr
);
1906 buffer_release(&bufout
);
1907 buffer_release(&buferr
);
1911 bool vis_cmd(Vis
*vis
, const char *cmdline
) {
1914 while (*cmdline
== ':')
1916 size_t len
= strlen(cmdline
);
1917 char *line
= malloc(len
+2);
1920 strncpy(line
, cmdline
, len
+1);
1922 for (char *end
= line
+ len
- 1; end
>= line
&& isspace((unsigned char)*end
); end
--)
1925 enum SamError err
= sam_cmd(vis
, line
);
1926 if (err
!= SAM_ERR_OK
)
1927 vis_info_show(vis
, "%s", sam_error(err
));
1929 return err
== SAM_ERR_OK
;
1932 void vis_file_snapshot(Vis
*vis
, File
*file
) {
1933 if (!vis
->replaying
)
1934 text_snapshot(file
->text
);
1937 Text
*vis_text(Vis
*vis
) {
1938 return vis
->win
->file
->text
;
1941 View
*vis_view(Vis
*vis
) {
1942 return vis
->win
->view
;
1945 Win
*vis_window(Vis
*vis
) {
1949 bool vis_get_autoindent(const Vis
*vis
) {
1950 return vis
->autoindent
;