Skip to content

Commit 6483893

Browse files
ThomasK33claude
andcommitted
fix(selection): clear visual cache on disable; cover demotion fire path
Address review findings (P3, P3) on PR #245: - DEREM-18: disable() now resets last_active_visual_selection so a disable/enable cycle does not leave a stale entry that causes a phantom demotion timer on the first normal-mode cursor move. - DEREM-17: Add a demotion_timer test that fires the timer while it is still the active one, exercising the identity-guard, stop/close, and the handle_selection_demotion success path with full assertions. Change-Id: Id84ee382503d236a5dfd18109ff9ee0554f6993b Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent 1598a89 commit 6483893

2 files changed

Lines changed: 52 additions & 0 deletions

File tree

lua/claudecode/selection.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ function M.disable()
4646
M._clear_autocommands()
4747

4848
M.state.latest_selection = nil
49+
M.state.last_active_visual_selection = nil
4950
M.server = nil
5051

5152
M._cancel_debounce_timer()

tests/selection_test.lua

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,57 @@ describe("Selection module", function()
531531

532532
terminal_module.get_active_terminal_bufnr = original_get
533533
end)
534+
535+
it("should demote to cursor position when timer fires normally", function()
536+
local original_get, terminal_module = install_terminal_stub()
537+
538+
selection.enable(mock_server)
539+
540+
local visual_selection = {
541+
text = "x",
542+
filePath = "/path/to/test.lua",
543+
fileUrl = "file:///path/to/test.lua",
544+
selection = {
545+
start = { line = 0, character = 0 },
546+
["end"] = { line = 0, character = 1 },
547+
isEmpty = false,
548+
},
549+
}
550+
selection.state.last_active_visual_selection = {
551+
bufnr = 1,
552+
selection_data = visual_selection,
553+
timestamp = 0,
554+
}
555+
selection.state.latest_selection = visual_selection
556+
557+
_G.vim.test.set_mode("n")
558+
_G.vim.test.set_cursor(0, 2, 3)
559+
mock_server.last_broadcast = nil
560+
561+
selection.update_selection()
562+
563+
local timer = selection.state.demotion_timer
564+
assert(timer ~= nil)
565+
566+
timer:fire()
567+
568+
assert(selection.state.demotion_timer == nil)
569+
assert.are.equal(1, timer._stop_calls)
570+
assert.are.equal(1, timer._close_calls)
571+
572+
local demoted = selection.state.latest_selection
573+
assert(demoted ~= nil)
574+
assert.are.equal("", demoted.text)
575+
assert.are.equal(true, demoted.selection.isEmpty)
576+
assert.are.equal(1, demoted.selection.start.line)
577+
assert.are.equal(3, demoted.selection.start.character)
578+
assert(selection.state.last_active_visual_selection == nil)
579+
assert(mock_server.last_broadcast ~= nil)
580+
assert.are.equal("selection_changed", mock_server.last_broadcast.event)
581+
582+
selection.disable()
583+
terminal_module.get_active_terminal_bufnr = original_get
584+
end)
534585
end)
535586

536587
it("should get cursor position in normal mode", function()

0 commit comments

Comments
 (0)