termyank.nvim
May 1, 2026 · View on GitHub
For us poor bastards that don't just use tmux for some reason.
Clean up register contents after yanking, deleting, or changing text inside a Neovim :terminal buffer: strip \r characters, and collapse wrap-induced line breaks so word-level yanks that cross terminal row boundaries paste as a single string.
Optionally overrides text-object commands (yiW, yi(, etc) to work across newlines.
If you want to preserve \r's use a Blockwise select (<C-v>).
Why
Terminal output uses \r\n line endings and terminal rows hard-wrap long lines. That means a URL, filename, or shell command can look like one logical token on screen but land in your register as multiple ^M-terminated lines. Pasting splits the token across lines. This plugin silently normalizes those yanks so they paste cleanly.
Install
lazy.nvim
{
"hawknewton/termyank.nvim",
event = { "TermOpen", "TermEnter" },
}
packer.nvim
use "hawknewton/termyank.nvim"
Configuration
All options are passed to setup(). Defaults shown below.
require("termyank").setup({
-- When true, override the following text objects inside terminal buffers
-- so they extend across adjacent non-blank buffer lines. Useful when a
-- token/string/bracketed expression was visually wrapped across terminal
-- rows by the PTY.
--
-- Overridden objects: iW aW, i" a", i' a', i` a`, i( a(, ib ab, i[ a[,
-- i{ a{, iB aB (and the matching close-delimiter keys).
text_objects_span_lines = true,
})
With lazy.nvim:
{
"hawknewton/termyank.nvim",
event = { "TermOpen", "TermEnter" },
opts = { text_objects_span_lines = true },
}
Commands
:TermYankOn— enable register sanitization.:TermYankOff— disable sanitization (registers are left untouched after yanks in terminal buffers).:TermYankToggle— flip between on and off.
Sanitization is on by default. Text-object overrides are not affected by these commands — they stay installed once text_objects_span_lines = true at setup time.
How it works
- A
TextYankPostautocmd handles yanks, deletes, and changes (Neovim fires this event for all three) originating in buffers where&buftype == "terminal". - On
TermOpenthe plugin setswrapfor the window so navigating past the right edge in normal mode doesn't side-scroll into the terminal's trailing padding. - On
TermOpen, iftext_objects_span_lines = true, buffer-local operator-pending and visual-mode mappings are installed for the wrap-aware text objects listed above.
Behavior by selection type:
- Charwise (
v,yw,yW,yi",y$, …) and Linewise (V,yy): every\ris stripped and all lines are joined into a single charwise line. A linewise yank from a terminal becomes charwise in the register — use a blockwise yank if you want multi-line paste behavior. - Blockwise (
<C-v>): left completely untouched. Rectangular selections keep their structure and their\rcharacters.
Wrap-aware text objects
When text_objects_span_lines = true, the overridden text objects behave like their builtin counterparts but search across adjacent non-blank buffer lines. Blank lines act as boundaries (so a yank won't accidentally swallow the next paragraph of terminal output).
iW/aW— extended WORD that spans wrapped rows.i"/a",i'/a',i`/a`— quoted strings that wrap.i(/a((andib/ab,i)/a)) — parens, with nesting.i[/a[(andi]/a]) — brackets, with nesting.i{/a{(andiB/aB,i}/a}) — braces, with nesting.
After the text object selects the multi-line range, the TextYankPost handler collapses it into a single charwise register entry as usual — so yiW on a wrapped URL yields the whole URL, and yi" on a wrapped quoted string yields the whole string, ready to paste as one line.