102

Is there a way to enclose some text in parentheses (or curly brackets) in vim?

In other words, how would you do this?

Initial string:

It is sunny outside.

Final string:

It is (sunny) outside.

On a funny note, I just hit :w to submit this question.

Happy vim-ing, SOCommunity!

terence hill
  • 3,354
  • 18
  • 31
eqb
  • 1,650
  • 3
  • 20
  • 25
  • Seems like a macro could easily get the job done, but I dare not prescribe one in particular - I'm still pretty green in VIM. But something like 'move to beginning of word, insert left paren, move to end of word, insert right paren' sounds like a macro that would work in most cases... – Kenan Banks Nov 09 '11 at 20:10
  • @Triptych - Right along the way you suggested, only a mapping instead of a macro. See my answer. – Rook Nov 09 '11 at 22:05

14 Answers14

123

For one word, I've been using:

bcw()<Esc>P

That is, go to the start of the word, cut the word and go into insert mode, type the brackets and then exit insert mode and paste the word back in.

Keep in mind that this will overwrite your yank register.

You can, of course replace bcw with whatever movement and selection types you need, like

5cc{<Enter>}<Esc>P
Ian Hatch
  • 1,241
  • 2
  • 8
  • 4
  • 43
    I hate it when my yanks get overwritten. – d1str0 Feb 25 '16 at 07:48
  • 1
    No plugin needed. Great. Honestly, with surround.vim you have to press about the same number of keys so why use a plugin? – hamid Feb 26 '18 at 18:43
  • vim's strength is its cursor-control language! Thanks! It is easy to record this into a script and make it a macro. `qbcw()Pq` then `@q` to quickly redo words. – Thomson Comer Feb 26 '18 at 22:10
  • 4
    @d1str0 you can still find your yank in register `"0` – Bananach Jun 26 '18 at 09:24
  • 2
    tangential, but yanks should really have been made a stack, much like undo. – hraban Mar 21 '19 at 11:07
  • To make it a bit quicker, for single words I usually do `ciw () "`. Control r pastes the last thing added to the register so I just make sure the cursor is within the parenthesis (most IDEs and editors automatically place there so it is fairly quick". `ciw` obviously can be changed to a visual selection and the rest of the command is the same. – joshpetit Sep 15 '20 at 02:13
  • Slight variation which works correctly even if the cursor is already at the beginning of the word: `ciw()P` i.e. "change inside word" instead of going to the beginning of the (possibly previous) word. – Magnus Teekivi Jan 18 '23 at 12:09
73

Here's examples using surround.vim. On vim normal mode, place your cursor over the desired word (sunny in this case) and type:

ysiw

Then type )

So:

initial string:

It is sunny outside.

Final string:

It is (sunny) outside.

Extra space: Use the opening paren, or bracket, like ysiw( to surround with parens and a space between paren and start + end of word)

Easier in Visual mode Enter visual mode by pressing v. Now if you type S(, the word will be surrounded by spaces. However if you use the closing ) instead S) it will not be surrounded by spaces.

This applies to all bracket pair surroundings, <> [] {} (), not merely to (). The behavior of S< is such that it expects a tag enclosure so only S> is able to surround as <>.

More:

Type ysiw followed by } to do the same for curlies

Might as well add some more examples here:

  • type cs(' to [c]hange [s]urroundings from ( to '

  • cs'( to change from ' surroundings to ()

  • ds(. to [d]elete ( [s]urroundings altogether

Even more:

And why not quote the rest of Tpope's page to save us from clicking through to the link?

//begin quote:

It's easiest to explain with examples.

Press cs"' inside

"Hello world!" to change it to

'Hello world!'

--

Now press cs'<q> to change it to

<q>Hello world!</q>

--

To go full circle, press cst" to get

"Hello world!"

--

To remove the delimiters entirely, press ds".

Hello world!

--

Now with the cursor on "Hello", press ysiw] (iw is a text object).

[Hello] world!

Let's make that braces and add some space (use } instead of { for no space): cs]{

{ Hello } world!

--

Now wrap the entire line in parentheses with yssb or yss).

({ Hello } world!)

--

Revert to the original text: ds{ds)

Hello world!

--

Emphasize hello: ysiw<em>

<em>Hello</em> world!

--

Finally, let's try out visual mode. Press a capital V (for linewise visual mode) followed by S<p class="important">.

<p class="important">
  <em>Hello</em> world!
</p>

This plugin is very powerful for HTML and XML editing, a niche which currently seems underfilled in Vim land. (As opposed to HTML/XML inserting, for which many plugins are available). Adding, changing, and removing pairs of tags simultaneously is a breeze.

The . command will work with ds, cs, and yss if you install repeat.vim.

Jonathan Holvey
  • 697
  • 9
  • 27
Danny
  • 3,982
  • 1
  • 34
  • 42
  • 3
    why do we need the y in ysiw? – Jwan622 Jul 08 '20 at 14:00
  • 1
    @Jwan622 Probably just to avoid conflicts with the `s` key in normal mode. If it was `siw`, the character under the cursor would be replaced with `iw` and we'd be left in insert mode. I think. I haven't tried it. – byxor Jun 02 '21 at 14:30
  • @Jwan622 because we kind of `y`ank the surroundings in there. You could also `d`elete `s`urroundings or `c`hange `s`urroundings ;-) – dedObed Aug 10 '22 at 13:44
