]> git.eshelyaron.com Git - emacs.git/commitdiff
Do rust's rls hack properly with new class-based api
authorJoão Távora <joaotavora@gmail.com>
Tue, 22 May 2018 10:14:08 +0000 (11:14 +0100)
committerJoão Távora <joaotavora@gmail.com>
Tue, 22 May 2018 10:19:16 +0000 (11:19 +0100)
* 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

lisp/progmodes/eglot.el

index cb213e515c4eba5fb197e59e2cf3cea06c3b2e67..43cda1b42ef9debb2dcfd989f4f22df04c6492f2 100644 (file)
@@ -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"))
@@ -112,6 +112,23 @@ lasted more than that many seconds."
   :type '(choice (boolean :tag "Whether to inhibit autoreconnection")
                  (integer :tag "Number of seconds")))
 
+\f
+;;; 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.")
+
 \f
 ;;; Process management
 (defvar eglot--servers-by-project (make-hash-table :test #'equal)
@@ -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")))
@@ -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))
@@ -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
@@ -1583,22 +1589,19 @@ Proceed? "
 \f
 ;;; 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)