summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJackson Moore <jacksonmoore@tuta.io>2026-09-04 10:52:25 +0200
committerJackson Moore <jacksonmoore@tuta.io>2026-09-04 10:52:25 +0200
commit0668744a8f18edd1da4914f901bf6f108ab2fd81 (patch)
tree724fdf2452718a9e96dad11dea9d0aaf8956e542
parent7c145a7e7369a2f02f32383603186ca1471794c0 (diff)
Toggle forward operator inclusivity
-rw-r--r--lua/clever_f/motion_executor.lua6
-rw-r--r--tests/run.lua37
2 files changed, 43 insertions, 0 deletions
diff --git a/lua/clever_f/motion_executor.lua b/lua/clever_f/motion_executor.lua
index f7722e8..7a0755b 100644
--- a/lua/clever_f/motion_executor.lua
+++ b/lua/clever_f/motion_executor.lua
@@ -77,6 +77,7 @@ local function require_host(host)
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"
then
fail("MotionExecutor host must provide movement state", 3)
end
@@ -216,6 +217,11 @@ function MotionExecutor:_execute_command(request)
local origin = host:read_cursor()
local outcome = calculate(self, request, origin)
if outcome.successful_steps > 0 then
+ if request.context.operator
+ and request.plan.descriptor.direction == domain.Direction.FORWARD
+ then
+ host:set_operator_inclusive(true)
+ end
host:apply_cursor(outcome.endpoint)
end
if not outcome.complete then
diff --git a/tests/run.lua b/tests/run.lua
index 2595224..393d51a 100644
--- a/tests/run.lua
+++ b/tests/run.lua
@@ -3500,6 +3500,43 @@ test("Visual execution preserves command direction and feedback anchor", functio
same(domain.Position.new(2, 1), state:get_previous_landing("v"))
end)
+test("Forward operator motions toggle characterwise inclusivity", function()
+ for _, descriptor in ipairs({ "f", "t" }) do
+ fresh_sequence_state()
+ local host = MemoryHost.new({
+ buffer_lines = { "hoge fuge" },
+ cursor = { line = 1, byte_column = 1 },
+ mode = "no",
+ pending_operator = "delete",
+ })
+ local plan = motion_plan.build(
+ target_plan.build(target("e"), matching_policy()),
+ descriptor
+ )
+ local view = text_topology.from_host(host)
+ host:clear_operations()
+ local outcome = motion_executor.new(host):execute(
+ view,
+ "no",
+ plan,
+ 1,
+ true
+ )
+
+ same(domain.ActionKind.MOVEMENT, outcome.kind, descriptor)
+ truthy(host:operator_inclusive(), descriptor)
+ local effect_operations = {}
+ for _, operation in ipairs(host:operations()) do
+ if operation.operation == "set_operator_inclusive"
+ or operation.operation == "apply_cursor"
+ then
+ effect_operations[#effect_operations + 1] = operation.operation
+ end
+ end
+ list_same({ "set_operator_inclusive", "apply_cursor" }, effect_operations)
+ end
+end)
+
for _, item in ipairs(tests) do
local ok, failure = xpcall(item.body, debug.traceback)
if not ok then