diff --git a/ets2panda/bindings/native/src/lsp.cpp b/ets2panda/bindings/native/src/lsp.cpp index fc95986732be952c7ff71ba0d742c173193adda5..5cd8210510f148947fa099f8d641ae9b578ccfd5 100644 --- a/ets2panda/bindings/native/src/lsp.cpp +++ b/ets2panda/bindings/native/src/lsp.cpp @@ -1881,6 +1881,12 @@ KNativePointer impl_getNodeInfosByDefinitionData(KNativePointer context, KInt po } TS_INTEROP_2(getNodeInfosByDefinitionData, KNativePointer, KNativePointer, KInt) +KNativePointer impl_CreateNodeInfoPtr(KStringPtr &nodeName, KInt nodeKind) +{ + return new NodeInfo(nodeName.Data(), ark::es2panda::ir::AstNodeType(nodeKind)); +} +TS_INTEROP_2(CreateNodeInfoPtr, KNativePointer, KStringPtr, KInt) + KNativePointer impl_getNameByNodeInfo(KNativePointer nodeInfo) { auto *info = reinterpret_cast(nodeInfo); @@ -1895,13 +1901,22 @@ KNativePointer impl_getKindByNodeInfo(KNativePointer nodeInfo) } TS_INTEROP_1(getKindByNodeInfo, KNativePointer, KNativePointer) -KNativePointer impl_getDefinitionDataFromNode(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +KNativePointer impl_getDefinitionDataFromNode(KNativePointer context, KStringArray pointerArrayPtr, KInt arraySize) { - auto ast = reinterpret_cast(astNodePtr); + auto pointerArray = ParsePointerArray(arraySize, pointerArrayPtr); + auto nodeInfos = std::vector {}; + nodeInfos.reserve(arraySize); + for (std::size_t i = 0; i < static_cast(arraySize); ++i) { + auto contextPtr = reinterpret_cast(pointerArray[i]); + if (contextPtr != nullptr) { + nodeInfos.push_back(contextPtr); + } + } + auto ctx = reinterpret_cast(context); LSPAPI const *impl = GetImpl(); - return new DefinitionInfo(impl->getDefinitionDataFromNode(ast, nodeNamePtr.Data())); + return new DefinitionInfo(impl->getDefinitionDataFromNode(ctx, nodeInfos)); } -TS_INTEROP_2(getDefinitionDataFromNode, KNativePointer, KNativePointer, KStringPtr) +TS_INTEROP_3(getDefinitionDataFromNode, KNativePointer, KNativePointer, KStringArray, KInt) KInt impl_getSourceLocationLine(KNativePointer locationPtr) { diff --git a/ets2panda/bindings/src/common/Es2pandaNativeModule.ts b/ets2panda/bindings/src/common/Es2pandaNativeModule.ts index d6f4b53f4fa90037468b87235f2ed7d63dcfbe5e..8695fd8e07401a08cbe41b7a7b62a47b95bba102 100644 --- a/ets2panda/bindings/src/common/Es2pandaNativeModule.ts +++ b/ets2panda/bindings/src/common/Es2pandaNativeModule.ts @@ -1026,6 +1026,10 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _CreateNodeInfoPtr(nodeName: String, nodeKind: KInt): KPtr { + throw new Error('Not implemented'); + } + _getProgramAst(context: KPtr): KPtr { throw new Error('Not implemented'); } @@ -1034,7 +1038,7 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } - _getDefinitionDataFromNode(astNode: KPtr, nodeName: String): KPtr { + _getDefinitionDataFromNode(context: KPtr, nodeInfoPtrs: Uint8Array, arraySize: KInt): KPtr { throw new Error('Not implemented'); } diff --git a/ets2panda/bindings/src/common/types.ts b/ets2panda/bindings/src/common/types.ts index 501904a65a6def8c254397637c202b0db0c1220e..fdfcd1d480c211d8c29cc1a9f3ca72dd586210c2 100644 --- a/ets2panda/bindings/src/common/types.ts +++ b/ets2panda/bindings/src/common/types.ts @@ -233,10 +233,25 @@ export interface TextDocumentChangeInfo { } export enum AstNodeType { + CLASS_DEFINITION = 14, + CLASS_PROPERTY = 17, + EXPORT_DEFAULT_DECLARATION = 27, + EXPORT_NAMED_DECLARATION = 28, + EXPORT_SPECIFIER = 29, + IDENTIFIER = 36, + MEMBER_EXPRESSION = 45, + METHOD_DEFINITION = 47, + PROPERTY = 56, + ETS_FUNCTION_TYPE = 69, + TS_ENUM_DECLARATION = 89, + TS_ENUM_MEMBER = 90, + TS_MODULE_DECLARATION = 125, + TS_TYPE_ALIAS_DECLARATION = 129, + TS_INTERFACE_DECLARATION = 133, + TS_CLASS_IMPLEMENTS = 141, UNKNOWN, - IDENTIFIER, - CLASS_DEFINITION } + export interface NodeInfo { name: string; kind: AstNodeType; diff --git a/ets2panda/bindings/src/lsp/lspNode.ts b/ets2panda/bindings/src/lsp/lspNode.ts index 80aedc2a7446fe3e905d305f2eef7804024fb89a..32111b13d6f9c8f16da13924fca64a93c71bc999 100644 --- a/ets2panda/bindings/src/lsp/lspNode.ts +++ b/ets2panda/bindings/src/lsp/lspNode.ts @@ -175,9 +175,9 @@ export class LspDiagsNode extends LspNode { } export class LspDefinitionData extends LspNode { - constructor(peer: KNativePointer) { + constructor(peer: KNativePointer, filePath?: string) { super(peer); - this.fileName = unpackString(global.es2panda._GetFileNameFromDef(peer)); + this.fileName = filePath ? filePath : unpackString(global.es2panda._GetFileNameFromDef(peer)); this.start = global.es2panda._GetStartFromDef(peer); this.length = global.es2panda._getLengthFromDef(peer); } diff --git a/ets2panda/bindings/src/lsp/lsp_helper.ts b/ets2panda/bindings/src/lsp/lsp_helper.ts index 5e6c3fba1bf1baf81ad6c81f013a6466a5f55c5b..e5fab550454031e98f113fb2ad98fffe58e01319 100644 --- a/ets2panda/bindings/src/lsp/lsp_helper.ts +++ b/ets2panda/bindings/src/lsp/lsp_helper.ts @@ -289,24 +289,18 @@ export class Lsp { private getDefinitionAtPositionByNodeInfos(declFilePath: String, nodeInfos: NodeInfo[]): LspDefinitionData { let ptr: KPointer; + let nodeInfoPtrs: KPointer[] = []; const sourceFilePath = this.declFileMap[declFilePath.valueOf()]; const [cfg, ctx] = this.createContext(sourceFilePath, false); - let astNode = global.es2panda._getProgramAst(ctx); - let currentNodeName: string = ''; 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); - } + nodeInfoPtrs.push(global.es2panda._CreateNodeInfoPtr(nodeInfo.name, nodeInfo.kind)); }); + ptr = global.es2panda._getDefinitionDataFromNode(ctx, passPointerArray(nodeInfoPtrs), nodeInfoPtrs.length); } finally { - ptr = global.es2panda._getDefinitionDataFromNode(astNode, currentNodeName); this.destroyContext(cfg, ctx); } - return new LspDefinitionData(ptr); + return new LspDefinitionData(ptr, sourceFilePath); } private getMergedCompileFiles(filename: String): string[] { diff --git a/ets2panda/bindings/test/cases.ts b/ets2panda/bindings/test/cases.ts index f5b6ee5b6a22ae708728a22582c19dc0e09d91cc..38968f674ed32d510d73fcd2280d527df96cd4df 100644 --- a/ets2panda/bindings/test/cases.ts +++ b/ets2panda/bindings/test/cases.ts @@ -62,7 +62,123 @@ export const basicCases: TestCases = { name: 'Foo' } ] as NodeInfo[] - ] + ], + '14': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition21.d.ets' + ), + 0, + [ + { + kind: AstNodeType.CLASS_DEFINITION, + name: 'Foo' + }, + { + kind: AstNodeType.METHOD_DEFINITION, + name: 'bar' + } + ] as NodeInfo[] + ], + '15': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition22.d.ets' + ), + 0, + [ + { + kind: AstNodeType.CLASS_DEFINITION, + name: 'Foo' + }, + { + kind: AstNodeType.CLASS_PROPERTY, + name: 'staticProperty' + } + ] as NodeInfo[] + ], + '16': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition23.d.ets' + ), + 0, + [ + { + kind: AstNodeType.MEMBER_EXPRESSION, + name: 'bar' + } + ] as NodeInfo[] + ], + '17': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition24.d.ets' + ), + 0, + [ + { + kind: AstNodeType.EXPORT_SPECIFIER, + name: 'PI' + } + ] as NodeInfo[] + ], + '18': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition25.d.ets' + ), + 0, + [ + { + kind: AstNodeType.TS_INTERFACE_DECLARATION, + name: 'User' + } + ] as NodeInfo[] + ], + '19': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition26.d.ets' + ), + 0, + [ + { + kind: AstNodeType.TS_TYPE_ALIAS_DECLARATION, + name: 'ID' + } + ] as NodeInfo[] + ], + '20': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition27.d.ets' + ), + 0, + [ + { + kind: AstNodeType.TS_ENUM_DECLARATION, + name: 'Color' + } + ] as NodeInfo[] + ], + '21': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition28.d.ets' + ), + 0, + [ + { + kind: AstNodeType.TS_ENUM_MEMBER, + name: 'Green' + } + ] as NodeInfo[] + ], + '22': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition30.d.ets' + ), + 0, + [ + { + kind: AstNodeType.TS_CLASS_IMPLEMENTS, + name: 'Printable' + } + ] as NodeInfo[] + ], }, getSemanticDiagnostics: { expectedFilePath: resolveTestPath('test/expected/getSemanticDiagnostics.json'), diff --git a/ets2panda/bindings/test/expected/getDefinitionAtPosition.json b/ets2panda/bindings/test/expected/getDefinitionAtPosition.json index bf898a7beb42480a6a9d871c359e54f939d82c86..ace1dbc19626ee2f4c4a6ac64cd7a73284330422 100644 --- a/ets2panda/bindings/test/expected/getDefinitionAtPosition.json +++ b/ets2panda/bindings/test/expected/getDefinitionAtPosition.json @@ -63,5 +63,50 @@ "fileName": "getDefinitionAtPosition20.ets", "start": 625, "length": 3 + }, + "14": { + "fileName": "getDefinitionAtPosition21.ets", + "start": 636, + "length": 3 + }, + "15": { + "fileName": "getDefinitionAtPosition22.ets", + "start": 643, + "length": 14 + }, + "16": { + "fileName": "getDefinitionAtPosition23.ets", + "start": 672, + "length": 3 + }, + "17": { + "fileName": "getDefinitionAtPosition24.ets", + "start": 629, + "length": 2 + }, + "18": { + "fileName": "getDefinitionAtPosition25.ets", + "start": 630, + "length": 4 + }, + "19": { + "fileName": "getDefinitionAtPosition26.ets", + "start": 625, + "length": 2 + }, + "20": { + "fileName": "getDefinitionAtPosition27.ets", + "start": 625, + "length": 5 + }, + "21": { + "fileName": "getDefinitionAtPosition28.ets", + "start": 642, + "length": 5 + }, + "22": { + "fileName": "getDefinitionAtPosition30.ets", + "start": 714, + "length": 9 } } diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition21.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition21.ets new file mode 100755 index 0000000000000000000000000000000000000000..1e19ece094f50e9d835c972fa4ca0653f0bbfb8f --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition21.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +'use static'; + +class Foo { + bar() {} +} diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition22.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition22.ets new file mode 100755 index 0000000000000000000000000000000000000000..7fe685370a5996d9f46df5da8b72f6972908fe0c --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition22.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +'use static'; + +class Foo { + static staticProperty: number = 42; +} diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition23.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition23.ets new file mode 100755 index 0000000000000000000000000000000000000000..1ebcc30df61c1e45de49fc3fea17bdd78ca122ad --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition23.ets @@ -0,0 +1,22 @@ +/* + * 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. + */ + +'use static'; + +class Foo { + bar() {} +} +let foo = new Foo(); +foo.bar(); diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition24.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition24.ets new file mode 100755 index 0000000000000000000000000000000000000000..0aac958da13e456ce2d2f4527189c2b6c25756bb --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition24.ets @@ -0,0 +1,19 @@ +/* + * 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. + */ + +'use static'; + +export { PI, E } from "std/math"; +export { PI as CirclePI, E as CircleE } from "std/math"; diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition25.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition25.ets new file mode 100755 index 0000000000000000000000000000000000000000..f289c0fd987d9748d643bd5e2b2d95a518fdbdcd --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition25.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +'use static'; + +interface User { + bar(): void; +} diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition26.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition26.ets new file mode 100755 index 0000000000000000000000000000000000000000..225abe748c097290ba8c38673ed65da8212f058c --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition26.ets @@ -0,0 +1,18 @@ +/* + * 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. + */ + +'use static'; + +type ID = string | number; diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition27.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition27.ets new file mode 100755 index 0000000000000000000000000000000000000000..1a10fa6bf4d50d64c4ce5a24b35caa1e183c2e4c --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition27.ets @@ -0,0 +1,22 @@ +/* + * 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. + */ + +'use static'; + +enum Color { + Red, + Green, + Blue +} diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition28.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition28.ets new file mode 100755 index 0000000000000000000000000000000000000000..1a10fa6bf4d50d64c4ce5a24b35caa1e183c2e4c --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition28.ets @@ -0,0 +1,22 @@ +/* + * 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. + */ + +'use static'; + +enum Color { + Red, + Green, + Blue +} diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition29.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition29.ets new file mode 100755 index 0000000000000000000000000000000000000000..b43ddf6a3916fb4525dab2c77a470ba49f078074 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition29.ets @@ -0,0 +1,19 @@ +/* + * 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. + */ + +'use static'; + +type Mapper = (input: T) => U; +type StringToNumber = (s: string) => number; diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition30.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition30.ets new file mode 100755 index 0000000000000000000000000000000000000000..c7fa72e90d1ddf53f5abff2a50dd1340ca7ff729 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition30.ets @@ -0,0 +1,32 @@ +/* + * 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. + */ + +'use static'; + +interface Printable { + print(): void; + getTitle(): string; +} + +class Document implements Printable { + constructor() {} + + print(): void { + } + + getTitle(): string { + return ""; + } +} \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition31.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition31.ets new file mode 100755 index 0000000000000000000000000000000000000000..c948f7e003adee0ac2896728e71e39c47114ae64 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition31.ets @@ -0,0 +1,27 @@ +/* + * 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. + */ + +'use static'; + +const obj = { + prop: "value", + methodProp: function() { + return "method result"; + }, + arrowProp: () => { + console.log("arrow function property"); + }, + arrayProp: [1, 2, 3, 4, 5], +}; diff --git a/ets2panda/lsp/BUILD.gn b/ets2panda/lsp/BUILD.gn index 601b93ad09317bab6d5f66fcddc18731bcb478ef..906d494c38ed022dee06f1ed9078196e073f0874 100644 --- a/ets2panda/lsp/BUILD.gn +++ b/ets2panda/lsp/BUILD.gn @@ -78,6 +78,7 @@ ohos_source_set("libes2panda_lsp_static") { "src/isolated_declaration.cpp", "src/line_column_offset.cpp", "src/navigate_to.cpp", + "src/node_matchers.cpp", "src/organize_imports.cpp", "src/quick_info.cpp", "src/refactor_provider.cpp", diff --git a/ets2panda/lsp/CMakeLists.txt b/ets2panda/lsp/CMakeLists.txt index e93fcc7acde9994304641f5ba604fa54ed4d2209..587f416137f5f502c579963be50f367cf1ac0591 100644 --- a/ets2panda/lsp/CMakeLists.txt +++ b/ets2panda/lsp/CMakeLists.txt @@ -135,6 +135,7 @@ set(ES2PANDA_LSP_SRC ./src/get_signature.cpp ./src/get_name_or_dotted_name_span.cpp ./src/get_node.cpp + ./src/node_matchers.cpp ) panda_frontend_add_library(${LSP_LIB} SHARED ${ES2PANDA_LSP_SRC}) diff --git a/ets2panda/lsp/include/api.h b/ets2panda/lsp/include/api.h index 8749f10b707fb6edc7fca030e1f55a14b10c2067..d4e723edff831f503f2d5b6ce486bb86a15c9b1d 100644 --- a/ets2panda/lsp/include/api.h +++ b/ets2panda/lsp/include/api.h @@ -567,7 +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); - DefinitionInfo (*getDefinitionDataFromNode)(es2panda_AstNode *astNode, const std::string &nodeName); + DefinitionInfo (*getDefinitionDataFromNode)(es2panda_Context *context, const std::vector nodeInfos); } LSPAPI; CAPI_EXPORT LSPAPI const *GetImpl(); // NOLINTEND diff --git a/ets2panda/lsp/include/node_matchers.h b/ets2panda/lsp/include/node_matchers.h new file mode 100755 index 0000000000000000000000000000000000000000..f70279ca93aa350092b65008287f1d8c9e96de48 --- /dev/null +++ b/ets2panda/lsp/include/node_matchers.h @@ -0,0 +1,46 @@ +/** + * 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 NODE_MATCHERS_H +#define NODE_MATCHERS_H + +#include +#include +#include "ir/astNode.h" +#include "api.h" + +namespace ark::es2panda::lsp { +using NodeMatcher = std::function; +using NodeExtractor = ir::AstNode* (*)(ir::AstNode*, const NodeInfo*); + +bool MatchClassDefinition(ir::AstNode *childNode, const NodeInfo *info); +bool MatchIdentifier(ir::AstNode *childNode, const NodeInfo *info); +bool MatchClassProperty(ir::AstNode *childNode, const NodeInfo *info); +bool MatchProperty(ir::AstNode *childNode, const NodeInfo *info); +bool MatchMethodDefinition(ir::AstNode *childNode, const NodeInfo *info); +bool MatchTSEnumDeclaration(ir::AstNode *childNode, const NodeInfo *info); +bool MatchTSEnumMember(ir::AstNode *childNode, const NodeInfo *info); +bool MatchTSInterfaceDeclaration(ir::AstNode *childNode, const NodeInfo *info); +bool MatchTSTypeAliasDeclaration(ir::AstNode *childNode, const NodeInfo *info); +bool MatchExportSpecifier(ir::AstNode *childNode, const NodeInfo *info); +bool MatchMemberExpression(ir::AstNode *childNode, const NodeInfo *info); +bool MatchTSClassImplements(ir::AstNode *childNode, const NodeInfo *info); + +ir::AstNode *ExtractExportSpecifierIdentifier(ir::AstNode *node, const NodeInfo *info); +ir::AstNode *ExtractTSClassImplementsIdentifier(ir::AstNode *node, const NodeInfo *info); +ir::AstNode* ExtractIdentifierFromNode(ir::AstNode* node, const NodeInfo* info); +extern const std::unordered_map nodeMatchers; +} // namespace ark::es2panda::lsp +#endif // NODE_MATCHERS_H \ No newline at end of file diff --git a/ets2panda/lsp/src/api.cpp b/ets2panda/lsp/src/api.cpp index 155d760fac9a59b2d6a18ff3152451818c6fc74d..1410e09142d41cf54e3aaffa799ba65f2530a090 100644 --- a/ets2panda/lsp/src/api.cpp +++ b/ets2panda/lsp/src/api.cpp @@ -42,6 +42,7 @@ #include "completions_details.h" #include "get_name_or_dotted_name_span.h" #include "get_signature.h" +#include "node_matchers.h" using ark::es2panda::lsp::details::GetCompletionEntryDetailsImpl; @@ -525,34 +526,48 @@ es2panda_AstNode *GetIdentifier(es2panda_AstNode *astNode, const std::string &no return GetIdentifierImpl(astNode, nodeName); } -DefinitionInfo GetDefinitionDataFromNode(es2panda_AstNode *astNode, const std::string &nodeName) +DefinitionInfo GetDefinitionDataFromNode(es2panda_Context *context, const std::vector nodeInfos) { - DefinitionInfo result; - if (astNode == nullptr) { + DefinitionInfo result {"", 0, 0}; + if (context == nullptr || nodeInfos.empty()) { return result; } - auto node = reinterpret_cast(astNode); - auto targetNode = node->IsIdentifier() ? node : node->FindChild([&nodeName](ir::AstNode *childNode) { - return childNode->IsIdentifier() && std::string(childNode->AsIdentifier()->Name()) == nodeName; - }); - std::string filePath; - while (node != nullptr) { - if (node->Range().start.Program() != nullptr) { - filePath = std::string(node->Range().start.Program()->SourceFile().GetAbsolutePath().Utf8()); - break; - } - if (node->IsETSModule()) { - filePath = std::string(node->AsETSModule()->Program()->SourceFilePath()); - break; + auto ctx = reinterpret_cast(context); + auto rootNode = reinterpret_cast(ctx->parserProgram->Ast()); + if (rootNode == nullptr) { + return result; + } + + ir::AstNode* lastFoundNode = nullptr; + NodeInfo* lastNodeInfo = nullptr; + for (auto info : nodeInfos) { + auto foundNode = rootNode->FindChild([info](ir::AstNode *childNode) -> bool { + auto it = nodeMatchers.find(info->kind); + if (it != nodeMatchers.end()) { + return it->second(childNode, info); + } + return false; + }); + if (foundNode == nullptr) { + return {"", 0, 0}; } - node = node->Parent(); + lastFoundNode = foundNode; + lastNodeInfo = info; } - if (targetNode != nullptr) { - result = {filePath, targetNode->Start().index, targetNode->End().index - targetNode->Start().index}; + + if (lastFoundNode != nullptr && lastNodeInfo != nullptr) { + ir::AstNode* identifierNode = ExtractIdentifierFromNode(lastFoundNode, lastNodeInfo); + if (identifierNode != nullptr) { + result = {"", identifierNode->Start().index, identifierNode->End().index - identifierNode->Start().index}; + } else { + result = {"", lastFoundNode->Start().index, lastFoundNode->End().index - lastFoundNode->Start().index}; + } } + return result; } + LSPAPI g_lspImpl = {GetDefinitionAtPosition, GetApplicableRefactors, GetImplementationAtPosition, diff --git a/ets2panda/lsp/src/node_matchers.cpp b/ets2panda/lsp/src/node_matchers.cpp new file mode 100755 index 0000000000000000000000000000000000000000..3520ae592ecf8a529734d18f8e386dad5113405d --- /dev/null +++ b/ets2panda/lsp/src/node_matchers.cpp @@ -0,0 +1,218 @@ +/* + * 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 "node_matchers.h" +#include +#include "public/es2panda_lib.h" +#include "public/public.h" +#include "ir/ets/etsReExportDeclaration.h" + +namespace ark::es2panda::lsp { + +bool MatchClassDefinition(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsClassDefinition() && std::string(childNode->AsClassDefinition()->Ident()->Name()) == info->name; +} + +bool MatchIdentifier(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsIdentifier() && std::string(childNode->AsIdentifier()->Name()) == info->name; +} + +bool MatchClassProperty(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsClassProperty() && std::string(childNode->AsClassProperty()->Id()->Name()) == info->name; +} + +bool MatchProperty(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsProperty() && std::string(childNode->AsProperty()->Key()->AsIdentifier()->Name()) == info->name; +} + +bool MatchMethodDefinition(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsMethodDefinition() && + std::string(childNode->AsMethodDefinition()->Function()->Id()->Name()) == info->name; +} + +bool MatchTSEnumDeclaration(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsTSEnumDeclaration() && + std::string(childNode->AsTSEnumDeclaration()->Key()->Name()) == info->name; +} + +bool MatchTSEnumMember(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsTSEnumMember() && std::string(childNode->AsTSEnumMember()->Name()) == info->name; +} + +bool MatchTSInterfaceDeclaration(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsTSInterfaceDeclaration() && + std::string(childNode->AsTSInterfaceDeclaration()->Id()->Name()) == info->name; +} + +bool MatchTSTypeAliasDeclaration(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsTSTypeAliasDeclaration() && + std::string(childNode->AsTSTypeAliasDeclaration()->Id()->Name()) == info->name; +} + +bool MatchExportSpecifier(ir::AstNode *childNode, const NodeInfo *info) +{ + if (!childNode->IsETSReExportDeclaration()) { + return false; + } + auto specifiers = childNode->AsETSReExportDeclaration()->GetETSImportDeclarations()->Specifiers(); + if (specifiers.empty()) { + return false; + } + for (auto *importSpecifier : specifiers) { + if (importSpecifier->IsImportSpecifier() && + importSpecifier->AsImportSpecifier()->Local()->Name().Mutf8() == info->name) { + return true; + } + } + return false; +} + +bool MatchMemberExpression(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsMemberExpression() && childNode->AsMemberExpression()->Property()->ToString() == info->name; +} + +bool MatchTSClassImplements(ir::AstNode *childNode, const NodeInfo *info) +{ + if (!childNode->IsTSClassImplements()) + return false; + auto dd = childNode->AsTSClassImplements()->Expr()->AsETSTypeReference()->Part(); + return std::string(dd->GetIdent()->Name()) == info->name; +} + +ir::AstNode *ExtractExportSpecifierIdentifier(ir::AstNode *node, const NodeInfo *info) +{ + if (!node->IsETSReExportDeclaration()) { + return node; + } + + auto specifiers = node->AsETSReExportDeclaration()->GetETSImportDeclarations()->Specifiers(); + if (specifiers.empty()) { + return node; + } + + for (auto *importSpecifier : specifiers) { + if (!importSpecifier->IsImportSpecifier()) { + continue; + } + + if (importSpecifier->AsImportSpecifier()->Local()->Name().Mutf8() == info->name) { + return importSpecifier->AsImportSpecifier()->Local(); + } + } + + return node; +} + +ir::AstNode *ExtractTSClassImplementsIdentifier(ir::AstNode *node, const NodeInfo *info) +{ + if (!node->IsTSClassImplements()) { + return node; + } + + auto expr = node->AsTSClassImplements()->Expr(); + if (!expr || !expr->IsETSTypeReference()) { + return node; + } + + auto part = expr->AsETSTypeReference()->Part(); + if (!part || !part->GetIdent()) { + return node; + } + + if (std::string(part->GetIdent()->Name()) == info->name) { + return static_cast(part->GetIdent()); + } + + return node; +} + +const std::unordered_map nodeExtractors = { + {ir::AstNodeType::CLASS_DEFINITION, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsClassDefinition() ? node->AsClassDefinition()->Ident() : node; + }}, + {ir::AstNodeType::IDENTIFIER, + [](ir::AstNode *node, const NodeInfo *) { return node->IsIdentifier() ? node->AsIdentifier() : node; }}, + {ir::AstNodeType::CLASS_PROPERTY, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsProperty() ? node->AsProperty()->Key()->AsIdentifier() : node; + }}, + {ir::AstNodeType::PROPERTY, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsProperty() ? node->AsProperty()->Key()->AsIdentifier() : node; + }}, + {ir::AstNodeType::METHOD_DEFINITION, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsMethodDefinition() ? node->AsMethodDefinition()->Function()->Id() : node; + }}, + {ir::AstNodeType::TS_ENUM_DECLARATION, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsTSEnumDeclaration() ? node->AsTSEnumDeclaration()->Key() : node; + }}, + {ir::AstNodeType::TS_ENUM_MEMBER, + [](ir::AstNode *node, const NodeInfo *) { return node->IsTSEnumMember() ? node->AsTSEnumMember()->Key() : node; }}, + {ir::AstNodeType::TS_INTERFACE_DECLARATION, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsTSInterfaceDeclaration() ? node->AsTSInterfaceDeclaration()->Id() : node; + }}, + {ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsTSTypeAliasDeclaration() ? node->AsTSTypeAliasDeclaration()->Id() : node; + }}, + {ir::AstNodeType::EXPORT_SPECIFIER, + [](ir::AstNode *node, const NodeInfo *info) { return ExtractExportSpecifierIdentifier(node, info); }}, + {ir::AstNodeType::MEMBER_EXPRESSION, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsMemberExpression() ? node->AsMemberExpression()->Property()->AsIdentifier() : node; + }}, + {ir::AstNodeType::TS_CLASS_IMPLEMENTS, + [](ir::AstNode *node, const NodeInfo *info) { return ExtractTSClassImplementsIdentifier(node, info); }}}; + +ir::AstNode *ExtractIdentifierFromNode(ir::AstNode *node, const NodeInfo *info) +{ + if (!node) + return node; + + auto it = nodeExtractors.find(info->kind); + if (it != nodeExtractors.end()) { + return it->second(node, info); + } + return node; +} + +const std::unordered_map nodeMatchers = { + {ir::AstNodeType::CLASS_DEFINITION, MatchClassDefinition}, + {ir::AstNodeType::IDENTIFIER, MatchIdentifier}, + {ir::AstNodeType::CLASS_PROPERTY, MatchClassProperty}, + {ir::AstNodeType::PROPERTY, MatchProperty}, + {ir::AstNodeType::METHOD_DEFINITION, MatchMethodDefinition}, + {ir::AstNodeType::TS_ENUM_DECLARATION, MatchTSEnumDeclaration}, + {ir::AstNodeType::TS_ENUM_MEMBER, MatchTSEnumMember}, + {ir::AstNodeType::TS_INTERFACE_DECLARATION, MatchTSInterfaceDeclaration}, + {ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION, MatchTSTypeAliasDeclaration}, + {ir::AstNodeType::EXPORT_SPECIFIER, MatchExportSpecifier}, + {ir::AstNodeType::MEMBER_EXPRESSION, MatchMemberExpression}, + {ir::AstNodeType::TS_CLASS_IMPLEMENTS, MatchTSClassImplements}}; +} // 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 f76ffe2b879853ec0fa52e338fd78d55edccd9fb..54037e940bbf35b76f5c1993d1775f1bcede10ef 100644 --- a/ets2panda/test/unit/lsp/CMakeLists.txt +++ b/ets2panda/test/unit/lsp/CMakeLists.txt @@ -342,10 +342,29 @@ ets2panda_add_gtest(lsp_api_spelling_test CPP_SOURCES fix_spelling_test.cpp ) +ets2panda_add_gtest(lsp_api_get_node_expression_test CPP_SOURCES + get_node_expression_test.cpp +) + 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_interface_test CPP_SOURCES + get_node_interface_test.cpp +) + +ets2panda_add_gtest(lsp_api_get_node_type_namespace_test CPP_SOURCES + get_node_type_namespace_test.cpp +) + +ets2panda_add_gtest(lsp_api_get_node_export_test CPP_SOURCES + get_node_export_test.cpp +) + +ets2panda_add_gtest(lsp_api_get_node_ts_class_Implements_test CPP_SOURCES + get_node_ts_class_Implements_test.cpp +) ets2panda_add_gtest(lsp_api_test_add_name_to_nameless_parameter CPP_SOURCES add_name_to_nameless_parameter_test.cpp ) \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_definition_from_node_test.cpp b/ets2panda/test/unit/lsp/get_definition_from_node_test.cpp index 82c209687102bf2a43c8eb260943d1630a4a7590..aedd69e4254c3f1a4c2ee8b090a4f414c13f95b1 100644 --- a/ets2panda/test/unit/lsp/get_definition_from_node_test.cpp +++ b/ets2panda/test/unit/lsp/get_definition_from_node_test.cpp @@ -34,18 +34,21 @@ TEST_F(LspGetDefinitionFromNodeTest, GetDefinitionFromNode1) ASSERT_EQ(filePaths.size(), expectedFileCount); Initializer initializer = Initializer(); - auto context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); - - auto ctx = reinterpret_cast(context); - auto ast = ctx->parserProgram->Ast(); + auto context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_PARSED); LSPAPI const *lspApi = GetImpl(); const std::string nodeName = "Foo"; - auto res = lspApi->getDefinitionDataFromNode(reinterpret_cast(ast), nodeName); + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(context, nodeInfoPtrs); + std::string extractedText(texts[0].substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; initializer.DestroyContext(context); - std::string expectedFileName = filePaths[0]; - size_t const expectedStart = 6; + size_t const expectedStart = 16; size_t const expectedLength = 3; - ASSERT_EQ(res.fileName, expectedFileName); ASSERT_EQ(res.start, expectedStart); ASSERT_EQ(res.length, expectedLength); } diff --git a/ets2panda/test/unit/lsp/get_node_export_test.cpp b/ets2panda/test/unit/lsp/get_node_export_test.cpp new file mode 100755 index 0000000000000000000000000000000000000000..da4c0cffdf05347c239ff77a81da4725069ae000 --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_export_test.cpp @@ -0,0 +1,88 @@ +/** + * 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 "ir/astNode.h" +#include "lsp/include/api.h" +#include "lsp_api_test.h" +#include "public/es2panda_lib.h" +#include "public/public.h" +#include "ir/ets/etsReExportDeclaration.h" +#include + +namespace { +using ark::es2panda::lsp::Initializer; + +class LspGetNodeExportTests : public LSPAPITests { +protected: + static void SetUpTestSuite() + { + initializer_ = new Initializer(); + sourceCode_ = R"( +export { PI } from "std/math"; +export { E as CircleE } from "std/math"; +)"; + GenerateContexts(*initializer_); + } + + static void TearDownTestSuite() + { + initializer_->DestroyContext(contexts_); + delete initializer_; + initializer_ = nullptr; + sourceCode_ = ""; + } + static void GenerateContexts(Initializer &initializer) + { + contexts_ = initializer.CreateContext("GetNodeExportTest.ts", ES2PANDA_STATE_PARSED, sourceCode_.c_str()); + } + // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) + static inline es2panda_Context *contexts_ = nullptr; + static inline Initializer *initializer_ = nullptr; + static inline std::string sourceCode_ = ""; + // NOLINTEND(fuchsia-statically-constructed-objects, cert-err58-cpp) +}; + +TEST_F(LspGetNodeExportTests, GetExportSpecifierDeclarationTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string moduleName = "PI"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {moduleName, ark::es2panda::ir::AstNodeType::EXPORT_SPECIFIER}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(moduleName), std::string::npos); +} + +TEST_F(LspGetNodeExportTests, GetExportSpecifierDeclarationAsNameTest1) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string moduleName = "CircleE"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {moduleName, ark::es2panda::ir::AstNodeType::EXPORT_SPECIFIER}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(moduleName), std::string::npos); +} +} // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_expression_test.cpp b/ets2panda/test/unit/lsp/get_node_expression_test.cpp new file mode 100755 index 0000000000000000000000000000000000000000..67c7484d9ce8d04575b875a669d32be9290d05b2 --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_expression_test.cpp @@ -0,0 +1,109 @@ +/** + * 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 "ir/astNode.h" +#include "lsp/include/api.h" +#include "lsp_api_test.h" +#include "public/es2panda_lib.h" +#include "public/public.h" +#include + +namespace { +using ark::es2panda::lsp::Initializer; + +class LspGetNodeExpressionTests : public LSPAPITests { +protected: + static void SetUpTestSuite() + { + initializer_ = new Initializer(); + sourceCode_ = R"( +class Foo { + bar() {} +} +let foo = new Foo(); +foo.bar(); + +let obj: Record = { + prop: "value" +}; +let propName = "prop"; +obj[propName]; +)"; + GenerateContexts(*initializer_); + } + + static void TearDownTestSuite() + { + initializer_->DestroyContext(contexts_); + delete initializer_; + initializer_ = nullptr; + sourceCode_ = ""; + } + static void GenerateContexts(Initializer &initializer) + { + contexts_ = initializer.CreateContext("GetNodeExpresion.ts", ES2PANDA_STATE_PARSED, sourceCode_.c_str()); + } + // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) + static inline es2panda_Context *contexts_ = nullptr; + static inline Initializer *initializer_ = nullptr; + static inline std::string sourceCode_ = ""; + // NOLINTEND(fuchsia-statically-constructed-objects, cert-err58-cpp) +}; +TEST_F(LspGetNodeExpressionTests, GetMemberExpression_PROPERTY_ACCESS) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "bar"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::MEMBER_EXPRESSION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeExpressionTests, GetMemberExpression_ELEMENT_ACCESS) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "propName"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::MEMBER_EXPRESSION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeExpressionTests, GetMemberExpression_NotFound) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "nonexistent"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::MEMBER_EXPRESSION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(nodeName), std::string::npos); +} +} // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_interface_test.cpp b/ets2panda/test/unit/lsp/get_node_interface_test.cpp new file mode 100755 index 0000000000000000000000000000000000000000..37118525b1ec81407ce7ae3d0a0b857f02672862 --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_interface_test.cpp @@ -0,0 +1,124 @@ +/** + * 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 "ir/astNode.h" +#include "lsp/include/api.h" +#include "lsp_api_test.h" +#include "public/es2panda_lib.h" +#include "public/public.h" +#include + +namespace { +using ark::es2panda::lsp::Initializer; + +class LspGetNodeInterfaceTests : public LSPAPITests { +protected: + static void SetUpTestSuite() + { + initializer_ = new Initializer(); + sourceCode_ = R"(export namespace a { + export interface User { + bar(): void; + } + export namespace b { + interface Client extends a.User {} + } +} +interface Worker extends a.User {})"; + GenerateContexts(*initializer_); + } + + static void TearDownTestSuite() + { + initializer_->DestroyContext(contexts_); + delete initializer_; + initializer_ = nullptr; + sourceCode_ = ""; + } + static void GenerateContexts(Initializer &initializer) + { + contexts_ = initializer.CreateContext("GetNodeInterfaceTest.ets", ES2PANDA_STATE_PARSED, sourceCode_.c_str()); + } + // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) + static inline es2panda_Context *contexts_ = nullptr; + static inline Initializer *initializer_ = nullptr; + static inline std::string sourceCode_ = ""; + // NOLINTEND(fuchsia-statically-constructed-objects, cert-err58-cpp) +}; + +TEST_F(LspGetNodeInterfaceTests, GetTsInterfaceDeclarationNonExistentTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string interfaceName = "NonExistentInterface"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {interfaceName, ark::es2panda::ir::AstNodeType::TS_INTERFACE_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_EQ(extractedText.find(interfaceName), std::string::npos); +} + +TEST_F(LspGetNodeInterfaceTests, GetTsInterfaceDeclarationTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string interfaceName = "User"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {interfaceName, ark::es2panda::ir::AstNodeType::TS_INTERFACE_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(interfaceName), std::string::npos); +} + +TEST_F(LspGetNodeInterfaceTests, GetTsInterfaceDeclarationExtendsTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string interfaceName = "Client"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {interfaceName, ark::es2panda::ir::AstNodeType::TS_INTERFACE_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(interfaceName), std::string::npos); +} + +TEST_F(LspGetNodeInterfaceTests, GetTsInterfaceDeclarationExtendsTest1) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string interfaceName = "Worker"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {interfaceName, ark::es2panda::ir::AstNodeType::TS_INTERFACE_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(interfaceName), std::string::npos); +} +} // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_test.cpp b/ets2panda/test/unit/lsp/get_node_test.cpp index 140f44ff2208943e776fb662e39c298cb8bd4bb4..d60f4b5463332e4d157a38a968c19e026dd8af57 100644 --- a/ets2panda/test/unit/lsp/get_node_test.cpp +++ b/ets2panda/test/unit/lsp/get_node_test.cpp @@ -28,6 +28,34 @@ protected: static void SetUpTestSuite() { initializer_ = new Initializer(); + sourceCode_ = R"(class Foo { + Foo = 1; + bar() {} + enum Color { + Red, + Green, + Blue + }; + + static staticProperty: number = 42; + optionalProperty?: boolean; + readonly readOnlyProperty: string = "read-only"; + protected protectedProperty: string = "protected"; +} + +const obj = { + prop: "value", + methodProp: function() { + return "method result"; + }, + arrowProp: () => { + console.log("arrow function property"); + }, + arrayProp: [1, 2, 3, 4, 5], +}; + +const myClassInstance = new Foo(); +})"; GenerateContexts(*initializer_); } @@ -36,16 +64,16 @@ protected: initializer_->DestroyContext(contexts_); delete initializer_; initializer_ = nullptr; + sourceCode_ = ""; } static void GenerateContexts(Initializer &initializer) { - contexts_ = initializer.CreateContext("GetNodeTest.ets", ES2PANDA_STATE_CHECKED, R"(class Foo { - Foo = 1; -})"); + contexts_ = initializer.CreateContext("GetNodeTest.ets", ES2PANDA_STATE_PARSED, sourceCode_.c_str()); } // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) static inline es2panda_Context *contexts_ = nullptr; static inline Initializer *initializer_ = nullptr; + static inline std::string sourceCode_ = ""; // NOLINTEND(fuchsia-statically-constructed-objects, cert-err58-cpp) }; @@ -60,25 +88,270 @@ TEST_F(LspGetNodeTests, GetProgramAst1) TEST_F(LspGetNodeTests, GetClassDefinition1) { - auto ctx = reinterpret_cast(contexts_); - auto ast = ctx->parserProgram->Ast(); LSPAPI const *lspApi = GetImpl(); const std::string nodeName = "Foo"; - auto res = lspApi->getClassDefinition(reinterpret_cast(ast), nodeName); - ASSERT_TRUE(reinterpret_cast(res)->IsClassDefinition()); - ASSERT_EQ(reinterpret_cast(res)->AsClassDefinition()->Ident()->Name(), - nodeName.data()); + + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_DEFINITION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(nodeName), std::string::npos); } TEST_F(LspGetNodeTests, GetIdentifier1) { - auto ctx = reinterpret_cast(contexts_); - auto ast = ctx->parserProgram->Ast(); LSPAPI const *lspApi = GetImpl(); const std::string nodeName = "Foo"; - auto res = lspApi->getIdentifier(reinterpret_cast(ast), nodeName); - ASSERT_TRUE(reinterpret_cast(res)->IsIdentifier()); - ASSERT_EQ(reinterpret_cast(res)->AsIdentifier()->Name(), nodeName.data()); + + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::IDENTIFIER}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetClassProperty1) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Foo"; + + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetClassProperty2) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "staticProperty"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetClassProperty3) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "optionalProperty"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetClassProperty4) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "readOnlyProperty"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetClassProperty5) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "protectedProperty"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetClassPropertyNotFound) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "nonExistentProperty"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetProperty1) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "prop"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetProperty2) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "methodProp"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetProperty3) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "arrowProp"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetProperty4) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "arrayProp"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); } +TEST_F(LspGetNodeTests, GetMethodDefinition1) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "bar"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::METHOD_DEFINITION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetMethodDefinition_NotFound) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "nonExistentMethod"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::METHOD_DEFINITION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetTsEnumDeclaration) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string enumName = "Color"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {enumName, ark::es2panda::ir::AstNodeType::TS_ENUM_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(enumName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetTsEnumDeclaration_NotFound) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string enumName = "nonExistentEnum"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {enumName, ark::es2panda::ir::AstNodeType::TS_ENUM_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(enumName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetTsEnumMember) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Red"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_ENUM_MEMBER}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(memberName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetTsEnumMember_NotFound) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "nonExistentEnumMember"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_ENUM_MEMBER}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(memberName), std::string::npos); +} } // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_ts_class_Implements_test.cpp b/ets2panda/test/unit/lsp/get_node_ts_class_Implements_test.cpp new file mode 100755 index 0000000000000000000000000000000000000000..bf70f8a5781e91d272e1fd3bba15008c964d2184 --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_ts_class_Implements_test.cpp @@ -0,0 +1,270 @@ +/** + * 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 "ir/astNode.h" +#include "lsp/include/api.h" +#include "lsp_api_test.h" +#include "public/es2panda_lib.h" +#include "public/public.h" +#include + +namespace { +using ark::es2panda::lsp::Initializer; + +class LspGetTsClassImplementsTests : public LSPAPITests {}; + +TEST_F(LspGetTsClassImplementsTests, GetTsClassImplements) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +interface Printable { + print(): void; + getTitle(): string; +} + +class Document implements Printable { + constructor() {} + + print(): void { + } + + getTitle(): string { + return ""; + } +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_PARSED, sourceCode.c_str()); + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Printable"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts, nodeInfoPtrs); + std::string extractedText(sourceCode.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + // ASSERT_NE(extractedText.find(memberName), std::string::npos); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetTsClassImplementsTests, MultipleInterfaces) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +interface Drawable { draw(): void; } + +class Circle implements Drawable { + draw(): void {} +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_PARSED, sourceCode.c_str()); + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Drawable"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts, nodeInfoPtrs); + std::string extractedText(sourceCode.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(memberName), std::string::npos); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetTsClassImplementsTests, MultipleInterfaces2) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +interface Drawable { draw(): void; } +interface Serializable { serialize(): string; } + +class Circle implements Drawable, Serializable { + draw(): void {} + serialize(): string { return "{}"; } +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_PARSED, sourceCode.c_str()); + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Serializable"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts, nodeInfoPtrs); + std::string extractedText(sourceCode.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(memberName), std::string::npos); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetTsClassImplementsTests, InterfaceNotFound) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +interface Printable { + print(): void; + getTitle(): string; +} + +class Document implements Printable { + constructor() {} + + print(): void { + } + + getTitle(): string { + return ""; + } +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_PARSED, sourceCode.c_str()); + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "NonExistentInterface"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + auto res = lspApi->getDefinitionDataFromNode(contexts, nodeInfoPtrs); + std::cout << "start " << res.start << "length " << res.length << std::endl; + std::string extractedText(sourceCode.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + ASSERT_EQ(extractedText.find(memberName), std::string::npos); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetTsClassImplementsTests, InNamespace) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +namespace Graphics { + interface Shape { area(): number; } + + class Rectangle implements Shape { + area(): number { return 0; } + } +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_PARSED, sourceCode.c_str()); + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Shape"; + + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + auto res = lspApi->getDefinitionDataFromNode(contexts, nodeInfoPtrs); + std::string extractedText(sourceCode.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(memberName), std::string::npos); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetTsClassImplementsTests, ExtendsAndImplements) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +class Base {} + +interface Loggable { log(): void; } + +class Derived extends Base implements Loggable { + log(): void {} +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_PARSED, sourceCode.c_str()); + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Loggable"; + + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + auto res = lspApi->getDefinitionDataFromNode(contexts, nodeInfoPtrs); + std::string extractedText(sourceCode.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(memberName), std::string::npos); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetTsClassImplementsTests, GenericInterface) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +interface Repository { + save(item: T): void; +} + +class User { + id: number; + name: string; + constructor(id: number, name: string) { + this.id = id; + this.name = name; + } +} + +class UserRepository implements Repository { + save(item: User): void {} +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_PARSED, sourceCode.c_str()); + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Repository"; + + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + auto res = lspApi->getDefinitionDataFromNode(contexts, nodeInfoPtrs); + std::string extractedText(sourceCode.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(memberName), std::string::npos); + initializer.DestroyContext(contexts); +} + + +TEST_F(LspGetTsClassImplementsTests, CaseSensitivity) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +interface MyInterface { + method(): void; +} + +class MyClass implements MyInterface { + method(): void {} +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_PARSED, sourceCode.c_str()); + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "MyInterface"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + auto res = lspApi->getDefinitionDataFromNode(contexts, nodeInfoPtrs); + std::string extractedText(sourceCode.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(memberName), std::string::npos); + initializer.DestroyContext(contexts); +} +} // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_type_namespace_test.cpp b/ets2panda/test/unit/lsp/get_node_type_namespace_test.cpp new file mode 100755 index 0000000000000000000000000000000000000000..b8a26bc87848b360e1ad6fb439de953e9bdf50fe --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_type_namespace_test.cpp @@ -0,0 +1,132 @@ +/** + * 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 "ir/astNode.h" +#include "lsp/include/api.h" +#include "lsp_api_test.h" +#include "public/es2panda_lib.h" +#include "public/public.h" +#include + +namespace { +using ark::es2panda::lsp::Initializer; + +class LspGetNodeTypNamespaceTests : public LSPAPITests { +protected: + static void SetUpTestSuite() + { + initializer_ = new Initializer(); + sourceCode_ = R"(type ID = string | number; +type Status = "active" | "inactive" | "pending"; +type List = T[]; +namespace Models { + namespace Utils { + type Formatter = (input: T) => string; + } +})"; + GenerateContexts(*initializer_); + } + + static void TearDownTestSuite() + { + initializer_->DestroyContext(contexts_); + delete initializer_; + initializer_ = nullptr; + sourceCode_ = ""; + } + static void GenerateContexts(Initializer &initializer) + { + contexts_ = initializer.CreateContext("GetNodeTypeNamespace.ets", ES2PANDA_STATE_PARSED, sourceCode_.c_str()); + } + // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) + static inline es2panda_Context *contexts_ = nullptr; + static inline Initializer *initializer_ = nullptr; + static inline std::string sourceCode_ = ""; + // NOLINTEND(fuchsia-statically-constructed-objects, cert-err58-cpp) +}; + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationNonExistentTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "NonExistentTypeDeclaration"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationIDTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "ID"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + std::cout << "extractedText " << extractedText << std::endl; + std::cout << "start " << res.start << " " << "length " << res.length << std::endl; + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationStatusTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Status"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + ASSERT_EQ(nodeInfoPtrs[0]->kind, ark::es2panda::ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationGenericTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "List"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationChildTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Formatter"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} +} // namespace \ No newline at end of file