diff options
| -rw-r--r-- | lua/clever_f/acquisition_service.lua | 43 | ||||
| -rw-r--r-- | tests/run.lua | 38 |
2 files changed, 78 insertions, 3 deletions
diff --git a/lua/clever_f/acquisition_service.lua b/lua/clever_f/acquisition_service.lua index d632748..77d08ec 100644 --- a/lua/clever_f/acquisition_service.lua +++ b/lua/clever_f/acquisition_service.lua @@ -1,6 +1,7 @@ local domain = require("clever_f.domain") local direct_preview_planner = require("clever_f.direct_preview_planner") local feedback_service = require("clever_f.feedback_service") +local motion_plan_factory = require("clever_f.motion_plan") local policy = require("clever_f.policy") local sequence_state = require("clever_f.sequence_state") local state_transitions = require("clever_f.state_transitions") @@ -137,14 +138,18 @@ function AcquisitionResult.new(request, options) if target_plan ~= nil and not domain.TargetPlan.is(target_plan) then fail("acquisition result target_plan must be a TargetPlan", 2) end + local motion_plan = options.motion_plan + if motion_plan ~= nil and not domain.ResolvedMotionPlan.is(motion_plan) then + fail("acquisition result motion_plan must be a ResolvedMotionPlan", 2) + end local result = setmetatable({}, result_metatable) result_records[result] = { request = request, outcome = outcome, target = target, target_plan = target_plan, - motion_plan = options.motion_plan, - resolved_motion_plan = options.motion_plan, + motion_plan = motion_plan, + resolved_motion_plan = motion_plan, previous_input_trigger = options.previous_input_trigger, previous_target_source = options.previous_target_source, cached_target = options.cached_target, @@ -198,6 +203,7 @@ function TemporaryResourceScope.new(request, feedback) missing_previous_input = false, text_view = nil, target_plan = nil, + motion_plan = nil, outcome = nil, } return scope @@ -270,6 +276,13 @@ function TemporaryResourceScope:set_target_plan(target_plan) return set_scope_resource(self, "target_plan", target_plan) end +function TemporaryResourceScope:set_motion_plan(motion_plan) + if not domain.ResolvedMotionPlan.is(motion_plan) then + fail("temporary resource scope motion plan must be a ResolvedMotionPlan", 2) + end + return set_scope_resource(self, "motion_plan", motion_plan) +end + function TemporaryResourceScope:set_outcome(outcome) if not domain.ActionOutcome.is(outcome) then fail("temporary resource scope outcome must be an ActionOutcome", 2) @@ -328,6 +341,14 @@ local function require_target_factory(factory, policy_service) return factory end +local function require_motion_factory(factory, policy_service) + factory = factory or motion_plan_factory.new({ policy = policy_service }) + if type(factory) ~= "table" or type(factory.build_for_context) ~= "function" then + fail("AcquisitionService motion factory must build contextual plans", 3) + end + return factory +end + local function require_feedback(feedback, host, policy_service, transitions) feedback = feedback or feedback_service.new({ host = host, @@ -397,6 +418,10 @@ function AcquisitionService.new(options, dependencies) options.target_factory or options.target_plan_factory, policy_service ), + motion_factory = require_motion_factory( + options.motion_factory or options.motion_plan_factory, + policy_service + ), window = options.window or options.current_window, last_scope = nil, started_scope_count = 0, @@ -714,19 +739,31 @@ function AcquisitionService:acquire(descriptor, context, position, count, macro_ ) local view = scope.text_view or scope:set_text_view(text_topology.from_host(record.host)) + local search_scope = record.policy:sample_search().search_scope local target_plan = scope:set_target_plan(record.target_factory:build( target, nil, { text_view = view, origin = request.position, - search_scope = record.policy:sample_search().search_scope, + search_scope = search_scope, effective_encoding = view.effective_encoding, } )) + local selection = request.context.visual and record.host:read_selection() or nil + local motion_plan = scope:set_motion_plan( + record.motion_factory:build_for_context( + target_plan, + request.descriptor, + request.context, + selection, + search_scope + ) + ) return AcquisitionResult.new(request, { target = target, target_plan = target_plan, + motion_plan = motion_plan, acquisition_time_ms = acquisition_time_ms, previous_input_trigger = trigger, previous_target_source = scope.previous_target_source, diff --git a/tests/run.lua b/tests/run.lua index 959de66..1249316 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -5378,6 +5378,44 @@ test("Acquisition builds one target plan from live matching policy", function() same(result.target_plan, service:last_temporary_scope().target_plan) end) +test("Acquisition builds a fresh initiating motion plan", function() + fresh_sequence_state() + local origin = domain.Position.new(1, 1) + local host = MemoryHost.new({ + buffer_lines = { "aha" }, + cursor = origin, + selection = { + active = true, + kind = "character", + anchor = origin, + focus = origin, + option = "exclusive", + }, + configuration = { + mark_cursor = false, + search_current_line_only = true, + }, + input_packets = { { kind = "text", text = "h" } }, + }) + local service = acquisition_service.new(host) + + local first = service:acquire("T", "v", origin, nil, nil) + truthy(domain.ResolvedMotionPlan.is(first.motion_plan)) + same(first.motion_plan, first.resolved_motion_plan) + same(first.motion_plan, service:last_temporary_scope().motion_plan) + same(first.target_plan, first.motion_plan.target_plan) + same(domain.Descriptor.TILL_BACKWARD, first.motion_plan.descriptor) + same(domain.SearchScope.CURRENT_LINE, first.motion_plan.search_scope) + same( + domain.EndpointPolicy.VISUAL_EXCLUSIVE, + first.motion_plan.endpoint_policy + ) + + host:push_input({ kind = "text", text = "h" }) + local second = service:acquire("T", "v", origin, nil, nil) + falsy(first.motion_plan == second.motion_plan) +end) + for _, item in ipairs(tests) do local ok, failure = xpcall(item.body, debug.traceback) if not ok then |
