local domain = require("clever_f.domain") local sequence_state = require("clever_f.sequence_state") local M = {} local RepeatResolver = {} M.RepeatResolver = RepeatResolver M.Decision = { ACQUIRE = "acquire", REPEAT = "repeat", } M.ACQUIRE = M.Decision.ACQUIRE M.REPEAT = M.Decision.REPEAT local resolver_records = setmetatable({}, { __mode = "k" }) local function fail(message, level) error(message, (level or 1) + 1) end local resolver_metatable = { __index = RepeatResolver, __newindex = function() fail("RepeatResolver values are immutable", 2) end, __tostring = function() return "repeat-resolver" end, __metatable = "clever_f.repeat_resolver.RepeatResolver", } function RepeatResolver.new(options) if RepeatResolver.is(options) then return options end if options == nil then options = {} elseif sequence_state.is(options) then options = { state = options } elseif type(options) ~= "table" then fail("RepeatResolver options must be a table", 2) end local state = options.state or sequence_state.get() if not sequence_state.is(state) then fail("RepeatResolver state must be the plugin-global SequenceState", 2) end local resolver = setmetatable({}, resolver_metatable) resolver_records[resolver] = { state = state, } return resolver end function RepeatResolver.is(value) return type(value) == "table" and resolver_records[value] ~= nil end function RepeatResolver:previous_landing(context) context = domain.ModeContext.from_full_mode(context) return resolver_records[self].state:get_previous_landing(context) end function RepeatResolver:decide(context, current_position, macro_state) current_position = domain.Position.coerce(current_position) local landing = self:previous_landing(context) if landing == nil or not domain.Position.equal(landing, current_position) then return M.Decision.ACQUIRE end if domain.MacroState.new(macro_state).executing then return M.Decision.ACQUIRE end return M.Decision.REPEAT end RepeatResolver.eligibility = RepeatResolver.decide RepeatResolver.resolve_eligibility = RepeatResolver.decide function M.new(options) return RepeatResolver.new(options) end M.landing = function(context, options) return RepeatResolver.new(options):previous_landing(context) end function M.decide(context, current_position, macro_state, options) return RepeatResolver.new(options):decide( context, current_position, macro_state ) end M.eligibility = M.decide setmetatable(M, { __call = function(_, options) return RepeatResolver.new(options) end, }) return M