# HG changeset patch # User Bram Moolenaar # Date 1571923803 -7200 # Node ID 3b80bdbdc8324b07c088aadbb49c92a52d0b6457 # Parent d58c4ff92b3f92558f74646c23051b58514d462a patch 8.1.2207: "gn" doesn't work quite right Commit: https://github.com/vim/vim/commit/edaad6e0a0e3c1fcb6a5c2771e647c52475bb19c Author: Bram Moolenaar Date: Thu Oct 24 15:23:37 2019 +0200 patch 8.1.2207: "gn" doesn't work quite right Problem: "gn" doesn't work quite right. (Jaehwang Jerry Jung) Solution: Improve and simplify the search logic. (Christian Brabandt, closes #5103, closes #5075) diff --git a/src/search.c b/src/search.c --- a/src/search.c +++ b/src/search.c @@ -4676,7 +4676,73 @@ abort_search: #endif /* FEAT_TEXTOBJ */ -static int is_one_char(char_u *pattern, int move, pos_T *cur, int direction); +/* + * Check if the pattern is one character long or zero-width. + * If move is TRUE, check from the beginning of the buffer, else from position + * "cur". + * "direction" is FORWARD or BACKWARD. + * Returns TRUE, FALSE or -1 for failure. + */ + static int +is_zero_width(char_u *pattern, int move, pos_T *cur, int direction) +{ + regmmatch_T regmatch; + int nmatched = 0; + int result = -1; + pos_T pos; + int save_called_emsg = called_emsg; + int flag = 0; + + if (pattern == NULL) + pattern = spats[last_idx].pat; + + if (search_regcomp(pattern, RE_SEARCH, RE_SEARCH, + SEARCH_KEEP, ®match) == FAIL) + return -1; + + // init startcol correctly + regmatch.startpos[0].col = -1; + // move to match + if (move) + { + CLEAR_POS(&pos); + } + else + { + pos = *cur; + // accept a match at the cursor position + flag = SEARCH_START; + } + + if (searchit(curwin, curbuf, &pos, NULL, direction, pattern, 1, + SEARCH_KEEP + flag, RE_SEARCH, NULL) != FAIL) + { + // Zero-width pattern should match somewhere, then we can check if + // start and end are in the same position. + called_emsg = FALSE; + do + { + regmatch.startpos[0].col++; + nmatched = vim_regexec_multi(®match, curwin, curbuf, + pos.lnum, regmatch.startpos[0].col, NULL, NULL); + if (nmatched != 0) + break; + } while (direction == FORWARD ? regmatch.startpos[0].col < pos.col + : regmatch.startpos[0].col > pos.col); + + if (!called_emsg) + { + result = (nmatched != 0 + && regmatch.startpos[0].lnum == regmatch.endpos[0].lnum + && regmatch.startpos[0].col == regmatch.endpos[0].col); + } + } + + called_emsg |= save_called_emsg; + vim_regfree(regmatch.regprog); + return result; +} + /* * Find next search match under cursor, cursor at end. @@ -4697,7 +4763,7 @@ current_search( char_u old_p_ws = p_ws; int flags = 0; pos_T save_VIsual = VIsual; - int one_char; + int zero_width; /* wrapping should not occur */ p_ws = FALSE; @@ -4706,29 +4772,20 @@ current_search( if (VIsual_active && *p_sel == 'e' && LT_POS(VIsual, curwin->w_cursor)) dec_cursor(); + orig_pos = pos = curwin->w_cursor; if (VIsual_active) { - orig_pos = curwin->w_cursor; - - pos = curwin->w_cursor; - - /* make sure, searching further will extend the match */ - if (VIsual_active) - { - if (forward) - incl(&pos); - else - decl(&pos); - } + if (forward) + incl(&pos); + else + decl(&pos); } - else - orig_pos = pos = curwin->w_cursor; /* Is the pattern is zero-width?, this time, don't care about the direction */ - one_char = is_one_char(spats[last_idx].pat, TRUE, &curwin->w_cursor, + zero_width = is_zero_width(spats[last_idx].pat, TRUE, &curwin->w_cursor, FORWARD); - if (one_char == -1) + if (zero_width == -1) { p_ws = old_p_ws; return FAIL; /* pattern not found */ @@ -4747,7 +4804,7 @@ current_search( dir = !i; flags = 0; - if (!dir && !one_char) + if (!dir && !zero_width) flags = SEARCH_END; end_pos = pos; @@ -4784,7 +4841,6 @@ current_search( ml_get(curwin->w_buffer->b_ml.ml_line_count)); } } - p_ws = old_p_ws; } start_pos = pos; @@ -4797,10 +4853,11 @@ current_search( curwin->w_cursor = end_pos; if (LT_POS(VIsual, end_pos)) dec_cursor(); + else if (VIsual_active && LT_POS(curwin->w_cursor, VIsual)) + curwin->w_cursor = pos; // put the cursor on the start of the match VIsual_active = TRUE; VIsual_mode = 'v'; - redraw_curbuf_later(INVERTED); /* update the inversion */ if (*p_sel == 'e') { /* Correction for exclusive selection depends on the direction. */ @@ -4828,77 +4885,6 @@ current_search( return OK; } -/* - * Check if the pattern is one character long or zero-width. - * If move is TRUE, check from the beginning of the buffer, else from position - * "cur". - * "direction" is FORWARD or BACKWARD. - * Returns TRUE, FALSE or -1 for failure. - */ - static int -is_one_char(char_u *pattern, int move, pos_T *cur, int direction) -{ - regmmatch_T regmatch; - int nmatched = 0; - int result = -1; - pos_T pos; - int save_called_emsg = called_emsg; - int flag = 0; - - if (pattern == NULL) - pattern = spats[last_idx].pat; - - if (search_regcomp(pattern, RE_SEARCH, RE_SEARCH, - SEARCH_KEEP, ®match) == FAIL) - return -1; - - /* init startcol correctly */ - regmatch.startpos[0].col = -1; - /* move to match */ - if (move) - { - CLEAR_POS(&pos); - } - else - { - pos = *cur; - /* accept a match at the cursor position */ - flag = SEARCH_START; - } - - if (searchit(curwin, curbuf, &pos, NULL, direction, pattern, 1, - SEARCH_KEEP + flag, RE_SEARCH, NULL) != FAIL) - { - /* Zero-width pattern should match somewhere, then we can check if - * start and end are in the same position. */ - called_emsg = FALSE; - do - { - regmatch.startpos[0].col++; - nmatched = vim_regexec_multi(®match, curwin, curbuf, - pos.lnum, regmatch.startpos[0].col, NULL, NULL); - if (nmatched != 0) - break; - } while (direction == FORWARD ? regmatch.startpos[0].col < pos.col - : regmatch.startpos[0].col > pos.col); - - if (!called_emsg) - { - result = (nmatched != 0 - && regmatch.startpos[0].lnum == regmatch.endpos[0].lnum - && regmatch.startpos[0].col == regmatch.endpos[0].col); - // one char width - if (!result && nmatched != 0 - && inc(&pos) >= 0 && pos.col == regmatch.endpos[0].col) - result = TRUE; - } - } - - called_emsg |= save_called_emsg; - vim_regfree(regmatch.regprog); - return result; -} - #if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(FEAT_TEXTOBJ) \ || defined(PROTO) /* diff --git a/src/testdir/test_gn.vim b/src/testdir/test_gn.vim --- a/src/testdir/test_gn.vim +++ b/src/testdir/test_gn.vim @@ -128,6 +128,27 @@ func Test_gn_command() call assert_equal([' nnoremap', '', 'match'], getline(1,'$')) sil! %d_ + " make sure it works correctly for one-char wide search items + call setline('.', ['abcdefghi']) + let @/ = 'a' + exe "norm! 0fhvhhgNgU" + call assert_equal(['ABCDEFGHi'], getline(1,'$')) + call setline('.', ['abcdefghi']) + let @/ = 'b' + exe "norm! 0fhvhhgngU" + call assert_equal(['abcdefghi'], getline(1,'$')) + sil! %d _ + call setline('.', ['abcdefghi']) + let @/ = 'f' + exe "norm! 0vllgngU" + call assert_equal(['ABCDEFghi'], getline(1,'$')) + sil! %d _ + call setline('.', ['12345678']) + let @/ = '5' + norm! gg0f7vhhhhgnd + call assert_equal(['12348'], getline(1,'$')) + sil! %d _ + set wrapscan&vim endfu diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2207, +/**/ 2206, /**/ 2205,