diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/buffer.c | 15 | ||||
-rw-r--r-- | src/nvim/eval.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 325 | ||||
-rw-r--r-- | src/nvim/ex_cmds.h | 22 | ||||
-rw-r--r-- | src/nvim/ex_cmds.lua | 2 | ||||
-rw-r--r-- | src/nvim/ex_cmds_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 46 | ||||
-rw-r--r-- | src/nvim/ex_docmd.h | 1 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 4 | ||||
-rw-r--r-- | src/nvim/fileio.c | 2 | ||||
-rw-r--r-- | src/nvim/globals.h | 7 | ||||
-rw-r--r-- | src/nvim/mark.c | 2 | ||||
-rw-r--r-- | src/nvim/option.c | 11 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/options.lua | 8 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 2 | ||||
-rw-r--r-- | src/nvim/shada.c | 2 | ||||
-rw-r--r-- | src/nvim/syntax.c | 1 | ||||
-rw-r--r-- | src/nvim/undo.c | 74 | ||||
-rw-r--r-- | src/nvim/window.c | 2 |
20 files changed, 472 insertions, 58 deletions
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 <string.h> #include <stdlib.h> #include <inttypes.h> +#include <math.h> #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 @@ -1211,6 +1211,14 @@ return { 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'}, vi_def=true, 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; |