feat(vimfn): use Lua for more excmds/vimfns

Problem:
Too much boilerplate needed to use Lua to impl an excmd or f_xx
function.

Solution:
- Add `nlua_call_vimfn` which takes the args typval, executes
  Lua, and returns a typval.
- refactor(excmd): lua impl for :log, :lsp
This commit is contained in:
Justin M. Keyes
2026-04-17 18:14:14 +02:00
parent 48d11681c2
commit 3ebfa2a3cb
11 changed files with 253 additions and 181 deletions

View File

@@ -2,6 +2,10 @@ local api = vim.api
local fs = vim.fs
local util = require('vim._core.util')
--- Parsed ex command arguments for builtin commands, passed from C via `nlua_call_excmd`.
--- Inherits fields from user command args: args, bang, line1, line2, range, count, reg, smods.
--- @class vim._core.ExCmdArgs : vim.api.keyset.create_user_command.command_args
local M = {}
--- @param msg string
@@ -151,9 +155,9 @@ local actions = {
local available_subcmds = vim.tbl_keys(actions)
--- Implements command: `:lsp {subcmd} {name}?`.
--- @param args string
M.ex_lsp = function(args)
local fargs = api.nvim_parse_cmd('lsp ' .. args, {}).args
--- @param eap vim._core.ExCmdArgs
M.ex_lsp = function(eap)
local fargs = api.nvim_parse_cmd('lsp ' .. eap.args, {}).args
if not fargs then
return
end
@@ -192,11 +196,11 @@ end
local log_dir = vim.fn.stdpath('log')
--- Implements command: `:log {file}`.
--- @param filename string
--- @param mods string
M.ex_log = function(filename, mods)
--- @param eap vim._core.ExCmdArgs
M.ex_log = function(eap)
local filename = eap.args
if filename == '' then
util.wrapped_edit(log_dir, mods)
util.wrapped_edit(log_dir, eap.smods)
else
local path --- @type string
-- Special case for NVIM_LOG_FILE
@@ -210,7 +214,7 @@ M.ex_log = function(filename, mods)
echo_err(("No such log file: '%s'"):format(path))
return
end
util.wrapped_edit(path, mods)
util.wrapped_edit(path, eap.smods)
vim.cmd.normal { 'G', bang = true }
end
end

View File

@@ -2,36 +2,40 @@
local M = {}
--- Called by builtin serverlist(). Returns all running servers in stdpath("run").
--- Called by builtin serverlist(). Returns the combined server list (own + peers).
---
--- - TODO: track TCP servers, somehow.
--- - TODO: support Windows named pipes.
---
--- @param listed string[] Already listed servers
--- @return string[] # List of servers found on the current machine in stdpath("run").
function M.serverlist(listed)
--- @param opts? table Options:
--- - opts.peer is true, also discover peer servers.
--- @param addrs string[] Internal ("own") addresses, from `server_address_list`.
--- @return string[] # Combined list of servers (own + peers).
function M.serverlist(opts, addrs)
if type(opts) ~= 'table' or not opts.peer then
return addrs
end
-- Discover peer servers in stdpath("run").
-- TODO: track TCP servers, somehow.
-- TODO: support Windows named pipes.
local root = vim.fs.normalize(vim.fn.stdpath('run') .. '/..')
local socket_paths = vim.fs.find(function(name, _)
return name:match('nvim.*')
end, { path = root, type = 'socket', limit = math.huge })
local found = {} ---@type string[]
for _, socket in ipairs(socket_paths) do
-- Don't list servers twice
if not vim.list_contains(listed, socket) then
if not vim.list_contains(addrs, socket) then
local ok, chan = pcall(vim.fn.sockconnect, 'pipe', socket, { rpc = true })
if ok and chan then
-- Check that the server is responding
-- TODO: do we need a timeout or error handling here?
if vim.fn.rpcrequest(chan, 'nvim_get_chan_info', 0).id then
table.insert(found, socket)
table.insert(addrs, socket)
end
vim.fn.chanclose(chan)
end
end
end
return found
return addrs
end
return M

View File

@@ -62,12 +62,14 @@ end
--- :edit, but it respects commands like :hor, :vert, :tab, etc.
--- @param file string
--- @param mods_str string
function M.wrapped_edit(file, mods_str)
local cmdline = table.concat({ mods_str, 'edit' }, ' ')
local mods = vim.api.nvim_parse_cmd(cmdline, {}).mods
--- @diagnostic disable-next-line: need-check-nil
if mods.tab > 0 or mods.split ~= '' or mods.horizontal or mods.vertical then
--- @param mods string|vim.api.keyset.cmd_mods Modifier string ("vertical") or structured mods table.
function M.wrapped_edit(file, mods)
assert(mods)
if type(mods) == 'string' then
mods = vim.api.nvim_parse_cmd(mods .. ' edit', {}).mods --[[@as vim.api.keyset.cmd_mods]]
end
--- @cast mods vim.api.keyset.cmd_mods
if (mods.tab or 0) > 0 or (mods.split or '') ~= '' or mods.horizontal or mods.vertical then
local buf = M.get_buf_by_name(file)
if buf == nil then
buf = vim.api.nvim_create_buf(true, false)

View File

@@ -111,7 +111,7 @@ error('Cannot require a meta file')
---
--- Command modifiers in a structured format. Has the same structure as the
--- "mods" key of |nvim_parse_cmd()|.
--- @field smods table
--- @field smods vim.api.keyset.cmd_mods
--- @class vim.api.keyset.command_info
--- @field name string