38

When cursor is at middle of screen and i scroll down, the cursor moves upwards on the screen. I don't want it to do that.

How can i scroll without changing cursors on-screen position?

Solution, added after answer:

noremap <C-k> 14j14<C-e>
noremap <C-l> 14k14<C-y>
General Grievance
  • 4,555
  • 31
  • 31
  • 45
john-jones
  • 7,490
  • 18
  • 53
  • 86
  • You can scroll with the cursor, or scroll the screen without moving the cursor from it's current line (though it will move in position on the screen since the lines are moving). I don't think you can accomplish exactly what you're looking for... how about just hitting page down?.. – Fosco Aug 17 '10 at 14:34
  • page down is just larger-movement scrolling. – john-jones Aug 17 '10 at 14:39
  • 3
    This is a repeat of: http://stackoverflow.com/questions/3458689/how-to-move-screen-without-moving-cursor-in-vim/ – GWW Aug 17 '10 at 15:03
  • 4
    @GWW this is not a repeat of that question. He wanted the screen to move such that his cursor will become the first line. – joedevon Oct 26 '11 at 06:47

6 Answers6

47

There are two ways I can think of: ctrl-E and ctrl-Y scroll the buffer without moving the cursor's position relative to the window. I think that is what you want. Also, if you set scrolloff to a large number, you will get the same effect as ctrl-E and ctrl-Y with the movement keys. scrolloff setting will make it hard to get the cursor to move vertically relative to the window though. (Use something like :set so=999, so is an abbreviation for scrolloff.)

:help 'scrolloff'
:help scrolling
Hulk1991
  • 3,079
  • 13
  • 31
  • 46
Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
  • 3
    `ctrl-E` **does** move the cursor's position relative to the window, the cursor follows the text; if the cursor is in the middle of the window/screen and you press `ctrl-E` repeatedly, the cursor moves to the first line; this exactly is what the asker does not want; he wants to do `14j14` just with 14 replaced by a number depending on the size of his screen – Matthias 009 Nov 22 '12 at 18:13
  • 2
    you second suggestion, setting scrolloff, does what the op asks for, but it **does not** give the same effect as `ctrl-E`; only problem with scrolloff is that it is permanent in the sense that now it is not possible to move without scrolling; it would be good to have one key go move with and one to move without scrolling – Matthias 009 Nov 22 '12 at 18:41
  • Upvote for the `so` option, but @Matthias009 is right. – Big McLargeHuge Jan 02 '15 at 20:07
12

ctrl-D and ctrl-U is what you want.

ctrl-D has the same effect as 14j14<C-e> (just that the number 14 is not hard coded and the amount of movement depends on the actual size of your screen): You move the cursor several lines down in the text but the cursor stays in the middle of the screen.

Similarly ctrl-U works like 14k14<C-y>.

Addendum: If your screen has 30 lines then the the two are exactly the same.

Hulk1991
  • 3,079
  • 13
  • 31
  • 46
Matthias 009
  • 1,624
  • 5
  • 16
  • 18
11

If you want to both move the cursor and the viewport with the cursor anywhere in the screen, perhaps you should set up some custom key bindings to do both at once.

Such as:

:nnoremap <C-M-u> j<C-e>

This will move the cursor down (j) and move the viewport (Ctrl-e) whenever you press Ctrl-Alt-u (only in normal mode).

wds
  • 31,873
  • 11
  • 59
  • 84
1

Try this mapping in .vimrc

map <ScrollWheelUp>   5<C-Y>
map <ScrollWheelDown> 5<C-E>
IvanM
  • 2,913
  • 2
  • 30
  • 30
1

There are two methods I know of. Add these lines to your .vimrc file (selecting only one of the two methods):

Method 1:

function! s:GetNumScroll(num)
  let num_rows = winheight(0)
  let num_scroll = a:num
  if (a:num == -1)
    let num_scroll = (num_rows + 1) / 2
  elseif (a:num == -2)
    let num_scroll = num_rows
  endif
  if (num_scroll < 1)
    let num_scroll = 1
  endif
  return num_scroll
endfunction

function! s:RtrnToOrig(before_scr_line)
  normal H
  let delta = a:before_scr_line - winline()
  while (delta != 0)
    if (delta < 0)
      let delta = winline() - a:before_scr_line
      let iter = 1
      while (iter <= delta)
        execute "normal" "gk"
        let iter +=1
      endwhile
    elseif (delta > 0)
      let iter = 1
      while (iter <= delta)
        execute "normal" "gj"
        let iter +=1
      endwhile
    endif
    let delta = a:before_scr_line - winline()
  endwhile
endfunction

function! s:scrollUP(num)
  let num_scroll = <SID>GetNumScroll(a:num)
  let num_rows = winheight(0)
  " -------------
  let before_scr_line = winline()
  normal L
  let after_scr_line = winline()
  let extra = num_rows - after_scr_line
  let extra += num_scroll
  " move by 1 to prevent over scrolling
  let iter = 1
  while (iter <= extra)
    execute "normal" "gj"
    let iter +=1
  endwhile
  " -------------
  call <SID>RtrnToOrig(before_scr_line)
endfunction

