diff options
Diffstat (limited to 'lua/clever_f/motion_plan.lua')
| -rw-r--r-- | lua/clever_f/motion_plan.lua | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/lua/clever_f/motion_plan.lua b/lua/clever_f/motion_plan.lua new file mode 100644 index 0000000..362b978 --- /dev/null +++ b/lua/clever_f/motion_plan.lua @@ -0,0 +1,122 @@ +local domain = require("clever_f.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_f.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 + +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 + +M.create = M.build +M.resolve = M.build + +setmetatable(M, { + __call = function(_, options) + return MotionPlanFactory.new(options) + end, +}) + +return M |
