…lines 1–302 unchanged (302 lines)
303 )
304 end
305
306 return urls
307end
308
309do
310 ---@class ProgressMessage
311 ---@field id? number|string ID of the progress message
312 ---@field title? string Title of the progress message
313 ---@field status string Status: "running" | "success" | "failed" | "cancel"
314 ---@field percent? integer Percent complete (0–100)
315 ---@private
316
317 --- Cache of active progress messages, keyed by msg_id
318 --- TODO(justinmk): visibility of "stale" (never-finished) Progress. https://github.com/neovim/neovim/pull/35428#discussion_r2942696157
319 ---@type table<integer, ProgressMessage>
320 local progress = {}
321
322 -- store progress events
323 local progress_group, progress_autocmd = nil, nil
324
325 ---Initialize progress event listeners
326 local function progress_init()
327 progress_group = vim.api.nvim_create_augroup('nvim.ui.progress_status', { clear = true })
328 progress_autocmd = vim.api.nvim_create_autocmd('Progress', {
329 group = progress_group,
330 desc = 'Tracks progress messages for vim.ui.progress_status()',
331 ---@param ev {data: {id: integer, title: string, status: string, percent: integer}}
332 callback = function(ev)
333 if not ev.data or not ev.data.id then
334 return
335 end
336 progress[ev.data.id] = {
337 id = ev.data.id,
338 title = ev.data.title,
339 status = ev.data.status,
340 percent = ev.data.percent or 0,
341 }
342
343 -- Clear finished items
344 if
345 ev.data.status == 'success'
346 or ev.data.percent == 100
347 or ev.data.status == 'failed'
348 or ev.data.status == 'cancel'
349 then
350 progress[ev.data.id] = nil
351 end
352 end,
353 })
354 end
355
356 ---Return statusline text summarizing progress messages.
357 --- - If none: returns empty string
358 --- - If one running item: "title: 42%"
359 --- - If multiple running items: "Progress: N items AVG%"
360 ---@param running ProgressMessage[]
361 ---@return string
362 local function progress_status_fmt(running)
363 local count = #running
364 if count == 0 then
365 return '' -- nothing to show
366 elseif count == 1 then
367 local progress_item = running[1]
368 if progress_item.title == nil then
369 return string.format('%d%%%% ', progress_item.percent or 0)
370 end
371 return string.format('%s: %d%%%% ', progress_item.title, progress_item.percent or 0)
372 else
373 local sum = 0 ---@type integer
374 for _, progress_item in ipairs(running) do
375 sum = sum + (progress_item.percent or 0)
376 end
377 local avg = math.floor(sum / count)
378 return string.format('Progress: %d items %d%%%% ', count, avg)
379 end
380 end
381
382 --- Gets the status of currently running progress messages, in a format
383 --- convenient for inclusion in 'statusline'.
384 ---@return string formatted text of progress status for statusline
385 function M.progress_status()
386 -- Create progress event listener on first call
387 if progress_autocmd == nil then
388 progress_init()
389 end
390
391 local running = vim.tbl_values(progress)
392 return progress_status_fmt(running) or ''
393 end
394end
395
396return M
397