Vim Tips Wiki

Insert a single character

Redirected from VimTip466

1,624pages on
this wiki
Add New Page
Talk1 Share
Tip 270 Printable Monobook Previous Next

created 2002 · complexity basic · author Mikko Pulkkinen · version 6.0

Using insert mode to insert a single character feels clumsy because three key presses are needed to insert one character. Here is a slightly easier way:

:nnoremap <Space> i_<Esc>r

Now, when in normal mode, press Space followed by the character that you want to insert.

Bug: Repeating the insertion with . does not work.


I use:

:nnoremap ,i i_<Esc>r
:nnoremap ,a a_<Esc>r

I consider this a better solution:

:nnoremap s :exec "normal i".nr2char(getchar())."\e"<CR>
:nnoremap S :exec "normal a".nr2char(getchar())."\e"<CR>

Such inserts can be repeated with '.' thus making this command worthy (it's not a big problem to make an additional keypress but imagine you need to manually put some spaces or other separators).

's' and 'S' were chosen because:

  • They remind of 'single'.
  • If they are used occasionally, one can use synonyms 'cl' and 'cc' instead.
What is the "\e" for?
It might be meant to represent <Escape> although in Vim 7.2 this method does not need to force an escape from insert mode.
The built-in s command is very useful, but that's a choice for the user. JohnBeckett 22:56, 15 June 2009 (UTC)

I quite like the solution you proposed, but it does not allow for repetitions, I found myself wanting to do '5s '. I couldn't use the repetition inline because it would repeat the call to getchar so I added a simple proxy function, the result is:

function! RepeatChar(char, count)
  return repeat(a:char, a:count)
nnoremap s :<C-U>exec "normal i".RepeatChar(nr2char(getchar()), v:count1)<CR>
nnoremap S :<C-U>exec "normal a".RepeatChar(nr2char(getchar()), v:count1)<CR>

Note that the C-U is necessary to support the ranges and as far as I could tell the '."\e"' was unnecessary, so I removed it

Thanks, I packaged this as a plugin on github at
Interesting, thanks. Why wouldn't repeat(getchar(), v:count1) work (why the RepeatChar function)? JohnBeckett 11:20, September 28, 2010 (UTC)
Because if you use repeat(nr2char(getchar()), v:count1) it executes the "nr2char(getchar())", "v:count1" times, meaning it will try to get "v:count1" keypresses. When you pass it as a parameter to a function it is evaluated only once and the result is repeated. 12:44, September 20, 2010 (UTC)
This appears to be wrong. I tested this without a separate RepeatChar function, and it does not prompt multiple keypresses; it executes exactly the same. I am using VIM 7.4.1689. 20:30, August 23, 2016 (EDT)

This is what I am using for a similar purpose. The only difference is, if a character is not provided promptly, it inserts a space:

function! InsertSingle()
  sleep 120m|let l:a = getchar(0)
  if l:a != 0
    silent! exec "normal a" . nr2char(l:a)
    silent! exec "normal a "
nnoremap <silent> <Space> :call InsertSingle()<CR>

By exploiting the InsertCharPre and InsertLeave events we could do the trick. Insertion of a specified number of characters or a single character can now be done without manually switching back to Normal mode.

Since I have switched to Neovim in which Meta key bindings works even in the terminal, I used <M-i> and <M-a> as the shortcuts. If you are using original Vim on a terminal, you could use the trick provided by Tim Pope in his rsi.vim plugin to make the meta key work.

let s:insert_char_pre = ''
let s:insert_leave = ''

autocmd InsertCharPre * execute s:insert_char_pre
autocmd InsertLeave   * execute s:insert_leave

" basic layer
function! s:QuickInput (operator, insert_char_pre) 
    let s:insert_char_pre = a:insert_char_pre
    let s:insert_leave = 'call <SID>RemoveFootprint()'
    call feedkeys(a:operator, 'n')

function! s:RemoveFootprint() 
    let s:insert_char_pre = ''
    let s:insert_leave = ''
    let s:char_count = 0

" secondary layer
function! QuickInput_Count (operator, count) 
    let insert_char_pre = 'call <SID>CountChars('.a:count.')'
    call <SID>QuickInput(a:operator, insert_char_pre)

let s:char_count = 0
function! s:CountChars (count) 
    let s:char_count += 1
    if s:char_count == a:count
        call feedkeys("\<Esc>")

" secondary layer
function! QuickInput_Repeat (operator, count) 
    let insert_char_pre = 'let v:char = repeat(v:char, '.a:count.') | call feedkeys("\<Esc>")'
    call <SID>QuickInput(a:operator, insert_char_pre)

nnoremap i :<C-u>execute 'call ' v:count? 'QuickInput_Count("i", v:count)' : "feedkeys('i', 'n')"<CR>
nnoremap a :<C-u>execute 'call ' v:count? 'QuickInput_Count("a", v:count)' : "feedkeys('a', 'n')"<CR>

nnoremap <Plug>InsertAChar :<C-u>call QuickInput_Repeat('i', v:count1)<CR>
nnoremap <Plug>AppendAChar :<C-u>call QuickInput_Repeat('a', v:count1)<CR>

nmap <M-i> <Plug>InsertAChar
nmap <M-a> <Plug>AppendAChar

Ad blocker interference detected!

Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.

Also on Fandom

Random Wiki