aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2022-07-06 20:44:41 +0800
committerGitHub <noreply@github.com>2022-07-06 20:44:41 +0800
commitc84ae5706fb0c31c415fedbc15021e9b2ef46065 (patch)
treeeb972208a75d0a28213cc040bad4d093ab2aec89 /src
parent93c8fe77cbfd3934c7599196e573136f75f6e9af (diff)
parent1a490a5bc552fcdc299684b725c5044a9e9c73c7 (diff)
downloadrneovim-c84ae5706fb0c31c415fedbc15021e9b2ef46065.tar.gz
rneovim-c84ae5706fb0c31c415fedbc15021e9b2ef46065.tar.bz2
rneovim-c84ae5706fb0c31c415fedbc15021e9b2ef46065.zip
Merge pull request #19247 from zeertzjq/vim-8.2.4165
vim-patch:7.4.212,8.2.{4165,4221}: some functions in normal.c are too long
Diffstat (limited to 'src')
-rw-r--r--src/nvim/edit.c9
-rw-r--r--src/nvim/normal.c1010
-rw-r--r--src/nvim/ops.c4
-rw-r--r--src/nvim/search.c13
-rw-r--r--src/nvim/testdir/test_normal.vim41
5 files changed, 592 insertions, 485 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 06f6d7f97b..de6f2c32de 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -7935,13 +7935,8 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
* Don't do it for CTRL-O, unless past the end of the line.
*/
if (!nomove
- && (curwin->w_cursor.col != 0
- || curwin->w_cursor.coladd > 0
- )
- && (restart_edit == NUL
- || (gchar_cursor() == NUL
- && !VIsual_active
- ))
+ && (curwin->w_cursor.col != 0 || curwin->w_cursor.coladd > 0)
+ && (restart_edit == NUL || (gchar_cursor() == NUL && !VIsual_active))
&& !revins_on) {
if (curwin->w_cursor.coladd > 0 || get_ve_flags() == VE_ALL) {
oneleft();
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index f871b6a884..3a552ef053 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -559,6 +559,14 @@ static bool normal_need_additional_char(NormalState *s)
static bool normal_need_redraw_mode_message(NormalState *s)
{
+ // In Visual mode and with "^O" in Insert mode, a short message will be
+ // overwritten by the mode message. Wait a bit, until a key is hit.
+ // In Visual mode, it's more important to keep the Visual area updated
+ // than keeping a message (e.g. from a /pat search).
+ // Only do this if the command was typed, not from a mapping.
+ // Don't wait when emsg_silent is non-zero.
+ // Also wait a bit after an error message, e.g. for "^O:".
+ // Don't redraw the screen, it would remove the message.
return (
// 'showmode' is set and messages can be printed
((p_smd && msg_silent == 0
@@ -892,14 +900,6 @@ static void normal_finish_command(NormalState *s)
// Wait for a moment when a message is displayed that will be overwritten
// by the mode message.
- // In Visual mode and with "^O" in Insert mode, a short message will be
- // overwritten by the mode message. Wait a bit, until a key is hit.
- // In Visual mode, it's more important to keep the Visual area updated
- // than keeping a message (e.g. from a /pat search).
- // Only do this if the command was typed, not from a mapping.
- // Don't wait when emsg_silent is non-zero.
- // Also wait a bit after an error message, e.g. for "^O:".
- // Don't redraw the screen, it would remove the message.
if (normal_need_redraw_mode_message(s)) {
normal_redraw_mode_message(s);
}
@@ -2078,8 +2078,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
} else if ((mod_mask & MOD_MASK_SHIFT)) {
// Shift-Mouse click searches for the next occurrence of the word under
// the mouse pointer
- if (State & MODE_INSERT
- || (VIsual_active && VIsual_select)) {
+ if (State & MODE_INSERT || (VIsual_active && VIsual_select)) {
stuffcharReadbuff(Ctrl_O);
}
if (which_button == MOUSE_LEFT) {
@@ -2546,8 +2545,7 @@ static bool checkclearop(oparg_T *oap)
/// @return true if operator or Visual was active.
static bool checkclearopq(oparg_T *oap)
{
- if (oap->op_type == OP_NOP
- && !VIsual_active) {
+ if (oap->op_type == OP_NOP && !VIsual_active) {
return false;
}
clearopbeep(oap);
@@ -3474,6 +3472,108 @@ void scroll_redraw(int up, long count)
redraw_later(curwin, VALID);
}
+/// Get the count specified after a 'z' command.
+/// @return true to process the 'z' command and false to skip it.
+static bool nv_z_get_count(cmdarg_T *cap, int *nchar_arg)
+{
+ int nchar = *nchar_arg;
+
+ // "z123{nchar}": edit the count before obtaining {nchar}
+ if (checkclearop(cap->oap)) {
+ return false;
+ }
+ long n = nchar - '0';
+
+ for (;;) {
+ no_mapping++;
+ allow_keys++; // no mapping for nchar, but allow key codes
+ nchar = plain_vgetc();
+ LANGMAP_ADJUST(nchar, true);
+ no_mapping--;
+ allow_keys--;
+ (void)add_to_showcmd(nchar);
+ if (nchar == K_DEL || nchar == K_KDEL) {
+ n /= 10;
+ } else if (ascii_isdigit(nchar)) {
+ n = n * 10 + (nchar - '0');
+ } else if (nchar == CAR) {
+ win_setheight((int)n);
+ break;
+ } else if (nchar == 'l'
+ || nchar == 'h'
+ || nchar == K_LEFT
+ || nchar == K_RIGHT) {
+ cap->count1 = n ? n * cap->count1 : cap->count1;
+ *nchar_arg = nchar;
+ return true;
+ } else {
+ clearopbeep(cap->oap);
+ break;
+ }
+ }
+ cap->oap->op_type = OP_NOP;
+ return false;
+}
+
+/// "zug" and "zuw": undo "zg" and "zw"
+/// "zg": add good word to word list
+/// "zw": add wrong word to word list
+/// "zG": add good word to temp word list
+/// "zW": add wrong word to temp word list
+static int nv_zg_zw(cmdarg_T *cap, int nchar)
+{
+ bool undo = false;
+
+ if (nchar == 'u') {
+ no_mapping++;
+ allow_keys++; // no mapping for nchar, but allow key codes
+ nchar = plain_vgetc();
+ LANGMAP_ADJUST(nchar, true);
+ no_mapping--;
+ allow_keys--;
+ (void)add_to_showcmd(nchar);
+ if (vim_strchr("gGwW", nchar) == NULL) {
+ clearopbeep(cap->oap);
+ return OK;
+ }
+ undo = true;
+ }
+
+ if (checkclearop(cap->oap)) {
+ return OK;
+ }
+ char_u *ptr = NULL;
+ size_t len;
+ if (VIsual_active && !get_visual_text(cap, &ptr, &len)) {
+ return FAIL;
+ }
+ if (ptr == NULL) {
+ pos_T pos = curwin->w_cursor;
+
+ // Find bad word under the cursor. When 'spell' is
+ // off this fails and find_ident_under_cursor() is
+ // used below.
+ emsg_off++;
+ len = spell_move_to(curwin, FORWARD, true, true, NULL);
+ emsg_off--;
+ if (len != 0 && curwin->w_cursor.col <= pos.col) {
+ ptr = ml_get_pos(&curwin->w_cursor);
+ }
+ curwin->w_cursor = pos;
+ }
+
+ if (ptr == NULL && (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) {
+ return FAIL;
+ }
+ assert(len <= INT_MAX);
+ spell_add_word(ptr, (int)len,
+ nchar == 'w' || nchar == 'W' ? SPELL_ADD_BAD : SPELL_ADD_GOOD,
+ (nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1,
+ undo);
+
+ return OK;
+}
+
/// Commands that start with "z".
static void nv_zet(cmdarg_T *cap)
{
@@ -3482,47 +3582,13 @@ static void nv_zet(cmdarg_T *cap)
int nchar = cap->nchar;
long old_fdl = curwin->w_p_fdl;
int old_fen = curwin->w_p_fen;
- bool undo = false;
int l_p_siso = (int)get_sidescrolloff_value(curwin);
- if (ascii_isdigit(nchar)) {
- // "z123{nchar}": edit the count before obtaining {nchar}
- if (checkclearop(cap->oap)) {
- return;
- }
- n = nchar - '0';
- for (;;) {
- no_mapping++;
- allow_keys++; // no mapping for nchar, but allow key codes
- nchar = plain_vgetc();
- LANGMAP_ADJUST(nchar, true);
- no_mapping--;
- allow_keys--;
- (void)add_to_showcmd(nchar);
- if (nchar == K_DEL || nchar == K_KDEL) {
- n /= 10;
- } else if (ascii_isdigit(nchar)) {
- n = n * 10 + (nchar - '0');
- } else if (nchar == CAR) {
- win_setheight(n);
- break;
- } else if (nchar == 'l'
- || nchar == 'h'
- || nchar == K_LEFT
- || nchar == K_RIGHT) {
- cap->count1 = n ? n * cap->count1 : cap->count1;
- goto dozet;
- } else {
- clearopbeep(cap->oap);
- break;
- }
- }
- cap->oap->op_type = OP_NOP;
+ if (ascii_isdigit(nchar) && !nv_z_get_count(cap, &nchar)) {
return;
}
-dozet:
// "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc"
// and "zC" only in Visual mode. "zj" and "zk" are motion
// commands.
@@ -3872,60 +3938,14 @@ dozet:
break;
case 'u': // "zug" and "zuw": undo "zg" and "zw"
- no_mapping++;
- allow_keys++; // no mapping for nchar, but allow key codes
- nchar = plain_vgetc();
- LANGMAP_ADJUST(nchar, true);
- no_mapping--;
- allow_keys--;
- (void)add_to_showcmd(nchar);
- if (vim_strchr("gGwW", nchar) == NULL) {
- clearopbeep(cap->oap);
- break;
- }
- undo = true;
- FALLTHROUGH;
-
case 'g': // "zg": add good word to word list
case 'w': // "zw": add wrong word to word list
case 'G': // "zG": add good word to temp word list
case 'W': // "zW": add wrong word to temp word list
- {
- char_u *ptr = NULL;
- size_t len;
-
- if (checkclearop(cap->oap)) {
- break;
- }
- if (VIsual_active && !get_visual_text(cap, &ptr, &len)) {
+ if (nv_zg_zw(cap, nchar) == FAIL) {
return;
}
- if (ptr == NULL) {
- pos_T pos = curwin->w_cursor;
-
- // Find bad word under the cursor. When 'spell' is
- // off this fails and find_ident_under_cursor() is
- // used below.
- emsg_off++;
- len = spell_move_to(curwin, FORWARD, true, true, NULL);
- emsg_off--;
- if (len != 0 && curwin->w_cursor.col <= pos.col) {
- ptr = ml_get_pos(&curwin->w_cursor);
- }
- curwin->w_cursor = pos;
- }
-
- if (ptr == NULL && (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) {
- return;
- }
- assert(len <= INT_MAX);
- spell_add_word(ptr, (int)len,
- nchar == 'w' || nchar == 'W'
- ? SPELL_ADD_BAD : SPELL_ADD_GOOD,
- (nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1,
- undo);
- }
- break;
+ break;
case '=': // "z=": suggestions for a badly spelled word
if (!checkclearop(cap->oap)) {
@@ -4120,6 +4140,69 @@ void do_nv_ident(int c1, int c2)
nv_ident(&ca);
}
+/// 'K' normal-mode command. Get the command to lookup the keyword under the
+/// cursor.
+static size_t nv_K_getcmd(cmdarg_T *cap, char_u *kp, bool kp_help, bool kp_ex, char_u **ptr_arg,
+ size_t n, char *buf, size_t buf_size)
+{
+ if (kp_help) {
+ // in the help buffer
+ STRCPY(buf, "he! ");
+ return n;
+ }
+
+ if (kp_ex) {
+ // 'keywordprg' is an ex command
+ if (cap->count0 != 0) { // Send the count to the ex command.
+ snprintf(buf, buf_size, "%" PRId64, (int64_t)(cap->count0));
+ }
+ STRCAT(buf, kp);
+ STRCAT(buf, " ");
+ return n;
+ }
+
+ char_u *ptr = *ptr_arg;
+
+ // An external command will probably use an argument starting
+ // with "-" as an option. To avoid trouble we skip the "-".
+ while (*ptr == '-' && n > 0) {
+ ptr++;
+ n--;
+ }
+ if (n == 0) {
+ // found dashes only
+ emsg(_(e_noident));
+ xfree(buf);
+ *ptr_arg = ptr;
+ return 0;
+ }
+
+ // When a count is given, turn it into a range. Is this
+ // really what we want?
+ bool isman = (STRCMP(kp, "man") == 0);
+ bool isman_s = (STRCMP(kp, "man -s") == 0);
+ if (cap->count0 != 0 && !(isman || isman_s)) {
+ snprintf(buf, buf_size, ".,.+%" PRId64, (int64_t)(cap->count0 - 1));
+ }
+
+ do_cmdline_cmd("tabnew");
+ STRCAT(buf, "terminal ");
+ if (cap->count0 == 0 && isman_s) {
+ STRCAT(buf, "man");
+ } else {
+ STRCAT(buf, kp);
+ }
+ STRCAT(buf, " ");
+ if (cap->count0 != 0 && (isman || isman_s)) {
+ snprintf(buf + STRLEN(buf), buf_size - STRLEN(buf), "%" PRId64,
+ (int64_t)cap->count0);
+ STRCAT(buf, " ");
+ }
+
+ *ptr_arg = ptr;
+ return n;
+}
+
/// Handle the commands that use the word under the cursor.
/// [g] CTRL-] :ta to current identifier
/// [g] 'K' run program for current identifier
@@ -4199,48 +4282,9 @@ static void nv_ident(cmdarg_T *cap)
break;
case 'K':
- if (kp_help) {
- STRCPY(buf, "he! ");
- } else if (kp_ex) {
- if (cap->count0 != 0) { // Send the count to the ex command.
- snprintf(buf, buf_size, "%" PRId64, (int64_t)(cap->count0));
- }
- STRCAT(buf, kp);
- STRCAT(buf, " ");
- } else {
- // An external command will probably use an argument starting
- // with "-" as an option. To avoid trouble we skip the "-".
- while (*ptr == '-' && n > 0) {
- ptr++;
- n--;
- }
- if (n == 0) {
- emsg(_(e_noident)); // found dashes only
- xfree(buf);
- return;
- }
-
- // When a count is given, turn it into a range. Is this
- // really what we want?
- bool isman = (STRCMP(kp, "man") == 0);
- bool isman_s = (STRCMP(kp, "man -s") == 0);
- if (cap->count0 != 0 && !(isman || isman_s)) {
- snprintf(buf, buf_size, ".,.+%" PRId64, (int64_t)(cap->count0 - 1));
- }
-
- do_cmdline_cmd("tabnew");
- STRCAT(buf, "terminal ");
- if (cap->count0 == 0 && isman_s) {
- STRCAT(buf, "man");
- } else {
- STRCAT(buf, kp);
- }
- STRCAT(buf, " ");
- if (cap->count0 != 0 && (isman || isman_s)) {
- snprintf(buf + STRLEN(buf), buf_size - STRLEN(buf), "%" PRId64,
- (int64_t)cap->count0);
- STRCAT(buf, " ");
- }
+ n = nv_K_getcmd(cap, kp, kp_help, kp_ex, &ptr, n, buf, buf_size);
+ if (n == 0) {
+ return;
}
break;
@@ -4472,7 +4516,6 @@ static void nv_scroll(cmdarg_T *cap)
static void nv_right(cmdarg_T *cap)
{
long n;
- int PAST_LINE;
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
// <C-Right> and <S-Right> move a word or WORD right
@@ -4485,20 +4528,19 @@ static void nv_right(cmdarg_T *cap)
cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
- PAST_LINE = (VIsual_active && *p_sel != 'o');
+ bool past_line = (VIsual_active && *p_sel != 'o');
- // In virtual mode, there's no such thing as "PAST_LINE", as lines are
- // (theoretically) infinitely long.
+ // In virtual edit mode, there's no such thing as "past_line", as lines
+ // are (theoretically) infinitely long.
if (virtual_active()) {
- PAST_LINE = 0;
+ past_line = false;
}
for (n = cap->count1; n > 0; n--) {
- if ((!PAST_LINE && oneright() == false)
- || (PAST_LINE
- && *get_cursor_pos_ptr() == NUL)) {
- // <Space> wraps to next line if 'whichwrap' has 's'.
- // 'l' wraps to next line if 'whichwrap' has 'l'.
+ if ((!past_line && oneright() == false)
+ || (past_line && *get_cursor_pos_ptr() == NUL)) {
+ // <Space> wraps to next line if 'whichwrap' has 's'.
+ // 'l' wraps to next line if 'whichwrap' has 'l'.
// CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
if (((cap->cmdchar == ' ' && vim_strchr((char *)p_ww, 's') != NULL)
|| (cap->cmdchar == 'l' && vim_strchr((char *)p_ww, 'l') != NULL)
@@ -4531,7 +4573,7 @@ static void nv_right(cmdarg_T *cap)
}
}
break;
- } else if (PAST_LINE) {
+ } else if (past_line) {
curwin->w_set_curswant = true;
if (virtual_active()) {
oneright();
@@ -4847,18 +4889,131 @@ static void nv_csearch(cmdarg_T *cap)
}
}
+/// "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
+/// "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
+/// "[/", "[*", "]/", "]*": go to Nth comment start/end.
+/// "[m" or "]m" search for prev/next start of (Java) method.
+/// "[M" or "]M" search for prev/next end of (Java) method.
+static void nv_bracket_block(cmdarg_T *cap, const pos_T *old_pos)
+{
+ pos_T new_pos = { 0, 0, 0 };
+ pos_T *pos = NULL; // init for GCC
+ pos_T prev_pos;
+ long n;
+ int findc;
+ int c;
+
+ if (cap->nchar == '*') {
+ cap->nchar = '/';
+ }
+ prev_pos.lnum = 0;
+ if (cap->nchar == 'm' || cap->nchar == 'M') {
+ if (cap->cmdchar == '[') {
+ findc = '{';
+ } else {
+ findc = '}';
+ }
+ n = 9999;
+ } else {
+ findc = cap->nchar;
+ n = cap->count1;
+ }
+ for (; n > 0; n--) {
+ if ((pos = findmatchlimit(cap->oap, findc,
+ (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL) {
+ if (new_pos.lnum == 0) { // nothing found
+ if (cap->nchar != 'm' && cap->nchar != 'M') {
+ clearopbeep(cap->oap);
+ }
+ } else {
+ pos = &new_pos; // use last one found
+ }
+ break;
+ }
+ prev_pos = new_pos;
+ curwin->w_cursor = *pos;
+ new_pos = *pos;
+ }
+ curwin->w_cursor = *old_pos;
+
+ // Handle "[m", "]m", "[M" and "[M". The findmatchlimit() only
+ // brought us to the match for "[m" and "]M" when inside a method.
+ // Try finding the '{' or '}' we want to be at.
+ // Also repeat for the given count.
+ if (cap->nchar == 'm' || cap->nchar == 'M') {
+ // norm is true for "]M" and "[m"
+ int norm = ((findc == '{') == (cap->nchar == 'm'));
+
+ n = cap->count1;
+ // found a match: we were inside a method
+ if (prev_pos.lnum != 0) {
+ pos = &prev_pos;
+ curwin->w_cursor = prev_pos;
+ if (norm) {
+ n--;
+ }
+ } else {
+ pos = NULL;
+ }
+ while (n > 0) {
+ for (;;) {
+ if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0) {
+ // if not found anything, that's an error
+ if (pos == NULL) {
+ clearopbeep(cap->oap);
+ }
+ n = 0;
+ break;
+ }
+ c = gchar_cursor();
+ if (c == '{' || c == '}') {
+ // Must have found end/start of class: use it.
+ // Or found the place to be at.
+ if ((c == findc && norm) || (n == 1 && !norm)) {
+ new_pos = curwin->w_cursor;
+ pos = &new_pos;
+ n = 0;
+ } else if (new_pos.lnum == 0) {
+ // if no match found at all, we started outside of the
+ // class and we're inside now. Just go on.
+ new_pos = curwin->w_cursor;
+ pos = &new_pos;
+ } else if ((pos = findmatchlimit(cap->oap, findc,
+ (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD,
+ 0)) == NULL) {
+ // found start/end of other method: go to match
+ n = 0;
+ } else {
+ curwin->w_cursor = *pos;
+ }
+ break;
+ }
+ }
+ n--;
+ }
+ curwin->w_cursor = *old_pos;
+ if (pos == NULL && new_pos.lnum != 0) {
+ clearopbeep(cap->oap);
+ }
+ }
+ if (pos != NULL) {
+ setpcmark();
+ curwin->w_cursor = *pos;
+ curwin->w_set_curswant = true;
+ if ((fdo_flags & FDO_BLOCK) && KeyTyped
+ && cap->oap->op_type == OP_NOP) {
+ foldOpenCursor();
+ }
+ }
+}
+
/// "[" and "]" commands.
/// cap->arg is BACKWARD for "[" and FORWARD for "]".
static void nv_brackets(cmdarg_T *cap)
{
- pos_T new_pos = { 0, 0, 0 };
- pos_T prev_pos;
- pos_T *pos = NULL; // init for GCC
pos_T old_pos; // cursor position before command
int flag;
long n;
- int findc;
- int c;
cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
@@ -4874,7 +5029,7 @@ static void nv_brackets(cmdarg_T *cap)
//
// search list jump
// fwd bwd fwd bwd fwd bwd
- // identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
+ // identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
// define "]d" "[d" "]D" "[D" "]^D" "[^D"
char_u *ptr;
size_t len;
@@ -4907,108 +5062,7 @@ static void nv_brackets(cmdarg_T *cap)
// "[/", "[*", "]/", "]*": go to Nth comment start/end.
// "[m" or "]m" search for prev/next start of (Java) method.
// "[M" or "]M" search for prev/next end of (Java) method.
- if (cap->nchar == '*') {
- cap->nchar = '/';
- }
- prev_pos.lnum = 0;
- if (cap->nchar == 'm' || cap->nchar == 'M') {
- if (cap->cmdchar == '[') {
- findc = '{';
- } else {
- findc = '}';
- }
- n = 9999;
- } else {
- findc = cap->nchar;
- n = cap->count1;
- }
- for (; n > 0; n--) {
- if ((pos = findmatchlimit(cap->oap, findc,
- (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL) {
- if (new_pos.lnum == 0) { // nothing found
- if (cap->nchar != 'm' && cap->nchar != 'M') {
- clearopbeep(cap->oap);
- }
- } else {
- pos = &new_pos; // use last one found
- }
- break;
- }
- prev_pos = new_pos;
- curwin->w_cursor = *pos;
- new_pos = *pos;
- }
- curwin->w_cursor = old_pos;
-
- // Handle "[m", "]m", "[M" and "[M". The findmatchlimit() only
- // brought us to the match for "[m" and "]M" when inside a method.
- // Try finding the '{' or '}' we want to be at.
- // Also repeat for the given count.
- if (cap->nchar == 'm' || cap->nchar == 'M') {
- // norm is true for "]M" and "[m"
- int norm = ((findc == '{') == (cap->nchar == 'm'));
-
- n = cap->count1;
- // found a match: we were inside a method
- if (prev_pos.lnum != 0) {
- pos = &prev_pos;
- curwin->w_cursor = prev_pos;
- if (norm) {
- n--;
- }
- } else {
- pos = NULL;
- }
- while (n > 0) {
- for (;;) {
- if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0) {
- // if not found anything, that's an error
- if (pos == NULL) {
- clearopbeep(cap->oap);
- }
- n = 0;
- break;
- }
- c = gchar_cursor();
- if (c == '{' || c == '}') {
- // Must have found end/start of class: use it.
- // Or found the place to be at.
- if ((c == findc && norm) || (n == 1 && !norm)) {
- new_pos = curwin->w_cursor;
- pos = &new_pos;
- n = 0;
- } else if (new_pos.lnum == 0) {
- // if no match found at all, we started outside of the
- // class and we're inside now. Just go on.
- new_pos = curwin->w_cursor;
- pos = &new_pos;
- } else if ((pos = findmatchlimit(cap->oap, findc,
- (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD,
- 0)) == NULL) {
- // found start/end of other method: go to match
- n = 0;
- } else {
- curwin->w_cursor = *pos;
- }
- break;
- }
- }
- n--;
- }
- curwin->w_cursor = old_pos;
- if (pos == NULL && new_pos.lnum != 0) {
- clearopbeep(cap->oap);
- }
- }
- if (pos != NULL) {
- setpcmark();
- curwin->w_cursor = *pos;
- curwin->w_set_curswant = true;
- if ((fdo_flags & FDO_BLOCK) && KeyTyped
- && cap->oap->op_type == OP_NOP) {
- foldOpenCursor();
- }
- }
+ nv_bracket_block(cap, &old_pos);
} else if (cap->nchar == '[' || cap->nchar == ']') {
// "[[", "[]", "]]" and "][": move to start or end of function
if (cap->nchar == cap->cmdchar) { // "]]" or "[["
@@ -5035,19 +5089,19 @@ static void nv_brackets(cmdarg_T *cap)
nv_put_opt(cap, true);
} else if (cap->nchar == '\'' || cap->nchar == '`') {
// "['", "[`", "]'" and "]`": jump to next mark
- fmark_T *fm = NULL;
- pos = &curwin->w_cursor;
+ fmark_T *fm = pos_to_mark(curbuf, NULL, curwin->w_cursor);
+ fmark_T *prev_fm;
for (n = cap->count1; n > 0; n--) {
- prev_pos = *pos;
- fm = getnextmark(pos, cap->cmdchar == '[' ? BACKWARD : FORWARD,
+ prev_fm = fm;
+ fm = getnextmark(&fm->mark, cap->cmdchar == '[' ? BACKWARD : FORWARD,
cap->nchar == '\'');
- if (pos == NULL) {
+ if (fm == NULL) {
break;
- } else {
- pos = fm != NULL ? &fm->mark : NULL; // Adjust for the next iteration.
}
}
- fm = fm == NULL ? pos_to_mark(curbuf, NULL, curwin->w_cursor) : fm;
+ if (fm == NULL) {
+ fm = prev_fm;
+ }
MarkMove flags = kMarkContext;
flags |= cap->nchar == '\'' ? kMarkBeginLine: 0;
nv_mark_move_to(cap, flags, fm);
@@ -5883,13 +5937,218 @@ static void nv_suspend(cmdarg_T *cap)
do_cmdline_cmd("st");
}
+/// "gv": Reselect the previous Visual area. If Visual already active,
+/// exchange previous and current Visual area.
+static void nv_gv_cmd(cmdarg_T *cap)
+{
+ if (checkclearop(cap->oap)) {
+ return;
+ }
+
+ if (curbuf->b_visual.vi_start.lnum == 0
+ || curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count
+ || curbuf->b_visual.vi_end.lnum == 0) {
+ beep_flush();
+ return;
+ }
+
+ pos_T tpos;
+ // set w_cursor to the start of the Visual area, tpos to the end
+ if (VIsual_active) {
+ int i = VIsual_mode;
+ VIsual_mode = curbuf->b_visual.vi_mode;
+ curbuf->b_visual.vi_mode = i;
+ curbuf->b_visual_mode_eval = i;
+ i = curwin->w_curswant;
+ curwin->w_curswant = curbuf->b_visual.vi_curswant;
+ curbuf->b_visual.vi_curswant = i;
+
+ tpos = curbuf->b_visual.vi_end;
+ curbuf->b_visual.vi_end = curwin->w_cursor;
+ curwin->w_cursor = curbuf->b_visual.vi_start;
+ curbuf->b_visual.vi_start = VIsual;
+ } else {
+ VIsual_mode = curbuf->b_visual.vi_mode;
+ curwin->w_curswant = curbuf->b_visual.vi_curswant;
+ tpos = curbuf->b_visual.vi_end;
+ curwin->w_cursor = curbuf->b_visual.vi_start;
+ }
+
+ VIsual_active = true;
+ VIsual_reselect = true;
+
+ // Set Visual to the start and w_cursor to the end of the Visual
+ // area. Make sure they are on an existing character.
+ check_cursor();
+ VIsual = curwin->w_cursor;
+ curwin->w_cursor = tpos;
+ check_cursor();
+ update_topline(curwin);
+
+ // When called from normal "g" command: start Select mode when
+ // 'selectmode' contains "cmd". When called for K_SELECT, always
+ // start Select mode.
+ if (cap->arg) {
+ VIsual_select = true;
+ VIsual_select_reg = 0;
+ } else {
+ may_start_select('c');
+ }
+ setmouse();
+ redraw_curbuf_later(INVERTED);
+ showmode();
+}
+
+/// "g0", "g^" : Like "0" and "^" but for screen lines.
+/// "gm": middle of "g0" and "g$".
+static void nv_g_home_m_cmd(cmdarg_T *cap)
+{
+ int i;
+ const bool flag = cap->nchar == '^';
+
+ cap->oap->motion_type = kMTCharWise;
+ cap->oap->inclusive = false;
+ if (curwin->w_p_wrap
+ && curwin->w_width_inner != 0) {
+ int width1 = curwin->w_width_inner - curwin_col_off();
+ int width2 = width1 + curwin_col_off2();
+
+ validate_virtcol();
+ i = 0;
+ if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0) {
+ i = (curwin->w_virtcol - width1) / width2 * width2 + width1;
+ }
+ } else {
+ i = curwin->w_leftcol;
+ }
+ // Go to the middle of the screen line. When 'number' or
+ // 'relativenumber' is on and lines are wrapping the middle can be more
+ // to the left.
+ if (cap->nchar == 'm') {
+ i += (curwin->w_width_inner - curwin_col_off()
+ + ((curwin->w_p_wrap && i > 0) ? curwin_col_off2() : 0)) / 2;
+ }
+ coladvance((colnr_T)i);
+ if (flag) {
+ do {
+ i = gchar_cursor();
+ } while (ascii_iswhite(i) && oneright());
+ curwin->w_valid &= ~VALID_WCOL;
+ }
+ curwin->w_set_curswant = true;
+}
+
+/// "g_": to the last non-blank character in the line or <count> lines downward.
+static void nv_g_underscore_cmd(cmdarg_T *cap)
+{
+ cap->oap->motion_type = kMTCharWise;
+ cap->oap->inclusive = true;
+ curwin->w_curswant = MAXCOL;
+ if (cursor_down(cap->count1 - 1, cap->oap->op_type == OP_NOP) == false) {
+ clearopbeep(cap->oap);
+ return;
+ }
+
+ char_u *ptr = get_cursor_line_ptr();
+
+ // In Visual mode we may end up after the line.
+ if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL) {
+ curwin->w_cursor.col--;
+ }
+
+ // Decrease the cursor column until it's on a non-blank.
+ while (curwin->w_cursor.col > 0 && ascii_iswhite(ptr[curwin->w_cursor.col])) {
+ curwin->w_cursor.col--;
+ }
+ curwin->w_set_curswant = true;
+ adjust_for_sel(cap);
+}
+
+/// "g$" : Like "$" but for screen lines.
+static void nv_g_dollar_cmd(cmdarg_T *cap)
+{
+ oparg_T *oap = cap->oap;
+ int i;
+ int col_off = curwin_col_off();
+
+ oap->motion_type = kMTCharWise;
+ oap->inclusive = true;
+ if (curwin->w_p_wrap && curwin->w_width_inner != 0) {
+ curwin->w_curswant = MAXCOL; // so we stay at the end
+ if (cap->count1 == 1) {
+ int width1 = curwin->w_width_inner - col_off;
+ int width2 = width1 + curwin_col_off2();
+
+ validate_virtcol();
+ i = width1 - 1;
+ if (curwin->w_virtcol >= (colnr_T)width1) {
+ i += ((curwin->w_virtcol - width1) / width2 + 1) * width2;
+ }
+ coladvance((colnr_T)i);
+
+ // Make sure we stick in this column.
+ validate_virtcol();
+ curwin->w_curswant = curwin->w_virtcol;
+ curwin->w_set_curswant = false;
+ if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) {
+ // Check for landing on a character that got split at
+ // the end of the line. We do not want to advance to
+ // the next screen line.
+ if (curwin->w_virtcol > (colnr_T)i) {
+ curwin->w_cursor.col--;
+ }
+ }
+ } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false) {
+ clearopbeep(oap);
+ }
+ } else {
+ if (cap->count1 > 1) {
+ // if it fails, let the cursor still move to the last char
+ (void)cursor_down(cap->count1 - 1, false);
+ }
+ i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1;
+ coladvance((colnr_T)i);
+
+ // if the character doesn't fit move one back
+ if (curwin->w_cursor.col > 0 && utf_ptr2cells((const char *)get_cursor_pos_ptr()) > 1) {
+ colnr_T vcol;
+
+ getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
+ if (vcol >= curwin->w_leftcol + curwin->w_width - col_off) {
+ curwin->w_cursor.col--;
+ }
+ }
+
+ // Make sure we stick in this column.
+ validate_virtcol();
+ curwin->w_curswant = curwin->w_virtcol;
+ curwin->w_set_curswant = false;
+ }
+}
+
+/// "gi": start Insert at the last position.
+static void nv_gi_cmd(cmdarg_T *cap)
+{
+ if (curbuf->b_last_insert.mark.lnum != 0) {
+ curwin->w_cursor = curbuf->b_last_insert.mark;
+ check_cursor_lnum();
+ int i = (int)STRLEN(get_cursor_line_ptr());
+ if (curwin->w_cursor.col > (colnr_T)i) {
+ if (virtual_active()) {
+ curwin->w_cursor.coladd += curwin->w_cursor.col - i;
+ }
+ curwin->w_cursor.col = i;
+ }
+ }
+ cap->cmdchar = 'i';
+ nv_edit(cap);
+}
+
/// Commands starting with "g".
static void nv_g_cmd(cmdarg_T *cap)
{
oparg_T *oap = cap->oap;
- pos_T tpos;
int i;
- bool flag = false;
switch (cap->nchar) {
// "g^A/g^X": Sequentially increment visually selected region.
@@ -5920,61 +6179,9 @@ static void nv_g_cmd(cmdarg_T *cap)
break;
// "gv": Reselect the previous Visual area. If Visual already active,
- // exchange previous and current Visual area.
+ // exchange previous and current Visual area.
case 'v':
- if (checkclearop(oap)) {
- break;
- }
-
- if (curbuf->b_visual.vi_start.lnum == 0
- || curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count
- || curbuf->b_visual.vi_end.lnum == 0) {
- beep_flush();
- } else {
- // set w_cursor to the start of the Visual area, tpos to the end
- if (VIsual_active) {
- i = VIsual_mode;
- VIsual_mode = curbuf->b_visual.vi_mode;
- curbuf->b_visual.vi_mode = i;
- curbuf->b_visual_mode_eval = i;
- i = curwin->w_curswant;
- curwin->w_curswant = curbuf->b_visual.vi_curswant;
- curbuf->b_visual.vi_curswant = i;
-
- tpos = curbuf->b_visual.vi_end;
- curbuf->b_visual.vi_end = curwin->w_cursor;
- curwin->w_cursor = curbuf->b_visual.vi_start;
- curbuf->b_visual.vi_start = VIsual;
- } else {
- VIsual_mode = curbuf->b_visual.vi_mode;
- curwin->w_curswant = curbuf->b_visual.vi_curswant;
- tpos = curbuf->b_visual.vi_end;
- curwin->w_cursor = curbuf->b_visual.vi_start;
- }
-
- VIsual_active = true;
- VIsual_reselect = true;
-
- // Set Visual to the start and w_cursor to the end of the Visual
- // area. Make sure they are on an existing character.
- check_cursor();
- VIsual = curwin->w_cursor;
- curwin->w_cursor = tpos;
- check_cursor();
- update_topline(curwin);
- // When called from normal "g" command: start Select mode when
- // 'selectmode' contains "cmd". When called for K_SELECT, always
- // start Select mode.
- if (cap->arg) {
- VIsual_select = true;
- VIsual_select_reg = 0;
- } else {
- may_start_select('c');
- }
- setmouse();
- redraw_curbuf_later(INVERTED);
- showmode();
- }
+ nv_gv_cmd(cap);
break;
// "gV": Don't reselect the previous Visual area after a Select mode mapping of menu.
case 'V':
@@ -6040,47 +6247,14 @@ static void nv_g_cmd(cmdarg_T *cap)
nv_join(cap);
break;
- // "g0", "g^" and "g$": Like "0", "^" and "$" but for screen lines.
+ // "g0", "g^" : Like "0" and "^" but for screen lines.
// "gm": middle of "g0" and "g$".
case '^':
- flag = true;
- FALLTHROUGH;
-
case '0':
case 'm':
case K_HOME:
case K_KHOME:
- oap->motion_type = kMTCharWise;
- oap->inclusive = false;
- if (curwin->w_p_wrap
- && curwin->w_width_inner != 0) {
- int width1 = curwin->w_width_inner - curwin_col_off();
- int width2 = width1 + curwin_col_off2();
-
- validate_virtcol();
- i = 0;
- if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0) {
- i = (curwin->w_virtcol - width1) / width2 * width2 + width1;
- }
- } else {
- i = curwin->w_leftcol;
- }
- // Go to the middle of the screen line. When 'number' or
- // 'relativenumber' is on and lines are wrapping the middle can be more
- // to the left.
- if (cap->nchar == 'm') {
- i += (curwin->w_width_inner - curwin_col_off()
- + ((curwin->w_p_wrap && i > 0)
- ? curwin_col_off2() : 0)) / 2;
- }
- coladvance((colnr_T)i);
- if (flag) {
- do {
- i = gchar_cursor();
- } while (ascii_iswhite(i) && oneright());
- curwin->w_valid &= ~VALID_WCOL;
- }
- curwin->w_set_curswant = true;
+ nv_g_home_m_cmd(cap);
break;
case 'M':
@@ -6095,94 +6269,17 @@ static void nv_g_cmd(cmdarg_T *cap)
curwin->w_set_curswant = true;
break;
+ // "g_": to the last non-blank character in the line or <count> lines downward.
case '_':
- // "g_": to the last non-blank character in the line or <count> lines downward.
- cap->oap->motion_type = kMTCharWise;
- cap->oap->inclusive = true;
- curwin->w_curswant = MAXCOL;
- if (cursor_down(cap->count1 - 1,
- cap->oap->op_type == OP_NOP) == false) {
- clearopbeep(cap->oap);
- } else {
- char_u *ptr = get_cursor_line_ptr();
-
- // In Visual mode we may end up after the line.
- if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL) {
- curwin->w_cursor.col--;
- }
-
- // Decrease the cursor column until it's on a non-blank.
- while (curwin->w_cursor.col > 0
- && ascii_iswhite(ptr[curwin->w_cursor.col])) {
- curwin->w_cursor.col--;
- }
- curwin->w_set_curswant = true;
- adjust_for_sel(cap);
- }
+ nv_g_underscore_cmd(cap);
break;
+ // "g$" : Like "$" but for screen lines.
case '$':
case K_END:
- case K_KEND: {
- int col_off = curwin_col_off();
-
- oap->motion_type = kMTCharWise;
- oap->inclusive = true;
- if (curwin->w_p_wrap
- && curwin->w_width_inner != 0) {
- curwin->w_curswant = MAXCOL; // so we stay at the end
- if (cap->count1 == 1) {
- int width1 = curwin->w_width_inner - col_off;
- int width2 = width1 + curwin_col_off2();
-
- validate_virtcol();
- i = width1 - 1;
- if (curwin->w_virtcol >= (colnr_T)width1) {
- i += ((curwin->w_virtcol - width1) / width2 + 1)
- * width2;
- }
- coladvance((colnr_T)i);
-
- // Make sure we stick in this column.
- validate_virtcol();
- curwin->w_curswant = curwin->w_virtcol;
- curwin->w_set_curswant = false;
- if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) {
- // Check for landing on a character that got split at
- // the end of the line. We do not want to advance to
- // the next screen line.
- if (curwin->w_virtcol > (colnr_T)i) {
- curwin->w_cursor.col--;
- }
- }
- } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false) {
- clearopbeep(oap);
- }
- } else {
- if (cap->count1 > 1) {
- // if it fails, let the cursor still move to the last char
- (void)cursor_down(cap->count1 - 1, false);
- }
- i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1;
- coladvance((colnr_T)i);
-
- // if the character doesn't fit move one back
- if (curwin->w_cursor.col > 0 && utf_ptr2cells((const char *)get_cursor_pos_ptr()) > 1) {
- colnr_T vcol;
-
- getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
- if (vcol >= curwin->w_leftcol + curwin->w_width - col_off) {
- curwin->w_cursor.col--;
- }
- }
-
- // Make sure we stick in this column.
- validate_virtcol();
- curwin->w_curswant = curwin->w_virtcol;
- curwin->w_set_curswant = false;
- }
- }
- break;
+ case K_KEND:
+ nv_g_dollar_cmd(cap);
+ break;
// "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
case '*':
@@ -6213,19 +6310,7 @@ static void nv_g_cmd(cmdarg_T *cap)
// "gi": start Insert at the last position.
case 'i':
- if (curbuf->b_last_insert.mark.lnum != 0) {
- curwin->w_cursor = curbuf->b_last_insert.mark;
- check_cursor_lnum();
- i = (int)STRLEN(get_cursor_line_ptr());
- if (curwin->w_cursor.col > (colnr_T)i) {
- if (virtual_active()) {
- curwin->w_cursor.coladd += curwin->w_cursor.col - i;
- }
- curwin->w_cursor.col = i;
- }
- }
- cap->cmdchar = 'i';
- nv_edit(cap);
+ nv_gi_cmd(cap);
break;
// "gI": Start insert in column 1.
@@ -6284,14 +6369,14 @@ static void nv_g_cmd(cmdarg_T *cap)
nv_goto(cap);
break;
- // Two-character operators:
- // "gq" Format text
- // "gw" Format text and keep cursor position
- // "g~" Toggle the case of the text.
- // "gu" Change text to lower case.
- // "gU" Change text to upper case.
- // "g?" rot13 encoding
- // "g@" call 'operatorfunc'
+ // Two-character operators:
+ // "gq" Format text
+ // "gw" Format text and keep cursor position
+ // "g~" Toggle the case of the text.
+ // "gu" Change text to lower case.
+ // "gU" Change text to upper case.
+ // "g?" rot13 encoding
+ // "g@" call 'operatorfunc'
case 'q':
case 'w':
oap->cursor_start = curwin->w_cursor;
@@ -6472,8 +6557,7 @@ static void nv_redo_or_register(cmdarg_T *cap)
static void nv_Undo(cmdarg_T *cap)
{
// In Visual mode and typing "gUU" triggers an operator
- if (cap->oap->op_type == OP_UPPER
- || VIsual_active) {
+ if (cap->oap->op_type == OP_UPPER || VIsual_active) {
// translate "gUU" to "gUgU"
cap->cmdchar = 'g';
cap->nchar = 'U';
@@ -6488,9 +6572,7 @@ static void nv_Undo(cmdarg_T *cap)
/// single character.
static void nv_tilde(cmdarg_T *cap)
{
- if (!p_to
- && !VIsual_active
- && cap->oap->op_type != OP_TILDE) {
+ if (!p_to && !VIsual_active && cap->oap->op_type != OP_TILDE) {
if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
clearopbeep(cap->oap);
return;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 2f29e94ff1..21ab26898e 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -6627,9 +6627,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
switch (oap->op_type) {
case OP_LSHIFT:
case OP_RSHIFT:
- op_shift(oap, true,
- oap->is_VIsual ? (int)cap->count1 :
- 1);
+ op_shift(oap, true, oap->is_VIsual ? (int)cap->count1 : 1);
auto_format(false, true);
break;
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 15e8bd9e13..a915594e26 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -514,7 +514,7 @@ void last_pat_prog(regmmatch_T *regmatch)
--emsg_off;
}
-/// lowest level search function.
+/// Lowest level search function.
/// Search for 'count'th occurrence of pattern "pat" in direction "dir".
/// Start at position "pos" and return the found position in "pos".
///
@@ -3991,8 +3991,7 @@ static int find_prev_quote(char_u *line, int col_start, int quotechar, char_u *e
}
if (n & 1) {
col_start -= n; // uneven number of escape chars, skip it
- } else if (line[col_start] ==
- quotechar) {
+ } else if (line[col_start] == quotechar) {
break;
}
}
@@ -4115,8 +4114,7 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
col_end = curwin->w_cursor.col;
}
}
- } else if (line[col_start] == quotechar
- || !vis_empty) {
+ } else if (line[col_start] == quotechar || !vis_empty) {
int first_col = col_start;
if (!vis_empty) {
@@ -4185,9 +4183,8 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
// Set start position. After vi" another i" must include the ".
// For v2i" include the quotes.
- if (!include && count < 2
- && (vis_empty || !inside_quotes)) {
- ++col_start;
+ if (!include && count < 2 && (vis_empty || !inside_quotes)) {
+ col_start++;
}
curwin->w_cursor.col = col_start;
if (VIsual_active) {
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 82ba2cfd48..f146726b7b 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -1422,9 +1422,16 @@ func Test_normal27_bracket()
call assert_equal(5, line('.'))
call assert_equal(3, col('.'))
- " No mark after line 21, cursor moves to first non blank on current line
+ " No mark before line 1, cursor moves to first non-blank on current line
+ 1
+ norm! 5|['
+ call assert_equal(' 1 b', getline('.'))
+ call assert_equal(1, line('.'))
+ call assert_equal(3, col('.'))
+
+ " No mark after line 21, cursor moves to first non-blank on current line
21
- norm! $]'
+ norm! 5|]'
call assert_equal(' 21 b', getline('.'))
call assert_equal(21, line('.'))
call assert_equal(3, col('.'))
@@ -1441,12 +1448,40 @@ func Test_normal27_bracket()
call assert_equal(20, line('.'))
call assert_equal(8, col('.'))
+ " No mark before line 1, cursor does not move
+ 1
+ norm! 5|[`
+ call assert_equal(' 1 b', getline('.'))
+ call assert_equal(1, line('.'))
+ call assert_equal(5, col('.'))
+
+ " No mark after line 21, cursor does not move
+ 21
+ norm! 5|]`
+ call assert_equal(' 21 b', getline('.'))
+ call assert_equal(21, line('.'))
+ call assert_equal(5, col('.'))
+
+ " Count too large for [`
+ " cursor moves to first lowercase mark
+ norm! 99[`
+ call assert_equal(' 1 b', getline('.'))
+ call assert_equal(1, line('.'))
+ call assert_equal(7, col('.'))
+
+ " Count too large for ]`
+ " cursor moves to last lowercase mark
+ norm! 99]`
+ call assert_equal(' 20 b', getline('.'))
+ call assert_equal(20, line('.'))
+ call assert_equal(8, col('.'))
+
" clean up
bw!
endfunc
+" Test for ( and ) sentence movements
func Test_normal28_parenthesis()
- " basic testing for ( and )
new
call append(0, ['This is a test. With some sentences!', '', 'Even with a question? And one more. And no sentence here'])