42

Surround.vim should do the trick. If you want to repeat that with '.', see here.

Community
  • 1
  • 1
mwilson
  • 1,255
  • 12
  • 17
9

Why not use :s// (search and replace) ?

Eg:

:s/sunny/(&)/

You probably have something else in mind, but I can't tell from your question what it is.

(& is shorthand for the matched text)


Aside: To automate this, you could record a macro like so: (with cursor at start of sunny):

  • qq - start recording macro in register q
  • ye - yank to the end of the word (sunny, in this case) - could also yE, etc.
  • :s/ - enter command mode, and start a search-and-replace command
  • Ctrl+R, ", paste the yanked text
  • /(&)/ - finish the command, as described above. The command line will now read :s/sunny/(&)/
  • enter - run the command, which adds the parentheses.
  • q - stop recording macro

Now, if you go to the start a different word, you can type @q to run the macro. Nothing in the macro is specific to the word sunny, so it should work on anything.

You could update it to work in the middle of a word by first doing b, or so. You get the idea. You might also want to save and restore the cursor position, which is easy to add (eg: mz at start and `z at end).

jwd
  • 10,837
  • 3
  • 43
  • 67
  • 7
    Because you end up typing twice a word that is already written, when what you want is to select it and put parentheses around. – Titou May 04 '17 at 07:04
  • To avoid typing it twice, you can yank it (`yw`), then paste that into your command (`q:` and edit normally). Of course, that process can be automated, too. – jwd Jul 14 '21 at 18:12
  • Thank you. `:s/.*/[&](&)/g` can be used to transform a list of files (from `:.!ls`) to a list of markdown links. – Paul Rougieux Mar 23 '23 at 14:22
9

You can define a simple mapping for that sort'a thing. This ought to do it. While in normal mode type z to surround a word in parenthesis.

:nnoremap <leader>z viw<esc>a)<esc>hbi(<esc>lel
Rook
  • 60,248
  • 49
  • 165
  • 242
  • Awesome. Simple press 2 buttons "leader + z". This is my favorite answer – Darren Haynes Feb 06 '17 at 21:16
  • Why passing in view mode ? The simpler moves seem to make it `:nnoremap ( bi(eli)` Or did I miss something ? Btw very good answer. – Titou Jan 16 '18 at 12:11
  • 1
    @Titou Because if you call it while your cursor is at the end of the word, it surrounds the next term after – branboyer Aug 09 '23 at 10:05
6

You can use the following commands:

cw(<C-r><C-o>")<ESC>

The advantage is that you can use the dot command for repeat this action for different words. This works for custom encloses too, for example:

Before change:

word

then use the command:

cw\enquote{<C-r><C-o>"}<ESC>

Final result:

\enquote{word}
vasconssa
  • 61
  • 2
  • 3
  • 1
    This is brilliant. You can also mark a text with `v` and then use just ```c(")``` obviously. The pasting of the default or unnamed register containing the text of the last delete or yank (see `:help i_^r`) to the last position of the cursor (`:help ^o`) and immediately inserting a closing parenthesis is a quite handy. – AdamKalisz Nov 17 '19 at 23:14
  • @AdamKalisz sorry, but what does do here? From `:help` it only says "Switch to normal mode for a single command, then back." What exactly do we need to go back to normal mode for `"`? – bing Jul 25 '21 at 21:28
  • @bing I am not sure, but it works. :-) If you find out more, please post it here. – AdamKalisz Jul 25 '21 at 21:41
  • 1
    Ok. Found it. :h i_ctrl-r_ctrl-o This one inserts the contents of the register, LITERALLY – bing Jul 26 '21 at 05:56
6

Surround.vim

Ahmed Kotb
  • 6,269
  • 6
  • 33
  • 52
1

I prefer not to use any plugins and want to keep everything restricted to my dotfiles for easy portability. The easiest way I can think of is to simply record it and replay it. If you plan to use it more often across sessions, you can map the key in your .vimrc as well but I didn't need to (and I'm really bad at figuring out which keystroke to map to).

Assuming you have a novice level understanding of vim, let's say I have a line (for simplicity) that I want to surround with square brackets. e.g.

hello world

Go into normal mode. q1 to start recording. Now do the following

ESC ^i[ ESC $a] ESC

This will leave the current line parenthesized. Now you can choose any line and press @1 to repeat it.

If for example you would want the current word to be paranthesised then simply record and playback the following recipe:

ESC bi[ ESC ea] ESC

You can ofcourse try to optimise for keystrokes but this serves for me for now.

zakishaheen
  • 5,551
  • 1
  • 22
  • 29
1

I know this is an old question, but such a complex solution like this has not been mentioned here yet and I am sure it could help someone. In my .vimrc file I have a line like this:

xnoremap <silent> ( <ESC>:let p = &paste<CR>:set paste<CR>:let a = line2byte(line('.')) + col('.')<CR>gvc()<ESC>:if getregtype() ==# 'V'<CR>call setreg('"', substitute(@", '\n$', '', ''), 'c')<CR>endif<CR>P:exe "goto ".a<CR>:exe "let &paste=".p<CR>

It is enough to select some text in visual mode and to press (. This mapping works also for linewise and blockwise visual mode.

How does it work?

<ESC> will quit the visual mode

:let p = &paste<CR>:set paste<CR>...:exe "let &paste=".p<CR> will make sure that the macro will be executed in paste mode (because autoindent and similar improvements could include some extra spaces we do not want) while paste status after macro will stay the same as before macro was executed

:let a = line2byte(line('.')) + col('.')<CR> will find the current byte offset (1-based)

gv will reselect the previously selected text

c will delete selection and brings us to insert mode to replace the selection

()<ESC> will put in our desired parentheses and quit insert mode

:if getregtype()==#'V'<CR>call setreg('"',substitute(@",'\n$','',''),'c')<CR>endif<CR> will remove trailing \n from unnamed (") register and change its type to characterwise, but only if it was linewise before (to make it possible to paste from the registry within the parentheses)

P will paste previously replaced text within the parentheses

:exe "goto ".a<CR> will bring cursor back on the previous position (goto uses 0-based offset, so we do not have to increment previously recorded position by one because of inserted left parenthesis)

Michal
  • 90
  • 1
  • 2
  • 5
1

Another useful tidbit I haven't seen in other answers, yet:

In a mapping, use expand("<cword>") (or any of the other similar <cWORD>, <cexpr>, etc.). This gives you access to the "word under the cursor."

For instance, to replace all occurrences on the current line of "word under the cursor" with parenthesized versions:

nnoremap <leader>( :execute "s!" . expand("<cword>") . "!(&)!g"<CR>

There are some downsides:

  • If <cword> has an exclamation mark in it, this breaks (you can choose a different s/// character, of course).
  • You can't replace only the single word under the cursor. You can do the first one on a line (the default), or all occurrences on a line (as above, with /g flag). You could even do all occurrences in a file with %s....
jwd
  • 10,837
  • 3
  • 43
  • 67
1

Check out vim-sandwich

If you've used surround.vim before, you can even make vim-sandwich use surround.vim keymaps

Tbh the only thing that made me switch from surround.vim is that vim-sandwich has the "closest pair" operator.

Below is the result of subsequently writing "dsb" on a few parentheses . "dsb" is the default "remove nearest bracket pair" binding when using vim-sandwich with surround.vim keybindings. The standard sandwich.vim keybinding though is "sdb" (sandwich delete bracket)

X - cursor

Text User input in normal mode
[ { ( X ) } ] dsb
[ { X } ] dsb
[ X ] dsb
1

I would add another way, in normal mode write:

ciw

which is "change internal word", then in edit mode write both parentheses "()", press escape and just paste the word with:

P
0

For something like this it's probably better to play with ranges and groups than to employ external plugins:

:%s~\v(\w{5}) ~(\1) ~g

the above searches for a 5 letter word followed by a space, groups the match using (), and substitutes it with surrounding parentheses around the grouped match.

gts
  • 627
  • 5
  • 13
0

Ok vim-surround is cool.

But have you used to use less 3rd party code?

I use this following keybindings to create my own surroundings with nvim.lua

local keymap = vim.api.nvim_set_keymap
local opts = { noremap = true, silent = true }

keymap("v", "\"", ":s/\\%V\\(.*\\)\\%V/\"\\1\"/<CR>", opts)
keymap("v", "\'", ":s/\\%V\\(.*\\)\\%V/\'\\1\'/<CR>", opts)
keymap("v", "(", ":s/\\%V\\(.*\\)\\%V/(\\1)/<CR>", opts)
keymap("v", "[", ":s/\\%V\\(.*\\)\\%V/[\\1]/<CR>", opts)
keymap("v", "{", ":s/\\%V\\(.*\\)\\%V/{\\1}/<CR>", opts)

Highlight what you want to enclose (so you are in VISUAL MODE) and then press the key you want to enclose with!

Super easy super,super reproducible

alex
  • 89
  • 7