emacs-sideline / sideline

Show information on the side
GNU General Public License v3.0
118 stars 14 forks source link

any plan to support eglot #5

Closed thinkiny closed 7 months ago

thinkiny commented 1 year ago

eglot is going to merge to master. Is there any plan to support eglot like sideline-lsp?

jcs090218 commented 1 year ago

Yes, it would be great to support eglot. However, I am not quite sure how this can work with eglot at the moment due to my insufficient knowledge to eglot.

toniz4 commented 1 year ago

eglot is going to merge to master. Is there any plan to support eglot like sideline-lsp?

It doesn't already support it? Eglot uses flymake to display the errors in the buffer, so sideline-flymake already covers that.

jcs090218 commented 1 year ago

I think he is referring to the code action from eglot since sideline-lsp provides it.

Yuanda-Dong commented 1 year ago

There was a discussion https://github.com/joaotavora/eglot/discussions/1070 on Eglot repo with some code snippets.

jcs090218 commented 1 year ago

The implementation looks interesting, but I don't know how to apply the action working. 🤔

(defun sideline-eglot-code-actions (command)
  "Sideline backend for Eglot code actions."
  (cl-case command
    (`candidates
     (cons :async
           (lambda (callback &rest _)
             (funcall callback (mapcar (lambda (x) (concat (substring x 0 (min (length x) 50)) "  "))
                                       (eglot-code-actions (point)))))))
    (`action (lambda (candidate &rest _)   ; optional
               (message "Execute command for `%s`!" candidate)))))  ; How to resolve the code action depends on selected candidate?

@joaotavora Can you help us here? 😕

joaotavora commented 1 year ago

Sorry, I don't have time to learn about this sideline API right now. Nothing is the code looks fishy, but then I don't understand what candidates and action mean. "how to apply the action working" is not something I can understand. What is the type of the variable candidate?

joaotavora commented 1 year ago

In the latest master version pushed just now (to Emacs's upstream, not here) I offer a new eglot-execute API function that can be used to ask a server (which you can get with the existing eglot-current-server) to execute one of the items returned by the existing eglot-code-actions.

jcs090218 commented 1 year ago

Sorry, I don't have time to learn about this sideline API right now.

The design is the same as company-mode, and it seems like you know company-mode very well. 🤔

Nothing is the code looks fishy, but then I don't understand what candidates and action mean. "how to apply the action working" is not something I can understand. What is the type of the variable candidate?

candidate is just a string you displayed by the sideline, therefore:

(defun my-backend (command)
  "Example backend."
  (cl-case command
    (`candidates '("info 1" "info 2"))  ; this returns the candidates
    (`action (lambda (candidate &rest _)   ; candidate is the string got clicked! In this case, it will either be `info 1` or `info 2`.
               (message "Execute command for `%s`!" candidate)))))  ; we expect to execute the corresponding code action here

So we need a way to use the string as an ID (I normally use hash-table) to find the right code action to execute.

joaotavora commented 1 year ago

The design is the same as company-mode, and it seems like you know company-mode very well. thinking

Company mode is a completely different thing. I don't appreciate or remember much about its API. And what I knew back then is not what I know now. I think you may as well believe me when I tell you i don't know how this APi works (no documentation was shown to me until now) and you should believe me when I tell you I don't have time to learn it. No need for "thinking" emoji.

You should solve this problem, not me. I've provided eglot-execute. Use text properties or something.

jcs090218 commented 1 year ago

Thanks for the response! I will check into it when I have time for this! ;)

herbertjones commented 1 year ago

Something like this seems to work, but I'm having trouble with eglot timing out when using the mouse.

(defvar-local sideline-eglot--async-candidates-timer nil)
(defvar-local sideline-eglot--ht-candidates nil)

(defun sideline-eglot--async-candidates (callback &rest _)
  (when sideline-eglot--async-candidates-timer
    (cancel-timer sideline-eglot--async-candidates-timer)
    (setq sideline-eglot--async-candidates-timer nil))
  (let ((p (point)))
    (setq sideline-eglot--async-candidates-timer
          (run-with-idle-timer
           0.3 nil
           (lambda ()
             (setq sideline-eglot--ht-candidates (ht-create))
             (dolist (row (eglot-code-actions p))
               (ht-set! sideline-eglot--ht-candidates (getf row :title) row))
             (funcall callback (ht-keys sideline-eglot--ht-candidates)))))))

(defun sideline-eglot (command)
  "Eglot backend for sideline."
  (cl-case command
    (`candidates
     (cons :async #'sideline-eglot--async-candidates))
    (`action
     (lambda (candidate &rest _)
       (let ((matching-code-action (ht-get sideline-eglot--ht-candidates candidate)))
         (unless matching-code-action
           (error "Failed to find candidate: %s" candidate))
         (let ((command (getf matching-code-action :command))
               (server (eglot-current-server)))
           (unless server
             (error "Server disappeared"))
           (eglot-execute-command server
                                  (getf command :command)
                                  (getf command :arguments))))))))
andrewbanchich commented 7 months ago

Any updates on this @jcs090218 ? Would be really awesome to have a sideline mode for eglot that works out of the box.

jcs090218 commented 7 months ago

I've created the package sideline-eglot. It's currently only on JCS-ELPA.

andrewbanchich commented 7 months ago

Thank you!