From 8ac47485777c2eaee9fe7aa93d1ed6c5748d2da9 Mon Sep 17 00:00:00 2001 From: Aufar Gilbran Date: Tue, 4 Aug 2020 23:51:53 +0800 Subject: vim-patch:8.1.0265: the getcmdline() function is way too big Problem: The getcmdline() function is way too big. Solution: Factor out the incremental search highlighting. https://github.com/vim/vim/commit/0ee81cb63876e6ec1b2a6e0125295b43f1c63c7d --- src/nvim/ex_getln.c | 407 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 232 insertions(+), 175 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 93684e8606..8ef1540737 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -137,6 +137,7 @@ struct cmdline_info { /// Last value of prompt_id, incremented when doing new prompt static unsigned last_prompt_id = 0; +// Struct to store the viewstate during 'incsearch' highlighting. typedef struct { colnr_T vs_curswant; colnr_T vs_leftcol; @@ -146,6 +147,18 @@ typedef struct { int vs_empty_rows; } viewstate_T; +// Struct to store the state of 'incsearch' highlighting. +typedef struct { + pos_T search_start; // where 'incsearch' starts searching + pos_T save_cursor; + viewstate_T init_viewstate; + viewstate_T old_viewstate; + pos_T match_start; + pos_T match_end; + bool did_incsearch; + bool incsearch_postponed; +} incsearch_state_T; + typedef struct command_line_state { VimState state; int firstc; @@ -159,14 +172,7 @@ typedef struct command_line_state { int save_hiscnt; // history line before attempting // to jump to next match int histype; // history type to be used - pos_T search_start; // where 'incsearch' starts searching - pos_T save_cursor; - viewstate_T init_viewstate; - viewstate_T old_viewstate; - pos_T match_start; - pos_T match_end; - int did_incsearch; - int incsearch_postponed; + incsearch_state_T is_state; int did_wild_list; // did wild_list() recently int wim_index; // index in wim_flags[] int res; @@ -252,6 +258,184 @@ static void restore_viewstate(viewstate_T *vs) curwin->w_empty_rows = vs->vs_empty_rows; } +static void init_incsearch_state(incsearch_state_T *s) +{ + s->match_start = curwin->w_cursor; + s->did_incsearch = false; + s->incsearch_postponed = false; + clearpos(&s->match_end); + s->save_cursor = curwin->w_cursor; // may be restored later + s->search_start = curwin->w_cursor; + save_viewstate(&s->init_viewstate); + save_viewstate(&s->old_viewstate); +} + +// Return true when 'incsearch' highlighting is to be done. +static bool do_incsearch_highlighting(int firstc) +{ + return p_is && !cmd_silent && (firstc == '/' || firstc == '?'); +} + +// May do 'incsearch' highlighting if desired. +static void may_do_incsearch_highlighting(int firstc, long count, + incsearch_state_T *s) +{ + pos_T end_pos; + proftime_T tm; + searchit_arg_T sia; + + if (!do_incsearch_highlighting(firstc)) { + return; + } + + // if there is a character waiting, search and redraw later + if (char_avail()) { + s->incsearch_postponed = true; + return; + } + s->incsearch_postponed = false; + curwin->w_cursor = s->search_start; // start at old position + save_last_search_pattern(); + int i; + + // If there is no command line, don't do anything + if (ccline.cmdlen == 0) { + i = 0; + set_no_hlsearch(true); // turn off previous highlight + redraw_all_later(SOME_VALID); + } else { + int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK; + ui_busy_start(); + ui_flush(); + emsg_off++; // So it doesn't beep if bad expr + // Set the time limit to half a second. + tm = profile_setlimit(500L); + if (!p_hls) { + search_flags += SEARCH_KEEP; + } + memset(&sia, 0, sizeof(sia)); + sia.sa_tm = &tm; + i = do_search(NULL, firstc, ccline.cmdbuff, count, + search_flags, &sia); + emsg_off--; + // if interrupted while searching, behave like it failed + if (got_int) { + (void)vpeekc(); // remove from input stream + got_int = false; // don't abandon the command line + i = 0; + } else if (char_avail()) { + // cancelled searching because a char was typed + s->incsearch_postponed = true; + } + ui_busy_stop(); + } + + if (i != 0) { + highlight_match = true; // highlight position + } else { + highlight_match = false; // remove highlight + } + + // first restore the old curwin values, so the screen is + // positioned in the same way as the actual search command + restore_viewstate(&s->old_viewstate); + changed_cline_bef_curs(); + update_topline(); + + if (i != 0) { + pos_T save_pos = curwin->w_cursor; + + s->match_start = curwin->w_cursor; + set_search_match(&curwin->w_cursor); + validate_cursor(); + end_pos = curwin->w_cursor; + s->match_end = end_pos; + curwin->w_cursor = save_pos; + } else { + end_pos = curwin->w_cursor; // shutup gcc 4 + } + + // Disable 'hlsearch' highlighting if the pattern matches + // everything. Avoids a flash when typing "foo\|". + if (empty_pattern(ccline.cmdbuff)) { + set_no_hlsearch(true); + } + + validate_cursor(); + // May redraw the status line to show the cursor position. + if (p_ru && curwin->w_status_height > 0) { + curwin->w_redr_status = true; + } + + update_screen(SOME_VALID); + restore_last_search_pattern(); + + // Leave it at the end to make CTRL-R CTRL-W work. + if (i != 0) { + curwin->w_cursor = end_pos; + } + + msg_starthere(); + redrawcmdline(); + s->did_incsearch = true; +} + +// When CTRL-L typed: add character from the match to the pattern. +// May set "*c" to the added character. +// Return OK when calling command_line_not_changed. +static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s) +{ + // Add a character from under the cursor for 'incsearch' + if (!do_incsearch_highlighting(firstc)) { + return FAIL; + } + + if (s->did_incsearch) { + curwin->w_cursor = s->match_end; + if (!equalpos(curwin->w_cursor, s->search_start)) { + *c = gchar_cursor(); + // If 'ignorecase' and 'smartcase' are set and the + // command line has no uppercase characters, convert + // the character to lowercase + if (p_ic && p_scs + && !pat_has_uppercase(ccline.cmdbuff)) { + *c = mb_tolower(*c); + } + if (*c != NUL) { + if (*c == firstc + || vim_strchr((char_u *)(p_magic ? "\\~^$.*[" : "\\^$"), *c) + != NULL) { + // put a backslash before special characters + stuffcharReadbuff(*c); + *c = '\\'; + } + return FAIL; + } + } + } + return OK; +} + +static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s) +{ + if (s->did_incsearch) { + if (gotesc) { + curwin->w_cursor = s->save_cursor; + } else { + if (!equalpos(s->save_cursor, s->search_start)) { + // put the '" mark at the original position + curwin->w_cursor = s->save_cursor; + setpcmark(); + } + curwin->w_cursor = s->search_start; // -V519 + } + restore_viewstate(&s->old_viewstate); + highlight_match = false; + validate_cursor(); // needed for TAB + redraw_all_later(SOME_VALID); + } +} + /// Internal entry point for cmdline mode. /// /// caller must use save_cmdline and restore_cmdline. Best is to use @@ -269,11 +453,10 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) .save_msg_scroll = msg_scroll, .save_State = State, .ignore_drag_release = true, - .match_start = curwin->w_cursor, }; CommandLineState *s = &state; s->save_p_icm = vim_strsave(p_icm); - save_viewstate(&state.init_viewstate); + init_incsearch_state(&s->is_state); if (s->firstc == -1) { s->firstc = NUL; @@ -288,10 +471,6 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) ccline.prompt_id = last_prompt_id++; ccline.level = cmdline_level; ccline.overstrike = false; // always start in insert mode - clearpos(&s->match_end); - s->save_cursor = curwin->w_cursor; // may be restored later - s->search_start = curwin->w_cursor; - save_viewstate(&state.old_viewstate); assert(indent >= 0); @@ -455,22 +634,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) close_preview_windows(); } - if (s->did_incsearch) { - if (s->gotesc) { - curwin->w_cursor = s->save_cursor; - } else { - if (!equalpos(s->save_cursor, s->search_start)) { - // put the '" mark at the original position - curwin->w_cursor = s->save_cursor; - setpcmark(); - } - curwin->w_cursor = s->search_start; // -V519 - } - restore_viewstate(&s->old_viewstate); - highlight_match = false; - validate_cursor(); // needed for TAB - redraw_all_later(SOME_VALID); - } + finish_incsearch_highlighting(s->gotesc, &s->is_state); if (ccline.cmdbuff != NULL) { // Put line in history buffer (":" and "=" only when it was typed). @@ -1075,8 +1239,20 @@ static int command_line_execute(VimState *state, int key) return command_line_handle_key(s); } -static void command_line_next_incsearch(CommandLineState *s, bool next_match) +// May adjust 'incsearch' highlighting for typing CTRL-G and CTRL-T, go to next +// or previous match. +// Returns FAIL when calling command_line_not_changed. +static int may_do_command_line_next_incsearch(int firstc, long count, + incsearch_state_T *s, + bool next_match) { + if (!do_incsearch_highlighting(firstc)) { + return OK; + } + if (ccline.cmdlen == 0) { + return FAIL; + } + ui_busy_start(); ui_flush(); @@ -1085,7 +1261,7 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match) int search_flags = SEARCH_NOOF; - if (s->firstc == ccline.cmdbuff[0]) { + if (firstc == ccline.cmdbuff[0]) { pat = last_search_pattern(); } else { pat = ccline.cmdbuff; @@ -1110,7 +1286,7 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match) emsg_off++; int found = searchit(curwin, curbuf, &t, NULL, next_match ? FORWARD : BACKWARD, - pat, s->count, search_flags, + pat, count, search_flags, RE_SEARCH, NULL); emsg_off--; ui_busy_stop(); @@ -1118,13 +1294,13 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match) s->search_start = s->match_start; s->match_end = t; s->match_start = t; - if (!next_match && s->firstc == '/') { + if (!next_match && firstc == '/') { // move just before the current match, so that // when nv_search finishes the cursor will be // put back on the match s->search_start = t; (void)decl(&s->search_start); - } else if (next_match && s->firstc == '?') { + } else if (next_match && firstc == '?') { // move just after the current match, so that // when nv_search finishes the cursor will be // put back on the match @@ -1134,7 +1310,7 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match) if (lt(t, s->search_start) && next_match) { // wrap around s->search_start = t; - if (s->firstc == '?') { + if (firstc == '?') { (void)incl(&s->search_start); } else { (void)decl(&s->search_start); @@ -1154,7 +1330,7 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match) vim_beep(BO_ERROR); } restore_last_search_pattern(); - return; + return FAIL; } static void command_line_next_histidx(CommandLineState *s, bool next_match) @@ -1265,10 +1441,10 @@ static int command_line_handle_key(CommandLineState *s) // Truncate at the end, required for multi-byte chars. ccline.cmdbuff[ccline.cmdlen] = NUL; if (ccline.cmdlen == 0) { - s->search_start = s->save_cursor; + s->is_state.search_start = s->is_state.save_cursor; // save view settings, so that the screen won't be restored at the // wrong position - s->old_viewstate = s->init_viewstate; + s->is_state.old_viewstate = s->is_state.init_viewstate; } redrawcmd(); } else if (ccline.cmdlen == 0 && s->c != Ctrl_W @@ -1287,7 +1463,7 @@ static int command_line_handle_key(CommandLineState *s) } msg_putchar(' '); // delete ':' } - s->search_start = s->save_cursor; + s->is_state.search_start = s->is_state.save_cursor; redraw_cmdline = true; return 0; // back to cmd mode } @@ -1337,7 +1513,7 @@ static int command_line_handle_key(CommandLineState *s) // Truncate at the end, required for multi-byte chars. ccline.cmdbuff[ccline.cmdlen] = NUL; if (ccline.cmdlen == 0) { - s->search_start = s->save_cursor; + s->is_state.search_start = s->is_state.save_cursor; } redrawcmd(); return command_line_changed(s); @@ -1565,31 +1741,7 @@ static int command_line_handle_key(CommandLineState *s) return command_line_changed(s); case Ctrl_L: - if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) { - // Add a character from under the cursor for 'incsearch' - if (s->did_incsearch) { - curwin->w_cursor = s->match_end; - if (!equalpos(curwin->w_cursor, s->search_start)) { - s->c = gchar_cursor(); - // If 'ignorecase' and 'smartcase' are set and the - // command line has no uppercase characters, convert - // the character to lowercase - if (p_ic && p_scs - && !pat_has_uppercase(ccline.cmdbuff)) { - s->c = mb_tolower(s->c); - } - if (s->c != NUL) { - if (s->c == s->firstc - || vim_strchr((char_u *)(p_magic ? "\\~^$.*[" : "\\^$"), s->c) - != NULL) { - // put a backslash before special characters - stuffcharReadbuff(s->c); - s->c = '\\'; - } - break; - } - } - } + if (may_add_char_to_search(s->firstc, &s->c, &s->is_state) == OK) { return command_line_not_changed(s); } @@ -1703,10 +1855,8 @@ static int command_line_handle_key(CommandLineState *s) case Ctrl_G: // next match case Ctrl_T: // previous match - if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) { - if (ccline.cmdlen != 0) { - command_line_next_incsearch(s, s->c == Ctrl_G); - } + if (may_do_command_line_next_incsearch(s->firstc, s->count, &s->is_state, + s->c == Ctrl_G) == FAIL) { return command_line_not_changed(s); } break; @@ -1791,7 +1941,7 @@ static int command_line_not_changed(CommandLineState *s) // command line did not change. Then we only search and redraw if something // changed in the past. // Enter command_line_changed() when the command line did change. - if (!s->incsearch_postponed) { + if (!s->is_state.incsearch_postponed) { return 1; } return command_line_changed(s); @@ -1843,108 +1993,13 @@ static int command_line_changed(CommandLineState *s) } // 'incsearch' highlighting. - if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) { - pos_T end_pos; - proftime_T tm; - searchit_arg_T sia; - - // if there is a character waiting, search and redraw later - if (char_avail()) { - s->incsearch_postponed = true; - return 1; - } - s->incsearch_postponed = false; - curwin->w_cursor = s->search_start; // start at old position - save_last_search_pattern(); - int i; - - // If there is no command line, don't do anything - if (ccline.cmdlen == 0) { - i = 0; - set_no_hlsearch(true); // turn off previous highlight - redraw_all_later(SOME_VALID); - } else { - int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK; - ui_busy_start(); - ui_flush(); - ++emsg_off; // So it doesn't beep if bad expr - // Set the time limit to half a second. - tm = profile_setlimit(500L); - if (!p_hls) { - search_flags += SEARCH_KEEP; - } - memset(&sia, 0, sizeof(sia)); - sia.sa_tm = &tm; - i = do_search(NULL, s->firstc, ccline.cmdbuff, s->count, - search_flags, &sia); - emsg_off--; - // if interrupted while searching, behave like it failed - if (got_int) { - (void)vpeekc(); // remove from input stream - got_int = false; // don't abandon the command line - i = 0; - } else if (char_avail()) { - // cancelled searching because a char was typed - s->incsearch_postponed = true; - } - ui_busy_stop(); - } - - if (i != 0) { - highlight_match = true; // highlight position - } else { - highlight_match = false; // remove highlight - } - - // first restore the old curwin values, so the screen is - // positioned in the same way as the actual search command - restore_viewstate(&s->old_viewstate); - changed_cline_bef_curs(); - update_topline(); - - if (i != 0) { - pos_T save_pos = curwin->w_cursor; - - s->match_start = curwin->w_cursor; - set_search_match(&curwin->w_cursor); - validate_cursor(); - end_pos = curwin->w_cursor; - s->match_end = end_pos; - curwin->w_cursor = save_pos; - } else { - end_pos = curwin->w_cursor; // shutup gcc 4 - } - - // Disable 'hlsearch' highlighting if the pattern matches - // everything. Avoids a flash when typing "foo\|". - if (empty_pattern(ccline.cmdbuff)) { - set_no_hlsearch(true); - } - - validate_cursor(); - // May redraw the status line to show the cursor position. - if (p_ru && curwin->w_status_height > 0) { - curwin->w_redr_status = true; - } - - update_screen(SOME_VALID); - restore_last_search_pattern(); - - // Leave it at the end to make CTRL-R CTRL-W work. - if (i != 0) { - curwin->w_cursor = end_pos; - } - - msg_starthere(); - redrawcmdline(); - s->did_incsearch = true; - } else if (s->firstc == ':' - && current_sctx.sc_sid == 0 // only if interactive - && *p_icm != NUL // 'inccommand' is set - && curbuf->b_p_ma // buffer is modifiable - && cmdline_star == 0 // not typing a password - && cmd_can_preview(ccline.cmdbuff) - && !vpeekc_any()) { + if (s->firstc == ':' + && current_sctx.sc_sid == 0 // only if interactive + && *p_icm != NUL // 'inccommand' is set + && curbuf->b_p_ma // buffer is modifiable + && cmdline_star == 0 // not typing a password + && cmd_can_preview(ccline.cmdbuff) + && !vpeekc_any()) { // Show 'inccommand' preview. It works like this: // 1. Do the command. // 2. Command implementation detects CMDPREVIEW state, then: @@ -1958,8 +2013,8 @@ static int command_line_changed(CommandLineState *s) emsg_silent--; // Unblock error reporting // Restore the window "view". - curwin->w_cursor = s->save_cursor; - restore_viewstate(&s->old_viewstate); + curwin->w_cursor = s->is_state.save_cursor; + restore_viewstate(&s->is_state.old_viewstate); update_topline(); redrawcmdline(); @@ -1968,6 +2023,8 @@ static int command_line_changed(CommandLineState *s) State = (State & ~CMDPREVIEW); close_preview_windows(); update_screen(SOME_VALID); // Clear 'inccommand' preview. + } else { + may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state); } if (cmdmsg_rl || (p_arshape && !p_tbidi && enc_utf8)) { -- cgit From e8a8b9ed08405c830a049c4e43910c5ce9cdb669 Mon Sep 17 00:00:00 2001 From: Aufar Gilbran Date: Mon, 10 Aug 2020 03:02:54 +0800 Subject: vim-patch:8.1.0271: 'incsearch' doesn't work for :s, :g or :v Problem: 'incsearch' doesn't work for :s, :g or :v. Solution: Also use 'incsearch' for other commands that use a pattern. https://github.com/vim/vim/commit/b0acacd767a2b0618a7f3c08087708f4329580d0 --- src/nvim/ex_getln.c | 114 ++++++++++++++++++++++++++++++++++----- src/nvim/globals.h | 8 +-- src/nvim/screen.c | 6 +++ src/nvim/testdir/test_search.vim | 57 ++++++++++++++++++++ 4 files changed, 169 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 8ef1540737..47bfc89bdf 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -271,9 +271,68 @@ static void init_incsearch_state(incsearch_state_T *s) } // Return true when 'incsearch' highlighting is to be done. -static bool do_incsearch_highlighting(int firstc) +// Sets search_first_line and search_last_line to the address range. +static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s, + int *skiplen, int *patlen) { - return p_is && !cmd_silent && (firstc == '/' || firstc == '?'); + *skiplen = 0; + *patlen = ccline.cmdlen; + + if (p_is && !cmd_silent) { + // by default search all lines + search_first_line = 0; + search_last_line = MAXLNUM; + + if (firstc == '/' || firstc == '?') { + return true; + } + if (firstc == ':') { + char_u *cmd = skip_range(ccline.cmdbuff, NULL); + char_u *p; + int delim; + char_u *end; + + if (*cmd == 's' || *cmd == 'g' || *cmd == 'v') { + // Skip over "substitute" to find the pattern separator. + for (p = cmd; ASCII_ISALPHA(*p); p++) {} + if (*p != NUL) { + delim = *p++; + end = skip_regexp(p, delim, p_magic, NULL); + if (end > p) { + char_u *dummy; + exarg_T ea; + pos_T save_cursor = curwin->w_cursor; + + // found a non-empty pattern + *skiplen = (int)(p - ccline.cmdbuff); + *patlen = (int)(end - p); + + // parse the address range + memset(&ea, 0, sizeof(ea)); + ea.line1 = 1; + ea.line2 = 1; + ea.cmd = ccline.cmdbuff; + ea.addr_type = ADDR_LINES; + parse_cmd_address(&ea, &dummy); + curwin->w_cursor = s->search_start; + if (ea.addr_count > 0) { + search_first_line = ea.line1; + search_last_line = ea.line2; + } else if (*cmd == 's') { + // :s defaults to the current line + search_first_line = curwin->w_cursor.lnum; + search_last_line = curwin->w_cursor.lnum; + } + + curwin->w_cursor = save_cursor; + return true; + } + } + } + } + } + + return false; } // May do 'incsearch' highlighting if desired. @@ -283,8 +342,10 @@ static void may_do_incsearch_highlighting(int firstc, long count, pos_T end_pos; proftime_T tm; searchit_arg_T sia; + int skiplen, patlen; + char_u c; - if (!do_incsearch_highlighting(firstc)) { + if (!do_incsearch_highlighting(firstc, s, &skiplen, &patlen)) { return; } @@ -294,12 +355,20 @@ static void may_do_incsearch_highlighting(int firstc, long count, return; } s->incsearch_postponed = false; - curwin->w_cursor = s->search_start; // start at old position + + if (search_first_line == 0) { + // start at the original cursor position + curwin->w_cursor = s->search_start; + } else { + // start at the first line in the range + curwin->w_cursor.lnum = search_first_line; + curwin->w_cursor.col = 0; + } save_last_search_pattern(); int i; // If there is no command line, don't do anything - if (ccline.cmdlen == 0) { + if (patlen == 0) { i = 0; set_no_hlsearch(true); // turn off previous highlight redraw_all_later(SOME_VALID); @@ -313,11 +382,21 @@ static void may_do_incsearch_highlighting(int firstc, long count, if (!p_hls) { search_flags += SEARCH_KEEP; } + c = ccline.cmdbuff[skiplen + patlen]; + ccline.cmdbuff[skiplen + patlen] = NUL; memset(&sia, 0, sizeof(sia)); sia.sa_tm = &tm; - i = do_search(NULL, firstc, ccline.cmdbuff, count, + i = do_search(NULL, firstc == ':' ? '/' : firstc, + ccline.cmdbuff + skiplen, count, search_flags, &sia); + ccline.cmdbuff[skiplen + patlen] = c; emsg_off--; + if (curwin->w_cursor.lnum < search_first_line + || curwin->w_cursor.lnum > search_last_line) { + // match outside of address range + i = 0; + } + // if interrupted while searching, behave like it failed if (got_int) { (void)vpeekc(); // remove from input stream @@ -357,9 +436,12 @@ static void may_do_incsearch_highlighting(int firstc, long count, // Disable 'hlsearch' highlighting if the pattern matches // everything. Avoids a flash when typing "foo\|". + c = ccline.cmdbuff[skiplen + patlen]; + ccline.cmdbuff[skiplen + patlen] = NUL; if (empty_pattern(ccline.cmdbuff)) { set_no_hlsearch(true); } + ccline.cmdbuff[skiplen + patlen] = c; validate_cursor(); // May redraw the status line to show the cursor position. @@ -385,8 +467,9 @@ static void may_do_incsearch_highlighting(int firstc, long count, // Return OK when calling command_line_not_changed. static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s) { + int skiplen, patlen; // Add a character from under the cursor for 'incsearch' - if (!do_incsearch_highlighting(firstc)) { + if (!do_incsearch_highlighting(firstc, s, &skiplen, &patlen)) { return FAIL; } @@ -398,7 +481,7 @@ static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s) // command line has no uppercase characters, convert // the character to lowercase if (p_ic && p_scs - && !pat_has_uppercase(ccline.cmdbuff)) { + && !pat_has_uppercase(ccline.cmdbuff + skiplen)) { *c = mb_tolower(*c); } if (*c != NUL) { @@ -1246,10 +1329,11 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_state_T *s, bool next_match) { - if (!do_incsearch_highlighting(firstc)) { + int skiplen, patlen; + if (!do_incsearch_highlighting(firstc, s, &skiplen, &patlen)) { return OK; } - if (ccline.cmdlen == 0) { + if (patlen == 0 && ccline.cmdbuff[skiplen] == NUL) { return FAIL; } @@ -1259,12 +1343,13 @@ static int may_do_command_line_next_incsearch(int firstc, long count, pos_T t; char_u *pat; int search_flags = SEARCH_NOOF; + char_u save; - if (firstc == ccline.cmdbuff[0]) { + if (firstc == ccline.cmdbuff[skiplen]) { pat = last_search_pattern(); } else { - pat = ccline.cmdbuff; + pat = ccline.cmdbuff + skiplen; } save_last_search_pattern(); @@ -1284,17 +1369,20 @@ static int may_do_command_line_next_incsearch(int firstc, long count, search_flags += SEARCH_KEEP; } emsg_off++; + save = pat[patlen]; + pat[patlen] = NUL; int found = searchit(curwin, curbuf, &t, NULL, next_match ? FORWARD : BACKWARD, pat, count, search_flags, RE_SEARCH, NULL); emsg_off--; + pat[patlen] = save; ui_busy_stop(); if (found) { s->search_start = s->match_start; s->match_end = t; s->match_start = t; - if (!next_match && firstc == '/') { + if (!next_match && firstc != '?') { // move just before the current match, so that // when nv_search finishes the cursor will be // put back on the match diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 91a0eab947..205be4b811 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -353,9 +353,11 @@ EXTERN int t_colors INIT(= 256); // int value of T_CCO // position. Search_match_lines is the number of lines after the match (0 for // a match within one line), search_match_endcol the column number of the // character just after the match in the last line. -EXTERN int highlight_match INIT(= false); // show search match pos -EXTERN linenr_T search_match_lines; // lines of of matched string -EXTERN colnr_T search_match_endcol; // col nr of match end +EXTERN bool highlight_match INIT(= false); // show search match pos +EXTERN linenr_T search_match_lines; // lines of of matched string +EXTERN colnr_T search_match_endcol; // col nr of match end +EXTERN linenr_T search_first_line INIT(= 0); // for :{FIRST},{last}s/pat +EXTERN linenr_T search_last_line INIT(= MAXLNUM); // for :{first},{LAST}s/pat EXTERN int no_smartcase INIT(= false); // don't use 'smartcase' once diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 69de1de6b2..4292ed2865 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -5918,6 +5918,12 @@ next_search_hl ( long nmatched = 0; int save_called_emsg = called_emsg; + // for :{range}s/pat only highlight inside the range + if (lnum < search_first_line || lnum > search_last_line) { + shl->lnum = 0; + return; + } + if (shl->lnum != 0) { // Check for three situations: // 1. If the "lnum" is below a previous match, start a new search. diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 8036dea29f..087a261cca 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -367,6 +367,63 @@ func Test_search_cmdline3() bw! endfunc +func Cmdline3_prep() + throw 'skipped: Nvim does not support test_override()' + " need to disable char_avail, + " so that expansion of commandline works + call test_override("char_avail", 1) + new + call setline(1, [' 1', ' 2 the~e', ' 3 the theother']) + set incsearch +endfunc + +func Cmdline3_cleanup() + throw 'skipped: Nvim does not support test_override()' + set noincsearch + call test_override("char_avail", 0) + bw! +endfunc + +func Test_search_cmdline3s() + throw 'skipped: Nvim does not support test_override()' + if !exists('+incsearch') + return + endif + call Cmdline3_prep() + 1 + call feedkeys(":%s/the\/xxx\", 'tx') + call assert_equal(' 2 xxxe', getline('.')) + + call Cmdline3_cleanup() +endfunc + +func Test_search_cmdline3g() + throw 'skipped: Nvim does not support test_override()' + if !exists('+incsearch') + return + endif + call Cmdline3_prep() + 1 + call feedkeys(":g/the\/d\", 'tx') + call assert_equal(' 3 the theother', getline(2)) + + call Cmdline3_cleanup() +endfunc + +func Test_search_cmdline3v() + throw 'skipped: Nvim does not support test_override()' + if !exists('+incsearch') + return + endif + call Cmdline3_prep() + 1 + call feedkeys(":v/the\/d\", 'tx') + call assert_equal(1, line('$')) + call assert_equal(' 2 the~e', getline(1)) + + call Cmdline3_cleanup() +endfunc + func Test_search_cmdline4() " See test/functional/legacy/search_spec.lua throw 'skipped: Nvim does not support test_override()' -- cgit From 5eb71330214e739b910ebd6c822f39f17fb2cc04 Mon Sep 17 00:00:00 2001 From: Aufar Gilbran Date: Wed, 12 Aug 2020 03:08:05 +0800 Subject: vim-patch:8.1.0273: invalid memory access when using 'incsearch' Problem: Invalid memory access when using 'incsearch'. Solution: Reset "patlen" when using previous search pattern. https://github.com/vim/vim/commit/ef73a28401cde680dc0d02d4b0fb19ed659ec1d3 --- src/nvim/ex_getln.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 47bfc89bdf..a30b13ae34 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1348,6 +1348,8 @@ static int may_do_command_line_next_incsearch(int firstc, long count, if (firstc == ccline.cmdbuff[skiplen]) { pat = last_search_pattern(); + skiplen = 0; + patlen = (int)STRLEN(pat); } else { pat = ccline.cmdbuff + skiplen; } -- cgit From 9e834a89df6a24c78bcc9d7c69175dd3ae684bdd Mon Sep 17 00:00:00 2001 From: Aufar Gilbran Date: Mon, 10 Aug 2020 19:06:51 +0800 Subject: vim-patch:8.1.0274: 'incsearch' triggers on ":source" Problem: 'incsearch' triggers on ":source". Solution: Check for the whole command name. https://github.com/vim/vim/commit/21f990e1c22ffa2fdb66a548ebbe25e6e7194776 --- src/nvim/ex_getln.c | 5 ++- src/nvim/testdir/test_search.vim | 87 ++++++++++++++++++++++++++++++---------- 2 files changed, 70 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a30b13ae34..6023b8d26b 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -295,7 +295,10 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s, if (*cmd == 's' || *cmd == 'g' || *cmd == 'v') { // Skip over "substitute" to find the pattern separator. for (p = cmd; ASCII_ISALPHA(*p); p++) {} - if (*p != NUL) { + if (*p != NUL + && (STRNCMP(cmd, "substitute", p - cmd) == 0 + || STRNCMP(cmd, "global", p - cmd) == 0 + || STRNCMP(cmd, "vglobal", p - cmd) == 0)) { delim = *p++; end = skip_regexp(p, delim, p_magic, NULL); if (end > p) { diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 087a261cca..7348c8c4e2 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -346,27 +346,6 @@ func Test_searchc() bw! endfunc -func Test_search_cmdline3() - throw 'skipped: Nvim does not support test_override()' - if !exists('+incsearch') - return - endif - " need to disable char_avail, - " so that expansion of commandline works - call test_override("char_avail", 1) - new - call setline(1, [' 1', ' 2 the~e', ' 3 the theother']) - set incsearch - 1 - " first match - call feedkeys("/the\\", 'tx') - call assert_equal(' 2 the~e', getline('.')) - " clean up - set noincsearch - call test_override("char_avail", 0) - bw! -endfunc - func Cmdline3_prep() throw 'skipped: Nvim does not support test_override()' " need to disable char_avail, @@ -384,6 +363,20 @@ func Cmdline3_cleanup() bw! endfunc +func Test_search_cmdline3() + throw 'skipped: Nvim does not support test_override()' + if !exists('+incsearch') + return + endif + call Cmdline3_prep() + 1 + " first match + call feedkeys("/the\\", 'tx') + call assert_equal(' 2 the~e', getline('.')) + + call Cmdline3_cleanup() +endfunc + func Test_search_cmdline3s() throw 'skipped: Nvim does not support test_override()' if !exists('+incsearch') @@ -393,6 +386,12 @@ func Test_search_cmdline3s() 1 call feedkeys(":%s/the\/xxx\", 'tx') call assert_equal(' 2 xxxe', getline('.')) + undo + call feedkeys(":%subs/the\/xxx\", 'tx') + call assert_equal(' 2 xxxe', getline('.')) + undo + call feedkeys(":%substitute/the\/xxx\", 'tx') + call assert_equal(' 2 xxxe', getline('.')) call Cmdline3_cleanup() endfunc @@ -406,6 +405,9 @@ func Test_search_cmdline3g() 1 call feedkeys(":g/the\/d\", 'tx') call assert_equal(' 3 the theother', getline(2)) + undo + call feedkeys(":global/the\/d\", 'tx') + call assert_equal(' 3 the theother', getline(2)) call Cmdline3_cleanup() endfunc @@ -420,6 +422,10 @@ func Test_search_cmdline3v() call feedkeys(":v/the\/d\", 'tx') call assert_equal(1, line('$')) call assert_equal(' 2 the~e', getline(1)) + undo + call feedkeys(":vglobal/the\/d\", 'tx') + call assert_equal(1, line('$')) + call assert_equal(' 2 the~e', getline(1)) call Cmdline3_cleanup() endfunc @@ -480,6 +486,45 @@ func Test_search_cmdline5() bw! endfunc +func Test_search_cmdline7() + throw 'skipped: Nvim does not support test_override()' + " Test that an pressing in an empty command line + " does not move the cursor + if !exists('+incsearch') + return + endif + " need to disable char_avail, + " so that expansion of commandline works + call test_override("char_avail", 1) + new + let @/ = 'b' + call setline(1, [' bbvimb', '']) + set incsearch + " first match + norm! gg0 + " moves to next match of previous search pattern, just like / + call feedkeys("/\\", 'tx') + call assert_equal([0,1,2,0], getpos('.')) + " moves to next match of previous search pattern, just like / + call feedkeys("/\", 'tx') + call assert_equal([0,1,3,0], getpos('.')) + " moves to next match of previous search pattern, just like / + call feedkeys("/\\", 'tx') + call assert_equal([0,1,7,0], getpos('.')) + + " using an offset uses the last search pattern + call cursor(1, 1) + call setline(1, ['1 bbvimb', ' 2 bbvimb']) + let @/ = 'b' + call feedkeys("//e\\", 'tx') + call assert_equal('1 bbvimb', getline('.')) + call assert_equal(4, col('.')) + + set noincsearch + call test_override("char_avail", 0) + bw! +endfunc + " Tests for regexp with various magic settings func Test_search_regexp() enew! -- cgit From dd08f6367b9ecd231028ee19b479b5ad723458e4 Mon Sep 17 00:00:00 2001 From: Aufar Gilbran Date: Mon, 10 Aug 2020 19:15:57 +0800 Subject: vim-patch:8.1.0275: 'incsearch' with :s doesn't start at cursor line Problem: 'incsearch' with :s doesn't start at cursor line. Solution: Set cursor before parsing address. (closes vim/vim#3318) Also accept a match at the start of the first line. https://github.com/vim/vim/commit/976b847f43dd16eb6cd809d2dcab7dde6045e176 --- src/nvim/ex_getln.c | 5 ++++- src/nvim/testdir/test_search.vim | 32 +++++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 6023b8d26b..54be8dd590 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -316,8 +316,8 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s, ea.line2 = 1; ea.cmd = ccline.cmdbuff; ea.addr_type = ADDR_LINES; - parse_cmd_address(&ea, &dummy); curwin->w_cursor = s->search_start; + parse_cmd_address(&ea, &dummy); if (ea.addr_count > 0) { search_first_line = ea.line1; search_last_line = ea.line2; @@ -385,6 +385,9 @@ static void may_do_incsearch_highlighting(int firstc, long count, if (!p_hls) { search_flags += SEARCH_KEEP; } + if (search_first_line != 0) { + search_flags += SEARCH_START; + } c = ccline.cmdbuff[skiplen + patlen]; ccline.cmdbuff[skiplen + patlen] = NUL; memset(&sia, 0, sizeof(sia)); diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 7348c8c4e2..333f5807fd 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -356,7 +356,7 @@ func Cmdline3_prep() set incsearch endfunc -func Cmdline3_cleanup() +func Incsearch_cleanup() throw 'skipped: Nvim does not support test_override()' set noincsearch call test_override("char_avail", 0) @@ -374,7 +374,7 @@ func Test_search_cmdline3() call feedkeys("/the\\", 'tx') call assert_equal(' 2 the~e', getline('.')) - call Cmdline3_cleanup() + call Incsearch_cleanup() endfunc func Test_search_cmdline3s() @@ -393,7 +393,7 @@ func Test_search_cmdline3s() call feedkeys(":%substitute/the\/xxx\", 'tx') call assert_equal(' 2 xxxe', getline('.')) - call Cmdline3_cleanup() + call Incsearch_cleanup() endfunc func Test_search_cmdline3g() @@ -409,7 +409,7 @@ func Test_search_cmdline3g() call feedkeys(":global/the\/d\", 'tx') call assert_equal(' 3 the theother', getline(2)) - call Cmdline3_cleanup() + call Incsearch_cleanup() endfunc func Test_search_cmdline3v() @@ -427,7 +427,7 @@ func Test_search_cmdline3v() call assert_equal(1, line('$')) call assert_equal(' 2 the~e', getline(1)) - call Cmdline3_cleanup() + call Incsearch_cleanup() endfunc func Test_search_cmdline4() @@ -682,6 +682,28 @@ func Test_incsearch_scrolling() call delete('Xscript') endfunc +func Test_incsearch_substitute() + throw 'skipped: Nvim does not support test_override()' + if !exists('+incsearch') + return + endif + call test_override("char_avail", 1) + new + set incsearch + for n in range(1, 10) + call setline(n, 'foo ' . n) + endfor + 4 + call feedkeys(":.,.+2s/foo\o\o/xxx\", 'tx') + call assert_equal('foo 3', getline(3)) + call assert_equal('xxx 4', getline(4)) + call assert_equal('xxx 5', getline(5)) + call assert_equal('xxx 6', getline(6)) + call assert_equal('foo 7', getline(7)) + + call Incsearch_cleanup() +endfunc + func Test_search_undefined_behaviour() if !has("terminal") return -- cgit From 50da4d4f451c24f53189a66b653236ac72cd2353 Mon Sep 17 00:00:00 2001 From: Aufar Gilbran Date: Mon, 10 Aug 2020 20:36:56 +0800 Subject: vim-patch:8.1.0277: 'incsearch' highlighting wrong in a few cases Problem: 'incsearch' highlighting wrong in a few cases. Solution: Fix using last search pattern. Restore highlighting when changing command. (issue vim/vim#3321) https://github.com/vim/vim/commit/c7f08b7ee1c1ff2080d425c2fcdb6907c26fc98e --- src/nvim/ex_getln.c | 42 ++++++++++++++++++++++++++-------------- src/nvim/testdir/test_search.vim | 15 ++++++++++++++ 2 files changed, 43 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 54be8dd590..d37075640d 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -301,7 +301,7 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s, || STRNCMP(cmd, "vglobal", p - cmd) == 0)) { delim = *p++; end = skip_regexp(p, delim, p_magic, NULL); - if (end > p) { + if (end > p || *end == delim) { char_u *dummy; exarg_T ea; pos_T save_cursor = curwin->w_cursor; @@ -346,9 +346,11 @@ static void may_do_incsearch_highlighting(int firstc, long count, proftime_T tm; searchit_arg_T sia; int skiplen, patlen; - char_u c; + char_u next_char; + char_u use_last_pat; if (!do_incsearch_highlighting(firstc, s, &skiplen, &patlen)) { + finish_incsearch_highlighting(false, s, true); return; } @@ -370,8 +372,13 @@ static void may_do_incsearch_highlighting(int firstc, long count, save_last_search_pattern(); int i; - // If there is no command line, don't do anything - if (patlen == 0) { + // Use the previous pattern for ":s//". + next_char = ccline.cmdbuff[skiplen + patlen]; + use_last_pat = patlen == 0 && skiplen > 0 + && ccline.cmdbuff[skiplen - 1] == next_char; + + // If there is no pattern, don't do anything. + if (patlen == 0 && !use_last_pat) { i = 0; set_no_hlsearch(true); // turn off previous highlight redraw_all_later(SOME_VALID); @@ -388,14 +395,13 @@ static void may_do_incsearch_highlighting(int firstc, long count, if (search_first_line != 0) { search_flags += SEARCH_START; } - c = ccline.cmdbuff[skiplen + patlen]; ccline.cmdbuff[skiplen + patlen] = NUL; memset(&sia, 0, sizeof(sia)); sia.sa_tm = &tm; i = do_search(NULL, firstc == ':' ? '/' : firstc, ccline.cmdbuff + skiplen, count, search_flags, &sia); - ccline.cmdbuff[skiplen + patlen] = c; + ccline.cmdbuff[skiplen + patlen] = next_char; emsg_off--; if (curwin->w_cursor.lnum < search_first_line || curwin->w_cursor.lnum > search_last_line) { @@ -442,12 +448,14 @@ static void may_do_incsearch_highlighting(int firstc, long count, // Disable 'hlsearch' highlighting if the pattern matches // everything. Avoids a flash when typing "foo\|". - c = ccline.cmdbuff[skiplen + patlen]; - ccline.cmdbuff[skiplen + patlen] = NUL; - if (empty_pattern(ccline.cmdbuff)) { - set_no_hlsearch(true); + if (!use_last_pat) { + next_char = ccline.cmdbuff[skiplen + patlen]; + ccline.cmdbuff[skiplen + patlen] = NUL; + if (empty_pattern(ccline.cmdbuff)) { + set_no_hlsearch(true); + } + ccline.cmdbuff[skiplen + patlen] = next_char; } - ccline.cmdbuff[skiplen + patlen] = c; validate_cursor(); // May redraw the status line to show the cursor position. @@ -505,9 +513,11 @@ static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s) return OK; } -static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s) +static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, + bool call_update_screen) { if (s->did_incsearch) { + s->did_incsearch = false; if (gotesc) { curwin->w_cursor = s->save_cursor; } else { @@ -521,7 +531,11 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s) restore_viewstate(&s->old_viewstate); highlight_match = false; validate_cursor(); // needed for TAB - redraw_all_later(SOME_VALID); + if (call_update_screen) { + update_screen(SOME_VALID); + } else { + redraw_all_later(SOME_VALID); + } } } @@ -723,7 +737,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) close_preview_windows(); } - finish_incsearch_highlighting(s->gotesc, &s->is_state); + finish_incsearch_highlighting(s->gotesc, &s->is_state, false); if (ccline.cmdbuff != NULL) { // Put line in history buffer (":" and "=" only when it was typed). diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 333f5807fd..c2093f47fd 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -614,6 +614,7 @@ func Test_incsearch_substitute_dump() sleep 100m " Need to send one key at a time to force a redraw. + " Select three lines at the cursor with typed pattern. call term_sendkeys(buf, ':.,.+2s/') sleep 100m call term_sendkeys(buf, 'f') @@ -621,7 +622,21 @@ func Test_incsearch_substitute_dump() call term_sendkeys(buf, 'o') sleep 100m call term_sendkeys(buf, 'o') + sleep 100m call VerifyScreenDump(buf, 'Test_incsearch_substitute_01', {}) + call term_sendkeys(buf, "\") + + " Select three lines at the cursor using previous pattern. + call term_sendkeys(buf, "/foo\") + sleep 100m + call term_sendkeys(buf, ':.,.+2s//') + sleep 100m + call VerifyScreenDump(buf, 'Test_incsearch_substitute_02', {}) + + " Deleting last slash should remove the match. + call term_sendkeys(buf, "\") + sleep 100m + call VerifyScreenDump(buf, 'Test_incsearch_substitute_03', {}) call term_sendkeys(buf, "\") call StopVimInTerminal(buf) -- cgit From 83f3218b286eb76e42c21dcd2ec7bec18683ead3 Mon Sep 17 00:00:00 2001 From: Aufar Gilbran Date: Tue, 11 Aug 2020 00:47:50 +0800 Subject: vim-patch:8.1.0278: 'incsearch' highlighting does not accept reverse range Problem: 'incsearch' highlighting does not accept reverse range. Solution: Swap the range when needed. (issue vim/vim#3321) https://github.com/vim/vim/commit/60d0871000e9abf3716ee035cba5b5a9d659e327 --- src/nvim/ex_getln.c | 10 ++++++++-- src/nvim/testdir/test_search.vim | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index d37075640d..a1a889d652 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -319,8 +319,14 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s, curwin->w_cursor = s->search_start; parse_cmd_address(&ea, &dummy); if (ea.addr_count > 0) { - search_first_line = ea.line1; - search_last_line = ea.line2; + // Allow for reverse match. + if (ea.line2 < ea.line1) { + search_first_line = ea.line2; + search_last_line = ea.line1; + } else { + search_first_line = ea.line1; + search_last_line = ea.line2; + } } else if (*cmd == 's') { // :s defaults to the current line search_first_line = curwin->w_cursor.lnum; diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index c2093f47fd..c2584f99b9 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -637,6 +637,12 @@ func Test_incsearch_substitute_dump() call term_sendkeys(buf, "\") sleep 100m call VerifyScreenDump(buf, 'Test_incsearch_substitute_03', {}) + call term_sendkeys(buf, "\") + + " Reverse range is accepted + call term_sendkeys(buf, ':5,2s/foo') + sleep 100m + call VerifyScreenDump(buf, 'Test_incsearch_substitute_04', {}) call term_sendkeys(buf, "\") call StopVimInTerminal(buf) -- cgit From 8ae47ddf63bdc6e880eac2f5752da137507a34c0 Mon Sep 17 00:00:00 2001 From: Aufar Gilbran Date: Tue, 11 Aug 2020 00:49:12 +0800 Subject: vim-patch:8.1.0279: 'incsearch' highlighting does not skip white space Problem: 'incsearch' highlighting does not skip white space. Solution: Skip white space after the command. (issue vim/vim#3321) https://github.com/vim/vim/commit/2b926fcb3c5d8bd09a219009336bbec7c66ae67e --- src/nvim/ex_getln.c | 3 ++- src/nvim/testdir/test_search.vim | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a1a889d652..6a60592f14 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -295,10 +295,11 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s, if (*cmd == 's' || *cmd == 'g' || *cmd == 'v') { // Skip over "substitute" to find the pattern separator. for (p = cmd; ASCII_ISALPHA(*p); p++) {} - if (*p != NUL + if (*skipwhite(p) != NUL && (STRNCMP(cmd, "substitute", p - cmd) == 0 || STRNCMP(cmd, "global", p - cmd) == 0 || STRNCMP(cmd, "vglobal", p - cmd) == 0)) { + p = skipwhite(p); delim = *p++; end = skip_regexp(p, delim, p_magic, NULL); if (end > p || *end == delim) { diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index c2584f99b9..0b403f8d2f 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -643,8 +643,14 @@ func Test_incsearch_substitute_dump() call term_sendkeys(buf, ':5,2s/foo') sleep 100m call VerifyScreenDump(buf, 'Test_incsearch_substitute_04', {}) + call term_sendkeys(buf, "\") + " White space after the command is skipped + call term_sendkeys(buf, ':2,3sub /fo') + sleep 100m + call VerifyScreenDump(buf, 'Test_incsearch_substitute_05', {}) call term_sendkeys(buf, "\") + call StopVimInTerminal(buf) call delete('Xis_subst_script') endfunc -- cgit From 841ec4316c13179fc15ec6d74237377636f7090f Mon Sep 17 00:00:00 2001 From: Aufar Gilbran Date: Wed, 12 Aug 2020 03:09:45 +0800 Subject: vim-patch:8.1.0280: 'incsearch' highlighting does not work for ":g!/" Problem: 'incsearch' highlighting does not work for ":g!/". Solution: Skip the exclamation mark. (Hirohito Higashi) https://github.com/vim/vim/commit/def7b1dc6104a6ce6d7c3e3a615231178601b124 --- src/nvim/ex_getln.c | 7 +++++++ src/nvim/testdir/test_search.vim | 8 ++++++++ 2 files changed, 15 insertions(+) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 6a60592f14..9769d5f58b 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -299,6 +299,13 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s, && (STRNCMP(cmd, "substitute", p - cmd) == 0 || STRNCMP(cmd, "global", p - cmd) == 0 || STRNCMP(cmd, "vglobal", p - cmd) == 0)) { + // Check for "global!/". + if (*cmd == 'g' && *p == '!') { + p++; + if (*skipwhite(p) == NUL) { + return false; + } + } p = skipwhite(p); delim = *p++; end = skip_regexp(p, delim, p_magic, NULL); diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 0b403f8d2f..01e6f2d61c 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -408,6 +408,14 @@ func Test_search_cmdline3g() undo call feedkeys(":global/the\/d\", 'tx') call assert_equal(' 3 the theother', getline(2)) + undo + call feedkeys(":g!/the\/d\", 'tx') + call assert_equal(1, line('$')) + call assert_equal(' 2 the~e', getline(1)) + undo + call feedkeys(":global!/the\/d\", 'tx') + call assert_equal(1, line('$')) + call assert_equal(' 2 the~e', getline(1)) call Incsearch_cleanup() endfunc -- cgit From 9a6fd99a4b316425328affacdbd1e6868753dee5 Mon Sep 17 00:00:00 2001 From: Aufar Gilbran Date: Tue, 18 Aug 2020 00:11:18 +0800 Subject: vim-patch:8.1.0281: parsing command modifiers is not separated Problem: Parsing command modifiers is not separated. Solution: Move command modifier parsing to a separate function. https://github.com/vim/vim/commit/effed9315c6c5a35fc2824b90da4af753c7a02dc --- src/nvim/ex_docmd.c | 462 +++++++++++++++++++++++++++------------------------- 1 file changed, 242 insertions(+), 220 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 1160ab835b..73c4332543 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1273,6 +1273,7 @@ static void parse_state_from_global(parse_state_T *parse_state) static bool parse_one_cmd( char_u **cmdlinep, parse_state_T *const out, + cstack_T *cstack, LineGetter fgetline, void *fgetline_cookie) { @@ -1289,219 +1290,15 @@ static bool parse_one_cmd( /* * Repeat until no more command modifiers are found. + * The "ea" structure holds the arguments that can be used. */ ea.cmd = *cmdlinep; - for (;; ) { - /* - * 1. Skip comment lines and leading white space and colons. - */ - while (*ea.cmd == ' ' - || *ea.cmd == '\t' - || *ea.cmd == ':') { - ea.cmd++; - } - - // in ex mode, an empty line works like :+ - if (*ea.cmd == NUL && exmode_active - && (getline_equal(fgetline, fgetline_cookie, getexmodeline) - || getline_equal(fgetline, fgetline_cookie, getexline)) - && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - ea.cmd = (char_u *)"+"; - out->ex_pressedreturn = true; - } - - // ignore comment and empty lines - if (*ea.cmd == '"') { - return false; - } - if (*ea.cmd == NUL) { - out->ex_pressedreturn = true; - return false; - } - - /* - * 2. Handle command modifiers. - */ - char_u *p = skip_range(ea.cmd, NULL); - switch (*p) { - // When adding an entry, also modify cmd_exists(). - case 'a': if (!checkforcmd(&ea.cmd, "aboveleft", 3)) - break; - out->cmdmod.split |= WSP_ABOVE; - continue; - - case 'b': if (checkforcmd(&ea.cmd, "belowright", 3)) { - out->cmdmod.split |= WSP_BELOW; - continue; - } - if (checkforcmd(&ea.cmd, "browse", 3)) { - out->cmdmod.browse = true; - continue; - } - if (!checkforcmd(&ea.cmd, "botright", 2)) { - break; - } - out->cmdmod.split |= WSP_BOT; - continue; - - case 'c': if (!checkforcmd(&ea.cmd, "confirm", 4)) - break; - out->cmdmod.confirm = true; - continue; - - case 'k': if (checkforcmd(&ea.cmd, "keepmarks", 3)) { - out->cmdmod.keepmarks = true; - continue; - } - if (checkforcmd(&ea.cmd, "keepalt", 5)) { - out->cmdmod.keepalt = true; - continue; - } - if (checkforcmd(&ea.cmd, "keeppatterns", 5)) { - out->cmdmod.keeppatterns = true; - continue; - } - if (!checkforcmd(&ea.cmd, "keepjumps", 5)) { - break; - } - out->cmdmod.keepjumps = true; - continue; - - case 'f': { // only accept ":filter {pat} cmd" - char_u *reg_pat; - - if (!checkforcmd(&p, "filter", 4) || *p == NUL || ends_excmd(*p)) { - break; - } - if (*p == '!') { - out->cmdmod.filter_force = true; - p = skipwhite(p + 1); - if (*p == NUL || ends_excmd(*p)) { - break; - } - } - p = skip_vimgrep_pat(p, ®_pat, NULL); - if (p == NULL || *p == NUL) { - break; - } - out->cmdmod.filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC); - if (out->cmdmod.filter_regmatch.regprog == NULL) { - break; - } - ea.cmd = p; - continue; - } - - // ":hide" and ":hide | cmd" are not modifiers - case 'h': if (p != ea.cmd || !checkforcmd(&p, "hide", 3) - || *p == NUL || ends_excmd(*p)) - break; - ea.cmd = p; - out->cmdmod.hide = true; - continue; - - case 'l': if (checkforcmd(&ea.cmd, "lockmarks", 3)) { - out->cmdmod.lockmarks = true; - continue; - } - - if (!checkforcmd(&ea.cmd, "leftabove", 5)) { - break; - } - out->cmdmod.split |= WSP_ABOVE; - continue; - - case 'n': - if (checkforcmd(&ea.cmd, "noautocmd", 3)) { - if (out->cmdmod.save_ei == NULL) { - // Set 'eventignore' to "all". Restore the - // existing option value later. - out->cmdmod.save_ei = vim_strsave(p_ei); - out->set_eventignore = true; - } - continue; - } - if (!checkforcmd(&ea.cmd, "noswapfile", 3)) { - break; - } - out->cmdmod.noswapfile = true; - continue; - - case 'r': if (!checkforcmd(&ea.cmd, "rightbelow", 6)) - break; - out->cmdmod.split |= WSP_BELOW; - continue; - - case 's': if (checkforcmd(&ea.cmd, "sandbox", 3)) { - if (!out->did_sandbox) { - out->sandbox++; - } - out->did_sandbox = true; - continue; - } - if (!checkforcmd(&ea.cmd, "silent", 3)) { - break; - } - if (out->save_msg_silent == -1) { - out->save_msg_silent = out->msg_silent; - } - out->msg_silent++; - if (*ea.cmd == '!' && !ascii_iswhite(ea.cmd[-1])) { - // ":silent!", but not "silent !cmd" - ea.cmd = skipwhite(ea.cmd + 1); - out->emsg_silent++; - out->did_esilent++; - } - continue; - - case 't': if (checkforcmd(&p, "tab", 3)) { - long tabnr = get_address( - &ea, &ea.cmd, ADDR_TABS, ea.skip, false, 1); - - if (tabnr == MAXLNUM) { - out->cmdmod.tab = tabpage_index(curtab) + 1; - } else { - if (tabnr < 0 || tabnr > LAST_TAB_NR) { - out->errormsg = (char_u *)_(e_invrange); - return false; - } - out->cmdmod.tab = tabnr + 1; - } - ea.cmd = p; - continue; - } - if (!checkforcmd(&ea.cmd, "topleft", 2)) { - break; - } - out->cmdmod.split |= WSP_TOP; - continue; - - case 'u': if (!checkforcmd(&ea.cmd, "unsilent", 3)) - break; - if (out->save_msg_silent == -1) { - out->save_msg_silent = out->msg_silent; - } - out->msg_silent = 0; - continue; - - case 'v': if (checkforcmd(&ea.cmd, "vertical", 4)) { - out->cmdmod.split |= WSP_VERT; - continue; - } - if (!checkforcmd(&p, "verbose", 4)) - break; - if (out->verbose_save < 0) { - out->verbose_save = out->p_verbose; - } - if (ascii_isdigit(*ea.cmd)) { - out->p_verbose = atoi((char *)ea.cmd); - } else { - out->p_verbose = 1; - } - ea.cmd = p; - continue; - } - break; + ea.cmdlinep = cmdlinep; + ea.getline = fgetline; + ea.cookie = fgetline_cookie; + ea.cstack = cstack; + if (parse_command_modifiers(&ea, out, &out->errormsg) == FAIL) { + return false; } out->after_modifier = ea.cmd; @@ -1571,7 +1368,6 @@ static char_u * do_one_cmd(char_u **cmdlinep, * recursive calls. */ save_cmdmod = cmdmod; - memset(&cmdmod, 0, sizeof(cmdmod)); parse_state_from_global(&parsed); parsed.eap = &ea; @@ -1579,7 +1375,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, parsed.save_msg_silent = -1; parsed.did_esilent = 0; parsed.did_sandbox = false; - bool parse_success = parse_one_cmd(cmdlinep, &parsed, fgetline, cookie); + bool parse_success = parse_one_cmd(cmdlinep, &parsed, cstack, fgetline, cookie); parse_state_to_global(&parsed); // Update locals from parse_one_cmd() @@ -2231,13 +2027,6 @@ static char_u * do_one_cmd(char_u **cmdlinep, } // 7. Execute the command. - // - // The "ea" structure holds the arguments that can be used. - ea.cmdlinep = cmdlinep; - ea.getline = fgetline; - ea.cookie = cookie; - ea.cstack = cstack; - if (IS_USER_CMDIDX(ea.cmdidx)) { /* * Execute a user-defined command. @@ -2342,6 +2131,239 @@ doend: return ea.nextcmd; } +// Parse and skip over command modifiers: +// - update eap->cmd +// - store flags in "cmdmod". +// - Set ex_pressedreturn for an empty command line. +// - set msg_silent for ":silent" +// - set p_verbose for ":verbose" +// - Increment "sandbox" for ":sandbox" +// Return FAIL when the command is not to be executed. +// May set "errormsg" to an error message. +int parse_command_modifiers(exarg_T *eap, parse_state_T *const out, char_u **errormsg) +{ + char_u *p; + + memset(&cmdmod, 0, sizeof(cmdmod)); + out->verbose_save = -1; + out->save_msg_silent = -1; + + for (;; ) { + /* + * 1. Skip comment lines and leading white space and colons. + */ + while (*eap->cmd == ' ' + || *eap->cmd == '\t' + || *eap->cmd == ':') { + eap->cmd++; + } + + // in ex mode, an empty line works like :+ + if (*eap->cmd == NUL && exmode_active + && (getline_equal(eap->getline, eap->cookie, getexmodeline) + || getline_equal(eap->getline, eap->cookie, getexline)) + && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { + eap->cmd = (char_u *)"+"; + out->ex_pressedreturn = true; + } + + // ignore comment and empty lines + if (*eap->cmd == '"') { + return FAIL; + } + if (*eap->cmd == NUL) { + out->ex_pressedreturn = true; + return FAIL; + } + + /* + * 2. Handle command modifiers. + */ + p = skip_range(eap->cmd, NULL); + switch (*p) { + // When adding an entry, also modify cmd_exists(). + case 'a': if (!checkforcmd(&eap->cmd, "aboveleft", 3)) + break; + out->cmdmod.split |= WSP_ABOVE; + continue; + + case 'b': if (checkforcmd(&eap->cmd, "belowright", 3)) { + out->cmdmod.split |= WSP_BELOW; + continue; + } + if (checkforcmd(&eap->cmd, "browse", 3)) { + out->cmdmod.browse = true; + continue; + } + if (!checkforcmd(&eap->cmd, "botright", 2)) { + break; + } + out->cmdmod.split |= WSP_BOT; + continue; + + case 'c': if (!checkforcmd(&eap->cmd, "confirm", 4)) + break; + out->cmdmod.confirm = true; + continue; + + case 'k': if (checkforcmd(&eap->cmd, "keepmarks", 3)) { + out->cmdmod.keepmarks = true; + continue; + } + if (checkforcmd(&eap->cmd, "keepalt", 5)) { + out->cmdmod.keepalt = true; + continue; + } + if (checkforcmd(&eap->cmd, "keeppatterns", 5)) { + out->cmdmod.keeppatterns = true; + continue; + } + if (!checkforcmd(&eap->cmd, "keepjumps", 5)) { + break; + } + out->cmdmod.keepjumps = true; + continue; + + case 'f': { // only accept ":filter {pat} cmd" + char_u *reg_pat; + + if (!checkforcmd(&p, "filter", 4) || *p == NUL || ends_excmd(*p)) { + break