Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/lua/std/dbg.lua.bak

Raw
local user_command = require 'std.user_command'
local icon = require 'std.icon'

local DBG_FILE = vim.fn.stdpath 'state' .. '/bugabinga_config_debug.jsonl'

---Debug kinds and their mapped highlight groups and icons.
---@type table<string,[string, string]>
local DBG_KINDS = {
	--TODO
	DEBUG = {},
	TRACE = {},
	ERROR = {},
}

---@alias dbg_kind
---| 'DEBUG' normal debug event -> use this, when logging debug information
---| 'TRACE' function trace event -> use this, when tracing function call flow
---| 'ERROR' error event        -> use this, when logging unexpected events

---Get context information from `debug.traceback()` and other `debug` functions.
---Wwhen getting the stacktrace, we need to account for the function call graph inside this `dbg` module.
---Either its: `callsite -> metatable -> print_debug -> print_debug_kind -> get_context`.
---Or: `callsite -> print_debug_kind -> get_context`.
---In both cases we are only interested in callsite to determine file name, the function name and line number.
---
---@return string filename The filename, the calling function resides in.
---@return string function_name The name of the function, where `dbg` was called in.
---@return integer line_number Line number the calling functions is invoking the `dbg` function at.
local function get_context()
	--TODO
	local filename = nil
	local function_name = nil
	local line_number = nil
	return filename, function_name, line_number
end

--- Encodes debug event to JSON
--- @param event table<string, table, string, table, string, string, integer> debug event to log
--- @return string json JSON-encoded debug event
local function encode( event )
	return vim.json.encode( event, {
		escape_slash = true,
	} )
end

--- Decodes debug event from JSON
--- @param line string JSON-encoded debug event
--- @return table<string, table, string, table, string, string, integer> event debug event to log
local function decode( line )
	return vim.json.decode( line, {
		luanil = {
			object = true,
			array = true,
		},
	} )
end

--- Writes debug events to the debug file, one per line.
---
--- @see encode
--- @param event table<string, table, string, table, string, string, integer> debug event to log
local function write_to_debug_file( event )
	local file, errmsg = io.open( DBG_FILE, 'a+' )
	if not file then
		error( 'Unable to open debug log file `' .. DBG_FILE .. '`: ' .. errmsg )
	end
	local json_line = encode( event )
	file:write( json_line )
	file:flush()
	file:close()
end

---Replaces expressions inside the given `format`, by indexing into `user_data`.
---
---Resolve `format`, by replacing `{<expr>}` inside format string.
---`<expr>` is used to index into `user_data`.
---e.g.: `user_data = {a = 1, b = 2, c = 3}`
---To get `b`: the format expression would be `{b}`.
---`{b}` would be replaced by `2` in format string.
---
---e.g.: `user_data = {{a=1},{b=2},{c=3}}`
---To index into nested structures, use numbers: `{2.b}`.
---`{2.b}` would be replaced by `2` in format string.
---Indexing is 1-based, as per usual in lua.
---Values are converted by `vim.inspect` to strings.
---
---@see vim.inspect
---
---@param format string Message with optional "{}"-style expressions inside
---@param data table Arbitrary data, that can be indexed into, to pick values for the `format` string
---@return string formatted_message Message with format expressions resolved
local function format_message( format, data )
	local formatted_message
	--TODO
	return formatted_message
end

if vim.g.bugabinga_debug_mode == nil then
	vim.g.bugabinga_debug_mode = false
end

local function is_debug_mode()
	return vim.g.bugabinga_debug_mode
end

---Log debug message of particular kind.
---@param kind dbg_kind kind of event
---@param message string message format string
---@param ... any arbitrary user data, that can be referenced in message format
local function print_debug_kind( kind, message, ... )
	if not is_debug_mode() then
		return
	end
	local now = os.date '!*t'
	local user_data = { ... }
	local filename, function_name, line_number = get_context()
	write_to_debug_file {
		kind = kind,
		date = now,
		format = message,
		data = user_data,
		filename = filename,
		fn = function_name,
		line = line_number,
	}
end

---Log a debug event.
---@param message string message format string
---@param ... any arbitrary user data, that can be referenced in message format
local function print_debug( message, ... )
	if not is_debug_mode() then
		return
	end
	print_debug_kind( 'DEBUG', message, ... )
end

local function toggle_debug_mode()
	vim.g.bugabinga_debug_mode = not vim.g.bugabinga_debug_mode
	vim.notify( 'Toggled debug mode to ' .. tostring( is_debug_mode() ) )
end

--- Opens the debug file in a special buffer, that is not modifiable.
--- It renders the debug events in a table with special functions:
--- - filter by type
--- - jump to location
--- - colors and icons for different kinds
--- Filters are a label, e.g. "Filter by kind: " plus a special region after it on one line in the buffer above the table.
--- Typing in that region activates the filtering in the table.
---
--- Icons are shown as prefix to kind values in their cells: <icon> <KIND>.
--- The table rows are highlighted according to kinds in `DBG_KINDS`.
--- Note: table row can be multiline because of formatted messages. Highlighting needs to account for that.
---
--- Pressing enter while the cursor is on a table row, jumps to this events location.
---
--- Debug events are read and decoded from `DBG_FILE` and converted to a table of:
--- Kind, Date, Message, Location
---
--- Kind -> debug event kind + icon
--- Date -> human readable datetime in hosts timezone
--- Message -> formatted message from format string and user data
--- Locataion -> combined file name, function name and line number
local function open_special_debug_buffer()
	--TODO
end

--- Opens the file `DBG_FILE` in a normal neovim buffer
local function open_raw_debug_file()
	--TODO
end

user_command.Debug 'Toggle global vim config debug mode.' ( toggle_debug_mode )
user_command.DebugOpen 'Open special buffer with debug events' ( open_special_debug_buffer )
user_command.DebugOpenRaw 'Opens the JSON debug file in normal buffer' ( open_raw_debug_file )

return setmetatable(
	{
		kind = print_debug_kind,
		toggle = toggle_debug_mode,
		get = is_debug_mode,
	}, {
		--TODO: refactor usages of this function in other code to new name
		__call = print_debug,
		__newindex = function ( ... )
			error(
				'Assignments are not supported for std.dbg.\n' .. vim.inspect( ... ), 2 )
		end,
	} )