Vim Tips Wiki
m (minor reformatting)
(Change <tt> to <code>, perhaps also minor tweak.)
(17 intermediate revisions by 10 users not shown)
Line 1: Line 1:
  +
{{TipImported
{{Tip
 
 
|id=315
 
|id=315
  +
|previous=313
|title="Smart <home>"
 
  +
|next=316
|created=August 14, 2002 16:46
+
|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
|text=
 
  +
|category2=
to make it faster to navigate through indented code here is a common way to "go home"...
 
 
fun! s:SmartHome()
 
if col('.') != match(getline('.'), '\S')+1
 
norm ^
 
else
 
:call cursor(line('.'),2)
 
norm h
 
endif
 
endfun
 
 
inoremap <silent><home> C-O > :call <SID>SmartHome()<CR>
 
nnoremap <silent><home> :call <SID>SmartHome()<CR>
 
vnoremap <silent><home> :call <SID>SmartHome()<CR>
 
 
what this snippet does is make the &lt;home&gt; key behave as it does in such IDEs as PythonWin or MSVisualStudio, and that is first go to the first non whitespace, and then to the first char on the line.
 
 
}}
 
}}
  +
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.
   
== Comments ==
+
==Simple mappings==
  +
The following lines (in your [[vimrc]]) implement smart home for normal, visual, operator pending, and insert modes.
What's the point of this tip? I'm happy using ^ and 0 (zero).
 
  +
<pre>
 
  +
noremap <expr> <silent> <Home> col('.') == match(getline('.'),'\S')+1 ? '0' : '^'
 
  +
imap <silent> <Home> <C-O><Home>
'''Anonymous'''
 
  +
</pre>
, August 15, 2002 1:16
 
----
 
nmap &lt;HOME&gt; ^
 
But, I agree with the previous comment, 0 and ^ are within finger reach.
 
 
'''Anonymous'''
 
, August 15, 2002 4:33
 
----
 
This tip does have a point -- it's helpful for those who are transitioning from their favorite IDEs to Vim. I use 0 and ^ because I have them memorized, but others might not...
 
 
For those who spend a lot of time in their text editors, it's worth a few hours of learning up-front -- certainly, the time spent learning shortcuts like "*" and "t" pays for itself many times over. For others, who might spend only a little while with their editor, it might not. For them, it's useful to know that a few lines of code can make Vim behave like what they already know. And if you happen be such a person, you may wish to check out Cream, which makes Vim much easier to use for beginners:
 
http://cream.sourceforge.net/
 
 
And, while I'm being unorthodox, I may as well add a question to my note:
 
In some IDEs, the behavior of Home varies depending on how many times you press it. The first time, it goes to the beginning of the line; the second, to the top of the screen; and the third, to the top of the file. This is trivial to accomplish in Vim -- something like
 
 
map &lt;HOME&gt; ^
 
map &lt;HOME&gt;&lt;HOME&gt; H
 
map &lt;HOME&gt;&lt;HOME&gt;&lt;HOME&gt; gg
 
 
But Vim will hesitate after the first Home press to see if you are going to press Home again (and likewise for the second Home). This interval can be adjusted with the (t)timeoutlen option, but it is difficult to select a value that makes &lt;HOME&gt; near-instantaneous and still make the whole sequence easy to enter without hand spasms.
 
 
Is there any way to see what the last few keypresses were? If so, it'd be fairly simple to check them and behave correctly. Or, I suppose, you could check and see if the cursor was _already_ at the beginning of the line (or at the top of the screen) and behave correctly. Hmmm.
 
 
jmcpherson--AT--softhome.net
 
, August 15, 2002 10:38
 
----
 
"Banging" the ":normal" calls could be useful.
 
 
Otherwise, here is another way to do the same thing :
 
http://hermitte.free.fr/vim/ressources/vimfiles/plugin/homeLikeVC++.vim
 
 
[[User:Luc Hermitte|Luc Hermitte]], August 15, 2002 13:29
 
