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