-
-
Notifications
You must be signed in to change notification settings - Fork 178
Expand file tree
/
Copy pathinit.lua
More file actions
196 lines (161 loc) · 5.43 KB
/
init.lua
File metadata and controls
196 lines (161 loc) · 5.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
local Duration = require('orgmode.objects.duration')
local utils = require('orgmode.utils')
local Promise = require('orgmode.utils.promise')
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:_schedule_async_preload()
return data
end
---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()
local last_clocked_headline = self.files:get_clocked_headline()
if last_clocked_headline and last_clocked_headline:is_clocked_in() then
self.clocked_headline = last_clocked_headline
end
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
function Clock:org_clock_in()
self:update_clocked_headline()
local item = self.files:get_closest_headline()
if item:is_clocked_in() then
return utils.echo_info(string.format('Clock continues in "%s"', item:get_title()))
end
local promise = Promise.resolve()
if self.clocked_headline and self.clocked_headline:is_clocked_in() then
local file = self.clocked_headline.file
promise = file:update(function()
local clocked_item = file:reload_sync():get_closest_headline({ self.clocked_headline:get_range().start_line, 0 })
clocked_item:clock_out()
end)
end
return promise:next(function()
item:clock_in()
self.clocked_headline = item
end)
end
function Clock:org_clock_out()
self:update_clocked_headline()
if not self.clocked_headline or not self.clocked_headline:is_clocked_in() then
return
end
self.clocked_headline:clock_out()
self.clocked_headline = nil
end
function Clock:org_clock_cancel()
self:update_clocked_headline()
if not self.clocked_headline or not self.clocked_headline:is_clocked_in() then
return utils.echo_info('No active clock')
end
self.clocked_headline:cancel_active_clock()
self.clocked_headline = nil
utils.echo_info('Clock canceled')
end
function Clock:org_clock_goto()
self:update_clocked_headline()
if not self.clocked_headline then
return utils.echo_info('No active or recent clock task')
end
if not self.clocked_headline:is_clocked_in() then
utils.echo_info('No running clock, this is the most recently clocked task')
end
utils.goto_headline(self.clocked_headline)
end
function Clock:org_set_effort()
local item = self.files:get_closest_headline()
-- TODO: Add Effort_ALL property as autocompletion
local current_effort = item:get_property('Effort')
return Input.open('Effort: ', current_effort or ''):next(function(effort)
if not effort then
return false
end
local duration = Duration.parse(effort)
if duration == nil then
return utils.echo_error('Invalid duration format: ' .. effort)
end
item:set_property('Effort', effort)
return item
end)
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
local effort = self.clocked_headline:get_property('effort', false)
local total = self.clocked_headline:get_logbook():get_total_with_active():to_string()
if effort then
return string.format('(Org) [%s/%s] (%s)', total, effort or '', self.clocked_headline:get_title())
end
return string.format('(Org) [%s] (%s)', total, self.clocked_headline:get_title())
end
return Clock