From cec20265a32d9db837914320317ae0ad9533d461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Fri, 10 Mar 2023 20:58:20 +0100 Subject: [PATCH 1/2] vimode: Refactor motion-word.c so the motion commands can be reused This is a preparation for implementation of "iw", "ow", "iW" and "oW" text objects. --- vimode/src/cmds/motion-word.c | 233 +++++++++++++++++++--------------- 1 file changed, 133 insertions(+), 100 deletions(-) diff --git a/vimode/src/cmds/motion-word.c b/vimode/src/cmds/motion-word.c index dbb3e6fae..e18410d1e 100644 --- a/vimode/src/cmds/motion-word.c +++ b/vimode/src/cmds/motion-word.c @@ -21,13 +21,6 @@ typedef gboolean (*CharacterPredicate)(gchar c); -static void get_current(ScintillaObject *sci, gchar *ch, gint *pos) -{ - *pos = SSM(sci, SCI_GETCURRENTPOS, 0, 0); - *ch = SSM(sci, SCI_GETCHARAT, *pos, 0); -} - - static void move_left(ScintillaObject *sci, gchar *ch, gint *pos) { *pos = PREV(sci, *pos); @@ -90,177 +83,217 @@ static gboolean skip_to_right(CharacterPredicate is_in_group, ScintillaObject *s } -void cmd_goto_next_word(CmdContext *c, CmdParams *p) +static gint find_next_word(ScintillaObject *sci, gint pos, gint num) { gint i; - gint len = SSM(p->sci, SCI_GETLENGTH, 0, 0); + gint len = SSM(sci, SCI_GETLENGTH, 0, 0); - for (i = 0; i < p->num; i++) + for (i = 0; i < num; i++) { - gint pos; - gchar ch; + gchar ch = SSM(sci, SCI_GETCHARAT, pos, 0); - get_current(p->sci, &ch, &pos); + if (!skip_to_right(is_wordchar, sci, &ch, &pos, len)) + skip_to_right(is_nonwordchar, sci, &ch, &pos, len); + skip_to_right(is_space, sci, &ch, &pos, len); + } + return pos; +} - if (!skip_to_right(is_wordchar, p->sci, &ch, &pos, len)) - skip_to_right(is_nonwordchar, p->sci, &ch, &pos, len); - skip_to_right(is_space, p->sci, &ch, &pos, len); - if (!is_space(ch)) - SET_POS(p->sci, pos, TRUE); - } +void cmd_goto_next_word(CmdContext *c, CmdParams *p) +{ + gint pos = SSM(p->sci, SCI_GETCURRENTPOS, 0, 0); + pos = find_next_word(p->sci, pos, p->num); + SET_POS(p->sci, pos, TRUE); } -void cmd_goto_previous_word(CmdContext *c, CmdParams *p) +static gint find_previous_word(ScintillaObject *sci, gint pos, gint num) { gint i; - for (i = 0; i < p->num; i++) + for (i = 0; i < num; i++) { - gint pos; - gchar ch; + gchar ch = SSM(sci, SCI_GETCHARAT, pos, 0); - get_current(p->sci, &ch, &pos); - move_left(p->sci, &ch, &pos); + move_left(sci, &ch, &pos); - skip_to_left(is_space, p->sci, &ch, &pos); - if (!skip_to_left(is_wordchar, p->sci, &ch, &pos)) - skip_to_left(is_nonwordchar, p->sci, &ch, &pos); + skip_to_left(is_space, sci, &ch, &pos); + if (!skip_to_left(is_wordchar, sci, &ch, &pos)) + skip_to_left(is_nonwordchar, sci, &ch, &pos); if (pos != 0 || is_space(ch)) - move_right(p->sci, &ch, &pos); - if (!is_space(ch)) - SET_POS(p->sci, pos, TRUE); + move_right(sci, &ch, &pos); } + return pos; } -void cmd_goto_next_word_end(CmdContext *c, CmdParams *p) +void cmd_goto_previous_word(CmdContext *c, CmdParams *p) +{ + gint pos = SSM(p->sci, SCI_GETCURRENTPOS, 0, 0); + pos = find_previous_word(p->sci, pos, p->num); + SET_POS(p->sci, pos, TRUE); +} + + +static gint find_next_word_end(ScintillaObject *sci, gint pos, gint num, gboolean include_last) { gint i; - gint len = SSM(p->sci, SCI_GETLENGTH, 0, 0); + gint len = SSM(sci, SCI_GETLENGTH, 0, 0); - for (i = 0; i < p->num; i++) + for (i = 0; i < num; i++) { - gint pos; - gchar ch; + gchar ch = SSM(sci, SCI_GETCHARAT, pos, 0); - get_current(p->sci, &ch, &pos); - move_right(p->sci, &ch, &pos); + move_right(sci, &ch, &pos); - skip_to_right(is_space, p->sci, &ch, &pos, len); - if (!skip_to_right(is_wordchar, p->sci, &ch, &pos, len)) - skip_to_right(is_nonwordchar, p->sci, &ch, &pos, len); + skip_to_right(is_space, sci, &ch, &pos, len); + if (!skip_to_right(is_wordchar, sci, &ch, &pos, len)) + skip_to_right(is_nonwordchar, sci, &ch, &pos, len); - if (pos < len - 1 || is_space(ch)) - move_left(p->sci, &ch, &pos); - if (!is_space(ch)) - SET_POS(p->sci, pos, TRUE); + if (!include_last) + { + if (pos < len - 1 || is_space(ch)) + move_left(sci, &ch, &pos); + } } + return pos; } -void cmd_goto_previous_word_end(CmdContext *c, CmdParams *p) +void cmd_goto_next_word_end(CmdContext *c, CmdParams *p) +{ + gint pos = SSM(p->sci, SCI_GETCURRENTPOS, 0, 0); + pos = find_next_word_end(p->sci, pos, p->num, FALSE); + SET_POS(p->sci, pos, TRUE); +} + + +static gint find_previous_word_end(ScintillaObject *sci, gint pos, gint num) { gint i; - for (i = 0; i < p->num; i++) + for (i = 0; i < num; i++) { - gint pos; - gchar ch; + gchar ch = SSM(sci, SCI_GETCHARAT, pos, 0); - get_current(p->sci, &ch, &pos); + if (!skip_to_left(is_wordchar, sci, &ch, &pos)) + skip_to_left(is_nonwordchar, sci, &ch, &pos); + skip_to_left(is_space, sci, &ch, &pos); + } + return pos; +} - if (!skip_to_left(is_wordchar, p->sci, &ch, &pos)) - skip_to_left(is_nonwordchar, p->sci, &ch, &pos); - skip_to_left(is_space, p->sci, &ch, &pos); - if (!is_space(ch)) - SET_POS(p->sci, pos, TRUE); - } +void cmd_goto_previous_word_end(CmdContext *c, CmdParams *p) +{ + gint pos = SSM(p->sci, SCI_GETCURRENTPOS, 0, 0); + pos = find_previous_word_end(p->sci, pos, p->num); + SET_POS(p->sci, pos, TRUE); } -void cmd_goto_next_word_space(CmdContext *c, CmdParams *p) +static gint find_next_word_space(ScintillaObject *sci, gint pos, gint num) { gint i; - gint len = SSM(p->sci, SCI_GETLENGTH, 0, 0); + gint len = SSM(sci, SCI_GETLENGTH, 0, 0); - for (i = 0; i < p->num; i++) + for (i = 0; i < num; i++) { - gint pos; - gchar ch; + gchar ch = SSM(sci, SCI_GETCHARAT, pos, 0); - get_current(p->sci, &ch, &pos); + skip_to_right(is_nonspace, sci, &ch, &pos, len); + skip_to_right(is_space, sci, &ch, &pos, len); + } + return pos; +} - skip_to_right(is_nonspace, p->sci, &ch, &pos, len); - skip_to_right(is_space, p->sci, &ch, &pos, len); - if (!is_space(ch)) - SET_POS(p->sci, pos, TRUE); - } +void cmd_goto_next_word_space(CmdContext *c, CmdParams *p) +{ + gint pos = SSM(p->sci, SCI_GETCURRENTPOS, 0, 0); + pos = find_next_word_space(p->sci, pos, p->num); + SET_POS(p->sci, pos, TRUE); } -void cmd_goto_previous_word_space(CmdContext *c, CmdParams *p) +static gint find_previous_word_space(ScintillaObject *sci, gint pos, gint num) { gint i; - for (i = 0; i < p->num; i++) + for (i = 0; i < num; i++) { - gint pos; - gchar ch; + gchar ch = SSM(sci, SCI_GETCHARAT, pos, 0); - get_current(p->sci, &ch, &pos); - move_left(p->sci, &ch, &pos); + move_left(sci, &ch, &pos); - skip_to_left(is_space, p->sci, &ch, &pos); - skip_to_left(is_nonspace, p->sci, &ch, &pos); + skip_to_left(is_space, sci, &ch, &pos); + skip_to_left(is_nonspace, sci, &ch, &pos); if (pos != 0 || is_space(ch)) - move_right(p->sci, &ch, &pos); - if (!is_space(ch)) - SET_POS(p->sci, pos, TRUE); + move_right(sci, &ch, &pos); } + return pos; } -void cmd_goto_next_word_end_space(CmdContext *c, CmdParams *p) +void cmd_goto_previous_word_space(CmdContext *c, CmdParams *p) +{ + gint pos = SSM(p->sci, SCI_GETCURRENTPOS, 0, 0); + pos = find_previous_word_space(p->sci, pos, p->num); + SET_POS(p->sci, pos, TRUE); +} + + +static gint find_next_word_end_space(ScintillaObject *sci, gint pos, gint num, gboolean include_last) { gint i; - gint len = SSM(p->sci, SCI_GETLENGTH, 0, 0); + gint len = SSM(sci, SCI_GETLENGTH, 0, 0); - for (i = 0; i < p->num; i++) + for (i = 0; i < num; i++) { - gint pos; - gchar ch; + gchar ch = SSM(sci, SCI_GETCHARAT, pos, 0); - get_current(p->sci, &ch, &pos); - move_right(p->sci, &ch, &pos); + move_right(sci, &ch, &pos); - skip_to_right(is_space, p->sci, &ch, &pos, len); - skip_to_right(is_nonspace, p->sci, &ch, &pos, len); + skip_to_right(is_space, sci, &ch, &pos, len); + skip_to_right(is_nonspace, sci, &ch, &pos, len); - if (pos < len - 1 || is_space(ch)) - move_left(p->sci, &ch, &pos); - if (!is_space(ch)) - SET_POS(p->sci, pos, TRUE); + if (!include_last) + { + if (pos < len - 1 || is_space(ch)) + move_left(sci, &ch, &pos); + } } + return pos; } -void cmd_goto_previous_word_end_space(CmdContext *c, CmdParams *p) +void cmd_goto_next_word_end_space(CmdContext *c, CmdParams *p) +{ + gint pos = SSM(p->sci, SCI_GETCURRENTPOS, 0, 0); + pos = find_next_word_end_space(p->sci, pos, p->num, FALSE); + SET_POS(p->sci, pos, TRUE); +} + + +static gint find_previous_word_end_space(ScintillaObject *sci, gint pos, gint num) { gint i; - for (i = 0; i < p->num; i++) + for (i = 0; i < num; i++) { - gint pos; - gchar ch; + gchar ch = SSM(sci, SCI_GETCHARAT, pos, 0); - get_current(p->sci, &ch, &pos); + skip_to_left(is_nonspace, sci, &ch, &pos); + skip_to_left(is_space, sci, &ch, &pos); + } + return pos; +} - skip_to_left(is_nonspace, p->sci, &ch, &pos); - skip_to_left(is_space, p->sci, &ch, &pos); - if (!is_space(ch)) - SET_POS(p->sci, pos, TRUE); +void cmd_goto_previous_word_end_space(CmdContext *c, CmdParams *p) +{ + gint pos = SSM(p->sci, SCI_GETCURRENTPOS, 0, 0); + pos = find_previous_word_end_space(p->sci, pos, p->num); + SET_POS(p->sci, pos, TRUE); +} } } From e045c740f50a278fa3a8621ac139273e5d8c26c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Fri, 10 Mar 2023 21:41:52 +0100 Subject: [PATCH 2/2] vimode: implement "iw", "ow", "iW" and "oW" text objects This enables a family of word-related text object commands such as: "ciw", "diw", "viw" etc. Fixes #1155. --- vimode/README | 4 ++ vimode/src/cmd-runner.c | 4 ++ vimode/src/cmds/motion-word.c | 90 +++++++++++++++++++++++++++++++++++ vimode/src/cmds/motion-word.h | 3 ++ vimode/src/cmds/txtobjs.c | 44 +++++++++++++++++ vimode/src/cmds/txtobjs.h | 4 ++ 6 files changed, 149 insertions(+) diff --git a/vimode/README b/vimode/README index 20a1c5141..08202a56d 100644 --- a/vimode/README +++ b/vimode/README @@ -448,10 +448,12 @@ a new command, please do not forget to update the table below.:: v_a< a< "a <>" from '<' to the matching '>' v_a> a> same as a< v_aB aB "a Block" from "[{" to "]}" (with brackets) + v_aW aW "a WORD" (with white space) v_a[ a[ "a []" from '[' to the matching ']' v_a] a] same as a[ v_a` a` string in backticks v_ab ab "a block" from "[(" to "])" (with braces) + v_aw aw "a word" (with white space) v_a{ a{ same as aB v_a} a} same as aB v_iquote i" double quoted string without the quotes @@ -461,10 +463,12 @@ a new command, please do not forget to update the table below.:: v_i< i< "inner <>" from '<' to the matching '>' v_i> i> same as i< v_iB iB "inner Block" from "[{" and "]}" + v_iW iW "inner WORD" v_i[ i[ "inner []" from '[' to the matching ']' v_i] i] same as i[ v_i` i` string in backticks without the backticks v_ib ib "inner block" from "[(" to "])" + v_iw iw "inner word" v_i{ i{ same as iB v_i} i} same as iB diff --git a/vimode/src/cmd-runner.c b/vimode/src/cmd-runner.c index 3a6f35054..49706a539 100644 --- a/vimode/src/cmd-runner.c +++ b/vimode/src/cmd-runner.c @@ -222,6 +222,8 @@ CmdDef operator_cmds[] = { {cmd_select_less, GDK_KEY_a, GDK_KEY_greater, 0, 0, FALSE, FALSE}, \ {cmd_select_bracket, GDK_KEY_a, GDK_KEY_bracketleft, 0, 0, FALSE, FALSE}, \ {cmd_select_bracket, GDK_KEY_a, GDK_KEY_bracketright, 0, 0, FALSE, FALSE}, \ + {cmd_select_word, GDK_KEY_a, GDK_KEY_w, 0, 0, FALSE, FALSE}, \ + {cmd_select_word_space, GDK_KEY_a, GDK_KEY_W, 0, 0, FALSE, FALSE}, \ /* inner */ \ {cmd_select_quotedbl_inner, GDK_KEY_i, GDK_KEY_quotedbl, 0, 0, FALSE, FALSE}, \ {cmd_select_quoteleft_inner, GDK_KEY_i, GDK_KEY_quoteleft, 0, 0, FALSE, FALSE}, \ @@ -236,6 +238,8 @@ CmdDef operator_cmds[] = { {cmd_select_less_inner, GDK_KEY_i, GDK_KEY_greater, 0, 0, FALSE, FALSE}, \ {cmd_select_bracket_inner, GDK_KEY_i, GDK_KEY_bracketleft, 0, 0, FALSE, FALSE}, \ {cmd_select_bracket_inner, GDK_KEY_i, GDK_KEY_bracketright, 0, 0, FALSE, FALSE}, \ + {cmd_select_word_inner, GDK_KEY_i, GDK_KEY_w, 0, 0, FALSE, FALSE}, \ + {cmd_select_word_space_inner, GDK_KEY_i, GDK_KEY_W, 0, 0, FALSE, FALSE}, \ /* END */ diff --git a/vimode/src/cmds/motion-word.c b/vimode/src/cmds/motion-word.c index e18410d1e..512d6da07 100644 --- a/vimode/src/cmds/motion-word.c +++ b/vimode/src/cmds/motion-word.c @@ -295,5 +295,95 @@ void cmd_goto_previous_word_end_space(CmdContext *c, CmdParams *p) pos = find_previous_word_end_space(p->sci, pos, p->num); SET_POS(p->sci, pos, TRUE); } + + +void get_word_range(ScintillaObject *sci, gboolean word_space, gboolean inner, + gint pos, gint num, gint *sel_start, gint *sel_len) +{ + guint i; + gint start_pos = pos; + gint end_pos; + gchar ch = SSM(sci, SCI_GETCHARAT, pos, 0); + gchar prev_ch = SSM(sci, SCI_GETCHARAT, PREV(sci, pos), 0); + gchar next_ch = SSM(sci, SCI_GETCHARAT, NEXT(sci, pos), 0); + + if (word_space) + { + if (is_space(prev_ch) && !is_space(ch)) + ; // already there + else if (is_space(ch) && !is_space(prev_ch)) + ; // already there + else if (is_space(ch)) + start_pos = NEXT(sci, find_previous_word_end_space(sci, pos, 1)); + else if (!is_space(ch)) + start_pos = find_previous_word_space(sci, pos, 1); + + if (inner && !is_space(ch) && is_space(next_ch)) + { + num--; // already there once + pos = NEXT(sci, pos); + } + + for (i = 0; i < num; i++) + { + if (is_space(ch)) + { + if (inner) + pos = find_next_word_space(sci, pos, 1); + else + pos = find_next_word_end_space(sci, pos, 1, TRUE); + } + else if (!is_space(ch)) + { + if (inner) + pos = find_next_word_end_space(sci, pos, 1, TRUE); + else + pos = find_next_word_space(sci, pos, 1); + } + } + + end_pos = pos; } + else + { + if ((is_space(prev_ch) || is_nonwordchar(prev_ch)) && is_wordchar(ch)) + ; // already there + else if ((is_wordchar(prev_ch) || is_nonwordchar(prev_ch)) && is_space(ch)) + ; // already there + else if ((is_space(prev_ch) || is_wordchar(prev_ch)) && is_nonwordchar(ch)) + ; // already there + else if (is_space(ch) || is_nonwordchar(ch)) + start_pos = NEXT(sci, find_previous_word_end(sci, pos, 1)); + else if (is_wordchar(ch)) + start_pos = find_previous_word(sci, pos, 1); + + if (inner && (is_space(next_ch) || is_nonwordchar(next_ch)) && is_wordchar(ch)) + { + num--; // already there once + pos = NEXT(sci, pos); + } + + for (i = 0; i < num; i++) + { + if (is_space(ch) || is_nonwordchar(ch)) + { + if (inner) + pos = find_next_word(sci, pos, 1); + else + pos = find_next_word_end(sci, pos, 1, TRUE); + } + else if (is_wordchar(ch)) + { + if (inner) + pos = find_next_word_end(sci, pos, 1, TRUE); + else + pos = find_next_word(sci, pos, 1); + } + } + + end_pos = pos; + } + + *sel_start = start_pos; + *sel_len = end_pos - start_pos; } diff --git a/vimode/src/cmds/motion-word.h b/vimode/src/cmds/motion-word.h index db0550ca4..473491543 100644 --- a/vimode/src/cmds/motion-word.h +++ b/vimode/src/cmds/motion-word.h @@ -31,4 +31,7 @@ void cmd_goto_previous_word_space(CmdContext *c, CmdParams *p); void cmd_goto_next_word_end_space(CmdContext *c, CmdParams *p); void cmd_goto_previous_word_end_space(CmdContext *c, CmdParams *p); +void get_word_range(ScintillaObject *sci, gboolean word_space, gboolean inner, + gint pos, gint num, gint *sel_start, gint *sel_len); + #endif diff --git a/vimode/src/cmds/txtobjs.c b/vimode/src/cmds/txtobjs.c index fa31278ca..395f86199 100644 --- a/vimode/src/cmds/txtobjs.c +++ b/vimode/src/cmds/txtobjs.c @@ -17,6 +17,7 @@ */ #include "cmds/txtobjs.h" +#include "cmds/motion-word.h" static gint find_upper_level_brace(ScintillaObject *sci, gint pos, gint open_brace, gint close_brace) @@ -188,3 +189,46 @@ void cmd_select_bracket_inner(CmdContext *c, CmdParams *p) { select_brace(c, p, '[', ']', TRUE); } + + +static void select_word(CmdContext *c, CmdParams *p, gboolean word_space, gboolean inner) +{ + gint sel_start, sel_len; + + get_word_range(p->sci, word_space, inner, p->pos, p->num, &sel_start, &sel_len); + + if (VI_IS_VISUAL(vi_get_mode())) + { + c->sel_anchor = sel_start; + SET_POS(p->sci, sel_start + sel_len, TRUE); + } + else + { + p->sel_start = sel_start; + p->sel_len = sel_len; + } +} + + +void cmd_select_word(CmdContext *c, CmdParams *p) +{ + select_word(c, p, FALSE, FALSE); +} + + +void cmd_select_word_space(CmdContext *c, CmdParams *p) +{ + select_word(c, p, TRUE, FALSE); +} + + +void cmd_select_word_inner(CmdContext *c, CmdParams *p) +{ + select_word(c, p, FALSE, TRUE); +} + + +void cmd_select_word_space_inner(CmdContext *c, CmdParams *p) +{ + select_word(c, p, TRUE, TRUE); +} diff --git a/vimode/src/cmds/txtobjs.h b/vimode/src/cmds/txtobjs.h index 39b96a098..bfeb68357 100644 --- a/vimode/src/cmds/txtobjs.h +++ b/vimode/src/cmds/txtobjs.h @@ -29,6 +29,8 @@ void cmd_select_brace(CmdContext *c, CmdParams *p); void cmd_select_paren(CmdContext *c, CmdParams *p); void cmd_select_less(CmdContext *c, CmdParams *p); void cmd_select_bracket(CmdContext *c, CmdParams *p); +void cmd_select_word(CmdContext *c, CmdParams *p); +void cmd_select_word_space(CmdContext *c, CmdParams *p); void cmd_select_quotedbl_inner(CmdContext *c, CmdParams *p); void cmd_select_quoteleft_inner(CmdContext *c, CmdParams *p); @@ -37,5 +39,7 @@ void cmd_select_brace_inner(CmdContext *c, CmdParams *p); void cmd_select_paren_inner(CmdContext *c, CmdParams *p); void cmd_select_less_inner(CmdContext *c, CmdParams *p); void cmd_select_bracket_inner(CmdContext *c, CmdParams *p); +void cmd_select_word_inner(CmdContext *c, CmdParams *p); +void cmd_select_word_space_inner(CmdContext *c, CmdParams *p); #endif