Skip to content

Instantly share code, notes, and snippets.

@norcalli
Created December 4, 2019 17:19
Show Gist options
  • Save norcalli/58ec1fb49d6043a2e4940c42f256089b to your computer and use it in GitHub Desktop.
Save norcalli/58ec1fb49d6043a2e4940c42f256089b to your computer and use it in GitHub Desktop.
local trie_maker = require 'trie'
local vim = vim
local api = vim.api
local alphabet = 'abcdefghijklmnopqrstuvwxyz'
local Trie = trie_maker('.0123456789'..alphabet..alphabet:upper())
-- local Trie = trie_maker('^.0123456789'..alphabet..alphabet:upper())
local M = {}
local function log_pcall_err(message, status, ...)
if not status then
api.nvim_err_writeln(message..': '..select(1, ...))
vim.cmd 'redraw'
return
end
return ...
end
local function pcall_log(message, fn, ...)
assert(type(message) == 'string', 'need a message')
return log_pcall_err(message, pcall(fn, ...))
end
local function empty_table()
return {}
end
local function setget(t, k, factory)
local v = t[k]
if not v then
v = factory()
t[k] = v
end
return v
end
function M.reset()
vim.cmd "augroup K_ONEVENT | autocmd! | augroup END"
M.autocmds = setmetatable({}, {
__index = function(t, evt)
vim.cmd(string.format("autocmd K_ONEVENT %s * lua require'events'.dispatch(%q)", evt, evt))
local v = {}
v.suffix_trie = Trie()
v.suffix_map = {}
v.basename_map = {}
v.unconditional = {}
rawset(t, evt, v)
return v
end
})
end
M.reset()
-- TODO(ashkan) try to find a better basename
local function basename(fname)
return vim.fn.fnamemodify(fname, ":t")
end
-- Examples
-- ```lua
-- events.on_event("BufEnter", "*", print)
-- events.on_event("BufEnter", { ext = {".lua"} }, print)
-- events.on_event("BufEnter", { ext = ".lua" }, print)
-- ```
function M.on_event(evt, patterns, callback)
local A = assert(M.autocmds[evt])
if type(patterns) == 'table' then
-- TODO(ashkan) error on extra keys in patterns?
vim.validate {
extensions = {patterns.ext, 't', true};
basenames = {patterns.basename, 't', true};
}
if patterns.ext then
if type(patterns.ext) == 'string' then
patterns.ext = {patterns.ext}
end
for _, v in ipairs(patterns.ext) do
local key = v:reverse()
A.suffix_trie:insert(key)
table.insert(setget(A.suffix_map, key, empty_table), callback)
end
end
if patterns.basename then
if type(patterns.basename) == 'string' then
patterns.basename = {patterns.basename}
end
for _, v in ipairs(patterns.basename) do
table.insert(setget(A.basename_map, basename(v), empty_table), callback)
end
end
elseif patterns == "*" then
table.insert(A.unconditional, callback)
else
error("pattern of type "..type(patterns).." is unsupported.")
end
end
function M.dispatch(evt, input)
input = input or vim.fn.expand("<amatch>")
local A = assert(M.autocmds[evt])
local callbacks = vim.tbl_flatten{
A.basename_map[basename(input)] or {};
A.suffix_map[A.suffix_trie:longest_prefix(input:reverse())] or {};
A.unconditional or {};
}
for _, fn in ipairs(callbacks) do
pcall_log("on_event dispatch failed", fn, evt, input)
end
end
return M
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment