3

In using an iabbrev, vim help gives the following example of how it can be used to ignore the character after typing in the keyword:

Towards the bottom of Abbreviations:

You can even do more complicated things.  For example, to consume the space
typed after an abbreviation:
   func Eatchar(pat)
      let c = nr2char(getchar(0))
      return (c =~ a:pat) ? '' : c
   endfunc
   iabbr <silent> if if ()<Left><C-R>=Eatchar('\s')<CR>

How exactly does this work? For example, I'm clear on up to the <C-R>= to enter the expression register to invoke the output of the EatChar command and it is passing any space-likechar with '\s', but what do these two lines do?

let c = nr2char(getchar(0))
return (c =~ a:pat) ? '' : c

Additionally:

  • nr2char returns the character of an asci number, for example: echom nr2char(80) ==> P
  • How does getchar(0) work? Every time I try that I get 0, so what is that doing?
  • Finally, it 'returns' either a character or empty string. But where does that go exactly? How does that 'eat' a character?

1 Answer 1

3

but what do these two lines do?

let c = nr2char(getchar(0))
return (c =~ a:pat) ? '' : c

The first line consumes a character from the typeahead buffer; if the latter is empty, getchar() doesn't wait for a character to be written inside, thanks to the optional 0 argument.

The second line spits back the consumed character, but only if it's not a whitespace.


More details:

fu s:eat_space()
   let c = nr2char(getchar(0))
   return c =~# '\s' ? '' : c
endfu
inorea <silent> if if ()<left><c-r>=<sid>eat_space()<cr>

Here's what happens:

  • you type i; it's written in the typeahead buffer, then executed (i.e. inserted in your buffer)

  • you type f; same thing

  • you type space which should be a non-keyword character:

    • it's written in the typeahead buffer

    • Vim checks whether the text before the cursor matches an abbreviation

    • it finds one; as a result, it removes its lhs from your buffer, and inserts its rhs into the typeahead; i.e. if is removed from your buffer, and if ()<left><c-r>=<sid>eat_space()<cr> is inserted in the typeahead in front of the space

    • the typeahead is executed; when <c-r>=<sid>eat_space()<cr> is executed, s:eat_space() consumes the last remaining space from the typeahead (via getchar())

    • the typeahead buffer is now empty, and the space is not executed (i.e. inserted in your buffer)

You may wonder why if ()... is written in the typeahead buffer. I think it has to, because if you define a recursive abbreviation (iab vs inorea), mappings are applied; that can only happen in the typeahead buffer.


You could probably replace this line:

return c =~# '\s' ? '' : c

With:

return ''

However, if you were to use s:eat_space() in another abbreviation, and for some reason you wanted to use it in a different position in the rhs, the function could wrongly consume some non-whitespace character which would be unexpected. So the current statement, while not necessary here, is a good practice.


The fact that getchar() consumes the typeahead buffer is not really documented, but it's briefly mentioned at :h :map-<expr> /typeahead:

You can use getchar(), it consumes typeahead if there is any.

How does getchar(0) work? Every time I try that I get 0, so what is that doing?

From :h getchar():

If [expr] is 0, only get a character when one is available. Return zero otherwise.

No character is available in the typeahead buffer when you run :echo getchar(0), so 0 is returned.

1

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.

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