From 3a252fc0add7954a6b42bac4451308b10de16eaa 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 ca914dc6..bc8da9ae 100644 --- a/src/config.hh +++ b/src/config.hh @@ -102,6 +102,8 @@ struct Config { } clang; struct ClientCapability { + // TextDocumentClientCapabilities.publishDiagnostics.relatedInformation + bool diagnosticsRelatedInformation = true; // TextDocumentClientCapabilities.documentSymbol.hierarchicalDocumentSymbolSupport bool hierarchicalDocumentSymbolSupport = true; // TextDocumentClientCapabilities.definition.linkSupport @@ -299,8 +301,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 8f8efda9..b92578b8 100644 --- a/src/lsp.hh +++ b/src/lsp.hh @@ -238,12 +238,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 949fc242..61d7e1c3 100644 --- a/src/message_handler.hh +++ b/src/message_handler.hh @@ -195,7 +195,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 6259e8d0..707798d0 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -171,6 +171,10 @@ struct TextDocumentClientCap { struct DocumentSymbol { bool hierarchicalDocumentSymbolSupport = false; } documentSymbol; + + struct PublishDiagnostics { + bool relatedInformation = false; + } publishDiagnostics; }; REFLECT_STRUCT(TextDocumentClientCap::Completion::CompletionItem, @@ -179,7 +183,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; @@ -306,6 +311,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 a80bd0d7..d78b5e06 100644 --- a/src/sema_manager.cc +++ b/src/sema_manager.cc @@ -514,7 +514,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 @@ -612,27 +612,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); + } } }