diff options
| author | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 12:03:36 +0200 |
|---|---|---|
| committer | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 12:03:36 +0200 |
| commit | 44b0624d22ea62983cae7737a0b1d1bd0280c5e5 (patch) | |
| tree | 3df7b1cb63067f8ce4fb5249226dbe478ab03096 /lua/clever_f | |
| parent | dbbbe30b637fccfacb15cd7317d4af87b658cfb5 (diff) | |
Return Escape acquisition outcomes
Diffstat (limited to 'lua/clever_f')
| -rw-r--r-- | lua/clever_f/acquisition_service.lua | 90 |
1 files changed, 89 insertions, 1 deletions
diff --git a/lua/clever_f/acquisition_service.lua b/lua/clever_f/acquisition_service.lua index b1e74bb..d341414 100644 --- a/lua/clever_f/acquisition_service.lua +++ b/lua/clever_f/acquisition_service.lua @@ -8,10 +8,12 @@ local text_topology = require("clever_f.text_topology") local M = {} local AcquisitionRequest = {} +local AcquisitionResult = {} local AcquisitionService = {} local TemporaryResourceScope = {} AcquisitionService.__index = AcquisitionService M.AcquisitionRequest = AcquisitionRequest +M.AcquisitionResult = AcquisitionResult M.AcquisitionService = AcquisitionService M.TemporaryResourceScope = TemporaryResourceScope M.RepeatedDirection = { @@ -20,6 +22,7 @@ M.RepeatedDirection = { M.PROMPT = "clever-f: " local request_records = setmetatable({}, { __mode = "k" }) +local result_records = setmetatable({}, { __mode = "k" }) local service_records = setmetatable({}, { __mode = "k" }) local scope_records = setmetatable({}, { __mode = "k" }) @@ -91,6 +94,64 @@ function AcquisitionRequest:to_table() } end +local result_metatable = { + __index = function(result, key) + local method = AcquisitionResult[key] + if method ~= nil then + return method + end + return result_records[result][key] + end, + __newindex = function() + fail("AcquisitionResult values are immutable", 2) + end, + __tostring = function(result) + local outcome = result_records[result].outcome + return outcome and tostring(outcome) or "acquisition:resolved" + end, + __metatable = "clever_f.acquisition_service.AcquisitionResult", +} + +function AcquisitionResult.new(request, options) + if AcquisitionResult.is(request) and options == nil then + return request + end + if not AcquisitionRequest.is(request) then + fail("acquisition result requires an AcquisitionRequest", 2) + end + options = options or {} + if type(options) ~= "table" then + fail("acquisition result options must be a table", 2) + end + local outcome = options.outcome + if outcome ~= nil and not domain.ActionOutcome.is(outcome) then + fail("acquisition result outcome must be an ActionOutcome", 2) + end + local target = options.target + if target ~= nil and not domain.TargetValue.is(target) then + fail("acquisition result target must be a TargetValue", 2) + end + local result = setmetatable({}, result_metatable) + result_records[result] = { + request = request, + outcome = outcome, + target = target, + target_plan = options.target_plan, + motion_plan = options.motion_plan, + resolved_motion_plan = options.motion_plan, + completed = outcome ~= nil or target ~= nil, + } + return result +end + +function AcquisitionResult.is(value) + return type(value) == "table" and result_records[value] ~= nil +end + +function AcquisitionResult:has_outcome() + return self.outcome ~= nil +end + local scope_metatable = { __index = function(scope, key) local method = TemporaryResourceScope[key] @@ -119,6 +180,7 @@ function TemporaryResourceScope.new(request, feedback) cursor_presentation_lease = nil, input_packet = nil, acquired_target = nil, + outcome = nil, } return scope end @@ -156,6 +218,13 @@ function TemporaryResourceScope:set_acquired_target(target) return set_scope_resource(self, "acquired_target", target) end +function TemporaryResourceScope:set_outcome(outcome) + if not domain.ActionOutcome.is(outcome) then + fail("temporary resource scope outcome must be an ActionOutcome", 2) + end + return set_scope_resource(self, "outcome", outcome) +end + function TemporaryResourceScope:release() local record = scope_records[self] if record == nil then @@ -372,6 +441,20 @@ function M.normalize_ordinary_input(packet) return domain.TargetValue.character(character, first_code) end +function M.is_escape(packet) + packet = domain.InputPacket.from_table(packet) + if packet.kind == domain.InputPacketKind.SPECIAL_KEY + and (packet.name == "Escape" or packet.name == "Esc") + then + return true + end + if packet.kind == domain.InputPacketKind.TEXT then + return packet.text == string.char(27) + end + local bytes = packet:bytes() + return bytes ~= nil and #bytes == 1 and bytes[1] == 27 +end + function M.is_terminal_artifact(packet) packet = domain.InputPacket.from_table(packet) if packet.kind ~= domain.InputPacketKind.RAW_BYTES then @@ -449,12 +532,17 @@ function AcquisitionService:acquire(descriptor, context, position, count, macro_ end record.transitions:BeginAcquisition(request.context, request.descriptor) local packet = scope:set_input_packet(read_input_packet(record.host)) + if M.is_escape(packet) then + local outcome = scope:set_outcome(domain.ActionOutcome.escape(request.position)) + scope:release() + return AcquisitionResult.new(request, { outcome = outcome }) + end if packet.kind == domain.InputPacketKind.TEXT or packet.kind == domain.InputPacketKind.RAW_BYTES then scope:set_acquired_target(M.normalize_ordinary_input(packet)) end - return request + return AcquisitionResult.new(request, { target = scope.acquired_target }) end function M.new(options, dependencies) |
