• 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/include/quick_info.h"
17 #include "lsp_api_test.h"
18 #include "lsp/include/internal_api.h"
19 #include <gtest/gtest.h>
20 
21 namespace {
22 using ark::es2panda::lsp::Initializer;
23 
24 class LspQuickInfoTests : public LSPAPITests {};
25 
TEST_F(LspQuickInfoTests,GetQuickInfoAtPosition1)26 TEST_F(LspQuickInfoTests, GetQuickInfoAtPosition1)
27 {
28     Initializer initializer = Initializer();
29     es2panda_Context *ctx =
30         initializer.CreateContext("quick_info3.ets", ES2PANDA_STATE_CHECKED, R"(enum MyStrings { A = 'hello' };)");
31     size_t const offset = 17;
32     LSPAPI const *lspApi = GetImpl();
33     auto quickInfo = lspApi->getQuickInfoAtPosition("quick_info3.ets", ctx, offset);
34     ASSERT_NE(quickInfo, QuickInfo());
35     std::vector<DocTagInfo> tags {};
36     std::vector<SymbolDisplayPart> document {};
37     const std::string kind;
38     size_t const start = 17;
39     size_t const length = 1;
40     TextSpan span(start, length);
41     const std::string kindModifiers = "static public readonly";
42     const std::string expectedFileName = "quick_info3.ets";
43 
44     std::vector<SymbolDisplayPart> expected;
45     expected.emplace_back("MyStrings", "enumName");
46     expected.emplace_back(".", "punctuation");
47     expected.emplace_back("A", "enumMember");
48     expected.emplace_back(" ", "space");
49     expected.emplace_back("=", "operator");
50     expected.emplace_back(" ", "space");
51     expected.emplace_back("\"", "punctuation");
52     expected.emplace_back("hello", "text");
53     expected.emplace_back("\"", "punctuation");
54 
55     auto expectedQuickInfo = QuickInfo(kind, kindModifiers, span, expected, document, tags, expectedFileName);
56     initializer.DestroyContext(ctx);
57     ASSERT_EQ(quickInfo, expectedQuickInfo);
58 }
59 
TEST_F(LspQuickInfoTests,GetQuickInfoAtPosition2)60 TEST_F(LspQuickInfoTests, GetQuickInfoAtPosition2)
61 {
62     Initializer initializer = Initializer();
63     es2panda_Context *ctx = initializer.CreateContext("quick-info-test.ets", ES2PANDA_STATE_CHECKED,
64                                                       "class MyClass {\n  public myProp: number = 0;\n}");
65     ASSERT_EQ(ContextState(ctx), ES2PANDA_STATE_CHECKED);
66     size_t const offset = 9;
67     const std::string fileName = "quick-info-test.ets";
68     auto quickInfo = ark::es2panda::lsp::GetQuickInfoAtPositionImpl(ctx, offset, fileName);
69     ASSERT_NE(quickInfo, QuickInfo());
70     std::vector<DocTagInfo> tags {};
71     std::vector<SymbolDisplayPart> document {};
72     const std::string kind = "class";
73     std::vector<SymbolDisplayPart> expected;
74     size_t const start = 6;
75     size_t const length = 7;
76     TextSpan span(start, length);
77     const std::string kindModifiers;
78 
79     expected.emplace_back("class", "keyword");
80     expected.emplace_back(" ", "space");
81     expected.emplace_back("MyClass", "className");
82 
83     auto expectedQuickInfo = QuickInfo(kind, kindModifiers, span, expected, document, tags, fileName);
84     ASSERT_EQ(quickInfo, expectedQuickInfo);
85 
86     initializer.DestroyContext(ctx);
87 }
88 
TEST_F(LspQuickInfoTests,GetQuickInfoAtPosition3)89 TEST_F(LspQuickInfoTests, GetQuickInfoAtPosition3)
90 {
91     Initializer initializer = Initializer();
92     es2panda_Context *ctx =
93         initializer.CreateContext("quick-info-test.ets", ES2PANDA_STATE_CHECKED,
94                                   "interface objI { key : string; }\nlet obj : objI = { key:\"valueaaaaaaaaa,\" }");
95     ASSERT_EQ(ContextState(ctx), ES2PANDA_STATE_CHECKED);
96     size_t const offset = 54;
97     const std::string fileName = "quick-info-test.ets";
98     auto quickInfo = ark::es2panda::lsp::GetQuickInfoAtPositionImpl(ctx, offset, fileName);
99     ASSERT_NE(quickInfo, QuickInfo());
100     std::vector<DocTagInfo> tags {};
101     std::vector<SymbolDisplayPart> document {};
102     const std::string kind = "property";
103     const std::string kindModifiers = "public abstract";
104     size_t const start = 52;
105     size_t const length = 3;
106     TextSpan span(start, length);
107     std::vector<SymbolDisplayPart> expected;
108 
109     expected.emplace_back("objI", "interface");
110     expected.emplace_back(".", "punctuation");
111     expected.emplace_back("key", "property");
112     expected.emplace_back(":", "punctuation");
113     expected.emplace_back(" ", "space");
114     expected.emplace_back("string", "returnType");
115 
116     auto expectedQuickInfo = QuickInfo(kind, kindModifiers, span, expected, document, tags, fileName);
117     ASSERT_EQ(quickInfo, expectedQuickInfo);
118 
119     initializer.DestroyContext(ctx);
120 }
121 
TEST_F(LspQuickInfoTests,getPropertySymbolFromContextualType1)122 TEST_F(LspQuickInfoTests, getPropertySymbolFromContextualType1)
123 {
124     Initializer initializer = Initializer();
125     es2panda_Context *ctx =
126         initializer.CreateContext("contextual-type-test.ets", ES2PANDA_STATE_CHECKED,
127                                   "interface objI { key : string; }\nlet obj : objI = { key:\"valueaaaaaaaaa,\" }");
128     ASSERT_EQ(ContextState(ctx), ES2PANDA_STATE_CHECKED);
129     size_t const offset = 54;
130     auto node = ark::es2panda::lsp::GetTokenForQuickInfo(ctx, offset);
131     ASSERT_NE(node, nullptr);
132     auto containingObjectNode = ark::es2panda::lsp::GetContainingObjectLiteralNode(node);
133     ASSERT_NE(containingObjectNode, nullptr);
134     auto contextualTypeNode = ark::es2panda::lsp::GetContextualTypeNode(containingObjectNode->Parent());
135     ASSERT_NE(contextualTypeNode, nullptr);
136     ASSERT_EQ(contextualTypeNode->Type(), ark::es2panda::ir::AstNodeType::TS_INTERFACE_DECLARATION);
137     auto propertyNode = ark::es2panda::lsp::GetPropertyNodeFromContextualType(containingObjectNode, contextualTypeNode);
138     ASSERT_NE(propertyNode, nullptr);
139     auto propertyDef = propertyNode->AsMethodDefinition();
140     ASSERT_EQ(propertyDef->Key()->AsIdentifier()->Name(), "key");
141 
142     initializer.DestroyContext(ctx);
143 }
144 
TEST_F(LspQuickInfoTests,getPropertySymbolFromContextualType2)145 TEST_F(LspQuickInfoTests, getPropertySymbolFromContextualType2)
146 {
147     Initializer initializer = Initializer();
148     es2panda_Context *ctx = initializer.CreateContext("contextual-type-test.ets", ES2PANDA_STATE_CHECKED,
149                                                       "const record : Record<string,number> = { \"hello\":1234 }");
150     ASSERT_EQ(ContextState(ctx), ES2PANDA_STATE_CHECKED);
151     size_t const offset = 44;
152     auto node = ark::es2panda::lsp::GetTokenForQuickInfo(ctx, offset);
153     ASSERT_NE(node, nullptr);
154     auto containingObjectNode = ark::es2panda::lsp::GetContainingObjectLiteralNode(node);
155     ASSERT_NE(containingObjectNode, nullptr);
156     auto contextualTypeNode = ark::es2panda::lsp::GetContextualTypeNode(containingObjectNode->Parent());
157     ASSERT_NE(contextualTypeNode, nullptr);
158     ASSERT_EQ(contextualTypeNode->Type(), ark::es2panda::ir::AstNodeType::CLASS_DEFINITION);
159     auto propertyNode = ark::es2panda::lsp::GetPropertyNodeFromContextualType(containingObjectNode, contextualTypeNode);
160     ASSERT_EQ(propertyNode->Type(), ark::es2panda::ir::AstNodeType::PROPERTY);
161 
162     initializer.DestroyContext(ctx);
163 }
164 
TEST_F(LspQuickInfoTests,GetNodeAtLocationForQuickInfo1)165 TEST_F(LspQuickInfoTests, GetNodeAtLocationForQuickInfo1)
166 {
167     Initializer initializer = Initializer();
168     es2panda_Context *ctx =
169         initializer.CreateContext("quick-info-test.ets", ES2PANDA_STATE_CHECKED,
170                                   "interface objI { key : string; }\nlet obj : objI = { key:\"valueaaaaaaaaa,\" }");
171     ASSERT_EQ(ContextState(ctx), ES2PANDA_STATE_CHECKED);
172     size_t const offset = 54;
173     auto node = ark::es2panda::lsp::GetTokenForQuickInfo(ctx, offset);
174     ASSERT_NE(node, nullptr);
175     auto nodeAtLocationForQuickInfo = ark::es2panda::lsp::GetNodeAtLocationForQuickInfo(node);
176     ASSERT_NE(nodeAtLocationForQuickInfo, nullptr);
177     auto propertyDef = nodeAtLocationForQuickInfo->AsMethodDefinition();
178     ASSERT_EQ(propertyDef->Key()->AsIdentifier()->Name(), "key");
179 
180     initializer.DestroyContext(ctx);
181 }
182 
TEST_F(LspQuickInfoTests,GetNodeAtLocationForQuickInfo2)183 TEST_F(LspQuickInfoTests, GetNodeAtLocationForQuickInfo2)
184 {
185     Initializer initializer = Initializer();
186     es2panda_Context *ctx =
187         initializer.CreateContext("quick-info-test.ets", ES2PANDA_STATE_CHECKED,
188                                   "class Test {\n  private _a: number = 1;\n  public get a(): number {\n    "
189                                   "return this._a;\n  }\n  public static ccc:number = 1\n\n  constructor(a : "
190                                   "number) {\n  }\n}\n\nlet a = 1\nlet test: Test = new Test(a)\nlet t_a = test.a");
191     ASSERT_EQ(ContextState(ctx), ES2PANDA_STATE_CHECKED);
192     size_t const offset = 8;
193     auto node = ark::es2panda::lsp::GetTokenForQuickInfo(ctx, offset);
194     ASSERT_NE(node, nullptr);
195     auto nodeAtLocationForQuickInfo = ark::es2panda::lsp::GetNodeAtLocationForQuickInfo(node);
196     ASSERT_NE(nodeAtLocationForQuickInfo, nullptr);
197     ASSERT_EQ(nodeAtLocationForQuickInfo->Type(), ark::es2panda::ir::AstNodeType::CLASS_DEFINITION);
198 
199     size_t const position = 191;
200     node = ark::es2panda::lsp::GetTokenForQuickInfo(ctx, position);
201     ASSERT_NE(node, nullptr);
202     nodeAtLocationForQuickInfo = ark::es2panda::lsp::GetNodeAtLocationForQuickInfo(node);
203     ASSERT_NE(nodeAtLocationForQuickInfo, nullptr);
204     ASSERT_EQ(nodeAtLocationForQuickInfo->Type(), ark::es2panda::ir::AstNodeType::CLASS_DEFINITION);
205 
206     initializer.DestroyContext(ctx);
207 }
208 
TEST_F(LspQuickInfoTests,GetNodeAtLocationForQuickInfo3)209 TEST_F(LspQuickInfoTests, GetNodeAtLocationForQuickInfo3)
210 {
211     Initializer initializer = Initializer();
212     es2panda_Context *ctx =
213         initializer.CreateContext("quick-info-test.ets", ES2PANDA_STATE_CHECKED,
214                                   "function func():string {\n  return \"func\"\n}\nlet f = func();");
215     ASSERT_EQ(ContextState(ctx), ES2PANDA_STATE_CHECKED);
216     size_t const offset = 11;
217     auto node = ark::es2panda::lsp::GetTokenForQuickInfo(ctx, offset);
218     ASSERT_NE(node, nullptr);
219     auto nodeAtLocationForQuickInfo = ark::es2panda::lsp::GetNodeAtLocationForQuickInfo(node);
220     ASSERT_NE(nodeAtLocationForQuickInfo, nullptr);
221     ASSERT_EQ(nodeAtLocationForQuickInfo->Type(), ark::es2panda::ir::AstNodeType::METHOD_DEFINITION);
222 
223     size_t const position = 53;
224     node = ark::es2panda::lsp::GetTokenForQuickInfo(ctx, position);
225     ASSERT_NE(node, nullptr);
226     nodeAtLocationForQuickInfo = ark::es2panda::lsp::GetNodeAtLocationForQuickInfo(node);
227     ASSERT_NE(nodeAtLocationForQuickInfo, nullptr);
228     ASSERT_EQ(nodeAtLocationForQuickInfo->Type(), ark::es2panda::ir::AstNodeType::METHOD_DEFINITION);
229 
230     initializer.DestroyContext(ctx);
231 }
232 
TEST_F(LspQuickInfoTests,GetNodeAtLocationForQuickInfo4)233 TEST_F(LspQuickInfoTests, GetNodeAtLocationForQuickInfo4)
234 {
235     Initializer initializer = Initializer();
236     es2panda_Context *ctx =
237         initializer.CreateContext("quick-info-test.ets", ES2PANDA_STATE_CHECKED,
238                                   "type NullableObject = Object | null\nlet nullOb: NullableObject = null");
239     ASSERT_EQ(ContextState(ctx), ES2PANDA_STATE_CHECKED);
240     size_t const offset = 11;
241     auto node = ark::es2panda::lsp::GetTokenForQuickInfo(ctx, offset);
242     ASSERT_NE(node, nullptr);
243     auto nodeAtLocationForQuickInfo = ark::es2panda::lsp::GetNodeAtLocationForQuickInfo(node);
244     ASSERT_NE(nodeAtLocationForQuickInfo, nullptr);
245     ASSERT_EQ(nodeAtLocationForQuickInfo->Type(), ark::es2panda::ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION);
246 
247     initializer.DestroyContext(ctx);
248 }
249 
TEST_F(LspQuickInfoTests,GetNodeAtLocationForQuickInfo5)250 TEST_F(LspQuickInfoTests, GetNodeAtLocationForQuickInfo5)
251 {
252     Initializer initializer = Initializer();
253     es2panda_Context *ctx = initializer.CreateContext(
254         "quick-info-test.ets", ES2PANDA_STATE_CHECKED,
255         "enum Color {\n  Red = \"red\",\n  Blue = \"blue\"\n}\n\nlet myColor: Color = Color.Red;");
256     ASSERT_EQ(ContextState(ctx), ES2PANDA_STATE_CHECKED);
257     size_t const offset = 7;
258     auto node = ark::es2panda::lsp::GetTokenForQuickInfo(ctx, offset);
259     ASSERT_NE(node, nullptr);
260     auto nodeAtLocationForQuickInfo = ark::es2panda::lsp::GetNodeAtLocationForQuickInfo(node);
261     ASSERT_NE(nodeAtLocationForQuickInfo, nullptr);
262     ASSERT_EQ(nodeAtLocationForQuickInfo->Type(), ark::es2panda::ir::AstNodeType::TS_ENUM_DECLARATION);
263 
264     size_t const position = 70;
265     node = ark::es2panda::lsp::GetTokenForQuickInfo(ctx, position);
266     ASSERT_NE(node, nullptr);
267     nodeAtLocationForQuickInfo = ark::es2panda::lsp::GetNodeAtLocationForQuickInfo(node);
268     ASSERT_NE(nodeAtLocationForQuickInfo, nullptr);
269     ASSERT_EQ(nodeAtLocationForQuickInfo->Type(), ark::es2panda::ir::AstNodeType::TS_ENUM_DECLARATION);
270 
271     initializer.DestroyContext(ctx);
272 }
273 
TEST_F(LspQuickInfoTests,CreateDisplayForEnumMemberWithNumberLiteral)274 TEST_F(LspQuickInfoTests, CreateDisplayForEnumMemberWithNumberLiteral)
275 {
276     Initializer initializer = Initializer();
277     es2panda_Context *ctx = initializer.CreateContext("enum-member-test.ets", ES2PANDA_STATE_CHECKED,
278                                                       "enum MyEnum { First = 1, Second = 2 }");
279     ASSERT_EQ(ContextState(ctx), ES2PANDA_STATE_CHECKED);
280     auto context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
281     auto ast = reinterpret_cast<ark::es2panda::ir::AstNode *>(context->parserProgram->Ast());
282     auto checkFunc = [](ark::es2panda::ir::AstNode *node) {
283         return node->Type() == ark::es2panda::ir::AstNodeType::CLASS_PROPERTY &&
284                node->AsClassProperty()->Key()->AsIdentifier()->Name() == "First";
285     };
286     auto found = ast->FindChild(checkFunc);
287     auto parent = found->Parent();
288     auto enumDecl = parent->AsClassDefinition()->OrigEnumDecl()->AsTSEnumDeclaration();
289     auto enumMember = enumDecl->FindChild([&found](ark::es2panda::ir::AstNode *child) {
290         return child->IsTSEnumMember() && child->AsTSEnumMember()->Key()->AsIdentifier()->Name() ==
291                                               found->AsClassProperty()->Key()->AsIdentifier()->Name();
292     });
293     std::vector<SymbolDisplayPart> display = ark::es2panda::lsp::CreateDisplayForEnumMember(enumMember);
294     std::vector<SymbolDisplayPart> expected;
295     expected.emplace_back("MyEnum", "enumName");
296     expected.emplace_back(".", "punctuation");
297     expected.emplace_back("First", "enumMember");
298     expected.emplace_back(" ", "space");
299     expected.emplace_back("=", "operator");
300     expected.emplace_back(" ", "space");
301     expected.emplace_back("1", "text");
302     ASSERT_EQ(expected, display);
303     initializer.DestroyContext(ctx);
304 }
305 
TEST_F(LspQuickInfoTests,CreateDisplayForEnumMemberWithStringLiteral)306 TEST_F(LspQuickInfoTests, CreateDisplayForEnumMemberWithStringLiteral)
307 {
308     Initializer initializer = Initializer();
309     es2panda_Context *ctx = initializer.CreateContext("enum-member-string-test.ets", ES2PANDA_STATE_CHECKED,
310                                                       "enum MyStrings { A = 'hello' }");
311     ASSERT_EQ(ContextState(ctx), ES2PANDA_STATE_CHECKED);
312     auto context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
313     auto ast = reinterpret_cast<ark::es2panda::ir::AstNode *>(context->parserProgram->Ast());
314     auto checkFunc = [](ark::es2panda::ir::AstNode *node) {
315         return node->Type() == ark::es2panda::ir::AstNodeType::CLASS_PROPERTY &&
316                node->AsClassProperty()->Key()->AsIdentifier()->Name() == "A";
317     };
318     auto found = ast->FindChild(checkFunc);
319     auto parent = found->Parent();
320     auto enumDecl = parent->AsClassDefinition()->OrigEnumDecl()->AsTSEnumDeclaration();
321     auto enumMember = enumDecl->FindChild([&found](ark::es2panda::ir::AstNode *child) {
322         return child->Type() == ark::es2panda::ir::AstNodeType::TS_ENUM_MEMBER &&
323                child->AsTSEnumMember()->Key()->AsIdentifier()->Name() ==
324                    found->AsClassProperty()->Key()->AsIdentifier()->Name();
325     });
326     std::vector<SymbolDisplayPart> display = ark::es2panda::lsp::CreateDisplayForEnumMember(enumMember);
327     std::vector<SymbolDisplayPart> expected;
328     expected.emplace_back("MyStrings", "enumName");
329     expected.emplace_back(".", "punctuation");
330     expected.emplace_back("A", "enumMember");
331     expected.emplace_back(" ", "space");
332     expected.emplace_back("=", "operator");
333     expected.emplace_back(" ", "space");
334     expected.emplace_back("\"", "punctuation");
335     expected.emplace_back("hello", "text");
336     expected.emplace_back("\"", "punctuation");
337     ASSERT_EQ(expected, display);
338     initializer.DestroyContext(ctx);
339 }
340 }  // namespace
341