nixd
Loading...
Searching...
No Matches
RewriteString.cpp
Go to the documentation of this file.
1/// \file
2/// \brief Implementation of string literal syntax conversion code action.
3
4#include "RewriteString.h"
5#include "Utils.h"
6
7#include "../Convert.h"
8
10
11namespace nixd {
12
13namespace {
14
15/// \brief Check if the string at the given source position is indented style.
16///
17/// Indented strings start with '' while double-quoted strings start with ".
18bool isIndentedString(llvm::StringRef Src,
19 const nixf::LexerCursorRange &Range) {
20 size_t Offset = Range.lCur().offset();
21 if (Offset + 1 >= Src.size())
22 return false;
23 return Src[Offset] == '\'' && Src[Offset + 1] == '\'';
24}
25
26/// \brief Escape a string for indented string literal syntax.
27///
28/// Escapes:
29/// - '' -> ''' (two single quotes become three)
30/// - ${ -> ''${ (prevent interpolation)
31std::string escapeForIndentedString(llvm::StringRef S) {
32 std::string Result;
33 Result.reserve(S.size() + S.size() / 8);
34
35 for (size_t I = 0; I < S.size(); ++I) {
36 char C = S[I];
37
38 // Check for '' sequence that needs escaping
39 if (C == '\'' && I + 1 < S.size() && S[I + 1] == '\'') {
40 Result += "'''";
41 ++I; // Skip the second quote
42 continue;
43 }
44
45 // Check for ${ that needs escaping
46 if (C == '$' && I + 1 < S.size() && S[I + 1] == '{') {
47 Result += "''${";
48 ++I; // Skip the '{'
49 continue;
50 }
51
52 Result += C;
53 }
54
55 return Result;
56}
57
58/// \brief Build a double-quoted string from InterpolatedParts.
59///
60/// Iterates through fragments, escaping text parts and preserving
61/// interpolations from source.
62std::string buildDoubleQuotedString(const nixf::InterpolatedParts &Parts,
63 llvm::StringRef Src) {
64 std::string Result = "\"";
65
66 for (const auto &Frag : Parts.fragments()) {
67 if (Frag.kind() == nixf::InterpolablePart::SPK_Escaped) {
68 // Escape the unescaped content for double-quoted string
69 Result += escapeNixString(Frag.escaped());
70 } else {
71 // SPK_Interpolation: preserve source text exactly
72 const nixf::Interpolation &Interp = Frag.interpolation();
73 Result += Interp.src(Src);
74 }
75 }
76
77 Result += "\"";
78 return Result;
79}
80
81/// \brief Build an indented string from InterpolatedParts.
82///
83/// Iterates through fragments, escaping text parts and preserving
84/// interpolations from source.
85std::string buildIndentedString(const nixf::InterpolatedParts &Parts,
86 llvm::StringRef Src) {
87 std::string Result = "''";
88
89 for (const auto &Frag : Parts.fragments()) {
90 if (Frag.kind() == nixf::InterpolablePart::SPK_Escaped) {
91 // Escape the unescaped content for indented string
92 Result += escapeForIndentedString(Frag.escaped());
93 } else {
94 // SPK_Interpolation: preserve source text exactly
95 const nixf::Interpolation &Interp = Frag.interpolation();
96 Result += Interp.src(Src);
97 }
98 }
99
100 Result += "''";
101 return Result;
102}
103
104} // namespace
105
107 const nixf::ParentMapAnalysis &PM,
108 const std::string &FileURI, llvm::StringRef Src,
109 std::vector<lspserver::CodeAction> &Actions) {
110 // Find if we're inside an ExprString
111 const nixf::Node *StringNode = PM.upTo(N, nixf::Node::NK_ExprString);
112 if (!StringNode)
113 return;
114
115 // Skip if this string is used as an attribute name (handled by AttrName
116 // actions)
117 if (PM.upTo(*StringNode, nixf::Node::NK_AttrName))
118 return;
119
120 const auto &Str = static_cast<const nixf::ExprString &>(*StringNode);
121
122 // Determine current string type
123 bool IsIndented = isIndentedString(Src, Str.range());
124
125 // Build the converted string
126 std::string NewText;
127 std::string Title;
128
129 if (IsIndented) {
130 // Convert indented -> double-quoted
131 NewText = buildDoubleQuotedString(Str.parts(), Src);
132 Title = "Convert to double-quoted string";
133 } else {
134 // Convert double-quoted -> indented
135 NewText = buildIndentedString(Str.parts(), Src);
136 Title = "Convert to indented string";
137 }
138
139 Actions.emplace_back(createSingleEditAction(
141 toLSPRange(Src, Str.range()), std::move(NewText)));
142}
143
144} // namespace nixd
Convert between LSP and nixf types.
Code action for converting between string literal syntaxes.
Shared utilities for code actions.
const std::vector< InterpolablePart > & fragments() const
Definition Simple.h:92
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 addRewriteStringAction(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, const std::string &FileURI, llvm::StringRef Src, std::vector< lspserver::CodeAction > &Actions)
Add rewrite action for string literal syntax conversion.
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
std::string escapeNixString(llvm::StringRef S)
Escape special characters for Nix double-quoted string literals.
Definition Utils.cpp:52
static const llvm::StringLiteral REFACTOR_REWRITE_KIND