Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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.

share|improve this question
    
Have you tried autoclose? You can jump out of pairs with ), remapping c-j to that might work. –  Kevin Nov 19 '12 at 23:15
add comment

4 Answers

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.

share|improve this answer
add comment

Something like this might work out for you.

inoremap {      {}<Left>
inoremap {<CR>  {<CR>}<Esc>O
inoremap {{     {
inoremap {}     {}
share|improve this answer
    
What you suggest is a simple mapping which does not allow to jump over the closing brace. –  Marco Nov 20 '12 at 9:25
add comment

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>+

There is nothing very novel here, however I hope these solutions may help.

share|improve this answer
add comment

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
share|improve this answer
    
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 at 15:28
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.