Explore without losing context. Stackable, editable floating popups for Neovim.
2025-08-03.17-36-16-00.00.06.979-00.01.42.114.mp4
You know the frustration: you're deep in a function, need to check a definition, so you jump to it... and now you've lost your place. Or you use a peek feature but can't fix that typo you just spotted.
overlook.nvim creates stackable floating popups that are actual buffers - edit them, save them, navigate from them. Build a visual trail of your code exploration without ever losing where you started.
- π Peek at definitions - View LSP definitions, marks, or any location in floating windows
- βοΈ Actually editable - Spot a bug? Fix it right there in the popup and
:wto save - π Visual stack navigation - See your entire exploration path as cascading popups
- π Undo your exploration - Accidentally closed a popup? Bring it back with
restore_popup() - πͺ Popup promotion - Found something important? Convert any popup to a split/tab
- π― Window-local stacks - Each window maintains its own popup stack for parallel exploration
The core philosophy is simple: popups are buffers. Everything else follows from that one decision. Because a popup is a real buffer in a real window, it is editable and saveable; every keybinding, plugin, and picker you already have works inside it; it can host the next peek (which is why popups stack); and when a quick glance turns into real work, it can be promoted to a split or tab. There is nothing special to learn - it behaves exactly like any other window.
- Trace through call chains: Peek definition β find another reference β peek again β you now have a visual stack showing your exploration path
- Fix as you explore: Reviewing a function and spot a typo? Fix it in the popup and save - no context switching needed
- Visual debugging: Build a breadcrumb trail of function calls while stepping through the code
- All your keybindings work normally in popups
- Switch buffers inside popups -
:bnext,<C-^>, telescope/fzf all work - Popups automatically offset and resize to stay readable when stacked
- Full undo/redo support for your exploration history
- Neovim >= 0.11 (CI tests against 0.11.x and nightly)
- An LSP client only for
peek_definition()-peek_cursor()andpeek_mark()need nothing extra - No external dependencies
Using lazy.nvim:
{
"WilliamHsieh/overlook.nvim",
opts = {},
-- Optional: set up common keybindings
keys = {
{ "<leader>pd", function() require("overlook.api").peek_definition() end, desc = "Overlook: Peek definition" },
{ "<leader>pc", function() require("overlook.api").close_all() end, desc = "Overlook: Close all popup" },
{ "<leader>pu", function() require("overlook.api").restore_popup() end, desc = "Overlook: Restore popup" },
},
}- Install the plugin with your favorite package manager
- Add a keybinding for
peek_definition() - Navigate to any symbol and trigger the peek
- Edit the popup content if needed
- Press
qto close or continue exploring
That's it! No complex setup required.
overlook.nvim works out of the box with sensible defaults - opts = {} is all you need. Pass a table to setup() to override only the pieces you care about. The example below sets non-default values to illustrate the shape:
require("overlook").setup({
-- Overrides (not defaults) - shown to illustrate the shape
ui = {
border = "single", -- default is "rounded"
size_ratio = 0.8, -- default is 0.65
},
-- Run custom logic when the last popup in a stack closes
on_stack_empty = function()
-- Your custom logic here
end,
})The full, always-current list of options and their defaults lives in :h overlook-config - it is generated from the source annotations, so it can't drift out of sync.
vim.keymap.set("n", "<leader>pd", require("overlook.api").peek_definition, { desc = "Peek definition" })
vim.keymap.set("n", "<leader>pp", require("overlook.api").peek_cursor, { desc = "Peek cursor" })
vim.keymap.set("n", "<leader>pu", require("overlook.api").restore_popup, { desc = "Restore last popup" })
vim.keymap.set("n", "<leader>pU", require("overlook.api").restore_all_popups, { desc = "Restore all popups" })
vim.keymap.set("n", "<leader>pc", require("overlook.api").close_all, { desc = "Close all popups" })
vim.keymap.set("n", "<leader>pf", require("overlook.api").switch_focus, { desc = "Switch focus" })
vim.keymap.set("n", "<leader>ps", require("overlook.api").open_in_split, { desc = "Open popup in split" })
vim.keymap.set("n", "<leader>pv", require("overlook.api").open_in_vsplit, { desc = "Open popup in vsplit" })
vim.keymap.set("n", "<leader>pt", require("overlook.api").open_in_tab, { desc = "Open popup in tab" })
vim.keymap.set("n", "<leader>po", require("overlook.api").open_in_original_window, { desc = "Open popup in current window" })Check :h overlook-api for more details.
peek_definition()- Peek at the LSP definition under cursorpeek_cursor()- Create a popup at current cursor positionpeek_mark()- Prompt for a mark and peek at its locationrestore_popup()- Restore the last closed popuprestore_all_popups()- Restore all closed popupsclose_all()- Close all overlook popupsswitch_focus()- Switch focus between popups and root windowopen_in_split()- Promote popup to horizontal splitopen_in_vsplit()- Promote popup to vertical splitopen_in_tab()- Promote popup to new tabopen_in_original_window()- Replace current window with popup content
A peek source is just a function: gather whatever context you need, build an options table, and call require("overlook.window").open_popup(...). There is no registration step - once you have the function, bind it to a key exactly like any built-in.
local function peek_alternate_file()
local alt = vim.fn.bufnr("#") -- the alternate buffer
if alt == -1 then
return vim.notify("Overlook: no alternate buffer", vim.log.levels.WARN)
end
require("overlook.window").open_popup({
target_bufnr = alt,
lnum = 1,
col = 1,
title = vim.fn.bufname(alt),
})
end
vim.keymap.set("n", "<leader>pa", peek_alternate_file, { desc = "Peek alternate file" })Async sources (LSP, pickers) work the same way - they just call open_popup later from their own callback. See :h overlook-peek for the full contract and overlook.peek.* for the built-in examples.
make test- run the plenary test suitemake vimdoc- regeneratedoc/from the source annotations (never editdoc/*.txtby hand)- stylua - format Lua code
Special thanks to the lspsaga.nvim project for the original peek_definition implementation that inspired this plugin.