2017-02-07 18:28:20 +08:00
|
|
|
//===--- JSONRPCDispatcher.h - Main JSON parser entry point -----*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H
|
|
|
|
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H
|
|
|
|
|
Adds a json::Expr type to represent intermediate JSON expressions.
Summary:
This form can be created with a nice clang-format-friendly literal syntax,
and gets escaping right. It knows how to call unparse() on our Protocol types.
All the places where we pass around JSON internally now use this type.
Object properties are sorted (stored as std::map) and so serialization is
canonicalized, with optional prettyprinting (triggered by a -pretty flag).
This makes the lit tests much nicer to read and somewhat nicer to debug.
(Unfortunately the completion tests use CHECK-DAG, which only has
line-granularity, so pretty-printing is disabled there. In future we
could make completion ordering deterministic, or switch to unittests).
Compared to the current approach, it has some efficiencies like avoiding copies
of string literals used as object keys, but is probably slower overall.
I think the code/test quality benefits are worth it.
This patch doesn't attempt to do anything about JSON *parsing*.
It takes direction from the proposal in this doc[1], but is limited in scope
and visibility, for now.
I am of half a mind just to use Expr as the target of a parser, and maybe do a
little string deduplication, but not bother with clever memory allocation.
That would be simple, and fast enough for clangd...
[1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit
+cc d0k so he can tell me not to use std::map.
Reviewers: ioeric, malaperle
Subscribers: bkramer, ilya-biryukov, mgorny, klimek
Differential Revision: https://reviews.llvm.org/D39435
llvm-svn: 317486
2017-11-06 23:40:30 +08:00
|
|
|
#include "JSONExpr.h"
|
2017-09-20 15:24:15 +08:00
|
|
|
#include "Logger.h"
|
2017-11-07 18:21:02 +08:00
|
|
|
#include "Protocol.h"
|
2017-11-24 01:12:04 +08:00
|
|
|
#include "Trace.h"
|
2017-02-07 18:28:20 +08:00
|
|
|
#include "clang/Basic/LLVM.h"
|
2017-10-12 21:29:58 +08:00
|
|
|
#include "llvm/ADT/SmallString.h"
|
2017-02-07 18:28:20 +08:00
|
|
|
#include "llvm/ADT/StringMap.h"
|
2017-05-16 23:23:55 +08:00
|
|
|
#include <iosfwd>
|
2017-02-10 22:08:40 +08:00
|
|
|
#include <mutex>
|
2017-02-07 18:28:20 +08:00
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace clangd {
|
|
|
|
|
2017-02-10 22:08:40 +08:00
|
|
|
/// Encapsulates output and logs streams and provides thread-safe access to
|
|
|
|
/// them.
|
2017-09-20 15:24:15 +08:00
|
|
|
class JSONOutput : public Logger {
|
2017-12-13 20:51:22 +08:00
|
|
|
// FIXME(ibiryukov): figure out if we can shrink the public interface of
|
|
|
|
// JSONOutput now that we pass Context everywhere.
|
2017-02-10 22:08:40 +08:00
|
|
|
public:
|
2017-10-10 17:08:47 +08:00
|
|
|
JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs,
|
Adds a json::Expr type to represent intermediate JSON expressions.
Summary:
This form can be created with a nice clang-format-friendly literal syntax,
and gets escaping right. It knows how to call unparse() on our Protocol types.
All the places where we pass around JSON internally now use this type.
Object properties are sorted (stored as std::map) and so serialization is
canonicalized, with optional prettyprinting (triggered by a -pretty flag).
This makes the lit tests much nicer to read and somewhat nicer to debug.
(Unfortunately the completion tests use CHECK-DAG, which only has
line-granularity, so pretty-printing is disabled there. In future we
could make completion ordering deterministic, or switch to unittests).
Compared to the current approach, it has some efficiencies like avoiding copies
of string literals used as object keys, but is probably slower overall.
I think the code/test quality benefits are worth it.
This patch doesn't attempt to do anything about JSON *parsing*.
It takes direction from the proposal in this doc[1], but is limited in scope
and visibility, for now.
I am of half a mind just to use Expr as the target of a parser, and maybe do a
little string deduplication, but not bother with clever memory allocation.
That would be simple, and fast enough for clangd...
[1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit
+cc d0k so he can tell me not to use std::map.
Reviewers: ioeric, malaperle
Subscribers: bkramer, ilya-biryukov, mgorny, klimek
Differential Revision: https://reviews.llvm.org/D39435
llvm-svn: 317486
2017-11-06 23:40:30 +08:00
|
|
|
llvm::raw_ostream *InputMirror = nullptr, bool Pretty = false)
|
2017-11-28 17:37:43 +08:00
|
|
|
: Pretty(Pretty), Outs(Outs), Logs(Logs), InputMirror(InputMirror) {}
|
2017-02-10 22:08:40 +08:00
|
|
|
|
|
|
|
/// Emit a JSONRPC message.
|
Adds a json::Expr type to represent intermediate JSON expressions.
Summary:
This form can be created with a nice clang-format-friendly literal syntax,
and gets escaping right. It knows how to call unparse() on our Protocol types.
All the places where we pass around JSON internally now use this type.
Object properties are sorted (stored as std::map) and so serialization is
canonicalized, with optional prettyprinting (triggered by a -pretty flag).
This makes the lit tests much nicer to read and somewhat nicer to debug.
(Unfortunately the completion tests use CHECK-DAG, which only has
line-granularity, so pretty-printing is disabled there. In future we
could make completion ordering deterministic, or switch to unittests).
Compared to the current approach, it has some efficiencies like avoiding copies
of string literals used as object keys, but is probably slower overall.
I think the code/test quality benefits are worth it.
This patch doesn't attempt to do anything about JSON *parsing*.
It takes direction from the proposal in this doc[1], but is limited in scope
and visibility, for now.
I am of half a mind just to use Expr as the target of a parser, and maybe do a
little string deduplication, but not bother with clever memory allocation.
That would be simple, and fast enough for clangd...
[1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit
+cc d0k so he can tell me not to use std::map.
Reviewers: ioeric, malaperle
Subscribers: bkramer, ilya-biryukov, mgorny, klimek
Differential Revision: https://reviews.llvm.org/D39435
llvm-svn: 317486
2017-11-06 23:40:30 +08:00
|
|
|
void writeMessage(const json::Expr &Result);
|
2017-02-10 22:08:40 +08:00
|
|
|
|
2017-12-01 07:21:34 +08:00
|
|
|
/// Write a line to the logging stream.
|
[clangd] Pass Context implicitly using TLS.
Summary:
Instead of passing Context explicitly around, we now have a thread-local
Context object `Context::current()` which is an implicit argument to
every function.
Most manipulation of this should use the WithContextValue helper, which
augments the current Context to add a single KV pair, and restores the
old context on destruction.
Advantages are:
- less boilerplate in functions that just propagate contexts
- reading most code doesn't require understanding context at all, and
using context as values in fewer places still
- fewer options to pass the "wrong" context when it changes within a
scope (e.g. when using Span)
- contexts pass through interfaces we can't modify, such as VFS
- propagating contexts across threads was slightly tricky (e.g.
copy vs move, no move-init in lambdas), and is now encapsulated in
the threadpool
Disadvantages are all the usual TLS stuff - hidden magic, and
potential for higher memory usage on threads that don't use the
context. (In practice, it's just one pointer)
Reviewers: ilya-biryukov
Subscribers: klimek, jkorous-apple, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D42517
llvm-svn: 323872
2018-01-31 21:40:48 +08:00
|
|
|
void log(const Twine &Message) override;
|
2017-02-10 22:08:40 +08:00
|
|
|
|
2017-10-10 17:08:47 +08:00
|
|
|
/// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is
|
|
|
|
/// null.
|
|
|
|
/// Unlike other methods of JSONOutput, mirrorInput is not thread-safe.
|
|
|
|
void mirrorInput(const Twine &Message);
|
|
|
|
|
2017-11-28 17:37:43 +08:00
|
|
|
// Whether output should be pretty-printed.
|
|
|
|
const bool Pretty;
|
|
|
|
|
2017-02-10 22:08:40 +08:00
|
|
|
private:
|
|
|
|
llvm::raw_ostream &Outs;
|
|
|
|
llvm::raw_ostream &Logs;
|
2017-10-10 17:08:47 +08:00
|
|
|
llvm::raw_ostream *InputMirror;
|
2017-02-10 22:08:40 +08:00
|
|
|
|
|
|
|
std::mutex StreamMutex;
|
|
|
|
};
|
|
|
|
|
[clangd] Pass Context implicitly using TLS.
Summary:
Instead of passing Context explicitly around, we now have a thread-local
Context object `Context::current()` which is an implicit argument to
every function.
Most manipulation of this should use the WithContextValue helper, which
augments the current Context to add a single KV pair, and restores the
old context on destruction.
Advantages are:
- less boilerplate in functions that just propagate contexts
- reading most code doesn't require understanding context at all, and
using context as values in fewer places still
- fewer options to pass the "wrong" context when it changes within a
scope (e.g. when using Span)
- contexts pass through interfaces we can't modify, such as VFS
- propagating contexts across threads was slightly tricky (e.g.
copy vs move, no move-init in lambdas), and is now encapsulated in
the threadpool
Disadvantages are all the usual TLS stuff - hidden magic, and
potential for higher memory usage on threads that don't use the
context. (In practice, it's just one pointer)
Reviewers: ilya-biryukov
Subscribers: klimek, jkorous-apple, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D42517
llvm-svn: 323872
2018-01-31 21:40:48 +08:00
|
|
|
/// Sends a successful reply.
|
|
|
|
/// Current context must derive from JSONRPCDispatcher::Handler.
|
|
|
|
void reply(json::Expr &&Result);
|
|
|
|
/// Sends an error response to the client, and logs it.
|
|
|
|
/// Current context must derive from JSONRPCDispatcher::Handler.
|
|
|
|
void replyError(ErrorCode code, const llvm::StringRef &Message);
|
|
|
|
/// Sends a request to the client.
|
|
|
|
/// Current context must derive from JSONRPCDispatcher::Handler.
|
|
|
|
void call(llvm::StringRef Method, json::Expr &&Params);
|
2017-02-07 18:28:20 +08:00
|
|
|
|
|
|
|
/// Main JSONRPC entry point. This parses the JSONRPC "header" and calls the
|
|
|
|
/// registered Handler for the method received.
|
|
|
|
class JSONRPCDispatcher {
|
|
|
|
public:
|
2017-10-12 21:29:58 +08:00
|
|
|
// A handler responds to requests for a particular method name.
|
[clangd] Pass Context implicitly using TLS.
Summary:
Instead of passing Context explicitly around, we now have a thread-local
Context object `Context::current()` which is an implicit argument to
every function.
Most manipulation of this should use the WithContextValue helper, which
augments the current Context to add a single KV pair, and restores the
old context on destruction.
Advantages are:
- less boilerplate in functions that just propagate contexts
- reading most code doesn't require understanding context at all, and
using context as values in fewer places still
- fewer options to pass the "wrong" context when it changes within a
scope (e.g. when using Span)
- contexts pass through interfaces we can't modify, such as VFS
- propagating contexts across threads was slightly tricky (e.g.
copy vs move, no move-init in lambdas), and is now encapsulated in
the threadpool
Disadvantages are all the usual TLS stuff - hidden magic, and
potential for higher memory usage on threads that don't use the
context. (In practice, it's just one pointer)
Reviewers: ilya-biryukov
Subscribers: klimek, jkorous-apple, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D42517
llvm-svn: 323872
2018-01-31 21:40:48 +08:00
|
|
|
using Handler = std::function<void(const json::Expr &)>;
|
2017-10-12 21:29:58 +08:00
|
|
|
|
2017-02-07 18:28:20 +08:00
|
|
|
/// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown
|
|
|
|
/// method is received.
|
2017-10-12 21:29:58 +08:00
|
|
|
JSONRPCDispatcher(Handler UnknownHandler)
|
2017-02-07 18:28:20 +08:00
|
|
|
: UnknownHandler(std::move(UnknownHandler)) {}
|
|
|
|
|
|
|
|
/// Registers a Handler for the specified Method.
|
2017-10-12 21:29:58 +08:00
|
|
|
void registerHandler(StringRef Method, Handler H);
|
2017-02-07 18:28:20 +08:00
|
|
|
|
|
|
|
/// Parses a JSONRPC message and calls the Handler for it.
|
2017-11-28 17:37:43 +08:00
|
|
|
bool call(const json::Expr &Message, JSONOutput &Out) const;
|
2017-02-07 18:28:20 +08:00
|
|
|
|
|
|
|
private:
|
2017-10-12 21:29:58 +08:00
|
|
|
llvm::StringMap<Handler> Handlers;
|
|
|
|
Handler UnknownHandler;
|
2017-02-07 18:28:20 +08:00
|
|
|
};
|
|
|
|
|
2018-02-06 18:47:30 +08:00
|
|
|
/// Controls the way JSON-RPC messages are encoded (both input and output).
|
|
|
|
enum JSONStreamStyle {
|
|
|
|
/// Encoding per the LSP specification, with mandatory Content-Length header.
|
|
|
|
Standard,
|
|
|
|
/// Messages are delimited by a '---' line. Comment lines start with #.
|
|
|
|
Delimited
|
|
|
|
};
|
|
|
|
|
2017-05-16 22:40:30 +08:00
|
|
|
/// Parses input queries from LSP client (coming from \p In) and runs call
|
|
|
|
/// method of \p Dispatcher for each query.
|
|
|
|
/// After handling each query checks if \p IsDone is set true and exits the loop
|
|
|
|
/// if it is.
|
|
|
|
/// Input stream(\p In) must be opened in binary mode to avoid preliminary
|
|
|
|
/// replacements of \r\n with \n.
|
|
|
|
void runLanguageServerLoop(std::istream &In, JSONOutput &Out,
|
2018-02-06 18:47:30 +08:00
|
|
|
JSONStreamStyle InputStyle,
|
2017-05-16 22:40:30 +08:00
|
|
|
JSONRPCDispatcher &Dispatcher, bool &IsDone);
|
|
|
|
|
2017-02-07 18:28:20 +08:00
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|
|
|
|
|
|
|
|
#endif
|