copilot-emacs / copilot.el

An unofficial Copilot plugin for Emacs.
MIT License
1.79k stars 126 forks source link

[Bug] Args out of range: 0, 1 #143

Closed rudiaraujo closed 1 year ago

rudiaraujo commented 1 year ago

Description

Sometimes while using copilot-mode I get an args-out-of-range error seemingly at random times.

Setting

GNU Emacs 28.2 (build 1, aarch64-apple-darwin22.4.0, Carbon Version 169 AppKit 2299.5) of 2023-05-20 MacBook Pro M1 Mac OS Ventura 13.3 Copilot.el Git commit bcc0407e1f00e3a865438f73386442de7978a547

Reproducing

Unfortunately, since the suggestions are quite difficult to predict, it's hard to describe a reproducible scenario.

Analysis

After looking at the backtrace, I found that the issue came from this function:

(defun copilot--set-overlay-text (ov completion)
  "Set overlay OV with COMPLETION."
  (move-overlay ov (point) (line-end-position))
  (let* ((tail (buffer-substring (copilot--overlay-end ov) (line-end-position)))
         (p-completion (concat (propertize completion 'face 'copilot-overlay-face)
                               tail)))
    (if (eolp)
        (progn
          (overlay-put ov 'after-string "") ; make sure posn is correct
          (setq copilot--real-posn (cons (point) (posn-at-point)))
          (put-text-property 0 1 'cursor t p-completion)
          (overlay-put ov 'display "")
          (overlay-put ov 'after-string p-completion))
      (overlay-put ov 'display (substring p-completion 0 1))
      (overlay-put ov 'after-string (substring p-completion 1)))
    (overlay-put ov 'completion completion)
    (overlay-put ov 'start (point))))

The particular line where it comes from is:

          (put-text-property 0 1 'cursor t p-completion)

And it happens because p-completion is an empty string.

The workaround

The way I worked around the issue was to replace the aforementioned line by:

          (unless (string= p-completion "")
            (put-text-property 0 1 'cursor t p-completion))

I don't know if this is a reasonable fix.

zerolfx commented 1 year ago

Can you please share the backtrace?

I have reviewed all instances of copilot--set-overlay-text and cannot identify any instance where an empty parameter for completion could be passed.

rudiaraujo commented 1 year ago

Yes, apologies, forgot to do that. Here you go (redacted some content just to be on the safe side, but shouldn't make a difference):

Debugger entered--Lisp error: (args-out-of-range 0 1)
  put-text-property(0 1 cursor t "")
  (progn (overlay-put ov 'after-string "") (setq copilot--real-posn (cons (point) (posn-at-point))) (put-text-property 0 1 'cursor t p-completion) (overlay-put ov 'display "") (overlay-put ov 'after-string p-completion))
  (if (eolp) (progn (overlay-put ov 'after-string "") (setq copilot--real-posn (cons (point) (posn-at-point))) (put-text-property 0 1 'cursor t p-completion) (overlay-put ov 'display "") (overlay-put ov 'after-string p-completion)) (overlay-put ov 'display (substring p-completion 0 1)) (overlay-put ov 'after-string (substring p-completion 1)))
  (let* ((tail (buffer-substring (copilot--overlay-end ov) (line-end-position))) (p-completion (concat (propertize completion 'face 'copilot-overlay-face) tail))) (if (eolp) (progn (overlay-put ov 'after-string "") (setq copilot--real-posn (cons (point) (posn-at-point))) (put-text-property 0 1 'cursor t p-completion) (overlay-put ov 'display "") (overlay-put ov 'after-string p-completion)) (overlay-put ov 'display (substring p-completion 0 1)) (overlay-put ov 'after-string (substring p-completion 1))) (overlay-put ov 'completion completion) (overlay-put ov 'start (point)))
  copilot--set-overlay-text(#<overlay from 422 to 422 in **redacted_file_name**.py> "")
  copilot--display-overlay-completion("" "**redacted_uuid**" 422 422)
  copilot--show-completion((:uuid "**redacted_uuid**" :text "            \"_id\": ObjectId(\"**redacted**\")," :range (:start (:line 21 :character 0) :end (:line 21 :character 56)) :displayText "\")," :position (:line 21 :character 53) :docVersion 411))
  #f(compiled-function (&rest rest) #<bytecode 0x1285f722a93f30c7>)(:completions [(:uuid "**redacted_uuid**" :text "            \"_id\": ObjectId(\"**redacted**\")," :range (:start (:line 21 :character 0) :end (:line 21 :character 56)) :displayText "\")," :position (:line 21 :character 53) :docVersion 411)])
  apply(#f(compiled-function (&rest rest) #<bytecode 0x1285f722a93f30c7>) (:completions [(:uuid "**redacted_uuid**" :text "            \"_id\": ObjectId(\"**redacted**\")," :range (:start (:line 21 :character 0) :end (:line 21 :character 56)) :displayText "\")," :position (:line 21 :character 53) :docVersion 411)]))
  #f(compiled-function (jsonrpc-lambda-elem1) #<bytecode 0xc92ee7b9e670d18>)((:completions [(:uuid "**redacted_uuid**" :text "            \"_id\": ObjectId(\"**redacted**\")," :range (:start (:line 21 :character 0) :end (:line 21 :character 56)) :displayText "\")," :position (:line 21 :character 53) :docVersion 411)]))
  #f(compiled-function (result) #<bytecode -0x1f1dc3bf8cd4b55c>)((:completions [(:uuid "**redacted_uuid**" :text "            \"_id\": ObjectId(\"**redacted**\")," :range (:start (:line 21 :character 0) :end (:line 21 :character 56)) :displayText "\")," :position (:line 21 :character 53) :docVersion 411)]))
  jsonrpc-connection-receive(#<jsonrpc-process-connection jsonrpc-process-connection-47091018> (:jsonrpc "2.0" :id 4774 :result (:completions [(:uuid "**redacted_uuid**" :text "            \"_id\": ObjectId(\"**redacted**\")," :range (:start (:line 21 :character 0) :end (:line 21 :character 56)) :displayText "\")," :position (:line 21 :character 53) :docVersion 411)])))
  jsonrpc--process-filter(#<process copilot agent> "Content-Length: 414\15\n\15\n{\"jsonrpc\":\"2.0\",\"method\":\"LogMessage\",\"params\":{\"level\":1,\"message\":\"[INFO] [default] [2023-05-23T15:25:28.722Z] request.response: [https://copilot-proxy.githubusercontent.com/...")

Admittedly, this could be due to some interaction with another package, but I'm at a loss as to which one that would be.

I can also give you some more details of the situation. I have a Python file with something like the following lines:

    var = [
        {
            "_id": ObjectId(""),
            "aField": "some value",
            "anotherField": "another value",
        },
    ]

The cursor is placed between the double quotes of the string in the call to ObjectId. Copilot gives me a suggestion that I ignore, and I yank a value that I have on my kill-ring. Then the error occurs.

zerolfx commented 1 year ago
copilot--display-overlay-completion("" "**redacted_uuid**" 422 422)

The trace shows a function call to copilot--display-overlay-completion with an empty completion. However, the function checks if the parameter completion is non-empty using s-present-p, and it should not execute copilot--set-overlay-text if the parameter is empty.

(defun copilot--display-overlay-completion (completion uuid start end)
  "Show COMPLETION with UUID between START and END."
  (copilot-clear-overlay)
  (when (and (s-present-p completion)
             (or (= start (point)) ; up-to-date completion
                 (and (< start (point)) ; special case for removing indentation
                      (s-blank-p (s-trim (buffer-substring-no-properties start (point)))))))
    ...

I feel confused...

rudiaraujo commented 1 year ago

Oh I know what's happening. This change was added just one day after I installed Copilot.el. I will try updating it and see if it works.

rudiaraujo commented 1 year ago

Yes, upgrading seemed to fix it. Thanks a lot for the help, and apologies for the noise!