diff options
| author | Torben | 2026-05-28 12:00:09 +0200 |
|---|---|---|
| committer | GitHub | 2026-05-28 10:00:09 +0000 |
| commit | 399c0ffd64affec81331c9046d454fd5d4be6c78 (patch) | |
| tree | 16b6f8c4cd15cd043c5603fc8d54a6ee6440c85b | |
| parent | 2a3af30fb6a725ec7215435369b310b1d2dc4c09 (diff) | |
| download | ale-399c0ffd64affec81331c9046d454fd5d4be6c78.tar.gz | |
Fix infinite autocompletion loop (#5130)
- Check for timeout while waiting for language server result
- Return empty result if language server has no completion capabilities
- Add test for OmniFunc timeout
- Add documentation for timeout option
| -rw-r--r-- | autoload/ale/completion.vim | 12 | ||||
| -rw-r--r-- | doc/ale.txt | 14 | ||||
| -rw-r--r-- | test/completion/test_omnifunc_completion.vader | 7 |
3 files changed, 33 insertions, 0 deletions
diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index bd9fdf720..a435eb13f 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -764,6 +764,9 @@ function! s:OnReady(linter, lsp_details) abort let l:id = a:lsp_details.connection_id if !ale#lsp#HasCapability(l:id, 'completion') + " Return at least an empty result to avoid OmniFunc timeout + call ale#completion#Show([]) + return endif @@ -945,9 +948,18 @@ function! ale#completion#OmniFunc(findstart, base) abort else let l:result = ale#completion#GetCompletionResult() + let l:timeout = get(g:, 'ale_completion_timeout', 3) + let l:timeout_start = reltime() + while l:result is v:null && !complete_check() sleep 2ms let l:result = ale#completion#GetCompletionResult() + + if reltimefloat(reltime(l:timeout_start)) > l:timeout + " no-custom-checks + echoerr 'no result within timeout (' . l:timeout . 's)' + break + endif endwhile return l:result isnot v:null ? l:result : [] diff --git a/doc/ale.txt b/doc/ale.txt index 53453b54f..99d1d621f 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -1143,6 +1143,20 @@ g:ale_completion_max_suggestions Adjust this option as needed, depending on the complexity of your codebase and your available processing power. + *ale-options.completion_timeout* + *g:ale_completion_timeout* +completion_timeout +g:ale_completion_timeout + Type: |Number| + Default: `3` + + The maximum time in seconds ale#completion#OmniFunc() will wait for a + completion result from the language server. + + When the timeout is exceeded ale#completion#OmniFunc() will print an error + message and return an empty result. Setting a higher value allows longer + response times, but it may also keep Vim unresponsible for a longer time. + *ale-options.cursor_detail* *g:ale_cursor_detail* cursor_detail diff --git a/test/completion/test_omnifunc_completion.vader b/test/completion/test_omnifunc_completion.vader index c9ecd205a..8c0a339f8 100644 --- a/test/completion/test_omnifunc_completion.vader +++ b/test/completion/test_omnifunc_completion.vader @@ -58,3 +58,10 @@ Execute(The omnifunc function should parse and return async responses): AssertEqual ['foo'], ale#completion#OmniFunc(0, '') endif + +Execute (The omnifunc function should timeout with an error, if no result is returned): + " WARNING: This will lock the whole test-suite, if the timeout does not work! + let g:ale_completion_timeout = 0.2 + + AssertThrows call ale#completion#OmniFunc(0, '') + AssertEqual 'Vim(echoerr):no result within timeout (0.2s)', g:vader_exception |