diff options
| author | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 10:55:34 +0200 |
|---|---|---|
| committer | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 10:55:34 +0200 |
| commit | be971036f82c688e0a07fa2e3ff286e414cc5c1d (patch) | |
| tree | 9c41a5effecc3781055e503ec48481438b39e40a | |
| parent | 24aacfe6495320018bf73d071a66eda280869d77 (diff) | |
Create operator dot payloads
| -rw-r--r-- | lua/clever_f/motion_executor.lua | 51 | ||||
| -rw-r--r-- | tests/run.lua | 33 |
2 files changed, 82 insertions, 2 deletions
diff --git a/lua/clever_f/motion_executor.lua b/lua/clever_f/motion_executor.lua index 4e972cd..062a57f 100644 --- a/lua/clever_f/motion_executor.lua +++ b/lua/clever_f/motion_executor.lua @@ -45,6 +45,13 @@ function M.command_moved_forward(descriptor, origin, destination) return M.moved_forward(origin, destination) end +function M.create_dot_payload(plan) + if not domain.ResolvedMotionPlan.is(plan) then + fail("dot payload plan must be a ResolvedMotionPlan", 2) + end + return domain.DotPayload.new(plan.descriptor, plan.target_plan.target) +end + local function copy_options(options) local result = {} for key, value in pairs(options or {}) do @@ -74,10 +81,12 @@ end local function require_host(host) if type(host) ~= "table" or type(host.read_cursor) ~= "function" + or type(host.read_pending_operator) ~= "function" or type(host.read_selection) ~= "function" or type(host.apply_cursor) ~= "function" or type(host.apply_selection) ~= "function" or type(host.set_operator_inclusive) ~= "function" + or type(host.register_dot_repeat) ~= "function" then fail("MotionExecutor host must provide movement state", 3) end @@ -191,6 +200,24 @@ local function calculate(executor, request, origin) ) end +local function command_action( + host, + outcome, + descriptor, + dot_payload, + use_current_position +) + return domain.ActionOutcome.new({ + kind = outcome.complete + and domain.ActionKind.MOVEMENT + or domain.ActionKind.FAILED_SEARCH, + position = use_current_position and host:read_cursor() or outcome.endpoint, + search_outcome = outcome, + effective_descriptor = descriptor, + dot_payload = dot_payload, + }) +end + local function migrate_command_feedback(executor, request, origin, outcome) local record = executor_records[executor] local feedback = record.feedback_service @@ -215,6 +242,9 @@ end function MotionExecutor:_execute_command(request) local host = executor_records[self].host local origin = host:read_cursor() + local pending_operator = request.context.operator + and host:read_pending_operator() + or nil local outcome = calculate(self, request, origin) if outcome.successful_steps > 0 then if request.context.operator @@ -229,7 +259,13 @@ function MotionExecutor:_execute_command(request) }) end if not outcome.complete then - return domain.ActionOutcome.from_search(outcome, request.plan.descriptor) + return command_action( + host, + outcome, + request.plan.descriptor, + nil, + pending_operator ~= nil and pending_operator ~= "" + ) end request.moved_forward = M.command_moved_forward( request.plan.descriptor, @@ -242,7 +278,18 @@ function MotionExecutor:_execute_command(request) outcome.endpoint, request.moved_forward ) - return domain.ActionOutcome.from_search(outcome, request.plan.descriptor) + local dot_payload + if pending_operator ~= nil and pending_operator ~= "" then + dot_payload = M.create_dot_payload(request.plan) + host:register_dot_repeat(dot_payload) + end + return command_action( + host, + outcome, + request.plan.descriptor, + dot_payload, + pending_operator ~= nil and pending_operator ~= "" + ) end function MotionExecutor:_execute_visual(request) diff --git a/tests/run.lua b/tests/run.lua index 1c07824..5dfe425 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -3606,6 +3606,39 @@ test("Operator execution preserves FIND and TILL endpoint conventions", function end end) +test("Operator success creates a resolved dot payload", function() + fresh_sequence_state() + local target_value = target("e") + local host = MemoryHost.new({ + buffer_lines = { "hoge fuge piye poye" }, + cursor = { line = 1, byte_column = 1 }, + mode = "no", + pending_operator = "delete", + }) + local plan = motion_plan.build( + target_plan.build(target_value, matching_policy()), + "f" + ) + local direct_payload = motion_executor.create_dot_payload(plan) + same(domain.Descriptor.FIND_FORWARD, direct_payload.descriptor) + same(target_value, direct_payload.target) + + local outcome = motion_executor.new(host):execute( + text_topology.from_host(host), + "no", + plan, + 1, + true + ) + same(domain.ActionKind.MOVEMENT, outcome.kind) + same(domain.Position.new(1, 1), outcome.position) + same(domain.Position.new(1, 4), outcome.search_outcome.endpoint) + truthy(domain.DotPayload.is(outcome.dot_payload)) + same(domain.Descriptor.FIND_FORWARD, outcome.dot_payload.descriptor) + same(target_value, outcome.dot_payload.target) + same(outcome.dot_payload, host:dot_repeat_payload()) +end) + for _, item in ipairs(tests) do local ok, failure = xpcall(item.body, debug.traceback) if not ok then |
