Skip to content

Commit

Permalink
nixd/Sema: use CompletionBuilder for OptionWorker
Browse files Browse the repository at this point in the history
  • Loading branch information
inclyc committed Aug 20, 2023
1 parent dac20e0 commit d5268eb
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 67 deletions.
5 changes: 5 additions & 0 deletions nixd/include/nixd/Nix/Value.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <nix/attr-path.hh>
#include <nix/eval.hh>
#include <nix/value.hh>

namespace nixd {

Expand All @@ -12,6 +13,10 @@ bool isDerivation(nix::EvalState &State, nix::Value &V);
std::optional<std::string> 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<std::string> &AttrPath);

extern int PrintDepth;

} // namespace nixd
4 changes: 4 additions & 0 deletions nixd/include/nixd/Sema/CompletionBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> &AttrPath);

CompletionResult &getResult() { return Result; }

void setLimit(size_t Limit) { this->Limit = Limit; }
Expand Down
12 changes: 12 additions & 0 deletions nixd/lib/Nix/Value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <nix/eval.hh>
#include <nix/print.hh>

#include <boost/algorithm/string/join.hpp>

namespace nixd {

bool isOption(nix::EvalState &State, nix::Value &V) {
Expand Down Expand Up @@ -41,6 +43,16 @@ std::optional<std::string> attrPathStr(nix::EvalState &State, nix::Value &V,

int PrintDepth;

nix::Value selectAttrPath(nix::EvalState &State, nix::Value Set,
const std::vector<std::string> &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 {
Expand Down
34 changes: 34 additions & 0 deletions nixd/lib/Sema/CompletionBuilder.cpp
Original file line number Diff line number Diff line change
@@ -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"

Expand Down Expand Up @@ -136,4 +138,36 @@ void CompletionBuilder::addStaticEnv(const nix::SymbolTable &STable,
addStaticEnv(STable, SEnv);
}

void CompletionBuilder::addOption(nix::EvalState &State, nix::Value &OptionSet,
const std::vector<std::string> &AttrPath) {
using lspserver::MarkupContent;
using lspserver::MarkupKind;

try {
nix::Value V = selectAttrPath(State, OptionSet, 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
77 changes: 10 additions & 67 deletions nixd/lib/Server/OptionWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -31,37 +32,26 @@ void OptionWorker::onDeclaration(
const ipc::AttrPathParams &Params,
lspserver::Callback<lspserver::Location> Reply) {
using namespace lspserver;
using boost::algorithm::join;
ReplyRAII<Location> RR(std::move(Reply));
if (!OptionAttrSet)
return;
if (OptionAttrSet->type() != nix::ValueType::nAttrs)
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();
Expand Down Expand Up @@ -110,61 +100,14 @@ void OptionWorker::onEvalOptionSet(
void OptionWorker::onCompletion(const ipc::AttrPathParams &Params,
lspserver::Callback<llvm::json::Value> Reply) {
using namespace lspserver;
using boost::algorithm::join;
ReplyRAII<CompletionList> 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

0 comments on commit d5268eb

Please sign in to comment.