Skip to content

Commit

Permalink
fix(completion): correctly parse incomplete attrsets and provide comp…
Browse files Browse the repository at this point in the history
…letions
  • Loading branch information
Aetherall committed Jan 13, 2025
1 parent 6e58141 commit f26c107
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 11 deletions.
9 changes: 9 additions & 0 deletions libnixf/include/nixf/Basic/Range.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <cstddef> // std::size_t
#include <cstdint>
#include <string>

namespace nixf {

Expand All @@ -28,6 +29,10 @@ class Position {
friend bool operator<=(const Position &LHS, const Position &RHS) {
return LHS < RHS || LHS == RHS;
}

std::string to_string() const {
return std::to_string(Line) + ":" + std::to_string(Column);
}
};

class PositionRange {
Expand Down Expand Up @@ -123,6 +128,10 @@ class LexerCursorRange {
[[nodiscard]] PositionRange range() const {
return {LCur.position(), RCur.position()};
}

std::string to_string() const {
return LCur.position().to_string() + " -> " + RCur.position().to_string();
}
};

} // namespace nixf
8 changes: 6 additions & 2 deletions libnixf/src/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ std::shared_ptr<Expr> Parser::parseExprApp(int Limit) {
}

std::shared_ptr<Expr> Parser::parseExpr() {
// Look ahead 3 tokens.
// Look ahead 4 tokens.
switch (peek().kind()) {
case tok_id: {
switch (peek(1).kind()) {
Expand All @@ -93,8 +93,12 @@ std::shared_ptr<Expr> Parser::parseExpr() {
case tok_comma: // { a ,
case tok_id: // { a b
case tok_ellipsis: // { a ...
case tok_r_curly:
return parseExprLambda();
case tok_r_curly:
if(peek(3).kind() == tok_colon){
return parseExprLambda();
}
return parseExprAttrs();
default:
break;
}
Expand Down
151 changes: 143 additions & 8 deletions libnixf/test/Parse/ParseAttrs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,27 +223,162 @@ TEST(Parser, AttrsBinding) {
}
)"sv;

std::vector<Diagnostic> Diags;
auto AST = nixf::parse(Src, Diags);
ASSERT_TRUE(AST);
ASSERT_EQ(Diags.size(), 0);

ASSERT_EQ(AST->kind(), Node::NK_ExprAttrs);
ASSERT_EQ(AST->range().to_string(), "1:0 -> 4:1");

auto &attrs = *static_cast<ExprAttrs *>(AST.get());
auto &bindings = attrs.binds()->bindings();

ASSERT_EQ(bindings.size(), 2);

ASSERT_EQ(bindings[0]->range().to_string(), "2:2 -> 2:8");
ASSERT_EQ(bindings[1]->range().to_string(), "3:2 -> 3:8");
}

TEST(Parser, AttrsBindingWriting) {
auto Src = R"(
{
a
}
)"sv;

std::vector<Diagnostic> Diags;
auto AST = nixf::parse(Src, Diags);

ASSERT_TRUE(AST);
ASSERT_EQ(AST->kind(), Node::NK_ExprAttrs);

auto &attrs = *static_cast<ExprAttrs *>(AST.get());
ASSERT_EQ(attrs.binds()->bindings().size(), 1);

const auto &binding = static_cast<Binding *>(attrs.binds()->bindings()[0].get());

ASSERT_EQ(binding->kind(), Node::NK_Binding);

const auto &path = binding->path();
ASSERT_EQ(path.names().size(), 1);

const auto &name = path.names().at(0);

ASSERT_EQ(name->kind(), Node::NK_Interpolation);
ASSERT_EQ(name->id()->kind(), Node::NK_Identifier);
ASSERT_EQ(name->id()->name(), "a");
ASSERT_EQ(name->range().to_string(), "2:2 -> 2:3");
}


TEST(Parser, AttrsBindingWritingDot) {
auto Src = R"(
{
a.
}
)"sv;

std::vector<Diagnostic> Diags;
auto AST = nixf::parse(Src, Diags);

ASSERT_TRUE(AST);
ASSERT_EQ(AST->kind(), Node::NK_ExprAttrs);

auto &attrs = *static_cast<ExprAttrs *>(AST.get());
ASSERT_EQ(attrs.binds()->bindings().size(), 1);

const auto &binding = static_cast<Binding *>(attrs.binds()->bindings()[0].get());

ASSERT_EQ(binding->kind(), Node::NK_Binding);

const auto &path = binding->path();
ASSERT_EQ(path.names().size(), 1);

const auto &name = path.names().at(0);

ASSERT_EQ(name->kind(), Node::NK_Interpolation);
ASSERT_EQ(name->id()->kind(), Node::NK_Identifier);
ASSERT_EQ(name->id()->name(), "a");
ASSERT_EQ(name->range().to_string(), "2:2 -> 2:3");
}



TEST(Parser, AttrsNestedInLambda) {
auto Src = R"(
{...}: {
a = {
b = 1;
};
}
)"sv;

std::vector<Diagnostic> Diags;
auto AST = nixf::parse(Src, Diags);

ASSERT_TRUE(AST);
ASSERT_EQ(AST->kind(), Node::NK_ExprLambda);
ASSERT_EQ(AST->range().to_string(), "1:0 -> 5:1");

ASSERT_EQ(Diags.size(), 0);

const auto &attrs = static_cast<ExprAttrs *>(static_cast<ExprLambda *>(AST.get())->body());
ASSERT_EQ(attrs->kind(), Node::NK_ExprAttrs);

const auto &binding = static_cast<Binding *>(attrs->binds()->bindings()[0].get());

ASSERT_EQ(binding->kind(), Node::NK_Binding);

const auto &nestedAttrs = static_cast<ExprAttrs *>(binding->value().get());

ASSERT_EQ(nestedAttrs->kind(), Node::NK_ExprAttrs);
}

