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_f/migemo_catalog.lua | |
| parent | 9013636a57144e1f57c9339e7888d588430aae6a (diff) | |
Rename plugin to clever-tee
Diffstat (limited to 'lua/clever_f/migemo_catalog.lua')
| -rw-r--r-- | lua/clever_f/migemo_catalog.lua | 527 |
1 files changed, 0 insertions, 527 deletions
diff --git a/lua/clever_f/migemo_catalog.lua b/lua/clever_f/migemo_catalog.lua deleted file mode 100644 index 18d9541..0000000 --- a/lua/clever_f/migemo_catalog.lua +++ /dev/null @@ -1,527 +0,0 @@ -local domain = require("clever_f.domain") -local sequence_state = require("clever_f.sequence_state") -local state_transitions = require("clever_f.state_transitions") -local text_topology = require("clever_f.text_topology") - -local M = {} -local MigemoCatalog = {} -local MigemoDictionary = {} -M.MigemoCatalog = MigemoCatalog -M.MigemoDictionary = MigemoDictionary - -local catalog_records = setmetatable({}, { __mode = "k" }) -local dictionary_records = setmetatable({}, { __mode = "k" }) - -local function fail(message, level) - error(message, (level or 1) + 1) -end - -local function copy_list(values) - local result = {} - for index = 1, #values do - result[index] = values[index] - end - return result -end - -local EXPECTED_KEYS = {} -for code = string.byte("a"), string.byte("z") do - EXPECTED_KEYS[#EXPECTED_KEYS + 1] = string.char(code) -end -for code = string.byte("A"), string.byte("Z") do - EXPECTED_KEYS[#EXPECTED_KEYS + 1] = string.char(code) -end - -local EXPECTED_KEY_SET = {} -for _, key in ipairs(EXPECTED_KEYS) do - EXPECTED_KEY_SET[key] = true -end - -local ASSETS = { - ["utf-8"] = { - file = "utf8.vim", - function_name = "clever_f#migemo#utf8#load_dict", - }, - cp932 = { - file = "cp932.vim", - function_name = "clever_f#migemo#cp932#load_dict", - }, - ["euc-jp"] = { - file = "eucjp.vim", - function_name = "clever_f#migemo#eucjp#load_dict", - }, -} - -local module_source = debug.getinfo(1, "S").source -local module_file = module_source:sub(1, 1) == "@" and module_source:sub(2) or module_source -local bundled_root = module_file:match("^(.*)/lua/clever_f/migemo_catalog%.lua$") - -local function require_nonempty_string(value, name) - if type(value) ~= "string" or value == "" then - fail((name or "value") .. " must be a nonempty string", 2) - end - return value -end - -local function asset_path(asset) - if bundled_root == nil then - fail("Migemo catalog could not locate its bundled assets", 2) - end - return bundled_root .. "/autoload/clever_f/migemo/" .. asset.file -end - -local function read_asset_key_order(path) - local handle, open_error = io.open(path, "rb") - if handle == nil then - fail("Migemo asset could not be opened: " .. tostring(open_error), 2) - end - - local keys = {} - for line in handle:lines() do - local key = line:match("^%s*\\%s*'([A-Za-z])'%s*:") - if key ~= nil then - keys[#keys + 1] = key - end - end - handle:close() - return keys -end - -local function assert_key_order(keys, encoding) - if type(keys) ~= "table" or #keys ~= #EXPECTED_KEYS then - fail( - "Migemo " .. encoding .. " asset must contain exactly 52 ordered keys", - 2 - ) - end - for index, expected in ipairs(EXPECTED_KEYS) do - if keys[index] ~= expected then - fail( - "Migemo " .. encoding - .. " asset keys must be ordered a through z, then A through Z", - 2 - ) - end - end -end - -local function nvim_runtime() - local runtime = rawget(_G, "vim") - if type(runtime) ~= "table" - or runtime.cmd == nil - or type(runtime.fn) ~= "table" - or type(runtime.fn.fnameescape) ~= "function" - then - fail("Migemo dictionary loading requires Nvim", 2) - end - return runtime -end - -local function default_asset_loader(encoding, asset) - local runtime = nvim_runtime() - local path = asset_path(asset) - local keys = read_asset_key_order(path) - assert_key_order(keys, encoding) - - runtime.cmd("silent source " .. runtime.fn.fnameescape(path)) - local loader = runtime.fn[asset.function_name] - if type(loader) ~= "function" then - fail("Migemo " .. encoding .. " asset did not define its dictionary loader", 2) - end - local dictionary = loader() - return dictionary, keys, path -end - -local function explicit_pattern(pattern, case_mode) - local case_flag = case_mode == domain.CaseMode.INSENSITIVE and "\\c" or "\\C" - return "\\m" .. case_flag .. "^" .. pattern -end - -local function default_pattern_compiler(pattern, key, encoding) - local runtime = nvim_runtime() - if type(runtime.regex) ~= "function" or type(runtime.fn.match) ~= "function" then - fail("Migemo pattern evaluation requires Nvim regular expressions", 2) - end - - local sensitive = explicit_pattern(pattern, domain.CaseMode.SENSITIVE) - local insensitive = explicit_pattern(pattern, domain.CaseMode.INSENSITIVE) - local ok, compile_error = pcall(runtime.regex, sensitive) - if ok then - ok, compile_error = pcall(runtime.regex, insensitive) - end - if not ok then - fail( - "Migemo " .. encoding .. " pattern for '" .. key - .. "' could not be compiled: " .. tostring(compile_error), - 2 - ) - end - - return function(text, case_mode) - if type(text) ~= "string" then - fail("Migemo assertion text must be a string", 2) - end - case_mode = domain.CaseMode.from_string(case_mode) - local selected = case_mode == domain.CaseMode.INSENSITIVE - and insensitive - or sensitive - local matched, start_or_error = pcall(runtime.fn.match, text, selected) - if not matched then - fail( - "Migemo " .. encoding .. " pattern for '" .. key - .. "' could not be evaluated: " .. tostring(start_or_error), - 2 - ) - end - return start_or_error == 0 - end -end - -local dictionary_metatable = { - __index = function(dictionary, key) - local method = MigemoDictionary[key] - if method ~= nil then - return method - end - - local record = dictionary_records[dictionary] - if key == "encoding" or key == "effective_encoding" then - return record.encoding - end - if key == "entry_count" then - return #record.keys - end - if key == "asset_path" then - return record.asset_path - end - if EXPECTED_KEY_SET[key] then - return record.predicates[key] - end - return nil - end, - __newindex = function() - fail("MigemoDictionary values are immutable", 2) - end, - __tostring = function(dictionary) - return "migemo-dictionary:" .. dictionary_records[dictionary].encoding - end, - __metatable = "clever_f.migemo_catalog.MigemoDictionary", -} - -local function validate_dictionary_data(data, ordered_keys, encoding) - if type(data) ~= "table" then - fail("Migemo " .. encoding .. " asset must return a dictionary", 3) - end - assert_key_order(ordered_keys, encoding) - - local count = 0 - for key, pattern in pairs(data) do - count = count + 1 - if EXPECTED_KEY_SET[key] ~= true then - fail("Migemo " .. encoding .. " asset contains an unexpected key", 3) - end - if type(pattern) ~= "string" or pattern == "" then - fail("Migemo " .. encoding .. " patterns must be nonempty strings", 3) - end - end - if count ~= #EXPECTED_KEYS then - fail("Migemo " .. encoding .. " asset must contain exactly 52 keys", 3) - end - for _, key in ipairs(EXPECTED_KEYS) do - if data[key] == nil then - fail("Migemo " .. encoding .. " asset is missing key '" .. key .. "'", 3) - end - end -end - -local function new_dictionary(encoding, data, ordered_keys, path, compiler) - validate_dictionary_data(data, ordered_keys, encoding) - - local patterns = {} - local predicates = {} - for _, key in ipairs(EXPECTED_KEYS) do - local pattern = data[key] - patterns[key] = pattern - local predicate = compiler(pattern, key, encoding) - if type(predicate) ~= "function" then - fail("Migemo pattern compiler must return a predicate", 3) - end - predicates[key] = predicate - end - - local dictionary = setmetatable({}, dictionary_metatable) - dictionary_records[dictionary] = { - encoding = encoding, - keys = copy_list(ordered_keys), - patterns = patterns, - predicates = predicates, - asset_path = path, - } - return dictionary -end - -function MigemoDictionary.is(value) - return type(value) == "table" and dictionary_records[value] ~= nil -end - -local function dictionary_record(dictionary) - if not MigemoDictionary.is(dictionary) then - fail("value must be a MigemoDictionary", 3) - end - return dictionary_records[dictionary] -end - -function MigemoDictionary:keys() - return copy_list(dictionary_record(self).keys) -end - -function MigemoDictionary:has(key) - return type(key) == "string" - and dictionary_record(self).predicates[key] ~= nil -end - -function MigemoDictionary:pattern(key) - require_nonempty_string(key, "Migemo dictionary key") - local pattern = dictionary_record(self).patterns[key] - if pattern == nil then - fail("Migemo dictionary key must be one ASCII alphabetic character", 2) - end - return pattern -end - -function MigemoDictionary:predicate(key, case_mode) - require_nonempty_string(key, "Migemo dictionary key") - local predicate = dictionary_record(self).predicates[key] - if predicate == nil then - fail("Migemo dictionary key must be one ASCII alphabetic character", 2) - end - if case_mode == nil then - return predicate - end - - case_mode = domain.CaseMode.from_string(case_mode) - return function(text) - return predicate(text, case_mode) - end -end - -function MigemoDictionary:matches(key, text, case_mode) - return self:predicate(key)(text, case_mode) -end - -function MigemoDictionary:to_table() - local record = dictionary_record(self) - return { - encoding = record.encoding, - entry_count = #record.keys, - keys = copy_list(record.keys), - asset_path = record.asset_path, - } -end - -local function normalize_catalog_options(options) - if options == nil then - return {} - end - if type(options) ~= "table" then - fail("MigemoCatalog options must be a table", 3) - end - if type(options.disable_migemo_for_unsupported_encoding) == "function" - and options.policy == nil - and options.policy_service == nil - and options.transitions == nil - and options.state == nil - and options.asset_loader == nil - and options.pattern_compiler == nil - then - return { policy = options } - end - return options -end - -local function require_policy(service) - if service ~= nil and (type(service) ~= "table" - or type(service.disable_migemo_for_unsupported_encoding) ~= "function") - then - fail( - "MigemoCatalog policy must provide disable_migemo_for_unsupported_encoding", - 3 - ) - end - return service -end - -local function require_transitions(transitions, state) - transitions = transitions or state_transitions.new(state) - if type(transitions) ~= "table" - or type(transitions.CacheMigemo) ~= "function" - or type(transitions.state) ~= "function" - or transitions:state() ~= state - then - fail("MigemoCatalog transitions must mutate its SequenceState", 3) - end - return transitions -end - -local function selected_function(value, fallback, name) - value = value or fallback - if type(value) ~= "function" then - fail("MigemoCatalog " .. name .. " must be a function", 3) - end - return value -end - -local catalog_metatable = { - __index = MigemoCatalog, - __newindex = function() - fail("MigemoCatalog values are immutable", 2) - end, - __tostring = function() - return "migemo-catalog" - end, - __metatable = "clever_f.migemo_catalog.MigemoCatalog", -} - -function MigemoCatalog.new(options) - if MigemoCatalog.is(options) then - return options - end - options = normalize_catalog_options(options) - local state = options.state or sequence_state.get() - if not sequence_state.is(state) then - fail("MigemoCatalog requires the plugin-global SequenceState", 2) - end - - local catalog = setmetatable({}, catalog_metatable) - catalog_records[catalog] = { - state = state, - transitions = require_transitions(options.transitions, state), - policy = require_policy(options.policy or options.policy_service), - disable_migemo = options.disable_migemo, - asset_loader = selected_function( - options.asset_loader, - default_asset_loader, - "asset_loader" - ), - pattern_compiler = selected_function( - options.pattern_compiler, - default_pattern_compiler, - "pattern_compiler" - ), - load_counts = {}, - } - if catalog_records[catalog].disable_migemo ~= nil - and type(catalog_records[catalog].disable_migemo) ~= "function" - then - fail("MigemoCatalog disable_migemo must be a function", 2) - end - return catalog -end - -function MigemoCatalog.is(value) - return type(value) == "table" and catalog_records[value] ~= nil -end - -function M.new(options) - return MigemoCatalog.new(options) -end - -setmetatable(M, { - __call = function(_, options) - return MigemoCatalog.new(options) - end, -}) - -local function catalog_record(catalog) - if not MigemoCatalog.is(catalog) then - fail("value must be a MigemoCatalog", 3) - end - return catalog_records[catalog] -end - -local function unsupported(catalog, requested_encoding, policy_override) - local record = catalog_record(catalog) - local active_policy = policy_override or record.policy - if active_policy ~= nil then - require_policy(active_policy):disable_migemo_for_unsupported_encoding() - elseif record.disable_migemo ~= nil then - record.disable_migemo() - end - error( - "clever-f: Encoding '" .. requested_encoding - .. "' is not supported. Migemo is disabled", - 0 - ) -end - -function MigemoCatalog:get(effective_encoding, policy_override) - local requested = require_nonempty_string(effective_encoding, "effective encoding") - local encoding = text_topology.normalize_encoding(requested) - local asset = ASSETS[encoding] - if asset == nil then - return unsupported(self, requested, policy_override) - end - - local record = catalog_record(self) - local cached = record.state:get_migemo(encoding) - if cached ~= nil then - if not MigemoDictionary.is(cached) then - fail("Migemo cache contains an invalid dictionary", 2) - end - return cached - end - - local data, ordered_keys, path = record.asset_loader(encoding, asset) - local dictionary = new_dictionary( - encoding, - data, - ordered_keys, - path, - record.pattern_compiler - ) - record.transitions:CacheMigemo(encoding, dictionary) - record.load_counts[encoding] = (record.load_counts[encoding] or 0) + 1 - return dictionary -end - -function MigemoCatalog:load_count(effective_encoding) - local encoding = text_topology.normalize_encoding(effective_encoding) - return catalog_record(self).load_counts[encoding] or 0 -end - -function MigemoCatalog:cached(effective_encoding) - local encoding = text_topology.normalize_encoding(effective_encoding) - local value = catalog_record(self).state:get_migemo(encoding) - if value ~= nil and not MigemoDictionary.is(value) then - fail("Migemo cache contains an invalid dictionary", 2) - end - return value -end - -MigemoCatalog.load = MigemoCatalog.get -MigemoCatalog.select = MigemoCatalog.get -MigemoCatalog.dictionary = MigemoCatalog.get - -function M.expected_keys() - return copy_list(EXPECTED_KEYS) -end - -function M.supported_encodings() - return { "utf-8", "cp932", "euc-jp" } -end - -function M.bundled_asset_path(effective_encoding) - local encoding = text_topology.normalize_encoding(effective_encoding) - local asset = ASSETS[encoding] - if asset == nil then - return nil - end - return asset_path(asset) -end - -M.load = function(effective_encoding, options) - return MigemoCatalog.new(options):get(effective_encoding) -end -M.EXPECTED_ENTRY_COUNT = #EXPECTED_KEYS - -return M |
