• 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 "completions.h"
17 #include "internal_api.h"
18 #include "compiler/lowering/util.h"
19 #include "ir/astNode.h"
20 #include "public/public.h"
21 #include <algorithm>
22 #include <regex>
23 #include <cstddef>
24 #include <optional>
25 #include <string>
26 #include "generated/tokenType.h"
27 #include <cstdio>
28 #include "public/es2panda_lib.h"
29 #include "lexer/token/letters.h"
30 namespace ark::es2panda::lsp {
31 
ToLowerCase(const std::string & str)32 std::string ToLowerCase(const std::string &str)
33 {
34     std::string lowerStr = str;
35     std::transform(lowerStr.begin(), lowerStr.end(), lowerStr.begin(), [](unsigned char c) { return std::tolower(c); });
36     return lowerStr;
37 }
38 
AllKeywordsCompletions()39 std::vector<CompletionEntry> AllKeywordsCompletions()
40 {
41     std::vector<CompletionEntry> keywords;
42     for (int i = static_cast<int>(lexer::TokenType::FIRST_KEYW); i <= static_cast<int>(lexer::TokenType::KEYW_YIELD);
43          i++) {
44         keywords.emplace_back(lsp::CompletionEntry(TokenToString(static_cast<lexer::TokenType>(i)),
45                                                    CompletionEntryKind::KEYWORD,
46                                                    std::string(sort_text::GLOBALS_OR_KEYWORDS)));
47     }
48     return keywords;
49 }
50 
GetKeywordCompletions(const std::string & input)51 std::vector<CompletionEntry> GetKeywordCompletions(const std::string &input)
52 {
53     std::vector<CompletionEntry> allKeywords = AllKeywordsCompletions();
54     std::vector<CompletionEntry> completions;
55 
56     for (const auto &entry : allKeywords) {
57         if (ToLowerCase(entry.GetName()).find(ToLowerCase(input)) == 0) {
58             completions.push_back(entry);
59         }
60     }
61     return completions;
62 }
63 
GetDeclarationEntry(ir::AstNode * node)64 CompletionEntry GetDeclarationEntry(ir::AstNode *node)
65 {
66     if (node == nullptr) {
67         return CompletionEntry();
68     }
69     // GetClassPropertyName function could get name of ClassDeclaration
70     if (node->IsClassDeclaration()) {
71         return CompletionEntry(GetClassPropertyName(node), CompletionEntryKind::CLASS,
72                                std::string(sort_text::GLOBALS_OR_KEYWORDS));
73     }
74     if (node->IsTSInterfaceDeclaration()) {
75         if (node->AsTSInterfaceDeclaration()->Id() == nullptr) {
76             return CompletionEntry();
77         }
78         return CompletionEntry(std::string(node->AsTSInterfaceDeclaration()->Id()->Name()),
79                                CompletionEntryKind::INTERFACE, std::string(sort_text::GLOBALS_OR_KEYWORDS));
80     }
81     if (node->IsMethodDefinition()) {
82         if (node->AsMethodDefinition()->Key() == nullptr && !node->AsMethodDefinition()->Key()->IsIdentifier()) {
83             return CompletionEntry();
84         }
85         return CompletionEntry(std::string(node->AsMethodDefinition()->Key()->AsIdentifier()->Name()),
86                                CompletionEntryKind::METHOD, std::string(sort_text::GLOBALS_OR_KEYWORDS));
87     }
88     if (node->IsClassProperty()) {
89         return CompletionEntry(GetClassPropertyName(node), CompletionEntryKind::PROPERTY,
90                                std::string(sort_text::GLOBALS_OR_KEYWORDS));
91     }
92     if (node->IsETSStructDeclaration()) {
93         if (node->AsETSStructDeclaration()->Definition() == nullptr) {
94             return CompletionEntry();
95         }
96         if (node->AsETSStructDeclaration()->Definition()->Ident() == nullptr) {
97             return CompletionEntry();
98         }
99         return CompletionEntry(std::string(node->AsETSStructDeclaration()->Definition()->Ident()->Name()),
100                                CompletionEntryKind::STRUCT, std::string(sort_text::GLOBALS_OR_KEYWORDS));
101     }
102     return CompletionEntry();
103 }
104 
GetExportsFromProgram(parser::Program * program)105 std::vector<CompletionEntry> GetExportsFromProgram(parser::Program *program)
106 {
107     std::vector<CompletionEntry> exportEntries;
108     auto ast = program->Ast();
109     auto collectExportNames = [&exportEntries](ir::AstNode *node) {
110         if (node->IsExported()) {
111             auto entry = GetDeclarationEntry(node);
112             if (!entry.GetName().empty()) {
113                 exportEntries.emplace_back(entry);
114             }
115         }
116     };
117     ast->IterateRecursivelyPreorder(collectExportNames);
118 
119     return exportEntries;
120 }
121 
GetSystemInterfaceCompletions(const std::string & input,parser::Program * program)122 std::vector<CompletionEntry> GetSystemInterfaceCompletions(const std::string &input, parser::Program *program)
123 {
124     std::vector<CompletionEntry> allExternalSourceExports;
125     std::vector<CompletionEntry> completions;
126     for (auto [_, programList] : program->ExternalSources()) {
127         for (auto prog : programList) {
128             auto exports = GetExportsFromProgram(prog);
129             if (!exports.empty()) {
130                 allExternalSourceExports.insert(allExternalSourceExports.end(), exports.begin(), exports.end());
131             }
132         }
133     }
134 
135     for (const auto &entry : allExternalSourceExports) {
136         if (ToLowerCase(entry.GetName()).find(ToLowerCase(input)) == 0) {
137             completions.emplace_back(entry);
138         }
139     }
140     return completions;
141 }
142 
IsPointValid(const std::string & str)143 bool IsPointValid(const std::string &str)
144 {
145     std::regex pattern(R"(^[a-zA-Z_$][a-zA-Z0-9_$\-]*(\?)?\.$)");
146     return std::regex_match(str, pattern);
147 }
148 
IsEndWithValidPoint(std::string str)149 bool IsEndWithValidPoint(std::string str)
150 {
151     return !str.empty() && str.back() == '.' && IsPointValid(str);
152 }
153 
IsEndWithToken(ir::AstNode * preNode,std::string str)154 bool IsEndWithToken(ir::AstNode *preNode, std::string str)
155 {
156     return str.back() != '.' && preNode->IsIdentifier() && preNode->AsIdentifier()->Name() != "*ERROR_LITERAL*";
157 }
158 
IsEndWithWildcard(ir::AstNode * preNode,const std::string & str)159 bool IsEndWithWildcard(ir::AstNode *preNode, const std::string &str)
160 {
161     const std::string wildcardStr = "_WILDCARD";
162     if (str == wildcardStr) {
163         return preNode->IsIdentifier() && preNode->AsIdentifier()->Name() != "*ERROR_LITERAL*";
164     }
165     return false;
166 }
167 
GetPrecedingTokenPosition(std::string sourceCode,size_t pos)168 size_t GetPrecedingTokenPosition(std::string sourceCode, size_t pos)
169 {
170     while (pos > 0) {
171         char c = sourceCode[pos];
172         if (std::isalnum(c) != 0 || c == '_') {
173             return pos;
174         }
175         pos--;
176     }
177     return pos;
178 }
179 
IsIgnoredName(const std::string & name)180 bool IsIgnoredName(const std::string &name)
181 {
182     static const std::unordered_set<std::string> IGNORED_NAMES = {"constructor", "_$init$_",
183                                                                   "_$initializerBlockInit$_"};
184     return IGNORED_NAMES.find(name) != IGNORED_NAMES.end();
185 }
186 
IsWordPartOfIdentifierName(ir::AstNode * node,std::string triggerWord)187 bool IsWordPartOfIdentifierName(ir::AstNode *node, std::string triggerWord)
188 {
189     if (node == nullptr || !node->IsIdentifier()) {
190         return false;
191     }
192     std::string name(node->AsIdentifier()->Name());
193     std::string lowerTrigger = std::move(triggerWord);
194     std::string lowerName = name;
195     std::transform(lowerTrigger.begin(), lowerTrigger.end(), lowerTrigger.begin(), ::tolower);
196     std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
197 
198     return lowerName.find(lowerTrigger) != std::string::npos && !IsIgnoredName(name);
199 }
200 
FilterFromBody(const ark::ArenaVector<ir::AstNode * > & bodyNodes,const std::string & triggerWord)201 std::vector<ir::AstNode *> FilterFromBody(const ark::ArenaVector<ir::AstNode *> &bodyNodes,
202                                           const std::string &triggerWord)
203 {
204     std::vector<ir::AstNode *> res;
205     if (bodyNodes.empty()) {
206         return res;
207     }
208     for (auto node : bodyNodes) {
209         if (node->IsClassProperty() && IsWordPartOfIdentifierName(node->AsClassProperty()->Key(), triggerWord)) {
210             res.emplace_back(node);
211         }
212         if (node->IsClassDeclaration()) {
213             auto def = node->AsClassDeclaration()->Definition()->AsClassDefinition();
214             if (def != nullptr && def->Ident() != nullptr && IsWordPartOfIdentifierName(def->Ident(), triggerWord)) {
215                 res.emplace_back(node);
216             }
217         }
218         if (node->IsMethodDefinition() && IsWordPartOfIdentifierName(node->AsMethodDefinition()->Key(), triggerWord)) {
219             res.emplace_back(node);
220         }
221     }
222     return res;
223 }
224 
FilterFromEnumMember(const ark::ArenaVector<ir::AstNode * > & members,const std::string & triggerWord)225 std::vector<ir::AstNode *> FilterFromEnumMember(const ark::ArenaVector<ir::AstNode *> &members,
226                                                 const std::string &triggerWord)
227 {
228     std::vector<ir::AstNode *> res;
229     if (members.empty()) {
230         return res;
231     }
232     for (auto member : members) {
233         if (member->IsTSEnumMember() && IsWordPartOfIdentifierName(member->AsTSEnumMember()->Key(), triggerWord)) {
234             res.emplace_back(member);
235         }
236     }
237     return res;
238 }
239 
FilterFromInterfaceBody(const ark::ArenaVector<ir::AstNode * > & members,const std::string & triggerWord)240 std::vector<ir::AstNode *> FilterFromInterfaceBody(const ark::ArenaVector<ir::AstNode *> &members,
241                                                    const std::string &triggerWord)
242 {
243     std::vector<ir::AstNode *> res;
244     if (members.empty()) {
245         return res;
246     }
247     for (auto member : members) {
248         if (member->IsMethodDefinition() &&
249             IsWordPartOfIdentifierName(member->AsMethodDefinition()->Key(), triggerWord)) {
250             res.emplace_back(member);
251         }
252     }
253     return res;
254 }
255 
GetClassPropertyName(ir::AstNode * node)256 std::string GetClassPropertyName(ir::AstNode *node)
257 {
258     // property in class
259     if (node->IsClassProperty()) {
260         auto key = node->AsClassProperty()->Key();
261         if (key != nullptr && key->IsIdentifier()) {
262             return std::string(key->AsIdentifier()->Name());
263         }
264     }
265     // class in namespace
266     if (node->IsClassDeclaration()) {
267         auto def = node->AsClassDeclaration()->Definition();
268         if (def != nullptr && def->IsClassDefinition() && def->AsClassDefinition()->Ident() != nullptr &&
269             def->AsClassDefinition()->Ident()->IsIdentifier()) {
270             return std::string(def->AsClassDefinition()->Ident()->AsIdentifier()->Name());
271         }
272     }
273     return "";
274 }
275 
GetEnumMemberName(ir::AstNode * node)276 std::string GetEnumMemberName(ir::AstNode *node)
277 {
278     if (!node->IsTSEnumMember()) {
279         return "";
280     }
281     auto id = node->AsTSEnumMember()->Key();
282     if (id == nullptr || !id->IsIdentifier()) {
283         return "";
284     }
285     return std::string(id->AsIdentifier()->Name());
286 }
287 
GetMethodDefinitionName(ir::AstNode * node)288 std::string GetMethodDefinitionName(ir::AstNode *node)
289 {
290     if (!node->IsMethodDefinition()) {
291         return "";
292     }
293     auto key = node->AsMethodDefinition()->Key();
294     if (key == nullptr || !key->IsIdentifier()) {
295         return "";
296     }
297     return std::string(key->AsIdentifier()->Name());
298 }
299 
GetClassDefinitionFromClassProperty(ir::AstNode * node)300 ir::AstNode *GetClassDefinitionFromClassProperty(ir::AstNode *node)
301 {
302     if (!node->IsClassProperty()) {
303         return nullptr;
304     }
305     auto value = node->AsClassProperty()->Value();
306     auto ident = GetIdentFromNewClassExprPart(value);
307     if (ident != nullptr) {
308         return compiler::DeclarationFromIdentifier(ident);
309     }
310     auto type = node->AsClassProperty()->TypeAnnotation();
311     if (type != nullptr && type->IsETSTypeReference()) {
312         auto typeRefPart = type->AsETSTypeReference()->Part();
313         if (typeRefPart == nullptr) {
314             return nullptr;
315         }
316         auto id = typeRefPart->Name();
317         if (id != nullptr && id->IsIdentifier()) {
318             return compiler::DeclarationFromIdentifier(id->AsIdentifier());
319         }
320     }
321     return nullptr;
322 }
323 
GetEntriesForClassDeclaration(const std::vector<ark::es2panda::ir::AstNode * > & propertyNodes)324 std::vector<CompletionEntry> GetEntriesForClassDeclaration(
325     const std::vector<ark::es2panda::ir::AstNode *> &propertyNodes)
326 {
327     if (propertyNodes.empty()) {
328         return {};
329     }
330     std::vector<CompletionEntry> completions;
331     completions.reserve(propertyNodes.size());
332     for (auto node : propertyNodes) {
333         if (node->IsClassProperty()) {
334             completions.emplace_back(lsp::CompletionEntry(GetClassPropertyName(node), CompletionEntryKind::PROPERTY,
335                                                           std::string(sort_text::SUGGESTED_CLASS_MEMBERS)));
336         }
337         if (node->IsClassDeclaration()) {
338             completions.emplace_back(
339                 lsp::CompletionEntry(GetClassPropertyName(node), CompletionEntryKind::CLASS,
340                                      std::string(sort_text::MEMBER_DECLARED_BY_SPREAD_ASSIGNMENT)));
341         }
342         if (node->IsMethodDefinition()) {
343             completions.emplace_back(lsp::CompletionEntry(GetMethodDefinitionName(node), CompletionEntryKind::METHOD,
344                                                           std::string(sort_text::CLASS_MEMBER_SNIPPETS)));
345         }
346     }
347     return completions;
348 }
349 
GetEntriesForTSInterfaceDeclaration(const std::vector<ark::es2panda::ir::AstNode * > & propertyNodes)350 std::vector<CompletionEntry> GetEntriesForTSInterfaceDeclaration(
351     const std::vector<ark::es2panda::ir::AstNode *> &propertyNodes)
352 {
353     if (propertyNodes.empty()) {
354         return {};
355     }
356     std::vector<CompletionEntry> completions;
357     completions.reserve(propertyNodes.size());
358     for (auto node : propertyNodes) {
359         if (node->IsMethodDefinition()) {
360             completions.emplace_back(lsp::CompletionEntry(GetMethodDefinitionName(node), CompletionEntryKind::METHOD,
361                                                           std::string(sort_text::CLASS_MEMBER_SNIPPETS)));
362         }
363     }
364     return completions;
365 }
366 
GetEntriesForEnumDeclaration(const std::vector<ark::es2panda::ir::AstNode * > & qualifiedMembers)367 std::vector<CompletionEntry> GetEntriesForEnumDeclaration(
368     const std::vector<ark::es2panda::ir::AstNode *> &qualifiedMembers)
369 {
370     if (qualifiedMembers.empty()) {
371         return {};
372     }
373     std::vector<CompletionEntry> completions;
374     completions.reserve(qualifiedMembers.size());
375     for (auto member : qualifiedMembers) {
376         completions.emplace_back(lsp::CompletionEntry(GetEnumMemberName(member), CompletionEntryKind::ENUM_MEMBER,
377                                                       std::string(sort_text::MEMBER_DECLARED_BY_SPREAD_ASSIGNMENT)));
378     }
379 
380     return completions;
381 }
382 
GetIdentifierFromSuper(ir::AstNode * super)383 ir::AstNode *GetIdentifierFromSuper(ir::AstNode *super)
384 {
385     if (super == nullptr || !super->IsETSTypeReference()) {
386         return nullptr;
387     }
388     auto part = super->AsETSTypeReference()->Part();
389     if (part == nullptr || !part->IsETSTypeReferencePart()) {
390         return nullptr;
391     }
392     return part->AsETSTypeReferencePart()->Name();
393 }
394 
GetCompletionFromClassDefinition(ir::ClassDefinition * decl,const std::string & triggerWord)395 std::vector<CompletionEntry> GetCompletionFromClassDefinition(ir::ClassDefinition *decl, const std::string &triggerWord)
396 {
397     // After enum refactoring, enum declaration is transformed to a class declaration
398     if (compiler::ClassDefinitionIsEnumTransformed(decl)) {
399         if (decl->AsClassDefinition()->OrigEnumDecl() == nullptr) {
400             return {};
401         }
402         auto members = decl->AsClassDefinition()->OrigEnumDecl()->AsTSEnumDeclaration()->Members();
403         auto qualifiedMembers = FilterFromEnumMember(members, triggerWord);
404         return GetEntriesForEnumDeclaration(qualifiedMembers);
405     }
406     auto bodyNodes = decl->AsClassDefinition()->Body();
407     std::vector<CompletionEntry> extendCompletions;
408     auto super = decl->AsClassDefinition()->Super();
409     if (super != nullptr) {
410         auto ident = GetIdentifierFromSuper(super);
411         extendCompletions = GetPropertyCompletions(ident, triggerWord);
412     }
413     auto propertyNodes = FilterFromBody(bodyNodes, triggerWord);
414     auto res = GetEntriesForClassDeclaration(propertyNodes);
415     res.insert(res.end(), extendCompletions.begin(), extendCompletions.end());
416     return res;
417 }
418 
GetIdentifierFromTSInterfaceHeritage(ir::AstNode * node)419 ir::AstNode *GetIdentifierFromTSInterfaceHeritage(ir::AstNode *node)
420 {
421     if (node == nullptr) {
422         return nullptr;
423     }
424     ir::AstNode *expr = nullptr;
425     if (node->IsTSInterfaceHeritage()) {
426         expr = node->AsTSInterfaceHeritage()->Expr();
427     } else if (node->IsTSClassImplements()) {
428         expr = node->AsTSClassImplements()->Expr();
429     } else {
430         return nullptr;
431     }
432     if (expr == nullptr) {
433         return nullptr;
434     }
435     auto part = expr->AsETSTypeReference()->Part();
436     if (part == nullptr) {
437         return nullptr;
438     }
439     return part->AsETSTypeReferencePart()->Name();
440 }
441 
GetCompletionFromTSInterfaceDeclaration(ir::TSInterfaceDeclaration * decl,const std::string & triggerWord)442 std::vector<CompletionEntry> GetCompletionFromTSInterfaceDeclaration(ir::TSInterfaceDeclaration *decl,
443                                                                      const std::string &triggerWord)
444 {
445     std::vector<CompletionEntry> completions;
446     auto body = decl->AsTSInterfaceDeclaration()->Body();
447     if (body == nullptr) {
448         return {};
449     }
450     auto bodies = body->Body();
451     std::vector<CompletionEntry> extendCompletions;
452     auto extends = decl->AsTSInterfaceDeclaration()->Extends();
453     for (auto extend : extends) {
454         auto ident = GetIdentifierFromTSInterfaceHeritage(extend);
455         if (ident != nullptr && ident->IsIdentifier()) {
456             auto extendInterf = compiler::DeclarationFromIdentifier(ident->AsIdentifier());
457             if (extendInterf == nullptr) {
458                 continue;
459             }
460             auto extendCom =
461                 extendInterf->IsTSInterfaceDeclaration()
462                     ? GetCompletionFromTSInterfaceDeclaration(extendInterf->AsTSInterfaceDeclaration(), triggerWord)
463                     : completions;
464             extendCompletions.insert(extendCompletions.end(), extendCom.begin(), extendCom.end());
465         }
466     }
467     auto qualifiedBodies = FilterFromInterfaceBody(bodies, triggerWord);
468     auto res = GetEntriesForTSInterfaceDeclaration(qualifiedBodies);
469     res.insert(res.end(), extendCompletions.begin(), extendCompletions.end());
470     return res;
471 }
472 
GetCompletionFromMethodDefinition(ir::MethodDefinition * decl,const std::string & triggerWord)473 std::vector<CompletionEntry> GetCompletionFromMethodDefinition(ir::MethodDefinition *decl,
474                                                                const std::string &triggerWord)
475 {
476     auto value = decl->AsMethodDefinition()->Value();
477     if (value == nullptr || !value->IsFunctionExpression()) {
478         return {};
479     }
480     auto func = value->AsFunctionExpression()->Function();
481     if (func == nullptr || func->ReturnTypeAnnotation() == nullptr) {
482         return {};
483     }
484     auto returnType = func->ReturnTypeAnnotation();
485     if (returnType == nullptr || !returnType->IsETSTypeReference()) {
486         return {};
487     }
488     auto ident = returnType->AsETSTypeReference()->Part()->Name();
489     if (ident == nullptr || !ident->IsIdentifier()) {
490         return {};
491     }
492     return GetPropertyCompletions(reinterpret_cast<ir::AstNode *>(ident), triggerWord);
493 }
494 
GetIndentifierFromCallExpression(ir::AstNode * node)495 ir::AstNode *GetIndentifierFromCallExpression(ir::AstNode *node)
496 {
497     if (!node->IsCallExpression()) {
498         return nullptr;
499     }
500     auto callee = node->AsCallExpression()->Callee();
501     if (callee != nullptr && callee->IsMemberExpression()) {
502         auto object = callee->AsMemberExpression()->Object();
503         if (object->IsCallExpression()) {
504             return GetIndentifierFromCallExpression(object);
505         }
506     }
507     return callee;
508 }
509 
GetNameFromDefinition(ir::AstNode * preNode)510 util::StringView GetNameFromDefinition(ir::AstNode *preNode)
511 {
512     if (preNode == nullptr || !preNode->IsClassDefinition()) {
513         return "";
514     }
515     auto ident = preNode->AsClassDefinition()->Ident();
516     if (ident == nullptr) {
517         return "";
518     }
519     return ident->Name();
520 }
521 
IsDeclarationNameDefined(util::StringView name)522 bool IsDeclarationNameDefined(util::StringView name)
523 {
524     static const std::unordered_set<util::StringView> IGNORED_NAMES = {"ETSGLOBAL"};
525     return IGNORED_NAMES.find(name) == IGNORED_NAMES.end();
526 }
527 
IsDefinedClassOrStruct(ir::AstNode * preNode)528 bool IsDefinedClassOrStruct(ir::AstNode *preNode)
529 {
530     if (preNode == nullptr) {
531         return false;
532     }
533     if (!preNode->IsClassDeclaration() && !preNode->IsETSStructDeclaration()) {
534         return false;
535     }
536     if (preNode->IsClassDeclaration()) {
537         return IsDeclarationNameDefined(GetNameFromDefinition(preNode->AsClassDeclaration()->Definition()));
538     }
539     if (preNode->IsETSStructDeclaration()) {
540         return IsDeclarationNameDefined(GetNameFromDefinition(preNode->AsETSStructDeclaration()->Definition()));
541     }
542     return false;
543 }
544 
GetDefinitionOfThisExpression(ir::AstNode * preNode)545 ir::AstNode *GetDefinitionOfThisExpression(ir::AstNode *preNode)
546 {
547     if (preNode == nullptr || !preNode->IsThisExpression()) {
548         return nullptr;
549     }
550     while (preNode->Parent() != nullptr) {
551         preNode = preNode->Parent();
552         if (preNode->IsClassDeclaration() && IsDefinedClassOrStruct(preNode)) {
553             return preNode->AsClassDeclaration()->Definition();
554         }
555         if (preNode->IsETSStructDeclaration() && IsDefinedClassOrStruct(preNode)) {
556             return preNode->AsETSStructDeclaration()->Definition();
557         }
558     }
559     return nullptr;
560 }
561 
GetCompletionFromThisExpression(ir::AstNode * preNode,const std::string & triggerWord)562 std::vector<CompletionEntry> GetCompletionFromThisExpression(ir::AstNode *preNode, const std::string &triggerWord)
563 {
564     if (preNode == nullptr || !preNode->IsThisExpression()) {
565         return {};
566     }
567     auto def = GetDefinitionOfThisExpression(preNode);
568     if (def == nullptr || !def->IsClassDefinition()) {
569         return {};
570     }
571     return GetCompletionFromClassDefinition(def->AsClassDefinition(), triggerWord);
572 }
573 
GetPropertyCompletions(ir::AstNode * preNode,const std::string & triggerWord)574 std::vector<CompletionEntry> GetPropertyCompletions(ir::AstNode *preNode, const std::string &triggerWord)
575 {
576     std::vector<CompletionEntry> completions;
577     if (preNode == nullptr) {
578         return completions;
579     }
580     if (preNode->IsCallExpression()) {
581         preNode = GetIndentifierFromCallExpression(preNode);
582     }
583     if (preNode->IsThisExpression()) {
584         return GetCompletionFromThisExpression(preNode, triggerWord);
585     }
586     if (!preNode->IsIdentifier()) {
587         return completions;
588     }
589     auto decl = compiler::DeclarationFromIdentifier(preNode->AsIdentifier());
590     if (decl == nullptr) {
591         return completions;
592     }
593     if (decl->IsClassProperty()) {
594         decl = GetClassDefinitionFromClassProperty(decl);
595     }
596     if (decl->IsClassDeclaration()) {
597         decl = decl->AsClassDeclaration()->Definition();
598     }
599     if (decl == nullptr) {
600         return completions;
601     }
602     if (decl->IsMethodDefinition()) {
603         return GetCompletionFromMethodDefinition(decl->AsMethodDefinition(), triggerWord);
604     }
605     if (decl->IsTSInterfaceDeclaration()) {
606         return GetCompletionFromTSInterfaceDeclaration(decl->AsTSInterfaceDeclaration(), triggerWord);
607     }
608     if (decl->IsClassDefinition()) {
609         return GetCompletionFromClassDefinition(decl->AsClassDefinition(), triggerWord);
610     }
611     return completions;
612 }
613 
KeywordCompletionData(const std::string & input)614 Request KeywordCompletionData(const std::string &input)
615 {
616     return {
617         CompletionDataKind::KEYWORDS,
618         GetKeywordCompletions(input),
619     };
620 }
621 
GetDeclName(const ir::AstNode * decl)622 std::string GetDeclName(const ir::AstNode *decl)
623 {
624     switch (decl->Type()) {
625         case ir::AstNodeType::IDENTIFIER:
626             return decl->AsIdentifier()->ToString();
627         case ir::AstNodeType::METHOD_DEFINITION:
628             return decl->AsMethodDefinition()->Key()->AsIdentifier()->ToString();
629         case ir::AstNodeType::CLASS_PROPERTY:
630             return decl->AsClassProperty()->Key()->AsIdentifier()->ToString();
631         default:
632             return "";
633     }
634 }
635 
IsGlobalVar(const ir::AstNode * node)636 bool IsGlobalVar(const ir::AstNode *node)
637 {
638     return node->IsClassProperty() && node->Parent()->IsClassDefinition() &&
639            node->Parent()->AsClassDefinition()->IsGlobal();
640 }
641 
IsVariableOfKind(const ir::Identifier * node,ir::VariableDeclaration::VariableDeclarationKind kind)642 bool IsVariableOfKind(const ir::Identifier *node, ir::VariableDeclaration::VariableDeclarationKind kind)
643 {
644     /** A VariableDeclaration statement:
645      *  - type: VariableDeclaration
646      *      - type: VariableDeclarator
647      *      - id
648      *          - type: Identifier
649      *  - Declaration kind
650      */
651     return node->Parent() != nullptr &&  // try to get the VariableDeclarator
652            node->Parent()->IsVariableDeclarator() &&
653            node->Parent()->Parent() != nullptr &&  // try to get the VariableDeclaration
654            node->Parent()->Parent()->IsVariableDeclaration() &&
655            node->Parent()->Parent()->AsVariableDeclaration()->Kind() == kind;
656 }
657 
IsConstVar(const ir::AstNode * node)658 bool IsConstVar(const ir::AstNode *node)
659 {
660     if (!node->IsIdentifier()) {
661         return false;
662     }
663     return IsVariableOfKind(node->AsIdentifier(), ir::VariableDeclaration::VariableDeclarationKind::CONST);
664 }
665 
IsLetVar(const ir::AstNode * node)666 bool IsLetVar(const ir::AstNode *node)
667 {
668     if (!node->IsIdentifier()) {
669         return false;
670     }
671     return IsVariableOfKind(node->AsIdentifier(), ir::VariableDeclaration::VariableDeclarationKind::LET);
672 }
673 
IsValidDecl(const ir::AstNode * decl)674 bool IsValidDecl(const ir::AstNode *decl)
675 {
676     return decl != nullptr && NodeHasTokens(decl) &&
677            (decl->IsMethodDefinition() || IsLetVar(decl) || IsConstVar(decl) || IsGlobalVar(decl));
678 }
679 
InitEntry(const ir::AstNode * decl)680 CompletionEntry InitEntry(const ir::AstNode *decl)
681 {
682     auto name = GetDeclName(decl);
683     auto sortText = sort_text::GLOBALS_OR_KEYWORDS;
684     auto kind = CompletionEntryKind::KEYWORD;
685     if (IsLetVar(decl)) {
686         kind = CompletionEntryKind::VARIABLE;
687     } else if (IsConstVar(decl)) {
688         kind = CompletionEntryKind::CONSTANT;
689     } else if (IsGlobalVar(decl)) {
690         auto globalDefiniton = decl->Parent()->AsClassDefinition();
691         auto initMethod = globalDefiniton->FindChild([](ir::AstNode *child) {
692             return child->IsMethodDefinition() &&
693                    child->AsMethodDefinition()->Key()->AsIdentifier()->Name() == compiler::Signatures::INIT_METHOD;
694         });
695         if (initMethod == nullptr) {
696             return CompletionEntry(name, CompletionEntryKind::CONSTANT, std::string(sortText));
697         }
698         auto found = initMethod->FindChild([&name](ir::AstNode *child) {
699             return child->IsAssignmentExpression() && child->AsAssignmentExpression()->Left()->IsIdentifier() &&
700                    child->AsAssignmentExpression()->Left()->AsIdentifier()->ToString() == name;
701         });
702         if (found != nullptr) {
703             // let variable in global definition need to be assigned in _$init$_ method
704             kind = CompletionEntryKind::VARIABLE;
705         } else {
706             kind = CompletionEntryKind::CONSTANT;
707         }
708     } else if (decl->IsMethodDefinition()) {
709         kind = CompletionEntryKind::FUNCTION;
710     }
711     return CompletionEntry(name, kind, std::string(sortText));
712 }
713 
GetIdentifiersInScope(const varbinder::Scope * scope,size_t position,ArenaVector<ir::AstNode * > & results)714 void GetIdentifiersInScope(const varbinder::Scope *scope, size_t position, ArenaVector<ir::AstNode *> &results)
715 {
716     if (scope->Node() == nullptr) {
717         return;
718     }
719     auto checkFunc = [scope, position](ir::AstNode *child) -> bool {
720         return child->End().index < position && NodeHasTokens(child) && compiler::NearestScope(child) == scope &&
721                child->IsIdentifier();
722     };
723     FindAllChild(scope->Node(), checkFunc, results);
724 }
725 
GetDeclByScopePath(ArenaVector<varbinder::Scope * > & scopePath,size_t position,ArenaAllocator * allocator)726 auto GetDeclByScopePath(ArenaVector<varbinder::Scope *> &scopePath, size_t position, ArenaAllocator *allocator)
727 {
728     auto hashFunc = [](const ir::AstNode *node) {
729         static std::hash<std::string> strHasher;
730         return strHasher(GetDeclName(node));
731     };
732     auto equalFunc = [](const ir::AstNode *lhs, const ir::AstNode *rhs) {
733         return GetDeclName(lhs) == GetDeclName(rhs);
734     };
735     auto decls = ArenaUnorderedSet<ir::AstNode *, decltype(hashFunc), decltype(equalFunc)>(0, hashFunc, equalFunc,
736                                                                                            allocator->Adapter());
737     for (auto scope : scopePath) {
738         auto nodes = ArenaVector<ir::AstNode *>(allocator->Adapter());
739         GetIdentifiersInScope(scope, position, nodes);
740         for (auto node : nodes) {
741             auto decl = compiler::DeclarationFromIdentifier(node->AsIdentifier());
742             if (IsValidDecl(decl)) {
743                 decls.insert(decl);
744             }
745         }
746     }
747     return decls;
748 }
749 
750 // Support: global variables, local variables, functions, keywords
GetGlobalCompletions(es2panda_Context * context,size_t position)751 std::vector<CompletionEntry> GetGlobalCompletions(es2panda_Context *context, size_t position)
752 {
753     auto ctx = reinterpret_cast<public_lib::Context *>(context);
754     auto allocator = ctx->allocator;
755     if (allocator == nullptr) {
756         return {};
757     }
758     auto precedingToken = FindPrecedingToken(position, ctx->parserProgram->Ast(), allocator);
759     if (precedingToken == nullptr) {
760         return {};
761     }
762     auto scopePath = BuildScopePath(compiler::NearestScope(precedingToken), allocator);
763     auto prefix = GetCurrentTokenValueImpl(context, position);
764     auto decls = GetDeclByScopePath(scopePath, position, allocator);
765     std::vector<CompletionEntry> completions;
766 
767     for (auto decl : decls) {
768         auto entry = InitEntry(decl);
769         if (entry.GetName().find(prefix) != 0) {
770             continue;
771         }
772         entry = ProcessAutoImportForEntry(entry);
773         completions.push_back(entry);
774     }
775 
776     auto keywordCompletions = GetKeywordCompletions(prefix);
777     completions.insert(completions.end(), keywordCompletions.begin(), keywordCompletions.end());
778     auto systemInterfaceCompletions = GetSystemInterfaceCompletions(prefix, ctx->parserProgram);
779     completions.insert(completions.end(), systemInterfaceCompletions.begin(), systemInterfaceCompletions.end());
780 
781     return completions;
782 }
783 
BuildScopePath(varbinder::Scope * startScope,ArenaAllocator * allocator)784 ArenaVector<varbinder::Scope *> BuildScopePath(varbinder::Scope *startScope, ArenaAllocator *allocator)
785 {
786     ArenaVector<varbinder::Scope *> scopePath(allocator->Adapter());
787     for (auto scope = startScope; scope != nullptr; scope = scope->Parent()) {
788         scopePath.push_back(scope);
789     }
790     return scopePath;
791 }
792 
ProcessAutoImportForEntry(CompletionEntry & entry)793 CompletionEntry ProcessAutoImportForEntry(CompletionEntry &entry)
794 {
795     auto dataOpt = entry.GetCompletionEntryData();
796     if (!dataOpt.has_value()) {
797         return entry;
798     }
799 
800     auto config = GetArkTsConfigFromFile(dataOpt->GetFileName());
801     if (config == nullptr) {
802         return entry;
803     }
804 
805     auto autoImportData = GetAutoImportCompletionEntry(&dataOpt.value(), config, entry.GetName());
806     if (!autoImportData.has_value()) {
807         return entry;
808     }
809 
810     return CompletionEntry(entry.GetName(), entry.GetCompletionKind(), entry.GetSortText(), entry.GetInsertText(),
811                            autoImportData);
812 }
813 
GetCompletionsAtPositionImpl(es2panda_Context * context,size_t pos)814 std::vector<CompletionEntry> GetCompletionsAtPositionImpl(es2panda_Context *context, size_t pos)
815 {
816     if (context == nullptr) {
817         return {};
818     }
819     auto ctx = reinterpret_cast<public_lib::Context *>(context);
820     if (ctx->parserProgram == nullptr || ctx->parserProgram->Ast() == nullptr) {
821         return {};
822     }
823     auto allocator = ctx->allocator;
824     std::string sourceCode(ctx->parserProgram->SourceCode());
825     // Current GetPrecedingPosition cannot get token of "obj." with position.
826     auto precedingToken = FindPrecedingToken(pos, ctx->parserProgram->Ast(), allocator);
827     if (precedingToken == nullptr) {
828         return {};
829     }
830     auto triggerValue = GetCurrentTokenValueImpl(context, pos);
831     // Unsupported yet because of ast parsing problem
832     if (IsEndWithValidPoint(triggerValue)) {
833         return GetPropertyCompletions(precedingToken, "");
834     }
835     auto memberExpr = precedingToken->Parent();
836     // This is a temporary solution to support "obj." with wildcard for better solution in internal issue.
837     if (IsEndWithWildcard(precedingToken, triggerValue) && memberExpr != nullptr) {
838         if (memberExpr->IsMemberExpression()) {
839             precedingToken = memberExpr->AsMemberExpression()->Object();
840             return GetPropertyCompletions(precedingToken, "");
841         }
842         if (memberExpr->IsTSQualifiedName()) {
843             precedingToken = memberExpr->AsTSQualifiedName()->Left();
844             return GetPropertyCompletions(precedingToken, "");
845         }
846     }
847     if (IsEndWithToken(precedingToken, triggerValue) && memberExpr != nullptr) {
848         if (memberExpr->IsMemberExpression()) {
849             precedingToken = memberExpr->AsMemberExpression()->Object();
850             return GetPropertyCompletions(precedingToken, triggerValue);
851         }
852         if (memberExpr->IsTSQualifiedName()) {
853             precedingToken = memberExpr->AsTSQualifiedName()->Left();
854             return GetPropertyCompletions(precedingToken, triggerValue);
855         }
856     }
857     return GetGlobalCompletions(context, pos);
858 }
859 
GetAutoImportCompletionEntry(ark::es2panda::lsp::CompletionEntryData * data,const std::shared_ptr<ArkTsConfig> & config,const std::string & name)860 std::optional<CompletionEntryData> GetAutoImportCompletionEntry(ark::es2panda::lsp::CompletionEntryData *data,
861                                                                 const std::shared_ptr<ArkTsConfig> &config,
862                                                                 const std::string &name)
863 {
864     const char *fileName = data->GetFileName();
865     if (fileName == nullptr || std::strlen(fileName) == 0) {
866         return std::nullopt;
867     }
868     if (config == nullptr) {
869         return std::nullopt;
870     }
871     return CompletionEntryDataToOriginInfo(data, config, name);
872 }
873 
CompletionEntryDataToOriginInfo(ark::es2panda::lsp::CompletionEntryData * data,const std::shared_ptr<ArkTsConfig> & config,const std::string & name)874 std::optional<CompletionEntryData> CompletionEntryDataToOriginInfo(ark::es2panda::lsp::CompletionEntryData *data,
875                                                                    const std::shared_ptr<ArkTsConfig> &config,
876                                                                    const std::string &name)
877 {
878     if (IsCompletionEntryDataResolved(data, config) == true) {
879         return CompletionEntryData(data->GetFileName(), data->GetNamedExport(), data->GetImportDeclaration(), name,
880                                    ResolutionStatus::RESOLVED);
881     }
882     if (IsCompletionEntryDataResolved(data, config) == false) {
883         return CompletionEntryData(data->GetFileName(), data->GetNamedExport(), data->GetImportDeclaration(), name,
884                                    ResolutionStatus::UNRESOLVED);
885     }
886     return std::nullopt;
887 }
888 
StartsWith(const std::string & str,const std::string & prefix)889 bool StartsWith(const std::string &str, const std::string &prefix)
890 {
891     return str.size() >= prefix.size() && str.compare(0, prefix.size(), prefix) == 0;
892 }
893 
IsCompletionEntryDataResolved(ark::es2panda::lsp::CompletionEntryData * data,const std::shared_ptr<ArkTsConfig> & config)894 std::optional<bool> IsCompletionEntryDataResolved(ark::es2panda::lsp::CompletionEntryData *data,
895                                                   const std::shared_ptr<ArkTsConfig> &config)
896 {
897     auto importDecl = data->GetImportDeclaration();
898     if (importDecl.length() == 0) {
899         return std::nullopt;
900     }
901     if (StartsWith(importDecl, "./") || StartsWith(importDecl, "../")) {
902         return true;
903     }
904 
905     const char slash = static_cast<char>(lexer::LEX_CHAR_SLASH);
906     const int pos = importDecl.find(slash);
907     const std::string importSub = importDecl.substr(0, pos);
908     auto configPaths = config->Paths();
909     if (configPaths.count(importSub) != 0) {
910         return false;
911     }
912 
913     return std::nullopt;
914 }
915 
GetArkTsConfigFromFile(const char * fileName)916 std::shared_ptr<ArkTsConfig> GetArkTsConfigFromFile(const char *fileName)
917 {
918     Initializer initializer = Initializer();
919 
920     auto ctx = initializer.CreateContext(fileName, ES2PANDA_STATE_CHECKED);
921     auto config = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx)->config->options->ArkTSConfig();
922     initializer.DestroyContext(ctx);
923 
924     return config;
925 }
926 }  // namespace ark::es2panda::lsp