diff options
| author | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 14:16:18 +0200 |
|---|---|---|
| committer | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 14:16:18 +0200 |
| commit | 401a2851c643c5051c75310704cfdc94d6b0a072 (patch) | |
| tree | fa99960f4b41608f8cc5d0761f6453973fb1f759 | |
| parent | e192a496e9264249ff60b625453a3f1227af2221 (diff) | |
Compose global state activation
| -rw-r--r-- | lua/clever_f/composition_root.lua | 130 | ||||
| -rw-r--r-- | tests/run.lua | 15 |
2 files changed, 145 insertions, 0 deletions
diff --git a/lua/clever_f/composition_root.lua b/lua/clever_f/composition_root.lua new file mode 100644 index 0000000..e04c177 --- /dev/null +++ b/lua/clever_f/composition_root.lua @@ -0,0 +1,130 @@ +local action_facade = require("clever_f.action_facade") +local capabilities = require("clever_f.capabilities") +local feedback_service = require("clever_f.feedback_service") +local policy = require("clever_f.policy") +local sequence_coordinator = require("clever_f.sequence_coordinator") +local sequence_state = require("clever_f.sequence_state") +local state_transitions = require("clever_f.state_transitions") + +local M = {} +local CompositionRoot = {} +CompositionRoot.__index = CompositionRoot +M.CompositionRoot = CompositionRoot + +local root_records = setmetatable({}, { __mode = "k" }) + +local function fail(message, level) + error(message, (level or 1) + 1) +end + +local function normalize_options(options) + if type(options) ~= "table" then + fail("CompositionRoot options must be a table", 3) + end + if options.host == nil then + return { host = options } + end + return options +end + +function CompositionRoot.new(options) + if CompositionRoot.is(options) then + return options + end + options = normalize_options(options) + local host = capabilities.assert_implements(options.host) + local state = sequence_state.new() + local transitions = options.transitions + or options.state_transitions + or state_transitions.new(state) + local policy_service = options.policy + or options.policy_service + or policy.new(host) + local feedback = options.feedback + or options.feedback_service + or feedback_service.new({ + host = host, + state = state, + transitions = transitions, + policy = policy_service, + }) + local coordinator = options.coordinator + or options.sequence_coordinator + or sequence_coordinator.new({ + host = host, + state = state, + transitions = transitions, + policy = policy_service, + feedback = feedback, + }) + local facade = options.facade + or options.action_facade + or action_facade.new({ coordinator = coordinator }) + + local root = setmetatable({}, CompositionRoot) + root_records[root] = { + host = host, + state = state, + transitions = transitions, + policy = policy_service, + feedback = feedback, + coordinator = coordinator, + facade = facade, + activation = nil, + } + return root +end + +function CompositionRoot.is(value) + return type(value) == "table" and root_records[value] ~= nil +end + +function CompositionRoot:activate() + local record = root_records[self] + if record.activation == nil then + record.activation = { + state = record.state, + } + end + return record.activation +end + +function CompositionRoot:host() + return root_records[self].host +end + +function CompositionRoot:state() + return root_records[self].state +end + +function CompositionRoot:transitions() + return root_records[self].transitions +end + +function CompositionRoot:policy() + return root_records[self].policy +end + +function CompositionRoot:feedback() + return root_records[self].feedback +end + +function CompositionRoot:coordinator() + return root_records[self].coordinator +end + +function CompositionRoot:facade() + return root_records[self].facade +end + +function M.new(options) + return CompositionRoot.new(options) +end + +setmetatable(M, { + __call = function(_, options) + return CompositionRoot.new(options) + end, +}) + +return M diff --git a/tests/run.lua b/tests/run.lua index 9ccb079..8520d5a 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -10,6 +10,7 @@ local domain = require("clever_f.domain") local action_facade = require("clever_f.action_facade") local acquisition_service = require("clever_f.acquisition_service") local capabilities = require("clever_f.capabilities") +local composition_root = require("clever_f.composition_root") local destination_engine = require("clever_f.destination_engine") local direct_preview_planner = require("clever_f.direct_preview_planner") local feedback_service = require("clever_f.feedback_service") @@ -7071,6 +7072,20 @@ test("Eager cleanup preserves history and finalizer registrations", function() same(nil, state.highlight_timer) end) +test("Core activation owns the plugin-global sequence state", function() + local state = fresh_sequence_state() + local host = MemoryHost.new() + local root = composition_root.new({ host = host }) + local activation = root:activate() + + truthy(composition_root.CompositionRoot.is(root)) + same(state, activation.state) + same(state, root:state()) + same(state, root:transitions():state()) + same(root:coordinator(), root:facade():coordinator()) + same(activation, root:activate()) +end) + test("Primary coordination accepts only the four motion descriptors", function() local coordinator = sequence_coordinator.new({ host = MemoryHost.new() }) for _, value in ipairs({ "f", "F", "t", "T" }) do |
