Skip to content

Commit 2c7cc1a

Browse files
committed
feat(events): Emitter passes event objects. Allow stopping propagation.
1 parent c6c51ab commit 2c7cc1a

File tree

12 files changed

+118
-77
lines changed

12 files changed

+118
-77
lines changed

lua/diffview/bootstrap.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ _G.DiffviewGlobal = {
3838

3939
DiffviewGlobal.emitter:on_any(function(event, args)
4040
local utils = require("diffview.utils")
41-
require("diffview").nore_emit(event, utils.tbl_unpack(args))
42-
require("diffview.config").user_emitter:nore_emit(event, utils.tbl_unpack(args))
41+
require("diffview").nore_emit(event.id, utils.tbl_unpack(args))
42+
require("diffview.config").user_emitter:nore_emit(event.id, utils.tbl_unpack(args))
4343
end)
4444

4545
return true

lua/diffview/config.lua

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,9 @@ function M.setup(user_config)
491491

492492
for event, callback in pairs(M._config.hooks) do
493493
if type(callback) == "function" then
494-
M.user_emitter:on(event, callback)
494+
M.user_emitter:on(event, function (_, ...)
495+
callback(...)
496+
end)
495497
end
496498
end
497499

lua/diffview/events.lua

Lines changed: 96 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ local utils = lazy.require("diffview.utils")
66

77
local M = {}
88

9-
---@enum Event
10-
local Event = {
9+
---@enum EventName
10+
local EventName = {
1111
FILES_STAGED = 1,
1212
}
1313

@@ -18,10 +18,26 @@ local Event = {
1818
---@field callback function The original callback
1919
---@field call function
2020

21+
---@class Event : diffview.Object
22+
---@operator call:Event
23+
---@field id any
24+
---@field propagate boolean
25+
local Event = oop.create_class("Event")
26+
27+
function Event:init(opt)
28+
self.id = opt.id
29+
self.propagate = true
30+
end
31+
32+
function Event:stop_propagation()
33+
self.propagate = false
34+
end
35+
36+
2137
---@class EventEmitter : diffview.Object
22-
---@field event_map table<Event, Listener[]> # Registered events mapped to subscribed listeners.
38+
---@field event_map table<any, Listener[]> # Registered events mapped to subscribed listeners.
2339
---@field any_listeners Listener[] # Listeners subscribed to all events.
24-
---@field emit_lock table<Event, boolean>
40+
---@field emit_lock table<any, boolean>
2541
local EventEmitter = oop.create_class("EventEmitter")
2642

2743
---EventEmitter constructor.
@@ -32,49 +48,48 @@ function EventEmitter:init()
3248
end
3349

3450
---Subscribe to a given event.
35-
---@param event any Event identifier.
36-
---@param callback function
37-
function EventEmitter:on(event, callback)
38-
if not self.event_map[event] then
39-
self.event_map[event] = {}
51+
---@param event_id any Event identifier.
52+
---@param callback fun(event: Event, ...)
53+
function EventEmitter:on(event_id, callback)
54+
if not self.event_map[event_id] then
55+
self.event_map[event_id] = {}
4056
end
4157

42-
table.insert(self.event_map[event], {
58+
table.insert(self.event_map[event_id], 1, {
4359
type = "normal",
4460
callback = callback,
45-
call = function(args)
46-
return callback(utils.tbl_unpack(args))
61+
call = function(event, args)
62+
return callback(event, utils.tbl_unpack(args))
4763
end,
4864
})
4965
end
5066

5167
---Subscribe a one-shot listener to a given event.
52-
---@param event any Event identifier.
53-
---@param callback function
54-
function EventEmitter:once(event, callback)
55-
if not self.event_map[event] then
56-
self.event_map[event] = {}
68+
---@param event_id any Event identifier.
69+
---@param callback fun(event: Event, ...)
70+
function EventEmitter:once(event_id, callback)
71+
if not self.event_map[event_id] then
72+
self.event_map[event_id] = {}
5773
end
5874

5975
local emitted = false
6076

61-
table.insert(self.event_map[event], {
77+
table.insert(self.event_map[event_id], 1, {
6278
type = "once",
6379
callback = callback,
64-
call = function(args)
80+
call = function(event, args)
6581
if not emitted then
6682
emitted = true
67-
self:off(callback, event)
68-
return callback(utils.tbl_unpack(args))
83+
return callback(event, utils.tbl_unpack(args))
6984
end
7085
end,
7186
})
7287
end
7388

7489
---Add a new any-listener, subscribed to all events.
75-
---@param callback function
90+
---@param callback fun(event: Event, ...)
7691
function EventEmitter:on_any(callback)
77-
table.insert(self.any_listeners, {
92+
table.insert(self.any_listeners, 1, {
7893
type = "any",
7994
callback = callback,
8095
call = function(event, args)
@@ -84,11 +99,11 @@ function EventEmitter:on_any(callback)
8499
end
85100

86101
---Add a new one-shot any-listener, subscribed to all events.
87-
---@param callback function
102+
---@param callback fun(event: Event, ...)
88103
function EventEmitter:once_any(callback)
89104
local emitted = false
90105

91-
table.insert(self.any_listeners, {
106+
table.insert(self.any_listeners, 1, {
92107
type = "any_once",
93108
callback = callback,
94109
call = function(event, args)
@@ -103,13 +118,13 @@ end
103118
---Unsubscribe a listener. If no event is given, the listener is unsubscribed
104119
---from all events.
105120
---@param callback function
106-
---@param event? any Only unsubscribe listeners from this event.
107-
function EventEmitter:off(callback, event)
121+
---@param event_id? any Only unsubscribe listeners from this event.
122+
function EventEmitter:off(callback, event_id)
108123
---@type Listener[][]
109124
local all
110125

111-
if event then
112-
all = { self.event_map[event] }
126+
if event_id then
127+
all = { self.event_map[event_id] }
113128
else
114129
all = utils.vec_join(
115130
vim.tbl_values(self.event_map),
@@ -133,63 +148,87 @@ function EventEmitter:off(callback, event)
133148
end
134149

135150
---Clear all listeners for a given event. If no event is given: clear all listeners.
136-
---@param event any?
137-
function EventEmitter:clear(event)
151+
---@param event_id any?
152+
function EventEmitter:clear(event_id)
138153
for e, _ in pairs(self.event_map) do
139-
if event == nil or event == e then
154+
if event_id == nil or event_id == e then
140155
self.event_map[e] = nil
141156
end
142157
end
143158
end
144159

160+
---@param listeners Listener[]
161+
---@param event Event
162+
---@param args table
163+
---@return Listener[]
164+
local function filter_call(listeners, event, args)
165+
listeners = utils.vec_slice(listeners) --[[@as Listener[] ]]
166+
local result = {}
167+
168+
for i = 1, #listeners do
169+
local cur = listeners[i]
170+
local ret = cur.call(event, args)
171+
172+
if not (cur.type == "once" or cur.type == "any_once") and not ret then
173+
result[#result + 1] = cur
174+
end
175+
176+
if not event.propagate then
177+
for j = i + 1, #listeners do result[j] = listeners[j] end
178+
break
179+
end
180+
end
181+
182+
return result
183+
end
184+
145185
---Notify all listeners subscribed to a given event.
146-
---@param event any Event identifier.
186+
---@param event_id any Event identifier.
147187
---@param ... any Event callback args.
148-
function EventEmitter:emit(event, ...)
149-
if not self.emit_lock[event] then
188+
function EventEmitter:emit(event_id, ...)
189+
if not self.emit_lock[event_id] then
150190
local args = utils.tbl_pack(...)
191+
local e = Event({ id = event_id })
151192

152-
if type(self.event_map[event]) == "table" then
153-
self.event_map[event] = utils.tbl_fmap(self.event_map[event], function(listeners)
154-
return not listeners.call(args) and listeners or nil
155-
end)
193+
if type(self.event_map[event_id]) == "table" then
194+
self.event_map[event_id] = filter_call(self.event_map[event_id], e, args)
156195
end
157196

158-
self.any_listeners = utils.tbl_fmap(self.any_listeners, function(listeners)
159-
return not listeners.call(event, args) and listeners or nil
160-
end)
197+
if e.propagate then
198+
self.any_listeners = filter_call(self.any_listeners, e, args)
199+
end
161200
end
162201
end
163202

164203
---Non-recursively notify all listeners subscribed to a given event.
165-
---@param event any Event identifier.
204+
---@param event_id any Event identifier.
166205
---@param ... any Event callback args.
167-
function EventEmitter:nore_emit(event, ...)
168-
if not self.emit_lock[event] then
169-
self.emit_lock[event] = true
206+
function EventEmitter:nore_emit(event_id, ...)
207+
if not self.emit_lock[event_id] then
208+
self.emit_lock[event_id] = true
170209
local args = utils.tbl_pack(...)
210+
local e = Event({ id = event_id })
171211

172-
if type(self.event_map[event]) == "table" then
173-
self.event_map[event] = utils.tbl_fmap(self.event_map[event], function(listeners)
174-
return not listeners.call(args) and listeners or nil
175-
end)
212+
if type(self.event_map[event_id]) == "table" then
213+
self.event_map[event_id] = filter_call(self.event_map[event_id], e, args)
176214
end
177215

178-
self.any_listeners = utils.tbl_fmap(self.any_listeners, function(listeners)
179-
return not listeners.call(event, args) and listeners or nil
180-
end)
216+
if e.propagate then
217+
self.any_listeners = filter_call(self.any_listeners, e, args)
218+
end
181219

182-
self.emit_lock[event] = false
220+
self.emit_lock[event_id] = false
183221
end
184222
end
185223

186224
---Get all listeners subscribed to the given event.
187-
---@param event any Event identifier.
225+
---@param event_id any Event identifier.
188226
---@return Listener[]?
189-
function EventEmitter:get(event)
190-
return self.event_map[event]
227+
function EventEmitter:get(event_id)
228+
return self.event_map[event_id]
191229
end
192230

231+
M.EventName = EventName
193232
M.Event = Event
194233
M.EventEmitter = EventEmitter
195234
return M

lua/diffview/scene/layout.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ function Layout:init(opt)
2323

2424
---@param other Layout
2525
---@diagnostic disable-next-line: unused-local
26-
self.emitter:on("create_pre", function(other)
26+
self.emitter:on("create_pre", function(_, other)
2727
last_equalalways = vim.o.equalalways
2828
vim.opt.equalalways = true
2929
end)
3030

3131
---@param other Layout
32-
self.emitter:on("create_post", function(other)
32+
self.emitter:on("create_post", function(_, other)
3333
other:open_null()
3434
other:open_files()
3535
vim.opt.equalalways = last_equalalways

lua/diffview/scene/view.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function View:init(opt)
5252
self.closing = utils.sate(opt.closing, false)
5353

5454
local function wrap_event(event)
55-
DiffviewGlobal.emitter:on(event, function(view, ...)
55+
DiffviewGlobal.emitter:on(event, function(_, view, ...)
5656
local cur_view = require("diffview.lib").get_current_view()
5757

5858
if (view and view == self) or (not view and cur_view == self) then

lua/diffview/scene/views/diff/diff_view.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ local oop = require("diffview.oop")
44
local CommitLogPanel = lazy.access("diffview.ui.panels.commit_log_panel", "CommitLogPanel") ---@type CommitLogPanel|LazyModule
55
local Diff = lazy.access("diffview.diff", "Diff") ---@type Diff|LazyModule
66
local EditToken = lazy.access("diffview.diff", "EditToken") ---@type EditToken|LazyModule
7-
local Event = lazy.access("diffview.events", "Event") ---@type Event|LazyModule
7+
local EventName = lazy.access("diffview.events", "EventName") ---@type EventName|LazyModule
88
local FileDict = lazy.access("diffview.vcs.file_dict", "FileDict") ---@type FileDict|LazyModule
99
local FileEntry = lazy.access("diffview.scene.file_entry", "FileEntry") ---@type FileEntry|LazyModule
1010
local FilePanel = lazy.access("diffview.scene.views.diff.file_panel", "FilePanel") ---@type FilePanel|LazyModule
@@ -71,7 +71,7 @@ function DiffView:init(opt)
7171
self.attached_bufs = {}
7272

7373
---@param entry FileEntry
74-
self.emitter:on("file_open_post", function(entry)
74+
self.emitter:on("file_open_post", function(_, entry)
7575
if entry.kind == "conflicting" then
7676
local file = entry.layout:get_main_win().file
7777

@@ -466,7 +466,7 @@ function DiffView:file_safeguard()
466466
end
467467

468468
function DiffView:on_files_staged(callback)
469-
self.emitter:on(Event.FILES_STAGED, callback)
469+
self.emitter:on(EventName.FILES_STAGED, callback)
470470
end
471471

472472
function DiffView:init_event_listeners()

lua/diffview/scene/views/diff/listeners.lua

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
local lazy = require("diffview.lazy")
22

3-
local Event = lazy.access("diffview.events", "Event") ---@type Event|LazyModule
3+
local EventName = lazy.access("diffview.events", "EventName") ---@type EventName|LazyModule
44
local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|LazyModule
55
local actions = lazy.require("diffview.actions") ---@module "diffview.actions"
66
local async = lazy.require("plenary.async") ---@module "plenary.async"
@@ -52,7 +52,7 @@ return function(view)
5252
end
5353
end,
5454
---@diagnostic disable-next-line: unused-local
55-
files_updated = function(files)
55+
files_updated = function(_, files)
5656
view.initialized = true
5757
end,
5858
close = function()
@@ -171,7 +171,7 @@ return function(view)
171171
view:update_files(function()
172172
view.panel:highlight_cur_file()
173173
end)
174-
view.emitter:emit(Event.FILES_STAGED, view)
174+
view.emitter:emit(EventName.FILES_STAGED, view)
175175
end
176176
end,
177177
stage_all = function()
@@ -190,7 +190,7 @@ return function(view)
190190
view:update_files(function()
191191
view.panel:highlight_cur_file()
192192
end)
193-
view.emitter:emit(Event.FILES_STAGED, view)
193+
view.emitter:emit(EventName.FILES_STAGED, view)
194194
end
195195
end,
196196
unstage_all = function()
@@ -202,7 +202,7 @@ return function(view)
202202
end
203203

204204
view:update_files()
205-
view.emitter:emit(Event.FILES_STAGED, view)
205+
view.emitter:emit(EventName.FILES_STAGED, view)
206206
end,
207207
restore_entry = async.void(function()
208208
local commit

lua/diffview/scene/views/file_history/file_history_view.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ local lazy = require("diffview.lazy")
22
local oop = require("diffview.oop")
33

44
local CommitLogPanel = lazy.access("diffview.ui.panels.commit_log_panel", "CommitLogPanel") ---@type CommitLogPanel|LazyModule
5-
local Event = lazy.access("diffview.events", "Event") ---@type Event|LazyModule
5+
local EventName = lazy.access("diffview.events", "EventName") ---@type EventName|LazyModule
66
local FileEntry = lazy.access("diffview.scene.file_entry", "FileEntry") ---@type FileEntry|LazyModule
77
local FileHistoryPanel = lazy.access("diffview.scene.views.file_history.file_history_panel", "FileHistoryPanel") ---@type FileHistoryPanel|LazyModule
88
local JobStatus = lazy.access("diffview.vcs.utils", "JobStatus") ---@type JobStatus|LazyModule
@@ -173,7 +173,7 @@ function FileHistoryView:file_safeguard()
173173
end
174174

175175
function FileHistoryView:on_files_staged(callback)
176-
self.emitter:on(Event.FILES_STAGED, callback)
176+
self.emitter:on(EventName.FILES_STAGED, callback)
177177
end
178178

179179
function FileHistoryView:init_event_listeners()

0 commit comments

Comments
 (0)