minad / cape

🦸cape.el - Completion At Point Extensions
GNU General Public License v3.0
584 stars 20 forks source link

Error `<cape--done> is undefined` #25

Closed alternateved closed 2 years ago

alternateved commented 2 years ago

Hello,

I think I might suffer from similar issue as in #23. I try to use company adapter with psc-ide and its company-psc-ide-backend like so:

(setq-local completion-at-point-functions
      (list (cape-company-to-capf #'company-psc-ide-backend)))

The result of that is that something (most likely completion but there is no child frame popup) hangs every now and then - I have to manually exit process by C-g - then in the minibuffer I see the message:

<cape--done> is undefined

If there is anything else that I could provide that would be helpful, please let me know.

minad commented 2 years ago

Ah okay, @galeo already mentioned that. I don't know how to resolve this. It seems like an Emacs event handler bug. You can probably work around this by adding this single line to your config:

(global-set-key [cape--done] #'ignore)
galeo commented 2 years ago

Hi @alternateved,

I haven't paid attention to this problem recently. Could you try the following code to see if there is any improvement:

(defun cape--company-call (&rest app)
  "Apply APP and handle future return values."
  ;; Backends are non-interruptible. Disable interrupts!
  (let ((toi throw-on-input)
        (throw-on-input nil))
    (pcase (apply app)
      ;; Handle async future return values.
      (`(:async . ,fetch)
       (let ((res 'cape--waiting))
         (unwind-protect
             (progn
               (funcall fetch (lambda (arg)
                                (when (eq res 'cape--waiting)
                                  (push 'cape--done unread-command-events))
                                (setq res arg)))
               ;; Force synchronization.
               (while (eq res 'cape--waiting)
                 ;; When we've got input, interrupt the computation.
                 (when unread-command-events (throw toi nil))
                 (sit-for 0.5 'nodisplay)))
           ;; Remove cape--done introduced by future callback.
           ;; NOTE: `sit-for' converts cape--done to (t . cape--done).
           ;; It seems that `sit-for' does not use a robust method to
           ;; reinject inputs, maybe the implementation will change in
           ;; the future.
           (setq unread-command-events
                 (delq 'cape--done
                       (delete '(t . cape--done)
                               unread-command-events))))
         (and (consp res) res)))
      ;; Plain old synchronous return value.
      (res res))))

Thanks.

minad commented 2 years ago

@galeo Where is the difference? I don't see how your code is supposed to solve the issue.

galeo commented 2 years ago

@galeo Where is the difference? I don't see how your code is supposed to solve the issue.

Just increase the sit-for time to 0.5 seconds. I did some tests and no error reported as before.

minad commented 2 years ago

This does not sound like an actual fix, it rather makes the issue less likely?

galeo commented 2 years ago

This does not sound like an actual fix, it rather makes the issue less likely?

Indeed. I can't find the cause and solution of the problem either. I also encountered serious hangs situation similar to @alternateved. The above adjustments is only useable.

minad commented 2 years ago

I wonder what the problem is. The code is essentially equivalent to the Company synchronization code.

alternateved commented 2 years ago

@minad solution did not work for me, so I decided to test the master version of Emacs (I was lagging behind a couple of weeks behind master). It is hard to verify it - I will know more in next days - but for now it seems that no hangs are happening with that latest version.

alternateved commented 2 years ago

Okay, sorry, false alarm. It is still happening.

minad commented 2 years ago

I pushed a commit which tries a different synchronization method. Does this lead to improvements?

alternateved commented 2 years ago

The hangs are not grabbing me now - I just encounter errors from which I get out immediately. This is what appears in the *Messages* buffer:

<cape--done> is undefined
r is undefined

So you might say it is a bit better, although screen flashing for no reason (visual-bell) is a bit disturbing.

minad commented 2 years ago

Did you try the newest commit?

alternateved commented 2 years ago

I just tried the newest one. I tried to record when it happens but it seems that the gif recording skipped the frame where error flashed.

Nevertheless you could see that the <cape--done> is undefined message still occurs.

peek

minad commented 2 years ago

Okay, no idea about this. It is also low priority for me. My interest in fighting against badly designed Emacs APIs is limited. It is unclear to me how cape--done can escape from the synchronization code.

Did you try to define cape--done as global key as proposed in my other comment?

alternateved commented 2 years ago

That's understandable. Defining cape--done as global key seems to work now (with earlier commits it was hanging), so it is better now. Thank you a lot for help.

minad commented 2 years ago

@alternateved Please give it another try without the global-set-key. I think I understood the issue now. The problem was that the future wasn't properly canceled.

alternateved commented 2 years ago

Amazing! I think it works now.

galeo commented 2 years ago

In a quick test it still echo message like x cape--done occasionally.

clip_gif

galeo commented 2 years ago

@minad From my last message:

In a quick test it still echo message like x cape--done occasionally.

I'm trying to solve this problem. It seems that set echo-keystrokes to 0 can avoid echoing the unfinished commands.

diff --git a/cape.el b/cape.el
index 17e3e11..d449883 100644
--- a/cape.el
+++ b/cape.el
@@ -908,7 +908,9 @@ If INTERACTIVE is nil the function acts like a capf."
                                 (push 'cape--done unread-command-events)
                                 (setq res arg))))
                    (when (eq res 'cape--waiting)
-                     (let ((ev (let (input-method-function) (read-event nil t))))
+                     (let ((ev (let (input-method-function
+                                     (echo-keystrokes 0))
+                                 (read-event nil t))))
                        (unless (eq ev 'cape--done)
                          (push (cons t ev) unread-command-events)
                          (setq res 'cape--cancelled)

Thanks.