diff options
| author | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 18:44:32 +0200 |
|---|---|---|
| committer | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 18:44:32 +0200 |
| commit | c7b14ffb6d14969c9c41864e827f33f8e80fc24e (patch) | |
| tree | c7c0ac23dc93a4a8b75449d2be4d0e05c4f05f9e /lua/clever_tee/motion_plan.lua | |
| parent | 9013636a57144e1f57c9339e7888d588430aae6a (diff) | |
Rename plugin to clever-tee
Diffstat (limited to 'lua/clever_tee/motion_plan.lua')
| -rw-r--r-- | lua/clever_tee/motion_plan.lua | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/lua/clever_tee/motion_plan.lua b/lua/clever_tee/motion_plan.lua new file mode 100644 index 0000000..17e06eb --- /dev/null +++ b/lua/clever_tee/motion_plan.lua @@ -0,0 +1,188 @@ +local domain = require("clever_tee.domain") + +local M = {} +local MotionPlanFactory = {} +M.MotionPlanFactory = MotionPlanFactory + +local factory_records = setmetatable({}, { __mode = "k" }) + +local function fail(message, level) + error(message, (level or 1) + 1) +end + +local function normalize_options(options) + if options == nil then + return {} + end + if type(options) == "table" and type(options.sample_search) == "function" then + return { policy = options } + end + if type(options) ~= "table" then + fail("MotionPlanFactory options must be a table", 3) + end + return options +end + +local function require_policy(service) + if service ~= nil and (type(service) ~= "table" + or type(service.sample_search) ~= "function") + then + fail("MotionPlanFactory policy must provide sample_search", 3) + end + return service +end + +local factory_metatable = { + __index = MotionPlanFactory, + __newindex = function() + fail("MotionPlanFactory values are immutable", 2) + end, + __tostring = function() + return "motion-plan-factory" + end, + __metatable = "clever_tee.motion_plan.MotionPlanFactory", +} + +function MotionPlanFactory.new(options) + if MotionPlanFactory.is(options) then + return options + end + options = normalize_options(options) + local factory = setmetatable({}, factory_metatable) + factory_records[factory] = { + policy = require_policy(options.policy or options.policy_service), + } + return factory +end + +function MotionPlanFactory.is(value) + return type(value) == "table" and factory_records[value] ~= nil +end + +local function resolved_scope(factory, search_scope) + if search_scope == nil then + local policy = factory_records[factory].policy + if policy ~= nil then + search_scope = policy:sample_search().search_scope + else + search_scope = domain.SearchScope.BUFFER + end + elseif search_scope == "line" then + search_scope = domain.SearchScope.CURRENT_LINE + end + return domain.SearchScope.from_string(search_scope) +end + +function MotionPlanFactory:build( + target_plan, + effective_descriptor, + search_scope, + endpoint_policy +) + if not domain.TargetPlan.is(target_plan) then + fail("motion target plan must be a TargetPlan", 2) + end + + return domain.ResolvedMotionPlan.new({ + target_plan = target_plan, + descriptor = effective_descriptor, + search_scope = resolved_scope(self, search_scope), + endpoint_policy = endpoint_policy or domain.EndpointPolicy.REGULAR, + }) +end + +local function selection_option(selection) + if selection == nil then + return domain.SelectionOption.INCLUSIVE + end + if domain.Selection.is(selection) then + return selection.option + end + if domain.SelectionOption.is(selection) then + return selection + end + if type(selection) == "table" and selection.option ~= nil then + return domain.SelectionOption.from_string(selection.option) + end + return domain.SelectionOption.from_string(selection) +end + +function M.endpoint_policy(context, selection) + context = domain.ModeContext.from_full_mode(context) + local option = selection_option(selection) + local visual_kind = context.visual_kind + if option == domain.SelectionOption.EXCLUSIVE + and (visual_kind == domain.SelectionKind.CHARACTER + or visual_kind == domain.SelectionKind.LINE) + then + return domain.EndpointPolicy.VISUAL_EXCLUSIVE + end + return domain.EndpointPolicy.REGULAR +end + +function MotionPlanFactory:endpoint_policy(context, selection) + return M.endpoint_policy(context, selection) +end + +function MotionPlanFactory:build_for_context( + target_plan, + effective_descriptor, + context, + selection, + search_scope +) + return self:build( + target_plan, + effective_descriptor, + search_scope, + self:endpoint_policy(context, selection) + ) +end + +function M.new(options) + return MotionPlanFactory.new(options) +end + +function M.build( + target_plan, + effective_descriptor, + search_scope, + endpoint_policy, + options +) + return MotionPlanFactory.new(options):build( + target_plan, + effective_descriptor, + search_scope, + endpoint_policy + ) +end + +function M.build_for_context( + target_plan, + effective_descriptor, + context, + selection, + search_scope, + options +) + return MotionPlanFactory.new(options):build_for_context( + target_plan, + effective_descriptor, + context, + selection, + search_scope + ) +end + +M.create = M.build +M.resolve = M.build +M.for_context = M.build_for_context + +setmetatable(M, { + __call = function(_, options) + return MotionPlanFactory.new(options) + end, +}) + +return M |
