[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/eglot 7f4e273 31/69: Merge master into jsonrpc-refactor
From: |
João Távora |
Subject: |
[elpa] externals/eglot 7f4e273 31/69: Merge master into jsonrpc-refactor (using imerge) |
Date: |
Fri, 22 Jun 2018 11:54:59 -0400 (EDT) |
branch: externals/eglot
commit 7f4e27352c37dd2cbfb25d22caab6f4b57f91964
Merge: 9e9dc57 4211e0c
Author: João Távora <address@hidden>
Commit: João Távora <address@hidden>
Merge master into jsonrpc-refactor (using imerge)
---
README.md | 7 +
eglot-tests.el | 48 +++++--
eglot.el | 423 +++++++++++++++++++++++++++++----------------------------
jsonrpc.el | 122 +++++++++--------
4 files changed, 327 insertions(+), 273 deletions(-)
diff --git a/README.md b/README.md
index b87666c..c88f804 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,8 @@ for the language of your choice. Otherwise, it prompts you to
enter one:
* Python's [pyls][pyls]
* Bash's [bash-language-server][bash-language-server]
* PHP's [php-language-server][php-language-server]
+* [cquery][cquery] for C/C++
+
I'll add to this list as I test more servers. In the meantime you can
customize `eglot-server-programs`:
@@ -53,6 +55,9 @@ Here's a summary of available commands:
- `M-x eglot-events-buffer` jumps to the events buffer for debugging
communication with the server.
+- `M-x eglot-stderr-buffer` if the LSP server is printing useful debug
+information in stderr, jumps to a buffer with these contents.
+
There are *no keybindings* specific to Eglot, but you can bind stuff
in `eglot-mode-map`, which is active as long as Eglot is managing a
file in your project. The commands don't need to be Eglot-specific,
@@ -196,5 +201,7 @@ Under the hood:
[bash-language-server]: https://github.com/mads-hartmann/bash-language-server
[php-language-server]: https://github.com/felixfbecker/php-language-server
[company-mode]: https://github.com/company-mode/company-mode
+[cquery]: https://github.com/cquery-project/cquery
+
diff --git a/eglot-tests.el b/eglot-tests.el
index bdb8b21..c9cc3fd 100644
--- a/eglot-tests.el
+++ b/eglot-tests.el
@@ -234,11 +234,11 @@ Pass TIMEOUT to `eglot--with-timeout'."
(skip-unless (null (getenv "TRAVIS_TESTING")))
(let ((eglot-autoreconnect 1))
(eglot--with-dirs-and-files
- '(("project" . (("coiso.rs" . "bla")
- ("merdix.rs" . "bla"))))
+ '(("watch-project" . (("coiso.rs" . "bla")
+ ("merdix.rs" . "bla"))))
(eglot--with-timeout 10
(with-current-buffer
- (eglot--find-file-noselect "project/coiso.rs")
+ (eglot--find-file-noselect "watch-project/coiso.rs")
(should (zerop (shell-command "cargo init")))
(eglot--sniffing (
:server-requests s-requests
@@ -265,21 +265,22 @@ Pass TIMEOUT to `eglot--with-timeout'."
(= type 3)))))))))))
(ert-deftest rls-basic-diagnostics ()
- "Hover and highlightChanges are tricky in RLS."
+ "Test basic diagnostics in RLS."
(skip-unless (executable-find "rls"))
(skip-unless (executable-find "cargo"))
(eglot--with-dirs-and-files
- '(("project" . (("main.rs" . "bla"))))
+ '(("diag-project" . (("main.rs" . "fn main() {\nprintfoo!(\"Hello,
world!\");\n}"))))
(eglot--with-timeout 3
(with-current-buffer
- (eglot--find-file-noselect "project/main.rs")
+ (eglot--find-file-noselect "diag-project/main.rs")
(should (zerop (shell-command "cargo init")))
(eglot--sniffing (:server-notifications s-notifs)
- (insert "fn main() {\nprintfoo!(\"Hello, world!\");\n}")
(apply #'eglot (eglot--interactive))
(eglot--wait-for (s-notifs 1)
(&key _id method &allow-other-keys)
(string= method "textDocument/publishDiagnostics"))
+ (flymake-start)
+ (goto-char (point-min))
(flymake-goto-next-error)
(should (eq 'flymake-error (face-at-point))))))))
@@ -287,11 +288,14 @@ Pass TIMEOUT to `eglot--with-timeout'."
"Hover and highlightChanges are tricky in RLS."
(skip-unless (executable-find "rls"))
(skip-unless (executable-find "cargo"))
+ (skip-unless (null (getenv "TRAVIS_TESTING")))
(eglot--with-dirs-and-files
- '(("project" . (("main.rs" . "bla"))))
+ '(("hover-project" .
+ (("main.rs" .
+ "fn test() -> i32 { let test=3; return te; }"))))
(eglot--with-timeout 3
(with-current-buffer
- (eglot--find-file-noselect "project/main.rs")
+ (eglot--find-file-noselect "hover-project/main.rs")
(should (zerop (shell-command "cargo init")))
(eglot--sniffing (
:server-notifications s-notifs
@@ -301,7 +305,6 @@ Pass TIMEOUT to `eglot--with-timeout'."
:client-replies c-replies
:client-requests c-reqs
)
- (insert "fn test() -> i32 { let test=3; return te; }")
(apply #'eglot (eglot--interactive))
(goto-char (point-min))
(search-forward "return te")
@@ -320,6 +323,31 @@ Pass TIMEOUT to `eglot--with-timeout'."
(&key id &allow-other-keys)
(eq id pending-id))))))))
+(ert-deftest rls-rename ()
+ "Test renaming in RLS."
+ (skip-unless (executable-find "rls"))
+ (skip-unless (executable-find "cargo"))
+ (eglot--with-dirs-and-files
+ '(("rename-project"
+ . (("main.rs" .
+ "fn test() -> i32 { let test=3; return test; }"))))
+ (eglot--with-timeout 3
+ (with-current-buffer
+ (eglot--find-file-noselect "rename-project/main.rs")
+ (should (zerop (shell-command "cargo init")))
+ (eglot--sniffing (
+ :server-notifications s-notifs
+ :server-requests s-requests
+ :server-replies s-replies
+ :client-notifications c-notifs
+ :client-replies c-replies
+ :client-requests c-reqs
+ )
+ (apply #'eglot (eglot--interactive))
+ (goto-char (point-min)) (search-forward "return te")
+ (eglot-rename "bla")
+ (should (equal (buffer-string) "fn test() -> i32 { let bla=3; return
bla; }")))))))
+
(ert-deftest basic-completions ()
"Test basic autocompletion in a python LSP"
(skip-unless (executable-find "pyls"))
diff --git a/eglot.el b/eglot.el
index ccb7b49..31ef081 100644
--- a/eglot.el
+++ b/eglot.el
@@ -2,7 +2,7 @@
;; Copyright (C) 2018 Free Software Foundation, Inc.
-;; Version: 0.4
+;; Version: 0.7
;; Author: João Távora <address@hidden>
;; Maintainer: João Távora <address@hidden>
;; URL: https://github.com/joaotavora/eglot
@@ -61,6 +61,7 @@
(require 'subr-x)
(require 'jsonrpc)
(require 'filenotify)
+(require 'ert)
;;; User tweakable stuff
@@ -73,6 +74,8 @@
(python-mode . ("pyls"))
(js-mode . ("javascript-typescript-stdio"))
(sh-mode . ("bash-language-server" "start"))
+ (c++-mode . (eglot-cquery "cquery"))
+ (c-mode . (eglot-cquery "cquery"))
(php-mode . ("php" "vendor/felixfbecker/\
language-server/bin/php-language-server.php")))
"How the command `eglot' guesses the server to start.
@@ -112,13 +115,21 @@ lasted more than that many seconds."
;;; API (WORK-IN-PROGRESS!)
;;;
-(defmacro eglot--obj (&rest what)
- "Make WHAT a JSON object suitable for `json-encode'."
- (declare (debug (&rest form)))
- ;; FIXME: not really API. Should it be?
- ;; FIXME: maybe later actually do something, for now this just fixes
- ;; the indenting of literal plists.
- `(list ,@what))
+(cl-defmacro eglot--with-live-buffer (buf &rest body)
+ "Check BUF live, then do BODY in it." (declare (indent 1) (debug t))
+ (let ((b (cl-gensym)))
+ `(let ((,b ,buf)) (if (buffer-live-p ,b) (with-current-buffer ,b
,@body)))))
+
+(cl-defmacro eglot--lambda (cl-lambda-list &body body)
+ "Make a unary function of ARG, a plist-like JSON object.
+CL-LAMBDA-LIST destructures ARGS before running BODY."
+ (declare (indent 1) (debug (sexp &rest form)))
+ (let ((e (gensym "eglot--lambda-elem")))
+ `(lambda (,e) (apply (cl-function (lambda ,cl-lambda-list ,@body)) ,e))))
+
+(cl-defmacro eglot--widening (&rest body)
+ "Save excursion and restriction. Widen. Then run BODY." (declare (debug t))
+ `(save-excursion (save-restriction (widen) ,@body)))
(cl-defgeneric eglot-handle-request (server method id &rest params)
"Handle SERVER's METHOD request with ID and PARAMS.")
@@ -133,15 +144,15 @@ lasted more than that many seconds."
(cl-defgeneric eglot-client-capabilities (server)
"What the EGLOT LSP client supports for SERVER."
(:method (_s)
- (eglot--obj
- :workspace (eglot--obj
+ (list
+ :workspace (list
:applyEdit t
:workspaceEdit `(:documentChanges :json-false)
:didChangeWatchesFiles `(:dynamicRegistration t)
:symbol `(:dynamicRegistration :json-false))
:textDocument
- (eglot--obj
- :synchronization (eglot--obj
+ (list
+ :synchronization (list
:dynamicRegistration :json-false
:willSave t :willSaveWaitUntil t :didSave t)
:completion `(:dynamicRegistration :json-false)
@@ -153,7 +164,7 @@ lasted more than that many seconds."
:documentHighlight `(:dynamicRegistration :json-false)
:rename `(:dynamicRegistration :json-false)
:publishDiagnostics `(:relatedInformation :json-false))
- :experimental (eglot--obj))))
+ :experimental (list))))
(defclass eglot-lsp-server (jsonrpc-process-connection)
((project-nickname
@@ -165,9 +176,9 @@ lasted more than that many seconds."
(capabilities
:documentation "JSON object containing server capabilities."
:accessor eglot--capabilities)
- (moribund
+ (shutdown-requested
:documentation "Flag set when server is shutting down."
- :accessor eglot--moribund)
+ :accessor eglot--shutdown-requested)
(project
:documentation "Project associated with server."
:initarg :project :accessor eglot--project)
@@ -204,15 +215,17 @@ function with the server still running."
(eglot--message "Asking %s politely to terminate" (jsonrpc-name server))
(unwind-protect
(progn
- (setf (eglot--moribund server) t)
+ (setf (eglot--shutdown-requested server) t)
(jsonrpc-request server :shutdown nil :timeout 3)
;; this one is supposed to always fail, hence ignore-errors
(ignore-errors (jsonrpc-request server :exit nil :timeout 1)))
;; Turn off `eglot--managed-mode' where appropriate.
(dolist (buffer (eglot--managed-buffers server))
(with-current-buffer buffer (eglot--managed-mode-onoff server -1)))
- (when (process-live-p (eglot--process server))
- (eglot--warn "Brutally deleting non-compliant server %s" (jsonrpc-name
server))
+ (while (progn (accept-process-output nil 0.1)
+ (not (eq (eglot--shutdown-requested server) :sentinel-done)))
+ (eglot--warn "Sentinel for %s still hasn't run, brutally deleting it!"
+ (eglot--process server))
(delete-process (eglot--process server)))))
(defun eglot--on-shutdown (server)
@@ -224,11 +237,12 @@ function with the server still running."
(maphash (lambda (_id watches)
(mapcar #'file-notify-rm-watch watches))
(eglot--file-watches server))
- ;; Sever the project/process relationship for proc
+ ;; Sever the project/server relationship for `server'
(setf (gethash (eglot--project server) eglot--servers-by-project)
(delq server
(gethash (eglot--project server) eglot--servers-by-project)))
- (cond ((eglot--moribund server))
+ (cond ((eglot--shutdown-requested server)
+ (setf (eglot--shutdown-requested server) :sentinel-done))
((not (eglot--inhibit-autoreconnect server))
(eglot--warn "Reconnecting after unexpected server exit.")
(eglot-reconnect server))
@@ -340,7 +354,7 @@ INTERACTIVE is t if called interactively."
interactive
(y-or-n-p "[eglot] Live process found, reconnect instead? "))
(eglot-reconnect current-server interactive)
- (when live-p (eglot-shutdown current-server))
+ (when live-p (ignore-errors (eglot-shutdown current-server)))
(let ((server (eglot--connect project
managed-major-mode
(format "%s/%s" nickname
managed-major-mode)
@@ -357,7 +371,7 @@ managing `%s' buffers in project `%s'."
INTERACTIVE is t if called interactively."
(interactive (list (jsonrpc-current-connection-or-lose) t))
(when (process-live-p (eglot--process server))
- (eglot-shutdown server interactive))
+ (ignore-errors (eglot-shutdown server interactive)))
(eglot--connect (eglot--project server)
(eglot--major-mode server)
(jsonrpc-name server)
@@ -395,22 +409,22 @@ And NICKNAME and CONTACT."
(jsonrpc-request
server
:initialize
- (jsonrpc-obj :processId (unless (eq (process-type
- (eglot--process server))
- 'network)
- (emacs-pid))
- :rootPath (expand-file-name
- (car (project-roots project)))
- :rootUri (eglot--path-to-uri
- (car (project-roots project)))
- :initializationOptions (eglot-initialization-options
server)
- :capabilities (eglot-client-capabilities server)))
+ (list :processId (unless (eq (process-type
+ (eglot--process server))
+ 'network)
+ (emacs-pid))
+ :rootPath (expand-file-name
+ (car (project-roots project)))
+ :rootUri (eglot--path-to-uri
+ (car (project-roots project)))
+ :initializationOptions (eglot-initialization-options server)
+ :capabilities (eglot-client-capabilities server)))
(setf (eglot--capabilities server) capabilities)
(setf (jsonrpc-status server) nil)
(dolist (buffer (buffer-list))
(with-current-buffer buffer
(eglot--maybe-activate-editing-mode server)))
- (jsonrpc-notify server :initialized (jsonrpc-obj :__dummy__ t))
+ (jsonrpc-notify server :initialized `(:__dummy__ t))
(setf (eglot--inhibit-autoreconnect server)
(cond
((booleanp eglot-autoreconnect) (not eglot-autoreconnect))
@@ -420,12 +434,11 @@ And NICKNAME and CONTACT."
(setf (eglot--inhibit-autoreconnect server)
(null eglot-autoreconnect)))))))
(setq success server))
- (unless (or success (not (process-live-p (eglot--process server)))
- (eglot--moribund server))
+ (unless (or success (not (process-live-p (eglot--process server))))
(eglot-shutdown server)))))
-;;; Helpers
+;;; Helpers (move these to API?)
;;;
(defun eglot--error (format &rest args)
"Error out with FORMAT with ARGS."
@@ -444,9 +457,9 @@ And NICKNAME and CONTACT."
(defun eglot--pos-to-lsp-position (&optional pos)
"Convert point POS to LSP position."
(save-excursion
- (jsonrpc-obj :line (1- (line-number-at-pos pos t)) ; F!@&#$CKING OFF-BY-ONE
- :character (- (goto-char (or pos (point)))
- (line-beginning-position)))))
+ (list :line (1- (line-number-at-pos pos t)) ; F!@&#$CKING OFF-BY-ONE
+ :character (- (goto-char (or pos (point)))
+ (line-beginning-position)))))
(defun eglot--lsp-position-to-point (pos-plist &optional marker)
"Convert LSP position POS-PLIST to Emacs point.
@@ -500,23 +513,30 @@ If optional MARKER, return a marker instead"
finally (cl-return (or probe t))))
(defun eglot--range-region (range &optional markers)
- "Return region (BEG END) that represents LSP RANGE.
+ "Return region (BEG . END) that represents LSP RANGE.
If optional MARKERS, make markers."
- (list (eglot--lsp-position-to-point (plist-get range :start) markers)
- (eglot--lsp-position-to-point (plist-get range :end) markers)))
+ (let* ((st (plist-get range :start))
+ (beg (eglot--lsp-position-to-point st markers))
+ (end (eglot--lsp-position-to-point (plist-get range :end) markers)))
+ ;; Fallback to `flymake-diag-region' if server botched the range
+ (if (/= beg end) (cons beg end) (flymake-diag-region
+ (current-buffer) (plist-get st :line)
+ (1- (plist-get st :character))))))
;;; Minor modes
;;;
(defvar eglot-mode-map (make-sparse-keymap))
+(defvar-local eglot--current-flymake-report-fn nil
+ "Current flymake report function for this buffer")
+
(define-minor-mode eglot--managed-mode
"Mode for source buffers managed by some EGLOT project."
nil nil eglot-mode-map
(cond
(eglot--managed-mode
(add-hook 'jsonrpc-find-connection-functions 'eglot--find-current-server
nil t)
- (add-hook 'jsonrpc-ready-predicates 'eglot--server-ready-p nil t)
(add-hook 'after-change-functions 'eglot--after-change nil t)
(add-hook 'before-change-functions 'eglot--before-change nil t)
(add-hook 'flymake-diagnostic-functions 'eglot-flymake-backend nil t)
@@ -531,7 +551,6 @@ If optional MARKERS, make markers."
(add-function :around (local imenu-create-index-function) #'eglot-imenu))
(t
(remove-hook 'jsonrpc-find-connection-functions
'eglot--find-current-server t)
- (remove-hook 'jsonrpc-ready-predicates 'eglot--server-ready-p t)
(remove-hook 'flymake-diagnostic-functions 'eglot-flymake-backend t)
(remove-hook 'after-change-functions 'eglot--after-change t)
(remove-hook 'before-change-functions 'eglot--before-change t)
@@ -543,7 +562,8 @@ If optional MARKERS, make markers."
(remove-hook 'completion-at-point-functions #'eglot-completion-at-point t)
(remove-function (local 'eldoc-documentation-function)
#'eglot-eldoc-function)
- (remove-function (local imenu-create-index-function) #'eglot-imenu))))
+ (remove-function (local imenu-create-index-function) #'eglot-imenu)
+ (setq eglot--current-flymake-report-fn nil))))
(defun eglot--managed-mode-onoff (server arg)
"Proxy for function `eglot--managed-mode' with ARG and SERVER."
@@ -557,9 +577,6 @@ If optional MARKERS, make markers."
(add-hook 'eglot--managed-mode-hook 'flymake-mode)
(add-hook 'eglot--managed-mode-hook 'eldoc-mode)
-(defvar-local eglot--current-flymake-report-fn nil
- "Current flymake report function for this buffer")
-
(defun eglot--find-current-server ()
"Find the current logical EGLOT server."
(let* ((probe (or (project-current) `(transient . ,default-directory))))
@@ -575,9 +592,7 @@ that case, also signal textDocument/didOpen."
(server (or (and (null server) cur) (and server (eq server cur)
cur))))
(when server
(eglot--managed-mode-onoff server 1)
- (eglot--signal-textDocument/didOpen)
- (flymake-start)
- (funcall (or eglot--current-flymake-report-fn #'ignore) nil))))
+ (eglot--signal-textDocument/didOpen))))
(add-hook 'find-file-hook 'eglot--maybe-activate-editing-mode)
@@ -622,27 +637,25 @@ Uses THING, FACE, DEFS and PREPEND."
`(,(eglot--mode-line-props "eglot" 'eglot-mode-line nil))
(when nick
`(":" ,(eglot--mode-line-props
- nick'eglot-mode-line
- '((mouse-1 eglot-events-buffer "go to events buffer")
+ nick 'eglot-mode-line
+ '((C-mouse-1 jsonrpc-stderr-buffer "go to stderr buffer")
+ (mouse-1 eglot-events-buffer "go to events buffer")
(mouse-2 eglot-shutdown "quit server")
(mouse-3 eglot-reconnect "reconnect to server")))
,@(when serious-p
`("/" ,(eglot--mode-line-props
"error" 'compilation-mode-line-fail
- '((mouse-1 eglot-events-buffer "go to events buffer")
- (mouse-3 jsonrpc-clear-status "clear this status"))
+ '((mouse-3 jsonrpc-clear-status "clear this status"))
(format "An error occured: %s\n" status))))
,@(when (and doing (not done-p))
`("/" ,(eglot--mode-line-props
(format "%s%s" doing
(if detail (format ":%s" detail) ""))
- 'compilation-mode-line-run
- '((mouse-1 eglot-events-buffer "go to events buffer")))))
+ 'compilation-mode-line-run '())))
,@(when (cl-plusp pending)
`("/" ,(eglot--mode-line-props
(format "%d oustanding requests" pending) 'warning
- '((mouse-1 eglot-events-buffer "go to events buffer")
- (mouse-3 jsonrpc-forget-pending-continuations
+ '((mouse-3 jsonrpc-forget-pending-continuations
"fahgettaboudit"))))))))))
(add-to-list 'mode-line-misc-info
@@ -674,10 +687,9 @@ Uses THING, FACE, DEFS and PREPEND."
'("OK"))
nil t (plist-get (elt actions 0) :title)))
(if reply
- (jsonrpc-reply server id :result (jsonrpc-obj :title reply))
+ (jsonrpc-reply server id :result `(:title ,reply))
(jsonrpc-reply server id
- :error (jsonrpc-obj :code -32800
- :message "User cancelled"))))))
+ :error `(:code -32800 :message "User cancelled"))))))
(cl-defmethod eglot-handle-notification
(_server (_method (eql :window/logMessage)) &key _type _message)
@@ -691,7 +703,7 @@ Uses THING, FACE, DEFS and PREPEND."
"Unreported diagnostics for this buffer.")
(cl-defmethod eglot-handle-notification
- (_server (_method (eql :textDocument/publishDiagnostics)) &key uri
diagnostics)
+ (server (_method (eql :textDocument/publishDiagnostics)) &key uri
diagnostics)
"Handle notification publishDiagnostics"
(if-let ((buffer (find-buffer-visiting (eglot--uri-to-path uri))))
(with-current-buffer buffer
@@ -700,7 +712,7 @@ Uses THING, FACE, DEFS and PREPEND."
collect (cl-destructuring-bind (&key range severity _group
_code source message)
diag-spec
- (pcase-let ((`(,beg ,end) (eglot--range-region range)))
+ (pcase-let ((`(,beg . ,end) (eglot--range-region range)))
(flymake-make-diagnostic (current-buffer)
beg end
(cond ((<= severity 1) :error)
@@ -712,8 +724,8 @@ Uses THING, FACE, DEFS and PREPEND."
(funcall eglot--current-flymake-report-fn diags)
(setq eglot--unreported-diagnostics nil))
(t
- (setq eglot--unreported-diagnostics diags)))))
- (eglot--warn "Diagnostics received for unvisited %s" uri)))
+ (setq eglot--unreported-diagnostics (cons t diags))))))
+ (jsonrpc--debug server "Diagnostics received for unvisited %s" uri)))
(cl-defun eglot--register-unregister (server jsonrpc-id things how)
"Helper for `registerCapability'.
@@ -729,7 +741,7 @@ THINGS are either registrations or unregisterations."
(jsonrpc-reply
server jsonrpc-id
:error `(:code -32601 :message ,(or (cadr retval)
"sorry")))))))))
- (jsonrpc-reply server jsonrpc-id :result (jsonrpc-obj :message "OK")))
+ (jsonrpc-reply server jsonrpc-id :result `(:message "OK")))
(cl-defmethod eglot-handle-request
(server id (_method (eql :client/registerCapability)) &key registrations)
@@ -750,52 +762,44 @@ THINGS are either registrations or unregisterations."
(jsonrpc-reply server id :result `(:applied )))
(error (jsonrpc-reply server id
:result `(:applied :json-false)
- :error (eglot--obj :code -32001
- :message (format "%s" err))))))
+ :error `(:code -32001 :message (format "%s"
,err))))))
(defun eglot--TextDocumentIdentifier ()
"Compute TextDocumentIdentifier object for current buffer."
- (jsonrpc-obj :uri (eglot--path-to-uri buffer-file-name)))
+ `(:uri ,(eglot--path-to-uri buffer-file-name)))
(defvar-local eglot--versioned-identifier 0)
(defun eglot--VersionedTextDocumentIdentifier ()
"Compute VersionedTextDocumentIdentifier object for current buffer."
(append (eglot--TextDocumentIdentifier)
- (jsonrpc-obj :version eglot--versioned-identifier)))
+ `(:version ,eglot--versioned-identifier)))
(defun eglot--TextDocumentItem ()
"Compute TextDocumentItem object for current buffer."
(append
(eglot--VersionedTextDocumentIdentifier)
- (jsonrpc-obj :languageId
- (if (string-match "\\(.*\\)-mode" (symbol-name major-mode))
- (match-string 1 (symbol-name major-mode))
- "unknown")
- :text
- (save-restriction
- (widen)
- (buffer-substring-no-properties (point-min) (point-max))))))
+ (list :languageId
+ (if (string-match "\\(.*\\)-mode" (symbol-name major-mode))
+ (match-string 1 (symbol-name major-mode))
+ "unknown")
+ :text
+ (eglot--widening
+ (buffer-substring-no-properties (point-min) (point-max))))))
(defun eglot--TextDocumentPositionParams ()
"Compute TextDocumentPositionParams."
- (jsonrpc-obj :textDocument (eglot--TextDocumentIdentifier)
- :position (eglot--pos-to-lsp-position)))
+ (list :textDocument (eglot--TextDocumentIdentifier)
+ :position (eglot--pos-to-lsp-position)))
(defvar-local eglot--recent-changes nil
"Recent buffer changes as collected by `eglot--before-change'.")
-(defun eglot--outstanding-edits-p ()
- "Non-nil if there are outstanding edits."
- (cl-plusp (+ (length (car eglot--recent-changes))
- (length (cdr eglot--recent-changes)))))
-
(cl-defmethod jsonrpc-connection-ready-p ((_server eglot-lsp-server) _what)
- "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."
- (and (cl-call-next-method)
- (not (eglot--outstanding-edits-p))))
+ "Tell if SERVER is ready for WHAT in current buffer."
+ (and (cl-call-next-method) (not eglot--recent-changes)))
+
+(defvar-local eglot--change-idle-timer nil "Idle timer for didChange signals.")
(defun eglot--before-change (start end)
"Hook onto `before-change-functions'.
@@ -803,23 +807,32 @@ Records START and END, crucially convert them into
LSP (line/char) positions before that information is
lost (because the after-change thingy doesn't know if newlines
were deleted/added)"
- (setf (car eglot--recent-changes)
- (vconcat (car eglot--recent-changes)
- `[(,(eglot--pos-to-lsp-position start)
- ,(eglot--pos-to-lsp-position end))])))
+ (when (listp eglot--recent-changes)
+ (push `(,(eglot--pos-to-lsp-position start)
+ ,(eglot--pos-to-lsp-position end))
+ eglot--recent-changes)))
(defun eglot--after-change (start end pre-change-length)
"Hook onto `after-change-functions'.
Records START, END and PRE-CHANGE-LENGTH locally."
(cl-incf eglot--versioned-identifier)
- (setf (cdr eglot--recent-changes)
- (vconcat (cdr eglot--recent-changes)
- `[(,pre-change-length
- ,(buffer-substring-no-properties start end))])))
+ (if (and (listp eglot--recent-changes)
+ (null (cddr (car eglot--recent-changes))))
+ (setf (cddr (car eglot--recent-changes))
+ `(,pre-change-length ,(buffer-substring-no-properties start end)))
+ (setf eglot--recent-changes :emacs-messup))
+ (when eglot--change-idle-timer (cancel-timer eglot--change-idle-timer))
+ (let ((buf (current-buffer)))
+ (setq eglot--change-idle-timer
+ (run-with-idle-timer
+ 0.5 nil (lambda () (eglot--with-live-buffer buf
+ (when eglot--managed-mode
+ (eglot--signal-textDocument/didChange)
+ (setq eglot--change-idle-timer nil))))))))
;; HACK! 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 `jsonrpc-ready-predicates'.
+;; chance to run, because `jsonrpc-connection-ready-p'.
(advice-add #'jsonrpc-request :before
(cl-function (lambda (_proc _method _params &key deferred _timeout)
(when (and eglot--managed-mode deferred)
@@ -828,40 +841,30 @@ Records START, END and PRE-CHANGE-LENGTH locally."
(defun eglot--signal-textDocument/didChange ()
"Send textDocument/didChange to server."
- (when (eglot--outstanding-edits-p)
+ (when eglot--recent-changes
(let* ((server (jsonrpc-current-connection-or-lose))
(sync-kind (eglot--server-capable :textDocumentSync))
- (emacs-messup (/= (length (car eglot--recent-changes))
- (length (cdr eglot--recent-changes))))
- (full-sync-p (or (eq sync-kind 1) emacs-messup)))
- (when emacs-messup
- (eglot--warn "`eglot--recent-changes' messup: %s"
eglot--recent-changes))
- (save-restriction
- (widen)
- (jsonrpc-notify
- server :textDocument/didChange
- (jsonrpc-obj
- :textDocument
- (eglot--VersionedTextDocumentIdentifier)
- :contentChanges
- (if full-sync-p (vector
- (jsonrpc-obj
- :text (buffer-substring-no-properties (point-min)
-
(point-max))))
- (cl-loop for (start-pos end-pos) across (car eglot--recent-changes)
- for (len after-text) across (cdr eglot--recent-changes)
- vconcat `[,(jsonrpc-obj :range (jsonrpc-obj :start
start-pos
- :end end-pos)
- :rangeLength len
- :text after-text)])))))
- (setq eglot--recent-changes (cons [] []))
+ (full-sync-p (or (eq sync-kind 1)
+ (eq :emacs-messup eglot--recent-changes))))
+ (jsonrpc-notify
+ server :textDocument/didChange
+ (list
+ :textDocument (eglot--VersionedTextDocumentIdentifier)
+ :contentChanges
+ (if full-sync-p
+ (vector `(:text ,(eglot--widening
+ (buffer-substring-no-properties (point-min)
+ (point-max)))))
+ (cl-loop for (beg end len text) in (reverse eglot--recent-changes)
+ vconcat `[,(list :range `(:start ,beg :end ,end)
+ :rangeLength len :text text)]))))
+ (setq eglot--recent-changes nil)
(setf (eglot--spinner server) (list nil :textDocument/didChange t))
- ;; HACK! perhaps jsonrpc should just call this on every send
(jsonrpc--call-deferred server))))
(defun eglot--signal-textDocument/didOpen ()
"Send textDocument/didOpen to server."
- (setq eglot--recent-changes (cons [] []))
+ (setq eglot--recent-changes nil eglot--versioned-identifier 0)
(jsonrpc-notify
(jsonrpc-current-connection-or-lose)
:textDocument/didOpen `(:textDocument ,(eglot--TextDocumentItem))))
@@ -888,7 +891,7 @@ Records START, END and PRE-CHANGE-LENGTH locally."
(jsonrpc-notify
(jsonrpc-current-connection-or-lose)
:textDocument/didSave
- (jsonrpc-obj
+ (list
;; TODO: Handle TextDocumentSaveRegistrationOptions to control this.
:text (buffer-substring-no-properties (point-min) (point-max))
:textDocument (eglot--TextDocumentIdentifier))))
@@ -899,10 +902,8 @@ Calls REPORT-FN maybe if server publishes diagnostics in
time."
(setq eglot--current-flymake-report-fn report-fn)
;; Report anything unreported
(when eglot--unreported-diagnostics
- (funcall report-fn eglot--unreported-diagnostics)
- (setq eglot--unreported-diagnostics nil))
- ;; Signal a didChange that might eventually bring new diagnotics
- (eglot--signal-textDocument/didChange))
+ (funcall report-fn (cdr eglot--unreported-diagnostics))
+ (setq eglot--unreported-diagnostics nil)))
(defun eglot-xref-backend ()
"EGLOT xref backend."
@@ -938,17 +939,16 @@ DUMMY is ignored"
(&key name kind location containerName)
(propertize name
:textDocumentPositionParams
- (jsonrpc-obj :textDocument text-id
- :position (plist-get
- (plist-get location
:range)
- :start))
+ (list :textDocument text-id
+ :position (plist-get
+ (plist-get location :range)
+ :start))
:locations (list location)
:kind kind
:containerName containerName))
(jsonrpc-request server
:textDocument/documentSymbol
- (jsonrpc-obj
- :textDocument text-id))))
+ `(:textDocument ,text-id))))
(all-completions string eglot--xref-known-symbols))))))
(cl-defmethod xref-backend-identifier-at-point ((_backend (eql eglot)))
@@ -987,8 +987,8 @@ DUMMY is ignored"
:textDocument/references
(append
params
- (jsonrpc-obj :context
- (jsonrpc-obj :includeDeclaration t)))))))
+ (list :context
+ (list :includeDeclaration t)))))))
(cl-defmethod xref-backend-apropos ((_backend (eql eglot)) pattern)
(when (eglot--server-capable :workspaceSymbolProvider)
@@ -998,7 +998,7 @@ DUMMY is ignored"
(eglot--xref-make name uri (plist-get range :start))))
(jsonrpc-request (jsonrpc-current-connection-or-lose)
:workspace/symbol
- (jsonrpc-obj :query pattern)))))
+ `(:query ,pattern)))))
(defun eglot-completion-at-point ()
"EGLOT's `completion-at-point' function."
@@ -1055,11 +1055,12 @@ DUMMY is ignored"
(defvar eglot--highlights nil "Overlays for textDocument/documentHighlight.")
(defun eglot--hover-info (contents &optional range)
- (concat (and range (pcase-let ((`(,beg ,end) (eglot--range-region range)))
- (concat (buffer-substring beg end) ": ")))
- (mapconcat #'eglot--format-markup
- (append (cond ((vectorp contents) contents)
- (contents (list contents)))) "\n")))
+ (let ((heading (and range (pcase-let ((`(,beg . ,end) (eglot--range-region
range)))
+ (concat (buffer-substring beg end) ": "))))
+ (body (mapconcat #'eglot--format-markup
+ (append (cond ((vectorp contents) contents)
+ ((stringp contents) (list contents))))
"\n")))
+ (when (or heading (cl-plusp (length body))) (concat heading body))))
(defun eglot--sig-info (sigs active-sig active-param)
(cl-loop
@@ -1090,9 +1091,9 @@ DUMMY is ignored"
(jsonrpc-request (jsonrpc-current-connection-or-lose) :textDocument/hover
(eglot--TextDocumentPositionParams))
(when (seq-empty-p contents) (eglot--error "No hover info here"))
- (with-help-window "*eglot help*"
- (with-current-buffer standard-output
- (insert (eglot--hover-info contents range))))))
+ (let ((blurb (eglot--hover-info contents range)))
+ (with-help-window "*eglot help*"
+ (with-current-buffer standard-output (insert blurb))))))
(defun eglot-eldoc-function ()
"EGLOT's `eldoc-documentation-function' function.
@@ -1102,8 +1103,9 @@ If SKIP-SIGNATURE, don't try to send
textDocument/signatureHelp."
(position-params (eglot--TextDocumentPositionParams))
sig-showing)
(cl-macrolet ((when-buffer-window
- (&body body) `(when (get-buffer-window buffer)
- (with-current-buffer buffer ,@body))))
+ (&body body) ; notice the exception when testing with `ert'
+ `(when (or (get-buffer-window buffer) (ert-running-test))
+ (with-current-buffer buffer ,@body))))
(when (eglot--server-capable :signatureHelpProvider)
(jsonrpc-async-request
server :textDocument/signatureHelp position-params
@@ -1122,10 +1124,10 @@ If SKIP-SIGNATURE, don't try to send
textDocument/signatureHelp."
server :textDocument/hover position-params
:success-fn (jsonrpc-lambda (&key contents range)
(unless sig-showing
- ;; for eglot-tests.el's sake, set this unconditionally
- (setq eldoc-last-message
- (eglot--hover-info contents range))
- (when-buffer-window (eldoc-message
eldoc-last-message))))
+ (when-buffer-window
+ (when-let (info (eglot--hover-info contents range))
+ (eglot--message "OK so info is %S and %S" info
(null info))
+ (eldoc-message info)))))
:deferred :textDocument/hover))
(when (eglot--server-capable :documentHighlightProvider)
(jsonrpc-async-request
@@ -1136,7 +1138,7 @@ If SKIP-SIGNATURE, don't try to send
textDocument/signatureHelp."
(setq eglot--highlights
(when-buffer-window
(mapcar
- (jsonrpc-lambda (&key range _kind)
+ (jsonrpc-lambda (&key range _kind _role)
(pcase-let ((`(,beg ,end)
(eglot--range-region range)))
(let ((ov (make-overlay beg end)))
@@ -1159,8 +1161,7 @@ If SKIP-SIGNATURE, don't try to send
textDocument/signatureHelp."
(plist-get (plist-get location :range) :start))))
(jsonrpc-request (jsonrpc-current-connection-or-lose)
:textDocument/documentSymbol
- (jsonrpc-obj
- :textDocument
(eglot--TextDocumentIdentifier))))))
+ `(:textDocument
,(eglot--TextDocumentIdentifier))))))
(append
(seq-group-by (lambda (e) (get-text-property 0 :kind (car e)))
entries)
@@ -1172,51 +1173,40 @@ If SKIP-SIGNATURE, don't try to send
textDocument/signatureHelp."
(unless (or (not version) (equal version eglot--versioned-identifier))
(eglot--error "Edits on `%s' require version %d, you have %d"
(current-buffer) version eglot--versioned-identifier))
- (save-restriction
- (widen)
- (save-excursion
- (mapc (jsonrpc-lambda (newText beg end)
- (goto-char beg) (delete-region beg end) (insert newText))
- (mapcar (jsonrpc-lambda (&key range newText)
- (cons newText (eglot--range-region range 'markers)))
- edits))))
+ (eglot--widening
+ (mapc (pcase-lambda (`(,newText ,beg . ,end))
+ (goto-char beg) (delete-region beg end) (insert newText))
+ (mapcar (jsonrpc-lambda (&key range newText)
+ (cons newText (eglot--range-region range 'markers)))
+ edits)))
(eglot--message "%s: Performed %s edits" (current-buffer) (length edits)))
(defun eglot--apply-workspace-edit (wedit &optional confirm)
"Apply the workspace edit WEDIT. If CONFIRM, ask user first."
- (let (prepared)
- (cl-destructuring-bind (&key changes documentChanges)
- wedit
- (cl-loop
- for change on documentChanges
- do (push (cl-destructuring-bind (&key textDocument edits) change
- (cl-destructuring-bind (&key uri version) textDocument
- (list (eglot--uri-to-path uri) edits version)))
- prepared))
+ (cl-destructuring-bind (&key changes documentChanges) wedit
+ (let ((prepared
+ (mapcar (jsonrpc-lambda (&key textDocument edits)
+ (cl-destructuring-bind (&key uri version) textDocument
+ (list (eglot--uri-to-path uri) edits version)))
+ documentChanges)))
(cl-loop for (uri edits) on changes by #'cddr
- do (push (list (eglot--uri-to-path uri) edits) prepared)))
- (if (or confirm
- (cl-notevery #'find-buffer-visiting
- (mapcar #'car prepared)))
- (unless (y-or-n-p
- (format "[eglot] Server requests to edit %s files.\n %s\n\
-Proceed? "
- (length prepared)
- (mapconcat #'identity
- (mapcar #'car prepared)
- "\n ")))
- (eglot--error "User cancelled server edit")))
- (unwind-protect
- (let (edit)
- (while (setq edit (car prepared))
- (cl-destructuring-bind (path edits &optional version) edit
- (with-current-buffer (find-file-noselect path)
- (eglot--apply-text-edits edits version))
- (pop prepared))))
- (if prepared
- (eglot--warn "Caution: edits of files %s failed."
- (mapcar #'car prepared))
- (eglot--message "Edit successful!")))))
+ do (push (list (eglot--uri-to-path uri) edits) prepared))
+ (if (or confirm
+ (cl-notevery #'find-buffer-visiting
+ (mapcar #'car prepared)))
+ (unless (y-or-n-p
+ (format "[eglot] Server wants to edit:\n %s\n Proceed? "
+ (mapconcat #'identity (mapcar #'car prepared) "\n
")))
+ (eglot--error "User cancelled server edit")))
+ (unwind-protect
+ (let (edit) (while (setq edit (car prepared))
+ (cl-destructuring-bind (path edits &optional version)
edit
+ (with-current-buffer (find-file-noselect path)
+ (eglot--apply-text-edits edits version))
+ (pop prepared))))
+ (if prepared (eglot--warn "Caution: edits of files %s failed."
+ (mapcar #'car prepared))
+ (eglot--message "Edit successful!"))))))
(defun eglot-rename (newname)
"Rename the current symbol to NEWNAME."
@@ -1227,7 +1217,7 @@ Proceed? "
(eglot--apply-workspace-edit
(jsonrpc-request (jsonrpc-current-connection-or-lose)
:textDocument/rename
`(,@(eglot--TextDocumentPositionParams)
- ,@(jsonrpc-obj :newName newname)))
+ :newName ,newname))
current-prefix-arg))
@@ -1290,12 +1280,35 @@ Proceed? "
((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)
- (dolist (buffer (eglot--managed-buffers server))
- (with-current-buffer buffer
- (funcall (or eglot--current-flymake-report-fn #'ignore)
- eglot--unreported-diagnostics)))))
+ (setf (eglot--spinner server) (list id title done message)))
+
+
+;;; cquery-specific
+;;;
+(defclass eglot-cquery (eglot-lsp-server) ()
+ :documentation "cquery's C/C++ langserver.")
+
+(cl-defmethod eglot-initialization-options ((server eglot-cquery))
+ "Passes through required cquery initialization options"
+ (let* ((root (car (project-roots (eglot--project server))))
+ (cache (expand-file-name ".cquery_cached_index/" root)))
+ (vector :cacheDirectory (file-name-as-directory cache)
+ :progressReportFrequencyMs -1)))
+
+(cl-defmethod eglot-handle-notification
+ ((_server eglot-cquery) (_method (eql :$cquery/progress))
+ &rest counts &key _activeThreads &allow-other-keys)
+ "No-op for noisy $cquery/progress extension")
+
+(cl-defmethod eglot-handle-notification
+ ((_server eglot-cquery) (_method (eql :$cquery/setInactiveRegions))
+ &key _uri _inactiveRegions &allow-other-keys)
+ "No-op for unsupported $cquery/setInactiveRegions extension")
+
+(cl-defmethod eglot-handle-notification
+ ((_server eglot-cquery) (_method (eql :$cquery/publishSemanticHighlighting))
+ &key _uri _symbols &allow-other-keys)
+ "No-op for unsupported $cquery/publishSemanticHighlighting extension")
(provide 'eglot)
;;; eglot.el ends here
diff --git a/jsonrpc.el b/jsonrpc.el
index 5a869aa..2bcff2e 100644
--- a/jsonrpc.el
+++ b/jsonrpc.el
@@ -162,6 +162,11 @@ FORMAT as the message."
"Message out with FORMAT with ARGS."
(message "[jsonrpc] %s" (concat "[jsonrpc] %s" (apply #'format format
args))))
+(defun jsonrpc--debug (server format &rest args)
+ "Debug message for SERVER with FORMAT and ARGS."
+ (jsonrpc-log-event
+ server (if (stringp format)`(:message ,(format format args)) format)))
+
(defun jsonrpc-warn (format &rest args)
"Warning message with FORMAT and ARGS."
(apply #'jsonrpc-message (concat "(warning) " format) args)
@@ -199,7 +204,9 @@ FORMAT as the message."
(-deferred-actions
:initform (make-hash-table :test #'equal)
:accessor jsonrpc--deferred-actions
- :documentation "Actions deferred to when server is thought to be ready.")))
+ :documentation "Map (DEFERRED BUF) to (FN TIMER ID). FN is\
+a saved DEFERRED `async-request' from BUF, to be sent not later\
+than TIMER as ID.")))
(defclass jsonrpc-process-connection (jsonrpc-connection)
((-process
@@ -268,6 +275,7 @@ If successful, `jsonrpc-connect' returns a
endpoint."
(let* ((readable-name (format "JSON-RPC server (%s)" name))
(buffer (get-buffer-create (format "*%s output*" readable-name)))
+ (stderr)
(original-contact contact)
(connection
(cond
@@ -288,10 +296,12 @@ endpoint."
:command contact
:connection-type 'pipe
:coding 'no-conversion
- :stderr (get-buffer-create
- (format "*%s stderr*" name)))))))))
+ :stderr (setq stderr
+ (get-buffer-create
+ (format "*%s stderr*"
name))))))))))
(proc (jsonrpc--process connection)))
(set-process-buffer proc buffer)
+ (process-put proc 'jsonrpc-stderr stderr)
(set-marker (process-mark proc) (with-current-buffer buffer (point-min)))
(set-process-filter proc #'jsonrpc--process-filter)
(set-process-sentinel proc #'jsonrpc--process-sentinel)
@@ -308,7 +318,7 @@ endpoint."
(defun jsonrpc--process-sentinel (proc change)
"Called when PROC undergoes CHANGE."
(let ((connection (process-get proc 'jsonrpc-connection)))
- (jsonrpc-log-event connection `(:message "Connection state changed"
:change ,change))
+ (jsonrpc--debug connection `(:message "Connection state changed" :change
,change))
(when (not (process-live-p proc))
(with-current-buffer (jsonrpc-events-buffer connection)
(let ((inhibit-read-only t))
@@ -325,12 +335,8 @@ endpoint."
(funcall error `(:code -1 :message "Server died"))))
(jsonrpc--request-continuations connection))
(jsonrpc-message "Server exited with status %s" (process-exit-status
proc))
- (unwind-protect
- (funcall (jsonrpc--on-shutdown connection) connection))
- (when (process-live-p proc)
- (jsonrpc-warn "Brutally deleting non-compliant %s"
- (jsonrpc-name connection))
- (delete-process proc))))))
+ (delete-process proc)
+ (funcall (jsonrpc--on-shutdown connection) connection)))))
(defun jsonrpc--process-filter (proc string)
"Called when new data STRING has arrived for PROC."
@@ -421,6 +427,12 @@ INTERACTIVE is t if called interactively."
(when interactive (display-buffer buffer))
buffer))
+(defun jsonrpc-stderr-buffer (connection)
+ "Pop to stderr of CONNECTION, if it exists, else error."
+ (interactive (list (jsonrpc-current-connection-or-lose)))
+ (if-let ((b (process-get (jsonrpc--process connection) 'jsonrpc-stderr)))
+ (pop-to-buffer b) (user-error "[eglot] No stderr buffer!")))
+
(defun jsonrpc-log-event (connection message &optional type)
"Log an jsonrpc-related event.
CONNECTION is the current connection. MESSAGE is a JSON-like
@@ -501,12 +513,6 @@ originated."
json))
(jsonrpc-log-event connection message 'client)))
-(defvar jsonrpc--next-request-id 0)
-
-(defun jsonrpc--next-request-id ()
- "Compute the next id for a client request."
- (setq jsonrpc--next-request-id (1+ jsonrpc--next-request-id)))
-
(defun jsonrpc-forget-pending-continuations (connection)
"Stop waiting for responses from the current JSONRPC CONNECTION."
(interactive (list (jsonrpc-current-connection-or-lose)))
@@ -520,7 +526,7 @@ originated."
(defun jsonrpc--call-deferred (connection)
"Call CONNECTION's deferred actions, who may again defer themselves."
(when-let ((actions (hash-table-values (jsonrpc--deferred-actions
connection))))
- (jsonrpc-log-event connection `(:running-deferred ,(length actions)))
+ (jsonrpc--debug connection `(:maybe-run-deferred ,(mapcar #'caddr
actions)))
(mapc #'funcall (mapcar #'car actions))))
(cl-defgeneric jsonrpc-connection-ready-p (connection what) ;; API
@@ -538,6 +544,9 @@ for sending requests immediately."
(defconst jrpc-default-request-timeout 10
"Time in seconds before timing out a JSONRPC request.")
+(defvar-local jsonrpc--next-request-id 0)
+
+
(cl-defun jsonrpc-async-request (connection
method
params
@@ -559,9 +568,9 @@ ERROR-FN and TIMEOUT-FN simply log the events into
If DEFERRED is non-nil, maybe defer the request to a future time
when the server is thought to be ready according to
-`jsonrpc-ready-predicates' (which see). The request might never be
-sent at all, in case it is overridden in the meantime by a new
-request with identical DEFERRED and for the same buffer.
+`jsonrpc-connection-ready-p' (which see). The request might
+never be sent at all, in case it is overridden in the meantime by
+a new request with identical DEFERRED and for the same buffer.
However, in that situation, the original timeout is kept.
Returns nil."
@@ -580,43 +589,40 @@ Returns nil."
Return a list (ID TIMER). ID is the new request's ID, or nil if
the request was deferred. TIMER is a timer object set (or nil, if
TIMEOUT is nil)."
- (let* ((id (jsonrpc--next-request-id))
- (timer nil)
- (make-timer
- (lambda ( )
- (or timer
- (when timeout
- (run-with-timer
- timeout nil
- (lambda ()
- (remhash id (jsonrpc--request-continuations connection))
- (funcall (or timeout-fn
- (lambda ()
- (jsonrpc-log-event
- connection `(:timed-out ,method :id ,id
- :params
,params))))))))))))
+ (pcase-let* ((buf (current-buffer)) (point (point))
+ (`(,_ ,timer ,old-id)
+ (and deferred (gethash (list deferred buf)
+ (jsonrpc--deferred-actions
connection))))
+ (id (or old-id (cl-incf jsonrpc--next-request-id)))
+ (make-timer
+ (lambda ( )
+ (when timeout
+ (run-with-timer
+ timeout nil
+ (lambda ()
+ (remhash id (jsonrpc--request-continuations connection))
+ (if timeout-fn (funcall timeout-fn)
+ (jsonrpc--debug
+ connection `(:timed-out ,method :id ,id
+ :params ,params)))))))))
(when deferred
- (let* ((buf (current-buffer))
- (existing (gethash (list deferred buf)
- (jsonrpc--deferred-actions connection))))
- (when existing (setq timer (cadr existing)))
- (if (jsonrpc-connection-ready-p connection deferred)
- (remhash (list deferred buf) (jsonrpc--deferred-actions
connection))
- (jsonrpc-log-event connection `(:deferring ,method :id ,id :params
- ,params))
- (let* ((buf (current-buffer)) (point (point))
- (later (lambda ()
- (when (buffer-live-p buf)
- (with-current-buffer buf
- (save-excursion (goto-char point)
- (apply #'jsonrpc-async-request
- connection
- method params args)))))))
- (puthash (list deferred buf)
- (list later (setq timer (funcall make-timer)))
- (jsonrpc--deferred-actions connection))
- ;; Non-local exit!
- (cl-return-from jsonrpc--async-request-1 (list nil timer))))))
+ (if (jsonrpc-connection-ready-p connection deferred)
+ ;; Server is ready, we jump below and send it immediately.
+ (remhash (list deferred buf) (jsonrpc--deferred-actions connection))
+ ;; Otherwise, save in `eglot--deferred-actions' and exit non-locally
+ (unless old-id
+ (jsonrpc--debug connection `(:deferring ,method :id ,id :params
+ ,params)))
+ (puthash (list deferred buf)
+ (list (lambda ()
+ (when (buffer-live-p buf)
+ (with-current-buffer buf
+ (save-excursion (goto-char point)
+ (apply #'jsonrpc-async-request
+ connection
+ method params args)))))
+ (or timer (funcall make-timer)) id)
+ (jsonrpc--deferred-actions connection))))
;; Really send it
;;
(jsonrpc-connection-send connection (jsonrpc-obj :jsonrpc "2.0"
@@ -626,12 +632,12 @@ TIMEOUT is nil)."
(puthash id
(list (or success-fn
(jsonrpc-lambda (&rest _ignored)
- (jsonrpc-log-event
+ (jsonrpc--debug
connection (jsonrpc-obj :message "success ignored"
:id id))))
(or error-fn
(jsonrpc-lambda (&key code message &allow-other-keys)
(setf (jsonrpc-status connection) `(,message t))
- (jsonrpc-log-event
+ (jsonrpc--debug
connection (jsonrpc-obj :message "error ignored,
status set"
:id id :error code))))
(setq timer (funcall make-timer)))
- [elpa] externals/eglot 6531c8b 58/69: Merge branch 'master' into jsonrpc-refactor, (continued)
- [elpa] externals/eglot 6531c8b 58/69: Merge branch 'master' into jsonrpc-refactor, João Távora, 2018/06/22
- [elpa] externals/eglot 59cc3fb 61/69: jsonrpc-connection-receive is now a public convenience function, João Távora, 2018/06/22
- [elpa] externals/eglot d371f05 49/69: Request dispatcher's return value determines response, João Távora, 2018/06/22
- [elpa] externals/eglot 0f20fdf 68/69: Tiny README.md change, João Távora, 2018/06/22
- [elpa] externals/eglot cef3c29 22/69: Heroically merge master into jsonrpc-refactor (using imerge), João Távora, 2018/06/22
- [elpa] externals/eglot a4441c6 37/69: Merge master into jsonrpc-refactor (using imerge), João Távora, 2018/06/22
- [elpa] externals/eglot 0e44b27 27/69: jsonrpc.el uses classes and generic functions, João Távora, 2018/06/22
- [elpa] externals/eglot 856a224 62/69: Simplify jsonrpc-connection-send, João Távora, 2018/06/22
- [elpa] externals/eglot 1f09fd3 59/69: Review commentary section before another review cycle, João Távora, 2018/06/22
- [elpa] externals/eglot 8fda30c 67/69: Merge master into jsonrpc-refactor (using imerge), João Távora, 2018/06/22
- [elpa] externals/eglot 7f4e273 31/69: Merge master into jsonrpc-refactor (using imerge),
João Távora <=
- [elpa] externals/eglot 4525eca 43/69: Support json.c. API purely based on classes, João Távora, 2018/06/22
- [elpa] externals/eglot bb60c0c 21/69: Rename jrpc.el to jsonrpc.el, João Távora, 2018/06/22
- [elpa] externals/eglot 46e6107 54/69: Reshuffle definitions inside jsonrpc.el, João Távora, 2018/06/22
- [elpa] externals/eglot 6f1ecc6 28/69: Merge branch use-eieio-server-defclass into jsonrpc-refactor, João Távora, 2018/06/22
- [elpa] externals/eglot 10559a5 56/69: Shuffle definitions around again, João Távora, 2018/06/22
- [elpa] externals/eglot b3c8b59 02/69: Refactor JSON-RPC lib jrpc.el from eglot.el, João Távora, 2018/06/22
- [elpa] externals/eglot 1ec47fb 51/69: Remove connection grabbing antics from jsonrpc.el, João Távora, 2018/06/22
- [elpa] externals/eglot f385d9c 69/69: Merge branch 'jsonrpc-refactor', bump version to 1.0, João Távora, 2018/06/22