I am three months into my (4th) new development environment that I have bounced
around over the last three decades. I finally put in the time to learn
vim/neovim to get away from graphical IDEs and return back to shell development.
With this brings a whole new timesuck of constantly tweaking your .vimrc
to
the never reaching goal of perfection.
Now that I have my plugins and environments setup, I recently enabled a setting
to help me find my cursor faster in my .vimrc
file.
set cursorline
I was warned in the vim documentation that “Will make screen redrawing slower.” Little did I know just how slow it would make it crawl! I first noticed it with neovim. To confirm it wasn’t neovim itself, I attempted to load vim with the same config and oh wow how horrible slow things scrolled. CPU usage of both neovim and vim spiked to 99% on OS X under iterm2 and tmux.
The issue is exasperated when you increase your keyboard’s key repeat and shorten the repeat delay. OS X is not fast enough for me; so, I have to use Karabiner’s Key Repeat feature to speed things up greatly (Delay @ 150ms, Key Repeat at 10ms is just right).
A few quick Google searches surfaced the issue: cursorline
, and similar
cursorcolumn
was the issue when you have a plugin that highlights a bunch of
text. Most people were having issues with Ruby’s code plugins.
I was using vim-go
, and its highlighting, when I noticed the issue.
This Stackoverflow answer is, as the comments say, a lifesaver. Basically it outlines the very reason why scrolling is slow and how to debug exactly what regex pattern is causing it.
How to Debug Slow Scrolling in VIM
You can debug what is slowing things down by first enabling the vim option
called syntime
which is adequately defined as “When scrolling is slow.”
:syntime on
Then scroll up and down a lot, get it to bog down. I also recommend doing this within vim instead of neovim to really make things slow. After 10 seconds or so, generate a report with the following.
:syntime report
For me, here’s the top 10 results when a FileType
of go
was being scrolled:
TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN
2.482624 7066 0 0.009561 0.000351 goInterfaceDef \(type\s\+\)\@<=\w\+\(\s\+interface\s\+{\)\@=
2.476090 7066 0 0.008820 0.000350 goStructDef \(type\s\+\)\@<=\w\+\(\s\+struct\s\+{\)\@=
2.457858 7278 212 0.008375 0.000338 goFunction \(func\s\+\)\@<=\w\+\((\)\@=
2.440439 7066 0 0.007554 0.000345 goFunction \()\s\+\)\@<=\w\+\((\)\@=
0.757577 7180 114 0.001380 0.000106 goInterface \(.\)\@<=\w\+\({\)\@=
0.745827 7104 38 0.001105 0.000105 goStruct \(.\)\@<=\w\+\({\)\@=
0.640945 7064 0 0.004620 0.000091 goSpaceError \(\(^\|[={(,;]\)\s*<-\)\@<=\s\+
0.223065 12827 5910 0.000239 0.000017 goMethod \(\.\)\@<=\w\+\((\)\@=
0.071478 7064 0 0.000128 0.000010 goSpaceError \(\(<-\)\@<!\<chan\>\)\@<=\s\+\(<-\)\@=
0.058679 7064 0 0.000100 0.000008 goSpaceError \(\(\<chan\>\)\@<!<-\)\@<=\s\+\(\<chan\>\)\@=
Immediately you can see it is vim-go
’s regex patterns that is slowing down the
scrolling. Interesting how there were two goFunction
regex patterns caught,
and both are slow.
At first glance, there doesn’t seem to be any big issues with them. Just a lot of matching. Running the first one through regex101.com shows the following definition:
/\(type\s\+\)\@<=\w\+\(\s\+interface\s\+{\)\@=/
\( matches the character ( literally
type matches the characters type literally (case sensitive)
\s match any white space character [\r\n\t\f ]
\+ matches the character + literally
\) matches the character ) literally
\@ matches the character @ literally
<= matches the characters <= literally
\w match any word character [a-zA-Z0-9_]
\+ matches the character + literally
\( matches the character ( literally
\s match any white space character [\r\n\t\f ]
\+ matches the character + literally
interface matches the characters interface literally (case sensitive)
\s match any white space character [\r\n\t\f ]
\+ matches the character + literally
{ matches the character { literally
\) matches the character ) literally
\@ matches the character @ literally
= matches the character = literally
That is a lot of matching in a single regex! Perhaps vim’s regex interrupter is just that bad. I notice a significant speedup when using Neovim over Vim; but, it is still very slow.
The Fix for Slow Scrolling in VIM
Now, I could spend some time debugging this regex, inserting conditionals and groupings in an attempt to limit the amount of matching which should in theory speed that up. But I need to get some work done.
One could just toggle off cursorline
when it is slow and move on and toggle it
back on with the same command later. Set it to a mapped key to make it faster.
:set cursorline!
An option that I found in the help that does speed things up is lazyredraw
.
Though, it is tolerable with Neovim, vim was still a little bit choppy. I have
this enabled as default in my .vimrc
regardless.
:set lazyredraw
Some people have gotten success by disabling syntax highlighting after 128 columns and/or minlines set to 256. Neither worked for my environment though.
set synmaxcol=128
syntax sync minlines=256
Personally, I just disabled some of (but not all of) vim-go
’s syntax
highlighting because I visually value the cursorline
highlighting more
than syntax highlight. Besides, Rob Pike calls syntax highlighting
juvenile.
This option is very plugin
specific; so, your mileage will vary of rather
your vim plugin supports selective disabling of syntax highlighting.
For vim-go
, they have highlighting disabled by default and you must explicitly
enable it. To disable syntax highlighting, just remove what you did to enable it
in the first place in your .vimrc
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
You can see the three lines I commented out above. I still get plenty of syntax highlighting without it. So I’m good with this for now.
Finally, one could just use more of PageUp/PageDown to move around the file.