summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lua/clever_f/feedback_service.lua35
-rw-r--r--tests/run.lua37
2 files changed, 70 insertions, 2 deletions
diff --git a/lua/clever_f/feedback_service.lua b/lua/clever_f/feedback_service.lua
index e553cd7..9d7ef28 100644
--- a/lua/clever_f/feedback_service.lua
+++ b/lua/clever_f/feedback_service.lua
@@ -622,10 +622,41 @@ function M.command_migration_reason(request)
return nil
end
+local function has_target_overlay(record, window)
+ for _, resource in ipairs(record.state.target_overlays) do
+ if resource.window == window then
+ return true
+ end
+ end
+ return false
+end
+
function FeedbackService:migrate_command(request)
+ local reason = M.command_migration_reason(request)
+ local record = service_records[self]
+ local window = request.window or record.host:read_window()
+ if reason == nil or not has_target_overlay(record, window) then
+ return {
+ migrated = false,
+ reason = reason,
+ }
+ end
+
+ local plan = request.resolved_motion_plan or request.plan
+ if not domain.ResolvedMotionPlan.is(plan) then
+ fail("command feedback migration requires a ResolvedMotionPlan", 2)
+ end
+ local overlay = self:request_persistent({
+ context = request.context,
+ anchor = request.destination,
+ target_plan = plan.target_plan,
+ motion_plan = plan,
+ window = window,
+ })
return {
- migrated = false,
- reason = M.command_migration_reason(request),
+ migrated = true,
+ reason = reason,
+ overlay = overlay,
}
end
diff --git a/tests/run.lua b/tests/run.lua
index e73dda7..0663c2e 100644
--- a/tests/run.lua
+++ b/tests/run.lua
@@ -6399,6 +6399,43 @@ test("Command feedback migration compares origin and destination lines", functio
)
end)
+test("Cross-line command movement rebuilds feedback at destination", function()
+ local state, transitions = fresh_sequence_state()
+ local host = MemoryHost.new({ buffer_lines = { "a", "xax" } })
+ local feedback = feedback_service.new({
+ host = host,
+ transitions = transitions,
+ })
+ local shared_target = target_plan.build(target("a"), matching_policy())
+ local movement = motion_plan.build(shared_target, "f")
+ local first = feedback:request_persistent({
+ context = "n",
+ anchor = domain.Position.new(1, 1),
+ target_plan = shared_target,
+ motion_plan = movement,
+ window = "window-1",
+ })
+
+ local migration = feedback:migrate_command({
+ context = domain.ModeContext.from_full_mode("n"),
+ origin = domain.Position.new(1, 1),
+ destination = domain.Position.new(2, 2),
+ outcome = domain.SearchOutcome.complete(domain.Position.new(2, 2), 1),
+ resolved_motion_plan = movement,
+ first_move = true,
+ moved_forward = true,
+ })
+
+ truthy(migration.migrated)
+ same(feedback_service.MigrationReason.LINE_CHANGE, migration.reason)
+ same(2, migration.overlay.anchor.line)
+ same(2, migration.overlay.match_start_line)
+ same(nil, host:highlights()[first.identity])
+ truthy(host:highlights()[migration.overlay.identity] ~= nil)
+ same(1, #state.target_overlays)
+ same(2, state.target_overlays[1].anchor_line)
+end)
+
for _, item in ipairs(tests) do
local ok, failure = xpcall(item.body, debug.traceback)
if not ok then