diff options
| author | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 14:26:06 +0200 |
|---|---|---|
| committer | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 14:26:06 +0200 |
| commit | ae03a565105416f52a09bc0a171a7fdbb9ced5a0 (patch) | |
| tree | bb4f92e9934e759e1a327e60b34e8ccd34260a86 /lua | |
| parent | 4cb3fb0f1532de3465962903a1697339ad3d6159 (diff) | |
Translate timers and events
Diffstat (limited to 'lua')
| -rw-r--r-- | lua/clever_f/host_adapter.lua | 206 |
1 files changed, 205 insertions, 1 deletions
diff --git a/lua/clever_f/host_adapter.lua b/lua/clever_f/host_adapter.lua index 747d9b8..ab3467c 100644 --- a/lua/clever_f/host_adapter.lua +++ b/lua/clever_f/host_adapter.lua @@ -1,3 +1,4 @@ +local capabilities = require("clever_f.capabilities") local domain = require("clever_f.domain") local M = {} @@ -39,11 +40,19 @@ function HostAdapter.new(options) return options end local adapter = setmetatable({}, HostAdapter) - adapter_records[adapter] = { + local record = { runtime = require_runtime(current_runtime(options)), next_identity = 1, highlights = {}, + timers = {}, + events = {}, + event_order = {}, + augroup = nil, } + record.event_queue = capabilities.EventQueue.new(function(_, delivery) + delivery.callback(delivery.name, delivery.payload) + end) + adapter_records[adapter] = record return adapter end @@ -213,6 +222,201 @@ function HostAdapter:remove_highlight(identity) return true end +local function nonnegative_integer(value, name) + if type(value) ~= "number" + or value < 0 + or value ~= math.floor(value) + or value == math.huge + then + fail((name or "value") .. " must be a nonnegative integer", 3) + end + return value +end + +function HostAdapter:supports_timers() + local fn = self:runtime().fn + return type(fn) == "table" + and type(fn.timer_start) == "function" + and type(fn.timer_stop) == "function" +end + +function HostAdapter:start_timer(delay_ms, callback) + nonnegative_integer(delay_ms, "timer delay") + if type(callback) ~= "function" then + fail("timer callback must be a function", 2) + end + if not self:supports_timers() then + return nil + end + local record = adapter_records[self] + local identity = next_identity(self, "timer") + local timer_id = record.runtime.fn.timer_start(delay_ms, function() + local resource = record.timers[identity] + if resource == nil or not resource.active then + return + end + resource.active = false + callback(identity) + end) + if type(timer_id) ~= "number" or timer_id < 0 then + fail("Nvim could not start the timer", 2) + end + record.timers[identity] = { + timer_id = timer_id, + active = true, + } + return identity +end + +function HostAdapter:stop_timer(identity) + local record = adapter_records[self] + local resource = record.timers[identity] + if resource == nil or not resource.active then + return false + end + resource.active = false + record.runtime.fn.timer_stop(resource.timer_id) + return true +end + +local function event_names(value) + if type(value) == "string" then + value = { value } + end + if type(value) ~= "table" or #value == 0 then + fail("event names must be a nonempty list", 3) + end + local names = {} + local set = {} + for index, name in ipairs(value) do + if type(name) ~= "string" or name == "" then + fail("event name must be a nonempty string", 3) + end + if not set[name] then + names[#names + 1] = name + set[name] = true + end + end + return names, set +end + +local function event_payload(adapter, event) + local payload = { + buffer = event.buf, + file = event.file, + match = event.match, + data = event.data, + } + local api = adapter:runtime().api + if type(api.nvim_get_current_win) == "function" then + payload.window = api.nvim_get_current_win() + end + return payload +end + +local function event_augroup(record) + if record.augroup == nil then + record.augroup = record.runtime.api.nvim_create_augroup( + "clever_f", + { clear = true } + ) + end + return record.augroup +end + +local function queue_event(record, name, payload, callback) + return record.event_queue:emit(name, { + name = name, + payload = payload, + callback = callback, + }) +end + +function HostAdapter:register_events(names, callback, options) + local name_set + names, name_set = event_names(names) + if type(callback) ~= "function" then + fail("event callback must be a function", 2) + end + options = options or {} + if type(options) ~= "table" then + fail("event registration options must be a table", 2) + end + local record = adapter_records[self] + local identity = next_identity(self, "event-registration") + local autocmd_options = { + group = event_augroup(record), + desc = "clever-f " .. table.concat(names, "/"), + callback = function(event) + local resource = record.events[identity] + if resource ~= nil and resource.active then + queue_event( + record, + event.event, + event_payload(self, event), + resource.callback + ) + end + end, + } + if options.buffer ~= nil then + autocmd_options.buffer = options.buffer + end + local autocmd_id = record.runtime.api.nvim_create_autocmd( + names, + autocmd_options + ) + record.events[identity] = { + autocmd_id = autocmd_id, + names = names, + name_set = name_set, + callback = callback, + buffer = options.buffer, + active = true, + } + record.event_order[#record.event_order + 1] = identity + return identity +end + +function HostAdapter:remove_event_registration(identity) + local record = adapter_records[self] + local resource = record.events[identity] + if resource == nil or not resource.active then + return false + end + resource.active = false + record.runtime.api.nvim_del_autocmd(resource.autocmd_id) + return true +end + +function HostAdapter:deliver_event(name, payload) + if type(name) ~= "string" or name == "" then + fail("event name must be a nonempty string", 2) + end + payload = payload or {} + local record = adapter_records[self] + local event_buffer = payload.buffer + for _, identity in ipairs(record.event_order) do + local resource = record.events[identity] + if resource.active + and resource.name_set[name] + and (resource.buffer == nil + or event_buffer == nil + or resource.buffer == event_buffer) + then + queue_event(record, name, payload, resource.callback) + end + end +end + +function HostAdapter:begin_action_transition() + return adapter_records[self].event_queue:begin_transition() +end + +function HostAdapter:commit_action_transition(token) + return adapter_records[self].event_queue:commit_transition(token) +end + local function escape_key(runtime) if type(runtime.keycode) == "function" then return runtime.keycode("<Esc>") |
