diff --git a/ets2panda/lsp/include/quick_info.h b/ets2panda/lsp/include/quick_info.h index f234a884b3bf88b6a84fe72dc7da582842e60c38..7c4075b6ae96f249d3af3261e10a1da6fe6ca953 100644 --- a/ets2panda/lsp/include/quick_info.h +++ b/ets2panda/lsp/include/quick_info.h @@ -37,7 +37,7 @@ std::vector CreateDisplayForEnum(ir::AstNode *node); std::vector CreateDisplayForEnumMember(ir::AstNode *node); std::vector CreateDisplayForTypeParameter(ir::AstNode *node); std::vector CreateDisplayForMethodDefinition(ir::AstNode *node, const std::string &kindModifier); -std::vector CreateDisplayForClassProperty(ir::AstNode *node, const std::string &kindModifier); +std::vector CreateDisplayForClassProperty(ir::AstNode *node); std::vector CreateDisplayForETSParameterExpression(ir::AstNode *node); QuickInfo GetQuickInfoAtPositionImpl(es2panda_Context *context, size_t position, std::string fileName); std::string GetNameForTypeReference(const ir::TypeNode *typeReference); diff --git a/ets2panda/lsp/src/completions_details.cpp b/ets2panda/lsp/src/completions_details.cpp index f82c5f40a4c1b828d566a3a4b0c755799bf71d36..de85c72152384c4c04824958af9564389adf3782 100644 --- a/ets2panda/lsp/src/completions_details.cpp +++ b/ets2panda/lsp/src/completions_details.cpp @@ -37,10 +37,10 @@ void GetDisplayPartAndKind(ir::AstNode *node, std::vector &di if (IsClass(node)) { displayParts = ark::es2panda::lsp::CreateDisplayForClass(node); kind = "class"; - } else if (node->Type() == ir::AstNodeType::ETS_PARAMETER_EXPRESSION) { + } else if (node->IsETSParameterExpression()) { displayParts = ark::es2panda::lsp::CreateDisplayForETSParameterExpression(node); kind = "parameter"; - } else if (node->Type() == ir::AstNodeType::CLASS_PROPERTY) { + } else if (node->IsClassProperty()) { // After enum refactoring, enum declaration is transformed to a class declaration if (compiler::ClassDefinitionIsEnumTransformed(node->Parent())) { auto enumDecl = node->Parent()->AsClassDefinition()->OrigEnumDecl()->AsTSEnumDeclaration(); @@ -48,28 +48,28 @@ void GetDisplayPartAndKind(ir::AstNode *node, std::vector &di displayParts = ark::es2panda::lsp::CreateDisplayForEnumMember(enumMember); kind = "enum member"; } else { - displayParts = ark::es2panda::lsp::CreateDisplayForClassProperty(node, kindModifiers); + displayParts = ark::es2panda::lsp::CreateDisplayForClassProperty(node); kind = "property"; } - } else if (node->Type() == ir::AstNodeType::TS_INTERFACE_DECLARATION) { + } else if (node->IsTSInterfaceDeclaration()) { displayParts = ark::es2panda::lsp::CreateDisplayForInterface(node); kind = "interface"; - } else if (node->Type() == ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION) { + } else if (node->IsTSTypeAliasDeclaration()) { displayParts = ark::es2panda::lsp::CreateDisplayForTypeAlias(node); kind = "type alias"; - } else if (node->Type() == ir::AstNodeType::TS_ENUM_DECLARATION) { + } else if (node->IsTSEnumDeclaration()) { displayParts = ark::es2panda::lsp::CreateDisplayForEnum(node); kind = "enum"; - } else if (node->Type() == ir::AstNodeType::IMPORT_DECLARATION) { + } else if (node->IsImportDeclaration()) { displayParts = CreateDisplayForImportDeclaration(node); kind = "import"; - } else if (node->Type() == ir::AstNodeType::TS_TYPE_PARAMETER) { + } else if (node->IsTSTypeParameter()) { displayParts = ark::es2panda::lsp::CreateDisplayForTypeParameter(node); kind = "type parameter"; - } else if (node->Type() == ir::AstNodeType::METHOD_DEFINITION) { + } else if (node->IsMethodDefinition()) { displayParts = ark::es2panda::lsp::CreateDisplayForMethodDefinition(node, kindModifiers); kind = "function"; - if (node->Parent() != nullptr && node->Parent()->Type() == ir::AstNodeType::TS_INTERFACE_BODY) { + if (node->Parent() != nullptr && node->Parent()->IsTSInterfaceBody()) { kind = "property"; } } diff --git a/ets2panda/lsp/src/quick_info.cpp b/ets2panda/lsp/src/quick_info.cpp index 7b9289beef8f9516fd5ff78434c8d69f7f8ad3a0..097decf6413a839f5ee1c9d31a6a0cc073392d3d 100644 --- a/ets2panda/lsp/src/quick_info.cpp +++ b/ets2panda/lsp/src/quick_info.cpp @@ -17,6 +17,7 @@ #include "internal_api.h" #include "ir/astNode.h" #include "public/public.h" +#include "ir/ets/etsTuple.h" #include "ir/ets/etsUnionType.h" #include "api.h" #include "compiler/lowering/util.h" @@ -396,12 +397,26 @@ std::string GetNameForUnionType(const ir::TypeNode *unionType) for (size_t i = 0; i < types.size(); ++i) { newstr += GetNameForTypeNode(types[i]); if (i != types.size() - 1) { - newstr += "|"; + newstr += " | "; } } return newstr; } +std::string GetNameForTupleType(const ir::TypeNode *tupleType) +{ + const auto &types = tupleType->AsETSTuple()->GetTupleTypeAnnotationsList(); + std::string newstr = "["; + for (size_t i = 0; i < types.size(); ++i) { + newstr += GetNameForTypeNode(types[i]); + if (i != types.size() - 1) { + newstr += ", "; + } + } + newstr += "]"; + return newstr; +} + std::string GetNameForTypeReference(const ir::TypeNode *typeReference) { auto type = typeReference->AsETSTypeReference()->Part(); @@ -441,6 +456,68 @@ std::string GetNameForFunctionType(const ir::TypeNode *functionType) return "((" + params + ") => " + returnType + ")"; } +std::string EscapeJsonString(const std::string &input) +{ + std::ostringstream oss; + oss << "\""; + for (char c : input) { + switch (c) { + case '\"': + oss << "\\\""; + break; + case '\\': + oss << "\\\\"; + break; + case '\n': + oss << "\\n"; + break; + case '\r': + oss << "\\r"; + break; + case '\t': + oss << "\\t"; + break; + default: + oss << c; + break; + } + } + oss << "\""; + return oss.str(); +} + +std::string GetNameForLiteralTypeNode(const ir::AstNode *node, bool iskindModifier = false) +{ + if (node == nullptr) { + return "undefined"; + } + if (node->IsStringLiteral()) { + return iskindModifier ? EscapeJsonString(std::string(node->AsStringLiteral()->Str())) : "String"; + } + if (node->IsNumberLiteral()) { + return iskindModifier ? std::string(node->AsNumberLiteral()->Str()) : "Number"; + } + if (node->IsBooleanLiteral()) { + return iskindModifier ? node->AsBooleanLiteral()->Value() ? "true" : "false" : "Boolean"; + } + if (node->IsBigIntLiteral()) { + return "Bigint"; + } + if (node->IsIdentifier()) { + auto name = node->AsIdentifier()->Name(); + if (name.Is("NaN")) { + return "Number"; + } + } + if (node->IsNullLiteral()) { + return "null"; + } + if (node->IsETSStringLiteralType()) { + return "String"; + } + return "undefined"; +} + std::string GetNameForTypeNode(const ir::TypeNode *typeAnnotation) { if (typeAnnotation->IsETSUnionType()) { @@ -449,27 +526,25 @@ std::string GetNameForTypeNode(const ir::TypeNode *typeAnnotation) if (typeAnnotation->IsETSPrimitiveType()) { return PrimitiveTypeToName(typeAnnotation->AsETSPrimitiveType()->GetPrimitiveType()); } - if (typeAnnotation->IsETSTypeReference()) { return GetNameForTypeReference(typeAnnotation); } - if (typeAnnotation->IsETSFunctionType()) { return GetNameForFunctionType(typeAnnotation); } - if (typeAnnotation->IsTSArrayType()) { return GetNameForTypeNode(typeAnnotation->AsTSArrayType()->ElementType()) + "[]"; } - if (typeAnnotation->IsETSNullType()) { return "null"; } - if (typeAnnotation->IsETSUndefinedType()) { return "undefined"; } - return "undefined"; + if (typeAnnotation->IsETSTuple()) { + return GetNameForTupleType(typeAnnotation); + } + return GetNameForLiteralTypeNode(typeAnnotation); } std::string GetNameForETSUnionType(const ir::TypeNode *typeAnnotation) @@ -481,7 +556,7 @@ std::string GetNameForETSUnionType(const ir::TypeNode *typeAnnotation) std::string str = GetNameForTypeNode(type); newstr += str; if (i != typeAnnotation->AsETSUnionType()->Types().size() - 1) { - newstr += "|"; + newstr += " | "; } } return newstr; @@ -1000,13 +1075,7 @@ std::vector CreateDisplayForMethodDefinition(ir::AstNode *nod return displayParts; } -bool IsKindModifierInSet(const std::string &target) -{ - static std::set kindModifierSet = {"const", "static public declare const"}; - return kindModifierSet.find(target) != kindModifierSet.end(); -} - -std::vector CreateDisplayForClassProperty(ir::AstNode *node, const std::string &kindModifier) +std::vector CreateDisplayForClassProperty(ir::AstNode *node) { std::vector displayParts; if (node->Type() != ir::AstNodeType::CLASS_PROPERTY) { @@ -1015,10 +1084,11 @@ std::vector CreateDisplayForClassProperty(ir::AstNode *node, auto classDef = node->Parent(); if (classDef->Type() == ir::AstNodeType::CLASS_DEFINITION) { auto className = classDef->AsClassDefinition()->Ident()->Name(); + auto isConst = (node->Modifiers() & ir::ModifierFlags::CONST) != 0; if (className != "ETSGLOBAL") { displayParts.emplace_back(CreateClassName(std::string(className))); displayParts.emplace_back(CreatePunctuation(".")); - } else if (IsKindModifierInSet(kindModifier)) { + } else if (isConst) { displayParts.emplace_back(CreateKeyword("const")); displayParts.emplace_back(CreateSpace()); } else { @@ -1035,7 +1105,8 @@ std::vector CreateDisplayForClassProperty(ir::AstNode *node, if (typeAnnotation == nullptr) { if (node->AsClassProperty()->Value() == nullptr || !node->AsClassProperty()->Value()->IsETSNewClassInstanceExpression()) { - displayParts.emplace_back(CreateTypeName("undefined")); + displayParts.emplace_back( + CreateTypeName(GetNameForLiteralTypeNode(node->AsClassProperty()->Value(), isConst))); return displayParts; } auto newClassExpr = node->AsClassProperty()->Value()->AsETSNewClassInstanceExpression(); @@ -1124,7 +1195,7 @@ QuickInfo GetQuickInfo(ir::AstNode *node, ir::AstNode *containerNode, ir::AstNod auto enumMember = GetEnumMemberByName(enumDecl, node->AsClassProperty()->Key()->AsIdentifier()->Name()); displayParts = CreateDisplayForEnumMember(enumMember); } else { - displayParts = CreateDisplayForClassProperty(node, kindModifiers); + displayParts = CreateDisplayForClassProperty(node); kind = "property"; } } else if (node->Type() == ir::AstNodeType::TS_INTERFACE_DECLARATION) { diff --git a/ets2panda/test/unit/lsp/get_completions_entry_details.cpp b/ets2panda/test/unit/lsp/get_completions_entry_details.cpp index 1bb64d6bbab1f18d0a4adddb10fb21cf04a8d247..c635af38c8482a3c030f7f6382ebb5f8da81ced0 100644 --- a/ets2panda/test/unit/lsp/get_completions_entry_details.cpp +++ b/ets2panda/test/unit/lsp/get_completions_entry_details.cpp @@ -238,7 +238,7 @@ TEST_F(LSPCompletionsEntryDetailsTests, GetCompletionEntryDetails5) expected.emplace_back("value", "functionParameter"); expected.emplace_back(":", "punctuation"); expected.emplace_back(" ", "space"); - expected.emplace_back("string|number|boolean", "typeParameter"); + expected.emplace_back("string | number | boolean", "typeParameter"); expected.emplace_back(")", "punctuation"); expected.emplace_back(":", "punctuation"); expected.emplace_back(" ", "space"); @@ -250,6 +250,35 @@ TEST_F(LSPCompletionsEntryDetailsTests, GetCompletionEntryDetails5) ASSERT_EQ(completionEntryDetails, expectedCompletionEntryDetails); } +TEST_F(LSPCompletionsEntryDetailsTests, GetCompletionEntryDetails6) +{ + Initializer initializer = Initializer(); + std::string expectedFileName = "completion_entry_details12.ets"; + es2panda_Context *ctx = + initializer.CreateContext(expectedFileName.c_str(), ES2PANDA_STATE_CHECKED, + R"(export const mqw1: [string, number, number] = ['Alice', 30, 10];)"); + size_t const offset = 16; + const char *entryName = "mqw1"; + LSPAPI const *lspApi = GetImpl(); + auto completionEntryDetails = lspApi->getCompletionEntryDetails(entryName, expectedFileName.c_str(), ctx, offset); + std::vector source {}; + std::vector sourceDisplay {}; + std::vector document {}; + const std::string kind = "property"; + const std::string kindModifiers = "static public const export"; + std::vector expected; + expected.emplace_back("const", "keyword"); + expected.emplace_back(" ", "space"); + expected.emplace_back("mqw1", "property"); + expected.emplace_back(":", "punctuation"); + expected.emplace_back(" ", "space"); + expected.emplace_back("[string, number, number]", "typeName"); + auto expectedCompletionEntryDetails = CompletionEntryDetails(entryName, kind, kindModifiers, expected, document, + source, sourceDisplay, expectedFileName); + initializer.DestroyContext(ctx); + ASSERT_EQ(completionEntryDetails, expectedCompletionEntryDetails); +} + TEST_F(LSPCompletionsEntryDetailsTests, CreateDisplayForClass) { Initializer initializer = Initializer(); @@ -329,7 +358,7 @@ TEST_F(LSPCompletionsEntryDetailsTests, CreateDisplayForUnionTypeAlias) expected.emplace_back(" ", "space"); expected.emplace_back("=", "operator"); expected.emplace_back(" ", "space"); - expected.emplace_back("string|number", "typeName"); + expected.emplace_back("string | number", "typeName"); ASSERT_EQ(expected, display); initializer.DestroyContext(ctx); } diff --git a/ets2panda/test/unit/lsp/get_constructor_test.cpp b/ets2panda/test/unit/lsp/get_constructor_test.cpp index 7976967dc034125f1473ce23faaa6be77190c72f..cd98d1ac16e4e7d80b0cd5d5ea45e521e221b977 100644 --- a/ets2panda/test/unit/lsp/get_constructor_test.cpp +++ b/ets2panda/test/unit/lsp/get_constructor_test.cpp @@ -105,7 +105,7 @@ export class Foo extends FooParent { std::string expectedText = "constructor(f: Number, str: String, name: String, x: Number, primaryColor: Colors, isActive: Boolean, " - "items: Array, point: Array, optionalValue: String|null|undefined) {\n super(f, str);\n" + "items: Array, point: Array, optionalValue: String | null | undefined) {\n super(f, str);\n" " this.name = name;\n this.x = x;\n this.primaryColor = primaryColor;\n this.isActive = isActive;\n" " this.items = items;\n this.point = point;\n this.optionalValue = optionalValue;\n}"; size_t const expectedPosition = 269; @@ -245,7 +245,7 @@ class Car { auto res = lspApi->getClassConstructorInfo(ctx, offset, properties); std::string expectedText = - "constructor(name: String, address: Address, tupleProperty: undefined, enumProperty: Color, " + "constructor(name: String, address: Address, tupleProperty: [String, Number], enumProperty: Color, " "onClick: (() => void), engine: Engine) {\n this.name = name;\n this.address = address;\n " "this.tupleProperty = tupleProperty;\n this.enumProperty = enumProperty;\n this.onClick = onClick;\n " "this.engine = engine;\n}"; @@ -485,7 +485,7 @@ class CommonEventRegister { auto res = lspApi->getClassConstructorInfo(ctx, offset, properties); std::string expectedText = - "constructor(subscriber: CommonEventManager.CommonEventSubscriber|null, subscribeInfo: SubscribeInfoType) " + "constructor(subscriber: CommonEventManager.CommonEventSubscriber | null, subscribeInfo: SubscribeInfoType) " "{\n this.subscriber = subscriber;\n this.subscribeInfo = subscribeInfo;\n}"; size_t const expectedPosition = 148; std::vector expectedFileTextChanges = diff --git a/ets2panda/test/unit/lsp/quick_info_test.cpp b/ets2panda/test/unit/lsp/quick_info_test.cpp index f0da63dba8b4c69329d8c2193c8e3eba2ebd7095..75ad5b99213e0c41e11b5fe172b725140ae2e1ce 100644 --- a/ets2panda/test/unit/lsp/quick_info_test.cpp +++ b/ets2panda/test/unit/lsp/quick_info_test.cpp @@ -408,7 +408,7 @@ TEST_F(LspQuickInfoTests, CreateDisplayForUnionTypeAlias) expected.emplace_back(" ", "space"); expected.emplace_back("=", "operator"); expected.emplace_back(" ", "space"); - expected.emplace_back("string|number", "typeName"); + expected.emplace_back("string | number", "typeName"); ASSERT_EQ(expected, display); initializer.DestroyContext(ctx); } @@ -594,7 +594,7 @@ TEST_F(LspQuickInfoTests, CreateDisplayForClassProperty1) { Initializer initializer = Initializer(); es2panda_Context *ctx = - initializer.CreateContext("class-property-test.ets", ES2PANDA_STATE_CHECKED, + initializer.CreateContext("class-property-test2.ets", ES2PANDA_STATE_CHECKED, "class MyClass {\n public myProp: number = 0;\n}\n let obj = new MyClass();"); ASSERT_EQ(ContextState(ctx), ES2PANDA_STATE_CHECKED); auto context = reinterpret_cast(ctx); @@ -604,10 +604,9 @@ TEST_F(LspQuickInfoTests, CreateDisplayForClassProperty1) node->AsClassProperty()->Key()->AsIdentifier()->Name() != "ETSGLOBAL"; }; auto found = ast->FindChild(checkFunc); - std::vector display = ark::es2panda::lsp::CreateDisplayForClassProperty(found, "const"); + std::vector display = ark::es2panda::lsp::CreateDisplayForClassProperty(found); std::vector expected; - - expected.emplace_back("const", "keyword"); + expected.emplace_back("let", "keyword"); expected.emplace_back(" ", "space"); expected.emplace_back("obj", "property"); expected.emplace_back(":", "punctuation"); @@ -631,7 +630,7 @@ TEST_F(LspQuickInfoTests, CreateDisplayForClassProperty2) node->AsClassProperty()->Key()->AsIdentifier()->Name() != "ETSGLOBAL"; }; auto found = ast->FindChild(checkFunc); - std::vector display = ark::es2panda::lsp::CreateDisplayForClassProperty(found, "const"); + std::vector display = ark::es2panda::lsp::CreateDisplayForClassProperty(found); std::vector expected; expected.emplace_back("MyClass", "className");