diff options
| author | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 12:41:35 +0200 |
|---|---|---|
| committer | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 12:41:35 +0200 |
| commit | 275729dbfdf809dc7972d87cba0c5e589a39a5dc (patch) | |
| tree | 2281ec61f177ae664bc1da209419c431d7c292db | |
| parent | 70bc720156f8ff315fcf54f478becfdf098f3994 (diff) | |
Rebuild cross-line feedback
| -rw-r--r-- | lua/clever_f/feedback_service.lua | 35 | ||||
| -rw-r--r-- | tests/run.lua | 37 |
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 |
