aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.def.h834
-rw-r--r--config.mk2
-rw-r--r--editor.c21
-rw-r--r--editor.h17
-rw-r--r--macro.h2
-rw-r--r--ui-curses.c84
-rw-r--r--ui-curses.h15
-rw-r--r--ui.h9
-rw-r--r--vis.c178
9 files changed, 585 insertions, 577 deletions
diff --git a/config.def.h b/config.def.h
index 1516762..758bf69 100644
--- a/config.def.h
+++ b/config.def.h
@@ -1,10 +1,4 @@
/** start by reading from the top of vis.c up until config.h is included */
-/* macros used to specify keys for key bindings */
-#define ESC 0x1B
-#define NONE(k) { .str = { k }, .code = 0 }
-#define KEY(k) { .str = { '\0' }, .code = UI_KEY_##k }
-#define CONTROL(k) NONE((k)&0x1F)
-#define META(k) { .str = { ESC, (k) }, .code = 0 }
/* a mode contains a set of key bindings which are currently valid.
*
@@ -70,160 +64,159 @@ static Command cmds[] = {
/* called before any other keybindings are checked, if the function returns false
* the key is completely ignored. */
-static bool vis_keypress(Key *key) {
+static bool vis_keypress(const char *key) {
editor_info_hide(vis);
- if (vis->recording)
- macro_append(vis->recording, key, sizeof(*key));
+ if (vis->recording && key)
+ macro_append(vis->recording, key);
return true;
}
static KeyBinding basic_movement[] = {
- { { CONTROL('Z') }, suspend, { NULL } },
- { { KEY(LEFT) }, movement, { .i = MOVE_CHAR_PREV } },
- { { KEY(SHIFT_LEFT) }, movement, { .i = MOVE_LONGWORD_START_PREV } },
- { { KEY(RIGHT) }, movement, { .i = MOVE_CHAR_NEXT } },
- { { KEY(SHIFT_RIGHT) }, movement, { .i = MOVE_LONGWORD_START_NEXT } },
- { { KEY(UP) }, movement, { .i = MOVE_LINE_UP } },
- { { KEY(DOWN) }, movement, { .i = MOVE_LINE_DOWN } },
- { { KEY(PAGE_UP) }, wscroll, { .i = -PAGE } },
- { { KEY(PAGE_DOWN) }, wscroll, { .i = +PAGE } },
- { { KEY(HOME) }, movement, { .i = MOVE_LINE_START } },
- { { KEY(END) }, movement, { .i = MOVE_LINE_FINISH } },
+ { "<C-z>" , suspend, { NULL } },
+ { "<Left>" , movement, { .i = MOVE_CHAR_PREV } },
+ { "<S-Left>" , movement, { .i = MOVE_LONGWORD_START_PREV } },
+ { "<Right>" , movement, { .i = MOVE_CHAR_NEXT } },
+ { "<S-Right>" , movement, { .i = MOVE_LONGWORD_START_NEXT } },
+ { "<Up>" , movement, { .i = MOVE_LINE_UP } },
+ { "<Down>" , movement, { .i = MOVE_LINE_DOWN } },
+ { "<PageUp>" , wscroll, { .i = -PAGE } },
+ { "<PageDown>" , wscroll, { .i = +PAGE } },
+ { "<Home>" , movement, { .i = MOVE_LINE_START } },
+ { "<End>" , movement, { .i = MOVE_LINE_FINISH } },
{ /* empty last element, array terminator */ },
};
static KeyBinding vis_movements[] = {
- { { KEY(BACKSPACE) }, movement, { .i = MOVE_CHAR_PREV } },
- { { NONE('h') }, movement, { .i = MOVE_CHAR_PREV } },
- { { NONE(' ') }, movement, { .i = MOVE_CHAR_NEXT } },
- { { NONE('l') }, movement, { .i = MOVE_CHAR_NEXT } },
- { { NONE('k') }, movement, { .i = MOVE_LINE_UP } },
- { { CONTROL('P') }, movement, { .i = MOVE_LINE_UP } },
- { { NONE('g'), NONE('k') }, movement, { .i = MOVE_SCREEN_LINE_UP } },
- { { NONE('g'), KEY(UP) }, movement, { .i = MOVE_SCREEN_LINE_UP } },
- { { NONE('j') }, movement, { .i = MOVE_LINE_DOWN } },
- { { CONTROL('J') }, movement, { .i = MOVE_LINE_DOWN } },
- { { CONTROL('N') }, movement, { .i = MOVE_LINE_DOWN } },
- { { NONE('\n') }, movement, { .i = MOVE_LINE_DOWN } },
- { { NONE('\r') }, movement, { .i = MOVE_LINE_DOWN } },
- { { NONE('g'), NONE('j') }, movement, { .i = MOVE_SCREEN_LINE_DOWN } },
- { { NONE('g'), KEY(DOWN) }, movement, { .i = MOVE_SCREEN_LINE_DOWN } },
- { { NONE('^') }, movement, { .i = MOVE_LINE_START } },
- { { NONE('g'), NONE('_') }, movement, { .i = MOVE_LINE_FINISH } },
- { { NONE('$') }, movement, { .i = MOVE_LINE_LASTCHAR } },
- { { NONE('%') }, movement, { .i = MOVE_BRACKET_MATCH } },
- { { NONE('b') }, movement, { .i = MOVE_WORD_START_PREV } },
- { { NONE('B') }, movement, { .i = MOVE_LONGWORD_START_PREV } },
- { { NONE('w') }, movement, { .i = MOVE_WORD_START_NEXT } },
- { { NONE('W') }, movement, { .i = MOVE_LONGWORD_START_NEXT } },
- { { NONE('g'), NONE('e') }, movement, { .i = MOVE_WORD_END_PREV } },
- { { NONE('g'), NONE('E') }, movement, { .i = MOVE_LONGWORD_END_PREV } },
- { { NONE('e') }, movement, { .i = MOVE_WORD_END_NEXT } },
- { { NONE('E') }, movement, { .i = MOVE_LONGWORD_END_NEXT } },
- { { NONE('{') }, movement, { .i = MOVE_PARAGRAPH_PREV } },
- { { NONE('}') }, movement, { .i = MOVE_PARAGRAPH_NEXT } },
- { { NONE('(') }, movement, { .i = MOVE_SENTENCE_PREV } },
- { { NONE(')') }, movement, { .i = MOVE_SENTENCE_NEXT } },
- { { NONE('['), NONE('[') }, movement, { .i = MOVE_FUNCTION_START_PREV } },
- { { NONE('['), NONE(']') }, movement, { .i = MOVE_FUNCTION_END_PREV } },
- { { NONE(']'), NONE('[') }, movement, { .i = MOVE_FUNCTION_START_NEXT } },
- { { NONE(']'), NONE(']') }, movement, { .i = MOVE_FUNCTION_END_NEXT } },
- { { NONE('g'), NONE('g') }, gotoline, { .i = -1 } },
- { { NONE('g'), NONE('0') }, movement, { .i = MOVE_SCREEN_LINE_BEGIN } },
- { { NONE('g'), NONE('m') }, movement, { .i = MOVE_SCREEN_LINE_MIDDLE } },
- { { NONE('g'), NONE('$') }, movement, { .i = MOVE_SCREEN_LINE_END } },
- { { NONE('G') }, gotoline, { .i = +1 } },
- { { NONE('|') }, movement, { .i = MOVE_COLUMN } },
- { { NONE('n') }, movement, { .i = MOVE_SEARCH_FORWARD } },
- { { NONE('N') }, movement, { .i = MOVE_SEARCH_BACKWARD } },
- { { NONE('H') }, movement, { .i = MOVE_WINDOW_LINE_TOP } },
- { { NONE('M') }, movement, { .i = MOVE_WINDOW_LINE_MIDDLE } },
- { { NONE('L') }, movement, { .i = MOVE_WINDOW_LINE_BOTTOM } },
- { { NONE('*') }, movement, { .i = MOVE_SEARCH_WORD_FORWARD } },
- { { NONE('#') }, movement, { .i = MOVE_SEARCH_WORD_BACKWARD} },
- { { NONE('f') }, movement_key, { .i = MOVE_RIGHT_TO } },
- { { NONE('F') }, movement_key, { .i = MOVE_LEFT_TO } },
- { { NONE('t') }, movement_key, { .i = MOVE_RIGHT_TILL } },
- { { NONE('T') }, movement_key, { .i = MOVE_LEFT_TILL } },
- { { NONE(';') }, totill_repeat, { NULL } },
- { { NONE(',') }, totill_reverse,{ NULL } },
- { { NONE('/') }, prompt_search,{ .s = "/" } },
- { { NONE('?') }, prompt_search,{ .s = "?" } },
+ { "<Backspace>" , movement, { .i = MOVE_CHAR_PREV } },
+ { "h" , movement, { .i = MOVE_CHAR_PREV } },
+ { " " , movement, { .i = MOVE_CHAR_NEXT } },
+ { "l" , movement, { .i = MOVE_CHAR_NEXT } },
+ { "k" , movement, { .i = MOVE_LINE_UP } },
+ { "C-p" , movement, { .i = MOVE_LINE_UP } },
+ { "gk" , movement, { .i = MOVE_SCREEN_LINE_UP } },
+ { "g<Up>" , movement, { .i = MOVE_SCREEN_LINE_UP } },
+ { "j" , movement, { .i = MOVE_LINE_DOWN } },
+ { "<C-j>" , movement, { .i = MOVE_LINE_DOWN } },
+ { "<C-n>" , movement, { .i = MOVE_LINE_DOWN } },
+ { "<Enter>" , movement, { .i = MOVE_LINE_DOWN } },
+ { "gj" , movement, { .i = MOVE_SCREEN_LINE_DOWN } },
+ { "g<Down>" , movement, { .i = MOVE_SCREEN_LINE_DOWN } },
+ { "^" , movement, { .i = MOVE_LINE_START } },
+ { "g_" , movement, { .i = MOVE_LINE_FINISH } },
+ { "$" , movement, { .i = MOVE_LINE_LASTCHAR } },
+ { "%" , movement, { .i = MOVE_BRACKET_MATCH } },
+ { "b" , movement, { .i = MOVE_WORD_START_PREV } },
+ { "B" , movement, { .i = MOVE_LONGWORD_START_PREV } },
+ { "w" , movement, { .i = MOVE_WORD_START_NEXT } },
+ { "W" , movement, { .i = MOVE_LONGWORD_START_NEXT } },
+ { "ge" , movement, { .i = MOVE_WORD_END_PREV } },
+ { "gE" , movement, { .i = MOVE_LONGWORD_END_PREV } },
+ { "e" , movement, { .i = MOVE_WORD_END_NEXT } },
+ { "E" , movement, { .i = MOVE_LONGWORD_END_NEXT } },
+ { "{" , movement, { .i = MOVE_PARAGRAPH_PREV } },
+ { "}" , movement, { .i = MOVE_PARAGRAPH_NEXT } },
+ { "(" , movement, { .i = MOVE_SENTENCE_PREV } },
+ { ")" , movement, { .i = MOVE_SENTENCE_NEXT } },
+ { "[[" , movement, { .i = MOVE_FUNCTION_START_PREV } },
+ { "[]" , movement, { .i = MOVE_FUNCTION_END_PREV } },
+ { "][" , movement, { .i = MOVE_FUNCTION_START_NEXT } },
+ { "]]" , movement, { .i = MOVE_FUNCTION_END_NEXT } },
+ { "gg" , gotoline, { .i = -1 } },
+ { "g0" , movement, { .i = MOVE_SCREEN_LINE_BEGIN } },
+ { "gm" , movement, { .i = MOVE_SCREEN_LINE_MIDDLE } },
+ { "g$" , movement, { .i = MOVE_SCREEN_LINE_END } },
+ { "G" , gotoline, { .i = +1 } },
+ { "|" , movement, { .i = MOVE_COLUMN } },
+ { "n" , movement, { .i = MOVE_SEARCH_FORWARD } },
+ { "N" , movement, { .i = MOVE_SEARCH_BACKWARD } },
+ { "H" , movement, { .i = MOVE_WINDOW_LINE_TOP } },
+ { "M" , movement, { .i = MOVE_WINDOW_LINE_MIDDLE } },
+ { "L" , movement, { .i = MOVE_WINDOW_LINE_BOTTOM } },
+ { "*" , movement, { .i = MOVE_SEARCH_WORD_FORWARD } },
+ { "#" , movement, { .i = MOVE_SEARCH_WORD_BACKWARD} },
+ { "f" , movement_key, { .i = MOVE_RIGHT_TO } },
+ { "F" , movement_key, { .i = MOVE_LEFT_TO } },
+ { "t" , movement_key, { .i = MOVE_RIGHT_TILL } },
+ { "T" , movement_key, { .i = MOVE_LEFT_TILL } },
+ { ";" , totill_repeat, { NULL } },
+ { "," , totill_reverse,{ NULL } },
+ { "/" , prompt_search,{ .s = "/" } },
+ { "?" , prompt_search,{ .s = "?" } },
{ /* empty last element, array terminator */ },
};
static KeyBinding vis_textobjs[] = {
- { { NONE('a'), NONE('w') }, textobj, { .i = TEXT_OBJ_OUTER_WORD } },
- { { NONE('a'), NONE('W') }, textobj, { .i = TEXT_OBJ_OUTER_LONGWORD } },
- { { NONE('a'), NONE('s') }, textobj, { .i = TEXT_OBJ_SENTENCE } },
- { { NONE('a'), NONE('p') }, textobj, { .i = TEXT_OBJ_PARAGRAPH } },
- { { NONE('a'), NONE('[') }, textobj, { .i = TEXT_OBJ_OUTER_SQUARE_BRACKET } },
- { { NONE('a'), NONE(']') }, textobj, { .i = TEXT_OBJ_OUTER_SQUARE_BRACKET } },
- { { NONE('a'), NONE('(') }, textobj, { .i = TEXT_OBJ_OUTER_PARANTHESE } },
- { { NONE('a'), NONE(')') }, textobj, { .i = TEXT_OBJ_OUTER_PARANTHESE } },
- { { NONE('a'), NONE('b') }, textobj, { .i = TEXT_OBJ_OUTER_PARANTHESE } },
- { { NONE('a'), NONE('<') }, textobj, { .i = TEXT_OBJ_OUTER_ANGLE_BRACKET } },
- { { NONE('a'), NONE('>') }, textobj, { .i = TEXT_OBJ_OUTER_ANGLE_BRACKET } },
- { { NONE('a'), NONE('{') }, textobj, { .i = TEXT_OBJ_OUTER_CURLY_BRACKET } },
- { { NONE('a'), NONE('}') }, textobj, { .i = TEXT_OBJ_OUTER_CURLY_BRACKET } },
- { { NONE('a'), NONE('B') }, textobj, { .i = TEXT_OBJ_OUTER_CURLY_BRACKET } },
- { { NONE('a'), NONE('"') }, textobj, { .i = TEXT_OBJ_OUTER_QUOTE } },
- { { NONE('a'), NONE('\'') }, textobj, { .i = TEXT_OBJ_OUTER_SINGLE_QUOTE } },
- { { NONE('a'), NONE('`') }, textobj, { .i = TEXT_OBJ_OUTER_BACKTICK } },
- { { NONE('a'), NONE('e') }, textobj, { .i = TEXT_OBJ_OUTER_ENTIRE } },
- { { NONE('a'), NONE('f') }, textobj, { .i = TEXT_OBJ_OUTER_FUNCTION } },
- { { NONE('a'), NONE('l') }, textobj, { .i = TEXT_OBJ_OUTER_LINE } },
+ { "aw", textobj, { .i = TEXT_OBJ_OUTER_WORD } },
+ { "aW", textobj, { .i = TEXT_OBJ_OUTER_LONGWORD } },
+ { "as", textobj, { .i = TEXT_OBJ_SENTENCE } },
+ { "ap", textobj, { .i = TEXT_OBJ_PARAGRAPH } },
+ { "a[", textobj, { .i = TEXT_OBJ_OUTER_SQUARE_BRACKET } },
+ { "a]", textobj, { .i = TEXT_OBJ_OUTER_SQUARE_BRACKET } },
+ { "a(", textobj, { .i = TEXT_OBJ_OUTER_PARANTHESE } },
+ { "a)", textobj, { .i = TEXT_OBJ_OUTER_PARANTHESE } },
+ { "ab", textobj, { .i = TEXT_OBJ_OUTER_PARANTHESE } },
+ { "a<", textobj, { .i = TEXT_OBJ_OUTER_ANGLE_BRACKET } },
+ { "a>", textobj, { .i = TEXT_OBJ_OUTER_ANGLE_BRACKET } },
+ { "a{", textobj, { .i = TEXT_OBJ_OUTER_CURLY_BRACKET } },
+ { "a}", textobj, { .i = TEXT_OBJ_OUTER_CURLY_BRACKET } },
+ { "aB", textobj, { .i = TEXT_OBJ_OUTER_CURLY_BRACKET } },
+ { "a\"", textobj, { .i = TEXT_OBJ_OUTER_QUOTE } },
+ { "a\'", textobj, { .i = TEXT_OBJ_OUTER_SINGLE_QUOTE } },
+ { "a`", textobj, { .i = TEXT_OBJ_OUTER_BACKTICK } },
+ { "ae", textobj, { .i = TEXT_OBJ_OUTER_ENTIRE } },
+ { "af", textobj, { .i = TEXT_OBJ_OUTER_FUNCTION } },
+ { "al", textobj, { .i = TEXT_OBJ_OUTER_LINE } },
{ /* empty last element, array terminator */ },
};
static KeyBinding vis_inner_textobjs[] = {
- { { NONE('i'), NONE('w') }, textobj, { .i = TEXT_OBJ_INNER_WORD } },
- { { NONE('i'), NONE('W') }, textobj, { .i = TEXT_OBJ_INNER_LONGWORD } },
- { { NONE('i'), NONE('s') }, textobj, { .i = TEXT_OBJ_SENTENCE } },
- { { NONE('i'), NONE('p') }, textobj, { .i = TEXT_OBJ_PARAGRAPH } },
- { { NONE('i'), NONE('[') }, textobj, { .i = TEXT_OBJ_INNER_SQUARE_BRACKET } },
- { { NONE('i'), NONE(']') }, textobj, { .i = TEXT_OBJ_INNER_SQUARE_BRACKET } },
- { { NONE('i'), NONE('(') }, textobj, { .i = TEXT_OBJ_INNER_PARANTHESE } },
- { { NONE('i'), NONE(')') }, textobj, { .i = TEXT_OBJ_INNER_PARANTHESE } },
- { { NONE('i'), NONE('b') }, textobj, { .i = TEXT_OBJ_INNER_PARANTHESE } },
- { { NONE('i'), NONE('<') }, textobj, { .i = TEXT_OBJ_INNER_ANGLE_BRACKET } },
- { { NONE('i'), NONE('>') }, textobj, { .i = TEXT_OBJ_INNER_ANGLE_BRACKET } },
- { { NONE('i'), NONE('{') }, textobj, { .i = TEXT_OBJ_INNER_CURLY_BRACKET } },
- { { NONE('i'), NONE('}') }, textobj, { .i = TEXT_OBJ_INNER_CURLY_BRACKET } },
- { { NONE('i'), NONE('B') }, textobj, { .i = TEXT_OBJ_INNER_CURLY_BRACKET } },
- { { NONE('i'), NONE('"') }, textobj, { .i = TEXT_OBJ_INNER_QUOTE } },
- { { NONE('i'), NONE('\'') }, textobj, { .i = TEXT_OBJ_INNER_SINGLE_QUOTE } },
- { { NONE('i'), NONE('`') }, textobj, { .i = TEXT_OBJ_INNER_BACKTICK } },
- { { NONE('i'), NONE('e') }, textobj, { .i = TEXT_OBJ_INNER_ENTIRE } },
- { { NONE('i'), NONE('f') }, textobj, { .i = TEXT_OBJ_INNER_FUNCTION } },
- { { NONE('i'), NONE('l') }, textobj, { .i = TEXT_OBJ_INNER_LINE } },
+ { "iw", textobj, { .i = TEXT_OBJ_INNER_WORD } },
+ { "iW", textobj, { .i = TEXT_OBJ_INNER_LONGWORD } },
+ { "is", textobj, { .i = TEXT_OBJ_SENTENCE } },
+ { "ip", textobj, { .i = TEXT_OBJ_PARAGRAPH } },
+ { "i[", textobj, { .i = TEXT_OBJ_INNER_SQUARE_BRACKET } },
+ { "i]", textobj, { .i = TEXT_OBJ_INNER_SQUARE_BRACKET } },
+ { "i(", textobj, { .i = TEXT_OBJ_INNER_PARANTHESE } },
+ { "i)", textobj, { .i = TEXT_OBJ_INNER_PARANTHESE } },
+ { "ib", textobj, { .i = TEXT_OBJ_INNER_PARANTHESE } },
+ { "i<", textobj, { .i = TEXT_OBJ_INNER_ANGLE_BRACKET } },
+ { "i>", textobj, { .i = TEXT_OBJ_INNER_ANGLE_BRACKET } },
+ { "i{", textobj, { .i = TEXT_OBJ_INNER_CURLY_BRACKET } },
+ { "i}", textobj, { .i = TEXT_OBJ_INNER_CURLY_BRACKET } },
+ { "iB", textobj, { .i = TEXT_OBJ_INNER_CURLY_BRACKET } },
+ { "i\"", textobj, { .i = TEXT_OBJ_INNER_QUOTE } },
+ { "i\'", textobj, { .i = TEXT_OBJ_INNER_SINGLE_QUOTE } },
+ { "i`", textobj, { .i = TEXT_OBJ_INNER_BACKTICK } },
+ { "ie", textobj, { .i = TEXT_OBJ_INNER_ENTIRE } },
+ { "if", textobj, { .i = TEXT_OBJ_INNER_FUNCTION } },
+ { "il", textobj, { .i = TEXT_OBJ_INNER_LINE } },
{ /* empty last element, array terminator */ },
};
static KeyBinding vis_operators[] = {
- { { NONE('0') }, zero, { NULL } },
- { { NONE('1') }, count, { .i = 1 } },
- { { NONE('2') }, count, { .i = 2 } },
- { { NONE('3') }, count, { .i = 3 } },
- { { NONE('4') }, count, { .i = 4 } },
- { { NONE('5') }, count, { .i = 5 } },
- { { NONE('6') }, count, { .i = 6 } },
- { { NONE('7') }, count, { .i = 7 } },
- { { NONE('8') }, count, { .i = 8 } },
- { { NONE('9') }, count, { .i = 9 } },
- { { NONE('d') }, operator, { .i = OP_DELETE } },
- { { NONE('c') }, operator, { .i = OP_CHANGE } },
- { { NONE('y') }, operator, { .i = OP_YANK } },
- { { NONE('p') }, put, { .i = PUT_AFTER } },
- { { NONE('P') }, put, { .i = PUT_BEFORE } },
- { { NONE('g'), NONE('p') }, put, { .i = PUT_AFTER_END } },
- { { NONE('g'), NONE('P') }, put, { .i = PUT_BEFORE_END } },
- { { NONE('>') }, operator, { .i = OP_SHIFT_RIGHT } },
- { { NONE('<') }, operator, { .i = OP_SHIFT_LEFT } },
- { { NONE('g'), NONE('U') }, changecase, { .i = +1 } },
- { { NONE('~') }, changecase, { .i = 0 } },
- { { NONE('g'), NONE('~') }, changecase, { .i = 0 } },
- { { NONE('g'), NONE('u') }, changecase, { .i = -1 } },
+ { "0" , zero, { NULL } },
+ { "1" , count, { .i = 1 } },
+ { "2" , count, { .i = 2 } },
+ { "3" , count, { .i = 3 } },
+ { "4" , count, { .i = 4 } },
+ { "5" , count, { .i = 5 } },
+ { "6" , count, { .i = 6 } },
+ { "7" , count, { .i = 7 } },
+ { "8" , count, { .i = 8 } },
+ { "9" , count, { .i = 9 } },
+ { "d" , operator, { .i = OP_DELETE } },
+ { "c" , operator, { .i = OP_CHANGE } },
+ { "y" , operator, { .i = OP_YANK } },
+ { "p" , put, { .i = PUT_AFTER } },
+ { "P" , put, { .i = PUT_BEFORE } },
+ { "gp" , put, { .i = PUT_AFTER_END } },
+ { "gP" , put, { .i = PUT_BEFORE_END } },
+ { ">" , operator, { .i = OP_SHIFT_RIGHT } },
+ { "<" , operator, { .i = OP_SHIFT_LEFT } },
+ { "gU" , changecase, { .i = +1 } },
+ { "~" , changecase, { .i = 0 } },
+ { "g~" , changecase, { .i = 0 } },
+ { "gu" , changecase, { .i = -1 } },
{ /* empty last element, array terminator */ },
};
@@ -242,224 +235,224 @@ static void vis_mode_operator_input(const char *str, size_t len) {
}
static KeyBinding vis_operator_options[] = {
- { { NONE('v') }, motiontype, { .i = CHARWISE } },
- { { NONE('V') }, motiontype, { .i = LINEWISE } },
+ { "v" , motiontype, { .i = CHARWISE } },
+ { "V" , motiontype, { .i = LINEWISE } },
{ /* empty last element, array terminator */ },
};
static KeyBinding vis_registers[] = { /* {a-zA-Z0-9.%#:-"} */
- { { NONE('"'), NONE('a') }, reg, { .i = REG_a } },
- { { NONE('"'), NONE('b') }, reg, { .i = REG_b } },
- { { NONE('"'), NONE('c') }, reg, { .i = REG_c } },
- { { NONE('"'), NONE('d') }, reg, { .i = REG_d } },
- { { NONE('"'), NONE('e') }, reg, { .i = REG_e } },
- { { NONE('"'), NONE('f') }, reg, { .i = REG_f } },
- { { NONE('"'), NONE('g') }, reg, { .i = REG_g } },
- { { NONE('"'), NONE('h') }, reg, { .i = REG_h } },
- { { NONE('"'), NONE('i') }, reg, { .i = REG_i } },
- { { NONE('"'), NONE('j') }, reg, { .i = REG_j } },
- { { NONE('"'), NONE('k') }, reg, { .i = REG_k } },
- { { NONE('"'), NONE('l') }, reg, { .i = REG_l } },
- { { NONE('"'), NONE('m') }, reg, { .i = REG_m } },
- { { NONE('"'), NONE('n') }, reg, { .i = REG_n } },
- { { NONE('"'), NONE('o') }, reg, { .i = REG_o } },
- { { NONE('"'), NONE('p') }, reg, { .i = REG_p } },
- { { NONE('"'), NONE('q') }, reg, { .i = REG_q } },
- { { NONE('"'), NONE('r') }, reg, { .i = REG_r } },
- { { NONE('"'), NONE('s') }, reg, { .i = REG_s } },
- { { NONE('"'), NONE('t') }, reg, { .i = REG_t } },
- { { NONE('"'), NONE('u') }, reg, { .i = REG_u } },
- { { NONE('"'), NONE('v') }, reg, { .i = REG_v } },
- { { NONE('"'), NONE('w') }, reg, { .i = REG_w } },
- { { NONE('"'), NONE('x') }, reg, { .i = REG_x } },
- { { NONE('"'), NONE('y') }, reg, { .i = REG_y } },
- { { NONE('"'), NONE('z') }, reg, { .i = REG_z } },
- { /* empty last element, array terminator */ },
+ { "\"a", reg, { .i = REG_a } },
+ { "\"b", reg, { .i = REG_b } },
+ { "\"c", reg, { .i = REG_c } },
+ { "\"d", reg, { .i = REG_d } },
+ { "\"e", reg, { .i = REG_e } },
+ { "\"f", reg, { .i = REG_f } },
+ { "\"g", reg, { .i = REG_g } },
+ { "\"h", reg, { .i = REG_h } },
+ { "\"i", reg, { .i = REG_i } },
+ { "\"j", reg, { .i = REG_j } },
+ { "\"k", reg, { .i = REG_k } },
+ { "\"l", reg, { .i = REG_l } },
+ { "\"m", reg, { .i = REG_m } },
+ { "\"n", reg, { .i = REG_n } },
+ { "\"o", reg, { .i = REG_o } },
+ { "\"p", reg, { .i = REG_p } },
+ { "\"q", reg, { .i = REG_q } },
+ { "\"r", reg, { .i = REG_r } },
+ { "\"s", reg, { .i = REG_s } },
+ { "\"t", reg, { .i = REG_t } },
+ { "\"u", reg, { .i = REG_u } },
+ { "\"v", reg, { .i = REG_v } },
+ { "\"w", reg, { .i = REG_w } },
+ { "\"x", reg, { .i = REG_x } },
+ { "\"y", reg, { .i = REG_y } },
+ { "\"z", reg, { .i = REG_z } },
+ { /* empty last element, array terminator */ },
};
static KeyBinding vis_marks[] = {
- { { NONE('`'), NONE('a') }, mark, { .i = MARK_a } },
- { { NONE('`'), NONE('b') }, mark, { .i = MARK_b } },
- { { NONE('`'), NONE('c') }, mark, { .i = MARK_c } },
- { { NONE('`'), NONE('d') }, mark, { .i = MARK_d } },
- { { NONE('`'), NONE('e') }, mark, { .i = MARK_e } },
- { { NONE('`'), NONE('f') }, mark, { .i = MARK_f } },
- { { NONE('`'), NONE('g') }, mark, { .i = MARK_g } },
- { { NONE('`'), NONE('h') }, mark, { .i = MARK_h } },
- { { NONE('`'), NONE('i') }, mark, { .i = MARK_i } },
- { { NONE('`'), NONE('j') }, mark, { .i = MARK_j } },
- { { NONE('`'), NONE('k') }, mark, { .i = MARK_k } },
- { { NONE('`'), NONE('l') }, mark, { .i = MARK_l } },
- { { NONE('`'), NONE('m') }, mark, { .i = MARK_m } },
- { { NONE('`'), NONE('n') }, mark, { .i = MARK_n } },
- { { NONE('`'), NONE('o') }, mark, { .i = MARK_o } },
- { { NONE('`'), NONE('p') }, mark, { .i = MARK_p } },
- { { NONE('`'), NONE('q') }, mark, { .i = MARK_q } },
- { { NONE('`'), NONE('r') }, mark, { .i = MARK_r } },
- { { NONE('`'), NONE('s') }, mark, { .i = MARK_s } },
- { { NONE('`'), NONE('t') }, mark, { .i = MARK_t } },
- { { NONE('`'), NONE('u') }, mark, { .i = MARK_u } },
- { { NONE('`'), NONE('v') }, mark, { .i = MARK_v } },
- { { NONE('`'), NONE('w') }, mark, { .i = MARK_w } },
- { { NONE('`'), NONE('x') }, mark, { .i = MARK_x } },
- { { NONE('`'), NONE('y') }, mark, { .i = MARK_y } },
- { { NONE('`'), NONE('z') }, mark, { .i = MARK_z } },
- { { NONE('`'), NONE('<') }, mark, { .i = MARK_SELECTION_START } },
- { { NONE('`'), NONE('>') }, mark, { .i = MARK_SELECTION_END } },
- { /* empty last element, array terminator */ },
+ { "`a'", mark, { .i = MARK_a } },
+ { "`b'", mark, { .i = MARK_b } },
+ { "`c'", mark, { .i = MARK_c } },
+ { "`d'", mark, { .i = MARK_d } },
+ { "`e'", mark, { .i = MARK_e } },
+ { "`f'", mark, { .i = MARK_f } },
+ { "`g'", mark, { .i = MARK_g } },
+ { "`h'", mark, { .i = MARK_h } },
+ { "`i'", mark, { .i = MARK_i } },
+ { "`j'", mark, { .i = MARK_j } },
+ { "`k'", mark, { .i = MARK_k } },
+ { "`l'", mark, { .i = MARK_l } },
+ { "`m'", mark, { .i = MARK_m } },
+ { "`n'", mark, { .i = MARK_n } },
+ { "`o'", mark, { .i = MARK_o } },
+ { "`p'", mark, { .i = MARK_p } },
+ { "`q'", mark, { .i = MARK_q } },
+ { "`r'", mark, { .i = MARK_r } },
+ { "`s'", mark, { .i = MARK_s } },
+ { "`t'", mark, { .i = MARK_t } },
+ { "`u'", mark, { .i = MARK_u } },
+ { "`v'", mark, { .i = MARK_v } },
+ { "`w'", mark, { .i = MARK_w } },
+ { "`x'", mark, { .i = MARK_x } },
+ { "`y'", mark, { .i = MARK_y } },
+ { "`z'", mark, { .i = MARK_z } },
+ { "`<'", mark, { .i = MARK_SELECTION_START } },
+ { "`>'", mark, { .i = MARK_SELECTION_END } },
+ { /* empty last element, array terminator */ },
};
static KeyBinding vis_marks_line[] = {
- { { NONE('\''), NONE('a') }, mark_line, { .i = MARK_a } },
- { { NONE('\''), NONE('b') }, mark_line, { .i = MARK_b } },
- { { NONE('\''), NONE('c') }, mark_line, { .i = MARK_c } },
- { { NONE('\''), NONE('d') }, mark_line, { .i = MARK_d } },
- { { NONE('\''), NONE('e') }, mark_line, { .i = MARK_e } },
- { { NONE('\''), NONE('f') }, mark_line, { .i = MARK_f } },
- { { NONE('\''), NONE('g') }, mark_line, { .i = MARK_g } },
- { { NONE('\''), NONE('h') }, mark_line, { .i = MARK_h } },
- { { NONE('\''), NONE('i') }, mark_line, { .i = MARK_i } },
- { { NONE('\''), NONE('j') }, mark_line, { .i = MARK_j } },
- { { NONE('\''), NONE('k') }, mark_line, { .i = MARK_k } },
- { { NONE('\''), NONE('l') }, mark_line, { .i = MARK_l } },
- { { NONE('\''), NONE('m') }, mark_line, { .i = MARK_m } },
- { { NONE('\''), NONE('n') }, mark_line, { .i = MARK_n } },
- { { NONE('\''), NONE('o') }, mark_line, { .i = MARK_o } },
- { { NONE('\''), NONE('p') }, mark_line, { .i = MARK_p } },
- { { NONE('\''), NONE('q') }, mark_line, { .i = MARK_q } },
- { { NONE('\''), NONE('r') }, mark_line, { .i = MARK_r } },
- { { NONE('\''), NONE('s') }, mark_line, { .i = MARK_s } },
- { { NONE('\''), NONE('t') }, mark_line, { .i = MARK_t } },
- { { NONE('\''), NONE('u') }, mark_line, { .i = MARK_u } },
- { { NONE('\''), NONE('v') }, mark_line, { .i = MARK_v } },
- { { NONE('\''), NONE('w') }, mark_line, { .i = MARK_w } },
- { { NONE('\''), NONE('x') }, mark_line, { .i = MARK_x } },
- { { NONE('\''), NONE('y') }, mark_line, { .i = MARK_y } },
- { { NONE('\''), NONE('z') }, mark_line, { .i = MARK_z } },
- { { NONE('\''), NONE('<') }, mark_line, { .i = MARK_SELECTION_START } },
- { { NONE('\''), NONE('>') }, mark_line, { .i = MARK_SELECTION_END } },
- { /* empty last element, array terminator */ },
+ { "'a", mark_line, { .i = MARK_a } },
+ { "'b", mark_line, { .i = MARK_b } },
+ { "'c", mark_line, { .i = MARK_c } },
+ { "'d", mark_line, { .i = MARK_d } },
+ { "'e", mark_line, { .i = MARK_e } },
+ { "'f", mark_line, { .i = MARK_f } },
+ { "'g", mark_line, { .i = MARK_g } },
+ { "'h", mark_line, { .i = MARK_h } },
+ { "'i", mark_line, { .i = MARK_i } },
+ { "'j", mark_line, { .i = MARK_j } },
+ { "'k", mark_line, { .i = MARK_k } },
+ { "'l", mark_line, { .i = MARK_l } },
+ { "'m", mark_line, { .i = MARK_m } },
+ { "'n", mark_line, { .i = MARK_n } },
+ { "'o", mark_line, { .i = MARK_o } },
+ { "'p", mark_line, { .i = MARK_p } },
+ { "'q", mark_line, { .i = MARK_q } },
+ { "'r", mark_line, { .i = MARK_r } },
+ { "'s", mark_line, { .i = MARK_s } },
+ { "'t", mark_line, { .i = MARK_t } },
+ { "'u", mark_line, { .i = MARK_u } },
+ { "'v", mark_line, { .i = MARK_v } },
+ { "'w", mark_line, { .i = MARK_w } },
+ { "'x", mark_line, { .i = MARK_x } },
+ { "'y", mark_line, { .i = MARK_y } },
+ { "'z", mark_line, { .i = MARK_z } },
+ { "'<", mark_line, { .i = MARK_SELECTION_START } },
+ { "'>", mark_line, { .i = MARK_SELECTION_END } },
+ { /* empty last element, array terminator */ },
};
static KeyBinding vis_marks_set[] = {
- { { NONE('m'), NONE('a') }, mark_set, { .i = MARK_a } },
- { { NONE('m'), NONE('b') }, mark_set, { .i = MARK_b } },
- { { NONE('m'), NONE('c') }, mark_set, { .i = MARK_c } },
- { { NONE('m'), NONE('d') }, mark_set, { .i = MARK_d } },
- { { NONE('m'), NONE('e') }, mark_set, { .i = MARK_e } },
- { { NONE('m'), NONE('f') }, mark_set, { .i = MARK_f } },
- { { NONE('m'), NONE('g') }, mark_set, { .i = MARK_g } },
- { { NONE('m'), NONE('h') }, mark_set, { .i = MARK_h } },
- { { NONE('m'), NONE('i') }, mark_set, { .i = MARK_i } },
- { { NONE('m'), NONE('j') }, mark_set, { .i = MARK_j } },
- { { NONE('m'), NONE('k') }, mark_set, { .i = MARK_k } },
- { { NONE('m'), NONE('l') }, mark_set, { .i = MARK_l } },
- { { NONE('m'), NONE('m') }, mark_set, { .i = MARK_m } },
- { { NONE('m'), NONE('n') }, mark_set, { .i = MARK_n } },
- { { NONE('m'), NONE('o') }, mark_set, { .i = MARK_o } },
- { { NONE('m'), NONE('p') }, mark_set, { .i = MARK_p } },
- { { NONE('m'), NONE('q') }, mark_set, { .i = MARK_q } },
- { { NONE('m'), NONE('r') }, mark_set, { .i = MARK_r } },
- { { NONE('m'), NONE('s') }, mark_set, { .i = MARK_s } },
- { { NONE('m'), NONE('t') }, mark_set, { .i = MARK_t } },
- { { NONE('m'), NONE('u') }, mark_set, { .i = MARK_u } },
- { { NONE('m'), NONE('v') }, mark_set, { .i = MARK_v } },
- { { NONE('m'), NONE('w') }, mark_set, { .i = MARK_w } },
- { { NONE('m'), NONE('x') }, mark_set, { .i = MARK_x } },
- { { NONE('m'), NONE('y') }, mark_set, { .i = MARK_y } },
- { { NONE('m'), NONE('z') }, mark_set, { .i = MARK_z } },
- { /* empty last element, array terminator */ },
+ { "ma", mark_set, { .i = MARK_a } },
+ { "mb", mark_set, { .i = MARK_b } },
+ { "mc", mark_set, { .i = MARK_c } },
+ { "md", mark_set, { .i = MARK_d } },
+ { "me", mark_set, { .i = MARK_e } },
+ { "mf", mark_set, { .i = MARK_f } },
+ { "mg", mark_set, { .i = MARK_g } },
+ { "mh", mark_set, { .i = MARK_h } },
+ { "mi", mark_set, { .i = MARK_i } },
+ { "mj", mark_set, { .i = MARK_j } },
+ { "mk", mark_set, { .i = MARK_k } },
+ { "ml", mark_set, { .i = MARK_l } },
+ { "mm", mark_set, { .i = MARK_m } },
+ { "mn", mark_set, { .i = MARK_n } },
+ { "mo", mark_set, { .i = MARK_o } },
+ { "mp", mark_set, { .i = MARK_p } },
+ { "mq", mark_set, { .i = MARK_q } },
+ { "mr", mark_set, { .i = MARK_r } },
+ { "ms", mark_set, { .i = MARK_s } },
+ { "mt", mark_set, { .i = MARK_t } },
+ { "mu", mark_set, { .i = MARK_u } },
+ { "mv", mark_set, { .i = MARK_v } },
+ { "mw", mark_set, { .i = MARK_w } },
+ { "mx", mark_set, { .i = MARK_x } },
+ { "my", mark_set, { .i = MARK_y } },
+ { "mz", mark_set, { .i = MARK_z } },
+ { /* empty last element, array terminator */ },
};
static KeyBinding vis_mode_normal[] = {
- { { NONE(ESC) }, cursors_clear, { } },
- { { CONTROL('K') }, cursors_new, { .i = -1 } },
- { { CONTROL('J') }, cursors_new, { .i = +1 } },
- { { CONTROL('A') }, cursors_align, { } },
- { { CONTROL('N') }, cursors_select, { } },
- { { CONTROL('P') }, cursors_remove, { } },
- { { CONTROL('w'), NONE('n') }, cmd, { .s = "open" } },
- { { CONTROL('w'), NONE('c') }, cmd, { .s = "q" } },
- { { CONTROL('w'), NONE('s') }, cmd, { .s = "split" } },
- { { CONTROL('w'), NONE('v') }, cmd, { .s = "vsplit" } },
- { { CONTROL('w'), NONE('j') }, call, { .f = editor_window_next } },
- { { CONTROL('w'), NONE('l') }, call, { .f = editor_window_next } },
- { { CONTROL('w'), NONE('k') }, call, { .f = editor_window_prev } },
- { { CONTROL('w'), NONE('h') }, call, { .f = editor_window_prev } },
- { { CONTROL('w'), CONTROL('j') }, call, { .f = editor_window_next } },
- { { CONTROL('w'), CONTROL('l') }, call, { .f = editor_window_next } },
- { { CONTROL('w'), CONTROL('k') }, call, { .f = editor_window_prev } },
- { { CONTROL('w'), CONTROL('w') }, call, { .f = editor_window_next } },
- { { CONTROL('w'), CONTROL('h') }, call, { .f = editor_window_prev } },
- { { CONTROL('w'), KEY(BACKSPACE) }, call, { .f = editor_window_prev } },
- { { CONTROL('B') }, wscroll, { .i = -PAGE } },
- { { CONTROL('F') }, wscroll, { .i = +PAGE } },
- { { CONTROL('U') }, wscroll, { .i = -PAGE_HALF } },
- { { CONTROL('D') }, wscroll, { .i = +PAGE_HALF } },
- { { CONTROL('E') }, wslide, { .i = -1 } },
- { { CONTROL('Y') }, wslide, { .i = +1 } },
- { { CONTROL('O') }, jumplist, { .i = -1 } },
- { { CONTROL('I') }, jumplist, { .i = +1 } },
- { { NONE('g'), NONE(';') }, changelist, { .i = -1 } },
- { { NONE('g'), NONE(',') }, changelist, { .i = +1 } },
- { { NONE('a') }, insertmode, { .i = MOVE_CHAR_NEXT } },
- { { NONE('A') }, insertmode, { .i = MOVE_LINE_END } },
- { { NONE('C') }, change, { .i = MOVE_LINE_END } },
- { { NONE('D') }, delete, { .i = MOVE_LINE_END } },
- { { NONE('I') }, insertmode, { .i = MOVE_LINE_START } },
- { { NONE('.') }, repeat, { NULL } },
- { { NONE('o') }, openline, { .i = MOVE_LINE_NEXT } },
- { { NONE('O') }, openline, { .i = MOVE_LINE_PREV } },
- { { NONE('J') }, join, { .i = MOVE_LINE_NEXT } },
- { { NONE('x') }, delete, { .i = MOVE_CHAR_NEXT } },
- { { NONE('r') }, replace, { NULL } },
- { { NONE('i') }, switchmode, { .i = VIS_MODE_INSERT } },
- { { NONE('v') }, switchmode, { .i = VIS_MODE_VISUAL } },
- { { NONE('V') }, switchmode, { .i = VIS_MODE_VISUAL_LINE } },
- { { NONE('R') }, switchmode, { .i = VIS_MODE_REPLACE } },
- { { NONE('S') }, operator_twice, { .i = OP_CHANGE } },
- { { NONE('s') }, change, { .i = MOVE_CHAR_NEXT } },
- { { NONE('Y') }, operator_twice, { .i = OP_YANK } },
- { { NONE('X') }, delete, { .i = MOVE_CHAR_PREV } },
- { { NONE('u') }, undo, { NULL } },
- { { CONTROL('R') }, redo, { NULL } },
- { { NONE('g'), NONE('+') }, later, { NULL } },
- { { NONE('g'), NONE('-') }, earlier, { NULL } },
- { { CONTROL('L') }, call, { .f = editor_draw } },
- { { NONE(':') }, prompt_cmd, { .s = "" } },
- { { NONE('Z'), NONE('Z') }, cmd, { .s = "wq" } },
- { { NONE('Z'), NONE('Q') }, cmd, { .s = "q!" } },
- { { NONE('z'), NONE('t') }, window, { .w = view_redraw_top } },
- { { NONE('z'), NONE('z') }, window, { .w = view_redraw_center } },
- { { NONE('z'), NONE('b') }, window, { .w = view_redraw_bottom } },
- { { NONE('q') }, macro_record, { NULL } },
- { { NONE('@') }, macro_replay, { NULL } },
- { { NONE('g'), NONE('v') }, selection_restore, { NULL } },
- { /* empty last element, array terminator */ },
+ { "<Escape>", cursors_clear, { } },
+ { "<C-k>", cursors_new, { .i = -1 } },
+ { "<C-j>", cursors_new, { .i = +1 } },
+ { "<C-a>", cursors_align, { } },
+ { "<C-n>", cursors_select, { } },
+ { "<C-p>", cursors_remove, { } },
+ { "<C-w>n", cmd, { .s = "open" } },
+ { "<C-w>c", cmd, { .s = "q" } },
+ { "<C-w>s", cmd, { .s = "split" } },
+ { "<C-w>v", cmd, { .s = "vsplit" } },
+ { "<C-w>j", call, { .f = editor_window_next } },
+ { "<C-w>l", call, { .f = editor_window_next } },
+ { "<C-w>k", call, { .f = editor_window_prev } },
+ { "<C-w>h", call, { .f = editor_window_prev } },
+ { "<C-w><C-j>", call, { .f = editor_window_next } },
+ { "<C-w><C-l>", call, { .f = editor_window_next } },
+ { "<C-w><C-k>", call, { .f = editor_window_prev } },
+ { "<C-w><C-w>", call, { .f = editor_window_next } },
+ { "<C-w><C-h>", call, { .f = editor_window_prev } },
+ { "<C-w><Backspace>", call, { .f = editor_window_prev } },
+ { "<C-b>", wscroll, { .i = -PAGE } },
+ { "<C-f>", wscroll, { .i = +PAGE } },
+ { "<C-u>", wscroll, { .i = -PAGE_HALF } },
+ { "<C-d>", wscroll, { .i = +PAGE_HALF } },
+ { "<C-e>", wslide, { .i = -1 } },
+ { "<C-y>", wslide, { .i = +1 } },
+ { "<C-o>", jumplist, { .i = -1 } },
+ { "<C-i>", jumplist, { .i = +1 } },
+ { "g;", changelist, { .i = -1 } },
+ { "g,", changelist, { .i = +1 } },
+ { "a", insertmode, { .i = MOVE_CHAR_NEXT } },
+ { "A", insertmode, { .i = MOVE_LINE_END } },
+ { "C", change, { .i = MOVE_LINE_END } },
+ { "D", delete, { .i = MOVE_LINE_END } },
+ { "I", insertmode, { .i = MOVE_LINE_START } },
+ { ".", repeat, { NULL } },
+ { "o", openline, { .i = MOVE_LINE_NEXT } },
+ { "O", openline, { .i = MOVE_LINE_PREV } },
+ { "J", join, { .i = MOVE_LINE_NEXT } },
+ { "x", delete, { .i = MOVE_CHAR_NEXT } },
+ { "r", replace, { NULL } },
+ { "i", switchmode, { .i = VIS_MODE_INSERT } },
+ { "v", switchmode, { .i = VIS_MODE_VISUAL } },
+ { "V", switchmode, { .i = VIS_MODE_VISUAL_LINE } },
+ { "R", switchmode, { .i = VIS_MODE_REPLACE } },
+ { "S", operator_twice, { .i = OP_CHANGE } },
+ { "s", change, { .i = MOVE_CHAR_NEXT } },
+ { "Y", operator_twice, { .i = OP_YANK } },
+ { "X", delete, { .i = MOVE_CHAR_PREV } },
+ { "u", undo, { NULL } },
+ { "<C-r>", redo, { NULL } },
+ { "g+", later, { NULL } },
+ { "g-", earlier, { NULL } },
+ { "<C-l>", call, { .f = editor_draw } },
+ { ":", prompt_cmd, { .s = "" } },
+ { "ZZ", cmd, { .s = "wq" } },
+ { "ZQ", cmd, { .s = "q!" } },
+ { "zt", window, { .w = view_redraw_top } },
+ { "zz", window, { .w = view_redraw_center } },
+ { "zb", window, { .w = view_redraw_bottom } },
+ { "q", macro_record, { NULL } },
+ { "@", macro_replay, { NULL } },
+ { "gv", selection_restore, { NULL } },
+ { /* empty last element, array terminator */ },
};
static KeyBinding vis_mode_visual[] = {
- { { CONTROL('N') }, cursors_select_next, { } },
- { { CONTROL('X') }, cursors_select_skip, { } },
- { { CONTROL('P') }, cursors_remove, { } },
- { { NONE('I') }, cursors_split, { .i = -1 } },
- { { NONE('A') }, cursors_split, { .i = +1 } },
- { { KEY(BACKSPACE) }, operator, { .i = OP_DELETE } },
- { { KEY(DELETE) }, operator, { .i = OP_DELETE } },
- { { NONE(ESC) }, switchmode, { .i = VIS_MODE_NORMAL } },
- { { CONTROL('c') }, switchmode, { .i = VIS_MODE_NORMAL } },
- { { NONE('v') }, switchmode, { .i = VIS_MODE_NORMAL } },
- { { NONE('V') }, switchmode, { .i = VIS_MODE_VISUAL_LINE } },
- { { NONE(':') }, prompt_cmd, { .s = "'<,'>" } },
- { { CONTROL('H') }, operator, { .i = OP_DELETE } },
- { { NONE('d') }, operator, { .i = OP_DELETE } },
- { { NONE('x') }, operator, { .i = OP_DELETE } },
- { { NONE('y') }, operator, { .i = OP_YANK } },
- { { NONE('c') }, operator, { .i = OP_CHANGE } },
- { { NONE('r') }, operator, { .i = OP_CHANGE } },
- { { NONE('s') }, operator, { .i = OP_CHANGE } },
- { { NONE('J') }, operator, { .i = OP_JOIN } },
- { { NONE('o') }, selection_end, { NULL } },
+ { "<C-n>", cursors_select_next, { } },
+ { "<C-x>", cursors_select_skip, { } },
+ { "<C-p>", cursors_remove, { } },
+ { "I", cursors_split, { .i = -1 } },
+ { "A", cursors_split, { .i = +1 } },
+ { "<Backspace>", operator, { .i = OP_DELETE } },
+ { "<DEL>", operator, { .i = OP_DELETE } },
+ { "<Escape>", switchmode, { .i = VIS_MODE_NORMAL } },
+ { "<C-c>", switchmode, { .i = VIS_MODE_NORMAL } },
+ { "v", switchmode, { .i = VIS_MODE_NORMAL } },
+ { "V", switchmode, { .i = VIS_MODE_VISUAL_LINE } },
+ { ":", prompt_cmd, { .s = "'<,'>" } },
+ { "<C-h>", operator, { .i = OP_DELETE } },
+ { "d", operator, { .i = OP_DELETE } },
+ { "x", operator, { .i = OP_DELETE } },
+ { "y", operator, { .i = OP_YANK } },
+ { "c", operator, { .i = OP_CHANGE } },
+ { "r", operator, { .i = OP_CHANGE } },
+ { "s", operator, { .i = OP_CHANGE } },
+ { "J", operator, { .i = OP_JOIN } },
+ { "o", selection_end, { NULL } },
{ /* empty last element, array terminator */ },
};
@@ -479,9 +472,9 @@ static void vis_mode_visual_leave(Mode *new) {
}
static KeyBinding vis_mode_visual_line[] = {
- { { NONE('v') }, switchmode, { .i = VIS_MODE_VISUAL } },
- { { NONE('V') }, switchmode, { .i = VIS_MODE_NORMAL } },
- { /* empty last element, array terminator */ },
+ { "v", switchmode, { .i = VIS_MODE_VISUAL } },
+ { "V", switchmode, { .i = VIS_MODE_NORMAL } },
+ { /* empty last element, array terminator */ },
};
static void vis_mode_visual_line_enter(Mode *old) {
@@ -503,28 +496,27 @@ static void vis_mode_visual_line_leave(Mode *new) {
}
static KeyBinding vis_mode_readline[] = {
- { { KEY(BACKSPACE) }, delete, { .i = MOVE_CHAR_PREV } },
- { { NONE(ESC) }, switchmode, { .i = VIS_MODE_NORMAL } },
- { { CONTROL('c') }, switchmode, { .i = VIS_MODE_NORMAL } },
- { { CONTROL('D') }, delete , { .i = MOVE_CHAR_NEXT } },
- { { CONTROL('W') }, delete, { .i = MOVE_LONGWORD_START_PREV } },
- { { CONTROL('U') }, delete, { .i = MOVE_LINE_BEGIN } },
- { /* empty last element, array terminator */ },
+ { "<Backspace>", delete, { .i = MOVE_CHAR_PREV } },
+ { "<Escape>", switchmode, { .i = VIS_MODE_NORMAL } },
+ { "<C-c>", switchmode, { .i = VIS_MODE_NORMAL } },
+ { "<C-d>", delete , { .i = MOVE_CHAR_NEXT } },
+ { "<C-w>", delete, { .i = MOVE_LONGWORD_START_PREV } },
+ { "<C-u>", delete, { .i = MOVE_LINE_BEGIN } },
+ { /* empty last element, array terminator */ },
};
static KeyBinding vis_mode_prompt[] = {
- { { KEY(BACKSPACE) }, prompt_backspace,{ .s = NULL } },
- { { NONE('\n') }, prompt_enter, { NULL } },
- { { NONE('\r') }, prompt_enter, { NULL } },
- { { CONTROL('J') }, prompt_enter, { NULL } },
- { { KEY(UP) }, prompt_up, { NULL } },
- { { KEY(DOWN) }, prompt_down, { NULL } },
- { { KEY(HOME) }, movement, { .i = MOVE_FILE_BEGIN } },
- { { CONTROL('B') }, movement, { .i = MOVE_FILE_BEGIN } },
- { { KEY(END) }, movement, { .i = MOVE_FILE_END } },
- { { CONTROL('E') }, movement, { .i = MOVE_FILE_END } },
- { { NONE('\t') }, NULL, { NULL } },
- { /* empty last element, array terminator */ },
+ { "<Backspace>", prompt_backspace,{ .s = NULL } },
+ { "<Enter>", prompt_enter, { NULL } },
+ { "<C-j>", prompt_enter, { NULL } },
+ { "<Up>", prompt_up, { NULL } },
+ { "<Down>", prompt_down, { NULL } },
+ { "<Home>", movement, { .i = MOVE_FILE_BEGIN } },
+ { "<C-b>", movement, { .i = MOVE_FILE_BEGIN } },
+ { "<End>", movement, { .i = MOVE_FILE_END } },
+ { "<C-e>", movement, { .i = MOVE_FILE_END } },
+ { "<Tab>", NULL, { NULL } },
+ { /* empty last element, array terminator */ },
};
static void vis_mode_prompt_input(const char *str, size_t len) {
@@ -542,50 +534,52 @@ static void vis_mode_prompt_leave(Mode *new) {
}
static KeyBinding vis_mode_insert_register[] = {
- { { CONTROL('R'), NONE('a') }, insert_register, { .i = REG_a } },
- { { CONTROL('R'), NONE('b') }, insert_register, { .i = REG_b } },
- { { CONTROL('R'), NONE('c') }, insert_register, { .i = REG_c } },
- { { CONTROL('R'), NONE('d') }, insert_register, { .i = REG_d } },
- { { CONTROL('R'), NONE('e') }, insert_register, { .i = REG_e } },
- { { CONTROL('R'), NONE('f') }, insert_register, { .i = REG_f } },
- { { CONTROL('R'), NONE('g') }, insert_register, { .i = REG_g } },
- { { CONTROL('R'), NONE('h') }, insert_register, { .i = REG_h } },
- { { CONTROL('R'), NONE('i') }, insert_register, { .i = REG_i } },
- { { CONTROL('R'), NONE('j') }, insert_register, { .i = REG_j } },
- { { CONTROL('R'), NONE('k') }, insert_register, { .i = REG_k } },
- { { CONTROL('R'), NONE('l') }, insert_register, { .i = REG_l } },
- { { CONTROL('R'), NONE('m') }, insert_register, { .i = REG_m } },
- { { CONTROL('R'), NONE('n') }, insert_register, { .i = REG_n } },
- { { CONTROL('R'), NONE('o') }, insert_register, { .i = REG_o } },
- { { CONTROL('R'), NONE('p') }, insert_register, { .i = REG_p } },
- { { CONTROL('R'), NONE('q') }, insert_register, { .i = REG_q } },
- { { CONTROL('R'), NONE('r') }, insert_register, { .i = REG_r } },
- { { CONTROL('R'), NONE('s') }, insert_register, { .i = REG_s } },
- { { CONTROL('R'), NONE('t') }, insert_register, { .i = REG_t } },
- { { CONTROL('R'), NONE('u') }, insert_register, { .i = REG_u } },
- { { CONTROL('R'), NONE('v') }, insert_register, { .i = REG_v } },
- { { CONTROL('R'), NONE('w') }, insert_register, { .i = REG_w } },
- { { CONTROL('R'), NONE('x') }, insert_register, { .i = REG_x } },
- { { CONTROL('R'), NONE('y') }, insert_register, { .i = REG_y } },
- { { CONTROL('R'), NONE('z') }, insert_register, { .i = REG_z } },
- { /* empty last element, array terminator */ },
+ { "<C-r>a", insert_register, { .i = REG_a } },
+ { "<C-r>b", insert_register, { .i = REG_b } },
+ { "<C-r>c", insert_register, { .i = REG_c } },
+ { "<C-r>d", insert_register, { .i = REG_d } },
+ { "<C-r>e", insert_register, { .i = REG_e } },
+ { "<C-r>f", insert_register, { .i = REG_f } },
+ { "<C-r>g", insert_register, { .i = REG_g } },
+ { "<C-r>h", insert_register, { .i = REG_h } },
+ { "<C-r>i", insert_register, { .i = REG_i } },
+ { "<C-r>j", insert_register, { .i = REG_j } },
+ { "<C-r>k", insert_register, { .i = REG_k } },
+ { "<C-r>l", insert_register, { .i = REG_l } },
+ { "<C-r>m", insert_register, { .i = REG_m } },
+ { "<C-r>n", insert_register, { .i = REG_n } },
+ { "<C-r>o", insert_register, { .i = REG_o } },
+ { "<C-r>p", insert_register, { .i = REG_p } },
+ { "<C-r>q", insert_register, { .i = REG_q } },
+ { "<C-r>r", insert_register, { .i = REG_r } },
+ { "<C-r>s", insert_register, { .i = REG_s } },
+ { "<C-r>t", insert_register, { .i = REG_t } },
+ { "<C-r>u", insert_register, { .i = REG_u } },
+ { "<C-r>v", insert_register, { .i = REG_v } },
+ { "<C-r>w", insert_register, { .i = REG_w } },
+ { "<C-r>x", insert_register, { .i = REG_x } },
+ { "<C-r>y", insert_register, { .i = REG_y } },
+ { "<C-r>z", insert_register, { .i = REG_z } },
+ { /* empty last element, array terminator */ },
};
static KeyBinding vis_mode_insert[] = {
- { { CONTROL('L') }, switchmode, { .i = VIS_MODE_NORMAL } },
- { { CONTROL('[') }, switchmode, { .i = VIS_MODE_NORMAL } },
- { { CONTROL('I') }, insert_tab, { NULL } },
- { { CONTROL('J') }, insert_newline, { NULL } },
- { { CONTROL('M') }, insert_newline, { NULL } },
- { { CONTROL('O') }, switchmode, { .i = VIS_MODE_OPERATOR } },
- { { CONTROL('V') }, insert_verbatim, { NULL } },
- { { CONTROL('D') }, operator_twice, { .i = OP_SHIFT_LEFT } },
- { { CONTROL('T') }, operator_twice, { .i = OP_SHIFT_RIGHT } },
- { { CONTROL('X'), CONTROL('E') }, wslide, { .i = -1 } },
- { { CONTROL('X'), CONTROL('Y') }, wslide, { .i = +1 } },
- { { NONE('\t') }, insert_tab, { NULL } },
- { { KEY(END) }, movement, { .i = MOVE_LINE_END } },
- { /* empty last element, array terminator */ },
+ { "<Escape>", switchmode, { .i = VIS_MODE_NORMAL } },
+ { "<C-l>", switchmode, { .i = VIS_MODE_NORMAL } },
+ { "<C-[>", switchmode, { .i = VIS_MODE_NORMAL } },
+ { "<C-i>", insert_tab, { NULL } },
+ { "<C-j>", insert_newline, { NULL } },
+ { "<C-m>", insert_newline, { NULL } },
+ { "<Enter>", insert_newline, { NULL } },
+ { "<C-o>", switchmode, { .i = VIS_MODE_OPERATOR } },
+ { "<C-v>", insert_verbatim, { NULL } },
+ { "<C-d>", operator_twice, { .i = OP_SHIFT_LEFT } },
+ { "<C-t>", operator_twice, { .i = OP_SHIFT_RIGHT } },
+ { "<C-x><C-e>", wslide, { .i = -1 } },
+ { "<C-x><C-y>", wslide, { .i = +1 } },
+ { "<Tab>", insert_tab, { NULL } },
+ { "<End>", movement, { .i = MOVE_LINE_END } },
+ { /* empty last element, array terminator */ },
};
static void vis_mode_insert_leave(Mode *old) {
@@ -610,7 +604,7 @@ static void vis_mode_insert_input(const char *str, size_t len) {
}
static KeyBinding vis_mode_replace[] = {
- { { NONE(ESC) }, switchmode, { .i = VIS_MODE_NORMAL } },
+ { "<Escape>", switchmode, { .i = VIS_MODE_NORMAL } },
{ /* empty last element, array terminator */ },
};
@@ -677,46 +671,46 @@ static Mode vis_modes[] = {
[VIS_MODE_BASIC] = {
.name = "BASIC",
.parent = NULL,
- .bindings = basic_movement,
+ .default_bindings = basic_movement,
},
[VIS_MODE_MARK] = {
.name = "MARK",
.common_prefix = true,
.parent = &vis_modes[VIS_MODE_BASIC],
- .bindings = vis_marks,
+ .default_bindings = vis_marks,
},
[VIS_MODE_MARK_LINE] = {
.name = "MARK-LINE",
.common_prefix = true,
.parent = &vis_modes[VIS_MODE_MARK],
- .bindings = vis_marks_line,
+ .default_bindings = vis_marks_line,
},
[VIS_MODE_MOVE] = {
.name = "MOVE",
.parent = &vis_modes[VIS_MODE_MARK_LINE],
- .bindings = vis_movements,
+ .default_bindings = vis_movements,
},
[VIS_MODE_INNER_TEXTOBJ] = {
.name = "INNER-TEXTOBJ",
.common_prefix = true,
.parent = &vis_modes[VIS_MODE_MOVE],
- .bindings = vis_inner_textobjs,
+ .default_bindings = vis_inner_textobjs,
},
[VIS_MODE_TEXTOBJ] = {
.name = "TEXTOBJ",
.common_prefix = true,
.parent = &vis_modes[VIS_MODE_INNER_TEXTOBJ],
- .bindings = vis_textobjs,
+ .default_bindings = vis_textobjs,
},
[VIS_MODE_OPERATOR_OPTION] = {
.name = "OPERATOR-OPTION",
.parent = &vis_modes[VIS_MODE_TEXTOBJ],
- .bindings = vis_operator_options,
+ .default_bindings = vis_operator_options,
},
[VIS_MODE_OPERATOR] = {
.name = "OPERATOR",
.parent = &vis_modes[VIS_MODE_MOVE],
- .bindings = vis_operators,
+ .default_bindings = vis_operators,
.enter = vis_mode_operator_enter,
.leave = vis_mode_operator_leave,
.input = vis_mode_operator_input,
@@ -725,25 +719,25 @@ static Mode vis_modes[] = {
.name = "REGISTER",
.common_prefix = true,
.parent = &vis_modes[VIS_MODE_OPERATOR],
- .bindings = vis_registers,
+ .default_bindings = vis_registers,
},
[VIS_MODE_MARK_SET] = {
.name = "MARK-SET",
.common_prefix = true,
.parent = &vis_modes[VIS_MODE_REGISTER],
- .bindings = vis_marks_set,
+ .default_bindings = vis_marks_set,
},
[VIS_MODE_NORMAL] = {
.name = "NORMAL",
.isuser = true,
.parent = &vis_modes[VIS_MODE_MARK_SET],
- .bindings = vis_mode_normal,
+ .default_bindings = vis_mode_normal,
},
[VIS_MODE_VISUAL] = {
.name = "--VISUAL--",
.isuser = true,
.parent = &vis_modes[VIS_MODE_REGISTER],
- .bindings = vis_mode_visual,
+ .default_bindings = vis_mode_visual,
.enter = vis_mode_visual_enter,
.leave = vis_mode_visual_leave,
.visual = true,
@@ -752,7 +746,7 @@ static Mode vis_modes[] = {
.name = "--VISUAL LINE--",
.isuser = true,
.parent = &vis_modes[VIS_MODE_VISUAL],
- .bindings = vis_mode_visual_line,
+ .default_bindings = vis_mode_visual_line,
.enter = vis_mode_visual_line_enter,
.leave = vis_mode_visual_line_leave,
.visual = true,
@@ -760,13 +754,13 @@ static Mode vis_modes[] = {
[VIS_MODE_READLINE] = {
.name = "READLINE",
.parent = &vis_modes[VIS_MODE_BASIC],
- .bindings = vis_mode_readline,
+ .default_bindings = vis_mode_readline,
},
[VIS_MODE_PROMPT] = {
.name = "PROMPT",
.isuser = true,
.parent = &vis_modes[VIS_MODE_READLINE],
- .bindings = vis_mode_prompt,
+ .default_bindings = vis_mode_prompt,
.input = vis_mode_prompt_input,
.enter = vis_mode_prompt_enter,
.leave = vis_mode_prompt_leave,
@@ -775,13 +769,13 @@ static Mode vis_modes[] = {
.name = "INSERT-REGISTER",
.common_prefix = true,
.parent = &vis_modes[VIS_MODE_READLINE],
- .bindings = vis_mode_insert_register,
+ .default_bindings = vis_mode_insert_register,
},
[VIS_MODE_INSERT] = {
.name = "--INSERT--",
.isuser = true,
.parent = &vis_modes[VIS_MODE_INSERT_REGISTER],
- .bindings = vis_mode_insert,
+ .default_bindings = vis_mode_insert,
.leave = vis_mode_insert_leave,
.input = vis_mode_insert_input,
.idle = vis_mode_insert_idle,
@@ -791,7 +785,7 @@ static Mode vis_modes[] = {
.name = "--REPLACE--",
.isuser = true,
.parent = &vis_modes[VIS_MODE_INSERT],
- .bindings = vis_mode_replace,
+ .default_bindings = vis_mode_replace,
.leave = vis_mode_replace_leave,
.input = vis_mode_replace_input,
.idle = vis_mode_insert_idle,
diff --git a/config.mk b/config.mk
index 85ddc17..0fe5fd6 100644
--- a/config.mk
+++ b/config.mk
@@ -18,7 +18,7 @@ PREFIX ?= /usr/local
MANPREFIX = ${PREFIX}/share/man
INCS = -I.
-LIBS = -lc -lncursesw
+LIBS = -lc -lncursesw -ltermkey
OS = $(shell uname)
diff --git a/editor.c b/editor.c
index e06e946..70cc162 100644
--- a/editor.c
+++ b/editor.c
@@ -230,6 +230,27 @@ void editor_suspend(Editor *ed) {
ed->ui->suspend(ed->ui);
}
+bool editor_mode_map(Mode *mode, const char *name, KeyBinding *binding) {
+ return map_put(mode->bindings, name, binding);
+}
+
+bool editor_mode_bindings(Mode *mode, KeyBinding **bindings) {
+ if (!mode->bindings)
+ mode->bindings = map_new();
+ if (!mode->bindings)
+ return false;
+ bool success = true;
+ for (KeyBinding *kb = *bindings; kb->key; kb++) {
+ if (!editor_mode_map(mode, kb->key, kb))
+ success = false;
+ }
+ return success;
+}
+
+bool editor_mode_unmap(Mode *mode, const char *name) {
+ return map_delete(mode->bindings, name);
+}
+
static void window_free(Win *win) {
if (!win)
return;
diff --git a/editor.h b/editor.h
index 2d0819b..2c500a9 100644
--- a/editor.h
+++ b/editor.h
@@ -27,11 +27,8 @@ typedef union {
void (*f)(Editor*); /* generic editor commands */
} Arg;
-#define MAX_KEYS 2
-typedef Key KeyCombo[MAX_KEYS];
-
typedef struct {
- KeyCombo key;
+ const char *key;
void (*func)(const Arg *arg);
const Arg arg;
} KeyBinding;
@@ -39,14 +36,13 @@ typedef struct {
typedef struct Mode Mode;
struct Mode {
Mode *parent; /* if no match is found in this mode, search will continue there */
- KeyBinding *bindings; /* NULL terminated array of keybindings for this mode */
+ Map *bindings;
+ KeyBinding *default_bindings;
const char *name; /* descriptive, user facing name of the mode */
bool isuser; /* whether this is a user or internal mode */
bool common_prefix; /* whether the first key in this mode is always the same */
void (*enter)(Mode *old); /* called right before the mode becomes active */
void (*leave)(Mode *new); /* called right before the mode becomes inactive */
- bool (*unknown)(KeyCombo); /* called whenever a key combination is not found in this mode,
- the return value determines whether parent modes will be searched */
void (*input)(const char*, size_t); /* called whenever a key is not found in this mode and all its parent modes */
void (*idle)(void); /* called whenever a certain idle time i.e. without any user input elapsed */
time_t idle_timeout; /* idle time in seconds after which the registered function will be called */
@@ -56,7 +52,7 @@ struct Mode {
typedef struct {
char *name; /* is used to match against argv[0] to enable this config */
Mode *mode; /* default mode in which the editor should start in */
- bool (*keypress)(Key*); /* called before any other keybindings are checked,
+ bool (*keypress)(const char *key); /* called before any other keybindings are checked,
* return value decides whether key should be ignored */
} Config;
@@ -237,6 +233,7 @@ struct Editor {
Map *cmds; /* ":"-commands, used for unique prefix queries */
Map *options; /* ":set"-options */
Buffer buffer_repeat; /* holds data to repeat last insertion/replacement */
+ Buffer input_queue; /* holds pending input keys */
Action action; /* current action which is in progress */
Action action_prev; /* last operator action used by the repeat '.' key */
@@ -256,6 +253,10 @@ void editor_draw(Editor*);
void editor_update(Editor*);
void editor_suspend(Editor*);
+bool editor_mode_bindings(Mode*, KeyBinding**);
+bool editor_mode_map(Mode*, const char *name, KeyBinding*);
+bool editor_mode_unmap(Mode*, const char *name);
+
/* these function operate on the currently focused window but make sure
* that all windows which show the affected region are redrawn too. */
void editor_insert_key(Editor*, const char *data, size_t len);
diff --git a/macro.h b/macro.h
index c91fcca..bd52646 100644
--- a/macro.h
+++ b/macro.h
@@ -6,6 +6,6 @@
typedef Buffer Macro;
#define macro_release buffer_release
#define macro_reset buffer_truncate
-#define macro_append buffer_append
+#define macro_append buffer_append0
#endif
diff --git a/ui-curses.c b/ui-curses.c
index 4705d08..b2fd099 100644
--- a/ui-curses.c
+++ b/ui-curses.c
@@ -3,6 +3,7 @@
#include <string.h>
#include <signal.h>
#include <locale.h>
+#include <poll.h>
#include <sys/ioctl.h>
#include "ui.h"
@@ -62,6 +63,8 @@ typedef struct {
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 */
+ TermKey *termkey; /* libtermkey instance to handle keyboard input */
+ char key[64]; /* string representation of last pressed key */
} UiCurses;
struct UiCursesWin {
@@ -518,6 +521,11 @@ static bool ui_init(Ui *ui, Editor *ed) {
return true;
}
+static TermKey *ui_termkey_get(Ui *ui) {
+ UiCurses *uic = (UiCurses*)ui;
+ return uic->termkey;
+}
+
static void ui_suspend(Ui *ui) {
endwin();
raise(SIGSTOP);
@@ -532,59 +540,51 @@ static bool ui_haskey(Ui *ui) {
return c != ERR;
}
-static Key ui_getkey(Ui *ui) {
- Key key = { .str = "", .code = 0 };
- int keycode = getch(), cur = 0;
- if (keycode == ERR)
- return key;
-
- if (keycode >= KEY_MIN) {
- key.code = keycode;
- } else {
- key.str[cur++] = keycode;
- int len = 1;
- unsigned char keychar = keycode;
- if (ISASCII(keychar)) len = 1;
- else if (keychar == 0x1B || keychar >= 0xFC) len = 6;
- else if (keychar >= 0xF8) len = 5;
- else if (keychar >= 0xF0) len = 4;
- else if (keychar >= 0xE0) len = 3;
- else if (keychar >= 0xC0) len = 2;
- len = MIN(len, LENGTH(key.str));
-
- if (cur < len) {
- nodelay(stdscr, TRUE);
- for (int t; cur < len && (t = getch()) != ERR; cur++)
- key.str[cur] = t;
- nodelay(stdscr, FALSE);
- }
-
- if (len == 1) {
- switch (key.str[0]) {
- case 127:
- case CONTROL('H'):
- key.code = KEY_BACKSPACE;
- key.str[0] = '\0';
- break;
- }
- }
+static const char *ui_getkey(Ui *ui) {
+ UiCurses *uic = (UiCurses*)ui;
+ TermKeyKey key;
+ TermKeyResult ret = termkey_getkey(uic->termkey, &key);
+
+ if (ret == TERMKEY_RES_AGAIN) {
+ struct pollfd fd;
+ fd.fd = STDIN_FILENO;
+ fd.events = POLLIN;
+ if (poll(&fd, 1, termkey_get_waittime(uic->termkey)) == 0)
+ ret = termkey_getkey_force(uic->termkey, &key);
}
-
- return key;
+
+ if (ret != TERMKEY_RES_KEY)
+ return NULL;
+ termkey_strfkey(uic->termkey, uic->key, sizeof(uic->key), &key, TERMKEY_FORMAT_VIM);
+ return uic->key;
}
static void ui_terminal_save(Ui *ui) {
+ UiCurses *uic = (UiCurses*)ui;
+ termkey_stop(uic->termkey);
curs_set(1);
reset_shell_mode();
}
static void ui_terminal_restore(Ui *ui) {
+ UiCurses *uic = (UiCurses*)ui;
reset_prog_mode();
wclear(stdscr);
curs_set(0);
+ termkey_start(uic->termkey);
}
Ui *ui_curses_new(Color *colors) {
+
+ UiCurses *uic = calloc(1, sizeof(UiCurses));
+ Ui *ui = (Ui*)uic;
+ if (!uic)
+ return NULL;
+ if (!(uic->termkey = termkey_new(STDIN_FILENO, TERMKEY_FLAG_UTF8))) {
+ ui_curses_free(ui);
+ return NULL;
+ }
+
setlocale(LC_CTYPE, "");
if (!getenv("ESCDELAY"))
set_escdelay(50);
@@ -605,14 +605,10 @@ Ui *ui_curses_new(Color *colors) {
would clear the screen (overwrite it with an empty / unused stdscr */
refresh();
- UiCurses *uic = calloc(1, sizeof(UiCurses));
- Ui *ui = (Ui*)uic;
- if (!uic)
- return NULL;
-
*ui = (Ui) {
.init = ui_init,
.free = ui_curses_free,
+ .termkey_get = ui_termkey_get,
.suspend = ui_suspend,
.resume = ui_resize,
.resize = ui_resize,
@@ -660,6 +656,8 @@ void ui_curses_free(Ui *ui) {
while (uic->windows)
ui_window_free((UiWin*)uic->windows);
endwin();
+ if (uic->termkey)
+ termkey_destroy(uic->termkey);
free(uic);
}
diff --git a/ui-curses.h b/ui-curses.h
index a2e1dc6..b9b63b1 100644
--- a/ui-curses.h
+++ b/ui-curses.h
@@ -6,21 +6,6 @@
#include "syntax.h"
enum {
- UI_KEY_DOWN = KEY_DOWN,
- UI_KEY_UP = KEY_UP,
- UI_KEY_LEFT = KEY_LEFT,
- UI_KEY_RIGHT = KEY_RIGHT,
- UI_KEY_HOME = KEY_HOME,
- UI_KEY_BACKSPACE = KEY_BACKSPACE,
- UI_KEY_DELETE = KEY_DC,
- UI_KEY_PAGE_DOWN = KEY_NPAGE,
- UI_KEY_PAGE_UP = KEY_PPAGE,
- UI_KEY_END = KEY_END,
- UI_KEY_SHIFT_LEFT = KEY_SLEFT,
- UI_KEY_SHIFT_RIGHT = KEY_SRIGHT,
-};
-
-enum {
UI_COLOR_DEFAULT = -1,
UI_COLOR_BLACK = COLOR_BLACK,
UI_COLOR_RED = COLOR_RED,
diff --git a/ui.h b/ui.h
index 4f61ec1..797b73a 100644
--- a/ui.h
+++ b/ui.h
@@ -15,13 +15,9 @@ enum UiOption {
UI_OPTION_LINE_NUMBERS_RELATIVE = 1 << 1,
};
-typedef struct {
- char str[6]; /* UTF8 character or terminal escape code */
- int code; /* curses KEY_* constant */
-} Key;
-
#include <stdbool.h>
#include <stdarg.h>
+#include <termkey.h>
#include "text.h"
#include "view.h"
#include "editor.h"
@@ -44,10 +40,11 @@ struct Ui {
void (*update)(Ui*);
void (*suspend)(Ui*);
void (*resume)(Ui*);
- Key (*getkey)(Ui*);
+ const char* (*getkey)(Ui*);
bool (*haskey)(Ui*);
void (*terminal_save)(Ui*);
void (*terminal_restore)(Ui*);
+ TermKey* (*termkey_get)(Ui*);
};
struct UiWin {
diff --git a/vis.c b/vis.c
index c263a6c..c0e10e0 100644
--- a/vis.c
+++ b/vis.c
@@ -33,6 +33,7 @@
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <termkey.h>
#include "ui-curses.h"
#include "editor.h"
#include "text-util.h"
@@ -472,8 +473,8 @@ static bool vis_window_split(Win *win);
#include "config.h"
-static Key getkey(void);
-static void keypress(Key *key);
+static const char *getkey(void);
+static bool keypress(const char *key);
static void action_do(Action *a);
static bool exec_command(char type, const char *cmdline);
@@ -816,10 +817,10 @@ static void changelist(const Arg *arg) {
static Macro *key2macro(const Arg *arg) {
if (arg->i)
return &vis->macros[arg->i];
- Key key = getkey();
- if (key.str[0] >= 'a' && key.str[0] <= 'z')
- return &vis->macros[key.str[0] - 'a'];
- if (key.str[0] == '@')
+ const char *key = getkey();
+ if (key && key[0] >= 'a' && key[0] <= 'z')
+ return &vis->macros[key[0] - 'a'];
+ if (key && key[0] == '@')
return vis->last_recording;
return NULL;
}
@@ -828,7 +829,8 @@ static void macro_record(const Arg *arg) {
if (vis->recording) {
/* hack to remove last recorded key, otherwise upon replay
* we would start another recording */
- vis->recording->len -= sizeof(Key);
+ // XXX: HACK
+ vis->recording->len--;
vis->last_recording = vis->recording;
vis->recording = NULL;
} else {
@@ -843,8 +845,7 @@ static void macro_replay(const Arg *arg) {
Macro *macro = key2macro(arg);
if (!macro || macro == vis->recording)
return;
- for (size_t i = 0; i < macro->len; i += sizeof(Key))
- keypress((Key*)(macro->data + i));
+ keypress(macro->data);
}
static void suspend(const Arg *arg) {
@@ -983,13 +984,13 @@ static void cursors_remove(const Arg *arg) {
}
static void replace(const Arg *arg) {
- Key k = getkey();
- if (!k.str[0])
+ const char *key = getkey();
+ if (!key)
return;
action_reset(&vis->action_prev);
vis->action_prev.op = &ops[OP_REPEAT_REPLACE];
- buffer_put(&vis->buffer_repeat, k.str, strlen(k.str));
- editor_replace_key(vis, k.str, strlen(k.str));
+ buffer_put(&vis->buffer_repeat, key, strlen(key));
+ editor_replace_key(vis, key, strlen(key));
text_snapshot(vis->win->file->text);
}
@@ -1042,12 +1043,12 @@ static void changecase(const Arg *arg) {
}
static void movement_key(const Arg *arg) {
- Key k = getkey();
- if (!k.str[0]) {
+ const char *key = getkey();
+ if (!key) {
action_reset(&vis->action);
return;
}
- strncpy(vis->search_char, k.str, sizeof(vis->search_char));
+ strncpy(vis->search_char, key, sizeof(vis->search_char));
vis->last_totill = arg->i;
vis->action.movement = &moves[arg->i];
action_do(&vis->action);
@@ -1211,8 +1212,10 @@ static void prompt_backspace(const Arg *arg) {
static void insert_verbatim(const Arg *arg) {
int len = 0, count = 0, base;
Rune rune = 0;
- Key key = getkey();
- char buf[4], type = key.str[0];
+ const char *key = getkey();
+ if (!key)
+ return;
+ char buf[4], type = key[0];
switch (type) {
case 'o':
case 'O':
@@ -1243,14 +1246,16 @@ static void insert_verbatim(const Arg *arg) {
while (count-- > 0) {
key = getkey();
int v = 0;
- if (base == 8 && '0' <= key.str[0] && key.str[0] <= '7')
- v = key.str[0] - '0';
- else if ((base == 10 || base == 16) && '0' <= key.str[0] && key.str[0] <= '9')
- v = key.str[0] - '0';
- else if (base == 16 && 'a' <= key.str[0] && key.str[0] <= 'f')
- v = 10 + key.str[0] - 'a';
- else if (base == 16 && 'A' <= key.str[0] && key.str[0] <= 'F')
- v = 10 + key.str[0] - 'A';
+ if (!key)
+ break;
+ else if (base == 8 && '0' <= key[0] && key[0] <= '7')
+ v = key[0] - '0';
+ else if ((base == 10 || base == 16) && '0' <= key[0] && key[0] <= '9')
+ v = key[0] - '0';
+ else if (base == 16 && 'a' <= key[0] && key[0] <= 'f')
+ v = 10 + key[0] - 'a';
+ else if (base == 16 && 'A' <= key[0] && key[0] <= 'F')
+ v = 10 + key[0] - 'A';
else
break;
rune = rune * base + v;
@@ -2475,68 +2480,63 @@ static void die(const char *errstr, ...) {
exit(EXIT_FAILURE);
}
-static bool keymatch(Key *key0, Key *key1) {
- return (key0->str[0] && memcmp(key0->str, key1->str, sizeof(key1->str)) == 0) ||
- (key0->code && key0->code == key1->code);
-}
-
-static bool keyvalid(Key *k) {
- return k && (k->str[0] || k->code);
-}
+static bool keypress(const char *input) {
+ if (!input)
+ return true;
+ TermKey *termkey = vis->ui->termkey_get(vis->ui);
+ char *keys = strdup(input), *start = keys, *cur = keys, *end;
+ if (!keys)
+ return true;
-static KeyBinding *keybinding(Mode *mode, KeyCombo keys) {
- int combolen = 0;
- while (combolen < MAX_KEYS && keyvalid(&keys[combolen]))
- combolen++;
- for (; mode; mode = mode->parent) {
- if (mode->common_prefix && !keymatch(&keys[0], &mode->bindings->key[0]))
- continue;
- for (KeyBinding *kb = mode->bindings; kb && keyvalid(&kb->key[0]); kb++) {
- for (int k = 0; k < combolen; k++) {
- if (!keymatch(&keys[k], &kb->key[k]))
- break;
- if (k == combolen - 1)
- return kb;
- }
+ TermKeyKey key;
+ bool prefix = false;
+ KeyBinding *binding = NULL;
+
+ while (cur && *cur) {
+ /* first try to parse a special key of the form <Key> */
+ if (*cur == '<' && (end = (char*)termkey_strpkey(termkey, cur+1, &key, TERMKEY_FORMAT_VIM)) && *end == '>') {
+ end++;
+ } else if (!(end = (char*)termkey_strpkey(termkey, cur, &key, TERMKEY_FORMAT_VIM))) {
+ // XXX: insert as document
+ free(keys);
+ return true;
}
- if (mode->unknown && !mode->unknown(keys))
- break;
- }
- return NULL;
-}
-
-static void keypress(Key *key) {
- static KeyCombo keys;
- static int keylen;
-
-
- keys[keylen++] = *key;
- KeyBinding *action = keybinding(vis->mode, keys);
- if (action) {
- int combolen = 0;
- while (combolen < MAX_KEYS && keyvalid(&action->key[combolen]))
- combolen++;
- if (keylen < combolen)
- return; /* combo not yet complete */
- /* need to reset state before calling action->func in case
- * it will call us (=keypress) again as e.g. macro_replay */
- keylen = 0;
- memset(keys, 0, sizeof(keys));
- if (action->func)
- action->func(&action->arg);
- } else if (keylen == 1 && key->code == 0 && vis->mode->input) {
- vis->mode->input(key->str, strlen(key->str));
+ char tmp = *end;
+ *end = '\0';
+ prefix = false;
+ binding = NULL;
+
+ for (Mode *mode = vis->mode; mode && !binding && !prefix; mode = mode->parent) {
+ binding = map_get(mode->bindings, start);
+ /* "<" is never treated as a prefix because it is used to denote
+ * special key symbols */
+ if (strcmp(cur, "<"))
+ prefix = !binding && map_contains(mode->bindings, start);
+ }
+
+ if (binding) { /* exact match */
+ binding->func(&binding->arg);
+ start = cur = end;
+ } else if (prefix) { /* incomplete key binding? */
+ cur = end;
+ } else { /* no keybinding */
+ if (vis->mode->input)
+ vis->mode->input(start, end - start);
+ start = cur = end;
+ }
+
+ *end = tmp;
}
- keylen = 0;
- memset(keys, 0, sizeof(keys));
+ free(keys);
+ return !prefix;
}
-static Key getkey(void) {
- Key key = vis->ui->getkey(vis->ui);
- if (config->keypress && !config->keypress(&key))
- return (Key){ .str = "", .code = 0 };
+static const char *getkey(void) {
+ const char *key = vis->ui->getkey(vis->ui);
+ if (!key || (config->keypress && !config->keypress(key)))
+ return NULL;
return key;
}
@@ -2609,8 +2609,14 @@ static void mainloop() {
continue;
}
- Key key = getkey();
- keypress(&key);
+
+ TermKey *termkey = vis->ui->termkey_get(vis->ui);
+ termkey_advisereadable(termkey);
+ const char *key;
+ while ((key = getkey())) {
+ if (buffer_append0(&vis->input_queue, key) && keypress(vis->input_queue.data))
+ buffer_truncate(&vis->input_queue);
+ }
if (vis->mode->idle)
timeout = &idle;
@@ -2630,6 +2636,12 @@ int main(int argc, char *argv[]) {
}
}
+ for (int i = 0; i < LENGTH(vis_modes); i++) {
+ Mode *mode = &vis_modes[i];
+ if (!editor_mode_bindings(mode, &mode->default_bindings))
+ die("Could not load bindings for mode: %s\n", mode->name);
+ }
+
if (!(vis = editor_new(ui_curses_new(colors))))
die("Could not allocate editor core\n");