summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJackson Moore <jacksonmoore@tuta.io>2026-09-04 13:40:39 +0200
committerJackson Moore <jacksonmoore@tuta.io>2026-09-04 13:40:39 +0200
commit38d3c7480a5db02793dec95250a69b707f6af9d9 (patch)
tree6c6df6d097b90ed4f27457689afd8ee41e8b11ea
parentae1c33d38ae1d7ae0a393298d1a92b711ab4d7d7 (diff)
Build live repeated target plan
-rw-r--r--lua/clever_f/sequence_coordinator.lua39
-rw-r--r--tests/run.lua36
2 files changed, 75 insertions, 0 deletions
diff --git a/lua/clever_f/sequence_coordinator.lua b/lua/clever_f/sequence_coordinator.lua
index 0be5494..51f74d5 100644
--- a/lua/clever_f/sequence_coordinator.lua
+++ b/lua/clever_f/sequence_coordinator.lua
@@ -5,6 +5,8 @@ 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 target_plan_factory = require("clever_f.target_plan")
+local text_topology = require("clever_f.text_topology")
local M = {}
local SequenceCoordinator = {}
@@ -69,6 +71,12 @@ function SequenceCoordinator.new(options)
transitions = transitions,
policy = policy_service,
})
+ local target_factory = options.target_factory
+ or options.target_plan_factory
+ or target_plan_factory.new({ policy = policy_service })
+ if type(target_factory) ~= "table" or type(target_factory.build) ~= "function" then
+ fail("SequenceCoordinator target factory must provide build", 2)
+ end
local acquisition = options.acquisition
or options.acquisition_service
or acquisition_service_factory.new({
@@ -77,6 +85,7 @@ function SequenceCoordinator.new(options)
transitions = transitions,
policy = policy_service,
feedback = feedback,
+ target_factory = target_factory,
})
if type(acquisition) ~= "table" or type(acquisition.acquire) ~= "function" then
fail("SequenceCoordinator acquisition service must provide acquire", 2)
@@ -90,6 +99,7 @@ function SequenceCoordinator.new(options)
policy = policy_service,
repeat_resolver = resolver,
feedback = feedback,
+ target_factory = target_factory,
acquisition = acquisition,
}
return coordinator
@@ -261,6 +271,28 @@ function SequenceCoordinator:evaluate_repeat_timeout(invocation)
}
end
+function SequenceCoordinator:build_live_target_plan(target, invocation)
+ if not domain.TargetValue.is(target) then
+ fail("primary target planning requires a TargetValue", 2)
+ end
+ if type(invocation) ~= "table" or not domain.Position.is(invocation.position) then
+ fail("primary target planning requires invocation state", 2)
+ end
+ local record = coordinator_records[self]
+ local view = text_topology.from_host(record.host)
+ local sampled_search = record.policy:sample_search()
+ local target_plan = record.target_factory:build(target, nil, {
+ text_view = view,
+ origin = invocation.position,
+ search_scope = sampled_search.search_scope,
+ effective_encoding = view.effective_encoding,
+ })
+ if not domain.TargetPlan.is(target_plan) then
+ fail("TargetPlanFactory must return a TargetPlan", 2)
+ end
+ return target_plan, view, sampled_search.search_scope
+end
+
function SequenceCoordinator:stored_primary_resolution(
invocation,
pressed_descriptor,
@@ -286,6 +318,10 @@ function SequenceCoordinator:stored_primary_resolution(
if effective_descriptor.family ~= stored_descriptor.family then
fail("primary repetition must preserve the stored motion family", 2)
end
+ local target_plan, text_view, search_scope = self:build_live_target_plan(
+ stored_target,
+ invocation
+ )
return {
kind = "repeat",
invocation = invocation,
@@ -293,6 +329,9 @@ function SequenceCoordinator:stored_primary_resolution(
timeout = timeout,
stored_descriptor = stored_descriptor,
target = stored_target,
+ target_plan = target_plan,
+ text_view = text_view,
+ search_scope = search_scope,
effective_descriptor = effective_descriptor,
}
end
diff --git a/tests/run.lua b/tests/run.lua
index 3537f0a..da13a3b 100644
--- a/tests/run.lua
+++ b/tests/run.lua
@@ -7442,6 +7442,42 @@ test("Primary repetition preserves the stored motion family", function()
end
end)
+test("Primary repetition builds one target plan from live matching policy", function()
+ local _, transitions = fresh_sequence_state()
+ local landing = domain.Position.new(1, 2)
+ local stored_target = target("a")
+ transitions:BeginAcquisition("n", "f")
+ transitions:CommitAcquiredTarget("n", stored_target)
+ transitions:CommitCommandSuccess("n", landing, true)
+ local host = MemoryHost.new({
+ buffer_lines = { "xAx" },
+ cursor = landing,
+ configuration = { ignore_case = false },
+ })
+ local underlying = target_plan.new({ policy = policy.new(host) })
+ local build_count = 0
+ local factory = {
+ build = function(_, ...)
+ build_count = build_count + 1
+ return underlying:build(...)
+ end,
+ }
+ local coordinator = sequence_coordinator.new({
+ host = host,
+ target_factory = factory,
+ })
+
+ local sensitive = coordinator:primary("f")
+ same(1, build_count)
+ same(domain.CaseMode.SENSITIVE, sensitive.target_plan.case_mode)
+ falsy(sensitive.target_plan:matches("A"))
+ host:set_configuration("ignore_case", true)
+ local insensitive = coordinator:primary("f")
+ same(2, build_count)
+ same(domain.CaseMode.INSENSITIVE, insensitive.target_plan.case_mode)
+ truthy(insensitive.target_plan:matches("A"))
+end)
+
for _, item in ipairs(tests) do
local ok, failure = xpcall(item.body, debug.traceback)
if not ok then