[clangd] Refactor stream output into a single thread-safe output object.

This abstracts away the passing of raw_ostreams everywhere, thread
safety will be used soon.

llvm-svn: 294747
This commit is contained in:
Benjamin Kramer 2017-02-10 14:08:40 +00:00
parent c371159aac
commit d0b2ccd219
5 changed files with 63 additions and 39 deletions

View File

@ -19,6 +19,7 @@ using namespace clang::clangd;
int main(int argc, char *argv[]) {
llvm::raw_ostream &Outs = llvm::outs();
llvm::raw_ostream &Logs = llvm::errs();
JSONOutput Out(Outs, Logs);
// Change stdin to binary to not lose \r\n on windows.
llvm::sys::ChangeStdinToBinary();
@ -26,24 +27,24 @@ int main(int argc, char *argv[]) {
// Set up a document store and intialize all the method handlers for JSONRPC
// dispatching.
DocumentStore Store;
JSONRPCDispatcher Dispatcher(llvm::make_unique<Handler>(Outs, Logs));
JSONRPCDispatcher Dispatcher(llvm::make_unique<Handler>(Out));
Dispatcher.registerHandler("initialize",
llvm::make_unique<InitializeHandler>(Outs, Logs));
llvm::make_unique<InitializeHandler>(Out));
Dispatcher.registerHandler("shutdown",
llvm::make_unique<ShutdownHandler>(Outs, Logs));
llvm::make_unique<ShutdownHandler>(Out));
Dispatcher.registerHandler(
"textDocument/didOpen",
llvm::make_unique<TextDocumentDidOpenHandler>(Outs, Logs, Store));
llvm::make_unique<TextDocumentDidOpenHandler>(Out, Store));
// FIXME: Implement textDocument/didClose.
Dispatcher.registerHandler(
"textDocument/didChange",
llvm::make_unique<TextDocumentDidChangeHandler>(Outs, Logs, Store));
llvm::make_unique<TextDocumentDidChangeHandler>(Out, Store));
Dispatcher.registerHandler(
"textDocument/rangeFormatting",
llvm::make_unique<TextDocumentRangeFormattingHandler>(Outs, Logs, Store));
llvm::make_unique<TextDocumentRangeFormattingHandler>(Out, Store));
Dispatcher.registerHandler(
"textDocument/formatting",
llvm::make_unique<TextDocumentFormattingHandler>(Outs, Logs, Store));
llvm::make_unique<TextDocumentFormattingHandler>(Out, Store));
while (std::cin.good()) {
// A Language Server Protocol message starts with a HTTP header, delimited
@ -89,6 +90,10 @@ int main(int argc, char *argv[]) {
// Finally, execute the action for this JSON message.
if (!Dispatcher.call(JSONRef))
Logs << "JSON dispatch failed!\n";
// If we're done, exit the loop.
if (Out.isDone())
break;
}
}
}

View File

