aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.def.h15
-rw-r--r--main.c44
-rw-r--r--ui-curses.c126
-rw-r--r--ui.h5
-rw-r--r--vis-cmds.c16
-rw-r--r--vis-core.h10
-rw-r--r--vis-modes.c61
-rw-r--r--vis.c241
-rw-r--r--vis.h12
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,
- },
};
diff --git a/main.c b/main.c
index fc4f383..9834dbf 100644
--- a/main.c
+++ b/main.c
@@ -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();
diff --git a/ui.h b/ui.h
index e8beded..6c5b63d 100644
--- a/ui.h
+++ b/ui.h
@@ -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*);
diff --git a/vis-cmds.c b/vis-cmds.c
index 065b9d3..8cc704b 100644
--- a/vis-cmds.c
+++ b/vis-cmds.c
@@ -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)
diff --git a/vis-core.h b/vis-core.h
index 3093a92..2a4d514 100644
--- a/vis-core.h
+++ b/vis-core.h
@@ -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--",
diff --git a/vis.c b/vis.c
index e16fca4..4875303 100644
--- a/vis.c
+++ b/vis.c
@@ -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;
}
diff --git a/vis.h b/vis.h
index d783eea..8905be1 100644
--- a/vis.h
+++ b/vis.h
@@ -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,