From ba398cbb4cdb04ffadf2935a2bb536a1c5105400 Mon Sep 17 00:00:00 2001 From: Jackson Moore Date: Fri, 4 Sep 2026 12:15:32 +0200 Subject: Make temporary releases idempotent --- lua/clever_f/acquisition_service.lua | 34 +++++++++++++++++++++++++++++----- lua/clever_f/feedback_service.lua | 24 ++++++++++++++++++++---- tests/run.lua | 6 ++++++ 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/lua/clever_f/acquisition_service.lua b/lua/clever_f/acquisition_service.lua index a19f8f8..5cbe6ab 100644 --- a/lua/clever_f/acquisition_service.lua +++ b/lua/clever_f/acquisition_service.lua @@ -321,23 +321,47 @@ function TemporaryResourceScope:release() if record == nil then fail("temporary resource scope is invalid", 2) end + if not record.active then + return false + end + record.active = false + + local first_error + local function release_operation(operation) + local ok, failure = pcall(operation) + if not ok and first_error == nil then + first_error = failure + end + end + if record.interactive and record.prompt_shown and record.input_completed and record.acquisition_completed then - record.host:redraw("full") + release_operation(function() + record.host:redraw("full") + end) end if record.direct_marker ~= nil then - record.feedback:remove_temporary_overlay(record.direct_marker) + release_operation(function() + record.feedback:remove_temporary_overlay(record.direct_marker) + end) end if record.cursor_marker ~= nil then - record.feedback:remove_temporary_overlay(record.cursor_marker) + release_operation(function() + record.feedback:remove_temporary_overlay(record.cursor_marker) + end) end if record.cursor_presentation_lease ~= nil then - record.cursor_presentation_lease:release() + release_operation(function() + record.cursor_presentation_lease:release() + end) end - record.active = false + if first_error ~= nil then + error(first_error, 0) + end + return true end local function require_policy(service, host) diff --git a/lua/clever_f/feedback_service.lua b/lua/clever_f/feedback_service.lua index 2b100a4..084b30a 100644 --- a/lua/clever_f/feedback_service.lua +++ b/lua/clever_f/feedback_service.lua @@ -23,6 +23,7 @@ local OVERLAY_PRIORITIES = { local service_records = setmetatable({}, { __mode = "k" }) local cursor_lease_records = setmetatable({}, { __mode = "k" }) +local temporary_release_records = setmetatable({}, { __mode = "k" }) local FEATURE_GROUPS = { "CleverFCursor", @@ -186,8 +187,8 @@ function CursorPresentationLease:release() if not record.active then return false end - record.host:restore_cursor_presentation(record.identity) record.active = false + record.host:restore_cursor_presentation(record.identity) return true end @@ -242,12 +243,14 @@ function FeedbackService:create_direct_markers(positions, window) priority = M.overlay_priority("CleverFDirect"), }) record.transitions:AddTemporaryOverlay(identity, window, "CleverFDirect") - return { + local resource = { identity = identity, window = window, group = "CleverFDirect", positions = positions, } + temporary_release_records[resource] = false + return resource end function FeedbackService:create_cursor_marker(position, window) @@ -264,21 +267,34 @@ function FeedbackService:create_cursor_marker(position, window) priority = M.overlay_priority("CleverFCursor"), }) record.transitions:AddTemporaryOverlay(identity, window, "CleverFCursor") - return { + local resource = { identity = identity, window = window, group = "CleverFCursor", position = position, } + temporary_release_records[resource] = false + return resource end function FeedbackService:remove_temporary_overlay(resource) if type(resource) ~= "table" or resource.identity == nil then fail("temporary overlay resource must identify its highlight", 2) end + if temporary_release_records[resource] == true then + return false + end + temporary_release_records[resource] = true local record = service_records[self] - local removed = record.host:remove_highlight(resource.identity) + local ok, removed = pcall( + record.host.remove_highlight, + record.host, + resource.identity + ) record.transitions:RemoveTemporaryOverlay(resource.identity, resource.window) + if not ok then + error(removed, 0) + end return removed end diff --git a/tests/run.lua b/tests/run.lua index 4139544..4549ab7 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -4466,6 +4466,9 @@ test("Cursor marker overlays the exact cursor byte position", function() truthy(feedback:remove_temporary_overlay(marker)) same(nil, host:highlights()[marker.identity]) same(0, #state:temporary_overlay_identities()) + local operation_count = #host:operations() + falsy(feedback:remove_temporary_overlay(marker)) + same(operation_count, #host:operations()) end) test("Cursor presentation lease restores every prior value", function() @@ -5561,6 +5564,9 @@ test("Acquisition releases direct cursor and presentation resources", function() same(scope.direct_marker.identity, removals[1].identity) same(scope.cursor_marker.identity, removals[2].identity) truthy(restore_index > removals[2].index) + local operation_count = #host:operations() + falsy(scope:release()) + same(operation_count, #host:operations()) end) test("Acquisition cleanup runs from a finally block", function() -- cgit v1.2.3