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 14, 2025
1 parent 6e58141 commit 871aef5
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 11 deletions.
11 changes: 9 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,15 @@ 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:
switch (peek(3).kind()) {
case tok_colon: // { a } :
case tok_at: // { a } @
return parseExprLambda();
default:
return parseExprAttrs();
}
default:
break;
}
Expand Down
182 changes: 173 additions & 9 deletions libnixf/test/Parse/ParseAttrs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,25 +223,189 @@ 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().lCur().line(), 1);
ASSERT_EQ(AST->range().lCur().column(), 0);
ASSERT_EQ(AST->range().rCur().line(), 4);
ASSERT_EQ(AST->range().rCur().column(), 1);


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

ASSERT_EQ(bindings.size(), 2);

ASSERT_EQ(bindings[0]->range().lCur().line(), 2);
ASSERT_EQ(bindings[0]->range().lCur().column(), 2);
ASSERT_EQ(bindings[0]->range().rCur().line(), 2);
ASSERT_EQ(bindings[0]->range().rCur().column(), 8);

ASSERT_EQ(bindings[1]->range().lCur().line(), 3);
ASSERT_EQ(bindings[1]->range().lCur().column(), 2);
ASSERT_EQ(bindings[1]->range().rCur().line(), 3);
ASSERT_EQ(bindings[1]->range().rCur().column(), 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().lCur().line(), 2);
ASSERT_EQ(name->range().lCur().column(), 2);
ASSERT_EQ(name->range().rCur().line(), 2);
ASSERT_EQ(name->range().rCur().column(), 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().lCur().line(), 2);
ASSERT_EQ(name->range().lCur().column(), 2);
ASSERT_EQ(name->range().rCur().line(), 2);
ASSERT_EQ(name->range().rCur().column(), 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().lCur().line(), 1);
ASSERT_EQ(AST->range().lCur().column(), 0);
ASSERT_EQ(AST->range().rCur().line(), 5);
ASSERT_EQ(AST->range().rCur().column(), 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));

ASSERT_TRUE(B.binds()->bindings()[1]->range().lCur().isAt(3, 2, 14));
ASSERT_TRUE(B.binds()->bindings()[1]->range().rCur().isAt(3, 8, 20));
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_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().lCur().line(), 3);
ASSERT_EQ(BaRHSb->range().lCur().column(), 4);
ASSERT_EQ(BaRHSb->range().rCur().line(), 3);
ASSERT_EQ(BaRHSb->range().rCur().column(), 10);

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

TEST(Parser, AttrsBindingEarlyExit) {
Expand Down

0 comments on commit 871aef5

Please sign in to comment.