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

            Line data    Source code
       1            1 : local M = {}
       2              : 
       3              : -- Cap venv_detection_times at this many unique root directories.
       4              : -- Projects accumulate across a session; without a bound this table grows forever.
       5            1 : local MAX_VENV_ENTRIES = 50
       6            1 : local venv_key_order = {} -- insertion-order list for FIFO eviction
       7              : 
       8            1 : local metrics = {
       9            1 :   attach_times = {},
      10            1 :   venv_detection_times = {},
      11            1 :   restart_counts = {},
      12            1 :   config_times = {},
      13              : }
      14              : 
      15            1 : function M.track_lsp_attach(server_name, start_time)
      16              :   local elapsed = (vim.uv.hrtime() - start_time) / 1000000
      17              : 
      18              :   if not metrics.attach_times[server_name] then
      19              :     metrics.attach_times[server_name] = { total = 0, count = 0, max = 0, min = math.huge }
      20              :   end
      21              : 
      22              :   local metric = metrics.attach_times[server_name]
      23              :   metric.total = metric.total + elapsed
      24              :   metric.count = metric.count + 1
      25              :   metric.max = math.max(metric.max, elapsed)
      26              :   metric.min = math.min(metric.min, elapsed)
      27              : 
      28              :   if elapsed > 500 then
      29              :     vim.notify(string.format("Slow LSP attach: %s took %.2fms", server_name, elapsed), vim.log.levels.WARN)
      30              :   end
      31            1 : end
      32              : 
      33            1 : function M.track_venv_detection(root_dir, start_time, found)
      34           17 :   local elapsed = (vim.uv.hrtime() - start_time) / 1000000
      35              : 
      36           17 :   if not metrics.venv_detection_times[root_dir] then
      37              :     -- Evict the oldest root_dir entry when at capacity
      38            8 :     if #venv_key_order >= MAX_VENV_ENTRIES then
      39              :       local oldest = table.remove(venv_key_order, 1)
      40              :       metrics.venv_detection_times[oldest] = nil
      41              :     end
      42            8 :     metrics.venv_detection_times[root_dir] = { total = 0, count = 0, found = 0 }
      43            8 :     table.insert(venv_key_order, root_dir)
      44              :   end
      45              : 
      46           17 :   local metric = metrics.venv_detection_times[root_dir]
      47           17 :   metric.total = metric.total + elapsed
      48           17 :   metric.count = metric.count + 1
      49           17 :   if found then
      50              :     metric.found = metric.found + 1
      51              :   end
      52           18 : end
      53              : 
      54            1 : function M.track_lsp_restart(server_name)
      55              :   if not metrics.restart_counts[server_name] then
      56              :     metrics.restart_counts[server_name] = 0
      57              :   end
      58              : 
      59              :   metrics.restart_counts[server_name] = metrics.restart_counts[server_name] + 1
      60              : 
      61              :   if metrics.restart_counts[server_name] > 5 then
      62              :     vim.notify(string.format("LSP %s has restarted %d times this session", server_name, metrics.restart_counts[server_name]), vim.log.levels.WARN)
      63              :   end
      64            1 : end
      65              : 
      66            1 : function M.track_config_time(server_name, start_time)
      67              :   local elapsed = (vim.uv.hrtime() - start_time) / 1000000
      68              : 
      69              :   if not metrics.config_times[server_name] then
      70              :     metrics.config_times[server_name] = { total = 0, count = 0, max = 0 }
      71              :   end
      72              : 
      73              :   local metric = metrics.config_times[server_name]
      74              :   metric.total = metric.total + elapsed
      75              :   metric.count = metric.count + 1
      76              :   metric.max = math.max(metric.max, elapsed)
      77            1 : end
      78              : 
      79            1 : function M.get_report()
      80              :   return {
      81              :     attach_times = metrics.attach_times,
      82              :     venv_detection = metrics.venv_detection_times,
      83              :     restarts = metrics.restart_counts,
      84              :     config_times = metrics.config_times,
      85              :   }
      86            1 : end
      87              : 
      88            1 : function M.reset_metrics()
      89              :   metrics = {
      90              :     attach_times = {},
      91              :     venv_detection_times = {},
      92              :     restart_counts = {},
      93              :     config_times = {},
      94              :   }
      95              :   venv_key_order = {}
      96            1 : end
      97              : 
      98            1 : function M.setup_commands()
      99              :   vim.api.nvim_create_user_command("LSPPerfReport", function()
     100              :     local report = M.get_report()
     101              :     local lines = { "=== LSP Performance Report ===", "" }
     102              : 
     103              :     table.insert(lines, "--- Attach Times ---")
     104              :     if vim.tbl_count(report.attach_times) == 0 then
     105              :       table.insert(lines, "  No LSP attaches recorded")
     106              :     else
     107              :       for server, data in pairs(report.attach_times) do
     108              :         table.insert(
     109              :           lines,
     110              :           string.format("  %s: avg=%.2fms, min=%.2fms, max=%.2fms, count=%d", server, data.total / data.count, data.min, data.max, data.count)
     111              :         )
     112              :       end
     113              :     end
     114              : 
     115              :     table.insert(lines, "")
     116              :     table.insert(lines, "--- Virtual Environment Detection ---")
     117              :     if vim.tbl_count(report.venv_detection) == 0 then
     118              :       table.insert(lines, "  No venv detections recorded")
     119              :     else
     120              :       for root, data in pairs(report.venv_detection) do
     121              :         local avg_time = data.total / data.count
     122              :         local success_rate = (data.found / data.count) * 100
     123              :         table.insert(lines, string.format("  %s: avg=%.2fms, success=%.1f%%, count=%d", root, avg_time, success_rate, data.count))
     124              :       end
     125              :     end
     126              : 
     127              :     table.insert(lines, "")
     128              :     table.insert(lines, "--- LSP Restarts ---")
     129              :     if vim.tbl_count(report.restarts) == 0 then
     130              :       table.insert(lines, "  No LSP restarts recorded")
     131              :     else
     132              :       for server, count in pairs(report.restarts) do
     133              :         local status = count > 5 and "warning" or "ok"
     134              :         table.insert(lines, string.format("  [%s] %s: %d restarts", status, server, count))
     135              :       end
     136              :     end
     137              : 
     138              :     table.insert(lines, "")
     139              :     table.insert(lines, "--- Configuration Times ---")
     140              :     if vim.tbl_count(report.config_times) == 0 then
     141              :       table.insert(lines, "  No config times recorded")
     142              :     else
     143              :       for server, data in pairs(report.config_times) do
     144              :         table.insert(lines, string.format("  %s: avg=%.2fms, max=%.2fms, count=%d", server, data.total / data.count, data.max, data.count))
     145              :       end
     146              :     end
     147              : 
     148              :     table.insert(lines, "")
     149              :     table.insert(lines, "========================")
     150              :     vim.notify(table.concat(lines, "\n"), vim.log.levels.INFO)
     151              :   end, { desc = "Show LSP performance metrics" })
     152              : 
     153              :   vim.api.nvim_create_user_command("LSPPerfReset", function()
     154              :     M.reset_metrics()
     155              :     vim.notify("LSP performance metrics reset", vim.log.levels.INFO)
     156              :   end, { desc = "Reset LSP performance metrics" })
     157            1 : end
     158              : 
     159            1 : return M
        

Generated by: LCOV version 2.0-1