86

The main scrolling commands in Vim are:

  1. Ctrl-B and Ctrl-F, as well as PageUp and PageDown scroll by full page
  2. Ctrl-U and Ctrl-D scroll half a page by default
  3. Ctrl-Y and Ctrl-E scroll one line

I lose visual context every time for the former two, so I have developed the bad habit of hitting the latter (Ctrl-Y and Ctrl-E) repetitively.

Since there is currently no first party support for smooth scrolling, what are the least objectionable workarounds/plugins?

I use both Vim and GVim depending on the task, and am happy to customize them separately if there is no one really good hack that works for both. The mouse scroll wheel works nicely in GVim, but I'm looking for keyboard based solutions.

Keith Pinson
  • 7,835
  • 7
  • 61
  • 104
Andrew Wagner
  • 22,677
  • 21
  • 86
  • 100

12 Answers12

40

Update: I have now pushed this code, refactored somewhat according to the guidelines at :help write-plugin, to a Github repo.

Using the Keyboard

Here is what I have in my .vimrc:

function SmoothScroll(up)
    if a:up
        let scrollaction="^Y"
    else
        let scrollaction="^E"
    endif
    exec "normal " . scrollaction
    redraw
    let counter=1
    while counter<&scroll
        let counter+=1
        sleep 10m
        redraw
        exec "normal " . scrollaction
    endwhile
endfunction

nnoremap <C-U> :call SmoothScroll(1)<Enter>
nnoremap <C-D> :call SmoothScroll(0)<Enter>
inoremap <C-U> <Esc>:call SmoothScroll(1)<Enter>i
inoremap <C-D> <Esc>:call SmoothScroll(0)<Enter>i

Features:

  • Scroll on the base of the Vim scroll option.
  • Customizable scrolling speed (adjust time argument of the sleep command; I use ten milliseconds). Note: just like slowing down the frame rate on a video, if you slow down the smooth scroll too much it will be jerky scroll, not smooth scroll. But whatever works best for you.
  • Works in normal or insert mode.

Note: all you copy-and-pasters, remember that the ^ character indicates a control character; copy-paste will produce invalid results and these must be entered manually!

  • ^YCTRL-V then CTRL-Y
  • ^ECTRL-V then CTRL-E

However, the <C-U> and <Enter> style syntaxes are literally typed as those characters; the map command intelligently converts them to control characters.

Using the Mouse

The question mentions that scrolling with the mouse works well in GVim, but a keyboard solution is desired. This implies to me that the asker may be interested in a mouse solution if it works in regular terminal Vim.

For me, turning mouse support on allows smooth scrolling through the mouse wheel. Also, for me, smooth scrolling is most important when I am looking around (i.e. in normal mode), not when I am editing (in insert mode), and if I am not actively editing, the need for my hands to stay on the keyboard at all times is removed, so this works well.

On the basis of this question, though, it would seem that some people have to do some more manual setup beyond simply turning the mouse on (I just use set mouse=n):


My .vimrc has the following lines

 set mouse=a
 map <ScrollWheelUp> <C-Y>
 map <ScrollWheelDown> <C-E>

Community
  • 1
  • 1
Keith Pinson
  • 7,835
  • 7
  • 61
  • 104
  • This works great... and is not dependent on the speed of the machine like the other answer. Cheers! – Steve Vermeulen Jan 08 '13 at 23:33
  • 1
    Warning: I've found that you shouldn't push Ctrl-D etc. until the previous scroll has finished or the behavior is weird. I haven't run into this problem before, so I don't know whether I just didn't notice it, or it is specific to the Vim configuration on the current machine I am using. If anybody knows how to deal with multithreading in Vim (!!), please feel free to chime in. – Keith Pinson Jan 11 '13 at 23:56
  • A possible improvement here that occurred to me would be to apply the same smooth scrolling to the zz, z-, z commands – Steve Vermeulen Jan 12 '13 at 19:15
  • That's a good idea. You could remap them in just the same way, I suppose. – Keith Pinson Jan 12 '13 at 21:33
  • 3
    A small comment on 'remember that the ^ character indicates a control character': you can replace 'let scrollaction="^Y"' with 'let scrollaction="\"' such that you dont have to use to produce the keybinding AND it makes copy paste possible :) – gospes Jan 20 '14 at 09:41
  • @SteveVermeulen How would you do that? Thanks. – Nanashi No Gombe Jan 07 '20 at 15:13
  • @NanashiNoGombe Not sure. I don't use smooth scrolling anymore – Steve Vermeulen Jan 08 '20 at 02:58
