From 970db02504aeaf5c392a1bcad2f148734ba6ea48 Mon Sep 17 00:00:00 2001 From: Jackson Moore Date: Fri, 4 Sep 2026 14:23:03 +0200 Subject: Translate action outcomes --- lua/clever_f/host_adapter.lua | 108 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 lua/clever_f/host_adapter.lua (limited to 'lua/clever_f') diff --git a/lua/clever_f/host_adapter.lua b/lua/clever_f/host_adapter.lua new file mode 100644 index 0000000..a0eec66 --- /dev/null +++ b/lua/clever_f/host_adapter.lua @@ -0,0 +1,108 @@ +local domain = require("clever_f.domain") + +local M = {} +local HostAdapter = {} +HostAdapter.__index = HostAdapter +M.HostAdapter = HostAdapter + +M.ActionEffect = { + NONE = "none", + ESCAPE = "escape", + ERROR = "error", +} + +local adapter_records = setmetatable({}, { __mode = "k" }) + +local function fail(message, level) + error(message, (level or 1) + 1) +end + +local function current_runtime(options) + if type(options) == "table" and options.runtime ~= nil then + return options.runtime + end + if options ~= nil and options ~= HostAdapter then + return options + end + return rawget(_G, "vim") +end + +local function require_runtime(runtime) + if type(runtime) ~= "table" or type(runtime.api) ~= "table" then + fail("HostAdapter requires the Nvim Lua runtime", 3) + end + return runtime +end + +function HostAdapter.new(options) + if HostAdapter.is(options) then + return options + end + local adapter = setmetatable({}, HostAdapter) + adapter_records[adapter] = { + runtime = require_runtime(current_runtime(options)), + } + return adapter +end + +function HostAdapter.is(value) + return type(value) == "table" and adapter_records[value] ~= nil +end + +function HostAdapter:runtime() + return adapter_records[self].runtime +end + +local function escape_key(runtime) + if type(runtime.keycode) == "function" then + return runtime.keycode("") + end + if type(runtime.api.nvim_replace_termcodes) == "function" then + return runtime.api.nvim_replace_termcodes("", true, false, true) + end + return string.char(27) +end + +function HostAdapter:return_escape() + local runtime = self:runtime() + if type(runtime.api.nvim_feedkeys) ~= "function" then + fail("HostAdapter runtime must provide nvim_feedkeys", 2) + end + runtime.api.nvim_feedkeys(escape_key(runtime), "n", false) +end + +function HostAdapter:emit_action_error(text) + local runtime = self:runtime() + if type(runtime.notify) ~= "function" then + fail("HostAdapter runtime must provide notify", 2) + end + local levels = type(runtime.log) == "table" and runtime.log.levels or nil + runtime.notify(text, levels and levels.ERROR or nil, { title = "clever-f" }) +end + +function HostAdapter:translate_action_outcome(outcome) + if not domain.ActionOutcome.is(outcome) then + fail("host action translation requires an ActionOutcome", 2) + end + if outcome.kind == domain.ActionKind.ESCAPE then + self:return_escape() + return M.ActionEffect.ESCAPE + end + if outcome.kind == domain.ActionKind.ERROR then + self:emit_action_error(outcome.diagnostic) + return M.ActionEffect.ERROR + end + return M.ActionEffect.NONE +end + +function M.new(options) + return HostAdapter.new(options) +end + +setmetatable(M, { + __call = function(_, options) + return HostAdapter.new(options) + end, +}) + +return M -- cgit v1.2.3