nixd
Loading...
Searching...
No Matches
FlattenAttrs.cpp
Go to the documentation of this file.
1/// \file
2/// \brief Implementation of flatten nested attribute sets code action.
3
4#include "FlattenAttrs.h"
5#include "Utils.h"
6
7#include "../Convert.h"
8
10
11namespace nixd {
12
13namespace {
14
15/// \brief Check if an ExprAttrs can be flattened (no rec, inherit, dynamic).
16/// Returns the Binds node if flattenable, nullptr otherwise.
17const nixf::Binds *getFlattenableBinds(const nixf::ExprAttrs &Attrs) {
18 // Block if recursive attribute set
19 if (Attrs.isRecursive())
20 return nullptr;
21
22 const nixf::Binds *B = Attrs.binds();
23 if (!B || B->bindings().empty())
24 return nullptr;
25
26 // Check all bindings: must be plain Binding nodes (no Inherit)
27 // and all attribute names must be static (no dynamic ${} interpolation)
28 for (const auto &Child : B->bindings()) {
29 if (Child->kind() != nixf::Node::NK_Binding)
30 return nullptr; // Inherit node found
31
32 const auto &Bind = static_cast<const nixf::Binding &>(*Child);
33 for (const auto &Name : Bind.path().names()) {
34 if (!Name->isStatic())
35 return nullptr; // Dynamic attribute name
36 }
37 }
38
39 return B;
40}
41
42} // namespace
43
46 const std::string &FileURI, llvm::StringRef Src,
47 std::vector<lspserver::CodeAction> &Actions) {
48 // Find if we're inside a Binding
49 const nixf::Node *BindingNode = PM.upTo(N, nixf::Node::NK_Binding);
50 if (!BindingNode)
51 return;
52
53 const auto &Bind = static_cast<const nixf::Binding &>(*BindingNode);
54
55 // Check if the binding's value is an ExprAttrs
56 if (!Bind.value() || Bind.value()->kind() != nixf::Node::NK_ExprAttrs)
57 return;
58
59 const auto &NestedAttrs = static_cast<const nixf::ExprAttrs &>(*Bind.value());
60
61 // Check if flattenable
62 const nixf::Binds *NestedBinds = getFlattenableBinds(NestedAttrs);
63 if (!NestedBinds)
64 return;
65
66 // Check outer path is static too
67 for (const auto &Name : Bind.path().names()) {
68 if (!Name->isStatic())
69 return;
70 }
71
72 // Build the flattened text
73 std::string NewText;
74 const auto &NestedBindings = NestedBinds->bindings();
75
76 // Pre-allocate to reduce reallocations. The +40 accounts for inner path,
77 // " = ", value text, and ";". May under-allocate for complex expressions
78 // but still reduces reallocations significantly.
79 const std::string_view OuterPath = Bind.path().src(Src);
80 size_t EstimatedSize = NestedBindings.size() * (OuterPath.size() + 40);
81 NewText.reserve(EstimatedSize);
82
83 for (size_t I = 0; I < NestedBindings.size(); ++I) {
84 const auto &InnerBind =
85 static_cast<const nixf::Binding &>(*NestedBindings[I]);
86
87 // Build path: outer.inner
88 const std::string_view InnerPath = InnerBind.path().src(Src);
89
90 NewText += OuterPath;
91 NewText += ".";
92 NewText += InnerPath;
93 NewText += " = ";
94
95 if (InnerBind.value()) {
96 NewText += InnerBind.value()->src(Src);
97 }
98 NewText += ";";
99
100 if (I + 1 < NestedBindings.size())
101 NewText += " ";
102 }
103
104 Actions.emplace_back(createSingleEditAction(
105 "Flatten nested attribute set",
107 toLSPRange(Src, Bind.range()), std::move(NewText)));
108}
109
110} // namespace nixd
Convert between LSP and nixf types.
Code action for flattening nested attribute sets.
Shared utilities for code actions.
const AttrPath & path() const
Definition Attrs.h:131
const std::vector< std::shared_ptr< Node > > & bindings() const
Definition Attrs.h:180
bool isRecursive() const
Definition Attrs.h:287
const Binds * binds() const
Definition Attrs.h:284
std::string_view src(std::string_view Src) const
Definition Basic.h:63
const Node * upTo(const Node &N, Node::NodeKind Kind) const
Search up until some kind of node is found.
Definition ParentMap.cpp:27
void addFlattenAttrsAction(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, const std::string &FileURI, llvm::StringRef Src, std::vector< lspserver::CodeAction > &Actions)
Add flatten action for nested attribute sets.
lspserver::CodeAction createSingleEditAction(const std::string &Title, llvm::StringLiteral Kind, const std::string &FileURI, const lspserver::Range &EditRange, std::string NewText)
Create a CodeAction with a single text edit.
Definition Utils.cpp:10
lspserver::Range toLSPRange(llvm::StringRef Code, const nixf::LexerCursorRange &R)
Definition Convert.cpp:40
static const llvm::StringLiteral REFACTOR_REWRITE_KIND