nvim-log-insert: A Neovim Plugin That Writes Your console.log Statements For You

A tiny Lua plugin that inserts a detailed console.log — file, line number, function, and variable name — with a single leader keymap.

3 min read

Sabin Shrestha

Full-Stack Developer — Next.js, React & React Native

If you spend your day in JavaScript or TypeScript, you already know the drill: you need to check what a variable actually is at runtime, so you type out console.log(...), and if you want it to actually be useful later — which file, which line, which function — you end up typing something like:

console.log('🚀 🐞 ~ file: app.js:10 ~ anonymous ~ myVar:', myVar);

Typing that by hand, every time, for every variable you want to inspect, gets old fast. nvim-log-insert exists so I never have to type it again — put the cursor on a variable, hit <leader>log, done.

What it actually does #

Put your cursor on a variable name and press <leader>log. The plugin drops a fully-formed log statement onto the next line, already filled in with the current filename, line number, the enclosing function (when it can find one), and the variable under the cursor:

console.log('🚀 🐞 ~ file: app.js:10 ~ anonymous ~ myVar:', myVar);

No more manually typing the filename, guessing the line number, or forgetting to actually include the variable name in the log message itself.

Installation #

Using Lazy.nvim:

return {
  "sabinsthnp/nvim-log-insert",
  config = function()
    require("nvim-log-insert").setup()
  end,
},

Using Packer.nvim:

use {
  "sabinsthnp/nvim-log-insert",
  config = function()
    require("nvim-log-insert").setup()
  end,
}

Or manually:

git clone https://github.com/sabinsthnp/nvim-log-insert ~/.config/nvim/pack/plugins/start/log-insert.nvim
require("log-insert").setup()

How it works under the hood #

The whole plugin is one setup() function that registers a normal-mode keymap:

-- lua/nvim-log-insert/init.lua
local M = {}
 
M.setup = function()
  vim.keymap.set('n', '<leader>log', function()
    local file_name = vim.fn.expand('%:t')
    local line_number = vim.fn.line('.')
    local variable_name = vim.fn.expand('<cword>')
    local func_name = vim.b.lsp_current_function or 'anonymous'
 
    local log_statement = string.format(
      "console.log('🚀 🐞 ~ file: %s:%d ~ %s ~ %s:', %s);",
      file_name, line_number, func_name, variable_name, variable_name
    )
 
    vim.api.nvim_put({ log_statement }, 'l', true, true)
  end, { desc = 'Insert detailed console.log statement' })
end
 
return M

Four Neovim built-ins do all the actual work:

  • vim.fn.expand('%:t') — the current buffer's filename (just the tail, e.g. app.js, not the full path).
  • vim.fn.line('.') — the cursor's current line number.
  • vim.fn.expand('<cword>') — the word currently under the cursor, which is what gets logged.
  • vim.api.nvim_put(...) — inserts the finished string as a new line right below the cursor.

The pieces get formatted into the log string with string.format, and that's the entire plugin — no dependencies, no config options, just one keymap doing exactly one job.

`func_name` needs something else setting it

vim.b.lsp_current_function isn't set automatically by Neovim or by most LSP clients out of the box — it's a buffer variable some statusline plugins populate. Unless something in your config sets it, this will always fall back to 'anonymous', which is exactly what you see in the plugin's own example output.

Contributing #

If you want to add something — actual function-name detection without relying on lsp_current_function, support for languages beyond JS/TS, configurable log formats — fork it and branch off:

git checkout -b feature/new-feature

As the README puts it: I am all ears.

Key takeaways #

  • A plugin doesn't need to be big to be useful — this is four vim.fn/vim.api calls and a string.format, and it removes a genuinely repetitive bit of typing.
  • Leaning on Neovim's own built-ins (expand, line, nvim_put) means zero external dependencies.
  • Being upfront about a limitation (the anonymous fallback) in the README/output is better than pretending the function-name detection is smarter than it is.
© 2026 Sabin Shrestha