Improve ALEFix performance for neovim (#3974)

* Avoid performance problems with setbufline() and Treesitter

Call nvim_buf_set_lines() instead.

Since this is a performance problem only in Neovim (Treesitter is only
available there), it doesn't matter that this API is unavailable in Vim.

Note: nvim_buf_set_lines() returns E5555, when set nomodifiable is on.

Fixes #3669

* Avoid sign flickering

The signs flickered because nvim_buf_set_lines() removes all signs from
lines that it touches, which will immediately be readded by Ale (causing
the brief flicker). This is intended behaviour in neovim [0].

Neovim itself faced this problem in their own LSP formatting sync,
although they had the problem with marks instead of signs [1].
Similar to how neovim fixed it by storing and restoring the marks [2],
we can do the same thing with signs.

In fact it is easier with signs, because sign_placelist() will just
ignore and skip invalid line numbers, so we don't need to filter signs
that are not valid anymore.

[0] https://github.com/neovim/neovim/issues/10880#issuecomment-526466042
[1] https://github.com/neovim/neovim/issues/14307
[2] https://github.com/neovim/neovim/pull/14630
This commit is contained in:
Magnus Groß
2023-09-05 15:36:17 +02:00
committed by GitHub
parent ea27441834
commit 59e455b4d8
2 changed files with 13 additions and 2 deletions

View File

@@ -32,7 +32,7 @@ function! ale#fix#ApplyQueuedFixes(buffer) abort
endif
endif
endif
catch /E21/
catch /E21\|E5555/
" If we cannot modify the buffer now, try again later.
let g:ale_fix_buffer_data[a:buffer] = l:data

View File

@@ -522,7 +522,18 @@ function! ale#util#SetBufferContents(buffer, lines) abort
" Use a Vim API for setting lines in other buffers, if available.
if l:has_bufline_api
call setbufline(a:buffer, 1, l:new_lines)
if has('nvim')
" save and restore signs to avoid flickering
let signs = sign_getplaced(a:buffer, {'group': 'ale'})[0].signs
call nvim_buf_set_lines(a:buffer, 0, l:first_line_to_remove, 0, l:new_lines)
" restore signs (invalid line numbers will be skipped)
call sign_placelist(map(signs, {_, v -> extend(v, {'buffer': a:buffer})}))
else
call setbufline(a:buffer, 1, l:new_lines)
endif
call deletebufline(a:buffer, l:first_line_to_remove, '$')
" Fall back on setting lines the old way, for the current buffer.
else