emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/eglot 4525eca 43/69: Support json.c. API purely based o


From: João Távora
Subject: [elpa] externals/eglot 4525eca 43/69: Support json.c. API purely based on classes
Date: Fri, 22 Jun 2018 11:55:01 -0400 (EDT)

branch: externals/eglot
commit 4525eca32a10046484b5a6dd1f0647927aae2dd6
Author: João Távora <address@hidden>
Commit: João Távora <address@hidden>

    Support json.c.  API purely based on classes
    
    No more jsonrpc-connect.
    
    This is a big commit because of a data loss problem. It should be at
    least two separate commits (json.c-support and new API)
    
    * eglot.el (eglot-server-programs): Rework docstring.
    (eglot-handle-request): Don't take ID param
    (eglot-lsp-server): No more initargs.
    (eglot--interactive): Return 5 args.
    (eglot): Take 5 args.
    (eglot-reconnect): Pass 6 args to eglot--connect.
    (eglot--dispatch): Remove.
    (eglot--connect): Take 6 args. Rework.
    (eglot-handle-notification): Change all specializations
    to use a non-keyword symbol spec.
    (eglot-handle-request): Remove ID param from all
    specializations.  Don't pass ID to jsonrpc-reply.
    (eglot--register-unregister): Don't take JSONRPC-ID arg.
    Don't pass ID to jsonrpc-reply.
    
    * jsonrpc-tests.el (returns-3, signals-an--32603-JSONRPC-error)
    (times-out, stretching-it-but-works)
    (json-el-cant-serialize-this, jsonrpc-connection-ready-p)
    (deferred-action-intime, deferred-action-toolate)
    (deferred-action-timeout): Pass JSON objects compatible with
    json.c
    (jsonrpc--test-client, jsonrpc--test-endpoint): New classes
    (jsonrpc--with-emacsrpc-fixture): Don't use jsonrpc-connect.
    (jsonrpc-connection-ready-p): Update signature.
    
    * jsonrpc.el: Rewrite commentary.
    (jsonrpc-connection): Rework class.
    (jsonrpc-process-connection): Rework class.
    (initialize-instance): New methods..
    (jsonrpc--json-read, jsonrpc--json-encode): Reindent.
    (jsonrpc-connect): Delete.
    (jsonrpc--json-read, jsonrpc--json-encode):
    New functions for working with json.c
    (jsonrpc--process-filter): Call them.
    (jsonrpc--unanswered-request-id): New variable.
    (jsonrpc--connection-receive): Use jsonrpc--unanswered-request-id
    (jsonrpc-connection-send): Take keyword params to build message
    instead of message.
    (jsonrpc-notify, jsonrpc--async-request-1): Use new
    jsonrpc-connection-send.
    (jsonrpc-reply): Simplify.
    
    * eglot-tests.el (rls-watches-files, rls-basic-diagnostics)
    (rls-hover-after-edit): Correctly compare using string= and
    non-keyword symbols.
---
 eglot-tests.el   |   8 +-
 eglot.el         | 171 ++++++++++++----------
 jsonrpc-tests.el |  65 +++++----
 jsonrpc.el       | 429 ++++++++++++++++++++++++-------------------------------
 4 files changed, 324 insertions(+), 349 deletions(-)

diff --git a/eglot-tests.el b/eglot-tests.el
index af8cc37..bd9e2f3 100644
--- a/eglot-tests.el
+++ b/eglot-tests.el
@@ -250,7 +250,7 @@ Pass TIMEOUT to `eglot--with-timeout'."
               (eglot--wait-for (s-requests 1)
                   (&key id method &allow-other-keys)
                 (setq register-id id)
-                (string= method "client/registerCapability"))
+                (string= method 'client/registerCapability))
               (eglot--wait-for (c-replies 1)
                   (&key id error &allow-other-keys)
                 (and (eq id register-id) (null error))))
@@ -258,7 +258,7 @@ Pass TIMEOUT to `eglot--with-timeout'."
             (eglot--wait-for
                 (c-notifs 3 "waiting for didChangeWatchedFiles notification")
                 (&key method params &allow-other-keys)
-              (and (eq method :workspace/didChangeWatchedFiles)
+              (and (string= method 'workspace/didChangeWatchedFiles)
                    (cl-destructuring-bind (&key uri type)
                        (elt (plist-get params :changes) 0)
                      (and (string= (eglot--path-to-uri "Cargo.toml") uri)
@@ -278,7 +278,7 @@ Pass TIMEOUT to `eglot--with-timeout'."
           (apply #'eglot (eglot--interactive))
           (eglot--wait-for (s-notifs 1)
               (&key _id method &allow-other-keys)
-            (string= method "textDocument/publishDiagnostics"))
+            (string= method 'textDocument/publishDiagnostics))
           (flymake-start)
           (goto-char (point-min))
           (flymake-goto-next-error 1 '() t)
@@ -318,7 +318,7 @@ Pass TIMEOUT to `eglot--with-timeout'."
             (eglot--wait-for (c-reqs)
                 (&key id method &allow-other-keys)
               (setq pending-id id)
-              (string= method :textDocument/documentHighlight))
+              (string= method 'textDocument/documentHighlight))
             (eglot--wait-for (s-replies)
                 (&key id &allow-other-keys)
               (eq id pending-id))))))))
diff --git a/eglot.el b/eglot.el
index d5498f9..e1592ab 100644
--- a/eglot.el
+++ b/eglot.el
@@ -79,8 +79,8 @@
                                 (php-mode . ("php" "vendor/felixfbecker/\
 language-server/bin/php-language-server.php")))
   "How the command `eglot' guesses the server to start.
-An association list of (MAJOR-MODE . SPEC) pair.  MAJOR-MODE is a
-mode symbol.  SPEC is
+An association list of (MAJOR-MODE . CONTACT) pair.  MAJOR-MODE
+is a mode symbol.  CONTACT is:
 
 * In the most common case, a list of strings (PROGRAM [ARGS...]).
 PROGRAM is called with ARGS and is expected to serve LSP requests
