From 63fc6fffaf69b86c2fd2eb74b8595fd477bcf525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Mon, 25 Nov 2024 18:59:20 +0100 Subject: [PATCH 1/9] WIP: Rename parameters --- .../IDE/Events/EventsParameterReplace.cpp | 251 ++++++++++++++++++ .../IDE/Events/EventsParameterReplacer.h | 55 ++++ .../IDE/Events/EventsPropertyReplacer.h | 1 + Core/GDCore/IDE/WholeProjectRefactorer.cpp | 42 +++ Core/GDCore/IDE/WholeProjectRefactorer.h | 7 + 5 files changed, 356 insertions(+) create mode 100644 Core/GDCore/IDE/Events/EventsParameterReplace.cpp create mode 100644 Core/GDCore/IDE/Events/EventsParameterReplacer.h diff --git a/Core/GDCore/IDE/Events/EventsParameterReplace.cpp b/Core/GDCore/IDE/Events/EventsParameterReplace.cpp new file mode 100644 index 000000000000..c3b68d532c21 --- /dev/null +++ b/Core/GDCore/IDE/Events/EventsParameterReplace.cpp @@ -0,0 +1,251 @@ +/* + * GDevelop Core + * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights + * reserved. This project is released under the MIT License. + */ +#include "GDCore/IDE/Events/EventsParameterReplacer.h" + +#include +#include +#include +#include +#include + +#include "GDCore/Events/Event.h" +#include "GDCore/Events/EventsList.h" +#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h" +#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h" +#include "GDCore/Extensions/Metadata/MetadataProvider.h" +#include "GDCore/Extensions/Metadata/ParameterMetadata.h" +#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h" +#include "GDCore/IDE/Events/ExpressionValidator.h" +#include "GDCore/Project/Layout.h" +#include "GDCore/Project/Project.h" +#include "GDCore/Project/ProjectScopedContainers.h" +#include "GDCore/Project/PropertiesContainer.h" +#include "GDCore/String.h" +#include "GDCore/Tools/Log.h" + +namespace gd { + +/** + * \brief Go through the nodes and rename parameters, + * or signal if the instruction must be renamed if a removed property is used. + * + * \see gd::ExpressionParser2 + */ +class GD_CORE_API ExpressionParameterReplacer + : public ExpressionParser2NodeWorker { + public: + ExpressionParameterReplacer( + const gd::Platform& platform_, + const gd::ProjectScopedContainers& projectScopedContainers_, + const gd::ParameterMetadataContainer& targetParameterContainer_, + bool isParentTypeAVariable_, + const std::unordered_map& oldToNewPropertyNames_) + : hasDoneRenaming(false), + platform(platform_), + projectScopedContainers(projectScopedContainers_), + targetParameterContainer(targetParameterContainer_), + isParentTypeAVariable(isParentTypeAVariable_), + oldToNewPropertyNames(oldToNewPropertyNames_){}; + virtual ~ExpressionParameterReplacer(){}; + + bool HasDoneRenaming() const { return hasDoneRenaming; } + + protected: + void OnVisitSubExpressionNode(SubExpressionNode& node) override { + node.expression->Visit(*this); + } + void OnVisitOperatorNode(OperatorNode& node) override { + node.leftHandSide->Visit(*this); + node.rightHandSide->Visit(*this); + } + void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override { + node.factor->Visit(*this); + } + void OnVisitNumberNode(NumberNode& node) override {} + void OnVisitTextNode(TextNode& node) override {} + void OnVisitVariableNode(VariableNode& node) override { + if (isParentTypeAVariable) { + // Do nothing, it's a variable. + if (node.child) node.child->Visit(*this); + return; + } + + // The node represents a variable or an object name on which a variable + // will be accessed, or a property with a child. + + projectScopedContainers.MatchIdentifierWithName( + // The property name is changed after the refactor operation. + node.name, + [&]() { + // Do nothing, it's an object variable. + if (node.child) node.child->Visit(*this); + }, [&]() { + // Do nothing, it's a variable. + if (node.child) node.child->Visit(*this); + }, [&]() { + // Do nothing, it's a property. + if (node.child) node.child->Visit(*this); + }, [&]() { + // This is a parameter + RenameParameter(node.name); + if (node.child) node.child->Visit(*this); + }, [&]() { + // Do nothing, it's something else. + if (node.child) node.child->Visit(*this); + }); + } + void OnVisitVariableAccessorNode(VariableAccessorNode& node) override { + if (node.child) node.child->Visit(*this); + } + void OnVisitVariableBracketAccessorNode( + VariableBracketAccessorNode& node) override { + bool isGrandParentTypeAVariable = isParentTypeAVariable; + isParentTypeAVariable = false; + node.expression->Visit(*this); + isParentTypeAVariable = isGrandParentTypeAVariable; + if (node.child) node.child->Visit(*this); + } + void OnVisitIdentifierNode(IdentifierNode& node) override { + if (isParentTypeAVariable) { + // Do nothing, it's a variable. + return; + } + + auto& propertiesContainersList = + projectScopedContainers.GetPropertiesContainersList(); + + projectScopedContainers.MatchIdentifierWithName( + // The property name is changed after the refactor operation + node.identifierName, + [&]() { + // Do nothing, it's an object variable. + }, [&]() { + // Do nothing, it's a variable. + }, [&]() { + // Do nothing, it's a property. + }, [&]() { + // This is a parameter. + RenameParameter(node.identifierName); + }, [&]() { + // Do nothing, it's something else. + }); + } + void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {} + void OnVisitFunctionCallNode(FunctionCallNode &node) override { + bool isGrandParentTypeAVariable = isParentTypeAVariable; + for (auto ¶meter : node.parameters) { + const auto ¶meterMetadata = + gd::MetadataProvider::GetFunctionCallParameterMetadata( + platform, projectScopedContainers.GetObjectsContainersList(), + node, *parameter); + if (!parameterMetadata) { + continue; + } + const auto ¶meterTypeMetadata = + parameterMetadata->GetValueTypeMetadata(); + if (gd::EventsParameterReplacer::CanContainProperty( + parameterTypeMetadata)) { + isParentTypeAVariable = parameterTypeMetadata.IsVariableOnly(); + parameter->Visit(*this); + } + } + isParentTypeAVariable = isGrandParentTypeAVariable; + } + void OnVisitEmptyNode(EmptyNode& node) override {} + + private: + bool hasDoneRenaming; + + bool RenameParameter( + gd::String& name) { + if (oldToNewPropertyNames.count(name) >= 1) { + name = oldToNewPropertyNames.find(name)->second; + hasDoneRenaming = true; + return true; + } + + return false; // Nothing was changed or done. + } + + // Scope: + const gd::Platform& platform; + const gd::ProjectScopedContainers& projectScopedContainers; + + // Renaming or removing to do: + const gd::ParameterMetadataContainer& targetParameterContainer; + const std::unordered_map& oldToNewPropertyNames; + + gd::String objectNameToUseForVariableAccessor; + bool isParentTypeAVariable; +}; + +bool EventsParameterReplacer::DoVisitInstruction(gd::Instruction& instruction, + bool isCondition) { + const auto& metadata = isCondition + ? gd::MetadataProvider::GetConditionMetadata( + platform, instruction.GetType()) + : gd::MetadataProvider::GetActionMetadata( + platform, instruction.GetType()); + + gd::ParameterMetadataTools::IterateOverParametersWithIndex( + instruction.GetParameters(), + metadata.GetParameters(), + [&](const gd::ParameterMetadata& parameterMetadata, + const gd::Expression& parameterValue, + size_t parameterIndex, + const gd::String& lastObjectName) { + if (!gd::EventsParameterReplacer::CanContainProperty( + parameterMetadata.GetValueTypeMetadata())) { + return; + } + auto node = parameterValue.GetRootNode(); + if (node) { + ExpressionParameterReplacer renamer( + platform, GetProjectScopedContainers(), targetParameterContainer, + parameterMetadata.GetValueTypeMetadata().IsVariableOnly(), + oldToNewPropertyNames); + node->Visit(renamer); + + if (renamer.HasDoneRenaming()) { + instruction.SetParameter( + parameterIndex, ExpressionParser2NodePrinter::PrintNode(*node)); + } + } + }); + + return false; +} + +bool EventsParameterReplacer::DoVisitEventExpression( + gd::Expression& expression, const gd::ParameterMetadata& metadata) { + if (!gd::EventsParameterReplacer::CanContainProperty( + metadata.GetValueTypeMetadata())) { + return false; + } + auto node = expression.GetRootNode(); + if (node) { + ExpressionParameterReplacer renamer( + platform, GetProjectScopedContainers(), targetParameterContainer, + metadata.GetValueTypeMetadata().IsVariableOnly(), oldToNewPropertyNames); + node->Visit(renamer); + + if (renamer.HasDoneRenaming()) { + expression = ExpressionParser2NodePrinter::PrintNode(*node); + } + } + + return false; +} + +bool EventsParameterReplacer::CanContainProperty( + const gd::ValueTypeMetadata &valueTypeMetadata) { + return valueTypeMetadata.IsVariable() || valueTypeMetadata.IsNumber() || + valueTypeMetadata.IsString(); +} + +EventsParameterReplacer::~EventsParameterReplacer() {} + +} // namespace gd diff --git a/Core/GDCore/IDE/Events/EventsParameterReplacer.h b/Core/GDCore/IDE/Events/EventsParameterReplacer.h new file mode 100644 index 000000000000..effc5e7d97e5 --- /dev/null +++ b/Core/GDCore/IDE/Events/EventsParameterReplacer.h @@ -0,0 +1,55 @@ +/* + * GDevelop Core + * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights + * reserved. This project is released under the MIT License. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include "GDCore/IDE/Events/ArbitraryEventsWorker.h" +#include "GDCore/String.h" +namespace gd { +class BaseEvent; +class PropertiesContainer; +class EventsList; +class Platform; +} // namespace gd + +namespace gd { +/** + * \brief Replace in expressions and in parameters of actions or conditions, + * references to the name of a property by another. + * + * \ingroup IDE + */ +class GD_CORE_API EventsParameterReplacer + : public ArbitraryEventsWorkerWithContext { + public: + EventsParameterReplacer( + const gd::Platform &platform_, + const gd::ParameterMetadataContainer &targetParameterContainer_, + const std::unordered_map &oldToNewPropertyNames_) + : platform(platform_), + targetParameterContainer(targetParameterContainer_), + oldToNewPropertyNames(oldToNewPropertyNames_){}; + virtual ~EventsParameterReplacer(); + + static bool CanContainProperty(const gd::ValueTypeMetadata &valueTypeMetadata); + + private: + bool DoVisitInstruction(gd::Instruction &instruction, + bool isCondition) override; + bool DoVisitEventExpression(gd::Expression &expression, + const gd::ParameterMetadata &metadata) override; + + const gd::Platform &platform; + const gd::ParameterMetadataContainer &targetParameterContainer; + const std::unordered_map &oldToNewPropertyNames; +}; + +} // namespace gd diff --git a/Core/GDCore/IDE/Events/EventsPropertyReplacer.h b/Core/GDCore/IDE/Events/EventsPropertyReplacer.h index 4a362b93bd10..5c850230a8a7 100644 --- a/Core/GDCore/IDE/Events/EventsPropertyReplacer.h +++ b/Core/GDCore/IDE/Events/EventsPropertyReplacer.h @@ -4,6 +4,7 @@ * reserved. This project is released under the MIT License. */ #pragma once + #include #include #include diff --git a/Core/GDCore/IDE/WholeProjectRefactorer.cpp b/Core/GDCore/IDE/WholeProjectRefactorer.cpp index 1a8088439769..b5f81da62b52 100644 --- a/Core/GDCore/IDE/WholeProjectRefactorer.cpp +++ b/Core/GDCore/IDE/WholeProjectRefactorer.cpp @@ -19,6 +19,7 @@ #include "GDCore/IDE/Events/BehaviorTypeRenamer.h" #include "GDCore/IDE/Events/CustomObjectTypeRenamer.h" #include "GDCore/IDE/Events/EventsBehaviorRenamer.h" +#include "GDCore/IDE/Events/EventsParameterReplacer.h" #include "GDCore/IDE/Events/EventsPropertyReplacer.h" #include "GDCore/IDE/Events/EventsRefactorer.h" #include "GDCore/IDE/Events/EventsVariableInstructionTypeSwitcher.h" @@ -816,6 +817,47 @@ void WholeProjectRefactorer::RenameObjectEventsFunction( } } +void WholeProjectRefactorer::RenameParameter( + gd::Project &project, gd::ProjectScopedContainers &projectScopedContainers, + gd::EventsFunction &eventsFunction, const gd::String &oldParameterName, + const gd::String &newParameterName) { + auto ¶meters = eventsFunction.GetParameters(); + if (!parameters.HasParameterNamed(oldParameterName)) + return; + auto ¶meter = parameters.GetParameter(oldParameterName); + if (parameter.GetType() == "Object") { + gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction( + project, projectScopedContainers, eventsFunction, oldParameterName, + newParameterName, false); + } else if (parameter.GetType() == "Behavior") { + size_t behaviorParameterIndex = parameters.GetParameterPosition(parameter); + size_t objectParameterIndex = + gd::ParameterMetadataTools::GetObjectParameterIndexFor( + parameters, behaviorParameterIndex); + const gd::String &objectName = + parameters.GetParameter(objectParameterIndex).GetName(); + gd::EventsBehaviorRenamer behaviorRenamer(project.GetCurrentPlatform(), + objectName, oldParameterName, + newParameterName); + behaviorRenamer.Launch(eventsFunction.GetEvents(), projectScopedContainers); + } else { + // Rename parameter names directly used as an identifier. + std::unordered_map oldToNewParameterNames = { + {oldParameterName, newParameterName}}; + gd::EventsParameterReplacer eventsParameterReplacer( + project.GetCurrentPlatform(), parameters, oldToNewParameterNames); + eventsParameterReplacer.Launch(eventsFunction.GetEvents(), + projectScopedContainers); + + // Rename parameter names in legacy expressions and instructions + gd::ProjectElementRenamer projectElementRenamer( + project.GetCurrentPlatform(), "functionParameterName", oldParameterName, + newParameterName); + projectElementRenamer.Launch(eventsFunction.GetEvents(), + projectScopedContainers); + } +} + void WholeProjectRefactorer::MoveEventsFunctionParameter( gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension, diff --git a/Core/GDCore/IDE/WholeProjectRefactorer.h b/Core/GDCore/IDE/WholeProjectRefactorer.h index 0cc21b020c93..268a282372ca 100644 --- a/Core/GDCore/IDE/WholeProjectRefactorer.h +++ b/Core/GDCore/IDE/WholeProjectRefactorer.h @@ -176,6 +176,13 @@ class GD_CORE_API WholeProjectRefactorer { const gd::String& oldFunctionName, const gd::String& newFunctionName); + static void + RenameParameter(gd::Project &project, + gd::ProjectScopedContainers &projectScopedContainers, + gd::EventsFunction &eventsFunction, + const gd::String &oldParameterName, + const gd::String &newParameterName); + /** * \brief Refactor the project **before** an events function parameter * is moved. From b01de52a5fb2d48e2013ef03df940d0e84fe7366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Sat, 30 Nov 2024 17:46:12 +0100 Subject: [PATCH 2/9] Fix object parameter renaming. --- .../IDE/Events/EventsParameterReplace.cpp | 21 +-- .../IDE/Events/EventsParameterReplacer.h | 5 +- Core/GDCore/IDE/Events/EventsRefactorer.cpp | 3 +- Core/GDCore/IDE/WholeProjectRefactorer.cpp | 9 +- Core/tests/DummyPlatform.cpp | 4 +- Core/tests/WholeProjectRefactorer.cpp | 154 ++++++++++++++++++ 6 files changed, 171 insertions(+), 25 deletions(-) diff --git a/Core/GDCore/IDE/Events/EventsParameterReplace.cpp b/Core/GDCore/IDE/Events/EventsParameterReplace.cpp index c3b68d532c21..8d1150ea986b 100644 --- a/Core/GDCore/IDE/Events/EventsParameterReplace.cpp +++ b/Core/GDCore/IDE/Events/EventsParameterReplace.cpp @@ -40,13 +40,11 @@ class GD_CORE_API ExpressionParameterReplacer ExpressionParameterReplacer( const gd::Platform& platform_, const gd::ProjectScopedContainers& projectScopedContainers_, - const gd::ParameterMetadataContainer& targetParameterContainer_, bool isParentTypeAVariable_, const std::unordered_map& oldToNewPropertyNames_) : hasDoneRenaming(false), platform(platform_), projectScopedContainers(projectScopedContainers_), - targetParameterContainer(targetParameterContainer_), isParentTypeAVariable(isParentTypeAVariable_), oldToNewPropertyNames(oldToNewPropertyNames_){}; virtual ~ExpressionParameterReplacer(){}; @@ -113,10 +111,6 @@ class GD_CORE_API ExpressionParameterReplacer // Do nothing, it's a variable. return; } - - auto& propertiesContainersList = - projectScopedContainers.GetPropertiesContainersList(); - projectScopedContainers.MatchIdentifierWithName( // The property name is changed after the refactor operation node.identifierName, @@ -146,7 +140,7 @@ class GD_CORE_API ExpressionParameterReplacer } const auto ¶meterTypeMetadata = parameterMetadata->GetValueTypeMetadata(); - if (gd::EventsParameterReplacer::CanContainProperty( + if (gd::EventsParameterReplacer::CanContainParameter( parameterTypeMetadata)) { isParentTypeAVariable = parameterTypeMetadata.IsVariableOnly(); parameter->Visit(*this); @@ -174,8 +168,7 @@ class GD_CORE_API ExpressionParameterReplacer const gd::Platform& platform; const gd::ProjectScopedContainers& projectScopedContainers; - // Renaming or removing to do: - const gd::ParameterMetadataContainer& targetParameterContainer; + // Renaming to do const std::unordered_map& oldToNewPropertyNames; gd::String objectNameToUseForVariableAccessor; @@ -197,14 +190,14 @@ bool EventsParameterReplacer::DoVisitInstruction(gd::Instruction& instruction, const gd::Expression& parameterValue, size_t parameterIndex, const gd::String& lastObjectName) { - if (!gd::EventsParameterReplacer::CanContainProperty( + if (!gd::EventsParameterReplacer::CanContainParameter( parameterMetadata.GetValueTypeMetadata())) { return; } auto node = parameterValue.GetRootNode(); if (node) { ExpressionParameterReplacer renamer( - platform, GetProjectScopedContainers(), targetParameterContainer, + platform, GetProjectScopedContainers(), parameterMetadata.GetValueTypeMetadata().IsVariableOnly(), oldToNewPropertyNames); node->Visit(renamer); @@ -221,14 +214,14 @@ bool EventsParameterReplacer::DoVisitInstruction(gd::Instruction& instruction, bool EventsParameterReplacer::DoVisitEventExpression( gd::Expression& expression, const gd::ParameterMetadata& metadata) { - if (!gd::EventsParameterReplacer::CanContainProperty( + if (!gd::EventsParameterReplacer::CanContainParameter( metadata.GetValueTypeMetadata())) { return false; } auto node = expression.GetRootNode(); if (node) { ExpressionParameterReplacer renamer( - platform, GetProjectScopedContainers(), targetParameterContainer, + platform, GetProjectScopedContainers(), metadata.GetValueTypeMetadata().IsVariableOnly(), oldToNewPropertyNames); node->Visit(renamer); @@ -240,7 +233,7 @@ bool EventsParameterReplacer::DoVisitEventExpression( return false; } -bool EventsParameterReplacer::CanContainProperty( +bool EventsParameterReplacer::CanContainParameter( const gd::ValueTypeMetadata &valueTypeMetadata) { return valueTypeMetadata.IsVariable() || valueTypeMetadata.IsNumber() || valueTypeMetadata.IsString(); diff --git a/Core/GDCore/IDE/Events/EventsParameterReplacer.h b/Core/GDCore/IDE/Events/EventsParameterReplacer.h index effc5e7d97e5..c11bfaaf79f0 100644 --- a/Core/GDCore/IDE/Events/EventsParameterReplacer.h +++ b/Core/GDCore/IDE/Events/EventsParameterReplacer.h @@ -32,14 +32,12 @@ class GD_CORE_API EventsParameterReplacer public: EventsParameterReplacer( const gd::Platform &platform_, - const gd::ParameterMetadataContainer &targetParameterContainer_, const std::unordered_map &oldToNewPropertyNames_) : platform(platform_), - targetParameterContainer(targetParameterContainer_), oldToNewPropertyNames(oldToNewPropertyNames_){}; virtual ~EventsParameterReplacer(); - static bool CanContainProperty(const gd::ValueTypeMetadata &valueTypeMetadata); + static bool CanContainParameter(const gd::ValueTypeMetadata &valueTypeMetadata); private: bool DoVisitInstruction(gd::Instruction &instruction, @@ -48,7 +46,6 @@ class GD_CORE_API EventsParameterReplacer const gd::ParameterMetadata &metadata) override; const gd::Platform &platform; - const gd::ParameterMetadataContainer &targetParameterContainer; const std::unordered_map &oldToNewPropertyNames; }; diff --git a/Core/GDCore/IDE/Events/EventsRefactorer.cpp b/Core/GDCore/IDE/Events/EventsRefactorer.cpp index 1b676888520e..bc44e1a0f245 100644 --- a/Core/GDCore/IDE/Events/EventsRefactorer.cpp +++ b/Core/GDCore/IDE/Events/EventsRefactorer.cpp @@ -55,12 +55,11 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker { gd::ExpressionNode& node, const gd::String& objectName, const gd::String& objectNewName) { - if (gd::ExpressionValidator::HasNoErrors(platform, projectScopedContainers, rootType, node)) { + // TODO Use the ProjectScopedContainers to check the targeted ObjectsContainer. ExpressionObjectRenamer renamer(platform, projectScopedContainers, rootType, objectName, objectNewName); node.Visit(renamer); return renamer.HasDoneRenaming(); - } return false; } diff --git a/Core/GDCore/IDE/WholeProjectRefactorer.cpp b/Core/GDCore/IDE/WholeProjectRefactorer.cpp index b5f81da62b52..66e96fc40d25 100644 --- a/Core/GDCore/IDE/WholeProjectRefactorer.cpp +++ b/Core/GDCore/IDE/WholeProjectRefactorer.cpp @@ -825,15 +825,18 @@ void WholeProjectRefactorer::RenameParameter( if (!parameters.HasParameterNamed(oldParameterName)) return; auto ¶meter = parameters.GetParameter(oldParameterName); - if (parameter.GetType() == "Object") { + if (parameter.GetValueTypeMetadata().IsObject()) { gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction( project, projectScopedContainers, eventsFunction, oldParameterName, newParameterName, false); - } else if (parameter.GetType() == "Behavior") { + } else if (parameter.GetValueTypeMetadata().IsBehavior()) { size_t behaviorParameterIndex = parameters.GetParameterPosition(parameter); size_t objectParameterIndex = gd::ParameterMetadataTools::GetObjectParameterIndexFor( parameters, behaviorParameterIndex); + if (objectParameterIndex == gd::String::npos) { + return; + } const gd::String &objectName = parameters.GetParameter(objectParameterIndex).GetName(); gd::EventsBehaviorRenamer behaviorRenamer(project.GetCurrentPlatform(), @@ -845,7 +848,7 @@ void WholeProjectRefactorer::RenameParameter( std::unordered_map oldToNewParameterNames = { {oldParameterName, newParameterName}}; gd::EventsParameterReplacer eventsParameterReplacer( - project.GetCurrentPlatform(), parameters, oldToNewParameterNames); + project.GetCurrentPlatform(), oldToNewParameterNames); eventsParameterReplacer.Launch(eventsFunction.GetEvents(), projectScopedContainers); diff --git a/Core/tests/DummyPlatform.cpp b/Core/tests/DummyPlatform.cpp index f11d34801a05..54f5651e5257 100644 --- a/Core/tests/DummyPlatform.cpp +++ b/Core/tests/DummyPlatform.cpp @@ -340,8 +340,8 @@ void SetupProjectWithDummyPlatform(gd::Project& project, "", "", "") - .AddParameter("object", _("Object 1 parameter")) - .AddParameter("object", _("Object 2 parameter")) + .AddParameter("object", "Object 1 parameter") + .AddParameter("object", "Object 2 parameter") .SetFunctionName("doSomethingWithObjects"); extension diff --git a/Core/tests/WholeProjectRefactorer.cpp b/Core/tests/WholeProjectRefactorer.cpp index 3811b32c269b..33c99d85fd97 100644 --- a/Core/tests/WholeProjectRefactorer.cpp +++ b/Core/tests/WholeProjectRefactorer.cpp @@ -91,6 +91,37 @@ CreateInstructionWithNumberParameter(gd::Project &project, return event.GetActions().Insert(instruction); } +const gd::Instruction & +CreateInstructionWithObjectParameter(gd::Project &project, + gd::EventsList &events, + const gd::String &objectName) { + gd::StandardEvent &event = dynamic_cast( + events.InsertNewEvent(project, "BuiltinCommonInstructions::Standard")); + + gd::Instruction instruction; + instruction.SetType("MyExtension::DoSomethingWithObjects"); + instruction.SetParametersCount(2); + instruction.SetParameter(0, objectName); + instruction.SetParameter(1, ""); + return event.GetActions().Insert(instruction); +} + +const gd::Instruction & +CreateInstructionWithBehaviorParameter(gd::Project &project, + gd::EventsList &events, + const gd::String &objectName, + const gd::String &behaviorName) { + gd::StandardEvent &event = dynamic_cast( + events.InsertNewEvent(project, "BuiltinCommonInstructions::Standard")); + + gd::Instruction instruction; + instruction.SetType("MyExtension::BehaviorDoSomething"); + instruction.SetParametersCount(2); + instruction.SetParameter(0, objectName); + instruction.SetParameter(1, behaviorName); + return event.GetActions().Insert(instruction); +} + const gd::Instruction & CreateInstructionWithVariableParameter(gd::Project &project, gd::EventsList &events, @@ -2306,6 +2337,129 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { } } + SECTION("(Free) number parameter renamed (in expressions)") { + gd::Project project; + gd::Platform platform; + SetupProjectWithDummyPlatform(project, platform); + auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project); + + auto &eventsFunction = + eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0); + eventsFunction.GetParameters() + .AddNewParameter("MyParameter") + .GetValueTypeMetadata() + .SetName("number"); + auto &instruction = CreateInstructionWithNumberParameter( + project, eventsFunction.GetEvents(), "MyParameter"); + auto &instruction2 = CreateInstructionWithNumberParameter( + project, eventsFunction.GetEvents(), + "MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyParameter])"); + + gd::ObjectsContainer parametersObjectsContainer( + gd::ObjectsContainer::SourceType::Function); + gd::VariablesContainer parameterVariablesContainer( + gd::VariablesContainer::SourceType::Parameters); + auto projectScopedContainers = gd::ProjectScopedContainers:: + MakeNewProjectScopedContainersForFreeEventsFunction( + project, eventsExtension, eventsFunction, + parametersObjectsContainer, parameterVariablesContainer); + gd::WholeProjectRefactorer::RenameParameter( + project, projectScopedContainers, eventsFunction, "MyParameter", + "MyRenamedParameter"); + + REQUIRE(instruction.GetParameter(0).GetPlainString() == + "MyRenamedParameter"); + REQUIRE(instruction2.GetParameter(0).GetPlainString() == + "MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyRenamedParameter])"); + } + + SECTION("(Free) object parameter renamed (in expressions)") { + gd::Project project; + gd::Platform platform; + SetupProjectWithDummyPlatform(project, platform); + auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project); + + auto &eventsFunction = + eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0); + eventsFunction.GetParameters() + .AddNewParameter("MyObject") + .GetValueTypeMetadata() + .SetName("objectList") + .SetExtraInfo("MyExtension::Sprite"); + auto &instruction = CreateInstructionWithObjectParameter( + project, eventsFunction.GetEvents(), "MyObject"); + auto &instruction2 = CreateInstructionWithNumberParameter( + project, eventsFunction.GetEvents(), "MyObject.GetObjectStringWith1Param(0)"); + auto &instruction3 = CreateInstructionWithNumberParameter( + project, eventsFunction.GetEvents(), + "MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyObject.GetObjectStringWith1Param(0)])"); + + gd::ObjectsContainer parametersObjectsContainer( + gd::ObjectsContainer::SourceType::Function); + gd::VariablesContainer parameterVariablesContainer( + gd::VariablesContainer::SourceType::Parameters); + auto projectScopedContainers = gd::ProjectScopedContainers:: + MakeNewProjectScopedContainersForFreeEventsFunction( + project, eventsExtension, eventsFunction, + parametersObjectsContainer, parameterVariablesContainer); + gd::WholeProjectRefactorer::RenameParameter( + project, projectScopedContainers, eventsFunction, "MyObject", + "MyRenamedObject"); + + REQUIRE(instruction.GetParameter(0).GetPlainString() == + "MyRenamedObject"); + REQUIRE(instruction2.GetParameter(0).GetPlainString() == + "MyRenamedObject.GetObjectStringWith1Param(0)"); + REQUIRE(instruction3.GetParameter(0).GetPlainString() == + "MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyRenamedObject.GetObjectStringWith1Param(0)])"); + } + + SECTION("(Free) behavior parameter renamed (in expressions)") { + gd::Project project; + gd::Platform platform; + SetupProjectWithDummyPlatform(project, platform); + auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project); + + auto &eventsFunction = + eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0); + eventsFunction.GetParameters() + .AddNewParameter("MyObject") + .GetValueTypeMetadata() + .SetName("objectList") + .SetExtraInfo("MyExtension::Sprite"); + eventsFunction.GetParameters() + .AddNewParameter("MyBehavior") + .GetValueTypeMetadata() + .SetName("behavior") + .SetExtraInfo("MyExtension::MyBehavior"); + auto &instruction = CreateInstructionWithBehaviorParameter( + project, eventsFunction.GetEvents(), "MyObject", "MyBehavior"); + auto &instruction2 = CreateInstructionWithNumberParameter( + project, eventsFunction.GetEvents(), "MyObject.MyBehavior::GetBehaviorStringWith1Param(0)"); + auto &instruction3 = CreateInstructionWithNumberParameter( + project, eventsFunction.GetEvents(), + "MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyObject.MyBehavior::GetBehaviorStringWith1Param(0)])"); + + gd::ObjectsContainer parametersObjectsContainer( + gd::ObjectsContainer::SourceType::Function); + gd::VariablesContainer parameterVariablesContainer( + gd::VariablesContainer::SourceType::Parameters); + auto projectScopedContainers = gd::ProjectScopedContainers:: + MakeNewProjectScopedContainersForFreeEventsFunction( + project, eventsExtension, eventsFunction, + parametersObjectsContainer, parameterVariablesContainer); + gd::WholeProjectRefactorer::RenameParameter( + project, projectScopedContainers, eventsFunction, "MyBehavior", + "MyRenamedBehavior"); + + REQUIRE(instruction.GetParameter(1).GetPlainString() == + "MyRenamedBehavior"); + REQUIRE(instruction2.GetParameter(0).GetPlainString() == + "MyObject.MyRenamedBehavior::GetBehaviorStringWith1Param(0)"); + REQUIRE(instruction3.GetParameter(0).GetPlainString() == + "MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyObject.MyRenamedBehavior::GetBehaviorStringWith1Param(0)])"); + } + SECTION("(Free) events action parameter moved") { gd::Project project; gd::Platform platform; From d7067546bd20d71ef33c45c328d8f05adc2b906f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Sat, 30 Nov 2024 23:58:53 +0100 Subject: [PATCH 3/9] Refactor object renaming to use ArbitraryEventsWorker. --- Core/GDCore/IDE/Events/EventsRefactorer.cpp | 238 +++++++------------- Core/GDCore/IDE/Events/EventsRefactorer.h | 38 ---- 2 files changed, 77 insertions(+), 199 deletions(-) diff --git a/Core/GDCore/IDE/Events/EventsRefactorer.cpp b/Core/GDCore/IDE/Events/EventsRefactorer.cpp index bc44e1a0f245..37e21b734780 100644 --- a/Core/GDCore/IDE/Events/EventsRefactorer.cpp +++ b/Core/GDCore/IDE/Events/EventsRefactorer.cpp @@ -22,6 +22,7 @@ #include "GDCore/Project/EventsBasedObject.h" #include "GDCore/Project/ProjectScopedContainers.h" #include "GDCore/IDE/Events/ExpressionTypeFinder.h" +#include "GDCore/IDE/Events/ArbitraryEventsWorker.h" using namespace std; @@ -294,183 +295,98 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker { const gd::String rootType; }; -bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform, - const gd::ProjectScopedContainers& projectScopedContainers, - gd::InstructionsList& actions, - gd::String oldName, - gd::String newName) { - bool somethingModified = false; - - for (std::size_t aId = 0; aId < actions.size(); ++aId) { - const gd::InstructionMetadata& instrInfos = - MetadataProvider::GetActionMetadata(platform, actions[aId].GetType()); - for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) { - // Replace object's name in parameters - if (gd::ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType()) && - actions[aId].GetParameter(pNb).GetPlainString() == oldName) - actions[aId].SetParameter(pNb, gd::Expression(newName)); - // Replace object's name in expressions - else if (ParameterMetadata::IsExpression( - "number", instrInfos.parameters.GetParameter(pNb).GetType())) { - auto node = actions[aId].GetParameter(pNb).GetRootNode(); - - if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) { - actions[aId].SetParameter( - pNb, ExpressionParser2NodePrinter::PrintNode(*node)); - } - } - // Replace object's name in text expressions - else if (ParameterMetadata::IsExpression( - "string", instrInfos.parameters.GetParameter(pNb).GetType())) { - auto node = actions[aId].GetParameter(pNb).GetRootNode(); - - if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) { - actions[aId].SetParameter( - pNb, ExpressionParser2NodePrinter::PrintNode(*node)); - } - } - } +/** + * \brief Replace in expressions and in parameters of actions or conditions, + * references to the name of an object by another. + * + * \ingroup IDE + */ +class GD_CORE_API EventsObjectReplacer + : public ArbitraryEventsWorkerWithContext { +public: + EventsObjectReplacer(const gd::Platform &platform_, + const gd::String &oldObjectName_, + const gd::String &newObjectName_) + : platform(platform_), oldObjectName(oldObjectName_), + newObjectName(newObjectName_){}; + + virtual ~EventsObjectReplacer() {} + +private: + bool DoVisitInstruction(gd::Instruction &instruction, + bool isCondition) override { + const auto &metadata = isCondition + ? gd::MetadataProvider::GetConditionMetadata( + platform, instruction.GetType()) + : gd::MetadataProvider::GetActionMetadata( + platform, instruction.GetType()); + + gd::ParameterMetadataTools::IterateOverParametersWithIndex( + instruction.GetParameters(), metadata.GetParameters(), + [&](const gd::ParameterMetadata ¶meterMetadata, + const gd::Expression ¶meterValue, size_t parameterIndex, + const gd::String &lastObjectName) { + if (!gd::EventsObjectReplacer::CanContainObject( + parameterMetadata.GetValueTypeMetadata())) { + return; + } + auto node = parameterValue.GetRootNode(); + if (node) { + ExpressionObjectRenamer renamer( + platform, GetProjectScopedContainers(), + parameterMetadata.GetValueTypeMetadata().GetName(), + oldObjectName, newObjectName); + node->Visit(renamer); + + if (renamer.HasDoneRenaming()) { + instruction.SetParameter( + parameterIndex, + ExpressionParser2NodePrinter::PrintNode(*node)); + } + } + }); - if (!actions[aId].GetSubInstructions().empty()) - somethingModified = - RenameObjectInActions(platform, - projectScopedContainers, - actions[aId].GetSubInstructions(), - oldName, - newName) || - somethingModified; + return false; } - return somethingModified; -} - -bool EventsRefactorer::RenameObjectInConditions( - const gd::Platform& platform, - const gd::ProjectScopedContainers& projectScopedContainers, - gd::InstructionsList& conditions, - gd::String oldName, - gd::String newName) { - bool somethingModified = false; - - for (std::size_t cId = 0; cId < conditions.size(); ++cId) { - const gd::InstructionMetadata& instrInfos = - MetadataProvider::GetConditionMetadata(platform, - conditions[cId].GetType()); - for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) { - // Replace object's name in parameters - if (gd::ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType()) && - conditions[cId].GetParameter(pNb).GetPlainString() == oldName) - conditions[cId].SetParameter(pNb, gd::Expression(newName)); - // Replace object's name in expressions - else if (ParameterMetadata::IsExpression( - "number", instrInfos.parameters.GetParameter(pNb).GetType())) { - auto node = conditions[cId].GetParameter(pNb).GetRootNode(); - - if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) { - conditions[cId].SetParameter( - pNb, ExpressionParser2NodePrinter::PrintNode(*node)); - } - } - // Replace object's name in text expressions - else if (ParameterMetadata::IsExpression( - "string", instrInfos.parameters.GetParameter(pNb).GetType())) { - auto node = conditions[cId].GetParameter(pNb).GetRootNode(); - - if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) { - conditions[cId].SetParameter( - pNb, ExpressionParser2NodePrinter::PrintNode(*node)); - } - } + bool DoVisitEventExpression(gd::Expression &expression, + const gd::ParameterMetadata &metadata) override { + if (!gd::EventsObjectReplacer::CanContainObject( + metadata.GetValueTypeMetadata())) { + return false; } - - if (!conditions[cId].GetSubInstructions().empty()) - somethingModified = - RenameObjectInConditions(platform, - projectScopedContainers, - conditions[cId].GetSubInstructions(), - oldName, - newName) || - somethingModified; - } - - return somethingModified; -} - -bool EventsRefactorer::RenameObjectInEventParameters( - const gd::Platform& platform, - const gd::ProjectScopedContainers& projectScopedContainers, - gd::Expression& expression, - gd::ParameterMetadata parameterMetadata, - gd::String oldName, - gd::String newName) { - bool somethingModified = false; - - if (gd::ParameterMetadata::IsObject(parameterMetadata.GetType()) && - expression.GetPlainString() == oldName) - expression = gd::Expression(newName); - // Replace object's name in expressions - else if (ParameterMetadata::IsExpression("number", - parameterMetadata.GetType())) { auto node = expression.GetRootNode(); - - if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) { - expression = ExpressionParser2NodePrinter::PrintNode(*node); + if (node) { + ExpressionObjectRenamer renamer(platform, GetProjectScopedContainers(), + metadata.GetValueTypeMetadata().GetName(), + oldObjectName, newObjectName); + node->Visit(renamer); + + if (renamer.HasDoneRenaming()) { + expression = ExpressionParser2NodePrinter::PrintNode(*node); + } } + + return false; } - // Replace object's name in text expressions - else if (ParameterMetadata::IsExpression("string", - parameterMetadata.GetType())) { - auto node = expression.GetRootNode(); - if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) { - expression = ExpressionParser2NodePrinter::PrintNode(*node); - } + bool CanContainObject(const gd::ValueTypeMetadata &valueTypeMetadata) { + return valueTypeMetadata.IsObject() || valueTypeMetadata.IsVariable() || + valueTypeMetadata.IsNumber() || valueTypeMetadata.IsString(); } - return somethingModified; -} + const gd::Platform &platform; + const gd::String &oldObjectName; + const gd::String &newObjectName; +}; void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform, const gd::ProjectScopedContainers& projectScopedContainers, gd::EventsList& events, gd::String oldName, gd::String newName) { - for (std::size_t i = 0; i < events.size(); ++i) { - vector conditionsVectors = - events[i].GetAllConditionsVectors(); - for (std::size_t j = 0; j < conditionsVectors.size(); ++j) { - bool somethingModified = RenameObjectInConditions( - platform, projectScopedContainers, *conditionsVectors[j], oldName, newName); - } - - vector actionsVectors = - events[i].GetAllActionsVectors(); - for (std::size_t j = 0; j < actionsVectors.size(); ++j) { - bool somethingModified = RenameObjectInActions( - platform, projectScopedContainers, *actionsVectors[j], oldName, newName); - } - - vector> - expressionsWithMetadata = events[i].GetAllExpressionsWithMetadata(); - for (std::size_t j = 0; j < expressionsWithMetadata.size(); ++j) { - gd::Expression* expression = expressionsWithMetadata[j].first; - gd::ParameterMetadata parameterMetadata = - expressionsWithMetadata[j].second; - bool somethingModified = RenameObjectInEventParameters(platform, - projectScopedContainers, - *expression, - parameterMetadata, - oldName, - newName); - } - - if (events[i].CanHaveSubEvents()) - RenameObjectInEvents(platform, - projectScopedContainers, - events[i].GetSubEvents(), - oldName, - newName); - } + gd::EventsObjectReplacer eventsParameterReplacer(platform, oldName, newName); + eventsParameterReplacer.Launch(events, projectScopedContainers); } bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform, diff --git a/Core/GDCore/IDE/Events/EventsRefactorer.h b/Core/GDCore/IDE/Events/EventsRefactorer.h index 099c0a6457d4..ed4fdc2df330 100644 --- a/Core/GDCore/IDE/Events/EventsRefactorer.h +++ b/Core/GDCore/IDE/Events/EventsRefactorer.h @@ -121,44 +121,6 @@ class GD_CORE_API EventsRefactorer { virtual ~EventsRefactorer(){}; private: - /** - * Replace all occurrences of an object name by another name in an action - * ( include : objects in parameters and in math/text expressions ). - * - * \return true if something was modified. - */ - static bool RenameObjectInActions(const gd::Platform& platform, - const gd::ProjectScopedContainers& projectScopedContainers, - gd::InstructionsList& instructions, - gd::String oldName, - gd::String newName); - - /** - * Replace all occurrences of an object name by another name in a condition - * ( include : objects in parameters and in math/text expressions ). - * - * \return true if something was modified. - */ - static bool RenameObjectInConditions(const gd::Platform& platform, - const gd::ProjectScopedContainers& projectScopedContainers, - gd::InstructionsList& instructions, - gd::String oldName, - gd::String newName); - /** - * Replace all occurrences of an object name by another name in an expression - * with the specified metadata - * ( include : objects or objects in math/text expressions ). - * - * \return true if something was modified. - */ - static bool RenameObjectInEventParameters( - const gd::Platform& platform, - const gd::ProjectScopedContainers& projectScopedContainers, - gd::Expression& expression, - gd::ParameterMetadata parameterMetadata, - gd::String oldName, - gd::String newName); - /** * Remove all conditions of the list using an object * From 594dd479b732a6ad195522d2a5065c9dfd25281c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Sun, 1 Dec 2024 00:54:21 +0100 Subject: [PATCH 4/9] Check renamed object container. --- Core/GDCore/IDE/Events/EventsRefactorer.cpp | 39 ++++++++++++------- Core/GDCore/IDE/Events/EventsRefactorer.h | 1 + Core/GDCore/IDE/WholeProjectRefactorer.cpp | 35 +++++++++++------ Core/GDCore/IDE/WholeProjectRefactorer.h | 8 ++++ Core/tests/WholeProjectRefactorer.cpp | 17 ++++---- GDevelop.js/Bindings/Bindings.idl | 9 ++++- GDevelop.js/types.d.ts | 4 +- GDevelop.js/types/gdeventsrefactorer.js | 2 +- GDevelop.js/types/gdwholeprojectrefactorer.js | 2 +- .../index.js | 3 ++ 10 files changed, 82 insertions(+), 38 deletions(-) diff --git a/Core/GDCore/IDE/Events/EventsRefactorer.cpp b/Core/GDCore/IDE/Events/EventsRefactorer.cpp index 37e21b734780..fc01bcc5a77e 100644 --- a/Core/GDCore/IDE/Events/EventsRefactorer.cpp +++ b/Core/GDCore/IDE/Events/EventsRefactorer.cpp @@ -52,17 +52,14 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker { static bool Rename(const gd::Platform &platform, const gd::ProjectScopedContainers &projectScopedContainers, - const gd::String &rootType, - gd::ExpressionNode& node, - const gd::String& objectName, - const gd::String& objectNewName) { - // TODO Use the ProjectScopedContainers to check the targeted ObjectsContainer. - ExpressionObjectRenamer renamer(platform, projectScopedContainers, rootType, objectName, objectNewName); - node.Visit(renamer); + const gd::String &rootType, gd::ExpressionNode &node, + const gd::String &objectName, + const gd::String &objectNewName) { + ExpressionObjectRenamer renamer(platform, projectScopedContainers, rootType, + objectName, objectNewName); + node.Visit(renamer); - return renamer.HasDoneRenaming(); - - return false; + return renamer.HasDoneRenaming(); } bool HasDoneRenaming() const { return hasDoneRenaming; } @@ -305,16 +302,24 @@ class GD_CORE_API EventsObjectReplacer : public ArbitraryEventsWorkerWithContext { public: EventsObjectReplacer(const gd::Platform &platform_, + const gd::ObjectsContainer &targetedObjectsContainer_, const gd::String &oldObjectName_, const gd::String &newObjectName_) - : platform(platform_), oldObjectName(oldObjectName_), - newObjectName(newObjectName_){}; + : platform(platform_), + targetedObjectsContainer(targetedObjectsContainer_), + oldObjectName(oldObjectName_), newObjectName(newObjectName_){}; virtual ~EventsObjectReplacer() {} private: bool DoVisitInstruction(gd::Instruction &instruction, bool isCondition) override { + if (&targetedObjectsContainer != + GetProjectScopedContainers() + .GetObjectsContainersList() + .GetObjectsContainerFromObjectName(oldObjectName)) { + return false; + } const auto &metadata = isCondition ? gd::MetadataProvider::GetConditionMetadata( platform, instruction.GetType()) @@ -351,6 +356,12 @@ class GD_CORE_API EventsObjectReplacer bool DoVisitEventExpression(gd::Expression &expression, const gd::ParameterMetadata &metadata) override { + if (&targetedObjectsContainer != + GetProjectScopedContainers() + .GetObjectsContainersList() + .GetObjectsContainerFromObjectName(oldObjectName)) { + return false; + } if (!gd::EventsObjectReplacer::CanContainObject( metadata.GetValueTypeMetadata())) { return false; @@ -376,6 +387,7 @@ class GD_CORE_API EventsObjectReplacer } const gd::Platform &platform; + const gd::ObjectsContainer &targetedObjectsContainer; const gd::String &oldObjectName; const gd::String &newObjectName; }; @@ -383,9 +395,10 @@ class GD_CORE_API EventsObjectReplacer void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform, const gd::ProjectScopedContainers& projectScopedContainers, gd::EventsList& events, + const gd::ObjectsContainer &targetedObjectsContainer, gd::String oldName, gd::String newName) { - gd::EventsObjectReplacer eventsParameterReplacer(platform, oldName, newName); + gd::EventsObjectReplacer eventsParameterReplacer(platform, targetedObjectsContainer, oldName, newName); eventsParameterReplacer.Launch(events, projectScopedContainers); } diff --git a/Core/GDCore/IDE/Events/EventsRefactorer.h b/Core/GDCore/IDE/Events/EventsRefactorer.h index ed4fdc2df330..73a49504c7a6 100644 --- a/Core/GDCore/IDE/Events/EventsRefactorer.h +++ b/Core/GDCore/IDE/Events/EventsRefactorer.h @@ -83,6 +83,7 @@ class GD_CORE_API EventsRefactorer { static void RenameObjectInEvents(const gd::Platform& platform, const gd::ProjectScopedContainers& projectScopedContainers, gd::EventsList& events, + const gd::ObjectsContainer &targetedObjectsContainer, gd::String oldName, gd::String newName); diff --git a/Core/GDCore/IDE/WholeProjectRefactorer.cpp b/Core/GDCore/IDE/WholeProjectRefactorer.cpp index 66e96fc40d25..7180dbf95611 100644 --- a/Core/GDCore/IDE/WholeProjectRefactorer.cpp +++ b/Core/GDCore/IDE/WholeProjectRefactorer.cpp @@ -819,16 +819,17 @@ void WholeProjectRefactorer::RenameObjectEventsFunction( void WholeProjectRefactorer::RenameParameter( gd::Project &project, gd::ProjectScopedContainers &projectScopedContainers, - gd::EventsFunction &eventsFunction, const gd::String &oldParameterName, - const gd::String &newParameterName) { + gd::EventsFunction &eventsFunction, + const gd::ObjectsContainer ¶meterObjectsContainer, + const gd::String &oldParameterName, const gd::String &newParameterName) { auto ¶meters = eventsFunction.GetParameters(); if (!parameters.HasParameterNamed(oldParameterName)) return; auto ¶meter = parameters.GetParameter(oldParameterName); if (parameter.GetValueTypeMetadata().IsObject()) { gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction( - project, projectScopedContainers, eventsFunction, oldParameterName, - newParameterName, false); + project, projectScopedContainers, eventsFunction, + parameterObjectsContainer, oldParameterName, newParameterName, false); } else if (parameter.GetValueTypeMetadata().IsBehavior()) { size_t behaviorParameterIndex = parameters.GetParameterPosition(parameter); size_t objectParameterIndex = @@ -1750,6 +1751,15 @@ void WholeProjectRefactorer::BehaviorsAddedToObjectInScene( void WholeProjectRefactorer::ObjectOrGroupRenamedInScene( gd::Project &project, gd::Layout &layout, const gd::String &oldName, const gd::String &newName, bool isObjectGroup) { + gd::WholeProjectRefactorer::ObjectOrGroupRenamedInScene( + project, layout, layout.GetObjects(), oldName, newName, isObjectGroup); +} + +void WholeProjectRefactorer::ObjectOrGroupRenamedInScene( + gd::Project &project, gd::Layout &layout, + const gd::ObjectsContainer &targetedObjectsContainer, + const gd::String &oldName, const gd::String &newName, bool isObjectGroup) { + if (oldName == newName || newName.empty() || oldName.empty()) return; @@ -1759,7 +1769,7 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInScene( // Rename object in the current layout gd::EventsRefactorer::RenameObjectInEvents( project.GetCurrentPlatform(), projectScopedContainers, layout.GetEvents(), - oldName, newName); + layout.GetObjects(), oldName, newName); // Object groups can't have instances or be in other groups if (!isObjectGroup) { @@ -1776,7 +1786,7 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInScene( auto &externalEvents = project.GetExternalEvents(externalEventsName); gd::EventsRefactorer::RenameObjectInEvents( project.GetCurrentPlatform(), projectScopedContainers, - externalEvents.GetEvents(), oldName, newName); + externalEvents.GetEvents(), layout.GetObjects(), oldName, newName); } // Rename object in external layouts @@ -2022,8 +2032,8 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject( eventsBasedObject.GetEventsFunctions().GetInternalVector()) { auto *function = functionUniquePtr.get(); WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction( - project, projectScopedContainers, *function, oldName, newName, - isObjectGroup); + project, projectScopedContainers, *function, + eventsBasedObject.GetObjects(), oldName, newName, isObjectGroup); } // Object groups can't have instances or be in other groups @@ -2040,11 +2050,12 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject( void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction( gd::Project &project, const gd::ProjectScopedContainers &projectScopedContainers, - gd::EventsFunction &eventsFunction, const gd::String &oldName, - const gd::String &newName, bool isObjectGroup) { + gd::EventsFunction &eventsFunction, + const gd::ObjectsContainer &targetedObjectsContainer, + const gd::String &oldName, const gd::String &newName, bool isObjectGroup) { gd::EventsRefactorer::RenameObjectInEvents( project.GetCurrentPlatform(), projectScopedContainers, - eventsFunction.GetEvents(), oldName, newName); + eventsFunction.GetEvents(), targetedObjectsContainer, oldName, newName); // Object groups can't be in other groups if (!isObjectGroup) { @@ -2070,7 +2081,7 @@ void WholeProjectRefactorer::GlobalObjectOrGroupRenamed( if (layout.GetObjects().HasObjectNamed(oldName)) continue; - ObjectOrGroupRenamedInScene(project, layout, oldName, newName, + ObjectOrGroupRenamedInScene(project, layout, project.GetObjects(), oldName, newName, isObjectGroup); } } diff --git a/Core/GDCore/IDE/WholeProjectRefactorer.h b/Core/GDCore/IDE/WholeProjectRefactorer.h index 268a282372ca..ff169720fb76 100644 --- a/Core/GDCore/IDE/WholeProjectRefactorer.h +++ b/Core/GDCore/IDE/WholeProjectRefactorer.h @@ -180,6 +180,7 @@ class GD_CORE_API WholeProjectRefactorer { RenameParameter(gd::Project &project, gd::ProjectScopedContainers &projectScopedContainers, gd::EventsFunction &eventsFunction, + const gd::ObjectsContainer ¶meterObjectsContainer, const gd::String &oldParameterName, const gd::String &newParameterName); @@ -536,6 +537,7 @@ class GD_CORE_API WholeProjectRefactorer { gd::Project& project, const gd::ProjectScopedContainers &projectScopedContainers, gd::EventsFunction& eventsFunction, + const gd::ObjectsContainer &targetedObjectsContainer, const gd::String& oldName, const gd::String& newName, bool isObjectGroup); @@ -656,6 +658,12 @@ class GD_CORE_API WholeProjectRefactorer { virtual ~WholeProjectRefactorer(){}; private: + static void ObjectOrGroupRenamedInScene(gd::Project &project, + gd::Layout &scene, + const gd::ObjectsContainer &targetedObjectsContainer, + const gd::String &oldName, + const gd::String &newName, + bool isObjectGroup); static std::vector GetAssociatedExternalLayouts( gd::Project& project, gd::Layout& layout); static std::vector diff --git a/Core/tests/WholeProjectRefactorer.cpp b/Core/tests/WholeProjectRefactorer.cpp index 33c99d85fd97..172d8b17c811 100644 --- a/Core/tests/WholeProjectRefactorer.cpp +++ b/Core/tests/WholeProjectRefactorer.cpp @@ -1602,7 +1602,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { // Trigger the refactoring after the renaming of an object gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction( project, projectScopedContainers, eventsFunction, - "Object1", "RenamedObject1", + parametersObjectsContainer, "Object1", "RenamedObject1", /* isObjectGroup=*/false); REQUIRE(objectGroup.Find("Object1") == false); @@ -1637,7 +1637,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { // Trigger the refactoring after the renaming of an object gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction( project, projectScopedContainers, eventsFunction, - "ObjectWithMyBehavior", "RenamedObjectWithMyBehavior", + parametersObjectsContainer, "ObjectWithMyBehavior", + "RenamedObjectWithMyBehavior", /* isObjectGroup=*/false); // Check object name has been renamed in action parameters. @@ -2364,8 +2365,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { project, eventsExtension, eventsFunction, parametersObjectsContainer, parameterVariablesContainer); gd::WholeProjectRefactorer::RenameParameter( - project, projectScopedContainers, eventsFunction, "MyParameter", - "MyRenamedParameter"); + project, projectScopedContainers, eventsFunction, + parametersObjectsContainer, "MyParameter", "MyRenamedParameter"); REQUIRE(instruction.GetParameter(0).GetPlainString() == "MyRenamedParameter"); @@ -2403,8 +2404,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { project, eventsExtension, eventsFunction, parametersObjectsContainer, parameterVariablesContainer); gd::WholeProjectRefactorer::RenameParameter( - project, projectScopedContainers, eventsFunction, "MyObject", - "MyRenamedObject"); + project, projectScopedContainers, eventsFunction, + parametersObjectsContainer, "MyObject", "MyRenamedObject"); REQUIRE(instruction.GetParameter(0).GetPlainString() == "MyRenamedObject"); @@ -2449,8 +2450,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { project, eventsExtension, eventsFunction, parametersObjectsContainer, parameterVariablesContainer); gd::WholeProjectRefactorer::RenameParameter( - project, projectScopedContainers, eventsFunction, "MyBehavior", - "MyRenamedBehavior"); + project, projectScopedContainers, eventsFunction, + parametersObjectsContainer, "MyBehavior", "MyRenamedBehavior"); REQUIRE(instruction.GetParameter(1).GetPlainString() == "MyRenamedBehavior"); diff --git a/GDevelop.js/Bindings/Bindings.idl b/GDevelop.js/Bindings/Bindings.idl index 278adb2abeeb..419b36c136b4 100644 --- a/GDevelop.js/Bindings/Bindings.idl +++ b/GDevelop.js/Bindings/Bindings.idl @@ -2437,7 +2437,13 @@ interface VectorEventsSearchResult { }; interface EventsRefactorer { - void STATIC_RenameObjectInEvents([Const, Ref] Platform platform, [Ref] ProjectScopedContainers projectScopedContainers, [Ref] EventsList events, [Const] DOMString oldName, [Const] DOMString newName); + void STATIC_RenameObjectInEvents( + [Const, Ref] Platform platform, + [Ref] ProjectScopedContainers projectScopedContainers, + [Ref] EventsList events, + [Const, Ref] ObjectsContainer targetedObjectsContainer, + [Const] DOMString oldName, + [Const] DOMString newName); [Value] VectorEventsSearchResult STATIC_ReplaceStringInEvents([Ref] ObjectsContainer project, [Ref] ObjectsContainer layout, [Ref] EventsList events, [Const] DOMString toReplace, [Const] DOMString newString, boolean matchCase, boolean inConditions, boolean inActions, boolean inEventStrings); [Value] VectorEventsSearchResult STATIC_SearchInEvents([Const, Ref] Platform platform, [Ref] EventsList events, [Const] DOMString search, boolean matchCase, boolean inConditions, boolean inActions, boolean inEventStrings, boolean inEventSentences); }; @@ -2668,6 +2674,7 @@ interface WholeProjectRefactorer { [Ref] Project project, [Ref] ProjectScopedContainers projectScopedContainers, [Ref] EventsFunction eventsFunction, + [Const, Ref] ObjectsContainer parameterObjectsContainer, [Const] DOMString oldName, [Const] DOMString newName, boolean isObjectGroup); diff --git a/GDevelop.js/types.d.ts b/GDevelop.js/types.d.ts index 2146cb0371bd..8fd018666595 100644 --- a/GDevelop.js/types.d.ts +++ b/GDevelop.js/types.d.ts @@ -1863,7 +1863,7 @@ export class VectorEventsSearchResult extends EmscriptenObject { } export class EventsRefactorer extends EmscriptenObject { - static renameObjectInEvents(platform: Platform, projectScopedContainers: ProjectScopedContainers, events: EventsList, oldName: string, newName: string): void; + static renameObjectInEvents(platform: Platform, projectScopedContainers: ProjectScopedContainers, events: EventsList, targetedObjectsContainer: ObjectsContainer, oldName: string, newName: string): void; static replaceStringInEvents(project: ObjectsContainer, layout: ObjectsContainer, events: EventsList, toReplace: string, newString: string, matchCase: boolean, inConditions: boolean, inActions: boolean, inEventStrings: boolean): VectorEventsSearchResult; static searchInEvents(platform: Platform, events: EventsList, search: string, matchCase: boolean, inConditions: boolean, inActions: boolean, inEventStrings: boolean, inEventSentences: boolean): VectorEventsSearchResult; } @@ -1931,7 +1931,7 @@ export class WholeProjectRefactorer extends EmscriptenObject { static objectOrGroupRenamedInScene(project: Project, scene: Layout, oldName: string, newName: string, isObjectGroup: boolean): void; static objectRemovedInScene(project: Project, scene: Layout, objectName: string): void; static behaviorsAddedToObjectInScene(project: Project, scene: Layout, objectName: string): void; - static objectOrGroupRenamedInEventsFunction(project: Project, projectScopedContainers: ProjectScopedContainers, eventsFunction: EventsFunction, oldName: string, newName: string, isObjectGroup: boolean): void; + static objectOrGroupRenamedInEventsFunction(project: Project, projectScopedContainers: ProjectScopedContainers, eventsFunction: EventsFunction, parameterObjectsContainer: ObjectsContainer, oldName: string, newName: string, isObjectGroup: boolean): void; static objectRemovedInEventsFunction(project: Project, eventsFunction: EventsFunction, objectName: string): void; static objectOrGroupRenamedInEventsBasedObject(project: Project, projectScopedContainers: ProjectScopedContainers, eventsBasedObject: EventsBasedObject, oldName: string, newName: string, isObjectGroup: boolean): void; static objectRemovedInEventsBasedObject(project: Project, eventsBasedObject: EventsBasedObject, objectName: string): void; diff --git a/GDevelop.js/types/gdeventsrefactorer.js b/GDevelop.js/types/gdeventsrefactorer.js index f187aa8e09ff..3c17c6045f46 100644 --- a/GDevelop.js/types/gdeventsrefactorer.js +++ b/GDevelop.js/types/gdeventsrefactorer.js @@ -1,6 +1,6 @@ // Automatically generated by GDevelop.js/scripts/generate-types.js declare class gdEventsRefactorer { - static renameObjectInEvents(platform: gdPlatform, projectScopedContainers: gdProjectScopedContainers, events: gdEventsList, oldName: string, newName: string): void; + static renameObjectInEvents(platform: gdPlatform, projectScopedContainers: gdProjectScopedContainers, events: gdEventsList, targetedObjectsContainer: gdObjectsContainer, oldName: string, newName: string): void; static replaceStringInEvents(project: gdObjectsContainer, layout: gdObjectsContainer, events: gdEventsList, toReplace: string, newString: string, matchCase: boolean, inConditions: boolean, inActions: boolean, inEventStrings: boolean): gdVectorEventsSearchResult; static searchInEvents(platform: gdPlatform, events: gdEventsList, search: string, matchCase: boolean, inConditions: boolean, inActions: boolean, inEventStrings: boolean, inEventSentences: boolean): gdVectorEventsSearchResult; delete(): void; diff --git a/GDevelop.js/types/gdwholeprojectrefactorer.js b/GDevelop.js/types/gdwholeprojectrefactorer.js index 5830ed55a40a..bdff4c7b93ed 100644 --- a/GDevelop.js/types/gdwholeprojectrefactorer.js +++ b/GDevelop.js/types/gdwholeprojectrefactorer.js @@ -35,7 +35,7 @@ declare class gdWholeProjectRefactorer { static objectOrGroupRenamedInScene(project: gdProject, scene: gdLayout, oldName: string, newName: string, isObjectGroup: boolean): void; static objectRemovedInScene(project: gdProject, scene: gdLayout, objectName: string): void; static behaviorsAddedToObjectInScene(project: gdProject, scene: gdLayout, objectName: string): void; - static objectOrGroupRenamedInEventsFunction(project: gdProject, projectScopedContainers: gdProjectScopedContainers, eventsFunction: gdEventsFunction, oldName: string, newName: string, isObjectGroup: boolean): void; + static objectOrGroupRenamedInEventsFunction(project: gdProject, projectScopedContainers: gdProjectScopedContainers, eventsFunction: gdEventsFunction, parameterObjectsContainer: gdObjectsContainer, oldName: string, newName: string, isObjectGroup: boolean): void; static objectRemovedInEventsFunction(project: gdProject, eventsFunction: gdEventsFunction, objectName: string): void; static objectOrGroupRenamedInEventsBasedObject(project: gdProject, projectScopedContainers: gdProjectScopedContainers, eventsBasedObject: gdEventsBasedObject, oldName: string, newName: string, isObjectGroup: boolean): void; static objectRemovedInEventsBasedObject(project: gdProject, eventsBasedObject: gdEventsBasedObject, objectName: string): void; diff --git a/newIDE/app/src/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor/index.js b/newIDE/app/src/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor/index.js index 700bc25fb55b..192c714ce41f 100644 --- a/newIDE/app/src/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor/index.js +++ b/newIDE/app/src/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor/index.js @@ -102,6 +102,7 @@ export default class EventsFunctionConfigurationEditor extends React.Component< project, projectScopedContainersAccessor, eventsFunction, + objectsContainer } = this.props; // newName is supposed to have been already validated @@ -112,6 +113,8 @@ export default class EventsFunctionConfigurationEditor extends React.Component< project, projectScopedContainersAccessor.get(), eventsFunction, + // This is the ObjectsContainer generated from parameters + objectsContainer, group.getName(), newName, /* isObjectGroup=*/ true From 5ffc0410dd13a50b8f8d6bd805cb975e4bee34b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Sun, 1 Dec 2024 12:34:32 +0100 Subject: [PATCH 5/9] Fix tests comments. --- Core/tests/WholeProjectRefactorer.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Core/tests/WholeProjectRefactorer.cpp b/Core/tests/WholeProjectRefactorer.cpp index 172d8b17c811..9be25c37f5f3 100644 --- a/Core/tests/WholeProjectRefactorer.cpp +++ b/Core/tests/WholeProjectRefactorer.cpp @@ -1492,7 +1492,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { auto &layout = project.GetLayout("Scene"); - // Trigger the refactoring after the renaming of an object + // Trigger the refactoring before the renaming of an object gd::WholeProjectRefactorer::ObjectOrGroupRenamedInScene( project, layout, "ObjectWithMyBehavior", "RenamedObjectWithMyBehavior", @@ -1520,7 +1520,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { auto &layout = project.GetLayout("Scene"); - // Trigger the refactoring after the renaming of a group + // Trigger the refactoring before the renaming of a group gd::WholeProjectRefactorer::ObjectOrGroupRenamedInScene( project, layout, "GroupWithMyBehavior", "RenamedGroupWithMyBehavior", /* isObjectGroup=*/true); @@ -1599,7 +1599,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { project, eventsExtension, eventsFunction, parametersObjectsContainer); - // Trigger the refactoring after the renaming of an object + // Trigger the refactoring before the renaming of an object gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction( project, projectScopedContainers, eventsFunction, parametersObjectsContainer, "Object1", "RenamedObject1", @@ -1634,7 +1634,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { objectWithMyBehavior.GetVariables().InsertNew("MyVariable"); objectWithMyBehavior.GetVariables().InsertNew("MyStructureVariable").CastTo(gd::Variable::Structure); - // Trigger the refactoring after the renaming of an object + // Trigger the refactoring before the renaming of an object gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction( project, projectScopedContainers, eventsFunction, parametersObjectsContainer, "ObjectWithMyBehavior", @@ -1754,7 +1754,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { project, eventsExtension, eventsBasedObject, parametersObjectsContainer); - // Trigger the refactoring after the renaming of an object + // Trigger the refactoring before the renaming of an object gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject( project, projectScopedContainers, eventsBasedObject, "ObjectWithMyBehavior", "RenamedObjectWithMyBehavior", From 3870dd1144f87b1bb2732ca869934187043c8e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Sun, 1 Dec 2024 13:02:48 +0100 Subject: [PATCH 6/9] Fix unintended renaming of object where it's a variable. --- .../Extensions/Metadata/ValueTypeMetadata.h | 12 +- Core/GDCore/IDE/Events/EventsRefactorer.cpp | 4 +- Core/tests/WholeProjectRefactorer.cpp | 121 ++++++++++++++++++ 3 files changed, 134 insertions(+), 3 deletions(-) diff --git a/Core/GDCore/Extensions/Metadata/ValueTypeMetadata.h b/Core/GDCore/Extensions/Metadata/ValueTypeMetadata.h index ccc37cd693d1..a39e082fd817 100644 --- a/Core/GDCore/Extensions/Metadata/ValueTypeMetadata.h +++ b/Core/GDCore/Extensions/Metadata/ValueTypeMetadata.h @@ -135,7 +135,17 @@ class GD_CORE_API ValueTypeMetadata { * and ExpressionAutocompletion) and in the EventsCodeGenerator. */ bool IsVariable() const { - return gd::ValueTypeMetadata::GetPrimitiveValueType(name) == "variable"; + return gd::ValueTypeMetadata::IsVariable(name); + } + + /** + * \brief Return true if the type of the parameter is a variable. + * \note If you had a new type of parameter, also add it in the IDE ( + * see EventsFunctionParametersEditor, ParameterRenderingService + * and ExpressionAutocompletion) and in the EventsCodeGenerator. + */ + static bool IsVariable(const gd::String &type) { + return gd::ValueTypeMetadata::GetPrimitiveValueType(type) == "variable"; } /** diff --git a/Core/GDCore/IDE/Events/EventsRefactorer.cpp b/Core/GDCore/IDE/Events/EventsRefactorer.cpp index fc01bcc5a77e..33a9a2ab5f65 100644 --- a/Core/GDCore/IDE/Events/EventsRefactorer.cpp +++ b/Core/GDCore/IDE/Events/EventsRefactorer.cpp @@ -80,7 +80,7 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker { void OnVisitVariableNode(VariableNode& node) override { auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node); - if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) { + if (gd::ValueTypeMetadata::IsVariable(type)) { // Nothing to do (this can't reference an object) } else { if (node.name == objectName) { @@ -116,7 +116,7 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker { node.identifierName == objectName) { hasDoneRenaming = true; node.identifierName = objectNewName; - } else if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) { + } else if (gd::ValueTypeMetadata::IsVariable(type)) { // Nothing to do (this can't reference an object) } else { if (node.identifierName == objectName) { diff --git a/Core/tests/WholeProjectRefactorer.cpp b/Core/tests/WholeProjectRefactorer.cpp index 9be25c37f5f3..78a9929a1024 100644 --- a/Core/tests/WholeProjectRefactorer.cpp +++ b/Core/tests/WholeProjectRefactorer.cpp @@ -2374,6 +2374,44 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { "MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyRenamedParameter])"); } + SECTION("(Free) number parameter not renamed (in variable parameter)") { + gd::Project project; + gd::Platform platform; + SetupProjectWithDummyPlatform(project, platform); + auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project); + + auto &eventsFunction = + eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0); + eventsFunction.GetParameters() + .AddNewParameter("MyParameter") + .GetValueTypeMetadata() + .SetName("number"); + // Parameters can't actually be used in "variable" parameters. + auto &instruction = CreateInstructionWithVariableParameter( + project, eventsFunction.GetEvents(), "MyParameter"); + auto &instruction2 = CreateInstructionWithNumberParameter( + project, eventsFunction.GetEvents(), + "MyExtension::GetVariableAsNumber(MyParameter)"); + + gd::ObjectsContainer parametersObjectsContainer( + gd::ObjectsContainer::SourceType::Function); + gd::VariablesContainer parameterVariablesContainer( + gd::VariablesContainer::SourceType::Parameters); + auto projectScopedContainers = gd::ProjectScopedContainers:: + MakeNewProjectScopedContainersForFreeEventsFunction( + project, eventsExtension, eventsFunction, + parametersObjectsContainer, parameterVariablesContainer); + gd::WholeProjectRefactorer::RenameParameter( + project, projectScopedContainers, eventsFunction, + parametersObjectsContainer, "MyParameter", "MyRenamedParameter"); + + // "variable" parameters are left untouched. + REQUIRE(instruction.GetParameter(0).GetPlainString() == + "MyParameter"); + REQUIRE(instruction2.GetParameter(0).GetPlainString() == + "MyExtension::GetVariableAsNumber(MyParameter)"); + } + SECTION("(Free) object parameter renamed (in expressions)") { gd::Project project; gd::Platform platform; @@ -2415,6 +2453,45 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { "MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyRenamedObject.GetObjectStringWith1Param(0)])"); } + SECTION("(Free) object parameter not renamed (in variable parameter)") { + gd::Project project; + gd::Platform platform; + SetupProjectWithDummyPlatform(project, platform); + auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project); + + auto &eventsFunction = + eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0); + eventsFunction.GetParameters() + .AddNewParameter("MyObject") + .GetValueTypeMetadata() + .SetName("objectList") + .SetExtraInfo("MyExtension::Sprite"); + // Parameters can't actually be used in "variable" parameters. + auto &instruction = CreateInstructionWithVariableParameter( + project, eventsFunction.GetEvents(), "MyObject"); + auto &instruction2 = CreateInstructionWithNumberParameter( + project, eventsFunction.GetEvents(), + "MyExtension::GetVariableAsNumber(MyObject)"); + + gd::ObjectsContainer parametersObjectsContainer( + gd::ObjectsContainer::SourceType::Function); + gd::VariablesContainer parameterVariablesContainer( + gd::VariablesContainer::SourceType::Parameters); + auto projectScopedContainers = gd::ProjectScopedContainers:: + MakeNewProjectScopedContainersForFreeEventsFunction( + project, eventsExtension, eventsFunction, + parametersObjectsContainer, parameterVariablesContainer); + gd::WholeProjectRefactorer::RenameParameter( + project, projectScopedContainers, eventsFunction, + parametersObjectsContainer, "MyObject", "MyRenamedObject"); + + // "variable" parameters are left untouched. + REQUIRE(instruction.GetParameter(0).GetPlainString() == + "MyObject"); + REQUIRE(instruction2.GetParameter(0).GetPlainString() == + "MyExtension::GetVariableAsNumber(MyObject)"); + } + SECTION("(Free) behavior parameter renamed (in expressions)") { gd::Project project; gd::Platform platform; @@ -2461,6 +2538,50 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { "MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyObject.MyRenamedBehavior::GetBehaviorStringWith1Param(0)])"); } + SECTION("(Free) behavior parameter not renamed (in variable parameter)") { + gd::Project project; + gd::Platform platform; + SetupProjectWithDummyPlatform(project, platform); + auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project); + + auto &eventsFunction = + eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0); + eventsFunction.GetParameters() + .AddNewParameter("MyObject") + .GetValueTypeMetadata() + .SetName("objectList") + .SetExtraInfo("MyExtension::Sprite"); + eventsFunction.GetParameters() + .AddNewParameter("MyBehavior") + .GetValueTypeMetadata() + .SetName("behavior") + .SetExtraInfo("MyExtension::MyBehavior"); + // Parameters can't actually be used in "variable" parameters. + auto &instruction = CreateInstructionWithVariableParameter( + project, eventsFunction.GetEvents(), "MyBehavior"); + auto &instruction2 = CreateInstructionWithNumberParameter( + project, eventsFunction.GetEvents(), + "MyExtension::GetVariableAsNumber(MyBehavior)"); + + gd::ObjectsContainer parametersObjectsContainer( + gd::ObjectsContainer::SourceType::Function); + gd::VariablesContainer parameterVariablesContainer( + gd::VariablesContainer::SourceType::Parameters); + auto projectScopedContainers = gd::ProjectScopedContainers:: + MakeNewProjectScopedContainersForFreeEventsFunction( + project, eventsExtension, eventsFunction, + parametersObjectsContainer, parameterVariablesContainer); + gd::WholeProjectRefactorer::RenameParameter( + project, projectScopedContainers, eventsFunction, + parametersObjectsContainer, "MyBehavior", "MyRenamedBehavior"); + + // "variable" parameters are left untouched. + REQUIRE(instruction.GetParameter(0).GetPlainString() == + "MyBehavior"); + REQUIRE(instruction2.GetParameter(0).GetPlainString() == + "MyExtension::GetVariableAsNumber(MyBehavior)"); + } + SECTION("(Free) events action parameter moved") { gd::Project project; gd::Platform platform; From 911dee6a2d782965adc0706187866cf5a46eb64c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Sun, 1 Dec 2024 15:40:18 +0100 Subject: [PATCH 7/9] Plug the refactor in the editor. --- GDevelop.js/Bindings/Bindings.idl | 7 ++ GDevelop.js/Bindings/Wrapper.cpp | 1 + GDevelop.js/types.d.ts | 1 + GDevelop.js/types/gdwholeprojectrefactorer.js | 1 + .../EventsFunctionParametersEditor.js | 72 ++++++++++++++----- .../index.js | 9 ++- .../EventsFunctionsExtensionEditor/index.js | 23 ++++++ .../EventsFunctionExtractorDialog.js | 3 + ...entsFunctionConfigurationEditor.stories.js | 12 ++++ 9 files changed, 109 insertions(+), 20 deletions(-) diff --git a/GDevelop.js/Bindings/Bindings.idl b/GDevelop.js/Bindings/Bindings.idl index 419b36c136b4..30095a72da3a 100644 --- a/GDevelop.js/Bindings/Bindings.idl +++ b/GDevelop.js/Bindings/Bindings.idl @@ -2523,6 +2523,13 @@ interface WholeProjectRefactorer { [Const, Ref] EventsBasedObject eventsBasedObject, [Const] DOMString oldName, [Const] DOMString newName); + void STATIC_RenameParameter( + [Ref] Project project, + [Ref] ProjectScopedContainers projectScopedContainers, + [Ref] EventsFunction eventsFunction, + [Const, Ref] ObjectsContainer parameterObjectsContainer, + [Const] DOMString oldName, + [Const] DOMString newName); void STATIC_MoveEventsFunctionParameter( [Ref] Project project, [Const, Ref] EventsFunctionsExtension eventsFunctionsExtension, diff --git a/GDevelop.js/Bindings/Wrapper.cpp b/GDevelop.js/Bindings/Wrapper.cpp index 4641cbbc2333..24ecab1a1820 100644 --- a/GDevelop.js/Bindings/Wrapper.cpp +++ b/GDevelop.js/Bindings/Wrapper.cpp @@ -727,6 +727,7 @@ typedef ExtensionAndMetadata ExtensionAndExpressionMetadata; #define STATIC_RenameEventsFunction RenameEventsFunction #define STATIC_RenameBehaviorEventsFunction RenameBehaviorEventsFunction #define STATIC_RenameObjectEventsFunction RenameObjectEventsFunction +#define STATIC_RenameParameter RenameParameter #define STATIC_MoveEventsFunctionParameter MoveEventsFunctionParameter #define STATIC_MoveBehaviorEventsFunctionParameter \ MoveBehaviorEventsFunctionParameter diff --git a/GDevelop.js/types.d.ts b/GDevelop.js/types.d.ts index 8fd018666595..c52301dc1a10 100644 --- a/GDevelop.js/types.d.ts +++ b/GDevelop.js/types.d.ts @@ -1905,6 +1905,7 @@ export class WholeProjectRefactorer extends EmscriptenObject { static renameEventsFunction(project: Project, eventsFunctionsExtension: EventsFunctionsExtension, oldName: string, newName: string): void; static renameBehaviorEventsFunction(project: Project, eventsFunctionsExtension: EventsFunctionsExtension, eventsBasedBehavior: EventsBasedBehavior, oldName: string, newName: string): void; static renameObjectEventsFunction(project: Project, eventsFunctionsExtension: EventsFunctionsExtension, eventsBasedObject: EventsBasedObject, oldName: string, newName: string): void; + static renameParameter(project: Project, projectScopedContainers: ProjectScopedContainers, eventsFunction: EventsFunction, parameterObjectsContainer: ObjectsContainer, oldName: string, newName: string): void; static moveEventsFunctionParameter(project: Project, eventsFunctionsExtension: EventsFunctionsExtension, functionName: string, oldIndex: number, newIndex: number): void; static moveBehaviorEventsFunctionParameter(project: Project, eventsFunctionsExtension: EventsFunctionsExtension, eventsBasedBehavior: EventsBasedBehavior, functionName: string, oldIndex: number, newIndex: number): void; static moveObjectEventsFunctionParameter(project: Project, eventsFunctionsExtension: EventsFunctionsExtension, eventsBasedObject: EventsBasedObject, functionName: string, oldIndex: number, newIndex: number): void; diff --git a/GDevelop.js/types/gdwholeprojectrefactorer.js b/GDevelop.js/types/gdwholeprojectrefactorer.js index bdff4c7b93ed..bb71b68309a3 100644 --- a/GDevelop.js/types/gdwholeprojectrefactorer.js +++ b/GDevelop.js/types/gdwholeprojectrefactorer.js @@ -9,6 +9,7 @@ declare class gdWholeProjectRefactorer { static renameEventsFunction(project: gdProject, eventsFunctionsExtension: gdEventsFunctionsExtension, oldName: string, newName: string): void; static renameBehaviorEventsFunction(project: gdProject, eventsFunctionsExtension: gdEventsFunctionsExtension, eventsBasedBehavior: gdEventsBasedBehavior, oldName: string, newName: string): void; static renameObjectEventsFunction(project: gdProject, eventsFunctionsExtension: gdEventsFunctionsExtension, eventsBasedObject: gdEventsBasedObject, oldName: string, newName: string): void; + static renameParameter(project: gdProject, projectScopedContainers: gdProjectScopedContainers, eventsFunction: gdEventsFunction, parameterObjectsContainer: gdObjectsContainer, oldName: string, newName: string): void; static moveEventsFunctionParameter(project: gdProject, eventsFunctionsExtension: gdEventsFunctionsExtension, functionName: string, oldIndex: number, newIndex: number): void; static moveBehaviorEventsFunctionParameter(project: gdProject, eventsFunctionsExtension: gdEventsFunctionsExtension, eventsBasedBehavior: gdEventsBasedBehavior, functionName: string, oldIndex: number, newIndex: number): void; static moveObjectEventsFunctionParameter(project: gdProject, eventsFunctionsExtension: gdEventsFunctionsExtension, eventsBasedObject: gdEventsBasedObject, functionName: string, oldIndex: number, newIndex: number): void; diff --git a/newIDE/app/src/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor/EventsFunctionParametersEditor.js b/newIDE/app/src/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor/EventsFunctionParametersEditor.js index eaa864a874d0..d7109b32e261 100644 --- a/newIDE/app/src/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor/EventsFunctionParametersEditor.js +++ b/newIDE/app/src/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor/EventsFunctionParametersEditor.js @@ -124,6 +124,11 @@ type Props = {| newIndex: number, done: (boolean) => void ) => void, + onFunctionParameterWillBeRenamed: ( + eventsFunction: gdEventsFunction, + oldName: string, + newName: string + ) => void, |}; export const EventsFunctionParametersEditor = ({ @@ -140,6 +145,7 @@ export const EventsFunctionParametersEditor = ({ onMoveFreeEventsParameter, onMoveBehaviorEventsParameter, onMoveObjectEventsParameter, + onFunctionParameterWillBeRenamed, }: Props) => { const scrollView = React.useRef(null); const [ @@ -193,18 +199,57 @@ export const EventsFunctionParametersEditor = ({ [eventsFunction, firstParameterIndex, freezeParameters] ); + const renameParameter = React.useCallback( + (parameter: gdParameterMetadata, newName: string) => { + if (newName === parameter.getName()) { + return; + } + const projectScopedContainers = projectScopedContainersAccessor.get(); + const validatedNewName = getValidatedParameterName( + eventsFunction.getParameters(), + projectScopedContainers, + newName + ); + onFunctionParameterWillBeRenamed( + eventsFunction, + parameter.getName(), + validatedNewName + ); + parameter.setName(validatedNewName); + forceUpdate(); + onParametersUpdated(); + }, + [ + eventsFunction, + forceUpdate, + onFunctionParameterWillBeRenamed, + onParametersUpdated, + projectScopedContainersAccessor, + ] + ); + const addParameterAt = React.useCallback( (index: number) => { const parameters = eventsFunction.getParameters(); - const newName = newNameGenerator('Parameter', name => - parameters.hasParameterNamed(name) + const projectScopedContainers = projectScopedContainersAccessor.get(); + const validatedNewName = getValidatedParameterName( + eventsFunction.getParameters(), + projectScopedContainers, + 'Parameter' ); - parameters.insertNewParameter(newName, index).setType('objectList'); + parameters + .insertNewParameter(validatedNewName, index) + .setType('objectList'); forceUpdate(); onParametersUpdated(); - setJustAddedParameterName(newName); + setJustAddedParameterName(validatedNewName); }, - [eventsFunction, forceUpdate, onParametersUpdated] + [ + eventsFunction, + forceUpdate, + onParametersUpdated, + projectScopedContainersAccessor, + ] ); const addParameter = React.useCallback( @@ -643,20 +688,9 @@ export const EventsFunctionParametersEditor = ({ margin="none" translatableHintText={t`Enter the parameter name (mandatory)`} value={parameter.getName()} - onChange={newName => { - if (newName === parameter.getName()) { - return; - } - const projectScopedContainers = projectScopedContainersAccessor.get(); - const validatedNewName = getValidatedParameterName( - parameters, - projectScopedContainers, - newName - ); - parameter.setName(validatedNewName); - forceUpdate(); - onParametersUpdated(); - }} + onChange={newName => + renameParameter(parameter, newName) + } disabled={isParameterDisabled(i)} fullWidth /> diff --git a/newIDE/app/src/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor/index.js b/newIDE/app/src/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor/index.js index 192c714ce41f..b65fe5a226a9 100644 --- a/newIDE/app/src/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor/index.js +++ b/newIDE/app/src/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor/index.js @@ -52,6 +52,11 @@ type Props = {| newIndex: number, done: (boolean) => void ) => void, + onFunctionParameterWillBeRenamed: ( + eventsFunction: gdEventsFunction, + oldName: string, + newName: string + ) => void, unsavedChanges?: ?UnsavedChanges, getFunctionGroupNames?: () => string[], |}; @@ -102,7 +107,7 @@ export default class EventsFunctionConfigurationEditor extends React.Component< project, projectScopedContainersAccessor, eventsFunction, - objectsContainer + objectsContainer, } = this.props; // newName is supposed to have been already validated @@ -150,6 +155,7 @@ export default class EventsFunctionConfigurationEditor extends React.Component< getFunctionGroupNames, eventsFunctionsContainer, eventsFunctionsExtension, + onFunctionParameterWillBeRenamed, } = this.props; const hasLegacyFunctionObjectGroups = @@ -215,6 +221,7 @@ export default class EventsFunctionConfigurationEditor extends React.Component< onMoveFreeEventsParameter={onMoveFreeEventsParameter} onMoveBehaviorEventsParameter={onMoveBehaviorEventsParameter} onMoveObjectEventsParameter={onMoveObjectEventsParameter} + onFunctionParameterWillBeRenamed={onFunctionParameterWillBeRenamed} key={eventsFunction ? eventsFunction.ptr : null} /> ) : null} diff --git a/newIDE/app/src/EventsFunctionsExtensionEditor/index.js b/newIDE/app/src/EventsFunctionsExtensionEditor/index.js index 1c1effdaa48e..a36776e7ad0e 100644 --- a/newIDE/app/src/EventsFunctionsExtensionEditor/index.js +++ b/newIDE/app/src/EventsFunctionsExtensionEditor/index.js @@ -1050,6 +1050,26 @@ export default class EventsFunctionsExtensionEditor extends React.Component< ); }; + _onFunctionParameterWillBeRenamed = ( + eventsFunction: gdEventsFunction, + oldName: string, + newName: string + ) => { + if (!this._projectScopedContainersAccessor) { + return; + } + const projectScopedContainers = this._projectScopedContainersAccessor.get(); + const { project } = this.props; + gd.WholeProjectRefactorer.renameParameter( + project, + projectScopedContainers, + eventsFunction, + this._objectsContainer, + oldName, + newName + ); + }; + _editOptions = (open: boolean = true) => { this.setState({ editOptionsDialogOpen: open, @@ -1339,6 +1359,9 @@ export default class EventsFunctionsExtensionEditor extends React.Component< onMoveObjectEventsParameter={this._makeMoveObjectEventsParameter( i18n )} + onFunctionParameterWillBeRenamed={ + this._onFunctionParameterWillBeRenamed + } unsavedChanges={this.props.unsavedChanges} getFunctionGroupNames={this._getFunctionGroupNames} /> diff --git a/newIDE/app/src/EventsSheet/EventsFunctionExtractor/EventsFunctionExtractorDialog.js b/newIDE/app/src/EventsSheet/EventsFunctionExtractor/EventsFunctionExtractorDialog.js index 228520192363..1a828f5e48db 100644 --- a/newIDE/app/src/EventsSheet/EventsFunctionExtractor/EventsFunctionExtractorDialog.js +++ b/newIDE/app/src/EventsSheet/EventsFunctionExtractor/EventsFunctionExtractorDialog.js @@ -326,6 +326,9 @@ export default class EventsFunctionExtractorDialog extends React.Component< // Force the dialog to adapt its size this.forceUpdate(); }} + onFunctionParameterWillBeRenamed={() => { + // Won't happen as the editor is freezed. + }} freezeParameters /> )} diff --git a/newIDE/app/src/stories/componentStories/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor.stories.js b/newIDE/app/src/stories/componentStories/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor.stories.js index f78fbb358754..e8b8a4f72bed 100644 --- a/newIDE/app/src/stories/componentStories/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor.stories.js +++ b/newIDE/app/src/stories/componentStories/EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor.stories.js @@ -32,6 +32,9 @@ export const DefaultFreeFunction = () => ( eventsFunctionsContainer={testProject.testEventsFunctionsExtension} eventsFunctionsExtension={testProject.testEventsFunctionsExtension} onParametersOrGroupsUpdated={action('Parameters or groups were updated')} + onFunctionParameterWillBeRenamed={action( + 'onFunctionParameterWillBeRenamed' + )} /> ); @@ -52,6 +55,9 @@ export const DefaultBehaviorFunction = () => ( eventsFunctionsContainer={testProject.testEventsBasedBehavior.getEventsFunctions()} eventsFunctionsExtension={testProject.testEventsFunctionsExtension} onParametersOrGroupsUpdated={action('Parameters or groups were updated')} + onFunctionParameterWillBeRenamed={action( + 'onFunctionParameterWillBeRenamed' + )} /> ); @@ -72,6 +78,9 @@ export const DefaultBehaviorLifecycleFunction = () => ( eventsFunctionsContainer={testProject.testEventsBasedBehavior.getEventsFunctions()} eventsFunctionsExtension={testProject.testEventsFunctionsExtension} onParametersOrGroupsUpdated={action('Parameters or groups were updated')} + onFunctionParameterWillBeRenamed={action( + 'onFunctionParameterWillBeRenamed' + )} /> ); @@ -92,6 +101,9 @@ export const DefaultObjectFunction = () => ( eventsFunctionsContainer={testProject.testEventsBasedObject.getEventsFunctions()} eventsFunctionsExtension={testProject.testEventsFunctionsExtension} onParametersOrGroupsUpdated={action('Parameters or groups were updated')} + onFunctionParameterWillBeRenamed={action( + 'onFunctionParameterWillBeRenamed' + )} /> ); From 77cee33e10787b404b6e81af5d79af2f4d4f7b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Sun, 1 Dec 2024 15:51:12 +0100 Subject: [PATCH 8/9] Rebase. --- .../IDE/Events/EventsParameterReplace.cpp | 6 ++--- Core/tests/WholeProjectRefactorer.cpp | 24 +++++-------------- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/Core/GDCore/IDE/Events/EventsParameterReplace.cpp b/Core/GDCore/IDE/Events/EventsParameterReplace.cpp index 8d1150ea986b..c6250bbb3e89 100644 --- a/Core/GDCore/IDE/Events/EventsParameterReplace.cpp +++ b/Core/GDCore/IDE/Events/EventsParameterReplace.cpp @@ -142,7 +142,7 @@ class GD_CORE_API ExpressionParameterReplacer parameterMetadata->GetValueTypeMetadata(); if (gd::EventsParameterReplacer::CanContainParameter( parameterTypeMetadata)) { - isParentTypeAVariable = parameterTypeMetadata.IsVariableOnly(); + isParentTypeAVariable = parameterTypeMetadata.IsVariable(); parameter->Visit(*this); } } @@ -198,7 +198,7 @@ bool EventsParameterReplacer::DoVisitInstruction(gd::Instruction& instruction, if (node) { ExpressionParameterReplacer renamer( platform, GetProjectScopedContainers(), - parameterMetadata.GetValueTypeMetadata().IsVariableOnly(), + parameterMetadata.GetValueTypeMetadata().IsVariable(), oldToNewPropertyNames); node->Visit(renamer); @@ -222,7 +222,7 @@ bool EventsParameterReplacer::DoVisitEventExpression( if (node) { ExpressionParameterReplacer renamer( platform, GetProjectScopedContainers(), - metadata.GetValueTypeMetadata().IsVariableOnly(), oldToNewPropertyNames); + metadata.GetValueTypeMetadata().IsVariable(), oldToNewPropertyNames); node->Visit(renamer); if (renamer.HasDoneRenaming()) { diff --git a/Core/tests/WholeProjectRefactorer.cpp b/Core/tests/WholeProjectRefactorer.cpp index 78a9929a1024..941440dd24b5 100644 --- a/Core/tests/WholeProjectRefactorer.cpp +++ b/Core/tests/WholeProjectRefactorer.cpp @@ -2358,12 +2358,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { gd::ObjectsContainer parametersObjectsContainer( gd::ObjectsContainer::SourceType::Function); - gd::VariablesContainer parameterVariablesContainer( - gd::VariablesContainer::SourceType::Parameters); auto projectScopedContainers = gd::ProjectScopedContainers:: MakeNewProjectScopedContainersForFreeEventsFunction( project, eventsExtension, eventsFunction, - parametersObjectsContainer, parameterVariablesContainer); + parametersObjectsContainer); gd::WholeProjectRefactorer::RenameParameter( project, projectScopedContainers, eventsFunction, parametersObjectsContainer, "MyParameter", "MyRenamedParameter"); @@ -2395,12 +2393,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { gd::ObjectsContainer parametersObjectsContainer( gd::ObjectsContainer::SourceType::Function); - gd::VariablesContainer parameterVariablesContainer( - gd::VariablesContainer::SourceType::Parameters); auto projectScopedContainers = gd::ProjectScopedContainers:: MakeNewProjectScopedContainersForFreeEventsFunction( project, eventsExtension, eventsFunction, - parametersObjectsContainer, parameterVariablesContainer); + parametersObjectsContainer); gd::WholeProjectRefactorer::RenameParameter( project, projectScopedContainers, eventsFunction, parametersObjectsContainer, "MyParameter", "MyRenamedParameter"); @@ -2435,12 +2431,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { gd::ObjectsContainer parametersObjectsContainer( gd::ObjectsContainer::SourceType::Function); - gd::VariablesContainer parameterVariablesContainer( - gd::VariablesContainer::SourceType::Parameters); auto projectScopedContainers = gd::ProjectScopedContainers:: MakeNewProjectScopedContainersForFreeEventsFunction( project, eventsExtension, eventsFunction, - parametersObjectsContainer, parameterVariablesContainer); + parametersObjectsContainer); gd::WholeProjectRefactorer::RenameParameter( project, projectScopedContainers, eventsFunction, parametersObjectsContainer, "MyObject", "MyRenamedObject"); @@ -2475,12 +2469,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { gd::ObjectsContainer parametersObjectsContainer( gd::ObjectsContainer::SourceType::Function); - gd::VariablesContainer parameterVariablesContainer( - gd::VariablesContainer::SourceType::Parameters); auto projectScopedContainers = gd::ProjectScopedContainers:: MakeNewProjectScopedContainersForFreeEventsFunction( project, eventsExtension, eventsFunction, - parametersObjectsContainer, parameterVariablesContainer); + parametersObjectsContainer); gd::WholeProjectRefactorer::RenameParameter( project, projectScopedContainers, eventsFunction, parametersObjectsContainer, "MyObject", "MyRenamedObject"); @@ -2520,12 +2512,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { gd::ObjectsContainer parametersObjectsContainer( gd::ObjectsContainer::SourceType::Function); - gd::VariablesContainer parameterVariablesContainer( - gd::VariablesContainer::SourceType::Parameters); auto projectScopedContainers = gd::ProjectScopedContainers:: MakeNewProjectScopedContainersForFreeEventsFunction( project, eventsExtension, eventsFunction, - parametersObjectsContainer, parameterVariablesContainer); + parametersObjectsContainer); gd::WholeProjectRefactorer::RenameParameter( project, projectScopedContainers, eventsFunction, parametersObjectsContainer, "MyBehavior", "MyRenamedBehavior"); @@ -2565,12 +2555,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") { gd::ObjectsContainer parametersObjectsContainer( gd::ObjectsContainer::SourceType::Function); - gd::VariablesContainer parameterVariablesContainer( - gd::VariablesContainer::SourceType::Parameters); auto projectScopedContainers = gd::ProjectScopedContainers:: MakeNewProjectScopedContainersForFreeEventsFunction( project, eventsExtension, eventsFunction, - parametersObjectsContainer, parameterVariablesContainer); + parametersObjectsContainer); gd::WholeProjectRefactorer::RenameParameter( project, projectScopedContainers, eventsFunction, parametersObjectsContainer, "MyBehavior", "MyRenamedBehavior"); From 93c0c3754ceb9cec9fbd439522e9e07b31140e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Sun, 1 Dec 2024 17:46:15 +0100 Subject: [PATCH 9/9] Put back a condition removed by mistake. --- Core/GDCore/IDE/Events/EventsParameterReplace.cpp | 3 +-- Core/GDCore/IDE/Events/EventsParameterReplacer.h | 2 +- Core/GDCore/IDE/Events/EventsRefactorer.cpp | 12 ++++++++---- Core/GDCore/IDE/WholeProjectRefactorer.h | 7 +++++++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Core/GDCore/IDE/Events/EventsParameterReplace.cpp b/Core/GDCore/IDE/Events/EventsParameterReplace.cpp index c6250bbb3e89..ae31c5a6548b 100644 --- a/Core/GDCore/IDE/Events/EventsParameterReplace.cpp +++ b/Core/GDCore/IDE/Events/EventsParameterReplace.cpp @@ -29,8 +29,7 @@ namespace gd { /** - * \brief Go through the nodes and rename parameters, - * or signal if the instruction must be renamed if a removed property is used. + * \brief Go through the nodes and rename parameters. * * \see gd::ExpressionParser2 */ diff --git a/Core/GDCore/IDE/Events/EventsParameterReplacer.h b/Core/GDCore/IDE/Events/EventsParameterReplacer.h index c11bfaaf79f0..3e233f617438 100644 --- a/Core/GDCore/IDE/Events/EventsParameterReplacer.h +++ b/Core/GDCore/IDE/Events/EventsParameterReplacer.h @@ -23,7 +23,7 @@ class Platform; namespace gd { /** * \brief Replace in expressions and in parameters of actions or conditions, - * references to the name of a property by another. + * references to the name of a parameter by another. * * \ingroup IDE */ diff --git a/Core/GDCore/IDE/Events/EventsRefactorer.cpp b/Core/GDCore/IDE/Events/EventsRefactorer.cpp index 33a9a2ab5f65..058d2fa4c3e4 100644 --- a/Core/GDCore/IDE/Events/EventsRefactorer.cpp +++ b/Core/GDCore/IDE/Events/EventsRefactorer.cpp @@ -55,11 +55,15 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker { const gd::String &rootType, gd::ExpressionNode &node, const gd::String &objectName, const gd::String &objectNewName) { - ExpressionObjectRenamer renamer(platform, projectScopedContainers, rootType, - objectName, objectNewName); - node.Visit(renamer); + if (gd::ExpressionValidator::HasNoErrors(platform, projectScopedContainers, + rootType, node)) { + ExpressionObjectRenamer renamer(platform, projectScopedContainers, + rootType, objectName, objectNewName); + node.Visit(renamer); - return renamer.HasDoneRenaming(); + return renamer.HasDoneRenaming(); + } + return false; } bool HasDoneRenaming() const { return hasDoneRenaming; } diff --git a/Core/GDCore/IDE/WholeProjectRefactorer.h b/Core/GDCore/IDE/WholeProjectRefactorer.h index ff169720fb76..cdf8f0f61a76 100644 --- a/Core/GDCore/IDE/WholeProjectRefactorer.h +++ b/Core/GDCore/IDE/WholeProjectRefactorer.h @@ -176,6 +176,13 @@ class GD_CORE_API WholeProjectRefactorer { const gd::String& oldFunctionName, const gd::String& newFunctionName); + /** + * \brief Refactor the function **before** a parameter is renamed. + * + * \warning Do the renaming of the specified parameter after calling this. + * This is because the function is expected to have its old name for the + * refactoring. + */ static void RenameParameter(gd::Project &project, gd::ProjectScopedContainers &projectScopedContainers,