From 1dbf96a10ff96b6976a8c7ba31d4434a263bbafa Mon Sep 17 00:00:00 2001 From: yanpeng51 Date: Sat, 16 Aug 2025 11:10:05 +0800 Subject: [PATCH] "lsp import" Signed-off-by: yanpeng51 --- ets2panda/bindings/native/src/lsp.cpp | 8 + .../src/common/Es2pandaNativeModule.ts | 3 + ets2panda/bindings/src/common/types.ts | 3 +- ets2panda/bindings/src/lsp/lsp_helper.ts | 16 +- ets2panda/lsp/include/api.h | 1 + ets2panda/lsp/include/get_node.h | 1 + ets2panda/lsp/src/api.cpp | 5 + ets2panda/lsp/src/get_node.cpp | 11 ++ ets2panda/test/unit/lsp/CMakeLists.txt | 4 + .../lsp/get_node_ts_qualified_name_test.cpp | 165 ++++++++++++++++++ 10 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 ets2panda/test/unit/lsp/get_node_ts_qualified_name_test.cpp diff --git a/ets2panda/bindings/native/src/lsp.cpp b/ets2panda/bindings/native/src/lsp.cpp index fc95986732..7db59f07d7 100644 --- a/ets2panda/bindings/native/src/lsp.cpp +++ b/ets2panda/bindings/native/src/lsp.cpp @@ -1923,3 +1923,11 @@ KNativePointer impl_getColAndLineByOffset(KStringPtr &sourceCodePtr, KInt offset return new std::pair(impl->getColAndLineByOffset(sourceCodePtr.Data(), offset)); } TS_INTEROP_2(getColAndLineByOffset, KNativePointer, KStringPtr, KInt) + +KNativePointer impl_getTsQualifiedName(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getTsQualifiedName(ast, nodeNamePtr.Data()); +} +TS_INTEROP_2(getTsQualifiedName, KNativePointer, KNativePointer, KStringPtr) diff --git a/ets2panda/bindings/src/common/Es2pandaNativeModule.ts b/ets2panda/bindings/src/common/Es2pandaNativeModule.ts index d6f4b53f4f..7cf646d133 100644 --- a/ets2panda/bindings/src/common/Es2pandaNativeModule.ts +++ b/ets2panda/bindings/src/common/Es2pandaNativeModule.ts @@ -1041,6 +1041,9 @@ export class Es2pandaNativeModule { _getIdentifier(astNode: KPtr, nodeName: String): KPtr { throw new Error('Not implemented'); } + _getTsQualifiedName(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } } export function initEs2panda(): Es2pandaNativeModule { diff --git a/ets2panda/bindings/src/common/types.ts b/ets2panda/bindings/src/common/types.ts index 501904a65a..5ed347c45c 100644 --- a/ets2panda/bindings/src/common/types.ts +++ b/ets2panda/bindings/src/common/types.ts @@ -235,7 +235,8 @@ export interface TextDocumentChangeInfo { export enum AstNodeType { UNKNOWN, IDENTIFIER, - CLASS_DEFINITION + CLASS_DEFINITION, + TS_QUALIFIED_NAME, } export interface NodeInfo { name: string; diff --git a/ets2panda/bindings/src/lsp/lsp_helper.ts b/ets2panda/bindings/src/lsp/lsp_helper.ts index 5e6c3fba1b..dce651bc1a 100644 --- a/ets2panda/bindings/src/lsp/lsp_helper.ts +++ b/ets2panda/bindings/src/lsp/lsp_helper.ts @@ -110,6 +110,7 @@ export class Lsp { private pathConfig: PathConfig; private lspDriverHelper = new LspDriverHelper(); private declFileMap: Record = {}; // Map + private typeAliasMap: Partial KPointer>> = {}; constructor(pathConfig: PathConfig, getContentCallback?: (filePath: string) => string, modules?: ModuleDescriptor[]) { initBuildEnv(); @@ -131,6 +132,7 @@ export class Lsp { this.defaultBuildConfig = Object.values(this.buildConfigs)[0]; PluginDriver.getInstance().initPlugins(this.defaultBuildConfig); this.initDeclFile(); + this.initTypeAliasMap(); } // Partially update for new file @@ -240,6 +242,11 @@ export class Lsp { this.generateDeclFile(filePath); } } + private initTypeAliasMap(): void { + this.typeAliasMap = { + [AstNodeType.TS_QUALIFIED_NAME]: global.es2panda._getTsQualifiedName, + }; + } updateDeclFile(filePath: string): void { if (!Object.prototype.hasOwnProperty.call(this.moduleInfos, filePath)) { @@ -296,10 +303,11 @@ export class Lsp { try { nodeInfos.forEach((nodeInfo) => { currentNodeName = nodeInfo.name; - if (nodeInfo.kind === AstNodeType.CLASS_DEFINITION) { - astNode = global.es2panda._getClassDefinition(astNode, currentNodeName); - } else if (nodeInfo.kind === AstNodeType.IDENTIFIER) { - astNode = global.es2panda._getIdentifier(astNode, currentNodeName); + const handler = this.typeAliasMap[nodeInfo.kind]; + if (handler) { + astNode = handler(astNode, currentNodeName); + } else { + console.warn(`No handler found for node type: ${nodeInfo.kind}`); } }); } finally { diff --git a/ets2panda/lsp/include/api.h b/ets2panda/lsp/include/api.h index 8749f10b70..7ef4db84c5 100644 --- a/ets2panda/lsp/include/api.h +++ b/ets2panda/lsp/include/api.h @@ -567,6 +567,7 @@ typedef struct LSPAPI { std::vector (*getNodeInfosByDefinitionData)(es2panda_Context *context, size_t position); es2panda_AstNode *(*getClassDefinition)(es2panda_AstNode *astNode, const std::string &nodeName); es2panda_AstNode *(*getIdentifier)(es2panda_AstNode *astNode, const std::string &nodeName); + es2panda_AstNode *(*getTsQualifiedName)(es2panda_AstNode *astNode, const std::string &nodeName); DefinitionInfo (*getDefinitionDataFromNode)(es2panda_AstNode *astNode, const std::string &nodeName); } LSPAPI; CAPI_EXPORT LSPAPI const *GetImpl(); diff --git a/ets2panda/lsp/include/get_node.h b/ets2panda/lsp/include/get_node.h index b2c63afacb..b778f847d1 100644 --- a/ets2panda/lsp/include/get_node.h +++ b/ets2panda/lsp/include/get_node.h @@ -22,6 +22,7 @@ namespace ark::es2panda::lsp { es2panda_AstNode *GetProgramAstImpl(es2panda_Context *context); es2panda_AstNode *GetClassDefinitionImpl(es2panda_AstNode *astNode, const std::string &nodeName); es2panda_AstNode *GetIdentifierImpl(es2panda_AstNode *astNode, const std::string &nodeName); +es2panda_AstNode *GetTsQualifiedNameImpl(es2panda_AstNode *astNode, const std::string &nodeName); } // namespace ark::es2panda::lsp #endif \ No newline at end of file diff --git a/ets2panda/lsp/src/api.cpp b/ets2panda/lsp/src/api.cpp index 155d760fac..466ac024c6 100644 --- a/ets2panda/lsp/src/api.cpp +++ b/ets2panda/lsp/src/api.cpp @@ -524,6 +524,10 @@ es2panda_AstNode *GetIdentifier(es2panda_AstNode *astNode, const std::string &no { return GetIdentifierImpl(astNode, nodeName); } +es2panda_AstNode *GetTsQualifiedName(es2panda_AstNode *astNode, const std::string &nodeName) +{ + return GetTsQualifiedNameImpl(astNode, nodeName); +} DefinitionInfo GetDefinitionDataFromNode(es2panda_AstNode *astNode, const std::string &nodeName) { @@ -601,6 +605,7 @@ LSPAPI g_lspImpl = {GetDefinitionAtPosition, GetNodeInfosByDefinitionData, GetClassDefinition, GetIdentifier, + GetTsQualifiedName, GetDefinitionDataFromNode}; } // namespace ark::es2panda::lsp diff --git a/ets2panda/lsp/src/get_node.cpp b/ets2panda/lsp/src/get_node.cpp index fa9ea5fea6..f29d672f51 100644 --- a/ets2panda/lsp/src/get_node.cpp +++ b/ets2panda/lsp/src/get_node.cpp @@ -52,4 +52,15 @@ es2panda_AstNode *GetIdentifierImpl(es2panda_AstNode *astNode, const std::string return reinterpret_cast(targetNode); } +es2panda_AstNode *GetTsQualifiedNameImpl(es2panda_AstNode *astNode, const std::string &nodeName) +{ + if (astNode == nullptr) { + return nullptr; + } + auto ast = reinterpret_cast(astNode); + auto targetNode = ast->FindChild([&nodeName](ir::AstNode *childNode) { + return childNode->IsTSQualifiedName() && std::string(childNode->AsTSQualifiedName()->Name()) == nodeName; + }); + return reinterpret_cast(targetNode); +} } // namespace ark::es2panda::lsp \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/CMakeLists.txt b/ets2panda/test/unit/lsp/CMakeLists.txt index a7968df400..c2bf1e75ba 100644 --- a/ets2panda/test/unit/lsp/CMakeLists.txt +++ b/ets2panda/test/unit/lsp/CMakeLists.txt @@ -340,4 +340,8 @@ ets2panda_add_gtest(lsp_api_spelling_test CPP_SOURCES ets2panda_add_gtest(lsp_api_test_constructor_for_derived_need_super_call CPP_SOURCES constructor_for_derived_need_super_call_test.cpp +) + +ets2panda_add_gtest(lsp_api_get_node_ts_qualified_name_test CPP_SOURCES + get_node_ts_qualified_name_test.cpp ) \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_ts_qualified_name_test.cpp b/ets2panda/test/unit/lsp/get_node_ts_qualified_name_test.cpp new file mode 100644 index 0000000000..254259e3ba --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_ts_qualified_name_test.cpp @@ -0,0 +1,165 @@ +/** + * 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. + */ + +#include +#include "ir/astNode.h" +#include "lsp/include/api.h" +#include "lsp_api_test.h" +#include "public/es2panda_lib.h" +#include "public/public.h" + +namespace { +class LspGetNodeQualifiedNameTests : public LSPAPITests {}; + +TEST_F(LspGetNodeQualifiedNameTests, GetTsQualifiedNameTest1) +{ + const std::string sourceCode = R"( + namespace A { + export class Box { + value: T; + } + } + + let box = new A.Box(); + )"; + ark::es2panda::lsp::Initializer initializer; + es2panda_Context *contexts_ = + initializer.CreateContext("GetNodeQualifiedNameTest.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + + const std::string qualifiedName = "box"; + auto res = lspApi->getTsQualifiedName(node, qualifiedName); + ASSERT_TRUE(res == nullptr); + initializer.DestroyContext(contexts_); +} + +TEST_F(LspGetNodeQualifiedNameTests, GetTsQualifiedNameTest2) +{ + const std::string sourceCode = R"( + namespace A { + export class Box { + value: T; + } + } + + let box = new A.Box(); + )"; + ark::es2panda::lsp::Initializer initializer; + es2panda_Context *contexts_ = + initializer.CreateContext("GetNodeQualifiedNameTest.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + + const std::string qualifiedName = "A.Box"; + auto res = lspApi->getTsQualifiedName(node, qualifiedName); + ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsTSQualifiedName()); + ASSERT_EQ(reinterpret_cast(res)->AsTSQualifiedName()->Name(), qualifiedName.data()); + initializer.DestroyContext(contexts_); +} + +TEST_F(LspGetNodeQualifiedNameTests, GetTsQualifiedNameTest3) +{ + const std::string sourceCode = R"( + namespace Outer { + export class Inner {} + } + const obj = new Outer.Inner(); + )"; + ark::es2panda::lsp::Initializer initializer; + es2panda_Context *contexts_ = + initializer.CreateContext("GetNodeQualifiedNameTest.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + + const std::string qualifiedName = "Outer.Inner"; + auto res = lspApi->getTsQualifiedName(node, qualifiedName); + ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsTSQualifiedName()); + ASSERT_EQ(reinterpret_cast(res)->AsTSQualifiedName()->Name(), qualifiedName.data()); + initializer.DestroyContext(contexts_); +} + +TEST_F(LspGetNodeQualifiedNameTests, GetTsQualifiedNameTest4) +{ + const std::string sourceCode = R"( + namespace HTTP { + export namespace Status { + export type Code = number; + } + } + interface Response { + status: HTTP.Status.Code; + } + )"; + ark::es2panda::lsp::Initializer initializer; + es2panda_Context *contexts_ = + initializer.CreateContext("GetNodeQualifiedNameTest.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + + const std::string qualifiedName = "HTTP.Status.Code"; + auto res = lspApi->getTsQualifiedName(node, qualifiedName); + ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsTSQualifiedName()); + ASSERT_EQ(reinterpret_cast(res)->AsTSQualifiedName()->Name(), qualifiedName.data()); + initializer.DestroyContext(contexts_); +} + +TEST_F(LspGetNodeQualifiedNameTests, GetTsQualifiedNameTest5) +{ + const std::string sourceCode = R"( + namespace A { + export namespace B { + export class C {} + } + } + const instance = new A.B.C(); + )"; + ark::es2panda::lsp::Initializer initializer; + es2panda_Context *contexts_ = + initializer.CreateContext("GetNodeQualifiedNameTest.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + + const std::string qualifiedName = "A.B.C"; + auto res = lspApi->getTsQualifiedName(node, qualifiedName); + ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsTSQualifiedName()); + ASSERT_EQ(reinterpret_cast(res)->AsTSQualifiedName()->Name(), qualifiedName.data()); + initializer.DestroyContext(contexts_); +} +} // namespace -- Gitee