From 51d3d07c3e3953dd3404a147255788ac0bc1c5ba Mon Sep 17 00:00:00 2001 From: leo9001 Date: Tue, 10 Jun 2025 20:04:22 +0800 Subject: [PATCH] support getApplicableRefactors refactors support getApplicableRefactors refactors Issue:https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICE3WK Signed-off-by: leo9001 --- ets2panda/bindings/native/src/lsp.cpp | 62 ++++ .../bindings/src/Es2pandaNativeModule.ts | 12 +- ets2panda/bindings/src/lspNode.ts | 21 +- ets2panda/bindings/src/lsp_helper.ts | 8 +- ets2panda/lsp/BUILD.gn | 4 + ets2panda/lsp/CMakeLists.txt | 4 + ets2panda/lsp/include/api.h | 5 +- ets2panda/lsp/include/internal_api.h | 1 + .../lsp/include/refactors/convert_chain.h | 35 +++ .../lsp/include/refactors/convert_export.h | 40 +++ .../lsp/include/refactors/convert_import.h | 43 +++ .../lsp/include/refactors/convert_template.h | 34 +++ .../lsp/include/refactors/refactor_types.h | 14 +- ets2panda/lsp/src/api.cpp | 8 +- ets2panda/lsp/src/internal_api.cpp | 11 + ets2panda/lsp/src/refactors/convert_chain.cpp | 79 +++++ .../lsp/src/refactors/convert_export.cpp | 76 +++++ .../lsp/src/refactors/convert_function.cpp | 32 +- .../lsp/src/refactors/convert_import.cpp | 86 ++++++ .../lsp/src/refactors/convert_template.cpp | 65 ++++ ets2panda/test/unit/lsp/CMakeLists.txt | 20 ++ .../unit/lsp/refactors_convert_chain_test.cpp | 121 ++++++++ .../lsp/refactors_convert_export_test.cpp | 101 +++++++ .../lsp/refactors_convert_function_test.cpp | 281 ++++++++++++++++++ .../lsp/refactors_convert_import_test.cpp | 78 +++++ .../lsp/refactors_convert_template_test.cpp | 234 +++++++++++++++ 26 files changed, 1451 insertions(+), 24 deletions(-) create mode 100644 ets2panda/lsp/include/refactors/convert_chain.h create mode 100644 ets2panda/lsp/include/refactors/convert_export.h create mode 100644 ets2panda/lsp/include/refactors/convert_import.h create mode 100644 ets2panda/lsp/include/refactors/convert_template.h create mode 100644 ets2panda/lsp/src/refactors/convert_chain.cpp create mode 100644 ets2panda/lsp/src/refactors/convert_export.cpp create mode 100644 ets2panda/lsp/src/refactors/convert_import.cpp create mode 100644 ets2panda/lsp/src/refactors/convert_template.cpp create mode 100644 ets2panda/test/unit/lsp/refactors_convert_chain_test.cpp create mode 100644 ets2panda/test/unit/lsp/refactors_convert_export_test.cpp create mode 100644 ets2panda/test/unit/lsp/refactors_convert_function_test.cpp create mode 100644 ets2panda/test/unit/lsp/refactors_convert_import_test.cpp create mode 100644 ets2panda/test/unit/lsp/refactors_convert_template_test.cpp diff --git a/ets2panda/bindings/native/src/lsp.cpp b/ets2panda/bindings/native/src/lsp.cpp index 21152cd337..589147ae3c 100644 --- a/ets2panda/bindings/native/src/lsp.cpp +++ b/ets2panda/bindings/native/src/lsp.cpp @@ -1006,6 +1006,68 @@ KNativePointer impl_getClassHierarchies(KNativePointer context, KStringPtr &file } TS_INTEROP_3(getClassHierarchies, KNativePointer, KNativePointer, KStringPtr, KInt) +KNativePointer impl_getApplicableRefactors(KNativePointer context, KStringPtr &kindPtr, KInt position) +{ + LSPAPI const *ctx = GetImpl(); + auto *result = new std::vector(ctx->getApplicableRefactors( + reinterpret_cast(context), GetStringCopy(kindPtr), static_cast(position))); + return result; +} +TS_INTEROP_3(getApplicableRefactors, KNativePointer, KNativePointer, KStringPtr, KInt) + +KNativePointer impl_getApplicableRefactorInfoList(KNativePointer infosPtr) +{ + auto *infos = reinterpret_cast *>(infosPtr); + std::vector ptrs; + for (auto &info : *infos) { + ptrs.push_back(new ark::es2panda::lsp::ApplicableRefactorInfo(info)); + } + return new std::vector(ptrs); +} +TS_INTEROP_1(getApplicableRefactorInfoList, KNativePointer, KNativePointer) + +KNativePointer impl_getRefactorActionName(KNativePointer refactorActionPtr) +{ + auto *refactorAction = reinterpret_cast(refactorActionPtr); + return new std::string(refactorAction->name); +} +TS_INTEROP_1(getRefactorActionName, KNativePointer, KNativePointer) + +KNativePointer impl_getRefactorActionDescription(KNativePointer refactorActionPtr) +{ + auto *refactorAction = reinterpret_cast(refactorActionPtr); + return new std::string(refactorAction->description); +} +TS_INTEROP_1(getRefactorActionDescription, KNativePointer, KNativePointer) + +KNativePointer impl_getRefactorActionKind(KNativePointer refactorActionPtr) +{ + auto *refactorAction = reinterpret_cast(refactorActionPtr); + return new std::string(refactorAction->kind); +} +TS_INTEROP_1(getRefactorActionKind, KNativePointer, KNativePointer) + +KNativePointer impl_getApplicableRefactorName(KNativePointer applRefsPtr) +{ + auto *applRefsInfo = reinterpret_cast(applRefsPtr); + return new std::string(applRefsInfo->name); +} +TS_INTEROP_1(getApplicableRefactorName, KNativePointer, KNativePointer) + +KNativePointer impl_getApplicableRefactorDescription(KNativePointer applRefsPtr) +{ + auto *applRefsInfo = reinterpret_cast(applRefsPtr); + return new std::string(applRefsInfo->description); +} +TS_INTEROP_1(getApplicableRefactorDescription, KNativePointer, KNativePointer) + +KNativePointer impl_getApplicableRefactorAction(KNativePointer applRefsPtr) +{ + auto *applRefsInfo = reinterpret_cast(applRefsPtr); + return new ark::es2panda::lsp::RefactorAction(applRefsInfo->action); +} +TS_INTEROP_1(getApplicableRefactorAction, KNativePointer, KNativePointer) + KNativePointer impl_getClassHierarchyList(KNativePointer infosPtr) { auto *infos = reinterpret_cast *>(infosPtr); diff --git a/ets2panda/bindings/src/Es2pandaNativeModule.ts b/ets2panda/bindings/src/Es2pandaNativeModule.ts index b998458325..0df1bd8fa6 100644 --- a/ets2panda/bindings/src/Es2pandaNativeModule.ts +++ b/ets2panda/bindings/src/Es2pandaNativeModule.ts @@ -355,19 +355,23 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } - _getRefactorAction(ptr: KNativePointer): KPtr { + _getApplicableRefactorAction(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getApplicableRefactors(context: KNativePointer, kind: String, position: number): KPtr { + _getApplicableRefactorName(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getApplicableRefactorName(ptr: KNativePointer): KPtr { + _getApplicableRefactorDescription(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getApplicableRefactorDescription(ptr: KNativePointer): KPtr { + _getApplicableRefactors(context: KNativePointer, kind: String, position: KInt): KPtr { + throw new Error('Not implemented'); + } + + _getApplicableRefactorInfoList(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } diff --git a/ets2panda/bindings/src/lspNode.ts b/ets2panda/bindings/src/lspNode.ts index f553bf46c6..ebaad9afa7 100644 --- a/ets2panda/bindings/src/lspNode.ts +++ b/ets2panda/bindings/src/lspNode.ts @@ -754,7 +754,7 @@ export class LspSafeDeleteLocation extends LspNode { readonly safeDeleteLocationInfos: LspSafeDeleteLocationInfo[]; } -export class LspRefactorAction extends LspNode { +export class RefactorAction extends LspNode { constructor(peer: KNativePointer) { super(peer); this.name = unpackString(global.es2panda._getRefactorActionName(peer)); @@ -766,17 +766,30 @@ export class LspRefactorAction extends LspNode { readonly kind: String; } -export class LspApplicableRefactorInfo extends LspNode { +export class ApplicableRefactorItemInfo extends LspNode { constructor(peer: KNativePointer) { super(peer); this.name = unpackString(global.es2panda._getApplicableRefactorName(peer)); this.description = unpackString(global.es2panda._getApplicableRefactorDescription(peer)); - this.action = new LspRefactorAction(global.es2panda._getRefactorAction(peer)); + this.action = new RefactorAction(global.es2panda._getApplicableRefactorAction(peer)); } readonly name: String; readonly description: String; - readonly action: LspRefactorAction; + readonly action: RefactorAction; +} + +export class LspApplicableRefactorInfo extends LspNode { + constructor(peer: KNativePointer) { + super(peer); + this.applicableRefactorInfo = new NativePtrDecoder() + .decode(global.es2panda._getApplicableRefactorInfoList(peer)) + .map((elPeer: KNativePointer) => { + return new ApplicableRefactorItemInfo(elPeer); + }); + } + + readonly applicableRefactorInfo: ApplicableRefactorItemInfo[]; } export class LspTypeHierarchies extends LspNode { diff --git a/ets2panda/bindings/src/lsp_helper.ts b/ets2panda/bindings/src/lsp_helper.ts index bdf10c82e9..9cb48cb3b3 100644 --- a/ets2panda/bindings/src/lsp_helper.ts +++ b/ets2panda/bindings/src/lsp_helper.ts @@ -30,6 +30,7 @@ import { LspLineAndCharacter, LspReferenceData, LspClassConstructorInfo, + ApplicableRefactorItemInfo, LspApplicableRefactorInfo, CompletionEntryDetails, LspFileTextChanges, @@ -535,7 +536,7 @@ export class Lsp { return new CompletionEntryDetails(ptr); } - getApplicableRefactors(filename: String, kind: String, offset: number): LspApplicableRefactorInfo { + getApplicableRefactors(filename: String, kind: String, offset: number): ApplicableRefactorItemInfo[] { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); let arktsconfig = this.fileNameToArktsconfig[filePath]; @@ -547,11 +548,14 @@ export class Lsp { lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); + let result: ApplicableRefactorItemInfo[] = []; let ptr = global.es2panda._getApplicableRefactors(localCtx, kind, offset); PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); lspDriverHelper.destroyContext(localCtx); lspDriverHelper.destroyConfig(localCfg); - return new LspApplicableRefactorInfo(ptr); + let refs = new LspApplicableRefactorInfo(ptr); + result.push(...refs.applicableRefactorInfo); + return Array.from(new Set(result)); } getClassConstructorInfo(filename: String, offset: number, properties: string[]): LspClassConstructorInfo { diff --git a/ets2panda/lsp/BUILD.gn b/ets2panda/lsp/BUILD.gn index ff8b060335..c2b590a5b2 100644 --- a/ets2panda/lsp/BUILD.gn +++ b/ets2panda/lsp/BUILD.gn @@ -73,7 +73,11 @@ ohos_source_set("libes2panda_lsp_static") { "src/organize_imports.cpp", "src/quick_info.cpp", "src/refactor_provider.cpp", + "src/refactors/convert_chain.cpp", + "src/refactors/convert_export.cpp", "src/refactors/convert_function.cpp", + "src/refactors/convert_import.cpp", + "src/refactors/convert_template.cpp", "src/refactors/refactor_types.cpp", "src/references.cpp", "src/register_code_fix/add_missing_declare_property.cpp", diff --git a/ets2panda/lsp/CMakeLists.txt b/ets2panda/lsp/CMakeLists.txt index 9cd9dab4f2..67fd269734 100644 --- a/ets2panda/lsp/CMakeLists.txt +++ b/ets2panda/lsp/CMakeLists.txt @@ -32,7 +32,11 @@ set(ES2PANDA_LSP_SRC ./src/refactors/refactor_types.cpp ./src/applicable_refactors.cpp ./src/refactor_provider.cpp + ./src/refactors/convert_chain.cpp + ./src/refactors/convert_export.cpp ./src/refactors/convert_function.cpp + ./src/refactors/convert_import.cpp + ./src/refactors/convert_template.cpp ./src/formatting/formatting_context.cpp ./src/formatting/formatting_settings.cpp ./src/formatting/formatting.cpp diff --git a/ets2panda/lsp/include/api.h b/ets2panda/lsp/include/api.h index 75ded0e385..44f28f20b9 100644 --- a/ets2panda/lsp/include/api.h +++ b/ets2panda/lsp/include/api.h @@ -493,8 +493,9 @@ struct CodeFixOptions { typedef struct LSPAPI { DefinitionInfo (*getDefinitionAtPosition)(es2panda_Context *context, size_t position); - std::vector (*getApplicableRefactors)( - const ark::es2panda::lsp::RefactorContext *context); + std::vector (*getApplicableRefactors)(es2panda_Context *context, + const char *kind, + size_t position); DefinitionInfo (*getImplementationAtPosition)(es2panda_Context *context, size_t position); bool (*isPackageModule)(es2panda_Context *context); ark::es2panda::lsp::CompletionEntryKind (*getAliasScriptElementKind)(es2panda_Context *context, size_t position); diff --git a/ets2panda/lsp/include/internal_api.h b/ets2panda/lsp/include/internal_api.h index 03fa2abb1b..91cd095e6a 100644 --- a/ets2panda/lsp/include/internal_api.h +++ b/ets2panda/lsp/include/internal_api.h @@ -129,6 +129,7 @@ std::string GetOwnerId(ir::AstNode *node); std::string GetIdentifierName(ir::AstNode *node); bool NodeHasTokens(const ir::AstNode *node); void FindAllChild(const ir::AstNode *ast, const ir::NodePredicate &cb, ArenaVector &results); +ir::AstNode *FindAncestor(ir::AstNode *node, const ir::NodePredicate &cb); std::vector GetCodeFixesAtPositionImpl(es2panda_Context *context, size_t startPosition, size_t endPosition, std::vector &errorCodes, CodeFixOptions &codeFixOptions); diff --git a/ets2panda/lsp/include/refactors/convert_chain.h b/ets2panda/lsp/include/refactors/convert_chain.h new file mode 100644 index 0000000000..44eee48056 --- /dev/null +++ b/ets2panda/lsp/include/refactors/convert_chain.h @@ -0,0 +1,35 @@ +/** + * 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 CONVERT_CHAIN_H +#define CONVERT_CHAIN_H + +#include "refactor_types.h" + +namespace ark::es2panda::lsp { + +constexpr RefactorActionView TO_NAMED_CHAIN_ACTION {"Convert to optional chain expression", + "Convert to optional chain expression", + "refactor.rewrite.expression.optionalChain"}; +class ConvertChainRefactor : public Refactor { +public: + ConvertChainRefactor(); + ApplicableRefactorInfo GetAvailableActions(const RefactorContext &context) const override; + std::unique_ptr GetEditsForAction(const RefactorContext &context, + const std::string &actionName) const override; +}; +} // namespace ark::es2panda::lsp + +#endif \ No newline at end of file diff --git a/ets2panda/lsp/include/refactors/convert_export.h b/ets2panda/lsp/include/refactors/convert_export.h new file mode 100644 index 0000000000..c4d4fd7982 --- /dev/null +++ b/ets2panda/lsp/include/refactors/convert_export.h @@ -0,0 +1,40 @@ +/** + * 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 CONVERT_EXPORT_H +#define CONVERT_EXPORT_H + +#include "refactor_types.h" + +namespace ark::es2panda::lsp { + +constexpr RefactorActionView TO_NAMED_EXPORT_ACTION {"Convert default export to named export", + "Convert default export to named export", + "refactor.rewrite.export.named"}; +constexpr RefactorActionView TO_DEFAULT_EXPORT_ACTION {"Convert named export to default export", + "Convert named export to default export", + "refactor.rewrite.export.default"}; + +class ConvertExportRefactor : public Refactor { +public: + ConvertExportRefactor(); + ApplicableRefactorInfo GetAvailableActions(const RefactorContext &context) const override; + std::unique_ptr GetEditsForAction(const RefactorContext &context, + const std::string &actionName) const override; +}; + +} // namespace ark::es2panda::lsp + +#endif // CONVERT_EXPORT_H diff --git a/ets2panda/lsp/include/refactors/convert_import.h b/ets2panda/lsp/include/refactors/convert_import.h new file mode 100644 index 0000000000..a41f5f5b6d --- /dev/null +++ b/ets2panda/lsp/include/refactors/convert_import.h @@ -0,0 +1,43 @@ +/** + * 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 CONVERT_IMPORT_H +#define CONVERT_IMPORT_H + +#include "refactor_types.h" + +namespace ark::es2panda::lsp { + +constexpr RefactorActionView TO_NAMED_IMPORT_ACTION {"Convert namespace import to named imports", + "Convert namespace import to named imports", + "refactor.rewrite.import.named"}; +constexpr RefactorActionView TO_NAMESPACE_IMPORT_ACTION {"Convert named imports to namespace import", + "Convert named imports to namespace import", + "refactor.rewrite.import.namespace"}; +constexpr RefactorActionView TO_DEFAULT_IMPORT_ACTION {"Convert named imports to default import", + "Convert named imports to default import", + "refactor.rewrite.import.default"}; + +class ConvertImportRefactor : public Refactor { +public: + ConvertImportRefactor(); + ApplicableRefactorInfo GetAvailableActions(const RefactorContext &context) const override; + std::unique_ptr GetEditsForAction(const RefactorContext &context, + const std::string &actionName) const override; +}; + +} // namespace ark::es2panda::lsp + +#endif // CONVERT_IMPORT_H diff --git a/ets2panda/lsp/include/refactors/convert_template.h b/ets2panda/lsp/include/refactors/convert_template.h new file mode 100644 index 0000000000..4c27a6d9c7 --- /dev/null +++ b/ets2panda/lsp/include/refactors/convert_template.h @@ -0,0 +1,34 @@ +/** + * 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 CONVERT_TEMPLATE_H +#define CONVERT_TEMPLATE_H + +#include "refactor_types.h" + +namespace ark::es2panda::lsp { + +constexpr RefactorActionView TO_NAMED_TEMPLATE_ACTION {"Convert to template string", "Convert to template string", + "refactor.rewrite.string"}; +class ConvertTemplateRefactor : public Refactor { +public: + ConvertTemplateRefactor(); + ApplicableRefactorInfo GetAvailableActions(const RefactorContext &context) const override; + std::unique_ptr GetEditsForAction(const RefactorContext &context, + const std::string &actionName) const override; +}; +} // namespace ark::es2panda::lsp + +#endif \ No newline at end of file diff --git a/ets2panda/lsp/include/refactors/refactor_types.h b/ets2panda/lsp/include/refactors/refactor_types.h index 72e3dd74ea..513ba22f97 100644 --- a/ets2panda/lsp/include/refactors/refactor_types.h +++ b/ets2panda/lsp/include/refactors/refactor_types.h @@ -49,10 +49,10 @@ struct TextRange { }; struct RefactorContext { - ark::es2panda::lsp::CancellationToken *cancellationToken; - ark::es2panda::lsp::UserPreferences *preferences; - TextRange span; - es2panda_Context *context; + ark::es2panda::lsp::CancellationToken *cancellationToken = nullptr; + ark::es2panda::lsp::UserPreferences *preferences = nullptr; + TextRange span = {0, 0}; + es2panda_Context *context = nullptr; std::string kind; }; @@ -76,10 +76,16 @@ using ApplicableRefactorInfo = struct ApplicableRefactorInfo { namespace refactor_name { constexpr std::string_view CONVERT_FUNCTION_REFACTOR_NAME = "Convert arrow function or function expression"; +constexpr std::string_view CONVERT_EXPORT_REFACTOR_NAME = "Convert export"; +constexpr std::string_view CONVERT_IMPORT_REFACTOR_NAME = "Convert import"; +constexpr std::string_view CONVERT_TEMPLATE_REFACTOR_NAME = "Convert to template string"; +constexpr std::string_view CONVERT_CHAIN_REFACTOR_NAME = "Convert to optional chain expression"; } // namespace refactor_name namespace refactor_description { constexpr std::string_view CONVERT_FUNCTION_REFACTOR_DESC = "Convert arrow function or function expression"; +constexpr std::string_view CONVERT_TEMPLATE_REFACTOR_DESC = "Convert to template string"; +constexpr std::string_view CONVERT_CHAIN_REFACTOR_DESC = "Convert to optional chain expression"; } // namespace refactor_description class Refactor { diff --git a/ets2panda/lsp/src/api.cpp b/ets2panda/lsp/src/api.cpp index f13d00069e..2b7eef45ac 100644 --- a/ets2panda/lsp/src/api.cpp +++ b/ets2panda/lsp/src/api.cpp @@ -379,9 +379,13 @@ LineAndCharacter ToLineColumnOffsetWrapper(es2panda_Context *context, size_t pos // Returns type of refactoring and action that can be performed based // on the input kind information and cursor position -std::vector GetApplicableRefactors(const RefactorContext *context) +std::vector GetApplicableRefactors(es2panda_Context *context, const char *kind, size_t position) { - auto result = GetApplicableRefactorsImpl(context); + RefactorContext refactorContext; + refactorContext.context = context; + refactorContext.kind = kind; + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); return result; } diff --git a/ets2panda/lsp/src/internal_api.cpp b/ets2panda/lsp/src/internal_api.cpp index 795777e89e..efeee45bfd 100644 --- a/ets2panda/lsp/src/internal_api.cpp +++ b/ets2panda/lsp/src/internal_api.cpp @@ -652,6 +652,17 @@ void FindAllChild(const ir::AstNode *ast, const ir::NodePredicate &cb, ArenaVect ast->Iterate([&results, cb](ir::AstNode *child) { FindAllChildHelper(child, cb, results); }); } +ir::AstNode *FindAncestor(ir::AstNode *node, const ir::NodePredicate &cb) +{ + while (node != nullptr) { + if (cb(node)) { + return node; + } + node = node->Parent(); + } + return node; +} + ArenaVector FindReferencesByName(ir::AstNode *ast, ir::AstNode *decl, ir::AstNode *node, ArenaAllocator *allocator) { diff --git a/ets2panda/lsp/src/refactors/convert_chain.cpp b/ets2panda/lsp/src/refactors/convert_chain.cpp new file mode 100644 index 0000000000..1d76ecaee2 --- /dev/null +++ b/ets2panda/lsp/src/refactors/convert_chain.cpp @@ -0,0 +1,79 @@ +/** + * 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 "refactors/convert_chain.h" +#include "refactor_provider.h" +#include "internal_api.h" + +namespace ark::es2panda::lsp { + +ark::es2panda::ir::AstNode *FindType(ark::es2panda::ir::AstNode *node); + +ConvertChainRefactor::ConvertChainRefactor() +{ + AddKind(std::string(TO_NAMED_CHAIN_ACTION.kind)); +} + +ark::es2panda::ir::AstNode *FindType(ark::es2panda::ir::AstNode *node) +{ + if ((node != nullptr) && (node->Parent() != nullptr)) { + if (node->Parent()->IsExpression()) { + return node; + } + auto cb = [](ir::AstNode *ancestorNode) { return ancestorNode->IsConditionalExpression(); }; + node = FindAncestor(node, cb); + return node; + } + return node; +} + +ApplicableRefactorInfo ConvertChainRefactor::GetAvailableActions(const RefactorContext &refContext) const +{ + es2panda_Context *context = refContext.context; + size_t position = refContext.span.pos; + + ApplicableRefactorInfo res; + + if (!IsKind(refContext.kind)) { + return res; + } + auto node = GetTouchingToken(context, position, false); + if (node == nullptr) { + return res; + } + + auto nodedec1 = FindType(node); + if (nodedec1 != nullptr && (nodedec1->IsConditionalExpression() || nodedec1->IsExpression())) { + res.name = refactor_name::CONVERT_CHAIN_REFACTOR_NAME; + res.description = refactor_description::CONVERT_CHAIN_REFACTOR_DESC; + res.action.kind = std::string(TO_NAMED_CHAIN_ACTION.kind); + res.action.name = std::string(TO_NAMED_CHAIN_ACTION.name); + res.action.description = std::string(TO_NAMED_CHAIN_ACTION.description); + } + return res; +} + +std::unique_ptr ConvertChainRefactor::GetEditsForAction(const RefactorContext &context, + const std::string &actionName) const +{ + (void)context; + (void)actionName; + return std::make_unique(); +} +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects, cert-err58-cpp) +AutoRefactorRegister g_convertChainRefactorRegister("ConvertChainRefactor"); + +} // namespace ark::es2panda::lsp \ No newline at end of file diff --git a/ets2panda/lsp/src/refactors/convert_export.cpp b/ets2panda/lsp/src/refactors/convert_export.cpp new file mode 100644 index 0000000000..3c76a57609 --- /dev/null +++ b/ets2panda/lsp/src/refactors/convert_export.cpp @@ -0,0 +1,76 @@ +/** + * 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 "refactors/convert_export.h" +#include "refactor_provider.h" +#include "internal_api.h" + +namespace ark::es2panda::lsp { + +ConvertExportRefactor::ConvertExportRefactor() +{ + AddKind(std::string(TO_NAMED_EXPORT_ACTION.kind)); + AddKind(std::string(TO_DEFAULT_EXPORT_ACTION.kind)); +} + +ApplicableRefactorInfo ConvertExportRefactor::GetAvailableActions(const RefactorContext &refContext) const +{ + es2panda_Context *context = refContext.context; + size_t position = refContext.span.pos; + + ApplicableRefactorInfo res; + + if (!IsKind(refContext.kind)) { + return res; + } + + auto node = GetTouchingToken(context, position, false); + if (node == nullptr) { + return res; + } + auto cb = [](ir::AstNode *ancestorNode) { return ancestorNode->IsDefaultExported() || ancestorNode->IsExported(); }; + auto ancestor = FindAncestor(node, cb); + if (ancestor == nullptr) { + return res; + } + if (ancestor->IsDefaultExported()) { + res.name = refactor_name::CONVERT_EXPORT_REFACTOR_NAME; + res.description = std::string(TO_NAMED_EXPORT_ACTION.description); + res.action.kind = std::string(TO_NAMED_EXPORT_ACTION.kind); + res.action.name = std::string(TO_NAMED_EXPORT_ACTION.name); + res.action.description = std::string(TO_NAMED_EXPORT_ACTION.description); + } else if (ancestor->IsExported()) { + res.name = refactor_name::CONVERT_EXPORT_REFACTOR_NAME; + res.description = std::string(TO_DEFAULT_EXPORT_ACTION.description); + res.action.kind = std::string(TO_DEFAULT_EXPORT_ACTION.kind); + res.action.name = std::string(TO_DEFAULT_EXPORT_ACTION.name); + res.action.description = std::string(TO_DEFAULT_EXPORT_ACTION.description); + } + + return res; +} + +std::unique_ptr ConvertExportRefactor::GetEditsForAction(const RefactorContext &context, + const std::string &actionName) const +{ + (void)context; + (void)actionName; + return std::make_unique(); +} +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects, cert-err58-cpp) +AutoRefactorRegister g_convertExportRefactorRegister("ConvertExportRefactor"); + +} // namespace ark::es2panda::lsp diff --git a/ets2panda/lsp/src/refactors/convert_function.cpp b/ets2panda/lsp/src/refactors/convert_function.cpp index e594fa230a..097fe6fe65 100644 --- a/ets2panda/lsp/src/refactors/convert_function.cpp +++ b/ets2panda/lsp/src/refactors/convert_function.cpp @@ -16,7 +16,6 @@ #include #include "refactors/convert_function.h" #include "refactor_provider.h" -#include "compiler/lowering/util.h" #include "internal_api.h" namespace ark::es2panda::lsp { @@ -28,6 +27,28 @@ ConvertFunctionRefactor::ConvertFunctionRefactor() AddKind(std::string(TO_ARROW_FUNCTION_ACTION.kind)); } +bool HasArrowFunction(ark::es2panda::ir::AstNode *node) +{ + if (!node->IsCallExpression() && !node->IsClassProperty() && !node->IsVariableDeclarator()) { + return false; + } + if ((node->IsClassProperty() && node->AsClassProperty()->Value() != nullptr && + node->AsClassProperty()->Value()->IsArrowFunctionExpression()) || + (node->IsVariableDeclarator() && node->AsVariableDeclarator()->Init() != nullptr && + node->AsVariableDeclarator()->Init()->IsArrowFunctionExpression())) { + return true; + } + if (node->IsCallExpression()) { + auto arguments = node->AsCallExpression()->Arguments(); + for (auto argument : arguments) { + if (argument->IsArrowFunctionExpression()) { + return true; + } + } + } + return false; +} + ApplicableRefactorInfo ConvertFunctionRefactor::GetAvailableActions(const RefactorContext &refContext) const { es2panda_Context *context = refContext.context; @@ -40,13 +61,12 @@ ApplicableRefactorInfo ConvertFunctionRefactor::GetAvailableActions(const Refact } auto node = GetTouchingToken(context, position, false); - if (node == nullptr || !node->IsIdentifier()) { + if (node == nullptr) { return res; } - - auto nodeDecl = compiler::DeclarationFromIdentifier(node->AsIdentifier()); - if (nodeDecl != nullptr && nodeDecl->IsClassProperty() && nodeDecl->AsClassProperty()->Value() != nullptr && - nodeDecl->AsClassProperty()->Value()->IsArrowFunctionExpression()) { + auto cb = [](ir::AstNode *ancestorNode) { return HasArrowFunction(ancestorNode); }; + auto ancestor = FindAncestor(node, cb); + if (ancestor != nullptr && ancestor->IsClassProperty()) { res.name = refactor_name::CONVERT_FUNCTION_REFACTOR_NAME; res.description = refactor_description::CONVERT_FUNCTION_REFACTOR_DESC; res.action.kind = std::string(TO_NAMED_FUNCTION_ACTION.kind); diff --git a/ets2panda/lsp/src/refactors/convert_import.cpp b/ets2panda/lsp/src/refactors/convert_import.cpp new file mode 100644 index 0000000000..fbe48a59f9 --- /dev/null +++ b/ets2panda/lsp/src/refactors/convert_import.cpp @@ -0,0 +1,86 @@ +/** + * 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 "refactors/convert_import.h" +#include "refactor_provider.h" +#include "internal_api.h" + +namespace ark::es2panda::lsp { + +ConvertImportRefactor::ConvertImportRefactor() +{ + AddKind(std::string(TO_NAMED_IMPORT_ACTION.kind)); + AddKind(std::string(TO_NAMESPACE_IMPORT_ACTION.kind)); + AddKind(std::string(TO_DEFAULT_IMPORT_ACTION.kind)); +} + +ApplicableRefactorInfo ConvertImportRefactor::GetAvailableActions(const RefactorContext &refContext) const +{ + es2panda_Context *context = refContext.context; + size_t position = refContext.span.pos; + + ApplicableRefactorInfo res; + + if (!IsKind(refContext.kind)) { + return res; + } + + auto node = GetTouchingToken(context, position, false); + if (node == nullptr) { + return res; + } + auto cb = [](ir::AstNode *ancestorNode) { + return ancestorNode->IsETSImportDeclaration() || ancestorNode->IsImportNamespaceSpecifier() || + ancestorNode->IsImportSpecifier(); + }; + auto ancestor = FindAncestor(node, cb); + if (ancestor != nullptr && ancestor->IsETSImportDeclaration()) { + auto specifiers = ancestor->AsETSImportDeclaration()->Specifiers(); + if (!specifiers.empty()) { + ancestor = specifiers[0]; + } + } + if (ancestor == nullptr) { + return res; + } + if (ancestor->IsImportNamespaceSpecifier()) { + res.name = refactor_name::CONVERT_IMPORT_REFACTOR_NAME; + res.description = std::string(TO_NAMED_IMPORT_ACTION.description); + res.action.kind = std::string(TO_NAMED_IMPORT_ACTION.kind); + res.action.name = std::string(TO_NAMED_IMPORT_ACTION.name); + res.action.description = std::string(TO_NAMED_IMPORT_ACTION.description); + } else if (ancestor->IsImportSpecifier()) { + res.name = refactor_name::CONVERT_IMPORT_REFACTOR_NAME; + res.description = std::string(TO_NAMESPACE_IMPORT_ACTION.description); + res.action.kind = std::string(TO_NAMESPACE_IMPORT_ACTION.kind); + res.action.name = std::string(TO_NAMESPACE_IMPORT_ACTION.name); + res.action.description = std::string(TO_NAMESPACE_IMPORT_ACTION.description); + } + + return res; +} + +std::unique_ptr ConvertImportRefactor::GetEditsForAction(const RefactorContext &context, + const std::string &actionName) const +{ + (void)context; + (void)actionName; + return std::make_unique(); +} +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects, cert-err58-cpp) +AutoRefactorRegister g_convertImportRefactorRegister("ConvertImportRefactor"); + +} // namespace ark::es2panda::lsp diff --git a/ets2panda/lsp/src/refactors/convert_template.cpp b/ets2panda/lsp/src/refactors/convert_template.cpp new file mode 100644 index 0000000000..ed3e76f0e7 --- /dev/null +++ b/ets2panda/lsp/src/refactors/convert_template.cpp @@ -0,0 +1,65 @@ +/** + * 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 "refactors/convert_template.h" +#include "refactor_provider.h" +#include "internal_api.h" + +namespace ark::es2panda::lsp { + +ConvertTemplateRefactor::ConvertTemplateRefactor() +{ + AddKind(std::string(TO_NAMED_TEMPLATE_ACTION.kind)); +} + +ApplicableRefactorInfo ConvertTemplateRefactor::GetAvailableActions(const RefactorContext &refContext) const +{ + es2panda_Context *context = refContext.context; + size_t position = refContext.span.pos; + + ApplicableRefactorInfo res; + + if (!IsKind(refContext.kind)) { + return res; + } + auto node = GetTouchingToken(context, position, false); + if (node == nullptr) { + return res; + } + + if (node != nullptr && node->Parent() != nullptr && + (node->Parent()->IsExpression() && node->Parent()->IsBinaryExpression())) { + res.name = refactor_name::CONVERT_TEMPLATE_REFACTOR_NAME; + res.description = refactor_description::CONVERT_TEMPLATE_REFACTOR_DESC; + res.action.kind = std::string(TO_NAMED_TEMPLATE_ACTION.kind); + res.action.name = std::string(TO_NAMED_TEMPLATE_ACTION.name); + res.action.description = std::string(TO_NAMED_TEMPLATE_ACTION.description); + } + + return res; +} + +std::unique_ptr ConvertTemplateRefactor::GetEditsForAction(const RefactorContext &context, + const std::string &actionName) const +{ + (void)context; + (void)actionName; + return std::make_unique(); +} +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects, cert-err58-cpp) +AutoRefactorRegister g_convertTemplateRefactorRegister("ConvertTemplateRefactor"); + +} // namespace ark::es2panda::lsp diff --git a/ets2panda/test/unit/lsp/CMakeLists.txt b/ets2panda/test/unit/lsp/CMakeLists.txt index 8106544cc6..3985870649 100644 --- a/ets2panda/test/unit/lsp/CMakeLists.txt +++ b/ets2panda/test/unit/lsp/CMakeLists.txt @@ -99,6 +99,26 @@ ets2panda_add_gtest(lsp_api_test_class_hierarchies CPP_SOURCES class_hierarchys_test.cpp ) +ets2panda_add_gtest(lsp_api_test_refactors_convert_chain CPP_SOURCES + refactors_convert_chain_test.cpp +) + +ets2panda_add_gtest(lsp_api_test_refactors_convert_export CPP_SOURCES + refactors_convert_export_test.cpp +) + +ets2panda_add_gtest(lsp_api_test_refactors_convert_function CPP_SOURCES + refactors_convert_function_test.cpp +) + +ets2panda_add_gtest(lsp_api_test_refactors_convert_import CPP_SOURCES + refactors_convert_import_test.cpp +) + +ets2panda_add_gtest(lsp_api_test_refactors_convert_template CPP_SOURCES + refactors_convert_template_test.cpp +) + ets2panda_add_gtest(lsp_api_diagnostics_test CPP_SOURCES get_compiler_options_diagnostics_test.cpp get_diagnostics.cpp diff --git a/ets2panda/test/unit/lsp/refactors_convert_chain_test.cpp b/ets2panda/test/unit/lsp/refactors_convert_chain_test.cpp new file mode 100644 index 0000000000..67e51357ec --- /dev/null +++ b/ets2panda/test/unit/lsp/refactors_convert_chain_test.cpp @@ -0,0 +1,121 @@ +/** + * 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 +#include +#include "lsp_api_test.h" +#include "lsp/include/applicable_refactors.h" + +const size_t REFACTOR_CHAIN_POSITION_OFFSET = 8; + +namespace { +using ark::es2panda::lsp::Initializer; + +class LspChainRefTests : public LSPAPITests { +public: + static constexpr std::string_view TO_NAMED_CHAIN_KIND = "refactor.rewrite.expression.optionalChain"; + static constexpr std::string_view TO_NAMED_CHAIN_NAME = "Convert to optional chain expression"; +}; + +TEST_F(LspChainRefTests, ConvertChainRefactor1) +{ + std::vector files = {"ConvertChainRefactor1.ets"}; + std::vector texts = {R"(let a = {b: ()=>{return () =>{c:0}}}/*1*/a&&a.b&&a.b()().c/*2*/;)"}; + + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + auto pos = texts[0].find("/*1*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_CHAIN_KIND); + refactorContext.span.pos = pos + REFACTOR_CHAIN_POSITION_OFFSET; + auto result = GetApplicableRefactorsImpl(&refactorContext); + + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_CHAIN_NAME), result[0].action.name); +} + +TEST_F(LspChainRefTests, ConvertChainRefactor2) +{ + std::vector files = {"ConvertChainRefactor2.ets"}; + std::vector texts = {R"(interface Foo{ +bar?:{ +baz?:string | null; +} +} +declare let foo: Foo; +let ccc = /*1*/foo.bar ? +foo.bar.baz : "whenFalse";/*2*/ + )"}; + + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + auto pos = texts[0].find("/*1*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_CHAIN_KIND); + refactorContext.span.pos = pos + REFACTOR_CHAIN_POSITION_OFFSET; + auto result = GetApplicableRefactorsImpl(&refactorContext); + + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_CHAIN_NAME), result[0].action.name); +} + +TEST_F(LspChainRefTests, ConvertChainRefactor3) +{ + std::vector files = {"ConvertChainRefactor3.ets"}; + std::vector texts = {R"(interface Foo{ +bar?:{ +baz: string; +} +} +declare let foo: Foo; +let ccc = /*1*/foo.bar? +foo.bar.baz: "whenFalse";/*2*/ + )"}; + + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + auto pos = texts[0].find("/*1*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_CHAIN_KIND); + refactorContext.span.pos = pos + REFACTOR_CHAIN_POSITION_OFFSET; + auto result = GetApplicableRefactorsImpl(&refactorContext); + + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_CHAIN_NAME), result[0].action.name); +} + +} // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/refactors_convert_export_test.cpp b/ets2panda/test/unit/lsp/refactors_convert_export_test.cpp new file mode 100644 index 0000000000..3cf7a64c12 --- /dev/null +++ b/ets2panda/test/unit/lsp/refactors_convert_export_test.cpp @@ -0,0 +1,101 @@ +/** + * 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 +#include +#include "lsp_api_test.h" +#include "lsp/include/applicable_refactors.h" + +namespace { +using ark::es2panda::lsp::Initializer; + +class LspConvExpTests : public LSPAPITests { +public: + static constexpr std::string_view TO_NAMED_EXPORT_KIND = "refactor.rewrite.export.named"; + static constexpr std::string_view TO_NAMED_EXPORT_NAME = "Convert default export to named export"; + static constexpr std::string_view TO_DEFAULT_EXPORT_KIND = "refactor.rewrite.export.default"; + static constexpr std::string_view TO_DEFAULT_EXPORT_NAME = "Convert named export to default export"; +}; + +TEST_F(LspConvExpTests, ConvertExportRefactor1) +{ + std::vector files = {"convertExportRefactor1.ets"}; + std::vector texts = {R"(export default function add(a: number, b: number): number { + return a + b; +})"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 26; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_EXPORT_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_EXPORT_NAME), result[0].action.name); +} + +TEST_F(LspConvExpTests, ConvertExportRefactor2) +{ + std::vector files = {"convertExportRefactor2.ets"}; + std::vector texts = {R"(export default class User { + name: string = ""; +})"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 24; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_EXPORT_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_EXPORT_NAME), result[0].action.name); +} + +TEST_F(LspConvExpTests, ConvertExportRefactor5) +{ + std::vector files = {"convertExportRefactor5.ets"}; + std::vector texts = {R"(export function add(a: number, b: number): number { + return a + b; +})"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 19; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_DEFAULT_EXPORT_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_DEFAULT_EXPORT_NAME), result[0].action.name); +} +} // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/refactors_convert_function_test.cpp b/ets2panda/test/unit/lsp/refactors_convert_function_test.cpp new file mode 100644 index 0000000000..854e59a483 --- /dev/null +++ b/ets2panda/test/unit/lsp/refactors_convert_function_test.cpp @@ -0,0 +1,281 @@ +/** + * 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 +#include +#include "lsp_api_test.h" +#include "lsp/include/applicable_refactors.h" + +namespace { +using ark::es2panda::lsp::Initializer; + +class LspConvFuncTests : public LSPAPITests { +public: + static constexpr std::string_view TO_NAMED_FUNCTION_KIND = "refactor.rewrite.function.named"; + static constexpr std::string_view INVALID_KIND = "aaabbbccc"; + static constexpr std::string_view TO_NAMED_FUNCTION_NAME = "Convert to named function"; +}; + +TEST_F(LspConvFuncTests, ConvertFunctionRefactor1) +{ + std::vector files = {"convertFunctionRefactor1.ets"}; + std::vector texts = {R"(const add = (x: number, y: number): number => { + return x + y; + };)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 8; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_FUNCTION_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_FUNCTION_NAME), result[0].action.name); +} + +TEST_F(LspConvFuncTests, ConvertFunctionRefactor2) +{ + std::vector files = {"convertFunctionRefactor2.ets"}; + std::vector texts = {R"(function sub(a: number, b: number): number{ + return a - b; + };)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 11; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_FUNCTION_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(0, result.size()); +} + +TEST_F(LspConvFuncTests, ConvertFunctionRefactor3) +{ + std::vector files = {"convertFunctionRefactor3.ets"}; + std::vector texts = {R"(const add = (x: number, y: number): number => { + return x + y; + };)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 8; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(INVALID_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(0, result.size()); +} + +TEST_F(LspConvFuncTests, ConvertFunctionRefactor4) +{ + std::vector files = {"convertFunctionRefactor4.ets"}; + std::vector texts = {R"(const foo = a => { +let b = 1; +return a + b; +};)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + size_t const position = 13; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_FUNCTION_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_FUNCTION_NAME), result[0].action.name); +} + +TEST_F(LspConvFuncTests, ConvertFunctionRefactor5) +{ + std::vector files = {"convertFunctionRefactor5.ets"}; + std::vector texts = {R"(function doSomething(a){} +doSomething(() => 1 + 1);)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 41; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_FUNCTION_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(0, result.size()); +} + +TEST_F(LspConvFuncTests, ConvertFunctionRefactor6) +{ + std::vector files = {"convertFunctionRefactor6.ets"}; + std::vector texts = {R"(const foo = (a,b,c) => a + 1;)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 14; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_FUNCTION_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_FUNCTION_NAME), result[0].action.name); +} + +TEST_F(LspConvFuncTests, ConvertFunctionRefactor7) +{ + std::vector files = {"convertFunctionRefactor7.ets"}; + std::vector texts = {R"(const foo = () => //comment +1)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 14; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_FUNCTION_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_FUNCTION_NAME), result[0].action.name); +} + +TEST_F(LspConvFuncTests, ConvertFunctionRefactor8) +{ + std::vector files = {"convertFunctionRefactor8.ets"}; + std::vector texts = {R"(function func() { +const test = () => { +} +})"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 28; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_FUNCTION_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(0, result.size()); +} + +TEST_F(LspConvFuncTests, ConvertFunctionRefactor9) +{ + std::vector files = {"convertFunctionRefactor9.ets"}; + std::vector texts = {R"(class AA { +func() { +const test = () => {} +} +})"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 31; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_FUNCTION_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(0, result.size()); +} + +TEST_F(LspConvFuncTests, ConvertFunctionRefactor10) +{ + std::vector files = {"convertFunctionRefactor10.ets"}; + std::vector texts = {R"(const func = () => { +const test = () => {} +})"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 31; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_FUNCTION_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(0, result.size()); +} + +TEST_F(LspConvFuncTests, ConvertFunctionRefactor11) +{ + std::vector files = {"convertFunctionRefactor11.ets"}; + std::vector texts = {R"(@Component +struct Index { +build() { +Text().onClick(()=>{ +const test = () =>{} +}) +} +})"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 70; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_FUNCTION_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(0, result.size()); +} +} // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/refactors_convert_import_test.cpp b/ets2panda/test/unit/lsp/refactors_convert_import_test.cpp new file mode 100644 index 0000000000..f2945334e0 --- /dev/null +++ b/ets2panda/test/unit/lsp/refactors_convert_import_test.cpp @@ -0,0 +1,78 @@ +/** + * 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 +#include +#include "lsp_api_test.h" +#include "lsp/include/applicable_refactors.h" + +namespace { +using ark::es2panda::lsp::Initializer; + +class LspConvImpTests : public LSPAPITests { +public: + static constexpr std::string_view TO_NAMED_IMPORT_KIND = "refactor.rewrite.import.named"; + static constexpr std::string_view TO_NAMED_IMPORT_NAME = "Convert namespace import to named imports"; + static constexpr std::string_view TO_NAMESPACE_IMPORT_KIND = "refactor.rewrite.import.namespace"; + static constexpr std::string_view TO_NAMESPACE_IMPORT_NAME = "Convert named imports to namespace import"; +}; + +TEST_F(LspConvImpTests, ConvertImportRefactor1) +{ + std::vector files = {"convertImportRefactor1.ets"}; + std::vector texts = {R"(import { add, subtract, multiply } from './math'; + +const result = add(1, subtract(5, multiply(2, 3)));)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 28; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMESPACE_IMPORT_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMESPACE_IMPORT_NAME), result[0].action.name); +} + +TEST_F(LspConvImpTests, ConvertImportRefactor2) +{ + std::vector files = {"convertImportRefactor2.ets"}; + std::vector texts = {R"(import * as math from './math'; + +const result = math.add(1, math.subtract(5, math.multiply(2, 3)));)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + size_t const position = 15; + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_IMPORT_KIND); + refactorContext.span.pos = position; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_IMPORT_NAME), result[0].action.name); +} +} // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/refactors_convert_template_test.cpp b/ets2panda/test/unit/lsp/refactors_convert_template_test.cpp new file mode 100644 index 0000000000..7ed519dbde --- /dev/null +++ b/ets2panda/test/unit/lsp/refactors_convert_template_test.cpp @@ -0,0 +1,234 @@ +/** + * 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 +#include +#include "lsp_api_test.h" +#include "lsp/include/applicable_refactors.h" + +const size_t REFACTOR_TEMPLATE_POSITION_OFFSET = 8; + +namespace { +using ark::es2panda::lsp::Initializer; + +class LspTemplateRefTests : public LSPAPITests { +public: + static constexpr std::string_view TO_NAMED_TEMPLATE_KIND = "refactor.rewrite.string"; + static constexpr std::string_view TO_NAMED_TEMPLATE_NAME = "Convert to template string"; +}; + +TEST_F(LspTemplateRefTests, ConvertTemplateRefactor1) +{ + std::vector files = {"ConvertTemplateRefactor1.ets"}; + std::vector texts = {R"(let a = /*1*/"\\0\\b\\f\\t\\r\\n" +text + "\\n"/*2*/;)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + auto pos = texts[0].find("/*1*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_TEMPLATE_KIND); + refactorContext.span.pos = pos + REFACTOR_TEMPLATE_POSITION_OFFSET; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_TEMPLATE_NAME), result[0].action.name); +} + +TEST_F(LspTemplateRefTests, ConvertTemplateRefactor2) +{ + std::vector files = {"ConvertTemplateRefactor2.ets"}; + std::vector texts = {R"(let b = /*3*/"" + text + ""/*4*/;)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + auto pos = texts[0].find("/*3*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_TEMPLATE_KIND); + refactorContext.span.pos = pos + REFACTOR_TEMPLATE_POSITION_OFFSET; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_TEMPLATE_NAME), result[0].action.name); +} + +TEST_F(LspTemplateRefTests, ConvertTemplateRefactor3) +{ + std::vector files = {"ConvertTemplateRefactor3.ets"}; + std::vector texts = {R"(let c = /*5*/'\$' + text + "\\\\/*6*/";)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + auto pos = texts[0].find("/*5*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_TEMPLATE_KIND); + refactorContext.span.pos = pos + REFACTOR_TEMPLATE_POSITION_OFFSET; + auto result = GetApplicableRefactorsImpl(&refactorContext); + + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_TEMPLATE_NAME), result[0].action.name); +} + +TEST_F(LspTemplateRefTests, ConvertTemplateRefactor4) +{ + std::vector files = {"ConvertTemplateRefactor4.ets"}; + std::vector texts = {R"(let d = /*7*/\'\$\' + text + \'\\\\\'/*8*/;)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + auto pos = texts[0].find("/*7*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_TEMPLATE_KIND); + refactorContext.span.pos = pos + REFACTOR_TEMPLATE_POSITION_OFFSET; + auto result = GetApplicableRefactorsImpl(&refactorContext); + + initializer.DestroyContext(ctx); + ASSERT_EQ(0, result.size()); +} + +TEST_F(LspTemplateRefTests, ConvertTemplateRefactor5) +{ + std::vector files = {"ConvertTemplateRefactor5.ets"}; + std::vector texts = {R"(let e = /*9*/'\$\{' + text + "\}"/*10*/;)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + auto pos = texts[0].find("/*9*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_TEMPLATE_KIND); + refactorContext.span.pos = pos + REFACTOR_TEMPLATE_POSITION_OFFSET; + auto result = GetApplicableRefactorsImpl(&refactorContext); + + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_TEMPLATE_NAME), result[0].action.name); +} + +TEST_F(LspTemplateRefTests, ConvertTemplateRefactor6) +{ + std::vector files = {"ConvertTemplateRefactor6.ets"}; + std::vector texts = {R"(let f = /*11*/ \'\\\$\{\' + text + \'\}\'/*12*/;)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + auto pos = texts[0].find("/*11*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_TEMPLATE_KIND); + refactorContext.span.pos = pos + REFACTOR_TEMPLATE_POSITION_OFFSET; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(0, result.size()); +} + +TEST_F(LspTemplateRefTests, ConvertTemplateRefactor7) +{ + std::vector files = {"ConvertTemplateRefactor7.ets"}; + std::vector texts = {R"(let g = /*13*/'\\\\$ + text + "\\\\"/*14*/;)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + auto pos = texts[0].find("/*13*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_TEMPLATE_KIND); + refactorContext.span.pos = pos + REFACTOR_TEMPLATE_POSITION_OFFSET; + auto result = GetApplicableRefactorsImpl(&refactorContext); + + initializer.DestroyContext(ctx); + ASSERT_EQ(0, result.size()); +} + +TEST_F(LspTemplateRefTests, ConvertTemplateRefactor8) +{ + std::vector files = {"ConvertTemplateRefactor8.ets"}; + std::vector texts = {R"(let h = /*15*/"\\u0041\\u0061" + text + "\\0\\u0000"/*16*/;)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + auto pos = texts[0].find("/*15*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_TEMPLATE_KIND); + refactorContext.span.pos = pos + REFACTOR_TEMPLATE_POSITION_OFFSET; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_TEMPLATE_NAME), result[0].action.name); +} + +TEST_F(LspTemplateRefTests, ConvertTemplateRefactor9) +{ + std::vector files = {"ConvertTemplateRefactor9.ets"}; + std::vector texts = {R"(let i = /*17*/'\$\`' + text + "\`\\\\"/*18*/;)"}; + auto filePaths = CreateTempFile(files, texts); + size_t const expectedFileCount = 1; + ASSERT_EQ(filePaths.size(), expectedFileCount); + + auto pos = texts[0].find("/*17*/"); + ASSERT_NE(pos, std::string::npos); + Initializer initializer = Initializer(); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + ark::es2panda::lsp::RefactorContext refactorContext; + refactorContext.context = ctx; + refactorContext.kind = std::string(TO_NAMED_TEMPLATE_KIND); + refactorContext.span.pos = pos + REFACTOR_TEMPLATE_POSITION_OFFSET; + auto result = GetApplicableRefactorsImpl(&refactorContext); + initializer.DestroyContext(ctx); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(std::string(TO_NAMED_TEMPLATE_NAME), result[0].action.name); +} + +} // namespace \ No newline at end of file -- Gitee