Search code examples
vim

How to make map in vim to work only when I don't write inside quotes?


I added

autocmd FileType rust inoremap {} {<CR>}<Esc>O

to my vimrc (should work with js and other languages with curly brackets)

So if I type {} I get

{

}

and cursor is inside the brackets.

The problem is - I need it only if I don't write in quotes like "{}"

The situation

Vim knows I'm writing inside quotes cos it's highlighting it different. So how can I get that conditional map in vimrc?


Solution

  • You can get the current highlight groups with this command (or one of the many variants that have been floating on the web for a long time):

    :echo map(synstack(line('.'), col('.')), 'synIDattr(v:val, "name")')
    

    In this case, you should get a list that looks like this:

    ['rustString']
    

    You can then write a function that tells if the cursor is in a string or not:

    function! IsString()
        return synstack(line('.'), col('.'))
               \ ->map('synIDattr(v:val, "name")')
               \ ->join(',')
               \ =~? 'String'
    endfunction
    

    In general, "string" highlight groups have names like rustString which contains the substring String, so we concatenate all the group names in the list and try to match String. The match is made case-insensitive, just in case.

    See :help user-functions, :help synstack(), :help line(), :help col(), :help map(), :help synIDattr(), :help join(), and :help =~?.

    The next step is to turn your mapping into an "expression mapping", where the RHS is an expression that is evaluated at runtime to produce the macro that will actually be executed:

    inoremap <expr> {} IsString() ? '{}' : '{<CR>}<Esc>O'
    

    See :help <expr> and the oddly named :help trinary.

    Note that the function doesn't assume a specific filetype. Therefore, neither the function nor the mapping need to be constrained to rust or javascript and the autocommand is not needed.