mirror of
https://github.com/neovim/neovim.git
synced 2025-10-01 23:48:32 +00:00
Merge #5561 'inccommand'
Initial work by: Robin Elrharbi-Fleury (Robinhola) Audrey Rayé (Adrey06) Philémon Hullot (DesbyP) Aymeric Collange (aym7) Clément Guyomard (Clement0) Major revisions by: KillTheMule Björn Linse <bjorn.linse@gmail.com> Justin M. Keyes <justinkz@gmail.com>
This commit is contained in:
@@ -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.
|
||||
///
|
||||
@@ -671,14 +686,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);
|
||||
}
|
||||
if (old_curbuf != NULL) {
|
||||
enter_buffer(old_curbuf);
|
||||
if (old_tw != curbuf->b_p_tw)
|
||||
@@ -1319,28 +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 */
|
||||
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 */
|
||||
)
|
||||
/// 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;
|
||||
|
||||
@@ -2374,10 +2391,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);
|
||||
if (buf != NULL && !cmdmod.keepalt)
|
||||
if (buf != NULL && !cmdmod.keepalt) {
|
||||
curwin->w_alt_fnum = buf->b_fnum;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -2412,8 +2430,9 @@ int buflist_add(char_u *fname, int flags)
|
||||
buf_T *buf;
|
||||
|
||||
buf = buflist_new(fname, NULL, (linenr_T)0, flags);
|
||||
if (buf != NULL)
|
||||
if (buf != NULL) {
|
||||
return buf->b_fnum;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5253,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);
|
||||
}
|
||||
|
@@ -8,7 +8,11 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
|
||||
#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"
|
||||
@@ -83,6 +87,15 @@ typedef struct {
|
||||
SubIgnoreType do_ic; ///< ignore case flag
|
||||
} subflags_T;
|
||||
|
||||
/// Lines matched during :substitute.
|
||||
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
|
||||
@@ -1524,8 +1537,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);
|
||||
if (buf != NULL && !cmdmod.keepalt)
|
||||
if (buf != NULL && !cmdmod.keepalt) {
|
||||
curwin->w_alt_fnum = buf->b_fnum;
|
||||
}
|
||||
}
|
||||
xfree(fname);
|
||||
xfree(sfname);
|
||||
@@ -2019,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,
|
||||
@@ -2174,9 +2185,10 @@ do_ecmd (
|
||||
buflist_altfpos(oldwin);
|
||||
}
|
||||
|
||||
if (fnum)
|
||||
if (fnum) {
|
||||
buf = buflist_findnr(fnum);
|
||||
else {
|
||||
} else {
|
||||
ILOG("here");
|
||||
if (flags & ECMD_ADDBUF) {
|
||||
linenr_T tlnum = 1L;
|
||||
|
||||
@@ -2189,7 +2201,7 @@ 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;
|
||||
@@ -2978,10 +2990,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;
|
||||
}
|
||||
@@ -3087,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 'inccommand' preview
|
||||
buf_T *do_sub(exarg_T *eap)
|
||||
{
|
||||
long i = 0;
|
||||
regmmatch_T regmatch;
|
||||
@@ -3112,20 +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;
|
||||
int save_b_changed = curbuf->b_changed;
|
||||
|
||||
if (!global_busy) {
|
||||
sub_nsubs = 0;
|
||||
@@ -3144,7 +3161,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:
|
||||
@@ -3155,21 +3172,26 @@ 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 */
|
||||
pat = (char_u *)""; /* empty search pattern */
|
||||
delimiter = *cmd++; /* remember delimiter character */
|
||||
} else { /* find the end of the regexp */
|
||||
if (p_altkeymap && curwin->w_p_rl)
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3188,7 +3210,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(),
|
||||
@@ -3198,7 +3220,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;
|
||||
@@ -3209,7 +3231,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);
|
||||
@@ -3223,7 +3245,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;
|
||||
@@ -3239,24 +3261,27 @@ 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;
|
||||
if (eap->skip) { // not executing commands, only parsing
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!subflags.do_count && !MODIFIABLE(curbuf)) {
|
||||
// Substitution is not allowed in non-'modifiable' buffer
|
||||
EMSG(_(e_modifiable));
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase'
|
||||
@@ -3343,6 +3368,8 @@ void do_sub(exarg_T *eap)
|
||||
sub_firstlnum = lnum;
|
||||
copycol = 0;
|
||||
matchcol = 0;
|
||||
// the current match
|
||||
MatchedLine matched_line = { 0, 0, NULL, KV_INITIAL_VALUE };
|
||||
|
||||
/* At first match, remember current cursor position. */
|
||||
if (!got_match) {
|
||||
@@ -3379,6 +3406,12 @@ void do_sub(exarg_T *eap)
|
||||
curwin->w_cursor.lnum = lnum;
|
||||
do_again = FALSE;
|
||||
|
||||
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
|
||||
* match. This reproduces the strange vi behaviour.
|
||||
@@ -3426,7 +3459,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,134 +3630,131 @@ void do_sub(exarg_T *eap)
|
||||
* use "\=col("."). */
|
||||
curwin->w_cursor.col = regmatch.startpos[0].col;
|
||||
|
||||
/*
|
||||
* 3. substitute the string.
|
||||
*/
|
||||
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--;
|
||||
// 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
|
||||
save_ma = curbuf->b_p_ma;
|
||||
curbuf->b_p_ma = false;
|
||||
sandbox++;
|
||||
}
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
} else {
|
||||
subflags.do_all = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* 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;
|
||||
// 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--;
|
||||
}
|
||||
} else if (has_mbyte)
|
||||
p1 += (*mb_ptr2len)(p1) - 1;
|
||||
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,
|
||||
(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;
|
||||
|
||||
// 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;
|
||||
} else {
|
||||
subflags.do_all = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. If subflags.do_all is set, find next match.
|
||||
@@ -3845,6 +3875,12 @@ skip:
|
||||
xfree(new_start); /* for when substitute was cancelled */
|
||||
xfree(sub_firstline); /* free the copy of the original line */
|
||||
sub_firstline = NULL;
|
||||
|
||||
if (eap->is_live) {
|
||||
matched_line.lnum = lnum;
|
||||
matched_line.line = vim_strsave(ml_get(lnum));
|
||||
kv_push(matched_lines, matched_line);
|
||||
}
|
||||
}
|
||||
|
||||
line_breakcheck();
|
||||
@@ -3880,7 +3916,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 +3948,26 @@ skip:
|
||||
// Restore the flag values, they can be used for ":&&".
|
||||
subflags.do_all = save_do_all;
|
||||
subflags.do_ask = save_do_ask;
|
||||
|
||||
// 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) {
|
||||
curbuf->b_changed = save_b_changed; // preserve 'modified' during preview
|
||||
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
|
||||
}
|
||||
|
||||
for (MatchedLine m; kv_size(matched_lines);) {
|
||||
m = kv_pop(matched_lines);
|
||||
xfree(m.line);
|
||||
kv_destroy(m.cols);
|
||||
}
|
||||
kv_destroy(matched_lines);
|
||||
|
||||
return preview_buf;
|
||||
} // NOLINT(readability/fn_size)
|
||||
|
||||
/*
|
||||
@@ -5793,50 +5849,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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -5956,3 +6001,174 @@ 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(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
|
||||
|
||||
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 *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 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_icm != 'n') && (sub_size || pat_size);
|
||||
if (preview_buf == curbuf) { // Preview buffer cannot preview itself!
|
||||
split = false;
|
||||
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();
|
||||
preview_buf = curbuf;
|
||||
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;
|
||||
curbuf->b_p_ul = -1;
|
||||
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;
|
||||
int col_width = log10(highest_num_line) + 1 + 3;
|
||||
|
||||
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 *)"Substitute", 13);
|
||||
|
||||
// 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;
|
||||
|
||||
// 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 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);
|
||||
|
||||
// highlight the replaced part
|
||||
if (sub_size > 0) {
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
// Update screen now. Must do this _before_ close_windows().
|
||||
int save_rd = RedrawingDisabled;
|
||||
RedrawingDisabled = 0;
|
||||
update_screen(NOT_VALID);
|
||||
RedrawingDisabled = save_rd;
|
||||
|
||||
cmdmod = save_cmdmod;
|
||||
|
||||
return preview_buf;
|
||||
}
|
||||
|
||||
/// :substitute command
|
||||
///
|
||||
/// 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_icm == NUL || !eap->is_live) { // 'inccommand' is disabled
|
||||
(void)do_sub(eap);
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
emsg_off++; // No error messages for live commands
|
||||
|
||||
buf_T *preview_buf = do_sub(eap);
|
||||
|
||||
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_p_ul = save_b_p_ul;
|
||||
eap->arg = save_eap;
|
||||
restore_search_patterns();
|
||||
emsg_off--;
|
||||
unblock_autocmds();
|
||||
}
|
||||
|
@@ -5,14 +5,17 @@
|
||||
|
||||
#include "nvim/os/time.h"
|
||||
#include "nvim/eval_defs.h"
|
||||
#include "nvim/pos.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 */
|
||||
|
||||
/* for lnum argument in do_ecmd() */
|
||||
#define ECMD_LASTL (linenr_T)0 /* use last position in loaded file */
|
||||
|
@@ -2194,7 +2194,7 @@ return {
|
||||
command='substitute',
|
||||
flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN),
|
||||
addr_type=ADDR_LINES,
|
||||
func='do_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',
|
||||
},
|
||||
}
|
||||
|
@@ -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; ///< 'inccommand' live preview
|
||||
};
|
||||
|
||||
#define FORCE_BIN 1 // ":edit ++bin file"
|
||||
|
@@ -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,7 +1248,8 @@ static char_u * do_one_cmd(char_u **cmdlinep,
|
||||
memset(&ea, 0, sizeof(ea));
|
||||
ea.line1 = 1;
|
||||
ea.line2 = 1;
|
||||
++ex_nesting_level;
|
||||
ea.is_live = flags & DOCMD_LIVE;
|
||||
ex_nesting_level++;
|
||||
|
||||
/* When the last file has not been edited :q has to be typed twice. */
|
||||
if (quitmore
|
||||
@@ -1726,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 (!sourcing)
|
||||
if (!(flags & DOCMD_VERBOSE)) {
|
||||
append_command(*cmdlinep);
|
||||
}
|
||||
errormsg = IObuff;
|
||||
did_emsg_syntax = TRUE;
|
||||
}
|
||||
@@ -1809,7 +1811,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 +2223,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 +9649,25 @@ static void ex_terminal(exarg_T *eap)
|
||||
xfree(name);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether commandline starts with a live command
|
||||
///
|
||||
/// @param[in] cmd Commandline to check. May start with a range.
|
||||
///
|
||||
/// @return True if first command is a live command
|
||||
bool cmd_is_live(char_u *cmd)
|
||||
{
|
||||
if (cmd == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
exarg_T ea;
|
||||
// parse the command line
|
||||
ea.cmd = skip_range(cmd, NULL);
|
||||
if (*ea.cmd == '*') {
|
||||
ea.cmd = skipwhite(ea.cmd + 1);
|
||||
}
|
||||
find_command(&ea, NULL);
|
||||
|
||||
return (ea.cmdidx == CMD_substitute);
|
||||
}
|
||||
|
@@ -3,13 +3,14 @@
|
||||
|
||||
#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 "." */
|
||||
// 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 0x40 // show updates as-you-type ("live" command)
|
||||
|
||||
/* defines for eval_vars() */
|
||||
#define VALID_PATH 1
|
||||
|
@@ -1591,6 +1591,15 @@ static int command_line_changed(CommandLineState *s)
|
||||
msg_starthere();
|
||||
redrawcmdline();
|
||||
s->did_incsearch = true;
|
||||
} 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')
|
||||
do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_LIVE);
|
||||
redrawcmdline();
|
||||
}
|
||||
|
||||
if (cmdmsg_rl || (p_arshape && !p_tbidi && enc_utf8)) {
|
||||
@@ -5137,16 +5146,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();
|
||||
|
@@ -5062,10 +5062,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);
|
||||
if (savebuf != NULL && buf == curbuf) {
|
||||
/* Open the memline. */
|
||||
@@ -6342,27 +6342,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;
|
||||
@@ -6371,49 +6368,50 @@ 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)
|
||||
&& 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->b_nwindows--;
|
||||
curbuf = aco->new_curbuf;
|
||||
curwin->w_buffer = curbuf;
|
||||
++curbuf->b_nwindows;
|
||||
curbuf->b_nwindows++;
|
||||
}
|
||||
|
||||
curwin = aco->save_curwin;
|
||||
|
@@ -600,9 +600,9 @@ 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
|
||||
|
||||
// Iterates over all buffers in the buffer list.
|
||||
# define FOR_ALL_BUFFERS(buf) for (buf_T *buf = firstbuf; buf != NULL; buf = buf->b_next)
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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_icm_values[]) = { "nosplit", "split", NULL };
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "option.c.generated.h"
|
||||
@@ -3100,9 +3101,13 @@ did_set_string_option (
|
||||
else if (gvarp == &p_cino) {
|
||||
/* TODO: recognize errors */
|
||||
parse_cino(curbuf);
|
||||
}
|
||||
/* Options that are a list of flags. */
|
||||
else {
|
||||
// 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.
|
||||
} else {
|
||||
p = NULL;
|
||||
if (varp == &p_ww)
|
||||
p = (char_u *)WW_ALL;
|
||||
|
@@ -468,6 +468,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'
|
||||
|
@@ -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'},
|
||||
|
@@ -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);
|
||||
if (newbuf == NULL)
|
||||
if (newbuf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Init the options. */
|
||||
buf_copy_options(newbuf, BCO_ENTER | BCO_NOHELP);
|
||||
|
@@ -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 Substitute Search",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -7159,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;
|
||||
}
|
||||
|
||||
|
@@ -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,56 @@ 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 or redo, depending on 'undo_undoes', 'count' times.
|
||||
*/
|
||||
static void u_doit(int startcount)
|
||||
/// undo, and remove the undo branch from the undo tree.
|
||||
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, bool quiet)
|
||||
{
|
||||
int count = startcount;
|
||||
|
||||
@@ -1722,7 +1763,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 +1783,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 +2096,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);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2299,16 +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" */
|
||||
)
|
||||
/// 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;
|
||||
@@ -2317,9 +2355,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 (quiet
|
||||
|| global_busy // no messages until global is finished
|
||||
|| !messaging()) { // 'lazyredraw' set, don't do messages now
|
||||
return;
|
||||
}
|
||||
|
||||
if (curbuf->b_ml.ml_flags & ML_EMPTY)
|
||||
--u_newcount;
|
||||
|
@@ -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);
|
||||
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 */
|
||||
|
Reference in New Issue
Block a user