lsp-mode
lsp-mode copied to clipboard
Completion backend hangs when using Corfu as completion frontend
Thank you for the bug report
- [X] I am using the latest version of
lsp-moderelated packages. - [X] I checked FAQ and Troubleshooting sections
- [ ] You may also try reproduce the issue using clean environment using the following command:
M-x lsp-start-plain
Bug description
Sometimes, lsp completion backend hangs when using Corfu as the completion front end.
Here is the profile:
731 80% - corfu--post-command
730 80% - corfu--update
726 80% - corfu--update-candidates
723 79% - corfu--recompute-candidates
723 79% - corfu--all-completions
723 79% - completion-all-completions
723 79% - apply
723 79% - #<compiled -0x187e8d863e1757aa>
723 79% - completion--nth-completion
722 79% - completion--some
722 79% - #<compiled 0xf84d7d713b6baaf>
722 79% - orderless-all-completions
722 79% - orderless-filter
722 79% - #<compiled -0xd4df90488ccba75>
722 79% - #<compiled 0x9ea0f36097de3b9>
722 79% - lsp-request-while-no-input
722 79% - sit-for
70 7% + read-event
5 0% + redisplay
1 0% completion--styles
3 0% redisplay
3 0% redisplay
1 0% - corfu--candidates-popup
1 0% corfu--popup-show
1 0% - corfu-quit
1 0% + completion-in-region-mode
116 12% + ...
29 3% + redisplay_internal (C function)
17 1% - timer-event-handler
17 1% - apply
17 1% - corfu--auto-complete
17 1% - corfu--update
5 0% + corfu--update-candidates
5 0% + corfu-quit
4 0% + corfu--candidates-popup
3 0% redisplay
12 1% - command-execute
12 1% - funcall-interactively
8 0% - corfu-insert
8 0% - corfu--insert
8 0% - corfu--done
8 0% + #<compiled -0x7aa2ac626036bae>
4 0% - self-insert-command
2 0% - tree-sitter--after-change
2 0% tree-sitter--do-parse
1 0% evil--jump-hook
More details in this issue: https://github.com/minad/corfu/issues/188
Steps to reproduce
It is quite troublesome to reproduce this issue on any computer, I am not sure could you find the problem through the profiler report. If you really need to reproduce this, please let me know and I will prepare the project.
Expected behavior
When Corfu completion is triggered, it does not hang that long. I saw that lsp-response-timeout is defined as 10.
Which Language Server did you use?
C++ with Clangd
OS
Windows
Error callstack
No response
Anything else?
No response
Could this be another symptom of #3607?
Hey, I am running into this issue, Corfu is hanging when using LSP when writing JSX in JavaScript and Typescript. I've noticed the most egregious behaviour when I start to call a component, I.E. a < followed by any character. The pause isn't guaranteed and happens semi-randomly, so it's hard for me to create a test repo. I also noticed it happening a lot when defining a self-closing element or component />. Typing / was fine, but when I tried to type >, it would freeze for multiple seconds.
As @minad points out, there is definitely something to be fixed on the LSP Mode side.
There may be a second issue lurking though. I've had random lock-ups with Emacs 28 and 29, independent of corfu and/or lsp-mode. Single-stepping in the debugger seemed to hint to an infinite loop somewhere in the code that tries to compute the font settings.
Due to other problems ("symbol's definition is void"), I had to do a
(byte-recompile-directory package-user-dir nil 'force), and have never
had another lock-up since. Also, a loaddefs related patch has been
pushed to Emacs 29 recently. Thus, FWIW, you might want to try the
byte-recompile-directory thing and see if that changes anything for
you?
Just my two cents anyway.
I've got a minimal config with which this is reproducible:
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
(add-to-list 'auto-mode-alist '("\\.tsx?\\'" . typescript-ts-mode))
(use-package lsp-mode
:ensure t
:init
;; set prefix for lsp-command-keymap (few alternatives - "C-l", "C-c l")
(setq lsp-keymap-prefix "C-l")
:hook (;; replace XXX-mode with concrete major-mode(e. g. python-mode)
(typescript-ts-mode . lsp))
:commands lsp
:custom
(lsp-completion-provider :none))
(use-package corfu
:custom
(corfu-auto t)
(corfu-preselect 'prompt)
(corfu-auto-delay 0.0)
(corfu-auto-prefix 0)
(corfu-quit-no-match 'separator)
(completion-styles '(basic))
(corfu-cycle t)
:bind
(:map corfu-map
("TAB" . corfu-next)
([tab] . corfu-next)
("S-TAB" . corfu-previous)
("<escape>" . quit-corfu-and-exit-to-normal-mode)
([backtab] . corfu-previous))
:init
(global-corfu-mode))
Ok, I lied, there might be some corfu-related setup here that isn't required. Emacs version is 29.2, started with emacs -Q. typescript-language-server version 4.3.2, and then just open a file in a big ts repo. The problem is, that it's pretty sporadic. Whenever it happens, the Emacs process pegs one CPU core to 100%, so there seems to be something going on in the call to lsp-request-async here. The sit-for loop below that line does reliably cancel the async call after the timeout, but it waits for lsp-response-timeout seconds, and hangs Emacs completely during that time. So that leads us to one workaround for these hangups:
Set lsp-response-timeout to some very low number, like 1.
(setq lsp-response-timeout 1)
If you'd like to keep the response timeout higher elsewhere, I'd suggest add-advice with :around.
I've added some logging here and there to try and debug the issue, but no luck so far.