From 8626d36f8c54ec8d1667e14cf3618edb183686ec Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Fri, 10 Aug 2018 17:25:07 +0000 Subject: [PATCH] [clangd] extend the publishDiagnostics response to send back fixits to the client if the client supports this extension This commit extends the 'textDocument/publishDiagnostics' notification sent from Clangd to the client. When it's enabled, Clangd sends out the fixits associated with the appropriate diagnostic in the body of the 'publishDiagnostics' notification. The client can enable this extension by setting 'clangdFixSupport' to true in the textDocument capabilities during initialization. Differential Revision: https://reviews.llvm.org/D50415 llvm-svn: 339454 --- clang-tools-extra/clangd/ClangdLSPServer.cpp | 18 +++++++++++++++++- clang-tools-extra/clangd/ClangdLSPServer.h | 2 ++ clang-tools-extra/clangd/Diagnostics.h | 6 ++++++ clang-tools-extra/clangd/Protocol.cpp | 10 ++++++++++ clang-tools-extra/clangd/Protocol.h | 15 +++++++++++++++ 5 files changed, 50 insertions(+), 1 deletion(-) diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index 99353e6..0d3023b 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -82,6 +82,8 @@ void ClangdLSPServer::onInitialize(InitializeParams &Params) { CCOpts.EnableSnippets = Params.capabilities.textDocument.completion.completionItem.snippetSupport; + DiagOpts.EmbedFixesInDiagnostics = + Params.capabilities.textDocument.publishDiagnostics.clangdFixSupport; if (Params.capabilities.workspace && Params.capabilities.workspace->symbol && Params.capabilities.workspace->symbol->symbolKind) { @@ -486,11 +488,25 @@ void ClangdLSPServer::onDiagnosticsReady(PathRef File, DiagnosticToReplacementMap LocalFixIts; // Temporary storage for (auto &Diag : Diagnostics) { toLSPDiags(Diag, [&](clangd::Diagnostic Diag, llvm::ArrayRef Fixes) { - DiagnosticsJSON.push_back(json::Object{ + json::Object LSPDiag({ {"range", Diag.range}, {"severity", Diag.severity}, {"message", Diag.message}, }); + // LSP extension: embed the fixes in the diagnostic. + if (DiagOpts.EmbedFixesInDiagnostics && !Fixes.empty()) { + json::Array ClangdFixes; + for (const auto &Fix : Fixes) { + WorkspaceEdit WE; + URIForFile URI{File}; + WE.changes = {{URI.uri(), std::vector(Fix.Edits.begin(), + Fix.Edits.end())}}; + ClangdFixes.push_back( + json::Object{{"edit", toJSON(WE)}, {"title", Fix.Message}}); + } + LSPDiag["clangd_fixes"] = std::move(ClangdFixes); + } + DiagnosticsJSON.push_back(std::move(LSPDiag)); auto &FixItsForDiagnostic = LocalFixIts[Diag]; std::copy(Fixes.begin(), Fixes.end(), diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h index 3850e4a..e4cbe50 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -155,6 +155,8 @@ private: RealFileSystemProvider FSProvider; /// Options used for code completion clangd::CodeCompleteOptions CCOpts; + /// Options used for diagnostics. + ClangdDiagnosticOptions DiagOpts; /// The supported kinds of the client. SymbolKindBitset SupportedSymbolKinds; diff --git a/clang-tools-extra/clangd/Diagnostics.h b/clang-tools-extra/clangd/Diagnostics.h index b43f04a..a08b9fe 100644 --- a/clang-tools-extra/clangd/Diagnostics.h +++ b/clang-tools-extra/clangd/Diagnostics.h @@ -23,6 +23,12 @@ namespace clang { namespace clangd { +struct ClangdDiagnosticOptions { + /// If true, Clangd uses an LSP extension to embed the fixes with the + /// diagnostics that are sent to the client. + bool EmbedFixesInDiagnostics = false; +}; + /// Contains basic information about a diagnostic. struct DiagBase { std::string Message; diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index 202fbfb..698bb04 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -178,6 +178,15 @@ bool fromJSON(const json::Value &Params, CompletionClientCapabilities &R) { return true; } +bool fromJSON(const llvm::json::Value &Params, + PublishDiagnosticsClientCapabilities &R) { + json::ObjectMapper O(Params); + if (!O) + return false; + O.map("clangdFixSupport", R.clangdFixSupport); + return true; +} + bool fromJSON(const json::Value &E, SymbolKind &Out) { if (auto T = E.getAsInteger()) { if (*T < static_cast(SymbolKind::File) || @@ -240,6 +249,7 @@ bool fromJSON(const json::Value &Params, TextDocumentClientCapabilities &R) { if (!O) return false; O.map("completion", R.completion); + O.map("publishDiagnostics", R.publishDiagnostics); return true; } diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h index b0d810e..f533c97 100644 --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -247,6 +247,18 @@ struct CompletionClientCapabilities { }; bool fromJSON(const llvm::json::Value &, CompletionClientCapabilities &); +struct PublishDiagnosticsClientCapabilities { + // Whether the client accepts diagnostics with related information. + // NOTE: not used by clangd at the moment. + // bool relatedInformation; + + /// Whether the client accepts diagnostics with fixes attached using the + /// "clangd_fixes" extension. + bool clangdFixSupport = false; +}; +bool fromJSON(const llvm::json::Value &, + PublishDiagnosticsClientCapabilities &); + /// A symbol kind. enum class SymbolKind { File = 1, @@ -313,6 +325,9 @@ bool fromJSON(const llvm::json::Value &, WorkspaceClientCapabilities &); struct TextDocumentClientCapabilities { /// Capabilities specific to the `textDocument/completion` CompletionClientCapabilities completion; + + /// Capabilities specific to the 'textDocument/publishDiagnostics' + PublishDiagnosticsClientCapabilities publishDiagnostics; }; bool fromJSON(const llvm::json::Value &, TextDocumentClientCapabilities &); -- 2.7.4