5

I would like to map <End> and <Home> to go to the end or beginning of the current line (not the screen), even if the cursor is on a wrapped line:

|1 First line.      |
|2 This is some very| <- Wrapped line 
|  long text        |
|3                  |

So here what should be the mappings:

<Home>  Cursor go to the first letter of visible line (wrapped or not).
<End>   Cursor go beyond the last letter of visible line (wrapped or not).

An important thing is that I use virtualedit:

set virtualedit=all
noremap <Up> gk
noremap <Down> gj
noremap k gk
noremap j gj

For a normal line, this can be solved using these mappings:

noremap <End> $l
noremap <Home> 0
"i_<End> already mapped
"i_<Home> already mapped

However on a wrapped line none of the above mappings work.

Here below the cases that must work:

|1 First line.       | Initial                   
       ^
|1 First line.       | <Home>                    (1)
   ^      
|1 First line.       | <End>                     (2)
              ^
|1 First line.       | <Home>                    
   ^                   l<Down>
|2 This is some very |                           
    ^
|2 This is some very | <End>                    (3)
                    ^
|2 This is some very | <Home>                    (4)
   ^                   l<Down>
|  long text         |                           
    ^ 
|  long text         | <Home>                    (5)
   ^
|  long text         | <End>                     (6)
            ^

The solution proposed by statox in his answer works for <Home>, but not really for <End>. So, I added a l to the <End> mapping. Then all the cases pass, but (3) that goes on the next line.

nnoremap <silent> <End>  :set virtualedit=<CR>g$:set virtualedit=all<CR>l 
nnoremap <silent> <Home> :set virtualedit=<CR>g0:set virtualedit=all<CR> 

Most editors solve case (3) by adding a virtual blank column at the end of the screen, so the cursor can be virtually placed here in this particular case.

How can I achieve the same behavior in Vim?

nowox
  • 459
  • 4
  • 14

1 Answers1

10

You're looking for g0 and g$ which are the equivalent of 0 and $ for wrapped lines. See :h g0, :h g$ and even :h g^.

To quote the doc about g$:

When lines wrap ('wrap' on): To the last character of the screen line and [count - 1] screen lines downward |inclusive|. Differs from "$" when a line is wider than the screen.

When lines don't wrap ('wrap' off): To the rightmost character of the current line that is visible on the screen. Differs from "$" when the last character of the line is not on the screen or when a count is used. Additionally, vertical movements keep the column, instead of going to the end of the line.


EDIT

About the virtualedit option maybe you could try something like:

nnoremap <silent> <end> :set virtualedit=<CR>g$:set virtualedit=all<CR>

The mapping can be explained like this:

nnoremap                   Create a normal mode mapping
<silent>                   Make the mapping silent to avoid messages in the command line
<end>                      The key to map
:set virtualedit=<CR>      Unset virtualedit
g$                         Use g$ normally
:set virtualedit=all<CR>   Set the virtualedit option again
statox
  • 49,782
  • 19
  • 148
  • 225
  • I can't reproduce your problem, on my system g$ moves the cursor on the . of your first line example. Have you tried using it with vim -u NONE? Where does g$ move your cursor? – statox Sep 30 '16 at 13:08
  • Then it is an option in your vimrc which is causing this. You should read the doc I linked and probably play with the wrap related options. You can also have a look at this question to help you debug your vimrc. – statox Sep 30 '16 at 13:15
  • @nowox actually the doc for g$ says When 'virtualedit' is enabled moves to the end of the screen line. so this is probably the option to tweak in your vimrc :) – statox Sep 30 '16 at 13:22
  • 1
    @nowox see my edit. – statox Sep 30 '16 at 13:33
  • I've edited my question (again), and I removed my comments. – nowox Sep 30 '16 at 20:26