• 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 <cstddef>
17 #include <string>
18 #include <utility>
19 #include <vector>
20 #include "api.h"
21 #include "internal_api.h"
22 #include "checker/types/type.h"
23 #include "compiler/lowering/util.h"
24 #include "ir/astNode.h"
25 #include "lexer/token/sourceLocation.h"
26 #include "macros.h"
27 #include "public/es2panda_lib.h"
28 #include "public/public.h"
29 #include "utils/arena_containers.h"
30 #include "formatting/formatting.h"
31 #include "code_fix_provider.h"
32 #include "get_class_property_info.h"
33 #include "code_fixes/code_fix_types.h"
34 
35 namespace ark::es2panda::lsp {
36 
Initializer()37 Initializer::Initializer()
38 {
39     impl_ = es2panda_GetImpl(ES2PANDA_LIB_VERSION);
40     std::string buildDir;
41 #ifdef BUILD_FOLDER
42     buildDir = std::string(BUILD_FOLDER) + util::PATH_DELIMITER + "bin" + util::PATH_DELIMITER;
43 #endif
44     const char *path = std::getenv("BUILD_FOLDER");
45     if (path != nullptr) {
46         buildDir = std::string(path);
47     }
48     std::array<const char *, 1> argv = {buildDir.c_str()};
49     cfg_ = impl_->CreateConfig(argv.size(), argv.data());
50     allocator_ = new ark::ArenaAllocator(ark::SpaceType::SPACE_TYPE_COMPILER);
51 }
52 
~Initializer()53 Initializer::~Initializer()
54 {
55     delete allocator_;
56     impl_->DestroyConfig(cfg_);
57 }
58 
GetTouchingToken(es2panda_Context * context,size_t pos,bool flagFindFirstMatch)59 ir::AstNode *GetTouchingToken(es2panda_Context *context, size_t pos, bool flagFindFirstMatch)
60 {
61     if (context == nullptr) {
62         return nullptr;
63     }
64     auto ctx = reinterpret_cast<public_lib::Context *>(context);
65     if (ctx->parserProgram == nullptr || ctx->parserProgram->Ast() == nullptr) {
66         return nullptr;
67     }
68     auto ast = reinterpret_cast<ir::AstNode *>(ctx->parserProgram->Ast());
69     auto checkFunc = [&pos](ir::AstNode *node) { return pos >= node->Start().index && pos < node->End().index; };
70     auto found = ast->FindChild(checkFunc);
71     while (found != nullptr && !flagFindFirstMatch) {
72         auto *nestedFound = found->FindChild(checkFunc);
73         if (nestedFound == nullptr) {
74             break;
75         }
76         found = nestedFound;
77     }
78     return found;
79 }
80 
TransSourcePositionToPosition(lexer::SourcePosition sourcePos)81 Position TransSourcePositionToPosition(lexer::SourcePosition sourcePos)
82 {
83     return Position(sourcePos.line, sourcePos.index);
84 }
85 
FormatStringFromArgs(const std::string & textStr,const std::vector<std::string> & args=std::vector<std::string> ())86 std::string FormatStringFromArgs(const std::string &textStr,
87                                  const std::vector<std::string> &args = std::vector<std::string>())
88 {
89     std::stringstream ss;
90     size_t pos = 0;
91     size_t start = 0;
92     while ((pos = textStr.find_first_of('{', start)) != std::string::npos) {
93         ss << textStr.substr(start, pos - start);
94         size_t end = textStr.find('}', pos);
95         if (end == std::string::npos) {
96             ss << textStr.substr(pos);
97             break;
98         }
99         std::string placeholder = textStr.substr(pos, end - pos + 1);
100         std::string indexStr = placeholder.substr(1, placeholder.length() - 2);
101         try {
102             int index = std::stoi(indexStr);
103             if (index >= 0 && index < static_cast<int>(args.size())) {
104                 ss << args.at(index);
105             } else {
106                 ss << placeholder;
107             }
108         } catch (const std::out_of_range &e) {
109             ss << placeholder;
110         } catch (const std::invalid_argument &e) {
111             ss << placeholder;
112         }
113         start = end + 1;
114     }
115     ss << textStr.substr(start);
116 
117     return ss.str();
118 }
119 
CreateFileDiagnostic(es2panda_AstNode * node,lexer::SourceRange span,Diagnostic diagnostic,const std::vector<std::string> & args=std::vector<std::string> ())120 FileDiagnostic CreateFileDiagnostic(es2panda_AstNode *node, lexer::SourceRange span, Diagnostic diagnostic,
121                                     const std::vector<std::string> &args = std::vector<std::string>())
122 {
123     if (!args.empty()) {
124         std::string newMessageStr = FormatStringFromArgs(diagnostic.message_, args);
125         diagnostic.message_ = newMessageStr;
126     }
127     FileDiagnostic fileDiagnostic(node, diagnostic, TransSourcePositionToPosition(span.start),
128                                   TransSourcePositionToPosition(span.end));
129     return fileDiagnostic;
130 }
131 
GetErrorRangeForNode(ir::AstNode * node)132 lexer::SourceRange GetErrorRangeForNode(ir::AstNode *node)
133 {
134     return lexer::SourceRange(node->Start(), node->End());
135 }
136 
CreateDiagnosticForNode(es2panda_AstNode * node,Diagnostic diagnostic,const std::vector<std::string> & args)137 FileDiagnostic CreateDiagnosticForNode(es2panda_AstNode *node, Diagnostic diagnostic,
138                                        const std::vector<std::string> &args)
139 {
140     auto span = GetErrorRangeForNode(reinterpret_cast<ir::AstNode *>(node));
141     auto res = CreateFileDiagnostic(node, span, std::move(diagnostic), args);
142     return res;
143 }
144 
GetFileReferencesImpl(es2panda_Context * referenceFileContext,char const * searchFileName,bool isPackageModule)145 References GetFileReferencesImpl(es2panda_Context *referenceFileContext, char const *searchFileName,
146                                  bool isPackageModule)
147 {
148     References result;
149     auto ctx = reinterpret_cast<public_lib::Context *>(referenceFileContext);
150     auto statements = ctx->parserProgram->Ast()->Statements();
151     for (auto statement : statements) {
152         if (!statement->IsETSImportDeclaration()) {
153             continue;
154         }
155         auto import = statement->AsETSImportDeclaration();
156         std::string importFileName {import->ResolvedSource()};
157         if (!import->Source()->IsStringLiteral()) {
158             continue;
159         }
160         auto start = import->Source()->Start().index;
161         auto end = import->Source()->End().index;
162         auto pos = std::string(searchFileName).rfind(util::PATH_DELIMITER);
163         auto fileDirectory = std::string(searchFileName).substr(0, pos);
164         if ((!isPackageModule && importFileName == searchFileName) ||
165             (isPackageModule && importFileName == fileDirectory)) {
166             auto fileName = ctx->sourceFileName;
167             result.referenceInfos.emplace_back(fileName, start, end - start);
168         }
169     }
170     return result;
171 }
172 
IsToken(const ir::AstNode * node)173 bool IsToken(const ir::AstNode *node)
174 {
175     /**
176      * True if node is of some token node.
177      * For example, this is true for an IDENTIFIER or NUMBER_LITERAL but not for BLOCK_STATEMENT or CallExpression.
178      * Keywords like "if" and "of" exist as TOKEN_TYPE and cannot be recognized as AstNode, so returning nodes like
179      * IfKeyword or OfKeyword is not supported.
180      */
181     return node->Type() == ir::AstNodeType::BIGINT_LITERAL || node->Type() == ir::AstNodeType::BOOLEAN_LITERAL ||
182            node->Type() == ir::AstNodeType::CHAR_LITERAL || node->Type() == ir::AstNodeType::IDENTIFIER ||
183            node->Type() == ir::AstNodeType::NULL_LITERAL || node->Type() == ir::AstNodeType::UNDEFINED_LITERAL ||
184            node->Type() == ir::AstNodeType::NUMBER_LITERAL || node->Type() == ir::AstNodeType::REGEXP_LITERAL ||
185            node->Type() == ir::AstNodeType::STRING_LITERAL || node->Type() == ir::AstNodeType::TS_NUMBER_KEYWORD ||
186            node->Type() == ir::AstNodeType::TS_ANY_KEYWORD || node->Type() == ir::AstNodeType::TS_BOOLEAN_KEYWORD ||
187            node->Type() == ir::AstNodeType::TS_VOID_KEYWORD || node->Type() == ir::AstNodeType::TS_UNDEFINED_KEYWORD ||
188            node->Type() == ir::AstNodeType::TS_UNKNOWN_KEYWORD || node->Type() == ir::AstNodeType::TS_OBJECT_KEYWORD ||
189            node->Type() == ir::AstNodeType::TS_BIGINT_KEYWORD || node->Type() == ir::AstNodeType::TS_NEVER_KEYWORD ||
190            node->Type() == ir::AstNodeType::TS_NULL_KEYWORD || node->Type() == ir::AstNodeType::TEMPLATE_ELEMENT;
191 }
192 
IsNonWhitespaceToken(const ir::AstNode * node)193 bool IsNonWhitespaceToken(const ir::AstNode *node)
194 {
195     return IsToken(node);
196 }
197 
NodeHasTokens(const ir::AstNode * node)198 bool NodeHasTokens(const ir::AstNode *node)
199 {
200     return node->Start().index != node->End().index;
201 }
202 
FindRightmostChildNodeWithTokens(const ArenaVector<ir::AstNode * > & nodes,int exclusiveStartPosition)203 ir::AstNode *FindRightmostChildNodeWithTokens(const ArenaVector<ir::AstNode *> &nodes, int exclusiveStartPosition)
204 {
205     for (int i = exclusiveStartPosition - 1; i >= 0; --i) {
206         if (NodeHasTokens(nodes[i])) {
207             return nodes[i];
208         }
209     }
210     return nullptr;
211 }
212 
GetChildren(const ir::AstNode * node,ArenaAllocator * allocator)213 ArenaVector<ir::AstNode *> GetChildren(const ir::AstNode *node, ArenaAllocator *allocator)
214 {
215     ArenaVector<ir::AstNode *> children(allocator->Adapter());
216     if (node->Type() == ir::AstNodeType::ETS_MODULE) {
217         // ETS_MODULE is the root node, need to get the definition of global class
218         auto globalClass =
219             node->FindChild([](ir::AstNode *child) { return child->IsClassDeclaration(); })->AsClassDeclaration();
220         node = globalClass->Definition();
221     }
222     node->Iterate([&children](ir::AstNode *child) { children.push_back(child); });
223     return children;
224 }
225 
FindRightmostToken(const ir::AstNode * node,ArenaAllocator * allocator)226 ir::AstNode *FindRightmostToken(const ir::AstNode *node, ArenaAllocator *allocator)
227 {
228     if (node == nullptr) {
229         return nullptr;
230     }
231     if (IsNonWhitespaceToken(node)) {
232         return const_cast<ir::AstNode *>(node);
233     }
234     auto children = GetChildren(node, allocator);
235     if (children.empty()) {
236         return const_cast<ir::AstNode *>(node);
237     }
238     auto candidate = FindRightmostChildNodeWithTokens(children, children.size());
239     return FindRightmostToken(candidate, allocator);
240 }
241 
FindNodeBeforePosition(const ArenaVector<ir::AstNode * > & children,size_t pos)242 ir::AstNode *FindNodeBeforePosition(const ArenaVector<ir::AstNode *> &children, size_t pos)
243 {
244     if (children.empty()) {
245         return nullptr;
246     }
247     size_t left = 0;
248     size_t right = children.size() - 1;
249     size_t mid = 0;
250     while (left <= right) {
251         mid = left + ((right - left) >> 1U);
252         if (pos < children[mid]->End().index) {
253             if (mid == 0 || pos >= children[mid - 1]->End().index) {
254                 break;
255             }
256             right = mid - 1;
257         } else {
258             left = mid + 1;
259         }
260     }
261     return FindRightmostChildNodeWithTokens(children, mid);
262 }
263 
FindPrecedingToken(const size_t pos,const ir::AstNode * startNode,ArenaAllocator * allocator)264 ir::AstNode *FindPrecedingToken(const size_t pos, const ir::AstNode *startNode, ArenaAllocator *allocator)
265 {
266     auto checkFunc = [&pos](ir::AstNode *node) { return node->Start().index <= pos && pos <= node->End().index; };
267     auto found = startNode->FindChild(checkFunc);
268     if (found != nullptr) {
269         auto nestedFound = found->FindChild(checkFunc);
270         while (nestedFound != nullptr) {
271             // try to find the minimum node that embraces position
272             found = nestedFound;
273             nestedFound = found->FindChild(checkFunc);
274         }
275 
276         // position is 0, found does not has any tokens
277         if (!NodeHasTokens(found)) {
278             return nullptr;
279         }
280 
281         if (IsNonWhitespaceToken(found)) {
282             return found;
283         }
284 
285         // found embraces the position, but none of its children do
286         // (ie: in a comment or whitespace preceding `child node`)
287         auto children = GetChildren(found, allocator);
288         auto candidate = FindNodeBeforePosition(children, pos);
289         return FindRightmostToken(candidate, allocator);
290     }
291 
292     // position is in the global scope but not 0, found will be nullptr.
293     auto children = GetChildren(startNode, allocator);
294     auto candidate = FindNodeBeforePosition(children, pos);
295     return FindRightmostToken(candidate, allocator);
296 }
297 
GetOriginalNode(ir::AstNode * astNode)298 ir::AstNode *GetOriginalNode(ir::AstNode *astNode)
299 {
300     while (astNode != nullptr && astNode->OriginalNode() != nullptr) {
301         astNode = astNode->OriginalNode();
302     }
303     return astNode;
304 }
305 
GetTypeOfSymbolAtLocation(checker::ETSChecker * checker,ir::AstNode * astNode)306 checker::VerifiedType GetTypeOfSymbolAtLocation(checker::ETSChecker *checker, ir::AstNode *astNode)
307 {
308     ES2PANDA_ASSERT(astNode);
309     auto originalNode = GetOriginalNode(astNode);
310     return originalNode->Check(checker);
311 }
312 
ReplaceQuotation(ark::es2panda::util::StringView strView)313 std::string ReplaceQuotation(ark::es2panda::util::StringView strView)
314 {
315     std::string str = std::string {strView};
316     str.erase(std::remove(str.begin(), str.end(), '\"'), str.end());
317     str.erase(std::remove(str.begin(), str.end(), '\''), str.end());
318     return str;
319 }
320 
GetCurrentTokenValueImpl(es2panda_Context * context,size_t position)321 std::string GetCurrentTokenValueImpl(es2panda_Context *context, size_t position)
322 {
323     auto ctx = reinterpret_cast<public_lib::Context *>(context);
324     auto program = ctx->parserProgram;
325     auto ast = program->Ast();
326     ir::AstNode *node = FindPrecedingToken(position, ast, ctx->allocator);
327     return node != nullptr ? ReplaceQuotation(program->SourceCode().Substr(node->Start().index, position)) : "";
328 }
329 
FindLeftToken(const size_t pos,const ArenaVector<ir::AstNode * > & nodes)330 ir::AstNode *FindLeftToken(const size_t pos, const ArenaVector<ir::AstNode *> &nodes)
331 {
332     int left = 0;
333     int right = nodes.size() - 1;
334     ir::AstNode *result = nullptr;
335     while (left <= right) {
336         int mid = left + (right - left) / 2;
337         if (nodes[mid]->End().index <= pos) {
338             result = nodes[mid];
339             left = mid + 1;
340         } else {
341             right = mid - 1;
342         }
343     }
344     return result;
345 }
346 
FindRightToken(const size_t pos,const ArenaVector<ir::AstNode * > & nodes)347 ir::AstNode *FindRightToken(const size_t pos, const ArenaVector<ir::AstNode *> &nodes)
348 {
349     int left = 0;
350     int right = nodes.size() - 1;
351     ir::AstNode *result = nullptr;
352     while (left <= right) {
353         int mid = left + (right - left) / 2;
354         if (nodes[mid]->Start().index > pos) {
355             result = nodes[mid];
356             right = mid - 1;
357         } else {
358             left = mid + 1;
359         }
360     }
361     return result;
362 }
363 
GetRangeOfCommentFromContext(std::string const & sourceCode,size_t leftPos,size_t rightPos,size_t pos,CommentRange * result)364 void GetRangeOfCommentFromContext(std::string const &sourceCode, size_t leftPos, size_t rightPos, size_t pos,
365                                   CommentRange *result)
366 {
367     constexpr size_t BLOCK_COMMENT_START_LENGTH = 2;
368     std::vector<CommentRange> commentRanges;
369     size_t startIndex = 0;
370     while (startIndex < sourceCode.size()) {
371         size_t blockCommentStart = sourceCode.find("/*", startIndex);
372         size_t lineCommentStart = sourceCode.find("//", startIndex);
373         if (blockCommentStart == std::string::npos && lineCommentStart == std::string::npos) {
374             break;
375         }
376         if (blockCommentStart < lineCommentStart || lineCommentStart == std::string::npos) {
377             if (blockCommentStart > rightPos) {
378                 break;
379             }
380             size_t blockCommentEnd = sourceCode.find("*/", blockCommentStart + BLOCK_COMMENT_START_LENGTH);
381             if (blockCommentEnd == std::string::npos) {
382                 break;
383             }
384             commentRanges.emplace_back(
385                 CommentRange(blockCommentStart, blockCommentEnd + BLOCK_COMMENT_START_LENGTH, CommentKind::MULTI_LINE));
386             startIndex = blockCommentEnd + BLOCK_COMMENT_START_LENGTH;
387             continue;
388         }
389         if (lineCommentStart > rightPos) {
390             break;
391         }
392         size_t lineCommentEnd = sourceCode.find('\n', lineCommentStart);
393         if (lineCommentEnd == std::string::npos) {
394             lineCommentEnd = sourceCode.size();
395         }
396         commentRanges.emplace_back(CommentRange(lineCommentStart, lineCommentEnd, CommentKind::SINGLE_LINE));
397         startIndex = lineCommentEnd;
398     }
399     for (const auto &range : commentRanges) {
400         if (range.pos_ <= pos && range.end_ >= pos && range.pos_ >= leftPos && range.end_ <= rightPos) {
401             result->pos_ = range.pos_;
402             result->kind_ = range.kind_;
403             result->end_ = range.end_;
404             break;
405         }
406     }
407 }
408 
GetRangeOfEnclosingComment(es2panda_Context * context,size_t pos,CommentRange * result)409 void GetRangeOfEnclosingComment(es2panda_Context *context, size_t pos, CommentRange *result)
410 {
411     auto touchingNode = GetTouchingToken(context, pos, false);
412     if (touchingNode != nullptr && IsToken(touchingNode)) {
413         return;
414     }
415     auto ctx = reinterpret_cast<public_lib::Context *>(context);
416     auto ast = reinterpret_cast<ir::AstNode *>(ctx->parserProgram->Ast());
417     ir::AstNode *parent = touchingNode != nullptr ? touchingNode : ast;
418     auto children = GetChildren(parent, ctx->allocator);
419     std::sort(children.begin(), children.end(), [](ir::AstNode *a, ir::AstNode *b) {
420         if (a->Start().index < b->Start().index) {
421             return true;
422         }
423         if (a->Start().index == b->Start().index) {
424             return a->End().index < b->End().index;
425         }
426         return false;
427     });
428     ir::AstNode *leftToken = FindLeftToken(pos, children);
429     ir::AstNode *rightToken = FindRightToken(pos, children);
430     size_t leftPos = leftToken != nullptr ? leftToken->End().index : parent->Start().index;
431     size_t rightPos = rightToken != nullptr ? rightToken->Start().index : parent->End().index;
432     std::string sourceCode(ctx->parserProgram->SourceCode());
433     GetRangeOfCommentFromContext(sourceCode, leftPos, rightPos, pos, result);
434 }
435 
RemoveFromFiles(std::vector<std::string> & files,const std::vector<std::string> & autoGenerateFolders)436 void RemoveFromFiles(std::vector<std::string> &files, const std::vector<std::string> &autoGenerateFolders)
437 {
438     auto rm = [&autoGenerateFolders](const std::string &file) {
439         return std::any_of(autoGenerateFolders.begin(), autoGenerateFolders.end(),
440                            [&file](const std::string &folder) { return file.find(folder) != std::string::npos; });
441     };
442 
443     files.erase(std::remove_if(files.begin(), files.end(), rm), files.end());
444     if (files.empty()) {
445         return;
446     }
447 }
448 
SaveNode(ir::AstNode * node,public_lib::Context * ctx,ReferenceLocationList * result)449 void SaveNode(ir::AstNode *node, public_lib::Context *ctx, ReferenceLocationList *result)
450 {
451     auto uri = ctx->sourceFileName;
452     auto start = node->Range().start.index;
453     auto end = node->Range().end.index;
454     auto accessKind = node->IsReadonly() ? AccessKind::READ : AccessKind::WRITE;
455     auto isDefinition = node->IsClassDefinition() && node->IsMethodDefinition();
456     auto isImport = node->IsImportDeclaration();
457     auto ref = ReferenceLocation {uri, start, end, isDefinition, accessKind, isImport};
458     auto it =
459         std::find_if(result->referenceLocation.begin(), result->referenceLocation.end(),
460                      [&](const ReferenceLocation &loc) { return (loc.uri == ref.uri) && (loc.start == ref.start); });
461     if (it == result->referenceLocation.end()) {
462         result->referenceLocation.push_back(ref);
463     }
464 }
465 
GetIdentifier(ir::AstNode * node)466 ir::AstNode *GetIdentifier(ir::AstNode *node)
467 {
468     if (!node->IsIdentifier()) {
469         return node->FindChild([](ir::AstNode *node1) { return node1->IsIdentifier(); });
470     }
471     return node;
472 }
473 
GetIdentifierName(ir::AstNode * node)474 std::string GetIdentifierName(ir::AstNode *node)
475 {
476     auto id = GetIdentifier(node);
477     if (id == nullptr) {
478         return "";
479     }
480     return std::string {id->AsIdentifier()->Name()};
481 }
482 
GetOwner(ir::AstNode * node)483 ir::AstNode *GetOwner(ir::AstNode *node)
484 {
485     auto id = GetIdentifier(node);
486     if (id == nullptr) {
487         return nullptr;
488     }
489     auto var = id->AsIdentifier()->Variable();
490     if (var == nullptr) {
491         return nullptr;
492     }
493     auto decl = id->AsIdentifier()->Variable()->Declaration();
494     if (decl == nullptr) {
495         return nullptr;
496     }
497     return decl->Node();
498 }
499 
GetOwnerId(ir::AstNode * node)500 std::string GetOwnerId(ir::AstNode *node)
501 {
502     auto owner = GetOwner(node);
503     if (owner == nullptr) {
504         return "";
505     }
506     return owner->DumpJSON();
507 }
508 
509 // convert from es2panda error type to LSP severity
GetSeverity(util::DiagnosticType errorType)510 DiagnosticSeverity GetSeverity(util::DiagnosticType errorType)
511 {
512     ES2PANDA_ASSERT(errorType != util::DiagnosticType::INVALID);
513     if (errorType == util::DiagnosticType::WARNING || errorType == util::DiagnosticType::PLUGIN_WARNING) {
514         return DiagnosticSeverity::Warning;
515     }
516     if (errorType == util::DiagnosticType::SYNTAX || errorType == util::DiagnosticType::SEMANTIC ||
517         errorType == util::DiagnosticType::FATAL || errorType == util::DiagnosticType::ARKTS_CONFIG_ERROR ||
518         errorType == util::DiagnosticType::PLUGIN_ERROR) {
519         return DiagnosticSeverity::Error;
520     }
521     throw std::runtime_error("Unknown error type!");
522 }
523 
524 // Temp design only support UI Plugin Diag.
CreateCodeForDiagnostic(const util::DiagnosticBase * error)525 int CreateCodeForDiagnostic(const util::DiagnosticBase *error)
526 {
527     const int uiCode = 4000;
528     if (error->Type() == util::DiagnosticType::PLUGIN_ERROR || error->Type() == util::DiagnosticType::PLUGIN_WARNING) {
529         return uiCode;
530     }
531     return 1;
532 }
533 
CreateDiagnosticForError(es2panda_Context * context,const util::DiagnosticBase & error)534 Diagnostic CreateDiagnosticForError(es2panda_Context *context, const util::DiagnosticBase &error)
535 {
536     auto ctx = reinterpret_cast<public_lib::Context *>(context);
537     auto index = lexer::LineIndex(ctx->parserProgram->SourceCode());
538     auto offset = index.GetOffset(lexer::SourceLocation(error.Line(), error.Offset(), ctx->parserProgram));
539     auto touchingToken = GetTouchingToken(context, offset, false);
540     lexer::SourceRange sourceRange;
541     lexer::SourceLocation sourceStartLocation;
542     lexer::SourceLocation sourceEndLocation;
543     std::string source;
544     if (touchingToken == nullptr) {
545         sourceStartLocation = lexer::SourceLocation(error.Line(), error.Offset(), ctx->parserProgram);
546         sourceEndLocation = lexer::SourceLocation(error.Line(), error.Offset(), ctx->parserProgram);
547         source = "";
548     } else {
549         sourceRange = touchingToken->Range();
550         sourceStartLocation = index.GetLocation(sourceRange.start);
551         sourceEndLocation = index.GetLocation(sourceRange.end);
552         source = touchingToken->DumpEtsSrc();
553     }
554     auto range = Range(Position(sourceStartLocation.line, sourceStartLocation.col),
555                        Position(sourceEndLocation.line, sourceEndLocation.col));
556     auto severity = GetSeverity(error.Type());
557     auto code = CreateCodeForDiagnostic(&error);
558     std::string message = error.Message();
559     auto codeDescription = CodeDescription("test code description");
560     auto tags = std::vector<DiagnosticTag>();
561     auto relatedInformation = std::vector<DiagnosticRelatedInformation>();
562     return Diagnostic(range, tags, relatedInformation, severity, code, message, codeDescription, source);
563 }
564 
CreateDiagnosticWithoutFile(const util::DiagnosticBase & error)565 Diagnostic CreateDiagnosticWithoutFile(const util::DiagnosticBase &error)
566 {
567     auto range = Range(Position(), Position());
568     auto severity = GetSeverity(error.Type());
569     auto code = 1;
570     std::string message = error.Message();
571     auto codeDescription = CodeDescription("test code description");
572     auto tags = std::vector<DiagnosticTag>();
573     auto relatedInformation = std::vector<DiagnosticRelatedInformation>();
574     return Diagnostic(range, tags, relatedInformation, severity, code, message, codeDescription, "");
575 }
576 
GetGlobalDiagnostics(es2panda_Context * context,DiagnosticReferences & compilerOptionsDiagnostics)577 void GetGlobalDiagnostics(es2panda_Context *context, DiagnosticReferences &compilerOptionsDiagnostics)
578 {
579     auto ctx = reinterpret_cast<public_lib::Context *>(context);
580     const auto &diagnostics = ctx->diagnosticEngine->GetDiagnosticStorage(util::DiagnosticType::FATAL);
581     for (const auto &diagnostic : diagnostics) {
582         if (diagnostic->File().empty()) {
583             compilerOptionsDiagnostics.diagnostic.push_back(CreateDiagnosticWithoutFile(*diagnostic));
584         } else {
585             compilerOptionsDiagnostics.diagnostic.push_back(CreateDiagnosticForError(context, *diagnostic));
586         }
587     }
588 }
589 
GetOptionDiagnostics(es2panda_Context * context,DiagnosticReferences & compilerOptionsDiagnostics)590 void GetOptionDiagnostics(es2panda_Context *context, DiagnosticReferences &compilerOptionsDiagnostics)
591 {
592     auto ctx = reinterpret_cast<public_lib::Context *>(context);
593     const auto &diagnostics = ctx->diagnosticEngine->GetDiagnosticStorage(util::DiagnosticType::ARKTS_CONFIG_ERROR);
594     for (const auto &diagnostic : diagnostics) {
595         compilerOptionsDiagnostics.diagnostic.push_back(CreateDiagnosticWithoutFile(*diagnostic));
596     }
597 }
598 
GetTokenPosOfNode(const ir::AstNode * astNode)599 size_t GetTokenPosOfNode(const ir::AstNode *astNode)
600 {
601     ES2PANDA_ASSERT(astNode);
602 
603     return astNode->Start().index;
604 }
605 
GetDefinitionAtPositionImpl(es2panda_Context * context,size_t pos)606 std::pair<ir::AstNode *, util::StringView> GetDefinitionAtPositionImpl(es2panda_Context *context, size_t pos)
607 {
608     std::pair<ir::AstNode *, util::StringView> res;
609     auto node = GetTouchingToken(context, pos, false);
610     if (node == nullptr) {
611         return res;
612     }
613     if (node->IsCallExpression()) {
614         node = node->AsCallExpression()->Callee()->AsIdentifier();
615     }
616     if (!node->IsIdentifier()) {
617         return res;
618     }
619     res = {compiler::DeclarationFromIdentifier(node->AsIdentifier()), node->AsIdentifier()->Name()};
620     return res;
621 }
622 
RemoveRefDuplicates(const ArenaVector<ir::AstNode * > & nodes,ArenaAllocator * allocator)623 ArenaVector<ir::AstNode *> RemoveRefDuplicates(const ArenaVector<ir::AstNode *> &nodes, ArenaAllocator *allocator)
624 {
625     auto hashFunc = [](const ir::AstNode *node) {
626         return std::hash<int>()(node->Start().index) ^ std::hash<int>()(node->End().index);
627     };
628     auto equalFunc = [](const ir::AstNode *lhs, const ir::AstNode *rhs) {
629         return lhs->Start().index == rhs->Start().index && lhs->End().index == rhs->End().index;
630     };
631 
632     ArenaUnorderedSet<ir::AstNode *, decltype(hashFunc), decltype(equalFunc)> uniqueNodes(
633         nodes.size(), hashFunc, equalFunc, allocator->Adapter());
634     for (auto node : nodes) {
635         uniqueNodes.insert(node);
636     }
637 
638     return ArenaVector<ir::AstNode *>(uniqueNodes.begin(), uniqueNodes.end(), allocator->Adapter());
639 }
640 
FindAllChildHelper(ir::AstNode * ast,const ir::NodePredicate & cb,ArenaVector<ir::AstNode * > & results)641 void FindAllChildHelper(ir::AstNode *ast, const ir::NodePredicate &cb, ArenaVector<ir::AstNode *> &results)
642 {
643     if (cb(ast)) {
644         results.push_back(ast);
645     }
646 
647     ast->Iterate([&results, cb](ir::AstNode *child) { FindAllChildHelper(child, cb, results); });
648 }
649 
FindAllChild(const ir::AstNode * ast,const ir::NodePredicate & cb,ArenaVector<ir::AstNode * > & results)650 void FindAllChild(const ir::AstNode *ast, const ir::NodePredicate &cb, ArenaVector<ir::AstNode *> &results)
651 {
652     ast->Iterate([&results, cb](ir::AstNode *child) { FindAllChildHelper(child, cb, results); });
653 }
654 
FindAncestor(ir::AstNode * node,const ir::NodePredicate & cb)655 ir::AstNode *FindAncestor(ir::AstNode *node, const ir::NodePredicate &cb)
656 {
657     while (node != nullptr) {
658         if (cb(node)) {
659             return node;
660         }
661         node = node->Parent();
662     }
663     return node;
664 }
665 
FindReferencesByName(ir::AstNode * ast,ir::AstNode * decl,ir::AstNode * node,ArenaAllocator * allocator)666 ArenaVector<ir::AstNode *> FindReferencesByName(ir::AstNode *ast, ir::AstNode *decl, ir::AstNode *node,
667                                                 ArenaAllocator *allocator)
668 {
669     ASSERT(node->IsIdentifier());
670     auto name = node->AsIdentifier()->Name();
671     auto checkFunc = [&name, &decl](ir::AstNode *child) {
672         return child->IsIdentifier() && compiler::DeclarationFromIdentifier(child->AsIdentifier()) == decl &&
673                child->AsIdentifier()->Name() == name;
674     };
675     auto references = ArenaVector<ir::AstNode *>(allocator->Adapter());
676     FindAllChild(ast, checkFunc, references);
677 
678     auto uniqueReferences = RemoveRefDuplicates(references, allocator);
679     std::sort(uniqueReferences.begin(), uniqueReferences.end(), [](const ir::AstNode *a, const ir::AstNode *b) {
680         return (a->Start().index != b->Start().index) ? a->Start().index < b->Start().index
681                                                       : a->End().index < b->End().index;
682     });
683     return uniqueReferences;
684 }
685 
GetHightlightSpanKind(ir::AstNode * identifierDeclaration,ir::Identifier * node)686 HighlightSpanKind GetHightlightSpanKind(ir::AstNode *identifierDeclaration, ir::Identifier *node)
687 {
688     if (identifierDeclaration->Start().index == node->Start().index &&
689         identifierDeclaration->End().index == node->End().index) {
690         return HighlightSpanKind::WRITTEN_REFERENCE;
691     }
692     return HighlightSpanKind::REFERENCE;
693 }
694 
GetSemanticDocumentHighlights(es2panda_Context * context,size_t position)695 DocumentHighlights GetSemanticDocumentHighlights(es2panda_Context *context, size_t position)
696 {
697     auto ctx = reinterpret_cast<public_lib::Context *>(context);
698     std::string fileName(ctx->sourceFile->filePath);
699     auto touchingToken = GetTouchingToken(context, position, false);
700     if (!touchingToken->IsIdentifier()) {
701         return DocumentHighlights(fileName, {});
702     }
703     auto decl = compiler::DeclarationFromIdentifier(touchingToken->AsIdentifier());
704     if (decl == nullptr) {
705         return DocumentHighlights(fileName, {});
706     }
707     auto references = FindReferencesByName(ctx->parserProgram->Ast(), decl, touchingToken, ctx->allocator);
708 
709     auto highlightSpans = std::vector<HighlightSpan>();
710     // Find the identifier's declaration. We consider the first found to be the identifier's declaration.
711     ir::AstNode *identifierDeclaration = decl->FindChild([&touchingToken](ir::AstNode *child) {
712         return child->IsIdentifier() && child->AsIdentifier()->Name() == touchingToken->AsIdentifier()->Name();
713     });
714     for (const auto &reference : references) {
715         auto start = reference->Start().index;
716         auto length = reference->AsIdentifier()->Name().Length();
717         TextSpan textSpan = {start, length};
718         TextSpan contextSpan = {0, 0};
719         auto kind = GetHightlightSpanKind(identifierDeclaration, reference->AsIdentifier());
720         highlightSpans.emplace_back(fileName, false, textSpan, contextSpan, kind);
721     }
722     return DocumentHighlights(fileName, highlightSpans);
723 }
724 
GetDocumentHighlightsImpl(es2panda_Context * context,size_t position)725 DocumentHighlights GetDocumentHighlightsImpl(es2panda_Context *context, size_t position)
726 {
727     return GetSemanticDocumentHighlights(context, position);
728 }
729 
CreateInstallPackageActionInfos(std::vector<InstallPackageAction> & commands)730 std::vector<InstallPackageActionInfo> CreateInstallPackageActionInfos(std::vector<InstallPackageAction> &commands)
731 {
732     std::vector<InstallPackageActionInfo> infos;
733     for (const auto &command : commands) {
734         InstallPackageActionInfo info {command.type, command.file, command.packageName};
735         infos.push_back(info);
736     }
737 
738     return infos;
739 }
740 
CreateCodeFixActionInfo(CodeFixAction & codeFixAction)741 CodeFixActionInfo CreateCodeFixActionInfo(CodeFixAction &codeFixAction)
742 {
743     auto infos = CreateInstallPackageActionInfos(codeFixAction.commands);
744 
745     CodeActionInfo codeActionInfo {codeFixAction.description, codeFixAction.changes, infos};
746 
747     return CodeFixActionInfo {codeActionInfo, codeFixAction.fixName, codeFixAction.fixId,
748                               codeFixAction.fixAllDescription};
749 }
750 
CreateCombinedCodeActionsInfo(CombinedCodeActions & combinedCodeActions)751 CombinedCodeActionsInfo CreateCombinedCodeActionsInfo(CombinedCodeActions &combinedCodeActions)
752 {
753     auto infos = CreateInstallPackageActionInfos(combinedCodeActions.commands);
754 
755     return CombinedCodeActionsInfo {combinedCodeActions.changes, infos};
756 }
757 
GetCodeFixesAtPositionImpl(es2panda_Context * context,size_t startPosition,size_t endPosition,std::vector<int> & errorCodes,CodeFixOptions & codeFixOptions)758 std::vector<CodeFixActionInfo> GetCodeFixesAtPositionImpl(es2panda_Context *context, size_t startPosition,
759                                                           size_t endPosition, std::vector<int> &errorCodes,
760                                                           CodeFixOptions &codeFixOptions)
761 {
762     TextSpan textspan = TextSpan(startPosition, endPosition);
763     std::vector<CodeFixActionInfo> actions;
764     auto formatContext = GetFormatContext(codeFixOptions.options);
765 
766     for (auto errorCode : errorCodes) {
767         if (codeFixOptions.token.IsCancellationRequested()) {
768             return actions;
769         }
770 
771         TextChangesContext textChangesContext {LanguageServiceHost(), formatContext, codeFixOptions.preferences};
772         CodeFixContextBase codeFixContextBase {textChangesContext, context, codeFixOptions.token};
773         CodeFixContext codeFixContent {codeFixContextBase, errorCode, textspan};
774 
775         auto fixes = CodeFixProvider::Instance().GetFixes(codeFixContent);
776         for (auto fix : fixes) {
777             auto codeFixes = CreateCodeFixActionInfo(fix);
778             actions.push_back(codeFixes);
779         }
780     }
781 
782     return actions;
783 }
784 
GetCombinedCodeFixImpl(es2panda_Context * context,const std::string & fixId,CodeFixOptions & codeFixOptions)785 CombinedCodeActionsInfo GetCombinedCodeFixImpl(es2panda_Context *context, const std::string &fixId,
786                                                CodeFixOptions &codeFixOptions)
787 {
788     auto formatContext = GetFormatContext(codeFixOptions.options);
789     TextChangesContext textChangesContext {LanguageServiceHost(), formatContext, codeFixOptions.preferences};
790     CodeFixContextBase codeFixContextBase {textChangesContext, context, codeFixOptions.token};
791     CodeFixAllContext codeFixAllContent {codeFixContextBase, fixId};
792 
793     auto fixes = CodeFixProvider::Instance().GetAllFixes(codeFixAllContent);
794 
795     return CreateCombinedCodeActionsInfo(fixes);
796 }
797 
798 }  // namespace ark::es2panda::lsp
799