I was wondering whether to autocomplete e.g. jj to print('') and then navigate within those ticks: is it better to have a macro, or a remap, or neither? Obviously a remap doesn't allow moving after the fact, unless there's something I'm missing.

  • I'm not saying it's the right way to do it, but I would have done that with an appreviation: iab jj print('')<CMD>norm!2h<CR><C-o>g – Zorzi Mar 25 '20 at 16:47
  • Actually, a map could do the job as well (inoremap jj print('')<CMD>norm!2h<CR>), but it's: if you only hit j one time, you'll have a cool down before the j actually appears.. that's why I prefer the abbreviation version – Zorzi Mar 25 '20 at 16:56
  • If you have a lot of these, best to start thinking about a snippets engine (I've not used any myself, but there are several popular ones—or you could roll your own). – D. Ben Knoble Mar 25 '20 at 20:56

You can use <Left> to move the cursor to inside the single quotes after you do the expansion. For example:

inoremap jj print('')<Left><Left>

The main problem with using an insert-mode mapping here is that typing a single j in insert mode is weird and blocks waiting for another character... Also, typing jj in any context, including inside a word, also gets affected.

An abbreviation works a little bit better here in that jj (or whatever you pick) will only expand when it's typed as a single word. A naive attempt would be:

iabbrev jj print('')<Left><Left>

But now the problem with this approach is that you need to type a whitespace or otherwise non-keyword character after jj to trigger the abbreviation (well, you can actually use CTRL-] to explicitly trigger the abbreviation, but that's not too handy.)

You can work around the issue by having your abbreviation expansion "eat" the whitespace using getchar() calls. This is mentioned in an example in :help abbreviations:

func Eatchar(pat)
    let c = nr2char(getchar(0))
    return (c =~ a:pat) ? '' : c
iabbrev <silent> jj print('')<Left><Left><C-R>=Eatchar('[ \t\r\n]')<CR>

(I also adapted the example to "eat" any of [ \t\r\n] to cover eating the <CR> in jj<CR>, which ends up matching \r which isn't covered by \s.)

If you type a non-keyword argument after jj, it actually ends up inserted into the printed string, so jj* expands to print('*') with the cursor right after the *. I believe that's desirable, acceptable, intuitive.

With an abbreviation, you're also free to use a prefix that's not so unique as jj. For instance, using something like pr would be totally acceptable, as long as you're not using that as a keyword or variable name in the language you're coding on.

  • On your first point about insert mode mapping, jj almost never appears in any context. If we choose a mapping (e.g. jj) that never appears in our writing then we should be fine. – Mahbub Alam Mar 25 '20 at 17:54
  • 1
    @MahbubAlam The problem with a jj mapping in insert mode is that typing the first j gets weird, so it affects only one. Also, not very scalable since you're likely to run out of sequences that never appear in any context, while using abbreviations only relies on full words that won't appear in that context. Using something more mnemonic such as pr is acceptable in that case. – filbranden Mar 25 '20 at 17:56

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.