1 /**
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "rename.h"
17 #include "get_adjusted_location.h"
18 #include "macros.h"
19 #include "lexer/token/letters.h"
20 #include "public/public.h"
21 #include <string>
22 #include <utility>
23
24 namespace ark::es2panda::lsp {
25
26 constexpr size_t FIRST_CHAR_INDEX = 0;
27 constexpr size_t QUOTE_END_OFFSET = 2;
28 constexpr size_t MIN_QUOTED_LENGTH = 2;
29 constexpr size_t QUOTE_START_OFFSET = 1;
30
GetRenameInfo(es2panda_Context * context,size_t pos)31 RenameInfoType GetRenameInfo(es2panda_Context *context, size_t pos)
32 {
33 auto ctx = reinterpret_cast<public_lib::Context *>(context);
34 auto checker = reinterpret_cast<public_lib::Context *>(ctx)->checker->AsETSChecker();
35 auto program = reinterpret_cast<public_lib::Context *>(ctx)->parserProgram;
36 auto node = GetAdjustedLocation(GetTouchingPropertyName(context, pos), true, ctx->allocator);
37 if (node.has_value() && NodeIsEligibleForRename(node.value())) {
38 auto renameInfo = GetRenameInfoForNode(node.value(), checker, program);
39 if (renameInfo.has_value()) {
40 return renameInfo.value();
41 }
42 }
43 const std::string diagnosticMessage = "You cannot rename this element";
44 return GetRenameInfoError(diagnosticMessage);
45 }
46
GetRenameInfoError(std::string diagnosticMessage)47 RenameInfoFailure GetRenameInfoError(std::string diagnosticMessage)
48 {
49 return RenameInfoFailure(false, std::move(diagnosticMessage));
50 }
51
GetRenameInfoSuccess(std::string displayName,std::string fullDisplayName,std::string kind,std::string kindModifiers,ir::AstNode * node)52 RenameInfoSuccess GetRenameInfoSuccess(std::string displayName, std::string fullDisplayName, std::string kind,
53 std::string kindModifiers, ir::AstNode *node)
54 {
55 TextSpan triggerSpan = CreateTriggerSpanForNode(node);
56 return RenameInfoSuccess(true, "", std::move(kind), std::move(displayName), std::move(fullDisplayName),
57 std::move(kindModifiers), triggerSpan);
58 }
59
CreateTriggerSpanForNode(ir::AstNode * node)60 TextSpan CreateTriggerSpanForNode(ir::AstNode *node)
61 {
62 TextSpan span(node->Range().start.index, node->Range().end.index - node->Range().start.index);
63
64 if (node->IsStringLiteral()) {
65 span.start = span.start + QUOTE_START_OFFSET;
66 span.length = span.length - QUOTE_END_OFFSET;
67 }
68
69 return span;
70 }
71
GetRenameInfoForNode(ir::AstNode * node,checker::ETSChecker * checker,parser::Program * program)72 std::optional<RenameInfoType> GetRenameInfoForNode(ir::AstNode *node, checker::ETSChecker *checker,
73 parser::Program *program)
74 {
75 if (node->IsStringLiteral()) {
76 auto type = GetContextualTypeFromParentOrAncestorTypeNode(node, checker);
77 if (type) {
78 const std::string kind = "string";
79 return GetRenameInfoSuccess(node->AsStringLiteral()->ToString(), node->AsStringLiteral()->ToString(), kind,
80 "", node);
81 }
82 }
83 if (node->IsLabelledStatement() ||
84 (node->IsIdentifier() && (node->Parent()->IsContinueStatement() || node->Parent()->IsBreakStatement()))) {
85 const std::string name = GetTextOfNode(node, program);
86 const std::string kind = "label";
87 return GetRenameInfoSuccess(name, name, kind, "", node);
88 }
89
90 if (node->IsIdentifier()) {
91 return std::nullopt;
92 }
93
94 if (node->IsStringLiteral() && TryGetImportFromModuleSpecifier(node) != nullptr) {
95 return GetRenameInfoForModule(node, program);
96 }
97
98 const std::string kind = GetNodeKindForRenameInfo(node);
99
100 std::optional<std::string> specifierName;
101 if ((IsImportOrExportSpecifierName(node) || IsStringOrNumericLiteralLike(node)) &&
102 node->Parent()->Type() == ir::AstNodeType::PROPERTY) {
103 specifierName = StripQuotes(static_cast<std::string>(node->AsIdentifier()->Name()));
104 } else {
105 specifierName = std::nullopt;
106 }
107 const std::string displayName = specifierName.has_value() ? specifierName.value() : "";
108 const std::string fullDisplayName = specifierName.has_value() ? specifierName.value() : "";
109 return GetRenameInfoSuccess(displayName, fullDisplayName, kind, "", node);
110 }
111
GetContextualTypeFromParentOrAncestorTypeNode(ir::AstNode * node,checker::ETSChecker * checker)112 std::optional<checker::VerifiedType> GetContextualTypeFromParentOrAncestorTypeNode(ir::AstNode *node,
113 checker::ETSChecker *checker)
114 {
115 auto contextualType = node->Check(checker);
116 if (contextualType != nullptr) {
117 return contextualType;
118 }
119
120 auto ancestorTypeNode = node;
121
122 while (ancestorTypeNode != nullptr) {
123 if (IsValidAncestorType(ancestorTypeNode->Type())) {
124 break;
125 }
126 ancestorTypeNode = ancestorTypeNode->Parent();
127 }
128
129 if (ancestorTypeNode == nullptr) {
130 return std::nullopt;
131 }
132 return ancestorTypeNode->Check(checker);
133 }
134
GetTextOfNode(ir::AstNode * node,parser::Program * program)135 std::string GetTextOfNode(ir::AstNode *node, parser::Program *program)
136 {
137 auto sourceCode = program->SourceCode();
138 return GetSourceTextOfNodeFromSourceFile(sourceCode, node);
139 }
140
GetSourceTextOfNodeFromSourceFile(util::StringView sourceCode,ir::AstNode * node)141 std::string GetSourceTextOfNodeFromSourceFile(util::StringView sourceCode, ir::AstNode *node)
142 {
143 if (NodeIsMissing(node)) {
144 return "";
145 }
146
147 size_t pos = node->Range().start.index;
148 size_t end = node->Range().end.index;
149
150 auto text = std::string(sourceCode.Substr(pos, end));
151 return text;
152 }
153
TryGetImportFromModuleSpecifier(ir::AstNode * node)154 ir::AstNode *TryGetImportFromModuleSpecifier(ir::AstNode *node)
155 {
156 if (node == nullptr) {
157 return nullptr;
158 }
159
160 ir::AstNode *parent = node->Parent();
161 if (parent == nullptr) {
162 return nullptr;
163 }
164
165 switch (parent->Type()) {
166 case ir::AstNodeType::IMPORT_DECLARATION:
167 case ir::AstNodeType::ETS_IMPORT_DECLARATION:
168 case ir::AstNodeType::EXPORT_DEFAULT_DECLARATION:
169 return parent;
170 case ir::AstNodeType::TS_EXTERNAL_MODULE_REFERENCE:
171 return parent->Parent();
172 case ir::AstNodeType::CALL_EXPRESSION:
173 return parent;
174 case ir::AstNodeType::TS_LITERAL_TYPE:
175 ASSERT(node->IsStringLiteral());
176 if (parent->Parent()->IsTSImportType()) {
177 return parent->Parent();
178 }
179 return nullptr;
180 default:
181 return nullptr;
182 }
183 }
184
NodeIsMissing(ir::AstNode * node)185 bool NodeIsMissing(ir::AstNode *node)
186 {
187 if (node == nullptr) {
188 return true;
189 }
190 size_t pos = node->Range().start.index;
191 size_t end = node->Range().end.index;
192 return pos == end;
193 }
194
IsValidAncestorType(ir::AstNodeType type)195 bool IsValidAncestorType(ir::AstNodeType type)
196 {
197 const int countAncestorType = 10;
198 const std::array<ir::AstNodeType, countAncestorType> validTypes = {
199 ir::AstNodeType::TS_ANY_KEYWORD, ir::AstNodeType::TS_UNKNOWN_KEYWORD, ir::AstNodeType::TS_NUMBER_KEYWORD,
200 ir::AstNodeType::TS_BIGINT_KEYWORD, ir::AstNodeType::TS_OBJECT_KEYWORD, ir::AstNodeType::TS_BOOLEAN_KEYWORD,
201 ir::AstNodeType::TS_STRING_KEYWORD, ir::AstNodeType::TS_VOID_KEYWORD, ir::AstNodeType::TS_UNDEFINED_KEYWORD,
202 ir::AstNodeType::TS_NEVER_KEYWORD};
203 return std::find(validTypes.begin(), validTypes.end(), type) != validTypes.end();
204 }
205
StripQuotes(std::string name)206 std::string StripQuotes(std::string name)
207 {
208 auto length = name.length();
209 if (length >= MIN_QUOTED_LENGTH && name[FIRST_CHAR_INDEX] == name[length - QUOTE_START_OFFSET] &&
210 IsQuoteOrBacktick(static_cast<int>(name[FIRST_CHAR_INDEX]))) {
211 return name.substr(QUOTE_START_OFFSET, length - QUOTE_END_OFFSET);
212 }
213 return name;
214 }
215
IsQuoteOrBacktick(int charCode)216 bool IsQuoteOrBacktick(int charCode)
217 {
218 return charCode == lexer::LEX_CHAR_SINGLE_QUOTE || charCode == lexer::LEX_CHAR_DOUBLE_QUOTE ||
219 charCode == lexer::LEX_CHAR_BACK_TICK;
220 }
221
GetRenameInfoForModule(ir::AstNode * node,parser::Program * program)222 std::optional<RenameInfoSuccess> GetRenameInfoForModule(ir::AstNode *node, parser::Program *program)
223 {
224 auto moduleSourceFile = program->SourceFile();
225 if (moduleSourceFile.GetPath().Empty()) {
226 return std::nullopt;
227 }
228 std::string suffix = "/index";
229 std::optional<std::string> withoutIndex;
230 if (util::Helpers::EndsWith(node->AsStringLiteral()->ToString(), suffix)) {
231 withoutIndex = std::nullopt;
232 } else {
233 std::string name = node->AsStringLiteral()->ToString();
234 size_t dotPos = name.find_last_of('.');
235 name = dotPos == std::string::npos ? name : name.substr(FIRST_CHAR_INDEX, dotPos);
236 if (util::Helpers::EndsWith(name, suffix)) {
237 withoutIndex = name.substr(FIRST_CHAR_INDEX, name.length() - suffix.length());
238 } else {
239 withoutIndex = std::nullopt;
240 }
241 }
242 std::string name =
243 withoutIndex.has_value() ? static_cast<std::string>(withoutIndex.value()) : node->AsStringLiteral()->ToString();
244 std::string displayName =
245 withoutIndex.has_value() ? static_cast<std::string>(withoutIndex.value()) : node->AsStringLiteral()->ToString();
246 std::string fullDisplayName =
247 withoutIndex.has_value() ? static_cast<std::string>(withoutIndex.value()) : node->AsStringLiteral()->ToString();
248 std::string kind =
249 withoutIndex.has_value() ? static_cast<std::string>("directory") : static_cast<std::string>("module");
250 auto indexAfterLastSlash = node->AsStringLiteral()->ToString().find_last_of('/') + 1;
251 auto triggerSpan = TextSpan(node->Range().start.index + 1 + indexAfterLastSlash,
252 node->AsStringLiteral()->ToString().length() - indexAfterLastSlash);
253 return RenameInfoSuccess(true, std::move(name), std::move(kind), std::move(displayName), std::move(fullDisplayName),
254 "", triggerSpan);
255 }
256
GetNodeKindForRenameInfo(ir::AstNode * node)257 std::string GetNodeKindForRenameInfo(ir::AstNode *node)
258 {
259 switch (node->Type()) {
260 case ir::AstNodeType::TS_ENUM_DECLARATION:
261 return "enum";
262 case ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION:
263 return "type";
264 case ir::AstNodeType::TS_INTERFACE_DECLARATION:
265 return "interface";
266 case ir::AstNodeType::TS_TYPE_PARAMETER:
267 return "type parameter";
268 case ir::AstNodeType::TS_ENUM_MEMBER:
269 return "enum member";
270 case ir::AstNodeType::TS_MODULE_DECLARATION:
271 return "module";
272 default:
273 return "";
274 }
275 }
276
IsImportOrExportSpecifierName(ir::AstNode * node)277 bool IsImportOrExportSpecifierName(ir::AstNode *node)
278 {
279 return (node->IsImportSpecifier() || node->IsExportSpecifier()) || node->IsIdentifier();
280 }
281
IsStringOrNumericLiteralLike(ir::AstNode * node)282 bool IsStringOrNumericLiteralLike(ir::AstNode *node)
283 {
284 return node->IsStringLiteral() || node->IsNumberLiteral();
285 }
286
NodeIsEligibleForRename(ir::AstNode * node)287 bool NodeIsEligibleForRename(ir::AstNode *node)
288 {
289 switch (node->Type()) {
290 case ir::AstNodeType::IDENTIFIER:
291 case ir::AstNodeType::STRING_LITERAL:
292 case ir::AstNodeType::TEMPLATE_LITERAL:
293 case ir::AstNodeType::THIS_EXPRESSION:
294 return true;
295 case ir::AstNodeType::NUMBER_LITERAL:
296 return IsLiteralNameOfPropertyDeclarationOrIndexAccess(node->AsNumberLiteral());
297 default:
298 return false;
299 }
300 }
301
IsLiteralNameOfPropertyDeclarationOrIndexAccess(ir::AstNode * node)302 bool IsLiteralNameOfPropertyDeclarationOrIndexAccess(ir::AstNode *node)
303 {
304 switch (node->Parent()->Type()) {
305 case ir::AstNodeType::PROPERTY:
306 case ir::AstNodeType::TS_PROPERTY_SIGNATURE:
307 case ir::AstNodeType::ASSIGNMENT_EXPRESSION:
308 case ir::AstNodeType::TS_ENUM_MEMBER:
309 case ir::AstNodeType::FUNCTION_DECLARATION:
310 case ir::AstNodeType::TS_METHOD_SIGNATURE:
311 case ir::AstNodeType::TS_MODULE_DECLARATION:
312 return GetNameOfDeclaration(node->Parent()) == node;
313 case ir::AstNodeType::TS_LITERAL_TYPE:
314 return node->Parent()->Parent()->Type() == ir::AstNodeType::TS_INDEXED_ACCESS_TYPE;
315 default:
316 return false;
317 }
318 }
319
GetNameOfDeclaration(ir::AstNode * node)320 ir::AstNode *GetNameOfDeclaration(ir::AstNode *node)
321 {
322 if (node == nullptr) {
323 return nullptr;
324 }
325 if (GetNonAssignedNameOfDeclaration(node) != nullptr) {
326 return GetNonAssignedNameOfDeclaration(node);
327 }
328 if (node->IsFunctionExpression() || node->IsArrowFunctionExpression() || node->IsClassExpression()) {
329 return GetAssignedName(node);
330 }
331 return nullptr;
332 }
333
GetNonAssignedNameOfDeclaration(ir::AstNode * node)334 ir::AstNode *GetNonAssignedNameOfDeclaration(ir::AstNode *node)
335 {
336 if (node->Type() == ir::AstNodeType::IDENTIFIER) {
337 return node->AsIdentifier();
338 }
339 if (node->Type() == ir::AstNodeType::CALL_EXPRESSION || node->Type() == ir::AstNodeType::BINARY_EXPRESSION) {
340 if (node->AsBinaryExpression()->IsTSThisType() || node->AsBinaryExpression()->IsProperty()) {
341 return node->AsBinaryExpression();
342 }
343 if (node->AsCallExpression()->IsTSThisType() || node->AsCallExpression()->IsProperty()) {
344 return node->AsCallExpression();
345 }
346 return nullptr;
347 }
348 if (node->Type() == ir::AstNodeType::EXPORT_DEFAULT_DECLARATION) {
349 return node->AsExportAllDeclaration()->IsIdentifier() ? node->AsExportAllDeclaration() : nullptr;
350 }
351 if (node->IsMemberExpression() &&
352 node->AsMemberExpression()->HasMemberKind(ir::MemberExpressionKind::ELEMENT_ACCESS)) {
353 return node->AsMemberExpression();
354 }
355 return node->IsNamedType() ? node->AsNamedType() : nullptr;
356 }
357
GetAssignedName(ir::AstNode * node)358 ir::AstNode *GetAssignedName(ir::AstNode *node)
359 {
360 auto parent = node->Parent();
361 if (parent == nullptr) {
362 return nullptr;
363 }
364
365 if (parent->IsProperty()) {
366 return parent->AsProperty();
367 }
368 if (parent->IsBinaryExpression() && node == parent->AsBinaryExpression()->Right()) {
369 if (parent->AsBinaryExpression()->Left()->IsIdentifier() ||
370 parent->AsBinaryExpression()->Left()->Type() == ir::AstNodeType::TS_INDEXED_ACCESS_TYPE) {
371 return parent->AsBinaryExpression()->Left();
372 }
373 }
374 if (parent->IsVariableDeclaration() && parent->IsIdentifier()) {
375 return parent;
376 }
377 return nullptr;
378 }
379
380 } // namespace ark::es2panda::lsp