copilot-emacs / copilot.el

An unofficial Copilot plugin for Emacs.
MIT License
1.74k stars 124 forks source link

Corrupted completions caused by using copilot-accept-completion-by-word and copilot-accept-completion-by-line #136

Closed fpl9000 closed 1 year ago

fpl9000 commented 1 year ago

Using the latest version of copilot.el in Gnu Emacs for Windows, I'm seeing corrupted completions when using copilot-accept-completion-by-word and copilot-accept-completion-by-line. To reproduce:

  1. In an empty buffer with Emacs-Lisp mode enabled, type (defun my-increment (number) and press ENTER.
  2. Wait for a completion to appear.
  3. Press the keybinding for copilot-accept-completion-by-line just once.

Corrupted completion

The Elisp docstring line is the first line of the completion. The pale blue text on the last 2 lines is the remaining completion (it's that color, because I changed the foreground color of copilot-overlay-face). Notice that the accepted line is duplicated at the end of the completion, but without the completion face applied. This also happens with individual words when copilot-accept-completion-by-word is used.

If I press the keybinding for copilot-accept-completion-by-line again, the first accepted line is now gone, and the 2nd line in the original completion is now repeated at the end of the completion.

I have Font Lock mode turned on, but the issue also happens with it turned off.

I have this in my .emacs startup file:

(setq copilot-enable-predicates '(copilot--buffer-changed)
      copilot-idle-delay 1.0)
fpl9000 commented 1 year ago

UPDATE: By adding (progn (sit-for 0) (sleep-for 5)) in various places as a poor man's breakpoint that freezes Emacs display updates, I tracked down the last function call prior to the appearance of the extra face-free text at the end of the completion. It's this indicated call to copilot--set-overlay-text in copilot-accept-completion:

(defun copilot-accept-completion (&optional transform-fn)
  "Accept completion. Return t if there is a completion.
Use TRANSFORM-FN to transform completion if provided."
  (interactive)
  (when (copilot--overlay-visible)
    (let* ((completion (overlay-get copilot--overlay 'completion))
           (start (overlay-get copilot--overlay 'start))
           (end (overlay-get copilot--overlay 'end))
           (uuid (overlay-get copilot--overlay 'uuid))
           (t-completion (funcall (or transform-fn #'identity) completion)))
      (copilot--async-request 'notifyAccepted (list :uuid uuid))
      (copilot-clear-overlay)
      (delete-region start end)
      (insert t-completion)
      ; if it is a partial completion
      (when (and (s-prefix-p t-completion completion)
                 (not (s-equals-p t-completion completion)))
        (copilot--set-overlay-text (copilot--get-overlay) (s-chop-prefix t-completion completion)))
;;      ^^^^^^^^^^^^^^^^^^^^^^^^^^
      t)))

If instead I put (progn (sit-for 0) (sleep-for 5)) just after (insert t-completion), I see my accepted line and no completion, probably because it was cleared/deleted just before.

BTW, why does copilot-clear-overlay send a notifyRejected message to the server for the completion that is being accepted? It seems to be rejecting the same UUID that was just accepted by copilot-accept-completion.

zerolfx commented 1 year ago

Fixed in d6bdeee. Thanks for your detailed issue description and investigation.

rakotomandimby commented 1 year ago

Thanks guys. Encountered it, was on the way to declare the bug, but you fixed it before..

fpl9000 commented 1 year ago

Thanks for such a rapid response, @zerolfx! Your changes fixed it for me.