diff options
| author | Marc André Tanner <mat@brain-dump.org> | 2014-08-31 11:07:42 +0200 |
|---|---|---|
| committer | Marc André Tanner <mat@brain-dump.org> | 2014-08-31 11:07:42 +0200 |
| commit | c72fe77e5d3cf4ecb3b691a5fa582d0e96b5ec1e (patch) | |
| tree | 6b5487c3384e8c35c5b531fffca1f2b2bacc193d | |
| parent | fbaeffb469d2fd6833a1301255f1ac73c7226a83 (diff) | |
| download | vis-c72fe77e5d3cf4ecb3b691a5fa582d0e96b5ec1e.tar.gz vis-c72fe77e5d3cf4ecb3b691a5fa582d0e96b5ec1e.tar.xz | |
First part of a vi like frontend
Still very incomplete
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | config.def.h | 413 | ||||
| -rw-r--r-- | editor.c | 15 | ||||
| -rw-r--r-- | editor.h | 3 | ||||
| -rw-r--r-- | vis.c | 36 |
5 files changed, 363 insertions, 108 deletions
@@ -1,7 +1,7 @@ include config.mk -SRC += vis.c colors.c editor.c text.c text-motions.c text-objects.c -ALL = ${SRC} util.h editor.h text.h text-motions.h text-objects.h \ +SRC += vis.c colors.c editor.c text.c text-motions.c text-objects.c register.c +ALL = ${SRC} util.h editor.h text.h text-motions.h text-objects.h register.h \ config.def.h config.mk Makefile LICENSE README vis.1 OBJ = ${SRC:.c=.o} diff --git a/config.def.h b/config.def.h index eef2d55..66e3044 100644 --- a/config.def.h +++ b/config.def.h @@ -3,11 +3,137 @@ #define KEY(k) { .str = { '\0' }, .code = KEY_##k } #define CONTROL(k) NONE((k)&0x1F) #define META(k) { .str = { ESC, (k) }, .code = 0 } -#define BACKSPACE(func, arg) \ - { { KEY(BACKSPACE) }, (func), { .m = (arg) } }, \ - { { CONTROL('H') }, (func), { .m = (arg) } }, \ - { { NONE(127) }, (func), { .m = (arg) } }, \ - { { CONTROL('B') }, (func), { .m = (arg) } } +#define BACKSPACE(func, name, arg) \ + { { KEY(BACKSPACE) }, (func), { .name = (arg) } }, \ + { { CONTROL('H') }, (func), { .name = (arg) } }, \ + { { NONE(127) }, (func), { .name = (arg) } }, \ + { { CONTROL('B') }, (func), { .name = (arg) } } + +static void switchmode(const Arg *arg); + +enum { + VIS_MODE_BASIC, + VIS_MODE_MOVE, + VIS_MODE_TEXTOBJ, + VIS_MODE_OPERATOR, + VIS_MODE_NORMAL, + VIS_MODE_VISUAL, + VIS_MODE_INSERT, + VIS_MODE_REPLACE, +}; + +enum { + OP_DELETE, + OP_CHANGE, + OP_YANK, +}; + +void op_delete(OperatorContext *c) { + if (c->range.start == (size_t)-1) + return; + size_t len = c->range.end - c->range.start; + text_delete(editor_text_get(editor), c->range.start, len); + if (c->pos > c->range.start) + editor_cursor_to(editor, c->range.start); + editor_draw(editor); +} + +void op_change(OperatorContext *c) { + op_delete(c); + switchmode(&(const Arg){ .i = VIS_MODE_INSERT }); +} + +void op_yank(OperatorContext *c) {} + +static Operator *ops[] = { + [OP_DELETE] = op_delete, + [OP_CHANGE] = op_change, + [OP_YANK] = op_yank, +}; + +enum { + MOVE_CHAR_PREV, + MOVE_CHAR_NEXT, + MOVE_LINE_UP, + MOVE_LINE_DOWN, + MOVE_LINE_BEGIN, + MOVE_LINE_START, + MOVE_LINE_FINISH, + MOVE_LINE_END, + MOVE_WORD_START_PREV, + MOVE_WORD_START_NEXT, + MOVE_WORD_END_PREV, + MOVE_WORD_END_NEXT, + MOVE_SENTENCE_PREV, + MOVE_SENTENCE_NEXT, + MOVE_PARAGRAPH_PREV, + MOVE_PARAGRAPH_NEXT, + MOVE_BRACKET_MATCH, + MOVE_FILE_BEGIN, + MOVE_FILE_END, +}; + +static Movement moves[] = { + [MOVE_CHAR_PREV] = { .win = editor_char_prev }, + [MOVE_CHAR_NEXT] = { .win = editor_char_next }, + [MOVE_LINE_UP] = { .win = editor_line_up }, + [MOVE_LINE_DOWN] = { .win = editor_line_down }, + [MOVE_LINE_BEGIN] = { .txt = text_line_begin, .type = LINEWISE }, + [MOVE_LINE_START] = { .txt = text_line_start, .type = LINEWISE }, + [MOVE_LINE_FINISH] = { .txt = text_line_finish, .type = LINEWISE }, + [MOVE_LINE_END] = { .txt = text_line_end, .type = LINEWISE }, + [MOVE_WORD_START_PREV] = { .txt = text_word_start_prev, .type = CHARWISE }, + [MOVE_WORD_START_NEXT] = { .txt = text_word_start_next, .type = CHARWISE }, + [MOVE_WORD_END_PREV] = { .txt = text_word_end_prev, .type = CHARWISE|INCLUSIVE }, + [MOVE_WORD_END_NEXT] = { .txt = text_word_end_next, .type = CHARWISE|INCLUSIVE }, + [MOVE_SENTENCE_PREV] = { .txt = text_sentence_prev, .type = LINEWISE }, + [MOVE_SENTENCE_NEXT] = { .txt = text_sentence_next, .type = LINEWISE }, + [MOVE_PARAGRAPH_PREV] = { .txt = text_paragraph_prev, .type = LINEWISE }, + [MOVE_PARAGRAPH_NEXT] = { .txt = text_paragraph_next, .type = LINEWISE }, + [MOVE_BRACKET_MATCH] = { .txt = text_bracket_match, .type = LINEWISE|INCLUSIVE }, + [MOVE_FILE_BEGIN] = { .txt = text_begin, .type = LINEWISE }, + [MOVE_FILE_END] = { .txt = text_end, .type = LINEWISE }, +}; + +enum { + TEXT_OBJ_WORD, + TEXT_OBJ_SENTENCE, + TEXT_OBJ_PARAGRAPH, + TEXT_OBJ_OUTER_SQUARE_BRACKET, + TEXT_OBJ_INNER_SQUARE_BRACKET, + TEXT_OBJ_OUTER_CURLY_BRACKET, + TEXT_OBJ_INNER_CURLY_BRACKET, + TEXT_OBJ_OUTER_ANGLE_BRACKET, + TEXT_OBJ_INNER_ANGLE_BRACKET, + TEXT_OBJ_OUTER_PARANTHESE, + TEXT_OBJ_INNER_PARANTHESE, + TEXT_OBJ_OUTER_QUOTE, + TEXT_OBJ_INNER_QUOTE, + TEXT_OBJ_OUTER_SINGLE_QUOTE, + TEXT_OBJ_INNER_SINGLE_QUOTE, + TEXT_OBJ_OUTER_BACKTICK, + TEXT_OBJ_INNER_BACKTICK, +}; + +static TextObject textobjs[] = { + [TEXT_OBJ_WORD] = { text_object_word }, + [TEXT_OBJ_SENTENCE] = { text_object_sentence }, + [TEXT_OBJ_PARAGRAPH] = { text_object_paragraph }, + [TEXT_OBJ_OUTER_SQUARE_BRACKET] = { text_object_square_bracket, OUTER }, + [TEXT_OBJ_INNER_SQUARE_BRACKET] = { text_object_square_bracket, INNER }, + [TEXT_OBJ_OUTER_CURLY_BRACKET] = { text_object_curly_bracket, OUTER }, + [TEXT_OBJ_INNER_CURLY_BRACKET] = { text_object_curly_bracket, INNER }, + [TEXT_OBJ_OUTER_ANGLE_BRACKET] = { text_object_angle_bracket, OUTER }, + [TEXT_OBJ_INNER_ANGLE_BRACKET] = { text_object_angle_bracket, INNER }, + [TEXT_OBJ_OUTER_PARANTHESE] = { text_object_paranthese, OUTER }, + [TEXT_OBJ_INNER_PARANTHESE] = { text_object_paranthese, INNER }, + [TEXT_OBJ_OUTER_QUOTE] = { text_object_quote, OUTER }, + [TEXT_OBJ_INNER_QUOTE] = { text_object_quote, INNER }, + [TEXT_OBJ_OUTER_SINGLE_QUOTE] = { text_object_single_quote, OUTER }, + [TEXT_OBJ_INNER_SINGLE_QUOTE] = { text_object_single_quote, INNER }, + [TEXT_OBJ_OUTER_BACKTICK] = { text_object_backtick, OUTER }, + [TEXT_OBJ_INNER_BACKTICK] = { text_object_backtick, INNER }, +}; /* draw a statubar, do whatever you want with the given curses window */ static void statusbar(WINDOW *win, bool active, const char *filename, int line, int col) { @@ -25,16 +151,6 @@ static void statusbar(WINDOW *win, bool active, const char *filename, int line, } } -static void switchmode(const Arg *arg); -enum { - VIS_MODE_BASIC, - VIS_MODE_MOVE, - VIS_MODE_NORMAL, - VIS_MODE_VISUAL, - VIS_MODE_INSERT, - VIS_MODE_REPLACE, -}; - void quit(const Arg *arg) { endwin(); exit(0); @@ -51,6 +167,81 @@ static void mark_set(const Arg *arg) { static void mark_goto(const Arg *arg) { editor_mark_goto(editor, arg->i); } + +static Action action; +void action_do(Action *a); + +static void count(const Arg *arg) { + action.count = action.count * 10 + arg->i; +} + +static void operator(const Arg *arg) { + action.op = ops[arg->i]; +} + +static bool operator_unknown(Key *key1, Key *key2) { + action.op = NULL; + return true; +} + +static void movement(const Arg *arg) { + action.movement = &moves[arg->i]; + action_do(&action); +} + +static void textobj(const Arg *arg) { + action.textobj = &textobjs[arg->i]; + action_do(&action); +} + +void action_reset(Action *a) { + a->count = 0; + a->op = NULL; + a->movement = NULL; + a->textobj = NULL; + a->reg = NULL; +} + +void action_do(Action *a) { + Text *txt = editor_text_get(editor); + OperatorContext c; + size_t pos = editor_cursor_get(editor); + c.pos = pos; + if (a->count == 0) + a->count = 1; + if (a->movement) { + size_t start = pos; + for (int i = 0; i < a->count; i++) { + if (a->movement->txt) + pos = a->movement->txt(txt, pos); + else + pos = a->movement->win(editor); + } + c.range.start = MIN(start, pos); + c.range.end = MAX(start, pos); + if (!a->op) { + if (a->movement->type & CHARWISE) + editor_scroll_to(editor, pos); + else + editor_cursor_to(editor, pos); + } else if (a->movement->type & INCLUSIVE) { + Iterator it = text_iterator_get(txt, c.range.end); + text_iterator_char_next(&it, NULL); + c.range.end = it.pos; + } + } else if (a->textobj) { + c.range = a->textobj->range(txt, pos); + if (c.range.start != (size_t)-1 && a->textobj->type == OUTER) { + c.range.start--; + c.range.end++; + } + } + c.count = a->count; + if (a->op) + a->op(&c); + action_reset(a); +} + /* use vim's :help motion :h operator @@ -58,57 +249,133 @@ static void mark_goto(const Arg *arg) { as reference */ -static KeyBinding movement[] = { - { { KEY(LEFT) }, cursor, { .m = editor_char_prev } }, - { { KEY(RIGHT) }, cursor, { .m = editor_char_next } }, - { { KEY(UP) }, cursor, { .m = editor_line_up } }, - { { KEY(DOWN) }, cursor, { .m = editor_line_down } }, +static KeyBinding basic_movement[] = { + { { KEY(LEFT) }, movement, { .i = MOVE_CHAR_PREV } }, + { { KEY(RIGHT) }, movement, { .i = MOVE_CHAR_NEXT } }, + { { KEY(UP) }, movement, { .i = MOVE_LINE_UP } }, + { { KEY(DOWN) }, movement, { .i = MOVE_LINE_DOWN } }, { { KEY(PPAGE) }, cursor, { .m = editor_page_up } }, { { KEY(NPAGE) }, cursor, { .m = editor_page_down } }, - { { KEY(HOME) }, cursor, { .m = editor_line_start } }, - { { KEY(END) }, cursor, { .m = editor_line_end } }, + { { KEY(HOME) }, movement, { .i = MOVE_LINE_START } }, + { { KEY(END) }, movement, { .i = MOVE_LINE_FINISH } }, // temporary until we have a way to enter user commands { { CONTROL('c') }, quit, }, { /* empty last element, array terminator */ }, }; -static KeyBinding vis_movement[] = { - BACKSPACE( cursor, editor_char_prev ), - { { NONE(' ') }, cursor, { .m = editor_char_next } }, +#if 0 +static KeyBinding vis_commands[] = { + // DEMO STUFF + { { NONE('5') }, line, { .i = 50 } }, + { { NONE('s') }, mark_set, { .i = 0 } }, + { { NONE('9') }, mark_goto, { .i = 0 } }, + + { /* empty last element, array terminator */ }, +}; +#endif + +static KeyBinding vis_movements[] = { + 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('j') }, movement, { .i = MOVE_LINE_DOWN } }, + { { CONTROL('J') }, movement, { .i = MOVE_LINE_DOWN } }, + { { CONTROL('N') }, movement, { .i = MOVE_LINE_DOWN } }, + { { KEY(ENTER) }, movement, { .i = MOVE_LINE_DOWN } }, + { { NONE('0') }, movement, { .i = MOVE_LINE_BEGIN } }, + { { NONE('^') }, movement, { .i = MOVE_LINE_START } }, + { { NONE('g'), NONE('_') }, movement, { .i = MOVE_LINE_FINISH } }, + { { NONE('$') }, movement, { .i = MOVE_LINE_END } }, + { { NONE('%') }, movement, { .i = MOVE_BRACKET_MATCH } }, + { { NONE('b') }, movement, { .i = MOVE_WORD_START_PREV } }, + { { KEY(SLEFT) }, movement, { .i = MOVE_WORD_START_PREV } }, + { { NONE('w') }, movement, { .i = MOVE_WORD_START_NEXT } }, + { { KEY(SRIGHT) }, movement, { .i = MOVE_WORD_START_NEXT } }, + { { NONE('g'), NONE('e') }, movement, { .i = MOVE_WORD_END_PREV } }, + { { NONE('e') }, movement, { .i = MOVE_WORD_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('g'), NONE('g') }, movement, { .i = MOVE_FILE_BEGIN } }, + { { NONE('G') }, movement, { .i = MOVE_FILE_END } }, + { /* empty last element, array terminator */ }, +}; + +// TODO: factor out prefix [ia] into spearate mode which sets a flag +static KeyBinding vis_textobjs[] = { + { { NONE('a'), NONE('w') }, textobj, { .i = TEXT_OBJ_WORD } }, + { { NONE('i'), NONE('w') }, textobj, { .i = TEXT_OBJ_WORD } }, + { { NONE('a'), NONE('s') }, textobj, { .i = TEXT_OBJ_SENTENCE } }, + { { NONE('i'), NONE('s') }, textobj, { .i = TEXT_OBJ_SENTENCE } }, + { { NONE('a'), NONE('p') }, textobj, { .i = TEXT_OBJ_PARAGRAPH } }, + { { NONE('i'), 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('i'), NONE('[') }, textobj, { .i = TEXT_OBJ_INNER_SQUARE_BRACKET } }, + { { NONE('i'), NONE(']') }, textobj, { .i = TEXT_OBJ_INNER_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('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('a'), NONE('<') }, textobj, { .i = TEXT_OBJ_OUTER_ANGLE_BRACKET } }, + { { NONE('a'), NONE('>') }, textobj, { .i = TEXT_OBJ_OUTER_ANGLE_BRACKET } }, + { { NONE('i'), NONE('<') }, textobj, { .i = TEXT_OBJ_INNER_ANGLE_BRACKET } }, + { { NONE('i'), NONE('>') }, textobj, { .i = TEXT_OBJ_INNER_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('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('a'), NONE('"') }, textobj, { .i = TEXT_OBJ_OUTER_QUOTE } }, + { { NONE('i'), NONE('"') }, textobj, { .i = TEXT_OBJ_INNER_QUOTE } }, + { { NONE('a'), NONE('\'') }, textobj, { .i = TEXT_OBJ_OUTER_SINGLE_QUOTE } }, + { { NONE('i'), NONE('\'') }, textobj, { .i = TEXT_OBJ_INNER_SINGLE_QUOTE } }, + { { NONE('a'), NONE('`') }, textobj, { .i = TEXT_OBJ_OUTER_BACKTICK } }, + { { NONE('i'), NONE('`') }, textobj, { .i = TEXT_OBJ_INNER_BACKTICK } }, +}; + +static KeyBinding vis_operators[] = { + { { 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 } }, + { /* empty last element, array terminator */ }, +}; + +static KeyBinding vis_registers[] = { /* {a-zA-Z0-9.%#:-"} */ +// { { NONE('"'), NONE('a') }, reg, { .i = 1 } }, + { /* empty last element, array terminator */ }, +}; + +static KeyBinding vis_marks[] = { /* {a-zA-Z} */ +// { { NONE('`'), NONE('a') }, mark, { .i = 1 } }, +// { { NONE('\''), NONE('a') }, mark, { .i = 1 } }, + { /* empty last element, array terminator */ }, +}; + +static KeyBinding vis_normal[] = { { { CONTROL('w'), NONE('c') }, split, { .s = NULL } }, { { CONTROL('w'), NONE('j') }, call, { .f = editor_window_next } }, { { CONTROL('w'), NONE('k') }, call, { .f = editor_window_prev } }, - { { NONE('h') }, cursor, { .m = editor_char_prev } }, - { { NONE('l') }, cursor, { .m = editor_char_next } }, - { { NONE('k') }, cursor, { .m = editor_line_up } }, - { { CONTROL('P') }, cursor, { .m = editor_line_up } }, - { { NONE('j') }, cursor, { .m = editor_line_down } }, - { { CONTROL('J') }, cursor, { .m = editor_line_down } }, - { { CONTROL('N') }, cursor, { .m = editor_line_down } }, - { { KEY(ENTER) }, cursor, { .m = editor_line_down } }, - { { NONE('0') }, cursor, { .m = editor_line_begin } }, - { { NONE('^') }, cursor, { .m = editor_line_start } }, - { { NONE('g'), NONE('_') }, cursor, { .m = editor_line_finish } }, - { { NONE('$') }, cursor, { .m = editor_line_end } }, { { CONTROL('F') }, cursor, { .m = editor_page_up } }, { { CONTROL('B') }, cursor, { .m = editor_page_down } }, - { { NONE('%') }, cursor, { .m = editor_bracket_match } }, - { { NONE('b') }, cursor, { .m = editor_word_start_prev } }, - { { KEY(SLEFT) }, cursor, { .m = editor_word_start_prev } }, - { { NONE('w') }, cursor, { .m = editor_word_start_next } }, - { { KEY(SRIGHT) }, cursor, { .m = editor_word_start_next } }, - { { NONE('g'), NONE('e') }, cursor, { .m = editor_word_end_prev } }, - { { NONE('e') }, cursor, { .m = editor_word_end_next } }, - { { NONE('}') }, cursor, { .m = editor_paragraph_next } }, - { { NONE('{') }, cursor, { .m = editor_paragraph_prev } }, - { { NONE(')') }, cursor, { .m = editor_sentence_next } }, - { { NONE('(') }, cursor, { .m = editor_sentence_prev } }, - { { NONE('g'), NONE('g') }, cursor, { .m = editor_file_begin } }, - { { NONE('G') }, cursor, { .m = editor_file_end } }, - { /* empty last element, array terminator */ }, -}; - -static KeyBinding vis_normal[] = { + { { NONE('n') }, find_forward, { .s = "if" } }, + { { NONE('p') }, find_backward, { .s = "if" } }, { { NONE('x') }, cursor, { .m = editor_delete } }, { { NONE('i') }, switchmode, { .i = VIS_MODE_INSERT } }, { { NONE('v') }, switchmode, { .i = VIS_MODE_VISUAL } }, @@ -116,12 +383,6 @@ static KeyBinding vis_normal[] = { { { NONE('u') }, call, { .f = editor_undo } }, { { CONTROL('R') }, call, { .f = editor_redo } }, { { CONTROL('L') }, call, { .f = editor_draw } }, - // DEMO STUFF - { { NONE('n') }, find_forward, { .s = "if" } }, - { { NONE('p') }, find_backward, { .s = "if" } }, - { { NONE('5') }, line, { .i = 50 } }, - { { NONE('s') }, mark_set, { .i = 0 } }, - { { NONE('9') }, mark_goto, { .i = 0 } }, { /* empty last element, array terminator */ }, }; @@ -139,10 +400,10 @@ static void vis_visual_leave(void) { } static KeyBinding vis_insert[] = { - { { NONE(ESC) }, switchmode, { .i = VIS_MODE_NORMAL } }, - { { CONTROL('D') }, cursor, { .m = editor_delete } }, - BACKSPACE( cursor, editor_backspace ), - { /* empty last element, array terminator */ }, + { { NONE(ESC) }, switchmode, { .i = VIS_MODE_NORMAL } }, + { { CONTROL('D') }, cursor, { .m = editor_delete } }, + BACKSPACE( cursor, m, editor_backspace ), + { /* empty last element, array terminator */ }, }; static bool vis_insert_input(const char *str, size_t len) { @@ -152,8 +413,6 @@ static bool vis_insert_input(const char *str, size_t len) { static KeyBinding vis_replace[] = { { { NONE(ESC) }, switchmode, { .i = VIS_MODE_NORMAL } }, - { { CONTROL('D') }, cursor, { .m = editor_delete } }, - BACKSPACE( cursor, editor_backspace ), { /* empty last element, array terminator */ }, }; @@ -165,19 +424,27 @@ static bool vis_replace_input(const char *str, size_t len) { static Mode vis[] = { [VIS_MODE_BASIC] = { .parent = NULL, - .bindings = movement, + .bindings = basic_movement, }, [VIS_MODE_MOVE] = { .parent = &vis[VIS_MODE_BASIC], - .bindings = vis_movement, + .bindings = vis_movements, }, - [VIS_MODE_NORMAL] = { + [VIS_MODE_TEXTOBJ] = { .parent = &vis[VIS_MODE_MOVE], + .bindings = vis_textobjs, + }, + [VIS_MODE_OPERATOR] = { + .parent = &vis[VIS_MODE_MOVE], + .bindings = vis_operators, + }, + [VIS_MODE_NORMAL] = { + .parent = &vis[VIS_MODE_OPERATOR], .bindings = vis_normal, }, [VIS_MODE_VISUAL] = { .name = "VISUAL", - .parent = &vis[VIS_MODE_MOVE], + .parent = &vis[VIS_MODE_OPERATOR], .bindings = vis_visual, .enter = vis_visual_enter, .leave = vis_visual_leave, @@ -190,7 +457,7 @@ static Mode vis[] = { }, [VIS_MODE_REPLACE] = { .name = "REPLACE", - .parent = &vis[VIS_MODE_BASIC], + .parent = &vis[VIS_MODE_INSERT], .bindings = vis_replace, .input = vis_replace_input, }, @@ -234,7 +501,7 @@ XXX: CONTROL(' ') = 0, ^Space Go forward one word #if 1 static KeyBinding nano_keys[] = { { { CONTROL('D') }, cursor, { .m = editor_delete } }, - BACKSPACE( cursor, editor_backspace ), + BACKSPACE( cursor, m, editor_backspace ), { { KEY(LEFT) }, cursor, { .m = editor_char_prev } }, { { KEY(RIGHT) }, cursor, { .m = editor_char_next } }, { { CONTROL('F') }, cursor, { .m = editor_char_next } }, @@ -270,7 +537,7 @@ static KeyBinding nano_keys[] = { #endif static Mode nano[] = { - { .parent = NULL, .bindings = movement, }, + { .parent = NULL, .bindings = basic_movement, }, { .parent = &nano[0], .bindings = nano_keys, .input = vis_insert_input, }, }; @@ -972,6 +972,21 @@ size_t editor_cursor_get(Editor *ed) { return ed->win->cursor.pos; } +Text *editor_text_get(Editor *ed) { + return ed->win->text; +} + +void editor_scroll_to(Editor *ed, size_t pos) { + Win *win = ed->win; + while (pos < win->start && scroll_line_up(win, 1)); + while (pos > win->end && scroll_line_down(win, 1)); + cursor_move_to(win, pos); +} + +void editor_cursor_to(Editor *ed, size_t pos) { + cursor_move_to(ed->win, pos); +} + size_t editor_selection_start(Editor *ed) { return ed->win->sel.start = editor_cursor_get(ed); } @@ -84,6 +84,9 @@ bool editor_syntax_load(Editor*, Syntax *syntaxes, Color *colors); void editor_syntax_unload(Editor*); size_t editor_cursor_get(Editor*); +void editor_scroll_to(Editor*, size_t pos); +void editor_cursor_to(Editor*, size_t pos); +Text *editor_text_get(Editor*); // TODO void return type? size_t editor_selection_start(Editor*); size_t editor_selection_end(Editor*); @@ -9,6 +9,7 @@ #include <sys/ioctl.h> #include "editor.h" +#include "vis.h" #include "util.h" #ifdef PDCURSES @@ -18,39 +19,6 @@ int ESCDELAY; # define set_escdelay(d) (ESCDELAY = (d)) #endif -typedef union { - size_t i; - const char *s; - size_t (*m)(Editor*); - void (*f)(Editor*); -} Arg; - -typedef struct { - char str[6]; - int code; -} Key; - -typedef struct { - Key key[2]; - void (*func)(const Arg *arg); - const Arg arg; -} KeyBinding; - -typedef struct Mode Mode; -struct Mode { - Mode *parent; - KeyBinding *bindings; - const char *name; - void (*enter)(void); - void (*leave)(void); - bool (*input)(const char *str, size_t len); -}; - -typedef struct { - char *name; - Mode *mode; -} Config; - static void cursor(const Arg *arg); static void call(const Arg *arg); static void insert(const Arg *arg); @@ -152,6 +120,8 @@ static KeyBinding *keybinding(Mode *mode, Key *key0, Key *key1) { if (keymatch(key0, &kb->key[0]) && (!key1 || keymatch(key1, &kb->key[1]))) return kb; } + if (mode->unknown && !mode->unknown(key0, key1)) + break; } return NULL; } |
