vis
a vi-like editor based on Plan 9's structural regular expressions
git clone https://9o.is/git/vis.git
commit 1b7e6348ae9b51bae5ae4faf1a0a5f1752652a03 parent 382a3e8512520e2c60e79ba63ba7038a43925817 Author: Marc André Tanner <mat@brain-dump.org> Date: Tue, 21 Mar 2017 15:14:43 +0100 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 Diffstat:
| M | vis-motions.c | | | 57 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
| M | vis.h | | | 2 | ++ |
2 files changed, 57 insertions(+), 2 deletions(-)
diff --git 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, diff --git a/vis.h b/vis.h @@ -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,