All checks were successful
NeoVim tests / code-quality (push) Successful in 1m22s
192 lines
5.8 KiB
Lua
192 lines
5.8 KiB
Lua
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
|