diff --git a/ets2panda/bindings/native/src/lsp.cpp b/ets2panda/bindings/native/src/lsp.cpp index 286efef5979db72e531f34aaf04047fea3954b6e..d23b1e01cd05595cc05a5df125c3926ae9440e74 100644 --- a/ets2panda/bindings/native/src/lsp.cpp +++ b/ets2panda/bindings/native/src/lsp.cpp @@ -28,7 +28,9 @@ namespace { using ark::es2panda::lsp::ClassHierarchy; using ark::es2panda::lsp::ClassHierarchyInfo; +using ark::es2panda::lsp::ClassHierarchyItem; using ark::es2panda::lsp::ClassMethodItem; +using ark::es2panda::lsp::ClassPropertyItem; } // namespace char *GetStringCopy(KStringPtr &ptr) @@ -722,14 +724,14 @@ KNativePointer impl_getClassNameFromClassHierarchyInfo(KNativePointer info) } TS_INTEROP_1(getClassNameFromClassHierarchyInfo, KNativePointer, KNativePointer) -KNativePointer impl_getMethodListFromClassHierarchyInfo(KNativePointer info) +KNativePointer impl_getMethodItemsFromClassHierarchyInfo(KNativePointer info) { auto *infoPtr = reinterpret_cast(info); if (infoPtr == nullptr) { return nullptr; } std::vector ptrs; - for (const auto &element : infoPtr->GetMethodList()) { + for (const auto &element : infoPtr->GetMethodItemList()) { if (element.second == nullptr) { continue; } @@ -737,17 +739,34 @@ KNativePointer impl_getMethodListFromClassHierarchyInfo(KNativePointer info) } return new std::vector(ptrs); } -TS_INTEROP_1(getMethodListFromClassHierarchyInfo, KNativePointer, KNativePointer) +TS_INTEROP_1(getMethodItemsFromClassHierarchyInfo, KNativePointer, KNativePointer) -KNativePointer impl_getDetailFromClassMethodItem(KNativePointer item) +KNativePointer impl_getPropertyItemsFromClassHierarchyInfo(KNativePointer info) { - auto *itemPtr = reinterpret_cast(item); + auto *infoPtr = reinterpret_cast(info); + if (infoPtr == nullptr) { + return nullptr; + } + std::vector ptrs; + for (const auto &element : infoPtr->GetPropertyItemList()) { + if (element.second == nullptr) { + continue; + } + ptrs.push_back(new ClassPropertyItem(*(element.second))); + } + return new std::vector(ptrs); +} +TS_INTEROP_1(getPropertyItemsFromClassHierarchyInfo, KNativePointer, KNativePointer) + +KNativePointer impl_getDetailFromClassHierarchyItem(KNativePointer item) +{ + auto *itemPtr = reinterpret_cast(item); if (itemPtr == nullptr) { return nullptr; } - return new std::string(itemPtr->GetFunctionDetail()); + return new std::string(itemPtr->GetDetail()); } -TS_INTEROP_1(getDetailFromClassMethodItem, KNativePointer, KNativePointer) +TS_INTEROP_1(getDetailFromClassHierarchyItem, KNativePointer, KNativePointer) KInt impl_getSetterStyleFromClassMethodItem(KNativePointer item) { @@ -759,15 +778,15 @@ KInt impl_getSetterStyleFromClassMethodItem(KNativePointer item) } TS_INTEROP_1(getSetterStyleFromClassMethodItem, KInt, KNativePointer) -KInt impl_getAccessModifierStyleFromClassMethodItem(KNativePointer item) +KInt impl_getAccessModifierStyleFromClassHierarchyItem(KNativePointer item) { - auto *itemPtr = reinterpret_cast(item); + auto *itemPtr = reinterpret_cast(item); if (itemPtr == nullptr) { return 0; } return static_cast(itemPtr->GetAccessModifierStyle()); } -TS_INTEROP_1(getAccessModifierStyleFromClassMethodItem, KInt, KNativePointer) +TS_INTEROP_1(getAccessModifierStyleFromClassHierarchyItem, KInt, KNativePointer) KInt impl_getAliasScriptElementKind(KNativePointer context, KInt position) { diff --git a/ets2panda/bindings/src/Es2pandaNativeModule.ts b/ets2panda/bindings/src/Es2pandaNativeModule.ts index 210ad83e1cdbc4ee92919e027e49b61c61c67017..c8ac45f8a8303b9fb605715d84acafbe1027ef26 100644 --- a/ets2panda/bindings/src/Es2pandaNativeModule.ts +++ b/ets2panda/bindings/src/Es2pandaNativeModule.ts @@ -205,19 +205,23 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } - _getMethodListFromClassHierarchyInfo(ptr: KNativePointer): KPtr { + _getMethodItemsFromClassHierarchyInfo(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getDetailFromClassMethodItem(ptr: KNativePointer): KPtr { + _getPropertyItemsFromClassHierarchyInfo(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getSetterStyleFromClassMethodItem(ptr: KNativePointer): KInt { + _getDetailFromClassHierarchyItem(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getAccessModifierStyleFromClassMethodItem(ptr: KNativePointer): KInt { + _getAccessModifierStyleFromClassHierarchyItem(ptr: KNativePointer): KInt { + throw new Error('Not implemented'); + } + + _getSetterStyleFromClassMethodItem(ptr: KNativePointer): KInt { throw new Error('Not implemented'); } diff --git a/ets2panda/bindings/src/lspNode.ts b/ets2panda/bindings/src/lspNode.ts index 17f846fce21df036fb1b966dca1ac0218aeff665..ebe31ff07c7b57994c3dc8cbbe876e26b176d2e5 100644 --- a/ets2panda/bindings/src/lspNode.ts +++ b/ets2panda/bindings/src/lspNode.ts @@ -23,7 +23,7 @@ import { NativePtrDecoder } from './Platform'; enum HierarchyType { OTHERS, INTERFACE, CLASS }; export enum SetterStyle { - METHOD = 0, + NONE = 0, SETTER, GETTER } @@ -36,6 +36,11 @@ export enum AccessModifierStyle { enum ClassRelationKind { UNKNOWN, INTERFACE, CLASS, FIELD, METHOD, PROPERTY }; +export enum ClassDefinitionStyle { + FIELD = 0, + METHOD +} + export abstract class LspNode { readonly peer: KNativePointer; @@ -228,30 +233,50 @@ export class LspSymbolDisplayPart extends LspNode { readonly kind: String; } -export class LspClassMethodItem extends LspNode { - constructor(peer: KNativePointer) { +export class LspClassHierarchyItem extends LspNode { + constructor(peer: KNativePointer, style: ClassDefinitionStyle) { super(peer); - this.functionDetail = unpackString(global.es2panda._getDetailFromClassMethodItem(this.peer)); + this.style = style; + this.detail = unpackString(global.es2panda._getDetailFromClassHierarchyItem(this.peer)); + this.accessModifier = global.es2panda._getAccessModifierStyleFromClassHierarchyItem(this.peer); + } + readonly detail: string; + readonly accessModifier: AccessModifierStyle; + readonly style: ClassDefinitionStyle; +} + +export class LspClassMethodItem extends LspClassHierarchyItem { + constructor(peer: KNativePointer) { + super(peer, ClassDefinitionStyle.METHOD); this.setter = global.es2panda._getSetterStyleFromClassMethodItem(this.peer); - this.accessModifier = global.es2panda._getAccessModifierStyleFromClassMethodItem(this.peer); } - readonly functionDetail: string; readonly setter: SetterStyle; - readonly accessModifier: AccessModifierStyle; +} + +export class LspClassPropertyItem extends LspClassHierarchyItem { + constructor(peer: KNativePointer) { + super(peer, ClassDefinitionStyle.FIELD); + } } export class LspClassHierarchyInfo extends LspNode { constructor(peer: KNativePointer) { super(peer); this.className = unpackString(global.es2panda._getClassNameFromClassHierarchyInfo(this.peer)); - this.items = new NativePtrDecoder() - .decode(global.es2panda._getMethodListFromClassHierarchyInfo(this.peer)) + this.methodItems = new NativePtrDecoder() + .decode(global.es2panda._getMethodItemsFromClassHierarchyInfo(this.peer)) .map((elPeer: KNativePointer) => { return new LspClassMethodItem(elPeer); }); + this.fieldItems = new NativePtrDecoder() + .decode(global.es2panda._getPropertyItemsFromClassHierarchyInfo(this.peer)) + .map((elPeer: KNativePointer) => { + return new LspClassPropertyItem(elPeer); + }); } readonly className: string; - readonly items: LspClassMethodItem[]; + readonly methodItems: LspClassMethodItem[]; + readonly fieldItems: LspClassPropertyItem[]; } export class LspClassHierarchy extends LspNode { diff --git a/ets2panda/lsp/include/class_hierarchy_info.h b/ets2panda/lsp/include/class_hierarchy_info.h index f7730151ede001b7006a22b2b6df3ba85a471e43..8ff110fa20480d7bbf656dfba7e0b69b06bbe7fe 100644 --- a/ets2panda/lsp/include/class_hierarchy_info.h +++ b/ets2panda/lsp/include/class_hierarchy_info.h @@ -17,9 +17,9 @@ #define ES2PANDA_LSP_CLASS_HIERARCHY_INFO_H #include -#include #include #include +#include "class_hierarchy_item.h" #include "public/es2panda_lib.h" namespace ark::es2panda::lsp { @@ -45,59 +45,6 @@ private: std::string kind_; }; -enum class SetterStyle { METHOD = 0, SETTER, GETTER }; - -enum class AccessModifierStyle { PUBLIC = 0, PROTECTED, PRIVATE }; - -class ClassMethodItem { -public: - ClassMethodItem(std::string detail, SetterStyle setter, AccessModifierStyle access) - : detail_(std::move(detail)), setter_(setter), accessModifier_(access) - { - } - - virtual ~ClassMethodItem() = default; - - ClassMethodItem(const ClassMethodItem &other) = default; - ClassMethodItem(ClassMethodItem &&other) = default; - ClassMethodItem &operator=(const ClassMethodItem &other) = default; - ClassMethodItem &operator=(ClassMethodItem &&other) = default; - - void SetFunctionName(const std::string &functionName) - { - if (functionName.empty()) { - return; - } - funcName_ = functionName; - } - - const std::string &GetFunctionName() const - { - return funcName_; - } - - const std::string &GetFunctionDetail() const - { - return detail_; - } - - SetterStyle GetSetterStyle() const - { - return setter_; - } - - AccessModifierStyle GetAccessModifierStyle() const - { - return accessModifier_; - } - -private: - std::string funcName_; - std::string detail_; - SetterStyle setter_; - AccessModifierStyle accessModifier_; -}; - class ClassHierarchyInfo { public: ClassHierarchyInfo() = default; @@ -122,58 +69,98 @@ public: return className_; } - const std::unordered_map> &GetMethodList() const + const std::unordered_map> &GetMethodItemList() const + { + return methodItems_; + } + + const std::unordered_map> &GetPropertyItemList() const + { + return propertyItems_; + } + + bool AddItemToMethodList(const std::shared_ptr &item) { - return methods_; + if (item == nullptr) { + return false; + } + auto detail = item->GetDetail(); + auto result = methodItems_.try_emplace(detail, item); + return result.second; } - bool AddClassMethodItem(const std::shared_ptr &item) + bool AddItemToPropertyList(const std::shared_ptr &item) { - if (item == nullptr || IsItemExist(item)) { + if (item == nullptr) { return false; } - auto funcDetail = item->GetFunctionDetail(); - methods_[funcDetail] = item; - return true; + auto detail = item->GetDetail(); + auto result = propertyItems_.try_emplace(detail, item); + return result.second; } - void DeleteClassMethodItem(const std::shared_ptr &item) + void DeleteTargetItemInMethodList(const std::shared_ptr &item) { if (item == nullptr) { return; } - auto funcDetail = item->GetFunctionDetail(); - methods_.erase(funcDetail); + auto detail = item->GetDetail(); + methodItems_.erase(detail); } - void DeleteAllClassMethodItem() + void DeleteTargetItemInPropertyList(const std::shared_ptr &item) { - methods_.clear(); + if (item == nullptr) { + return; + } + auto detail = item->GetDetail(); + propertyItems_.erase(detail); } - bool IsItemExist(const std::shared_ptr &item) const + void DeleteAllItemsInMethodList() + { + methodItems_.clear(); + } + + void DeleteAllItemsInPropertyList() + { + propertyItems_.clear(); + } + + bool IsItemExistInMethodList(const std::shared_ptr &item) const { if (item == nullptr) { return false; } - auto func = item->GetFunctionDetail(); - auto iter = methods_.find(func); - return iter != methods_.end(); + auto detail = item->GetDetail(); + auto iter = methodItems_.find(detail); + return iter != methodItems_.end(); + } + + bool IsItemExistInPropertyList(const std::shared_ptr &item) const + { + if (item == nullptr) { + return false; + } + auto detail = item->GetDetail(); + auto iter = propertyItems_.find(detail); + return iter != propertyItems_.end(); } private: std::string className_; - std::unordered_map> methods_; + std::unordered_map> methodItems_; + std::unordered_map> propertyItems_; }; using ClassHierarchy = std::vector; /** - * Retrieve the list of undefined virtual functions in the parent class. + * Retrieve the list of undefined virtual functions and properties in the parent class. * * such as ets: * class Animal { - * private body_: string = ''; + * public body_: string = ''; * * public action(): void { * console.log("need Animal action"); @@ -192,9 +179,9 @@ using ClassHierarchy = std::vector; * console.log("need Bird Drink"); * } * } - * - * when clicking 'Bird'. - * ClassHierarchy is [ { "Animal", { detail: sleep(), SetterStyle: METHOD, AccessModifierStyle: PUBLIC } } ]. + * when clicking 'Bird'. ClassHierarchy is : + * [ { "Animal", { detail: body_: string, ClassDefinitionStyle FIELD, AccessModifierStyle: PUBLIC } }, + * { "Animal", { detail: sleep(): void, ClassDefinitionStyle: METHOD, AccessModifierStyle: PUBLIC } } ]. */ ClassHierarchy GetClassHierarchyInfoImpl(es2panda_Context *context, size_t position); } // namespace ark::es2panda::lsp diff --git a/ets2panda/lsp/include/class_hierarchy_item.h b/ets2panda/lsp/include/class_hierarchy_item.h new file mode 100644 index 0000000000000000000000000000000000000000..a2d6789aa7dc9c0d06598621f11670afca406c50 --- /dev/null +++ b/ets2panda/lsp/include/class_hierarchy_item.h @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ES2PANDA_LSP_CLASS_HIERARCHY_ITEM_H +#define ES2PANDA_LSP_CLASS_HIERARCHY_ITEM_H + +#include + +namespace ark::es2panda::lsp { +enum class SetterStyle { NONE = 0, SETTER, GETTER }; + +enum class AccessModifierStyle { PUBLIC = 0, PROTECTED, PRIVATE }; + +enum class ClassDefinitionStyle { FIELD = 0, METHOD }; + +class ClassHierarchyItem { +public: + ClassHierarchyItem(AccessModifierStyle access, std::string detail) + : accessModifier_(access), detail_(std::move(detail)) + { + } + + virtual ~ClassHierarchyItem() = default; + + ClassHierarchyItem(const ClassHierarchyItem &other) = default; + ClassHierarchyItem(ClassHierarchyItem &&other) = default; + ClassHierarchyItem &operator=(const ClassHierarchyItem &other) = default; + ClassHierarchyItem &operator=(ClassHierarchyItem &&other) = default; + + virtual ClassDefinitionStyle GetClassDefinitionStyle() const = 0; + + AccessModifierStyle GetAccessModifierStyle() const + { + return accessModifier_; + } + + const std::string &GetDetail() const + { + return detail_; + } + +private: + AccessModifierStyle accessModifier_; + std::string detail_; +}; + +class ClassPropertyItem : public ClassHierarchyItem { +public: + ClassPropertyItem(AccessModifierStyle access, std::string detail) : ClassHierarchyItem(access, std::move(detail)) {} + + ~ClassPropertyItem() override = default; + + ClassPropertyItem(const ClassPropertyItem &other) = default; + ClassPropertyItem(ClassPropertyItem &&other) = default; + ClassPropertyItem &operator=(const ClassPropertyItem &other) = default; + ClassPropertyItem &operator=(ClassPropertyItem &&other) = default; + + ClassDefinitionStyle GetClassDefinitionStyle() const override + { + return ClassDefinitionStyle::FIELD; + } + + void SetVariableName(const std::string &variableName) + { + if (variableName.empty()) { + return; + } + variableName_ = variableName; + } + + const std::string &GetVariableName() const + { + return variableName_; + } + +private: + std::string variableName_; +}; + +class ClassMethodItem : public ClassHierarchyItem { +public: + ClassMethodItem(AccessModifierStyle access, std::string detail, SetterStyle setter) + : ClassHierarchyItem(access, std::move(detail)), setter_(setter) + { + } + + ~ClassMethodItem() override = default; + + ClassMethodItem(const ClassMethodItem &other) = default; + ClassMethodItem(ClassMethodItem &&other) = default; + ClassMethodItem &operator=(const ClassMethodItem &other) = default; + ClassMethodItem &operator=(ClassMethodItem &&other) = default; + + ClassDefinitionStyle GetClassDefinitionStyle() const override + { + return ClassDefinitionStyle::METHOD; + } + + void SetFunctionName(const std::string &functionName) + { + if (functionName.empty()) { + return; + } + funcName_ = functionName; + } + + const std::string &GetFunctionName() const + { + return funcName_; + } + + SetterStyle GetSetterStyle() const + { + return setter_; + } + +private: + std::string funcName_; + SetterStyle setter_; +}; +} // namespace ark::es2panda::lsp + +#endif diff --git a/ets2panda/lsp/include/internal_api.h b/ets2panda/lsp/include/internal_api.h index 9d1f8ff9b669def2e1c67fc8f40f65fad351df29..428dbbf8060327b1a14cfc264d279a228a2ff6bc 100644 --- a/ets2panda/lsp/include/internal_api.h +++ b/ets2panda/lsp/include/internal_api.h @@ -93,7 +93,7 @@ std::vector GetCodeFixesAtPositionImpl(es2panda_Context *cont CodeFixOptions &codeFixOptions); CombinedCodeActionsInfo GetCombinedCodeFixImpl(es2panda_Context *context, const std::string &fixId, CodeFixOptions &codeFixOptions); - +ir::Identifier *GetIdentFromNewClassExprPart(const ir::Expression *value); } // namespace ark::es2panda::lsp #endif \ No newline at end of file diff --git a/ets2panda/lsp/src/class_hierarchy_info.cpp b/ets2panda/lsp/src/class_hierarchy_info.cpp index 6534cd30d9335201bfa4f3a73712978181aae7ed..94aa7c9c7aac1905b049b7f48e5abd194bf4850b 100644 --- a/ets2panda/lsp/src/class_hierarchy_info.cpp +++ b/ets2panda/lsp/src/class_hierarchy_info.cpp @@ -17,6 +17,7 @@ #include "internal_api.h" #include "public/public.h" #include "compiler/lowering/util.h" +#include "quick_info.h" namespace ark::es2panda::lsp { std::string GetNameFromIdentifierNode(const ir::AstNode *node) @@ -24,7 +25,7 @@ std::string GetNameFromIdentifierNode(const ir::AstNode *node) if (node == nullptr || !node->IsIdentifier()) { return ""; } - return std::string(node->AsIdentifier()->Name()); + return node->AsIdentifier()->ToString(); } // Currently only considering enum scenarios. @@ -77,9 +78,17 @@ std::string SpliceFunctionDetailStr(const std::string &functionName, const std:: return result; } +std::string SplicePropertyDetailStr(const std::string &name, const std::string &type) +{ + if (name.empty() || type.empty()) { + return ""; + } + return name + ": " + type; +} + std::string GetFunctionNameFromScriptFunction(const ir::ScriptFunction *function) { - if (function == nullptr) { + if (function == nullptr || function->Id() == nullptr) { return ""; } return function->Id()->ToString(); @@ -92,21 +101,23 @@ std::vector GetParamListFromScriptFunction(const ir::ScriptF return params; } auto nodeParams = function->Params(); - for (const auto &nodeParam : nodeParams) { + for (const auto *it : nodeParams) { + if (it == nullptr || !it->IsETSParameterExpression()) { + continue; + } std::string paramName; std::string paramKind; - if (!nodeParam->IsETSParameterExpression()) { + auto nodeParam = it->AsETSParameterExpression(); + if (nodeParam->IsRestParameter()) { + paramName = nodeParam->RestParameter()->ToString(); + } else { + paramName = GetNameFromIdentifierNode(nodeParam->Ident()); + } + if (paramName == INVALID_EXPRESSION || nodeParam->TypeAnnotation() == nullptr) { continue; } - paramName = std::string(nodeParam->AsETSParameterExpression()->Name()); - nodeParam->AsETSParameterExpression()->FindChild([¶mKind](ir::AstNode *childNode) { - if (childNode->IsETSTypeReference()) { - paramKind = childNode->AsETSTypeReference()->Part()->Name()->ToString(); - } - return false; - }); - FunctionParamStyle tmp(paramName, paramKind); - params.emplace_back(std::move(tmp)); + paramKind = GetNameForTypeNode(nodeParam->TypeAnnotation()); + params.emplace_back(FunctionParamStyle(std::move(paramName), std::move(paramKind))); } return params; } @@ -116,20 +127,17 @@ std::string GetReturnTypeFromScriptFunction(const ir::ScriptFunction *function) if (function == nullptr) { return ""; } - auto nodeReturn = function->ReturnTypeAnnotation(); - if (nodeReturn == nullptr || !nodeReturn->IsETSTypeReference()) { + auto returnNode = function->ReturnTypeAnnotation(); + if (returnNode == nullptr) { return ""; } - auto ident = nodeReturn->AsETSTypeReference()->Part()->Name(); - if (ident == nullptr || !ident->IsIdentifier()) { - return ""; - } - return std::string(ident->AsIdentifier()->Name()); + auto returnType = GetNameForTypeNode(returnNode); + return returnType; } SetterStyle CreateSetterStyle(ir::MethodDefinitionKind kind) { - SetterStyle setter = SetterStyle::METHOD; + SetterStyle setter = SetterStyle::NONE; switch (kind) { case ir::MethodDefinitionKind::GET: case ir::MethodDefinitionKind::EXTENSION_GET: @@ -151,17 +159,31 @@ std::shared_ptr CreateClassMethodItem(const ir::MethodDefinitio if (methodDefinition == nullptr || funcName.empty() || detail.empty()) { return nullptr; } - auto setter = CreateSetterStyle(methodDefinition->Kind()); AccessModifierStyle access = AccessModifierStyle::PUBLIC; if (methodDefinition->IsProtected()) { access = AccessModifierStyle::PROTECTED; } - auto item = std::make_shared(std::move(detail), setter, access); + auto item = std::make_shared(access, std::move(detail), setter); item->SetFunctionName(funcName); return item; } +std::shared_ptr CreateClassPropertyItem(const ir::ClassProperty *property, + const std::string &propertyName, std::string detail) +{ + if (property == nullptr || propertyName.empty() || detail.empty()) { + return nullptr; + } + AccessModifierStyle access = AccessModifierStyle::PUBLIC; + if (property->IsProtected()) { + access = AccessModifierStyle::PROTECTED; + } + auto item = std::make_shared(access, std::move(detail)); + item->SetVariableName(propertyName); + return item; +} + std::shared_ptr ParseFunctionStyleWithCreateItem(const ir::MethodDefinition *methodDefinition, bool isCurrentToken) { @@ -184,6 +206,48 @@ std::shared_ptr ParseFunctionStyleWithCreateItem(const ir::Meth return CreateClassMethodItem(methodDefinition, functionName, functionDetail); } +ir::Identifier *GetIdentFromNewClassExprPart(const ir::Expression *value) +{ + if (value == nullptr || !value->IsETSNewClassInstanceExpression()) { + return nullptr; + } + auto typeRef = value->AsETSNewClassInstanceExpression()->GetTypeRef(); + if (typeRef == nullptr || !typeRef->IsETSTypeReference()) { + return nullptr; + } + auto part = typeRef->AsETSTypeReference()->Part(); + if (part == nullptr) { + return nullptr; + } + return part->GetIdent(); +} + +std::shared_ptr ParsePropertyStyleWithCreateItem(const ir::ClassProperty *property, + bool isCurrentToken) +{ + if (property == nullptr) { + return nullptr; + } + if ((isCurrentToken && property->IsStatic()) || + (!isCurrentToken && (property->IsPrivate() || property->IsStatic()))) { + return nullptr; + } + std::string propertyName = GetNameFromIdentifierNode(property->Key()); + std::string type; + if (property->TypeAnnotation() == nullptr) { + auto value = property->Value(); + auto ident = GetIdentFromNewClassExprPart(value); + type = GetNameFromIdentifierNode(ident); + } else { + type = GetNameForTypeNode(property->TypeAnnotation()); + } + if (propertyName == INVALID_EXPRESSION || type == INVALID_EXPRESSION) { + return nullptr; + } + auto detail = SplicePropertyDetailStr(propertyName, type); + return CreateClassPropertyItem(property, propertyName, detail); +} + ClassHierarchyInfo CreateClassHierarchyInfoFromBody(const ir::ClassDefinition *classDefinition, const std::string &className, bool isCurrentToken) { @@ -194,23 +258,28 @@ ClassHierarchyInfo CreateClassHierarchyInfoFromBody(const ir::ClassDefinition *c result.SetClassName(className); auto bodyNodes = classDefinition->Body(); for (const auto &node : bodyNodes) { - if (node == nullptr || !node->IsMethodDefinition()) { - continue; - } - auto methodDefinition = node->AsMethodDefinition(); - if (methodDefinition == nullptr) { + if (node == nullptr) { continue; } - auto item = ParseFunctionStyleWithCreateItem(methodDefinition, isCurrentToken); - if (item != nullptr) { - result.AddClassMethodItem(item); - } - auto overLoads = methodDefinition->Overloads(); - for (const auto *overLoadMethodDefinition : overLoads) { - auto overLoadItem = ParseFunctionStyleWithCreateItem(overLoadMethodDefinition, isCurrentToken); - if (overLoadItem != nullptr) { - result.AddClassMethodItem(overLoadItem); + if (node->IsMethodDefinition()) { + auto methodDefinition = node->AsMethodDefinition(); + if (methodDefinition == nullptr) { + continue; + } + auto item = ParseFunctionStyleWithCreateItem(methodDefinition, isCurrentToken); + result.AddItemToMethodList(item); + auto overLoads = methodDefinition->Overloads(); + for (const auto *overLoadMethodDefinition : overLoads) { + auto overLoadItem = ParseFunctionStyleWithCreateItem(overLoadMethodDefinition, isCurrentToken); + result.AddItemToMethodList(overLoadItem); + } + } else if (node->IsClassProperty()) { + auto property = node->AsClassProperty(); + if (property == nullptr) { + continue; } + auto item = ParsePropertyStyleWithCreateItem(property, isCurrentToken); + result.AddItemToPropertyList(item); } } return result; @@ -230,13 +299,29 @@ ir::AstNode *GetSuperClassNode(const ir::ClassDefinition *classDefinition) void ComputeClassHierarchyInfo(const ClassHierarchyInfo &deriveInfo, ClassHierarchyInfo &superInfo) { - auto deriveMethods = deriveInfo.GetMethodList(); + auto deriveMethods = deriveInfo.GetMethodItemList(); for (const auto &method : deriveMethods) { - superInfo.DeleteClassMethodItem(method.second); + superInfo.DeleteTargetItemInMethodList(method.second); + } + auto deriveProperties = deriveInfo.GetPropertyItemList(); + for (const auto &property : deriveProperties) { + superInfo.DeleteTargetItemInPropertyList(property.second); + } +} + +void FillBaseClassHierarchyInfo(const ClassHierarchyInfo &extraInfo, ClassHierarchyInfo &baseInfo) +{ + auto extraMethods = extraInfo.GetMethodItemList(); + for (const auto &method : extraMethods) { + baseInfo.AddItemToMethodList(method.second); + } + auto extraProperties = extraInfo.GetPropertyItemList(); + for (const auto &property : extraProperties) { + baseInfo.AddItemToPropertyList(property.second); } } -void ProcessClassHierarchy(const ir::AstNode *token, const ClassHierarchyInfo &baseInfo, ClassHierarchy &result) +void ProcessClassHierarchy(const ir::AstNode *token, ClassHierarchyInfo &baseInfo, ClassHierarchy &result) { if (token == nullptr || !token->IsIdentifier()) { return; @@ -250,8 +335,10 @@ void ProcessClassHierarchy(const ir::AstNode *token, const ClassHierarchyInfo &b if (!className.empty()) { // Calculate the difference between the obtained parent class info and the current clicked node class info. ComputeClassHierarchyInfo(baseInfo, info); - if (info.GetClassName() == className && !info.GetMethodList().empty()) { + if (info.GetClassName() == className && + (!info.GetMethodItemList().empty() || !info.GetPropertyItemList().empty())) { result.emplace_back(info); + FillBaseClassHierarchyInfo(info, baseInfo); } } auto superClass = GetSuperClassNode(classDefinition); diff --git a/ets2panda/lsp/src/completions.cpp b/ets2panda/lsp/src/completions.cpp index cc8bda93246fb41cffd852d5ce797da567f5306e..8b1961ff2f652bc15af599ad21d3e00b04e04ef3 100644 --- a/ets2panda/lsp/src/completions.cpp +++ b/ets2panda/lsp/src/completions.cpp @@ -303,17 +303,9 @@ ir::AstNode *GetClassDefinitionFromClassProperty(ir::AstNode *node) return nullptr; } auto value = node->AsClassProperty()->Value(); - if (value != nullptr && value->IsETSNewClassInstanceExpression() && - value->AsETSNewClassInstanceExpression()->GetTypeRef() != nullptr && - value->AsETSNewClassInstanceExpression()->GetTypeRef()->IsETSTypeReference()) { - auto typeReferencePart = value->AsETSNewClassInstanceExpression()->GetTypeRef()->AsETSTypeReference()->Part(); - if (typeReferencePart == nullptr) { - return nullptr; - } - auto id = typeReferencePart->Name(); - if (id != nullptr && id->IsIdentifier()) { - return compiler::DeclarationFromIdentifier(id->AsIdentifier()); - } + auto ident = GetIdentFromNewClassExprPart(value); + if (ident != nullptr) { + return compiler::DeclarationFromIdentifier(ident); } auto type = node->AsClassProperty()->TypeAnnotation(); if (type != nullptr && type->IsETSTypeReference()) { diff --git a/ets2panda/test/unit/lsp/class_hierarchy_info_test.cpp b/ets2panda/test/unit/lsp/class_hierarchy_info_test.cpp index 6d2466a99b55152b430fea42e2e18c50e2d20183..231e38ae5658c12547f273c480e2ca334ff58cbd 100644 --- a/ets2panda/test/unit/lsp/class_hierarchy_info_test.cpp +++ b/ets2panda/test/unit/lsp/class_hierarchy_info_test.cpp @@ -22,9 +22,9 @@ using ark::es2panda::lsp::Initializer; namespace { -class LspScriptElementKindTests : public LSPAPITests {}; +class LspGetClassHierarchyInfoTests : public LSPAPITests {}; -TEST_F(LSPAPITests, GetClassHierarchyInfo_1) +TEST_F(LspGetClassHierarchyInfoTests, GetClassHierarchyInfo_1) { LSPAPI const *lspApi = GetImpl(); ASSERT_TRUE(lspApi != nullptr); @@ -56,21 +56,21 @@ private privateMethod(): void { ASSERT_EQ(classHierarchy.size(), 1); ASSERT_EQ(classHierarchy[0].GetClassName(), "Parent"); - auto methods = classHierarchy[0].GetMethodList(); - auto it = methods.find("publicMethod()"); + auto methods = classHierarchy[0].GetMethodItemList(); + auto it = methods.find("publicMethod(): void"); ASSERT_TRUE(it != methods.end()); ASSERT_TRUE(it->second != nullptr); - ASSERT_EQ(it->second->GetSetterStyle(), ark::es2panda::lsp::SetterStyle::METHOD); + ASSERT_EQ(it->second->GetSetterStyle(), ark::es2panda::lsp::SetterStyle::NONE); ASSERT_EQ(it->second->GetAccessModifierStyle(), ark::es2panda::lsp::AccessModifierStyle::PUBLIC); it = methods.find("action(fileName: string, position: number): number"); ASSERT_TRUE(it != methods.end()); ASSERT_TRUE(it->second != nullptr); - ASSERT_EQ(it->second->GetSetterStyle(), ark::es2panda::lsp::SetterStyle::METHOD); + ASSERT_EQ(it->second->GetSetterStyle(), ark::es2panda::lsp::SetterStyle::NONE); ASSERT_EQ(it->second->GetAccessModifierStyle(), ark::es2panda::lsp::AccessModifierStyle::PROTECTED); initializer.DestroyContext(context); } -TEST_F(LSPAPITests, GetClassHierarchyInfo_2) +TEST_F(LspGetClassHierarchyInfoTests, GetClassHierarchyInfo_2) { LSPAPI const *lspApi = GetImpl(); ASSERT_TRUE(lspApi != nullptr); @@ -110,16 +110,16 @@ class Magpie extends Bird { ASSERT_EQ(classHierarchy.size(), 1); ASSERT_EQ(classHierarchy[0].GetClassName(), "Animal"); - auto methods = classHierarchy[0].GetMethodList(); - auto it = methods.find("sleep()"); + auto methods = classHierarchy[0].GetMethodItemList(); + auto it = methods.find("sleep(): void"); ASSERT_TRUE(it != methods.end()); ASSERT_TRUE(it->second != nullptr); - ASSERT_EQ(it->second->GetSetterStyle(), ark::es2panda::lsp::SetterStyle::METHOD); + ASSERT_EQ(it->second->GetSetterStyle(), ark::es2panda::lsp::SetterStyle::NONE); ASSERT_EQ(it->second->GetAccessModifierStyle(), ark::es2panda::lsp::AccessModifierStyle::PROTECTED); initializer.DestroyContext(context); } -TEST_F(LSPAPITests, GetClassHierarchyInfo_3) +TEST_F(LspGetClassHierarchyInfoTests, GetClassHierarchyInfo_3) { LSPAPI const *lspApi = GetImpl(); ASSERT_TRUE(lspApi != nullptr); @@ -143,7 +143,7 @@ TEST_F(LSPAPITests, GetClassHierarchyInfo_3) initializer.DestroyContext(context); } -TEST_F(LSPAPITests, GetClassHierarchyInfo_4) +TEST_F(LspGetClassHierarchyInfoTests, GetClassHierarchyInfo_4) { LSPAPI const *lspApi = GetImpl(); ASSERT_TRUE(lspApi != nullptr); @@ -177,7 +177,7 @@ TEST_F(LSPAPITests, GetClassHierarchyInfo_4) ASSERT_EQ(classHierarchy.size(), 1); ASSERT_EQ(classHierarchy[0].GetClassName(), "ii"); - auto methods = classHierarchy[0].GetMethodList(); + auto methods = classHierarchy[0].GetMethodItemList(); auto it = methods.find("Body(): string"); ASSERT_TRUE(it != methods.end()); ASSERT_TRUE(it->second != nullptr); @@ -190,4 +190,177 @@ TEST_F(LSPAPITests, GetClassHierarchyInfo_4) ASSERT_EQ(it->second->GetAccessModifierStyle(), ark::es2panda::lsp::AccessModifierStyle::PUBLIC); initializer.DestroyContext(context); } + +TEST_F(LspGetClassHierarchyInfoTests, GetClassHierarchyInfo_5) +{ + LSPAPI const *lspApi = GetImpl(); + ASSERT_TRUE(lspApi != nullptr); + const std::string text = R"(class C { + func1(): void {} + func2(): string { + return "1"; + } + func3(): number { + return 1; + } + func4(): boolean { + return false; + } + func5(): Array { + return [1, 2]; + } +} + +class B extends C { + method1(): void {} + method2(parameter1: string, callBack: () => void): void {} +} + +class A extends B {/*1*/};)"; + + auto pos = text.find("/*1*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto context = initializer.CreateContext("class_hierarchy_info_5.ets", ES2PANDA_STATE_CHECKED, text.c_str()); + auto classHierarchy = lspApi->getClassHierarchyInfo(context, pos); + size_t expectInfoListSize = 2; + ASSERT_EQ(classHierarchy.size(), expectInfoListSize); + ASSERT_EQ(classHierarchy[0].GetClassName(), "B"); + ASSERT_EQ(classHierarchy[1].GetClassName(), "C"); + auto classBItems = classHierarchy[0].GetMethodItemList(); + ASSERT_TRUE(classBItems.find("method1(): void") != classBItems.end()); + ASSERT_TRUE(classBItems.find("method2(parameter1: string, callBack: (() => void)): void") != classBItems.end()); + auto classCItems = classHierarchy[1].GetMethodItemList(); + ASSERT_TRUE(classCItems.find("func1(): void") != classCItems.end()); + ASSERT_TRUE(classCItems.find("func2(): string") != classCItems.end()); + ASSERT_TRUE(classCItems.find("func3(): number") != classCItems.end()); + ASSERT_TRUE(classCItems.find("func4(): boolean") != classCItems.end()); + ASSERT_TRUE(classCItems.find("func5(): Array") != classCItems.end()); + initializer.DestroyContext(context); +} + +TEST_F(LspGetClassHierarchyInfoTests, GetClassHierarchyInfo_6) +{ + LSPAPI const *lspApi = GetImpl(); + ASSERT_TRUE(lspApi != nullptr); + const std::string text = R"(type parameter = number; + +class B { + public method1(parameter1: number): parameter { + return 1; + } + method2(parameter1: number): number { + return parameter1 as number; + } + async method3(parameter1: string): Promise { + return '1'; + } +} + +class A extends B {/*1*/};)"; + + auto pos = text.find("/*1*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto context = initializer.CreateContext("class_hierarchy_info_6.ets", ES2PANDA_STATE_CHECKED, text.c_str()); + auto classHierarchy = lspApi->getClassHierarchyInfo(context, pos); + ASSERT_FALSE(classHierarchy.empty()); + ASSERT_EQ(classHierarchy[0].GetClassName(), "B"); + auto classBItems = classHierarchy[0].GetMethodItemList(); + ASSERT_TRUE(classBItems.find("method1(parameter1: number): parameter") != classBItems.end()); + ASSERT_TRUE(classBItems.find("method2(parameter1: number): number") != classBItems.end()); + ASSERT_TRUE(classBItems.find("method3(parameter1: string): Promise") != classBItems.end()); + initializer.DestroyContext(context); +} + +TEST_F(LspGetClassHierarchyInfoTests, GetClassHierarchyInfo_7) +{ + LSPAPI const *lspApi = GetImpl(); + ASSERT_TRUE(lspApi != nullptr); + const std::string text = R"(class C { + func1(): void {} + + func2(): boolean { + return false; + } + + func3(): Array { + return [1, 2]; + } +}; + +class B extends C { + method1() {} + func2(): boolean { + return false; + } +} + +class A extends B { + method1() {} + func3(): Array { + return [1, 2]; + }/*1*/ +})"; + + auto pos = text.find("/*1*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto context = initializer.CreateContext("class_hierarchy_info_7.ets", ES2PANDA_STATE_CHECKED, text.c_str()); + auto classHierarchy = lspApi->getClassHierarchyInfo(context, pos); + size_t expectInfoListSize = 2; + ASSERT_EQ(classHierarchy.size(), expectInfoListSize); + ASSERT_EQ(classHierarchy[0].GetClassName(), "B"); + ASSERT_EQ(classHierarchy[1].GetClassName(), "C"); + auto classBItems = classHierarchy[0].GetMethodItemList(); + ASSERT_EQ(classBItems.size(), 1); + ASSERT_TRUE(classBItems.find("func2(): boolean") != classBItems.end()); + auto classCItems = classHierarchy[1].GetMethodItemList(); + ASSERT_EQ(classCItems.size(), 1); + ASSERT_TRUE(classCItems.find("func1(): void") != classCItems.end()); + initializer.DestroyContext(context); +} + +TEST_F(LspGetClassHierarchyInfoTests, GetClassHierarchyInfo_8) +{ + LSPAPI const *lspApi = GetImpl(); + ASSERT_TRUE(lspApi != nullptr); + const std::string text = R"(class Parent { + public property1: number = 1; + protected property2: string = '1'; + private property3: boolean = true; + readonly property4: number = 1; + static property5: number = 1; +} + +class Son extends Parent { + property1: number = 2; + ChildExtraProperty1: number = 2; + ChildExtraProperty2: number = 2; +} + +class GrandSon extends Son {/*1*/ + public property2: string = '2'; + ChildExtraProperty1: number = 3; +})"; + + auto pos = text.find("/*1*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto context = initializer.CreateContext("class_hierarchy_info_8.ets", ES2PANDA_STATE_CHECKED, text.c_str()); + auto classHierarchy = lspApi->getClassHierarchyInfo(context, pos); + size_t expectInfoListSize = 2; + ASSERT_EQ(classHierarchy.size(), expectInfoListSize); + ASSERT_EQ(classHierarchy[0].GetClassName(), "Son"); + ASSERT_EQ(classHierarchy[1].GetClassName(), "Parent"); + auto sonItems = classHierarchy[0].GetPropertyItemList(); + size_t expectPropertyListSize = 2; + ASSERT_EQ(sonItems.size(), expectPropertyListSize); + ASSERT_TRUE(sonItems.find("property1: number") != sonItems.end()); + ASSERT_TRUE(sonItems.find("ChildExtraProperty2: number") != sonItems.end()); + auto parentItems = classHierarchy[1].GetPropertyItemList(); + ASSERT_EQ(parentItems.size(), 1); + ASSERT_TRUE(parentItems.find("property4: number") != parentItems.end()); + initializer.DestroyContext(context); +} } // namespace