local action_facade = require("clever_tee.action_facade") local capabilities = require("clever_tee.capabilities") local feedback_service = require("clever_tee.feedback_service") local policy = require("clever_tee.policy") local sequence_coordinator = require("clever_tee.sequence_coordinator") local sequence_state = require("clever_tee.sequence_state") local state_transitions = require("clever_tee.state_transitions") local M = {} local CompositionRoot = {} CompositionRoot.__index = CompositionRoot M.CompositionRoot = CompositionRoot M.ACTION_NAMES = { "StartFindForward", "StartFindBackward", "StartTillForward", "StartTillBackward", "Reset", "RepeatSameDirection", "RepeatOppositeDirection", } M.DEFAULT_MAPPING_MODES = { "n", "x", "o" } M.DEFAULT_MAPPING_OPTIONS = { silent = true, remap = false, preserve_count = true, } M.DEFAULT_MAPPINGS = { { lhs = "f", action = "StartFindForward" }, { lhs = "F", action = "StartFindBackward" }, { lhs = "t", action = "StartTillForward" }, { lhs = "T", action = "StartTillBackward" }, } local ACTION_INVOCATIONS = { StartFindForward = function(facade) return facade:start_find_forward() end, StartFindBackward = function(facade) return facade:start_find_backward() end, StartTillForward = function(facade) return facade:start_till_forward() end, StartTillBackward = function(facade) return facade:start_till_backward() end, Reset = function(facade) return facade:reset() end, RepeatSameDirection = function(facade) return facade:repeat_same_direction() end, RepeatOppositeDirection = function(facade) return facade:repeat_opposite_direction() end, } local root_records = setmetatable({}, { __mode = "k" }) local function fail(message, level) error(message, (level or 1) + 1) end local function normalize_options(options) if type(options) ~= "table" then fail("CompositionRoot options must be a table", 3) end if options.host == nil then return { host = options } end return options end function CompositionRoot.new(options) if CompositionRoot.is(options) then return options end options = normalize_options(options) local host = capabilities.assert_implements(options.host) local state = sequence_state.new() local transitions = options.transitions or options.state_transitions or state_transitions.new(state) local policy_service = options.policy or options.policy_service or policy.new(host) local feedback = options.feedback or options.feedback_service or feedback_service.new({ host = host, state = state, transitions = transitions, policy = policy_service, }) local coordinator = options.coordinator or options.sequence_coordinator or sequence_coordinator.new({ host = host, state = state, transitions = transitions, policy = policy_service, feedback = feedback, }) local facade = options.facade or options.action_facade or action_facade.new({ coordinator = coordinator }) local root = setmetatable({}, CompositionRoot) root_records[root] = { host = host, state = state, transitions = transitions, policy = policy_service, feedback = feedback, coordinator = coordinator, facade = facade, activation = nil, last_highlight_refresh = nil, } return root end function CompositionRoot.is(value) return type(value) == "table" and root_records[value] ~= nil end local function register_logical_actions(record) local registrations = {} for _, name in ipairs(M.ACTION_NAMES) do local invoke = ACTION_INVOCATIONS[name] registrations[name] = record.host:register_action(name, function(...) return invoke(record.facade, ...) end) end return registrations end local function register_default_mappings(record, setup) local registrations = {} if not setup.install_default_mappings then return registrations end for _, mapping in ipairs(M.DEFAULT_MAPPINGS) do registrations[mapping.lhs] = record.host:register_mapping( M.DEFAULT_MAPPING_MODES, mapping.lhs, mapping.action, M.DEFAULT_MAPPING_OPTIONS ) end return registrations end function CompositionRoot:activate() local record = root_records[self] if record.activation == nil then local setup = record.policy:capture_activation() local feedback_activation = record.feedback:activate() local highlights = record.feedback:evaluate_highlights() local colorscheme_registration = record.host:register_events( "ColorScheme", function() record.last_highlight_refresh = record.feedback:evaluate_highlights() end, { owner = "clever_tee", lifecycle = "colorscheme" } ) local actions = register_logical_actions(record) record.activation = { state = record.state, setup = setup, feedback = feedback_activation, highlights = highlights, colorscheme_registration = colorscheme_registration, actions = actions, mappings = register_default_mappings(record, setup), } end return record.activation end function CompositionRoot:last_highlight_refresh() return root_records[self].last_highlight_refresh end function CompositionRoot:host() return root_records[self].host end function CompositionRoot:state() return root_records[self].state end function CompositionRoot:transitions() return root_records[self].transitions end function CompositionRoot:policy() return root_records[self].policy end function CompositionRoot:feedback() return root_records[self].feedback end function CompositionRoot:coordinator() return root_records[self].coordinator end function CompositionRoot:facade() return root_records[self].facade end function CompositionRoot:invoke_descriptor(value) return self:facade():invoke_descriptor(value) end function CompositionRoot:diagnostic_full_reset() return self:facade():diagnostic_full_reset() end function M.new(options) return CompositionRoot.new(options) end setmetatable(M, { __call = function(_, options) return CompositionRoot.new(options) end, }) return M