3

Recently I have been experimenting with ways to improve Vim's efficiently with keyboard mappings, and I have these mapped in order to make Vim behave like a graphical IDE:

inoremap " ""<Left>
inoremap ( ()<Left>
inoremap ' ''<Left>
inoremap { {<Enter><Enter>}<Up>

That makes it so that every time you type one of those characters, you can simply start adding your text in between. However, I'm wondering if there's a way to add a single quote or parenthesis with the keys mapped like this. Normally I just use the delete key and that's easy enough, yet I'm wondering if this mapping disables you entirely from entering a single character.

I tried mapping a key to this:

:put =nr2char(ASCII code here)

but it didn't work.

1
  • I think these mappings break . when you do i[<esc>.
    – D. Ben Knoble
    Jun 2 at 18:06

5 Answers 5

9

You can do <C-v><char> to insert <char> without triggering the mapping.

See :help i_ctrl-v.


FWIW, over the years I found "auto-closing" to be more trouble than it's worth. The safety argument is mostly a non-issue in the real world (linters, syntax highlighting) and the extra key you have to press to get out of the pair kind of defeats the purpose anyway.

"Auto-expanding", on the other hand, really saves me a lot of uninteresting and repetitive typing on aggregate. I have had these beauties in my vimrc for a very long time:

inoremap (; (<CR>);<C-c>O
inoremap (, (<CR>),<C-c>O
inoremap {; {<CR>};<C-c>O
inoremap {, {<CR>},<C-c>O
inoremap [; [<CR>];<C-c>O
inoremap [, [<CR>],<C-c>O
1
  • I've definitely found that to be true for the curly brackets
    – user8919
    Jun 4 at 17:16
5

You can use Ctrl+V to prevent the next character to invoke a mapping.

See :help i_CTRL-V:

The characters typed right after CTRL-V are not considered for mapping.

So in your case you can use Ctrl+V' to enter a single quote without triggering the mapping. (Same for left paren or left bracket, etc.)

3

This is standard requirement.

My advice would be to use one of the numerous plugins that address the need.

Here are some of the most popular (according to GitHub stars):

3
  • are there any that you like to use in particular?
    – user8919
    Jun 2 at 17:22
  • I'm using auto-pairs that is the most popular on Git-Hub at the time or writing and support the . command. Jun 2 at 17:42
  • If you are using Neovim nvim-autopairs seems to have a lot of traction (the number of stars are increasing steadily) Jun 2 at 17:51
1

For brackets specifically, I've been enjoying using ] to close all brackets. In essence, pressing any of the closing brackets inserts the correct one (sometimes it's wrong; override with <C-v> as usual).

https://github.com/benknoble/Dotfiles/blob/master/links/vim/plugin/rparen.vim

if exists('g:loaded_rparen')
  finish
endif
let g:loaded_rparen = 1

inoremap <expr> ] rparen#MatchingParenType(']')
inoremap <expr> ) rparen#MatchingParenType(')')
inoremap <expr> } rparen#MatchingParenType('}')

and https://github.com/benknoble/Dotfiles/blob/master/links/vim/autoload/rparen.vim

" searchpairpos() timeout in milliseconds
let s:paren_search_timeout = 50

" set to line("w0") to search to top of screen
" set to 1 to search to top of file
let s:paren_search_top = 1

function rparen#MatchingParenType(map)
  " taken from Bram Moolenaar's `matchparen.vim`
  if !has("syntax") || !exists("g:syntax_on")
    let skip = "0"
  else
    let skip = '!empty(filter(map(synstack(line("."), col(".")), ''synIDattr(v:val, "name")''), ' .
          \ '''v:val =~? "string\\|character\\|singlequote\\|escape\\|symbol\\|comment"''))'
    try
      execute 'if' skip '| let skip = "0" | endif'
    catch /^Vim\%((\a\+)\)\=:E363/
      " pattern uses more memory than 'maxmempattern'
      return
    endtry
  endif

  let s:parenOfs = {} " using s: here so we can access it in the lambda below
  let parens = []

  for delims in split(&matchpairs, ',')
    let [left, right] = split(delims, ':')
    let parens += [right]

    let [line, col] = searchpairpos('\M'.left, '', '\M'.right, 'nbW', skip,
          \ s:paren_search_top, s:paren_search_timeout)
    let ofs = line2byte(line) + col
    let s:parenOfs[right] = ofs
  endfor

  eval parens->sort({lhs, rhs -> s:parenOfs[rhs] - s:parenOfs[lhs]})
  if s:parenOfs->values()->filter('v:val >= 0')->empty()
    return a:map
  endif
  return parens[0]
endfunction
0

I use the plugin luasnip to accomplish this. Just make ( as a snippet. You can choose the way to trigger this snippet. The advantage of luasnip over auto-pairs, delimitMate and so on is that I can use <tab> to jump from with in a group of () to the outside of it. In addition, I use delimitMate with let delimitMate_autoclose=0(yes, I don't use the main function of this plugin) such that when I delete ), it automatically delete (.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.