aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer.c15
-rw-r--r--src/nvim/eval.c2
-rw-r--r--src/nvim/ex_cmds.c325
-rw-r--r--src/nvim/ex_cmds.h22
-rw-r--r--src/nvim/ex_cmds.lua2
-rw-r--r--src/nvim/ex_cmds_defs.h1
-rw-r--r--src/nvim/ex_docmd.c46
-rw-r--r--src/nvim/ex_docmd.h1
-rw-r--r--src/nvim/ex_getln.c4
-rw-r--r--src/nvim/fileio.c2
-rw-r--r--src/nvim/globals.h7
-rw-r--r--src/nvim/mark.c2
-rw-r--r--src/nvim/option.c11
-rw-r--r--src/nvim/option_defs.h1
-rw-r--r--src/nvim/options.lua8
-rw-r--r--src/nvim/quickfix.c2
-rw-r--r--src/nvim/shada.c2
-rw-r--r--src/nvim/syntax.c1
-rw-r--r--src/nvim/undo.c74
-rw-r--r--src/nvim/window.c2
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, &regmatch) == FAIL) {
+ int search_options = eap->is_live ? 0 : SEARCH_HIS;
+ if (search_regcomp(pat, RE_SUBST, which_pat, search_options,
+ &regmatch) == 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;