diff options
| author | Marc André Tanner <mat@brain-dump.org> | 2017-03-21 15:14:43 +0100 |
|---|---|---|
| committer | Marc André Tanner <mat@brain-dump.org> | 2017-03-21 15:38:27 +0100 |
| commit | 8c283071dc0e51acc868015fe7ae79ab8edbdf85 (patch) | |
| tree | 353626b1340d5c4a6d6576ba64368d2411b084a5 | |
| parent | 937d8a83610ac4a642d52412e94bebda3ed6b138 (diff) | |
| download | vis-8c283071dc0e51acc868015fe7ae79ab8edbdf85.tar.gz vis-8c283071dc0e51acc868015fe7ae79ab8edbdf85.tar.xz | |
vis: make `cw` and `cW` more vim compatible
If the starting position is:
* on a space or tab use the `w` motion
* on the last letter of a word use `l` or `e` depending on whether
a count was given. This also applies for single letter words.
* otherwise use the `e` motion
As in vim `cw` and `dw` behave differently, whether that is desirable
remains to be seen.
Might fix #521
| -rw-r--r-- | vis-motions.c | 57 | ||||
| -rw-r--r-- | vis.h | 2 |
2 files changed, 57 insertions, 2 deletions
diff --git a/vis-motions.c b/vis-motions.c index f8baf1d..3e7f269 100644 --- a/vis-motions.c +++ b/vis-motions.c @@ -56,6 +56,51 @@ static size_t search_backward(Vis *vis, Text *txt, size_t pos) { return pos; } +static size_t common_word_next(Vis *vis, Text *txt, size_t pos, enum VisMotion end_next) { + char c; + Iterator it = text_iterator_get(txt, pos); + if (!text_iterator_byte_get(&it, &c)) + return pos; + const Movement *motion = NULL; + int count = vis_count_get_default(vis, 1); + if (c == ' ' || c == '\t') { + motion = &vis_motions[VIS_MOVE_WORD_START_NEXT]; + } else if (text_iterator_char_next(&it, &c) && (c == ' ' || c == '\t')) { + /* we are on the last character of a word */ + if (count == 1) { + /* map `cw` to `cl` */ + motion = &vis_motions[VIS_MOVE_CHAR_NEXT]; + } else { + /* map `c{n}w` to `c{n-1}e` */ + count--; + motion = &vis_motions[end_next]; + } + } else { + /* map `cw` to `ce` */ + motion = &vis_motions[end_next]; + } + + while (count--) { + size_t newpos = motion->txt(txt, pos); + if (newpos == pos) + break; + pos = newpos; + } + + if (motion->type & INCLUSIVE) + pos = text_char_next(txt, pos); + + return pos; +} + +static size_t word_next(Vis *vis, Text *txt, size_t pos) { + return common_word_next(vis, txt, pos, VIS_MOVE_WORD_END_NEXT); +} + +static size_t longword_next(Vis *vis, Text *txt, size_t pos) { + return common_word_next(vis, txt, pos, VIS_MOVE_LONGWORD_END_NEXT); +} + static size_t mark_goto(Vis *vis, File *file, size_t pos) { return text_mark_get(file->text, file->marks[vis->action.mark]); } @@ -266,11 +311,11 @@ bool vis_motion(Vis *vis, enum VisMotion motion, ...) { switch (motion) { case VIS_MOVE_WORD_START_NEXT: if (vis->action.op == &vis_operators[VIS_OP_CHANGE]) - motion = VIS_MOVE_WORD_END_NEXT; + motion = VIS_MOVE_WORD_NEXT; break; case VIS_MOVE_LONGWORD_START_NEXT: if (vis->action.op == &vis_operators[VIS_OP_CHANGE]) - motion = VIS_MOVE_LONGWORD_END_NEXT; + motion = VIS_MOVE_LONGWORD_NEXT; break; case VIS_MOVE_SEARCH_FORWARD: case VIS_MOVE_SEARCH_BACKWARD: @@ -427,6 +472,10 @@ const Movement vis_motions[] = { .txt = text_line_char_next, .type = CHARWISE, }, + [VIS_MOVE_WORD_NEXT] = { + .vis = word_next, + .type = CHARWISE|IDEMPOTENT, + }, [VIS_MOVE_WORD_START_PREV] = { .txt = text_word_start_prev, .type = CHARWISE, @@ -443,6 +492,10 @@ const Movement vis_motions[] = { .txt = text_word_end_next, .type = CHARWISE|INCLUSIVE, }, + [VIS_MOVE_LONGWORD_NEXT] = { + .vis = longword_next, + .type = CHARWISE|IDEMPOTENT, + }, [VIS_MOVE_LONGWORD_START_PREV] = { .txt = text_longword_start_prev, .type = CHARWISE, @@ -268,10 +268,12 @@ enum VisMotion { VIS_MOVE_CHAR_NEXT, VIS_MOVE_LINE_CHAR_PREV, VIS_MOVE_LINE_CHAR_NEXT, + VIS_MOVE_WORD_NEXT, VIS_MOVE_WORD_START_NEXT, VIS_MOVE_WORD_END_PREV, VIS_MOVE_WORD_END_NEXT, VIS_MOVE_WORD_START_PREV, + VIS_MOVE_LONGWORD_NEXT, VIS_MOVE_LONGWORD_START_PREV, VIS_MOVE_LONGWORD_START_NEXT, VIS_MOVE_LONGWORD_END_PREV, |
