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 const MarkDef vis_marks
[] = {
32 [VIS_MARK_SELECTION_START
] = { '<', VIS_HELP("Last selection start") },
33 [VIS_MARK_SELECTION_END
] = { '>', VIS_HELP("Last selection end") },
36 static void macro_replay(Vis
*vis
, const Macro
*macro
);
37 static void macro_replay_internal(Vis
*vis
, const Macro
*macro
);
38 static void vis_keys_push(Vis
*vis
, const char *input
, size_t pos
, bool record
);
40 bool vis_event_emit(Vis
*vis
, enum VisEvents id
, ...) {
44 if (!vis
->initialized
) {
45 vis
->initialized
= true;
46 vis
->ui
->init(vis
->ui
, vis
);
48 vis
->event
->init(vis
);
59 if (vis
->event
->start
)
60 vis
->event
->start(vis
);
62 case VIS_EVENT_FILE_OPEN
:
63 case VIS_EVENT_FILE_SAVE_PRE
:
64 case VIS_EVENT_FILE_SAVE_POST
:
65 case VIS_EVENT_FILE_CLOSE
:
67 File
*file
= va_arg(ap
, File
*);
70 if (id
== VIS_EVENT_FILE_OPEN
&& vis
->event
->file_open
) {
71 vis
->event
->file_open(vis
, file
);
72 } else if (id
== VIS_EVENT_FILE_SAVE_PRE
&& vis
->event
->file_save_pre
) {
73 const char *path
= va_arg(ap
, const char*);
74 ret
= vis
->event
->file_save_pre(vis
, file
, path
);
75 } else if (id
== VIS_EVENT_FILE_SAVE_POST
&& vis
->event
->file_save_post
) {
76 const char *path
= va_arg(ap
, const char*);
77 vis
->event
->file_save_post(vis
, file
, path
);
78 } else if (id
== VIS_EVENT_FILE_CLOSE
&& vis
->event
->file_close
) {
79 vis
->event
->file_close(vis
, file
);
83 case VIS_EVENT_WIN_OPEN
:
84 case VIS_EVENT_WIN_CLOSE
:
85 case VIS_EVENT_WIN_HIGHLIGHT
:
86 case VIS_EVENT_WIN_STATUS
:
88 Win
*win
= va_arg(ap
, Win
*);
89 if (win
->file
->internal
&& id
!= VIS_EVENT_WIN_STATUS
)
91 if (vis
->event
->win_open
&& id
== VIS_EVENT_WIN_OPEN
) {
92 vis
->event
->win_open(vis
, win
);
93 } else if (vis
->event
->win_close
&& id
== VIS_EVENT_WIN_CLOSE
) {
94 vis
->event
->win_close(vis
, win
);
95 } else if (vis
->event
->win_highlight
&& id
== VIS_EVENT_WIN_HIGHLIGHT
) {
96 vis
->event
->win_highlight(vis
, win
);
97 } else if (vis
->event
->win_status
&& id
== VIS_EVENT_WIN_STATUS
) {
98 vis
->event
->win_status(vis
, win
);
103 if (vis
->event
->quit
)
104 vis
->event
->quit(vis
);
112 /** window / file handling */
114 static void file_free(Vis
*vis
, File
*file
) {
117 if (file
->refcount
> 1) {
121 vis_event_emit(vis
, VIS_EVENT_FILE_CLOSE
, file
);
122 text_free(file
->text
);
123 free((char*)file
->name
);
126 file
->prev
->next
= file
->next
;
128 file
->next
->prev
= file
->prev
;
129 if (vis
->files
== file
)
130 vis
->files
= file
->next
;
134 static File
*file_new_text(Vis
*vis
, Text
*text
) {
135 File
*file
= calloc(1, sizeof(*file
));
140 file
->stat
= text_stat(text
);
142 vis
->files
->prev
= file
;
143 file
->next
= vis
->files
;
148 static char *absolute_path(const char *name
) {
151 char *copy1
= strdup(name
);
152 char *copy2
= strdup(name
);
153 char *path_absolute
= NULL
;
154 char path_normalized
[PATH_MAX
] = "";
156 if (!copy1
|| !copy2
)
159 char *dir
= dirname(copy1
);
160 char *base
= basename(copy2
);
161 if (!(path_absolute
= realpath(dir
, NULL
)))
163 if (strcmp(path_absolute
, "/") == 0)
164 path_absolute
[0] = '\0';
166 snprintf(path_normalized
, sizeof(path_normalized
), "%s/%s",
167 path_absolute
, base
);
172 return path_normalized
[0] ? strdup(path_normalized
) : NULL
;
175 static File
*file_new(Vis
*vis
, const char *name
) {
176 char *name_absolute
= NULL
;
178 if (!(name_absolute
= absolute_path(name
)))
180 File
*existing
= NULL
;
181 /* try to detect whether the same file is already open in another window
182 * TODO: do this based on inodes */
183 for (File
*file
= vis
->files
; file
; file
= file
->next
) {
184 if (file
->name
&& strcmp(file
->name
, name_absolute
) == 0) {
196 Text
*text
= text_load(name
);
197 if (!text
&& name
&& errno
== ENOENT
)
198 text
= text_load(NULL
);
201 if (!(file
= file_new_text(vis
, text
)))
203 file
->name
= name_absolute
;
204 vis_event_emit(vis
, VIS_EVENT_FILE_OPEN
, file
);
209 file_free(vis
, file
);
213 static File
*file_new_internal(Vis
*vis
, const char *filename
) {
214 File
*file
= file_new(vis
, filename
);
217 file
->internal
= true;
222 void file_name_set(File
*file
, const char *name
) {
223 if (name
== file
->name
)
225 free((char*)file
->name
);
226 file
->name
= absolute_path(name
);
229 const char *file_name_get(File
*file
) {
230 /* TODO: calculate path relative to working directory, cache result */
234 if (!getcwd(cwd
, sizeof cwd
))
236 const char *path
= strstr(file
->name
, cwd
);
237 if (path
!= file
->name
)
239 size_t cwdlen
= strlen(cwd
);
240 return file
->name
[cwdlen
] == '/' ? file
->name
+cwdlen
+1 : file
->name
;
243 void vis_window_status(Win
*win
, const char *status
) {
244 win
->ui
->status(win
->ui
, status
);
247 void window_selection_save(Win
*win
) {
249 File
*file
= win
->file
;
250 Filerange sel
= view_selections_get(view_selections(win
->view
));
251 file
->marks
[VIS_MARK_SELECTION_START
] = text_mark_set(file
->text
, sel
.start
);
252 file
->marks
[VIS_MARK_SELECTION_END
] = text_mark_set(file
->text
, sel
.end
);
253 if (!vis
->action
.op
) {
254 for (Selection
*s
= view_selections(win
->view
); s
; s
= view_selections_next(s
))
255 view_selections_save(s
);
259 static void window_free(Win
*win
) {
263 for (Win
*other
= vis
->windows
; other
; other
= other
->next
) {
264 if (other
->parent
== win
)
265 other
->parent
= NULL
;
268 vis
->ui
->window_free(win
->ui
);
269 view_free(win
->view
);
270 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++)
271 map_free(win
->modes
[i
].bindings
);
272 ringbuf_free(win
->jumplist
);
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
) {
298 /* This screen line contains the cell we want to highlight */
299 if (line_cols
>= cc
) {
300 l
->cells
[(cc
- 1) % width
].style
= style
;
306 static void window_draw_cursorline(Win
*win
) {
308 View
*view
= win
->view
;
309 enum UiOption options
= view_options_get(view
);
310 if (!(options
& UI_OPTION_CURSOR_LINE
))
312 if (vis
->mode
->visual
|| vis
->win
!= win
)
314 if (view_selections_count(view
) > 1)
317 int width
= view_width_get(view
);
318 CellStyle style
= win
->ui
->style_get(win
->ui
, UI_STYLE_CURSOR_LINE
);
319 Selection
*sel
= view_selections_primary_get(view
);
320 size_t lineno
= view_cursors_line_get(sel
)->lineno
;
321 for (Line
*l
= view_lines_first(view
); l
; l
= l
->next
) {
322 if (l
->lineno
== lineno
) {
323 for (int x
= 0; x
< width
; x
++) {
324 l
->cells
[x
].style
.attr
|= style
.attr
;
325 l
->cells
[x
].style
.bg
= style
.bg
;
327 } else if (l
->lineno
> lineno
) {
333 static void window_draw_selection(View
*view
, Selection
*cur
, CellStyle
*style
) {
334 Filerange sel
= view_selections_get(cur
);
335 if (!text_range_valid(&sel
))
337 Line
*start_line
; int start_col
;
338 Line
*end_line
; int end_col
;
339 view_coord_get(view
, sel
.start
, &start_line
, NULL
, &start_col
);
340 view_coord_get(view
, sel
.end
, &end_line
, NULL
, &end_col
);
341 if (!start_line
&& !end_line
)
344 start_line
= view_lines_first(view
);
348 end_line
= view_lines_last(view
);
349 end_col
= end_line
->width
;
351 for (Line
*l
= start_line
; l
!= end_line
->next
; l
= l
->next
) {
352 int col
= (l
== start_line
) ? start_col
: 0;
353 int end
= (l
== end_line
) ? end_col
: l
->width
;
355 if (cell_color_equal(l
->cells
[col
].style
.fg
, style
->bg
)) {
356 CellStyle old
= l
->cells
[col
].style
;
357 l
->cells
[col
].style
.fg
= old
.bg
;
358 l
->cells
[col
].style
.bg
= old
.fg
;
360 l
->cells
[col
].style
.bg
= style
->bg
;
367 static void window_draw_cursor_matching(Win
*win
, Selection
*cur
, CellStyle
*style
) {
368 if (win
->vis
->mode
->visual
)
370 Line
*line_match
; int col_match
;
371 size_t pos
= view_cursors_pos(cur
);
372 size_t pos_match
= text_bracket_match_symbol(win
->file
->text
, pos
, "(){}[]\"'`");
373 if (pos
== pos_match
)
375 if (!view_coord_get(win
->view
, pos_match
, &line_match
, NULL
, &col_match
))
377 if (cell_color_equal(line_match
->cells
[col_match
].style
.fg
, style
->fg
)) {
378 CellStyle old
= line_match
->cells
[col_match
].style
;
379 line_match
->cells
[col_match
].style
.fg
= old
.bg
;
380 line_match
->cells
[col_match
].style
.bg
= old
.fg
;
382 line_match
->cells
[col_match
].style
.bg
= style
->bg
;
386 static void window_draw_cursor(Win
*win
, Selection
*cur
, CellStyle
*style
, CellStyle
*sel_style
) {
387 if (win
->vis
->win
!= win
)
389 Line
*line
= view_cursors_line_get(cur
);
390 int col
= view_cursors_cell_get(cur
);
391 if (!line
|| col
== -1)
393 line
->cells
[col
].style
= *style
;
394 window_draw_cursor_matching(win
, cur
, sel_style
);
398 static void window_draw_cursors(Win
*win
) {
399 View
*view
= win
->view
;
400 Filerange viewport
= view_viewport_get(view
);
401 bool multiple_cursors
= view_selections_count(view
) > 1;
402 Selection
*sel
= view_selections_primary_get(view
);
403 CellStyle style_cursor
= win
->ui
->style_get(win
->ui
, UI_STYLE_CURSOR
);
404 CellStyle style_cursor_primary
= win
->ui
->style_get(win
->ui
, UI_STYLE_CURSOR_PRIMARY
);
405 CellStyle style_selection
= win
->ui
->style_get(win
->ui
, UI_STYLE_SELECTION
);
406 for (Selection
*s
= view_selections_prev(sel
); s
; s
= view_selections_prev(s
)) {
407 window_draw_selection(win
->view
, s
, &style_selection
);
408 size_t pos
= view_cursors_pos(s
);
409 if (pos
< viewport
.start
)
411 window_draw_cursor(win
, s
, &style_cursor
, &style_selection
);
413 window_draw_selection(win
->view
, sel
, &style_selection
);
414 window_draw_cursor(win
, sel
, multiple_cursors
? &style_cursor_primary
: &style_cursor
, &style_selection
);
415 for (Selection
*s
= view_selections_next(sel
); s
; s
= view_selections_next(s
)) {
416 window_draw_selection(win
->view
, s
, &style_selection
);
417 size_t pos
= view_cursors_pos(s
);
418 if (pos
> viewport
.end
)
420 window_draw_cursor(win
, s
, &style_cursor
, &style_selection
);
424 static void window_draw_eof(Win
*win
) {
425 View
*view
= win
->view
;
426 if (view_width_get(view
) == 0)
428 CellStyle style
= win
->ui
->style_get(win
->ui
, UI_STYLE_EOF
);
429 for (Line
*l
= view_lines_last(view
)->next
; l
; l
= l
->next
) {
430 strcpy(l
->cells
[0].data
, "~");
431 l
->cells
[0].style
= style
;
435 void vis_window_draw(Win
*win
) {
436 if (!win
->ui
|| !view_update(win
->view
))
439 vis_event_emit(vis
, VIS_EVENT_WIN_HIGHLIGHT
, win
);
441 window_draw_colorcolumn(win
);
442 window_draw_cursorline(win
);
443 window_draw_cursors(win
);
444 window_draw_eof(win
);
446 vis_event_emit(vis
, VIS_EVENT_WIN_STATUS
, win
);
450 void vis_window_invalidate(Win
*win
) {
451 for (Win
*w
= win
->vis
->windows
; w
; w
= w
->next
) {
452 if (w
->file
== win
->file
)
457 Win
*window_new_file(Vis
*vis
, File
*file
, enum UiOption options
) {
458 Win
*win
= calloc(1, sizeof(Win
));
463 win
->jumplist
= ringbuf_alloc(31);
464 win
->view
= view_new(file
->text
);
465 win
->ui
= vis
->ui
->window_new(vis
->ui
, win
, options
);
466 if (!win
->jumplist
|| !win
->view
|| !win
->ui
) {
471 view_tabwidth_set(win
->view
, vis
->tabwidth
);
474 vis
->windows
->prev
= win
;
475 win
->next
= vis
->windows
;
478 vis
->ui
->window_focus(win
->ui
);
479 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++)
480 win
->modes
[i
].parent
= &vis_modes
[i
];
481 vis_event_emit(vis
, VIS_EVENT_WIN_OPEN
, win
);
485 bool vis_window_reload(Win
*win
) {
486 const char *name
= win
->file
->name
;
488 return false; /* can't reload unsaved file */
489 /* temporarily unset file name, otherwise file_new returns the same File */
490 win
->file
->name
= NULL
;
491 File
*file
= file_new(win
->vis
, name
);
492 win
->file
->name
= name
;
495 file_free(win
->vis
, win
->file
);
498 view_reload(win
->view
, file
->text
);
502 bool vis_window_split(Win
*original
) {
503 Win
*win
= window_new_file(original
->vis
, original
->file
, UI_OPTION_STATUSBAR
);
506 for (size_t i
= 0; i
< LENGTH(win
->modes
); i
++) {
507 if (original
->modes
[i
].bindings
)
508 win
->modes
[i
].bindings
= map_new();
509 if (win
->modes
[i
].bindings
)
510 map_copy(win
->modes
[i
].bindings
, original
->modes
[i
].bindings
);
512 win
->file
= original
->file
;
513 view_options_set(win
->view
, view_options_get(original
->view
));
514 view_cursor_to(win
->view
, view_cursor_get(original
->view
));
518 void vis_window_focus(Win
*win
) {
523 vis
->ui
->window_focus(win
->ui
);
526 void vis_window_next(Vis
*vis
) {
530 vis_window_focus(sel
->next
? sel
->next
: vis
->windows
);
533 void vis_window_prev(Vis
*vis
) {
539 for (sel
= vis
->windows
; sel
->next
; sel
= sel
->next
);
540 vis_window_focus(sel
);
543 int vis_window_width_get(const Win
*win
) {
544 return win
->ui
->window_width(win
->ui
);
547 int vis_window_height_get(const Win
*win
) {
548 return win
->ui
->window_height(win
->ui
);
551 void vis_draw(Vis
*vis
) {
552 for (Win
*win
= vis
->windows
; win
; win
= win
->next
)
553 view_draw(win
->view
);
556 void vis_redraw(Vis
*vis
) {
557 vis
->ui
->redraw(vis
->ui
);
561 void vis_update(Vis
*vis
) {
562 vis
->ui
->draw(vis
->ui
);
565 void vis_suspend(Vis
*vis
) {
566 vis
->ui
->suspend(vis
->ui
);
569 void vis_resume(Vis
*vis
) {
570 vis
->ui
->resume(vis
->ui
);
573 bool vis_window_new(Vis
*vis
, const char *filename
) {
574 File
*file
= file_new(vis
, filename
);
577 Win
*win
= window_new_file(vis
, file
, UI_OPTION_STATUSBAR
);
579 file_free(vis
, file
);
586 bool vis_window_new_fd(Vis
*vis
, int fd
) {
589 if (!vis_window_new(vis
, NULL
))
591 vis
->win
->file
->fd
= fd
;
595 bool vis_window_closable(Win
*win
) {
596 if (!win
|| !text_modified(win
->file
->text
))
598 return win
->file
->refcount
> 1;
601 void vis_window_swap(Win
*a
, Win
*b
) {
602 if (a
== b
|| !a
|| !b
)
619 if (vis
->windows
== a
)
621 else if (vis
->windows
== b
)
623 vis
->ui
->window_swap(a
->ui
, b
->ui
);
626 else if (vis
->win
== b
)
630 void vis_window_close(Win
*win
) {
634 vis_event_emit(vis
, VIS_EVENT_WIN_CLOSE
, win
);
635 file_free(vis
, win
->file
);
637 win
->prev
->next
= win
->next
;
639 win
->next
->prev
= win
->prev
;
640 if (vis
->windows
== win
)
641 vis
->windows
= win
->next
;
643 vis
->win
= win
->next
? win
->next
: win
->prev
;
644 if (win
== vis
->message_window
)
645 vis
->message_window
= NULL
;
648 vis
->ui
->window_focus(vis
->win
->ui
);
652 Vis
*vis_new(Ui
*ui
, VisEvent
*event
) {
655 Vis
*vis
= calloc(1, sizeof(Vis
));
658 vis
->exit_status
= -1;
661 vis
->expandtab
= false;
662 vis
->change_colors
= true;
663 for (size_t i
= 0; i
< LENGTH(vis
->registers
); i
++)
664 register_init(&vis
->registers
[i
]);
665 vis
->registers
[VIS_REG_BLACKHOLE
].type
= REGISTER_BLACKHOLE
;
666 vis
->registers
[VIS_REG_CLIPBOARD
].type
= REGISTER_CLIPBOARD
;
667 vis
->registers
[VIS_REG_NUMBER
].type
= REGISTER_NUMBER
;
668 array_init(&vis
->operators
);
669 array_init(&vis
->motions
);
670 array_init(&vis
->textobjects
);
671 array_init(&vis
->bindings
);
672 array_init(&vis
->actions_user
);
673 action_reset(&vis
->action
);
674 buffer_init(&vis
->input_queue
);
675 if (!(vis
->command_file
= file_new_internal(vis
, NULL
)))
677 if (!(vis
->search_file
= file_new_internal(vis
, NULL
)))
679 if (!(vis
->error_file
= file_new_internal(vis
, NULL
)))
681 if (!(vis
->actions
= map_new()))
683 if (!(vis
->keymap
= map_new()))
688 char *shell
= getenv("SHELL");
689 if ((!shell
|| !*shell
) && (pw
= getpwuid(getuid())))
690 shell
= pw
->pw_shell
;
691 if (!shell
|| !*shell
)
693 if (!(vis
->shell
= strdup(shell
)))
695 vis
->mode_prev
= vis
->mode
= &vis_modes
[VIS_MODE_NORMAL
];
698 if (event
->mode_insert_input
)
699 vis_modes
[VIS_MODE_INSERT
].input
= event
->mode_insert_input
;
700 if (event
->mode_replace_input
)
701 vis_modes
[VIS_MODE_REPLACE
].input
= event
->mode_replace_input
;
709 void vis_free(Vis
*vis
) {
712 vis_event_emit(vis
, VIS_EVENT_QUIT
);
715 vis_window_close(vis
->windows
);
716 file_free(vis
, vis
->command_file
);
717 file_free(vis
, vis
->search_file
);
718 file_free(vis
, vis
->error_file
);
719 for (int i
= 0; i
< LENGTH(vis
->registers
); i
++)
720 register_release(&vis
->registers
[i
]);
721 vis
->ui
->free(vis
->ui
);
724 while (map_first(vis
->usercmds
, &name
) && vis_cmd_unregister(vis
, name
));
726 map_free(vis
->usercmds
);
730 while (map_first(vis
->options
, &name
) && vis_option_unregister(vis
, name
));
732 map_free(vis
->options
);
733 map_free(vis
->actions
);
734 map_free(vis
->keymap
);
735 buffer_release(&vis
->input_queue
);
736 for (int i
= 0; i
< VIS_MODE_INVALID
; i
++)
737 map_free(vis_modes
[i
].bindings
);
738 array_release_full(&vis
->operators
);
739 array_release_full(&vis
->motions
);
740 array_release_full(&vis
->textobjects
);
741 while (array_length(&vis
->bindings
))
742 vis_binding_free(vis
, array_get_ptr(&vis
->bindings
, 0));
743 array_release(&vis
->bindings
);
744 while (array_length(&vis
->actions_user
))
745 vis_action_free(vis
, array_get_ptr(&vis
->actions_user
, 0));
746 array_release(&vis
->actions_user
);
751 void vis_insert(Vis
*vis
, size_t pos
, const char *data
, size_t len
) {
752 text_insert(vis
->win
->file
->text
, pos
, data
, len
);
753 vis_window_invalidate(vis
->win
);
756 void vis_insert_key(Vis
*vis
, const char *data
, size_t len
) {
757 for (Selection
*s
= view_selections(vis
->win
->view
); s
; s
= view_selections_next(s
)) {
758 size_t pos
= view_cursors_pos(s
);
759 vis_insert(vis
, pos
, data
, len
);
760 view_cursors_scroll_to(s
, pos
+ len
);
764 void vis_replace(Vis
*vis
, size_t pos
, const char *data
, size_t len
) {
765 Text
*txt
= vis
->win
->file
->text
;
766 Iterator it
= text_iterator_get(txt
, pos
);
767 int chars
= text_char_count(data
, len
);
768 for (char c
; chars
-- > 0 && text_iterator_byte_get(&it
, &c
) && c
!= '\n'; )
769 text_iterator_char_next(&it
, NULL
);
771 text_delete(txt
, pos
, it
.pos
- pos
);
772 vis_insert(vis
, pos
, data
, len
);
775 void vis_replace_key(Vis
*vis
, const char *data
, size_t len
) {
776 for (Selection
*s
= view_selections(vis
->win
->view
); s
; s
= view_selections_next(s
)) {
777 size_t pos
= view_cursors_pos(s
);
778 vis_replace(vis
, pos
, data
, len
);
779 view_cursors_scroll_to(s
, pos
+ len
);
783 void vis_delete(Vis
*vis
, size_t pos
, size_t len
) {
784 text_delete(vis
->win
->file
->text
, pos
, len
);
785 vis_window_invalidate(vis
->win
);
788 bool vis_action_register(Vis
*vis
, const KeyAction
*action
) {
789 return map_put(vis
->actions
, action
->name
, action
);
792 bool vis_keymap_add(Vis
*vis
, const char *key
, const char *mapping
) {
793 return map_put(vis
->keymap
, key
, mapping
);
796 void vis_keymap_disable(Vis
*vis
) {
797 vis
->keymap_disabled
= true;
800 static void window_jumplist_add(Win
*win
, size_t pos
) {
801 Mark mark
= text_mark_set(win
->file
->text
, pos
);
802 if (mark
&& win
->jumplist
)
803 ringbuf_add(win
->jumplist
, (void*)mark
);
806 static void window_jumplist_invalidate(Win
*win
) {
808 ringbuf_invalidate(win
->jumplist
);
811 void vis_interrupt(Vis
*vis
) {
812 vis
->interrupted
= true;
815 bool vis_interrupt_requested(Vis
*vis
) {
816 return vis
->interrupted
;
819 void vis_do(Vis
*vis
) {
821 File
*file
= win
->file
;
822 Text
*txt
= file
->text
;
823 View
*view
= win
->view
;
824 Action
*a
= &vis
->action
;
826 if (a
->op
== &vis_operators
[VIS_OP_FILTER
] && !vis
->mode
->visual
)
827 vis_mode_switch(vis
, VIS_MODE_VISUAL_LINE
);
829 int count
= MAX(a
->count
, 1);
830 if (a
->op
== &vis_operators
[VIS_OP_MODESWITCH
])
831 count
= 1; /* count should apply to inserted text not motion */
832 bool repeatable
= a
->op
&& !vis
->macro_operator
&& !vis
->win
->parent
;
833 bool multiple_cursors
= view_selections_count(view
) > 1;
834 bool linewise
= !(a
->type
& CHARWISE
) && (
835 a
->type
& LINEWISE
|| (a
->movement
&& a
->movement
->type
& LINEWISE
) ||
836 vis
->mode
== &vis_modes
[VIS_MODE_VISUAL_LINE
]);
839 Register
*reg
= a
->reg
;
840 size_t reg_slot
= multiple_cursors
? EPOS
: 0;
841 size_t last_reg_slot
= reg_slot
;
843 reg
= &vis
->registers
[file
->internal
? VIS_REG_PROMPT
: VIS_REG_DEFAULT
];
844 if (a
->op
== &vis_operators
[VIS_OP_PUT_AFTER
] && multiple_cursors
&& vis_register_count(vis
, reg
) == 1)
847 for (Selection
*sel
= view_selections(view
), *next
; sel
; sel
= next
) {
848 if (vis
->interrupted
)
851 next
= view_selections_next(sel
);
853 size_t pos
= view_cursors_pos(sel
);
855 if (!view_selections_dispose(sel
))
856 view_cursors_to(sel
, 0);
860 OperatorContext c
= {
864 .range
= text_range_empty(),
866 .reg_slot
= reg_slot
== EPOS
? (size_t)view_selections_number(sel
) : reg_slot
,
867 .linewise
= linewise
,
869 .context
= a
->op
? a
->op
->context
: NULL
,
872 last_reg_slot
= c
.reg_slot
;
877 for (int i
= 0; i
< count
; i
++) {
878 size_t pos_prev
= pos
;
879 if (a
->movement
->txt
)
880 pos
= a
->movement
->txt(txt
, pos
);
881 else if (a
->movement
->cur
)
882 pos
= a
->movement
->cur(sel
);
883 else if (a
->movement
->file
)
884 pos
= a
->movement
->file(vis
, file
, pos
);
885 else if (a
->movement
->vis
)
886 pos
= a
->movement
->vis(vis
, txt
, pos
);
887 else if (a
->movement
->view
)
888 pos
= a
->movement
->view(vis
, view
);
889 else if (a
->movement
->win
)
890 pos
= a
->movement
->win(vis
, win
, pos
);
891 else if (a
->movement
->user
)
892 pos
= a
->movement
->user(vis
, win
, a
->movement
->data
, pos
);
893 if (pos
== EPOS
|| a
->movement
->type
& IDEMPOTENT
|| pos
== pos_prev
) {
894 err
= a
->movement
->type
& COUNT_EXACT
;
905 c
.range
.start
= start
;
909 c
.range
= text_range_new(start
, pos
);
914 if (a
->movement
->type
& CHARWISE
)
915 view_cursors_scroll_to(sel
, pos
);
917 view_cursors_to(sel
, pos
);
918 if (vis
->mode
->visual
)
919 c
.range
= view_selections_get(sel
);
920 if (a
->movement
->type
& JUMP
)
921 window_jumplist_add(win
, pos
);
923 window_jumplist_invalidate(win
);
924 } else if (a
->movement
->type
& INCLUSIVE
&& c
.range
.end
> start
) {
925 c
.range
.end
= text_char_next(txt
, c
.range
.end
);
926 } else if (linewise
&& (a
->movement
->type
& LINEWISE_INCLUSIVE
)) {
927 c
.range
.end
= text_char_next(txt
, c
.range
.end
);
929 } else if (a
->textobj
) {
930 if (vis
->mode
->visual
)
931 c
.range
= view_selections_get(sel
);
933 c
.range
.start
= c
.range
.end
= pos
;
934 for (int i
= 0; i
< count
; i
++) {
935 Filerange r
= text_range_empty();
937 r
= a
->textobj
->txt(txt
, pos
);
938 else if (a
->textobj
->vis
)
939 r
= a
->textobj
->vis(vis
, txt
, pos
);
940 else if (a
->textobj
->user
)
941 r
= a
->textobj
->user(vis
, win
, a
->textobj
->data
, pos
);
942 if (!text_range_valid(&r
))
944 if (a
->textobj
->type
& TEXTOBJECT_DELIMITED_OUTER
) {
949 if (vis
->mode
->visual
|| (i
> 0 && !(a
->textobj
->type
& TEXTOBJECT_NON_CONTIGUOUS
)))
950 c
.range
= text_range_union(&c
.range
, &r
);
955 if (a
->textobj
->type
& TEXTOBJECT_EXTEND_BACKWARD
) {
957 if ((a
->textobj
->type
& TEXTOBJECT_DELIMITED_INNER
) && pos
> 0)
961 if (a
->textobj
->type
& TEXTOBJECT_DELIMITED_INNER
)
966 } else if (vis
->mode
->visual
) {
967 c
.range
= view_selections_get(sel
);
968 if (!text_range_valid(&c
.range
))
969 c
.range
.start
= c
.range
.end
= pos
;
972 if (linewise
&& vis
->mode
!= &vis_modes
[VIS_MODE_VISUAL
])
973 c
.range
= text_range_linewise(txt
, &c
.range
);
974 if (vis
->mode
->visual
) {
975 view_selections_set(sel
, &c
.range
);
976 view_selections_anchor(sel
);
980 size_t pos
= a
->op
->func(vis
, txt
, &c
);
982 view_selections_dispose(sel
);
983 } else if (pos
<= text_size(txt
)) {
984 if (vis
->mode
->visual
)
985 view_selections_save(sel
);
986 view_cursors_to(sel
, pos
);
987 if (vis
->mode
->visual
)
988 view_selection_clear(sel
);
993 view_selections_normalize(view
);
997 if (a
->op
== &vis_operators
[VIS_OP_YANK
] ||
998 a
->op
== &vis_operators
[VIS_OP_DELETE
] ||
999 a
->op
== &vis_operators
[VIS_OP_CHANGE
] ||
1000 a
->op
== &vis_operators
[VIS_OP_REPLACE
]) {
1001 register_resize(reg
, last_reg_slot
+1);
1004 /* we do not support visual repeat, still do something resonable */
1005 if (vis
->mode
->visual
&& !a
->movement
&& !a
->textobj
)
1006 a
->movement
= &vis_motions
[VIS_MOVE_NOP
];
1008 /* operator implementations must not change the mode,
1009 * they might get called multiple times (once for every cursor)
1011 if (a
->op
== &vis_operators
[VIS_OP_CHANGE
]) {
1012 vis_mode_switch(vis
, VIS_MODE_INSERT
);
1013 } else if (a
->op
== &vis_operators
[VIS_OP_MODESWITCH
]) {
1014 vis_mode_switch(vis
, a
->mode
);
1015 } else if (a
->op
== &vis_operators
[VIS_OP_FILTER
]) {
1017 vis_cmd(vis
, a
->arg
.s
);
1019 vis_prompt_show(vis
, ":|");
1020 } else if (vis
->mode
== &vis_modes
[VIS_MODE_OPERATOR_PENDING
]) {
1021 mode_set(vis
, vis
->mode_prev
);
1022 } else if (vis
->mode
->visual
) {
1023 vis_mode_switch(vis
, VIS_MODE_NORMAL
);
1026 if (vis
->mode
== &vis_modes
[VIS_MODE_NORMAL
])
1027 vis_file_snapshot(vis
, file
);
1031 if (a
!= &vis
->action_prev
) {
1034 a
->macro
= vis
->macro_operator
;
1035 vis
->action_prev
= *a
;
1041 void action_reset(Action
*a
) {
1042 memset(a
, 0, sizeof(*a
));
1043 a
->count
= VIS_COUNT_UNKNOWN
;
1046 void vis_cancel(Vis
*vis
) {
1047 action_reset(&vis
->action
);
1050 void vis_die(Vis
*vis
, const char *msg
, ...) {
1053 vis
->ui
->die(vis
->ui
, msg
, ap
);
1057 const char *vis_keys_next(Vis
*vis
, const char *keys
) {
1058 if (!keys
|| !*keys
)
1061 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1062 const char *next
= NULL
;
1063 /* first try to parse a special key of the form <Key> */
1064 if (*keys
== '<' && keys
[1] && (next
= termkey_strpkey(termkey
, keys
+1, &key
, TERMKEY_FORMAT_VIM
)) && *next
== '>')
1066 if (strncmp(keys
, "<vis-", 5) == 0) {
1067 const char *start
= keys
+ 1, *end
= start
;
1068 while (*end
&& *end
!= '>')
1070 if (end
> start
&& end
- start
- 1 < VIS_KEY_LENGTH_MAX
&& *end
== '>') {
1071 char key
[VIS_KEY_LENGTH_MAX
];
1072 memcpy(key
, start
, end
- start
);
1073 key
[end
- start
] = '\0';
1074 if (map_get(vis
->actions
, key
))
1080 while (!ISUTF8(*keys
))
1085 long vis_keys_codepoint(Vis
*vis
, const char *keys
) {
1086 long codepoint
= -1;
1089 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1093 if (keys
[0] == '<' && !keys
[1])
1096 if (keys
[0] == '<' && (next
= termkey_strpkey(termkey
, keys
+1, &key
, TERMKEY_FORMAT_VIM
)) && *next
== '>')
1097 codepoint
= (key
.type
== TERMKEY_TYPE_UNICODE
) ? key
.code
.codepoint
: -1;
1098 else if ((next
= termkey_strpkey(termkey
, keys
, &key
, TERMKEY_FORMAT_VIM
)))
1099 codepoint
= (key
.type
== TERMKEY_TYPE_UNICODE
) ? key
.code
.codepoint
: -1;
1101 if (codepoint
!= -1) {
1102 if (key
.modifiers
== TERMKEY_KEYMOD_CTRL
)
1107 if (!next
|| key
.type
!= TERMKEY_TYPE_KEYSYM
)
1110 const int keysym
[] = {
1111 TERMKEY_SYM_ENTER
, '\n',
1112 TERMKEY_SYM_TAB
, '\t',
1113 TERMKEY_SYM_BACKSPACE
, '\b',
1114 TERMKEY_SYM_ESCAPE
, 0x1b,
1115 TERMKEY_SYM_DELETE
, 0x7f,
1119 for (const int *k
= keysym
; k
[0]; k
+= 2) {
1120 if (key
.code
.sym
== k
[0])
1127 bool vis_keys_utf8(Vis
*vis
, const char *keys
, char utf8
[static UTFmax
+1]) {
1128 Rune rune
= vis_keys_codepoint(vis
, keys
);
1129 if (rune
== (Rune
)-1)
1131 size_t len
= runetochar(utf8
, &rune
);
1136 static void vis_keys_process(Vis
*vis
, size_t pos
) {
1137 Buffer
*buf
= &vis
->input_queue
;
1138 char *keys
= buf
->data
+ pos
, *start
= keys
, *cur
= keys
, *end
= keys
, *binding_end
= keys
;;
1139 bool prefix
= false;
1140 KeyBinding
*binding
= NULL
;
1142 while (cur
&& *cur
) {
1144 if (!(end
= (char*)vis_keys_next(vis
, cur
))) {
1145 buffer_remove(buf
, keys
- buf
->data
, strlen(keys
));
1153 for (Mode
*global_mode
= vis
->mode
; global_mode
&& !prefix
; global_mode
= global_mode
->parent
) {
1154 for (int global
= 0; global
< 2 && !prefix
; global
++) {
1155 Mode
*mode
= (global
|| !vis
->win
) ?
1157 &vis
->win
->modes
[global_mode
->id
];
1158 if (!mode
->bindings
)
1160 /* keep track of longest matching binding */
1161 KeyBinding
*match
= map_get(mode
->bindings
, start
);
1162 if (match
&& end
> binding_end
) {
1166 /* "<" is never treated as a prefix because it
1167 * is used to denote special key symbols */
1168 if (strcmp(start
, "<")) {
1169 prefix
= (!match
&& map_contains(mode
->bindings
, start
)) ||
1170 (match
&& !map_leaf(mode
->bindings
, start
));
1178 /* input sofar is ambigious, wait for more */
1181 } else if (binding
) { /* exact match */
1182 if (binding
->action
) {
1183 size_t len
= binding_end
- start
;
1184 strcpy(vis
->key_prev
, vis
->key_current
);
1185 strncpy(vis
->key_current
, start
, len
);
1186 vis
->key_current
[len
] = '\0';
1187 end
= (char*)binding
->action
->func(vis
, binding_end
, &binding
->action
->arg
);
1193 } else if (binding
->alias
) {
1194 buffer_remove(buf
, start
- buf
->data
, binding_end
- start
);
1195 buffer_insert0(buf
, start
- buf
->data
, binding
->alias
);
1199 binding_end
= start
;
1200 } else { /* no keybinding */
1201 KeyAction
*action
= NULL
;
1202 if (start
[0] == '<' && end
[-1] == '>') {
1203 /* test for special editor key command */
1206 action
= map_get(vis
->actions
, start
+1);
1209 size_t len
= end
- start
;
1210 strcpy(vis
->key_prev
, vis
->key_current
);
1211 strncpy(vis
->key_current
, start
, len
);
1212 vis
->key_current
[len
] = '\0';
1213 end
= (char*)action
->func(vis
, end
, &action
->arg
);
1220 if (!action
&& vis
->mode
->input
) {
1221 end
= (char*)vis_keys_next(vis
, start
);
1222 vis
->mode
->input(vis
, start
, end
- start
);
1228 buffer_remove(buf
, keys
- buf
->data
, end
- keys
);
1231 void vis_keys_feed(Vis
*vis
, const char *input
) {
1236 if (!macro_append(¯o
, input
))
1238 /* use internal function, to keep Lua based tests which use undo points working */
1239 macro_replay_internal(vis
, ¯o
);
1240 macro_release(¯o
);
1243 static void vis_keys_push(Vis
*vis
, const char *input
, size_t pos
, bool record
) {
1246 if (record
&& vis
->recording
)
1247 macro_append(vis
->recording
, input
);
1248 if (vis
->macro_operator
)
1249 macro_append(vis
->macro_operator
, input
);
1250 if (buffer_append0(&vis
->input_queue
, input
))
1251 vis_keys_process(vis
, pos
);
1254 static const char *getkey(Vis
*vis
) {
1255 TermKeyKey key
= { 0 };
1256 if (!vis
->ui
->getkey(vis
->ui
, &key
))
1259 bool use_keymap
= vis
->mode
->id
!= VIS_MODE_INSERT
&&
1260 vis
->mode
->id
!= VIS_MODE_REPLACE
&&
1261 !vis
->keymap_disabled
;
1262 vis
->keymap_disabled
= false;
1263 if (key
.type
== TERMKEY_TYPE_UNICODE
&& use_keymap
) {
1264 const char *mapped
= map_get(vis
->keymap
, key
.utf8
);
1266 size_t len
= strlen(mapped
)+1;
1267 if (len
<= sizeof(key
.utf8
))
1268 memcpy(key
.utf8
, mapped
, len
);
1272 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1273 termkey_strfkey(termkey
, vis
->key
, sizeof(vis
->key
), &key
, TERMKEY_FORMAT_VIM
);
1277 bool vis_signal_handler(Vis
*vis
, int signum
, const siginfo_t
*siginfo
, const void *context
) {
1280 for (File
*file
= vis
->files
; file
; file
= file
->next
) {
1281 if (text_mmaped(file
->text
, siginfo
->si_addr
))
1282 file
->truncated
= true;
1286 siglongjmp(vis
->sigbus_jmpbuf
, 1);
1289 vis
->interrupted
= true;
1295 vis
->need_resize
= true;
1299 vis
->terminate
= true;
1305 int vis_run(Vis
*vis
) {
1307 return EXIT_SUCCESS
;
1308 if (vis
->exit_status
!= -1)
1309 return vis
->exit_status
;
1310 vis
->running
= true;
1312 vis_event_emit(vis
, VIS_EVENT_START
);
1314 struct timespec idle
= { .tv_nsec
= 0 }, *timeout
= NULL
;
1317 sigemptyset(&emptyset
);
1319 vis
->exit_status
= EXIT_SUCCESS
;
1321 sigsetjmp(vis
->sigbus_jmpbuf
, 1);
1323 while (vis
->running
) {
1326 FD_SET(STDIN_FILENO
, &fds
);
1330 for (Win
*next
, *win
= vis
->windows
; win
; win
= next
) {
1332 if (win
->file
->truncated
) {
1334 name
= strdup(win
->file
->name
);
1335 vis_window_close(win
);
1339 vis_die(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
1341 vis_info_show(vis
, "WARNING: file `%s' truncated!\n", name
? name
: "-");
1342 vis
->sigbus
= false;
1347 vis_die(vis
, "Killed by SIGTERM\n");
1348 if (vis
->interrupted
) {
1349 vis
->interrupted
= false;
1350 vis_keys_feed(vis
, "<C-c>");
1355 vis
->resume
= false;
1358 if (vis
->need_resize
) {
1359 vis
->ui
->resize(vis
->ui
);
1360 vis
->need_resize
= false;
1364 idle
.tv_sec
= vis
->mode
->idle_timeout
;
1365 int r
= pselect(1, &fds
, NULL
, NULL
, timeout
, &emptyset
);
1366 if (r
== -1 && errno
== EINTR
)
1370 /* TODO save all pending changes to a ~suffixed file */
1371 vis_die(vis
, "Error in mainloop: %s\n", strerror(errno
));
1374 if (!FD_ISSET(STDIN_FILENO
, &fds
)) {
1375 if (vis
->mode
->idle
)
1376 vis
->mode
->idle(vis
);
1381 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
1382 termkey_advisereadable(termkey
);
1385 while ((key
= getkey(vis
)))
1386 vis_keys_push(vis
, key
, 0, true);
1388 if (vis
->mode
->idle
)
1391 return vis
->exit_status
;
1394 Macro
*macro_get(Vis
*vis
, enum VisRegister id
) {
1395 if (id
== VIS_MACRO_LAST_RECORDED
)
1396 return vis
->last_recording
;
1397 if (VIS_REG_A
<= id
&& id
<= VIS_REG_Z
)
1399 if (id
< LENGTH(vis
->registers
))
1400 return array_get(&vis
->registers
[id
].values
, 0);
1404 void macro_operator_record(Vis
*vis
) {
1405 if (vis
->macro_operator
)
1407 vis
->macro_operator
= macro_get(vis
, VIS_MACRO_OPERATOR
);
1408 macro_reset(vis
->macro_operator
);
1411 void macro_operator_stop(Vis
*vis
) {
1412 if (!vis
->macro_operator
)
1414 Macro
*dot
= macro_get(vis
, VIS_REG_DOT
);
1415 buffer_put(dot
, vis
->macro_operator
->data
, vis
->macro_operator
->len
);
1416 vis
->action_prev
.macro
= dot
;
1417 vis
->macro_operator
= NULL
;
1420 bool vis_macro_record(Vis
*vis
, enum VisRegister id
) {
1421 Macro
*macro
= macro_get(vis
, id
);
1422 if (vis
->recording
|| !macro
)
1424 if (!(VIS_REG_A
<= id
&& id
<= VIS_REG_Z
))
1426 vis
->recording
= macro
;
1427 vis_event_emit(vis
, VIS_EVENT_WIN_STATUS
, vis
->win
);
1431 bool vis_macro_record_stop(Vis
*vis
) {
1432 if (!vis
->recording
)
1434 /* XXX: hack to remove last recorded key, otherwise upon replay
1435 * we would start another recording */
1436 if (vis
->recording
->len
> 1) {
1437 vis
->recording
->len
--;
1438 vis
->recording
->data
[vis
->recording
->len
-1] = '\0';
1440 vis
->last_recording
= vis
->recording
;
1441 vis
->recording
= NULL
;
1442 vis_event_emit(vis
, VIS_EVENT_WIN_STATUS
, vis
->win
);
1446 bool vis_macro_recording(Vis
*vis
) {
1447 return vis
->recording
;
1450 static void macro_replay(Vis
*vis
, const Macro
*macro
) {
1451 const Macro
*replaying
= vis
->replaying
;
1452 vis
->replaying
= macro
;
1453 macro_replay_internal(vis
, macro
);
1454 vis
->replaying
= replaying
;
1457 static void macro_replay_internal(Vis
*vis
, const Macro
*macro
) {
1458 size_t pos
= buffer_length0(&vis
->input_queue
);
1459 for (char *key
= macro
->data
, *next
; key
; key
= next
) {
1461 next
= (char*)vis_keys_next(vis
, key
);
1467 vis_keys_push(vis
, key
, pos
, false);
1474 bool vis_macro_replay(Vis
*vis
, enum VisRegister id
) {
1475 if (id
== VIS_REG_SEARCH
)
1476 return vis_motion(vis
, VIS_MOVE_SEARCH_REPEAT_FORWARD
);
1477 if (id
== VIS_REG_COMMAND
) {
1478 const char *cmd
= register_get(vis
, &vis
->registers
[id
], NULL
);
1479 return vis_cmd(vis
, cmd
);
1482 Macro
*macro
= macro_get(vis
, id
);
1483 if (!macro
|| macro
== vis
->recording
)
1485 int count
= vis_count_get_default(vis
, 1);
1487 for (int i
= 0; i
< count
; i
++)
1488 macro_replay(vis
, macro
);
1489 vis_file_snapshot(vis
, vis
->win
->file
);
1493 void vis_repeat(Vis
*vis
) {
1494 const Macro
*macro
= vis
->action_prev
.macro
;
1495 int count
= vis
->action
.count
;
1496 if (count
!= VIS_COUNT_UNKNOWN
)
1497 vis
->action_prev
.count
= count
;
1499 count
= vis
->action_prev
.count
;
1500 vis
->action
= vis
->action_prev
;
1503 Mode
*mode
= vis
->mode
;
1504 Action action_prev
= vis
->action_prev
;
1506 action_prev
.op
== &vis_operators
[VIS_OP_CHANGE
] ||
1507 action_prev
.op
== &vis_operators
[VIS_OP_FILTER
])
1509 if (vis
->action_prev
.op
== &vis_operators
[VIS_OP_MODESWITCH
])
1510 vis
->action_prev
.count
= 1;
1511 for (int i
= 0; i
< count
; i
++) {
1512 if (vis
->interrupted
)
1514 mode_set(vis
, mode
);
1515 macro_replay(vis
, macro
);
1517 vis
->action_prev
= action_prev
;
1520 vis_file_snapshot(vis
, vis
->win
->file
);
1523 enum VisMark
vis_mark_from(Vis
*vis
, char mark
) {
1524 if (mark
>= 'a' && mark
<= 'z')
1525 return VIS_MARK_a
+ mark
- 'a';
1526 for (size_t i
= 0; i
< LENGTH(vis_marks
); i
++) {
1527 if (vis_marks
[i
].name
== mark
)
1530 return VIS_MARK_INVALID
;
1533 void vis_mark_set(Vis
*vis
, enum VisMark mark
, size_t pos
) {
1534 File
*file
= vis
->win
->file
;
1535 if (mark
< LENGTH(file
->marks
))
1536 file
->marks
[mark
] = text_mark_set(file
->text
, pos
);
1539 int vis_count_get(Vis
*vis
) {
1540 return vis
->action
.count
;
1543 int vis_count_get_default(Vis
*vis
, int def
) {
1544 if (vis
->action
.count
== VIS_COUNT_UNKNOWN
)
1546 return vis
->action
.count
;
1549 void vis_count_set(Vis
*vis
, int count
) {
1550 vis
->action
.count
= (count
>= 0 ? count
: VIS_COUNT_UNKNOWN
);
1553 VisCountIterator
vis_count_iterator_get(Vis
*vis
, int def
) {
1554 return (VisCountIterator
) {
1557 .count
= vis_count_get_default(vis
, def
),
1561 VisCountIterator
vis_count_iterator_init(Vis
*vis
, int count
) {
1562 return (VisCountIterator
) {
1569 bool vis_count_iterator_next(VisCountIterator
*it
) {
1570 if (it
->vis
->interrupted
)
1572 return it
->iteration
++ < it
->count
;
1575 void vis_exit(Vis
*vis
, int status
) {
1576 vis
->running
= false;
1577 vis
->exit_status
= status
;
1580 void vis_insert_tab(Vis
*vis
) {
1581 if (!vis
->expandtab
) {
1582 vis_insert_key(vis
, "\t", 1);
1586 int tabwidth
= MIN(vis
->tabwidth
, LENGTH(spaces
) - 1);
1587 for (Selection
*s
= view_selections(vis
->win
->view
); s
; s
= view_selections_next(s
)) {
1588 size_t pos
= view_cursors_pos(s
);
1589 int width
= text_line_width_get(vis
->win
->file
->text
, pos
);
1590 int count
= tabwidth
- (width
% tabwidth
);
1591 for (int i
= 0; i
< count
; i
++)
1593 spaces
[count
] = '\0';
1594 vis_insert(vis
, pos
, spaces
, count
);
1595 view_cursors_scroll_to(s
, pos
+ count
);
1599 size_t vis_text_insert_nl(Vis
*vis
, Text
*txt
, size_t pos
) {
1600 size_t indent_len
= 0;
1601 char byte
, *indent
= NULL
;
1602 /* insert second newline at end of file, except if there is already one */
1603 bool eof
= pos
== text_size(txt
);
1604 bool nl2
= eof
&& !(pos
> 0 && text_byte_get(txt
, pos
-1, &byte
) && byte
== '\n');
1606 if (vis
->autoindent
) {
1607 /* copy leading white space of current line */
1608 size_t begin
= text_line_begin(txt
, pos
);
1609 size_t start
= text_line_start(txt
, begin
);
1610 size_t end
= text_line_end(txt
, start
);
1613 indent_len
= start
>= begin
? start
-begin
: 0;
1617 indent
= malloc(indent_len
+1);
1619 indent_len
= text_bytes_get(txt
, begin
, indent_len
, indent
);
1623 text_insert(txt
, pos
, "\n", 1);
1626 text_insert(txt
, text_size(txt
), "\n", 1);
1628 pos
--; /* place cursor before, not after nl */
1633 text_insert(txt
, pos
, indent
, indent_len
);
1635 return pos
+ indent_len
;
1638 void vis_insert_nl(Vis
*vis
) {
1639 Win
*win
= vis
->win
;
1640 View
*view
= win
->view
;
1641 Text
*txt
= win
->file
->text
;
1642 for (Selection
*s
= view_selections(view
); s
; s
= view_selections_next(s
)) {
1643 size_t pos
= view_cursors_pos(s
);
1644 size_t newpos
= vis_text_insert_nl(vis
, txt
, pos
);
1645 /* This is a bit of a hack to fix cursor positioning when
1646 * inserting a new line at the start of the view port.
1647 * It has the effect of reseting the mark used by the view
1648 * code to keep track of the start of the visible region.
1650 view_cursors_to(s
, pos
);
1651 view_cursors_to(s
, newpos
);
1653 vis_window_invalidate(win
);
1656 Regex
*vis_regex(Vis
*vis
, const char *pattern
) {
1657 if (!pattern
&& !(pattern
= register_get(vis
, &vis
->registers
[VIS_REG_SEARCH
], NULL
)))
1659 Regex
*regex
= text_regex_new();
1662 if (text_regex_compile(regex
, pattern
, REG_EXTENDED
|REG_NEWLINE
) != 0) {
1663 text_regex_free(regex
);
1666 register_put0(vis
, &vis
->registers
[VIS_REG_SEARCH
], pattern
);
1670 int vis_pipe(Vis
*vis
, File
*file
, Filerange
*range
, const char *argv
[],
1671 void *stdout_context
, ssize_t (*read_stdout
)(void *stdout_context
, char *data
, size_t len
),
1672 void *stderr_context
, ssize_t (*read_stderr
)(void *stderr_context
, char *data
, size_t len
)) {
1674 /* if an invalid range was given, stdin (i.e. key board input) is passed
1675 * through the external command. */
1676 Text
*text
= file
->text
;
1677 int pin
[2], pout
[2], perr
[2], status
= -1;
1678 bool interactive
= !text_range_valid(range
);
1679 Filerange rout
= interactive
? text_range_new(0, 0) : *range
;
1681 if (pipe(pin
) == -1)
1683 if (pipe(pout
) == -1) {
1689 if (pipe(perr
) == -1) {
1697 vis
->ui
->terminal_save(vis
->ui
);
1707 vis_info_show(vis
, "fork failure: %s", strerror(errno
));
1709 } else if (pid
== 0) { /* child i.e filter */
1710 sigset_t sigterm_mask
;
1711 sigemptyset(&sigterm_mask
);
1712 sigaddset(&sigterm_mask
, SIGTERM
);
1713 if (sigprocmask(SIG_UNBLOCK
, &sigterm_mask
, NULL
) == -1) {
1714 fprintf(stderr
, "failed to reset signal mask");
1718 int null
= open("/dev/null", O_RDWR
);
1720 fprintf(stderr
, "failed to open /dev/null");
1725 /* If we have nothing to write, let stdin point to
1726 * /dev/null instead of a pipe which is immediately
1727 * closed. Some programs behave differently when used
1730 if (text_range_size(range
) == 0)
1731 dup2(null
, STDIN_FILENO
);
1733 dup2(pin
[0], STDIN_FILENO
);
1739 dup2(STDERR_FILENO
, STDOUT_FILENO
);
1740 /* For some reason the first byte written by the
1741 * interactive application is not being displayed.
1742 * It probably has something to do with the terminal
1743 * state change. By writing a dummy byte ourself we
1744 * ensure that the complete output is visible.
1746 while(write(STDOUT_FILENO
, " ", 1) == -1 && errno
== EINTR
);
1747 } else if (read_stdout
) {
1748 dup2(pout
[1], STDOUT_FILENO
);
1750 dup2(null
, STDOUT_FILENO
);
1756 dup2(perr
[1], STDERR_FILENO
);
1758 dup2(null
, STDERR_FILENO
);
1765 char *name
= strrchr(file
->name
, '/');
1766 setenv("vis_filepath", file
->name
, 1);
1767 setenv("vis_filename", name
? name
+1 : file
->name
, 1);
1771 execlp(vis
->shell
, vis
->shell
, "-c", argv
[0], (char*)NULL
);
1773 execvp(argv
[0], (char* const*)argv
);
1774 fprintf(stderr
, "exec failure: %s", strerror(errno
));
1778 vis
->interrupted
= false;
1784 if (fcntl(pout
[0], F_SETFL
, O_NONBLOCK
) == -1 ||
1785 fcntl(perr
[0], F_SETFL
, O_NONBLOCK
) == -1)
1791 if (vis
->interrupted
) {
1799 FD_SET(pin
[1], &wfds
);
1801 FD_SET(pout
[0], &rfds
);
1803 FD_SET(perr
[0], &rfds
);
1805 if (select(FD_SETSIZE
, &rfds
, &wfds
, NULL
, NULL
) == -1) {
1808 vis_info_show(vis
, "Select failure");
1812 if (pin
[1] != -1 && FD_ISSET(pin
[1], &wfds
)) {
1813 Filerange junk
= rout
;
1814 if (junk
.end
> junk
.start
+ PIPE_BUF
)
1815 junk
.end
= junk
.start
+ PIPE_BUF
;
1816 ssize_t len
= text_write_range(text
, &junk
, pin
[1]);
1819 if (text_range_size(&rout
) == 0) {
1827 vis_info_show(vis
, "Error writing to external command");
1831 if (pout
[0] != -1 && FD_ISSET(pout
[0], &rfds
)) {
1833 ssize_t len
= read(pout
[0], buf
, sizeof buf
);
1836 (*read_stdout
)(stdout_context
, buf
, len
);
1837 } else if (len
== 0) {
1840 } else if (errno
!= EINTR
&& errno
!= EWOULDBLOCK
) {
1841 vis_info_show(vis
, "Error reading from filter stdout");
1847 if (perr
[0] != -1 && FD_ISSET(perr
[0], &rfds
)) {
1849 ssize_t len
= read(perr
[0], buf
, sizeof buf
);
1852 (*read_stderr
)(stderr_context
, buf
, len
);
1853 } else if (len
== 0) {
1856 } else if (errno
!= EINTR
&& errno
!= EWOULDBLOCK
) {
1857 vis_info_show(vis
, "Error reading from filter stderr");
1863 } while (pin
[1] != -1 || pout
[0] != -1 || perr
[0] != -1);
1874 if (vis
->interrupted
)
1876 pid_t died
= waitpid(pid
, &status
, 0);
1877 if ((died
== -1 && errno
== ECHILD
) || pid
== died
)
1881 /* clear any pending SIGTERM */
1882 struct sigaction sigterm_ignore
, sigterm_old
;
1883 sigterm_ignore
.sa_handler
= SIG_IGN
;
1884 sigterm_ignore
.sa_flags
= 0;
1885 sigemptyset(&sigterm_ignore
.sa_mask
);
1887 sigaction(SIGTERM
, &sigterm_ignore
, &sigterm_old
);
1888 sigaction(SIGTERM
, &sigterm_old
, NULL
);
1890 vis
->interrupted
= false;
1891 vis
->ui
->terminal_restore(vis
->ui
);
1896 static ssize_t
read_buffer(void *context
, char *data
, size_t len
) {
1897 buffer_append(context
, data
, len
);
1901 int vis_pipe_collect(Vis
*vis
, File
*file
, Filerange
*range
, const char *argv
[], char **out
, char **err
) {
1902 Buffer bufout
, buferr
;
1903 buffer_init(&bufout
);
1904 buffer_init(&buferr
);
1905 int status
= vis_pipe(vis
, file
, range
, argv
,
1906 &bufout
, out
? read_buffer
: NULL
,
1907 &buferr
, err
? read_buffer
: NULL
);
1908 buffer_terminate(&bufout
);
1909 buffer_terminate(&buferr
);
1911 *out
= buffer_move(&bufout
);
1913 *err
= buffer_move(&buferr
);
1914 buffer_release(&bufout
);
1915 buffer_release(&buferr
);
1919 bool vis_cmd(Vis
*vis
, const char *cmdline
) {
1922 while (*cmdline
== ':')
1924 size_t len
= strlen(cmdline
);
1925 char *line
= malloc(len
+2);
1928 strncpy(line
, cmdline
, len
+1);
1930 for (char *end
= line
+ len
- 1; end
>= line
&& isspace((unsigned char)*end
); end
--)
1933 enum SamError err
= sam_cmd(vis
, line
);
1934 if (err
!= SAM_ERR_OK
)
1935 vis_info_show(vis
, "%s", sam_error(err
));
1937 return err
== SAM_ERR_OK
;
1940 void vis_file_snapshot(Vis
*vis
, File
*file
) {
1941 if (!vis
->replaying
)
1942 text_snapshot(file
->text
);
1945 Text
*vis_text(Vis
*vis
) {
1946 return vis
->win
->file
->text
;
1949 View
*vis_view(Vis
*vis
) {
1950 return vis
->win
->view
;
1953 Win
*vis_window(Vis
*vis
) {
1957 bool vis_get_autoindent(const Vis
*vis
) {
1958 return vis
->autoindent
;