127 lines
4.0 KiB
Lua
127 lines
4.0 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
|
|
|
|
--- 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.range == 0) 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
|
|
|
|
return M
|