Skip to content

Commit

Permalink
nixd: diagnostic control system (#505)
Browse files Browse the repository at this point in the history
  • Loading branch information
inclyc authored May 23, 2024
1 parent ff3eb71 commit 052071a
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 3 deletions.
22 changes: 22 additions & 0 deletions nixd/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions nixd/include/nixd/Controller/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,15 @@ struct Configuration {
/// \brief Expression to eval. Treat it as "import <nixpkgs> { }"
std::string expr;
} nixpkgs;

struct Diagnostic {
std::vector<std::string> 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);

Expand Down
14 changes: 14 additions & 0 deletions nixd/include/nixd/Controller/Controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
#include "lspserver/LSPServer.h"
#include "lspserver/Protocol.h"
#include "nixd/Eval/AttrSetClient.h"
#include "nixf/Basic/Diagnostic.h"

#include <boost/asio/thread_pool.hpp>

#include <set>

namespace nixd {

class Controller : public lspserver::LSPServer {
Expand Down Expand Up @@ -199,6 +202,17 @@ class Controller : public lspserver::LSPServer {
const lspserver::DocumentLinkParams &Params,
lspserver::Callback<std::vector<lspserver::DocumentLink>> Reply);

std::set<nixf::Diagnostic::DiagnosticKind>
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<std::string> &Sup);

/// Determine whether or not this diagnostic is suppressed.
bool isSuppressed(nixf::Diagnostic::DiagnosticKind Kind);
void publishDiagnostics(lspserver::PathRef File,
std::optional<int64_t> Version,
const std::vector<nixf::Diagnostic> &Diagnostics);
Expand Down
16 changes: 16 additions & 0 deletions nixd/lib/Controller/Configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) //
;
}

Expand Down Expand Up @@ -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() {
Expand Down
52 changes: 49 additions & 3 deletions nixd/lib/Controller/Diagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,65 @@

#include "nixd/Controller/Controller.h"

namespace nixd {
#include <nixf/Basic/Diagnostic.h>

#include <mutex>
#include <optional>

using namespace nixd;
using namespace llvm::json;
using namespace lspserver;

void Controller::updateSuppressed(const std::vector<std::string> &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<std::string, nixf::Diagnostic::DiagnosticKind>
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 <nixf/Basic/DiagnosticKinds.inc>
#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<int64_t> Version,
const std::vector<nixf::Diagnostic> &Diagnostics) {
std::vector<Diagnostic> 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();

Expand Down Expand Up @@ -85,5 +133,3 @@ void Controller::publishDiagnostics(
.version = Version,
});
}

} // namespace nixd

0 comments on commit 052071a

Please sign in to comment.