lervag / vimtex

VimTeX: A modern Vim and neovim filetype plugin for LaTeX files.
MIT License
5.53k stars 390 forks source link

Cursor motion slows down near the end of a long line #1721

Closed sanghyukmoon closed 4 years ago

sanghyukmoon commented 4 years ago

Issue

Holding down the cursor motion keys (e.g., l,j,k,l,w,b) produces significant delay near the end of a long line (~1000 characters). For example, if I keep holding down l at the first character of the line, the cursor initially runs through the characters smoothly but at some point it starts producing the delay, such that the cursor stalls for a few second and jumps over many characters. The profiling experiment indicates that the _get_delim() function is causing the delay (see below).

minimal.vim

set nocompatible

let &rtp = '~/.vim/plugged/vimtex,' . &rtp
let g:tex_flavor = 'latex'

filetype plugin indent on
syntax enable

minimal.tex

% no premables
There are a number of astronomical systems, such as galactic and protostellar disks, where self-gravity and rotation play an essential role in dynamical evolution. For instance, starburst activity occurring in massive circumnuclear disks can not only inflate the natal disks to form thick tori surrounding active galactic nuclei (AGN) but also drive large-scale galactic winds and outflows. Accretion disks around AGN may be gravitationally unstable at some radii to form stars.  Self-gravity is also important in formation of large-scale spiral structure and giant molecular clouds on larger scales in galactic disks. In addition, recent observations of young stellar objects indicate that at least in the early stage of evolution, protostellar disks are massive enough to be self-gravitating. Gravitational instability of such disks may form trailing spirals that can redistribute the mass and angular momentum and induce heat via shocks and may be responsible for the formation of giant planets. There are a number of astronomical systems, such as galactic and protostellar disks, where self-gravity and rotation play an essential role in dynamical evolution. For instance, starburst activity occurring in massive circumnuclear disks can not only inflate the natal disks to form thick tori surrounding active galactic nuclei (AGN) but also drive large-scale galactic winds and outflows. Accretion disks around AGN may be gravitationally unstable at some radii to form stars.  Self-gravity is also important in formation of large-scale spiral structure and giant molecular clouds on larger scales in galactic disks. In addition, recent observations of young stellar objects indicate that at least in the early stage of evolution, protostellar disks are massive enough to be self-gravitating. Gravitational instability of such disks may form trailing spirals that can redistribute the mass and angular momentum and induce heat via shocks and may be responsible for the formation of giant planets.

Commands/Input

vim ms.tex l (keep holding down)

Observed Behaviour

Cursor motion slows down as it moves to the right (i.e., to the end of the line).

Expected Behaviour

Cursor motion should not produce noticeable time delay in lines of a reasonable length.

Output from VimtexInfo

System info
  OS: Ubuntu 20.04 LTS
  Vim version: VIM 8.1 (1-2269) 
  Has clientserver: true
  Servername: undefined (vim started without --servername) 

vimtex project: ms
  base: ms.tex
  root: /home/smoon
  tex: /home/smoon/ms.tex
  out: 
  log: 
  aux: 
  fls: 
  main parser: current file
  compiler: latexmk
    backend: jobs
    output: /tmp/v8utAqL/1
    configuration: 
      continuous: 1
      callback: 1
      latexmk options:
        -verbose
        -file-line-error
        -synctex=1
        -interaction=nonstopmode
      latexmk engine: -pdf
  viewer: General
  qf: LaTeX logfile
    config: 
      packages: 
        default: 1
      default: 1
  document class: 

Additional Informations I profiled the cursor motion using the above minimal tex file.

Experiment 1) Move cursor to the start of the long line :profile start start.log :profile func * Hold down l for ~3 second

Contents of start.log

FUNCTIONS SORTED ON TOTAL TIME
count  total (s)   self (s)  function
   85   1.246503   0.002923  180()
   85   1.008682   0.001187  vimtex#delim#get_current()
   85   1.007495   1.004456  <SNR>52_get_delim()
   85   0.233492   0.000773  vimtex#util#in_comment()
   85   0.232719             vimtex#util#in_syntax()
   85   0.007183             <SNR>12_Highlight_Matching_Pair()
   85   0.002543   0.000714  vimtex#pos#set_cursor()
   85   0.001829             <SNR>64_parse_args()
   85   0.001406             179()
   85   0.000496             vimtex#pos#get_cursor()
    1   0.000297   0.000069  <SNR>22_quit()
    1   0.000171             <SNR>22_buffer_deleted()
    1   0.000129   0.000100  vimtex#compiler#stop_all()
    1   0.000121   0.000087  2()
    1   0.000098   0.000059  vimtex#cache#write_all()
    1   0.000034   0.000022  21()
    2   0.000026             34()
    2   0.000024             vimtex#state#list_all()
    1   0.000022             134()
    1   0.000017             45()

