From 8c283071dc0e51acc868015fe7ae79ab8edbdf85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Andr=C3=A9=20Tanner?= Date: Tue, 21 Mar 2017 15:14:43 +0100 Subject: 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 --- vis-motions.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 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, diff --git a/vis.h b/vis.h index 6c6e0fe..da1677c 100644 --- 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, -- cgit v1.2.3