The Vim Surround plugin lets you add, delete, or change delimiters around text objects, and it’s surprisingly powerful for something so focused.
Let’s see it in action. Imagine you have this text:
This is a test.
And you want to wrap the word "test" in parentheses. You’d put your cursor on the word "test" and type:
ysiw(
y- yank (like copy)s- surround (the core command of the plugin)iw- inner word (the text object)(- the delimiter to add
The result:
This is a (test).
Or maybe you want to change the parentheses to square brackets:
cs( [
c- changes- surround(- the old delimiter[- the new delimiter
The result:
This is a [test].
To delete the delimiters:
ds(
d- deletes- surround(- the delimiter to delete
The result:
This is a test.
The magic of surround.vim is its ability to work with any text object. You’re not limited to words. You can surround lines, paragraphs, or even custom selections.
Here are some common text objects you’ll use with ys, cs, and ds:
iw: inner wordaw: a wordi": inner double quotesa": a double quotei': inner single quotesa': a single quotei(orib: inner parenthesesab: a parentheses blocki[ori]: inner square bracketsa[ora]: a square brackets blocki{ori}: inner curly bracesa{ora}: a curly braces blockit: inner tag (for HTML/XML)at: a tagip: inner paragraphap: a paragraphi$: inner line (from cursor to end of line)a$: a line (from cursor to end of line)i}: inner motion (e.g.,y<motion>)a}: a motion (e.g.,y<motion>)
The "add" command ys takes a text object and a delimiter. The "change" command cs takes an old delimiter and a new delimiter. The "delete" command ds takes the delimiter to remove.
Let’s say you have a block of text and want to wrap the whole thing in triple backticks for a code block. Select the lines visually (e.g., Vjjj) and then type:
`S```
S- the visual mode version ofys(surround the selection)`- the delimiter to add
The result:
Line 1 Line 2 Line 3
This works because surround.vim registers itself as an operator. When you type y, c, or d followed by s, it intercepts that command and applies its surround logic. The s is the key that tells Vim "hey, this is a surround command."
The delimiters themselves can be more than just single characters. You can use pairs like (), [], {}, "", '', and even HTML tags like <b> and </b>.
When changing delimiters, cs is smart. If you have (some text), and you type cs[, it will correctly change it to [some text]. It understands matching pairs.
One common pattern is to surround a whole line. If your cursor is anywhere on a line, typing ys$" will wrap the entire line (from the cursor to the end) in double quotes. If you want the whole line, including text before the cursor, you can use ys}}" (where }} represents the motion for the whole line).
The plugin also handles nested delimiters intelligently. If you have (outer [inner]), and you want to change the inner brackets to curly braces, you can place your cursor on [inner] and type cs[ {. It will become (outer {inner}).
The ds command is particularly useful for quickly cleaning up markup. If you have a line like * This is a list item., and you want to remove the asterisk and space, you can type ds*. It will remove the asterisk and any trailing whitespace that was part of the "surrounded" object.
You can also use motions with ys. For example, ysj( will surround the current line and the line below it with parentheses. Or ys/abc<enter>( will surround the text from your cursor up to the next occurrence of "abc" with parentheses.
The plugin’s configuration is minimal, but you can customize things like which delimiters are considered "pairs" or add custom ones in your .vimrc. For instance, let g:surround_pairs = [ ["(", ")"], ["[", "]"], ["{", "}"], ["<", ">"], ["|", "|"] ] is a common starting point.
If you find yourself frequently typing manual characters to wrap text, like i(" to get inside quotes, or a" to select quoted text, surround.vim can drastically speed up those operations. It’s one of those plugins that feels like magic once you get used to it, making text manipulation feel fluid and intuitive.
The next thing you’ll likely want to explore is how to create custom surround mappings for frequently used, non-standard delimiters.