Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Messages from server lost when doing lsp-request #4323

Open
2 of 3 tasks
imakira opened this issue Feb 4, 2024 · 0 comments
Open
2 of 3 tasks

Messages from server lost when doing lsp-request #4323

imakira opened this issue Feb 4, 2024 · 0 comments
Labels

Comments

@imakira
Copy link

imakira commented Feb 4, 2024

Thank you for the bug report

  • I am using the latest version of lsp-mode related packages.
  • 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

Function lsp-request relies on throwing an error in the callback function

;; in `lsp-request'
(lsp-request-async method params
                               (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
                               :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
                               :no-merge no-merge
                               :mode 'detached
                               :cancel-token :sync-request)

While at the end of function lsp--create-filter-function, we can see it processes messages one by one.

;; in `lsp--create-filter-function'
(mapc (lambda (msg)
                (lsp--parser-on-message msg workspace))
              (nreverse messages))

If I understand it correctly, when an error is thrown (that is, when the callback is executed) in one of lsp--parser-on-message, the remaining messages will be lost forever.

Context: I was trying to make Razor language server work with lsp-mode, and I noticed some notifications didn't get processed in lsp-mode while I'm sure they were sent by the server.

Steps to reproduce

I wrote a simple dummy lsp server to help reproduce this problem.

(declaim (optimize (speed 0) (space 0) (debug 3)))

(asdf:load-system :com.inuoe.jzon)
(asdf:load-system :alexandria)

(defpackage :dummy-lsp
  (:use :cl :alexandria)
  (:local-nicknames (:jzon :com.inuoe.jzon)))
(in-package :dummy-lsp)

(defun read-message ()
  (let* ((header (read-line))
         (len (parse-integer (uiop:stripln (subseq header 15)))))
    (read-line)
    (let ((content (make-string len)))
      (read-sequence content *standard-input*)
      (jzon:parse content))))

(defun write-message (msg)
  (let ((msg-str (jzon:stringify msg)))
    (write-string (format nil "Content-Length: ~A~C~C" (length msg-str) #\Return #\Newline))
    (write-string (format nil "~C~C" #\Return #\Newline))
    (write-string msg-str)))

(defun main ()
  (print (jzon:stringify (read-message))
         uiop:*stderr*)
    (write-message (plist-hash-table
                    (list "capabilities"
                          (make-hash-table)
                          "serverInfo"
                          (plist-hash-table (list :name "dummy-lsp" :version "1.0")))))
  (force-output)
  (loop
   (let ((message (read-message)))
     (print (jzon:stringify message)
            uiop:*stderr*)
     (force-output uiop:*stderr*)
     (when (equal (gethash "method" message)
                  "dummy/request")
       (write-message (plist-hash-table
                       (list "method" "dummy/notification"
                             "params" (list "index"
                                            1))))
       (write-message (plist-hash-table
                       (list "id" (gethash "id" message)
                             "result" "request result")))
       (write-message (plist-hash-table
                       (list "method" "dummy/notification"
                             "params" (list "index"
                                            2))))
       (force-output)))))

(main)

Put the code at /tmp/dummy-lsp.lisp

When the dummy server receives a dummy/request request, it will send a response sandwiched by two notifications.

Client side config:

(lsp-register-client
 (make-lsp-client :new-connection
                  (lsp-stdio-connection
                   '("sbcl" "--noinform" "--noprint" "--load" "/tmp/dummy-lsp.lisp"))
                  :activation-fn
                  (lambda (&rest _)
                    t)
                  :notification-handlers
                  (ht
                   ("dummy/notification"
                    (lambda (workspace notif)
                      (pp (list "dummy/notification"
                                notif)))))
                  :server-id 'dummy-lsp
                  :major-modes '(emacs-lisp-mode)))

Now launch lsp in an emacs lisp buffer, and execute (pp (list "Request-result: " (lsp-request "dummy/request" (list :test "test"))))
Here is the output:

("dummy/notification" #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("index" 1)))
("Request-result: " "request result")

We can see the first notification got correctly handled, while the second got ignored.

Also, the second notification wasn't shown in *lsp-log: dummy-lsp* buffer either.

[Trace - 05:45:21 PM] Sending request 'dummy/request - (19)'.
Params: {
"test": "test"
}

[Trace - 05:45:21 PM] Received notification 'dummy/notification'.
Params: {
"index": 1
}

[Trace - 05:45:21 PM] Received response 'dummy/request - (19)' in 2ms.
Result: "request result"

Expected behavior

I think all notifications should get processed and the corresponding handlers should be called.

Which Language Server did you use?

shouldn't matter

OS

Linux

Error callstack

No response

Anything else?

No response

@imakira imakira added the bug label Feb 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant