summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJackson Moore <jacksonmoore@tuta.io>2026-09-04 10:55:34 +0200
committerJackson Moore <jacksonmoore@tuta.io>2026-09-04 10:55:34 +0200
commitbe971036f82c688e0a07fa2e3ff286e414cc5c1d (patch)
tree9c41a5effecc3781055e503ec48481438b39e40a
parent24aacfe6495320018bf73d071a66eda280869d77 (diff)
Create operator dot payloads
-rw-r--r--lua/clever_f/motion_executor.lua51
-rw-r--r--tests/run.lua33
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