From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Philipp Stephani
Philipp Stephani <p.stephani2@gmail.com> writes= :
> Jo=C3=A3o T=C3=A1vora <joaotavora@gmail.com> schrieb am Fr., 18. Mai 2018 um = 18:28 Uhr:
>
>>=C2=A0 I propose this library, jrpc.el, for Emacs core (or GNU ELPA= , or
> Thanks. I think that separating out JSONRPC from LSP and having a
> JSONRPC library in core are highly valuable additions that I'd
> strongly support.=C2=A0 Some review comments follow; sorry that there = are
> so many, but I think as a foundational library jrpc.el should be as
> high-quality as possible:
Apology *not accepted* :-). Having reviewers provide so many insightful
comments is a previlige and a pleasure. The high standards of the Emacs
project are one of the reasons I like to contribute to it.
I'll triaged your comments into "No", "Maybe", &quo= t;Ok". The former require
some more convincing on your part :-)
I've already applied some of your suggestions (and some that Stefan sen= t
off-list, too). See the jsonrpc-refactor branch of Eglot's github
repository. The latest version of jsonrpc.el (I changed the name) lives
in:
https://raw.githubuser= content.com/joaotavora/eglot/jsonrpc-refactor/jsonrpc.el
> - Please add extensive unit and integration tests. There are lots of > corner cases (what happens when the server times out, sends an invalid=
> or incomplete response, sends responses in the wrong order, etc.) that=
> should all be tested.
OK. It's in the plans, if not in the stars. Help appreciated. :-) We ca= n
convert some of those examples to tests.=C2=A0 Eglot's jsonrpc-refactor=
branch, where I'm currently developing jsonrpc.el, already has
integration tests, so at least a part of jsonrpc.el is already covered.
=
> - Please don't use the `error' symbol directly. Instead, defin= e your
> own error symbols for each error category using `define-error'. On= ly
> then clients have the chance to catch and handle errors.
OK. Done. Though technically they can already catch and handle
them. What they can't do is catch *only* JSONRPC errors and let the
others thru. I happen to think that's a bad idea because a JSONRPC erro= r
is an error in your program. But there's no reason to deny this, too.
> - Please make it possible to run servers remotely. Unfortunately this<= br> > isn't possible with `make-process', so you have to use
> `start-file-process' instead if `default-directory' is remote.= As an
> alternative to using `default-directory', consider passing a
> REMOTE-DIRECTORY argument to `jrpc-connect' so that the behavior > doesn't depend on ambient context.
Maybe. I don't fully understand the example. But jsonrpc-connect allows= you to
pass any pre-connected process object. It will replace its buffer,
sentinel and filter. In the "autostart-server" branch of eglot.el= , for
example, there is a function to I start a local process with special
arguments to that it open a server, then connect to it, then return that
connected process. This is for Windows LSP servers that can't to stdio<= br> comms.
> - I think many of the names can be internal (i.e. contain a double
> dash) unless they are explicitly part of the public API.
Maybe. What names? The names that I want to make public are already
public, and all the others aren't.
> - Probably jrpc-async-request should just return nil. Or is there a
> situation where callers have to know about IDs or timers? I think
> those should be treated as implementation details and not be exposed > to clients.
OK. Makes sense.
> - Please use hashtables or alists instead of plists. There are many
> more functions to operate on hashtables and alists,
Maybe. Hmm, I really really don't know about this. Have you tried
cl-destructuring-bind with plists? It really is the SH&T for JSON.
<= /blockquote>Not really, how would it work? Typically I = use plain gethash, assq, etc. Or `let-alist'.=C2=A0
> and json.c doesn't support plists.
That's the superior argument. I didn't know about json.c until very=
recently. I'll play along, but can you help me rewrite jsonrpc-lambda t= o
something less consing perhaps?
(cl-defmacro jsonrpc-lambda (cl-lambda-list &body body)
=C2=A0 (declare (indent 1) (debug (sexp &rest form)))
=C2=A0 (let ((jsonobj (gensym "jsonrpc-lambda-elem")))
=C2=A0 =C2=A0 `(lambda (,jsonobj)
=C2=A0 =C2=A0 =C2=A0 =C2=A0(apply (cl-function (lambda ,cl-lambda-list ,@bo= dy))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (let (plist)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (maphash (lambda (k= v)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0(push v plist)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0(push (if (keywordp k) k
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(intern (format ":= %s" k)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0plist))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0,jsonobj)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 plist)))))Why do we need jsonrpc-lambda? Writing a normal= lambda and some gethash or assq calls doesn't sound overly bad to me. = I'd just remove jsonrpc-lambda entirely; it's the kind of syntactic= sugar that I think doesn't make code significantly more readable.=C2=A0
> - The HTTP-style enveloping (using HTTP headers to separate responses)=
> is technically not part of JSONRPC. You could either add support for > other enveloping methods (though I'm not aware of any), or at leas= t
> call this out in the documentation.
OK. Yes, it's most definetly not JSONRPC. It's an LSP thing. This i= s the
reason I need to rework the "process" object into a proper defcla= ss and
make generic functions that only apply this enveloping to the proper
LSPlike objects (which could/should be the default object types returned
by jsonrpc-connect).
> - I think `string-bytes' isn't guaranteed to return the number= of
> UTF-8 bytes. Rather you should encode the string explicitly to a
> unibyte string using `encode-coding-string', and then use
> `no-conversion' for the process coding systems.
>
I think Eli has already answered this authoritatively.
> - Consider also sending (and parsing) a Content-Type header:
> Content-Type: application/vscode-jsonrpc; charset=3Dutf-8.
OK.
> - I don't quite understand how `jrpc-ready-predicates' is supp= osed to
> be used. Are clients supposed to have some form of server-specific
> sidechannel to detect when the server is ready?
Yes that's precisely it (though it's entirely optional on the clien= t
part).
> It might be simpler to solve this problem in Emacs core, e.g. by
> adding a timeout to `process-send-string'.
No, the "ready" here is for the application, not the transport.
I understand it's obscure for a reader, so let me explain and then this=
might go into the doc.=C2=A0 Obviously, this came out of necessity in
eglot.el, but I think its good functionality to keep in jsonrpc.el
refactor (and it'd be really hard to implement outside it.)If it should stay in jsonrpc.el, then it should be= clear without referring to eglot.el.=C2=A0
The problem arises in the ordering of sending multiple asynchronous
JSONRPC requests. So, for example in LSP, it doesn't make sense to send=
most requests if there are outstanding changes in the buffer that you
haven't told the server about. But because the latter can come in
through idle timers, it's useful to have this generic :deferred
mechanism: you just put in `jsonrpc-ready-predicates' a function that returns nil if there are outstanding changes and then write code without
worrying about whether the server is ready in that particular
situation. This is better than sprinkling the code with
"send-outstanding-changes-just-to-be-sure" before you make reques= ts.Hmm. I need to think a bit more abo= ut this. I guess it makes sense, but please make it a property of the clien= t, not a global variable for now.=C2=A0