vimtricks.wiki Concise Vim tricks, one at a time.

How do I run Lua code in the context of a specific buffer without switching to it in Neovim?

Answer

vim.api.nvim_buf_call()

Explanation

vim.api.nvim_buf_call(bufnr, fn) temporarily sets the given buffer as the current buffer, runs your function, then restores the original buffer — all without any visible window switching. This is useful when you need buffer-local context (like % filename expansion, b: variables, or local directory state) for a buffer that is not currently displayed.

How it works

-- Get the full path of a buffer by number without switching to it
local path = vim.api.nvim_buf_call(bufnr, function()
  return vim.fn.expand("%:p")
end)

-- Set a buffer-local option on a hidden buffer
vim.api.nvim_buf_call(bufnr, function()
  vim.opt_local.tabstop = 4
end)

The return value of the function is returned by nvim_buf_call().

Example

Building a list of full file paths for all loaded buffers:

local paths = {}
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
  if vim.api.nvim_buf_is_loaded(bufnr) then
    local p = vim.api.nvim_buf_call(bufnr, function()
      return vim.fn.expand("%:p")
    end)
    table.insert(paths, p)
  end
end

Tips

  • nvim_buf_call() does not change the window layout or trigger BufEnter/BufLeave autocommands, making it safe to use in scripts and plugins
  • For window-local context (like w: variables or winnr()), use the analogous vim.api.nvim_win_call(winnr, fn)
  • This is the correct way to call vim.fn functions that implicitly use the current buffer (like expand(), getline(), or setline()) on a specific non-current buffer

Next

How do I programmatically read and write Vim register contents including their type from Vimscript?