summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lua/clever_f/acquisition_service.lua137
-rw-r--r--tests/run.lua30
2 files changed, 167 insertions, 0 deletions
diff --git a/lua/clever_f/acquisition_service.lua b/lua/clever_f/acquisition_service.lua
new file mode 100644
index 0000000..cdbcd81
--- /dev/null
+++ b/lua/clever_f/acquisition_service.lua
@@ -0,0 +1,137 @@
+local domain = require("clever_f.domain")
+
+local M = {}
+local AcquisitionRequest = {}
+local AcquisitionService = {}
+AcquisitionService.__index = AcquisitionService
+M.AcquisitionRequest = AcquisitionRequest
+M.AcquisitionService = AcquisitionService
+
+local request_records = setmetatable({}, { __mode = "k" })
+local service_records = setmetatable({}, { __mode = "k" })
+
+local function fail(message, level)
+ error(message, (level or 1) + 1)
+end
+
+local function normalize_macro_state(value)
+ if type(value) == "table" and not domain.MacroState.is(value) then
+ value = value.register
+ end
+ return domain.MacroState.new(value)
+end
+
+local request_metatable = {
+ __index = function(request, key)
+ local method = AcquisitionRequest[key]
+ if method ~= nil then
+ return method
+ end
+ return request_records[request][key]
+ end,
+ __newindex = function()
+ fail("AcquisitionRequest values are immutable", 2)
+ end,
+ __tostring = function(request)
+ return "acquisition-request:" .. request_records[request].descriptor.value
+ end,
+ __metatable = "clever_f.acquisition_service.AcquisitionRequest",
+}
+
+function AcquisitionRequest.new(descriptor, context, position, count, macro_state)
+ if AcquisitionRequest.is(descriptor) then
+ return descriptor
+ end
+ if type(descriptor) == "table" and not domain.Descriptor.is(descriptor) then
+ local options = descriptor
+ descriptor = options.descriptor
+ context = options.context
+ position = options.position or options.origin
+ count = options.count
+ macro_state = options.macro_state
+ end
+
+ local request = setmetatable({}, request_metatable)
+ request_records[request] = {
+ descriptor = domain.Descriptor.from_string(descriptor),
+ context = domain.ModeContext.from_full_mode(context),
+ position = domain.Position.coerce(position),
+ count = domain.Count.new(count),
+ macro_state = normalize_macro_state(macro_state),
+ }
+ return request
+end
+
+function AcquisitionRequest.is(value)
+ return type(value) == "table" and request_records[value] ~= nil
+end
+
+function AcquisitionRequest:to_table()
+ return {
+ descriptor = self.descriptor.value,
+ context = self.context.key,
+ position = self.position:to_table(),
+ count = self.count.value,
+ macro_register = self.macro_state.register,
+ }
+end
+
+local function normalize_options(options, dependencies)
+ if AcquisitionService.is(options) and dependencies == nil then
+ return options
+ end
+ if type(options) ~= "table" then
+ fail("AcquisitionService options must be a table", 3)
+ end
+ if options.host ~= nil then
+ if dependencies ~= nil then
+ fail("AcquisitionService dependencies must be part of its options", 3)
+ end
+ return options
+ end
+ local result = {}
+ for key, value in pairs(dependencies or {}) do
+ result[key] = value
+ end
+ result.host = options
+ return result
+end
+
+function AcquisitionService.new(options, dependencies)
+ options = normalize_options(options, dependencies)
+ if AcquisitionService.is(options) then
+ return options
+ end
+ if type(options.host) ~= "table" then
+ fail("AcquisitionService host must be a table", 2)
+ end
+ local service = setmetatable({}, AcquisitionService)
+ service_records[service] = {
+ host = options.host,
+ }
+ return service
+end
+
+function AcquisitionService.is(value)
+ return type(value) == "table" and service_records[value] ~= nil
+end
+
+function AcquisitionService:request(descriptor, context, position, count, macro_state)
+ return AcquisitionRequest.new(descriptor, context, position, count, macro_state)
+end
+
+function AcquisitionService:acquire(descriptor, context, position, count, macro_state)
+ return self:request(descriptor, context, position, count, macro_state)
+end
+
+function M.new(options, dependencies)
+ return AcquisitionService.new(options, dependencies)
+end
+
+setmetatable(M, {
+ __call = function(_, options, dependencies)
+ return AcquisitionService.new(options, dependencies)
+ end,
+})
+
+return M
diff --git a/tests/run.lua b/tests/run.lua
index 61ab1ab..d2c7f5e 100644
--- a/tests/run.lua
+++ b/tests/run.lua
@@ -7,6 +7,7 @@ package.path = table.concat({
}, ";")
local domain = require("clever_f.domain")
+local acquisition_service = require("clever_f.acquisition_service")
local capabilities = require("clever_f.capabilities")
local destination_engine = require("clever_f.destination_engine")
local direct_preview_planner = require("clever_f.direct_preview_planner")
@@ -4865,6 +4866,35 @@ test("Feedback creates one ordinary direct marker set", function()
)
end)
+test("Acquisition accepts complete initiating action inputs", function()
+ local service = acquisition_service.new(MemoryHost.new())
+ truthy(acquisition_service.AcquisitionService.is(service))
+
+ local request = service:request(
+ "T",
+ domain.ModeContext.from_full_mode("nov"),
+ domain.Position.new(3, 5),
+ 2,
+ domain.MacroState.new("q")
+ )
+ truthy(acquisition_service.AcquisitionRequest.is(request))
+ same(domain.Descriptor.TILL_BACKWARD, request.descriptor)
+ same(domain.ModeContext.from_full_mode("no"), request.context)
+ same(domain.Position.new(3, 5), request.position)
+ same(2, request.count.value)
+ truthy(request.macro_state.executing)
+ same("q", request.macro_state.register)
+
+ local from_table = acquisition_service.AcquisitionRequest.new({
+ descriptor = "f",
+ context = "n",
+ origin = { line = 1, byte_column = 1 },
+ macro_state = { register = nil },
+ })
+ same(domain.Count.ONE, from_table.count)
+ falsy(from_table.macro_state.executing)
+end)
+
for _, item in ipairs(tests) do
local ok, failure = xpcall(item.body, debug.traceback)
if not ok then