2

I'm using vim to code C++ and I want to handle the parenthesis more easily.

I knew that there was a plugin named Surround, which can help us but it is not what I expected.

I set some shortcuts as below:

inoremap " ""<left>
inoremap ' ''<left>
inoremap ( ()<left>
inoremap [ []<left>
inoremap < <><left>
inoremap { {}<left>

With their help, if I type (, () will be printed and the cursor will be located between ( and ). See below:

(|)  # | is the cursor and the vim is under INSERT mode

Now, what I expected is if I type Backspace now, it will delete ( and ) together.

Is it possible to do so?

3

It is possible. There are plugins like Lexima, auto-pairs and others that handle deletion, but handling pairs of characters doesn't seem to be easy to solve in insert mode: I've used both and there's always a corner case that doesn't give you the right result. I think Surround is more reliable

| improve this answer | |
  • OK, if there is not a good way to solve this issue under INSERT mode, I'll use Surround. – Yves Apr 22 at 9:09
  • 1
    I think you should try Lexima or auto-pairs anyway, they work most of the time. Currently I use Surround and auto-pairs but I may try Lexima again – Tae Apr 22 at 9:11
  • 1
    auto-pairs works for me. Thanks dude. – Yves Apr 22 at 9:12
  • 1
    Your answer is good, but I think it could be improved by describing a corner case or a few where auto-pairs falls short. In my case, whenever I start using it I feel it's wonderful and then I hit one of those (trying to add quotes or braces around existing text, for instance) and get annoyed... I actually find that the cases where auto-pairs falls short are exactly the ones where vim-surround shines. So I actually thing auto-pairs is good if complemented by vim-surround! And using auto-pairs will teach you to pick surround where you know you'll need it. – filbranden Apr 22 at 14:42
  • @Yves From your last 5 or so questions... Yeah it's pretty clear auto-pairs is for you!!! 😁 You'll find the corner cases, keep vim-surround around. When auto-pairs does something weird, that typically means you were trying to do something better done in Normal mode. Undo the auto-pairs action (better than trying to fix it manually) and check how you'd solve that using surround. In time you'll learn to use this combo very effectively! – filbranden Apr 22 at 14:46
3

I saw the previous answer, but if you don't want to burden yourself with a plugin, you can achieve that with a function and a mapping:

let g:couples = ['(#)', '[#]', '{#}', '<#>', '<div>#</div>']
function BetterBS()

    for l:couple in g:couples
        if ! (l:couple =~ '#')
            continue
        endif
        let l:regex = substitute(escape(l:couple, '/\^$*.[~'), '#', '\\%#', '')
        if search(l:regex, 'n')
            let l:out = repeat("\<BS>", len(matchstr(l:couple, '^.\{-}\ze#')))
            let l:out .= repeat("\<DEL>", len(matchstr(l:couple, '#\zs.\{-}$')))
            return l:out
        endif
    endfor

    return "\<BS>"
endfunction
inoremap <silent> <BS> <C-r>=BetterBS()<CR>

You just have to modify the g:couples variable and you're good to go! (# identifies the position of the cursor)

| improve this answer | |
3

It's really not that complicated, you don't even need regex for this to work:

inoremap <expr> <bs> <sid>remove_pair()
imap <c-h> <bs>

function s:remove_pair() abort
  let pair = getline('.')[ col('.')-2 : col('.')-1 ]
  return stridx('""''''()[]<>{}', pair) % 2 == 0 ? "\<del>\<c-h>" : "\<bs>"
endfunction

It works by test the current pair string with all pair string, if the result index is even, you are good to go. If you map <bs>, you should also map <c-h>.

You might also want to use <c-g>U to reserve undo:

inoremap " ""<c-g>U<left>
...

I have used auto-pairs before, the corner cases is so annoying that I throw it away eventually.

I use Surround now, but when I need to type blank pair, I do it by hand, <esc> don't count as key stroke IMO.

Whatever approach you choose to go with, make sure it doesn't surprise you.

| improve this answer | |
2

I'm maintaining lh-brackets that provides several things here:

  • redoable insertion of various pair of brackets

    • adding a new pair is quite easy e.g.

      :Brackets <+ +>
      :Brackets <div> </div> -trigger=<m-d>
      
  • removal of empty pairs on <BS> when the cursor is in the middle of the pair (note that it works with the previous (<+|+>, and <div>|</div>). The removal is redo-friendly as well.

    • it also takes care of the optional (but activated by default) placeholders
  • various ways to jumps over one or several closing bracket sequences are provided
  • surrounding is also provided, not in a vim way (like Tim Pope's surround plugin), but with more direct actions: just type (, {, <leader>[, '' (double), "", etc. to surround the current selection or the current word (or line...)
  • removing bracket pairs around a non empty sequence is also provided (<m-b><del> with gvim, I don't remember the default keybinding for vim in terminal)

The only annoying corner cases I haven't solved yet are related to:

  • unbalanced pairs (when we manually delete the opening or the closing sequence),
  • symmetric pairs (like quotes): When the cursor is in front of a quote, it can be difficult to distinguish whether we want to jump-over the quote (i.e. close the current string), or whether we want to insert a new string.

I don't see any situation were hitting <BS> in (|) case is problematic.

| improve this answer | |
1

Surround has a philosophy of being very « vim-like »—it uses operators to work on objects, much like many of vim’s most powerful normal mode constructs. I would argue that it is not what some expect because they expect an auto-pairs-like experience that they are used to from other editors, or because they are still learning about normal-mode and the many operator/object compositions.

Surround is composable, which in this case essentially means that the more vim you learn, the more places surround will be useable and useful.

But surround does (often) require you to go to normal mode—which is fine, when it’s your default mode! (Note: the insert-mode <c-s> mapping means this isn’t strictly true, but I don’t use it as much as I use the normal mode mappings.)


One fairly low-level way to do this pre-surround was with marks and %:

nnoremap dsb mc%mp%x`px`c

I’ve taken extra care to restore the cursor position here (the c mark).

We can adapt this for insert mode either with

imap <bs> <c-o>dsb

or

inoremap <bs> <esc>%mp%x`pxgi

(Not recommended if you like being able to backspace in insert mode. You could use expr-mappings or other tricks to be more robust, or simply pick a different mapping.)

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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