----
 
indeed '0' and '^' are the natural and fast way to use, unless you are in insert mode, and then it turns into '&lt;home&gt;' and '&lt;C-O&gt;^'. the later takes two keystrokes "longer" to type, and is used more often (at least by me)!
 
 
imap &lt;home&gt; &lt;C-O&gt;^
 
would have been easier to write, but my goal was to make it both as simple (from the users' point of view) and as fast as possible.. the perfect solution in my opinion was the way it was done by the packages I mentioned in the tip.
 
 
P.S. I'm on vim since version 4.3a3 (WVim to be exact) :)
 
 
Best Regards...
 
Alex.
 
 
Alex A. Naanou &lt;alex_nanou--AT--yahoo.com&gt;
 
, August 15, 2002 15:56
 
----
 
I don't own a keyboard where the home key is any near my fingers, so I prefer to use '^' and the like. Considering the time it takes to go into normal mode, do my thing and then return to insert mode rather than using home, this function is useless; to me, anyway.
 
 
Simon Shine &lt;simon at blueshell.dk&gt;
 
, August 19, 2002 11:13
 
----
 
Unless I'm confused, why not just use the function like so:
 
 
fun! s:SmartHome()
 
if col('.') == 1
 
norm ^
 
else
 
norm 0
 
endif
 
endfun
 
 
Please send me email in this regard...
 
 
lailoken--AT--freeshell.org
 
, August 26, 2002 9:17
 
----
 
Thanks for the remark!!!
 
The function mentioned above is OK, but the thing I wanted is to do '^' if we are not on the first non-whitespace else do '0'. the proposed above function does the opposite (first '0' then '^')
 
 
// And I must admit that my original else clause is over-complicated!! (using norm 0 is sufficient) :)
 
   
  +
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).
and here we have the new and improved version:
 
   
  +
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.
fun! s:SmartHome()
 
" this line checks if we are not on the first whitespace.
 
if col('.') != match(getline('.'), '\S')+1
 
norm ^
 
else
 
norm 0
 
endif
 
endfun
 
 
inoremap &lt;silent&gt;&lt;home&gt; &lt;C-O&gt;:call &lt;SID&gt;SmartHome()&lt;CR&gt;
 
nnoremap &lt;silent&gt;&lt;home&gt; :call &lt;SID&gt;SmartHome()&lt;CR&gt;
 
vnoremap &lt;silent&gt;&lt;home&gt; :call &lt;SID&gt;SmartHome()&lt;CR&gt;
 
   
  +
The simple mappings have two deficiencies:
Thanks again!
 
  +
*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==
Alex A. Naanou &lt;alex_nanou--AT--yahoo.com&gt;
 
  +
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.
, August 28, 2002 16:23
 
----
 
Great tip! I prefer it to only go to first column when current column is between first column and second character:
 
   
  +
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.
function SmartHome()
 
  +
<pre>
if col(".") == 1 || col(".") &gt; match(getline("."), '\S') + 1
 
 
function! SmartHome()
normal g^
 
 
let first_nonblank = match(getline('.'), '\S') + 1
else
 
  +
if first_nonblank == 0
normal g0
 
  +
return col('.') + 1 >= col('$') ? '0' : '^'
endif
 
 
endif
endfunction
 
 
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>
   
  +
==See also==
Gerald Lai
 
  +
*[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
, May 4, 2005 18:38
 
----
 
Thankyou very much for the tip. It is very useful, both for the code, and the knowledge of the "^" and "0" keys.
 
   
  +
==Comments==
jhales.perth--AT--gmail.com
 
  +
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:
, June 13, 2006 7:12
 
  +
<pre>
----
 
  +
noremap <expr> <Home> (col('.') == matchend(getline('.'), '^\s*')+1 ? '0' : '^')
<!-- parsed by vimtips.py in 0.324812 seconds-->
 
 
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>
  +
</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>