diff options
| author | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 13:41:10 +0200 |
|---|---|---|
| committer | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 13:41:10 +0200 |
| commit | 15ed87626e8774ea2a54a82ff3bf9ac399ba4fc0 (patch) | |
| tree | 26bd73d45a47978998f7ff112437f2fbbf98dcbd | |
| parent | 38d3c7480a5db02793dec95250a69b707f6af9d9 (diff) | |
Build repeated movement plan
| -rw-r--r-- | lua/clever_f/sequence_coordinator.lua | 45 | ||||
| -rw-r--r-- | tests/run.lua | 30 |
2 files changed, 75 insertions, 0 deletions
diff --git a/lua/clever_f/sequence_coordinator.lua b/lua/clever_f/sequence_coordinator.lua index 51f74d5..c5ee808 100644 --- a/lua/clever_f/sequence_coordinator.lua +++ b/lua/clever_f/sequence_coordinator.lua @@ -1,6 +1,7 @@ local acquisition_service_factory = require("clever_f.acquisition_service") local domain = require("clever_f.domain") local feedback_service_factory = require("clever_f.feedback_service") +local motion_plan_factory = require("clever_f.motion_plan") local policy = require("clever_f.policy") local repeat_resolver_factory = require("clever_f.repeat_resolver") local sequence_state = require("clever_f.sequence_state") @@ -77,6 +78,14 @@ function SequenceCoordinator.new(options) if type(target_factory) ~= "table" or type(target_factory.build) ~= "function" then fail("SequenceCoordinator target factory must provide build", 2) end + local motion_factory = options.motion_factory + or options.motion_plan_factory + or motion_plan_factory.new({ policy = policy_service }) + if type(motion_factory) ~= "table" + or type(motion_factory.build_for_context) ~= "function" + then + fail("SequenceCoordinator motion factory must build contextual plans", 2) + end local acquisition = options.acquisition or options.acquisition_service or acquisition_service_factory.new({ @@ -86,6 +95,7 @@ function SequenceCoordinator.new(options) policy = policy_service, feedback = feedback, target_factory = target_factory, + motion_factory = motion_factory, }) if type(acquisition) ~= "table" or type(acquisition.acquire) ~= "function" then fail("SequenceCoordinator acquisition service must provide acquire", 2) @@ -100,6 +110,7 @@ function SequenceCoordinator.new(options) repeat_resolver = resolver, feedback = feedback, target_factory = target_factory, + motion_factory = motion_factory, acquisition = acquisition, } return coordinator @@ -293,6 +304,33 @@ function SequenceCoordinator:build_live_target_plan(target, invocation) return target_plan, view, sampled_search.search_scope end +function SequenceCoordinator:build_movement_plan( + target_plan, + effective_descriptor, + invocation, + search_scope +) + if not domain.TargetPlan.is(target_plan) then + fail("movement planning requires a TargetPlan", 2) + end + if type(invocation) ~= "table" or not domain.ModeContext.is(invocation.context) then + fail("movement planning requires invocation state", 2) + end + local record = coordinator_records[self] + local selection = invocation.context.visual and record.host:read_selection() or nil + local motion_plan = record.motion_factory:build_for_context( + target_plan, + effective_descriptor, + invocation.context, + selection, + search_scope + ) + if not domain.ResolvedMotionPlan.is(motion_plan) then + fail("MotionPlanFactory must return a ResolvedMotionPlan", 2) + end + return motion_plan +end + function SequenceCoordinator:stored_primary_resolution( invocation, pressed_descriptor, @@ -322,6 +360,12 @@ function SequenceCoordinator:stored_primary_resolution( stored_target, invocation ) + local motion_plan = self:build_movement_plan( + target_plan, + effective_descriptor, + invocation, + search_scope + ) return { kind = "repeat", invocation = invocation, @@ -330,6 +374,7 @@ function SequenceCoordinator:stored_primary_resolution( stored_descriptor = stored_descriptor, target = stored_target, target_plan = target_plan, + motion_plan = motion_plan, text_view = text_view, search_scope = search_scope, effective_descriptor = effective_descriptor, diff --git a/tests/run.lua b/tests/run.lua index da13a3b..eb82d0b 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -7478,6 +7478,36 @@ test("Primary repetition builds one target plan from live matching policy", func truthy(insensitive.target_plan:matches("A")) end) +test("Primary repetition builds its effective contextual motion plan", function() + local _, transitions = fresh_sequence_state() + local landing = domain.Position.new(1, 3) + transitions:BeginAcquisition("v", "t") + transitions:CommitAcquiredTarget("v", target("a")) + transitions:CommitVisualSuccess("v", landing) + local host = MemoryHost.new({ + buffer_lines = { "ababa" }, + cursor = landing, + mode = "v", + selection = domain.Selection.active( + domain.SelectionKind.CHARACTER, + domain.Position.new(1, 1), + landing, + domain.SelectionOption.EXCLUSIVE + ), + }) + + local resolution = sequence_coordinator.new({ host = host }):primary("F") + + same(resolution.target_plan, resolution.motion_plan.target_plan) + same(domain.Descriptor.TILL_BACKWARD, resolution.effective_descriptor) + same(resolution.effective_descriptor, resolution.motion_plan.descriptor) + same(domain.SearchScope.BUFFER, resolution.motion_plan.search_scope) + same( + domain.EndpointPolicy.VISUAL_EXCLUSIVE, + resolution.motion_plan.endpoint_policy + ) +end) + for _, item in ipairs(tests) do local ok, failure = xpcall(item.body, debug.traceback) if not ok then |
