local encode = require 'std.dbg.encode' local decode = require 'std.dbg.encode' local DBG_FILE = vim.fn.stdpath 'state' .. '/bugabinga_config_debug.jsonl' ---Returns the path to the debug log file. ---@return string local function get_path() return vim.g.bugabinga_debug_file_override or DBG_FILE end ---Returns the filename, function name, and line number of the caller. ---Skips all frames inside std/dbg/* modules. ---@return string filename ---@return string function_name ---@return integer line_number local function get_context() local level = 2 while true do local info = debug.getinfo(level, 'Sln') if not info then break end local src = info.short_src or info.source if src and not src:match 'std/dbg' and not src:match '@std/dbg' then return src, info.name or '', info.currentline or 0 end level = level + 1 end return 'unknown', '', 0 end ---Appends a debug event as a JSON line to the debug file. ---@param event table debug event ---@param path string? override path local function write(event, path) local file_path = path or DBG_FILE local dir = vim.fs.dirname(file_path) if dir and dir ~= '' then vim.fn.mkdir(dir, 'p') end local file, errmsg = io.open(file_path, 'a') if not file then error('Unable to open debug log file `' .. file_path .. '`: ' .. tostring(errmsg)) end local json_line = encode.encode(event) file:write(json_line .. '\n') file:flush() file:close() end ---Closes the open file descriptor. No-op with io.open. local function close_fd() end ---Clears the debug log file by truncating it. local function clear_log() local dir = vim.fs.dirname(DBG_FILE) if dir and dir ~= '' then vim.fn.mkdir(dir, 'p') end local file, errmsg = io.open(DBG_FILE, 'w') if file then file:close() end end ---Reads all debug events from a JSONL file. ---@param path string? override path (defaults to DBG_FILE) ---@return table[] list of decoded events local function read_all(path) local file_path = path or DBG_FILE local file = io.open(file_path, 'r') if not file then return {} end local events = {} for line in file:lines() do if line and line ~= '' then local ok, event = pcall(decode.decode, line) if ok and event then table.insert(events, event) end end end file:close() return events end ---Reads only new events from the JSONL file, skipping the first `last_count` lines. ---Use for incremental updates when tailing a growing file. ---@param last_count number skip this many lines before reading new ones ---@param path string? override path (defaults to DBG_FILE) ---@return table[] new_events list of newly decoded events ---@return number new_total total count of events in file after reading local function read_new(last_count, path) local file_path = path or DBG_FILE local file = io.open(file_path, 'r') if not file then return {}, 0 end local new_events = {} local current_count = 0 for line in file:lines() do if line and line ~= '' then current_count = current_count + 1 if current_count > last_count then local ok, event = pcall(decode.decode, line) if ok and event then table.insert(new_events, event) end end end end file:close() return new_events, current_count end return { get_path = get_path, get_context = get_context, write = write, read_all = read_all, read_new = read_new, close_fd = close_fd, clear_log = clear_log, }