From 7f9a4e77874922046fd5a29beff4e133b7e65988 Mon Sep 17 00:00:00 2001 From: Celso Benedetti Date: Mon, 2 Mar 2026 10:03:34 -0300 Subject: [PATCH 1/2] feat: ClockedIn and ClockedOut events --- lua/orgmode/events/types/clocked_in.lua | 15 +++++++++++++++ lua/orgmode/events/types/clocked_out.lua | 15 +++++++++++++++ lua/orgmode/events/types/init.lua | 2 ++ lua/orgmode/files/headline.lua | 2 ++ 4 files changed, 34 insertions(+) create mode 100644 lua/orgmode/events/types/clocked_in.lua create mode 100644 lua/orgmode/events/types/clocked_out.lua diff --git a/lua/orgmode/events/types/clocked_in.lua b/lua/orgmode/events/types/clocked_in.lua new file mode 100644 index 000000000..6cca066f1 --- /dev/null +++ b/lua/orgmode/events/types/clocked_in.lua @@ -0,0 +1,15 @@ +---@class OrgClockedInEvent: OrgEvent +---@field headline? OrgHeadline +local ClockedInEvent = { + type = 'orgmode.clocked_in', +} +ClockedInEvent.__index = ClockedInEvent + +---@param headline OrgHeadline +function ClockedInEvent:new(headline) + return setmetatable({ + headline = headline, + }, self) +end + +return ClockedInEvent diff --git a/lua/orgmode/events/types/clocked_out.lua b/lua/orgmode/events/types/clocked_out.lua new file mode 100644 index 000000000..8ca277b5c --- /dev/null +++ b/lua/orgmode/events/types/clocked_out.lua @@ -0,0 +1,15 @@ +---@class OrgClockedOutEvent: OrgEvent +---@field headline? OrgHeadline +local ClockedOutEvent = { + type = 'orgmode.clocked_out', +} +ClockedOutEvent.__index = ClockedOutEvent + +---@param headline OrgHeadline +function ClockedOutEvent:new(headline) + return setmetatable({ + headline = headline, + }, self) +end + +return ClockedOutEvent diff --git a/lua/orgmode/events/types/init.lua b/lua/orgmode/events/types/init.lua index 2558280fa..1681e81ee 100644 --- a/lua/orgmode/events/types/init.lua +++ b/lua/orgmode/events/types/init.lua @@ -7,4 +7,6 @@ return { HeadlineDemoted = require('orgmode.events.types.headline_demoted_event'), HeadingToggled = require('orgmode.events.types.heading_toggled'), NoteAdded = require('orgmode.events.types.note_added_event'), + ClockedIn = require('orgmode.events.types.clocked_in'), + ClockedOut = require('orgmode.events.types.clocked_out'), } diff --git a/lua/orgmode/files/headline.lua b/lua/orgmode/files/headline.lua index 024fd1648..712cab68b 100644 --- a/lua/orgmode/files/headline.lua +++ b/lua/orgmode/files/headline.lua @@ -156,6 +156,7 @@ function Headline:clock_in() logbook = Logbook.new_from_headline(self) end logbook:add_clock_in() + EventManager.dispatch(events.ClockedIn:new(self)) return self:refresh() end @@ -163,6 +164,7 @@ function Headline:clock_out() local logbook = self:get_logbook() if logbook then logbook:clock_out() + EventManager.dispatch(events.ClockedOut:new(self)) end return self:refresh() end From 8d50b11230971156fb60e98663c97794e4071bee Mon Sep 17 00:00:00 2001 From: Celso Benedetti Date: Mon, 2 Mar 2026 18:29:21 -0300 Subject: [PATCH 2/2] test: clock events --- tests/plenary/ui/clock_spec.lua | 92 +++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/tests/plenary/ui/clock_spec.lua b/tests/plenary/ui/clock_spec.lua index 29a13515d..385c6f5df 100644 --- a/tests/plenary/ui/clock_spec.lua +++ b/tests/plenary/ui/clock_spec.lua @@ -1,6 +1,8 @@ local helpers = require('tests.plenary.helpers') local Date = require('orgmode.objects.date') local orgmode = require('orgmode') +local EventManager = require('orgmode.events') +local events = EventManager.event describe('Clock', function() local files = {} @@ -211,4 +213,94 @@ describe('Clock', function() '* TODO Test 3', }, vim.api.nvim_buf_get_lines(0, 0, -1, false)) end) + + it('should dispatch ClockedIn event when clocking in', function() + local file = helpers.create_agenda_file({ + '* TODO Clock event test', + }) + + local received_event = nil + local listener = function(event) + received_event = event + end + EventManager.listen(events.ClockedIn, listener) + + vim.cmd('edit ' .. file.filename) + vim.fn.cursor({ 1, 1 }) + vim.cmd([[norm ,oxi]]) + vim.wait(100) + + assert.is_not_nil(received_event) + assert.are.same('orgmode.clocked_in', received_event.type) + assert.are.same('Clock event test', received_event.headline:get_title()) + + -- cleanup listener + local listeners = EventManager._listeners[events.ClockedIn.type] + for i, l in ipairs(listeners) do + if l == listener then + table.remove(listeners, i) + break + end + end + end) + + it('should dispatch ClockedOut event when clocking out', function() + local file = helpers.create_agenda_file({ + '* TODO Clock out event test', + }) + + vim.cmd('edit ' .. file.filename) + vim.fn.cursor({ 1, 1 }) + vim.cmd([[norm ,oxi]]) + vim.wait(100) + + local received_event = nil + local listener = function(event) + received_event = event + end + EventManager.listen(events.ClockedOut, listener) + + vim.fn.cursor({ 1, 1 }) + vim.cmd([[norm ,oxo]]) + + assert.is_not_nil(received_event) + assert.are.same('orgmode.clocked_out', received_event.type) + assert.are.same('Clock out event test', received_event.headline:get_title()) + + -- cleanup listener + local listeners = EventManager._listeners[events.ClockedOut.type] + for i, l in ipairs(listeners) do + if l == listener then + table.remove(listeners, i) + break + end + end + end) + + it('should not dispatch ClockedOut event when headline has no logbook', function() + local file = helpers.create_agenda_file({ + '* TODO No logbook headline', + }) + + local received_event = nil + local listener = function(event) + received_event = event + end + EventManager.listen(events.ClockedOut, listener) + + vim.cmd('edit ' .. file.filename) + local headline = file:get_headlines()[1] + headline:clock_out() + + assert.is_nil(received_event) + + -- cleanup listener + local listeners = EventManager._listeners[events.ClockedOut.type] + for i, l in ipairs(listeners) do + if l == listener then + table.remove(listeners, i) + break + end + end + end) end)