forked from OSchip/llvm-project
[clangd] less boilerplate in RPC dispatch
Summary: Make the ProtocolHandlers glue between JSONRPCDispatcher and ClangdLSPServer generic. Eliminate small differences between methods, de-emphasize the unimportant distinction between notifications and methods. ClangdLSPServer is no longer responsible for producing a complete JSON-RPC response, just the JSON of the result object. (In future, we should move that JSON serialization out, too). Handler methods now take a context object that we may hang more functionality off in the future. Added documentation to ProtocolHandlers. Reviewers: ilya-biryukov, bkramer Reviewed By: ilya-biryukov Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D38464 llvm-svn: 315577
This commit is contained in:
parent
4d93120273
commit
8a5dded8a5
|
@ -37,11 +37,9 @@ replacementsToEdits(StringRef Code,
|
|||
|
||||
} // namespace
|
||||
|
||||
void ClangdLSPServer::onInitialize(StringRef ID, InitializeParams IP,
|
||||
JSONOutput &Out) {
|
||||
Out.writeMessage(
|
||||
R"({"jsonrpc":"2.0","id":)" + ID +
|
||||
R"(,"result":{"capabilities":{
|
||||
void ClangdLSPServer::onInitialize(Ctx C, InitializeParams &Params) {
|
||||
C.reply(
|
||||
R"({"capabilities":{
|
||||
"textDocumentSync": 1,
|
||||
"documentFormattingProvider": true,
|
||||
"documentRangeFormattingProvider": true,
|
||||
|
@ -50,73 +48,68 @@ void ClangdLSPServer::onInitialize(StringRef ID, InitializeParams IP,
|
|||
"completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">",":"]},
|
||||
"signatureHelpProvider": {"triggerCharacters": ["(",","]},
|
||||
"definitionProvider": true
|
||||
}}})");
|
||||
if (IP.rootUri && !IP.rootUri->file.empty())
|
||||
Server.setRootPath(IP.rootUri->file);
|
||||
else if (IP.rootPath && !IP.rootPath->empty())
|
||||
Server.setRootPath(*IP.rootPath);
|
||||
}})");
|
||||
if (Params.rootUri && !Params.rootUri->file.empty())
|
||||
Server.setRootPath(Params.rootUri->file);
|
||||
else if (Params.rootPath && !Params.rootPath->empty())
|
||||
Server.setRootPath(*Params.rootPath);
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onShutdown(JSONOutput &Out) { IsDone = true; }
|
||||
void ClangdLSPServer::onShutdown(Ctx C, ShutdownParams &Params) {
|
||||
IsDone = true;
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onDocumentDidOpen(DidOpenTextDocumentParams Params,
|
||||
JSONOutput &Out) {
|
||||
void ClangdLSPServer::onDocumentDidOpen(Ctx C,
|
||||
DidOpenTextDocumentParams &Params) {
|
||||
if (Params.metadata && !Params.metadata->extraFlags.empty())
|
||||
CDB.setExtraFlagsForFile(Params.textDocument.uri.file,
|
||||
std::move(Params.metadata->extraFlags));
|
||||
Server.addDocument(Params.textDocument.uri.file, Params.textDocument.text);
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onDocumentDidChange(DidChangeTextDocumentParams Params,
|
||||
JSONOutput &Out) {
|
||||
void ClangdLSPServer::onDocumentDidChange(Ctx C,
|
||||
DidChangeTextDocumentParams &Params) {
|
||||
// We only support full syncing right now.
|
||||
Server.addDocument(Params.textDocument.uri.file,
|
||||
Params.contentChanges[0].text);
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
|
||||
void ClangdLSPServer::onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) {
|
||||
Server.onFileEvent(Params);
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onDocumentDidClose(DidCloseTextDocumentParams Params,
|
||||
JSONOutput &Out) {
|
||||
void ClangdLSPServer::onDocumentDidClose(Ctx C,
|
||||
DidCloseTextDocumentParams &Params) {
|
||||
Server.removeDocument(Params.textDocument.uri.file);
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onDocumentOnTypeFormatting(
|
||||
DocumentOnTypeFormattingParams Params, StringRef ID, JSONOutput &Out) {
|
||||
Ctx C, DocumentOnTypeFormattingParams &Params) {
|
||||
auto File = Params.textDocument.uri.file;
|
||||
std::string Code = Server.getDocument(File);
|
||||
std::string Edits =
|
||||
replacementsToEdits(Code, Server.formatOnType(File, Params.position));
|
||||
|
||||
Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() +
|
||||
R"(,"result":[)" + Edits + R"(]})");
|
||||
C.reply("[" + Edits + "]");
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onDocumentRangeFormatting(
|
||||
DocumentRangeFormattingParams Params, StringRef ID, JSONOutput &Out) {
|
||||
Ctx C, DocumentRangeFormattingParams &Params) {
|
||||
auto File = Params.textDocument.uri.file;
|
||||
std::string Code = Server.getDocument(File);
|
||||
std::string Edits =
|
||||
replacementsToEdits(Code, Server.formatRange(File, Params.range));
|
||||
|
||||
Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() +
|
||||
R"(,"result":[)" + Edits + R"(]})");
|
||||
C.reply("[" + Edits + "]");
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onDocumentFormatting(DocumentFormattingParams Params,
|
||||
StringRef ID, JSONOutput &Out) {
|
||||
void ClangdLSPServer::onDocumentFormatting(Ctx C,
|
||||
DocumentFormattingParams &Params) {
|
||||
auto File = Params.textDocument.uri.file;
|
||||
std::string Code = Server.getDocument(File);
|
||||
std::string Edits = replacementsToEdits(Code, Server.formatFile(File));
|
||||
|
||||
Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() +
|
||||
R"(,"result":[)" + Edits + R"(]})");
|
||||
C.reply("[" + Edits + "]");
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onCodeAction(CodeActionParams Params, StringRef ID,
|
||||
JSONOutput &Out) {
|
||||
void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) {
|
||||
// We provide a code action for each diagnostic at the requested location
|
||||
// which has FixIts available.
|
||||
std::string Code = Server.getDocument(Params.textDocument.uri.file);
|
||||
|
@ -136,16 +129,10 @@ void ClangdLSPServer::onCodeAction(CodeActionParams Params, StringRef ID,
|
|||
}
|
||||
if (!Commands.empty())
|
||||
Commands.pop_back();
|
||||
|
||||
Out.writeMessage(
|
||||
R"({"jsonrpc":"2.0","id":)" + ID.str() +
|
||||
R"(, "result": [)" + Commands +
|
||||
R"(]})");
|
||||
C.reply("[" + Commands + "]");
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onCompletion(TextDocumentPositionParams Params,
|
||||
StringRef ID, JSONOutput &Out) {
|
||||
|
||||
void ClangdLSPServer::onCompletion(Ctx C, TextDocumentPositionParams &Params) {
|
||||
auto Items = Server
|
||||
.codeComplete(Params.textDocument.uri.file,
|
||||
Position{Params.position.line,
|
||||
|
@ -162,26 +149,21 @@ void ClangdLSPServer::onCompletion(TextDocumentPositionParams Params,
|
|||
}
|
||||
if (!Completions.empty())
|
||||
Completions.pop_back();
|
||||
Out.writeMessage(
|
||||
R"({"jsonrpc":"2.0","id":)" + ID.str() +
|
||||
R"(,"result":[)" + Completions + R"(]})");
|
||||
C.reply("[" + Completions + "]");
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onSignatureHelp(TextDocumentPositionParams Params,
|
||||
StringRef ID, JSONOutput &Out) {
|
||||
const auto SigHelp = SignatureHelp::unparse(
|
||||
void ClangdLSPServer::onSignatureHelp(Ctx C,
|
||||
TextDocumentPositionParams &Params) {
|
||||
C.reply(SignatureHelp::unparse(
|
||||
Server
|
||||
.signatureHelp(
|
||||
Params.textDocument.uri.file,
|
||||
Position{Params.position.line, Params.position.character})
|
||||
.Value);
|
||||
Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() + R"(,"result":)" +
|
||||
SigHelp + "}");
|
||||
.Value));
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onGoToDefinition(TextDocumentPositionParams Params,
|
||||
StringRef ID, JSONOutput &Out) {
|
||||
|
||||
void ClangdLSPServer::onGoToDefinition(Ctx C,
|
||||
TextDocumentPositionParams &Params) {
|
||||
auto Items = Server
|
||||
.findDefinitions(Params.textDocument.uri.file,
|
||||
Position{Params.position.line,
|
||||
|
@ -195,23 +177,14 @@ void ClangdLSPServer::onGoToDefinition(TextDocumentPositionParams Params,
|
|||
}
|
||||
if (!Locations.empty())
|
||||
Locations.pop_back();
|
||||
Out.writeMessage(
|
||||
R"({"jsonrpc":"2.0","id":)" + ID.str() +
|
||||
R"(,"result":[)" + Locations + R"(]})");
|
||||
C.reply("[" + Locations + "]");
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onSwitchSourceHeader(TextDocumentIdentifier Params,
|
||||
StringRef ID, JSONOutput &Out) {
|
||||
void ClangdLSPServer::onSwitchSourceHeader(Ctx C,
|
||||
TextDocumentIdentifier &Params) {
|
||||
llvm::Optional<Path> Result = Server.switchSourceHeader(Params.uri.file);
|
||||
std::string ResultUri;
|
||||
if (Result)
|
||||
ResultUri = URI::unparse(URI::fromFile(*Result));
|
||||
else
|
||||
ResultUri = "\"\"";
|
||||
|
||||
Out.writeMessage(
|
||||
R"({"jsonrpc":"2.0","id":)" + ID.str() +
|
||||
R"(,"result":)" + ResultUri + R"(})");
|
||||
C.reply(Result ? URI::unparse(URI::fromFile(*Result)) : R"("")");
|
||||
}
|
||||
|
||||
ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
|
||||
|
@ -226,7 +199,10 @@ void ClangdLSPServer::run(std::istream &In) {
|
|||
assert(!IsDone && "Run was called before");
|
||||
|
||||
// Set up JSONRPCDispatcher.
|
||||
JSONRPCDispatcher Dispatcher(llvm::make_unique<Handler>(Out));
|
||||
JSONRPCDispatcher Dispatcher(
|
||||
[](RequestContext Ctx, llvm::yaml::MappingNode *Params) {
|
||||
Ctx.replyError(-32601, "method not found");
|
||||
});
|
||||
registerCallbackHandlers(Dispatcher, Out, /*Callbacks=*/*this);
|
||||
|
||||
// Run the Language Server loop.
|
||||
|
|
|
@ -48,32 +48,24 @@ private:
|
|||
Tagged<std::vector<DiagWithFixIts>> Diagnostics) override;
|
||||
|
||||
// Implement ProtocolCallbacks.
|
||||
void onInitialize(StringRef ID, InitializeParams IP,
|
||||
JSONOutput &Out) override;
|
||||
void onShutdown(JSONOutput &Out) override;
|
||||
void onDocumentDidOpen(DidOpenTextDocumentParams Params,
|
||||
JSONOutput &Out) override;
|
||||
void onDocumentDidChange(DidChangeTextDocumentParams Params,
|
||||
JSONOutput &Out) override;
|
||||
void onDocumentDidClose(DidCloseTextDocumentParams Params,
|
||||
JSONOutput &Out) override;
|
||||
void onDocumentOnTypeFormatting(DocumentOnTypeFormattingParams Params,
|
||||
StringRef ID, JSONOutput &Out) override;
|
||||
void onDocumentRangeFormatting(DocumentRangeFormattingParams Params,
|
||||
StringRef ID, JSONOutput &Out) override;
|
||||
void onDocumentFormatting(DocumentFormattingParams Params, StringRef ID,
|
||||
JSONOutput &Out) override;
|
||||
void onCodeAction(CodeActionParams Params, StringRef ID,
|
||||
JSONOutput &Out) override;
|
||||
void onCompletion(TextDocumentPositionParams Params, StringRef ID,
|
||||
JSONOutput &Out) override;
|
||||
void onSignatureHelp(TextDocumentPositionParams Params, StringRef ID,
|
||||
JSONOutput &Out) override;
|
||||
void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
|
||||
JSONOutput &Out) override;
|
||||
void onSwitchSourceHeader(TextDocumentIdentifier Params, StringRef ID,
|
||||
JSONOutput &Out) override;
|
||||
void onFileEvent(const DidChangeWatchedFilesParams &Params) override;
|
||||
void onInitialize(Ctx C, InitializeParams &Params) override;
|
||||
void onShutdown(Ctx C, ShutdownParams &Params) override;
|
||||
void onDocumentDidOpen(Ctx C, DidOpenTextDocumentParams &Params) override;
|
||||
void onDocumentDidChange(Ctx C, DidChangeTextDocumentParams &Params) override;
|
||||
void onDocumentDidClose(Ctx C, DidCloseTextDocumentParams &Params) override;
|
||||
void
|
||||
onDocumentOnTypeFormatting(Ctx C,
|
||||
DocumentOnTypeFormattingParams &Params) override;
|
||||
void
|
||||
onDocumentRangeFormatting(Ctx C,
|
||||
DocumentRangeFormattingParams &Params) override;
|
||||
void onDocumentFormatting(Ctx C, DocumentFormattingParams &Params) override;
|
||||
void onCodeAction(Ctx C, CodeActionParams &Params) override;
|
||||
void onCompletion(Ctx C, TextDocumentPositionParams &Params) override;
|
||||
void onSignatureHelp(Ctx C, TextDocumentPositionParams &Params) override;
|
||||
void onGoToDefinition(Ctx C, TextDocumentPositionParams &Params) override;
|
||||
void onSwitchSourceHeader(Ctx C, TextDocumentIdentifier &Params) override;
|
||||
void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) override;
|
||||
|
||||
std::vector<clang::tooling::Replacement>
|
||||
getFixIts(StringRef File, const clangd::Diagnostic &D);
|
||||
|
|
|
@ -45,38 +45,42 @@ void JSONOutput::mirrorInput(const Twine &Message) {
|
|||
InputMirror->flush();
|
||||
}
|
||||
|
||||
void Handler::handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) {
|
||||
Output.log("Method ignored.\n");
|
||||
// Return that this method is unsupported.
|
||||
writeMessage(
|
||||
R"({"jsonrpc":"2.0","id":)" + ID +
|
||||
R"(,"error":{"code":-32601,"message":"method not found"}})");
|
||||
void RequestContext::reply(const llvm::Twine &Result) {
|
||||
if (ID.empty()) {
|
||||
Out.log("Attempted to reply to a notification!\n");
|
||||
return;
|
||||
}
|
||||
Out.writeMessage(llvm::Twine(R"({"jsonrpc":"2.0","id":)") + ID +
|
||||
R"(,"result":)" + Result + "}");
|
||||
}
|
||||
|
||||
void Handler::handleNotification(llvm::yaml::MappingNode *Params) {
|
||||
Output.log("Notification ignored.\n");
|
||||
void RequestContext::replyError(int code, const llvm::StringRef &Message) {
|
||||
Out.log("Error " + llvm::Twine(code) + ": " + Message + "\n");
|
||||
if (!ID.empty()) {
|
||||
Out.writeMessage(llvm::Twine(R"({"jsonrpc":"2.0","id":)") + ID +
|
||||
R"(,"error":{"code":)" + llvm::Twine(code) +
|
||||
R"(,"message":")" + llvm::yaml::escape(Message) +
|
||||
R"("}})");
|
||||
}
|
||||
}
|
||||
|
||||
void JSONRPCDispatcher::registerHandler(StringRef Method,
|
||||
std::unique_ptr<Handler> H) {
|
||||
void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) {
|
||||
assert(!Handlers.count(Method) && "Handler already registered!");
|
||||
Handlers[Method] = std::move(H);
|
||||
}
|
||||
|
||||
static void
|
||||
callHandler(const llvm::StringMap<std::unique_ptr<Handler>> &Handlers,
|
||||
callHandler(const llvm::StringMap<JSONRPCDispatcher::Handler> &Handlers,
|
||||
llvm::yaml::ScalarNode *Method, llvm::yaml::ScalarNode *Id,
|
||||
llvm::yaml::MappingNode *Params, Handler *UnknownHandler) {
|
||||
llvm::SmallString<10> MethodStorage;
|
||||
llvm::yaml::MappingNode *Params,
|
||||
const JSONRPCDispatcher::Handler &UnknownHandler, JSONOutput &Out) {
|
||||
llvm::SmallString<64> MethodStorage;
|
||||
auto I = Handlers.find(Method->getValue(MethodStorage));
|
||||
auto *Handler = I != Handlers.end() ? I->second.get() : UnknownHandler;
|
||||
if (Id)
|
||||
Handler->handleMethod(Params, Id->getRawValue());
|
||||
else
|
||||
Handler->handleNotification(Params);
|
||||
auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
|
||||
Handler(RequestContext(Out, Id ? Id->getRawValue() : ""), Params);
|
||||
}
|
||||
|
||||
bool JSONRPCDispatcher::call(StringRef Content) const {
|
||||
bool JSONRPCDispatcher::call(StringRef Content, JSONOutput &Out) const {
|
||||
llvm::SourceMgr SM;
|
||||
llvm::yaml::Stream YAMLStream(Content, SM);
|
||||
|
||||
|
@ -124,7 +128,7 @@ bool JSONRPCDispatcher::call(StringRef Content) const {
|
|||
// because it will break clients that put the id after params. A possible
|
||||
// fix would be to split the parsing and execution phases.
|
||||
Params = dyn_cast<llvm::yaml::MappingNode>(Value);
|
||||
callHandler(Handlers, Method, Id, Params, UnknownHandler.get());
|
||||
callHandler(Handlers, Method, Id, Params, UnknownHandler, Out);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -135,7 +139,7 @@ bool JSONRPCDispatcher::call(StringRef Content) const {
|
|||
// leftovers.
|
||||
if (!Method)
|
||||
return false;
|
||||
callHandler(Handlers, Method, Id, nullptr, UnknownHandler.get());
|
||||
callHandler(Handlers, Method, Id, nullptr, UnknownHandler, Out);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -215,7 +219,7 @@ void clangd::runLanguageServerLoop(std::istream &In, JSONOutput &Out,
|
|||
Out.log("<-- " + JSONRef + "\n");
|
||||
|
||||
// Finally, execute the action for this JSON message.
|
||||
if (!Dispatcher.call(JSONRef))
|
||||
if (!Dispatcher.call(JSONRef, Out))
|
||||
Out.log("JSON dispatch failed!\n");
|
||||
|
||||
// If we're done, exit the loop.
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "Logger.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/Support/YAMLParser.h"
|
||||
#include <iosfwd>
|
||||
|
@ -32,6 +33,7 @@ public:
|
|||
void writeMessage(const Twine &Message);
|
||||
|
||||
/// Write to the logging stream.
|
||||
/// No newline is implicitly added. (TODO: we should fix this!)
|
||||
void log(const Twine &Message) override;
|
||||
|
||||
/// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is
|
||||
|
@ -47,45 +49,43 @@ private:
|
|||
std::mutex StreamMutex;
|
||||
};
|
||||
|
||||
/// Callback for messages sent to the server, called by the JSONRPCDispatcher.
|
||||
class Handler {
|
||||
/// Context object passed to handlers to allow replies.
|
||||
class RequestContext {
|
||||
public:
|
||||
Handler(JSONOutput &Output) : Output(Output) {}
|
||||
virtual ~Handler() = default;
|
||||
RequestContext(JSONOutput &Out, StringRef ID) : Out(Out), ID(ID) {}
|
||||
|
||||
/// Called when the server receives a method call. This is supposed to return
|
||||
/// a result on Outs. The default implementation returns an "unknown method"
|
||||
/// error to the client and logs a warning.
|
||||
virtual void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID);
|
||||
/// Called when the server receives a notification. No result should be
|
||||
/// written to Outs. The default implemetation logs a warning.
|
||||
virtual void handleNotification(llvm::yaml::MappingNode *Params);
|
||||
/// Sends a successful reply. Result should be well-formed JSON.
|
||||
void reply(const Twine &Result);
|
||||
/// Sends an error response to the client, and logs it.
|
||||
void replyError(int code, const llvm::StringRef &Message);
|
||||
|
||||
protected:
|
||||
JSONOutput &Output;
|
||||
|
||||
/// Helper to write a JSONRPC result to Output.
|
||||
void writeMessage(const Twine &Message) { Output.writeMessage(Message); }
|
||||
private:
|
||||
JSONOutput &Out;
|
||||
llvm::SmallString<64> ID; // Valid JSON, or empty for a notification.
|
||||
};
|
||||
|
||||
/// Main JSONRPC entry point. This parses the JSONRPC "header" and calls the
|
||||
/// registered Handler for the method received.
|
||||
class JSONRPCDispatcher {
|
||||
public:
|
||||
// A handler responds to requests for a particular method name.
|
||||
using Handler =
|
||||
std::function<void(RequestContext, llvm::yaml::MappingNode *)>;
|
||||
|
||||
/// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown
|
||||
/// method is received.
|
||||
JSONRPCDispatcher(std::unique_ptr<Handler> UnknownHandler)
|
||||
JSONRPCDispatcher(Handler UnknownHandler)
|
||||
: UnknownHandler(std::move(UnknownHandler)) {}
|
||||
|
||||
/// Registers a Handler for the specified Method.
|
||||
void registerHandler(StringRef Method, std::unique_ptr<Handler> H);
|
||||
void registerHandler(StringRef Method, Handler H);
|
||||
|
||||
/// Parses a JSONRPC message and calls the Handler for it.
|
||||
bool call(StringRef Content) const;
|
||||
bool call(StringRef Content, JSONOutput &Out) const;
|
||||
|
||||
private:
|
||||
llvm::StringMap<std::unique_ptr<Handler>> Handlers;
|
||||
std::unique_ptr<Handler> UnknownHandler;
|
||||
llvm::StringMap<Handler> Handlers;
|
||||
Handler UnknownHandler;
|
||||
};
|
||||
|
||||
/// Parses input queries from LSP client (coming from \p In) and runs call
|
||||
|
|
|
@ -305,11 +305,16 @@ TraceLevel getTraceLevel(llvm::StringRef TraceLevelStr,
|
|||
llvm::Optional<InitializeParams>
|
||||
InitializeParams::parse(llvm::yaml::MappingNode *Params,
|
||||
clangd::Logger &Logger) {
|
||||
// If we don't understand the params, proceed with default parameters.
|
||||
auto ParseFailure = [&] {
|
||||
Logger.log("Failed to decode InitializeParams\n");
|
||||
return InitializeParams();
|
||||
};
|
||||
InitializeParams Result;
|
||||
for (auto &NextKeyValue : *Params) {
|
||||
auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
|
||||
if (!KeyString)
|
||||
return llvm::None;
|
||||
return ParseFailure();
|
||||
|
||||
llvm::SmallString<10> KeyStorage;
|
||||
StringRef KeyValue = KeyString->getValue(KeyStorage);
|
||||
|
@ -322,10 +327,10 @@ InitializeParams::parse(llvm::yaml::MappingNode *Params,
|
|||
auto *Value =
|
||||
dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
|
||||
if (!Value)
|
||||
return llvm::None;
|
||||
return ParseFailure();
|
||||
long long Val;
|
||||
if (llvm::getAsSignedInteger(Value->getValue(KeyStorage), 0, Val))
|
||||
return llvm::None;
|
||||
return ParseFailure();
|
||||
Result.processId = Val;
|
||||
} else if (KeyValue == "rootPath") {
|
||||
Result.rootPath = Value->getValue(KeyStorage);
|
||||
|
|
|
@ -166,6 +166,14 @@ enum class TraceLevel {
|
|||
Verbose = 2,
|
||||
};
|
||||
|
||||
struct NoParams {
|
||||
static llvm::Optional<NoParams> parse(llvm::yaml::MappingNode *Params,
|
||||
Logger &Logger) {
|
||||
return NoParams{};
|
||||
}
|
||||
};
|
||||
using ShutdownParams = NoParams;
|
||||
|
||||
struct InitializeParams {
|
||||
/// The process Id of the parent process that started
|
||||
/// the server. Is null if the process has not been started by another
|
||||
|
|
|
@ -11,255 +11,36 @@
|
|||
#include "ClangdLSPServer.h"
|
||||
#include "ClangdServer.h"
|
||||
#include "DraftStore.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace clangd;
|
||||
using namespace clang::clangd;
|
||||
|
||||
namespace {
|
||||
|
||||
struct InitializeHandler : Handler {
|
||||
InitializeHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
|
||||
auto IP = InitializeParams::parse(Params, Output);
|
||||
if (!IP) {
|
||||
Output.log("Failed to decode InitializeParams!\n");
|
||||
IP = InitializeParams();
|
||||
}
|
||||
|
||||
Callbacks.onInitialize(ID, *IP, Output);
|
||||
// Helper for attaching ProtocolCallbacks methods to a JSONRPCDispatcher.
|
||||
// Invoke like: Registerer("foo", &ProtocolCallbacks::onFoo)
|
||||
// onFoo should be: void onFoo(Ctx &C, FooParams &Params)
|
||||
// FooParams should have a static factory method: parse(yaml::MappingNode*).
|
||||
struct HandlerRegisterer {
|
||||
template <typename Param>
|
||||
void operator()(StringRef Method,
|
||||
void (ProtocolCallbacks::*Handler)(RequestContext, Param)) {
|
||||
// Capture pointers by value, as the lambda will outlive this object.
|
||||
auto *Out = this->Out;
|
||||
auto *Callbacks = this->Callbacks;
|
||||
Dispatcher.registerHandler(
|
||||
Method, [=](RequestContext C, llvm::yaml::MappingNode *RawParams) {
|
||||
if (auto P = std::decay<Param>::type::parse(RawParams, *Out)) {
|
||||
(Callbacks->*Handler)(std::move(C), *P);
|
||||
} else {
|
||||
Out->log("Failed to decode " + Method + " request.\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
};
|
||||
|
||||
struct ShutdownHandler : Handler {
|
||||
ShutdownHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
|
||||
Callbacks.onShutdown(Output);
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
};
|
||||
|
||||
struct TextDocumentDidOpenHandler : Handler {
|
||||
TextDocumentDidOpenHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleNotification(llvm::yaml::MappingNode *Params) override {
|
||||
auto DOTDP = DidOpenTextDocumentParams::parse(Params, Output);
|
||||
if (!DOTDP) {
|
||||
Output.log("Failed to decode DidOpenTextDocumentParams!\n");
|
||||
return;
|
||||
}
|
||||
Callbacks.onDocumentDidOpen(*DOTDP, Output);
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
};
|
||||
|
||||
struct TextDocumentDidChangeHandler : Handler {
|
||||
TextDocumentDidChangeHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleNotification(llvm::yaml::MappingNode *Params) override {
|
||||
auto DCTDP = DidChangeTextDocumentParams::parse(Params, Output);
|
||||
if (!DCTDP || DCTDP->contentChanges.size() != 1) {
|
||||
Output.log("Failed to decode DidChangeTextDocumentParams!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Callbacks.onDocumentDidChange(*DCTDP, Output);
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
};
|
||||
|
||||
struct TextDocumentDidCloseHandler : Handler {
|
||||
TextDocumentDidCloseHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleNotification(llvm::yaml::MappingNode *Params) override {
|
||||
auto DCTDP = DidCloseTextDocumentParams::parse(Params, Output);
|
||||
if (!DCTDP) {
|
||||
Output.log("Failed to decode DidCloseTextDocumentParams!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Callbacks.onDocumentDidClose(*DCTDP, Output);
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
};
|
||||
|
||||
struct TextDocumentOnTypeFormattingHandler : Handler {
|
||||
TextDocumentOnTypeFormattingHandler(JSONOutput &Output,
|
||||
ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
|
||||
auto DOTFP = DocumentOnTypeFormattingParams::parse(Params, Output);
|
||||
if (!DOTFP) {
|
||||
Output.log("Failed to decode DocumentOnTypeFormattingParams!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Callbacks.onDocumentOnTypeFormatting(*DOTFP, ID, Output);
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
};
|
||||
|
||||
struct TextDocumentRangeFormattingHandler : Handler {
|
||||
TextDocumentRangeFormattingHandler(JSONOutput &Output,
|
||||
ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
|
||||
auto DRFP = DocumentRangeFormattingParams::parse(Params, Output);
|
||||
if (!DRFP) {
|
||||
Output.log("Failed to decode DocumentRangeFormattingParams!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Callbacks.onDocumentRangeFormatting(*DRFP, ID, Output);
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
};
|
||||
|
||||
struct TextDocumentFormattingHandler : Handler {
|
||||
TextDocumentFormattingHandler(JSONOutput &Output,
|
||||
ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
|
||||
auto DFP = DocumentFormattingParams::parse(Params, Output);
|
||||
if (!DFP) {
|
||||
Output.log("Failed to decode DocumentFormattingParams!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Callbacks.onDocumentFormatting(*DFP, ID, Output);
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
};
|
||||
|
||||
struct CodeActionHandler : Handler {
|
||||
CodeActionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
|
||||
auto CAP = CodeActionParams::parse(Params, Output);
|
||||
if (!CAP) {
|
||||
Output.log("Failed to decode CodeActionParams!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Callbacks.onCodeAction(*CAP, ID, Output);
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
};
|
||||
|
||||
struct CompletionHandler : Handler {
|
||||
CompletionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
|
||||
auto TDPP = TextDocumentPositionParams::parse(Params, Output);
|
||||
if (!TDPP) {
|
||||
Output.log("Failed to decode TextDocumentPositionParams!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Callbacks.onCompletion(*TDPP, ID, Output);
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
};
|
||||
|
||||
struct SignatureHelpHandler : Handler {
|
||||
SignatureHelpHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
|
||||
auto TDPP = TextDocumentPositionParams::parse(Params, Output);
|
||||
if (!TDPP) {
|
||||
Output.log("Failed to decode TextDocumentPositionParams!\n");
|
||||
return;
|
||||
}
|
||||
Callbacks.onSignatureHelp(*TDPP, ID, Output);
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
};
|
||||
|
||||
struct GotoDefinitionHandler : Handler {
|
||||
GotoDefinitionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
|
||||
auto TDPP = TextDocumentPositionParams::parse(Params, Output);
|
||||
if (!TDPP) {
|
||||
Output.log("Failed to decode TextDocumentPositionParams!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Callbacks.onGoToDefinition(*TDPP, ID, Output);
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
};
|
||||
|
||||
struct SwitchSourceHeaderHandler : Handler {
|
||||
SwitchSourceHeaderHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
|
||||
auto TDPP = TextDocumentIdentifier::parse(Params, Output);
|
||||
if (!TDPP)
|
||||
return;
|
||||
|
||||
Callbacks.onSwitchSourceHeader(*TDPP, ID, Output);
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
};
|
||||
|
||||
struct WorkspaceDidChangeWatchedFilesHandler : Handler {
|
||||
WorkspaceDidChangeWatchedFilesHandler(JSONOutput &Output,
|
||||
ProtocolCallbacks &Callbacks)
|
||||
: Handler(Output), Callbacks(Callbacks) {}
|
||||
|
||||
void handleNotification(llvm::yaml::MappingNode *Params) {
|
||||
auto DCWFP = DidChangeWatchedFilesParams::parse(Params, Output);
|
||||
if (!DCWFP) {
|
||||
Output.log("Failed to decode DidChangeWatchedFilesParams.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Callbacks.onFileEvent(*DCWFP);
|
||||
}
|
||||
|
||||
private:
|
||||
ProtocolCallbacks &Callbacks;
|
||||
JSONRPCDispatcher &Dispatcher;
|
||||
JSONOutput *Out;
|
||||
ProtocolCallbacks *Callbacks;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -267,44 +48,23 @@ private:
|
|||
void clangd::registerCallbackHandlers(JSONRPCDispatcher &Dispatcher,
|
||||
JSONOutput &Out,
|
||||
ProtocolCallbacks &Callbacks) {
|
||||
Dispatcher.registerHandler(
|
||||
"initialize", llvm::make_unique<InitializeHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler(
|
||||
"shutdown", llvm::make_unique<ShutdownHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler(
|
||||
"textDocument/didOpen",
|
||||
llvm::make_unique<TextDocumentDidOpenHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler(
|
||||
"textDocument/didClose",
|
||||
llvm::make_unique<TextDocumentDidCloseHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler(
|
||||
"textDocument/didChange",
|
||||
llvm::make_unique<TextDocumentDidChangeHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler(
|
||||
"textDocument/rangeFormatting",
|
||||
llvm::make_unique<TextDocumentRangeFormattingHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler(
|
||||
"textDocument/onTypeFormatting",
|
||||
llvm::make_unique<TextDocumentOnTypeFormattingHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler(
|
||||
"textDocument/formatting",
|
||||
llvm::make_unique<TextDocumentFormattingHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler(
|
||||
"textDocument/codeAction",
|
||||
llvm::make_unique<CodeActionHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler(
|
||||
"textDocument/completion",
|
||||
llvm::make_unique<CompletionHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler(
|
||||
"textDocument/signatureHelp",
|
||||
llvm::make_unique<SignatureHelpHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler(
|
||||
"textDocument/definition",
|
||||
llvm::make_unique<GotoDefinitionHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler(
|
||||
"textDocument/switchSourceHeader",
|
||||
llvm::make_unique<SwitchSourceHeaderHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler(
|
||||
"workspace/didChangeWatchedFiles",
|
||||
llvm::make_unique<WorkspaceDidChangeWatchedFilesHandler>(Out, Callbacks));
|
||||
HandlerRegisterer Register{Dispatcher, &Out, &Callbacks};
|
||||
|
||||
Register("initialize", &ProtocolCallbacks::onInitialize);
|
||||
Register("shutdown", &ProtocolCallbacks::onShutdown);
|
||||
Register("textDocument/didOpen", &ProtocolCallbacks::onDocumentDidOpen);
|
||||
Register("textDocument/didClose", &ProtocolCallbacks::onDocumentDidClose);
|
||||
Register("textDocument/didChange", &ProtocolCallbacks::onDocumentDidChange);
|
||||
Register("textDocument/rangeFormatting",
|
||||
&ProtocolCallbacks::onDocumentRangeFormatting);
|
||||
Register("textDocument/onTypeFormatting",
|
||||
&ProtocolCallbacks::onDocumentOnTypeFormatting);
|
||||
Register("textDocument/formatting", &ProtocolCallbacks::onDocumentFormatting);
|
||||
Register("textDocument/codeAction", &ProtocolCallbacks::onCodeAction);
|
||||
Register("textDocument/completion", &ProtocolCallbacks::onCompletion);
|
||||
Register("textDocument/signatureHelp", &ProtocolCallbacks::onSignatureHelp);
|
||||
Register("textDocument/definition", &ProtocolCallbacks::onGoToDefinition);
|
||||
Register("textDocument/switchSourceHeader",
|
||||
&ProtocolCallbacks::onSwitchSourceHeader);
|
||||
Register("workspace/didChangeWatchedFiles", &ProtocolCallbacks::onFileEvent);
|
||||
}
|
||||
|
|
|
@ -7,8 +7,11 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains the actions performed when the server gets a specific
|
||||
// request.
|
||||
// ProtocolHandlers translates incoming JSON requests from JSONRPCDispatcher
|
||||
// into method calls on ClangLSPServer.
|
||||
//
|
||||
// Currently it parses requests into objects, but the ClangLSPServer is
|
||||
// responsible for producing JSON responses. We should move that here, too.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
@ -23,37 +26,31 @@
|
|||
namespace clang {
|
||||
namespace clangd {
|
||||
|
||||
// The interface implemented by ClangLSPServer to handle incoming requests.
|
||||
class ProtocolCallbacks {
|
||||
public:
|
||||
using Ctx = RequestContext;
|
||||
virtual ~ProtocolCallbacks() = default;
|
||||
|
||||
virtual void onInitialize(StringRef ID, InitializeParams IP,
|
||||
JSONOutput &Out) = 0;
|
||||
virtual void onShutdown(JSONOutput &Out) = 0;
|
||||
virtual void onDocumentDidOpen(DidOpenTextDocumentParams Params,
|
||||
JSONOutput &Out) = 0;
|
||||
virtual void onDocumentDidChange(DidChangeTextDocumentParams Params,
|
||||
JSONOutput &Out) = 0;
|
||||
|
||||
virtual void onDocumentDidClose(DidCloseTextDocumentParams Params,
|
||||
JSONOutput &Out) = 0;
|
||||
virtual void onDocumentFormatting(DocumentFormattingParams Params,
|
||||
StringRef ID, JSONOutput &Out) = 0;
|
||||
virtual void onDocumentOnTypeFormatting(DocumentOnTypeFormattingParams Params,
|
||||
StringRef ID, JSONOutput &Out) = 0;
|
||||
virtual void onDocumentRangeFormatting(DocumentRangeFormattingParams Params,
|
||||
StringRef ID, JSONOutput &Out) = 0;
|
||||
virtual void onCodeAction(CodeActionParams Params, StringRef ID,
|
||||
JSONOutput &Out) = 0;
|
||||
virtual void onCompletion(TextDocumentPositionParams Params, StringRef ID,
|
||||
JSONOutput &Out) = 0;
|
||||
virtual void onSignatureHelp(TextDocumentPositionParams Params, StringRef ID,
|
||||
JSONOutput &Out) = 0;
|
||||
virtual void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
|
||||
JSONOutput &Out) = 0;
|
||||
virtual void onSwitchSourceHeader(TextDocumentIdentifier Params, StringRef ID,
|
||||
JSONOutput &Out) = 0;
|
||||
virtual void onFileEvent(const DidChangeWatchedFilesParams &Params) = 0;
|
||||
virtual void onInitialize(Ctx C, InitializeParams &Params) = 0;
|
||||
virtual void onShutdown(Ctx C, ShutdownParams &Params) = 0;
|
||||
virtual void onDocumentDidOpen(Ctx C, DidOpenTextDocumentParams &Params) = 0;
|
||||
virtual void onDocumentDidChange(Ctx C,
|
||||
DidChangeTextDocumentParams &Params) = 0;
|
||||
virtual void onDocumentDidClose(Ctx C,
|
||||
DidCloseTextDocumentParams &Params) = 0;
|
||||
virtual void onDocumentFormatting(Ctx C,
|
||||
DocumentFormattingParams &Params) = 0;
|
||||
virtual void
|
||||
onDocumentOnTypeFormatting(Ctx C, DocumentOnTypeFormattingParams &Params) = 0;
|
||||
virtual void
|
||||
onDocumentRangeFormatting(Ctx C, DocumentRangeFormattingParams &Params) = 0;
|
||||
virtual void onCodeAction(Ctx C, CodeActionParams &Params) = 0;
|
||||
virtual void onCompletion(Ctx C, TextDocumentPositionParams &Params) = 0;
|
||||
virtual void onSignatureHelp(Ctx C, TextDocumentPositionParams &Params) = 0;
|
||||
virtual void onGoToDefinition(Ctx C, TextDocumentPositionParams &Params) = 0;
|
||||
virtual void onSwitchSourceHeader(Ctx C, TextDocumentIdentifier &Params) = 0;
|
||||
virtual void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) = 0;
|
||||
};
|
||||
|
||||
void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
|
||||
|
|
|
@ -45,12 +45,12 @@ Content-Length: 140
|
|||
Content-Length: 86
|
||||
|
||||
{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[""]}}
|
||||
# STDERR: Failed to decode DidChangeWatchedFilesParams.
|
||||
# STDERR: Failed to decode workspace/didChangeWatchedFiles request.
|
||||
# Changes field with no sequence
|
||||
Content-Length: 84
|
||||
|
||||
{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":""}}
|
||||
# STDERR: Failed to decode DidChangeWatchedFilesParams.
|
||||
# STDERR: Failed to decode workspace/didChangeWatchedFiles request.
|
||||
# Custom field
|
||||
Content-Length: 86
|
||||
|
||||
|
|
|
@ -15,13 +15,13 @@ Content-Length: 746
|
|||
|
||||
{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":2,"message":"using the result of an assignment as a condition without parentheses"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"place parentheses around the assignment to silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this assignment into an equality comparison"}]}}}
|
||||
#
|
||||
# CHECK: {"jsonrpc":"2.0","id":2, "result": [{"title":"Apply FixIt 'place parentheses around the assignment to silence this warning'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}}, "newText": ")"}]]},{"title":"Apply FixIt 'use '==' to turn this assignment into an equality comparison'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}}, "newText": "=="}]]}]
|
||||
# CHECK: {"jsonrpc":"2.0","id":2,"result":[{"title":"Apply FixIt 'place parentheses around the assignment to silence this warning'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}}, "newText": ")"}]]},{"title":"Apply FixIt 'use '==' to turn this assignment into an equality comparison'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}}, "newText": "=="}]]}]
|
||||
#
|
||||
Content-Length: 771
|
||||
|
||||
{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":2,"code":"1","source":"foo","message":"using the result of an assignment as a condition without parentheses"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"place parentheses around the assignment to silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this assignment into an equality comparison"}]}}}
|
||||
# Make sure unused "code" and "source" fields ignored gracefully
|
||||
# CHECK: {"jsonrpc":"2.0","id":2, "result": [{"title":"Apply FixIt 'place parentheses around the assignment to silence this warning'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}}, "newText": ")"}]]},{"title":"Apply FixIt 'use '==' to turn this assignment into an equality comparison'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}}, "newText": "=="}]]}]
|
||||
# CHECK: {"jsonrpc":"2.0","id":2,"result":[{"title":"Apply FixIt 'place parentheses around the assignment to silence this warning'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}}, "newText": ")"}]]},{"title":"Apply FixIt 'use '==' to turn this assignment into an equality comparison'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}}, "newText": "=="}]]}]
|
||||
#
|
||||
Content-Length: 44
|
||||
|
||||
|
|
Loading…
Reference in New Issue