diff options
| author | Marc André Tanner <mat@brain-dump.org> | 2015-11-15 13:56:35 +0100 |
|---|---|---|
| committer | Marc André Tanner <mat@brain-dump.org> | 2016-01-13 23:03:49 +0100 |
| commit | 6d86dc94cffee3fcda48f634fb05556cb861b0b5 (patch) | |
| tree | 6d39cde4af5c4f0de02080046ced36fdfe1f402b | |
| parent | 5d54271403b1a90ed7ef81894ea0ff91d7a8c5f6 (diff) | |
| download | vis-6d86dc94cffee3fcda48f634fb05556cb861b0b5.tar.gz vis-6d86dc94cffee3fcda48f634fb05556cb861b0b5.tar.xz | |
Implement command/search prompt history as a regular file
| -rw-r--r-- | config.def.h | 15 | ||||
| -rw-r--r-- | main.c | 44 | ||||
| -rw-r--r-- | ui-curses.c | 126 | ||||
| -rw-r--r-- | ui.h | 5 | ||||
| -rw-r--r-- | vis-cmds.c | 16 | ||||
| -rw-r--r-- | vis-core.h | 10 | ||||
| -rw-r--r-- | vis-modes.c | 61 | ||||
| -rw-r--r-- | vis.c | 241 | ||||
| -rw-r--r-- | vis.h | 12 |
9 files changed, 263 insertions, 267 deletions
diff --git a/config.def.h b/config.def.h index 73a0193..a40924a 100644 --- a/config.def.h +++ b/config.def.h @@ -275,15 +275,6 @@ static const KeyBinding bindings_readline[] = { { 0 /* empty last element, array terminator */ }, }; -static const KeyBinding bindings_prompt[] = { - { "<Backspace>", ACTION(PROMPT_BACKSPACE) }, - { "<C-h>", ALIAS("<Backspace>") }, - { "<Enter>", ACTION(PROMPT_ENTER) }, - { "<C-j>", ALIAS("<Enter>") }, - { "<Tab>", ACTION(NOP) }, - { 0 /* empty last element, array terminator */ }, -}; - static const KeyBinding bindings_insert[] = { { "<Escape>", ACTION(MODE_NORMAL) }, { "<C-c>", ALIAS("<Escape>") }, @@ -353,10 +344,4 @@ static const KeyBinding **default_bindings[] = { bindings_basic, NULL, }, - [VIS_MODE_PROMPT] = (const KeyBinding*[]){ - bindings_prompt, - bindings_readline, - bindings_basic, - NULL, - }, }; @@ -92,10 +92,7 @@ static const char *delete(Vis*, const char *keys, const Arg *arg); /* insert register content indicated by keys at current cursor position */ static const char *insert_register(Vis*, const char *keys, const Arg *arg); /* show a user prompt to get input with title arg->s */ -static const char *prompt_search(Vis*, const char *keys, const Arg *arg); -static const char *prompt_cmd(Vis*, const char *keys, const Arg *arg); -/* exit command mode if the last char is deleted */ -static const char *prompt_backspace(Vis*, const char *keys, const Arg *arg); +static const char *prompt_show(Vis*, const char *keys, const Arg *arg); /* blocks to read 3 consecutive digits and inserts the corresponding byte value */ static const char *insert_verbatim(Vis*, const char *keys, const Arg *arg); /* scroll window content according to arg->i which can be either PAGE, PAGE_HALF, @@ -216,8 +213,6 @@ enum { VIS_ACTION_JOIN_LINE_BELOW, VIS_ACTION_JOIN_LINES, VIS_ACTION_PROMPT_SHOW, - VIS_ACTION_PROMPT_BACKSPACE, - VIS_ACTION_PROMPT_ENTER, VIS_ACTION_PROMPT_SHOW_VISUAL, VIS_ACTION_REPEAT, VIS_ACTION_SELECTION_FLIP, @@ -637,12 +632,12 @@ static KeyAction vis_action[] = { [VIS_ACTION_PROMPT_SEARCH_FORWARD] = { "search-forward", "Search forward", - prompt_search, { .s = "/" } + prompt_show, { .s = "/" } }, [VIS_ACTION_PROMPT_SEARCH_BACKWARD] = { "search-backward", "Search backward", - prompt_search, { .s = "?" } + prompt_show, { .s = "?" } }, [VIS_ACTION_TILL_LEFT] = { "till-left", @@ -792,22 +787,12 @@ static KeyAction vis_action[] = { [VIS_ACTION_PROMPT_SHOW] = { "prompt-show", "Show editor command line prompt", - prompt_cmd, { .s = "" } - }, - [VIS_ACTION_PROMPT_BACKSPACE] = { - "prompt-backspace", - "Delete previous character in prompt", - prompt_backspace - }, - [VIS_ACTION_PROMPT_ENTER] = { - "prompt-enter", - "Execute current prompt content", - call, { .f = vis_prompt_enter } + prompt_show, { .s = ":" } }, [VIS_ACTION_PROMPT_SHOW_VISUAL] = { "prompt-show-visual", "Show editor command line prompt in visual mode", - prompt_cmd, { .s = "'<,'>" } + prompt_show, { .s = "'<,'>" } }, [VIS_ACTION_REPEAT] = { "editor-repeat", @@ -1409,23 +1394,8 @@ static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) { return keys; } -static const char *prompt_search(Vis *vis, const char *keys, const Arg *arg) { - vis_prompt_show(vis, arg->s, ""); - return keys; -} - -static const char *prompt_cmd(Vis *vis, const char *keys, const Arg *arg) { - vis_prompt_show(vis, ":", arg->s); - return keys; -} - -static const char *prompt_backspace(Vis *vis, const char *keys, const Arg *arg) { - char *cmd = vis_prompt_get(vis); - if (!cmd || !*cmd) - vis_mode_switch(vis, VIS_MODE_NORMAL); - else - delete(vis, keys, &(const Arg){ .i = VIS_MOVE_CHAR_PREV }); - free(cmd); +static const char *prompt_show(Vis *vis, const char *keys, const Arg *arg) { + vis_prompt_show(vis, arg->s); return keys; } diff --git a/ui-curses.c b/ui-curses.c index 68ab5b5..186de58 100644 --- a/ui-curses.c +++ b/ui-curses.c @@ -85,8 +85,6 @@ typedef struct { Vis *vis; /* editor instance to which this ui belongs */ UiCursesWin *windows; /* all windows managed by this ui */ UiCursesWin *selwin; /* the currently selected layout */ - char prompt_title[255]; /* prompt_title[0] == '\0' if prompt isn't shown */ - UiCursesWin *prompt_win; /* like a normal window but without a status bar */ char info[255]; /* info message displayed at the bottom of the screen */ int width, height; /* terminal dimensions available for all windows */ enum UiLayout layout; /* whether windows are displayed horizontally or vertically */ @@ -678,7 +676,7 @@ static void ui_window_draw(UiWin *w) { bool cursor_line = l->lineno == cursor_lineno; for (int x = 0; x < width; x++) { CellStyle *style = &win->styles[l->cells[x].attr]; - if (l->cells[x].cursor && (win->ui->selwin == win || win->ui->prompt_win == win)) { + if (l->cells[x].cursor && win->ui->selwin == win) { attr = style_to_attr(&win->styles[UI_STYLE_CURSOR]); prev_style = NULL; } else if (l->cells[x].selected) { @@ -730,25 +728,44 @@ static void ui_window_update(UiCursesWin *win) { static void ui_arrange(Ui *ui, enum UiLayout layout) { UiCurses *uic = (UiCurses*)ui; uic->layout = layout; - int n = 0, x = 0, y = 0; - for (UiCursesWin *win = uic->windows; win; win = win->next) - n++; - int max_height = uic->height - !!(uic->prompt_title[0] || uic->info[0]); + int n = 0, m = !!uic->info[0], x = 0, y = 0; + for (UiCursesWin *win = uic->windows; win; win = win->next) { + if (win->options & UI_OPTION_ONELINE) + m++; + else + n++; + } + int max_height = uic->height - m; int width = (uic->width / MAX(1, n)) - 1; int height = max_height / MAX(1, n); for (UiCursesWin *win = uic->windows; win; win = win->next) { + if (win->options & UI_OPTION_ONELINE) + continue; + n--; if (layout == UI_LAYOUT_HORIZONTAL) { - ui_window_resize(win, uic->width, win->next ? height : max_height - y); + int h = n ? height : max_height - y; + ui_window_resize(win, uic->width, h); ui_window_move(win, x, y); - y += height; + y += h; } else { - ui_window_resize(win, win->next ? width : uic->width - x, max_height); + int w = n ? width : uic->width - x; + ui_window_resize(win, w, max_height); ui_window_move(win, x, y); - x += width; - if (win->next) + x += w; + if (n) mvvline(0, x++, ACS_VLINE, max_height); } } + + if (layout == UI_LAYOUT_VERTICAL) + y = max_height; + + for (UiCursesWin *win = uic->windows; win; win = win->next) { + if (!(win->options & UI_OPTION_ONELINE)) + continue; + ui_window_resize(win, uic->width, 1); + ui_window_move(win, 0, y++); + } } static void ui_draw(Ui *ui) { @@ -764,12 +781,6 @@ static void ui_draw(Ui *ui) { mvaddstr(uic->height-1, 0, uic->info); } - if (uic->prompt_title[0]) { - attrset(A_NORMAL); - mvaddstr(uic->height-1, 0, uic->prompt_title); - ui_window_draw((UiWin*)uic->prompt_win); - } - wnoutrefresh(stdscr); } @@ -782,11 +793,6 @@ static void ui_resize_to(Ui *ui, int width, int height) { UiCurses *uic = (UiCurses*)ui; uic->width = width; uic->height = height; - if (uic->prompt_title[0]) { - size_t title_width = strlen(uic->prompt_title); - ui_window_resize(uic->prompt_win, width - title_width, 1); - ui_window_move(uic->prompt_win, title_width, height-1); - } ui_draw(ui); } @@ -819,10 +825,6 @@ static void ui_update(Ui *ui) { if (uic->selwin) ui_window_update(uic->selwin); - if (uic->prompt_title[0]) { - wnoutrefresh(uic->prompt_win->win); - ui_window_update(uic->prompt_win); - } doupdate(); } @@ -879,7 +881,27 @@ static void ui_window_options_set(UiWin *w, enum UiOption options) { delwin(win->winstatus); win->winstatus = NULL; } - ui_window_draw(w); + + if (options & UI_OPTION_ONELINE) { + /* move the new window to the end of the list */ + UiCurses *uic = win->ui; + UiCursesWin *last = uic->windows; + while (last->next) + last = last->next; + if (last != win) { + if (win->prev) + win->prev->next = win->next; + if (win->next) + win->next->prev = win->prev; + if (uic->windows == win) + uic->windows = win->next; + last->next = win; + win->prev = last; + win->next = NULL; + } + } + + ui_draw((Ui*)win->ui); } static enum UiOption ui_window_options_get(UiWin *w) { @@ -958,49 +980,6 @@ static void ui_info_hide(Ui *ui) { } } -static UiWin *ui_prompt_new(Ui *ui, View *view, File *file) { - UiCurses *uic = (UiCurses*)ui; - if (uic->prompt_win) - return (UiWin*)uic->prompt_win; - UiWin *uiwin = ui_window_new(ui, view, file, UI_OPTION_NONE); - UiCursesWin *win = (UiCursesWin*)uiwin; - if (!win) - return NULL; - uic->windows = win->next; - if (uic->windows) - uic->windows->prev = NULL; - uic->prompt_win = win; - return uiwin; -} - -static void ui_prompt(Ui *ui, const char *title, const char *data) { - UiCurses *uic = (UiCurses*)ui; - if (uic->prompt_title[0]) - return; - size_t len = strlen(data); - Text *text = vis_file_text(uic->prompt_win->file); - strncpy(uic->prompt_title, title, sizeof(uic->prompt_title)-1); - while (text_undo(text) != EPOS); - text_insert(text, 0, data, len); - view_cursor_to(uic->prompt_win->view, 0); - ui_resize_to(ui, uic->width, uic->height); - view_cursor_to(uic->prompt_win->view, len); -} - -static char *ui_prompt_input(Ui *ui) { - UiCurses *uic = (UiCurses*)ui; - if (!uic->prompt_win) - return NULL; - Text *text = vis_file_text(uic->prompt_win->file); - return text_bytes_alloc0(text, 0, text_size(text)); -} - -static void ui_prompt_hide(Ui *ui) { - UiCurses *uic = (UiCurses*)ui; - uic->prompt_title[0] = '\0'; - ui_resize_to(ui, uic->width, uic->height); -} - static bool ui_init(Ui *ui, Vis *vis) { UiCurses *uic = (UiCurses*)ui; uic->vis = vis; @@ -1111,10 +1090,6 @@ Ui *ui_curses_new(void) { .window_new = ui_window_new, .window_free = ui_window_free, .window_focus = ui_window_focus, - .prompt_new = ui_prompt_new, - .prompt = ui_prompt, - .prompt_input = ui_prompt_input, - .prompt_hide = ui_prompt_hide, .draw = ui_draw, .redraw = ui_redraw, .arrange = ui_arrange, @@ -1146,7 +1121,6 @@ void ui_curses_free(Ui *ui) { UiCurses *uic = (UiCurses*)ui; if (!uic) return; - ui_window_free((UiWin*)uic->prompt_win); while (uic->windows) ui_window_free((UiWin*)uic->windows); endwin(); @@ -24,6 +24,7 @@ enum UiOption { UI_OPTION_SYMBOL_EOF = 1 << 6, UI_OPTION_CURSOR_LINE = 1 << 7, UI_OPTION_STATUSBAR = 1 << 8, + UI_OPTION_ONELINE = 1 << 9, }; enum UiStyles { @@ -48,10 +49,6 @@ struct Ui { UiWin* (*window_new)(Ui*, View*, File*, enum UiOption); void (*window_free)(UiWin*); void (*window_focus)(UiWin*); - UiWin* (*prompt_new)(Ui*, View*, File*); - void (*prompt)(Ui*, const char *title, const char *value); - char* (*prompt_input)(Ui*); - void (*prompt_hide)(Ui*); void (*die)(Ui*, const char *msg, va_list ap) __attribute__((noreturn)); void (*info)(Ui*, const char *msg, va_list ap); void (*info_hide)(Ui*); @@ -443,13 +443,21 @@ static bool cmd_edit(Vis *vis, Filerange *range, enum CmdOpt opt, const char *ar return vis->win != oldwin; } +static bool has_windows(Vis *vis) { + for (Win *win = vis->windows; win; win = win->next) { + if (!win->file->internal) + return true; + } + return false; +} + static bool cmd_quit(Vis *vis, Filerange *range, enum CmdOpt opt, const char *argv[]) { if (!(opt & CMD_OPT_FORCE) && !is_view_closeable(vis->win)) { info_unsaved_changes(vis); return false; } vis_window_close(vis->win); - if (!vis->windows) + if (!has_windows(vis)) vis_exit(vis, EXIT_SUCCESS); return true; } @@ -473,7 +481,7 @@ static bool cmd_bdelete(Vis *vis, Filerange *range, enum CmdOpt opt, const char if (win->file->text == txt) vis_window_close(win); } - if (!vis->windows) + if (!has_windows(vis)) vis_exit(vis, EXIT_SUCCESS); return true; } @@ -484,7 +492,7 @@ static bool cmd_qall(Vis *vis, Filerange *range, enum CmdOpt opt, const char *ar if (!text_modified(vis->win->file->text) || (opt & CMD_OPT_FORCE)) vis_window_close(win); } - if (!vis->windows) + if (!has_windows(vis)) vis_exit(vis, EXIT_SUCCESS); else info_unsaved_changes(vis); @@ -1097,6 +1105,8 @@ static Command *lookup_cmd(Vis *vis, const char *name) { bool vis_cmd(Vis *vis, const char *cmdline) { enum CmdOpt opt = CMD_OPT_NONE; + while (*cmdline == ':') + cmdline++; size_t len = strlen(cmdline); char *line = malloc(len+2); if (!line) @@ -100,6 +100,7 @@ struct File { /* shared state among windows displaying the same file */ const char *name; /* file name used when loading/saving */ volatile sig_atomic_t truncated; /* whether the underlying memory mapped region became invalid (SIGBUS) */ bool is_stdin; /* whether file content was read from stdin */ + bool internal; /* whether it is an internal file (e.g. used for the prompt) */ struct stat stat; /* filesystem information when loaded/saved, used to detect changes outside the editor */ int refcount; /* how many windows are displaying this file? (always >= 1) */ Mark marks[VIS_MARK_INVALID]; /* marks which are shared across windows */ @@ -120,21 +121,22 @@ struct Win { RingBuffer *jumplist; /* LRU jump management */ ChangeList changelist; /* state for iterating through least recently changes */ Mode modes[VIS_MODE_LAST]; /* overlay mods used for per window key bindings */ + Win *parent; /* window which was active when showing the command prompt */ + Mode *parent_mode; /* mode which was active when showing the command prompt */ Win *prev, *next; /* neighbouring windows */ }; struct Vis { Ui *ui; /* user interface repsonsible for visual appearance */ File *files; /* all files currently managed by this editor instance */ + File *command_file; /* special internal file used to store :-command prompt */ + File *search_file; /* special internal file used to store /,? search prompt */ Win *windows; /* all windows currently managed by this editor instance */ Win *win; /* currently active/focused window */ Register registers[VIS_REG_INVALID]; /* registers used for yank and put */ Macro macros[VIS_MACRO_INVALID]; /* recorded macros */ Macro *recording, *last_recording; /* currently (if non NULL) and least recently recorded macro */ Macro *macro_operator; /* special macro used to repeat certain operators */ - Win *prompt; /* 1-line height window to get user input */ - Win *prompt_window; /* window which was focused before prompt was shown */ - char prompt_type; /* command ':' or search '/','?' prompt */ Mode *mode_before_prompt; /* user mode which was active before entering prompt */ Regex *search_pattern; /* last used search pattern */ char search_char[8]; /* last used character to search for via 'f', 'F', 't', 'T' */ @@ -179,4 +181,6 @@ void action_reset(Action*); void mode_set(Vis *vis, Mode *new_mode); Mode *mode_get(Vis *vis, enum VisMode mode); +void window_selection_save(Win *win); + #endif diff --git a/vis-modes.c b/vis-modes.c index 0b85ed7..7daaf05 100644 --- a/vis-modes.c +++ b/vis-modes.c @@ -61,31 +61,23 @@ static void vis_mode_operator_input(Vis *vis, const char *str, size_t len) { static void vis_mode_visual_enter(Vis *vis, Mode *old) { if (!old->visual) { - if (old != &vis_modes[VIS_MODE_PROMPT]) { - for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c)) - view_cursors_selection_start(c); - } + for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c)) + view_cursors_selection_start(c); } } static void vis_mode_visual_line_enter(Vis *vis, Mode *old) { if (!old->visual) { - if (old != &vis_modes[VIS_MODE_PROMPT]) { - for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c)) - view_cursors_selection_start(c); - } + for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c)) + view_cursors_selection_start(c); } vis_motion(vis, VIS_MOVE_LINE_END); } static void vis_mode_visual_line_leave(Vis *vis, Mode *new) { if (!new->visual) { - File *file = vis->win->file; - Filerange sel = view_cursors_selection_get(view_cursors(vis->win->view)); - file->marks[VIS_MARK_SELECTION_START] = text_mark_set(file->text, sel.start); - file->marks[VIS_MARK_SELECTION_END] = text_mark_set(file->text, sel.end); - if (new != &vis_modes[VIS_MODE_PROMPT]) - view_selections_clear(vis->win->view); + window_selection_save(vis->win); + view_selections_clear(vis->win->view); } else { view_cursor_to(vis->win->view, view_cursor_get(vis->win->view)); } @@ -93,37 +85,8 @@ static void vis_mode_visual_line_leave(Vis *vis, Mode *new) { static void vis_mode_visual_leave(Vis *vis, Mode *new) { if (!new->visual) { - File *file = vis->win->file; - Filerange sel = view_cursors_selection_get(view_cursors(vis->win->view)); - file->marks[VIS_MARK_SELECTION_START] = text_mark_set(file->text, sel.start); - file->marks[VIS_MARK_SELECTION_END] = text_mark_set(file->text, sel.end); - if (new != &vis_modes[VIS_MODE_PROMPT]) - view_selections_clear(vis->win->view); - } -} - -static void vis_mode_prompt_input(Vis *vis, const char *str, size_t len) { - vis_insert_key(vis, str, len); -} - -static void vis_mode_prompt_enter(Vis *vis, Mode *old) { - if (old->isuser && old != &vis_modes[VIS_MODE_PROMPT]) { - vis->mode_before_prompt = old; - /* prompt manipulations e.g. <Backspace> should not affect default register */ - Register tmp = vis->registers[VIS_REG_PROMPT]; - vis->registers[VIS_REG_PROMPT] = vis->registers[VIS_REG_DEFAULT]; - vis->registers[VIS_REG_DEFAULT] = tmp; - } -} - -static void vis_mode_prompt_leave(Vis *vis, Mode *new) { - if (new->isuser) { - vis_prompt_hide(vis); - Register tmp = vis->registers[VIS_REG_DEFAULT]; - vis->registers[VIS_REG_DEFAULT] = vis->registers[VIS_REG_PROMPT]; - vis->registers[VIS_REG_PROMPT] = tmp; - if (vis->action_prev.op == &ops[VIS_OP_FILTER]) - macro_operator_stop(vis); + window_selection_save(vis->win); + view_selections_clear(vis->win->view); } } @@ -202,14 +165,6 @@ Mode vis_modes[] = { .leave = vis_mode_visual_line_leave, .visual = true, }, - [VIS_MODE_PROMPT] = { - .name = "PROMPT", - .help = "", - .isuser = true, - .input = vis_mode_prompt_input, - .enter = vis_mode_prompt_enter, - .leave = vis_mode_prompt_leave, - }, [VIS_MODE_INSERT] = { .name = "INSERT", .status = "--INSERT--", @@ -62,8 +62,10 @@ const char *expandtab(Vis *vis) { static void file_free(Vis *vis, File *file) { if (!file) return; - if (--file->refcount > 0) + if (file->refcount > 1) { + --file->refcount; return; + } if (vis->event && vis->event->file_close) vis->event->file_close(vis, file); text_free(file->text); @@ -84,7 +86,6 @@ static File *file_new_text(Vis *vis, Text *text) { return NULL; file->text = text; file->stat = text_stat(text); - file->refcount++; if (vis->files) vis->files->prev = file; file->next = vis->files; @@ -98,7 +99,6 @@ static File *file_new(Vis *vis, const char *filename) { * TODO: do this based on inodes */ for (File *file = vis->files; file; file = file->next) { if (file->name && strcmp(file->name, filename) == 0) { - file->refcount++; return file; } } @@ -143,11 +143,22 @@ static void windows_invalidate(Vis *vis, size_t start, size_t end) { view_draw(vis->win->view); } +void window_selection_save(Win *win) { + File *file = win->file; + Filerange sel = view_cursors_selection_get(view_cursors(win->view)); + file->marks[VIS_MARK_SELECTION_START] = text_mark_set(file->text, sel.start); + file->marks[VIS_MARK_SELECTION_END] = text_mark_set(file->text, sel.end); +} + static void window_free(Win *win) { if (!win) return; Vis *vis = win->vis; - if (vis && vis->ui) + for (Win *other = vis->windows; other; other = other->next) { + if (other->parent == win) + other->parent = NULL; + } + if (vis->ui) vis->ui->window_free(win->ui); view_free(win->view); for (size_t i = 0; i < LENGTH(win->modes); i++) @@ -169,6 +180,7 @@ static Win *window_new_file(Vis *vis, File *file) { window_free(win); return NULL; } + file->refcount++; view_tabwidth_set(win->view, vis->tabwidth); if (vis->windows) vis->windows->prev = win; @@ -194,6 +206,7 @@ bool vis_window_reload(Win *win) { if (!file) return false; file_free(win->vis, win->file); + file->refcount = 1; win->file = file; win->ui->reload(win->ui, file); return true; @@ -286,8 +299,6 @@ void vis_window_close(Win *win) { vis->windows = win->next; if (vis->win == win) vis->win = win->next ? win->next : win->prev; - if (vis->prompt_window == win) - vis->prompt_window = NULL; window_free(win); if (vis->win) vis->ui->window_focus(vis->win->ui); @@ -306,18 +317,16 @@ Vis *vis_new(Ui *ui, VisEvent *event) { vis->ui->init(vis->ui, vis); vis->tabwidth = 8; vis->expandtab = false; - if (!(vis->prompt = calloc(1, sizeof(Win)))) - goto err; - if (!(vis->prompt->file = calloc(1, sizeof(File)))) - goto err; - if (!(vis->prompt->file->text = text_load(NULL))) - goto err; - if (!(vis->prompt->view = view_new(vis->prompt->file->text, NULL))) + if (!(vis->search_pattern = text_regex_new())) goto err; - if (!(vis->prompt->ui = vis->ui->prompt_new(vis->ui, vis->prompt->view, vis->prompt->file))) + if (!(vis->command_file = file_new(vis, NULL))) goto err; - if (!(vis->search_pattern = text_regex_new())) + vis->command_file->refcount = 1; + vis->command_file->internal = true; + if (!(vis->search_file = file_new(vis, NULL))) goto err; + vis->search_file->refcount = 1; + vis->search_file->internal = true; vis->mode_prev = vis->mode = &vis_modes[VIS_MODE_NORMAL]; vis->event = event; return vis; @@ -334,8 +343,8 @@ void vis_free(Vis *vis) { vis->event = NULL; while (vis->windows) vis_window_close(vis->windows); - file_free(vis, vis->prompt->file); - window_free(vis->prompt); + file_free(vis, vis->command_file); + file_free(vis, vis->search_file); text_regex_free(vis->search_pattern); for (int i = 0; i < LENGTH(vis->registers); i++) register_release(&vis->registers[i]); @@ -388,26 +397,157 @@ void vis_delete(Vis *vis, size_t pos, size_t len) { windows_invalidate(vis, pos, pos + len); } -void vis_prompt_show(Vis *vis, const char *title, const char *text) { - if (vis->prompt_window) - return; - vis->prompt_window = vis->win; - vis->prompt_type = title[0]; - vis->ui->prompt(vis->ui, title, text); - vis_mode_switch(vis, VIS_MODE_PROMPT); - vis->win = vis->prompt; +static bool prompt_cmd(Vis *vis, const char *cmd) { + if (!cmd || !cmd[0] || !cmd[1]) + return true; + switch (cmd[0]) { + case '/': + return vis_motion(vis, VIS_MOVE_SEARCH_FORWARD, cmd+1); + case '?': + return vis_motion(vis, VIS_MOVE_SEARCH_BACKWARD, cmd+1); + case '+': + case ':': + return vis_cmd(vis, cmd+1); + } + return false; } -void vis_prompt_hide(Vis *vis) { - if (!vis->prompt_window) - return; - vis->ui->prompt_hide(vis->ui); - vis->win = vis->prompt_window; - vis->prompt_window = NULL; +static void prompt_hide(Win *win) { + Vis *vis = win->vis; + Text *txt = win->file->text; + size_t size = text_size(txt); + /* make sure that file is new line terminated */ + char lastchar; + if (size > 1 && text_byte_get(txt, size-1, &lastchar) && lastchar != '\n') + text_insert(txt, size, "\n", 1); + /* remove empty entries */ + Filerange line = text_object_line(txt, size); + size_t line_size = text_range_size(&line); + if (line_size <= 2) + text_delete(txt, line.start, line_size); + if (win->parent) + vis->win = win->parent; + vis->mode = win->parent_mode; + vis_window_close(win); +} + +static const char *prompt_enter(Vis *vis, const char *keys, const Arg *arg) { + Win *prompt_win = vis->win; + View *view = prompt_win->view; + Text *txt = prompt_win->file->text; + Win *win = prompt_win->parent; + char *cmd = NULL; + + Filerange range = view_selection_get(view); + if (!text_range_valid(&range)) + range = text_object_line_inner(txt, view_cursor_get(view)); + if (text_range_valid(&range)) + cmd = text_bytes_alloc0(txt, range.start, text_range_size(&range)); + + if (!win || !cmd) { + vis_info_show(vis, "Prompt window invalid\n"); + prompt_hide(prompt_win); + free(cmd); + return keys; + } + + /* restore window and mode which was active before the prompt window + * we deliberately don't use vis_mode_switch because we do not want + * to invoke the modes enter/leave functions */ + vis->win = win; + vis->mode = prompt_win->parent_mode; + if (prompt_cmd(vis, cmd)) { + prompt_hide(prompt_win); + } else { + vis->win = prompt_win; + vis->mode = &vis_modes[VIS_MODE_INSERT]; + } + free(cmd); + vis_draw(vis); + return keys; } -char *vis_prompt_get(Vis *vis) { - return vis->ui->prompt_input(vis->ui); +static const char *prompt_esc(Vis *vis, const char *keys, const Arg *arg) { + if (view_cursors_count(vis->win->view) > 1) + view_cursors_clear(vis->win->view); + else + prompt_hide(vis->win); + return keys; +} + +static const char *prompt_up(Vis *vis, const char *keys, const Arg *arg) { + vis_motion(vis, VIS_MOVE_LINE_UP); + vis_window_mode_unmap(vis->win, VIS_MODE_INSERT, "<Up>"); + view_options_set(vis->win->view, UI_OPTION_NONE); + return keys; +} + +static const char *prompt_backspace(Vis *vis, const char *keys, const Arg *arg) { + Text *txt = vis->win->file->text; + size_t size = text_size(txt); + size_t pos = view_cursor_get(vis->win->view); + char c; + if (pos == size && (pos == 1 || (size >= 2 && text_byte_get(txt, size-2, &c) && c == '\n'))) { + prompt_hide(vis->win); + } else { + vis_operator(vis, VIS_OP_DELETE); + vis_motion(vis, VIS_MOVE_CHAR_PREV); + } + return keys; +} + +static const KeyBinding prompt_enter_binding = { + .key = "<Enter>", + .action = &(KeyAction){ + .func = prompt_enter, + }, +}; + +static const KeyBinding prompt_esc_binding = { + .key = "<Escape>", + .action = &(KeyAction){ + .func = prompt_esc, + }, +}; + +static const KeyBinding prompt_up_binding = { + .key = "<Up>", + .action = &(KeyAction){ + .func = prompt_up, + }, +}; + +static const KeyBinding prompt_backspace_binding = { + .key = "<Enter>", + .action = &(KeyAction){ + .func = prompt_backspace, + }, +}; + +void vis_prompt_show(Vis *vis, const char *title) { + Win *active = vis->win; + Win *prompt = window_new_file(vis, title[0] == ':' ? vis->command_file : vis->search_file); + if (!prompt) + return; + if (vis->mode->visual) + window_selection_save(active); + view_options_set(prompt->view, UI_OPTION_ONELINE); + Text *txt = prompt->file->text; + text_insert(txt, text_size(txt), title, strlen(title)); + view_cursors_scroll_to(view_cursor(prompt->view), text_size(txt)); + view_draw(prompt->view); + prompt->parent = active; + prompt->parent_mode = vis->mode; + vis_window_mode_map(prompt, VIS_MODE_NORMAL, "<Enter>", &prompt_enter_binding); + vis_window_mode_map(prompt, VIS_MODE_INSERT, "<Enter>", &prompt_enter_binding); + vis_window_mode_map(prompt, VIS_MODE_REPLACE, "<Enter>", &prompt_enter_binding); + vis_window_mode_map(prompt, VIS_MODE_VISUAL, "<Enter>", &prompt_enter_binding); + vis_window_mode_map(prompt, VIS_MODE_VISUAL_LINE, "<Enter>", &prompt_enter_binding); + vis_window_mode_map(prompt, VIS_MODE_NORMAL, "<Escape>", &prompt_esc_binding); + vis_window_mode_map(prompt, VIS_MODE_INSERT, "<Up>", &prompt_up_binding); + vis_window_mode_map(prompt, VIS_MODE_INSERT, "<Backspace>", &prompt_backspace_binding); + vis_mode_switch(vis, VIS_MODE_INSERT); + vis_draw(vis); } void vis_info_show(Vis *vis, const char *msg, ...) { @@ -605,7 +745,7 @@ static void action_do(Vis *vis, Action *a) { vis_mode_switch(vis, VIS_MODE_NORMAL); vis_cmd(vis, a->arg.s); } else { - vis_prompt_show(vis, ":", "'<,'>!"); + vis_prompt_show(vis, ":'<,'>!"); } } else if (vis->mode == &vis_modes[VIS_MODE_OPERATOR_PENDING]) { mode_set(vis, vis->mode_prev); @@ -634,21 +774,6 @@ void vis_cancel(Vis *vis) { action_reset(&vis->action); } -static bool prompt_cmd(Vis *vis, char type, const char *cmd) { - if (!cmd || !cmd[0]) - return true; - switch (type) { - case '/': - return vis_motion(vis, VIS_MOVE_SEARCH_FORWARD, cmd); - case '?': - return vis_motion(vis, VIS_MOVE_SEARCH_BACKWARD, cmd); - case '+': - case ':': - return vis_cmd(vis, cmd); - } - return false; -} - void vis_die(Vis *vis, const char *msg, ...) { va_list ap; va_start(ap, msg); @@ -838,7 +963,7 @@ static void vis_args(Vis *vis, int argc, char *argv[]) { } else if (!vis_window_new(vis, argv[i])) { vis_die(vis, "Can not load `%s': %s\n", argv[i], strerror(errno)); } else if (cmd) { - prompt_cmd(vis, cmd[0], cmd+1); + prompt_cmd(vis, cmd); cmd = NULL; } } @@ -866,7 +991,7 @@ static void vis_args(Vis *vis, int argc, char *argv[]) { vis_die(vis, "Can not create empty buffer\n"); } if (cmd) - prompt_cmd(vis, cmd[0], cmd+1); + prompt_cmd(vis, cmd); } } @@ -1284,20 +1409,6 @@ void vis_insert_nl(Vis *vis) { copy_indent_from_previous_line(vis->win); } -void vis_prompt_enter(Vis *vis) { - char *s = vis_prompt_get(vis); - /* it is important to switch back to the previous mode, which hides - * the prompt and more importantly resets vis->win to the currently - * focused editor window *before* anything is executed which depends - * on vis->win. - */ - mode_set(vis, vis->mode_before_prompt); - if (s && *s && prompt_cmd(vis, vis->prompt_type, s) && vis->prompt_type == ':') - vis_mode_switch(vis, VIS_MODE_NORMAL); - free(s); - vis_draw(vis); -} - Text *vis_text(Vis *vis) { return vis->win->file->text; } @@ -79,16 +79,7 @@ void vis_window_name(Win*, const char *filename); void vis_window_next(Vis*); void vis_window_prev(Vis*); /* display a user prompt with a certain title and default text */ -void vis_prompt_show(Vis*, const char *title, const char *text); -/* execute current prompt content as command, as if the user had pressed <Enter> */ -void vis_prompt_enter(Vis*); /* TODO: bad abstraction */ -/* hide the user prompt if it is currently shown */ -void vis_prompt_hide(Vis*); -/* return the content of the command prompt in a malloc(3)-ed string - * which the call site has to free. */ -char *vis_prompt_get(Vis*); -/* replace the current command line content with the one given */ -void vis_prompt_set(Vis*, const char *line); +void vis_prompt_show(Vis*, const char *title); /* display a message to the user */ void vis_info_show(Vis*, const char *msg, ...); @@ -124,7 +115,6 @@ enum VisMode { VIS_MODE_OPERATOR_PENDING, VIS_MODE_VISUAL, VIS_MODE_VISUAL_LINE, - VIS_MODE_PROMPT, VIS_MODE_INSERT, VIS_MODE_REPLACE, VIS_MODE_LAST, |