FUNCTIONS SORTED ON SELF TIME
count  total (s)   self (s)  function
   85   1.007495   1.004456  <SNR>52_get_delim()
   85              0.232719  vimtex#util#in_syntax()
   85              0.007183  <SNR>12_Highlight_Matching_Pair()
   85   1.246503   0.002923  180()
   85              0.001829  <SNR>64_parse_args()
   85              0.001406  179()
   85   1.008682   0.001187  vimtex#delim#get_current()
   85   0.233492   0.000773  vimtex#util#in_comment()
   85   0.002543   0.000714  vimtex#pos#set_cursor()
   85              0.000496  vimtex#pos#get_cursor()
    1              0.000171  <SNR>22_buffer_deleted()
    1   0.000129   0.000100  vimtex#compiler#stop_all()
    1   0.000121   0.000087  2()
    1   0.000297   0.000069  <SNR>22_quit()
    1   0.000098   0.000059  vimtex#cache#write_all()
    2              0.000026  34()
    2              0.000024  vimtex#state#list_all()
    1   0.000034   0.000022  21()
    1              0.000022  134()
    1              0.000017  45()

Experiment 2) Move cursor to the end of the long line :profile start end.log :profile func * Hold down h for ~3 second

Contents of end.log

FUNCTIONS SORTED ON TOTAL TIME
count  total (s)   self (s)  function
   98   5.609199   0.002640  180()
   98   5.352195   0.001073  vimtex#delim#get_current()
   98   5.351122   5.347601  <SNR>52_get_delim()
   98   0.253136   0.000639  vimtex#util#in_comment()
   98   0.252497             vimtex#util#in_syntax()
   98   0.022807             <SNR>12_Highlight_Matching_Pair()
   98   0.002351   0.000629  vimtex#pos#set_cursor()
   98   0.001722             <SNR>64_parse_args()
   98   0.001228             179()
   98   0.001170             vimtex#pos#get_cursor()
    1   0.000298   0.000066  <SNR>22_quit()
    1   0.000176             <SNR>22_buffer_deleted()
    1   0.000126   0.000097  vimtex#compiler#stop_all()
    1   0.000120   0.000086  2()
    1   0.000102   0.000063  vimtex#cache#write_all()
    1   0.000034   0.000022  21()
    2   0.000026             34()
    2   0.000025             vimtex#state#list_all()
    1   0.000023             134()
    1   0.000016             45()

FUNCTIONS SORTED ON SELF TIME
count  total (s)   self (s)  function
   98   5.351122   5.347601  <SNR>52_get_delim()
   98              0.252497  vimtex#util#in_syntax()
   98              0.022807  <SNR>12_Highlight_Matching_Pair()
   98   5.609199   0.002640  180()
   98              0.001722  <SNR>64_parse_args()
   98              0.001228  179()
   98              0.001170  vimtex#pos#get_cursor()
   98   5.352195   0.001073  vimtex#delim#get_current()
   98   0.253136   0.000639  vimtex#util#in_comment()
   98   0.002351   0.000629  vimtex#pos#set_cursor()
    1              0.000176  <SNR>22_buffer_deleted()
    1   0.000126   0.000097  vimtex#compiler#stop_all()
    1   0.000120   0.000086  2()
    1   0.000298   0.000066  <SNR>22_quit()
    1   0.000102   0.000063  vimtex#cache#write_all()
    2              0.000026  34()
    2              0.000025  vimtex#state#list_all()
    1              0.000023  134()
    1   0.000034   0.000022  21()
    1              0.000016  45()
lervag commented 4 years ago

Thanks for the good issue description!

The cause of this delay is the improved "highlight matching paren" feature. It requires a search for delimiters, thus the calls to s:get_delim. The search is relatively expensive, but I've already spent quite some time tuning it to make it run smooth.

There are two current options within vimtex:

  1. Disable the highlight feature with g:vimtex_matchparen_enabled. This should make things run fast, but matching delimiters will not work.

  2. Reduce the g:vimtex_delim_stopline setting (default is 500, you may see an improvement if you reduce it to e.g. 100). This won't work in the specific example, but it may help with files that contain a lot of such lines.

You may also try to use the dedicated plugin vim-matchup, see :help vimtex-nf-enhanced-matchparen. This implies option 1 above and replaces the vimtex implementation with that of matchup, which should be as good or better.

That said: I don't really see a very clear way to improve the code. I'll look into it a little bit more, but I'm not very hopeful.

As an alternative:

Cursor motion should not produce noticeable time delay in lines of a reasonable length.

I personally find it much better to write e.g. sentence per line. Long lines are also challenging due to a lot of regexes for syntax highlighting and similar that often look for the end or beginning of a line. I think the current issue is also related to this, in some sense. So, if that is possible for you, then it could also be an option.

