summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJackson Moore <jacksonmoore@tuta.io>2026-09-04 17:51:24 +0200
committerJackson Moore <jacksonmoore@tuta.io>2026-09-04 17:51:24 +0200
commit0ff6af88a8b955606312c9e4d4929057b9067826 (patch)
tree499f0ca5bea709d6ded9ec15e00844c750d634b3
parentfae69149ec18b2f75e124976623fc99ee3cb090a (diff)
Roll back feedback when finalizer setup fails
-rw-r--r--lua/clever_f/feedback_service.lua28
-rw-r--r--tests/run.lua26
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())