wrappin is a Neovim text reflow plugin for wrapping prose-like selections while preserving nearby structure such as comment prefixes, bullets, numbered lists, and Markdown headings.
It is designed for the common case where gq or plain line wrapping is too blunt:
- comment continuations should stay comments
- list items should keep hanging indentation
- numbered items should remain separate blocks
- wrapped regions should toggle back to their exact original text when possible
wrappin exposes a deliberately small runtime surface:
:Wrappin [width]- Lua API via
require("wrappin") - optional
formatexpr()bridge <Plug>(Wrappin)targets for normal and visual mode
The plugin runtime lives in plugin/ and lua/wrappin/. Tracked files under
tests/, bin/, and .github/ are development and CI support; they are not
part of the runtime path that Neovim loads for normal plugin use.
{
"Roundlay/wrappin",
opts = {},
}wrappin ships a lazy.lua package spec, so Lazy picks up the command-based
loading defaults automatically.
Local development checkout:
{
dir = "/absolute/path/to/wrappin",
name = "wrappin",
opts = {},
}If you later configure Lazy's dev.path, the plugin can also be loaded with:
{
"Roundlay/wrappin",
dev = true,
opts = {},
}The command is uppercase because Neovim user commands must start with an uppercase letter.
:Wrappin
:Wrappin 72
:Wrappin soft
:Wrappin soft 72
:Wrappin hard
:Wrappin hard 72
:Wrappin set hyphenate on
:Wrappin set hyphenchar =
:'<,'>Wrappin
:'<,'>Wrappin 72
:'<,'>Wrappin soft 72
:'<,'>Wrappin hard 72Without an explicit range, :Wrappin operates on the current line.
soft and hard select the corresponding wrap modes for that invocation.
If you omit the width, Wrappin falls back to textwidth or max_width just
as it does in the Lua API.
set updates the current session config for later invocations. Right now it
supports hyphenate on|off and hyphenchar <token> for hard wrap.
local wrappin = require("wrappin")
wrappin.wrap_current_line()
wrappin.wrap_visual()
wrappin.wrap({
start_line = 10,
end_line = 14,
max_width = 72,
wrap_mode = "soft",
})
wrappin.wrap({
start_line = 10,
end_line = 14,
max_width = 72,
wrap_mode = "hard",
hard_wrap_hyphenate = true,
})For Lazy users, it is usually simplest to map directly to the Lua API:
{
"Roundlay/wrappin",
opts = {},
keys = {
{
"<leader>w",
mode = "x",
function()
require("wrappin").wrap_visual()
end,
desc = "Wrappin visual selection",
},
},
}wrappin also defines <Plug>(Wrappin) targets in normal and visual mode. Those are most useful in traditional non-lazy setups or when your plugin manager loads wrappin before the mapping is used.
require("wrappin").setup({
max_width = 80,
wrap_mode = "normal",
hard_wrap_hyphenate = false,
hard_wrap_hyphenchar = "-",
use_textwidth = true,
context_scan_limit = 8,
set_formatexpr = false,
})Options:
max_width: fallback wrapping width when no explicit width and no activetextwidthare supplied.wrap_mode: wrapping strategy."normal"keeps lines at or under the target width by moving the next word to the following line."soft"breaks after the word that crosses the target width and preserves the separator as trailing space on the emitted line."hard"also targets the width, but splits oversized words when they cannot fit on a line.hard_wrap_hyphenate: whenwrap_modeis"hard", append a trailing-to non-final split chunks when there is room.hard_wrap_hyphenchar: marker text used for hyphenated hard-wrap chunks. The default is"-".use_textwidth: prefer the current buffer'stextwidthwhen it is greater than zero.context_scan_limit: number of surrounding lines inspected when inferring continuation schema.set_formatexpr: installrequire("wrappin").formatexpr()as the activeformatexpr.
Minimal headless integration run:
./tests/run-minimal-headless-integration-suite.shLazy minimal repro environment:
nvim -u ./tests/repro.luaLazy + Busted environment:
nvim -l ./tests/busted.lua tests/specGenerated local state from the development harnesses is written under the
ignored .tests/ and .repro/ directories. doc/tags is also ignored because
it is generated locally.
- Exact restore behavior depends on tracked extmark snapshots, so edits inside a wrapped region intentionally invalidate restoration and fall back to reflowing current text.