diff options
| author | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 18:44:32 +0200 |
|---|---|---|
| committer | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 18:44:32 +0200 |
| commit | c7b14ffb6d14969c9c41864e827f33f8e80fc24e (patch) | |
| tree | c7c0ac23dc93a4a8b75449d2be4d0e05c4f05f9e /lua/clever_tee/sequence_state.lua | |
| parent | 9013636a57144e1f57c9339e7888d588430aae6a (diff) | |
Rename plugin to clever-tee
Diffstat (limited to 'lua/clever_tee/sequence_state.lua')
| -rw-r--r-- | lua/clever_tee/sequence_state.lua | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/lua/clever_tee/sequence_state.lua b/lua/clever_tee/sequence_state.lua new file mode 100644 index 0000000..5c66a42 --- /dev/null +++ b/lua/clever_tee/sequence_state.lua @@ -0,0 +1,302 @@ +local domain = require("clever_tee.domain") + +local M = {} +local State = {} +M.State = State + +local MAP_FIELDS = { + "previous_descriptor", + "previous_landing", + "first_move", + "previous_target", +} + +local MAP_FIELD_SET = {} +for _, field in ipairs(MAP_FIELDS) do + MAP_FIELD_SET[field] = true +end + +local data = { + previous_descriptor = {}, + previous_landing = {}, + first_move = {}, + previous_target = {}, + known_contexts = {}, + last_input_context = nil, + moved_forward = false, + moved_forward_initialized = false, + migemo_cache = {}, + repeat_timestamp_ms = 0, + highlight_timer = nil, + target_overlays = {}, + temporary_overlays = {}, + finalizers = {}, +} + +local function fail(message, level) + error(message, (level or 1) + 1) +end + +local function normalize_context(context, name) + if domain.ModeContext.is(context) then + return domain.ModeContext.from_full_mode(context.full_mode) + end + if type(context) ~= "string" or context == "" then + fail((name or "context") .. " must be a ModeContext or full mode string", 2) + end + return domain.ModeContext.from_full_mode(context) +end + +local function copy_map(source) + local result = {} + for key, value in pairs(source) do + result[key] = value + end + return result +end + +local function copy_resource(resource) + local result = {} + for key, value in pairs(resource) do + result[key] = value + end + return result +end + +local function copy_resources(resources) + local result = {} + for index, resource in ipairs(resources) do + result[index] = copy_resource(resource) + end + return result +end + +local function sorted_contexts() + local result = {} + for context in pairs(data.known_contexts) do + result[#result + 1] = context + end + table.sort(result, function(left, right) + return left.key < right.key + end) + return result +end + +local function context_record(context) + return { + context = context, + previous_descriptor = data.previous_descriptor[context], + previous_landing = data.previous_landing[context], + first_move = data.first_move[context], + previous_target = data.previous_target[context], + } +end + +local function context_records() + local result = {} + for _, context in ipairs(sorted_contexts()) do + result[context.key] = context_record(context) + end + return result +end + +function State:get_previous_descriptor(context) + context = normalize_context(context) + return data.previous_descriptor[context] +end + +function State:get_previous_landing(context) + context = normalize_context(context) + return data.previous_landing[context] +end + +function State:get_first_move(context) + context = normalize_context(context) + return data.first_move[context] +end + +function State:get_previous_target(context) + context = normalize_context(context) + return data.previous_target[context] +end + +function State:get_context(context) + context = normalize_context(context) + return context_record(context) +end + +State.context = State.get_context + +function State:has_previous_landing(context) + return self:get_previous_landing(context) ~= nil +end + +function State:get_migemo(encoding) + if type(encoding) ~= "string" or encoding == "" then + fail("encoding must be a nonempty string", 2) + end + return data.migemo_cache[encoding] +end + +function State:target_overlay_identities() + local result = {} + for index, resource in ipairs(data.target_overlays) do + result[index] = resource.identity + end + return result +end + +function State:temporary_overlay_identities() + local result = {} + for index, resource in ipairs(data.temporary_overlays) do + result[index] = resource.identity + end + return result +end + +function State:finalizer_identities() + local result = {} + for index, resource in ipairs(data.finalizers) do + result[index] = resource.identity + end + return result +end + +function State:resources() + return { + highlight_timer = data.highlight_timer, + target_overlays = copy_resources(data.target_overlays), + temporary_overlays = copy_resources(data.temporary_overlays), + finalizers = copy_resources(data.finalizers), + } +end + +function State:snapshot() + local snapshot = { + contexts = context_records(), + last_input_context = data.last_input_context, + moved_forward = data.moved_forward, + moved_forward_initialized = data.moved_forward_initialized, + migemo_cache = copy_map(data.migemo_cache), + repeat_timestamp_ms = data.repeat_timestamp_ms, + highlight_timer = data.highlight_timer, + target_overlays = copy_resources(data.target_overlays), + temporary_overlays = copy_resources(data.temporary_overlays), + finalizers = copy_resources(data.finalizers), + } + for _, field in ipairs(MAP_FIELDS) do + snapshot[field] = copy_map(data[field]) + end + return snapshot +end + +local function target_to_table(target) + return target and target:to_table() or nil +end + +function State:to_table() + local contexts = {} + for _, context in ipairs(sorted_contexts()) do + local record = context_record(context) + contexts[context.key] = { + previous_descriptor = record.previous_descriptor + and record.previous_descriptor.value + or nil, + previous_landing = record.previous_landing + and record.previous_landing:to_table() + or nil, + first_move = record.first_move, + previous_target = target_to_table(record.previous_target), + } + end + + local cache_keys = {} + for encoding in pairs(data.migemo_cache) do + cache_keys[#cache_keys + 1] = encoding + end + table.sort(cache_keys) + + return { + contexts = contexts, + last_input_context = data.last_input_context and data.last_input_context.key or nil, + moved_forward = data.moved_forward, + moved_forward_initialized = data.moved_forward_initialized, + migemo_cache = cache_keys, + repeat_timestamp_ms = data.repeat_timestamp_ms, + highlight_timer = data.highlight_timer, + target_overlays = self:target_overlay_identities(), + temporary_overlays = self:temporary_overlay_identities(), + finalizers = self:finalizer_identities(), + } +end + +local state +local state_metatable = { + __index = function(_, key) + local method = State[key] + if method ~= nil then + return method + end + if MAP_FIELD_SET[key] then + return copy_map(data[key]) + end + if key == "contexts" then + return context_records() + end + if key == "last_input_context" + or key == "moved_forward" + or key == "moved_forward_initialized" + or key == "highlight_timer" + then + return data[key] + end + if key == "repeat_timestamp" or key == "repeat_timestamp_ms" then + return data.repeat_timestamp_ms + end + if key == "migemo_cache" then + return copy_map(data.migemo_cache) + end + if key == "target_overlays" + or key == "temporary_overlays" + or key == "finalizers" + then + return copy_resources(data[key]) + end + return nil + end, + __newindex = function() + fail("SequenceState is read-only; use StateTransitions", 2) + end, + __metatable = "clever_tee.sequence_state.State", +} +state = setmetatable({}, state_metatable) + +function M.get() + return state +end + +function M.new() + return state +end + +function M.is(value) + return value == state +end + +M.global = state + +function M._mutate(target, mutation) + if target ~= state then + fail("StateTransitions must use the plugin-global SequenceState", 2) + end + if type(mutation) ~= "function" then + fail("state mutation must be a function", 2) + end + return mutation(data) +end + +function M._normalize_context(context) + return normalize_context(context) +end + +return M |