Still, I agree that this should not be an issue, so if I'm able to improve things, I will.

lervag commented 4 years ago

Ok, I looked into it, and as expected the problem is related to a call to searchpos(...) with a rather complicated regex for every keypress. The search is more expensive at the end of long lines than at the beginning (I've read something about this once, but I don't remember the details). In any case, as indicated above, I don't see a good way to improve things from here. Suggestions are welcome, but otherwise I think you should try matchup or turning of the matchparen feature.

I noticed that the delay is much worse in Vim than in neovim. In neovim, repeating h 100 times from the end of line takes about 1.7 seconds (on my computer), whereas for Vim the same takes 9.5 seconds.

sanghyukmoon commented 4 years ago

Thanks for your detailed explanation!

Putting let g:vimtex_matchparen_enabled = 0 in the minivimrc indeed removed the delay. Although it no more highlights \left( \right) or \begin{equation} \end{equation}, etc., I can still jump between the matches using %.

let g:vimtex_delim_stopline=100 or even let g:vimtex_delim_stopline=0 did not solve the issue for the specific example I posted above.

I tried using vim-matchup, after installing it and putting let &rtp = '~/.vim/plugged/vim-matchup/,' . &rtp and let g:matchup_override_vimtex = 1 in my minivimrc. However, it does not highlight, for example,

\begin{equation}
  x^2 + x = 0
\end{equation}

or $\left( 1 + 1 = 2 \right)$ as the way vimtex does (i.e., highlighting the entire \left( and \right) instead of just the ( and )). I'm not sure if this is an included feature in vim-matchup.

I personally find it much better to write e.g. sentence per line.

This seems a very interesting idea that I haven't considered about. At this moment, I feel that it is somewhat redundant to put a line break after every sentence and it is more aesthetically pleasing to have a block of text per paragraph, although this may surely depend on personal preferences.

However, considering your remark,

Long lines are also challenging due to a lot of regexes for syntax highlighting and similar that often look for the end or beginning of a line.

I wonder whether it should be regarded as a bad practice to write a long line in tex document in general.

For now, I think I would first try let g:vimtex_matchparen_enabled = 0 and see if it works for me.

Thank you so much for maintaining THE best vim LaTeX plugin. I love it!

sanghyukmoon commented 4 years ago

Update: when using 1)

call plug#begin()
Plug 'lervag/vimtex'
Plug 'andymass/vim-matchup'
call plug#end()

instead of 2)

let &rtp = '~/.vim/plugged/vimtex,' . &rtp
let &rtp = '~/.vim/plugged/vim-matchup/,' . &rtp

in my minivimrc, vim-matchup behaves as expected.

I :set to see whether there are differences in runtimepath between 1) and 2) and found that ~/.vim/plugged/vimtex/after and ~/.vim/plugged/vim-matchup/after is not added to runtimepath when I use 2).

Anyway, using vim-matchup completely solved my issue and I'm happy with it. Please close the issue if you do not have further questions or comments!

lervag commented 4 years ago

Thanks for your detailed explanation!

No problem!

let g:vimtex_delim_stopline=100 or even let g:vimtex_delim_stopline=0 did not solve the issue for the specific example I posted above.

No, and I think I stated exactly that. This only works in a multiline example.

I personally find it much better to write e.g. sentence per line.

This seems a very interesting idea that I haven't considered about. At this moment, I feel that it is somewhat redundant to put a line break after every sentence and it is more aesthetically pleasing to have a block of text per paragraph, although this may surely depend on personal preferences.

Yes, it is personal preference. However, I found it is more convenient with a sentence per line when I collaborate with people with version control (e.g. Git), as the changes are more easily diffable.

Long lines are also challenging due to a lot of regexes for syntax highlighting and similar that often look for the end or beginning of a line.

I wonder whether it should be regarded as a bad practice to write a long line in tex document in general.

Not sure. But as you see, I do recommend other styles. Still, I think it should work well regardless of style, and I'm sorry that it does not work fully as expected.

For now, I think I would first try let g:vimtex_matchparen_enabled = 0 and see if it works for me.

Thank you so much for maintaining THE best vim LaTeX plugin. I love it!

Thanks! I appreciate it!

I :set to see whether there are differences in runtimepath between 1) and 2) and found that ~/.vim/plugged/vimtex/after and ~/.vim/plugged/vim-matchup/after is not added to runtimepath when I use 2).

Yes, my "go to" minimal vim settings also include the after paths:

set nocompatible
let &rtp = '~/.vim/bundle/vimtex,' . &rtp
let &rtp .= ',~/.vim/bundle/vimtex/after'
filetype plugin indent on
syntax enable

Anyway, using vim-matchup completely solved my issue and I'm happy with it. Please close the issue if you do not have further questions or comments!

Great, happy to hear it. I'll close, then.