From 3ebfa2a3cbbed848667fe26c16973ce7e43a04e4 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 17 Apr 2026 18:14:14 +0200 Subject: 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 --- runtime/lua/vim/_core/ex_cmd.lua | 20 ++++++++++++-------- runtime/lua/vim/_core/server.lua | 28 ++++++++++++++++------------ runtime/lua/vim/_core/util.lua | 14 ++++++++------ runtime/lua/vim/_meta/api_keysets_extra.lua | 2 +- 4 files changed, 37 insertions(+), 27 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_core/ex_cmd.lua b/runtime/lua/vim/_core/ex_cmd.lua index a9a7a588ed..3c58dc5ddd 100644 --- a/runtime/lua/vim/_core/ex_cmd.lua +++ b/runtime/lua/vim/_core/ex_cmd.lua @@ -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 diff --git a/runtime/lua/vim/_core/server.lua b/runtime/lua/vim/_core/server.lua index 14f942459d..54d8d104f0 100644 --- a/runtime/lua/vim/_core/server.lua +++ b/runtime/lua/vim/_core/server.lua @@ -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 diff --git a/runtime/lua/vim/_core/util.lua b/runtime/lua/vim/_core/util.lua index 973cf1da2d..29ba8c5720 100644 --- a/runtime/lua/vim/_core/util.lua +++ b/runtime/lua/vim/_core/util.lua @@ -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) diff --git a/runtime/lua/vim/_meta/api_keysets_extra.lua b/runtime/lua/vim/_meta/api_keysets_extra.lua index 3b8461892b..7714a27e93 100644 --- a/runtime/lua/vim/_meta/api_keysets_extra.lua +++ b/runtime/lua/vim/_meta/api_keysets_extra.lua @@ -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 -- cgit