diff options
| -rw-r--r-- | lua/clever_f/acquisition_service.lua | 137 | ||||
| -rw-r--r-- | tests/run.lua | 30 |
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 |
