19

How do I automatically create matching braces in vim?

It should work as follows: If I input an open brace the closing one should appear automatically and the cursor should be placed in between. To jump out of the brace pair the combination Ctrl-j is used.

There are plenty of plugins providing automatic brace insertion. But they

  • either use a different key combination than Ctrl-j to jump out of the brace pair or
  • interfere with the UltiSnips key bindings.

The following works as expected

:inoremap ( ()<Esc>:let leavechar=")"<CR>i
:imap <C-j> <Esc>:exec "normal f" . leavechar<CR>a

But with these settings the UltiSnips snippets don't work any more. So I used UltiSnips to insert the closing brace:

inoremap ( (<C-R>=UltiSnips_Anon('($1)$0', '(')<CR>

This almost works. The matching brace is inserted and I can skip the closing one with Ctrl-j. This, however, works only if there is a space in front of the open brace.

main () // works, the closing parentheses is added
main(   // fails without a space

My solutions either require a different key binding to jump over the closing brace or require a space in front of the open one. How to fix this?

Note: I used parentheses as an example. It should work with parentheses, braces, brackets and less-than signs and not interfere with the UltiSnips plugin.

1
  • 1
    Have you tried autoclose? You can jump out of pairs with ), remapping c-j to that might work.
    – Kevin
    Nov 19, 2012 at 23:15

6 Answers 6

5

How do I automatically create matching braces in vim?

This problem is non-trivial, as you will see. The simple answer is: Use a plugin, like autoclose or smartinput. Just naively remapping the ( [ .. keys will present you with edgecases in a couple of lines, that is why these plugins are made, and why they usually are quite complex (if they are any good).

So I used UltiSnips to insert the closing brace

You could try this instead (which is different from my original suggestion):

inoremap ( ()<CR>=UltiSnips_Anon('$1)$0', ')')<CR>

Where UltiSnips will split the line on ( instead of including main in the match. The problem with using a snippet plugin for this is that they (usually) don't support nesting, so they will forget any jump positions when the next (nested) snippet is inserted.

To jump out of the brace pair the combination Ctrl-j is used.

You can remap Ctrl-j for either UltiSnips or the conflicting plugin. If the mappings are not listed in the documentation, you can use :map/imap/nmap <key> to show the mapping. If you want Ctrl-j to pick and choose and do the work of both plugins, you are asking for:

  1. something a lot more advanced than I suspect you might think
  2. something I suspect you do not really want

Consider this bash loop:

while (( ${arr1[i]} < ${arr2[i<CURSOR>]} )); do
  [next_snippet_position_marker]
done

In this case, you'll need to press Ctrl-j 5 times to get to the loop body. Using the closing braces to jump out of a corresponding pair gives you much more control of where you actually want the cursor to move. You could implement a stack to keep track of inserted pairs and use Ctrl-j to pop and move, but then you'll run into problems if you start manually deleting braces without removing them from the stack. So you'll start solving problems which the brace-plugins are trying to solve. This is a hard wheel to reinvent.

Expanding on this, you can get what you initially asked for, using Ctrl-j to jump out of braces and snippets. If you use some dummy value to represent snippets positions and push them onto the stack in addition to the closing braces, with dynamic remapping of Ctrl-j to correspond to handing of snippets or braces. But then you'll need to implement some fairly advanced heuristics to figure out what magic should happen, since you are essentially taking on two problems at once, which is currently being solved by the creators of snippet insertion plugins and brace matching plugins separately, and re-solving these problems again, on top of their solutions, to achieve convenience and, well, magic.

It comes down to control vs convenience. I think control will serve you better in this case, meaning you should keep these problems and their associated plugins and keysmappings separate. If you still want convenience, it's doable, but it's hard.

2

I have found autoclose adding a bit of lag, and also interfering with my setup (Ultisnips and Supertab), but I like its approach of emulating Eclipse CDT.

I use delimitMate with the following maps to help with the navigation and indentation, trying to get the same results:

imap <C-F> <C-G>g           " To jump out brackets in same line.
inoremap <C-K> <ESC>ki<TAB> " To move and insert an indent on the line before the current cursor (assuming empty line)

I use it this way: insert opening { <CR><CR><C-K> and start writing in the indented inner line.

1
  • I found delimitMate to be adding a slight lag too
    – icc97
    May 29, 2018 at 8:00
2

Personally I use smartinput for auto insertion of brackets and quotes. For example typing { anywhere will produce {} with your cursor in the middle. To leave the { bracket group you just type the } when next to the inserted }.

However this does support your requirement of tabstops and jumping to outside the bracket. It also does not create a new between your brackets, but you just need one extra key for that anyway - <CR>.

You could map <C-J> to jump to the next line down:

:inoremap <C-J> <C-O>j

or if you wanted to move to the start of the next line:

:inoremap <C-J> <C-O>+
0

Something like this might work out for you.

inoremap {      {}<Left>
inoremap {<CR>  {<CR>}<Esc>O
inoremap {{     {
inoremap {}     {}
1
  • What you suggest is a simple mapping which does not allow to jump over the closing brace.
    – Marco
    Nov 20, 2012 at 9:25
0

I think delimitMate does what you need.

It automatically closes parentheses or quotes and puts your cursor in between. To jump out of a parentheses pair (while being in insert mode) you'd do Ctrl-g + g but you can remap this to be Ctrl-j by adding this to your vimrc:

% Jump out of a block of parentheses (uses delimitMate)
imap <C-j> <C-g>g
1
  • It is better to provide more details here, and use the link as a reference for further reading. That way your answer does not loose all of it value when the link becomes invalid.
    – Anthon
    Jan 26, 2014 at 15:28
0

Actually, Ultisnips can do it almost perfectly (this may be new). Slightly modifying answers given above, I have good success with

inoremap () ()<C-R>=UltiSnips#Anon('($1)$0', '()', 'double parentheses', 'i')<CR>

One has to type () which find better but that can be changed to ( as well. Then, the additional optional arguments to UltiSnips#Anon are a description and the modifier i which allows in-word expansion.

Jumping out of the parentheses with <c-j> works, also nested within other snippets. However, invoking nested () parentheses seems to break this feature, which is not a big deal.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .