• 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 <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([&param](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