Vim Tips Wiki
(Remove html character entities)
(Change <tt> to <code>, perhaps also minor tweak.)
(5 intermediate revisions by 5 users not shown)
Line 1: Line 1:
 
{{TipImported
 
{{TipImported
 
|id=315
 
|id=315
|previous=314
+
|previous=313
 
|next=316
 
|next=316
|created=August 14, 2002
+
|created=2002
 
|complexity=advanced
 
|complexity=advanced
|author=Alex A. Naanou
+
|author=
|version=5.7
+
|version=7.0
 
|rating=35/22
 
|rating=35/22
 
|category1=Moving
 
|category1=Moving
 
|category2=
 
|category2=
 
}}
 
}}
Put the following in your vimrc. When you press the Home key, the cursor will move to the first nonblank character on the line, or, if already at that position, the cursor will move to the first character.
+
With "smart home", pressing the Home key moves the cursor to the first nonblank character on the line, or, if already at that position, to the start of the line.
   
  +
==Simple mappings==
  +
The following lines (in your [[vimrc]]) implement smart home for normal, visual, operator pending, and insert modes.
 
<pre>
 
<pre>
  +
noremap <expr> <silent> <Home> col('.') == match(getline('.'),'\S')+1 ? '0' : '^'
function! SmartHome()
 
 
imap <silent> <Home> <C-O><Home>
let s:col = col(".")
 
normal! ^
 
if s:col == col(".")
 
normal! 0
 
endif
 
endfunction
 
nnoremap <silent> <Home> :call SmartHome()<CR>
 
inoremap <silent> <Home> <C-O>:call SmartHome()<CR>
 
 
</pre>
 
</pre>
   
  +
The first line is an expression mapping for normal, visual, and operator pending modes. The right-hand side of the mapping is an expression that is evaluated each time Home is pressed. The expression gives a string, and the result is as if the characters in the string had been pressed. The expression compares <code>col('.')</code> (the cursor column position, where 1 is the first column) and <code>match(getline('.'),'\S')+1</code> (the index of the first non-whitespace character in the current line; 1 is added because the index starts at 0). If both sides of "<code>==</code>" are equal, the result is <code>'0'</code> (move to start of line); otherwise it is <code>'^'</code> (move to first nonblank character).
== See also ==
 
* [http://code.google.com/p/lh-vim/source/browse/cpp/trunk/plugin/homeLikeVC++.vim homeLikeVC++.vim] provides the same feature in a slightly different way. Mappings for visual mode, and a smart-<tt><End></tt> are also provided.
 
   
  +
In insert mode, the second line applies and if Home is pressed the result is Ctrl-O followed by Home. In insert mode, pressing Ctrl-O executes what follows as a normal-mode command, so the Home which follows invokes the normal mode mapping.
==Comments==
 
   
  +
The simple mappings have two deficiencies:
If you don't mind smashing a mark (<code>'h</code> in this case), you can use the map in visual mode, too:
 
  +
*If there are no non-whitespace characters, <code>match()</code> returns −1 which becomes 0 after adding 1, and that value never equals the result from <code>col('.')</code>. Therefore the mapping always performs <code>^</code>.
  +
*The mapping does not account for the case where the <code>'wrap'</code> option is on and long lines are wrapped. In that case, pressing Home will jump to the first nonblank character or the start of the line, whereas moving to the start of the screen line may be wanted.
  +
  +
==More features==
  +
If lines are not wrapped, the following operates in the same manner as the above simple mappings, except that it works correctly on a line which consists only of whitespace characters.
  +
  +
If options <code>'wrap'</code> and <code>'linebreak'</code> are on, long lines will wrap and wrapped lines will start with a non-whitespace character. In that case, if the cursor is on a character in the middle of a wrapped line, pressing Home will move to the start of that screen line (not the start of the line of text in the buffer). Pressing Home again will move to the first nonblank character of the line in the buffer (on a different screen line). Pressing Home a third time will move to the start of the line.
 
<pre>
 
<pre>
 
