• 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 "lsp_api_test.h"
17 #include "lsp/include/completions.h"
18 #include "lsp/include/internal_api.h"
19 
20 class LSPCompletionsTests : public LSPAPITests {};
21 
22 using ark::es2panda::lsp::CompletionEntry;
23 using ark::es2panda::lsp::CompletionEntryKind;
24 using ark::es2panda::lsp::Initializer;
25 using ark::es2panda::lsp::sort_text::GLOBALS_OR_KEYWORDS;
26 
AssertCompletionsContainAndNotContainEntries(const std::vector<CompletionEntry> & entries,const std::vector<CompletionEntry> & expectedEntries,const std::vector<CompletionEntry> & unexpectedEntries)27 static void AssertCompletionsContainAndNotContainEntries(const std::vector<CompletionEntry> &entries,
28                                                          const std::vector<CompletionEntry> &expectedEntries,
29                                                          const std::vector<CompletionEntry> &unexpectedEntries)
30 {
31     auto emptyCheck = expectedEntries.empty() && !entries.empty();
32     ASSERT_FALSE(emptyCheck) << "Expected empty but the result is not. Actual account: " << entries.size();
33 
34     for (const auto &expectedEntry : expectedEntries) {
35         bool found = false;
36         for (const auto &entry : entries) {
37             if (entry.GetName() == expectedEntry.GetName() &&
38                 entry.GetCompletionKind() == expectedEntry.GetCompletionKind()) {
39                 found = true;
40                 break;
41             }
42         }
43         ASSERT_TRUE(found) << "Expected completion '" << expectedEntry.GetName() << "' not found";
44     }
45 
46     for (const auto &unexpectedEntry : unexpectedEntries) {
47         bool found = false;
48         for (const auto &entry : entries) {
49             if (entry.GetName() == unexpectedEntry.GetName() &&
50                 entry.GetCompletionKind() == unexpectedEntry.GetCompletionKind()) {
51                 found = true;
52                 break;
53             }
54         }
55         ASSERT_FALSE(found) << "Unexpected completion '" << unexpectedEntry.GetName() << "' found";
56     }
57 }
58 
59 namespace {
TEST_F(LSPCompletionsTests,MemberCompletionsForClassTest9)60 TEST_F(LSPCompletionsTests, MemberCompletionsForClassTest9)
61 {
62     std::vector<std::string> files = {"getCompletionsAtPositionMember9.ets"};
63     std::vector<std::string> texts = {R"delimiter(
64 namespace space {
65   export class classInSpace {
66     public  c: number = 2;
67   }
68 }
69 let numOfSpace: space._WILDCARD)delimiter"};
70     auto filePaths = CreateTempFile(files, texts);
71     int const expectedFileCount = 1;
72     ASSERT_EQ(filePaths.size(), expectedFileCount);
73     Initializer initializer = Initializer();
74     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
75     LSPAPI const *lspApi = GetImpl();
76     const size_t offset = 113;
77     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
78     auto entries = res.GetEntries();
79     std::string propertyName1 = "classInSpace";
80     ASSERT_TRUE(entries.size() == 1);
81     CompletionEntry entry1 =
82         CompletionEntry(propertyName1, CompletionEntryKind::CLASS,
83                         std::string(ark::es2panda::lsp::sort_text::MEMBER_DECLARED_BY_SPREAD_ASSIGNMENT));
84     initializer.DestroyContext(ctx);
85     ASSERT_EQ(entry1, entries[0]);
86 }
87 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition6)88 TEST_F(LSPCompletionsTests, getCompletionsAtPosition6)
89 {
90     std::vector<std::string> files = {"getCompletionsAtPosition9.ets"};
91     std::vector<std::string> texts = {R"delimiter(
92 let a: num
93 )delimiter"};
94     auto filePaths = CreateTempFile(files, texts);
95     int const expectedFileCount = 1;
96     ASSERT_EQ(filePaths.size(), expectedFileCount);
97 
98     LSPAPI const *lspApi = GetImpl();
99     size_t const offset = 11;  // after 'n' in 'let a = n'
100     Initializer initializer = Initializer();
101     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
102     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
103     auto expectedEntries = std::vector<CompletionEntry> {
104         CompletionEntry("number", ark::es2panda::lsp::CompletionEntryKind::KEYWORD, std::string(GLOBALS_OR_KEYWORDS))};
105     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
106     initializer.DestroyContext(ctx);
107 }
108 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition5)109 TEST_F(LSPCompletionsTests, getCompletionsAtPosition5)
110 {
111     std::vector<std::string> files = {"getCompletionsAtPosition8.ets"};
112     std::vector<std::string> texts = {R"delimiter(
113 class
114 )delimiter"};
115     auto filePaths = CreateTempFile(files, texts);
116     int const expectedFileCount = 1;
117     ASSERT_EQ(filePaths.size(), expectedFileCount);
118 
119     LSPAPI const *lspApi = GetImpl();
120     size_t const offset = 6;  // after 'ss' in 'class'
121     Initializer initializer = Initializer();
122     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
123     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
124     auto expectedEntries = std::vector<CompletionEntry> {};
125     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
126     initializer.DestroyContext(ctx);
127 }
128 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition0)129 TEST_F(LSPCompletionsTests, getCompletionsAtPosition0)
130 {
131     std::vector<std::string> files = {"getCompletionsAtPosition7.ets"};
132     std::vector<std::string> texts = {R"delimiter(
133 function num1() {
134     return 1;
135 }
136 
137 function num2() {
138     return 2;
139 }
140 
141 console.log(1);
142 
143 let a = n
144 )delimiter"};
145     auto filePaths = CreateTempFile(files, texts);
146     int const expectedFileCount = 1;
147     ASSERT_EQ(filePaths.size(), expectedFileCount);
148 
149     LSPAPI const *lspApi = GetImpl();
150     size_t const offset = 97;  // after 'n' in 'let a = n'
151     Initializer initializer = Initializer();
152     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
153     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
154     auto expectedEntries = std::vector<CompletionEntry> {
155         CompletionEntry("num1", ark::es2panda::lsp::CompletionEntryKind::FUNCTION, std::string(GLOBALS_OR_KEYWORDS)),
156         CompletionEntry("num2", ark::es2panda::lsp::CompletionEntryKind::FUNCTION, std::string(GLOBALS_OR_KEYWORDS)),
157     };
158     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
159     initializer.DestroyContext(ctx);
160 }
161 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition1)162 TEST_F(LSPCompletionsTests, getCompletionsAtPosition1)
163 {
164     std::vector<std::string> files = {"getCompletionsAtPosition1.ets"};
165     std::vector<std::string> texts = {R"delimiter(
166 function num1() {
167     return 1;
168 }
169 
170 function num2() {
171     return 2;
172 }
173 
174 let a = n
175 )delimiter"};
176     auto filePaths = CreateTempFile(files, texts);
177     int const expectedFileCount = 1;
178     ASSERT_EQ(filePaths.size(), expectedFileCount);
179     Initializer initializer = Initializer();
180     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
181     LSPAPI const *lspApi = GetImpl();
182     size_t const offset = 80;  // after 'n' in 'let a = n'
183     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
184     auto expectedEntries = std::vector<CompletionEntry> {
185         CompletionEntry("num1", ark::es2panda::lsp::CompletionEntryKind::FUNCTION, std::string(GLOBALS_OR_KEYWORDS)),
186         CompletionEntry("num2", ark::es2panda::lsp::CompletionEntryKind::FUNCTION, std::string(GLOBALS_OR_KEYWORDS)),
187     };
188     initializer.DestroyContext(ctx);
189     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
190 }
191 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition2)192 TEST_F(LSPCompletionsTests, getCompletionsAtPosition2)
193 {
194     std::vector<std::string> files = {"getCompletionsAtPosition2.ets"};
195     std::vector<std::string> texts = {R"delimiter(
196 let aaa = 123;
197 const abb = 333;
198 
199 function axx() {
200     return 444;
201 }
202 
203 function foo() {
204     let bbb = 222;
205     let ccc = bbb + a
206     return bbb + ccc;
207 }
208 )delimiter"};
209     auto filePaths = CreateTempFile(files, texts);
210     int const expectedFileCount = 1;
211     ASSERT_EQ(filePaths.size(), expectedFileCount);
212     Initializer initializer = Initializer();
213     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
214     LSPAPI const *lspApi = GetImpl();
215     size_t const offset = 127;  // after 'a' in 'let ccc = bbb + a'
216     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
217     auto expectedEntries = std::vector<CompletionEntry> {
218         CompletionEntry("aaa", ark::es2panda::lsp::CompletionEntryKind::VARIABLE, std::string(GLOBALS_OR_KEYWORDS)),
219         CompletionEntry("abb", ark::es2panda::lsp::CompletionEntryKind::CONSTANT, std::string(GLOBALS_OR_KEYWORDS)),
220         CompletionEntry("axx", ark::es2panda::lsp::CompletionEntryKind::FUNCTION, std::string(GLOBALS_OR_KEYWORDS)),
221     };
222     initializer.DestroyContext(ctx);
223     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
224 }
225 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition3)226 TEST_F(LSPCompletionsTests, getCompletionsAtPosition3)
227 {
228     std::vector<std::string> files = {"getCompletionsAtPosition3.ets"};
229     std::vector<std::string> texts = {R"delimiter(
230 class Foo {
231     bar: number = 1;
232 }
233 
234 let foo = new Foo();
235 foo.bar = 2;
236 let baa = 3;
237 let bbb = 4;
238 
239 function bxx() {
240     return 5;
241 }
242 
243 function fxx() {
244     let bcc = 6;
245     let axx = b
246 }
247 )delimiter"};
248     auto filePaths = CreateTempFile(files, texts);
249     int const expectedFileCount = 1;
250     ASSERT_EQ(filePaths.size(), expectedFileCount);
251     Initializer initializer = Initializer();
252     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
253     LSPAPI const *lspApi = GetImpl();
254     size_t const offset = 181;  // after 'b' in 'let axx = b'
255     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
256     auto expectedEntries = std::vector<CompletionEntry> {
257         CompletionEntry("baa", ark::es2panda::lsp::CompletionEntryKind::VARIABLE, std::string(GLOBALS_OR_KEYWORDS)),
258         CompletionEntry("bbb", ark::es2panda::lsp::CompletionEntryKind::VARIABLE, std::string(GLOBALS_OR_KEYWORDS)),
259         CompletionEntry("bcc", ark::es2panda::lsp::CompletionEntryKind::VARIABLE, std::string(GLOBALS_OR_KEYWORDS)),
260         CompletionEntry("bxx", ark::es2panda::lsp::CompletionEntryKind::FUNCTION, std::string(GLOBALS_OR_KEYWORDS)),
261     };
262     auto unexpectedEntries = std::vector<CompletionEntry> {
263         CompletionEntry("bar", ark::es2panda::lsp::CompletionEntryKind::VARIABLE, std::string(GLOBALS_OR_KEYWORDS)),
264     };
265     initializer.DestroyContext(ctx);
266     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, unexpectedEntries);
267 }
268 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition4)269 TEST_F(LSPCompletionsTests, getCompletionsAtPosition4)
270 {
271     std::vector<std::string> files = {"getCompletionsAtPosition4.ets"};
272     std::vector<std::string> texts = {R"delimiter(
273 class Foo {
274     bar: number = 1;
275 }
276 
277 let foo = new Foo();
278 foo.bar = 2;
279 let baa = 3;
280 let bbb = 4;
281 
282 function bxx() {
283     let bcc = 6;
284     return 5;
285 }
286 let axx = b
287 )delimiter"};
288     auto filePaths = CreateTempFile(files, texts);
289     int const expectedFileCount = 1;
290     ASSERT_EQ(filePaths.size(), expectedFileCount);
291     Initializer initializer = Initializer();
292     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
293     LSPAPI const *lspApi = GetImpl();
294     size_t const offset = 159;  // after 'b' in 'let axx = b'
295     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
296     auto expectedEntries = std::vector<CompletionEntry> {
297         CompletionEntry("baa", ark::es2panda::lsp::CompletionEntryKind::VARIABLE, std::string(GLOBALS_OR_KEYWORDS)),
298         CompletionEntry("bbb", ark::es2panda::lsp::CompletionEntryKind::VARIABLE, std::string(GLOBALS_OR_KEYWORDS)),
299         CompletionEntry("bxx", ark::es2panda::lsp::CompletionEntryKind::FUNCTION, std::string(GLOBALS_OR_KEYWORDS)),
300     };
301     auto unexpectedEntries = std::vector<CompletionEntry> {
302         CompletionEntry("bar", ark::es2panda::lsp::CompletionEntryKind::PROPERTY, std::string(GLOBALS_OR_KEYWORDS)),
303         CompletionEntry("bcc", ark::es2panda::lsp::CompletionEntryKind::VARIABLE, std::string(GLOBALS_OR_KEYWORDS)),
304     };
305     initializer.DestroyContext(ctx);
306     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, unexpectedEntries);
307 }
308 
TEST_F(LSPCompletionsTests,MemberCompletionsForClassTest1)309 TEST_F(LSPCompletionsTests, MemberCompletionsForClassTest1)
310 {
311     std::vector<std::string> files = {"getCompletionsAtPosition5.ets"};
312     std::vector<std::string> texts = {R"delimiter(
313 class MyClass1 {
314   public myProp: number = 0;
315   public prop: number = 1;
316 }
317 let obj1 = new MyClass1()
318 let prop = obj1.yp)delimiter"};
319     auto filePaths = CreateTempFile(files, texts);
320     int const expectedFileCount = 1;
321     ASSERT_EQ(filePaths.size(), expectedFileCount);
322     Initializer initializer = Initializer();
323     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
324     LSPAPI const *lspApi = GetImpl();
325     const size_t offset = 120;
326     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
327     auto entries = res.GetEntries();
328     ASSERT_TRUE(entries.size() == 1);
329 
330     std::string propertyName1 = "myProp";
331     CompletionEntry entry1 = CompletionEntry(propertyName1, CompletionEntryKind::PROPERTY,
332                                              std::string(ark::es2panda::lsp::sort_text::SUGGESTED_CLASS_MEMBERS));
333     initializer.DestroyContext(ctx);
334     ASSERT_EQ(entry1, entries[0]);
335 }
336 
TEST_F(LSPCompletionsTests,MemberCompletionsForClassTest2)337 TEST_F(LSPCompletionsTests, MemberCompletionsForClassTest2)
338 {
339     std::vector<std::string> files = {"getCompletionsAtPosition6.ets"};
340     std::vector<std::string> texts = {R"delimiter(
341 namespace space {
342   export class classInSpace {
343     public  c: number = 2;
344   }
345 }
346 let numOfSpace: space.classi)delimiter"};
347     auto filePaths = CreateTempFile(files, texts);
348     int const expectedFileCount = 1;
349     ASSERT_EQ(filePaths.size(), expectedFileCount);
350     Initializer initializer = Initializer();
351     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
352     LSPAPI const *lspApi = GetImpl();
353     const size_t offset = 110;
354     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
355     auto entries = res.GetEntries();
356     std::string propertyName1 = "classInSpace";
357     ASSERT_TRUE(entries.size() == 1);
358     CompletionEntry entry1 =
359         CompletionEntry(propertyName1, CompletionEntryKind::CLASS,
360                         std::string(ark::es2panda::lsp::sort_text::MEMBER_DECLARED_BY_SPREAD_ASSIGNMENT));
361     initializer.DestroyContext(ctx);
362     ASSERT_EQ(entry1, entries[0]);
363 }
364 
TEST_F(LSPCompletionsTests,MemberCompletionsForClassTest4)365 TEST_F(LSPCompletionsTests, MemberCompletionsForClassTest4)
366 {
367     std::vector<std::string> files = {"getCompletionsAtPosition6.ets"};
368     std::vector<std::string> texts = {R"delimiter(
369 enum Color {
370   Red = "red",
371   Blue = "blue"
372 }
373 let myColor: Color = Color.R)delimiter"};
374     auto filePaths = CreateTempFile(files, texts);
375     int const expectedFileCount = 1;
376     ASSERT_EQ(filePaths.size(), expectedFileCount);
377     Initializer initializer = Initializer();
378     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
379     LSPAPI const *lspApi = GetImpl();
380     const size_t offset = 75;
381     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
382     auto entries = res.GetEntries();
383     ASSERT_TRUE(entries.size() == 1);
384 
385     std::string propertyName1 = "Red";
386     CompletionEntry entry1 =
387         CompletionEntry(propertyName1, CompletionEntryKind::ENUM_MEMBER,
388                         std::string(ark::es2panda::lsp::sort_text::MEMBER_DECLARED_BY_SPREAD_ASSIGNMENT));
389     initializer.DestroyContext(ctx);
390     ASSERT_EQ(entry1, entries[0]);
391 }
392 }  // namespace