diff options
Diffstat (limited to 'src/nvim/ex_cmds.c')
-rw-r--r-- | src/nvim/ex_cmds.c | 251 |
1 files changed, 67 insertions, 184 deletions
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index ab0d58d749..7f75704de6 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -14,7 +14,6 @@ #include <string.h> #include "nvim/api/buffer.h" -#include "nvim/api/extmark.h" #include "nvim/api/private/defs.h" #include "nvim/ascii.h" #include "nvim/buffer.h" @@ -111,8 +110,6 @@ typedef struct { # include "ex_cmds.c.generated.h" #endif -static int preview_bufnr = 0; - /// ":ascii" and "ga" implementation void do_ascii(const exarg_T *const eap) { @@ -1013,7 +1010,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) disable_fold_update--; // send update regarding the new lines that were added - buf_updates_send_changes(curbuf, dest + 1, num_lines, 0, true); + buf_updates_send_changes(curbuf, dest + 1, num_lines, 0); /* * Now we delete the original text -- webb @@ -1055,7 +1052,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) } // send nvim_buf_lines_event regarding lines that were deleted - buf_updates_send_changes(curbuf, line1 + extra, 0, num_lines, true); + buf_updates_send_changes(curbuf, line1 + extra, 0, num_lines); return OK; } @@ -3438,8 +3435,8 @@ static int check_regexp_delim(int c) /// The usual escapes are supported as described in the regexp docs. /// /// @param do_buf_event If `true`, send buffer updates. -/// @return buffer used for 'inccommand' preview -static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle_T bufnr) +/// @return 0, 1 or 2. See show_cmdpreview() for more information on what the return value means. +static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T cmdpreview_bufnr) { long i = 0; regmmatch_T regmatch; @@ -3467,14 +3464,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle char *sub_firstline; // allocated copy of first sub line bool endcolumn = false; // cursor in last column when done PreviewLines preview_lines = { KV_INITIAL_VALUE, 0 }; - static int pre_src_id = 0; // Source id for the preview highlight static int pre_hl_id = 0; - buf_T *orig_buf = curbuf; // save to reset highlighting pos_T old_cursor = curwin->w_cursor; int start_nsubs; int save_ma = 0; - int save_b_changed = curbuf->b_changed; - bool preview = (State & MODE_CMDPREVIEW); bool did_save = false; @@ -3494,7 +3487,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle && vim_strchr("0123456789cegriIp|\"", *cmd) == NULL) { // don't accept alphanumeric for separator if (check_regexp_delim(*cmd) == FAIL) { - return NULL; + return 0; } // undocumented vi feature: @@ -3504,7 +3497,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle cmd++; if (vim_strchr("/?&", *cmd) == NULL) { emsg(_(e_backslash)); - return NULL; + return 0; } if (*cmd != '&') { which_pat = RE_SEARCH; // use last '/' pattern @@ -3540,7 +3533,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle MB_PTR_ADV(cmd); } - if (!eap->skip && !preview) { + if (!eap->skip && !cmdpreview) { sub_set_replacement((SubReplacementString) { .sub = xstrdup(sub), .timestamp = os_time(), @@ -3550,7 +3543,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle } else if (!eap->skip) { // use previous pattern and substitution if (old_sub.sub == NULL) { // there is no previous command emsg(_(e_nopresub)); - return NULL; + return 0; } pat = NULL; // search_regcomp() will use previous pattern sub = old_sub.sub; @@ -3560,8 +3553,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle endcolumn = (curwin->w_curswant == MAXCOL); } - if (sub != NULL && sub_joining_lines(eap, pat, sub, cmd, !preview)) { - return NULL; + if (sub != NULL && sub_joining_lines(eap, pat, sub, cmd, !cmdpreview)) { + return 0; } cmd = sub_parse_flags(cmd, &subflags, &which_pat); @@ -3575,7 +3568,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle i = getdigits_long((char_u **)&cmd, true, 0); if (i <= 0 && !eap->skip && subflags.do_error) { emsg(_(e_zerocount)); - return NULL; + return 0; } eap->line1 = eap->line2; eap->line2 += i - 1; @@ -3592,26 +3585,26 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle eap->nextcmd = (char *)check_nextcmd((char_u *)cmd); if (eap->nextcmd == NULL) { emsg(_(e_trailing)); - return NULL; + return 0; } } if (eap->skip) { // not executing commands, only parsing - return NULL; + return 0; } if (!subflags.do_count && !MODIFIABLE(curbuf)) { // Substitution is not allowed in non-'modifiable' buffer emsg(_(e_modifiable)); - return NULL; + return 0; } - if (search_regcomp((char_u *)pat, RE_SUBST, which_pat, (preview ? 0 : SEARCH_HIS), + if (search_regcomp((char_u *)pat, RE_SUBST, which_pat, (cmdpreview ? 0 : SEARCH_HIS), ®match) == FAIL) { if (subflags.do_error) { emsg(_(e_invcmd)); } - return NULL; + return 0; } // the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' @@ -3638,10 +3631,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle sub_copy = sub; } else { char *source = sub; - sub = (char *)regtilde((char_u *)sub, p_magic, preview); + sub = (char *)regtilde((char_u *)sub, p_magic, cmdpreview); // When previewing, the new pattern allocated by regtilde() needs to be freed // in this function because it will not be used or freed by regtilde() later. - sub_needs_free = preview && sub != source; + sub_needs_free = cmdpreview && sub != source; } // Check for a match on each line. @@ -3650,7 +3643,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle for (linenr_T lnum = eap->line1; lnum <= line2 && !got_quit && !aborting() - && (!preview || preview_lines.lines_needed <= (linenr_T)p_cwh + && (!cmdpreview || preview_lines.lines_needed <= (linenr_T)p_cwh || lnum <= curwin->w_botline); lnum++) { long nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, @@ -3817,7 +3810,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle } } - if (subflags.do_ask && !preview) { + if (subflags.do_ask && !cmdpreview) { int typed = 0; // change State to MODE_CONFIRM, so that the mouse works @@ -4049,7 +4042,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle // Save the line numbers for the preview buffer // NOTE: If the pattern matches a final newline, the next line will // be shown also, but should not be highlighted. Intentional for now. - if (preview && !has_second_delim) { + if (cmdpreview && !has_second_delim) { current_match.start.col = regmatch.startpos[0].col; if (current_match.end.lnum == 0) { current_match.end.lnum = sub_firstlnum + nmatch - 1; @@ -4064,7 +4057,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle // 3. Substitute the string. During 'inccommand' preview only do this if // there is a replace pattern. - if (!preview || has_second_delim) { + if (!cmdpreview || has_second_delim) { long lnum_start = lnum; // save the start lnum save_ma = curbuf->b_p_ma; if (subflags.do_count) { @@ -4312,7 +4305,7 @@ skip: #define PUSH_PREVIEW_LINES() \ do { \ - if (preview) { \ + if (cmdpreview) { \ linenr_T match_lines = current_match.end.lnum \ - current_match.start.lnum +1; \ if (preview_lines.subresults.size > 0) { \ @@ -4368,8 +4361,7 @@ skip: int64_t num_added = last_line - first_line; int64_t num_removed = num_added - i; - buf_updates_send_changes(curbuf, first_line, num_added, num_removed, - do_buf_event); + buf_updates_send_changes(curbuf, first_line, num_added, num_removed); } xfree(sub_firstline); // may have to free allocated copy of the line @@ -4396,7 +4388,7 @@ skip: beginline(BL_WHITE | BL_FIX); } } - if (!preview && !do_sub_msg(subflags.do_count) && subflags.do_ask) { + if (!cmdpreview && !do_sub_msg(subflags.do_count) && subflags.do_ask) { msg(""); } } else { @@ -4433,34 +4425,23 @@ skip: subflags.do_all = save_do_all; subflags.do_ask = save_do_ask; + int retv = 0; + // Show 'inccommand' preview if there are matched lines. - buf_T *preview_buf = NULL; - size_t subsize = preview_lines.subresults.size; - if (preview && !aborting()) { + if (cmdpreview && !aborting()) { if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable. set_string_option_direct("icm", -1, (char_u *)"", OPT_FREE, SID_NONE); } else if (*p_icm != NUL && pat != NULL) { - if (pre_src_id == 0) { - // Get a unique new src_id, saved in a static - pre_src_id = (int)nvim_create_namespace((String)STRING_INIT); - } if (pre_hl_id == 0) { pre_hl_id = syn_check_group(S_LEN("Substitute")); } - curbuf->b_changed = save_b_changed; // preserve 'modified' during preview - preview_buf = show_sub(eap, old_cursor, &preview_lines, - pre_hl_id, pre_src_id, bufnr); - if (subsize > 0) { - extmark_clear(orig_buf, pre_src_id, eap->line1 - 1, 0, - kv_last(preview_lines.subresults).end.lnum - 1, MAXCOL); - } + retv = show_sub(eap, old_cursor, &preview_lines, pre_hl_id, cmdpreview_ns, cmdpreview_bufnr); } } kv_destroy(preview_lines.subresults); - - return preview_buf; + return retv; #undef ADJUST_SUB_FIRSTLNUM #undef PUSH_PREVIEW_LINES } @@ -5856,52 +5837,26 @@ void ex_helpclose(exarg_T *eap) } } -/// Tries to enter to an existing window of given buffer. If no existing buffer -/// is found, creates a new split. -/// -/// @return OK/FAIL. -int sub_preview_win(buf_T *preview_buf) -{ - if (preview_buf != NULL) { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == preview_buf) { - win_enter(wp, false); - - return OK; - } - } - } - return win_split((int)p_cwh, WSP_BOT); -} - /// Shows the effects of the :substitute command being typed ('inccommand'). /// If inccommand=split, shows a preview window and later restores the layout. -static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, int hl_id, - int src_id, handle_T bufnr) +/// +/// @return 1 if preview window isn't needed, 2 if preview window is needed. +static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, int hl_id, + long cmdpreview_ns, handle_T cmdpreview_bufnr) FUNC_ATTR_NONNULL_ALL { - win_T *save_curwin = curwin; - cmdmod_T save_cmdmod = cmdmod; char *save_shm_p = (char *)vim_strsave(p_shm); PreviewLines lines = *preview_lines; buf_T *orig_buf = curbuf; - // We keep a special-purpose buffer around, but don't assume it exists. - buf_T *preview_buf = bufnr ? buflist_findnr(bufnr) : 0; - cmdmod.split = 0; // disable :leftabove/botright modifiers - cmdmod.tab = 0; // disable :tab modifier - cmdmod.noswapfile = true; // disable swap for preview buffer + buf_T *cmdpreview_buf = NULL; + // disable file info message set_string_option_direct("shm", -1, (char_u *)"F", OPT_FREE, SID_NONE); - bool outside_curline = (eap->line1 != old_cusr.lnum - || eap->line2 != old_cusr.lnum); - bool preview = outside_curline && (*p_icm != 'n'); - if (preview_buf == curbuf) { // Preview buffer cannot preview itself! - preview = false; - preview_buf = NULL; - } + // Update the topline to ensure that main window is on the correct line + update_topline(curwin); // Place cursor on nearest matching line, to undo do_sub() cursor placement. for (size_t i = 0; i < lines.subresults.size; i++) { @@ -5916,27 +5871,17 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines // Width of the "| lnum|..." column which displays the line numbers. linenr_T highest_num_line = 0; int col_width = 0; + // Use preview window only when inccommand=split and range is not just the current line + bool preview = (*p_icm != 'n') && (eap->line1 != old_cusr.lnum || eap->line2 != old_cusr.lnum); - if (preview && sub_preview_win(preview_buf) != FAIL) { - buf_open_scratch(preview_buf ? bufnr : 0, "[Preview]"); - buf_clear(); - preview_buf = curbuf; - curbuf->b_p_bl = false; - curbuf->b_p_ma = true; - curbuf->b_p_ul = -1; - curbuf->b_p_tw = 0; // Reset 'textwidth' (was set by ftplugin) - curwin->w_p_cul = false; - curwin->w_p_cuc = false; - curwin->w_p_spell = false; - curwin->w_p_fen = false; + if (preview) { + cmdpreview_buf = buflist_findnr(cmdpreview_bufnr); + assert(cmdpreview_buf != NULL); if (lines.subresults.size > 0) { highest_num_line = kv_last(lines.subresults).end.lnum; col_width = log10(highest_num_line) + 1 + 3; } - } else { - // Failed to split the window, don't show 'inccommand' preview. - preview_buf = NULL; } char *str = NULL; // construct the line to show in here @@ -5946,10 +5891,13 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines linenr_T linenr_origbuf = 0; // last line added to original buffer linenr_T next_linenr = 0; // next line to show for the match + // Temporarily switch to preview buffer + aco_save_T aco; + for (size_t matchidx = 0; matchidx < lines.subresults.size; matchidx++) { SubResult match = lines.subresults.items[matchidx]; - if (preview_buf) { + if (cmdpreview_buf) { lpos_T p_start = { 0, match.start.col }; // match starts here in preview lpos_T p_end = { 0, match.end.col }; // ... and ends here @@ -5988,115 +5936,50 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines // Put "|lnum| line" into `str` and append it to the preview buffer. snprintf(str, line_size, "|%*ld| %s", col_width - 3, next_linenr, line); + // Temporarily switch to preview buffer + aucmd_prepbuf(&aco, cmdpreview_buf); if (linenr_preview == 0) { ml_replace(1, str, true); } else { ml_append(linenr_preview, str, (colnr_T)line_size, false); } + aucmd_restbuf(&aco); linenr_preview += 1; } linenr_origbuf = match.end.lnum; - bufhl_add_hl_pos_offset(preview_buf, src_id, hl_id, p_start, - p_end, col_width); + bufhl_add_hl_pos_offset(cmdpreview_buf, cmdpreview_ns, hl_id, p_start, p_end, col_width); } - bufhl_add_hl_pos_offset(orig_buf, src_id, hl_id, match.start, - match.end, 0); + bufhl_add_hl_pos_offset(orig_buf, cmdpreview_ns, hl_id, match.start, match.end, 0); } - xfree(str); - redraw_later(curwin, SOME_VALID); - win_enter(save_curwin, false); // Return to original window - update_topline(curwin); - - // Update screen now. - int save_rd = RedrawingDisabled; - RedrawingDisabled = 0; - update_screen(SOME_VALID); - RedrawingDisabled = save_rd; + xfree(str); set_string_option_direct("shm", -1, (char_u *)save_shm_p, OPT_FREE, SID_NONE); xfree(save_shm_p); - cmdmod = save_cmdmod; - - return preview_buf; + return preview ? 2 : 1; } -/// Closes any open windows for inccommand preview buffer. -void close_preview_windows(void) +/// :substitute command. +void ex_substitute(exarg_T *eap) { - block_autocmds(); - buf_T *buf = preview_bufnr ? buflist_findnr(preview_bufnr) : NULL; - if (buf != NULL) { - close_windows(buf, false); - } - unblock_autocmds(); + (void)do_sub(eap, profile_zero(), 0, 0); + return; } -/// :substitute command -/// -/// If 'inccommand' is empty: calls do_sub(). -/// If 'inccommand' is set: shows a "live" preview then removes the changes. -/// from undo history. -void ex_substitute(exarg_T *eap) +/// :substitute command preview callback. +int ex_substitute_preview(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr) { - bool preview = (State & MODE_CMDPREVIEW); - if (*p_icm == NUL || !preview) { // 'inccommand' is disabled - close_preview_windows(); - (void)do_sub(eap, profile_zero(), true, preview_bufnr); - - return; + // Only preview once the pattern delimiter has been typed + if (*eap->arg && !ASCII_ISALNUM(*eap->arg)) { + char *save_eap = eap->arg; + int retv = do_sub(eap, profile_setlimit(p_rdt), cmdpreview_ns, cmdpreview_bufnr); + eap->arg = save_eap; + return retv; } - block_autocmds(); // Disable events during command preview. - - char *save_eap = eap->arg; - garray_T save_view; - win_size_save(&save_view); // Save current window sizes. - save_search_patterns(); - int save_changedtick = buf_get_changedtick(curbuf); - time_t save_b_u_time_cur = curbuf->b_u_time_cur; - u_header_T *save_b_u_newhead = curbuf->b_u_newhead; - long save_b_p_ul = curbuf->b_p_ul; - int save_w_p_cul = curwin->w_p_cul; - int save_w_p_cuc = curwin->w_p_cuc; - - curbuf->b_p_ul = LONG_MAX; // make sure we can undo all changes - curwin->w_p_cul = false; // Disable 'cursorline' - curwin->w_p_cuc = false; // Disable 'cursorcolumn' - - // Don't show search highlighting during live substitution - bool save_hls = p_hls; - p_hls = false; - buf_T *preview_buf = do_sub(eap, profile_setlimit(p_rdt), false, - preview_bufnr); - p_hls = save_hls; - - if (preview_buf != NULL) { - preview_bufnr = preview_buf->handle; - } - - if (save_changedtick != buf_get_changedtick(curbuf)) { - // Undo invisibly. This also moves the cursor! - if (!u_undo_and_forget(1)) { - abort(); - } - // Restore newhead. It is meaningless when curhead is valid, but we must - // restore it so that undotree() is identical before/after the preview. - curbuf->b_u_newhead = save_b_u_newhead; - curbuf->b_u_time_cur = save_b_u_time_cur; - buf_set_changedtick(curbuf, save_changedtick); - } - - curbuf->b_p_ul = save_b_p_ul; - curwin->w_p_cul = save_w_p_cul; // Restore 'cursorline' - curwin->w_p_cuc = save_w_p_cuc; // Restore 'cursorcolumn' - eap->arg = save_eap; - restore_search_patterns(); - win_size_restore(&save_view); - ga_clear(&save_view); - unblock_autocmds(); + return 0; } /// Skip over the pattern argument of ":vimgrep /pat/[g][j]". |