From f88deb4abc9731f7d4ee6286c2692eccf1f1b41a Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sun, 20 Aug 2023 15:52:11 +0800 Subject: [PATCH] nixd/Sema: use CompletionBuilder for OptionWorker --- nixd/include/nixd/Nix/Value.h | 5 ++ nixd/include/nixd/Sema/CompletionBuilder.h | 4 ++ nixd/lib/Nix/Value.cpp | 12 ++++ nixd/lib/Sema/CompletionBuilder.cpp | 35 ++++++++++ nixd/lib/Server/OptionWorker.cpp | 77 +++------------------- 5 files changed, 66 insertions(+), 67 deletions(-) diff --git a/nixd/include/nixd/Nix/Value.h b/nixd/include/nixd/Nix/Value.h index fdcd253f8..cdbac2a60 100644 --- a/nixd/include/nixd/Nix/Value.h +++ b/nixd/include/nixd/Nix/Value.h @@ -2,6 +2,7 @@ #include #include +#include namespace nixd { @@ -12,6 +13,10 @@ bool isDerivation(nix::EvalState &State, nix::Value &V); std::optional attrPathStr(nix::EvalState &State, nix::Value &V, const std::string &AttrPath) noexcept; +/// Select the path given in \p AttrPath, and return the value +nix::Value selectAttrPath(nix::EvalState &State, nix::Value Set, + const std::vector &AttrPath); + extern int PrintDepth; } // namespace nixd diff --git a/nixd/include/nixd/Sema/CompletionBuilder.h b/nixd/include/nixd/Sema/CompletionBuilder.h index 5d9a54bc8..48b63ef8c 100644 --- a/nixd/include/nixd/Sema/CompletionBuilder.h +++ b/nixd/include/nixd/Sema/CompletionBuilder.h @@ -45,6 +45,10 @@ class CompletionBuilder { /// builtins.* void addStaticEnv(const nix::SymbolTable &STable, const nix::StaticEnv &SEnv); + /// Complete option items, for Nixpkgs option system. + void addOption(nix::EvalState &State, nix::Value &OptionAttrSet, + const std::vector &AttrPath); + CompletionResult &getResult() { return Result; } void setLimit(size_t Limit) { this->Limit = Limit; } diff --git a/nixd/lib/Nix/Value.cpp b/nixd/lib/Nix/Value.cpp index d1d7f3173..49695c516 100644 --- a/nixd/lib/Nix/Value.cpp +++ b/nixd/lib/Nix/Value.cpp @@ -3,6 +3,8 @@ #include #include +#include + namespace nixd { bool isOption(nix::EvalState &State, nix::Value &V) { @@ -41,6 +43,16 @@ std::optional attrPathStr(nix::EvalState &State, nix::Value &V, int PrintDepth; +nix::Value selectAttrPath(nix::EvalState &State, nix::Value Set, + const std::vector &AttrPath) { + nix::Value V = Set; + if (!AttrPath.empty()) { + auto AttrPathStr = boost::algorithm::join(AttrPath, "."); + auto &Bindings(*State.allocBindings(0)); + V = *nix::findAlongAttrPath(State, AttrPathStr, Bindings, Set).first; + } + return V; +} } // namespace nixd namespace nix { diff --git a/nixd/lib/Sema/CompletionBuilder.cpp b/nixd/lib/Sema/CompletionBuilder.cpp index f016a51aa..c84db173d 100644 --- a/nixd/lib/Sema/CompletionBuilder.cpp +++ b/nixd/lib/Sema/CompletionBuilder.cpp @@ -1,6 +1,8 @@ #include "nixd/Sema/CompletionBuilder.h" #include "nixd/AST/ParseAST.h" #include "nixd/Nix/Eval.h" +#include "nixd/Nix/Option.h" +#include "nixd/Nix/Value.h" #include "lspserver/Protocol.h" @@ -136,4 +138,37 @@ void CompletionBuilder::addStaticEnv(const nix::SymbolTable &STable, addStaticEnv(STable, SEnv); } +void CompletionBuilder::addOption(nix::EvalState &State, + nix::Value &OptionAttrSet, + const std::vector &AttrPath) { + using lspserver::MarkupContent; + using lspserver::MarkupKind; + + try { + nix::Value V = selectAttrPath(State, OptionAttrSet, AttrPath); + State.forceValue(V, nix::noPos); + + if (V.type() != nix::ValueType::nAttrs) + return; + + if (isOption(State, V)) + return; + + for (const auto &Attr : *V.attrs) { + CompletionItem R; + R.label = State.symbols[Attr.name]; + if (isOption(State, *Attr.value)) { + auto Info = optionInfo(State, *Attr.value); + R.kind = CompletionItemKind::Constructor; + R.detail = Info.Type.value_or(""); + R.documentation = MarkupContent{MarkupKind::Markdown, Info.mdDoc()}; + } else { + R.kind = CompletionItemKind::Class; + R.detail = "{...}"; + } + addItem(std::move(R)); + } + } catch (...) { + } +} } // namespace nixd diff --git a/nixd/lib/Server/OptionWorker.cpp b/nixd/lib/Server/OptionWorker.cpp index f63e84e3f..e07cc86e9 100644 --- a/nixd/lib/Server/OptionWorker.cpp +++ b/nixd/lib/Server/OptionWorker.cpp @@ -2,6 +2,7 @@ #include "nixd/Nix/Init.h" #include "nixd/Nix/Option.h" #include "nixd/Nix/Value.h" +#include "nixd/Sema/CompletionBuilder.h" #include "nixd/Server/ConfigSerialization.h" #include "nixd/Server/IPCSerialization.h" #include "nixd/Support/Diagnostic.h" @@ -31,7 +32,6 @@ void OptionWorker::onDeclaration( const ipc::AttrPathParams &Params, lspserver::Callback Reply) { using namespace lspserver; - using boost::algorithm::join; ReplyRAII RR(std::move(Reply)); if (!OptionAttrSet) return; @@ -39,29 +39,19 @@ void OptionWorker::onDeclaration( return; try { - nix::Value *V = OptionAttrSet; - if (!Params.Path.empty()) { - auto AttrPath = join(Params.Path, "."); - auto &Bindings(*OptionIES->getState()->allocBindings(0)); - V = nix::findAlongAttrPath(*OptionIES->getState(), AttrPath, Bindings, - *OptionAttrSet) - .first; - } + auto Path = Params.Path; + Path.emplace_back("declarations"); auto &State = *OptionIES->getState(); - State.forceValue(*V, nix::noPos); - - auto *VDecl = nix::findAlongAttrPath(State, "declarations", - *State.allocBindings(0), *V) - .first; - State.forceValue(*VDecl, nix::noPos); + auto VDecl = selectAttrPath(State, *OptionAttrSet, Path); + State.forceValue(VDecl, nix::noPos); // declarations should be a list containing file paths - if (!VDecl->isList()) + if (!VDecl.isList()) return; - for (auto *VFile : VDecl->listItems()) { + for (auto *VFile : VDecl.listItems()) { State.forceValue(*VFile, nix::noPos); if (VFile->type() == nix::ValueType::nString) { auto File = VFile->str(); @@ -110,61 +100,14 @@ void OptionWorker::onEvalOptionSet( void OptionWorker::onCompletion(const ipc::AttrPathParams &Params, lspserver::Callback Reply) { using namespace lspserver; - using boost::algorithm::join; ReplyRAII RR(std::move(Reply)); if (!OptionAttrSet) return; - try { - CompletionList List; - List.isIncomplete = false; - List.items = decltype(CompletionList::items){}; - - auto &Items = List.items; - - if (OptionAttrSet->type() == nix::ValueType::nAttrs) { - nix::Value *V = OptionAttrSet; - if (!Params.Path.empty()) { - auto AttrPath = join(Params.Path, "."); - auto &Bindings(*OptionIES->getState()->allocBindings(0)); - V = nix::findAlongAttrPath(*OptionIES->getState(), AttrPath, Bindings, - *OptionAttrSet) - .first; - } - - OptionIES->getState()->forceValue(*V, nix::noPos); - - if (V->type() != nix::ValueType::nAttrs) - return; - - auto &State = *OptionIES->getState(); - - if (isOption(State, *V)) - return; - - for (auto Attr : *V->attrs) { - if (isOption(State, *Attr.value)) { - auto OI = optionInfo(State, *Attr.value); - Items.emplace_back(CompletionItem{ - .label = State.symbols[Attr.name], - .kind = CompletionItemKind::Constructor, - .detail = OI.Type.value_or(""), - .documentation = - MarkupContent{MarkupKind::Markdown, OI.mdDoc()}}); - } else { - Items.emplace_back( - CompletionItem{.label = OptionIES->getState()->symbols[Attr.name], - .kind = CompletionItemKind::Class, - .detail = "{...}", - .documentation = std::nullopt}); - } - } - RR.Response = std::move(List); - } - } catch (std::exception &E) { - RR.Response = error(E.what()); - } + CompletionBuilder Builder; + Builder.addOption(*OptionIES->getState(), *OptionAttrSet, Params.Path); + RR.Response = Builder.getResult(); } } // namespace nixd