From 6d1d45776b231304b0ff41b5e6098f07931ec44e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Andr=C3=A9=20Tanner?= Date: Thu, 8 Dec 2016 09:06:01 +0100 Subject: lua: add simple event multiplexing mechanism The editor core calls into the functions registered in the `vis.events` table which then multiplex the events to all registered event handlers. The first handler which returns a non `nil` value terminates event propagation. --- lua/doc/config.ld | 1 + lua/plugins/filetype.lua | 4 +- lua/vis-std.lua | 16 ++++---- lua/vis.lua | 102 ++++++++++++++++++++++++++++++++++++++++++++++- lua/visrc.lua | 11 ++--- vis-lua.c | 11 ++--- 6 files changed, 121 insertions(+), 24 deletions(-) diff --git a/lua/doc/config.ld b/lua/doc/config.ld index a955b38..a979f83 100644 --- a/lua/doc/config.ld +++ b/lua/doc/config.ld @@ -7,6 +7,7 @@ dir="." sort=true merge=true no_space_before_args=true +not_luadoc=true file={ "../../vis-lua.c", "../vis.lua", diff --git a/lua/plugins/filetype.lua b/lua/plugins/filetype.lua index e308ac0..7101e4c 100644 --- a/lua/plugins/filetype.lua +++ b/lua/plugins/filetype.lua @@ -356,7 +356,7 @@ vis.ftdetect.filetypes = { }, } -vis.filetype_detect = function(win) +vis.events.subscribe(vis.events.WIN_OPEN, function(win) local name = win.file.name -- remove ignored suffixes from filename local sanitizedfn = name @@ -425,5 +425,5 @@ vis.filetype_detect = function(win) end win.syntax = nil -end +end) diff --git a/lua/vis-std.lua b/lua/vis-std.lua index d8a32ad..a46896a 100644 --- a/lua/vis-std.lua +++ b/lua/vis-std.lua @@ -1,6 +1,6 @@ -- standard vis event handlers -vis.events.theme_change = function(name) +vis.events.subscribe(vis.events.THEME_CHANGE, function(name) if name ~= nil then local theme = 'themes/'..name package.loaded[theme] = nil @@ -12,9 +12,9 @@ vis.events.theme_change = function(name) for win in vis:windows() do win.syntax = win.syntax; end -end +end) -vis.events.win_syntax = function(win, name) +vis.events.subscribe(vis.events.WIN_SYNTAX, function(win, name) local lexers = vis.lexers if not lexers.load then return false end @@ -37,9 +37,9 @@ vis.events.win_syntax = function(win, name) end return true -end +end) -vis.events.win_highlight = function(win, horizon_max) +vis.events.subscribe(vis.events.WIN_HIGHLIGHT, function(win, horizon_max) if win.syntax == nil or vis.lexers == nil then return end local lexer = vis.lexers.load(win.syntax) if lexer == nil then return end @@ -67,7 +67,7 @@ vis.events.win_highlight = function(win, horizon_max) end token_start = token_end end -end +end) local modes = { [vis.MODE_NORMAL] = '', @@ -78,7 +78,7 @@ local modes = { [vis.MODE_REPLACE] = 'REPLACE', } -vis.events.win_status = function(win) +vis.events.subscribe(vis.events.WIN_STATUS, function(win) local left_parts = {} local right_parts = {} local file = win.file @@ -114,6 +114,6 @@ vis.events.win_status = function(win) local left = ' ' .. table.concat(left_parts, " » ") .. ' ' local right = ' ' .. table.concat(right_parts, " « ") .. ' ' win:status(left, right); -end +end) vis:command("set theme ".. (vis.ui.colors <= 16 and "default-16" or "default-256")) diff --git a/lua/vis.lua b/lua/vis.lua index 5a4af8d..c5ab47e 100644 --- a/lua/vis.lua +++ b/lua/vis.lua @@ -14,8 +14,6 @@ if not ok then vis:info('WARNING: could not load lexer module, is lpeg installed?') end -vis.events = {} - --- Map a new motion. -- -- Sets up a mapping in normal, visual and operator pending mode. @@ -71,4 +69,104 @@ vis.textobject_new = function(vis, key, textobject) return true end +--- Events. +-- +-- User scripts can subscribe Lua functions to certain events. Multiple functions +-- can be associated with the same event. They will be called in the order they were +-- registered. The first function which returns a non `nil` value terminates event +-- propagation. The remaining event handler will not be called. +-- +-- Keep in mind that the editor is blocked while the event handlers +-- are being executed, avoid long running tasks. +-- +-- @section Events + +--- Event names. +--- @table events +local events = { + FILE_CLOSE = "Event::FILE_CLOSE", -- see @{file_close} + FILE_OPEN = "Event::FILE_OPEN", -- see @{file_open} + FILE_SAVE_POST = "Event::FILE_SAVE_POST", -- see @{file_save_post} + FILE_SAVE_PRE = "Event::FILE_SAVE_PRE", -- see @{file_save_pre} + QUIT = "Event::QUIT", -- see @{quit} + START = "Event::START", -- see @{start} + THEME_CHANGE = "Event::THEME_CHANGE", -- see @{theme_change} + WIN_CLOSE = "Event::WIN_CLOSE", -- see @{win_close} + WIN_HIGHLIGHT = "Event::WIN_HIGHLIGHT", -- see @{win_highlight} + WIN_OPEN = "Event::WIN_OPEN", -- see @{win_open} + WIN_STATUS = "Event::WIN_STATUS", -- see @{win_status} + WIN_SYNTAX = "Event::WIN_SYNTAX", -- see @{win_syntax} +} + +events.file_close = function(...) events.emit(events.FILE_CLOSE, ...) end +events.file_open = function(...) events.emit(events.FILE_OPEN, ...) end +events.file_save_post = function(...) events.emit(events.FILE_SAVE_POST, ...) end +events.file_save_pre = function(...) return events.emit(events.FILE_SAVE_PRE, ...) end +events.quit = function(...) events.emit(events.QUIT, ...) end +events.start = function(...) events.emit(events.START, ...) end +events.theme_change = function(...) events.emit(events.THEME_CHANGE, ...) end +events.win_close = function(...) events.emit(events.WIN_CLOSE, ...) end +events.win_highlight = function(...) events.emit(events.WIN_HIGHLIGHT, ...) end +events.win_open = function(...) events.emit(events.WIN_OPEN, ...) end +events.win_status = function(...) events.emit(events.WIN_STATUS, ...) end +events.win_syntax = function(...) return events.emit(events.WIN_SYNTAX, ...) end + +local handlers = {} + +--- Subscribe to an event. +-- +-- Register an event handler. +-- @tparam string event the event name +-- @tparam function handler the event handler +-- @tparam[opt] int index the index at which to insert the handler (1 is the highest priority) +-- @usage +-- vis.events.subscribe(vis.events.FILE_SAVE_PRE, function(file, path) +-- -- do something useful +-- return true +-- end) +events.subscribe = function(event, handler, index) + if not event then error("Invalid event name") end + if type(handler) ~= 'function' then error("Invalid event handler") end + if not handlers[event] then handlers[event] = {} end + events.unsubscribe(event, handler) + table.insert(handlers[event], index or #handlers[event]+1, handler) +end + +--- Unsubscribe from an event. +-- +-- Remove a registered event handler. +-- @tparamm string event the event name +-- @tparam function handler the event handler to unsubscribe +-- @treturn bool whether the handler was successfully removed +events.unsubscribe = function(event, handler) + local h = handlers[event] + if not h then return end + for i = 1, #h do + if h[i] == handler then + table.remove(h, i) + return true + end + end + return false +end + +--- Generate event. +-- +-- Invokes all event handlers in the order they were registered. +-- Passes all arguments to the handler. The first handler which returns a non `nil` +-- value terminates the event propagation. The other handlers will not be called. +-- +-- @tparam string event the event name +-- @tparam ... ... the remaining paramters are passed on to the handler +events.emit = function(event, ...) + local h = handlers[event] + if not h then return end + for i = 1, #h do + local ret = h[i](...) + if type(ret) ~= 'nil' then return ret end + end +end + +vis.events = events + require('vis-std') diff --git a/lua/visrc.lua b/lua/visrc.lua index 4c44f6a..1a304b4 100644 --- a/lua/visrc.lua +++ b/lua/visrc.lua @@ -3,15 +3,12 @@ require('vis') require('plugins/filetype') require('plugins/textobject-lexer') -vis.events.start = function() +vis.events.subscribe(vis.events.START, function() -- Your global configuration options e.g. -- vis:command('map! normal j gj') -end - -vis.events.win_open = function(win) - -- enable syntax highlighting for known file types - vis.filetype_detect(win) +end) +vis.events.subscribe(vis.events.WIN_OPEN, function(win) -- Your per window configuration options e.g. -- vis:command('set number') -end +end) diff --git a/vis-lua.c b/vis-lua.c index f3b0114..29f3103 100644 --- a/vis-lua.c +++ b/vis-lua.c @@ -1733,13 +1733,14 @@ static const struct luaL_Reg file_lines_funcs[] = { */ /*** - * Events. + * Core Events. * * These events are invoked from the editor core. - * The following functions are looked up in the `vis.events` table. - * Keep in mind that the editor is blocked while the event handlers - * are being executed, avoid long running tasks. - * @section Events + * The following functions are invoked if they are registered in the + * `vis.events` table. Users scripts should generally use the [Events](#events) + * mechanism instead which multiplexes these core events. + * + * @section Core Events */ static void vis_lua_event_get(lua_State *L, const char *name) { -- cgit v1.2.3