diff options
author | Nicolas Hillegeer <nicolas@hillegeer.com> | 2021-12-15 15:47:11 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-15 07:47:11 -0700 |
commit | 5ba45a7cd6d178fbe9463499d13f40dd9eca54e4 (patch) | |
tree | 3cb1e32afd5b8b0d69bc476c4d56c50db24119a5 /runtime/lua/vim/lsp/util.lua | |
parent | 14ffcd190d61f12af29571c83bbcf07e8f127e54 (diff) | |
download | rneovim-5ba45a7cd6d178fbe9463499d13f40dd9eca54e4.tar.gz rneovim-5ba45a7cd6d178fbe9463499d13f40dd9eca54e4.tar.bz2 rneovim-5ba45a7cd6d178fbe9463499d13f40dd9eca54e4.zip |
fix(quickfix): avoid O(N^2) when filling from string typval (#16654)
When filling a quickfix/loclist from a string-typed VimL variable, the
complexity is O(N^2) in the number of lines in the variable.
The problem is caused by using `xstrlcpy(3)` to copy the characters from
the current position up to the next newline into the quickfix/loclist
buffer in a loop.
strlcpy(3) returns the length of `src`, so by necessity it has to
compute `strlen(src)`. This means scanning the full rest of the typval
on every iteration while only copying a small fraction (up to the next
'\n').
This is not a problem whenever the srclen-to-copylen ratio is close to
1, which it usually is. But not in this case. Since we already
calculated exactly how many bytes we want to copy, we should be using
memcpy(3).
This problem is not present in Vim, as it uses `vim_strncpy`, a
`strncpy(3)`-alike, which stops at either `\0` or `n`, whichever comes
first.
The quickfix/loclist window can be filled using a:
1. File (used by commands like :grep/:make/... to source directly
from their errorfile)
2. Buffer (used by :cbuffer and its variants)
3. Typval
a. String (used by :cexpr and its variants)
b. List of strings (used by setqflist(), setloclist(), :cepxr and its
variants)
This commit optimizes case (3a), especially when the typval is a long
string.
The pathological path is triggered by (e.g.) :grep enhancements as found
in https://gist.github.com/romainl/56f0c28ef953ffc157f36cc495947ab3:
function! Grep(...)
return system(join([&grepprg] + a:000), ' '))
endfunction
:cgetexpr Grep('foo')
It would've been better for Neovim to use `systemlist` here, before this
commit.
Diffstat (limited to 'runtime/lua/vim/lsp/util.lua')
0 files changed, 0 insertions, 0 deletions