@@ -91,12 +91,15 @@ a positive integer number for connecting to a server via 
TCP.
 Remaining ARGS are passed to `open-network-stream' for upgrading
 the connection with encryption or other capabilities.
 
-* A function of no arguments returning a connected process.
-
-* A cons (CLASS-NAME . SPEC) where CLASS-NAME is a symbol
-designating a subclass of symbol `eglot-lsp-server', for
-representing experimental LSP servers.  In this case SPEC is
-interpreted as described above this point.")
+* A cons (CLASS-NAME . INITARGS) where CLASS-NAME is a symbol
+designating a subclass of `eglot-lsp-server', for representing
+experimental LSP servers.  INITARGS is a keyword-value plist used
+to initialize CLASS-NAME, or a plain list interpreted as the
+previous descriptions of CONTACT, in which case it is converted
+to produce a plist with a suitable :PROCESS initarg to
+CLASS-NAME.  The class `eglot-lsp-server' descends
+`jsonrpc-process-connection', which you should see for semantics
+of the mandatory :PROCESS argument.")
 
 (defface eglot-mode-line
   '((t (:inherit font-lock-constant-face :weight bold)))
@@ -124,8 +127,8 @@ lasted more than that many seconds."
   "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.")
+(cl-defgeneric eglot-handle-request (server method &rest params)
+  "Handle SERVER's METHOD request with PARAMS.")
 
 (cl-defgeneric eglot-handle-notification (server method id &rest params)
   "Handle SERVER's METHOD notification with PARAMS.")
@@ -164,10 +167,10 @@ lasted more than that many seconds."
 (defclass eglot-lsp-server (jsonrpc-process-connection)
   ((project-nickname
     :documentation "Short nickname for the associated project."
-    :initarg :project-nickname :accessor eglot--project-nickname)
+    :accessor eglot--project-nickname)
    (major-mode
     :documentation "Major mode symbol."
-    :initarg :major-mode :accessor eglot--major-mode)
+    :accessor eglot--major-mode)
    (capabilities
     :documentation "JSON object containing server capabilities."
     :accessor eglot--capabilities)
@@ -176,19 +179,22 @@ lasted more than that many seconds."
     :accessor eglot--shutdown-requested)
    (project
     :documentation "Project associated with server."
-    :initarg :project :accessor eglot--project)
+    :accessor eglot--project)
    (spinner
     :documentation "List (ID DOING-WHAT DONE-P) representing server progress."
     :initform `(nil nil t) :accessor eglot--spinner)
    (inhibit-autoreconnect
     :documentation "Generalized boolean inhibiting auto-reconnection if true."
-    :initarg :inhibit-autoreconnect :accessor eglot--inhibit-autoreconnect)
+    :accessor eglot--inhibit-autoreconnect)
    (file-watches
     :documentation "Map ID to list of WATCHES for `didChangeWatchedFiles'."
     :initform (make-hash-table :test #'equal) :accessor eglot--file-watches)
    (managed-buffers
     :documentation "List of buffers managed by server."
-    :initarg :managed-buffers :accessor eglot--managed-buffers))
+    :accessor eglot--managed-buffers)
+   (saved-initargs
+    :documentation "Saved initargs for reconnection purposes"
+    :accessor eglot--saved-initargs))
   :documentation
   "Represents a server. Wraps a process for LSP communication.")
 
@@ -296,47 +302,35 @@ function with the server still running."
                     (list (match-string 1 s) (string-to-number (match-string 2 
s)))
                   (split-string-and-unquote s)))
             guess)))
-    (list managed-mode project (cons class contact) t)))
+    (list managed-mode project class contact t)))
 
 ;;;###autoload
