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 <cstdio>
18 #include <gtest/gtest.h>
19 #include <iostream>
20 #include <ostream>
21 #include <regex>
22 #include <string>
23 #include <vector>
24 #include "lsp_api_test.h"
25 #include "lsp/include/class_hierarchy.h"
26 #include "lsp/include/internal_api.h"
27 #include "lsp/include/services/text_change/change_tracker.h"
28 #include "public/es2panda_lib.h"
29 #include <variant>
30
31 namespace {
32
33 class LspClassChangeTracker : public LSPAPITests {};
34
35 const size_t START_POS = 0;
36 const size_t END_POS = 9;
37 const auto C6 = 6;
38
39 bool g_hasTextChange = false;
40 bool g_hasNodeChange = false;
41
GetTracker()42 ark::es2panda::lsp::ChangeTracker GetTracker()
43 {
44 const std::string defaultNewLine = "\n";
45 ark::es2panda::lsp::FormatCodeSettings settings;
46 auto formatContext = ark::es2panda::lsp::GetFormatContext(settings);
47 TextChangesContext changeText {{}, formatContext, {}};
48 auto tracker = ark::es2panda::lsp::ChangeTracker::FromContext(changeText);
49 return tracker;
50 }
51
TEST_F(LspClassChangeTracker,PushRaw_AddsChangeCorrectly)52 TEST_F(LspClassChangeTracker, PushRaw_AddsChangeCorrectly)
53 {
54 const char *source = R"(
55 function add(a: number, b: number) {
56 return a + b;
57 }
58 )";
59 ark::es2panda::lsp::Initializer initializer;
60 es2panda_Context *ctx =
61 initializer.CreateContext("pushRaw_AddsChangeCorrectly.ets", ES2PANDA_STATE_CHECKED, source);
62 auto *context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
63 auto sourceFile = context->sourceFile;
64
65 const auto *returnNode =
66 context->parserProgram->Ast()->FindChild([](const auto *node) { return node->IsReturnStatement(); });
67 ASSERT_NE(returnNode, nullptr);
68
69 auto spanStart = returnNode->Start().index;
70 auto spanLength = strlen("return a + b;");
71 TextSpan span = {spanStart, spanLength};
72 std::string retStr = "return a - b;";
73 TextChange change = {span, retStr};
74 FileTextChanges fileChange = {"pushRaw_AddsChangeCorrectly.ets", {change}};
75 auto tracker = GetTracker();
76 tracker.PushRaw(sourceFile, fileChange);
77
78 auto list = tracker.GetChanges();
79 EXPECT_FALSE(list.empty());
80 EXPECT_EQ(list.size(), 1);
81
82 const auto variant = list[0];
83 const auto changeText = variant.textChanges;
84 ASSERT_EQ(changeText[0].newText, retStr);
85
86 EXPECT_EQ(variant.fileName, fileChange.fileName);
87
88 const auto &textChange = changeText;
89 EXPECT_EQ(textChange[0].span.start, spanStart);
90 }
91
TEST_F(LspClassChangeTracker,DeleteMethods_BasicTest1)92 TEST_F(LspClassChangeTracker, DeleteMethods_BasicTest1)
93 {
94 const char *source = R"(
95 function test() {
96 const a = 1;
97 const b = 2;
98 }
99 )";
100 ark::es2panda::lsp::Initializer initializer;
101 es2panda_Context *ctx = initializer.CreateContext("deleteMethods_BasicTest1.ets", ES2PANDA_STATE_CHECKED, source);
102 auto *context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
103 const auto sourceFile = context->sourceFile;
104 ark::es2panda::ir::AstNode *ast = context->parserProgram->Ast();
105 ASSERT_NE(ast, nullptr);
106 const auto ca = "a";
107 const auto c0 = 0;
108 const auto c1 = 1;
109 const auto c2 = 2;
110 ark::es2panda::ir::AstNode *aDecl;
111 ast->FindChild([&ca, &aDecl](ark::es2panda::ir::AstNode *node) {
112 if (node->IsVariableDeclaration() &&
113 node->AsVariableDeclaration()->Declarators()[c0]->Id()->AsIdentifier()->Name() == ca) {
114 aDecl = node;
115 }
116 return false;
117 });
118 ASSERT_NE(aDecl, nullptr);
119 auto tracker = GetTracker();
120 std::variant<const ark::es2panda::ir::AstNode *, const std::vector<const ark::es2panda::ir::AstNode *>>
121 nodeVariant = aDecl;
122 tracker.Delete(sourceFile, nodeVariant);
123 const auto &deletedNodes = tracker.GetDeletedNodesList();
124 EXPECT_EQ(deletedNodes.size(), c1);
125 ark::es2panda::lsp::TextRange range = {aDecl->Start().index, aDecl->End().index};
126 tracker.DeleteRange(sourceFile, range);
127 const auto &changes = tracker.GetChangeList();
128 EXPECT_EQ(changes.size(), c1);
129 auto &change = std::get<ark::es2panda::lsp::RemoveNode>(changes[c0]);
130 EXPECT_EQ(change.kind, ark::es2panda::lsp::ChangeKind::REMOVE);
131 EXPECT_EQ(change.range.pos, range.pos);
132 EXPECT_EQ(change.range.end, range.end);
133 tracker.DeleteNode(ctx, sourceFile, aDecl);
134 EXPECT_EQ(tracker.GetChangeList().size(), c2);
135 initializer.DestroyContext(ctx);
136 }
137
TEST_F(LspClassChangeTracker,DeleteHelpers_BasicTest2)138 TEST_F(LspClassChangeTracker, DeleteHelpers_BasicTest2)
139 {
140 const char *source = "export async function test() { \n let a = 1; \n let b = 2; \n let c = 3; \n}";
141 ark::es2panda::lsp::Initializer initializer;
142 es2panda_Context *ctx = initializer.CreateContext("deleteHelpers_BasicTest2.ets", ES2PANDA_STATE_CHECKED, source);
143 auto *context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
144 auto *ast = context->parserProgram->Ast();
145 ASSERT_NE(ast, nullptr);
146 const auto *sourceFile = context->sourceFile;
147 auto tracker = GetTracker();
148 ark::es2panda::ir::AstNode *aDecl;
149 ark::es2panda::ir::AstNode *bDecl;
150 ark::es2panda::ir::AstNode *cDecl;
151 const auto c3 = 3;
152
153 ast->FindChild([&aDecl, &bDecl, &cDecl](ark::es2panda::ir::AstNode *node) {
154 if (node->IsVariableDeclaration() &&
155 node->AsVariableDeclaration()->Declarators()[0]->Id()->AsIdentifier()->Name() == "a") {
156 aDecl = node;
157 }
158 if (node->IsVariableDeclaration() &&
159 node->AsVariableDeclaration()->Declarators()[0]->Id()->AsIdentifier()->Name() == "b") {
160 bDecl = node;
161 }
162 if (node->IsVariableDeclaration() &&
163 node->AsVariableDeclaration()->Declarators()[0]->Id()->AsIdentifier()->Name() == "c") {
164 cDecl = node;
165 }
166 return false;
167 });
168 ASSERT_NE(aDecl, nullptr);
169 ASSERT_NE(bDecl, nullptr);
170 ASSERT_NE(cDecl, nullptr);
171 tracker.DeleteNode(ctx, sourceFile, aDecl);
172 tracker.DeleteNodeRange(ctx, bDecl, cDecl);
173 tracker.DeleteNodeRangeExcludingEnd(ctx, const_cast<ark::es2panda::ir::AstNode *>(aDecl), bDecl);
174 const auto &changes = tracker.GetChangeList(); // varsayalım internal erişim var
175 EXPECT_EQ(changes.size(), c3);
176 for (const auto &c : changes) {
177 if (const auto *removeNode = std::get_if<ark::es2panda::lsp::RemoveNode>(&c)) {
178 EXPECT_EQ(removeNode->kind, ark::es2panda::lsp::ChangeKind::REMOVE);
179 EXPECT_LT(removeNode->range.pos, removeNode->range.end);
180 }
181 }
182 initializer.DestroyContext(ctx);
183 }
184
TEST_F(LspClassChangeTracker,ReplaceHelpers_BasicTest1)185 TEST_F(LspClassChangeTracker, ReplaceHelpers_BasicTest1)
186 {
187 const char *source = "function test() {\nlet a = 1;\nlet b = 2;\n}";
188 ark::es2panda::lsp::Initializer initializer;
189 es2panda_Context *ctx = initializer.CreateContext("replaceHelpers_BasicTest1.ets", ES2PANDA_STATE_CHECKED, source);
190 auto *context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
191 auto *ast = context->parserProgram->Ast();
192 auto *sourceFile = context->sourceFile;
193 auto tracker = GetTracker();
194 ark::es2panda::ir::AstNode *aDecl;
195 ark::es2panda::ir::AstNode *bDecl;
196 ast->FindChild([&aDecl, &bDecl](ark::es2panda::ir::AstNode *node) {
197 if (node->IsVariableDeclaration() &&
198 node->AsVariableDeclaration()->Declarators()[0]->Id()->AsIdentifier()->Name() == "a") {
199 aDecl = node;
200 }
201 if (node->IsVariableDeclaration() &&
202 node->AsVariableDeclaration()->Declarators()[0]->Id()->AsIdentifier()->Name() == "b") {
203 bDecl = node;
204 }
205 return false;
206 });
207 const char *textReplace = "// replaced\n";
208 const char *replaceNode = "let x = 100;";
209 ark::es2panda::ir::AstNode *bClone = bDecl->Clone(context->allocator, nullptr);
210 ark::es2panda::lsp::ChangeNodeOptions options = {};
211 tracker.ReplaceNode(ctx, aDecl, bClone, options);
212 ark::es2panda::ir::AstNode *fnNode =
213 ast->FindChild([](ark::es2panda::ir::AstNode *node) { return node->IsFunctionExpression(); });
214 tracker.ReplaceNodeRange(ctx, bClone, fnNode, bClone);
215 std::vector<ark::es2panda::ir::AstNode *> bCloneVec {bClone};
216 tracker.ReplaceNodeWithNodes(ctx, aDecl, bCloneVec);
217 tracker.ReplaceNodeWithText(ctx, bClone, replaceNode);
218 tracker.ReplaceRangeWithText(sourceFile, {START_POS, END_POS}, textReplace);
219 tracker.ReplaceNodeRangeWithNodes(ctx, bClone, bClone, bCloneVec);
220 const auto &changes = tracker.GetChangeList(); // Internal erişim
221 EXPECT_EQ(changes.size(), C6);
222 for (const auto &change : changes) {
223 if (std::get_if<ark::es2panda::lsp::ChangeText>(&change) != nullptr) {
224 g_hasTextChange = true;
225 }
226 if (std::get_if<ark::es2panda::lsp::ReplaceWithSingleNode>(&change) != nullptr) {
227 g_hasNodeChange = true;
228 }
229 }
230 EXPECT_TRUE(g_hasTextChange);
231 EXPECT_TRUE(g_hasNodeChange);
232 initializer.DestroyContext(ctx);
233 }
234
TEST_F(LspClassChangeTracker,ReplacePropertyAssignment_BasicTest)235 TEST_F(LspClassChangeTracker, ReplacePropertyAssignment_BasicTest)
236 {
237 const char *source = R"(
238 let obj = {
239 foo: 1
240 };)";
241 ark::es2panda::lsp::Initializer initializer;
242 es2panda_Context *ctx =
243 initializer.CreateContext("replacePropertyAssignment_BasicTest.ets", ES2PANDA_STATE_CHECKED, source);
244 auto *context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
245 auto *ast = context->parserProgram->Ast();
246 ASSERT_NE(ast, nullptr);
247 auto *propertyNode = ast->FindChild([](const ark::es2panda::ir::AstNode *node) {
248 return node->IsProperty() && node->AsProperty()->Key()->AsIdentifier()->Name() == "foo";
249 });
250 ASSERT_NE(propertyNode, nullptr);
251 ark::es2panda::ir::AstNode *newProperty = propertyNode->Clone(context->allocator, nullptr);
252 ASSERT_NE(newProperty, nullptr);
253 auto *prop = const_cast<ark::es2panda::ir::Property *>(newProperty->AsProperty());
254 auto *ident = const_cast<ark::es2panda::ir::Identifier *>(prop->Key()->AsIdentifier());
255 ident->SetName("bar");
256 ark::es2panda::lsp::ChangeTracker tracker = GetTracker();
257 tracker.ReplacePropertyAssignment(ctx, propertyNode, newProperty);
258 const auto &changes = tracker.GetChangeList();
259 const size_t c1 = 1;
260 const auto bar = "bar";
261 const auto newLine = ",\n";
262 ASSERT_EQ(changes.size(), c1);
263 const auto &change = changes[0];
264 const auto *replace = std::get_if<ark::es2panda::lsp::ReplaceWithSingleNode>(&change);
265 ASSERT_NE(replace, nullptr);
266 EXPECT_EQ(replace->kind, ark::es2panda::lsp::ChangeKind::REPLACEWITHSINGLENODE);
267 EXPECT_EQ(replace->node->AsProperty()->Key()->AsIdentifier()->Name(), bar);
268 EXPECT_EQ(replace->options->suffix, newLine);
269 initializer.DestroyContext(ctx);
270 }
271
TEST_F(LspClassChangeTracker,ReplaceConstructorBody_BasicTest)272 TEST_F(LspClassChangeTracker, ReplaceConstructorBody_BasicTest)
273 {
274 const char *source = "class MyClass {\nconstructor() {\nlet a = 1;\n}\n}";
275 ark::es2panda::lsp::Initializer initializer;
276 es2panda_Context *ctx =
277 initializer.CreateContext("replaceConstructorBody_BasicTest.ets", ES2PANDA_STATE_CHECKED, source);
278 auto *context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
279 auto *ast = context->parserProgram->Ast();
280 ASSERT_NE(ast, nullptr);
281 ark::es2panda::ir::AstNode *ctrNode;
282 ast->FindChild([&ctrNode](ark::es2panda::ir::AstNode *node) {
283 if (node->IsFunctionExpression() && node->AsFunctionExpression()->Function()->Id()->Name() == "constructor") {
284 ctrNode = node;
285 }
286 return false;
287 });
288 ASSERT_NE(ctrNode, nullptr);
289 std::vector<ark::es2panda::ir::Statement *> dummyStatements;
290 ark::es2panda::lsp::ChangeTracker tracker = GetTracker();
291 tracker.ReplaceConstructorBody(ctx, ctrNode, dummyStatements);
292 const auto &changes = tracker.GetChangeList();
293 const size_t c1 = 1;
294 ASSERT_EQ(changes.size(), c1);
295 const auto &change = changes[0];
296 const auto *replace = std::get_if<ark::es2panda::lsp::ReplaceWithSingleNode>(&change);
297 ASSERT_NE(replace, nullptr);
298 EXPECT_EQ(replace->kind, ark::es2panda::lsp::ChangeKind::REPLACEWITHSINGLENODE);
299 EXPECT_EQ(replace->node, nullptr);
300 initializer.DestroyContext(ctx);
301 }
302
TEST_F(LspClassChangeTracker,InsertHelpers_BasicTest1)303 TEST_F(LspClassChangeTracker, InsertHelpers_BasicTest1)
304 {
305 const char *source = "function test() {\nlet a = 1;\nlet b = 2;\n}";
306 ark::es2panda::lsp::Initializer initializer;
307 es2panda_Context *ctx = initializer.CreateContext("insertHelpers_BasicTest1.ets", ES2PANDA_STATE_CHECKED, source);
308 auto *context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
309 auto *ast = context->parserProgram->Ast();
310 ASSERT_NE(ast, nullptr);
311 ark::es2panda::lsp::ChangeTracker tracker = GetTracker();
312 ark::es2panda::ir::AstNode *aDecl;
313 ark::es2panda::ir::AstNode *bDecl;
314 ast->FindChild([&aDecl, &bDecl](ark::es2panda::ir::AstNode *node) {
315 if (node->IsVariableDeclaration() &&
316 node->AsVariableDeclaration()->Declarators()[0]->Id()->AsIdentifier()->Name() == "a") {
317 aDecl = node;
318 }
319 if (node->IsVariableDeclaration() &&
320 node->AsVariableDeclaration()->Declarators()[0]->Id()->AsIdentifier()->Name() == "b") {
321 bDecl = node;
322 }
323 return false;
324 });
325 ASSERT_NE(aDecl, nullptr);
326 ASSERT_NE(bDecl, nullptr);
327 ark::es2panda::ir::AstNode *bClone = bDecl->Clone(context->allocator, nullptr);
328 ark::es2panda::lsp::InsertNodeOptions options;
329 ASSERT_NE(bClone, nullptr);
330 auto *ident = const_cast<ark::es2panda::ir::Identifier *>(
331 bClone->AsVariableDeclaration()->Declarators()[0]->Id()->AsIdentifier());
332 ident->SetName("bar");
333 tracker.InsertNodeAt(ctx, aDecl->Start().index, bClone, options);
334 ident->SetName("top");
335 tracker.InsertNodeAtTopOfFile(ctx, bClone, false);
336 ident->SetName("before");
337 tracker.InsertNodeBefore(ctx, aDecl, bClone, false);
338 ident->SetName("modify");
339 tracker.InsertModifierAt(ctx, aDecl->End().index, bClone, options);
340 ident->SetName("modifBefore");
341 tracker.InsertModifierBefore(ctx, aDecl, bClone);
342 const size_t c5 = 5;
343 ASSERT_EQ(tracker.GetChangeList().size(), c5);
344 initializer.DestroyContext(ctx);
345 }
346
TEST_F(LspClassChangeTracker,InsertTextTest)347 TEST_F(LspClassChangeTracker, InsertTextTest)
348 {
349 const std::string sourceText = "const a = 1;";
350 const std::string file = "insertTextTest_1.ets";
351 auto filePaths = CreateTempFile({file}, {sourceText});
352 ark::es2panda::lsp::Initializer initializer = ark::es2panda::lsp::Initializer();
353 es2panda_Context *ctx = initializer.CreateContext(file.c_str(), ES2PANDA_STATE_CHECKED, sourceText.c_str());
354 auto context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
355
356 ark::es2panda::lsp::ChangeTracker tracker = GetTracker();
357 const size_t insertPosition = 0;
358 const size_t c1 = 1;
359 const std::string textToInsert = "// Inserted text\n";
360 tracker.InsertText(context->sourceFile, insertPosition, textToInsert);
361 ASSERT_EQ(tracker.GetChangeList().size(), c1);
362 const auto &changeVariant = tracker.GetChangeList();
363 const auto &change = std::get<ark::es2panda::lsp::ChangeText>(changeVariant[0]);
364 ASSERT_EQ(change.kind, ark::es2panda::lsp::ChangeKind::TEXT);
365 EXPECT_EQ(change.range.pos, insertPosition);
366 EXPECT_EQ(change.text, textToInsert);
367 initializer.DestroyContext(ctx);
368 }
369
TEST_F(LspClassChangeTracker,TryInsertTypeAnnotationTest)370 TEST_F(LspClassChangeTracker, TryInsertTypeAnnotationTest)
371 {
372 const std::string sourceText = "const a = 1;";
373 const std::string file = "tryInsertTypeAnnotationTest.ets";
374 auto filePaths = CreateTempFile({file}, {sourceText});
375 ark::es2panda::lsp::Initializer initializer = ark::es2panda::lsp::Initializer();
376 es2panda_Context *ctx = initializer.CreateContext(file.c_str(), ES2PANDA_STATE_CHECKED, sourceText.c_str());
377 auto context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
378
379 ark::es2panda::lsp::ChangeTracker tracker = GetTracker();
380 auto ast = reinterpret_cast<ark::es2panda::ir::AstNode *>(context->parserProgram->Ast());
381 ark::es2panda::ir::AstNode *aDecl;
382 ast->FindChild([&aDecl](ark::es2panda::ir::AstNode *node) {
383 if (node->IsClassProperty() && node->AsClassProperty()->Id()->AsIdentifier()->Name() == "a") {
384 aDecl = node;
385 }
386 return false;
387 });
388 ASSERT_NE(aDecl, nullptr);
389 const size_t c1 = 1;
390 const auto prefix = ": ";
391 ark::es2panda::ir::AstNode *aClone = aDecl->Clone(context->allocator, nullptr);
392 tracker.TryInsertTypeAnnotation(ctx, aDecl, aClone);
393 ASSERT_EQ(tracker.GetChangeList().size(), c1);
394 const auto change = std::get<ark::es2panda::lsp::ReplaceWithSingleNode>(tracker.GetChangeList()[0]);
395 ASSERT_EQ(change.kind, ark::es2panda::lsp::ChangeKind::REPLACEWITHSINGLENODE);
396 EXPECT_EQ(change.range.pos, aDecl->End().index);
397 EXPECT_EQ(change.options->prefix, prefix);
398 initializer.DestroyContext(ctx);
399 }
400
TEST_F(LspClassChangeTracker,TryInsertThisTypeAnnotationTest)401 TEST_F(LspClassChangeTracker, TryInsertThisTypeAnnotationTest)
402 {
403 const std::string sourceText = "function foo() {}";
404 const std::string file = "tryInsertThisTypeAnnotationTest.ets";
405 auto filePaths = CreateTempFile({file}, {sourceText});
406 ark::es2panda::lsp::Initializer initializer = ark::es2panda::lsp::Initializer();
407 es2panda_Context *ctx = initializer.CreateContext(file.c_str(), ES2PANDA_STATE_CHECKED, sourceText.c_str());
408 auto context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
409 ark::es2panda::lsp::ChangeTracker tracker = GetTracker();
410 auto ast = reinterpret_cast<ark::es2panda::ir::AstNode *>(context->parserProgram->Ast());
411 ark::es2panda::ir::AstNode *funcDecl;
412 ast->FindChild([&funcDecl](ark::es2panda::ir::AstNode *node) {
413 if (node->IsFunctionExpression() &&
414 node->AsFunctionExpression()->Function()->Id()->AsIdentifier()->Name() == "foo") {
415 funcDecl = node;
416 }
417 return false;
418 });
419 ASSERT_NE(funcDecl, nullptr);
420 ark::es2panda::ir::AstNode *aClone = funcDecl->Clone(context->allocator, nullptr);
421 tracker.TryInsertThisTypeAnnotation(ctx, funcDecl, aClone);
422 const auto c1 = 1;
423 const auto prefix = "this: ";
424 ASSERT_EQ(tracker.GetChangeList().size(), c1);
425 const auto &changeVariant = tracker.GetChangeList();
426 const auto &change = std::get<ark::es2panda::lsp::ReplaceWithSingleNode>(changeVariant[0]);
427 ASSERT_EQ(change.kind, ark::es2panda::lsp::ChangeKind::REPLACEWITHSINGLENODE);
428 EXPECT_EQ(change.range.pos, funcDecl->Start().index);
429 EXPECT_EQ(change.options->prefix, prefix);
430 initializer.DestroyContext(ctx);
431 }
432
TEST_F(LspClassChangeTracker,InsertTypeParametersTest)433 TEST_F(LspClassChangeTracker, InsertTypeParametersTest)
434 {
435 const std::string sourceText = "function foo(a: number) {}";
436 const std::string file = "insertTypeParametersTest.ets";
437 auto filePaths = CreateTempFile({file}, {sourceText});
438 ark::es2panda::lsp::Initializer initializer = ark::es2panda::lsp::Initializer();
439 es2panda_Context *ctx = initializer.CreateContext(file.c_str(), ES2PANDA_STATE_CHECKED, sourceText.c_str());
440 auto context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
441 ark::es2panda::lsp::ChangeTracker tracker = GetTracker();
442 auto ast = reinterpret_cast<ark::es2panda::ir::AstNode *>(context->parserProgram->Ast());
443 ark::es2panda::ir::AstNode *funcDecl;
444 ark::es2panda::ir::AstNode *param;
445 ast->FindChild([&funcDecl](ark::es2panda::ir::AstNode *node) {
446 if (node->IsFunctionExpression() &&
447 node->AsFunctionExpression()->Function()->Id()->AsIdentifier()->Name() == "foo") {
448 funcDecl = node;
449 }
450 return false;
451 });
452 ast->FindChild([¶m](ark::es2panda::ir::AstNode *node) {
453 if (node->IsETSParameterExpression()) {
454 param = node;
455 }
456 return false;
457 });
458 ASSERT_NE(funcDecl, nullptr);
459 const auto param1 = param->Clone(context->allocator, nullptr);
460 const auto param2 = param->Clone(context->allocator, nullptr);
461 std::vector<ark::es2panda::ir::AstNode *> typeParams {param1, param2};
462 tracker.InsertTypeParameters(ctx, funcDecl, typeParams);
463 const auto c1 = 1;
464 const auto prefix = "<";
465 const auto suffix = ">";
466 const auto joiner = ", ";
467 ASSERT_EQ(tracker.GetChangeList().size(), c1);
468 const auto &changeVariant = tracker.GetChangeList();
469 const auto &change = std::get<ark::es2panda::lsp::ReplaceWithMultipleNodes>(changeVariant[0]);
470 ASSERT_EQ(change.kind, ark::es2panda::lsp::ChangeKind::REPLACEWITHMULTIPLENODES);
471 EXPECT_EQ(change.range.pos, funcDecl->End().index); // This may be wrong
472 EXPECT_EQ(change.options->prefix, prefix);
473 EXPECT_EQ(change.options->suffix, suffix);
474 EXPECT_EQ(change.options->joiner, joiner);
475 initializer.DestroyContext(ctx);
476 }
477
TEST_F(LspClassChangeTracker,InsertExportModifier_BasicTest)478 TEST_F(LspClassChangeTracker, InsertExportModifier_BasicTest)
479 {
480 const char *source = R"(
481 class ETSGLOBAL {
482 public static foo() {
483 let x = 10;
484 }
485 }
486 )";
487 ark::es2panda::lsp::Initializer initializer;
488 es2panda_Context *ctx =
489 initializer.CreateContext("insertExportModifier_BasicTest.ets", ES2PANDA_STATE_CHECKED, source);
490 auto *context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
491 ASSERT_NE(context->parserProgram->Ast(), nullptr);
492 auto *sourceFile = context->sourceFile;
493 ark::es2panda::lsp::ChangeTracker tracker = GetTracker();
494 ark::es2panda::ir::AstNode *classNode = context->parserProgram->Ast()->FindChild(
495 [](ark::es2panda::ir::AstNode *node) { return node->IsClassDeclaration(); });
496 ASSERT_NE(classNode, nullptr);
497 auto *statementNode = classNode->AsStatement();
498 ASSERT_NE(statementNode, nullptr);
499 tracker.InsertExportModifier(sourceFile, statementNode);
500 const auto c1 = 1;
501 const auto expText = "export ";
502 ASSERT_EQ(tracker.GetChangeList().size(), c1);
503 const auto &changeVariant = tracker.GetChangeList();
504 const auto &change = std::get<ark::es2panda::lsp::ChangeText>(changeVariant[0]);
505 ASSERT_NE(&change, nullptr);
506 EXPECT_EQ(change.text, expText);
507 EXPECT_EQ(change.range.pos, statementNode->Start().index);
508 EXPECT_EQ(change.range.pos, change.range.end); // Insert olduğu için boş aralık
509
510 initializer.DestroyContext(ctx);
511 }
512
513 } // namespace