From 13841a56b4b4916a2f18e66f97cb6f5e55d40e53 Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Fri, 12 Aug 2016 12:00:36 +0200 Subject: Incsubsitution feature Originally implemented by * Clement0 * DesbyP * aym7 * Adrey06 * Robinhola in #4811. Major reworkings and bug fixes by * bfredl Most tests suggested by ZyX-l, suggestions for improvements by oni-link. --- src/nvim/buffer.c | 15 ++- src/nvim/eval.c | 2 +- src/nvim/ex_cmds.c | 325 +++++++++++++++++++++++++++++++++++++++++++++--- src/nvim/ex_cmds.h | 22 ++++ src/nvim/ex_cmds.lua | 2 +- src/nvim/ex_cmds_defs.h | 1 + src/nvim/ex_docmd.c | 46 +++++-- src/nvim/ex_docmd.h | 1 + src/nvim/ex_getln.c | 4 + src/nvim/fileio.c | 2 +- src/nvim/globals.h | 7 +- src/nvim/mark.c | 2 +- src/nvim/option.c | 11 +- src/nvim/option_defs.h | 1 + src/nvim/options.lua | 8 ++ src/nvim/quickfix.c | 2 +- src/nvim/shada.c | 2 +- src/nvim/syntax.c | 1 + src/nvim/undo.c | 74 ++++++++--- src/nvim/window.c | 2 +- 20 files changed, 472 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index a66fdc1304..17300fbdfe 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -678,7 +678,7 @@ void handle_swap_exists(buf_T *old_curbuf) swap_exists_did_quit = TRUE; close_buffer(curwin, curbuf, DOBUF_UNLOAD, FALSE); if (!buf_valid(old_curbuf) || old_curbuf == curbuf) - old_curbuf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED); + old_curbuf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED, 0); if (old_curbuf != NULL) { enter_buffer(old_curbuf); if (old_tw != curbuf->b_p_tw) @@ -1339,7 +1339,8 @@ buflist_new ( char_u *ffname, /* full path of fname or relative */ char_u *sfname, /* short fname or NULL */ linenr_T lnum, /* preferred cursor line */ - int flags /* BLN_ defines */ + int flags, /* BLN_ defines */ + handle_T bufnr ) { buf_T *buf; @@ -1458,7 +1459,9 @@ buflist_new ( } lastbuf = buf; - buf->b_fnum = top_file_num++; + // If bufnr is nonzero it is assumed to be a previously + // reserved buffer number (handle) + buf->handle = bufnr != 0 ? bufnr : top_file_num++; handle_register_buffer(buf); if (top_file_num < 0) { // wrap around (may cause duplicates) EMSG(_("W14: Warning: List of file names overflow")); @@ -2375,7 +2378,7 @@ buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum) buf_T *buf; /* Create a buffer. 'buflisted' is not set if it's a new buffer */ - buf = buflist_new(ffname, sfname, lnum, 0); + buf = buflist_new(ffname, sfname, lnum, 0, 0); if (buf != NULL && !cmdmod.keepalt) curwin->w_alt_fnum = buf->b_fnum; return buf; @@ -2411,7 +2414,7 @@ int buflist_add(char_u *fname, int flags) { buf_T *buf; - buf = buflist_new(fname, NULL, (linenr_T)0, flags); + buf = buflist_new(fname, NULL, (linenr_T)0, flags, 0); if (buf != NULL) return buf->b_fnum; return 0; @@ -5193,7 +5196,7 @@ bool buf_contents_changed(buf_T *buf) bool differ = true; // Allocate a buffer without putting it in the buffer list. - buf_T *newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); + buf_T *newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY, 0); if (newbuf == NULL) { return true; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 512555eac1..508bdac46d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7703,7 +7703,7 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) && !error && (name = get_tv_string_chk(&argvars[0])) != NULL && !error) - buf = buflist_new(name, NULL, (linenr_T)1, 0); + buf = buflist_new(name, NULL, (linenr_T)1, 0, 0); if (buf != NULL) rettv->vval.v_number = buf->b_fnum; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 17780c58e4..767f4b50a2 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "nvim/vim.h" #include "nvim/ascii.h" @@ -64,6 +65,12 @@ */ typedef struct sign sign_T; +// boolean to know if inc_sub needs to undo +static bool inc_sub_did_changes = false; + +// reuse the same bufnr for inc_sub +static handle_T inc_sub_bufnr = 0; + /// Case matching style to use for :substitute typedef enum { kSubHonorOptions = 0, ///< Honor the user's 'ignorecase'/'smartcase' options @@ -1523,7 +1530,7 @@ int rename_buffer(char_u *new_fname) } curbuf->b_flags |= BF_NOTEDITED; if (xfname != NULL && *xfname != NUL) { - buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, 0); + buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, 0, 0); if (buf != NULL && !cmdmod.keepalt) curwin->w_alt_fnum = buf->b_fnum; } @@ -2174,7 +2181,7 @@ do_ecmd ( buflist_altfpos(oldwin); } - if (fnum) + if (fnum && !(flags & ECMD_RESERVED_BUFNR)) buf = buflist_findnr(fnum); else { if (flags & ECMD_ADDBUF) { @@ -2185,11 +2192,11 @@ do_ecmd ( if (tlnum <= 0) tlnum = 1L; } - (void)buflist_new(ffname, sfname, tlnum, BLN_LISTED); + (void)buflist_new(ffname, sfname, tlnum, BLN_LISTED, fnum); goto theend; } buf = buflist_new(ffname, sfname, 0L, - BLN_CURBUF | ((flags & ECMD_SET_HELP) ? 0 : BLN_LISTED)); + BLN_CURBUF | ((flags & ECMD_SET_HELP) ? 0 : BLN_LISTED), fnum); // Autocmds may change curwin and curbuf. if (oldwin != NULL) { oldwin = curwin; @@ -2978,10 +2985,12 @@ static bool sub_joining_lines(exarg_T *eap, char_u *pat, ex_may_print(eap); } - if (!cmdmod.keeppatterns) { - save_re_pat(RE_SUBST, pat, p_magic); + if (!eap->is_live){ + if (!cmdmod.keeppatterns) { + save_re_pat(RE_SUBST, pat, p_magic); + } + add_to_history(HIST_SEARCH, pat, TRUE, NUL); } - add_to_history(HIST_SEARCH, pat, TRUE, NUL); return true; } @@ -3127,6 +3136,9 @@ void do_sub(exarg_T *eap) int start_nsubs; int save_ma = 0; + inc_sub_did_changes = false; + bool has_second_delim = false; + if (!global_busy) { sub_nsubs = 0; sub_nlines = 0; @@ -3161,6 +3173,7 @@ void do_sub(exarg_T *eap) which_pat = RE_SEARCH; /* use last '/' pattern */ pat = (char_u *)""; /* empty search pattern */ delimiter = *cmd++; /* remember delimiter character */ + has_second_delim = true; } else { /* find the end of the regexp */ if (p_altkeymap && curwin->w_p_rl) lrF_sub(cmd); @@ -3168,8 +3181,10 @@ void do_sub(exarg_T *eap) delimiter = *cmd++; /* remember delimiter character */ pat = cmd; /* remember start of search pat */ cmd = skip_regexp(cmd, delimiter, p_magic, &eap->arg); - if (cmd[0] == delimiter) /* end delimiter found */ + if (cmd[0] == delimiter) { /* end delimiter found */ *cmd++ = NUL; /* replace it with a NUL */ + has_second_delim = true; + } } /* @@ -3188,7 +3203,7 @@ void do_sub(exarg_T *eap) mb_ptr_adv(cmd); } - if (!eap->skip) { + if (!eap->skip && !eap->is_live) { sub_set_replacement((SubReplacementString) { .sub = xstrdup((char *) sub), .timestamp = os_time(), @@ -3252,7 +3267,9 @@ void do_sub(exarg_T *eap) return; } - if (search_regcomp(pat, RE_SUBST, which_pat, SEARCH_HIS, ®match) == FAIL) { + int search_options = eap->is_live ? 0 : SEARCH_HIS; + if (search_regcomp(pat, RE_SUBST, which_pat, search_options, + ®match) == FAIL) { if (subflags.do_error) { EMSG(_(e_invcmd)); } @@ -3276,6 +3293,9 @@ void do_sub(exarg_T *eap) if (!(sub[0] == '\\' && sub[1] == '=')) sub = regtilde(sub, p_magic); + // list to save matched lines + MatchedLineVec lmatch = KV_INITIAL_VALUE; + // Check for a match on each line. linenr_T line2 = eap->line2; for (linenr_T lnum = eap->line1; @@ -3343,6 +3363,8 @@ void do_sub(exarg_T *eap) sub_firstlnum = lnum; copycol = 0; matchcol = 0; + // the current match + MatchedLine cmatch = { 0, 0, NULL, KV_INITIAL_VALUE }; /* At first match, remember current cursor position. */ if (!got_match) { @@ -3379,6 +3401,10 @@ void do_sub(exarg_T *eap) curwin->w_cursor.lnum = lnum; do_again = FALSE; + // increment number of match on the line and store the column + cmatch.nmatch++; + kv_push(cmatch.start_col, regmatch.startpos[0].col); + /* * 1. Match empty string does not count, except for first * match. This reproduces the strange vi behaviour. @@ -3426,7 +3452,7 @@ void do_sub(exarg_T *eap) goto skip; } - if (subflags.do_ask) { + if (subflags.do_ask && !eap->is_live) { int typed = 0; /* change State to CONFIRM, so that the mouse works @@ -3597,9 +3623,9 @@ void do_sub(exarg_T *eap) * use "\=col("."). */ curwin->w_cursor.col = regmatch.startpos[0].col; - /* - * 3. substitute the string. - */ + // 3. Substitute the string. Don't do this while incsubstitution and + // there's no word to replace by eg : ":%s/pattern" + if (!eap->is_live || has_second_delim) { if (subflags.do_count) { // prevent accidentally changing the buffer by a function save_ma = curbuf->b_p_ma; @@ -3726,6 +3752,7 @@ void do_sub(exarg_T *eap) } else if (has_mbyte) p1 += (*mb_ptr2len)(p1) - 1; } + } // 4. If subflags.do_all is set, find next match. // Prevent endless loop with patterns that match empty @@ -3845,6 +3872,12 @@ skip: xfree(new_start); /* for when substitute was cancelled */ xfree(sub_firstline); /* free the copy of the original line */ sub_firstline = NULL; + + // saving info about the matched line + cmatch.lnum = lnum; + cmatch.line = vim_strsave(ml_get(lnum)); + + kv_push(lmatch, cmatch); } line_breakcheck(); @@ -3880,7 +3913,7 @@ skip: beginline(BL_WHITE | BL_FIX); } } - if (!do_sub_msg(subflags.do_count) && subflags.do_ask) { + if (!eap->is_live && !do_sub_msg(subflags.do_count) && subflags.do_ask) { MSG(""); } } else { @@ -3912,6 +3945,79 @@ skip: // Restore the flag values, they can be used for ":&&". subflags.do_all = save_do_all; subflags.do_ask = save_do_ask; + + // inc_sub if sub on the whole file and there are results to display + if (lmatch.size != 0) { + // we did incsubstitute only if we had no word to replace by + // by and no ending slash + if (!subflags.do_count && (!eap->is_live || has_second_delim)) { + inc_sub_did_changes = true; + } + if (pat != NULL && *p_ics != NUL && eap->is_live) { + bool split = true; + + // p_ics is "", "nosplit" or "split" + if (*p_ics == 'n' || eap[0].cmdlinep[0][0] == 's') { + split = false; + } + + // Place cursor on the first match after the cursor + // If all matches are before the cursor, then do_sub did + // already place the cursor on the last match + + linenr_T cur_lnum = 0; + colnr_T cur_col = -1; + MatchedLine current; + + for (size_t j = 0; j < lmatch.size; j++) { + current = lmatch.items[j]; + cur_lnum = current.lnum; + + // 1. Match on line of the cursor, need to iterate over the + // matches on this line to see if there is one on a later + // column + if (cur_lnum == old_cursor.lnum) { + for (size_t i = 0; i < current.start_col.size; i++) { + if (current.start_col.items[i] >= old_cursor.col) { + cur_col = current.start_col.items[i]; + break; + } + } + // match on cursor's line, after the cursor + if (cur_col != -1) { + curwin->w_cursor.lnum = cur_lnum; + curwin->w_cursor.col = cur_col; + break; + } + // 2. Match on line after cursor, just put cursor on column + // of first match there + } else if (cur_lnum > old_cursor.lnum) { + cur_col = current.start_col.items[0]; + curwin->w_cursor.lnum = cur_lnum; + curwin->w_cursor.col = cur_col; + break; + } + } + + inc_sub_display(pat, sub, &lmatch, split); + } else if (*p_ics != NUL && eap->is_live) { + curwin->w_cursor = old_cursor; + } + } else { + curwin->w_cursor = old_cursor; + } + + MatchedLine current; + for (size_t j = 0; j < lmatch.size; j++) { + current = lmatch.items[j]; + + if (current.line) { xfree(current.line); } + + kv_destroy(current.start_col); + } + + + kv_destroy(lmatch); } // NOLINT(readability/fn_size) /* @@ -5951,3 +6057,192 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) } } } + +/// Open a window for displaying of the inc_sub mode. +/// +/// Does not allow editing in the window. Closes the window and restores +/// the window layout before returning. +/// +/// @param pat The pattern word +/// @param sub The replacement word +/// @param lmatch The list containing our data +static void inc_sub_display(char_u * pat, + char_u * sub, + MatchedLineVec *lmatch, + bool split) + FUNC_ATTR_NONNULL_ARG(1, 2, 3) +{ + garray_T winsizes; + int save_restart_edit = restart_edit; + int save_State = State; + int save_exmode = exmode_active; + int save_cmdmsg_rl = cmdmsg_rl; + + // Can't do this recursively. Can't do it when typing a password. + if (cmdline_star > 0) { + beep_flush(); + return; + } + + // Save current window sizes. + win_size_save(&winsizes); + + // Save the current window to restore it later + win_T *oldwin = curwin; + + if (split) { + // don't use a new tab page + cmdmod.tab = 0; + + // Create a window for the command-line buffer. + if (win_split((int)p_cwh, WSP_BOT) == FAIL) { + beep_flush(); + return; + } + cmdwin_type = get_cmdline_type(); + + // Create the command-line buffer empty. + (void)do_ecmd(inc_sub_bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE | ECMD_RESERVED_BUFNR, NULL); + inc_sub_bufnr = curbuf->handle; + (void)setfname(curbuf, (char_u *) "[inc_sub]", NULL, true); + set_option_value((char_u *) "bt", 0L, (char_u *) "incsub", OPT_LOCAL); + set_option_value((char_u *) "swf", 0L, NULL, OPT_LOCAL); + curbuf->b_p_ma = false; // Not Modifiable + curwin->w_p_fen = false; + curwin->w_p_rl = cmdmsg_rl; + cmdmsg_rl = false; + RESET_BINDING(curwin); + + // Showing the prompt may have set need_wait_return, reset it. + need_wait_return = false; + + // Reset 'textwidth' after setting 'filetype' + // (the Vim filetype plugin sets 'textwidth' to 78). + curbuf->b_p_tw = 0; + + // Save the buffer used in the split + livebuf = curbuf; + + // Initialize line and highlight variables + int line = 0; + int src_id_highlight = 0; + long sub_size = STRLEN(sub); + long pat_size = STRLEN(pat); + + // Get the width of the column which display the number of the line + linenr_T highest_num_line = kv_last(*lmatch).lnum; + + // computing the length of the column that will display line number + int col_width = log10(highest_num_line) + 1 + 3; + + // will be allocated in the loop + char *str = NULL; + + size_t old_line_size = 0; + size_t line_size; + + // Append the lines to our buffer + for (size_t i = 0; i < (*lmatch).size; i++) { + MatchedLine mat = (*lmatch).items[i]; + line_size = STRLEN(mat.line) + col_width + 1; + + // Reallocation if str not long enough + if (line_size > old_line_size) { + str = xrealloc(str, line_size * sizeof(char)); + old_line_size = line_size; + } + + // put ' [ lnum]line' into str and append it to the incsubstitute buffer + snprintf(str, line_size, " [%*ld]%s", col_width - 3, mat.lnum, mat.line); + ml_append(line++, (char_u *)str, (colnr_T)line_size, false); + + // highlight the replaced part + if (sub_size > 0) { + int hlgroup_ls = syn_check_group((char_u *)"IncSubstitute", 13); + + for (size_t j = 0; j < mat.start_col.size; j++) { + src_id_highlight = + bufhl_add_hl(curbuf, + src_id_highlight, + hlgroup_ls, // id of our highlight + line, + mat.start_col.items[j] + col_width + + j * (sub_size - pat_size) + 1, + mat.start_col.items[j] + col_width + + j * (sub_size - pat_size) + sub_size); + } + } + } + xfree(str); + redraw_later(SOME_VALID); + } + + // Restore the old window + win_enter(oldwin, false); + win_size_restore(&winsizes); + ga_clear(&winsizes); + exmode_active = save_exmode; + restart_edit = save_restart_edit; + cmdmsg_rl = save_cmdmsg_rl; + State = save_State; + + cmdwin_type = 0; + int save_rd = RedrawingDisabled; + RedrawingDisabled = 0; + update_screen(0); + RedrawingDisabled = save_rd; + + setmouse(); +} + + +/// :substitute command implementation +/// +/// Uses do_sub() to do the actual substitution. Undoes the substitution and +/// removes it from the undo history unless finishing the command. If +/// ics is set to "", it just calls do_sub(). +void do_inc_sub(exarg_T *eap) +{ + // if incsubstitute disabled, do it the classical way + if (*p_ics == NUL || !eap->is_live) { + do_sub(eap); + return; + } + + // Save the state of eap + char_u *tmp = eap->arg; + + save_search_patterns(); + + // save the value of undolevels to the maximum value to avoid losing + // history when it is set to a low value + long b_p_ul_save = curbuf->b_p_ul; + curbuf->b_p_ul = LONG_MAX; + + // Incsub window/buffer is opened in do_sub, so to suppress autocmd + // we need to start it before the call + block_autocmds(); + + emsg_off++; // No error messages for live commands + do_sub(eap); + emsg_off--; + if (inc_sub_did_changes) { + if (!u_undo_and_forget(1)) { + abort(); + } + inc_sub_did_changes = false; + } + + // Put back eap in first state + eap->arg = tmp; + restore_search_patterns(); + curbuf->b_p_ul = b_p_ul_save; + + update_screen(0); + if (livebuf != NULL && buf_valid(livebuf)) { + close_windows(livebuf, false); + wipe_buffer(livebuf, false); + } + unblock_autocmds(); + redraw_later(SOME_VALID); +} diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h index 721145efd8..991695deca 100644 --- a/src/nvim/ex_cmds.h +++ b/src/nvim/ex_cmds.h @@ -5,6 +5,9 @@ #include "nvim/os/time.h" #include "nvim/eval_defs.h" +#include "nvim/pos.h" +#include "nvim/lib/klist.h" +#include "nvim/lib/kvec.h" /* flags for do_ecmd() */ #define ECMD_HIDE 0x01 /* don't free the current buffer */ @@ -13,6 +16,7 @@ #define ECMD_OLDBUF 0x04 /* use existing buffer if it exists */ #define ECMD_FORCEIT 0x08 /* ! used in Ex command */ #define ECMD_ADDBUF 0x10 /* don't edit, just add to buffer list */ +#define ECMD_RESERVED_BUFNR 0x20 /* bufnr argument is reserved bufnr */ /* for lnum argument in do_ecmd() */ #define ECMD_LASTL (linenr_T)0 /* use last position in loaded file */ @@ -26,6 +30,24 @@ typedef struct { list_T *additional_elements; ///< Additional data left from ShaDa file. } SubReplacementString; + +// Defs for inc_sub functionality + +/// Structure to backup and display matched lines in incsubstitution +typedef struct { + linenr_T lnum; + long nmatch; + char_u *line; + // list of column numbers of matches on this line + kvec_t(colnr_T) start_col; +} MatchedLine; + +// List of matched lines +typedef kvec_t(MatchedLine) MatchedLineVec; + +// End defs for inc_sub functionality + + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_cmds.h.generated.h" #endif diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 3f5d9b3244..04fc163222 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -2194,7 +2194,7 @@ return { command='substitute', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN), addr_type=ADDR_LINES, - func='do_sub', + func='do_inc_sub', }, { command='sNext', diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index 8148eb5cee..5b647ff69a 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -124,6 +124,7 @@ struct exarg { LineGetter getline; ///< Function used to get the next line void *cookie; ///< argument for getline() struct condstack *cstack; ///< condition stack for ":if" etc. + bool is_live; ///< live preview }; #define FORCE_BIN 1 // ":edit ++bin file" diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 5e418bf099..a7b1ee2f54 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -271,7 +271,7 @@ do_exmode ( int do_cmdline_cmd(char *cmd) { return do_cmdline((char_u *)cmd, NULL, NULL, - DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); + DOCMD_NOWAIT|DOCMD_KEYTYPED); } /* @@ -597,11 +597,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, * do_one_cmd() will return NULL if there is no trailing '|'. * "cmdline_copy" can change, e.g. for '%' and '#' expansion. */ - ++recursive; - next_cmdline = do_one_cmd(&cmdline_copy, flags & DOCMD_VERBOSE, - &cstack, - cmd_getline, cmd_cookie); - --recursive; + recursive++; + next_cmdline = do_one_cmd(&cmdline_copy, flags, + &cstack, + cmd_getline, cmd_cookie); + recursive--; if (cmd_cookie == (void *)&cmd_loop_cookie) /* Use "current_line" from "cmd_loop_cookie", it may have been @@ -1225,7 +1225,7 @@ static void get_wincmd_addr_type(char_u *arg, exarg_T *eap) * This function may be called recursively! */ static char_u * do_one_cmd(char_u **cmdlinep, - int sourcing, + int flags, struct condstack *cstack, LineGetter fgetline, void *cookie /* argument for fgetline() */ @@ -1248,6 +1248,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, memset(&ea, 0, sizeof(ea)); ea.line1 = 1; ea.line2 = 1; + ea.is_live = flags & DOCMD_LIVE_PREVIEW; ++ex_nesting_level; /* When the last file has not been edited :q has to be typed twice. */ @@ -1726,7 +1727,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, if (ea.cmdidx == CMD_SIZE) { if (!ea.skip) { STRCPY(IObuff, _("E492: Not an editor command")); - if (!sourcing) + if (!(flags & DOCMD_VERBOSE)) append_command(*cmdlinep); errormsg = IObuff; did_emsg_syntax = TRUE; @@ -1809,7 +1810,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, */ if (!global_busy && ea.line1 > ea.line2) { if (msg_silent == 0) { - if (sourcing || exmode_active) { + if ((flags & DOCMD_VERBOSE) || exmode_active) { errormsg = (char_u *)_("E493: Backwards range given"); goto doend; } @@ -2221,7 +2222,7 @@ doend: curwin->w_cursor.lnum = 1; if (errormsg != NULL && *errormsg != NUL && !did_emsg) { - if (sourcing) { + if (flags & DOCMD_VERBOSE) { if (errormsg != IObuff) { STRCPY(IObuff, errormsg); errormsg = IObuff; @@ -9647,3 +9648,28 @@ static void ex_terminal(exarg_T *eap) xfree(name); } } + +/// Check whether commandline starts with a live command +/// +/// @param[in] cmd_live Commandline to check. May start with a range. +/// +/// @return True if first command is a live command +/// Currently :s is the only one +bool is_live(char_u *cmd_live) +{ + exarg_T ea; + ea.cmd = cmd_live; + + // parse the command line + if (ea.cmd != NULL) { + ea.cmd = skip_range(ea.cmd, NULL); + if (*ea.cmd == '*') { + ea.cmd = skipwhite(ea.cmd + 1); + } + find_command(&ea, NULL); + } else { + return false; + } + + return (ea.cmdidx == CMD_substitute); +} diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index bafad20169..d2106c545f 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -10,6 +10,7 @@ #define DOCMD_KEYTYPED 0x08 /* don't reset KeyTyped */ #define DOCMD_EXCRESET 0x10 /* reset exception environment (for debugging)*/ #define DOCMD_KEEPLINE 0x20 /* keep typed line for repeating with "." */ +#define DOCMD_LIVE_PREVIEW 0x40 // Command is a live preview like incsubstitute /* defines for eval_vars() */ #define VALID_PATH 1 diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index e525c949bd..d1a2ab8bcf 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1591,6 +1591,10 @@ static int command_line_changed(CommandLineState *s) msg_starthere(); redrawcmdline(); s->did_incsearch = true; + } else if (*p_ics != NUL && s->firstc == ':' && is_live(ccline.cmdbuff)) { + // compute a live action + do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_LIVE_PREVIEW); + redrawcmdline(); } if (cmdmsg_rl || (p_arshape && !p_tbidi && enc_utf8)) { diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index c0d4a71b35..3871a3ab78 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -5067,7 +5067,7 @@ void buf_reload(buf_T *buf, int orig_mode) savebuf = NULL; else { /* Allocate a buffer without putting it in the buffer list. */ - savebuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); + savebuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY, 0); if (savebuf != NULL && buf == curbuf) { /* Open the memline. */ curbuf = savebuf; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 87fb928b30..a5188e98d7 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -600,9 +600,10 @@ EXTERN int redraw_tabline INIT(= FALSE); /* need to redraw tabline */ * All buffers are linked in a list. 'firstbuf' points to the first entry, * 'lastbuf' to the last entry and 'curbuf' to the currently active buffer. */ -EXTERN buf_T *firstbuf INIT(= NULL); /* first buffer */ -EXTERN buf_T *lastbuf INIT(= NULL); /* last buffer */ -EXTERN buf_T *curbuf INIT(= NULL); /* currently active buffer */ +EXTERN buf_T *firstbuf INIT(= NULL); // first buffer +EXTERN buf_T *lastbuf INIT(= NULL); // last buffer +EXTERN buf_T *curbuf INIT(= NULL); // currently active buffer +EXTERN buf_T *livebuf INIT(= NULL); // buffer used for live actions // Iterates over all buffers in the buffer list. # define FOR_ALL_BUFFERS(buf) for (buf_T *buf = firstbuf; buf != NULL; buf = buf->b_next) diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 2a65cf396b..489e5a2dde 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -474,7 +474,7 @@ static void fname2fnum(xfmark_T *fm) p = path_shorten_fname(NameBuff, IObuff); /* buflist_new() will call fmarks_check_names() */ - (void)buflist_new(NameBuff, p, (linenr_T)1, 0); + (void)buflist_new(NameBuff, p, (linenr_T)1, 0, 0); } } diff --git a/src/nvim/option.c b/src/nvim/option.c index 81919c00d2..761e4451b9 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -288,6 +288,7 @@ static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent", static char *(p_fcl_values[]) = { "all", NULL }; static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview", "noinsert", "noselect", NULL }; +static char *(p_ics_values[]) = { "nosplit", "split", NULL }; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "option.c.generated.h" @@ -3110,9 +3111,13 @@ did_set_string_option ( else if (gvarp == &p_cino) { /* TODO: recognize errors */ parse_cino(curbuf); - } - /* Options that are a list of flags. */ - else { + // incsubstitute + } else if (varp == &p_ics) { + if (check_opt_strings(p_ics, p_ics_values, false) != OK) { + errmsg = e_invarg; + } + // Options that are a list of flags. + } else { p = NULL; if (varp == &p_ww) p = (char_u *)WW_ALL; diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index fdfcd1f428..e0711c7c8f 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -589,6 +589,7 @@ EXTERN int p_spr; // 'splitright' EXTERN int p_sol; // 'startofline' EXTERN char_u *p_su; // 'suffixes' EXTERN char_u *p_swb; // 'switchbuf' +EXTERN char_u *p_ics; // 'incsubstitute' EXTERN unsigned swb_flags; #ifdef IN_OPTION_C static char *(p_swb_values[]) = diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 583c63614a..359bf3fcee 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1210,6 +1210,14 @@ return { varname='p_is', defaults={if_true={vi=false, vim=true}} }, + { + full_name='incsubstitute', abbreviation='ics', + type='string', scope={'global'}, + vi_def=true, + redraw={'everything'}, + varname='p_ics', + defaults={if_true={vi=""}} + }, { full_name='indentexpr', abbreviation='inde', type='string', scope={'buffer'}, diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index a7aff15121..08345a323c 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3327,7 +3327,7 @@ load_dummy_buffer ( aco_save_T aco; /* Allocate a buffer without putting it in the buffer list. */ - newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); + newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY, 0); if (newbuf == NULL) return NULL; diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 01c0807d82..1cd8f44ea8 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1405,7 +1405,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) cur_entry.data.buffer_list.buffers[i].fname); buf_T *const buf = buflist_new( cur_entry.data.buffer_list.buffers[i].fname, sfname, 0, - BLN_LISTED); + BLN_LISTED, 0); if (buf != NULL) { RESET_FMARK(&buf->b_last_cursor, cur_entry.data.buffer_list.buffers[i].pos, 0); diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index b49ae9da21..a1c6718b06 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5902,6 +5902,7 @@ static char *highlight_init_both[] = "WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", "default link EndOfBuffer NonText", "default link QuickFixLine Search", + "default link IncSubstitute Search", NULL }; diff --git a/src/nvim/undo.c b/src/nvim/undo.c index d80aaf443a..a4af45d441 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1669,7 +1669,7 @@ void u_undo(int count) undo_undoes = TRUE; else undo_undoes = !undo_undoes; - u_doit(count); + u_doit(count, false); } /* @@ -1678,15 +1678,58 @@ void u_undo(int count) */ void u_redo(int count) { - if (vim_strchr(p_cpo, CPO_UNDO) == NULL) - undo_undoes = FALSE; - u_doit(count); + if (vim_strchr(p_cpo, CPO_UNDO) == NULL) { + undo_undoes = false; + } + + u_doit(count, false); +} + +/// undo and forget. +bool u_undo_and_forget(int count) +{ + if (curbuf->b_u_synced == false) { + u_sync(true); + count = 1; + } + undo_undoes = true; + u_doit(count, true); + + if (curbuf->b_u_curhead == NULL) { + // nothing was undone. + return false; + } + + // Delete the current redo header + // set the redo header to the next alternative branch (if any) + // otherwise we will be in the leaf state + u_header_T *to_forget = curbuf->b_u_curhead; + curbuf->b_u_newhead = to_forget->uh_next.ptr; + curbuf->b_u_curhead = to_forget->uh_alt_next.ptr; + if (curbuf->b_u_curhead) { + to_forget->uh_alt_next.ptr = NULL; + curbuf->b_u_curhead->uh_alt_prev.ptr = to_forget->uh_alt_prev.ptr; + curbuf->b_u_seq_cur = curbuf->b_u_curhead->uh_seq-1; + } else if (curbuf->b_u_newhead) { + curbuf->b_u_seq_cur = curbuf->b_u_newhead->uh_seq; + } + if (to_forget->uh_alt_prev.ptr) { + to_forget->uh_alt_prev.ptr->uh_alt_next.ptr = curbuf->b_u_curhead; + } + if (curbuf->b_u_newhead) { + curbuf->b_u_newhead->uh_prev.ptr = curbuf->b_u_curhead; + } + if (curbuf->b_u_seq_last == to_forget->uh_seq) { + curbuf->b_u_seq_last--; + } + u_freebranch(curbuf, to_forget, NULL); + return true; } /* * Undo or redo, depending on 'undo_undoes', 'count' times. */ -static void u_doit(int startcount) +static void u_doit(int startcount, bool quiet) { int count = startcount; @@ -1722,7 +1765,7 @@ static void u_doit(int startcount) break; } - u_undoredo(TRUE); + u_undoredo(true); } else { if (curbuf->b_u_curhead == NULL || get_undolevel() <= 0) { beep_flush(); /* nothing to redo */ @@ -1742,7 +1785,7 @@ static void u_doit(int startcount) curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr; } } - u_undo_end(undo_undoes, FALSE); + u_undo_end(undo_undoes, false, quiet); } /* @@ -2055,7 +2098,7 @@ void undo_time(long step, int sec, int file, int absolute) } } } - u_undo_end(did_undo, absolute); + u_undo_end(did_undo, absolute, false); } /* @@ -2304,10 +2347,11 @@ static void u_undoredo(int undo) * Otherwise, report the number of changes (this may be incorrect * in some cases, but it's better than nothing). */ -static void -u_undo_end ( - int did_undo, /* just did an undo */ - int absolute /* used ":undo N" */ +static void +u_undo_end( + int did_undo, // just did an undo + int absolute, // used ":undo N" + bool quiet ) { char *msgstr; @@ -2317,9 +2361,11 @@ u_undo_end ( if ((fdo_flags & FDO_UNDO) && KeyTyped) foldOpenCursor(); - if (global_busy /* no messages now, wait until global is finished */ - || !messaging()) /* 'lazyredraw' set, don't do messages now */ + if (global_busy // no messages now, wait until global is finished + || !messaging() // 'lazyredraw' set, don't do messages now + || quiet) { // livemode doesn't show messages return; + } if (curbuf->b_ml.ml_flags & ML_EMPTY) --u_newcount; diff --git a/src/nvim/window.c b/src/nvim/window.c index 9c6a2e26a6..53bc484b48 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2903,7 +2903,7 @@ static int win_alloc_firstwin(win_T *oldwin) if (oldwin == NULL) { /* Very first window, need to create an empty buffer for it and * initialize from scratch. */ - curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED); + curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED, 0); if (curbuf == NULL) return FAIL; curwin->w_buffer = curbuf; -- cgit From e8c0f909626094350be7ee7b524697804da38dc1 Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Tue, 23 Aug 2016 16:11:26 +0200 Subject: Linted --- src/nvim/buffer.c | 33 ++++--- src/nvim/ex_cmds.c | 275 ++++++++++++++++++++++++++-------------------------- src/nvim/ex_cmds.h | 16 +-- src/nvim/ex_docmd.c | 5 +- src/nvim/ex_docmd.h | 17 ++-- src/nvim/fileio.c | 6 +- src/nvim/mark.c | 2 +- src/nvim/quickfix.c | 5 +- src/nvim/window.c | 3 +- 9 files changed, 185 insertions(+), 177 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 17300fbdfe..65a42f063a 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -671,14 +671,15 @@ void handle_swap_exists(buf_T *old_curbuf) * aborting() returns FALSE when closing a buffer. */ enter_cleanup(&cs); - /* User selected Quit at ATTENTION prompt. Go back to previous - * buffer. If that buffer is gone or the same as the current one, - * open a new, empty buffer. */ - swap_exists_action = SEA_NONE; /* don't want it again */ - swap_exists_did_quit = TRUE; - close_buffer(curwin, curbuf, DOBUF_UNLOAD, FALSE); - if (!buf_valid(old_curbuf) || old_curbuf == curbuf) + // User selected Quit at ATTENTION prompt. Go back to previous + // buffer. If that buffer is gone or the same as the current one, + // open a new, empty buffer. + swap_exists_action = SEA_NONE; // don't want it again + swap_exists_did_quit = true; + close_buffer(curwin, curbuf, DOBUF_UNLOAD, false); + if (!buf_valid(old_curbuf) || old_curbuf == curbuf) { old_curbuf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED, 0); + } if (old_curbuf != NULL) { enter_buffer(old_curbuf); if (old_tw != curbuf->b_p_tw) @@ -1335,11 +1336,11 @@ void do_autochdir(void) static int top_file_num = 1; /* highest file number */ buf_T * -buflist_new ( - char_u *ffname, /* full path of fname or relative */ - char_u *sfname, /* short fname or NULL */ - linenr_T lnum, /* preferred cursor line */ - int flags, /* BLN_ defines */ +buflist_new( + char_u *ffname, // full path of fname or relative + char_u *sfname, // short fname or NULL + linenr_T lnum, // preferred cursor line + int flags, // BLN_ defines handle_T bufnr ) { @@ -2377,10 +2378,11 @@ buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum) { buf_T *buf; - /* Create a buffer. 'buflisted' is not set if it's a new buffer */ + // Create a buffer. 'buflisted' is not set if it's a new buffer buf = buflist_new(ffname, sfname, lnum, 0, 0); - if (buf != NULL && !cmdmod.keepalt) + if (buf != NULL && !cmdmod.keepalt) { curwin->w_alt_fnum = buf->b_fnum; + } return buf; } @@ -2415,8 +2417,9 @@ int buflist_add(char_u *fname, int flags) buf_T *buf; buf = buflist_new(fname, NULL, (linenr_T)0, flags, 0); - if (buf != NULL) + if (buf != NULL) { return buf->b_fnum; + } return 0; } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 767f4b50a2..97aac76860 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1531,8 +1531,9 @@ int rename_buffer(char_u *new_fname) curbuf->b_flags |= BF_NOTEDITED; if (xfname != NULL && *xfname != NUL) { buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, 0, 0); - if (buf != NULL && !cmdmod.keepalt) + if (buf != NULL && !cmdmod.keepalt) { curwin->w_alt_fnum = buf->b_fnum; + } } xfree(fname); xfree(sfname); @@ -2181,9 +2182,9 @@ do_ecmd ( buflist_altfpos(oldwin); } - if (fnum && !(flags & ECMD_RESERVED_BUFNR)) + if (fnum && !(flags & ECMD_RESERVED_BUFNR)) { buf = buflist_findnr(fnum); - else { + } else { if (flags & ECMD_ADDBUF) { linenr_T tlnum = 1L; @@ -2196,7 +2197,8 @@ do_ecmd ( goto theend; } buf = buflist_new(ffname, sfname, 0L, - BLN_CURBUF | ((flags & ECMD_SET_HELP) ? 0 : BLN_LISTED), fnum); + BLN_CURBUF | ((flags & ECMD_SET_HELP) ? 0 : BLN_LISTED), + fnum); // Autocmds may change curwin and curbuf. if (oldwin != NULL) { oldwin = curwin; @@ -2985,11 +2987,11 @@ static bool sub_joining_lines(exarg_T *eap, char_u *pat, ex_may_print(eap); } - if (!eap->is_live){ + if (!eap->is_live) { if (!cmdmod.keeppatterns) { save_re_pat(RE_SUBST, pat, p_magic); } - add_to_history(HIST_SEARCH, pat, TRUE, NUL); + add_to_history(HIST_SEARCH, pat, true, NUL); } return true; @@ -3169,20 +3171,22 @@ void do_sub(exarg_T *eap) EMSG(_(e_backslash)); return; } - if (*cmd != '&') - which_pat = RE_SEARCH; /* use last '/' pattern */ - pat = (char_u *)""; /* empty search pattern */ - delimiter = *cmd++; /* remember delimiter character */ + if (*cmd != '&') { + which_pat = RE_SEARCH; // use last '/' pattern + } + pat = (char_u *)""; // empty search pattern + delimiter = *cmd++; // remember delimiter character has_second_delim = true; - } else { /* find the end of the regexp */ - if (p_altkeymap && curwin->w_p_rl) + } else { // find the end of the regexp + if (p_altkeymap && curwin->w_p_rl) { lrF_sub(cmd); - which_pat = RE_LAST; /* use last used regexp */ - delimiter = *cmd++; /* remember delimiter character */ - pat = cmd; /* remember start of search pat */ + } + which_pat = RE_LAST; // use last used regexp + delimiter = *cmd++; // remember delimiter character + pat = cmd; // remember start of search pat cmd = skip_regexp(cmd, delimiter, p_magic, &eap->arg); - if (cmd[0] == delimiter) { /* end delimiter found */ - *cmd++ = NUL; /* replace it with a NUL */ + if (cmd[0] == delimiter) { // end delimiter found + *cmd++ = NUL; // replace it with a NUL has_second_delim = true; } } @@ -3626,132 +3630,128 @@ void do_sub(exarg_T *eap) // 3. Substitute the string. Don't do this while incsubstitution and // there's no word to replace by eg : ":%s/pattern" if (!eap->is_live || has_second_delim) { - if (subflags.do_count) { - // prevent accidentally changing the buffer by a function - save_ma = curbuf->b_p_ma; - curbuf->b_p_ma = false; - sandbox++; - } - // Save flags for recursion. They can change for e.g. - // :s/^/\=execute("s#^##gn") - subflags_T subflags_save = subflags; - // get length of substitution part - sublen = vim_regsub_multi(®match, - sub_firstlnum - regmatch.startpos[0].lnum, - sub, sub_firstline, false, p_magic, true); - // Don't keep flags set by a recursive call - subflags = subflags_save; - if (subflags.do_count) { - curbuf->b_p_ma = save_ma; - if (sandbox > 0) { - sandbox--; + if (subflags.do_count) { + // prevent accidentally changing the buffer by a function + save_ma = curbuf->b_p_ma; + curbuf->b_p_ma = false; + sandbox++; + } + // Save flags for recursion. They can change for e.g. + // :s/^/\=execute("s#^##gn") + subflags_T subflags_save = subflags; + // get length of substitution part + sublen = vim_regsub_multi(®match, + sub_firstlnum - regmatch.startpos[0].lnum, + sub, sub_firstline, false, p_magic, true); + // Don't keep flags set by a recursive call + subflags = subflags_save; + if (subflags.do_count) { + curbuf->b_p_ma = save_ma; + if (sandbox > 0) { + sandbox--; + } + goto skip; } - goto skip; - } - - /* When the match included the "$" of the last line it may - * go beyond the last line of the buffer. */ - if (nmatch > curbuf->b_ml.ml_line_count - sub_firstlnum + 1) { - nmatch = curbuf->b_ml.ml_line_count - sub_firstlnum + 1; - skip_match = TRUE; - } - /* Need room for: - * - result so far in new_start (not for first sub in line) - * - original text up to match - * - length of substituted part - * - original text after match - */ - if (nmatch == 1) - p1 = sub_firstline; - else { - p1 = ml_get(sub_firstlnum + nmatch - 1); - nmatch_tl += nmatch - 1; - } - size_t copy_len = regmatch.startpos[0].col - copycol; - new_end = sub_grow_buf(&new_start, - copy_len + (STRLEN(p1) - regmatch.endpos[0].col) - + sublen + 1); + // When the match included the "$" of the last line it may + // go beyond the last line of the buffer. + if (nmatch > curbuf->b_ml.ml_line_count - sub_firstlnum + 1) { + nmatch = curbuf->b_ml.ml_line_count - sub_firstlnum + 1; + skip_match = true; + } - /* - * copy the text up to the part that matched - */ - memmove(new_end, sub_firstline + copycol, (size_t)copy_len); - new_end += copy_len; - - (void)vim_regsub_multi(®match, - sub_firstlnum - regmatch.startpos[0].lnum, - sub, new_end, TRUE, p_magic, TRUE); - sub_nsubs++; - did_sub = TRUE; - - /* Move the cursor to the start of the line, to avoid that it - * is beyond the end of the line after the substitution. */ - curwin->w_cursor.col = 0; - - /* For a multi-line match, make a copy of the last matched - * line and continue in that one. */ - if (nmatch > 1) { - sub_firstlnum += nmatch - 1; - xfree(sub_firstline); - sub_firstline = vim_strsave(ml_get(sub_firstlnum)); - // When going beyond the last line, stop substituting. - if (sub_firstlnum <= line2) { - do_again = true; + // Need room for: + // - result so far in new_start (not for first sub in line) + // - original text up to match + // - length of substituted part + // - original text after match + if (nmatch == 1) { + p1 = sub_firstline; } else { - subflags.do_all = false; + p1 = ml_get(sub_firstlnum + nmatch - 1); + nmatch_tl += nmatch - 1; } - } + size_t copy_len = regmatch.startpos[0].col - copycol; + new_end = sub_grow_buf(&new_start, + (STRLEN(p1) - regmatch.endpos[0].col) + + copy_len + sublen + 1); + + // copy the text up to the part that matched + memmove(new_end, sub_firstline + copycol, (size_t)copy_len); + new_end += copy_len; + + (void)vim_regsub_multi(®match, + sub_firstlnum - regmatch.startpos[0].lnum, + sub, new_end, true, p_magic, true); + sub_nsubs++; + did_sub = true; - /* Remember next character to be copied. */ - copycol = regmatch.endpos[0].col; + // Move the cursor to the start of the line, to avoid that it + // is beyond the end of the line after the substitution. + curwin->w_cursor.col = 0; - if (skip_match) { - /* Already hit end of the buffer, sub_firstlnum is one - * less than what it ought to be. */ - xfree(sub_firstline); - sub_firstline = vim_strsave((char_u *)""); - copycol = 0; - } + // For a multi-line match, make a copy of the last matched + // line and continue in that one. + if (nmatch > 1) { + sub_firstlnum += nmatch - 1; + xfree(sub_firstline); + sub_firstline = vim_strsave(ml_get(sub_firstlnum)); + // When going beyond the last line, stop substituting. + if (sub_firstlnum <= line2) { + do_again = true; + } else { + subflags.do_all = false; + } + } - /* - * Now the trick is to replace CTRL-M chars with a real line - * break. This would make it impossible to insert a CTRL-M in - * the text. The line break can be avoided by preceding the - * CTRL-M with a backslash. To be able to insert a backslash, - * they must be doubled in the string and are halved here. - * That is Vi compatible. - */ - for (p1 = new_end; *p1; ++p1) { - if (p1[0] == '\\' && p1[1] != NUL) /* remove backslash */ - STRMOVE(p1, p1 + 1); - else if (*p1 == CAR) { - if (u_inssub(lnum) == OK) { /* prepare for undo */ - *p1 = NUL; /* truncate up to the CR */ - ml_append(lnum - 1, new_start, - (colnr_T)(p1 - new_start + 1), FALSE); - mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L); - if (subflags.do_ask) { - appended_lines(lnum - 1, 1L); - } else { - if (first_line == 0) { - first_line = lnum; + // Remember next character to be copied. + copycol = regmatch.endpos[0].col; + + if (skip_match) { + // Already hit end of the buffer, sub_firstlnum is one + // less than what it ought to be. + xfree(sub_firstline); + sub_firstline = vim_strsave((char_u *)""); + copycol = 0; + } + + // Now the trick is to replace CTRL-M chars with a real line + // break. This would make it impossible to insert a CTRL-M in + // the text. The line break can be avoided by preceding the + // CTRL-M with a backslash. To be able to insert a backslash, + // they must be doubled in the string and are halved here. + // That is Vi compatible. + for (p1 = new_end; *p1; p1++) { + if (p1[0] == '\\' && p1[1] != NUL) { // remove backslash + STRMOVE(p1, p1 + 1); + } else if (*p1 == CAR) { + if (u_inssub(lnum) == OK) { // prepare for undo + *p1 = NUL; // truncate up to the CR + ml_append(lnum - 1, new_start, + (colnr_T)(p1 - new_start + 1), false); + mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L); + if (subflags.do_ask) { + appended_lines(lnum - 1, 1L); + } else { + if (first_line == 0) { + first_line = lnum; + } + last_line = lnum + 1; } - last_line = lnum + 1; + // All line numbers increase. + sub_firstlnum++; + lnum++; + line2++; + // move the cursor to the new line, like Vi + curwin->w_cursor.lnum++; + // copy the rest + STRMOVE(new_start, p1 + 1); + p1 = new_start - 1; } - /* All line numbers increase. */ - ++sub_firstlnum; - ++lnum; - ++line2; - /* move the cursor to the new line, like Vi */ - ++curwin->w_cursor.lnum; - /* copy the rest */ - STRMOVE(new_start, p1 + 1); - p1 = new_start - 1; + } else if (has_mbyte) { + p1 += (*mb_ptr2len)(p1) - 1; } - } else if (has_mbyte) - p1 += (*mb_ptr2len)(p1) - 1; - } + } } // 4. If subflags.do_all is set, find next match. @@ -6066,10 +6066,10 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) /// @param pat The pattern word /// @param sub The replacement word /// @param lmatch The list containing our data -static void inc_sub_display(char_u * pat, - char_u * sub, - MatchedLineVec *lmatch, - bool split) +static void inc_sub_display(char_u *pat, + char_u *sub, + MatchedLineVec *lmatch, + bool split) FUNC_ATTR_NONNULL_ARG(1, 2, 3) { garray_T winsizes; @@ -6102,7 +6102,8 @@ static void inc_sub_display(char_u * pat, cmdwin_type = get_cmdline_type(); // Create the command-line buffer empty. - (void)do_ecmd(inc_sub_bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE | ECMD_RESERVED_BUFNR, NULL); + (void)do_ecmd(inc_sub_bufnr, NULL, NULL, NULL, ECMD_ONE, + ECMD_HIDE | ECMD_RESERVED_BUFNR, NULL); inc_sub_bufnr = curbuf->handle; (void)setfname(curbuf, (char_u *) "[inc_sub]", NULL, true); set_option_value((char_u *) "bt", 0L, (char_u *) "incsub", OPT_LOCAL); diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h index 991695deca..82848528ce 100644 --- a/src/nvim/ex_cmds.h +++ b/src/nvim/ex_cmds.h @@ -9,14 +9,14 @@ #include "nvim/lib/klist.h" #include "nvim/lib/kvec.h" -/* flags for do_ecmd() */ -#define ECMD_HIDE 0x01 /* don't free the current buffer */ -#define ECMD_SET_HELP 0x02 /* set b_help flag of (new) buffer before - opening file */ -#define ECMD_OLDBUF 0x04 /* use existing buffer if it exists */ -#define ECMD_FORCEIT 0x08 /* ! used in Ex command */ -#define ECMD_ADDBUF 0x10 /* don't edit, just add to buffer list */ -#define ECMD_RESERVED_BUFNR 0x20 /* bufnr argument is reserved bufnr */ +// flags for do_ecmd() +#define ECMD_HIDE 0x01 // don't free the current buffer +#define ECMD_SET_HELP 0x02 // set b_help flag of (new) buffer before + // opening file +#define ECMD_OLDBUF 0x04 // use existing buffer if it exists +#define ECMD_FORCEIT 0x08 // ! used in Ex command +#define ECMD_ADDBUF 0x10 // don't edit, just add to buffer list +#define ECMD_RESERVED_BUFNR 0x20 // bufnr argument is reserved bufnr /* for lnum argument in do_ecmd() */ #define ECMD_LASTL (linenr_T)0 /* use last position in loaded file */ diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index a7b1ee2f54..d8b92e9ec0 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1249,7 +1249,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line1 = 1; ea.line2 = 1; ea.is_live = flags & DOCMD_LIVE_PREVIEW; - ++ex_nesting_level; + ex_nesting_level++; /* When the last file has not been edited :q has to be typed twice. */ if (quitmore @@ -1727,8 +1727,9 @@ static char_u * do_one_cmd(char_u **cmdlinep, if (ea.cmdidx == CMD_SIZE) { if (!ea.skip) { STRCPY(IObuff, _("E492: Not an editor command")); - if (!(flags & DOCMD_VERBOSE)) + if (!(flags & DOCMD_VERBOSE)) { append_command(*cmdlinep); + } errormsg = IObuff; did_emsg_syntax = TRUE; } diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index d2106c545f..e30ece3e1b 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -3,14 +3,15 @@ #include "nvim/ex_cmds_defs.h" -/* flags for do_cmdline() */ -#define DOCMD_VERBOSE 0x01 /* included command in error message */ -#define DOCMD_NOWAIT 0x02 /* don't call wait_return() and friends */ -#define DOCMD_REPEAT 0x04 /* repeat exec. until getline() returns NULL */ -#define DOCMD_KEYTYPED 0x08 /* don't reset KeyTyped */ -#define DOCMD_EXCRESET 0x10 /* reset exception environment (for debugging)*/ -#define DOCMD_KEEPLINE 0x20 /* keep typed line for repeating with "." */ -#define DOCMD_LIVE_PREVIEW 0x40 // Command is a live preview like incsubstitute +// flags for do_cmdline() +#define DOCMD_VERBOSE 0x01 // included command in error message +#define DOCMD_NOWAIT 0x02 // don't call wait_return() and friends +#define DOCMD_REPEAT 0x04 // repeat exec. until getline() returns NULL +#define DOCMD_KEYTYPED 0x08 // don't reset KeyTyped +#define DOCMD_EXCRESET 0x10 // reset exception environment (for debugging +#define DOCMD_KEEPLINE 0x20 // keep typed line for repeating with "." +#define DOCMD_LIVE_PREVIEW 0x40 // Command is a live preview like + // incsubstitution /* defines for eval_vars() */ #define VALID_PATH 1 diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 3871a3ab78..7ce2c57420 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -5063,10 +5063,10 @@ void buf_reload(buf_T *buf, int orig_mode) * the old contents. Can't use memory only, the file might be * too big. Use a hidden buffer to move the buffer contents to. */ - if (bufempty() || saved == FAIL) + if (bufempty() || saved == FAIL) { savebuf = NULL; - else { - /* Allocate a buffer without putting it in the buffer list. */ + } else { + // Allocate a buffer without putting it in the buffer list. savebuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY, 0); if (savebuf != NULL && buf == curbuf) { /* Open the memline. */ diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 489e5a2dde..96983a6c3b 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -473,7 +473,7 @@ static void fname2fnum(xfmark_T *fm) os_dirname(IObuff, IOSIZE); p = path_shorten_fname(NameBuff, IObuff); - /* buflist_new() will call fmarks_check_names() */ + // buflist_new() will call fmarks_check_names() (void)buflist_new(NameBuff, p, (linenr_T)1, 0, 0); } } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 08345a323c..97172a15ff 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3326,10 +3326,11 @@ load_dummy_buffer ( int failed = TRUE; aco_save_T aco; - /* Allocate a buffer without putting it in the buffer list. */ + // Allocate a buffer without putting it in the buffer list. newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY, 0); - if (newbuf == NULL) + if (newbuf == NULL) { return NULL; + } /* Init the options. */ buf_copy_options(newbuf, BCO_ENTER | BCO_NOHELP); diff --git a/src/nvim/window.c b/src/nvim/window.c index 53bc484b48..45b52e9454 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2904,8 +2904,9 @@ static int win_alloc_firstwin(win_T *oldwin) /* Very first window, need to create an empty buffer for it and * initialize from scratch. */ curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED, 0); - if (curbuf == NULL) + if (curbuf == NULL) { return FAIL; + } curwin->w_buffer = curbuf; curwin->w_s = &(curbuf->b_s); curbuf->b_nwindows = 1; /* there is one window */ -- cgit From c04ffe866d276d6a6bd9e9c6a8b0dbb71504db7c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 31 Oct 2016 03:50:19 +0100 Subject: 'inccommand': rework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Eliminate/isolate static/global variables - Remove special-case parameter from buflist_new() - Remove special-case ECMD_RESERVED_BUFNR - To determine when u_undo_and_forget() should be done, check b_changedtick instead of a heuristic. - use mb_string2cells() instead of strlen() to measure the :sub patterns - call ml_close() before buf_clear_file(). Avoids leaks caught by ASan. Original patch by: Robin Elrharbi-Fleury (Robinhola) Audrey Rayé (Adrey06) Philémon Hullot (DesbyP) Aymeric Collange (aym7) Clément Guyomard (Clement0) --- src/nvim/buffer.c | 79 +++++--- src/nvim/eval.c | 2 +- src/nvim/ex_cmds.c | 513 ++++++++++++++++++++++----------------------------- src/nvim/ex_cmds.h | 21 +-- src/nvim/ex_cmds.lua | 6 +- src/nvim/ex_docmd.c | 25 ++- src/nvim/ex_docmd.h | 3 +- src/nvim/ex_getln.c | 23 ++- src/nvim/fileio.c | 5 +- src/nvim/globals.h | 1 - src/nvim/mark.c | 2 +- src/nvim/memline.c | 15 +- src/nvim/quickfix.c | 2 +- src/nvim/shada.c | 2 +- src/nvim/syntax.c | 13 +- src/nvim/undo.c | 30 ++- src/nvim/window.c | 2 +- 17 files changed, 335 insertions(+), 409 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 65a42f063a..190c281c76 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -268,6 +268,9 @@ open_buffer ( bool buf_valid(buf_T *buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { + if (buf == NULL) { + return false; + } FOR_ALL_BUFFERS(bp) { if (bp == buf) { return true; @@ -479,6 +482,18 @@ void buf_clear_file(buf_T *buf) buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */ } +/// Clears the current buffer contents. +void buf_clear(void) +{ + linenr_T line_count = curbuf->b_ml.ml_line_count; + while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) { + ml_delete((linenr_T)1, false); + } + deleted_lines_mark(1, line_count); // prepare for display + ml_close(curbuf, true); // free memline_T + buf_clear_file(curbuf); +} + /// buf_freeall() - free all things allocated for a buffer that are related to /// the file. Careful: get here with "curwin" NULL when exiting. /// @@ -678,7 +693,7 @@ void handle_swap_exists(buf_T *old_curbuf) swap_exists_did_quit = true; close_buffer(curwin, curbuf, DOBUF_UNLOAD, false); if (!buf_valid(old_curbuf) || old_curbuf == curbuf) { - old_curbuf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED, 0); + old_curbuf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED); } if (old_curbuf != NULL) { enter_buffer(old_curbuf); @@ -1320,29 +1335,29 @@ void do_autochdir(void) } } -/* - * functions for dealing with the buffer list - */ +// +// functions for dealing with the buffer list +// -/* - * Add a file name to the buffer list. Return a pointer to the buffer. - * If the same file name already exists return a pointer to that buffer. - * If it does not exist, or if fname == NULL, a new entry is created. - * If (flags & BLN_CURBUF) is TRUE, may use current buffer. - * If (flags & BLN_LISTED) is TRUE, add new buffer to buffer list. - * If (flags & BLN_DUMMY) is TRUE, don't count it as a real buffer. - * This is the ONLY way to create a new buffer. - */ static int top_file_num = 1; /* highest file number */ -buf_T * -buflist_new( - char_u *ffname, // full path of fname or relative - char_u *sfname, // short fname or NULL - linenr_T lnum, // preferred cursor line - int flags, // BLN_ defines - handle_T bufnr -) +/// Add a file name to the buffer list. +/// If the same file name already exists return a pointer to that buffer. +/// If it does not exist, or if fname == NULL, a new entry is created. +/// If (flags & BLN_CURBUF) is TRUE, may use current buffer. +/// If (flags & BLN_LISTED) is TRUE, add new buffer to buffer list. +/// If (flags & BLN_DUMMY) is TRUE, don't count it as a real buffer. +/// If (flags & BLN_NEW) is TRUE, don't use an existing buffer. +/// This is the ONLY way to create a new buffer. +/// +/// @param ffname full path of fname or relative +/// @param sfname short fname or NULL +/// @param lnum preferred cursor line +/// @param flags BLN_ defines +/// @param bufnr +/// +/// @return pointer to the buffer +buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) { buf_T *buf; @@ -1460,9 +1475,7 @@ buflist_new( } lastbuf = buf; - // If bufnr is nonzero it is assumed to be a previously - // reserved buffer number (handle) - buf->handle = bufnr != 0 ? bufnr : top_file_num++; + buf->b_fnum = top_file_num++; handle_register_buffer(buf); if (top_file_num < 0) { // wrap around (may cause duplicates) EMSG(_("W14: Warning: List of file names overflow")); @@ -2379,7 +2392,7 @@ buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum) buf_T *buf; // Create a buffer. 'buflisted' is not set if it's a new buffer - buf = buflist_new(ffname, sfname, lnum, 0, 0); + buf = buflist_new(ffname, sfname, lnum, 0); if (buf != NULL && !cmdmod.keepalt) { curwin->w_alt_fnum = buf->b_fnum; } @@ -2416,7 +2429,7 @@ int buflist_add(char_u *fname, int flags) { buf_T *buf; - buf = buflist_new(fname, NULL, (linenr_T)0, flags, 0); + buf = buflist_new(fname, NULL, (linenr_T)0, flags); if (buf != NULL) { return buf->b_fnum; } @@ -5199,7 +5212,7 @@ bool buf_contents_changed(buf_T *buf) bool differ = true; // Allocate a buffer without putting it in the buffer list. - buf_T *newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY, 0); + buf_T *newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); if (newbuf == NULL) { return true; } @@ -5259,3 +5272,15 @@ wipe_buffer ( unblock_autocmds(); } } + +/// Creates or switches to a special-purpose buffer. +/// +/// @param bufnr Buffer to switch to, or 0 to create a new buffer. +void buf_open_special(handle_T bufnr, char *bufname, char *buftype) +{ + (void)do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL); + (void)setfname(curbuf, (char_u *)bufname, NULL, true); + set_option_value((char_u *)"bt", 0L, (char_u *)buftype, OPT_LOCAL); + set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); + RESET_BINDING(curwin); +} diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 508bdac46d..512555eac1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7703,7 +7703,7 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) && !error && (name = get_tv_string_chk(&argvars[0])) != NULL && !error) - buf = buflist_new(name, NULL, (linenr_T)1, 0, 0); + buf = buflist_new(name, NULL, (linenr_T)1, 0); if (buf != NULL) rettv->vval.v_number = buf->b_fnum; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 97aac76860..9f781c0f8e 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -10,6 +10,9 @@ #include #include +#include "nvim/api/private/defs.h" +#include "nvim/api/buffer.h" +#include "nvim/log.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/ex_cmds.h" @@ -65,12 +68,6 @@ */ typedef struct sign sign_T; -// boolean to know if inc_sub needs to undo -static bool inc_sub_did_changes = false; - -// reuse the same bufnr for inc_sub -static handle_T inc_sub_bufnr = 0; - /// Case matching style to use for :substitute typedef enum { kSubHonorOptions = 0, ///< Honor the user's 'ignorecase'/'smartcase' options @@ -90,6 +87,15 @@ typedef struct { SubIgnoreType do_ic; ///< ignore case flag } subflags_T; +/// Lines matched during 'incsubstitute'. +typedef struct { + linenr_T lnum; + long nmatch; + char_u *line; + kvec_t(colnr_T) cols; //< columns of in-line matches +} MatchedLine; +typedef kvec_t(MatchedLine) MatchedLineVec; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_cmds.c.generated.h" #endif @@ -1530,7 +1536,7 @@ int rename_buffer(char_u *new_fname) } curbuf->b_flags |= BF_NOTEDITED; if (xfname != NULL && *xfname != NUL) { - buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, 0, 0); + buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, 0); if (buf != NULL && !cmdmod.keepalt) { curwin->w_alt_fnum = buf->b_fnum; } @@ -2027,37 +2033,34 @@ theend: return retval; } -/* - * start editing a new file - * - * fnum: file number; if zero use ffname/sfname - * ffname: the file name - * - full path if sfname used, - * - any file name if sfname is NULL - * - empty string to re-edit with the same file name (but may be - * in a different directory) - * - NULL to start an empty buffer - * sfname: the short file name (or NULL) - * eap: contains the command to be executed after loading the file and - * forced 'ff' and 'fenc' - * newlnum: if > 0: put cursor on this line number (if possible) - * if ECMD_LASTL: use last position in loaded file - * if ECMD_LAST: use last position in all files - * if ECMD_ONE: use first line - * flags: - * ECMD_HIDE: if TRUE don't free the current buffer - * ECMD_SET_HELP: set b_help flag of (new) buffer before opening file - * ECMD_OLDBUF: use existing buffer if it exists - * ECMD_FORCEIT: ! used for Ex command - * ECMD_ADDBUF: don't edit, just add to buffer list - * oldwin: Should be "curwin" when editing a new buffer in the current - * window, NULL when splitting the window first. When not NULL info - * of the previous buffer for "oldwin" is stored. - * - * return FAIL for failure, OK otherwise - */ -int -do_ecmd ( +/// start editing a new file +/// +/// @param fnum file number; if zero use ffname/sfname +/// @param ffname the file name +/// - full path if sfname used, +/// - any file name if sfname is NULL +/// - empty string to re-edit with the same file name (but may +/// be in a different directory) +/// - NULL to start an empty buffer +/// @param sfname the short file name (or NULL) +/// @param eap contains the command to be executed after loading the file +/// and forced 'ff' and 'fenc' +/// @param newlnum if > 0: put cursor on this line number (if possible) +/// ECMD_LASTL: use last position in loaded file +/// ECMD_LAST: use last position in all files +/// ECMD_ONE: use first line +/// @param flags ECMD_HIDE: if TRUE don't free the current buffer +/// ECMD_SET_HELP: set b_help flag of (new) buffer before +/// opening file +/// ECMD_OLDBUF: use existing buffer if it exists +/// ECMD_FORCEIT: ! used for Ex command +/// ECMD_ADDBUF: don't edit, just add to buffer list +/// @param oldwin Should be "curwin" when editing a new buffer in the current +/// window, NULL when splitting the window first. When not NULL +/// info of the previous buffer for "oldwin" is stored. +/// +/// @return FAIL for failure, OK otherwise +int do_ecmd ( int fnum, char_u *ffname, char_u *sfname, @@ -2182,9 +2185,10 @@ do_ecmd ( buflist_altfpos(oldwin); } - if (fnum && !(flags & ECMD_RESERVED_BUFNR)) { + if (fnum) { buf = buflist_findnr(fnum); } else { + ILOG("here"); if (flags & ECMD_ADDBUF) { linenr_T tlnum = 1L; @@ -2193,12 +2197,11 @@ do_ecmd ( if (tlnum <= 0) tlnum = 1L; } - (void)buflist_new(ffname, sfname, tlnum, BLN_LISTED, fnum); + (void)buflist_new(ffname, sfname, tlnum, BLN_LISTED); goto theend; } buf = buflist_new(ffname, sfname, 0L, - BLN_CURBUF | ((flags & ECMD_SET_HELP) ? 0 : BLN_LISTED), - fnum); + BLN_CURBUF | ((flags & ECMD_SET_HELP) ? 0 : BLN_LISTED)); // Autocmds may change curwin and curbuf. if (oldwin != NULL) { oldwin = curwin; @@ -3098,16 +3101,17 @@ static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags, return cmd; } -/* do_sub() - * - * Perform a substitution from line eap->line1 to line eap->line2 using the - * command pointed to by eap->arg which should be of the form: - * - * /pattern/substitution/{flags} - * - * The usual escapes are supported as described in the regexp docs. - */ -void do_sub(exarg_T *eap) +/// do_sub() +/// +/// Perform a substitution from line eap->line1 to line eap->line2 using the +/// command pointed to by eap->arg which should be of the form: +/// +/// /pattern/substitution/{flags} +/// +/// The usual escapes are supported as described in the regexp docs. +/// +/// @return buffer used for 'incsubstitute' +buf_T *do_sub(exarg_T *eap) { long i = 0; regmmatch_T regmatch; @@ -3123,24 +3127,22 @@ void do_sub(exarg_T *eap) }; char_u *pat = NULL, *sub = NULL; // init for GCC int delimiter; + bool has_second_delim = false; int sublen; int got_quit = false; int got_match = false; int which_pat; char_u *cmd = eap->arg; linenr_T first_line = 0; // first changed line - linenr_T last_line= 0; // below last changed line AFTER the - // change + linenr_T last_line= 0; // below last changed line AFTER the change linenr_T old_line_count = curbuf->b_ml.ml_line_count; - char_u *sub_firstline; // allocated copy of first sub line - bool endcolumn = false; // cursor in last column when done + char_u *sub_firstline; // allocated copy of first sub line + bool endcolumn = false; // cursor in last column when done + MatchedLineVec matched_lines = KV_INITIAL_VALUE; pos_T old_cursor = curwin->w_cursor; int start_nsubs; int save_ma = 0; - inc_sub_did_changes = false; - bool has_second_delim = false; - if (!global_busy) { sub_nsubs = 0; sub_nlines = 0; @@ -3158,7 +3160,7 @@ void do_sub(exarg_T *eap) /* don't accept alphanumeric for separator */ if (isalpha(*cmd)) { EMSG(_("E146: Regular expressions can't be delimited by letters")); - return; + return NULL; } /* * undocumented vi feature: @@ -3169,7 +3171,7 @@ void do_sub(exarg_T *eap) ++cmd; if (vim_strchr((char_u *)"/?&", *cmd) == NULL) { EMSG(_(e_backslash)); - return; + return NULL; } if (*cmd != '&') { which_pat = RE_SEARCH; // use last '/' pattern @@ -3217,7 +3219,7 @@ void do_sub(exarg_T *eap) } else if (!eap->skip) { /* use previous pattern and substitution */ if (old_sub.sub == NULL) { /* there is no previous command */ EMSG(_(e_nopresub)); - return; + return NULL; } pat = NULL; /* search_regcomp() will use previous pattern */ sub = (char_u *) old_sub.sub; @@ -3228,7 +3230,7 @@ void do_sub(exarg_T *eap) } if (sub_joining_lines(eap, pat, sub, cmd)) { - return; + return NULL; } cmd = sub_parse_flags(cmd, &subflags, &which_pat); @@ -3242,7 +3244,7 @@ void do_sub(exarg_T *eap) i = getdigits_long(&cmd); if (i <= 0 && !eap->skip && subflags.do_error) { EMSG(_(e_zerocount)); - return; + return NULL; } eap->line1 = eap->line2; eap->line2 += i - 1; @@ -3258,17 +3260,17 @@ void do_sub(exarg_T *eap) eap->nextcmd = check_nextcmd(cmd); if (eap->nextcmd == NULL) { EMSG(_(e_trailing)); - return; + return NULL; } } if (eap->skip) /* not executing commands, only parsing */ - return; + return NULL; if (!subflags.do_count && !MODIFIABLE(curbuf)) { // Substitution is not allowed in non-'modifiable' buffer EMSG(_(e_modifiable)); - return; + return NULL; } int search_options = eap->is_live ? 0 : SEARCH_HIS; @@ -3277,7 +3279,7 @@ void do_sub(exarg_T *eap) if (subflags.do_error) { EMSG(_(e_invcmd)); } - return; + return NULL; } // the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' @@ -3297,9 +3299,6 @@ void do_sub(exarg_T *eap) if (!(sub[0] == '\\' && sub[1] == '=')) sub = regtilde(sub, p_magic); - // list to save matched lines - MatchedLineVec lmatch = KV_INITIAL_VALUE; - // Check for a match on each line. linenr_T line2 = eap->line2; for (linenr_T lnum = eap->line1; @@ -3368,7 +3367,7 @@ void do_sub(exarg_T *eap) copycol = 0; matchcol = 0; // the current match - MatchedLine cmatch = { 0, 0, NULL, KV_INITIAL_VALUE }; + MatchedLine matched_line = { 0, 0, NULL, KV_INITIAL_VALUE }; /* At first match, remember current cursor position. */ if (!got_match) { @@ -3405,9 +3404,9 @@ void do_sub(exarg_T *eap) curwin->w_cursor.lnum = lnum; do_again = FALSE; - // increment number of match on the line and store the column - cmatch.nmatch++; - kv_push(cmatch.start_col, regmatch.startpos[0].col); + // Increment the in-line match count and store the column. + matched_line.nmatch++; + kv_push(matched_line.cols, regmatch.startpos[0].col); /* * 1. Match empty string does not count, except for first @@ -3627,8 +3626,8 @@ void do_sub(exarg_T *eap) * use "\=col("."). */ curwin->w_cursor.col = regmatch.startpos[0].col; - // 3. Substitute the string. Don't do this while incsubstitution and - // there's no word to replace by eg : ":%s/pattern" + // 3. Substitute the string. During 'incsubstitute' only do this if + // there is a replace pattern. if (!eap->is_live || has_second_delim) { if (subflags.do_count) { // prevent accidentally changing the buffer by a function @@ -3873,11 +3872,9 @@ skip: xfree(sub_firstline); /* free the copy of the original line */ sub_firstline = NULL; - // saving info about the matched line - cmatch.lnum = lnum; - cmatch.line = vim_strsave(ml_get(lnum)); - - kv_push(lmatch, cmatch); + matched_line.lnum = lnum; + matched_line.line = vim_strsave(ml_get(lnum)); + kv_push(matched_lines, matched_line); } line_breakcheck(); @@ -3946,78 +3943,65 @@ skip: subflags.do_all = save_do_all; subflags.do_ask = save_do_ask; - // inc_sub if sub on the whole file and there are results to display - if (lmatch.size != 0) { - // we did incsubstitute only if we had no word to replace by - // by and no ending slash - if (!subflags.do_count && (!eap->is_live || has_second_delim)) { - inc_sub_did_changes = true; - } - if (pat != NULL && *p_ics != NUL && eap->is_live) { - bool split = true; - - // p_ics is "", "nosplit" or "split" - if (*p_ics == 'n' || eap[0].cmdlinep[0][0] == 's') { - split = false; - } - - // Place cursor on the first match after the cursor - // If all matches are before the cursor, then do_sub did - // already place the cursor on the last match - - linenr_T cur_lnum = 0; - colnr_T cur_col = -1; - MatchedLine current; - - for (size_t j = 0; j < lmatch.size; j++) { - current = lmatch.items[j]; - cur_lnum = current.lnum; - - // 1. Match on line of the cursor, need to iterate over the - // matches on this line to see if there is one on a later - // column - if (cur_lnum == old_cursor.lnum) { - for (size_t i = 0; i < current.start_col.size; i++) { - if (current.start_col.items[i] >= old_cursor.col) { - cur_col = current.start_col.items[i]; - break; - } - } - // match on cursor's line, after the cursor - if (cur_col != -1) { - curwin->w_cursor.lnum = cur_lnum; - curwin->w_cursor.col = cur_col; + // Show 'incsubstitute' preview if there are matched lines. + buf_T *incsub_buf = NULL; + if (matched_lines.size != 0 && pat != NULL && *p_ics != NUL && eap->is_live) { + // Place cursor on the first match after the cursor. + // If all matches are before the cursor, then do_sub already placed the + // cursor on the last match. + + linenr_T cur_lnum = 0; + colnr_T cur_col = -1; + MatchedLine current; + + for (size_t j = 0; j < matched_lines.size; j++) { + current = matched_lines.items[j]; + cur_lnum = current.lnum; + + // 1. Match on line of the cursor, need to iterate over the + // matches on this line to see if there is one on a later + // column + if (cur_lnum == old_cursor.lnum) { + for (size_t i = 0; i < current.cols.size; i++) { + if (current.cols.items[i] >= old_cursor.col) { + cur_col = current.cols.items[i]; break; } - // 2. Match on line after cursor, just put cursor on column - // of first match there - } else if (cur_lnum > old_cursor.lnum) { - cur_col = current.start_col.items[0]; - curwin->w_cursor.lnum = cur_lnum; - curwin->w_cursor.col = cur_col; - break; } + // match on cursor's line, after the cursor + if (cur_col != -1) { + curwin->w_cursor.lnum = cur_lnum; + curwin->w_cursor.col = cur_col; + break; + } + // 2. Match on line after cursor, just put cursor on column + // of first match there + } else if (cur_lnum > old_cursor.lnum) { + cur_col = current.cols.items[0]; + curwin->w_cursor.lnum = cur_lnum; + curwin->w_cursor.col = cur_col; + break; } - - inc_sub_display(pat, sub, &lmatch, split); - } else if (*p_ics != NUL && eap->is_live) { - curwin->w_cursor = old_cursor; } - } else { - curwin->w_cursor = old_cursor; - } - - MatchedLine current; - for (size_t j = 0; j < lmatch.size; j++) { - current = lmatch.items[j]; - if (current.line) { xfree(current.line); } + incsub_buf = incsub_display(pat, sub, eap->line1, eap->line2, + &matched_lines); - kv_destroy(current.start_col); + } else if (*p_ics != NUL && eap->is_live) { + curwin->w_cursor = old_cursor; // don't move the cursor } + MatchedLine current; + for (size_t j = 0; j < matched_lines.size; j++) { + current = matched_lines.items[j]; + if (current.line) { + xfree(current.line); + } + kv_destroy(current.cols); + } - kv_destroy(lmatch); + kv_destroy(matched_lines); + return incsub_buf; } // NOLINT(readability/fn_size) /* @@ -6058,192 +6042,143 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) } } -/// Open a window for displaying of the inc_sub mode. -/// -/// Does not allow editing in the window. Closes the window and restores -/// the window layout before returning. -/// -/// @param pat The pattern word -/// @param sub The replacement word -/// @param lmatch The list containing our data -static void inc_sub_display(char_u *pat, - char_u *sub, - MatchedLineVec *lmatch, - bool split) - FUNC_ATTR_NONNULL_ARG(1, 2, 3) +/// Shows a preview of :substitute (for 'incsubstitute'). +/// With incsubstitute=split, shows a special buffer in a window, draws the +/// screen, then restores the layout. +static buf_T *incsub_display(char_u *pat, char_u *sub, + linenr_T line1, linenr_T line2, + MatchedLineVec *matched_lines) + FUNC_ATTR_NONNULL_ALL { - garray_T winsizes; - int save_restart_edit = restart_edit; - int save_State = State; - int save_exmode = exmode_active; - int save_cmdmsg_rl = cmdmsg_rl; - - // Can't do this recursively. Can't do it when typing a password. - if (cmdline_star > 0) { - beep_flush(); - return; - } - - // Save current window sizes. - win_size_save(&winsizes); - - // Save the current window to restore it later - win_T *oldwin = curwin; - - if (split) { - // don't use a new tab page - cmdmod.tab = 0; - - // Create a window for the command-line buffer. - if (win_split((int)p_cwh, WSP_BOT) == FAIL) { - beep_flush(); - return; - } - cmdwin_type = get_cmdline_type(); - - // Create the command-line buffer empty. - (void)do_ecmd(inc_sub_bufnr, NULL, NULL, NULL, ECMD_ONE, - ECMD_HIDE | ECMD_RESERVED_BUFNR, NULL); - inc_sub_bufnr = curbuf->handle; - (void)setfname(curbuf, (char_u *) "[inc_sub]", NULL, true); - set_option_value((char_u *) "bt", 0L, (char_u *) "incsub", OPT_LOCAL); - set_option_value((char_u *) "swf", 0L, NULL, OPT_LOCAL); - curbuf->b_p_ma = false; // Not Modifiable + static handle_T bufnr = 0; // special buffer, re-used on each visit + + garray_T save_winsizes; + win_T *save_curwin = curwin; + cmdmod_T save_cmdmod = cmdmod; + char_u *save_shm_p = vim_strsave(p_shm); + size_t sub_size = mb_string2cells(sub); + size_t pat_size = mb_string2cells(pat); + + // We keep a special-purpose buffer around, but don't assume it exists. + buf_T *incsub_buf = bufnr ? buflist_findnr(bufnr) : 0; + win_size_save(&save_winsizes); // Save current window sizes. + cmdmod.tab = 0; // disable :tab modifier + cmdmod.noswapfile = true; // disable swap for 'incsubstitute' buffer + // disable file info message + set_option_value((char_u *)"shm", 0L, (char_u *)"F", 0); + + bool outside_curline = (line1 != curwin->w_cursor.lnum + || line2 != curwin->w_cursor.lnum); + bool split = outside_curline && (*p_ics != 'n') && (sub_size || pat_size); + if (incsub_buf == curbuf) { // Preview buffer cannot preview itself! + split = false; + incsub_buf = NULL; + } + + if (split && win_split((int)p_cwh, WSP_BOT) != FAIL) { + buf_open_special(incsub_buf ? bufnr : 0, "[inc_sub]", "incsub"); + buf_clear(); + incsub_buf = curbuf; + set_option_value((char_u *)"bl", 0L, NULL, OPT_LOCAL); + set_option_value((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL); + bufnr = incsub_buf->handle; + curbuf->b_p_ma = true; + curbuf->b_p_ul = -1; curwin->w_p_fen = false; - curwin->w_p_rl = cmdmsg_rl; - cmdmsg_rl = false; - RESET_BINDING(curwin); + curbuf->b_p_tw = 0; // Reset 'textwidth' (was set by ftplugin) - // Showing the prompt may have set need_wait_return, reset it. - need_wait_return = false; - - // Reset 'textwidth' after setting 'filetype' - // (the Vim filetype plugin sets 'textwidth' to 78). - curbuf->b_p_tw = 0; - - // Save the buffer used in the split - livebuf = curbuf; - - // Initialize line and highlight variables - int line = 0; - int src_id_highlight = 0; - long sub_size = STRLEN(sub); - long pat_size = STRLEN(pat); - - // Get the width of the column which display the number of the line - linenr_T highest_num_line = kv_last(*lmatch).lnum; - - // computing the length of the column that will display line number + // Width of the "| lnum|..." column which displays the line numbers. + linenr_T highest_num_line = kv_last(*matched_lines).lnum; int col_width = log10(highest_num_line) + 1 + 3; - // will be allocated in the loop char *str = NULL; - size_t old_line_size = 0; size_t line_size; + int src_id_highlight = 0; + int hl_id = syn_check_group((char_u *)"IncSubstitute", 13); - // Append the lines to our buffer - for (size_t i = 0; i < (*lmatch).size; i++) { - MatchedLine mat = (*lmatch).items[i]; - line_size = STRLEN(mat.line) + col_width + 1; + // Dump the lines into the incsub buffer. + for (size_t line = 0; line < matched_lines->size; line++) { + MatchedLine mat = matched_lines->items[line]; + line_size = mb_string2cells(mat.line) + col_width + 1; - // Reallocation if str not long enough + // Reallocate if str not long enough if (line_size > old_line_size) { str = xrealloc(str, line_size * sizeof(char)); old_line_size = line_size; } - // put ' [ lnum]line' into str and append it to the incsubstitute buffer + // put " | lnum|line" into str and append it to the incsubstitute buffer snprintf(str, line_size, " [%*ld]%s", col_width - 3, mat.lnum, mat.line); - ml_append(line++, (char_u *)str, (colnr_T)line_size, false); + ml_append(line, (char_u *)str, (colnr_T)line_size, false); // highlight the replaced part if (sub_size > 0) { - int hlgroup_ls = syn_check_group((char_u *)"IncSubstitute", 13); - - for (size_t j = 0; j < mat.start_col.size; j++) { - src_id_highlight = - bufhl_add_hl(curbuf, - src_id_highlight, - hlgroup_ls, // id of our highlight - line, - mat.start_col.items[j] + col_width - + j * (sub_size - pat_size) + 1, - mat.start_col.items[j] + col_width - + j * (sub_size - pat_size) + sub_size); + for (size_t i = 0; i < mat.cols.size; i++) { + colnr_T col_start = mat.cols.items[i] + col_width + + i * (sub_size - pat_size) + 1; + colnr_T col_end = col_start - 1 + sub_size; + src_id_highlight = bufhl_add_hl(curbuf, src_id_highlight, hl_id, + line + 1, col_start, col_end); } } } xfree(str); - redraw_later(SOME_VALID); } - // Restore the old window - win_enter(oldwin, false); - win_size_restore(&winsizes); - ga_clear(&winsizes); - exmode_active = save_exmode; - restart_edit = save_restart_edit; - cmdmsg_rl = save_cmdmsg_rl; - State = save_State; + redraw_later(SOME_VALID); + + win_enter(save_curwin, false); // Return to original window + win_size_restore(&save_winsizes); + ga_clear(&save_winsizes); + + set_option_value((char_u *)"shm", 0L, save_shm_p, 0); + xfree(save_shm_p); - cmdwin_type = 0; + // Update screen now. Must do this _before_ close_windows(). int save_rd = RedrawingDisabled; RedrawingDisabled = 0; - update_screen(0); + update_screen(NOT_VALID); RedrawingDisabled = save_rd; - setmouse(); -} + cmdmod = save_cmdmod; + return incsub_buf; +} -/// :substitute command implementation +/// :substitute command /// -/// Uses do_sub() to do the actual substitution. Undoes the substitution and -/// removes it from the undo history unless finishing the command. If -/// ics is set to "", it just calls do_sub(). -void do_inc_sub(exarg_T *eap) +/// If 'incsubstitute' is empty, this just calls do_sub(). +/// If 'incsubstitute' is set, substitutes as-you-type ("live"). +/// If the command is cancelled the changes are removed from undo history. +void ex_substitute(exarg_T *eap) { - // if incsubstitute disabled, do it the classical way - if (*p_ics == NUL || !eap->is_live) { - do_sub(eap); + if (*p_ics == NUL || !eap->is_live) { // 'incsubstitute' is disabled + (void)do_sub(eap); return; } - // Save the state of eap - char_u *tmp = eap->arg; - + char_u *save_eap = eap->arg; save_search_patterns(); + int save_changedtick = curbuf->b_changedtick; + long save_b_p_ul = curbuf->b_p_ul; + curbuf->b_p_ul = LONG_MAX; // make sure we can undo all changes + block_autocmds(); // disable events before incsub opening window/buffer + emsg_off++; // No error messages for live commands - // save the value of undolevels to the maximum value to avoid losing - // history when it is set to a low value - long b_p_ul_save = curbuf->b_p_ul; - curbuf->b_p_ul = LONG_MAX; - - // Incsub window/buffer is opened in do_sub, so to suppress autocmd - // we need to start it before the call - block_autocmds(); + buf_T *incsub_buf = do_sub(eap); - emsg_off++; // No error messages for live commands - do_sub(eap); - emsg_off--; - if (inc_sub_did_changes) { - if (!u_undo_and_forget(1)) { - abort(); - } - inc_sub_did_changes = false; + if (save_changedtick != curbuf->b_changedtick + && !u_undo_and_forget(1)) { + abort(); } - - // Put back eap in first state - eap->arg = tmp; - restore_search_patterns(); - curbuf->b_p_ul = b_p_ul_save; - - update_screen(0); - if (livebuf != NULL && buf_valid(livebuf)) { - close_windows(livebuf, false); - wipe_buffer(livebuf, false); + if (buf_valid(incsub_buf)) { + // XXX: Must do this *after* u_undo_and_forget(), why? + close_windows(incsub_buf, false); } + curbuf->b_p_ul = save_b_p_ul; + eap->arg = save_eap; + restore_search_patterns(); + emsg_off--; unblock_autocmds(); - redraw_later(SOME_VALID); } diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h index 82848528ce..ccb2202edb 100644 --- a/src/nvim/ex_cmds.h +++ b/src/nvim/ex_cmds.h @@ -6,8 +6,6 @@ #include "nvim/os/time.h" #include "nvim/eval_defs.h" #include "nvim/pos.h" -#include "nvim/lib/klist.h" -#include "nvim/lib/kvec.h" // flags for do_ecmd() #define ECMD_HIDE 0x01 // don't free the current buffer @@ -18,6 +16,7 @@ #define ECMD_ADDBUF 0x10 // don't edit, just add to buffer list #define ECMD_RESERVED_BUFNR 0x20 // bufnr argument is reserved bufnr + /* for lnum argument in do_ecmd() */ #define ECMD_LASTL (linenr_T)0 /* use last position in loaded file */ #define ECMD_LAST (linenr_T)-1 /* use last position in all files */ @@ -30,24 +29,6 @@ typedef struct { list_T *additional_elements; ///< Additional data left from ShaDa file. } SubReplacementString; - -// Defs for inc_sub functionality - -/// Structure to backup and display matched lines in incsubstitution -typedef struct { - linenr_T lnum; - long nmatch; - char_u *line; - // list of column numbers of matches on this line - kvec_t(colnr_T) start_col; -} MatchedLine; - -// List of matched lines -typedef kvec_t(MatchedLine) MatchedLineVec; - -// End defs for inc_sub functionality - - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_cmds.h.generated.h" #endif diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 04fc163222..95ede4bdc5 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -2194,7 +2194,7 @@ return { command='substitute', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN), addr_type=ADDR_LINES, - func='do_inc_sub', + func='ex_substitute', }, { command='sNext', @@ -3181,7 +3181,7 @@ return { enum='CMD_and', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), addr_type=ADDR_LINES, - func='do_sub', + func='ex_substitute', }, { command='<', @@ -3222,6 +3222,6 @@ return { enum='CMD_tilde', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), addr_type=ADDR_LINES, - func='do_sub', + func='ex_substitute', }, } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d8b92e9ec0..30347cbe85 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1248,7 +1248,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, memset(&ea, 0, sizeof(ea)); ea.line1 = 1; ea.line2 = 1; - ea.is_live = flags & DOCMD_LIVE_PREVIEW; + ea.is_live = flags & DOCMD_LIVE; ex_nesting_level++; /* When the last file has not been edited :q has to be typed twice. */ @@ -9652,25 +9652,22 @@ static void ex_terminal(exarg_T *eap) /// Check whether commandline starts with a live command /// -/// @param[in] cmd_live Commandline to check. May start with a range. +/// @param[in] cmd Commandline to check. May start with a range. /// /// @return True if first command is a live command -/// Currently :s is the only one -bool is_live(char_u *cmd_live) +bool cmd_is_live(char_u *cmd) { - exarg_T ea; - ea.cmd = cmd_live; + if (cmd == NULL) { + return false; + } + exarg_T ea; // parse the command line - if (ea.cmd != NULL) { - ea.cmd = skip_range(ea.cmd, NULL); - if (*ea.cmd == '*') { - ea.cmd = skipwhite(ea.cmd + 1); - } - find_command(&ea, NULL); - } else { - return false; + ea.cmd = skip_range(cmd, NULL); + if (*ea.cmd == '*') { + ea.cmd = skipwhite(ea.cmd + 1); } + find_command(&ea, NULL); return (ea.cmdidx == CMD_substitute); } diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index e30ece3e1b..84f1a13a15 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -10,8 +10,7 @@ #define DOCMD_KEYTYPED 0x08 // don't reset KeyTyped #define DOCMD_EXCRESET 0x10 // reset exception environment (for debugging #define DOCMD_KEEPLINE 0x20 // keep typed line for repeating with "." -#define DOCMD_LIVE_PREVIEW 0x40 // Command is a live preview like - // incsubstitution +#define DOCMD_LIVE 0x40 // show updates as-you-type ("live" command) /* defines for eval_vars() */ #define VALID_PATH 1 diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index d1a2ab8bcf..dba7a107e2 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1591,9 +1591,12 @@ static int command_line_changed(CommandLineState *s) msg_starthere(); redrawcmdline(); s->did_incsearch = true; - } else if (*p_ics != NUL && s->firstc == ':' && is_live(ccline.cmdbuff)) { - // compute a live action - do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_LIVE_PREVIEW); + } else if (s->firstc == ':' + && *p_ics != NUL // 'incsubstitute' is set + && cmdline_star == 0 // not typing a password + && cmd_is_live(ccline.cmdbuff)) { + // process a "live" command + do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_LIVE); redrawcmdline(); } @@ -5141,16 +5144,12 @@ static int ex_window(void) } cmdwin_type = get_cmdline_type(); - /* Create the command-line buffer empty. */ - (void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL); - (void)setfname(curbuf, (char_u *)"[Command Line]", NULL, TRUE); - set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", OPT_LOCAL); - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); - curbuf->b_p_ma = TRUE; - curwin->w_p_fen = FALSE; + // Create empty command-line buffer. + buf_open_special(0, "[Command Line]", "nofile"); curwin->w_p_rl = cmdmsg_rl; - cmdmsg_rl = FALSE; - RESET_BINDING(curwin); + cmdmsg_rl = false; + curbuf->b_p_ma = true; + curwin->w_p_fen = false; /* Do execute autocommands for setting the filetype (load syntax). */ unblock_autocmds(); diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 7ce2c57420..e83e9f6b01 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -5067,7 +5067,7 @@ void buf_reload(buf_T *buf, int orig_mode) savebuf = NULL; } else { // Allocate a buffer without putting it in the buffer list. - savebuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY, 0); + savebuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); if (savebuf != NULL && buf == curbuf) { /* Open the memline. */ curbuf = savebuf; @@ -6409,8 +6409,9 @@ win_found: && curbuf != aco->new_curbuf && buf_valid(aco->new_curbuf) && aco->new_curbuf->b_ml.ml_mfp != NULL) { - if (curwin->w_s == &curbuf->b_s) + if (curwin->w_s == &curbuf->b_s) { curwin->w_s = &aco->new_curbuf->b_s; + } --curbuf->b_nwindows; curbuf = aco->new_curbuf; curwin->w_buffer = curbuf; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index a5188e98d7..690be70c4d 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -603,7 +603,6 @@ EXTERN int redraw_tabline INIT(= FALSE); /* need to redraw tabline */ EXTERN buf_T *firstbuf INIT(= NULL); // first buffer EXTERN buf_T *lastbuf INIT(= NULL); // last buffer EXTERN buf_T *curbuf INIT(= NULL); // currently active buffer -EXTERN buf_T *livebuf INIT(= NULL); // buffer used for live actions // Iterates over all buffers in the buffer list. # define FOR_ALL_BUFFERS(buf) for (buf_T *buf = firstbuf; buf != NULL; buf = buf->b_next) diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 96983a6c3b..6453c41415 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -474,7 +474,7 @@ static void fname2fnum(xfmark_T *fm) p = path_shorten_fname(NameBuff, IObuff); // buflist_new() will call fmarks_check_names() - (void)buflist_new(NameBuff, p, (linenr_T)1, 0, 0); + (void)buflist_new(NameBuff, p, (linenr_T)1, 0); } } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 5505335769..a9a53ebca7 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -2340,14 +2340,13 @@ int ml_replace(linenr_T lnum, char_u *line, int copy) return OK; } -/* - * Delete line 'lnum' in the current buffer. - * - * Check: The caller of this function should probably also call - * deleted_lines() after this. - * - * return FAIL for failure, OK otherwise - */ +/// Delete line `lnum` in the current buffer. +/// +/// @note The caller of this function should probably also call +/// deleted_lines() after this. +/// +/// @param message Show "--No lines in buffer--" message. +/// @return FAIL for failure, OK otherwise int ml_delete(linenr_T lnum, int message) { ml_flush_line(curbuf); diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 97172a15ff..f23037613b 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3327,7 +3327,7 @@ load_dummy_buffer ( aco_save_T aco; // Allocate a buffer without putting it in the buffer list. - newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY, 0); + newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); if (newbuf == NULL) { return NULL; } diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 1cd8f44ea8..01c0807d82 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1405,7 +1405,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) cur_entry.data.buffer_list.buffers[i].fname); buf_T *const buf = buflist_new( cur_entry.data.buffer_list.buffers[i].fname, sfname, 0, - BLN_LISTED, 0); + BLN_LISTED); if (buf != NULL) { RESET_FMARK(&buf->b_last_cursor, cur_entry.data.buffer_list.buffers[i].pos, 0); diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index a1c6718b06..68d0e3da0f 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -7160,16 +7160,13 @@ int syn_namen2id(char_u *linep, int len) */ int syn_check_group(char_u *pp, int len) { - int id; - char_u *name; - - name = vim_strnsave(pp, len); - - id = syn_name2id(name); - if (id == 0) /* doesn't exist yet */ + char_u *name = vim_strnsave(pp, len); + int id = syn_name2id(name); + if (id == 0) { // doesn't exist yet id = syn_add_group(name); - else + } else { xfree(name); + } return id; } diff --git a/src/nvim/undo.c b/src/nvim/undo.c index a4af45d441..20adf40012 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1685,7 +1685,7 @@ void u_redo(int count) u_doit(count, false); } -/// undo and forget. +/// undo, and remove the undo branch from the undo tree. bool u_undo_and_forget(int count) { if (curbuf->b_u_synced == false) { @@ -1726,9 +1726,7 @@ bool u_undo_and_forget(int count) return true; } -/* - * Undo or redo, depending on 'undo_undoes', 'count' times. - */ +/// Undo or redo, depending on `undo_undoes`, `count` times. static void u_doit(int startcount, bool quiet) { int count = startcount; @@ -2342,17 +2340,13 @@ static void u_undoredo(int undo) #endif } -/* - * If we deleted or added lines, report the number of less/more lines. - * Otherwise, report the number of changes (this may be incorrect - * in some cases, but it's better than nothing). - */ -static void -u_undo_end( - int did_undo, // just did an undo - int absolute, // used ":undo N" - bool quiet -) +/// If we deleted or added lines, report the number of less/more lines. +/// Otherwise, report the number of changes (this may be incorrect +/// in some cases, but it's better than nothing). +static void u_undo_end( + int did_undo, //< just did an undo + int absolute, //< used ":undo N" + bool quiet) { char *msgstr; u_header_T *uhp; @@ -2361,9 +2355,9 @@ u_undo_end( if ((fdo_flags & FDO_UNDO) && KeyTyped) foldOpenCursor(); - if (global_busy // no messages now, wait until global is finished - || !messaging() // 'lazyredraw' set, don't do messages now - || quiet) { // livemode doesn't show messages + if (quiet + || global_busy // no messages until global is finished + || !messaging()) { // 'lazyredraw' set, don't do messages now return; } diff --git a/src/nvim/window.c b/src/nvim/window.c index 45b52e9454..8512556c0a 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2903,7 +2903,7 @@ static int win_alloc_firstwin(win_T *oldwin) if (oldwin == NULL) { /* Very first window, need to create an empty buffer for it and * initialize from scratch. */ - curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED, 0); + curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED); if (curbuf == NULL) { return FAIL; } -- cgit From e4e7b2d239df79ff38277463b205061f4d81e393 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 6 Nov 2016 11:23:40 +0100 Subject: lint --- src/nvim/buffer.c | 2 +- src/nvim/ex_cmds.c | 78 ++++++++++++++++++++++++------------------------------ src/nvim/fileio.c | 55 ++++++++++++++++++-------------------- src/nvim/undo.c | 4 +-- 4 files changed, 63 insertions(+), 76 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 190c281c76..75caf2223b 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1339,7 +1339,7 @@ void do_autochdir(void) // functions for dealing with the buffer list // -static int top_file_num = 1; /* highest file number */ +static int top_file_num = 1; ///< highest file number /// Add a file name to the buffer list. /// If the same file name already exists return a pointer to that buffer. diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 9f781c0f8e..1f75f3e5bc 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -92,7 +92,7 @@ typedef struct { linenr_T lnum; long nmatch; char_u *line; - kvec_t(colnr_T) cols; //< columns of in-line matches + kvec_t(colnr_T) cols; ///< columns of in-line matches } MatchedLine; typedef kvec_t(MatchedLine) MatchedLineVec; @@ -2060,7 +2060,7 @@ theend: /// info of the previous buffer for "oldwin" is stored. /// /// @return FAIL for failure, OK otherwise -int do_ecmd ( +int do_ecmd( int fnum, char_u *ffname, char_u *sfname, @@ -2201,7 +2201,7 @@ int do_ecmd ( goto theend; } buf = buflist_new(ffname, sfname, 0L, - BLN_CURBUF | ((flags & ECMD_SET_HELP) ? 0 : BLN_LISTED)); + BLN_CURBUF | (flags & ECMD_SET_HELP ? 0 : BLN_LISTED)); // Autocmds may change curwin and curbuf. if (oldwin != NULL) { oldwin = curwin; @@ -3264,8 +3264,9 @@ buf_T *do_sub(exarg_T *eap) } } - if (eap->skip) /* not executing commands, only parsing */ + if (eap->skip) { // not executing commands, only parsing return NULL; + } if (!subflags.do_count && !MODIFIABLE(curbuf)) { // Substitution is not allowed in non-'modifiable' buffer @@ -5878,50 +5879,39 @@ static enum EXP_SIGN_NAMES /* expand with name of placed signs */ } expand_what; -/* - * Function given to ExpandGeneric() to obtain the sign command - * expansion. - */ +/// Function given to ExpandGeneric() to obtain the sign command +/// expansion. char_u * get_sign_name(expand_T *xp, int idx) { - sign_T *sp; - int current_idx; - - switch (expand_what) - { + switch (expand_what) + { case EXP_SUBCMD: - return (char_u *)cmds[idx]; - case EXP_DEFINE: - { - char *define_arg[] = - { - "icon=", "linehl=", "text=", "texthl=", NULL - }; - return (char_u *)define_arg[idx]; - } - case EXP_PLACE: - { - char *place_arg[] = - { - "line=", "name=", "file=", "buffer=", NULL - }; - return (char_u *)place_arg[idx]; - } - case EXP_UNPLACE: - { - char *unplace_arg[] = { "file=", "buffer=", NULL }; - return (char_u *)unplace_arg[idx]; - } - case EXP_SIGN_NAMES: - /* Complete with name of signs already defined */ - current_idx = 0; - for (sp = first_sign; sp != NULL; sp = sp->sn_next) - if (current_idx++ == idx) - return sp->sn_name; - return NULL; + return (char_u *)cmds[idx]; + case EXP_DEFINE: { + char *define_arg[] = { "icon=", "linehl=", "text=", "texthl=", NULL }; + return (char_u *)define_arg[idx]; + } + case EXP_PLACE: { + char *place_arg[] = { "line=", "name=", "file=", "buffer=", NULL }; + return (char_u *)place_arg[idx]; + } + case EXP_UNPLACE: { + char *unplace_arg[] = { "file=", "buffer=", NULL }; + return (char_u *)unplace_arg[idx]; + } + case EXP_SIGN_NAMES: { + // Complete with name of signs already defined + int current_idx = 0; + for (sign_T *sp = first_sign; sp != NULL; sp = sp->sn_next) { + if (current_idx++ == idx) { + return sp->sn_name; + } + } + } + return NULL; default: - return NULL; - } + return NULL; + } } /* diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index e83e9f6b01..d6e669a67b 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -6343,27 +6343,24 @@ aucmd_prepbuf ( aco->new_curbuf = curbuf; } -/* - * Cleanup after executing autocommands for a (hidden) buffer. - * Restore the window as it was (if possible). - */ -void -aucmd_restbuf ( - aco_save_T *aco /* structure holding saved values */ -) +/// Cleanup after executing autocommands for a (hidden) buffer. +/// Restore the window as it was (if possible). +/// +/// @param aco structure holding saved values +void aucmd_restbuf(aco_save_T *aco) { int dummy; if (aco->use_aucmd_win) { - --curbuf->b_nwindows; - /* Find "aucmd_win", it can't be closed, but it may be in another tab - * page. Do not trigger autocommands here. */ + curbuf->b_nwindows--; + // Find "aucmd_win", it can't be closed, but it may be in another tab page. + // Do not trigger autocommands here. block_autocmds(); if (curwin != aucmd_win) { FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp == aucmd_win) { if (tp != curtab) { - goto_tabpage_tp(tp, TRUE, TRUE); + goto_tabpage_tp(tp, true, true); } win_goto(aucmd_win); goto win_found; @@ -6372,39 +6369,39 @@ aucmd_restbuf ( } win_found: - /* Remove the window and frame from the tree of frames. */ + // Remove the window and frame from the tree of frames. (void)winframe_remove(curwin, &dummy, NULL); win_remove(curwin, NULL); - aucmd_win_used = FALSE; - last_status(FALSE); /* may need to remove last status line */ - restore_snapshot(SNAP_AUCMD_IDX, FALSE); - (void)win_comp_pos(); /* recompute window positions */ + aucmd_win_used = false; + last_status(false); // may need to remove last status line + restore_snapshot(SNAP_AUCMD_IDX, false); + (void)win_comp_pos(); // recompute window positions unblock_autocmds(); - if (win_valid(aco->save_curwin)) + if (win_valid(aco->save_curwin)) { curwin = aco->save_curwin; - else - /* Hmm, original window disappeared. Just use the first one. */ + } else { + // Hmm, original window disappeared. Just use the first one. curwin = firstwin; - vars_clear(&aucmd_win->w_vars->dv_hashtab); /* free all w: variables */ - hash_init(&aucmd_win->w_vars->dv_hashtab); /* re-use the hashtab */ + } + vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables + hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab curbuf = curwin->w_buffer; xfree(globaldir); globaldir = aco->globaldir; - /* the buffer contents may have changed */ + // the buffer contents may have changed check_cursor(); if (curwin->w_topline > curbuf->b_ml.ml_line_count) { curwin->w_topline = curbuf->b_ml.ml_line_count; curwin->w_topfill = 0; } } else { - /* restore curwin */ + // restore curwin if (win_valid(aco->save_curwin)) { - /* Restore the buffer which was previously edited by curwin, if - * it was changed, we are still the same window and the buffer is - * valid. */ + // Restore the buffer which was previously edited by curwin, if it was + // changed, we are still the same window and the buffer is valid. if (curwin == aco->new_curwin && curbuf != aco->new_curbuf && buf_valid(aco->new_curbuf) @@ -6412,10 +6409,10 @@ win_found: if (curwin->w_s == &curbuf->b_s) { curwin->w_s = &aco->new_curbuf->b_s; } - --curbuf->b_nwindows; + curbuf->b_nwindows--; curbuf = aco->new_curbuf; curwin->w_buffer = curbuf; - ++curbuf->b_nwindows; + curbuf->b_nwindows++; } curwin = aco->save_curwin; diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 20adf40012..2f4317980a 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2344,8 +2344,8 @@ static void u_undoredo(int undo) /// Otherwise, report the number of changes (this may be incorrect /// in some cases, but it's better than nothing). static void u_undo_end( - int did_undo, //< just did an undo - int absolute, //< used ":undo N" + int did_undo, ///< just did an undo + int absolute, ///< used ":undo N" bool quiet) { char *msgstr; -- cgit From e31f9007e4cacf500d0be61e6bfb2f16f556f2da Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 6 Nov 2016 16:57:44 +0100 Subject: 'inccommand': preserve b:changedtick --- src/nvim/ex_cmds.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 1f75f3e5bc..ca02bb68de 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -6166,6 +6166,7 @@ void ex_substitute(exarg_T *eap) // XXX: Must do this *after* u_undo_and_forget(), why? close_windows(incsub_buf, false); } + curbuf->b_changedtick = save_changedtick; curbuf->b_p_ul = save_b_p_ul; eap->arg = save_eap; restore_search_patterns(); -- cgit From d0689eb0b2473de479396b77fb685954d65d994c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 6 Nov 2016 23:58:53 +0100 Subject: 'inccommand': disable 'cursorline', 'spell' in preview --- src/nvim/ex_cmds.c | 69 +++++++++++++++++++++--------------------------------- 1 file changed, 27 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index ca02bb68de..6d4533f318 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3946,42 +3946,28 @@ skip: // Show 'incsubstitute' preview if there are matched lines. buf_T *incsub_buf = NULL; - if (matched_lines.size != 0 && pat != NULL && *p_ics != NUL && eap->is_live) { - // Place cursor on the first match after the cursor. - // If all matches are before the cursor, then do_sub already placed the - // cursor on the last match. - - linenr_T cur_lnum = 0; + if (eap->is_live && matched_lines.size != 0 && pat != NULL && *p_ics != NUL) { + // Place cursor on the first match after the cursor. (If all matches are + // above, then do_sub already placed cursor on the last match.) colnr_T cur_col = -1; - MatchedLine current; - - for (size_t j = 0; j < matched_lines.size; j++) { - current = matched_lines.items[j]; - cur_lnum = current.lnum; - - // 1. Match on line of the cursor, need to iterate over the - // matches on this line to see if there is one on a later - // column - if (cur_lnum == old_cursor.lnum) { - for (size_t i = 0; i < current.cols.size; i++) { - if (current.cols.items[i] >= old_cursor.col) { - cur_col = current.cols.items[i]; + MatchedLine curmatch; + for (size_t j = 0; j < matched_lines.size && cur_col == -1; j++) { + curmatch = matched_lines.items[j]; + if (curmatch.lnum == old_cursor.lnum) { + // On cursor line; iterate in-line matches to find one after cursor. + for (size_t i = 0; i < curmatch.cols.size; i++) { + if (curmatch.cols.items[i] >= old_cursor.col) { + cur_col = curmatch.cols.items[i]; + curwin->w_cursor.lnum = curmatch.lnum; + curwin->w_cursor.col = cur_col; break; } } - // match on cursor's line, after the cursor - if (cur_col != -1) { - curwin->w_cursor.lnum = cur_lnum; - curwin->w_cursor.col = cur_col; - break; - } - // 2. Match on line after cursor, just put cursor on column - // of first match there - } else if (cur_lnum > old_cursor.lnum) { - cur_col = current.cols.items[0]; - curwin->w_cursor.lnum = cur_lnum; - curwin->w_cursor.col = cur_col; - break; + } else if (curmatch.lnum > old_cursor.lnum) { + // After cursor; put cursor on first match there. + cur_col = curmatch.cols.items[0]; + curwin->w_cursor.lnum = curmatch.lnum; + curwin->w_cursor.col = cur_col; } } @@ -3992,16 +3978,13 @@ skip: curwin->w_cursor = old_cursor; // don't move the cursor } - MatchedLine current; - for (size_t j = 0; j < matched_lines.size; j++) { - current = matched_lines.items[j]; - if (current.line) { - xfree(current.line); - } - kv_destroy(current.cols); + for (MatchedLine m; kv_size(matched_lines);) { + m = kv_pop(matched_lines); + xfree(m.line); + kv_destroy(m.cols); } - kv_destroy(matched_lines); + return incsub_buf; } // NOLINT(readability/fn_size) @@ -6069,13 +6052,15 @@ static buf_T *incsub_display(char_u *pat, char_u *sub, buf_open_special(incsub_buf ? bufnr : 0, "[inc_sub]", "incsub"); buf_clear(); incsub_buf = curbuf; - set_option_value((char_u *)"bl", 0L, NULL, OPT_LOCAL); set_option_value((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL); bufnr = incsub_buf->handle; + curbuf->b_p_bl = false; curbuf->b_p_ma = true; curbuf->b_p_ul = -1; - curwin->w_p_fen = false; curbuf->b_p_tw = 0; // Reset 'textwidth' (was set by ftplugin) + curwin->w_p_cul = false; + curwin->w_p_spell = false; + curwin->w_p_fen = false; // Width of the "| lnum|..." column which displays the line numbers. linenr_T highest_num_line = kv_last(*matched_lines).lnum; -- cgit From 3ccf69c1ded94c0c3cac50d21dc6d55e5f7a8a21 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 7 Nov 2016 00:08:23 +0100 Subject: 'inccommand': set buffer name to [Preview] [inc_sub] is less obvious for users. Also, in the future we may want to generalize the idea of a "preview buffer", or "incremental commands" besides :substitute. --- src/nvim/ex_cmds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 6d4533f318..bf78be9f13 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -6049,7 +6049,7 @@ static buf_T *incsub_display(char_u *pat, char_u *sub, } if (split && win_split((int)p_cwh, WSP_BOT) != FAIL) { - buf_open_special(incsub_buf ? bufnr : 0, "[inc_sub]", "incsub"); + buf_open_special(incsub_buf ? bufnr : 0, "[Preview]", "incsub"); buf_clear(); incsub_buf = curbuf; set_option_value((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL); -- cgit From 1e0e301062f30846f33457ba3a689266213b8378 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 7 Nov 2016 00:17:57 +0100 Subject: 'inccommand': format line numbers as "|123| " This matches what Quickfix traditionally does. --- src/nvim/ex_cmds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index bf78be9f13..3dc190ddbf 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -6084,7 +6084,7 @@ static buf_T *incsub_display(char_u *pat, char_u *sub, } // put " | lnum|line" into str and append it to the incsubstitute buffer - snprintf(str, line_size, " [%*ld]%s", col_width - 3, mat.lnum, mat.line); + snprintf(str, line_size, "|%*ld| %s", col_width - 3, mat.lnum, mat.line); ml_append(line, (char_u *)str, (colnr_T)line_size, false); // highlight the replaced part -- cgit From 6a3f8d48d0c96330511565aef5e10ad965e986b4 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 7 Nov 2016 00:59:32 +0100 Subject: 'inccommand': rename 'incsubstitute' 'inccommand' allows us to expand the feature to other commands, such as: :cdo :cfdo :global Also rename "IncSubstitute" highlight group to "Substitute". --- src/nvim/ex_cmds.c | 72 ++++++++++++++++++++++++-------------------------- src/nvim/ex_getln.c | 2 +- src/nvim/option.c | 8 +++--- src/nvim/option_defs.h | 2 +- src/nvim/options.lua | 16 +++++------ src/nvim/syntax.c | 2 +- 6 files changed, 50 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 3dc190ddbf..66c4089ec7 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -87,7 +87,7 @@ typedef struct { SubIgnoreType do_ic; ///< ignore case flag } subflags_T; -/// Lines matched during 'incsubstitute'. +/// Lines matched during :substitute. typedef struct { linenr_T lnum; long nmatch; @@ -3110,7 +3110,7 @@ static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags, /// /// The usual escapes are supported as described in the regexp docs. /// -/// @return buffer used for 'incsubstitute' +/// @return buffer used for 'inccommand' preview buf_T *do_sub(exarg_T *eap) { long i = 0; @@ -3627,8 +3627,8 @@ buf_T *do_sub(exarg_T *eap) * use "\=col("."). */ curwin->w_cursor.col = regmatch.startpos[0].col; - // 3. Substitute the string. During 'incsubstitute' only do this if - // there is a replace pattern. + // 3. Substitute the string. During 'inccommand' only do this if there + // is a replace pattern. if (!eap->is_live || has_second_delim) { if (subflags.do_count) { // prevent accidentally changing the buffer by a function @@ -3944,9 +3944,9 @@ skip: subflags.do_all = save_do_all; subflags.do_ask = save_do_ask; - // Show 'incsubstitute' preview if there are matched lines. - buf_T *incsub_buf = NULL; - if (eap->is_live && matched_lines.size != 0 && pat != NULL && *p_ics != NUL) { + // Show 'inccommand' preview if there are matched lines. + buf_T *preview_buf = NULL; + if (eap->is_live && matched_lines.size != 0 && pat != NULL && *p_icm != NUL) { // Place cursor on the first match after the cursor. (If all matches are // above, then do_sub already placed cursor on the last match.) colnr_T cur_col = -1; @@ -3971,10 +3971,9 @@ skip: } } - incsub_buf = incsub_display(pat, sub, eap->line1, eap->line2, - &matched_lines); + preview_buf = show_sub(pat, sub, eap->line1, eap->line2, &matched_lines); - } else if (*p_ics != NUL && eap->is_live) { + } else if (*p_icm != NUL && eap->is_live) { curwin->w_cursor = old_cursor; // don't move the cursor } @@ -3985,7 +3984,7 @@ skip: } kv_destroy(matched_lines); - return incsub_buf; + return preview_buf; } // NOLINT(readability/fn_size) /* @@ -6015,12 +6014,11 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) } } -/// Shows a preview of :substitute (for 'incsubstitute'). -/// With incsubstitute=split, shows a special buffer in a window, draws the -/// screen, then restores the layout. -static buf_T *incsub_display(char_u *pat, char_u *sub, - linenr_T line1, linenr_T line2, - MatchedLineVec *matched_lines) +/// Shows the effects of the current :substitute command being typed +/// ('inccommand'). If inccommand=split, shows a preview window then later +/// restores the layout. +static buf_T *show_sub(char_u *pat, char_u *sub, linenr_T line1, linenr_T line2, + MatchedLineVec *matched_lines) FUNC_ATTR_NONNULL_ALL { static handle_T bufnr = 0; // special buffer, re-used on each visit @@ -6033,27 +6031,27 @@ static buf_T *incsub_display(char_u *pat, char_u *sub, size_t pat_size = mb_string2cells(pat); // We keep a special-purpose buffer around, but don't assume it exists. - buf_T *incsub_buf = bufnr ? buflist_findnr(bufnr) : 0; + buf_T *preview_buf = bufnr ? buflist_findnr(bufnr) : 0; win_size_save(&save_winsizes); // Save current window sizes. cmdmod.tab = 0; // disable :tab modifier - cmdmod.noswapfile = true; // disable swap for 'incsubstitute' buffer + cmdmod.noswapfile = true; // disable swap for preview buffer // disable file info message set_option_value((char_u *)"shm", 0L, (char_u *)"F", 0); bool outside_curline = (line1 != curwin->w_cursor.lnum || line2 != curwin->w_cursor.lnum); - bool split = outside_curline && (*p_ics != 'n') && (sub_size || pat_size); - if (incsub_buf == curbuf) { // Preview buffer cannot preview itself! + bool split = outside_curline && (*p_icm != 'n') && (sub_size || pat_size); + if (preview_buf == curbuf) { // Preview buffer cannot preview itself! split = false; - incsub_buf = NULL; + preview_buf = NULL; } if (split && win_split((int)p_cwh, WSP_BOT) != FAIL) { - buf_open_special(incsub_buf ? bufnr : 0, "[Preview]", "incsub"); + buf_open_special(preview_buf ? bufnr : 0, "[Preview]", "incsub"); buf_clear(); - incsub_buf = curbuf; + preview_buf = curbuf; set_option_value((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL); - bufnr = incsub_buf->handle; + bufnr = preview_buf->handle; curbuf->b_p_bl = false; curbuf->b_p_ma = true; curbuf->b_p_ul = -1; @@ -6070,9 +6068,9 @@ static buf_T *incsub_display(char_u *pat, char_u *sub, size_t old_line_size = 0; size_t line_size; int src_id_highlight = 0; - int hl_id = syn_check_group((char_u *)"IncSubstitute", 13); + int hl_id = syn_check_group((char_u *)"Substitute", 13); - // Dump the lines into the incsub buffer. + // Dump the lines into the preview buffer. for (size_t line = 0; line < matched_lines->size; line++) { MatchedLine mat = matched_lines->items[line]; line_size = mb_string2cells(mat.line) + col_width + 1; @@ -6083,7 +6081,7 @@ static buf_T *incsub_display(char_u *pat, char_u *sub, old_line_size = line_size; } - // put " | lnum|line" into str and append it to the incsubstitute buffer + // put " | lnum|line" into str and append it to the preview buffer snprintf(str, line_size, "|%*ld| %s", col_width - 3, mat.lnum, mat.line); ml_append(line, (char_u *)str, (colnr_T)line_size, false); @@ -6118,17 +6116,17 @@ static buf_T *incsub_display(char_u *pat, char_u *sub, cmdmod = save_cmdmod; - return incsub_buf; + return preview_buf; } /// :substitute command /// -/// If 'incsubstitute' is empty, this just calls do_sub(). -/// If 'incsubstitute' is set, substitutes as-you-type ("live"). -/// If the command is cancelled the changes are removed from undo history. +/// If 'inccommand' is empty this just calls do_sub(). +/// If 'inccommand' is set, shows a "live" preview then removes the changes +/// from undo history. void ex_substitute(exarg_T *eap) { - if (*p_ics == NUL || !eap->is_live) { // 'incsubstitute' is disabled + if (*p_icm == NUL || !eap->is_live) { // 'inccommand' is disabled (void)do_sub(eap); return; } @@ -6138,18 +6136,18 @@ void ex_substitute(exarg_T *eap) int save_changedtick = curbuf->b_changedtick; long save_b_p_ul = curbuf->b_p_ul; curbuf->b_p_ul = LONG_MAX; // make sure we can undo all changes - block_autocmds(); // disable events before incsub opening window/buffer + block_autocmds(); // disable events before show_sub() opens window/buffer emsg_off++; // No error messages for live commands - buf_T *incsub_buf = do_sub(eap); + buf_T *preview_buf = do_sub(eap); if (save_changedtick != curbuf->b_changedtick && !u_undo_and_forget(1)) { abort(); } - if (buf_valid(incsub_buf)) { + if (buf_valid(preview_buf)) { // XXX: Must do this *after* u_undo_and_forget(), why? - close_windows(incsub_buf, false); + close_windows(preview_buf, false); } curbuf->b_changedtick = save_changedtick; curbuf->b_p_ul = save_b_p_ul; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index dba7a107e2..07b50a8056 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1592,7 +1592,7 @@ static int command_line_changed(CommandLineState *s) redrawcmdline(); s->did_incsearch = true; } else if (s->firstc == ':' - && *p_ics != NUL // 'incsubstitute' is set + && *p_icm != NUL // 'inccommand' is set && cmdline_star == 0 // not typing a password && cmd_is_live(ccline.cmdbuff)) { // process a "live" command diff --git a/src/nvim/option.c b/src/nvim/option.c index 761e4451b9..ca66f84a70 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -288,7 +288,7 @@ static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent", static char *(p_fcl_values[]) = { "all", NULL }; static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview", "noinsert", "noselect", NULL }; -static char *(p_ics_values[]) = { "nosplit", "split", NULL }; +static char *(p_icm_values[]) = { "nosplit", "split", NULL }; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "option.c.generated.h" @@ -3111,9 +3111,9 @@ did_set_string_option ( else if (gvarp == &p_cino) { /* TODO: recognize errors */ parse_cino(curbuf); - // incsubstitute - } else if (varp == &p_ics) { - if (check_opt_strings(p_ics, p_ics_values, false) != OK) { + // inccommand + } else if (varp == &p_icm) { + if (check_opt_strings(p_icm, p_icm_values, false) != OK) { errmsg = e_invarg; } // Options that are a list of flags. diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index e0711c7c8f..57ad5f5d1a 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -470,6 +470,7 @@ EXTERN int p_icon; // 'icon' EXTERN char_u *p_iconstring; // 'iconstring' EXTERN int p_ic; // 'ignorecase' EXTERN int p_is; // 'incsearch' +EXTERN char_u *p_icm; // 'inccommand' EXTERN int p_im; // 'insertmode' EXTERN char_u *p_isf; // 'isfname' EXTERN char_u *p_isi; // 'isident' @@ -589,7 +590,6 @@ EXTERN int p_spr; // 'splitright' EXTERN int p_sol; // 'startofline' EXTERN char_u *p_su; // 'suffixes' EXTERN char_u *p_swb; // 'switchbuf' -EXTERN char_u *p_ics; // 'incsubstitute' EXTERN unsigned swb_flags; #ifdef IN_OPTION_C static char *(p_swb_values[]) = diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 359bf3fcee..14707aaa6c 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1187,6 +1187,14 @@ return { if_false={vi=macros('B_IMODE_NONE')}, } }, + { + full_name='inccommand', abbreviation='icm', + type='string', scope={'global'}, + vi_def=true, + redraw={'everything'}, + varname='p_icm', + defaults={if_true={vi=""}} + }, { full_name='include', abbreviation='inc', type='string', scope={'global', 'buffer'}, @@ -1210,14 +1218,6 @@ return { varname='p_is', defaults={if_true={vi=false, vim=true}} }, - { - full_name='incsubstitute', abbreviation='ics', - type='string', scope={'global'}, - vi_def=true, - redraw={'everything'}, - varname='p_ics', - defaults={if_true={vi=""}} - }, { full_name='indentexpr', abbreviation='inde', type='string', scope={'buffer'}, diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 68d0e3da0f..e57965ac2c 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5902,7 +5902,7 @@ static char *highlight_init_both[] = "WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", "default link EndOfBuffer NonText", "default link QuickFixLine Search", - "default link IncSubstitute Search", + "default link Substitute Search", NULL }; -- cgit From ff6ec703d5f5b57a3c18034ba8a110ffcbf41cea Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 7 Nov 2016 03:08:31 +0100 Subject: 'inccommand': Do not trigger during scripts, feedkeys(). --- src/nvim/ex_cmds_defs.h | 2 +- src/nvim/ex_getln.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index 5b647ff69a..8a2ebe2cd4 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -124,7 +124,7 @@ struct exarg { LineGetter getline; ///< Function used to get the next line void *cookie; ///< argument for getline() struct condstack *cstack; ///< condition stack for ":if" etc. - bool is_live; ///< live preview + bool is_live; ///< 'inccommand' live preview }; #define FORCE_BIN 1 // ":edit ++bin file" diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 07b50a8056..04e7d2fbcf 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1592,10 +1592,11 @@ static int command_line_changed(CommandLineState *s) redrawcmdline(); s->did_incsearch = true; } else if (s->firstc == ':' + && KeyTyped // only if interactive && *p_icm != NUL // 'inccommand' is set && cmdline_star == 0 // not typing a password && cmd_is_live(ccline.cmdbuff)) { - // process a "live" command + // process a "live" command ('inccommand') do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_LIVE); redrawcmdline(); } -- cgit From f3e8ca3bf50c5945ecfd801bf6eb49ffea5bbe0e Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 6 Nov 2016 20:48:12 +0100 Subject: 'inccommand': preserve 'modified' During the live preview, the buffer-local 'modified' flag should not be changed. --- src/nvim/ex_cmds.c | 10 ++++++---- src/nvim/ex_getln.c | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 66c4089ec7..6a0b69ae4a 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3142,6 +3142,7 @@ buf_T *do_sub(exarg_T *eap) pos_T old_cursor = curwin->w_cursor; int start_nsubs; int save_ma = 0; + int save_b_changed = curbuf->b_changed; if (!global_busy) { sub_nsubs = 0; @@ -3971,6 +3972,8 @@ skip: } } + curbuf->b_changed = save_b_changed; // preserve 'modified' during preview + // set_option_value((char_u *)"modified", 0L, NULL, OPT_LOCAL); preview_buf = show_sub(pat, sub, eap->line1, eap->line2, &matched_lines); } else if (*p_icm != NUL && eap->is_live) { @@ -6014,9 +6017,8 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) } } -/// Shows the effects of the current :substitute command being typed -/// ('inccommand'). If inccommand=split, shows a preview window then later -/// restores the layout. +/// 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(char_u *pat, char_u *sub, linenr_T line1, linenr_T line2, MatchedLineVec *matched_lines) FUNC_ATTR_NONNULL_ALL @@ -6050,7 +6052,7 @@ static buf_T *show_sub(char_u *pat, char_u *sub, linenr_T line1, linenr_T line2, buf_open_special(preview_buf ? bufnr : 0, "[Preview]", "incsub"); buf_clear(); preview_buf = curbuf; - set_option_value((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL); + set_option_value((char_u *)"bufhidden", 0L, (char_u *)"hide", OPT_LOCAL); bufnr = preview_buf->handle; curbuf->b_p_bl = false; curbuf->b_p_ma = true; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 04e7d2fbcf..17693ecfc8 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1594,6 +1594,7 @@ static int command_line_changed(CommandLineState *s) } else if (s->firstc == ':' && KeyTyped // only if interactive && *p_icm != NUL // 'inccommand' is set + && curbuf->b_p_ma // buffer is modifiable && cmdline_star == 0 // not typing a password && cmd_is_live(ccline.cmdbuff)) { // process a "live" command ('inccommand') -- cgit From 21dfbfbb9ab324b0731aec5c7b8556e106785e82 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 7 Nov 2016 17:37:59 +0100 Subject: perf: do_sub(): avoid work, avoid screen updates - Don't fill matched_lines if not showing a preview (!eap->is_live). - Encapsulate: Move cursor placement logic to show_sub(). --- src/nvim/ex_cmds.c | 72 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 6a0b69ae4a..6987bcfd89 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3406,9 +3406,11 @@ buf_T *do_sub(exarg_T *eap) curwin->w_cursor.lnum = lnum; do_again = FALSE; - // Increment the in-line match count and store the column. - matched_line.nmatch++; - kv_push(matched_line.cols, regmatch.startpos[0].col); + if (eap->is_live) { + // Increment the in-line match count and store the column. + matched_line.nmatch++; + kv_push(matched_line.cols, regmatch.startpos[0].col); + } /* * 1. Match empty string does not count, except for first @@ -3874,9 +3876,11 @@ skip: xfree(sub_firstline); /* free the copy of the original line */ sub_firstline = NULL; - matched_line.lnum = lnum; - matched_line.line = vim_strsave(ml_get(lnum)); - kv_push(matched_lines, matched_line); + if (eap->is_live) { + matched_line.lnum = lnum; + matched_line.line = vim_strsave(ml_get(lnum)); + kv_push(matched_lines, matched_line); + } } line_breakcheck(); @@ -3948,33 +3952,9 @@ skip: // Show 'inccommand' preview if there are matched lines. buf_T *preview_buf = NULL; if (eap->is_live && matched_lines.size != 0 && pat != NULL && *p_icm != NUL) { - // Place cursor on the first match after the cursor. (If all matches are - // above, then do_sub already placed cursor on the last match.) - colnr_T cur_col = -1; - MatchedLine curmatch; - for (size_t j = 0; j < matched_lines.size && cur_col == -1; j++) { - curmatch = matched_lines.items[j]; - if (curmatch.lnum == old_cursor.lnum) { - // On cursor line; iterate in-line matches to find one after cursor. - for (size_t i = 0; i < curmatch.cols.size; i++) { - if (curmatch.cols.items[i] >= old_cursor.col) { - cur_col = curmatch.cols.items[i]; - curwin->w_cursor.lnum = curmatch.lnum; - curwin->w_cursor.col = cur_col; - break; - } - } - } else if (curmatch.lnum > old_cursor.lnum) { - // After cursor; put cursor on first match there. - cur_col = curmatch.cols.items[0]; - curwin->w_cursor.lnum = curmatch.lnum; - curwin->w_cursor.col = cur_col; - } - } - curbuf->b_changed = save_b_changed; // preserve 'modified' during preview - // set_option_value((char_u *)"modified", 0L, NULL, OPT_LOCAL); - preview_buf = show_sub(pat, sub, eap->line1, eap->line2, &matched_lines); + preview_buf = show_sub(old_cursor, pat, sub, eap->line1, eap->line2, + &matched_lines); } else if (*p_icm != NUL && eap->is_live) { curwin->w_cursor = old_cursor; // don't move the cursor @@ -6019,8 +5999,8 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) /// 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(char_u *pat, char_u *sub, linenr_T line1, linenr_T line2, - MatchedLineVec *matched_lines) +static buf_T *show_sub(pos_T old_cusr, char_u *pat, char_u *sub, linenr_T line1, + linenr_T line2, MatchedLineVec *matched_lines) FUNC_ATTR_NONNULL_ALL { static handle_T bufnr = 0; // special buffer, re-used on each visit @@ -6048,6 +6028,30 @@ static buf_T *show_sub(char_u *pat, char_u *sub, linenr_T line1, linenr_T line2, preview_buf = NULL; } + // Place cursor on the first match after the cursor. (If all matches are + // above, then do_sub already placed cursor on the last match.) + colnr_T cur_col = -1; + MatchedLine curmatch; + for (size_t j = 0; j < matched_lines->size && cur_col == -1; j++) { + curmatch = matched_lines->items[j]; + if (curmatch.lnum == old_cusr.lnum) { + // On cursor line; iterate in-line matches to find one after cursor. + for (size_t i = 0; i < curmatch.cols.size; i++) { + if (curmatch.cols.items[i] >= old_cusr.col) { + cur_col = curmatch.cols.items[i]; + curwin->w_cursor.lnum = curmatch.lnum; + curwin->w_cursor.col = cur_col; + break; + } + } + } else if (curmatch.lnum > old_cusr.lnum) { + // After cursor; put cursor on first match there. + cur_col = curmatch.cols.items[0]; + curwin->w_cursor.lnum = curmatch.lnum; + curwin->w_cursor.col = cur_col; + } + } + if (split && win_split((int)p_cwh, WSP_BOT) != FAIL) { buf_open_special(preview_buf ? bufnr : 0, "[Preview]", "incsub"); buf_clear(); -- cgit From aa0e09d251610f9b17f0bf96a4f3485032c36e0f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 7 Nov 2016 22:48:39 +0100 Subject: 'inccommand': Preserve curbuf->b_u_newhead. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tests for undotree(). Helped-by: Björn Linse When "curhead" points to a valid head, the value of "newhead" is meaningless (and really should be set to null). The undo state for a buffer is _logically_ the enum: enum UndoState { CurrentHead(head), NewHead(head), EmptyTree } nvim _represents_ this as: whenever `curbuf->b_u_curhead` is nonnull it should be used as the current head, and `curbuf->b_u_newhead` is ignored. If the there is a current head, then this will be redoed on the next redo, and its parent will be undone on next undo. Only if `b_u_curhead` is NULL, `b_u_newhead` will be used as the head to undo (and it is not possible to redo). Also both can be NULL, to indicate an empty undotree. (To be fair, this only strictly true when calling undo.c from the outside, in some places _within_ a function in undo.c both values might be meaningful) Apparently `undotree()` breaks this non-abstraction, this _cosmetic_ issue can easily be fixed by `ex_substitute` also saving and restoring `b_u_newhead`, but is doesn't reflect any error really how `u_undo_and_forget` manipulates the _actual_ state of the undo tree. --- src/nvim/ex_cmds.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 6987bcfd89..0190db258f 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -6140,6 +6140,8 @@ void ex_substitute(exarg_T *eap) char_u *save_eap = eap->arg; save_search_patterns(); int save_changedtick = curbuf->b_changedtick; + 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; curbuf->b_p_ul = LONG_MAX; // make sure we can undo all changes block_autocmds(); // disable events before show_sub() opens window/buffer @@ -6147,15 +6149,18 @@ void ex_substitute(exarg_T *eap) buf_T *preview_buf = do_sub(eap); - if (save_changedtick != curbuf->b_changedtick - && !u_undo_and_forget(1)) { - abort(); + if (save_changedtick != curbuf->b_changedtick) { + 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; + curbuf->b_changedtick = save_changedtick; } if (buf_valid(preview_buf)) { // XXX: Must do this *after* u_undo_and_forget(), why? close_windows(preview_buf, false); } - curbuf->b_changedtick = save_changedtick; curbuf->b_p_ul = save_b_p_ul; eap->arg = save_eap; restore_search_patterns(); -- cgit