function! s:scrollDN(num)
  let num_scroll = <SID>GetNumScroll(a:num)
  " -------------
  let before_scr_line = winline()
  normal H
  let after_scr_line = line(".")
  execute "normal" "gk"
  let after_scr2_line = line(".")
  if ( (after_scr_line == after_scr2_line) && (after_scr_line > 1) )
    execute "normal" "gk"
  endif
  let extra = (num_scroll - 1)
  let extra += (winline() - 1)
  " move by 1 to prevent over scrolling
  let iter = 1
  while (iter <= extra)
    execute "normal" "gk"
    let iter +=1
  endwhile
  " -------------
  call <SID>RtrnToOrig(before_scr_line)
endfunction

 nmap <silent> <C-J>     :call <SID>scrollUP(1)<CR>
 nmap <silent> <C-K>     :call <SID>scrollDN(1)<CR>
 nmap <silent> <C-F>     :call <SID>scrollUP(-1)<CR>
 nmap <silent> <C-B>     :call <SID>scrollDN(-1)<CR>
 nmap <silent> <PageDown>:call <SID>scrollUP(-2)<CR>
 nmap <silent> <PageUp>  :call <SID>scrollDN(-2)<CR>

This uses the normal H, L to go to screen top, bot and the gk, gj commands to move up, down by screen line instead of actual line. Its more complicated than would seem needed just to work correctly when lines are longer than the screen width and wordwrap is on.

Or this method (which has previously been posted in vim tips wiki and on Stack Exchange):

Method 2:

" N<C-D> and N<C-U> idiotically change the scroll setting
function! s:Saving_scrollV(cmd)
  let save_scroll = &scroll
  execute "normal" a:cmd
  let &scroll = save_scroll
endfunction

" move and scroll
 nmap <silent> <C-J>           :call <SID>Saving_scrollV("1<C-V><C-D>")<CR>
 vmap <silent> <C-J> <Esc>     :call <SID>Saving_scrollV("gv1<C-V><C-D>")<CR>
 nmap <silent> <C-K>           :call <SID>Saving_scrollV("1<C-V><C-U>")<CR>
 vmap <silent> <C-K> <Esc>     :call <SID>Saving_scrollV("gv1<C-V><C-U>")<CR>

 nmap <silent> <C-F>           :call <SID>Saving_scrollV("<C-V><C-D>")<CR>
 vmap <silent> <C-F> <Esc>     :call <SID>Saving_scrollV("gv<C-V><C-D>")<CR>
 nmap <silent> <PageDown>      :call <SID>Saving_scrollV("<C-V><C-D>")<CR>
 vmap <silent> <PageDown> <Esc>:call <SID>Saving_scrollV("gv<C-V><C-D>")<CR>

 nmap <silent> <C-B>           :call <SID>Saving_scrollV("<C-V><C-U>")<CR>
 vmap <silent> <C-B> <Esc>     :call <SID>Saving_scrollV("gv<C-V><C-U>")<CR>
 nmap <silent> <PageUp>        :call <SID>Saving_scrollV("<C-V><C-U>")<CR>
 vmap <silent> <PageUp> <Esc>  :call <SID>Saving_scrollV("gv<C-V><C-U>")<CR>

The only issue I have with the second method is when lines are longer than the screen width and wordwrap is on then the cursor can move up or down some to account for the extra lines from the wrap. Also at the very top and bottom of the file the cursor can move. The first method really attempts to never move the cursor in all cases.

M Kelly
  • 51
  • 1
  • 2
  • Can you please provide more detailed explanations on your answers? Otherwise it's meaningless to anyone who comes along later. – Joe C Dec 13 '16 at 21:31
  • Well, you add these lines to your .vimrc file. It is pretty simple to scroll without moving the cursor when wordwrap is off, or when it is on but lines are all shorter than the window width. But when wordwrap is on and lines are longer than the window width scrolling can move the cursor up or down some as it goes down or up by a line. The top method maintains the cursor position no matter what, but I only use it in normal mode. – M Kelly Dec 14 '16 at 01:05
  • normal H moves to the top line and normal L moves to the bottom line and from there you can figure out how many screen lines to move with "gj" or "gk" to scroll a screen line and then go back to the original screen line. – M Kelly Dec 14 '16 at 01:08
  • Alright. Do you mind editing your answer with this information rather than keeping it in a comment? – Joe C Dec 14 '16 at 05:59
  • There is also this on vim wiki tips: http://vim.wikia.com/wiki/Page_up/down_and_keep_cursor_position – M Kelly Jul 19 '17 at 23:38
0

This changes the cursor on-screen position, but does not change the cursor line on-screen position:

noremap <C-k> @="1\<lt>C-D>"<CR>:set scroll=0<CR>
noremap <C-l> @="1\<lt>C-U>"<CR>:set scroll=0<CR>

This however resets the scroll option, so subsequent <C-D> and <C-U> will scroll by half screen. Without set scroll=0, the scroll option would have been set to 1, and subsequent <C-D> and <C-U> would be scrolling by one line (Vim is weird).

Probably a Vimscript function based on 1<C-D> and 1<C-U> would be the best.

Alexey
  • 3,843
  • 6
  • 30
  • 44