11

There is a simple remap hack in vim's tips.txt:

Smooth scrolling                    *scroll-smooth*

If you like the scrolling to go a bit smoother, you can use these mappings:

    :map <C-U> <C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y>
    :map <C-D> <C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E>
Andrew Wagner
  • 22,677
  • 21
  • 86
  • 100
  • 1
    Why would this be preferable to usng the 'scroll' option setting (e.g., :set scroll=15)? Scroll option seems preferable because you could even create an au command that changes it automatically when window height changes. ( I guess you could do something like that with this mapping method also, but scroll option simplifies things.) – Herbert Sitz Oct 31 '10 at 21:21
  • 1
    Actually, automatically setting scroll variable is one of the options I was considering too. As you mention, it needs to be appropriate to the window size. The scroll variable defaults to 1/2 the window height, but it doesn't seem to expose a way to change that fraction; you can only change it in terms of the number of lines. You might also have to set a smaller value for scroll than if you also had some sort of smooth scrolling, since your eyes won't have any intermediate scrolled states to improve persistence of vision. – Andrew Wagner Oct 31 '10 at 22:15
  • Update: I have tried a few variations on this answer, i.e. :map 2222222 :map 2222222 and the visual effect seems heavily dependent on how much stuff is on screen. i.e. the speed can vary by something like 4X. – Andrew Wagner Oct 31 '10 at 22:31
  • 1
    To clarify, c-d and c-u will scroll by whatever the 'scroll' option is set to. Default is 1/2 of window height, but you can set it to whatever you want. If you want to make a new value depend on window height then use the 'winheight("%")' function, which returns number of lines in window. So, e.g., the command ':set scroll = winheight("%")/3' would make c-u and c-d scroll 1/3 of window height. – Herbert Sitz Oct 31 '10 at 23:30
  • :set scroll=winheight("%")/3 resulted in E521: Number required after = – Andrew Wagner Nov 01 '10 at 02:48
  • Sorry about that, you need to write it this way: :execute "set scroll=".winheight("%")/3. You can then put that in an 'autocommand' (or probably several autocommands) so that it gets run automatically if the vim application window gets resized or if a buffer window is split. – Herbert Sitz Nov 01 '10 at 03:49
  • @Herbert Sitz `let &scroll=winheight('%')/3` is probably better. – ZyX Nov 01 '10 at 06:38
  • 1
    @Drew Wagner This mapping should be rewritten as `noremap repeat("\", 20)` for readability. – ZyX Nov 01 '10 at 06:41
  • @ZyX -- Thanks! I often use 'echo &opt' to see value of an option but I never thought to use it that way. Definitely better. – Herbert Sitz Nov 01 '10 at 22:33
  • 9
    Or just `:map 20` – anishsane Jun 12 '15 at 04:31
8

Shameless plug, but I created a plugin here that you can use to easily adjust the distance, speed, and duration of the scrolling animation: https://github.com/terryma/vim-smooth-scroll

terryma
  • 181
  • 2
  • 5
7

A Recent Plugin

I have posted something similar here, but basically there is a great plugin that we can use now for scrolling, called terryma/vim-smooth-scroll. It provides a very nice and smooth scrolling.


The install is quite easy:

1 I use Vundle so I simply appended this in .vimrc.bundles :

Bundle terryma/vim-smooth-scroll

In the latest version of Vundle available today, 29th April of 2016, you can put this in your .vimrc:

Plugin 'terryma/vim-smooth-scroll'

2 As stated in the doc, you can set up 3 arguments: distance, duration and speed.

I am using this in my .vimrc file:

noremap <silent> <c-b> :call smooth_scroll#up(&scroll*2, 10, 4)<CR>
noremap <silent> <c-f> :call smooth_scroll#down(&scroll*2, 10, 4)<CR>
Community
  • 1
  • 1
Mick
  • 30,759
  • 16
  • 111
  • 130
  • 1
    Sadly, this plugin also moves the cursor instead of what I wanted: to just scroll the screen like the `` and `` commands. – silviubogan Apr 29 '16 at 19:45
