vim-highlightedundo icon indicating copy to clipboard operation
vim-highlightedundo copied to clipboard

Slow in large undos

Open infokiller opened this issue 5 years ago • 3 comments

Thanks for this plugin, I think it's a great idea, but I have one gripe: I ran into major slowness when the undo region is large undos (a few hundred lines). For example, delete 500 lines and then try to undo them. For me, vim became unresponsive for a few seconds. Is this because the plugin tries to draw the entire screen? or is it because of the diff tool?

Thanks

infokiller avatar May 06 '20 10:05 infokiller

I think it should restrict max highlight item count.

For example, changing as below makes it greatly faster (I don't know it is valid fix, though).

--- a/autoload/highlightedundo.vim
+++ b/autoload/highlightedundo.vim
@@ -121,7 +121,7 @@ function! s:Diff(kind, from, to, lines) abort
   elseif a:kind ==# 'c'
     let fromlinenrlist = range(a:from[0], a:from[1])
     let tolinenrlist = range(a:to[0], a:to[1])
-    for i in range(max([len(fromlinenrlist), len(tolinenrlist)]))
+    for i in range(min([max([len(fromlinenrlist), len(tolinenrlist)]), 100]))
       if i < len(fromlinenrlist) && i < len(tolinenrlist)
         let before = a:lines.delete[i]
         let after = a:lines.add[i]

My check operation is as below:

:r !seq 1 1000                  " Create 1000 lines
:1d_                            " Remove extra empty line
:0i                             " Insert 10 lines to buffer head










.
:11,$ s/^/./                    " Insert `.` each lines of numbers
:noh                            " Turn the search highlight off to make this plugin's highlight visible

15gg                            " Move cursor pos (for cursor position check)
u                               " Undo (`.` disappears)

15gg                            " Move cursor pos (for cursor position check)
<C-r>                           " Redo (`.` appears; **THIS IS TOO SLOW**)

KSR-Yasuda avatar Jul 22 '22 02:07 KSR-Yasuda

Futhremore, in some cases, highlight match is applied line-by-line.

I think it can be hacked there, but will be a bit difficult change.


In my check operation above,

  • redoing :r !seq 1 1000 is whole-change (was a Region of wise = V, for whole part of the change)
  • redoing :11,$ s/^/./ is line-by-line (was Regions of wise = v, for each changed line).

In the function s:glow() that sets highlights, as subdiff, the former whole-change case gives only one Region object; and the latter line-by-line case gives line number of Region objects.

Each Region object turns into a highlightedundo#highlight object, and sets one matchaddpos().

A matchaddpos() allows having up to eight highlight position, so, in the latter line-by-line case, several Regions can be reduced into one matchaddpos().

To do so, it must be rewrote the way of Region object handling, or such (currently, every one Region object does one matchaddpos()). Not a piece of cake as the patch I showed above.

KSR-Yasuda avatar Jul 28 '22 07:07 KSR-Yasuda

Also this may help with too-long-line undos.

It stores diff lines into variables. So, huge diff takes too much memory spaces to extremely slow it down.

To avoid this, check up to 250 chars per line. As a side effect, diffs for more than 250 chars are not correctly highlighted.

--- a/autoload/highlightedundo.vim
+++ b/autoload/highlightedundo.vim
@@ -414,14 +414,15 @@ function! s:parsechunk(diffoutput, from, to, i, n) abort "{{{
       break
     endif
 
-    let [addedline, pos, _] = matchstrpos(line, '\m^>\s\zs.*')
+    " XXX: For performance, check only up to 250 chars.
+    let [addedline, pos, _] = matchstrpos(line, '\m^>\s\zs.\{,250}')
     if pos != -1
       call add(lines.add, addedline)
       let i += 1
       continue
     endif
 
-    let [deletedline, pos, _] = matchstrpos(line, '\m^<\s\zs.*')
+    let [deletedline, pos, _] = matchstrpos(line, '\m^<\s\zs.\{,250}')
     if pos != -1
       call add(lines.delete, deletedline)
       let i += 1

Check Operations

" Repeat `.` for 80 chars in one line
80a
.

" Copy the line to 10,000 lines
yy
9999p

" Join the 10,000 lines into one line
" CAUTION: Without the change above, it takes too much time.
"          Try with up to 30 or 40 lines.
gg
<Shift-V>
G
gJ

" Undo / Redo
u
<Ctrl-r>

KSR-Yasuda avatar Aug 15 '22 02:08 KSR-Yasuda