With other text editors, if you press Home, it first takes you to the indent, then all the way to the left.

Is this possible with Vim?


Yes, it is possible to create a mapping for that.

For example, a mapping that will go to the indent if you're elsewhere in the line, but go to the first character if you're on the indent:

noremap  <expr> <Home> col('.') == match(getline('.'), '\S') + 1 ? "\<Home>" : "^"
inoremap <expr> <Home> col('.') == match(getline('.'), '\S') + 1 ? "\<Home>" : "\<C-O>^"

The first mapping will work in Normal, Visual and Select modes. The second will work in Insert mode. Both use the ^ command to go to the indent, only the Insert mode mapping uses a <C-O> prefix to run just that motion in Normal mode, before returning to Insert mode at the new location.

The behavior of these mappings when you're at the indent is to just use the original behavior of <Home>, exactly as Vim implements it by default.

The mappings use <expr> mappings to evaluate a Vimscript expression. In this case, they're looking at whether the current column (col('.')) matches the first non-blank character of the line (we use a match() with a regular expression matching a non-blank '\S' to locate that column.) Based on whether these two match or not, we decide how to behave and which keys to emulate.

These mappings create a kind of "toggle" behavior, since from the indent you go to the beginning of the line and from the beginning of the line you go to the indent, so pressing <Home> repeatedly will toggle between the two locations. (If you wanted a different behavior, it should be easy to modify the mappings to adapt to your exact requirements.)

Note that this command will break the undo tree when used, but that's also the default behavior of <Home> in Insert mode, so I imagine that's acceptable.

  • By “break the undo tree,” do you mean similar to what Ctrl-g u does? Or do you mean break as in fubar/borked/cause a bug/issue?
    – D. Ben Knoble
    Mar 16 '20 at 21:51
  • @D.BenKnoble Yes, exactly, same as CTRL-G u. BTW that's the default behavior of the Home key. Actually, the example for CTRL-G U shows an example of how to make mappings for those that don't break the undo tree, but they use a repeat of <Left> or <Right> until getting to the right column...
    – filbranden
    Mar 16 '20 at 21:58

I have this since 2002 it seems

It also handles <end> key

inoremap <silent> <Home> <c-o>@=<SID>HomeLikeVCpp()<cr>
nnoremap <silent> <Home> @=<SID>HomeLikeVCpp()<cr>
vnoremap <silent> <Home> @=<SID>HomeLikeVCpp()<cr>

inoremap <silent> <End> <c-\><c-n>@=<SID>EndLikeVCpp()<cr>a
nnoremap <silent> <End> @=<SID>EndLikeVCpp()<cr>
vnoremap <silent> <End> @=<SID>EndLikeVCpp()<cr>

function! s:HomeLikeVCpp()
  let ll = strpart(getline('.'), -1, col('.'))
  if ll =~ '^\s\+$' | return '0'
  else              | return '^'

function! s:EndLikeVCpp()
  let l = strpart(getline('.'), col('.')-1)
  let ll = match(l, '^\S\s*$')

  if getline('.') =~ '^\s*$'
    if col('.') + (mode()!='v') == col('$') | return 'g_'
    else                                    | return '$'
    if ll >= 0 | return '$'
    else       | return 'g_'
  • Those could be expr mappings, no?
    – D. Ben Knoble
    Mar 16 '20 at 21:48
  • Definitively. This is what I'd do if I had to write nowadays. Mar 16 '20 at 22:14

This is what I came up with. It is not as short as other options, but seems to do the job:

func Cursor()
   let n_cur = col('.') - 1
   let n_ind = indent(line('.'))
   return [n_cur, n_ind]

func Home()
   let [n_cur, n_ind] = Cursor()
   if n_cur > n_ind
      normal ^
      normal 0

nmap <Home> :call Home()<CR>
imap <Home> <C-O>:call Home()<CR>
  • Prefer nnoremap. Also, by using an <expr> mapping, you can just return the (normal) command to execute. Or you can use exec F_home() and return an ex command.
    – D. Ben Knoble
    Mar 16 '20 at 13:13

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.