Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 70 additions & 3 deletions lua/orgmode/clock/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,87 @@ local Input = require('orgmode.ui.input')
---@class OrgClock
---@field files OrgFiles
---@field clocked_headline OrgHeadline|nil
---@field private _clocked_headline_searched boolean Lazy init flag: only search when needed
local Clock = {}

function Clock:new(opts)
local data = {
files = opts.files,
clocked_headline = nil,
_clocked_headline_searched = false,
}
setmetatable(data, self)
self.__index = self
data:init()

data:_schedule_async_preload()

return data
end

-- When first loading, check if there are active clocks
function Clock:init()
---Schedule async preload of clocked headline after files are loaded
function Clock:_schedule_async_preload()
vim.schedule(function()
if self.files.load_state == 'loaded' then
return self:_search_clocked_headline_async()
end

require('orgmode'):on_files_loaded(function()
vim.defer_fn(function()
self:_search_clocked_headline_async()
end, 1)
end)
end)
end

-- Async search for clocked headline - non-blocking background search
function Clock:_search_clocked_headline_async()
if self._clocked_headline_searched then
return
end

-- Don't search if files aren't loaded yet
if self.files.load_state ~= 'loaded' then
return
end

self._clocked_headline_searched = true

self.files:get_clocked_headline_async():next(function(headline)
if headline and headline:is_clocked_in() then
self.clocked_headline = headline
end
end)
end

-- Sync fallback for when immediate access is needed (e.g., clock operations before preload completes)
function Clock:_ensure_clocked_headline_searched()
if self._clocked_headline_searched then
return
end

-- Don't search if files aren't loaded yet
if self.files.load_state ~= 'loaded' then
return
end

self._clocked_headline_searched = true

local emit = require('orgmode.utils.emit')
emit.profile('start', 'clock', 'START (sync fallback)')

local last_clocked_headline = self.files:get_clocked_headline()
if profiler then
profiler.mark('get_clocked_headline() complete')
end

if last_clocked_headline and last_clocked_headline:is_clocked_in() then
self.clocked_headline = last_clocked_headline
end

if profiler then
profiler.mark('COMPLETE')
profiler.finish()
end
end

function Clock:update_clocked_headline()
Expand All @@ -35,6 +97,8 @@ function Clock:update_clocked_headline()
end

function Clock:has_clocked_headline()
-- Ensure we've done the initial search
self:_ensure_clocked_headline_searched()
self:update_clocked_headline()
return self.clocked_headline ~= nil
end
Expand Down Expand Up @@ -114,6 +178,9 @@ function Clock:org_set_effort()
end

function Clock:get_statusline()
-- Lazy init: search for clocked headline on first statusline call
self:_ensure_clocked_headline_searched()

if not self.clocked_headline or not self.clocked_headline:is_clocked_in() then
return ''
end
Expand Down
4 changes: 4 additions & 0 deletions lua/orgmode/config/defaults.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
local DefaultConfig = {
org_agenda_files = '',
org_default_notes_file = '',
org_async_loading = false, -- When true, use progressive async loading for files
org_todo_keywords = { 'TODO', '|', 'DONE' },
org_todo_repeat_to_state = nil,
org_todo_keyword_faces = {},
Expand Down Expand Up @@ -240,6 +241,9 @@ local DefaultConfig = {
},
},
},
profiling = {
enabled = false, -- Enable profiling (default: false for zero overhead)
},
}

return DefaultConfig
1 change: 1 addition & 0 deletions lua/orgmode/events/types/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ return {
HeadlinePromoted = require('orgmode.events.types.headline_promoted_event'),
HeadlineDemoted = require('orgmode.events.types.headline_demoted_event'),
HeadingToggled = require('orgmode.events.types.heading_toggled'),
Profiling = require('orgmode.events.types.profiling_event'),
}
50 changes: 50 additions & 0 deletions lua/orgmode/events/types/profiling_event.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---@class OrgProfilingEvent: OrgEvent
---@field type string Always 'orgmode.profiling'
---@field action 'start'|'mark'|'complete' The profiling action
---@field category string Session category (e.g., 'init', 'files', 'plugin:my-plugin')
---@field label string Human-readable label
---@field payload? table Optional additional data
local ProfilingEvent = {
type = 'orgmode.profiling',
}
ProfilingEvent.__index = ProfilingEvent

---@param action 'start'|'mark'|'complete'
---@param category string
---@param label string
---@param payload? table
---@return OrgProfilingEvent
function ProfilingEvent:new(action, category, label, payload)
local obj = setmetatable({}, self)
obj.action = action
obj.category = category
obj.label = label
obj.payload = payload
return obj
end

---@param category string
---@param label string
---@param payload? table
---@return OrgProfilingEvent
function ProfilingEvent:start(category, label, payload)
return self:new('start', category, label, payload)
end

---@param category string
---@param label string
---@param payload? table
---@return OrgProfilingEvent
function ProfilingEvent:mark(category, label, payload)
return self:new('mark', category, label, payload)
end

---@param category string
---@param label string
---@param payload? table
---@return OrgProfilingEvent
function ProfilingEvent:complete(category, label, payload)
return self:new('complete', category, label, payload)
end

return ProfilingEvent
1 change: 1 addition & 0 deletions lua/orgmode/files/file.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ local Buffers = require('orgmode.state.buffers')
---@field metadata OrgFileMetadata
---@field parser vim.treesitter.LanguageTree
---@field root TSNode
---@field _load_timing? { since_last_ms: number, since_start_ms: number } Progressive loading timing data
local OrgFile = {}

local memoize = Memoize:new(OrgFile, function(self)
Expand Down
Loading
Loading