diff options
| -rw-r--r-- | lua/clever_f/motion_executor.lua | 11 | ||||
| -rw-r--r-- | tests/run.lua | 35 |
2 files changed, 45 insertions, 1 deletions
diff --git a/lua/clever_f/motion_executor.lua b/lua/clever_f/motion_executor.lua index 72a66bd..afa3ee1 100644 --- a/lua/clever_f/motion_executor.lua +++ b/lua/clever_f/motion_executor.lua @@ -74,9 +74,10 @@ end local function require_host(host) if type(host) ~= "table" or type(host.read_cursor) ~= "function" + or type(host.read_selection) ~= "function" or type(host.apply_cursor) ~= "function" then - fail("MotionExecutor host must provide cursor movement", 3) + fail("MotionExecutor host must provide movement state", 3) end return host end @@ -234,8 +235,16 @@ end function MotionExecutor:_execute_visual(request) local host = executor_records[self].host + local selection = host:read_selection() + if not domain.Selection.is(selection) + or not selection.active + or selection.kind ~= request.context.visual_kind + then + fail("Visual motion execution requires its active selection kind", 2) + end local origin = host:read_cursor() local outcome = calculate(self, request, origin) + request.selection = selection return domain.ActionOutcome.from_search(outcome, request.plan.descriptor) end diff --git a/tests/run.lua b/tests/run.lua index 97fbacb..957a4bb 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -3340,6 +3340,41 @@ test("Complete command execution commits direction and landing", function() truthy(state.moved_forward_initialized) end) +test("Visual calculation retains the active selection", function() + local origin = domain.Position.new(1, 1) + local active = domain.Selection.active( + domain.SelectionKind.CHARACTER, + origin, + origin, + domain.SelectionOption.INCLUSIVE + ) + local host = MemoryHost.new({ + buffer_lines = { "abc" }, + cursor = origin, + mode = "v", + selection = active, + }) + local observed_selection + local engine = { + calculate = function(_, _, calculation_origin) + observed_selection = host:read_selection() + return domain.SearchOutcome.boundary_before_any(calculation_origin) + end, + } + local plan = motion_plan.build( + target_plan.build(target("z"), matching_policy()), + "f" + ) + local outcome = motion_executor.new({ + host = host, + destination_engine = engine, + }):execute(text_topology.from_host(host), "v", plan, 1, true) + + same(domain.ActionKind.FAILED_SEARCH, outcome.kind) + same(active, observed_selection) + same(active, host:read_selection()) +end) + for _, item in ipairs(tests) do local ok, failure = xpcall(item.body, debug.traceback) if not ok then |
