Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixd/Controller: support completion for pkgs.subpackages #563

Merged
merged 2 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 39 additions & 29 deletions nixd/lib/Controller/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,10 @@ IdiomSetT IdiomSet{nixd::idioms::Pkgs, nixd::idioms::Lib};
auto ItLib = IdiomSet.find(nixd::idioms::Lib);
auto ItPkgs = IdiomSet.find(nixd::idioms::Pkgs);

nixd::Selector idiomItSelector(IdiomSetT::iterator It) {
nixd::Selector getKnownIdiomSelector(IdiomSetT::iterator It) {
// Unknown name, cannot deal with it.
if (It == IdiomSet.end())
throw nixd::NotAnIdiomException();
throw nixd::idioms::NotAnIdiomException();

return [&]() -> nixd::Selector {
if (It == ItLib) {
Expand All @@ -162,25 +162,43 @@ nixd::Selector idiomItSelector(IdiomSetT::iterator It) {
}();
}

IdiomSetT::iterator varIdiomIt(const nixf::ExprVar &Var) {
return IdiomSet.find(Var.id().name());
};

nixd::Selector varSelector(const nixf::ExprVar &Var) {
return idiomItSelector(varIdiomIt(Var));
return getKnownIdiomSelector(IdiomSet.find(Var.id().name()));
};

nixd::Selector withSelector(const nixf::ExprWith &With) {
if (!With.with() || With.with()->kind() != Node::NK_ExprVar)
throw nixd::NotAnIdiomException();
return varSelector(static_cast<const nixf::ExprVar &>(*With.with()));
nixd::Selector withSelector(const nixf::ExprWith &With,
const nixf::VariableLookupAnalysis &VLA,
const nixf::ParentMapAnalysis &PM) {
if (!With.with())
throw nixd::idioms::NotAnIdiomException();
switch (With.with()->kind()) {
case Node::NK_ExprVar:
return nixd::idioms::mkVarSelector(
static_cast<const nixf::ExprVar &>(*With.with()), VLA, PM);
case Node::NK_ExprSelect:
return nixd::idioms::mkSelector(
static_cast<const nixf::ExprSelect &>(*With.with()), VLA, PM);
default:
break;
}
throw nixd::idioms::NotAnIdiomException();
}

} // namespace

nixd::Selector nixd::mkIdiomSelector(const nixf::ExprVar &Var,
const nixf::VariableLookupAnalysis &VLA,
const nixf::ParentMapAnalysis &PM) {
nixd::Selector nixd::idioms::mkSelector(const nixf::ExprSelect &Select,
nixd::Selector BaseSelector) {
if (Select.path())
return nixd::idioms::mkSelector(
*static_cast<const nixf::AttrPath *>(Select.path()),
std::move(BaseSelector));
return BaseSelector;
}

nixd::Selector
nixd::idioms::mkVarSelector(const nixf::ExprVar &Var,
const nixf::VariableLookupAnalysis &VLA,
const nixf::ParentMapAnalysis &PM) {
// Only check if the variable can be recogonized by some idiom.

using ResultKind = VariableLookupAnalysis::LookupResultKind;
Expand Down Expand Up @@ -208,7 +226,7 @@ nixd::Selector nixd::mkIdiomSelector(const nixf::ExprVar &Var,
assert(With && "parent of kwWith should be the with expression");
assert(With->kind() == nixf::Node::NK_ExprWith);
Selector WithSelector =
withSelector(static_cast<const nixf::ExprWith &>(*With));
withSelector(static_cast<const nixf::ExprWith &>(*With), VLA, PM);

// Append variable name after "with" expression selector.
// e.g.
Expand All @@ -228,8 +246,8 @@ nixd::Selector nixd::mkIdiomSelector(const nixf::ExprVar &Var,
return {};
}

nixd::Selector nixd::mkSelector(const nixf::AttrPath &AP,
Selector BaseSelector) {
nixd::Selector nixd::idioms::mkSelector(const nixf::AttrPath &AP,
Selector BaseSelector) {
const auto &Names = AP.names();
for (const auto &Name : Names) {
if (!Name->isStatic())
Expand All @@ -239,23 +257,15 @@ nixd::Selector nixd::mkSelector(const nixf::AttrPath &AP,
return BaseSelector;
}

nixd::Selector nixd::mkSelector(const nixf::ExprSelect &Select,
nixd::Selector BaseSelector) {
if (Select.path())
return nixd::mkSelector(*static_cast<const nixf::AttrPath *>(Select.path()),
std::move(BaseSelector));
return BaseSelector;
}

nixd::Selector nixd::mkSelector(const nixf::ExprSelect &Sel,
const nixf::VariableLookupAnalysis &VLA,
const nixf::ParentMapAnalysis &PM) {
nixd::Selector nixd::idioms::mkSelector(const nixf::ExprSelect &Sel,
const nixf::VariableLookupAnalysis &VLA,
const nixf::ParentMapAnalysis &PM) {
if (Sel.expr().kind() != Node::NK_ExprVar)
throw NotVariableSelect();

const auto &Var = static_cast<ExprVar &>(Sel.expr());

auto BaseSelector = mkIdiomSelector(Var, VLA, PM);
auto BaseSelector = mkVarSelector(Var, VLA, PM);

return mkSelector(Sel, std::move(BaseSelector));
}
Expand Down
56 changes: 28 additions & 28 deletions nixd/lib/Controller/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,6 @@ constexpr inline std::string_view Pkgs = "pkgs";
/// e.g. lib.genAttrs.
constexpr inline std::string_view Lib = "lib";

} // namespace idioms

/// \brief Search up until there are some node associated with "EnvNode".
[[nodiscard]] const nixf::EnvNode *
upEnv(const nixf::Node &Desc, const nixf::VariableLookupAnalysis &VLA,
const nixf::ParentMapAnalysis &PM);

/// \brief Determine whether or not some node has enclosed "with pkgs; [ ]"
///
/// Yes, this evaluation isn't flawless. What if the identifier isn't "pkgs"? We
/// can't dynamically evaluate everything each time and invalidate them
/// immediately after document updates. Therefore, this heuristic method
/// represents a trade-off between performance considerations.
[[nodiscard]] bool havePackageScope(const nixf::Node &N,
const nixf::VariableLookupAnalysis &VLA,
const nixf::ParentMapAnalysis &PM);

/// \brief get variable scope, and it's prefix name.
///
/// Nixpkgs has some packages scoped in "nested" attrs.
/// e.g. llvmPackages, pythonPackages.
/// Try to find these name as a pre-selected scope, the last value is "prefix".
std::pair<std::vector<std::string>, std::string>
getScopeAndPrefix(const nixf::Node &N, const nixf::ParentMapAnalysis &PM);

struct IdiomException : std::exception {};

/// \brief Exceptions scoped in nixd::mkIdiomSelector
Expand Down Expand Up @@ -81,9 +56,9 @@ struct UndefinedVarException : VLAException {
///
/// Try to heuristically find a selector of a variable, based on some known
/// idioms.
Selector mkIdiomSelector(const nixf::ExprVar &Var,
const nixf::VariableLookupAnalysis &VLA,
const nixf::ParentMapAnalysis &PM);
Selector mkVarSelector(const nixf::ExprVar &Var,
const nixf::VariableLookupAnalysis &VLA,
const nixf::ParentMapAnalysis &PM);

/// \brief The attrpath has a dynamic name, thus it cannot be trivially
/// transformed to "static" selector.
Expand All @@ -110,6 +85,31 @@ Selector mkSelector(const nixf::ExprSelect &Select,
const nixf::VariableLookupAnalysis &VLA,
const nixf::ParentMapAnalysis &PM);

} // namespace idioms

/// \brief Search up until there are some node associated with "EnvNode".
[[nodiscard]] const nixf::EnvNode *
upEnv(const nixf::Node &Desc, const nixf::VariableLookupAnalysis &VLA,
const nixf::ParentMapAnalysis &PM);

/// \brief Determine whether or not some node has enclosed "with pkgs; [ ]"
///
/// Yes, this evaluation isn't flawless. What if the identifier isn't "pkgs"? We
/// can't dynamically evaluate everything each time and invalidate them
/// immediately after document updates. Therefore, this heuristic method
/// represents a trade-off between performance considerations.
[[nodiscard]] bool havePackageScope(const nixf::Node &N,
const nixf::VariableLookupAnalysis &VLA,
const nixf::ParentMapAnalysis &PM);

/// \brief get variable scope, and it's prefix name.
///
/// Nixpkgs has some packages scoped in "nested" attrs.
/// e.g. llvmPackages, pythonPackages.
/// Try to find these name as a pre-selected scope, the last value is "prefix".
std::pair<std::vector<std::string>, std::string>
getScopeAndPrefix(const nixf::Node &N, const nixf::ParentMapAnalysis &PM);

enum class FindAttrPathResult {
OK,
Inherit,
Expand Down
5 changes: 3 additions & 2 deletions nixd/lib/Controller/Completion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ void completeVarName(const VariableLookupAnalysis &VLA,

// Try to complete the name by known idioms.
try {
Selector Sel = mkIdiomSelector(N, VLA, PM);
Selector Sel = idioms::mkVarSelector(N, VLA, PM);

// Clickling "pkgs" does not make sense for variable completion
if (Sel.empty())
Expand Down Expand Up @@ -359,7 +359,8 @@ void completeSelect(const nixf::ExprSelect &Select, AttrSetClient &Client,
NixpkgsCompletionProvider NCP(Client);

try {
Selector Sel = mkSelector(Select, mkIdiomSelector(Var, VLA, PM));
Selector Sel =
idioms::mkSelector(Select, idioms::mkVarSelector(Var, VLA, PM));
NCP.completePackages(mkParams(Sel, IsComplete), List);
} catch (ExceedSizeError &) {
// Let "onCompletion" catch this exception to set "inComplete" field.
Expand Down
3 changes: 2 additions & 1 deletion nixd/lib/Controller/Definition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <semaphore>

using namespace nixd;
using namespace nixd::idioms;
using namespace nixf;
using namespace lspserver;
using namespace llvm;
Expand Down Expand Up @@ -298,7 +299,7 @@ Locations defineVar(const ExprVar &Var, const VariableLookupAnalysis &VLA,

// Nixpkgs locations.
try {
Selector Sel = mkIdiomSelector(Var, VLA, PM);
Selector Sel = mkVarSelector(Var, VLA, PM);
Locations NixpkgsLocs = defineNixpkgsSelector(Sel, NixpkgsClient);
return mergeVec(std::move(StaticLocs), NixpkgsLocs);
} catch (std::exception &E) {
Expand Down
Loading