function! SmartHome()
vnoremap <silent> <Home> <Esc>:call <SID>SmartHome()<CR>mhgvg`h
 
  +
let first_nonblank = match(getline('.'), '\S') + 1
  +
if first_nonblank == 0
  +
return col('.') + 1 >= col('$') ? '0' : '^'
 
endif
  +
if col('.') == first_nonblank
  +
return '0' " if at first nonblank, go to start line
  +
endif
  +
return &wrap && wincol() > 1 ? 'g^' : '^'
 
endfunction
 
noremap <expr> <silent> <Home> SmartHome()
  +
imap <silent> <Home> <C-O><Home>
 
</pre>
 
</pre>
   
 
==See also==
----
 
 
*[http://code.google.com/p/lh-vim/source/browse/cpp/trunk/plugin/homeLikeVC%2B%2B.vim homeLikeVC++.vim] provides smart Home and smart End using <code>@=</code> rather than <code><expr></code>, which should work on versions prior to Vim 7
There is no need to smash any mark if we:
 
* return what must be executed instead of playing with <tt>:normal!</tt>
 
* and then use @=
 
   
 
==Comments==
See the latest version of homeLikeVC++ (the link has been updated)
 
 
The following provides smart Home and smart End and should work properly in normal mode, visual mode, insert mode, select mode, and operator-pending mode:
----
 
We don't need to use functions, <tt>:normal!</tt>, or <tt>@=</tt> if we use <tt><expr></tt> maps. These maps should work properly in normal mode, visual mode, insert mode, select mode, and operator-pending mode:
 
 
<pre>
 
<pre>
noremap <expr> <Home> (col('.') == matchend(getline('.'), '^\s*')+1 ? '0' : '^')
+
noremap <expr> <Home> (col('.') == matchend(getline('.'), '^\s*')+1 ? '0' : '^')
noremap <expr> <End> (col('.') == match(getline('.'), '\s*$') ? '$' : 'g_')
+
noremap <expr> <End> (col('.') == match(getline('.'), '\s*$') ? '$' : 'g_')
vnoremap <expr> <End> (col('.') == match(getline('.'), '\s*$') ? '$h' : 'g_')
+
vnoremap <expr> <End> (col('.') == match(getline('.'), '\s*$') ? '$h' : 'g_')
 
imap <Home> <C-o><Home>
 
imap <Home> <C-o><Home>
imap <End> <C-o><End>
+
imap <End> <C-o><End>
 
</pre>
 
</pre>

Revision as of 05:24, 13 July 2012

Tip 315 Printable Monobook Previous Next

created 2002 · complexity advanced · version 7.0


With "smart home", pressing the Home key moves the cursor to the first nonblank character on the line, or, if already at that position, to the start of the line.

Simple mappings

The following lines (in your vimrc) implement smart home for normal, visual, operator pending, and insert modes.

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

The first line is an expression mapping for normal, visual, and operator pending modes. The right-hand side of the mapping is an expression that is evaluated each time Home is pressed. The expression gives a string, and the result is as if the characters in the string had been pressed. The expression compares col('.') (the cursor column position, where 1 is the first column) and match(getline('.'),'\S')+1 (the index of the first non-whitespace character in the current line; 1 is added because the index starts at 0). If both sides of "==" are equal, the result is '0' (move to start of line); otherwise it is '^' (move to first nonblank character).

In insert mode, the second line applies and if Home is pressed the result is Ctrl-O followed by Home. In insert mode, pressing Ctrl-O executes what follows as a normal-mode command, so the Home which follows invokes the normal mode mapping.

The simple mappings have two deficiencies:

  • If there are no non-whitespace characters, match() returns −1 which becomes 0 after adding 1, and that value never equals the result from col('.'). Therefore the mapping always performs ^.
  • The mapping does not account for the case where the 'wrap' option is on and long lines are wrapped. In that case, pressing Home will jump to the first nonblank character or the start of the line, whereas moving to the start of the screen line may be wanted.

More features

If lines are not wrapped, the following operates in the same manner as the above simple mappings, except that it works correctly on a line which consists only of whitespace characters.

If options 'wrap' and 'linebreak' are on, long lines will wrap and wrapped lines will start with a non-whitespace character. In that case, if the cursor is on a character in the middle of a wrapped line, pressing Home will move to the start of that screen line (not the start of the line of text in the buffer). Pressing Home again will move to the first nonblank character of the line in the buffer (on a different screen line). Pressing Home a third time will move to the start of the line.

function! SmartHome()
  let first_nonblank = match(getline('.'), '\S') + 1
  if first_nonblank == 0
    return col('.') + 1 >= col('$') ? '0' : '^'
  endif
  if col('.') == first_nonblank
    return '0'  " if at first nonblank, go to start line
  endif
  return &wrap && wincol() > 1 ? 'g^' : '^'
endfunction
noremap <expr> <silent> <Home> SmartHome()
imap <silent> <Home> <C-O><Home>

See also

  • homeLikeVC++.vim provides smart Home and smart End using @= rather than <expr>, which should work on versions prior to Vim 7

Comments

The following provides smart Home and smart End and should work properly in normal mode, visual mode, insert mode, select mode, and operator-pending mode:

noremap <expr> <Home> (col('.') == matchend(getline('.'), '^\s*')+1 ? '0' : '^')
noremap <expr> <End> (col('.') == match(getline('.'), '\s*$') ? '$' : 'g_')
vnoremap <expr> <End> (col('.') == match(getline('.'), '\s*$') ? '$h' : 'g_')
imap <Home> <C-o><Home>
imap <End> <C-o><End>