mirror of
https://github.com/neovim/neovim.git
synced 2026-09-09 07:25:51 +00:00
fix(multicursor): cascade mapping atoms with insert entry #41714
Problem:
Pending mapping atoms prevent live insert-cascading (`nnoremap i ^i`).
Flushing them at insert-session start (3a02a39957) adds a separate
cascade boundary, which muddles the architecture.
Solution:
Let the first insert-span cascade pending mapping atoms on insert-entry.
This is an alternative fix for #41692.
Composite children (`.atoms`) BEFORE:
{ { 'motion', '^' }, { 'insert', '1iX<Esc>' } }
Composite children (`.atoms`) AFTER:
{ { 'motion', '^' }, { 'insert', '1i<Esc>' }, { 'insert', 'iX<Esc>' } }
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
// Context = "full app state" abstraction. (Note: it's named "Context" to disambiguate with state.c
|
||||
// which is about the event-loop state-machine, not "total program state".)
|
||||
// Context = "full app state" abstraction. So named to disambiguate state.c, which is about the
|
||||
// event-loop state-machine, not "total program state".
|
||||
//
|
||||
// Unified interface of:
|
||||
// + shada
|
||||
// + CtxSwitch/ctx_switch (FKA: aucmd_prepbuf, switch_win, win_execute_T)
|
||||
// + TODO: save_state_T/save_current_state
|
||||
// + TODO: sessions
|
||||
// + TODO: undo save/restore (for cmdpreview, multicursor)
|
||||
// + TODO: TRY_WRAP ?
|
||||
@@ -11,7 +12,7 @@
|
||||
// Related:
|
||||
// - vim.with()
|
||||
// - switch_option_context(), restore_option_context()
|
||||
// - McSandbox: input-replay guard. Sibling axis to ctx_switch() and ctx_save().
|
||||
// - McSandbox/mc_sandbox_enter: input-replay guard. Sibling axis to ctx_switch() and ctx_save().
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
@@ -7092,7 +7092,7 @@ void update_topline_cursor(void)
|
||||
update_curswant();
|
||||
}
|
||||
|
||||
/// Save the current State and go to Normal mode.
|
||||
/// Save the current State (editor-mode) and go to Normal mode.
|
||||
void save_current_state(save_state_T *sst)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
|
||||
@@ -493,7 +493,7 @@ EXTERN int inhibit_delete_count INIT( = 0);
|
||||
/// Encoding used when 'fencs' is set to "default"
|
||||
EXTERN char *fenc_default INIT( = NULL);
|
||||
|
||||
/// "State" is the main state of Vim.
|
||||
/// "State" is the main state (editor-mode) of Vim.
|
||||
/// There are other variables that modify the state:
|
||||
/// Visual_mode: When State is MODE_NORMAL or MODE_INSERT.
|
||||
/// finish_op : When State is MODE_NORMAL, after typing the operator and
|
||||
|
||||
@@ -595,10 +595,6 @@ void mc_ins_cascade_start(bool cascade, varnumber_T tick)
|
||||
return;
|
||||
}
|
||||
mc_ins_joined = false;
|
||||
if (cascade && mc_buf_has_cursors(curbuf) && kv_size(g_atoms) > 0) {
|
||||
// Cascade now if a mapping queued atoms before entering Insert ("nnoremap i ^i").
|
||||
mc_cascade();
|
||||
}
|
||||
mc_ins_span.active = cascade && mc_buf_has_cursors(curbuf);
|
||||
mc_ins_span.first = true;
|
||||
mc_ins_span.done_len = 0;
|
||||
@@ -640,11 +636,13 @@ static void mc_ins_restore_state(const McInsSaved *saved)
|
||||
curbuf->b_last_changedtick_i = saved->last_changedtick_i;
|
||||
}
|
||||
|
||||
/// Pushes one span and immediately cascades it. Takes ownership of `keys` and `text`.
|
||||
/// Pushes one span and immediately cascades it, plus any pending mapping atoms.
|
||||
/// Takes ownership of `keys` and `text`.
|
||||
static void mc_ins_span_push(char *keys, char *text)
|
||||
{
|
||||
mc_ins_span.first = false;
|
||||
// If all cursors disappear mid-session (e.g. by dedupe), emit but don't cascade.
|
||||
|
||||
// If all cursors disappear mid-session (e.g. dedupe), emit but don't cascade.
|
||||
bool cascade = mc_buf_has_cursors(curbuf);
|
||||
atom_push_raw(cascade, &(CmdAtom){
|
||||
.type = kAInsertSpan,
|
||||
@@ -655,6 +653,7 @@ static void mc_ins_span_push(char *keys, char *text)
|
||||
if (!cascade) {
|
||||
return;
|
||||
}
|
||||
|
||||
McInsSaved saved = mc_ins_save_state();
|
||||
block_autocmds(); // The span replay would fire InsertEnter/InsertLeave on every key.
|
||||
mc_cascade();
|
||||
@@ -823,7 +822,7 @@ void mc_ins_cascade_restart(void)
|
||||
void mc_ins_cascade(void)
|
||||
{
|
||||
if (!mc_ins_span.active || mc_replaying() || !(State & MODE_INSERT)
|
||||
|| !mc_buf_has_cursors(curbuf) || kv_size(g_atoms) != 0) {
|
||||
|| !mc_buf_has_cursors(curbuf)) {
|
||||
return;
|
||||
}
|
||||
String ins = redo_keys(NULL);
|
||||
|
||||
@@ -1215,14 +1215,10 @@ describe('multicursor', function()
|
||||
-- Also when a Normal-mode mapping moves before entering Insert. #41605
|
||||
clear_cursors()
|
||||
cursors({ 'aaaa', 'bbbb', 'cccc' }, 'llQjQj')
|
||||
eq({ { 0, 2 }, { 1, 2 } }, anchors())
|
||||
eq({ 3, 2 }, api.nvim_win_get_cursor(0))
|
||||
command('nnoremap i ^i')
|
||||
screen:expect([[
|
||||
aa{17:a}a |
|
||||
bb{17:b}b |
|
||||
cc^cc |
|
||||
{1:~ }|*2
|
||||
|
|
||||
]])
|
||||
atoms_start()
|
||||
feed('iX')
|
||||
screen:expect([[
|
||||
X{17:a}aaa |
|
||||
@@ -1233,6 +1229,16 @@ describe('multicursor', function()
|
||||
]])
|
||||
feed('<Esc>')
|
||||
eq({ 'Xaaaa', 'Xbbbb', 'Xcccc' }, get_lines())
|
||||
local ev = atom_last()
|
||||
eq(
|
||||
{ type = 'mapping', lhs = k('iX<Esc>'), changed = true },
|
||||
t_atom.pick(atom_last(), 'type', 'lhs', 'changed')
|
||||
)
|
||||
local children = {}
|
||||
for _, child in ipairs(ev.atoms) do
|
||||
children[#children + 1] = { child.type, child.keys }
|
||||
end
|
||||
eq({ { 'motion', '^' }, { 'insert', k('1i<Esc>') }, { 'insert', k('iX<Esc>') } }, children)
|
||||
end)
|
||||
end)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user