From 0ff6af88a8b955606312c9e4d4929057b9067826 Mon Sep 17 00:00:00 2001 From: Jackson Moore Date: Fri, 4 Sep 2026 17:51:24 +0200 Subject: Roll back feedback when finalizer setup fails --- lua/clever_f/feedback_service.lua | 28 ++++++++++++++++++++++------ tests/run.lua | 26 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/lua/clever_f/feedback_service.lua b/lua/clever_f/feedback_service.lua index 492d4a6..ca26746 100644 --- a/lua/clever_f/feedback_service.lua +++ b/lua/clever_f/feedback_service.lua @@ -622,12 +622,28 @@ local function materialize_persistent(service, request) }) request.group = "CleverFChar" request.priority = M.overlay_priority("CleverFChar") - record.transitions:AddTargetOverlay( - request.identity, - request.window, - request.anchor.line - ) - request.finalizers = register_finalizers(service, record) + local owned, ownership_error = pcall(function() + record.transitions:AddTargetOverlay( + request.identity, + request.window, + request.anchor.line + ) + request.finalizers = register_finalizers(service, record) + end) + if not owned then + local resources = record.transitions:RemoveTargetOverlay( + request.identity, + request.window + ) + if #resources == 0 then + resources[1] = { + identity = request.identity, + window = request.window, + } + end + pcall(release_target_overlays, record, resources) + error(ownership_error, 0) + end local requests = record.persistent_requests requests[#requests + 1] = request return request diff --git a/tests/run.lua b/tests/run.lua index 131086f..8b29fd3 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -6298,6 +6298,32 @@ test("Persistent feedback reuses the movement TargetPlan", function() end, "reuse the movement TargetPlan") end) +test("Persistent creation rolls back an unowned overlay", function() + local state = fresh_sequence_state() + local host = MemoryHost.new({ buffer_lines = { "ax" } }) + host.register_events = function() + error("event registration failed", 0) + end + local feedback = feedback_service.new(host) + local shared_target = target_plan.build(target("x"), matching_policy()) + local movement = motion_plan.build(shared_target, "f") + + fails(function() + feedback:request_persistent({ + context = "n", + anchor = domain.Position.new(1, 1), + target_plan = shared_target, + motion_plan = movement, + window = "window-1", + }) + end, "event registration failed") + + same(0, #state.target_overlays) + same(0, #state.finalizers) + same(0, map_size(host:highlights())) + same(0, #feedback:persistent_requests()) +end) + test("Persistent feedback uses its selected descriptor and endpoint policy", function() fresh_sequence_state() local feedback = feedback_service.new(MemoryHost.new()) -- cgit v1.2.3