-(defun eglot (managed-major-mode project contact &optional interactive)
+(defun eglot (managed-major-mode project class contact &optional interactive)
   "Manage a project with a Language Server Protocol (LSP) server.
 
-The LSP server is started (or contacted) via CONTACT.  If this
-operation is successful, current *and future* file buffers of
-MANAGED-MAJOR-MODE inside PROJECT automatically become
+The LSP server of CLASS started (or contacted) via CONTACT.  If
+this operation is successful, current *and future* file buffers
+of MANAGED-MAJOR-MODE inside PROJECT automatically become
 \"managed\" by the LSP server, meaning information about their
 contents is exchanged periodically to provide enhanced
 code-analysis via `xref-find-definitions', `flymake-mode',
 `eldoc-mode', `completion-at-point', among others.
 
 Interactively, the command attempts to guess MANAGED-MAJOR-MODE
-from current buffer, CONTACT from `eglot-server-programs' and
-PROJECT from `project-current'.  If it can't guess, the user is
-prompted.  With a single \\[universal-argument] prefix arg, it
-always prompt for COMMAND.  With two \\[universal-argument]
-prefix args, also prompts for MANAGED-MAJOR-MODE.
+from current buffer, CLASS and CONTACT from
+`eglot-server-programs' and PROJECT from `project-current'.  If
+it can't guess, the user is prompted.  With a single
+\\[universal-argument] prefix arg, it always prompt for COMMAND.
+With two \\[universal-argument] prefix args, also prompts for
+MANAGED-MAJOR-MODE.
 
 PROJECT is a project instance as returned by `project-current'.
 
-CONTACT specifies how to contact the server. It can be:
-
-* a list of strings (COMMAND [ARGS...]) specifying how
-to start a server subprocess to connect to.
-
-* A list with a string as the first element and an integer number
-as the second list is interpreted as (HOST PORT [PARAMETERS...])
-and connects to an existing server via TCP, with the remaining
-PARAMETERS being given as `open-network-stream's optional
-arguments.
+CLASS is a subclass of symbol `eglot-lsp-server'.
 
-* A list (CLASS-SYM CONTACT...) where CLASS-SYM names the
-subclass of `eglot-server' used to create the server object. The
-remaining arguments are processed as described in the previous
-paragraphs.
-
-* A function of arguments returning arguments compatible with the
-previous description.
+CONTACT specifies how to contact the server.  It is a
+keyword-value plist used to initialize CLASS or a plain list as
+described in `eglot-server-programs', which see.
 
 INTERACTIVE is t if called interactively."
   (interactive (eglot--interactive))
@@ -354,6 +348,7 @@ INTERACTIVE is t if called interactively."
                                     managed-major-mode
                                     (format "%s/%s" nickname 
managed-major-mode)
                                     nickname
+                                    class
                                     contact)))
         (eglot--message "Connected! Process `%s' now \
 managing `%s' buffers in project `%s'."
@@ -371,29 +366,51 @@ INTERACTIVE is t if called interactively."
                   (eglot--major-mode server)
                   (jsonrpc-name server)
                   (eglot--project-nickname server)
-                  (jsonrpc-contact server))
+                  (eieio-object-class-name server)
+                  (eglot--saved-initargs server))
   (eglot--message "Reconnected!"))
 
 (defalias 'eglot-events-buffer 'jsonrpc-events-buffer)
 
 (defvar eglot-connect-hook nil "Hook run after connecting in 
`eglot--connect'.")
 
-(defun eglot--dispatch (server method id params)
-  "Dispatcher passed to `jsonrpc-connect'.
-Calls a function on SERVER, METHOD ID and PARAMS."
-  (let ((method (intern (format ":%s" method))))
-    (if id
-        (apply #'eglot-handle-request server id method params)
-      (apply #'eglot-handle-notification server method params)
-      (force-mode-line-update t))))
-
-(defun eglot--connect (project managed-major-mode name nickname contact)
+(defun eglot--connect (project managed-major-mode name nickname
+                               class contact)
   "Connect to PROJECT, MANAGED-MAJOR-MODE, NAME.
-And NICKNAME and CONTACT."
-  (let* ((contact (if (functionp contact) (funcall contact) contact))
+And don't forget NICKNAME and CLASS, CONTACT.  This docstring
+appeases checkdoc, that's all."
+  (let* ((readable-name (format "EGLOT (%s/%s)" nickname managed-major-mode))
+         (initargs
+          (cond ((keywordp (car contact)) contact)
+                ((integerp (cadr contact))
+                 `(:process ,(lambda ()
+                               (apply #'open-network-stream
+                                      readable-name nil
+                                      (car contact) (cadr contact)
+                                      (cddr contact)))))
+                ((stringp (car contact))
+                 `(:process ,(lambda ()
+                               (make-process
+                                :name readable-name
+                                :command contact
+                                :connection-type 'pipe
+                                :coding 'utf-8-emacs-unix
+                                :stderr (get-buffer-create
+                                         (format "*%s stderr*" 
readable-name))))))))
+         (spread
+          (lambda (fn)
+            (lambda (&rest args)
+              (apply fn (append (butlast args) (car (last args)))))))
          (server
-          (jsonrpc-connect name contact #'eglot--dispatch 
#'eglot--on-shutdown))
+          (apply
+           #'make-instance class
+           :name name
+           :notification-dispatcher (funcall spread 
#'eglot-handle-notification)
+           :request-dispatcher (funcall spread #'eglot-handle-request)
+           :on-shutdown #'eglot--on-shutdown
+           initargs))
          success)
+    (setf (eglot--saved-initargs server) initargs)
     (setf (eglot--project server) project)
     (setf (eglot--project-nickname server) nickname)
     (setf (eglot--major-mode server) managed-major-mode)
@@ -688,14 +705,14 @@ Uses THING, FACE, DEFS and PREPEND."
 ;;; Protocol implementation (Requests, notifications, etc)
 ;;;
 (cl-defmethod eglot-handle-notification
-  (_server (_method (eql :window/showMessage)) &key type message)
+  (_server (_method (eql window/showMessage)) &key type message)
   "Handle notification window/showMessage"
   (eglot--message (propertize "Server reports (type=%s): %s"
                               'face (if (<= type 1) 'error))
                   type message))
 
 (cl-defmethod eglot-handle-request
-  (server id (_method (eql :window/showMessageRequest)) &key type message 
actions)
+  (server (_method (eql window/showMessageRequest)) &key type message actions)
   "Handle server request window/showMessageRequest"
   (let (reply)
     (unwind-protect
@@ -710,23 +727,23 @@ Uses THING, FACE, DEFS and PREPEND."
                    '("OK"))
                nil t (plist-get (elt actions 0) :title)))
       (if reply
-          (jsonrpc-reply server id :result `(:title ,reply))
-        (jsonrpc-reply server id
+          (jsonrpc-reply server :result `(:title ,reply))
+        (jsonrpc-reply server
                        :error `(:code -32800 :message "User cancelled"))))))
 
 (cl-defmethod eglot-handle-notification
-  (_server (_method (eql :window/logMessage)) &key _type _message)
+  (_server (_method (eql window/logMessage)) &key _type _message)
   "Handle notification window/logMessage") ;; noop, use events buffer
 
 (cl-defmethod eglot-handle-notification
-  (_server (_method (eql :telemetry/event)) &rest _any)
+  (_server (_method (eql telemetry/event)) &rest _any)
   "Handle notification telemetry/event") ;; noop, use events buffer
 
 (defvar-local eglot--unreported-diagnostics nil
   "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
@@ -750,7 +767,7 @@ Uses THING, FACE, DEFS and PREPEND."
                         (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)
+(cl-defun eglot--register-unregister (server things how)
   "Helper for `registerCapability'.
 THINGS are either registrations or unregisterations."
   (dolist (thing (cl-coerce things 'list))
@@ -762,28 +779,28 @@ THINGS are either registrations or unregisterations."
           (unless (eq t (car retval))
             (cl-return-from eglot--register-unregister
               (jsonrpc-reply
-               server jsonrpc-id
+               server
                :error `(:code -32601 :message ,(or (cadr retval) 
"sorry")))))))))
-  (jsonrpc-reply server jsonrpc-id :result `(:message "OK")))
+  (jsonrpc-reply server :result `(:message "OK")))
 
 (cl-defmethod eglot-handle-request
-  (server id (_method (eql :client/registerCapability)) &key registrations)
+  (server (_method (eql client/registerCapability)) &key registrations)
   "Handle server request client/registerCapability"
-  (eglot--register-unregister server id registrations 'register))
+  (eglot--register-unregister server registrations 'register))
 
 (cl-defmethod eglot-handle-request
-  (server id (_method (eql :client/unregisterCapability))
+  (server (_method (eql client/unregisterCapability))
           &key unregisterations) ;; XXX: "unregisterations" (sic)
   "Handle server request client/unregisterCapability"
-  (eglot--register-unregister server id unregisterations 'unregister))
+  (eglot--register-unregister server unregisterations 'unregister))
 
 (cl-defmethod eglot-handle-request
-  (server id (_method (eql :workspace/applyEdit)) &key _label edit)
+  (server (_method (eql workspace/applyEdit)) &key _label edit)
   "Handle server request workspace/applyEdit"
   (condition-case err
       (progn (eglot--apply-workspace-edit edit 'confirm)
-             (jsonrpc-reply server id :result `(:applied )))
-    (error (jsonrpc-reply server id
+             (jsonrpc-reply server :result `(:applied )))
+    (error (jsonrpc-reply server
                           :result `(:applied :json-false)
                           :error `(:code -32001 :message (format "%s" 
,err))))))
 
@@ -1348,7 +1365,7 @@ If SKIP-SIGNATURE, don't try to send 
textDocument/signatureHelp."
           (and (equal "Indexing" what) done)))))
 
 (cl-defmethod eglot-handle-notification
-  ((server eglot-rls) (_method (eql :window/progress))
+  ((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)))
@@ -1367,17 +1384,17 @@ If SKIP-SIGNATURE, don't try to send 
textDocument/signatureHelp."
           :progressReportFrequencyMs -1)))
 
 (cl-defmethod eglot-handle-notification
-  ((_server eglot-cquery) (_method (eql :$cquery/progress))
+  ((_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))
+  ((_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))
+  ((_server eglot-cquery) (_method (eql $cquery/publishSemanticHighlighting))
    &key _uri _symbols &allow-other-keys)
   "No-op for unsupported $cquery/publishSemanticHighlighting extension")
 
diff --git a/jsonrpc-tests.el b/jsonrpc-tests.el
index 33a7ae5..d59f1a0 100644
--- a/jsonrpc-tests.el
+++ b/jsonrpc-tests.el
@@ -30,9 +30,11 @@
 (require 'jsonrpc)
 (require 'eieio)
 
-(defclass jsonrpc-test-conn (jsonrpc-process-connection)
-  ((hold-deferred :initform t :accessor jsonrpc--hold-deferred)
-   (shutdown-complete-p :initform nil :accessor jsonrpc--shutdown-complete-p)))
+(defclass jsonrpc--test-endpoint (jsonrpc-process-connection)
+  ((scp :accessor jsonrpc--shutdown-complete-p)))
+
+(defclass jsonrpc--test-client (jsonrpc--test-endpoint)
+  ((hold-deferred :initform t :accessor jsonrpc--hold-deferred)))
 
 (cl-defmacro jsonrpc--with-emacsrpc-fixture ((endpoint-sym) &body body)
   (declare (indent 1) (debug t))
@@ -44,25 +46,30 @@
               :service 44444
               :log (lambda (_server client _message)
                      (setq ,server
-                           (jsonrpc-connect
-                            (process-name client)
-                            (make-instance 'jsonrpc-test-conn :process client)
-                            (lambda (endpoint method id params)
+                           (make-instance
+                            'jsonrpc--test-endpoint
+                            :name (process-name client)
+                            :process client
+                            :request-dispatcher
+                            (lambda (endpoint method params)
                               (unless (memq method '(+ - * / vconcat append
                                                        sit-for ignore))
                                 (signal 'jsonrpc-error
                                         `((jsonrpc-error-message
                                            . "Sorry, this isn't allowed")
                                           (jsonrpc-error-code . -32601))))
-                              (jsonrpc-reply endpoint id :result
-                                             (apply method (append params 
nil))))
+                              (let ((result (apply method (append params 
nil))))
+                                (jsonrpc-reply endpoint :result result)))
+                            :on-shutdown
                             (lambda (conn)
                               (setf (jsonrpc--shutdown-complete-p conn) 
t)))))))
-            (,endpoint-sym (jsonrpc-connect
+            (,endpoint-sym (make-instance
+                            'jsonrpc--test-client
                             "Emacs RPC client"
-                            '(jsonrpc-test-conn "localhost" 44444)
-                            (lambda (_endpoint method _id &rest _params)
-                              (message "server wants to %s" method))
+                            :process
+                            (open-network-stream "JSONRPC test tcp endpoint"
+                                                 nil "localhost" 44444)
+                            :on-shutdown
                             (lambda (conn)
                               (setf (jsonrpc--shutdown-complete-p conn) t)))))
        (unwind-protect
@@ -93,7 +100,7 @@
 (ert-deftest returns-3 ()
   "returns 3"
   (jsonrpc--with-emacsrpc-fixture (conn)
-    (should (= 3 (jsonrpc-request conn '+ '(1 2))))))
+    (should (= 3 (jsonrpc-request conn '+ [1 2])))))
 
 (ert-deftest errors-with--32601 ()
   "errors with -32601"
@@ -110,7 +117,7 @@
   (jsonrpc--with-emacsrpc-fixture (conn)
     (condition-case err
         (progn
-          (jsonrpc-request conn '+ '(a 2))
+          (jsonrpc-request conn '+ ["a" 2])
           (ert-fail "A `jsonrpc-error' should have been signalled!"))
       (jsonrpc-error
        (should (= -32603 (cdr (assoc 'jsonrpc-error-code (cdr err)))))))))
@@ -119,23 +126,23 @@
   "times out"
   (jsonrpc--with-emacsrpc-fixture (conn)
     (should-error
-     (jsonrpc-request conn 'sit-for '(5) :timeout 2))))
+     (jsonrpc-request conn 'sit-for [5] :timeout 2))))
 
 (ert-deftest stretching-it-but-works ()
   "stretching it, but works"
   (jsonrpc--with-emacsrpc-fixture (conn)
     (should (equal
              [1 2 3 3 4 5]
-             (jsonrpc-request conn 'vconcat '([1 2 3] [3 4 5]))))))
+             (jsonrpc-request conn 'vconcat [[1 2 3] [3 4 5]])))))
 
 (ert-deftest json-el-cant-serialize-this ()
-  "json.el can't serialize this, json.el errors and request isn't sent"
+  "json.el can't serialize the response."
   (jsonrpc--with-emacsrpc-fixture (conn)
     (should-error
-     (jsonrpc-request conn 'append '((1 2 3) (3 4 5))))))
+     (jsonrpc-request conn 'append [[1 2 3] [3 4 5]]))))
 
 (cl-defmethod jsonrpc-connection-ready-p
-  ((conn jsonrpc-test-conn) what)
+  ((conn jsonrpc--test-client) what)
   (and (cl-call-next-method)
        (or (not (string-match "deferred" what))
            (not (jsonrpc--hold-deferred conn)))))
@@ -146,14 +153,14 @@
   ;; success fun which sets the flag only runs after some time.
   (jsonrpc--with-emacsrpc-fixture (conn)
     (jsonrpc-async-request conn
-                           'sit-for '(0.5)
+                           'sit-for [0.5]
                            :success-fn
                            (lambda (_result)
                              (setf (jsonrpc--hold-deferred conn) nil)))
     ;; Now wait for an answer to this request, which should be sent as
     ;; soon as the previous one is answered.
     (should
-     (= 3 (jsonrpc-request conn '+ '(1 2)
+     (= 3 (jsonrpc-request conn '+ [1 2]
                            :deferred "deferred"
                            :timeout 1)))))
 
@@ -165,26 +172,26 @@
     (let (n-deferred-1 n-deferred-2)
       (jsonrpc-async-request
        conn
-       'sit-for '(0.1)
+       'sit-for [0.1]
        :success-fn
        (lambda (_result)
          (setq n-deferred-1 (hash-table-count (jsonrpc--deferred-actions 
conn)))))
       (should-error
-       (jsonrpc-request conn 'ignore '("first deferred")
+       (jsonrpc-request conn 'ignore ["first deferred"]
                         :deferred "first deferred"
                         :timeout 0.5)
        :type 'jsonrpc-error)
       (jsonrpc-async-request
        conn
-       'sit-for '(0.1)
+       'sit-for [0.1]
        :success-fn
        (lambda (_result)
          (setq n-deferred-2 (hash-table-count (jsonrpc--deferred-actions 
conn)))
          (setf (jsonrpc--hold-deferred conn) nil)))
-      (jsonrpc-async-request conn 'ignore '("second deferred")
+      (jsonrpc-async-request conn 'ignore ["second deferred"]
                              :deferred "second deferred"
                              :timeout 1)
-      (jsonrpc-request conn 'ignore '("third deferred")
+      (jsonrpc-request conn 'ignore ["third deferred"]
                        :deferred "third deferred"
                        :timeout 1)
       (should (eq 1 n-deferred-1))
@@ -195,11 +202,11 @@
   "Deferred request fails because noone clears the flag."
   (jsonrpc--with-emacsrpc-fixture (conn)
     (should-error
-     (jsonrpc-request conn '+ '(1 2)
+     (jsonrpc-request conn '+ [1 2]
                       :deferred "deferred-testing" :timeout 0.5)
      :type 'jsonrpc-error)
     (should
-     (= 3 (jsonrpc-request conn '+ '(1 2)
+     (= 3 (jsonrpc-request conn '+ [1 2]
                            :timeout 0.5)))))
 
 (provide 'jsonrpc-tests)
diff --git a/jsonrpc.el b/jsonrpc.el
index 55d4dba..d3aed36 100644
--- a/jsonrpc.el
+++ b/jsonrpc.el
@@ -34,46 +34,45 @@
 ;; http, or in many various message passing environments."
 ;;
 ;; To approach this agnosticism, jsonrpc.el uses objects derived from
-;; an abstract class, `jsonrpc-connection' to represent the connection
-;; to the remote JSON endpoint.  Abstract operations such as sending
-;; and receiving are modelled as generic functions, so that users of
-;; JSONRPC working in complicated transport infrastructures can
+;; a base `jsonrpc-connection' class, which is "abstract" or "virtual"
+;; (in modern OO parlance) and represents the connection to the remote
+;; JSON endpoint.  Equally abstract operations such as sending and
+;; receiving are modelled as generic functions, so JSONRPC
+;; applications operating over arbitrary transport infrastructures can
 ;; specify a subclass of `jsonrpc-connection' and write specific
-;; methods for it.  Nevertheless, jsonrpc.el comes built-in with
-;; `jsonrpc-process-connection' class that works both with local
-;; subprocesses (through stdin/stdout) and TCP hosts (using
-;; sockets). This uses some simple HTTP-style envelopping for JSON
-;; objects travelling through the wire.
+;; methods for it.
 ;;
-;; The entry point `jsonrpc-connect', returns one of these objects by
-;; default.  It is passed a name identifying the connection and a
-;; "contact", which will determine the connection type to make.  This
-;; contact can a list of strings (a command and arguments for creating
-;; subprocesses) or a list of the form (HOST PORT-NUMBER PARAMS...)
-;; for connecting via TCP.  For the providing the aforementioned
-;; flexibility, it can also be a any object of a subclass of
-;; `jsonrpc-connection'.
+;; The `jsonrpc-connection' constructor is the most generic entry
+;; point for these uses.  However, for convenience, jsonrpc.el comes
+;; built-in with `jsonrpc-process-connection' class for talking to
+;; local subprocesses (through stdin/stdout) and TCP hosts using
+;; sockets.  This uses some basic HTTP-style enveloping headers for
+;; JSON objects sent over the wire.  For an example of an application
+;; using this transport scheme on top of JSONRPC, see for example the
+;; Language Server Protocol
+;; (https://microsoft.github.io/language-server-protocol/specification).
 ;;
-;; `jsonrpc-connect' returns a connection upon connection.  This value
-;; should be saved to be later given to `jsonrpc-notify',
-;; `jsonrpc-reply', `jsonrpc-request' and `jsonrpc-async-request' as a
-;; way of contacting the connected remote endpoint.
+;; Whatever the method used to obtain a `jsonrpc-connection', it is
+;; given to `jsonrpc-notify', `jsonrpc-request' and
+;; `jsonrpc-async-request' as a way of contacting the connected remote
+;; endpoint.
 ;;
-;; `jsonrpc-connect' is also passed a dispatcher function for handling
-;; handling the contacts asynchronously initiated by the remote
-;; endpoint's, as well as a optional function for cleaning up after
-;; the tear-down of the JSONRPC connection.
+;; For handling remotely initiated contacts, `jsonrpc-connection'
+;; objects hold dispatcher functions that the application should pass
+;; to object's constructor if it is interested in those messages.
 ;;
 ;; The JSON objects are passed to the dispatcher after being read by
-;; `json-read' of Emacs's json.el library.  They are read as plists,
-;; and, likewise, json.el-compatible plists should be given to
-;; `jsonrpc-notify', `jsonrpc-request', etc...
+;; `jsonrpc--json-read', which may use either the longstanding json.el
+;; library or a newer and faster json.c library if it is available.
 ;;
-;; To facilitate handling of key-value plists, this library make
-;; liberal use of cl-lib.el and suggests (but doesn't force) its
-;; clients to do the same.  A macro `jsonrpc-lambda' can be used to
-;; create a lambda for destructuring a JSON-object like in this
-;; example:
+;; JSON objects are exchanged as plists: plists are handed to the
+;; dispatcher functions and, likewise, plists should be given to
+;; `jsonrpc-notify', `jsonrpc-request' and `jsonrpc-async-request'.
+;;
+;; To facilitate handling plists, this library make liberal use of
+;; cl-lib.el and suggests (but doesn't force) its clients to do the
+;; same.  A macro `jsonrpc-lambda' can be used to create a lambda for
+;; destructuring a JSON-object like in this example:
 ;;
 ;;  (jsonrpc-async-request
 ;;   myproc :frobnicate `(:foo "trix")
@@ -84,47 +83,6 @@
 ;;               (message "Sadly, server reports %s: %s"
 ;;                        code message)))
 ;;
-;;;; Usage example:
-;;
-;; Finally, here's an example Emacs JSONRPC server that offers a (very
-;; small) subset of Elisp for remote calling:
-;;
-;; (defvar server-server) (defvar server-endpoint)
-;; (defvar server-allowed-functions '(+ - * / vconcat append sit-for))
-;;
-;; (setq server-server
-;;       (make-network-process
-;;        :name "Emacs RPC server" :server t :host "localhost" :service 44444
-;;        :log (lambda (_server client _message)
-;;               (jsonrpc-connect
-;;                (process-name client)
-;;                (make-instance 'jsonrpc-process-connection :process client)
-;;                (lambda (endpoint method id params)
-;;                  (unless (memq method '(+ - * / vconcat append sit-for))
-;;                    (signal 'jsonrpc-error `((jsonrpc-error-message
-;;                                              . "Sorry, this isn't allowed")
-;;                                             (jsonrpc-error-code . -32601))))
-;;                  (jsonrpc-reply endpoint id :result
-;;                                 (apply method (append params nil))))))))
-;;
-;; (setq server-endpoint (jsonrpc-connect
-;;                        "Emacs RPC client" '("localhost" 9393)
-;;                        (lambda (endpoint method id &rest params)
-;;                          (message "server wants to %s" method))))
-;;
-;; ;; returns 3
-;; (jsonrpc-request server-endpoint '+ '(1 2))
-;; ;; errors with -32601
-;; (jsonrpc-request server-endpoint 'delete-directory "~/tmp")
-;; ;; signals an -32603 JSONRPC error
-;; (jsonrpc-request server-endpoint '+ '(a 2))
-;; ;; times out
-;; (jsonrpc-request server-endpoint 'sit-for '(5))
-;; ;; stretching it, but works
-;; (jsonrpc-request server-endpoint 'vconcat '([1 2 3] [3 4 5]))
-;; ;; json.el can't serialize this, json.el errors and request isn't sent
-;; (jsonrpc-request server-endpoint 'append '((1 2 3) (3 4 5)))
-;;
 ;;; Code:
 
 (require 'cl-lib)
@@ -175,13 +133,22 @@ FORMAT as the message."
                      (apply #'format format args)
                      :warning)))
 
+;;;###autoload
 (defclass jsonrpc-connection ()
   ((name
     :accessor jsonrpc-name
+    :initarg :name
     :documentation "A name for the connection")
-   (-dispatcher
-    :accessor jsonrpc--dispatcher
-    :documentation "Emacs-lisp function for server-invoked methods.")
+   (-request-dispatcher
+    :accessor jsonrpc--request-dispatcher
+    :initform #'ignore
+    :initarg :request-dispatcher
+    :documentation "Dispatcher for remotely invoked requests.")
+   (-notification-dispatcher
+    :accessor jsonrpc--notification-dispatcher
+    :initform #'ignore
+    :initarg :notification-dispatcher
+    :documentation "Dispatcher for remotely invoked notifications.")
    (status
     :initform `(:unknown nil) :accessor jsonrpc-status
     :documentation "Status (WHAT SERIOUS-P) as declared by the server.")
@@ -189,18 +156,9 @@ FORMAT as the message."
     :initform (make-hash-table)
     :accessor jsonrpc--request-continuations
     :documentation "A hash table of request ID to continuation lambdas.")
-   (-server-request-ids
-    :accessor jsonrpc--server-request-ids
-    :documentation "Server-initiated request ids that client hasn't replied 
to.")
    (-events-buffer
     :accessor jsonrpc--events-buffer
     :documentation "A buffer pretty-printing the JSON-RPC RPC events")
-   (contact
-    :accessor jsonrpc-contact
-    :documentation "Method used to contact a server.")
-   (-on-shutdown
-    :accessor jsonrpc--on-shutdown
-    :documentation "Function run when JSONRPC server is dying.")
    (-deferred-actions
     :initform (make-hash-table :test #'equal)
     :accessor jsonrpc--deferred-actions
@@ -210,16 +168,65 @@ than TIMER as ID.")
    (-next-request-id
     :initform 0
     :accessor jsonrpc--next-request-id
-    :documentation "Next number used for a request")))
+    :documentation "Next number used for a request"))
+  :documentation "Base class representing a JSONRPC connection.
+The following initargs are accepted:
+
+:NAME (mandatory), a string naming the connection
+
+:REQUEST-DISPATCHER (optional), a function of three
+arguments (CONN METHOD PARAMS) for handling JSONRPC requests.
+CONN is a `jsonrpc-connection' object, method is a symbol, and
+PARAMS is a plist representing a JSON object.  The function is
+expected to call `jsonrpc-reply' or signal an error of type
+`jsonrpc-error'.
 
+:NOTIFICATION-DISPATCHER (optional), a function of three
+arguments (CONN METHOD PARAMS) for handling JSONRPC
+notifications.  CONN, METHOD and PARAMS are the same as in
+:REQUEST-DISPATCHER.")
+
+;;;###autoload
 (defclass jsonrpc-process-connection (jsonrpc-connection)
   ((-process
     :initarg :process :accessor jsonrpc--process
-    ;; :initform (error "`:process' is a required initarg") ; doesn't work
     :documentation "Process object wrapped by the this connection.")
    (-expected-bytes
     :accessor jsonrpc--expected-bytes
-    :documentation "How many bytes declared by server")))
+    :documentation "How many bytes declared by server")
+   (-on-shutdown
+    :accessor jsonrpc--on-shutdown
+    :initform #'ignore
+    :initarg :on-shutdown
+    :documentation "Function run when the process dies."))
+  :documentation "A JSONRPC connection over an Emacs process.
+The following initargs are accepted:
+
+:PROCESS (mandatory), a live running Emacs process object or a
+function of no arguments producing one such object.  The process
+represents either a pipe connection to locally running process or
+a stream connection to a network host.  The remote endpoint is
+expected to understand JSONRPC messages with basic HTTP-style
+enveloping headers such as \"Content-Length:\".
+
+:ON-SHUTDOWN (optional), a function of one argument, the
+connection object, called when the process dies .")
+
+(cl-defmethod initialize-instance ((conn jsonrpc-process-connection) slots)
+  (cl-call-next-method)
+  (let* ((proc (plist-get slots :process))
+         (proc (if (functionp proc) (funcall proc) proc))
+         (buffer (get-buffer-create (format "*%s output*" (process-name 
proc))))
+         (stderr (get-buffer-create (format "*%s stderr*" (process-name 
proc)))))
+    (setf (jsonrpc--process conn) proc)
+    (set-process-buffer proc buffer)
+    (process-put proc 'jsonrpc-stderr stderr)
+    (set-process-filter proc #'jsonrpc--process-filter)
+    (set-process-sentinel proc #'jsonrpc--process-sentinel)
+    (with-current-buffer (process-buffer proc)
+      (set-marker (process-mark proc) (point-min))
+      (let ((inhibit-read-only t)) (erase-buffer) (read-only-mode t) proc))
+    (process-put proc 'jsonrpc-connection conn)))
 
 (defmacro jsonrpc-obj (&rest what)
   "Make WHAT a suitable argument for `json-encode'."
@@ -228,96 +235,26 @@ than TIMER as ID.")
   ;; the indenting of literal plists, i.e. is basically `list'
   `(list ,@what))
 
-;;;###autoload
-(cl-defun jsonrpc-connect (name contact dispatcher &optional on-shutdown)
-  "Connect to JSONRPC endpoint NAME through CONTACT.
-
-This function creates an object (subprocess or network
-connection) wrapped in a `jsonrpc-process-connection' object.
-
-NAME is a string naming the connection.
-
-In the most common case CONTACT is a list of strings (COMMAND
-ARGS...) specifying how to locally start a server subprocess to
-talk to via JSONRPC.  If the second element in the list is an
-integer number instead of a string, the list is interpreted
-as (HOST PORT PARAMETERS...) and an attempt is made to contact
-HOST on PORT, with the remaining PARAMETERS are given to
-`open-network-stream's optional arguments.
-
-Moreover, if in either of these cases the first element in the
-list is a symbol, that symbol is taken to name a subclass of
-`jsonrpc-process-connection' which is used to create the object
-returned by this function. The remaining arguments are processed
-as described in the previous paragraph.
-
-CONTACT can also be a an object of the type
-`jsonrpc-process-connection' (or a subclass thereof) containing a
-pre-connected process object. In that case the processes buffer,
-filter and sentinel are henceforth overwritten and managed by
-`jsonrpc-connect'.
-
-ON-SHUTDOWN, if non-nil, is a function called on server exit and
-passed the moribund connection object as a single argument.
-
-DISPATCHER specifies how the server-invoked methods find their
-Elisp counterpart. It is a function passed (PROC METHOD ID PARAMS
-as arguments. PROC is the connection object returned by this
-function. ID is the server identifier for a server request, or
-nil for a server notification. METHOD is a symbol. PARAMS
-contains the method parameters as JSON data.
-
-If ID is non-nil, DISPATCHER is expected to reply to the
-request. If it doesn't, or if it signals an error before doing
-so, jsonrpc.el will automatically reply with an error. If DISPATCHER
-signals an error with alist elements `jsonrpc-error-message' and
-`jsonrpc-error-code' in its DATA, the corresponding elements are
-used for the automated error reply.
-
-If successful, `jsonrpc-connect' returns a
-`jsonrpc-process-connection' object representing the remote
-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
-           ((cl-typep contact 'jsonrpc-process-connection)
-            (unless (process-live-p (jsonrpc--process contact))
-              (error "%s doesn't have a live process" contact))
-            contact)
-           ((listp contact)
-            (make-instance
-             (if (symbolp (car contact))
-                 (prog1 (car contact) (setq contact (cdr contact)))
-               'jsonrpc-process-connection)
-             :process
-             (cond ((integerp (cadr contact))
-                    (apply #'open-network-stream readable-name buffer contact))
-                   (t
-                    (make-process :name readable-name
-                                  :command contact
-                                  :connection-type 'pipe
-                                  :coding 'no-conversion
-                                  :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)
-    (with-current-buffer buffer
-      (let ((inhibit-read-only t)) (erase-buffer) (read-only-mode t) proc))
-    (process-put proc 'jsonrpc-connection connection)
-    (setf (jsonrpc--process connection) proc
-          (jsonrpc-contact connection) original-contact
-          (jsonrpc-name connection) name
-          (jsonrpc--dispatcher connection) dispatcher
-          (jsonrpc--on-shutdown connection) (or on-shutdown #'ignore))
-    connection))
+(defun jsonrpc--json-read ()
+  "Read JSON object in buffer, move point to end of buffer."
+  ;; TODO: I guess we can make these macros if/when jsonrpc.el
+  ;; goes into Emacs core.
+  (cond ((fboundp 'json-parse-buffer) (json-parse-buffer
+                                       :object-type 'plist
+                                       :null-object nil
+                                       :false-object :json-false))
+        (t                            (let ((json-object-type 'plist))
+                                        (json-read)))))
+
+(defun jsonrpc--json-encode (object)
+  "Encode OBJECT into a JSON string."
+  (cond ((fboundp 'json-serialize) (json-serialize
+                                    object
+                                    :false-object :json-false
+                                    :null-object nil))
+        (t                         (let ((json-false :json-false)
+                                         (json-null nil))
+                                     (json-encode object)))))
 
 (defun jsonrpc--process-sentinel (proc change)
   "Called when PROC undergoes CHANGE."
@@ -387,10 +324,9 @@ endpoint."
                         (unwind-protect
                             (save-restriction
                               (narrow-to-region (point) message-end)
-                              (let* ((json-object-type 'plist)
-                                     (json-message
+                              (let* ((json-message
                                       (condition-case-unless-debug oops
-                                          (json-read)
+                                          (jsonrpc--json-read)
                                         (error
                                          (jsonrpc-warn "Invalid JSON: %s %s"
                                                        (cdr oops) 
(buffer-string))
@@ -464,56 +400,71 @@ originated."
             (setq msg (propertize msg 'face 'error)))
           (insert-before-markers msg))))))
 
+(defvar jsonrpc--unanswered-request-id)
+
 (defun jsonrpc--connection-receive (connection message)
   "Connection MESSAGE from CONNECTION."
-  (pcase-let ((`(,method ,id ,error ,params ,result)
+  (cl-destructuring-bind
+      (&key method id error params result _jsonrpc)
+      message
+    (pcase-let* ((continuations)
+                 (lisp-err)
+                 (jsonrpc--unanswered-request-id id))
+      (jsonrpc-log-event connection message 'server)
+      (when error (setf (jsonrpc-status connection) `(,error t)))
+      (cond (method
+             (let ((debug-on-error
+                    (and debug-on-error
+                         (not (ert-running-test)))))
                (condition-case-unless-debug oops
-                   (cl-destructuring-bind
-                       (&rest args &key method id error params result _jsonrpc)
-                       message (list method id error params result))
-                 (error (jsonrpc-warn "Invalid JSONRPC message %s: %s" message
-                                      (cdr oops))
-                        nil)))
-              (continuations)
-              (lisp-err))
-    (jsonrpc-log-event connection message 'server)
-    (when error (setf (jsonrpc-status connection) `(,error t)))
-    (cond (method
-           (let ((debug-on-error
-                  (and debug-on-error
-                       (not (ert-running-test)))))
-             (condition-case-unless-debug oops
-                 (funcall (jsonrpc--dispatcher connection)
-                          connection (intern method) id params)
-               (error (setq lisp-err oops))))
-           (unless (or (member id (jsonrpc--server-request-ids connection))
-                       (not (or id lisp-err)))
-             (jsonrpc-reply
-              connection id
-              :error (jsonrpc-obj
-                      :code (or (alist-get 'jsonrpc-error-code (cdr lisp-err))
-                                -32603)
-                      :message (or (alist-get 'jsonrpc-error-message
-                                              (cdr lisp-err))
-                                   "Internal error"))))
-           (setf (jsonrpc--server-request-ids connection)
-                 (delete id (jsonrpc--server-request-ids connection))))
-          ((setq continuations
-                 (and id (gethash id (jsonrpc--request-continuations 
connection))))
-           (let ((timer (nth 2 continuations)))
-             (when timer (cancel-timer timer)))
-           (remhash id (jsonrpc--request-continuations connection))
-           (if error (funcall (nth 1 continuations) error)
-             (funcall (nth 0 continuations) result)))
-          (id
-           (jsonrpc-warn "No continuation for id %s" id)))
-    (jsonrpc--call-deferred connection)))
+                   (funcall (if id
+                                (jsonrpc--request-dispatcher connection)
+                              (jsonrpc--notification-dispatcher connection))
+                            connection (intern method) params)
+                 (error
+                  (setq lisp-err oops))))
+             (unless (or (not jsonrpc--unanswered-request-id)
+                         (not lisp-err))
+               (jsonrpc-reply
+                connection
+                :error (jsonrpc-obj
+                        :code (or (alist-get 'jsonrpc-error-code (cdr 
lisp-err))
+                                  -32603)
+                        :message (or (alist-get 'jsonrpc-error-message
+                                                (cdr lisp-err))
+                                     "Internal error")))))
+            ((setq continuations
+                   (and id (gethash id (jsonrpc--request-continuations 
connection))))
+             (let ((timer (nth 2 continuations)))
+               (when timer (cancel-timer timer)))
+             (remhash id (jsonrpc--request-continuations connection))
+             (if error (funcall (nth 1 continuations) error)
+               (funcall (nth 0 continuations) result)))
+            (id
+             (jsonrpc-warn "No continuation for id %s" id)))
+      (jsonrpc--call-deferred connection))))
 
 (cl-defmethod jsonrpc-connection-send ((connection jsonrpc-process-connection)
-                                       message)
+                                       &rest args
+                                       &key
+                                       id
+                                       method
+                                       params
+                                       result
+                                       error)
   "Send MESSAGE, a JSON object, to CONNECTION."
-  (let ((json-object-type 'plist)
-        (json (json-encode message)))
+  (let* ((method
+          (cond ((keywordp method)
+                 (substring (symbol-name method) 1))
+                ((and method (symbolp method)) (symbol-name method))
+                (t method)))
+         (message `(:jsonrpc "2.0"
+                             ,@(when method `(:method ,method))
+                             ,@(when id     `(:id     ,id))
+                             ,@(when params `(:params ,params))
+                             ,@(when result `(:result ,result))
+                             ,@(when error  `(:error  ,error))))
+         (json (jsonrpc--json-encode message)))
     (process-send-string (jsonrpc--process connection)
                          (format "Content-Length: %d\r\n\r\n%s"
                                  (string-bytes json)
@@ -632,10 +583,10 @@ TIMEOUT is nil)."
         (cl-return-from jsonrpc--async-request-1 (list id timer))))
     ;; Really send it
     ;;
-    (jsonrpc-connection-send connection (jsonrpc-obj :jsonrpc "2.0"
-                                                     :id id
-                                                     :method method
-                                                     :params params))
+    (jsonrpc-connection-send connection
+                             :id id
+                             :method method
+                             :params params)
     (puthash id
              (list (or success-fn
                        (jsonrpc-lambda (&rest _ignored)
@@ -695,20 +646,20 @@ DEFERRED is passed to `jsonrpc-async-request', which see."
 
 (cl-defun jsonrpc-notify (connection method params)
   "Notify CONNECTION of something, don't expect a reply.e"
-  (jsonrpc-connection-send connection (jsonrpc-obj :jsonrpc  "2.0"
-                                                   :method method
-                                                   :params params)))
+  (jsonrpc-connection-send connection
+                           :method method
+                           :params params))
 
-(cl-defun jsonrpc-reply (connection id &key (result nil result-supplied-p) 
error)
+(cl-defun jsonrpc-reply (connection &key (result nil result-supplied-p) error)
   "Reply to CONNECTION's request ID with RESULT or ERROR."
-  (unless id (jsonrpc-error "Need a non-nil ID"))
   (unless (xor result-supplied-p error)
     (jsonrpc-error "Can't pass both RESULT and ERROR!"))
-  (push id (jsonrpc--server-request-ids connection))
   (jsonrpc-connection-send
-   connection `(:jsonrpc  "2.0" :id ,id
-                          ,@(when result `(:result ,result))
-                          ,@(when error `(:error ,error)))))
+   connection
+   :id jsonrpc--unanswered-request-id
+   :result result
+   :error error)
+  (setq jsonrpc--unanswered-request-id nil))
 
 (provide 'jsonrpc)
 ;;; jsonrpc.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]