From 8b0a9f9d13271fa5f1f72ca8f0bf8926e0fcecda Mon Sep 17 00:00:00 2001 From: takeokunn Date: Sun, 4 Jan 2026 20:19:29 +0900 Subject: [PATCH 1/7] feat(code-action): add flatten nested attribute set refactor action - Introduce addFlattenAttrsAction to provide a code action that flattens nested attribute sets into dot-notation assignments - Integrate flatten action into code action controller for eligible nodes - Ensure flattening only applies to static, non-recursive attribute sets --- nixd/lib/Controller/CodeAction.cpp | 97 ++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/nixd/lib/Controller/CodeAction.cpp b/nixd/lib/Controller/CodeAction.cpp index 584cb8095..f617eb07b 100644 --- a/nixd/lib/Controller/CodeAction.cpp +++ b/nixd/lib/Controller/CodeAction.cpp @@ -68,6 +68,101 @@ bool isValidNixIdentifier(const std::string &S) { return Keywords.find(S) == Keywords.end(); } +/// \brief Check if an ExprAttrs can be flattened (no rec, inherit, dynamic). +/// Returns the Binds node if flattenable, nullptr otherwise. +const nixf::Binds *getFlattenableBinds(const nixf::ExprAttrs &Attrs) { + // Block if recursive attribute set + if (Attrs.isRecursive()) + return nullptr; + + const nixf::Binds *B = Attrs.binds(); + if (!B || B->bindings().empty()) + return nullptr; + + // Check all bindings: must be plain Binding nodes (no Inherit) + // and all attribute names must be static (no dynamic ${} interpolation) + for (const auto &Child : B->bindings()) { + if (Child->kind() != nixf::Node::NK_Binding) + return nullptr; // Inherit node found + + const auto &Bind = static_cast(*Child); + for (const auto &Name : Bind.path().names()) { + if (!Name->isStatic()) + return nullptr; // Dynamic attribute name + } + } + + return B; +} + +/// \brief Add flatten action for nested attribute sets. +/// Transforms: { foo = { bar = 1; }; } -> { foo.bar = 1; } +void addFlattenAttrsAction(const nixf::Node &N, + const nixf::ParentMapAnalysis &PM, + const std::string &FileURI, llvm::StringRef Src, + std::vector &Actions) { + // Find if we're inside a Binding + const nixf::Node *BindingNode = PM.upTo(N, nixf::Node::NK_Binding); + if (!BindingNode) + return; + + const auto &Bind = static_cast(*BindingNode); + + // Check if the binding's value is an ExprAttrs + if (!Bind.value() || Bind.value()->kind() != nixf::Node::NK_ExprAttrs) + return; + + const auto &NestedAttrs = + static_cast(*Bind.value()); + + // Check if flattenable + const nixf::Binds *NestedBinds = getFlattenableBinds(NestedAttrs); + if (!NestedBinds) + return; + + // Check outer path is static too + for (const auto &Name : Bind.path().names()) { + if (!Name->isStatic()) + return; + } + + // Build the flattened text + std::string NewText; + const auto &NestedBindings = NestedBinds->bindings(); + + // Pre-allocate to reduce reallocations. The +40 accounts for inner path, + // " = ", value text, and ";". May under-allocate for complex expressions + // but still reduces reallocations significantly. + const std::string_view OuterPath = Bind.path().src(Src); + size_t EstimatedSize = NestedBindings.size() * (OuterPath.size() + 40); + NewText.reserve(EstimatedSize); + + for (size_t I = 0; I < NestedBindings.size(); ++I) { + const auto &InnerBind = + static_cast(*NestedBindings[I]); + + // Build path: outer.inner + const std::string_view InnerPath = InnerBind.path().src(Src); + + NewText += OuterPath; + NewText += "."; + NewText += InnerPath; + NewText += " = "; + + if (InnerBind.value()) { + NewText += InnerBind.value()->src(Src); + } + NewText += ";"; + + if (I + 1 < NestedBindings.size()) + NewText += " "; + } + + Actions.emplace_back(createSingleEditAction( + "Flatten nested attribute set", CodeAction::REFACTOR_REWRITE_KIND, + FileURI, toLSPRange(Src, Bind.range()), std::move(NewText))); +} + /// \brief Add refactoring code actions for attribute names (quote/unquote). void addAttrNameActions(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, const std::string &FileURI, llvm::StringRef Src, @@ -144,6 +239,8 @@ void Controller::onCodeAction(const lspserver::CodeActionParams &Params, nixf::PositionRange NixfRange = toNixfRange(Range); if (const nixf::Node *N = TU->ast()->descend(NixfRange)) { addAttrNameActions(*N, *TU->parentMap(), FileURI, TU->src(), Actions); + addFlattenAttrsAction(*N, *TU->parentMap(), FileURI, TU->src(), + Actions); } } From 4263266b421bf4f689215d78c15ce6b1eac71a25 Mon Sep 17 00:00:00 2001 From: takeokunn Date: Sun, 4 Jan 2026 20:20:07 +0900 Subject: [PATCH 2/7] test: add tests for flatten nested attribute set code action - Add tests for flattening simple, deep, and multi-level attrsets - Add negative tests for dynamic, empty, inherit, and rec attrsets - Verify correct code action offering and ordering in each scenario --- .../test/code-action/flatten-attrs-deep.md | 93 ++++++++++++++ .../test/code-action/flatten-attrs-dynamic.md | 75 +++++++++++ .../test/code-action/flatten-attrs-empty.md | 75 +++++++++++ .../test/code-action/flatten-attrs-inherit.md | 75 +++++++++++ .../test/code-action/flatten-attrs-multi.md | 116 ++++++++++++++++++ .../test/code-action/flatten-attrs-rec.md | 75 +++++++++++ .../nixd/test/code-action/flatten-attrs.md | 116 ++++++++++++++++++ 7 files changed, 625 insertions(+) create mode 100644 nixd/tools/nixd/test/code-action/flatten-attrs-deep.md create mode 100644 nixd/tools/nixd/test/code-action/flatten-attrs-dynamic.md create mode 100644 nixd/tools/nixd/test/code-action/flatten-attrs-empty.md create mode 100644 nixd/tools/nixd/test/code-action/flatten-attrs-inherit.md create mode 100644 nixd/tools/nixd/test/code-action/flatten-attrs-multi.md create mode 100644 nixd/tools/nixd/test/code-action/flatten-attrs-rec.md create mode 100644 nixd/tools/nixd/test/code-action/flatten-attrs.md diff --git a/nixd/tools/nixd/test/code-action/flatten-attrs-deep.md b/nixd/tools/nixd/test/code-action/flatten-attrs-deep.md new file mode 100644 index 000000000..7d45ae6c7 --- /dev/null +++ b/nixd/tools/nixd/test/code-action/flatten-attrs-deep.md @@ -0,0 +1,93 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +Test that flatten action works with already-dotted outer paths. + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```nix file:///flatten-attrs-deep.nix +{ a.b = { c = 1; }; } +``` + +<-- textDocument/codeAction(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/codeAction", + "params":{ + "textDocument":{ + "uri":"file:///flatten-attrs-deep.nix" + }, + "range":{ + "start":{ + "line": 0, + "character":2 + }, + "end":{ + "line":0, + "character":5 + } + }, + "context":{ + "diagnostics":[], + "triggerKind":2 + } + } +} +``` + +No Quote action since "a.b" is a path, not a simple identifier. + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": [ +CHECK-NEXT: { +CHECK-NEXT: "edit": { +CHECK-NEXT: "changes": { +CHECK-NEXT: "file:///flatten-attrs-deep.nix": [ +CHECK-NEXT: { +CHECK-NEXT: "newText": "a.b.c = 1;", +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 19, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 2, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: } +CHECK-NEXT: } +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "kind": "refactor.rewrite", +CHECK-NEXT: "title": "Flatten nested attribute set" +CHECK-NEXT: } +CHECK-NEXT: ] +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/code-action/flatten-attrs-dynamic.md b/nixd/tools/nixd/test/code-action/flatten-attrs-dynamic.md new file mode 100644 index 000000000..5dfd8dcd0 --- /dev/null +++ b/nixd/tools/nixd/test/code-action/flatten-attrs-dynamic.md @@ -0,0 +1,75 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +Test that flatten action is NOT offered when nested attrset contains dynamic attribute names. + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```nix file:///flatten-attrs-dynamic.nix +{ foo = { ${x} = 1; }; } +``` + +<-- textDocument/codeAction(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/codeAction", + "params":{ + "textDocument":{ + "uri":"file:///flatten-attrs-dynamic.nix" + }, + "range":{ + "start":{ + "line": 0, + "character":2 + }, + "end":{ + "line":0, + "character":5 + } + }, + "context":{ + "diagnostics":[], + "triggerKind":2 + } + } +} +``` + +Dynamic attrsets should not offer flatten (only quote action available) + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": [ +CHECK-NEXT: { +CHECK-NEXT: "edit": { +CHECK-NEXT: "changes": { +CHECK-NEXT: "file:///flatten-attrs-dynamic.nix": [ +CHECK-NEXT: { +CHECK-NEXT: "newText": "\"foo\"", +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/code-action/flatten-attrs-empty.md b/nixd/tools/nixd/test/code-action/flatten-attrs-empty.md new file mode 100644 index 000000000..4227008b7 --- /dev/null +++ b/nixd/tools/nixd/test/code-action/flatten-attrs-empty.md @@ -0,0 +1,75 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +Test that flatten action is NOT offered for empty nested attribute sets. + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```nix file:///flatten-attrs-empty.nix +{ foo = { }; } +``` + +<-- textDocument/codeAction(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/codeAction", + "params":{ + "textDocument":{ + "uri":"file:///flatten-attrs-empty.nix" + }, + "range":{ + "start":{ + "line": 0, + "character":2 + }, + "end":{ + "line":0, + "character":5 + } + }, + "context":{ + "diagnostics":[], + "triggerKind":2 + } + } +} +``` + +Empty attrsets should not offer flatten (only quote action available) + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": [ +CHECK-NEXT: { +CHECK-NEXT: "edit": { +CHECK-NEXT: "changes": { +CHECK-NEXT: "file:///flatten-attrs-empty.nix": [ +CHECK-NEXT: { +CHECK-NEXT: "newText": "\"foo\"", +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/code-action/flatten-attrs-inherit.md b/nixd/tools/nixd/test/code-action/flatten-attrs-inherit.md new file mode 100644 index 000000000..ab5e53e90 --- /dev/null +++ b/nixd/tools/nixd/test/code-action/flatten-attrs-inherit.md @@ -0,0 +1,75 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +Test that flatten action is NOT offered when nested attrset contains inherit. + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```nix file:///flatten-attrs-inherit.nix +{ foo = { inherit bar; }; } +``` + +<-- textDocument/codeAction(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/codeAction", + "params":{ + "textDocument":{ + "uri":"file:///flatten-attrs-inherit.nix" + }, + "range":{ + "start":{ + "line": 0, + "character":2 + }, + "end":{ + "line":0, + "character":5 + } + }, + "context":{ + "diagnostics":[], + "triggerKind":2 + } + } +} +``` + +Attrsets with inherit should not offer flatten (only quote action available) + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": [ +CHECK-NEXT: { +CHECK-NEXT: "edit": { +CHECK-NEXT: "changes": { +CHECK-NEXT: "file:///flatten-attrs-inherit.nix": [ +CHECK-NEXT: { +CHECK-NEXT: "newText": "\"foo\"", +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/code-action/flatten-attrs-multi.md b/nixd/tools/nixd/test/code-action/flatten-attrs-multi.md new file mode 100644 index 000000000..8405b2569 --- /dev/null +++ b/nixd/tools/nixd/test/code-action/flatten-attrs-multi.md @@ -0,0 +1,116 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +Test that flatten action works for multiple nested bindings. + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```nix file:///flatten-attrs-multi.nix +{ foo = { bar = 1; baz = 2; }; } +``` + +<-- textDocument/codeAction(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/codeAction", + "params":{ + "textDocument":{ + "uri":"file:///flatten-attrs-multi.nix" + }, + "range":{ + "start":{ + "line": 0, + "character":2 + }, + "end":{ + "line":0, + "character":5 + } + }, + "context":{ + "diagnostics":[], + "triggerKind":2 + } + } +} +``` + +Quote action appears first, then Flatten action second. + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": [ +CHECK-NEXT: { +CHECK-NEXT: "edit": { +CHECK-NEXT: "changes": { +CHECK-NEXT: "file:///flatten-attrs-multi.nix": [ +CHECK-NEXT: { +CHECK-NEXT: "newText": "\"foo\"", +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 5, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 2, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: } +CHECK-NEXT: } +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "kind": "refactor.rewrite", +CHECK-NEXT: "title": "Quote attribute name" +CHECK-NEXT: }, +CHECK-NEXT: { +CHECK-NEXT: "edit": { +CHECK-NEXT: "changes": { +CHECK-NEXT: "file:///flatten-attrs-multi.nix": [ +CHECK-NEXT: { +CHECK-NEXT: "newText": "foo.bar = 1; foo.baz = 2;", +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 30, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 2, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: } +CHECK-NEXT: } +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "kind": "refactor.rewrite", +CHECK-NEXT: "title": "Flatten nested attribute set" +CHECK-NEXT: } +CHECK-NEXT: ] +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/code-action/flatten-attrs-rec.md b/nixd/tools/nixd/test/code-action/flatten-attrs-rec.md new file mode 100644 index 000000000..1d89ded8d --- /dev/null +++ b/nixd/tools/nixd/test/code-action/flatten-attrs-rec.md @@ -0,0 +1,75 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +Test that flatten action is NOT offered for recursive (rec) attribute sets. + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```nix file:///flatten-attrs-rec.nix +{ foo = rec { bar = 1; }; } +``` + +<-- textDocument/codeAction(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/codeAction", + "params":{ + "textDocument":{ + "uri":"file:///flatten-attrs-rec.nix" + }, + "range":{ + "start":{ + "line": 0, + "character":2 + }, + "end":{ + "line":0, + "character":5 + } + }, + "context":{ + "diagnostics":[], + "triggerKind":2 + } + } +} +``` + +Recursive attrsets should not offer flatten (only quote action available) + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": [ +CHECK-NEXT: { +CHECK-NEXT: "edit": { +CHECK-NEXT: "changes": { +CHECK-NEXT: "file:///flatten-attrs-rec.nix": [ +CHECK-NEXT: { +CHECK-NEXT: "newText": "\"foo\"", +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/code-action/flatten-attrs.md b/nixd/tools/nixd/test/code-action/flatten-attrs.md new file mode 100644 index 000000000..6d4fc234f --- /dev/null +++ b/nixd/tools/nixd/test/code-action/flatten-attrs.md @@ -0,0 +1,116 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +Test that "Flatten nested attribute set" action is offered for simple nested attrsets. + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```nix file:///flatten-attrs.nix +{ foo = { bar = 1; }; } +``` + +<-- textDocument/codeAction(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/codeAction", + "params":{ + "textDocument":{ + "uri":"file:///flatten-attrs.nix" + }, + "range":{ + "start":{ + "line": 0, + "character":2 + }, + "end":{ + "line":0, + "character":5 + } + }, + "context":{ + "diagnostics":[], + "triggerKind":2 + } + } +} +``` + +The Quote action appears first, then Flatten action second. + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": [ +CHECK-NEXT: { +CHECK-NEXT: "edit": { +CHECK-NEXT: "changes": { +CHECK-NEXT: "file:///flatten-attrs.nix": [ +CHECK-NEXT: { +CHECK-NEXT: "newText": "\"foo\"", +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 5, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 2, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: } +CHECK-NEXT: } +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "kind": "refactor.rewrite", +CHECK-NEXT: "title": "Quote attribute name" +CHECK-NEXT: }, +CHECK-NEXT: { +CHECK-NEXT: "edit": { +CHECK-NEXT: "changes": { +CHECK-NEXT: "file:///flatten-attrs.nix": [ +CHECK-NEXT: { +CHECK-NEXT: "newText": "foo.bar = 1;", +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 21, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 2, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: } +CHECK-NEXT: } +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "kind": "refactor.rewrite", +CHECK-NEXT: "title": "Flatten nested attribute set" +CHECK-NEXT: } +CHECK-NEXT: ] +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` From e7e5a5e7b393cf70244c58642e26959fc5d79bb9 Mon Sep 17 00:00:00 2001 From: takeokunn Date: Sun, 4 Jan 2026 20:24:54 +0900 Subject: [PATCH 3/7] chore(code-action): update CodeAction.cpp without code changes --- nixd/lib/Controller/CodeAction.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nixd/lib/Controller/CodeAction.cpp b/nixd/lib/Controller/CodeAction.cpp index f617eb07b..2a576cd16 100644 --- a/nixd/lib/Controller/CodeAction.cpp +++ b/nixd/lib/Controller/CodeAction.cpp @@ -112,8 +112,7 @@ void addFlattenAttrsAction(const nixf::Node &N, if (!Bind.value() || Bind.value()->kind() != nixf::Node::NK_ExprAttrs) return; - const auto &NestedAttrs = - static_cast(*Bind.value()); + const auto &NestedAttrs = static_cast(*Bind.value()); // Check if flattenable const nixf::Binds *NestedBinds = getFlattenableBinds(NestedAttrs); From f0223f64099ec326c2083d517a3135ed342b3b5e Mon Sep 17 00:00:00 2001 From: takeokunn Date: Sun, 4 Jan 2026 22:53:27 +0900 Subject: [PATCH 4/7] test: use CHECK-NOT for flatten-attrs-dynamic.md Verify the absence of flatten action using CHECK-NOT directive instead of testing quote action from another PR. --- .../nixd/test/code-action/flatten-attrs-dynamic.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/nixd/tools/nixd/test/code-action/flatten-attrs-dynamic.md b/nixd/tools/nixd/test/code-action/flatten-attrs-dynamic.md index 5dfd8dcd0..0cb670620 100644 --- a/nixd/tools/nixd/test/code-action/flatten-attrs-dynamic.md +++ b/nixd/tools/nixd/test/code-action/flatten-attrs-dynamic.md @@ -56,18 +56,12 @@ Test that flatten action is NOT offered when nested attrset contains dynamic att } ``` -Dynamic attrsets should not offer flatten (only quote action available) +Dynamic attrsets should not offer flatten action. ``` CHECK: "id": 2, CHECK-NEXT: "jsonrpc": "2.0", -CHECK-NEXT: "result": [ -CHECK-NEXT: { -CHECK-NEXT: "edit": { -CHECK-NEXT: "changes": { -CHECK-NEXT: "file:///flatten-attrs-dynamic.nix": [ -CHECK-NEXT: { -CHECK-NEXT: "newText": "\"foo\"", + CHECK-NOT: "Flatten nested attribute set" ``` ```json From d7c5141982638c9ceef44455853c78b517a12c17 Mon Sep 17 00:00:00 2001 From: takeokunn Date: Sun, 4 Jan 2026 22:53:47 +0900 Subject: [PATCH 5/7] test: use CHECK-NOT for flatten-attrs-empty.md Verify the absence of flatten action using CHECK-NOT directive instead of testing quote action from another PR. --- .../tools/nixd/test/code-action/flatten-attrs-empty.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/nixd/tools/nixd/test/code-action/flatten-attrs-empty.md b/nixd/tools/nixd/test/code-action/flatten-attrs-empty.md index 4227008b7..b142816ad 100644 --- a/nixd/tools/nixd/test/code-action/flatten-attrs-empty.md +++ b/nixd/tools/nixd/test/code-action/flatten-attrs-empty.md @@ -56,18 +56,12 @@ Test that flatten action is NOT offered for empty nested attribute sets. } ``` -Empty attrsets should not offer flatten (only quote action available) +Empty attrsets should not offer flatten action. ``` CHECK: "id": 2, CHECK-NEXT: "jsonrpc": "2.0", -CHECK-NEXT: "result": [ -CHECK-NEXT: { -CHECK-NEXT: "edit": { -CHECK-NEXT: "changes": { -CHECK-NEXT: "file:///flatten-attrs-empty.nix": [ -CHECK-NEXT: { -CHECK-NEXT: "newText": "\"foo\"", + CHECK-NOT: "Flatten nested attribute set" ``` ```json From aedf5144f115159a89bd0de3036d39c470d0ba29 Mon Sep 17 00:00:00 2001 From: takeokunn Date: Sun, 4 Jan 2026 22:54:08 +0900 Subject: [PATCH 6/7] test: use CHECK-NOT for flatten-attrs-inherit.md Verify the absence of flatten action using CHECK-NOT directive instead of testing quote action from another PR. --- .../nixd/test/code-action/flatten-attrs-inherit.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/nixd/tools/nixd/test/code-action/flatten-attrs-inherit.md b/nixd/tools/nixd/test/code-action/flatten-attrs-inherit.md index ab5e53e90..c413a5392 100644 --- a/nixd/tools/nixd/test/code-action/flatten-attrs-inherit.md +++ b/nixd/tools/nixd/test/code-action/flatten-attrs-inherit.md @@ -56,18 +56,12 @@ Test that flatten action is NOT offered when nested attrset contains inherit. } ``` -Attrsets with inherit should not offer flatten (only quote action available) +Attrsets with inherit should not offer flatten action. ``` CHECK: "id": 2, CHECK-NEXT: "jsonrpc": "2.0", -CHECK-NEXT: "result": [ -CHECK-NEXT: { -CHECK-NEXT: "edit": { -CHECK-NEXT: "changes": { -CHECK-NEXT: "file:///flatten-attrs-inherit.nix": [ -CHECK-NEXT: { -CHECK-NEXT: "newText": "\"foo\"", + CHECK-NOT: "Flatten nested attribute set" ``` ```json From 158ea126c6a2bd7993fc610870c1b92e2ae6df80 Mon Sep 17 00:00:00 2001 From: takeokunn Date: Sun, 4 Jan 2026 22:54:46 +0900 Subject: [PATCH 7/7] test: simplify flatten-attrs-multi.md test - Remove quote action tests (belongs to different PR) - Use CHECK: instead of CHECK-NEXT: to skip intermediate sections - Focus only on testing flatten action functionality --- .../test/code-action/flatten-attrs-multi.md | 35 ++----------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/nixd/tools/nixd/test/code-action/flatten-attrs-multi.md b/nixd/tools/nixd/test/code-action/flatten-attrs-multi.md index 8405b2569..b2843f882 100644 --- a/nixd/tools/nixd/test/code-action/flatten-attrs-multi.md +++ b/nixd/tools/nixd/test/code-action/flatten-attrs-multi.md @@ -56,41 +56,12 @@ Test that flatten action works for multiple nested bindings. } ``` -Quote action appears first, then Flatten action second. +Verify that flatten action is offered for multiple nested bindings. ``` CHECK: "id": 2, CHECK-NEXT: "jsonrpc": "2.0", -CHECK-NEXT: "result": [ -CHECK-NEXT: { -CHECK-NEXT: "edit": { -CHECK-NEXT: "changes": { -CHECK-NEXT: "file:///flatten-attrs-multi.nix": [ -CHECK-NEXT: { -CHECK-NEXT: "newText": "\"foo\"", -CHECK-NEXT: "range": { -CHECK-NEXT: "end": { -CHECK-NEXT: "character": 5, -CHECK-NEXT: "line": 0 -CHECK-NEXT: }, -CHECK-NEXT: "start": { -CHECK-NEXT: "character": 2, -CHECK-NEXT: "line": 0 -CHECK-NEXT: } -CHECK-NEXT: } -CHECK-NEXT: } -CHECK-NEXT: ] -CHECK-NEXT: } -CHECK-NEXT: }, -CHECK-NEXT: "kind": "refactor.rewrite", -CHECK-NEXT: "title": "Quote attribute name" -CHECK-NEXT: }, -CHECK-NEXT: { -CHECK-NEXT: "edit": { -CHECK-NEXT: "changes": { -CHECK-NEXT: "file:///flatten-attrs-multi.nix": [ -CHECK-NEXT: { -CHECK-NEXT: "newText": "foo.bar = 1; foo.baz = 2;", + CHECK: "newText": "foo.bar = 1; foo.baz = 2;", CHECK-NEXT: "range": { CHECK-NEXT: "end": { CHECK-NEXT: "character": 30, @@ -105,7 +76,7 @@ CHECK-NEXT: } CHECK-NEXT: ] CHECK-NEXT: } CHECK-NEXT: }, -CHECK-NEXT: "kind": "refactor.rewrite", + CHECK: "kind": "refactor.rewrite", CHECK-NEXT: "title": "Flatten nested attribute set" CHECK-NEXT: } CHECK-NEXT: ]