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