Skip to content

Commit

Permalink
lua: add simple event multiplexing mechanism
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
martanne committed Dec 8, 2016
1 parent 065a804 commit 6d1d457
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 24 deletions.
1 change: 1 addition & 0 deletions lua/doc/config.ld
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dir="."
sort=true
merge=true
no_space_before_args=true
not_luadoc=true
file={
"../../vis-lua.c",
"../vis.lua",
Expand Down
4 changes: 2 additions & 2 deletions lua/plugins/filetype.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -425,5 +425,5 @@ vis.filetype_detect = function(win)
end

win.syntax = nil
end
end)

16 changes: 8 additions & 8 deletions lua/vis-std.lua
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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] = '',
Expand All @@ -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
Expand Down Expand Up @@ -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"))
102 changes: 100 additions & 2 deletions lua/vis.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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')
11 changes: 4 additions & 7 deletions lua/visrc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
11 changes: 6 additions & 5 deletions vis-lua.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 6d1d457

Please sign in to comment.