@ -15,10 +15,11 @@
using namespace clang;
using namespace clangd;
void Handler::writeMessage(const Twine &Message) {
void JSONOutput::writeMessage(const Twine &Message) {
llvm::SmallString<128> Storage;
StringRef M = Message.toStringRef(Storage);
std::lock_guard<std::mutex> Guard(StreamMutex);
// Log without headers.
Logs << "--> " << M << '\n';
Logs.flush();
@ -29,7 +30,7 @@ void Handler::writeMessage(const Twine &Message) {
}
void Handler::handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) {
Logs << "Method ignored.\n";
Output.logs() << "Method ignored.\n";
// Return that this method is unsupported.
writeMessage(
R"({"jsonrpc":"2.0","id":)" + ID +
@ -37,7 +38,7 @@ void Handler::handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) {
}
void Handler::handleNotification(llvm::yaml::MappingNode *Params) {
Logs << "Notification ignored.\n";
Output.logs() << "Notification ignored.\n";
}
void JSONRPCDispatcher::registerHandler(StringRef Method,

View File

@ -13,15 +13,42 @@
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/YAMLParser.h"
#include <mutex>
namespace clang {
namespace clangd {
/// Encapsulates output and logs streams and provides thread-safe access to
/// them.
class JSONOutput {
public:
JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs)
: Outs(Outs), Logs(Logs) {}
/// Emit a JSONRPC message.
void writeMessage(const Twine &Message);
/// Get the logging stream.
llvm::raw_ostream &logs() { return Logs; }
/// Use this to indicate that the output stream should be closed and the
/// process should terminate.
void setDone() { Done = true; }
bool isDone() const { return Done; }
private:
llvm::raw_ostream &Outs;
llvm::raw_ostream &Logs;
bool Done = false;
std::mutex StreamMutex;
};
/// Callback for messages sent to the server, called by the JSONRPCDispatcher.
class Handler {
public:
Handler(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs)
: Outs(Outs), Logs(Logs) {}
Handler(JSONOutput &Output) : Output(Output) {}
virtual ~Handler() = default;
/// Called when the server receives a method call. This is supposed to return
@ -33,11 +60,10 @@ public:
virtual void handleNotification(llvm::yaml::MappingNode *Params);
protected:
llvm::raw_ostream &Outs;
llvm::raw_ostream &Logs;
JSONOutput &Output;
/// Helper to write a JSONRPC result to Outs.
void writeMessage(const Twine &Message);
/// Helper to write a JSONRPC result to Output.
void writeMessage(const Twine &Message) { Output.writeMessage(Message); }
};
/// Main JSONRPC entry point. This parses the JSONRPC "header" and calls the

View File

@ -17,7 +17,7 @@ void TextDocumentDidOpenHandler::handleNotification(
llvm::yaml::MappingNode *Params) {
auto DOTDP = DidOpenTextDocumentParams::parse(Params);
if (!DOTDP) {
Logs << "Failed to decode DidOpenTextDocumentParams!\n";
Output.logs() << "Failed to decode DidOpenTextDocumentParams!\n";
return;
}
Store.addDocument(DOTDP->textDocument.uri, DOTDP->textDocument.text);
@ -27,7 +27,7 @@ void TextDocumentDidChangeHandler::handleNotification(
llvm::yaml::MappingNode *Params) {
auto DCTDP = DidChangeTextDocumentParams::parse(Params);
if (!DCTDP || DCTDP->contentChanges.size() != 1) {
Logs << "Failed to decode DidChangeTextDocumentParams!\n";
Output.logs() << "Failed to decode DidChangeTextDocumentParams!\n";
return;
}
// We only support full syncing right now.
@ -91,7 +91,7 @@ void TextDocumentRangeFormattingHandler::handleMethod(
llvm::yaml::MappingNode *Params, StringRef ID) {
auto DRFP = DocumentRangeFormattingParams::parse(Params);
if (!DRFP) {
Logs << "Failed to decode DocumentRangeFormattingParams!\n";
Output.logs() << "Failed to decode DocumentRangeFormattingParams!\n";
return;
}
@ -108,7 +108,7 @@ void TextDocumentFormattingHandler::handleMethod(
llvm::yaml::MappingNode *Params, StringRef ID) {
auto DFP = DocumentFormattingParams::parse(Params);
if (!DFP) {
Logs << "Failed to decode DocumentFormattingParams!\n";
Output.logs() << "Failed to decode DocumentFormattingParams!\n";
return;
}

View File

@ -25,8 +25,7 @@ namespace clangd {
class DocumentStore;
struct InitializeHandler : Handler {
InitializeHandler(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs)
: Handler(Outs, Logs) {}
InitializeHandler(JSONOutput &Output) : Handler(Output) {}
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
writeMessage(
@ -40,19 +39,16 @@ struct InitializeHandler : Handler {
};
struct ShutdownHandler : Handler {
ShutdownHandler(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs)
: Handler(Outs, Logs) {}
ShutdownHandler(JSONOutput &Output) : Handler(Output) {}
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
// FIXME: Calling exit is rude, can we communicate to main somehow?
exit(0);
Output.setDone();
}
};
struct TextDocumentDidOpenHandler : Handler {
TextDocumentDidOpenHandler(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs,
DocumentStore &Store)
: Handler(Outs, Logs), Store(Store) {}
TextDocumentDidOpenHandler(JSONOutput &Output, DocumentStore &Store)
: Handler(Output), Store(Store) {}
void handleNotification(llvm::yaml::MappingNode *Params) override;
@ -61,9 +57,8 @@ private:
};
struct TextDocumentDidChangeHandler : Handler {
TextDocumentDidChangeHandler(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs,
DocumentStore &Store)
: Handler(Outs, Logs), Store(Store) {}
TextDocumentDidChangeHandler(JSONOutput &Output, DocumentStore &Store)
: Handler(Output), Store(Store) {}
void handleNotification(llvm::yaml::MappingNode *Params) override;
@ -72,10 +67,8 @@ private:
};
struct TextDocumentRangeFormattingHandler : Handler {
TextDocumentRangeFormattingHandler(llvm::raw_ostream &Outs,
llvm::raw_ostream &Logs,
DocumentStore &Store)
: Handler(Outs, Logs), Store(Store) {}
TextDocumentRangeFormattingHandler(JSONOutput &Output, DocumentStore &Store)
: Handler(Output), Store(Store) {}
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override;
@ -84,9 +77,8 @@ private:
};
struct TextDocumentFormattingHandler : Handler {
TextDocumentFormattingHandler(llvm::raw_ostream &Outs,
llvm::raw_ostream &Logs, DocumentStore &Store)
: Handler(Outs, Logs), Store(Store) {}
TextDocumentFormattingHandler(JSONOutput &Output, DocumentStore &Store)
: Handler(Output), Store(Store) {}
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override;