mirror of
https://github.com/neovim/neovim.git
synced 2026-08-17 18:14:46 -05:00
refactor(cmdatom): do "redo prep" in one place #41348
Problem: Redo prep is scattered/duplicated. - `do_pending_operator()` has 3 prep blocks whose conditions must be in sync with `atom_capture_op()`. - insert.c, spell_suggest() hand-roll `redo_new()` + `redo_append_xx()` sequences. - prep_redo() has 2 roles, decided by `keys != NULL`. Solution: - `atom_capture_op()` is the "operator" entry point: capture, then prep. - Extract `prep_redo_visual()`, `atom_capturable()`.
This commit is contained in:
+25
-15
@@ -591,31 +591,41 @@ void redo_free_all(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Prepare for redo of any command: stores `spec` and appends its command chars (redo_chars()).
|
||||
/// Prepare for redo of any command: stores `spec` and appends its command chars.
|
||||
///
|
||||
/// @param keys Visual-mode command: the body opens with the selection's captured keys, so "."
|
||||
/// re-executes the selection at cursor. Register/count compose into the body
|
||||
/// after them (zeroed in the stored spec, so replay doesn't also prefix them).
|
||||
/// NULL for a plain command: register/count stay spec fields.
|
||||
/// @param claim Claim it as the atom. False if the atom is captured by other means
|
||||
/// (insert-session entry/restart, "z=").
|
||||
/// @param arg_meta Skip the `arg` byte: an interactively-typed operand may need CTRL-V quoting
|
||||
/// or its composing-char string form, which the caller appends itself.
|
||||
void prep_redo(const char *keys, size_t len, bool arg_meta, CmdSpec spec)
|
||||
void prep_redo(bool claim, bool arg_meta, CmdSpec spec)
|
||||
{
|
||||
if (claim) {
|
||||
atom_redo_set(spec);
|
||||
}
|
||||
redo_new(spec);
|
||||
if (block_redo) {
|
||||
return;
|
||||
}
|
||||
redo_chars(&spec, &redobuff.cur.keys, arg_meta);
|
||||
}
|
||||
|
||||
/// Prepare for redo of a Visual-mode command: the body opens with `keys` (the captured selection),
|
||||
/// so "." re-executes the selection at cursor; the `["x][count]` prefix and command chars of
|
||||
/// `spec` compose into the body after them (zeroed in the stored spec, so replay doesn't also
|
||||
/// prefix them). Always claims (see prep_redo()).
|
||||
void prep_redo_visual(const char *keys, size_t len, CmdSpec spec)
|
||||
{
|
||||
CmdSpec stored = spec;
|
||||
if (keys != NULL) { // Visual-mode command.
|
||||
stored.regname = 0;
|
||||
stored.count = 0;
|
||||
}
|
||||
stored.regname = 0;
|
||||
stored.count = 0;
|
||||
atom_redo_set(stored);
|
||||
redo_new(stored);
|
||||
if (block_redo) {
|
||||
return;
|
||||
}
|
||||
if (keys != NULL) { // Visual-mode command.
|
||||
kv_concat_len(redobuff.cur.keys, keys, len);
|
||||
redo_prefix(&spec, &redobuff.cur.keys, false);
|
||||
}
|
||||
redo_chars(&spec, &redobuff.cur.keys, arg_meta);
|
||||
kv_concat_len(redobuff.cur.keys, keys, len);
|
||||
redo_prefix(&spec, &redobuff.cur.keys, false);
|
||||
redo_chars(&spec, &redobuff.cur.keys, false);
|
||||
}
|
||||
|
||||
/// Discard the contents of the redo buffer and restore the previous redo
|
||||
|
||||
+111
-49
@@ -33,6 +33,7 @@
|
||||
#include "nvim/normal.h"
|
||||
#include "nvim/normal_defs.h"
|
||||
#include "nvim/ops.h"
|
||||
#include "nvim/option_vars.h"
|
||||
#include "nvim/register.h"
|
||||
#include "nvim/state_defs.h"
|
||||
#include "nvim/strings.h"
|
||||
@@ -506,6 +507,12 @@ static bool atom_blocked(void)
|
||||
return atom_suppressed || (vatom.state != kVatomNone && Visual.active);
|
||||
}
|
||||
|
||||
/// Decides if the command is capturable.
|
||||
static bool atom_capturable(bool consumers, bool keytyped)
|
||||
{
|
||||
return consumers && atom_is_user_cmd() && (keytyped || atom_composite_active());
|
||||
}
|
||||
|
||||
/// True if anything consumes atoms from `curbuf`. For performance: skip capture if no consumers.
|
||||
static bool atom_buf_has_consumers(void)
|
||||
{
|
||||
@@ -662,8 +669,8 @@ void atom_op_global_set(void)
|
||||
curcmd.op_global = true;
|
||||
}
|
||||
|
||||
/// Claims the prepped redo as the command's atom. Only toplevel user commands (a nested redo-prep
|
||||
/// is not an atom). Declines Ex/Lua operators.
|
||||
/// Claims the prepped redo as the command's atom (prep_redo()). Only toplevel user commands (a
|
||||
/// nested redo-prep is not an atom). Declines Ex/Lua operators.
|
||||
void atom_redo_set(CmdSpec spec)
|
||||
{
|
||||
if (spec.cmd == ':' || spec.cmd == K_COMMAND || spec.cmd == K_LUA) {
|
||||
@@ -778,7 +785,7 @@ static bool atom_visual_end_suffix(char *suffix, const CmdSpec *spec, bool redoa
|
||||
if (suffix == NULL || !atom_visual_replayable()) {
|
||||
bool prepped = prep && spec->op != NUL && suffix != NULL;
|
||||
if (prepped) {
|
||||
prep_redo("1v", 2, false, (CmdSpec){ 0 }); // Equal-size fallback.
|
||||
prep_redo_visual("1v", 2, (CmdSpec){ 0 }); // Equal-size fallback.
|
||||
redo_append_str(suffix, -1);
|
||||
}
|
||||
xfree(suffix);
|
||||
@@ -791,7 +798,7 @@ static bool atom_visual_end_suffix(char *suffix, const CmdSpec *spec, bool redoa
|
||||
if (prep) {
|
||||
// Get the redo tail (register/count, op chars) from the suffix. Prevents divergence of prep vs
|
||||
// atom, and suffixes inexpressible as spec chars ("r<C-V><CR>") stay replayable.
|
||||
prep_redo(vkeys, prefix, false, (CmdSpec){ 0 });
|
||||
prep_redo_visual(vkeys, prefix, (CmdSpec){ 0 });
|
||||
redo_append_str(suffix, -1);
|
||||
}
|
||||
if (!atom_is_user_cmd() || vatom.state != kVatomTyped) {
|
||||
@@ -830,57 +837,112 @@ bool atom_visual_end(CmdSpec spec, bool redoable)
|
||||
return atom_visual_end_suffix(atom_redo_keys(spec), &spec, redoable);
|
||||
}
|
||||
|
||||
/// Captures a pending operator's atom before it executes. Prep-exempt commands (yank without cpo-y,
|
||||
/// "D", folds) build no redo, so atom_cmd_end() cannot derive their atom from redobuff; reconstruct
|
||||
/// it here (staged).
|
||||
/// Captures a pending operator's atom and preps its redo, before it executes. Prep-exempt commands
|
||||
/// (yank without cpo-y, "D", folds) build no redo, so atom_cmd_end() cannot derive their atom from
|
||||
/// redobuff; reconstruct it here (staged).
|
||||
///
|
||||
/// Not for:
|
||||
/// - OP_CHANGE/OP_INSERT/OP_APPEND
|
||||
/// Not captured (prep only):
|
||||
/// - OP_CHANGE/OP_INSERT/OP_APPEND (the insert session is the atom)
|
||||
/// - motions with an interactively-typed payload (search, Ex, Lua)
|
||||
///
|
||||
/// @param redo_yank True when a yank builds a redo ("y" in 'cpoptions', not a GUI yank).
|
||||
void atom_capture_op(oparg_T *oap, cmdarg_T *cap, bool redo_yank)
|
||||
{
|
||||
if (oap->op_type == OP_CHANGE || oap->op_type == OP_INSERT || oap->op_type == OP_APPEND) {
|
||||
return;
|
||||
}
|
||||
const bool redoable = op_redoable(oap->op_type, redo_yank);
|
||||
bool ins_op = oap->op_type == OP_CHANGE || oap->op_type == OP_INSERT
|
||||
|| oap->op_type == OP_APPEND;
|
||||
bool payload_motion = cap->cmdchar >= 0x100
|
||||
|| (cap->cmdchar != NUL && strchr("/?:!", cap->cmdchar) != NULL);
|
||||
if (payload_motion) {
|
||||
return;
|
||||
bool excmd = cap->cmdchar == ':' || cap->cmdchar == K_COMMAND;
|
||||
if (!ins_op && !payload_motion) {
|
||||
bool prep_exempt = !redoable || cap->cmdchar == 'D';
|
||||
CmdSpec spec = {
|
||||
.regname = oap->regname, .count = cap->count0,
|
||||
.op = get_op_char(oap->op_type), .op_extra = get_extra_op_char(oap->op_type),
|
||||
};
|
||||
if (prep_exempt && (!Visual.active || oap->motion_force)) {
|
||||
// Only capture _user_ input.
|
||||
if (atom_capturable(atom_buf_has_consumers(), KeyTyped)) {
|
||||
bool operand = nv_nchar_is_arg(cap->cmdchar);
|
||||
spec.motion_force = oap->motion_force;
|
||||
spec.cmd = cap->cmdchar;
|
||||
spec.cmd2 = operand ? NUL : cap->nchar;
|
||||
spec.arg = operand ? cap->nchar : NUL;
|
||||
atom_stage_set(atom_from_spec(kAOperator, spec));
|
||||
}
|
||||
} else if (!Visual.active || oap->motion_force) {
|
||||
// Prepped: atom_cmd_end() derives the atom from redobuff.
|
||||
} else if (oap->op_type == OP_REPLACE && cap->nchar <= 0) {
|
||||
// Visual "r<C-V><CR>": `spec.arg` cannot represent the sentinel nchar (REPLACE_CR_NCHAR), so
|
||||
// hand-compose the literal suffix keys.
|
||||
char suffix[4] = { 'r', Ctrl_V, cap->nchar == REPLACE_CR_NCHAR ? CAR : NL, NUL };
|
||||
atom_visual_end_suffix(xstrdup(suffix), &spec, redoable);
|
||||
} else {
|
||||
// Visual-mode op: complete the accumulated visual atom with the operator keys.
|
||||
spec.arg = oap->op_type == OP_REPLACE ? cap->nchar : NUL;
|
||||
if ((oap->op_type == OP_NR_ADD || oap->op_type == OP_NR_SUB) && cap->arg) {
|
||||
// g<C-A>: the "g" variant is distinguished by cap->arg, not the op char: compose it back.
|
||||
spec.op_extra = spec.op;
|
||||
spec.op = 'g';
|
||||
}
|
||||
atom_visual_end(spec, redoable);
|
||||
}
|
||||
}
|
||||
const bool redoable = op_redoable(oap->op_type, redo_yank);
|
||||
bool prep_exempt = !redoable || cap->cmdchar == 'D';
|
||||
CmdSpec spec = {
|
||||
.regname = oap->regname, .count = cap->count0,
|
||||
.op = get_op_char(oap->op_type), .op_extra = get_extra_op_char(oap->op_type),
|
||||
};
|
||||
if (prep_exempt && (!Visual.active || oap->motion_force)) {
|
||||
// Only capture _user_ input.
|
||||
if (atom_buf_has_consumers() && atom_is_user_cmd() && (KeyTyped || atom_composite_active())) {
|
||||
bool operand = nv_nchar_is_arg(cap->cmdchar);
|
||||
spec.motion_force = oap->motion_force;
|
||||
spec.cmd = cap->cmdchar;
|
||||
spec.cmd2 = operand ? NUL : cap->nchar;
|
||||
spec.arg = operand ? cap->nchar : NUL;
|
||||
atom_stage_set(atom_from_spec(kAOperator, spec));
|
||||
|
||||
// The prep decision. Runs after the capture above: a self-selecting op's prep overrides the
|
||||
// equal-size fallback the visual end may have prepped ("dgn").
|
||||
if (redoable && cap->cmdchar != 'D'
|
||||
&& ((!Visual.active || oap->motion_force)
|
||||
// Also redo Operator-pending Visual mode mappings.
|
||||
|| excmd || cap->cmdchar == K_LUA)) {
|
||||
prep_redo(true, false, (CmdSpec){
|
||||
.regname = oap->regname, .count = cap->count0,
|
||||
.op = get_op_char(oap->op_type), .op_extra = get_extra_op_char(oap->op_type),
|
||||
.motion_force = oap->motion_force, .cmd = cap->cmdchar, .cmd2 = cap->nchar,
|
||||
});
|
||||
if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search
|
||||
// If 'cpoptions' does not contain 'r', insert the search pattern to really repeat the
|
||||
// same command.
|
||||
if (vim_strchr(p_cpo, kCpoRedo) == NULL) {
|
||||
redo_append_lit(cap->searchbuf, -1);
|
||||
}
|
||||
redo_append_str(S_LEN(NL_STR));
|
||||
} else if (excmd) {
|
||||
// do_cmdline() has stored the first typed line in "repeat_cmdline". When several lines are
|
||||
// typed repeating won't be possible.
|
||||
if (repeat_cmdline == NULL) {
|
||||
redo_new((CmdSpec){ 0 });
|
||||
} else {
|
||||
if (cap->cmdchar == ':') {
|
||||
redo_append_lit(repeat_cmdline, -1);
|
||||
} else {
|
||||
redo_append_spec(repeat_cmdline);
|
||||
}
|
||||
redo_append_str(S_LEN(NL_STR));
|
||||
XFREE_CLEAR(repeat_cmdline);
|
||||
}
|
||||
} else if (cap->cmdchar == K_LUA) {
|
||||
redo_append_num(repeat_luaref);
|
||||
redo_append_str(S_LEN(NL_STR));
|
||||
}
|
||||
} else if (!Visual.active || oap->motion_force) {
|
||||
// Prepped: atom_cmd_end() derives the atom from redobuff.
|
||||
} else if (oap->op_type == OP_REPLACE && cap->nchar <= 0) {
|
||||
// Visual "r<C-V><CR>": `spec.arg` cannot represent the sentinel nchar (REPLACE_CR_NCHAR), so
|
||||
// hand-compose the literal suffix keys.
|
||||
char suffix[4] = { 'r', Ctrl_V, cap->nchar == REPLACE_CR_NCHAR ? CAR : NL, NUL };
|
||||
atom_visual_end_suffix(xstrdup(suffix), &spec, redoable);
|
||||
} else {
|
||||
// Visual-mode op: complete the accumulated visual atom with the operator keys.
|
||||
spec.arg = oap->op_type == OP_REPLACE ? cap->nchar : NUL;
|
||||
if ((oap->op_type == OP_NR_ADD || oap->op_type == OP_NR_SUB) && cap->arg) {
|
||||
// g<C-A>: the "g" variant is distinguished by cap->arg, not the op char: compose it back.
|
||||
spec.op_extra = spec.op;
|
||||
spec.op = 'g';
|
||||
} else if (Visual.active && redoable && oap->motion_force == NUL) {
|
||||
if (op_self_select(cap)) {
|
||||
prep_redo(true, false, (CmdSpec){
|
||||
.regname = oap->regname, .count = cap->count0,
|
||||
.op = get_op_char(oap->op_type), .op_extra = get_extra_op_char(oap->op_type),
|
||||
.motion_force = oap->motion_force, .cmd = cap->cmdchar, .cmd2 = cap->nchar,
|
||||
});
|
||||
} else if (ins_op && !excmd && cap->cmdchar != K_LUA) {
|
||||
// Visual-entered Insert: redo body opens with the selection's captured keys; appends the
|
||||
// op+text+<Esc>. Unreplayable (void) selection falls back to "1v" (fixed-size reselect).
|
||||
// (Ex/Lua-motion selections were already prepped above, as the motion's keys.)
|
||||
String v = atom_visual_span();
|
||||
prep_redo_visual(v.data != NULL ? v.data : "1v", v.data != NULL ? v.size : 2, (CmdSpec){
|
||||
.regname = oap->regname,
|
||||
.op = get_op_char(oap->op_type), .op_extra = get_extra_op_char(oap->op_type),
|
||||
});
|
||||
xfree(v.data);
|
||||
}
|
||||
atom_visual_end(spec, redoable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1025,10 +1087,10 @@ static void atom_capture_cmd(cmdarg_T *ca, const CmdBaseline *old, bool toplevel
|
||||
} else {
|
||||
atom_visual_reset();
|
||||
}
|
||||
} else if (user && old->consumers && atom_pushes == old->pushes && !atom_staged()
|
||||
} else if (atom_capturable(old->consumers, old->keytyped)
|
||||
&& atom_pushes == old->pushes && !atom_staged()
|
||||
&& ca->oap->op_type == OP_NOP
|
||||
&& stuff_empty() && !ins_cascaded
|
||||
&& (old->keytyped || atom_composite_active())) {
|
||||
&& stuff_empty() && !ins_cascaded) {
|
||||
// KeyTyped survives stuffing but not macro playback; mapping/macro-fed commands are covered by
|
||||
// atom_composite_active().
|
||||
bool special_motion = (keycls & kKeyMotion) != 0;
|
||||
@@ -1037,7 +1099,7 @@ static void atom_capture_cmd(cmdarg_T *ca, const CmdBaseline *old, bool toplevel
|
||||
bool jump_cmd = (keycls & kKeyJump) != 0;
|
||||
// Replayable? Register prefix ('"x') is captured as part of the command it prefixes; "@x"/"Q"
|
||||
// are translations, their resolution is the atom stream.
|
||||
bool capturable = (ca->cmdchar > 0 && ca->cmdchar < 0x100
|
||||
bool replayable = (ca->cmdchar > 0 && ca->cmdchar < 0x100
|
||||
&& ca->cmdchar != '"' && ca->cmdchar != '@' && ca->cmdchar != 'Q'
|
||||
&& !scroll_cmd)
|
||||
|| special_motion;
|
||||
@@ -1082,7 +1144,7 @@ static void atom_capture_cmd(cmdarg_T *ca, const CmdBaseline *old, bool toplevel
|
||||
CmdAtom atom = atom_from_cmdline(kAEx, ca, curcmd.cmdline);
|
||||
atom.changed = changed;
|
||||
atom_push(false, atom);
|
||||
} else if (capturable) {
|
||||
} else if (replayable) {
|
||||
// Non-redoable command (u, zz, q=): never cascaded as an edit.
|
||||
CmdAtom atom = atom_from_spec(motion ? kAMotion : jump_cmd ? kAJump : kACommand,
|
||||
atom_cmd_spec(ca));
|
||||
|
||||
+7
-12
@@ -220,17 +220,13 @@ static void insert_enter(InsertState *s)
|
||||
if (s->cmdchar != NUL && restart_edit == 0) {
|
||||
if (s->cmdchar == 'V' || s->cmdchar == 'v') {
|
||||
// "gR" or "gr" command
|
||||
redo_new((CmdSpec){ .count = s->count, .cmd = 'g', .cmd2 = (s->cmdchar == 'v') ? 'r' : 'R' });
|
||||
redo_append_char('g');
|
||||
redo_append_char((s->cmdchar == 'v') ? 'r' : 'R');
|
||||
prep_redo(false, false, (CmdSpec){ .count = s->count, .cmd = 'g',
|
||||
.cmd2 = (s->cmdchar == 'v') ? 'r' : 'R' });
|
||||
} else {
|
||||
redo_new((CmdSpec){ .count = s->count, .cmd = s->cmdchar,
|
||||
.cmd2 = (s->cmdchar == 'g') ? 'I' : NUL });
|
||||
redo_append_char(s->cmdchar);
|
||||
if (s->cmdchar == 'g') { // "gI" command
|
||||
redo_append_char('I');
|
||||
} else if (s->cmdchar == 'r') { // "r<CR>" command
|
||||
s->count = 1; // insert only one <CR>
|
||||
prep_redo(false, false, (CmdSpec){ .count = s->count, .cmd = s->cmdchar,
|
||||
.cmd2 = (s->cmdchar == 'g') ? 'I' : NUL });
|
||||
if (s->cmdchar == 'r') { // "r<CR>" command
|
||||
s->count = 1; // insert only one <CR>
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2234,8 +2230,7 @@ int stop_arrow(void)
|
||||
if (jumped) {
|
||||
// Non-captured cursor-move (mouse, <PageUp>, …): restart the capture as a "1i" insertion.
|
||||
// The count is a spec field (not body bytes), so "[count]." replaces it ("3i…").
|
||||
redo_new((CmdSpec){ .count = 1, .cmd = 'i' });
|
||||
redo_append_char('i');
|
||||
prep_redo(false, false, (CmdSpec){ .count = 1, .cmd = 'i' });
|
||||
Ins.new_insert_skip = 2;
|
||||
} else {
|
||||
// Cursor-move was captured (start_arrow()): the atom mc-cascade will replay it.
|
||||
|
||||
+1
-2
@@ -866,8 +866,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
|
||||
c1 = (dir == FORWARD) ? 'p' : 'P';
|
||||
c2 = NUL;
|
||||
}
|
||||
prep_redo(NULL, 0, false,
|
||||
(CmdSpec){ .regname = regname, .count = count, .cmd = c1, .cmd2 = c2 });
|
||||
prep_redo(true, false, (CmdSpec){ .regname = regname, .count = count, .cmd = c1, .cmd2 = c2 });
|
||||
|
||||
// Remember where the paste started, so in edit() Ins.start can be set to this position
|
||||
if (restart_edit != 0) {
|
||||
|
||||
+3
-3
@@ -1737,7 +1737,7 @@ static void prep_redo_cmd(cmdarg_T *cap)
|
||||
{
|
||||
// Composing chars: the operand's byte form is the composed string, not the single char.
|
||||
bool composing = cap->nchar_len > 0;
|
||||
prep_redo(NULL, 0, composing, atom_cmd_spec(cap));
|
||||
prep_redo(true, composing, atom_cmd_spec(cap));
|
||||
if (composing) {
|
||||
redo_append_str(cap->nchar_composing, -1);
|
||||
}
|
||||
@@ -4584,8 +4584,8 @@ static void nv_replace(cmdarg_T *cap)
|
||||
// Give 'r' to edit(), to get the redo command right.
|
||||
invoke_edit(cap, true, 'r', false);
|
||||
} else {
|
||||
prep_redo(NULL, 0, true, (CmdSpec){ .regname = cap->oap->regname, .count = cap->count1,
|
||||
.cmd = 'r', .arg = cap->nchar });
|
||||
prep_redo(true, true, (CmdSpec){ .regname = cap->oap->regname, .count = cap->count1,
|
||||
.cmd = 'r', .arg = cap->nchar });
|
||||
if (had_ctrl_v != NUL) {
|
||||
redo_append_char(had_ctrl_v);
|
||||
}
|
||||
|
||||
+9
-66
@@ -3256,16 +3256,22 @@ static bool is_ex_cmdchar(cmdarg_T *cap)
|
||||
return cap->cmdchar == ':' || cap->cmdchar == K_COMMAND;
|
||||
}
|
||||
|
||||
/// True for an operator whose motion selects its own region (gn/gN/gv): the redo replays the
|
||||
/// motion's own keys ("dgn" re-searches, "dgv" reselects).
|
||||
bool op_self_select(const cmdarg_T *cap)
|
||||
{
|
||||
return cap->cmdchar == 'g' && (cap->nchar == 'n' || cap->nchar == 'N' || cap->nchar == 'v');
|
||||
}
|
||||
|
||||
/// How an Insert-entering operator (OP_CHANGE/OP_INSERT/OP_APPEND) was entered from Visual mode:
|
||||
/// decides the session's redo/publish handling (atom_ins_start()).
|
||||
/// decides the session's redo/capture handling (atom_ins_start()).
|
||||
static VisualIns op_ins_visual(oparg_T *oap, cmdarg_T *cap)
|
||||
{
|
||||
if (!oap->is_VIsual) {
|
||||
return kVInsNone;
|
||||
}
|
||||
if (is_ex_cmdchar(cap) || cap->cmdchar == K_LUA || oap->motion_force != NUL
|
||||
|| (cap->cmdchar == 'g'
|
||||
&& (cap->nchar == 'n' || cap->nchar == 'N' || cap->nchar == 'v'))) {
|
||||
|| op_self_select(cap)) {
|
||||
// The selection came from a self-selecting motion (gn/gN/gv, an omap running ":normal", a Lua
|
||||
// motion) or a forced-motion operator: the redo replays the motion's own keys instead.
|
||||
return kVInsOther;
|
||||
@@ -3321,43 +3327,6 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
|
||||
}
|
||||
|
||||
atom_capture_op(oap, cap, redo_yank);
|
||||
if (op_redoable(oap->op_type, redo_yank)
|
||||
&& ((!Visual.active || oap->motion_force)
|
||||
// Also redo Operator-pending Visual mode mappings.
|
||||
|| is_ex_cmdchar(cap) || cap->cmdchar == K_LUA)
|
||||
&& cap->cmdchar != 'D') {
|
||||
prep_redo(NULL, 0, false, (CmdSpec){
|
||||
.regname = oap->regname, .count = cap->count0,
|
||||
.op = get_op_char(oap->op_type), .op_extra = get_extra_op_char(oap->op_type),
|
||||
.motion_force = oap->motion_force, .cmd = cap->cmdchar, .cmd2 = cap->nchar,
|
||||
});
|
||||
if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search
|
||||
// If 'cpoptions' does not contain 'r', insert the search
|
||||
// pattern to really repeat the same command.
|
||||
if (vim_strchr(p_cpo, kCpoRedo) == NULL) {
|
||||
redo_append_lit(cap->searchbuf, -1);
|
||||
}
|
||||
redo_append_str(S_LEN(NL_STR));
|
||||
} else if (is_ex_cmdchar(cap)) {
|
||||
// do_cmdline() has stored the first typed line in
|
||||
// "repeat_cmdline". When several lines are typed repeating
|
||||
// won't be possible.
|
||||
if (repeat_cmdline == NULL) {
|
||||
redo_new((CmdSpec){ 0 });
|
||||
} else {
|
||||
if (cap->cmdchar == ':') {
|
||||
redo_append_lit(repeat_cmdline, -1);
|
||||
} else {
|
||||
redo_append_spec(repeat_cmdline);
|
||||
}
|
||||
redo_append_str(S_LEN(NL_STR));
|
||||
XFREE_CLEAR(repeat_cmdline);
|
||||
}
|
||||
} else if (cap->cmdchar == K_LUA) {
|
||||
redo_append_num(repeat_luaref);
|
||||
redo_append_str(S_LEN(NL_STR));
|
||||
}
|
||||
}
|
||||
|
||||
if (Visual.active) {
|
||||
if (!gui_yank) {
|
||||
@@ -3465,32 +3434,6 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
|
||||
Visual.resel.line_count = oap->line_count;
|
||||
}
|
||||
|
||||
if (op_redoable(oap->op_type, redo_yank) && oap->motion_force == NUL) {
|
||||
// Prepare for redoing. "gn"/"gN"/"gv" motions select their own region: the
|
||||
// redo replays the motion itself ("dgn" re-searches, "dgv" reselects).
|
||||
if (cap->cmdchar == 'g' && (cap->nchar == 'n'
|
||||
|| cap->nchar == 'N'
|
||||
|| cap->nchar == 'v')) {
|
||||
prep_redo(NULL, 0, false, (CmdSpec){
|
||||
.regname = oap->regname, .count = cap->count0,
|
||||
.op = get_op_char(oap->op_type), .op_extra = get_extra_op_char(oap->op_type),
|
||||
.motion_force = oap->motion_force, .cmd = cap->cmdchar, .cmd2 = cap->nchar,
|
||||
});
|
||||
} else if ((oap->op_type == OP_CHANGE || oap->op_type == OP_INSERT
|
||||
|| oap->op_type == OP_APPEND)
|
||||
&& !is_ex_cmdchar(cap) && cap->cmdchar != K_LUA) {
|
||||
// Visual-entered Insert: redo body opens with the selection's captured keys; appends the
|
||||
// op+text+<Esc>. Unreplayable (void) selection falls back to "1v" (fixed-size reselect).
|
||||
// (Ex/Lua-motion selections were already prepped above, as the motion's keys.)
|
||||
String v = atom_visual_span();
|
||||
prep_redo(v.data != NULL ? v.data : "1v", v.data != NULL ? v.size : 2, false, (CmdSpec){
|
||||
.regname = oap->regname,
|
||||
.op = get_op_char(oap->op_type), .op_extra = get_extra_op_char(oap->op_type),
|
||||
});
|
||||
xfree(v.data);
|
||||
}
|
||||
}
|
||||
|
||||
// oap->inclusive defaults to true.
|
||||
// If oap->end is on a NUL (empty line) oap->inclusive becomes
|
||||
// false. This makes "d}P" and "v}dP" work the same.
|
||||
|
||||
@@ -625,8 +625,7 @@ void spell_suggest(int count)
|
||||
strcat(p, sug.su_badptr + stp->st_orglen);
|
||||
|
||||
// For redo we use a change-word command.
|
||||
redo_new((CmdSpec){ 0 });
|
||||
redo_append_str(S_LEN("ciw"));
|
||||
prep_redo(false, false, (CmdSpec){ .op = 'c', .cmd = 'i', .cmd2 = 'w' });
|
||||
redo_append_lit(p + c, stp->st_wordlen + sug.su_badlen - stp->st_orglen);
|
||||
redo_append_char(ESC);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user