@@ -11,6 +11,12 @@ local cache = {
1111
1212local CACHE_TTL = 20000 -- 20 seconds in milliseconds
1313
14+ -- Job state tracking to prevent multiple concurrent jobs
15+ local job_state = {
16+ blocks = { job_id = nil , pending_callbacks = {}, is_running = false },
17+ availability = { job_id = nil , pending_callbacks = {}, is_running = false },
18+ }
19+
1420--- Check if cached value is still valid
1521--- @param cache_entry table cache entry with data and timestamp
1622--- @return boolean
@@ -31,6 +37,34 @@ local function update_cache(cache_entry, data)
3137 cache_entry .timestamp = vim .uv .now ()
3238end
3339
40+ --- Queue callback for when job is already running
41+ --- @param job_type string either " blocks" or " availability"
42+ --- @param callback function callback to queue
43+ local function queue_callback (job_type , callback )
44+ if callback then
45+ table.insert (job_state [job_type ].pending_callbacks , callback )
46+ end
47+ end
48+
49+ --- Execute all queued callbacks with result
50+ --- @param job_type string either " blocks" or " availability"
51+ --- @param result any result to pass to callbacks
52+ local function execute_queued_callbacks (job_type , result )
53+ local callbacks = job_state [job_type ].pending_callbacks
54+ job_state [job_type ].pending_callbacks = {}
55+
56+ for _ , callback in ipairs (callbacks ) do
57+ callback (result )
58+ end
59+ end
60+
61+ --- Reset job state when job completes
62+ --- @param job_type string either " blocks" or " availability"
63+ local function reset_job_state (job_type )
64+ job_state [job_type ].job_id = nil
65+ job_state [job_type ].is_running = false
66+ end
67+
3468--- Get ccusage blocks data with simple caching
3569--- @param opts ? { bypass_cache ?: boolean , callback ?: fun ( data : CCUsage.Data ?)}
3670--- @return CCUsage.Data ?
@@ -45,9 +79,17 @@ M.ccusage_blocks = function(opts)
4579 return cache .blocks .data
4680 end
4781
82+ -- If job is already running, queue the callback and return cached data
83+ if job_state .blocks .is_running then
84+ queue_callback (" blocks" , opts .callback )
85+ return not opts .bypass_cache and cache .blocks .data or nil
86+ end
87+
88+ -- Start new job
89+ job_state .blocks .is_running = true
4890 local base_cmd = config .options .ccusage_cmd
4991
50- vim .fn .jobstart ({ base_cmd , " blocks" , " --json" , " --offline" }, {
92+ job_state . blocks . job_id = vim .fn .jobstart ({ base_cmd , " blocks" , " --json" , " --offline" }, {
5193 stdout_buffered = true ,
5294 on_stdout = function (_ , data , _ )
5395 if # data > 0 then
@@ -58,16 +100,23 @@ M.ccusage_blocks = function(opts)
58100 if not opts .bypass_cache then
59101 update_cache (cache .blocks , parsed )
60102 end
103+
104+ -- Execute current callback and all queued callbacks
61105 if opts .callback then
62106 opts .callback (parsed )
63107 end
108+ execute_queued_callbacks (" blocks" , parsed )
64109 end
65110 end
66111 end ,
67112 on_exit = function (_ , exit_code , _ )
68- if exit_code ~= 0 and opts .callback then
69- opts .callback (nil )
113+ if exit_code ~= 0 then
114+ if opts .callback then
115+ opts .callback (nil )
116+ end
117+ execute_queued_callbacks (" blocks" , nil )
70118 end
119+ reset_job_state (" blocks" )
71120 end ,
72121 })
73122
@@ -89,10 +138,18 @@ M.is_available = function(opts)
89138 return cache .availability .data
90139 end
91140
141+ -- If job is already running, queue the callback and return cached data
142+ if job_state .availability .is_running then
143+ queue_callback (" availability" , opts .callback )
144+ return not opts .bypass_cache and (cache .availability .data or false ) or false
145+ end
146+
147+ -- Start new job
148+ job_state .availability .is_running = true
92149 local base_cmd = config .options .ccusage_cmd
93150 local found = false
94151
95- vim .fn .jobstart ({ base_cmd , " --version" }, {
152+ job_state . availability . job_id = vim .fn .jobstart ({ base_cmd , " --version" }, {
96153 on_stdout = function (_ , data , _ )
97154 if # data > 0 and data [1 ] ~= " " then
98155 found = true
@@ -104,9 +161,13 @@ M.is_available = function(opts)
104161 if not opts .bypass_cache then
105162 update_cache (cache .availability , available )
106163 end
164+
165+ -- Execute current callback and all queued callbacks
107166 if opts .callback then
108167 opts .callback (available )
109168 end
169+ execute_queued_callbacks (" availability" , available )
170+ reset_job_state (" availability" )
110171 end ,
111172 })
112173
0 commit comments