TEST(Parser, AttrsNested) {
auto Src = R"(
{
a = {
b = 1;
};
}
)"sv;

std::vector<Diagnostic> Diags;
auto AST = nixf::parse(Src, Diags);

ASSERT_TRUE(AST);
ASSERT_EQ(AST->kind(), Node::NK_ExprAttrs);
ASSERT_TRUE(AST->range().lCur().isAt(1, 0, 1));
ASSERT_TRUE(AST->range().rCur().isAt(4, 1, 22));
ASSERT_TRUE(AST->range().rCur().isAt(5, 1, 28));

ASSERT_EQ(Diags.size(), 0);

// Check the bindings.
auto &B = *static_cast<ExprAttrs *>(AST.get());
assert(B.binds() && "expected bindings");
ASSERT_EQ(B.binds()->bindings().size(), 2);
ASSERT_TRUE(B.binds()->bindings()[0]->range().lCur().isAt(2, 2, 5));
ASSERT_TRUE(B.binds()->bindings()[0]->range().rCur().isAt(2, 8, 11));
auto &B = static_cast<ExprAttrs *>(AST.get())->binds()->bindings();
ASSERT_EQ(B.size(), 1);

const auto &Ba = static_cast<Binding *>(B[0].get());

ASSERT_EQ(Ba->kind(), Node::NK_Binding);
ASSERT_TRUE(Ba->range().lCur().isAt(2, 2, 5));
ASSERT_TRUE(Ba->range().rCur().isAt(4, 4, 26));

ASSERT_TRUE(B.binds()->bindings()[1]->range().lCur().isAt(3, 2, 14));
ASSERT_TRUE(B.binds()->bindings()[1]->range().rCur().isAt(3, 8, 20));
ASSERT_EQ(Ba->path().names().at(0)->id()->name(), "a");
ASSERT_EQ(Ba->value()->kind(), Node::NK_ExprAttrs);

auto &BaRHS = static_cast<ExprAttrs *>(Ba->value().get())->binds()->bindings();
ASSERT_EQ(BaRHS.size(), 1);
ASSERT_EQ(BaRHS[0]->kind(), Node::NK_Binding);

const auto &BaRHSb = static_cast<Binding *>(BaRHS[0].get());

ASSERT_EQ(BaRHSb->range().to_string(), "3:4 -> 3:10");
ASSERT_EQ(BaRHSb->path().names().at(0)->id()->name(), "b");
ASSERT_EQ(BaRHSb->value()->kind(), Node::NK_ExprInt);
}


TEST(Parser, AttrsBindingEarlyExit) {
auto Src = R"(
{
Expand Down
1 change: 0 additions & 1 deletion nixd/lib/Controller/Completion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,6 @@ void Controller::onCompletion(const CompletionParams &Params,
const auto AST = CheckDefault(getAST(*TU));

const auto *Desc = AST->descend({Pos, Pos});
CheckDefault(Desc && Desc->children().empty());

const auto &N = *Desc;
const auto &PM = *TU->parentMap();
Expand Down

0 comments on commit f26c107

Please sign in to comment.