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, 2023 at 18:06

5 Answers 5

10

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, 2023 at 17:16
6

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, 2023 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, 2023 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, 2023 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
1

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 you have read our privacy policy.