summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJackson Moore <jacksonmoore@tuta.io>2026-09-04 11:59:20 +0200
committerJackson Moore <jacksonmoore@tuta.io>2026-09-04 11:59:20 +0200
commit4005e0311f4dc951d3e9464ddf2f8f309d9b9a24 (patch)
tree32f60693b6af227db048d8220a58ac4e3473eaee
parent61233d0cfb09f6d5a42c48eb31df5edf1d91c12f (diff)
Create enabled direct acquisition previews
-rw-r--r--lua/clever_f/acquisition_service.lua65
-rw-r--r--lua/clever_f/policy.lua7
-rw-r--r--tests/run.lua42
3 files changed, 108 insertions, 6 deletions
diff --git a/lua/clever_f/acquisition_service.lua b/lua/clever_f/acquisition_service.lua
index 3c7cc14..0cfd843 100644
--- a/lua/clever_f/acquisition_service.lua
+++ b/lua/clever_f/acquisition_service.lua
@@ -1,8 +1,10 @@
local domain = require("clever_f.domain")
+local direct_preview_planner = require("clever_f.direct_preview_planner")
local feedback_service = require("clever_f.feedback_service")
local policy = require("clever_f.policy")
local sequence_state = require("clever_f.sequence_state")
local state_transitions = require("clever_f.state_transitions")
+local text_topology = require("clever_f.text_topology")
local M = {}
local AcquisitionRequest = {}
@@ -112,6 +114,7 @@ function TemporaryResourceScope.new(request, feedback)
feedback = feedback,
active = true,
cursor_marker = nil,
+ direct_marker = nil,
}
return scope
end
@@ -120,13 +123,21 @@ function TemporaryResourceScope.is(value)
return type(value) == "table" and scope_records[value] ~= nil
end
-function TemporaryResourceScope:set_cursor_marker(marker)
- local record = scope_records[self]
+local function set_scope_resource(scope, field, resource)
+ local record = scope_records[scope]
if record == nil or not record.active then
- fail("temporary resource scope must be active", 2)
+ fail("temporary resource scope must be active", 3)
end
- record.cursor_marker = marker
- return marker
+ record[field] = resource
+ return resource
+end
+
+function TemporaryResourceScope:set_cursor_marker(marker)
+ return set_scope_resource(self, "cursor_marker", marker)
+end
+
+function TemporaryResourceScope:set_direct_marker(marker)
+ return set_scope_resource(self, "direct_marker", marker)
end
function TemporaryResourceScope:release()
@@ -161,6 +172,14 @@ local function require_transitions(transitions, state)
return transitions
end
+local function require_direct_planner(planner)
+ planner = planner or direct_preview_planner.new()
+ if type(planner) ~= "table" or type(planner.plan) ~= "function" then
+ fail("AcquisitionService direct planner must provide plan", 3)
+ end
+ return planner
+end
+
local function require_feedback(feedback, host, policy_service, transitions)
feedback = feedback or feedback_service.new({
host = host,
@@ -222,6 +241,9 @@ function AcquisitionService.new(options, dependencies)
policy_service,
transitions
),
+ direct_planner = require_direct_planner(
+ options.direct_planner or options.direct_preview_planner
+ ),
window = options.window or options.current_window,
last_scope = nil,
started_scope_count = 0,
@@ -268,11 +290,25 @@ function AcquisitionService:started_scope_count()
return service_records[self].started_scope_count
end
+local function direct_preview_settings(policy_service)
+ if type(policy_service.sample_direct_preview) == "function" then
+ return policy_service:sample_direct_preview()
+ end
+ if type(policy_service.get_boolean) == "function" then
+ return {
+ ignore_case = policy_service:get_boolean("ignore_case"),
+ smart_case = policy_service:get_boolean("smart_case"),
+ }
+ end
+ fail("AcquisitionService policy must sample direct preview settings", 3)
+end
+
function AcquisitionService:acquire(descriptor, context, position, count, macro_state)
local request = self:request(descriptor, context, position, count, macro_state)
local scope = self:start_temporary_scope(request)
local record = service_records[self]
- if record.policy:sample_acquisition().mark_cursor then
+ local acquisition = record.policy:sample_acquisition()
+ if acquisition.mark_cursor then
scope:set_cursor_marker(record.feedback:create_cursor_marker(
request.position,
current_window(record)
@@ -281,6 +317,23 @@ function AcquisitionService:acquire(descriptor, context, position, count, macro_
record.host:redraw("screen")
end
end
+ if acquisition.mark_direct and not request.macro_state.executing then
+ local view = text_topology.from_host(record.host)
+ local positions = record.direct_planner:plan(
+ view,
+ request.position,
+ request.descriptor,
+ request.count,
+ direct_preview_settings(record.policy)
+ )
+ local window = scope.cursor_marker
+ and scope.cursor_marker.window
+ or current_window(record)
+ scope:set_direct_marker(record.feedback:create_direct_markers(
+ positions,
+ window
+ ))
+ end
return request
end
diff --git a/lua/clever_f/policy.lua b/lua/clever_f/policy.lua
index 3dbffa8..df9fb7a 100644
--- a/lua/clever_f/policy.lua
+++ b/lua/clever_f/policy.lua
@@ -453,6 +453,13 @@ function PolicyService:sample_acquisition()
}
end
+function PolicyService:sample_direct_preview()
+ return {
+ ignore_case = self:get_boolean("ignore_case"),
+ smart_case = self:get_boolean("smart_case"),
+ }
+end
+
function PolicyService:sample_markers()
return {
mark_cursor = self:get_boolean("mark_cursor"),
diff --git a/tests/run.lua b/tests/run.lua
index 9de914f..80294bb 100644
--- a/tests/run.lua
+++ b/tests/run.lua
@@ -4976,6 +4976,48 @@ test("Interactive acquisition redraws after cursor marker creation", function()
same(0, #macro_host:redraws())
end)
+test("Interactive acquisition creates enabled direct previews", function()
+ local state, transitions = fresh_sequence_state()
+ local host = MemoryHost.new({
+ buffer_lines = { "xaxa" },
+ cursor = { line = 1, byte_column = 1 },
+ count = 2,
+ configuration = {
+ mark_cursor = false,
+ mark_direct = true,
+ ignore_case = false,
+ smart_case = false,
+ },
+ })
+ local service = acquisition_service.new({
+ host = host,
+ transitions = transitions,
+ })
+
+ service:acquire("t", "n", host:read_cursor(), host:read_count(), nil)
+ local marker = service:last_temporary_scope().direct_marker
+ truthy(marker ~= nil)
+ same("CleverFDirect", marker.group)
+ same(1, #marker.positions)
+ same(domain.Position.new(1, 4), marker.positions[1])
+ same(marker.identity, state:temporary_overlay_identities()[1])
+
+ fresh_sequence_state()
+ local macro_host = MemoryHost.new({
+ buffer_lines = { "xaxa" },
+ configuration = { mark_cursor = false, mark_direct = true },
+ })
+ local macro_service = acquisition_service.new(macro_host)
+ macro_service:acquire(
+ "f",
+ "n",
+ domain.Position.new(1, 1),
+ nil,
+ "q"
+ )
+ same(nil, macro_service:last_temporary_scope().direct_marker)
+end)
+
for _, item in ipairs(tests) do
local ok, failure = xpcall(item.body, debug.traceback)
if not ok then