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?
Vi and Vim Stack Exchange is a question and answer site for people using the vi and Vim families of text editors. It only takes a minute to sign up.Sign up to join this community
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.
I have this since 2002 it seems
It also handles
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 '^' endif endfunction 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 '$' endif else if ll >= 0 | return '$' else | return 'g_' endif endif endfunction
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] endfunc func Home() let [n_cur, n_ind] = Cursor() if n_cur > n_ind normal ^ else normal 0 endif endfunc nmap <Home> :call Home()<CR> imap <Home> <C-O>:call Home()<CR>