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