diff --git a/lua/neocursor/init.lua b/lua/neocursor/init.lua index ddc34fa..b881f0c 100644 --- a/lua/neocursor/init.lua +++ b/lua/neocursor/init.lua @@ -884,7 +884,7 @@ local function do_accept(s) preview.clear(s.bufnr) state.suggestion = nil cancel_timer() -- a request scheduled before the accept would race the chain - vim.cmd("let &undolevels=&undolevels") -- one undo reverts the whole accept + vim.cmd("let &g:undolevels=&g:undolevels") -- one undo reverts the whole accept local lc = vim.api.nvim_buf_line_count(s.bufnr) vim.api.nvim_buf_set_lines(s.bufnr, s.start0, math.min(s.end0_excl, lc), false, s.lines) local pos = { s.start0 + #s.lines, #(s.lines[#s.lines] or "") } diff --git a/test/flow_spec.lua b/test/flow_spec.lua index 7ee536e..2ca0936 100644 --- a/test/flow_spec.lua +++ b/test/flow_spec.lua @@ -8,6 +8,7 @@ -- suggestions surviving the echo events of our own apply/jump, two-phase -- jump→accept, chain advance, and rapid tab-tab-tab never leaking a literal -- into the buffer. +-- an accept never copying a buffer-local 'undolevels' into the global option. local root = vim.fn.fnamemodify(debug.getinfo(1, "S").source:sub(2), ":h:h") vim.opt.rtp:prepend(root) vim.opt.swapfile = false -- a stale swap prompt would hang a headless run @@ -162,6 +163,27 @@ local function round5() end) end +-- Round 6: panel buffers (diffview/mason/neogit-style) set buffer-local +-- 'undolevels' to -1/0. Accepting must close the undo block WITHOUT copying +-- that local value into the global option — a leak disables undo everywhere. +local function round6() + poll(nc.has_suggestion, 3000, function() + check("suggestion arrives (round 6)", true, true) + local before = vim.api.nvim_get_option_value("undolevels", { scope = "global" }) + input("") + later(120, function() + check("accept applies edit with local undolevels", line(1), "line1 = 100") + local after = vim.api.nvim_get_option_value("undolevels", { scope = "global" }) + check("accept leaves global 'undolevels' unchanged", after, before) + vim.cmd("setlocal undolevels<") -- drop the buffer-local override + input("") + end) + end, function() + check("suggestion arrives (round 6)", false, true) + input("") + end) +end + vim.api.nvim_win_set_cursor(0, { 1, 0 }) later(50, round1) feed("Ax") -- blocks while round1 runs; returns on its @@ -186,5 +208,11 @@ vim.api.nvim_win_set_cursor(0, { 1, 0 }) later(50, round5) feed("A0") -- "line1 = 10" is a prefix of the fake's edit → inline ghost "0" +vim.api.nvim_buf_set_lines(0, 0, -1, false, seed) +vim.api.nvim_win_set_cursor(0, { 1, 0 }) +vim.api.nvim_set_option_value("undolevels", -1, { buf = 0 }) -- what panel buffers do +later(50, round6) +feed("A6") -- blocks while round6 runs; returns on its + io.stdout:write(failed == 0 and "ALL PASS\n" or (failed .. " FAILURES\n")) vim.cmd(failed == 0 and "qall!" or "cquit!")