• 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::CLASS_MEMBER_SNIPPETS;
26 using ark::es2panda::lsp::sort_text::GLOBALS_OR_KEYWORDS;
27 using ark::es2panda::lsp::sort_text::MEMBER_DECLARED_BY_SPREAD_ASSIGNMENT;
28 using ark::es2panda::lsp::sort_text::SUGGESTED_CLASS_MEMBERS;
29 
AssertCompletionsContainAndNotContainEntries(const std::vector<CompletionEntry> & entries,const std::vector<CompletionEntry> & expectedEntries,const std::vector<CompletionEntry> & unexpectedEntries)30 static void AssertCompletionsContainAndNotContainEntries(const std::vector<CompletionEntry> &entries,
31                                                          const std::vector<CompletionEntry> &expectedEntries,
32                                                          const std::vector<CompletionEntry> &unexpectedEntries)
33 {
34     auto emptyCheck = expectedEntries.empty() && !entries.empty();
35     ASSERT_FALSE(emptyCheck) << "Expected empty but the result is not. Actual account: " << entries.size();
36 
37     for (const auto &expectedEntry : expectedEntries) {
38         bool found = false;
39         for (const auto &entry : entries) {
40             if (entry.GetName() == expectedEntry.GetName() &&
41                 entry.GetCompletionKind() == expectedEntry.GetCompletionKind()) {
42                 found = true;
43                 break;
44             }
45         }
46         ASSERT_TRUE(found) << "Expected completion '" << expectedEntry.GetName() << "' not found";
47     }
48 
49     for (const auto &unexpectedEntry : unexpectedEntries) {
50         bool found = false;
51         for (const auto &entry : entries) {
52             if (entry.GetName() == unexpectedEntry.GetName() &&
53                 entry.GetCompletionKind() == unexpectedEntry.GetCompletionKind()) {
54                 found = true;
55                 break;
56             }
57         }
58         ASSERT_FALSE(found) << "Unexpected completion '" << unexpectedEntry.GetName() << "' found";
59     }
60 }
61 
62 namespace {
63 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition19)64 TEST_F(LSPCompletionsTests, getCompletionsAtPosition19)
65 {
66     std::vector<std::string> files = {"getCompletionsAtPosition20.ets"};
67     std::vector<std::string> texts = {R"delimiter(
68 deep
69 )delimiter"};
70     auto filePaths = CreateTempFile(files, texts);
71 
72     int const expectedFileCount = 1;
73     ASSERT_EQ(filePaths.size(), expectedFileCount);
74 
75     LSPAPI const *lspApi = GetImpl();
76     size_t const offset = 5;
77     Initializer initializer = Initializer();
78     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
79     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
80     auto expectedEntries = std::vector<CompletionEntry> {
81         CompletionEntry("deepcopy", ark::es2panda::lsp::CompletionEntryKind::METHOD, std::string(GLOBALS_OR_KEYWORDS))};
82     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
83     initializer.DestroyContext(ctx);
84 }
85 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition18)86 TEST_F(LSPCompletionsTests, getCompletionsAtPosition18)
87 {
88     std::vector<std::string> files = {"getCompletionsAtPosition19.ets"};
89     std::vector<std::string> texts = {R"delimiter(
90 Readonl
91 )delimiter"};
92     auto filePaths = CreateTempFile(files, texts);
93 
94     int const expectedFileCount = 1;
95     ASSERT_EQ(filePaths.size(), expectedFileCount);
96 
97     LSPAPI const *lspApi = GetImpl();
98     size_t const offset = 8;
99     Initializer initializer = Initializer();
100     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
101     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
102     auto expectedEntries = std::vector<CompletionEntry> {CompletionEntry(
103         "ReadonlyArray", ark::es2panda::lsp::CompletionEntryKind::INTERFACE, std::string(GLOBALS_OR_KEYWORDS))};
104     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
105     initializer.DestroyContext(ctx);
106 }
107 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition17)108 TEST_F(LSPCompletionsTests, getCompletionsAtPosition17)
109 {
110     std::vector<std::string> files = {"getCompletionsAtPosition18.ets"};
111     std::vector<std::string> texts = {R"delimiter(
112 con
113 )delimiter"};
114     auto filePaths = CreateTempFile(files, texts);
115 
116     int const expectedFileCount = 1;
117     ASSERT_EQ(filePaths.size(), expectedFileCount);
118 
119     LSPAPI const *lspApi = GetImpl();
120     size_t const offset = 4;
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         CompletionEntry("console", ark::es2panda::lsp::CompletionEntryKind::PROPERTY, std::string(GLOBALS_OR_KEYWORDS)),
126         CompletionEntry("Console", ark::es2panda::lsp::CompletionEntryKind::CLASS, std::string(GLOBALS_OR_KEYWORDS)),
127         CompletionEntry("ConcurrentHashMap", ark::es2panda::lsp::CompletionEntryKind::CLASS,
128                         std::string(GLOBALS_OR_KEYWORDS))};
129     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
130     initializer.DestroyContext(ctx);
131 }
132 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition16)133 TEST_F(LSPCompletionsTests, getCompletionsAtPosition16)
134 {
135     std::vector<std::string> files = {"getCompletionsAtPosition17.ets"};
136     std::vector<std::string> texts = {R"delimiter(
137 struct MyClass {
138   property: string = '1'
139   get() {
140     return this._WILDCARD
141   }
142 }
143 )delimiter"};
144     auto filePaths = CreateTempFile(files, texts);
145 
146     int const expectedFileCount = 1;
147     ASSERT_EQ(filePaths.size(), expectedFileCount);
148 
149     LSPAPI const *lspApi = GetImpl();
150     size_t const offset = 78;  // after 'j._WILDCARD'
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("property", ark::es2panda::lsp::CompletionEntryKind::PROPERTY,
156                         std::string(SUGGESTED_CLASS_MEMBERS)),
157         CompletionEntry("get", ark::es2panda::lsp::CompletionEntryKind::METHOD, std::string(CLASS_MEMBER_SNIPPETS))};
158     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
159     initializer.DestroyContext(ctx);
160 }
161 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition15)162 TEST_F(LSPCompletionsTests, getCompletionsAtPosition15)
163 {
164     std::vector<std::string> files = {"getCompletionsAtPosition16.ets"};
165     std::vector<std::string> texts = {R"delimiter(
166 export interface Method {
167   get(value: number): this;
168 }
169 export interface CommonMethod {
170   width(value: number): this;
171   height(value: number): this;
172 }
173 export interface TextAttribute extends CommonMethod {
174   font(value: number): this;
175   fontColor(value: number): this;
176 }
177 export declare function Text(
178   content?: string,
179   value?: string
180 ): TextAttribute
181 Text("Hello").font()._WILDCARD
182 )delimiter"};
183     auto filePaths = CreateTempFile(files, texts);
184 
185     int const expectedFileCount = 1;
186     ASSERT_EQ(filePaths.size(), expectedFileCount);
187 
188     LSPAPI const *lspApi = GetImpl();
189     size_t const offset = 385;  // after 'j._WILDCARD'
190     Initializer initializer = Initializer();
191     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
192     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
193     auto expectedEntries = std::vector<CompletionEntry> {
194         CompletionEntry("font", ark::es2panda::lsp::CompletionEntryKind::METHOD, std::string(CLASS_MEMBER_SNIPPETS)),
195         CompletionEntry("fontColor", ark::es2panda::lsp::CompletionEntryKind::METHOD,
196                         std::string(CLASS_MEMBER_SNIPPETS)),
197         CompletionEntry("width", ark::es2panda::lsp::CompletionEntryKind::METHOD, std::string(CLASS_MEMBER_SNIPPETS)),
198         CompletionEntry("height", ark::es2panda::lsp::CompletionEntryKind::METHOD, std::string(CLASS_MEMBER_SNIPPETS))};
199     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
200     initializer.DestroyContext(ctx);
201 }
202 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition14)203 TEST_F(LSPCompletionsTests, getCompletionsAtPosition14)
204 {
205     std::vector<std::string> files = {"getCompletionsAtPosition15.ets"};
206     std::vector<std::string> texts = {R"delimiter(
207 export class MyClass0 {
208   public property0: string = '0'
209   public get0() {}
210 }
211 export class MyClass extends MyClass0 {
212   public property: string = '1'
213   public get() {}
214 }
215 export class MySonClass extends MyClass {
216   public property2: string = '2'
217 }
218 let c = new MySonClass()
219 let p = c._WILDCARD
220 )delimiter"};
221     auto filePaths = CreateTempFile(files, texts);
222 
223     int const expectedFileCount = 1;
224     ASSERT_EQ(filePaths.size(), expectedFileCount);
225 
226     LSPAPI const *lspApi = GetImpl();
227     size_t const offset = 292;  // after 'j._WILDCARD'
228     Initializer initializer = Initializer();
229     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
230     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
231     auto expectedEntries = std::vector<CompletionEntry> {
232         CompletionEntry("property", ark::es2panda::lsp::CompletionEntryKind::PROPERTY,
233                         std::string(SUGGESTED_CLASS_MEMBERS)),
234         CompletionEntry("property", ark::es2panda::lsp::CompletionEntryKind::PROPERTY,
235                         std::string(SUGGESTED_CLASS_MEMBERS)),
236         CompletionEntry("property2", ark::es2panda::lsp::CompletionEntryKind::PROPERTY,
237                         std::string(SUGGESTED_CLASS_MEMBERS)),
238         CompletionEntry("get", ark::es2panda::lsp::CompletionEntryKind::METHOD, std::string(CLASS_MEMBER_SNIPPETS)),
239         CompletionEntry("get0", ark::es2panda::lsp::CompletionEntryKind::METHOD, std::string(CLASS_MEMBER_SNIPPETS))};
240     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
241     initializer.DestroyContext(ctx);
242 }
243 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition13)244 TEST_F(LSPCompletionsTests, getCompletionsAtPosition13)
245 {
246     std::vector<std::string> files = {"getCompletionsAtPosition14.ets"};
247     std::vector<std::string> texts = {R"delimiter(
248 export interface Method {
249   get(value: number): this;
250 }
251 export interface CommonMethod {
252   width(value: number): this;
253   height(value: number): this;
254 }
255 export interface TextAttribute extends CommonMethod {
256   font(value: number): this;
257   fontColor(value: number): this;
258 }
259 export declare function Text(
260   content?: string,
261   value?: string
262 ): TextAttribute
263 Text("Hello")._WILDCARD
264 )delimiter"};
265     auto filePaths = CreateTempFile(files, texts);
266 
267     int const expectedFileCount = 1;
268     ASSERT_EQ(filePaths.size(), expectedFileCount);
269 
270     LSPAPI const *lspApi = GetImpl();
271     size_t const offset = 378;  // after 'j._WILDCARD'
272     Initializer initializer = Initializer();
273     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
274     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
275     auto expectedEntries = std::vector<CompletionEntry> {
276         CompletionEntry("font", ark::es2panda::lsp::CompletionEntryKind::METHOD, std::string(CLASS_MEMBER_SNIPPETS)),
277         CompletionEntry("fontColor", ark::es2panda::lsp::CompletionEntryKind::METHOD,
278                         std::string(CLASS_MEMBER_SNIPPETS)),
279         CompletionEntry("width", ark::es2panda::lsp::CompletionEntryKind::METHOD, std::string(CLASS_MEMBER_SNIPPETS)),
280         CompletionEntry("height", ark::es2panda::lsp::CompletionEntryKind::METHOD, std::string(CLASS_MEMBER_SNIPPETS))};
281     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
282     initializer.DestroyContext(ctx);
283 }
284 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition12)285 TEST_F(LSPCompletionsTests, getCompletionsAtPosition12)
286 {
287     std::vector<std::string> files = {"getCompletionsAtPosition13.ets"};
288     std::vector<std::string> texts = {R"delimiter(
289 class JSON {
290   public static stringify(d: byte): String {
291     return StringBuilder.toString(d)
292   }
293 }
294 let j = new JSON()
295 let res = j._WILDCARD
296 )delimiter"};
297     auto filePaths = CreateTempFile(files, texts);
298     int const expectedFileCount = 1;
299     ASSERT_EQ(filePaths.size(), expectedFileCount);
300 
301     LSPAPI const *lspApi = GetImpl();
302     size_t const offset = 142;  // after 'j._WILDCARD'
303     Initializer initializer = Initializer();
304     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
305     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
306     auto expectedEntries = std::vector<CompletionEntry> {CompletionEntry(
307         "stringify", ark::es2panda::lsp::CompletionEntryKind::METHOD, std::string(CLASS_MEMBER_SNIPPETS))};
308     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
309     initializer.DestroyContext(ctx);
310 }
311 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition11)312 TEST_F(LSPCompletionsTests, getCompletionsAtPosition11)
313 {
314     std::vector<std::string> files = {"getCompletionsAtPosition12.ets"};
315     std::vector<std::string> texts = {R"delimiter(
316 interface Inner { key : string; }
317 let i: Inner
318 i.k
319 let a = 1;)delimiter"};
320     auto filePaths = CreateTempFile(files, texts);
321     int const expectedFileCount = 1;
322     ASSERT_EQ(filePaths.size(), expectedFileCount);
323 
324     LSPAPI const *lspApi = GetImpl();
325     size_t const offset = 51;  // after 'i.k'
326     Initializer initializer = Initializer();
327     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
328     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
329     auto expectedEntries = std::vector<CompletionEntry> {
330         CompletionEntry("key", ark::es2panda::lsp::CompletionEntryKind::METHOD, std::string(CLASS_MEMBER_SNIPPETS))};
331     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
332     initializer.DestroyContext(ctx);
333 }
334 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition10)335 TEST_F(LSPCompletionsTests, getCompletionsAtPosition10)
336 {
337     std::vector<std::string> files = {"getCompletionsAtPosition11.ets"};
338     std::vector<std::string> texts = {R"delimiter(
339 interface Inner { key : string; }
340 let i: Inner
341 i._WILDCARD
342 let a = 1;)delimiter"};
343     auto filePaths = CreateTempFile(files, texts);
344     int const expectedFileCount = 1;
345     ASSERT_EQ(filePaths.size(), expectedFileCount);
346 
347     LSPAPI const *lspApi = GetImpl();
348     size_t const offset = 59;  // after 'i._WILDCARD'
349     Initializer initializer = Initializer();
350     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
351     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
352     auto expectedEntries = std::vector<CompletionEntry> {
353         CompletionEntry("key", ark::es2panda::lsp::CompletionEntryKind::METHOD, std::string(CLASS_MEMBER_SNIPPETS))};
354     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
355     initializer.DestroyContext(ctx);
356 }
357 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition7)358 TEST_F(LSPCompletionsTests, getCompletionsAtPosition7)
359 {
360     std::vector<std::string> files = {"getCompletionsAtPosition10.ets"};
361     std::vector<std::string> texts = {R"delimiter(
362 class MyClass {
363   public myProp: number = 0;
364   public prop: number = 0;
365 }
366 let obj = new MyClass();
367 let p = obj._WILDCARD
368 let a = 1;)delimiter"};
369     auto filePaths = CreateTempFile(files, texts);
370     int const expectedFileCount = 1;
371     ASSERT_EQ(filePaths.size(), expectedFileCount);
372 
373     LSPAPI const *lspApi = GetImpl();
374     size_t const offset = 121;  // after 'obj._WILDCARD' in 'let p = obj._WILDCARD'
375     Initializer initializer = Initializer();
376     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
377     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
378     auto expectedEntries =
379         std::vector<CompletionEntry> {CompletionEntry("myProp", ark::es2panda::lsp::CompletionEntryKind::PROPERTY,
380                                                       std::string(SUGGESTED_CLASS_MEMBERS)),
381                                       CompletionEntry("prop", ark::es2panda::lsp::CompletionEntryKind::PROPERTY,
382                                                       std::string(SUGGESTED_CLASS_MEMBERS))};
383     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
384     initializer.DestroyContext(ctx);
385 }
386 
TEST_F(LSPCompletionsTests,getCompletionsAtPosition8)387 TEST_F(LSPCompletionsTests, getCompletionsAtPosition8)
388 {
389     std::vector<std::string> files = {"getCompletionsAtPosition11.ets"};
390     std::vector<std::string> texts = {R"delimiter(
391 enum Color {
392   Red = "red",
393   Blue = "blue"
394 }
395 let myColor: Color = Color._WILDCARD)delimiter"};
396     auto filePaths = CreateTempFile(files, texts);
397     int const expectedFileCount = 1;
398     ASSERT_EQ(filePaths.size(), expectedFileCount);
399 
400     LSPAPI const *lspApi = GetImpl();
401     size_t const offset = 83;  // after 'Color._WILDCARD'
402     Initializer initializer = Initializer();
403     auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
404     auto res = lspApi->getCompletionsAtPosition(ctx, offset);
405     auto expectedEntries = std::vector<CompletionEntry> {
406         CompletionEntry("Red", CompletionEntryKind::ENUM_MEMBER, std::string(MEMBER_DECLARED_BY_SPREAD_ASSIGNMENT)),
407         CompletionEntry("Blue", CompletionEntryKind::ENUM_MEMBER, std::string(MEMBER_DECLARED_BY_SPREAD_ASSIGNMENT))};
408     AssertCompletionsContainAndNotContainEntries(res.GetEntries(), expectedEntries, {});
409     initializer.DestroyContext(ctx);
410 }
411 
412 }  // namespace
413