From 6eb1a2415c97ef576e9d46f3c5d13c7e8c2b6916 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Fri, 26 Apr 2024 18:06:43 +0800 Subject: [PATCH] {libnixt,nixd/Eval}: support `attrsOf submodule` (#465) Currently these options are nested under `foo.bar..options` Fixes: #106 --- libnixt/include/nixt/Value.h | 13 ++++++++++ libnixt/lib/Value.cpp | 42 +++++++++++++++++++++++++++++++ nixd/lib/Eval/AttrSetProvider.cpp | 6 +++-- 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/libnixt/include/nixt/Value.h b/libnixt/include/nixt/Value.h index 3e7717f34..5e5ec02b2 100644 --- a/libnixt/include/nixt/Value.h +++ b/libnixt/include/nixt/Value.h @@ -33,11 +33,24 @@ std::vector toSymbols(nix::SymbolTable &STable, /// \brief Select attribute \p Attr nix::Value &selectAttr(nix::EvalState &State, nix::Value &V, nix::Symbol Attr); +nix::Value &selectOption(nix::EvalState &State, nix::Value &V, + nix::Symbol Attr); + /// \brief Given an attrpath in nix::Value \p V, select it nix::Value &selectAttrPath(nix::EvalState &State, nix::Value &V, std::vector::const_iterator Begin, std::vector::const_iterator End); +/// \brief Select the option declaration list, \p V, dive into "submodules". +nix::Value &selectOptions(nix::EvalState &State, nix::Value &V, + std::vector::const_iterator Begin, + std::vector::const_iterator End); + +inline nix::Value &selectOptions(nix::EvalState &State, nix::Value &V, + const std::vector &AttrPath) { + return selectOptions(State, V, AttrPath.begin(), AttrPath.end()); +} + /// \copydoc selectAttrPath inline nix::Value &selectSymbols(nix::EvalState &State, nix::Value &V, const std::vector &AttrPath) { diff --git a/libnixt/lib/Value.cpp b/libnixt/lib/Value.cpp index c9a23336f..b776507a5 100644 --- a/libnixt/lib/Value.cpp +++ b/libnixt/lib/Value.cpp @@ -109,3 +109,45 @@ nix::Value &nixt::selectAttrPath(nix::EvalState &State, nix::Value &V, nix::Value &Nested = selectAttr(State, V, *Begin); return selectAttrPath(State, Nested, ++Begin, End); } + +nix::Value &nixt::selectOptions(nix::EvalState &State, nix::Value &V, + std::vector::const_iterator Begin, + std::vector::const_iterator End) { + if (Begin == End) + return V; + + if (isOption(State, V)) { + // If currently "V" is an option, it can still be submodules. + // + // e.g. users.users <-- the main option stops at here. + // networking.interfaces + // + // Take care of such case. + nix::Value &Type = selectAttr(State, V, State.sType); + if (checkField(State, Type, "name", "attrsOf")) { + nix::Value NestedTypes = + selectAttr(State, Type, State.symbols.create("nestedTypes")); + 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); + } + } + } + + // Otherwise, simply select it. + nix::Value &Nested = selectAttr(State, V, *Begin); + return selectOptions(State, Nested, ++Begin, End); +} diff --git a/nixd/lib/Eval/AttrSetProvider.cpp b/nixd/lib/Eval/AttrSetProvider.cpp index 1607d7d41..2397c6f1d 100644 --- a/nixd/lib/Eval/AttrSetProvider.cpp +++ b/nixd/lib/Eval/AttrSetProvider.cpp @@ -232,7 +232,8 @@ void AttrSetProvider::onOptionInfo( return; } - nix::Value &Option = nixt::selectStrings(state(), Nixpkgs, AttrPath); + nix::Value &Option = nixt::selectOptions( + state(), Nixpkgs, nixt::toSymbols(state().symbols, AttrPath)); OptionInfoResponse R; @@ -253,7 +254,8 @@ void AttrSetProvider::onOptionComplete( const AttrPathCompleteParams &Params, lspserver::Callback Reply) { try { - nix::Value &Scope = nixt::selectStrings(state(), Nixpkgs, Params.Scope); + nix::Value &Scope = nixt::selectOptions( + state(), Nixpkgs, nixt::toSymbols(state().symbols, Params.Scope)); state().forceValue(Scope, nix::noPos);