Skip to content

Commit

Permalink
Activate support for unicode characters (including emojis) for all na…
Browse files Browse the repository at this point in the history
…mes (objects, groups, variables, functions, parameters) on all projects (#5703)
  • Loading branch information
ClementPasteau authored Oct 12, 2023
1 parent e960fe7 commit 740485b
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 219 deletions.
81 changes: 29 additions & 52 deletions Core/GDCore/Project/Project.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@ using namespace std;

namespace gd {

// By default, disallow unicode in identifiers, but this can be set to true
// by the IDE. In the future, this will be set to true by default, keeping backward compatibility.
// We keep it disabled by default to progressively ask users to test it in real projects.
bool Project::allowUsageOfUnicodeIdentifierNames = false;

Project::Project()
: name(_("Project")),
version("1.0.0"),
Expand Down Expand Up @@ -86,26 +81,24 @@ Project::~Project() {}

void Project::ResetProjectUuid() { projectUuid = UUID::MakeUuid4(); }

std::unique_ptr<gd::Object>
Project::CreateObject(const gd::String &objectType, const gd::String &name) const {
std::unique_ptr<gd::Object> object =
gd::make_unique<Object>(name, objectType, CreateObjectConfiguration(objectType));

auto &platform = GetCurrentPlatform();
auto &project = *this;
auto addDefaultBehavior =
[&platform,
&project,
&object,
&objectType](const gd::String& behaviorType) {
auto &behaviorMetadata =
std::unique_ptr<gd::Object> Project::CreateObject(
const gd::String& objectType, const gd::String& name) const {
std::unique_ptr<gd::Object> object = gd::make_unique<Object>(
name, objectType, CreateObjectConfiguration(objectType));

auto& platform = GetCurrentPlatform();
auto& project = *this;
auto addDefaultBehavior = [&platform, &project, &object, &objectType](
const gd::String& behaviorType) {
auto& behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(platform, behaviorType);
if (MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
gd::LogWarning("Object: " + objectType + " has an unknown default behavior: " + behaviorType);
gd::LogWarning("Object: " + objectType +
" has an unknown default behavior: " + behaviorType);
return;
}
auto* behavior = object->AddNewBehavior(project, behaviorType,
behaviorMetadata.GetDefaultName());
auto* behavior = object->AddNewBehavior(
project, behaviorType, behaviorMetadata.GetDefaultName());
behavior->SetDefaultBehavior(true);
};

Expand All @@ -114,18 +107,17 @@ Project::CreateObject(const gd::String &objectType, const gd::String &name) cons
addDefaultBehavior("ResizableCapability::ResizableBehavior");
addDefaultBehavior("ScalableCapability::ScalableBehavior");
addDefaultBehavior("FlippableCapability::FlippableBehavior");
}
else {
auto &objectMetadata = gd::MetadataProvider::GetObjectMetadata(platform, objectType);
} else {
auto& objectMetadata =
gd::MetadataProvider::GetObjectMetadata(platform, objectType);
if (MetadataProvider::IsBadObjectMetadata(objectMetadata)) {
gd::LogWarning("Object: " + name + " has an unknown type: " + objectType);
}
for (auto &behaviorType : objectMetadata.GetDefaultBehaviors()) {
for (auto& behaviorType : objectMetadata.GetDefaultBehaviors()) {
addDefaultBehavior(behaviorType);
}
}


return std::move(object);
}

Expand Down Expand Up @@ -1038,28 +1030,18 @@ void Project::SerializeTo(SerializerElement& element) const {
externalSourceFilesElement.AddChild("sourceFile"));
}

void Project::AllowUsageOfUnicodeIdentifierNames(bool enable) {
allowUsageOfUnicodeIdentifierNames = enable;
}

bool Project::IsNameSafe(const gd::String& name) {
if (name.empty()) return false;

if (isdigit(name[0])) return false;

if (!allowUsageOfUnicodeIdentifierNames) {
gd::String legacyAllowedCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
return !(name.find_first_not_of(legacyAllowedCharacters) != gd::String::npos);
} else {
for (auto character : name) {
if (!GrammarTerminals::IsAllowedInIdentifier(character)) {
return false;
}
for (auto character : name) {
if (!GrammarTerminals::IsAllowedInIdentifier(character)) {
return false;
}

return true;
}

return true;
}

gd::String Project::GetSafeName(const gd::String& name) {
Expand All @@ -1069,18 +1051,13 @@ gd::String Project::GetSafeName(const gd::String& name) {

if (isdigit(name[0])) newName = "_" + newName;

gd::String legacyAllowedCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";

for (size_t i = 0;i < newName.size();++i) {
// Note that iterating on the characters is not super efficient (O(n^2), which
// could be avoided with an iterator), but this function is not critical for performance
// (only used to generate a name when a user creates a new entity or rename one).
for (size_t i = 0; i < newName.size(); ++i) {
// Note that iterating on the characters is not super efficient (O(n^2),
// which could be avoided with an iterator), but this function is not
// critical for performance (only used to generate a name when a user
// creates a new entity or rename one).
auto character = newName[i];
bool isAllowed =
allowUsageOfUnicodeIdentifierNames
? GrammarTerminals::IsAllowedInIdentifier(character)
: legacyAllowedCharacters.find(character) != gd::String::npos;
bool isAllowed = GrammarTerminals::IsAllowedInIdentifier(character);

// Replace all unallowed letters by an underscore.
if (!isAllowed) {
Expand Down
87 changes: 44 additions & 43 deletions Core/GDCore/Project/Project.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@

#include "GDCore/Project/ExtensionProperties.h"
#include "GDCore/Project/LoadingScreen.h"
#include "GDCore/Project/Watermark.h"
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/PlatformSpecificAssets.h"
#include "GDCore/Project/ResourcesManager.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/Project/Watermark.h"
#include "GDCore/String.h"
namespace gd {
class Platform;
Expand Down Expand Up @@ -82,7 +82,9 @@ class GD_CORE_API Project : public ObjectsContainer {
/**
* \brief Change the project description
*/
void SetDescription(const gd::String& description_) { description = description_; };
void SetDescription(const gd::String& description_) {
description = description_;
};

/**
* \brief Get the project description
Expand Down Expand Up @@ -124,7 +126,9 @@ class GD_CORE_API Project : public ObjectsContainer {
/**
* \brief Get the author usernames of the project.
*/
const std::vector<gd::String>& GetAuthorUsernames() const { return authorUsernames; };
const std::vector<gd::String>& GetAuthorUsernames() const {
return authorUsernames;
};

/**
* \brief Get the author usernames of the project, to modify them (non-const).
Expand All @@ -135,7 +139,9 @@ class GD_CORE_API Project : public ObjectsContainer {
* Define the project as playable with a keyboard.
* \param enable True to define the project as playable with a keyboard.
*/
void SetPlayableWithKeyboard(bool playable = true) { isPlayableWithKeyboard = playable; }
void SetPlayableWithKeyboard(bool playable = true) {
isPlayableWithKeyboard = playable;
}

/**
* Check if the project is defined as playable with a keyboard.
Expand All @@ -146,7 +152,9 @@ class GD_CORE_API Project : public ObjectsContainer {
* Define the project as playable with a gamepad.
* \param enable True to define the project as playable with a gamepad.
*/
void SetPlayableWithGamepad(bool playable = true) { isPlayableWithGamepad = playable; }
void SetPlayableWithGamepad(bool playable = true) {
isPlayableWithGamepad = playable;
}

/**
* Check if the project is defined as playable with a gamepad.
Expand All @@ -157,7 +165,9 @@ class GD_CORE_API Project : public ObjectsContainer {
* Define the project as playable on a mobile.
* \param enable True to define the project as playable on a mobile.
*/
void SetPlayableWithMobile(bool playable = true) { isPlayableWithMobile = playable; }
void SetPlayableWithMobile(bool playable = true) {
isPlayableWithMobile = playable;
}

/**
* Check if the project is defined as playable on a mobile.
Expand Down Expand Up @@ -391,17 +401,23 @@ class GD_CORE_API Project : public ObjectsContainer {
/**
* Set the antialiasing mode used by the game ("none" or "MSAA").
*/
void SetAntialiasingMode(const gd::String& antialiasingMode_) { antialiasingMode = antialiasingMode_; }
void SetAntialiasingMode(const gd::String& antialiasingMode_) {
antialiasingMode = antialiasingMode_;
}

/**
* Return true if antialising is enabled on mobiles.
*/
bool IsAntialisingEnabledOnMobile() const { return isAntialisingEnabledOnMobile; }
bool IsAntialisingEnabledOnMobile() const {
return isAntialisingEnabledOnMobile;
}

/**
* Set whether antialising is enabled on mobiles or not.
*/
void SetAntialisingEnabledOnMobile(bool enable) { isAntialisingEnabledOnMobile = enable; }
void SetAntialisingEnabledOnMobile(bool enable) {
isAntialisingEnabledOnMobile = enable;
}

/**
* \brief Return if the project should set 0 as Z-order for objects created
Expand Down Expand Up @@ -905,7 +921,8 @@ class GD_CORE_API Project : public ObjectsContainer {
/**
* \brief Return the events based object with a given type.
*/
const gd::EventsBasedObject& GetEventsBasedObject(const gd::String& type) const;
const gd::EventsBasedObject& GetEventsBasedObject(
const gd::String& type) const;

/**
* \brief Check if events based behavior with a given type exists.
Expand All @@ -920,7 +937,8 @@ class GD_CORE_API Project : public ObjectsContainer {
/**
* \brief Return the events based behavior with a given type.
*/
const gd::EventsBasedBehavior& GetEventsBasedBehavior(const gd::String& type) const;
const gd::EventsBasedBehavior& GetEventsBasedBehavior(
const gd::String& type) const;

///@}

Expand Down Expand Up @@ -969,29 +987,15 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
///@{

/**
* Check if unicode names are allowed in identifier names.
* \see IsNameSafe
* \see GetSafeName
*/
static bool IsUsageOfUnicodeIdentifierNamesAllowed() { return allowUsageOfUnicodeIdentifierNames; };

/**
* Set if unicode names are allowed in identifier names.
* \see IsNameSafe
* \see GetSafeName
*/
static void AllowUsageOfUnicodeIdentifierNames(bool enable);

/**
* Return true if \a name is valid (can be used safely for an object,
* behavior, events function name, etc...).
*/
static bool IsNameSafe(const gd::String& name);

/**
* Return a name, based on the one passed in parameter, that can be safely used
* for an object, behavior, events function name, etc...
* Return a name, based on the one passed in parameter, that can be safely
* used for an object, behavior, events function name, etc...
*/
static gd::String GetSafeName(const gd::String& name);
///@}
Expand Down Expand Up @@ -1068,8 +1072,8 @@ class GD_CORE_API Project : public ObjectsContainer {
bool adaptGameResolutionAtRuntime; ///< Should the game resolution be adapted
///< to the window size at runtime
gd::String
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
///< "adaptWidth", "adaptHeight" or empty
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
///< "adaptWidth", "adaptHeight" or empty
gd::String antialiasingMode;
bool isAntialisingEnabledOnMobile;
gd::String projectUuid; ///< UUID useful to identify the game in online
Expand All @@ -1095,19 +1099,18 @@ class GD_CORE_API Project : public ObjectsContainer {
externalSourceFiles; ///< List of external source files used.
gd::String author; ///< Game author name, for publishing purpose.
std::vector<gd::String>
authorIds; ///< Game author ids, from GDevelop users DB.
authorIds; ///< Game author ids, from GDevelop users DB.
std::vector<gd::String>
authorUsernames; ///< Game author usernames, from GDevelop users DB.
std::vector<gd::String>
categories; ///< Game categories
bool isPlayableWithKeyboard; ///< The project is playable with a keyboard.
bool isPlayableWithGamepad; ///< The project is playable with a gamepad.
bool isPlayableWithMobile; ///< The project is playable on a mobile.
gd::String packageName; ///< Game package name
gd::String templateSlug; ///< The slug of the template from which the game is
///< created.
gd::String orientation; ///< Lock game orientation (on mobile devices).
///< "default", "landscape" or "portrait".
authorUsernames; ///< Game author usernames, from GDevelop users DB.
std::vector<gd::String> categories; ///< Game categories
bool isPlayableWithKeyboard; ///< The project is playable with a keyboard.
bool isPlayableWithGamepad; ///< The project is playable with a gamepad.
bool isPlayableWithMobile; ///< The project is playable on a mobile.
gd::String packageName; ///< Game package name
gd::String templateSlug; ///< The slug of the template from which the game is
///< created.
gd::String orientation; ///< Lock game orientation (on mobile devices).
///< "default", "landscape" or "portrait".
bool
folderProject; ///< True if folder project, false if single file project.
gd::String
Expand All @@ -1128,8 +1131,6 @@ class GD_CORE_API Project : public ObjectsContainer {
///< time the project was saved.
mutable unsigned int gdBuildVersion; ///< The GD build version used the last
///< time the project was saved.

static bool allowUsageOfUnicodeIdentifierNames;
};

} // namespace gd
Expand Down
2 changes: 0 additions & 2 deletions GDevelop.js/Bindings/Bindings.idl
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,6 @@ interface Project {
void SerializeTo([Ref] SerializerElement element);
void UnserializeFrom([Const, Ref] SerializerElement element);

boolean STATIC_IsUsageOfUnicodeIdentifierNamesAllowed();
void STATIC_AllowUsageOfUnicodeIdentifierNames(boolean enable);
boolean STATIC_IsNameSafe([Const] DOMString name);
[Const, Value] DOMString STATIC_GetSafeName([Const] DOMString name);

Expand Down
Loading

0 comments on commit 740485b

Please sign in to comment.