diff options
Diffstat (limited to 'lua/clever_tee/composition_root.lua')
| -rw-r--r-- | lua/clever_tee/composition_root.lua | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/lua/clever_tee/composition_root.lua b/lua/clever_tee/composition_root.lua new file mode 100644 index 0000000..4741bd4 --- /dev/null +++ b/lua/clever_tee/composition_root.lua @@ -0,0 +1,232 @@ +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 |
