Detour.nvim
Use popup windows to navigate files/buffer and to contain shells/TUIs
Install / Use
/learn @carbon-steel/Detour.nvimREADME
detour.nvim
<div dir="rtl"> J.R.R. Tolkien, The Lord of the Rings </div> </br></br>It's a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there's no knowing where you might be swept off to.
detour.nvim provides commands to open floating windows that position and shape
themselves.
Never lose your spot!📍🗺️
| What does detour.nvim do? | |
| :--: | :--: |
| :Detour/require('detour').Detour() <br />opens a floating window<br />over all current windows | |
|
:DetourCurrentWindow/<br />require('detour').DetourCurrentWindow()<br />opens a floating window over<br />only the current window | |
| Works with Neovim's
:split/:vsplit/<C-w>s/<C-w>v/<C-w>T commands | |
| You can nest detour popups |
|
Neovim's floating windows are a great utility to use in plugins and
functions, but they cannot be used manually. This is because
creating floats is not simple like calling :split or :vsplit.
vim.api.nvim_open_win(...) requires coordinates and dimensions to make
a float which is too tedious to do by hand.
Detour.nvim brings a single new feature to Neovim: detour windows (aka detours). Detours are floating windows with the ease-of-use of splits.
Detours will make sure not to overlap each other unless when a detour is nested within another.
Example keymaps
detour.nvim is designed as a utility library for keymaps people can write on their own.
NOTE If you'd like to share a keymap you made, please submit it in a github issue and we'll include it in the examples directory!
Here are a few basic examples...
Open your Neovim config
vim.keymap.set("n", "<leader>e", function()
-- Open detour
if not require("detour").Detour() then
return
end
vim.cmd.edit(vim.fn.stdpath("config")) -- open Neovim config directory
end)
Screencast from 2025-09-11 07-37-45.webm
Jump to definition in detour
vim.keymap.set("n", "<leader>gd", function()
-- Open detour with the same buffer
if not require("detour").Detour() then
return
end
vim.lsp.buf.definition() -- jump to definition
end)
Screencast from 2025-09-13 18-57-09.webm
Wrap a TUI: top
You can wrap any TUI in a detour. Here is an example.
Run top in a detour:
vim.keymap.set("n", "<leader>p", function()
local window_id = require("detour").Detour() -- open a detour
if not window_id then
return
end
vim.cmd.terminal("top") -- open a terminal buffer
vim.bo.bufhidden = "delete" -- close the terminal when window closes
vim.wo[window_id].signcolumn = "no" -- In Neovim 0.10, the signcolumn can push the TUI a bit out of window
-- It's common for people to have `<Esc>` mapped to `<C-\><C-n>` for terminals.
-- This can get in the way when interacting with TUIs.
-- This maps the escape key back to itself (for this buffer) to fix this problem.
vim.keymap.set("t", "<Esc>", "<Esc>", { buffer = true })
vim.cmd.startinsert() -- go into insert mode
vim.api.nvim_create_autocmd({ "TermClose" }, {
buffer = vim.api.nvim_get_current_buf(),
callback = function()
-- This automated keypress skips for you the "[Process exited 0]" message
-- that the embedded terminal shows.
vim.api.nvim_feedkeys("i", "n", false)
end,
})
end)
||
| :--: |
| Use keymap above -> Close window |
Installation
Lazy.nvim
{ "carbon-steel/detour.nvim",
config = function ()
require("detour").setup({
-- Put custom configuration here
})
vim.keymap.set('n', '<c-w><enter>', ":Detour<cr>")
vim.keymap.set('n', '<c-w>.', ":DetourCurrentWindow<cr>")
local detour_moves = require("detour.movements")
-- NOTE: While using `detour_moves` is not required to use this
-- plugin, it is strongly recommended as it makes window navigation
-- much more intuitive.
--
-- The following keymaps are drop in replacements for Vim's regular
-- window navigation commands. These replacements allows you to
-- skip over windows covered by detours (which is a much more
-- intuitive motion) but are otherwise the same as normal window
-- navigation.
--
-- This is an example set of keymaps, but if you use other keys to
-- navigate windows, changes these keymaps to suit your situation.
vim.keymap.set({ "n", "t" }, "<C-j>", detour_moves.DetourWinCmdJ)
vim.keymap.set({ "n", "t" }, "<C-w>j", detour_moves.DetourWinCmdJ)
vim.keymap.set({ "n", "t" }, "<C-w><C-j>", detour_moves.DetourWinCmdJ)
vim.keymap.set({ "n", "t" }, "<C-h>", detour_moves.DetourWinCmdH)
vim.keymap.set({ "n", "t" }, "<C-w>h", detour_moves.DetourWinCmdH)
vim.keymap.set({ "n", "t" }, "<C-w><C-h>", detour_moves.DetourWinCmdH)
vim.keymap.set({ "n", "t" }, "<C-k>", detour_moves.DetourWinCmdK)
vim.keymap.set({ "n", "t" }, "<C-w>k", detour_moves.DetourWinCmdK)
vim.keymap.set({ "n", "t" }, "<C-w><C-k>", detour_moves.DetourWinCmdK)
vim.keymap.set({ "n", "t" }, "<C-l>", detour_moves.DetourWinCmdL)
vim.keymap.set({ "n", "t" }, "<C-w>l", detour_moves.DetourWinCmdL)
vim.keymap.set({ "n", "t" }, "<C-w><C-l>", detour_moves.DetourWinCmdL)
vim.keymap.set({ "n", "t" }, "<C-w>w", detour_moves.DetourWinCmdW)
vim.keymap.set({ "n", "t" }, "<C-w><C-w>", detour_moves.DetourWinCmdW)
end
},
Options
| Option | Description | Default value |
| -- | -- | -- |
| title | "path" sets the path of the current buffer as the title of the float. "none" sets no title. | "path" |
Advanced
Using detours can be simple, but they also come with features for power users:
require("detour.features").UncoverWindow: Update all detours to uncover a given regular windowrequire("detour.features").UncoverWindowWithMouse: Same as above but select which window to uncover with the mouserequire("detour.features").HideAllDetours/RevealAllDetours: Hide and reveal all detours so you can see the windows behind themrequire("detour.features").CloseCurrentStack: Closes the current detour along with all detours it is nested within
Development
- Build help docs: run
make helpfrom the repo root.- Requires
lemmy-helpin your PATH (repo). - Optional: Neovim available for generating
helptags(target does not fail if absent).
- Requires
- Run test: run
make testfrom the repo root- Requires
docker
- Requires
FAQ
I want to convert detours to splits or tabs.
<C-w>s and <C-w>v can be used from within a popup to create splits. <C-w>T creates tabs.
My LSP keeps moving my cursor to other windows.
If your LSP movements (ex: go-to-definition) are opening locations in other windows, make sure that reuse_win is set to false.
My floating windows don't look good.
Some colorschemes don't have visually clear floating window border colors. Consider customizing your colorscheme's FloatBorder to a color that makes your floating windows clearer.
My TUI is slightly wider than the floating window it's in.
This is something I noticed happening when I upgraded to Neovim 0.10. After you create your detour floating window, make sure to turn off signcolumn.
vim.opt.signcolumn = "no"
