aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorTorben2026-05-28 12:00:09 +0200
committerGitHub2026-05-28 10:00:09 +0000
commit399c0ffd64affec81331c9046d454fd5d4be6c78 (patch)
tree16b6f8c4cd15cd043c5603fc8d54a6ee6440c85b
parent2a3af30fb6a725ec7215435369b310b1d2dc4c09 (diff)
downloadale-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.vim12
-rw-r--r--doc/ale.txt14
-rw-r--r--test/completion/test_omnifunc_completion.vader7
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