From 633d8936a8cc709d4ba722b5eaaafa124c08ed91 Mon Sep 17 00:00:00 2001 From: Leszek Swirski Date: Thu, 21 Feb 2019 02:23:21 +0100 Subject: [PATCH] Use DiagnosticRelatedInformation if client supports publishDiagnostics.relatedInformation (#276) In clients that support DiagnosticRelatedInformation, display clang notes as these nested diagnostics rather than appending them to the parent diagnostic's message. Behaviour for clients that don't support related information should be unchanged. --- src/config.hh | 6 +++-- src/lsp.hh | 6 +++++ src/message_handler.hh | 3 ++- src/messages/initialize.cc | 9 ++++++- src/sema_manager.cc | 49 ++++++++++++++++++++++++-------------- 5 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/config.hh b/src/config.hh index e1d44c78..94830b66 100644 --- a/src/config.hh +++ b/src/config.hh @@ -90,6 +90,8 @@ struct Config { } clang; struct ClientCapability { + // TextDocumentClientCapabilities.publishDiagnostics.relatedInformation + bool diagnosticsRelatedInformation = true; // TextDocumentClientCapabilities.documentSymbol.hierarchicalDocumentSymbolSupport bool hierarchicalDocumentSymbolSupport = true; // TextDocumentClientCapabilities.definition.linkSupport @@ -287,8 +289,8 @@ REFLECT_STRUCT(Config::ServerCap, documentOnTypeFormattingProvider, foldingRangeProvider, workspace); REFLECT_STRUCT(Config::Clang, excludeArgs, extraArgs, pathMappings, resourceDir); -REFLECT_STRUCT(Config::ClientCapability, hierarchicalDocumentSymbolSupport, - linkSupport, snippetSupport); +REFLECT_STRUCT(Config::ClientCapability, diagnosticsRelatedInformation, + hierarchicalDocumentSymbolSupport, linkSupport, snippetSupport); REFLECT_STRUCT(Config::CodeLens, localVariables); REFLECT_STRUCT(Config::Completion::Include, blacklist, maxPathSize, suffixWhitelist, whitelist); diff --git a/src/lsp.hh b/src/lsp.hh index ad77f993..8c507904 100644 --- a/src/lsp.hh +++ b/src/lsp.hh @@ -226,12 +226,18 @@ struct WorkspaceFolder { enum class MessageType : int { Error = 1, Warning = 2, Info = 3, Log = 4 }; REFLECT_UNDERLYING(MessageType) +struct DiagnosticRelatedInformation { + Location location; + std::string message; +}; + struct Diagnostic { lsRange range; int severity = 0; int code = 0; std::string source = "ccls"; std::string message; + std::vector relatedInformation; std::vector fixits_; }; diff --git a/src/message_handler.hh b/src/message_handler.hh index 6c040a29..ef428abc 100644 --- a/src/message_handler.hh +++ b/src/message_handler.hh @@ -183,7 +183,8 @@ REFLECT_STRUCT(TextDocumentIdentifier, uri); REFLECT_STRUCT(TextDocumentItem, uri, languageId, version, text); REFLECT_STRUCT(TextEdit, range, newText); REFLECT_STRUCT(VersionedTextDocumentIdentifier, uri, version); -REFLECT_STRUCT(Diagnostic, range, severity, code, source, message); +REFLECT_STRUCT(DiagnosticRelatedInformation, location, message); +REFLECT_STRUCT(Diagnostic, range, severity, code, source, message, relatedInformation); REFLECT_STRUCT(ShowMessageParam, type, message); REFLECT_UNDERLYING_B(LanguageId); diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index 1a3c40fc..3798a19d 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -160,6 +160,10 @@ struct TextDocumentClientCap { struct DocumentSymbol { bool hierarchicalDocumentSymbolSupport = false; } documentSymbol; + + struct PublishDiagnostics { + bool relatedInformation = false; + } publishDiagnostics; }; REFLECT_STRUCT(TextDocumentClientCap::Completion::CompletionItem, @@ -168,7 +172,8 @@ REFLECT_STRUCT(TextDocumentClientCap::Completion, completionItem); REFLECT_STRUCT(TextDocumentClientCap::DocumentSymbol, hierarchicalDocumentSymbolSupport); REFLECT_STRUCT(TextDocumentClientCap::LinkSupport, linkSupport); -REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol); +REFLECT_STRUCT(TextDocumentClientCap::PublishDiagnostics, relatedInformation); +REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol, publishDiagnostics); struct ClientCap { WorkspaceClientCap workspace; @@ -295,6 +300,8 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) { capabilities.textDocument.definition.linkSupport; g_config->client.snippetSupport &= capabilities.textDocument.completion.completionItem.snippetSupport; + g_config->client.diagnosticsRelatedInformation &= + capabilities.textDocument.publishDiagnostics.relatedInformation; didChangeWatchedFiles = capabilities.workspace.didChangeWatchedFiles.dynamicRegistration; diff --git a/src/sema_manager.cc b/src/sema_manager.cc index d761d69b..0791c59c 100644 --- a/src/sema_manager.cc +++ b/src/sema_manager.cc @@ -503,7 +503,7 @@ llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) { } } -void printDiag(llvm::raw_string_ostream &OS, const DiagBase &d) { +void PrintDiag(llvm::raw_string_ostream &OS, const DiagBase &d) { if (d.concerned) OS << llvm::sys::path::filename(d.file); else @@ -601,27 +601,40 @@ void *DiagnosticMain(void *manager_) { for (auto &d : diags) { if (!d.concerned) continue; - std::string buf; - llvm::raw_string_ostream OS(buf); Diagnostic &ls_diag = ls_diags.emplace_back(); Fill(d, ls_diag); ls_diag.fixits_ = d.edits; - OS << d.message; - for (auto &n : d.notes) { - OS << "\n\n"; - printDiag(OS, n); - } - OS.flush(); - ls_diag.message = std::move(buf); - for (auto &n : d.notes) { - if (!n.concerned) - continue; - Diagnostic &ls_diag1 = ls_diags.emplace_back(); - Fill(n, ls_diag1); - OS << n.message << "\n\n"; - printDiag(OS, d); + if (g_config->client.diagnosticsRelatedInformation) { + ls_diag.message = d.message; + for (const Note &n : d.notes) { + SmallString<256> Str(n.file); + llvm::sys::path::remove_dots(Str, true); + Location loc{DocumentUri::FromPath(Str.str()), + lsRange{{n.range.start.line, n.range.start.column}, + {n.range.end.line, n.range.end.column}}}; + ls_diag.relatedInformation.push_back({loc, n.message}); + } + } else { + std::string buf; + llvm::raw_string_ostream OS(buf); + OS << d.message; + for (const Note &n : d.notes) { + OS << "\n\n"; + PrintDiag(OS, n); + } OS.flush(); - ls_diag1.message = std::move(buf); + ls_diag.message = std::move(buf); + for (const Note &n : d.notes) { + if (!n.concerned) + continue; + Diagnostic &ls_diag1 = ls_diags.emplace_back(); + Fill(n, ls_diag1); + buf.clear(); + OS << n.message << "\n\n"; + PrintDiag(OS, d); + OS.flush(); + ls_diag1.message = std::move(buf); + } } }