summaryrefslogtreecommitdiff
path: root/lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua')
-rw-r--r--lua/clever_f/acquisition_service.lua90
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)