I find myself often aligning certain code blocks by inserting one or more spaces, like in the following situation:

case something of
    Just a▉-> ...
    Nothing -> ...

Here is the position of my cursor. In order to align the arrows, I enter insert mode with i, hit <Space> and <Esc>. In my vim configuration the <Space> key doesn't do anything meaningful, so I tried creating the following mapping, hoping that it would do the same job with just one keystroke:

nnoremap <Space> i<Space><Esc>

In case of one space character this works without any problems, and even repeating with . works. However, 3<Space> won't insert 3 spaces without moving the cursor. With the help of this Vim Tips wiki article, I made the following mapping:

nnoremap <Space> @="i\<lt>Space>\<lt>Esc>"<CR>

This successfully inserts n number of spaces with n<Space>, but the problem is that I cannot repeat n<Space> with .. I would like to make this repeatable in a way, that when I first insert 2 spaces with 2<Space> and hit the repeat operation ., it would insert 2 spaces again instead of one.

My knowledge of Vimscript is very limited, but I suppose one could write such mapping with help of a function, and use a plugin like repeat to do the repetition part? Or is this possible to do without any plugins?

  • To clarify: you wish for . to use the last count with your mapping? – D. Ben Knoble Mar 2 at 21:17
  • Yes, that's what I meant. Sorry, my explanation is probably not the best. English is not my first language. :) – Severi Jääskeläinen Mar 2 at 21:24
  • No your explanation is actually quite good—I just want to make sure answers focus on the right part – D. Ben Knoble Mar 2 at 21:26

You can create repeatable mappings by passing them through g@.

function! s:insspace(...)
  if a:0
    " perform operation
    execute 'normal' v:count1.'i '."\<esc>".'`['
    " set up
    let &operatorfunc = matchstr(expand('<sfile>'), '[^. ]*$')
    return "g@\<space>"

nnoremap <silent><expr> <space> <sid>insspace()

Normally g@ is used for operators, but by using g@<space>, this makes it an actual operation. The function here s:insspace actually called twice, once with no arguments meaning "set up," which sets the operatorfunc option to itself (the complicated-looking matchstr), and again by vim with one argument (see :help g@).

This approach is general. To use a different operation you only need to change the line after the comment "perform operation."


You could use `[ to return "to the first character of the previously changed or yanked text" (from :help `[)

nnoremap <Space> i<Space><esc>`[

This works with <Space> and 3<Space>.

Flaw: If you do 3<Space> and you want to add another three spaces with ., the cursor will move. From my understanding, this is because with . Vim repeats the change (inserting 3 spaces), but not the mapping. A following `[ will bring the cursor back to the initial position.

  • This is a operates the same as my solution, but is simpler and more elegant. However, what I really want is the repeat operation . to work as I mentioned. :/ – Severi Jääskeläinen Mar 3 at 10:57

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.