Created
December 4, 2019 17:19
-
-
Save norcalli/58ec1fb49d6043a2e4940c42f256089b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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