Skip to content

Commit

Permalink
libnixt: mangle submodule options to it's declaration (#518)
Browse files Browse the repository at this point in the history
Trying to get completions from nixvim gets trouble, because all of its
options are nested under a "submodule".

The PR adds basic support for mangling submodule options into it's
declaration, and completion for nixvim works.

Fixes: #517
  • Loading branch information
inclyc authored Jun 2, 2024
1 parent da2d7de commit be5ad5e
Showing 1 changed file with 53 additions and 13 deletions.
66 changes: 53 additions & 13 deletions libnixt/lib/Value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <nix/attr-path.hh>
#include <nix/nixexpr.hh>
#include <nix/symbol-table.hh>
#include <nix/value.hh>

using namespace nixt;
Expand Down Expand Up @@ -110,9 +111,58 @@ nix::Value &nixt::selectAttrPath(nix::EvalState &State, nix::Value &V,
return selectAttrPath(State, Nested, ++Begin, End);
}

namespace {

/// \brief Check if the \p Type is a submodule.
bool isTypeSubmodule(nix::EvalState &State, nix::Value &Type) {
return checkField(State, Type, "name", "submodule");
}

nix::Value *trySelectAttr(nix::EvalState &State, nix::Value &V, nix::Symbol S) {
try {
nix::Value &Type = selectAttr(State, V, State.sType);
return &Type;
} catch (nix::TypeError &) {
// The value is not an attrset, thus it definitely cannot be a submodule.
return nullptr;
} catch (nix::AttrPathNotFound &) {
// The value has no "type" field.
return nullptr;
}
return nullptr;
}

/// \brief Get the type of an option.
nix::Value *tryGetSubmoduleType(nix::EvalState &State, nix::Value &V) {
if (nix::Value *Type = trySelectAttr(State, V, State.sType))
return isTypeSubmodule(State, *Type) ? Type : nullptr;
return nullptr;
}

/// \brief Do proper operations to get options declaration on submodule type.
nix::Value getSubOptions(nix::EvalState &State, nix::Value &Type) {
// For example, programs.nixvim has all options nested into this attrpath.
nix::Value &GetSubOptions =
selectAttr(State, Type, State.symbols.create("getSubOptions"));

nix::Value EmptyList;
EmptyList.mkList(0);
// Invoke "GetSubOptions"
nix::Value VResult;
State.callFunction(GetSubOptions, EmptyList, VResult, nix::noPos);
return VResult;
}

} // namespace

nix::Value nixt::selectOptions(nix::EvalState &State, nix::Value &V,
std::vector<nix::Symbol>::const_iterator Begin,
std::vector<nix::Symbol>::const_iterator End) {
// Always try to mangle the value if it is a submodule
if (nix::Value *SubType = tryGetSubmoduleType(State, V))
// Invoke getSubOptions on that type, and reset the value to it.
V = getSubOptions(State, *SubType);

if (Begin == End)
return V;

Expand All @@ -130,19 +180,9 @@ nix::Value nixt::selectOptions(nix::EvalState &State, nix::Value &V,
nix::Value ElemType =
selectAttr(State, NestedTypes, State.symbols.create("elemType"));

if (checkField(State, ElemType, "name", "submodule")) {
// Current iterator may be ommited, and V becomes "V.getSubOptions []"
nix::Value &GetSubOptions =
selectAttr(State, ElemType, State.symbols.create("getSubOptions"));

nix::Value EmptyList;
EmptyList.mkList(0);

// Invoke "GetSubOptions"
nix::Value Next;
State.callFunction(GetSubOptions, EmptyList, Next, nix::noPos);

return selectOptions(State, Next, ++Begin, End);
if (isTypeSubmodule(State, ElemType)) {
nix::Value ElemOptions = getSubOptions(State, ElemType);
return selectOptions(State, ElemOptions, ++Begin, End);
}
}
}
Expand Down

0 comments on commit be5ad5e

Please sign in to comment.