From 040e74a8b6cd316f9553b80aa8b1a7f83672bbed Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 14 Oct 2019 22:04:08 +0200 Subject: edit: add vi alias with vi-style bindings The edit command already supports a few key bindings for code navigation. To improve user experience for vi-impaired users, add a vi alias that maps traditional vi key bindings to the barebox edit ones. This is done by adding a mode-aware read_modal_key that maps vi normal-mode keys to barebox edit. For operations that requires more than one barebox edit key, a backlog of two characters is additionally used. In interest of code size reduction, a command mode (and the associated readline overhead) are not implemented, the effect of the most common commands :q and :wq commands can be realized with vim-like ZQ and ZZ bindings instead. This increases the size of my LZO-compressed THUMB2 barebox by 843 bytes. Acked-by: Roland Hieber Signed-off-by: Ahmad Fatoum Signed-off-by: Sascha Hauer --- commands/edit.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 144 insertions(+), 8 deletions(-) (limited to 'commands') diff --git a/commands/edit.c b/commands/edit.c index 8668a8c065..4e661df14f 100644 --- a/commands/edit.c +++ b/commands/edit.c @@ -378,8 +378,128 @@ static void getwinsize(void) pos(0, 0); } +static void statusbar(const char *str) +{ + pos(0, screenheight+1); + printf("%*c\r%s", screenwidth, ' ', str); + pos(cursx, cursy); +} + +static int read_modal_key(bool is_modal) +{ + static enum { MODE_INSERT, MODE_NORMAL } mode = MODE_NORMAL; + static int backlog[2] = { -1, -1 }; + int c; + + if (backlog[0] >= 0) { + /* pop a character */ + c = backlog[0]; + backlog[0] = backlog[1]; + backlog[1] = -1; + } else { + c = read_key(); + } + + if (is_modal && mode == MODE_INSERT && (c == -1 || c == CTL_CH('c'))) { + mode = MODE_NORMAL; + statusbar(""); + return -EAGAIN; + } + + if (!is_modal || mode == MODE_INSERT) + return c; + + switch (c) { + case -1: /* invalid escape, e.g. two escapes in a row */ + break; + case 'i': + statusbar("-- INSERT --"); + mode = MODE_INSERT; + break; + case 'h': + return BB_KEY_LEFT; + case 'j': + return BB_KEY_DOWN; + case 'k': + return BB_KEY_UP; + case 'a': + statusbar("-- INSERT --"); + mode = MODE_INSERT; + /* fall through */ + case 'l': + return BB_KEY_RIGHT; + case 'O': + backlog[0] = '\n'; + backlog[1] = BB_KEY_UP; + /* fall through */ + case 'I': + statusbar("-- INSERT --"); + mode = MODE_INSERT; + /* fall through */ + case '^': + case '0': + return BB_KEY_HOME; + case 'g': + c = read_key(); + if (c != 'g') + break; + backlog[0] = CTL_CH('u'); + backlog[1] = CTL_CH('u'); + /* fall through */ + case CTL_CH('u'): + return BB_KEY_PAGEUP; + case 'G': + backlog[0] = CTL_CH('d'); + backlog[1] = CTL_CH('d'); + /* fall through */ + case CTL_CH('d'): + return BB_KEY_PAGEDOWN; + case 'o': + backlog[0] = '\n'; + /* fall through */ + case 'A': + statusbar("-- INSERT --"); + mode = MODE_INSERT; + /* fall through */ + case '$': + return BB_KEY_END; + case CTL_CH('c'): + statusbar("Type ZQ to abandon all changes and exit vi." + "Type ZZ to exit while saving them."); + break; + case 'x': + return BB_KEY_DEL; + case 'X': + return '\b'; + case BB_KEY_PAGEUP: + case BB_KEY_PAGEDOWN: + case BB_KEY_HOME: + case BB_KEY_END: + case BB_KEY_UP: + case BB_KEY_DOWN: + case BB_KEY_RIGHT: + case BB_KEY_LEFT: + return c; + case ':': + statusbar("ERROR: command mode not supported"); + break; + case 'Z': + c = read_key(); + if (c == 'Z') + return CTL_CH('d'); + if (c == 'Q') + return CTL_CH('c'); + default: + statusbar("ERROR: not implemented"); + break; + } + + return -EAGAIN; +} + static int do_edit(int argc, char *argv[]) { + bool is_vi = false; int lastscrcol; int i; int linepos; @@ -401,10 +521,14 @@ static int do_edit(int argc, char *argv[]) else screenheight = 25; - /* check if we are called as "sedit" instead of "edit" */ - if (*argv[0] == 's') { + /* check if we are not called as "edit" */ + if (*argv[0] != 'e') { smartscroll = 1; getwinsize(); + + /* check if we are called as "vi" */ + if (*argv[0] == 'v') + is_vi = true; } buffer = NULL; @@ -424,14 +548,22 @@ static int do_edit(int argc, char *argv[]) pos(0, -1); - printf("\x1b[7m %-25s : Save and quit : quit \x1b[0m", - argv[1]); + if (is_vi) { + screenheight -= 2; + printf("\x1b[7m%*c\x1b[0m", screenwidth , ' '); + pos(0, screenheight-1); + printf("\x1b[7m%*c\x1b[0m", screenwidth , ' '); + printf("\r\x1b[7m%-25s\x1b[0m", argv[1]); + } else { + printf("\x1b[7m %-25s : Save and quit : quit \x1b[0m", + argv[1]); + } + printf("\x1b[2;%dr", screenheight); + pos(0, 0); screenheight--; /* status line */ - pos(0, 0); - refresh(1); while (1) { @@ -469,7 +601,11 @@ static int do_edit(int argc, char *argv[]) lastscrline = scrline; pos(cursx, cursy); - c = read_key(); +again: + c = read_modal_key(is_vi); + if (c == -EAGAIN) + goto again; + switch (c) { case BB_KEY_UP: if (!curline->prev) @@ -559,7 +695,7 @@ out: return ret; } -static const char * const edit_aliases[] = { "sedit", NULL}; +static const char * const edit_aliases[] = { "sedit", "vi", NULL}; BAREBOX_CMD_HELP_START(edit) BAREBOX_CMD_HELP_TEXT("Use cursor keys, Ctrl-C to exit and Ctrl-D to exit-with-save.") -- cgit v1.2.3