4

What I do is I set the keyboard repeat to very fast, about 120 chars / second, and the delay small. Then I map to 4j and to 4k I navigate up and down source code using j and k which moves the cursor up and down nice and quick, pretty smooth. But here's the good part, and this works on Linux, not Windows. For a number of years now, X11's keyboard input works in such a way that when you press and hold j it obviously starts putting out j characters. But when you then keep holding down j and then also press the ctrl key, X11 starts putting out c-j characters without you having to re-press the j key. Then when you let go of the ctrl key and still keep on pressing j, X11 continues to put j's again. So j makes the cursor start moving nice and smooth downwards, and you can periodically hit ctrl without letting go of j to give it a boost, a jolt.

Also, I do what Devin does, and I set scrolloffset to 5.

Lastly, I swap ctrl and cap lock. The default position of the ctrl key is completely retarded (no offense intended). It makes you have to rotate your left hand. I almost never use caps lock, so I swap them. Then my left pink finger can reach the ctrl key without any yoga moves.

These things have worked for me for years. I only use vim, never gvim.

Mike
  • 2,393
  • 3
  • 25
  • 37
3

This isn't exactly smooth scrolling, but it's how I handle not losing context when jumping pages.

set so=7

'scrolloff' 'so' number (default 0) global
{not in Vi}
Minimal number of screen lines to keep above and below the cursor. This will make some context visible around where you are working. If you set it to a very large value (999) the cursor line will always be in the middle of the window (except at the start or end of the file or when long lines wrap). For scrolling horizontally see 'sidescrolloff'. NOTE: This option is set to 0 when 'compatible' is set.

3

The Plugin cskeeters/vim-smooth-scroll supports smooth scrolling and requires no configuration. It supports to support smooth scrolling with zt, zz, and zb. It's a fork of terryma's plugin. Some of the open pull requests have been applied.

Chad Skeeters
  • 1,468
  • 16
  • 19
2

This combines many of these answers, and this is what I use.

noremap <expr> <C-u> repeat("\<C-y> :sleep 10m<CR>", winheight('%')/2)
noremap <expr> <C-d> repeat("\<C-e> :sleep 10m<CR>", winheight('%')/2)
Jason Eveleth
  • 139
  • 1
  • 5
1
N <CR-E>
N <CR-Y>

...where 'N' is the number of single lines you want to scroll.

Not smooth in literal sense, but you keep the keyboard.

fde-capu
  • 205
  • 1
  • 4
  • 14
1

This may be controversial for hardcore users, but... the best way to smooth scroll in Vim is... mouse wheel.

fde-capu
  • 205
  • 1
  • 4
  • 14
0

I just found this plugin called "accelerated-smooth-scroll" ("Vim plugin for accelerated smooth scroll (mapping to <C-D>/<C-U>, <C-F>/<C-B>)") which can be for example used through Vundle by putting this line in your .vimrc:

Plugin 'yonchu/accelerated-smooth-scroll'

Then by restarting Vim and running the :PluginInstall command, then again restart Vim and use the <C-D> (Ctrl+D) and <C-O> (Ctrl+O) commands normally.

Sadly, this plugin also moves the cursor instead of what I wanted: to just scroll the screen like the <C-E> and <C-Y> commands.

silviubogan
  • 3,343
  • 3
  • 31
  • 57
0

I slightly modified @Keith Pinson's code so that ctrl-f and ctrl-b can be mapped too:

function SmoothScroll(scroll_direction, n_scroll)
    let n_scroll = a:n_scroll
    if a:scroll_direction == 1
        let scrollaction=""
    else 
        let scrollaction=""
    endif
    exec "normal " . scrollaction
    redraw
    let counter=1
    while counter<&scroll*n_scroll
        let counter+=1
        sleep 10m " ms per line
        redraw
        exec "normal " . scrollaction
    endwhile
endfunction

" smoothly scroll the screen for some scrolling operations
nnoremap <C-U> :call SmoothScroll(1,1)<cr>
nnoremap <C-D> :call SmoothScroll(2,1)<cr>
nnoremap <C-B> :call SmoothScroll(1,2)<cr>
nnoremap <C-F> :call SmoothScroll(2,2)<cr>
mattb
  • 2,787
  • 2
  • 6
  • 20