local M = {} -- -- Types -- --- @class u.utils.QfItem --- @field col number --- @field filename string --- @field kind string --- @field lnum number --- @field text string --- @class u.utils.RawCmdArgs --- @field args string --- @field bang boolean --- @field count number --- @field fargs string[] --- @field line1 number --- @field line2 number --- @field mods string --- @field name string --- @field range 0|1|2 --- @field reg string --- @field smods any --- @class u.utils.CmdArgs: u.utils.RawCmdArgs --- @field info u.Range|nil --- @class u.utils.UcmdArgs --- @field nargs? 0|1|'*'|'?'|'+' --- @field range? boolean|'%'|number --- @field count? boolean|number --- @field addr? string --- @field completion? string --- @field force? boolean --- @field preview? fun(opts: u.utils.UcmdArgs, ns: integer, buf: integer):0|1|2 -- -- Functions -- --- Debug utility that prints a value and returns it unchanged. --- Useful for debugging in the middle of expressions or function chains. --- --- @generic T --- @param x `T` The value to debug print --- @param message? string Optional message to print alongside the value --- @return T The original value, unchanged --- --- @usage --- ```lua --- -- Debug a value in the middle of a chain: --- local result = some_function() --- :map(utils.dbg) -- prints the intermediate value --- :filter(predicate) --- --- -- Debug with a custom message: --- local config = utils.dbg(get_config(), "Current config:") --- --- -- Debug return values: --- return utils.dbg(calculate_result(), "Final result") --- ``` function M.dbg(x, message) local t = {} if message ~= nil then table.insert(t, message) end table.insert(t, x) vim.print(t) return x end --- Creates a user command with enhanced argument processing. --- Automatically computes range information and attaches it as `args.info`. --- --- @param name string The command name (without the leading colon) --- @param cmd string | fun(args: u.utils.CmdArgs): any Command implementation --- @param opts? u.utils.UcmdArgs Command options (nargs, range, etc.) --- --- @usage --- ```lua --- -- Create a command that works with visual selections: --- utils.ucmd('MyCmd', function(args) --- -- Print the visually selected text: --- vim.print(args.info:text()) --- -- Or get the selection as an array of lines: --- vim.print(args.info:lines()) --- end, { nargs = '*', range = true }) --- --- -- Create a command that processes the current line: --- utils.ucmd('ProcessLine', function(args) --- local line_text = args.info:text() --- -- Process the line... --- end, { range = '%' }) --- --- -- Create a command with arguments: --- utils.ucmd('SearchReplace', function(args) --- local pattern, replacement = args.fargs[1], args.fargs[2] --- local text = args.info:text() --- -- Perform search and replace... --- end, { nargs = 2, range = true }) --- ``` -- luacheck: ignore function M.ucmd(name, cmd, opts) local Range = require 'u.range' opts = opts or {} local cmd2 = cmd if type(cmd) == 'function' then cmd2 = function(args) args.info = Range.from_cmd_args(args) return cmd(args) end end vim.api.nvim_create_user_command(name, cmd2, opts or {} --[[@as any]]) end --- Creates command arguments for delegating from one command to another. --- Preserves all relevant context (range, modifiers, bang, etc.) when --- implementing a derived command in terms of a base command. --- --- @param current_args vim.api.keyset.create_user_command.command_args|u.utils.RawCmdArgs The arguments from the current command --- @return vim.api.keyset.cmd Arguments suitable for vim.cmd() calls --- --- @usage --- ```lua --- -- Implement :MyEdit in terms of :edit, preserving all context: --- utils.ucmd('MyEdit', function(args) --- local delegated_args = utils.create_delegated_cmd_args(args) --- -- Add custom logic here... --- vim.cmd.edit(delegated_args) --- end, { nargs = '*', range = true, bang = true }) --- --- -- Implement :MySubstitute that delegates to :substitute: --- utils.ucmd('MySubstitute', function(args) --- -- Pre-process arguments --- local pattern = preprocess_pattern(args.fargs[1]) --- --- local delegated_args = utils.create_delegated_cmd_args(args) --- delegated_args.args = { pattern, args.fargs[2] } --- --- vim.cmd.substitute(delegated_args) --- end, { nargs = 2, range = true, bang = true }) --- ``` function M.create_delegated_cmd_args(current_args) --- @type vim.api.keyset.cmd local args = { range = current_args.range == 1 and { current_args.line1 } or current_args.range == 2 and { current_args.line1, current_args.line2 } or nil, count = current_args.count ~= -1 and current_args.count or nil, reg = current_args.reg ~= '' and current_args.reg or nil, bang = current_args.bang or nil, args = #current_args.fargs > 0 and current_args.fargs or nil, mods = current_args.smods, } return args end --- Gets the current editor dimensions. --- Useful for positioning floating windows or calculating layout sizes. --- --- @return { width: number, height: number } The editor dimensions in columns and lines --- --- @usage --- ```lua --- -- Center a floating window: --- local dims = utils.get_editor_dimensions() --- local win_width = 80 --- local win_height = 20 --- local col = math.floor((dims.width - win_width) / 2) --- local row = math.floor((dims.height - win_height) / 2) --- --- vim.api.nvim_open_win(bufnr, true, { --- relative = 'editor', --- width = win_width, --- height = win_height, --- col = col, --- row = row, --- }) --- --- -- Check if editor is wide enough for side-by-side layout: --- local dims = utils.get_editor_dimensions() --- if dims.width >= 160 then --- -- Use side-by-side layout --- else --- -- Use stacked layout --- end --- ``` function M.get_editor_dimensions() return { width = vim.go.columns, height = vim.go.lines } end return M