summaryrefslogtreecommitdiff
path: root/lua/clever_f
diff options
context:
space:
mode:
Diffstat (limited to 'lua/clever_f')
-rw-r--r--lua/clever_f/motion_plan.lua122
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