LCOV - code coverage report
Current view: top level - /lua/yoda - timer_manager.lua (source / functions) Coverage Total Hit
Test: lcov.info Lines: 100.0 % 100 100
Test Date: 2026-04-14 10:33:13 Functions: - 0 0

            Line data    Source code
       1              : -- lua/yoda/timer_manager.lua
       2              : -- Centralized timer management to prevent memory leaks
       3              : 
       4           26 : local M = {}
       5              : 
       6              : -- ============================================================================
       7              : -- Private State
       8              : -- ============================================================================
       9              : 
      10           26 : local active_timers = {}
      11           26 : local active_vim_timers = {}
      12           26 : local timer_id_counter = 0
      13           26 : local total_timers_created = 0
      14              : 
      15              : -- ============================================================================
      16              : -- Helper Functions
      17              : -- ============================================================================
      18              : 
      19              : --- Generate unique timer ID
      20              : --- @return string
      21              : local function generate_timer_id()
      22           10 :   timer_id_counter = timer_id_counter + 1
      23           10 :   return "timer_" .. timer_id_counter
      24           26 : end
      25              : 
      26              : --- Wrap callback in pcall for safety
      27              : --- @param callback function
      28              : --- @param timer_id string
      29              : --- @return function
      30              : local function wrap_callback(callback, timer_id)
      31              :   return function(...)
      32           11 :     local ok, err = pcall(callback, ...)
      33           11 :     if not ok then
      34              :       vim.notify("Timer callback error [" .. timer_id .. "]: " .. tostring(err), vim.log.levels.ERROR)
      35              :     end
      36           41 :   end
      37           26 : end
      38              : 
      39              : -- ============================================================================
      40              : -- Public API - vim.loop timers
      41              : -- ============================================================================
      42              : 
      43              : --- Create a new vim.loop timer with automatic cleanup
      44              : --- @param callback function Timer callback
      45              : --- @param timeout number Timeout in milliseconds
      46              : --- @param repeat_interval number|nil Repeat interval (0 for one-shot)
      47              : --- @param timer_id string|nil Optional timer ID for tracking
      48              : --- @return table timer Timer handle
      49              : --- @return string id Timer ID
      50           26 : function M.create_timer(callback, timeout, repeat_interval, timer_id)
      51           13 :   timer_id = timer_id or generate_timer_id()
      52            8 :   repeat_interval = repeat_interval or 0
      53              : 
      54            8 :   local timer = vim.uv.new_timer()
      55            8 :   if not timer then
      56              :     vim.notify("Failed to create timer: " .. timer_id, vim.log.levels.ERROR)
      57              :     return nil, nil
      58              :   end
      59              : 
      60            8 :   local wrapped_callback = wrap_callback(callback, timer_id)
      61              : 
      62            8 :   active_timers[timer_id] = {
      63            8 :     timer = timer,
      64            8 :     callback = callback,
      65            8 :     timeout = timeout,
      66            8 :     repeat_interval = repeat_interval,
      67            8 :   }
      68              : 
      69           16 :   timer:start(timeout, repeat_interval, vim.schedule_wrap(wrapped_callback))
      70              : 
      71            8 :   total_timers_created = total_timers_created + 1
      72              : 
      73            8 :   return timer, timer_id
      74           26 : end
      75              : 
      76              : --- Stop and cleanup a vim.loop timer
      77              : --- @param timer_id string Timer ID
      78           26 : function M.stop_timer(timer_id)
      79            9 :   local timer_data = active_timers[timer_id]
      80            9 :   if not timer_data then
      81            1 :     return
      82              :   end
      83              : 
      84            8 :   local timer = timer_data.timer
      85            8 :   if timer and not timer:is_closing() then
      86            8 :     timer:stop()
      87            8 :     timer:close()
      88              :   end
      89              : 
      90            8 :   active_timers[timer_id] = nil
      91           34 : end
      92              : 
      93              : --- Check if timer is active
      94              : --- @param timer_id string Timer ID
      95              : --- @return boolean
      96           26 : function M.is_timer_active(timer_id)
      97            6 :   return active_timers[timer_id] ~= nil
      98           26 : end
      99              : 
     100              : -- ============================================================================
     101              : -- Public API - vim.fn.timer_start timers
     102              : -- ============================================================================
     103              : 
     104              : --- Create a vim.fn.timer_start timer with automatic cleanup
     105              : --- @param callback function Timer callback
     106              : --- @param delay number Delay in milliseconds
     107              : --- @param timer_id string|nil Optional timer ID for tracking
     108              : --- @return number|nil timer_handle Timer handle
     109              : --- @return string|nil id Timer ID
     110           26 : function M.create_vim_timer(callback, delay, timer_id)
     111           27 :   timer_id = timer_id or generate_timer_id()
     112              : 
     113           44 :   local wrapped_callback = wrap_callback(function()
     114           11 :     callback()
     115           11 :     active_vim_timers[timer_id] = nil
     116           33 :   end, timer_id)
     117              : 
     118           24 :   local timer_handle = vim.fn.timer_start(delay, wrapped_callback)
     119              : 
     120           22 :   if timer_handle == -1 then
     121              :     vim.notify("Failed to create vim timer: " .. timer_id, vim.log.levels.ERROR)
     122              :     return nil, nil
     123              :   end
     124              : 
     125           22 :   active_vim_timers[timer_id] = {
     126           22 :     handle = timer_handle,
     127           22 :     callback = callback,
     128           22 :     delay = delay,
     129           22 :   }
     130              : 
     131           22 :   total_timers_created = total_timers_created + 1
     132              : 
     133           22 :   return timer_handle, timer_id
     134           26 : end
     135              : 
     136              : --- Stop a vim.fn.timer_start timer
     137              : --- @param timer_id string Timer ID
     138           26 : function M.stop_vim_timer(timer_id)
     139           12 :   local timer_data = active_vim_timers[timer_id]
     140           12 :   if not timer_data then
     141            1 :     return
     142              :   end
     143              : 
     144           14 :   pcall(vim.fn.timer_stop, timer_data.handle)
     145           11 :   active_vim_timers[timer_id] = nil
     146           37 : end
     147              : 
     148              : --- Check if vim timer is active
     149              : --- @param timer_id string Timer ID
     150              : --- @return boolean
     151           26 : function M.is_vim_timer_active(timer_id)
     152           50 :   return active_vim_timers[timer_id] ~= nil
     153           26 : end
     154              : 
     155              : -- ============================================================================
     156              : -- Cleanup Functions
     157              : -- ============================================================================
     158              : 
     159              : --- Stop all active timers
     160           26 : function M.stop_all_timers()
     161           42 :   for timer_id, _ in pairs(active_timers) do
     162            7 :     M.stop_timer(timer_id)
     163              :   end
     164              : 
     165           42 :   for timer_id, _ in pairs(active_vim_timers) do
     166            7 :     M.stop_vim_timer(timer_id)
     167              :   end
     168           61 : end
     169              : 
     170              : --- Get count of active timers
     171              : --- @return number loop_timers Number of vim.loop timers
     172              : --- @return number vim_timers Number of vim.fn timers
     173           26 : function M.get_timer_count()
     174            4 :   local loop_count = 0
     175            4 :   local vim_count = 0
     176              : 
     177            6 :   for _, _ in pairs(active_timers) do
     178            2 :     loop_count = loop_count + 1
     179              :   end
     180              : 
     181            6 :   for _, _ in pairs(active_vim_timers) do
     182            2 :     vim_count = vim_count + 1
     183              :   end
     184              : 
     185            4 :   return loop_count, vim_count
     186           26 : end
     187              : 
     188              : --- Get timer statistics for debugging
     189              : --- @return table stats Timer statistics
     190           26 : function M.get_stats()
     191            1 :   return {
     192            2 :     loop_timers = vim.tbl_keys(active_timers),
     193            2 :     vim_timers = vim.tbl_keys(active_vim_timers),
     194            1 :     total_created = total_timers_created,
     195            1 :   }
     196           26 : end
     197              : 
     198              : --- Reset timer manager state (for testing)
     199           26 : function M.reset()
     200           32 :   M.stop_all_timers()
     201           32 :   timer_id_counter = 0
     202           32 :   total_timers_created = 0
     203           58 : end
     204              : 
     205              : -- ============================================================================
     206              : -- Autocmd Setup
     207              : -- ============================================================================
     208              : 
     209              : --- Setup automatic timer cleanup on Neovim exit
     210           26 : function M.setup_cleanup()
     211            4 :   vim.api.nvim_create_autocmd("VimLeavePre", {
     212            2 :     group = vim.api.nvim_create_augroup("YodaTimerCleanup", { clear = true }),
     213              :     desc = "Cleanup all active timers before Neovim exits",
     214              :     callback = function()
     215              :       M.stop_all_timers()
     216            2 :     end,
     217              :   })
     218           28 : end
     219              : 
     220           26 : return M
        

Generated by: LCOV version 2.0-1