Skip to content

Commit 9719bf3

Browse files
Add event dispatcher (#411)
1 parent 35aa01c commit 9719bf3

File tree

10 files changed

+165
-5
lines changed

10 files changed

+165
-5
lines changed

lua/orgmode/events/init.lua

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
local Events = require('orgmode.events.types')
2+
local Listeners = require('orgmode.events.listeners')
3+
4+
---@class EventManager
5+
local EventManager = {
6+
initialized = false,
7+
_listeners = {},
8+
event = Events,
9+
}
10+
11+
---@param event Event
12+
function EventManager.dispatch(event)
13+
if EventManager._listeners[event.type] then
14+
for _, listener in ipairs(EventManager._listeners[event.type]) do
15+
listener(event)
16+
end
17+
end
18+
end
19+
20+
---@param event Event
21+
---@param listener fun(...)
22+
function EventManager.listen(event, listener)
23+
if not EventManager._listeners[event.type] then
24+
EventManager._listeners[event.type] = {}
25+
end
26+
if not vim.tbl_contains(EventManager._listeners[event.type], listener) then
27+
table.insert(EventManager._listeners[event.type], listener)
28+
end
29+
end
30+
31+
function EventManager.init()
32+
if EventManager.initialized then
33+
return
34+
end
35+
for event, listeners in pairs(Listeners) do
36+
for _, listener in ipairs(listeners) do
37+
EventManager.listen(event, listener)
38+
end
39+
end
40+
EventManager.initialized = true
41+
return EventManager
42+
end
43+
44+
return EventManager
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---@param event TodoChangedEvent | HeadlineDemotedEvent | HeadlinePromotedEvent
2+
return function(event)
3+
event.headline:align_tags()
4+
end

lua/orgmode/events/listeners/init.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
local Events = require('orgmode.events.types')
2+
local AlignTags = require('orgmode.events.listeners.align_tags')
3+
4+
return {
5+
[Events.TodoChanged] = {
6+
AlignTags,
7+
},
8+
[Events.HeadlineDemoted] = {
9+
AlignTags,
10+
},
11+
[Events.HeadlinePromoted] = {
12+
AlignTags,
13+
},
14+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---@class HeadlineDemotedEvent: Event
2+
---@field type string
3+
---@field section Section
4+
---@field headline Headline
5+
---@field old_headline? string
6+
7+
local HeadlineDemotedEvent = {
8+
type = 'orgmode.headline_demoted',
9+
}
10+
11+
---@param section Section
12+
---@param headline Headline
13+
---@param old_level number
14+
function HeadlineDemotedEvent:new(section, headline, old_level)
15+
local obj = setmetatable({}, self)
16+
self.__index = self
17+
obj.section = section
18+
obj.headline = headline
19+
obj.old_level = old_level
20+
return obj
21+
end
22+
23+
return HeadlineDemotedEvent
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---@class HeadlinePromotedEvent: Event
2+
---@field type string
3+
---@field section Section
4+
---@field headline Headline
5+
---@field old_headline? string
6+
7+
local HeadlinePromotedEvent = {
8+
type = 'orgmode.headline_promoted',
9+
}
10+
11+
---@param section Section
12+
---@param headline Headline
13+
---@param old_level number
14+
function HeadlinePromotedEvent:new(section, headline, old_level)
15+
local obj = setmetatable({}, self)
16+
self.__index = self
17+
obj.section = section
18+
obj.headline = headline
19+
obj.old_level = old_level
20+
return obj
21+
end
22+
23+
return HeadlinePromotedEvent

lua/orgmode/events/types/init.lua

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---@class Event
2+
---@field type string
3+
4+
return {
5+
TodoChanged = require('orgmode.events.types.todo_changed_event'),
6+
HeadlinePromoted = require('orgmode.events.types.headline_promoted_event'),
7+
HeadlineDemoted = require('orgmode.events.types.headline_demoted_event'),
8+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---@class TodoChangedEvent: Event
2+
---@field type string
3+
---@field section Section
4+
---@field headline Headline
5+
---@field old_todo_state? string
6+
---@field is_done? boolean
7+
local TodoChangedEvent = {
8+
type = 'orgmode.todo_changed',
9+
}
10+
11+
---@param section Section
12+
---@param headline Headline
13+
---@param old_todo_state? string
14+
---@param is_done? string
15+
function TodoChangedEvent:new(section, headline, old_todo_state, is_done)
16+
local obj = setmetatable({}, self)
17+
self.__index = self
18+
obj.section = section
19+
obj.headline = headline
20+
obj.old_todo_state = old_todo_state
21+
obj.is_done = is_done
22+
return obj
23+
end
24+
25+
return TodoChangedEvent

lua/orgmode/init.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ function Org:init()
2525
return
2626
end
2727
require('orgmode.colors.custom_highlighter').setup()
28+
require('orgmode.events').init()
2829
self.files = require('orgmode.parser.files').new()
2930
self.agenda = require('orgmode.agenda'):new()
3031
self.capture = require('orgmode.capture'):new()

lua/orgmode/org/mappings.lua

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ local ts_utils = require('nvim-treesitter.ts_utils')
1212
local utils = require('orgmode.utils')
1313
local ts_org = require('orgmode.treesitter')
1414
local ts_table = require('orgmode.treesitter.table')
15+
local EventManager = require('orgmode.events')
16+
local events = EventManager.event
1517

1618
---@class OrgMappings
1719
---@field capture Capture
@@ -381,10 +383,18 @@ function OrgMappings:_todo_change_state(direction)
381383
return
382384
end
383385
local item = Files.get_closest_headline()
384-
if not item:is_done() and not was_done then
386+
387+
local dispatchEvent = function()
388+
EventManager.dispatch(
389+
events.TodoChanged:new(Files.get_closest_headline(), ts_org.closest_headline(), old_state, was_done)
390+
)
385391
return item
386392
end
387393

394+
if not item:is_done() and not was_done then
395+
return dispatchEvent()
396+
end
397+
388398
local repeater_dates = item:get_repeater_dates()
389399
if #repeater_dates == 0 then
390400
local log_time = config.org_log_done == 'time'
@@ -394,7 +404,7 @@ function OrgMappings:_todo_change_state(direction)
394404
if log_time and not item:is_done() and was_done then
395405
headline:remove_closed_date()
396406
end
397-
return item
407+
return dispatchEvent()
398408
end
399409

400410
for _, date in ipairs(repeater_dates) do
@@ -408,31 +418,36 @@ function OrgMappings:_todo_change_state(direction)
408418
local data = item:add_properties({ LAST_REPEAT = '[' .. Date.now():to_string() .. ']' })
409419
if data.is_new then
410420
vim.fn.append(data.end_line, data.indent .. state_change)
411-
return item
421+
return dispatchEvent()
412422
end
413423
item = Files.get_closest_headline()
414424

415425
if item.properties.valid then
416426
vim.fn.append(item.properties.range.end_line, data.indent .. state_change)
417427
end
428+
dispatchEvent()
418429
end
419430

420431
function OrgMappings:do_promote(whole_subtree)
421432
local item = Files.get_closest_headline()
433+
local old_level = item.level
422434
local foldclosed = vim.fn.foldclosed('.')
423435
item:promote(1, whole_subtree)
424436
if foldclosed > -1 and vim.fn.foldclosed('.') == -1 then
425437
vim.cmd([[norm!zc]])
426438
end
439+
EventManager.dispatch(events.HeadlinePromoted:new(Files.get_closest_headline(), ts_org.closest_headline(), old_level))
427440
end
428441

429442
function OrgMappings:do_demote(whole_subtree)
430443
local item = Files.get_closest_headline()
444+
local old_level = item.level
431445
local foldclosed = vim.fn.foldclosed('.')
432446
item:demote(1, whole_subtree)
433447
if foldclosed > -1 and vim.fn.foldclosed('.') == -1 then
434448
vim.cmd([[norm!zc]])
435449
end
450+
EventManager.dispatch(events.HeadlineDemoted:new(Files.get_closest_headline(), ts_org.closest_headline(), old_level))
436451
end
437452

438453
function OrgMappings:org_return()

tests/plenary/ui/mappings_spec.lua

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,10 @@ describe('Mappings', function()
353353
vim.fn.cursor(5, 1)
354354
assert.are.same('** TODO [#A] Test orgmode level 2 :PRIVATE:', vim.fn.getline('.'))
355355
vim.cmd([[norm <<]])
356-
assert.are.same('* TODO [#A] Test orgmode level 2 :PRIVATE:', vim.fn.getline('.'))
356+
assert.are.same(
357+
'* TODO [#A] Test orgmode level 2 :PRIVATE:',
358+
vim.fn.getline('.')
359+
)
357360
end)
358361

359362
it('should promote the heading and its subtree (org_promote_subtree)', function()
@@ -383,7 +386,7 @@ describe('Mappings', function()
383386
assert.are.same({
384387
'* TODO Test orgmode',
385388
' DEADLINE: <2021-07-21 Wed 22:02>',
386-
'* TODO [#A] Test orgmode level 2 :PRIVATE:',
389+
'* TODO [#A] Test orgmode level 2 :PRIVATE:',
387390
'Some content for level 2',
388391
'** NEXT [#1] Level 3',
389392
'Content Level 3',

0 commit comments

Comments
 (0)