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); + } } }