diff options
| author | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 13:35:24 +0200 |
|---|---|---|
| committer | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 13:35:24 +0200 |
| commit | 1ed91850daac2c4960e0bf316818de20341143fd (patch) | |
| tree | 1843ce3fc5d90c44b006d02410bfd79e98ba056e | |
| parent | d03647b4f814782bb720f14523802856f638caca (diff) | |
Resolve primary sequence eligibility
| -rw-r--r-- | lua/clever_f/sequence_coordinator.lua | 49 | ||||
| -rw-r--r-- | tests/run.lua | 42 |
2 files changed, 90 insertions, 1 deletions
diff --git a/lua/clever_f/sequence_coordinator.lua b/lua/clever_f/sequence_coordinator.lua index d1316e6..334e058 100644 --- a/lua/clever_f/sequence_coordinator.lua +++ b/lua/clever_f/sequence_coordinator.lua @@ -1,4 +1,8 @@ local domain = require("clever_f.domain") +local policy = require("clever_f.policy") +local repeat_resolver_factory = require("clever_f.repeat_resolver") +local sequence_state = require("clever_f.sequence_state") +local state_transitions = require("clever_f.state_transitions") local M = {} local SequenceCoordinator = {} @@ -33,9 +37,36 @@ function SequenceCoordinator.new(options) if type(options) ~= "table" then fail("SequenceCoordinator options must be a table", 2) end + local host = options.host or options + local state = options.state or sequence_state.get() + if not sequence_state.is(state) then + fail("SequenceCoordinator state must be the plugin-global SequenceState", 2) + end + 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 resolver = options.repeat_resolver + or options.resolver + or repeat_resolver_factory.new({ + state = state, + transitions = transitions, + policy = policy_service, + clock = host, + }) + if type(resolver) ~= "table" or type(resolver.decide) ~= "function" then + fail("SequenceCoordinator repeat resolver must provide decide", 2) + end + local coordinator = setmetatable({}, SequenceCoordinator) coordinator_records[coordinator] = { - host = options.host or options, + host = host, + state = state, + transitions = transitions, + policy = policy_service, + repeat_resolver = resolver, } return coordinator end @@ -120,6 +151,21 @@ function SequenceCoordinator:open_enclosing_folds(invocation, fold_state) return opened end +function SequenceCoordinator:decide_primary(invocation) + if type(invocation) ~= "table" + or not domain.ModeContext.is(invocation.context) + or not domain.Position.is(invocation.position) + or not domain.MacroState.is(invocation.macro_state) + then + fail("primary decision requires invocation state", 2) + end + return coordinator_records[self].repeat_resolver:decide( + invocation.context, + invocation.position, + invocation.macro_state + ) +end + function SequenceCoordinator:primary(value) local descriptor = self:validate_primary_descriptor(value) local invocation = self:read_primary_invocation() @@ -128,6 +174,7 @@ function SequenceCoordinator:primary(value) invocation, invocation.fold_state ) + invocation.repeat_decision = self:decide_primary(invocation) return descriptor end diff --git a/tests/run.lua b/tests/run.lua index 1906c6d..e72114d 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -7153,6 +7153,48 @@ test("Primary preflight repeats fold opening until the line is visible", functio same(0, host:read_fold_state().closed_levels) end) +test("Primary coordination asks RepeatResolver for acquisition or repetition", function() + local state, transitions = fresh_sequence_state() + local landing = domain.Position.new(1, 2) + local host = MemoryHost.new({ + buffer_lines = { "aba" }, + cursor = landing, + }) + local coordinator = sequence_coordinator.new({ + host = host, + state = state, + transitions = transitions, + }) + local invocation = coordinator:read_primary_invocation() + + same(repeat_resolver.Decision.ACQUIRE, coordinator:decide_primary(invocation)) + transitions:CommitCommandSuccess("n", landing, true) + same(repeat_resolver.Decision.REPEAT, coordinator:decide_primary(invocation)) + host:set_macro_state("q") + invocation = coordinator:read_primary_invocation() + same(repeat_resolver.Decision.ACQUIRE, coordinator:decide_primary(invocation)) +end) + +test("Primary repetition guard runs after fold preflight", function() + fresh_sequence_state() + local host = MemoryHost.new({ + fold_open_policy = { "all" }, + closed_fold_levels = 2, + }) + local observed_levels + local resolver = { + decide = function() + observed_levels = host:read_fold_state().closed_levels + return repeat_resolver.Decision.ACQUIRE + end, + } + sequence_coordinator.new({ + host = host, + repeat_resolver = resolver, + }):primary("f") + same(0, observed_levels) +end) + for _, item in ipairs(tests) do local ok, failure = xpcall(item.body, debug.traceback) if not ok then |
