summaryrefslogtreecommitdiff
path: root/lua
diff options
context:
space:
mode:
authorJackson Moore <jacksonmoore@tuta.io>2026-09-04 10:57:10 +0200
committerJackson Moore <jacksonmoore@tuta.io>2026-09-04 10:57:10 +0200
commitb7af716a32e548987e3853f5e91198e53d051d27 (patch)
tree659bcd3372f8c015b02c1da9c4f4c32483aaea18 /lua
parentbe971036f82c688e0a07fa2e3ff286e414cc5c1d (diff)
Execute resolved dot payloads
Diffstat (limited to 'lua')
-rw-r--r--lua/clever_f/motion_executor.lua96
-rw-r--r--lua/clever_f/testing/memory_host.lua4
2 files changed, 95 insertions, 5 deletions
diff --git a/lua/clever_f/motion_executor.lua b/lua/clever_f/motion_executor.lua
index 062a57f..79cc2ab 100644
--- a/lua/clever_f/motion_executor.lua
+++ b/lua/clever_f/motion_executor.lua
@@ -52,6 +52,24 @@ function M.create_dot_payload(plan)
return domain.DotPayload.new(plan.descriptor, plan.target_plan.target)
end
+function M.plan_for_dot_payload(plan, payload)
+ if not domain.ResolvedMotionPlan.is(plan) then
+ fail("dot replay plan must be a ResolvedMotionPlan", 2)
+ end
+ if not domain.DotPayload.is(payload) then
+ fail("dot replay payload must be a DotPayload", 2)
+ end
+ if payload.target ~= plan.target_plan.target then
+ fail("dot replay payload target must match its resolved target plan", 2)
+ end
+ return domain.ResolvedMotionPlan.new({
+ target_plan = plan.target_plan,
+ descriptor = payload.descriptor,
+ search_scope = plan.search_scope,
+ endpoint_policy = plan.endpoint_policy,
+ })
+end
+
local function copy_options(options)
local result = {}
for key, value in pairs(options or {}) do
@@ -81,8 +99,10 @@ end
local function require_host(host)
if type(host) ~= "table"
or type(host.read_cursor) ~= "function"
+ or type(host.read_encoding) ~= "function"
or type(host.read_pending_operator) ~= "function"
or type(host.read_selection) ~= "function"
+ or type(host.read_text) ~= "function"
or type(host.apply_cursor) ~= "function"
or type(host.apply_selection) ~= "function"
or type(host.set_operator_inclusive) ~= "function"
@@ -169,7 +189,14 @@ function MotionExecutor.is(value)
return type(value) == "table" and executor_records[value] ~= nil
end
-local function execution_request(view, context, plan, count, first_move)
+local function execution_request(
+ view,
+ context,
+ plan,
+ count,
+ first_move,
+ execution_options
+)
if not text_topology.TextView.is(view) then
fail("motion execution view must be a TextView", 3)
end
@@ -181,12 +208,28 @@ local function execution_request(view, context, plan, count, first_move)
if type(first_move) ~= "boolean" then
fail("motion execution first_move must be a Boolean", 3)
end
+ execution_options = execution_options or {}
+ if type(execution_options) ~= "table" then
+ fail("motion execution options must be a table", 3)
+ end
+ local dot_payload = execution_options.dot_payload
+ if dot_payload ~= nil and not domain.DotPayload.is(dot_payload) then
+ fail("motion execution dot_payload must be a DotPayload", 3)
+ end
+ local register_dot_repeat = execution_options.register_dot_repeat
+ if register_dot_repeat == nil then
+ register_dot_repeat = true
+ elseif type(register_dot_repeat) ~= "boolean" then
+ fail("motion execution register_dot_repeat must be a Boolean", 3)
+ end
return {
view = view,
context = context,
plan = plan,
count = count,
first_move = first_move,
+ dot_payload = dot_payload,
+ register_dot_repeat = register_dot_repeat,
}
end
@@ -218,6 +261,19 @@ local function command_action(
})
end
+local function register_dot_replay(executor, request, payload)
+ local host = executor_records[executor].host
+ host:register_dot_repeat(payload, function(replayed_payload, replay_count)
+ return executor:execute_dot(
+ text_topology.from_host(host),
+ request.context,
+ request.plan,
+ replayed_payload,
+ replay_count
+ )
+ end)
+end
+
local function migrate_command_feedback(executor, request, origin, outcome)
local record = executor_records[executor]
local feedback = record.feedback_service
@@ -280,8 +336,10 @@ function MotionExecutor:_execute_command(request)
)
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)
+ dot_payload = request.dot_payload or M.create_dot_payload(request.plan)
+ if request.register_dot_repeat then
+ register_dot_replay(self, request, dot_payload)
+ end
end
return command_action(
host,
@@ -317,14 +375,42 @@ function MotionExecutor:_execute_visual(request)
return domain.ActionOutcome.from_search(outcome, request.plan.descriptor)
end
-function MotionExecutor:execute(view, context, plan, count, first_move)
- local request = execution_request(view, context, plan, count, first_move)
+function MotionExecutor:execute(
+ view,
+ context,
+ plan,
+ count,
+ first_move,
+ execution_options
+)
+ local request = execution_request(
+ view,
+ context,
+ plan,
+ count,
+ first_move,
+ execution_options
+ )
if M.execution_path(request.context) == M.ExecutionPath.VISUAL then
return self:_execute_visual(request)
end
return self:_execute_command(request)
end
+function MotionExecutor:execute_dot(view, context, plan, payload, count)
+ return self:execute(
+ view,
+ context,
+ M.plan_for_dot_payload(plan, payload),
+ count,
+ false,
+ {
+ dot_payload = payload,
+ register_dot_repeat = false,
+ }
+ )
+end
+
function M.new(options, dependencies)
return MotionExecutor.new(options, dependencies)
end
diff --git a/lua/clever_f/testing/memory_host.lua b/lua/clever_f/testing/memory_host.lua
index 8b473ad..42dab71 100644
--- a/lua/clever_f/testing/memory_host.lua
+++ b/lua/clever_f/testing/memory_host.lua
@@ -952,6 +952,8 @@ function MemoryHost:register_dot_repeat(payload, callback)
self._dot_repeat = {
payload = payload,
callback = callback,
+ operator = self._pending_operator,
+ mode = self._mode,
}
self:_record("register_dot_repeat", { payload = payload:to_table() })
return payload
@@ -965,6 +967,8 @@ function MemoryHost:replay_dot(count)
if self._dot_repeat == nil or self._dot_repeat.callback == nil then
error("dot repeat is not executable", 2)
end
+ self._pending_operator = self._dot_repeat.operator
+ self._mode = self._dot_repeat.mode
return self._dot_repeat.callback(self._dot_repeat.payload, domain.Count.new(count))
end