From 052071aea4a8afa3fa03ed6df779f430708c0683 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Thu, 23 May 2024 20:37:17 +0800 Subject: [PATCH] nixd: diagnostic control system (#505) --- nixd/docs/configuration.md | 22 +++++++++ nixd/include/nixd/Controller/Configuration.h | 7 +++ nixd/include/nixd/Controller/Controller.h | 14 ++++++ nixd/lib/Controller/Configuration.cpp | 16 ++++++ nixd/lib/Controller/Diagnostics.cpp | 52 ++++++++++++++++++-- 5 files changed, 108 insertions(+), 3 deletions(-) diff --git a/nixd/docs/configuration.md b/nixd/docs/configuration.md index 5df349bdd..909f1e4db 100644 --- a/nixd/docs/configuration.md +++ b/nixd/docs/configuration.md @@ -145,12 +145,34 @@ nvim_lsp.nixd.setup({ "home-manager": { "expr": "(builtins.getFlake \"/home/lyc/flakes\").homeConfigurations.\"lyc@adrastea\".options" } + }, + // Control the diagnostic system + "diagnostic": { + "suppress": [ + "sema-escaping-with" + ] } } ``` ### Fields explanation +#### Diagnostic Control ("diagnostic") + +Some users might feel confident in their understanding of the language and +prefer to suppress diagnostics altogether. This can be achieved by utilizing the diagnostic field. + +```jsonc +{ + "diagnostic": { + // A list of diagnostic short names + "suppress": [ + "sema-escaping-with" + ] + } +} +``` + #### Format ("formating") To configure which command will be used for formatting, you can change the "formatting" section. diff --git a/nixd/include/nixd/Controller/Configuration.h b/nixd/include/nixd/Controller/Configuration.h index 97a716455..48b8f5be4 100644 --- a/nixd/include/nixd/Controller/Configuration.h +++ b/nixd/include/nixd/Controller/Configuration.h @@ -27,8 +27,15 @@ struct Configuration { /// \brief Expression to eval. Treat it as "import { }" std::string expr; } nixpkgs; + + struct Diagnostic { + std::vector suppress; + } diagnostic; }; +bool fromJSON(const llvm::json::Value &Params, Configuration::Diagnostic &R, + llvm::json::Path P); + bool fromJSON(const llvm::json::Value &Params, Configuration::Formatting &R, llvm::json::Path P); diff --git a/nixd/include/nixd/Controller/Controller.h b/nixd/include/nixd/Controller/Controller.h index 20a993fb5..6f6611451 100644 --- a/nixd/include/nixd/Controller/Controller.h +++ b/nixd/include/nixd/Controller/Controller.h @@ -8,9 +8,12 @@ #include "lspserver/LSPServer.h" #include "lspserver/Protocol.h" #include "nixd/Eval/AttrSetClient.h" +#include "nixf/Basic/Diagnostic.h" #include +#include + namespace nixd { class Controller : public lspserver::LSPServer { @@ -199,6 +202,17 @@ class Controller : public lspserver::LSPServer { const lspserver::DocumentLinkParams &Params, lspserver::Callback> Reply); + std::set + SuppressedDiagnostics; // GUARDED_BY(SuppressedDiagnosticsLock) + + std::mutex SuppressedDiagnosticsLock; + + /// Update the suppressing set. There might be some invalid names, should be + /// logged then. + void updateSuppressed(const std::vector &Sup); + + /// Determine whether or not this diagnostic is suppressed. + bool isSuppressed(nixf::Diagnostic::DiagnosticKind Kind); void publishDiagnostics(lspserver::PathRef File, std::optional Version, const std::vector &Diagnostics); diff --git a/nixd/lib/Controller/Configuration.cpp b/nixd/lib/Controller/Configuration.cpp index b13d4c185..971ec72c8 100644 --- a/nixd/lib/Controller/Configuration.cpp +++ b/nixd/lib/Controller/Configuration.cpp @@ -8,6 +8,12 @@ using namespace lspserver; using llvm::json::ObjectMapper; using llvm::json::Value; +bool nixd::fromJSON(const Value &Params, Configuration::Diagnostic &R, + llvm::json::Path P) { + ObjectMapper O(Params, P); + return O && O.mapOptional("suppress", R.suppress); +} + bool nixd::fromJSON(const Value &Params, Configuration::Formatting &R, llvm::json::Path P) { // If it is a single string, treat it as a single vector @@ -37,6 +43,7 @@ bool nixd::fromJSON(const Value &Params, Configuration &R, llvm::json::Path P) { && O.mapOptional("formatting", R.formatting) // && O.mapOptional("options", R.options) // && O.mapOptional("nixpkgs", R.nixpkgs) // + && O.mapOptional("diagnostic", R.diagnostic) // ; } @@ -70,6 +77,15 @@ void Controller::updateConfig(Configuration NewConfig) { evalExprWithProgress(*Client->client(), Opt.expr, Name); } } + + // Update the diagnostic part. + updateSuppressed(Config.diagnostic.suppress); + + // After all, notify all AST modules the diagnostic set has been updated. + std::lock_guard TUsGuard(TUsLock); + for (const auto &[File, TU] : TUs) { + publishDiagnostics(File, std::nullopt, TU->diagnostics()); + } } void Controller::fetchConfig() { diff --git a/nixd/lib/Controller/Diagnostics.cpp b/nixd/lib/Controller/Diagnostics.cpp index d5ec40276..6fc512a20 100644 --- a/nixd/lib/Controller/Diagnostics.cpp +++ b/nixd/lib/Controller/Diagnostics.cpp @@ -7,17 +7,65 @@ #include "nixd/Controller/Controller.h" -namespace nixd { +#include +#include +#include + +using namespace nixd; using namespace llvm::json; using namespace lspserver; +void Controller::updateSuppressed(const std::vector &Sup) { + // For convenience, alias it to a short name + using DK = nixf::Diagnostic::DiagnosticKind; + // Acquire the lock because the set needs to be written. + std::lock_guard _(SuppressedDiagnosticsLock); + // Clear the set, just construct a new one. + SuppressedDiagnostics.clear(); + + // Hashed string used for lookup names + static std::unordered_map + DKMap; + + // Insert declared sname in nixf, diagnostics. + struct AddDK { + AddDK(const std::string &Name, DK Kind) { DKMap.insert({Name, Kind}); } + }; +#define DIAG(SNAME, CNAME, LEVEL, STR) \ + static AddDK DK_Add_##CNAME(SNAME, DK::DK_##CNAME); +#include +#undef DIAG + + // For each element, see if the name matches some declared name. + // If so, insert the set. + for (const auto &Name : Sup) { + if (auto It = DKMap.find(Name); It != DKMap.end()) { + SuppressedDiagnostics.insert(It->second); + } else { + // The name is not listed in knwon names. Log error here + lspserver::elog("diagnostic suppressing sname {0} is unknown", Name); + } + } +} + +bool Controller::isSuppressed(nixf::Diagnostic::DiagnosticKind Kind) { + std::lock_guard _(SuppressedDiagnosticsLock); + return SuppressedDiagnostics.contains(Kind); +} + void Controller::publishDiagnostics( PathRef File, std::optional Version, const std::vector &Diagnostics) { std::vector LSPDiags; LSPDiags.reserve(Diagnostics.size()); for (const nixf::Diagnostic &D : Diagnostics) { + // Before actually doing anything, + // let's check if the diagnostic is suppressed. + // If suppressed, just skip it. + if (isSuppressed(D.kind())) + continue; + // Format the message. std::string Message = D.format(); @@ -85,5 +133,3 @@ void Controller::publishDiagnostics( .version = Version, }); } - -} // namespace nixd