Skip to content

Commit

Permalink
Do Rust's RLS hack properly with new class-based API
Browse files Browse the repository at this point in the history
* eglot.el (eglot-server-ready-p): New API method.
(eglot-handle-request, eglot-handle-notification): New
defgeneric's.
(eglot--ready-predicates, eglot--server-ready-p): Remove.
(eglot--async-request): Call eglot-server-ready-p.
(eglot--request): Tweak comment.
(eglot--rls-probably-ready-for-p): Remove.
(eglot-server-ready-p eglot-rls): Adapts earlier
eglot--rls-probably-ready-for-p.
(eglot-handle-notification eglot-rls): Specialize to eglot-rls.
(eglot-rls): New eglot-lsp-server subclass.

* eglot-tests.el (auto-detect-running-server)
(auto-reconnect, basic-completions)
(hover-after-completions): Use eglot--interactive
  • Loading branch information
joaotavora committed May 22, 2018
1 parent 412e77d commit 62b8cf3
Showing 1 changed file with 32 additions and 29 deletions.
61 changes: 32 additions & 29 deletions eglot.el
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
:prefix "eglot-"
:group 'applications)

(defvar eglot-server-programs '((rust-mode . ("rls"))
(defvar eglot-server-programs '((rust-mode . (eglot-rls "rls"))
(python-mode . ("pyls"))
(js-mode . ("javascript-typescript-stdio"))
(sh-mode . ("bash-language-server" "start"))
Expand Down Expand Up @@ -112,6 +112,23 @@ lasted more than that many seconds."
:type '(choice (boolean :tag "Whether to inhibit autoreconnection")
(integer :tag "Number of seconds")))


;;; API
;;;
(cl-defgeneric eglot-server-ready-p (server what) ;; API
"Tell if SERVER is ready for WHAT in current buffer.
If it isn't, a deferrable `eglot--async-request' *will* be
deferred to the future."
(:method (_s _what)
"Normally not ready if outstanding changes."
(not (eglot--outstanding-edits-p))))

(cl-defgeneric eglot-handle-request (server method id &rest params)
"Handle SERVER's METHOD request with ID and PARAMS.")

(cl-defgeneric eglot-handle-notification (server method id &rest params)
"Handle SERVER's METHOD notification with PARAMS.")


;;; Process management
(defvar eglot--servers-by-project (make-hash-table :test #'equal)
Expand Down Expand Up @@ -619,16 +636,6 @@ originated."
(eglot--log-event server `(:running-deferred ,(length actions)))
(mapc #'funcall (mapcar #'car actions))))

(defvar eglot--ready-predicates '(eglot--server-ready-p)
"Special hook of predicates controlling deferred actions.
If one of these returns nil, a deferrable `eglot--async-request'
will be deferred. Each predicate is passed the symbol for the
request request and a process object.")

(defun eglot--server-ready-p (_what _server)
"Tell if SERVER is ready for processing deferred WHAT."
(not (eglot--outstanding-edits-p)))

(cl-defmacro eglot--lambda (cl-lambda-list &body body)
(declare (indent 1) (debug (sexp &rest form)))
(let ((e (gensym "eglot--lambda-elem")))
Expand Down Expand Up @@ -668,8 +675,7 @@ TIMER)."
(existing (gethash (list deferred buf)
(eglot--deferred-actions server))))
(when existing (setq existing (cadr existing)))
(if (run-hook-with-args-until-failure 'eglot--ready-predicates
deferred server)
(if (eglot-server-ready-p server deferred)
(remhash (list deferred buf) (eglot--deferred-actions server))
(eglot--log-event server `(:deferring ,method :id ,id :params ,params))
(let* ((buf (current-buffer)) (point (point))
Expand Down Expand Up @@ -708,9 +714,9 @@ TIMER)."
"Like `eglot--async-request' for SERVER, METHOD and PARAMS, but synchronous.
Meaning only return locally if successful, otherwise exit non-locally.
DEFERRED is passed to `eglot--async-request', which see."
;; Launching a deferred sync request with outstanding changes is a
;; bad idea, since that might lead to the request never having a
;; chance to run, because `eglot--ready-predicates'.
;; HACK: A deferred sync request with outstanding changes is a bad
;; idea, since that might lead to the request never having a chance
;; to run, because idle timers don't run in `accept-process-output'.
(when deferred (eglot--signal-textDocument/didChange))
(let* ((done (make-symbol "eglot-catch")) id-and-timer
(res
Expand Down Expand Up @@ -1583,22 +1589,19 @@ Proceed? "

;;; Rust-specific
;;;
(defun eglot--rls-probably-ready-for-p (what server)
"Guess if the RLS running in SERVER is ready for WHAT."
(or (eq what :textDocument/completion) ; RLS normally ready for this
; one, even if building ;
(pcase-let ((`(,_id ,what ,done ,_detail) (eglot--spinner server)))
(and (equal "Indexing" what) done))))
(defclass eglot-rls (eglot-lsp-server) () :documentation "Rustlang's RLS.")

;;;###autoload
(progn
(add-hook 'rust-mode-hook 'eglot--setup-rls-idiosyncrasies)
(defun eglot--setup-rls-idiosyncrasies ()
"Prepare `eglot' to deal with RLS's special treatment."
(add-hook 'eglot--ready-predicates 'eglot--rls-probably-ready-for-p t t)))
(cl-defmethod eglot-server-ready-p ((server eglot-rls) what)
"Except for :completion, RLS isn't ready until Indexing done."
(and (cl-call-next-method)
(or ;; RLS normally ready for this, even if building.
(eq :textDocument/completion what)
(pcase-let ((`(,_id ,what ,done ,_detail) (eglot--spinner server)))
(and (equal "Indexing" what) done)))))

(cl-defmethod eglot-handle-notification
(server (_method (eql :window/progress)) &key id done title message &allow-other-keys)
((server eglot-rls) (_method (eql :window/progress))
&key id done title message &allow-other-keys)
"Handle notification window/progress"
(setf (eglot--spinner server) (list id title done message))
(when (and (equal "Indexing" title) done)
Expand Down

0 comments on commit 62b8cf3

Please sign in to comment.