Files
neovim/runtime/lua/vim/_core/util.lua
T
Justin M. Keyes 3ebfa2a3cb 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
2026-04-18 16:57:37 +02:00

133 lines
4.1 KiB
Lua

-- Nursery for random things that may later find their way into stdlib if they mature.
local M = {}
--- Adds one or more blank lines above or below the cursor.
-- TODO: move to _core/defaults.lua once it is possible to assign a Lua function to options #25672
--- @param above? boolean Place blank line(s) above the cursor
local function add_blank(above)
local offset = above and 1 or 0
local repeated = vim.fn['repeat']({ '' }, vim.v.count1)
local linenr = vim.api.nvim_win_get_cursor(0)[1]
vim.api.nvim_buf_set_lines(0, linenr - offset, linenr - offset, true, repeated)
end
-- TODO: move to _core/defaults.lua once it is possible to assign a Lua function to options #25672
function M.space_above()
add_blank(true)
end
-- TODO: move to _core/defaults.lua once it is possible to assign a Lua function to options #25672
function M.space_below()
add_blank()
end
--- Gets a buffer by name
--- @param name string
--- @return integer?
function M.get_buf_by_name(name)
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
if vim.api.nvim_buf_get_name(buf) == name then
return buf
end
end
end
--- Edit a file in a specific window
--- @param winnr number
--- @param file string
--- @return number buffer number of the edited buffer
M.edit_in = function(winnr, file)
local function resolved_path(path)
if not path or path == '' then
return ''
end
return vim.fn.resolve(vim.fs.abspath(path))
end
return vim.api.nvim_win_call(winnr, function()
local current_buf = vim.api.nvim_win_get_buf(winnr)
local current = resolved_path(vim.api.nvim_buf_get_name(current_buf))
-- Check if the current buffer is already the target file
if current == resolved_path(file) then
return current_buf
end
-- Read the file into the buffer
vim.cmd.edit(vim.fn.fnameescape(file))
return vim.api.nvim_get_current_buf()
end)
end
--- :edit, but it respects commands like :hor, :vert, :tab, etc.
--- @param file string
--- @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)
end
vim.cmd.sbuffer { buf, mods = mods }
end
vim.cmd.edit { file }
end
--- Read a chunk of data from a file
--- @param file string
--- @param size number
--- @return string? chunk or nil on error
function M.read_chunk(file, size)
local fd = io.open(file, 'rb')
if not fd then
return nil
end
local chunk = fd:read(size)
fd:close()
return tostring(chunk)
end
--- Check if a range in a buffer is inside a Lua codeblock via treesitter injection.
--- Used by :source to detect Lua code in non-Lua files (e.g., vimdoc).
--- @param bufnr integer Buffer number
--- @param line1 integer Start line (1-indexed)
--- @param line2 integer End line (1-indexed)
--- @return boolean True if the range is in a Lua injection
function M.source_is_lua(bufnr, line1, line2)
local parser = vim.treesitter.get_parser(bufnr)
if not parser then
return false
end
-- Parse from buffer start through one line past line2 to include injection closing markers
local range = { line1 - 1, 0, line2 - 1, -1 }
parser:parse({ 0, 0, line2, -1 })
local lang_tree = parser:language_for_range(range)
return lang_tree:lang() == 'lua'
end
--- Returns the exit code string for the current buffer, given:
--- - Channel is attached to the current buffer
--- - Current buffer is a terminal buffer
---
--- @return string
function M.term_exitcode()
local chan_id = vim.bo.channel
if chan_id == 0 or vim.bo.buftype ~= 'terminal' then
return ''
end
local info = vim.api.nvim_get_chan_info(chan_id)
if info.exitcode and info.exitcode >= 0 then
return string.format('[Exit: %d]', info.exitcode)
end
return ''
end
return M