diff --git a/src/nvim/input_cmdatom.c b/src/nvim/input_cmdatom.c index 81b5a700f3..e2ba43dada 100644 --- a/src/nvim/input_cmdatom.c +++ b/src/nvim/input_cmdatom.c @@ -194,7 +194,7 @@ CmdSpec atom_cmd_spec(const cmdarg_T *cap) static CmdOrigin atom_origin(void) { CmdOrigin origin = { .win = curwin, .pos = curwin->w_cursor, - .tick = buf_get_changedtick(curbuf) }; + .tick = buf_get_changedtick(curbuf), .maptick = maptick }; set_bufref(&origin.buf, curbuf); return origin; } @@ -1167,9 +1167,12 @@ InsSession atom_ins_start(int cmd, long count, VisualIns vis, bool vblock) void atom_ins_end(const InsSession *session, bool busy) { bool visual = session->vis != kVInsNone; - if (!session->typed || busy || restart_edit != 0 || !atom_buf_has_consumers() + bool user_input = session->typed + // A session is user input, if user input occurred during it. #41516 + || maptick != session->origin.maptick; + if (!user_input || busy || restart_edit != 0 || !atom_buf_has_consumers() || (visual && session->vis != kVInsKeys)) { - if (session->typed && (busy || restart_edit != 0) && atom_composite_active()) { + if (user_input && (busy || restart_edit != 0) && atom_composite_active()) { // Incomplete session (i_CTRL-O): its resolution is never captured. composite.lossy = true; } diff --git a/src/nvim/input_cmdatom_defs.h b/src/nvim/input_cmdatom_defs.h index 6c6940fa7f..7347adaae3 100644 --- a/src/nvim/input_cmdatom_defs.h +++ b/src/nvim/input_cmdatom_defs.h @@ -36,6 +36,7 @@ typedef struct { const win_T *win; ///< Window. pos_T pos; ///< Cursor position. Stored here bc the window might be closed. varnumber_T tick; ///< b:changedtick. + int maptick; ///< Advances on typed input (globals.h:maptick). } CmdOrigin; /// How an insert-session was entered from Visual mode. diff --git a/test/functional/editor/cmdatom_spec.lua b/test/functional/editor/cmdatom_spec.lua index 16ddad36d5..d33c78571d 100644 --- a/test/functional/editor/cmdatom_spec.lua +++ b/test/functional/editor/cmdatom_spec.lua @@ -939,7 +939,7 @@ describe('CmdAtom', function() ) end) - it('an insert session atom captures its text', function() + it('insert-session atom captures its text', function() fn.setline(1, { 'aaa' }) feed('gg0') atoms_start() @@ -966,6 +966,38 @@ describe('CmdAtom', function() eq({ type = 'insert', count = 3, text = 'Z' }, pick(atom_last(), 'type', 'count', 'text')) end) + it('insert-session entered programmatically, still captures *user* input #41516', function() + atoms_start() + n.exec_lua([[ + vim.keymap.set('i', '', function() + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('', true, false, true), 'n', false) + vim.schedule(function() + vim.api.nvim_feedkeys('i', 'n', false) + end) + end) + ]]) + local before = #atoms() + feed('i') + n.exec_lua('vim.wait(50)') + feed('') + n.exec_lua('vim.wait(50)') + feed('') + local evs = atoms() + eq(before + 2, #evs) + eq({ k('1i'), k('1i') }, { evs[#evs - 1].keys, evs[#evs].keys }) + -- Typed text within such a session lands in its atom... + n.exec_lua([[vim.schedule(function() vim.api.nvim_feedkeys('i', 'n', false) end)]]) + n.exec_lua('vim.wait(50)') + feed('hi') + eq({ text = 'hi', keys = k('1ihi') }, pick(atom_last(), 'text', 'keys')) + -- ...but with NO typed input within it, the session emits nothing. + before = #atoms() + n.exec_lua([[vim.schedule(function() vim.api.nvim_feedkeys('i', 'n', false) end)]]) + n.exec_lua('vim.wait(50)') + n.exec_lua([[vim.api.nvim_feedkeys('\27', 'n', false)]]) + eq(before, #atoms()) + end) + it("operatorfunc atom includes the getchar()'d payload", function() n.exec(t_atom.minisurround_vim) fn.setline(1, { 'alpha beta' })