diff --git a/nixd-next/tools/nixd/src/Controller.cpp b/nixd-next/tools/nixd/src/Controller.cpp index ceeb98354..629883ce6 100644 --- a/nixd-next/tools/nixd/src/Controller.cpp +++ b/nixd-next/tools/nixd/src/Controller.cpp @@ -2,24 +2,77 @@ /// \brief Controller. The process interacting with users. #include "nixd-config.h" +#include "nixf/Basic/Diagnostic.h" +#include "nixf/Parse/Parser.h" + #include "lspserver/DraftStore.h" #include "lspserver/LSPServer.h" #include "lspserver/Path.h" +#include "lspserver/Protocol.h" #include "lspserver/SourceCode.h" #include +#include + namespace { using namespace lspserver; using namespace llvm::json; +Position toLSPPosition(const nixf::Point &P) { + return Position{static_cast(P.line()), static_cast(P.column())}; +} + +Range toLSPRange(const nixf::RangeTy &R) { + return Range{toLSPPosition(R.begin()), toLSPPosition(R.end())}; +} + +std::string simpleFormat(const char *Fmt, + const std::vector &Args) { + std::stringstream SS; + std::size_t ArgIdx = 0; + for (const char *Cur = Fmt; *Cur;) { + if (*Cur == '{' && *(Cur + 1) == '}') { + SS << Args[ArgIdx++]; + Cur += 2; + } else { + SS << *Cur; + ++Cur; + } + } + return SS.str(); +} + class Controller : public LSPServer { DraftStore Store; - void addDocument(PathRef File, llvm::StringRef Contents, - llvm::StringRef Version) { - Store.addDraft(File, Version, Contents); + llvm::unique_function + PublishDiagnostic; + + /// Action right after a document is added (including updates). + void actOnDocumentAdd(PathRef File, std::optional Version) { + auto Draft = Store.getDraft(File); + assert(Draft && "Added document is not in the store?"); + std::vector Diagnostics; + nixf::parse(*Draft->Contents, Diagnostics); + + std::vector LSPDiags; + LSPDiags.reserve(Diagnostics.size()); + for (const nixf::Diagnostic &D : Diagnostics) { + LSPDiags.emplace_back(Diagnostic{ + .range = toLSPRange(D.range()), + .severity = 0, + .code = D.sname(), + .source = "nixf", + .message = simpleFormat(D.message(), D.args()), + }); + } + PublishDiagnostic({ + .uri = URIForFile::canonicalize(File, File), + .diagnostics = std::move(LSPDiags), + .version = Version, + }); } void removeDocument(PathRef File) { Store.removeDraft(File); } @@ -48,14 +101,18 @@ class Controller : public LSPServer { }}; Reply(std::move(Result)); + + PublishDiagnostic = mkOutNotifiction( + "textDocument/publishDiagnostics"); } void onInitialized([[maybe_unused]] const InitializedParams &Params) {} void onDocumentDidOpen(const DidOpenTextDocumentParams &Params) { PathRef File = Params.textDocument.uri.file(); const std::string &Contents = Params.textDocument.text; - addDocument(File, Contents, - DraftStore::encodeVersion(Params.textDocument.version)); + std::optional Version = Params.textDocument.version; + Store.addDraft(File, DraftStore::encodeVersion(Version), Contents); + actOnDocumentAdd(File, Version); } void onDocumentDidChange(const DidChangeTextDocumentParams &Params) { @@ -76,9 +133,9 @@ class Controller : public LSPServer { return; } } - std::string Version = - DraftStore::encodeVersion(Params.textDocument.version); - addDocument(File, NewCode, Version); + std::optional Version = Params.textDocument.version; + Store.addDraft(File, DraftStore::encodeVersion(Version), NewCode); + actOnDocumentAdd(File, Version); } void onDocumentDidClose(const DidCloseTextDocumentParams &Params) {