• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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