diff options
Diffstat (limited to 'core/string')
-rw-r--r-- | core/string/optimized_translation.cpp | 10 | ||||
-rw-r--r-- | core/string/print_string.cpp | 2 | ||||
-rw-r--r-- | core/string/print_string.h | 12 | ||||
-rw-r--r-- | core/string/string_name.cpp | 17 | ||||
-rw-r--r-- | core/string/string_name.h | 15 | ||||
-rw-r--r-- | core/string/translation.cpp | 44 | ||||
-rw-r--r-- | core/string/translation.h | 20 | ||||
-rw-r--r-- | core/string/translation_po.cpp | 43 | ||||
-rw-r--r-- | core/string/ustring.cpp | 196 | ||||
-rw-r--r-- | core/string/ustring.h | 20 |
10 files changed, 241 insertions, 138 deletions
diff --git a/core/string/optimized_translation.cpp b/core/string/optimized_translation.cpp index 07b58f2418..07302cc8c3 100644 --- a/core/string/optimized_translation.cpp +++ b/core/string/optimized_translation.cpp @@ -37,9 +37,9 @@ extern "C" { } struct CompressedString { - int orig_len; + int orig_len = 0; CharString compressed; - int offset; + int offset = 0; }; void OptimizedTranslation::generate(const Ref<Translation> &p_from) { @@ -53,7 +53,7 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) { int size = Math::larger_prime(keys.size()); Vector<Vector<Pair<int, CharString>>> buckets; - Vector<Map<uint32_t, int>> table; + Vector<HashMap<uint32_t, int>> table; Vector<uint32_t> hfunc_table; Vector<CompressedString> compressed; @@ -108,7 +108,7 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) { for (int i = 0; i < size; i++) { const Vector<Pair<int, CharString>> &b = buckets[i]; - Map<uint32_t, int> &t = table.write[i]; + HashMap<uint32_t, int> &t = table.write[i]; if (b.size() == 0) { continue; @@ -147,7 +147,7 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) { int btindex = 0; for (int i = 0; i < size; i++) { - const Map<uint32_t, int> &t = table[i]; + const HashMap<uint32_t, int> &t = table[i]; if (t.size() == 0) { htw[i] = 0xFFFFFFFF; //nothing continue; diff --git a/core/string/print_string.cpp b/core/string/print_string.cpp index 97e119bcf6..919c9e08e3 100644 --- a/core/string/print_string.cpp +++ b/core/string/print_string.cpp @@ -45,7 +45,7 @@ void add_print_handler(PrintHandlerList *p_handler) { _global_unlock(); } -void remove_print_handler(PrintHandlerList *p_handler) { +void remove_print_handler(const PrintHandlerList *p_handler) { _global_lock(); PrintHandlerList *prev = nullptr; diff --git a/core/string/print_string.h b/core/string/print_string.h index 669d2ea316..f7d0f25030 100644 --- a/core/string/print_string.h +++ b/core/string/print_string.h @@ -54,13 +54,21 @@ String stringify_variants(Variant p_var, Args... p_args) { } void add_print_handler(PrintHandlerList *p_handler); -void remove_print_handler(PrintHandlerList *p_handler); +void remove_print_handler(const PrintHandlerList *p_handler); extern bool _print_line_enabled; extern bool _print_error_enabled; extern void __print_line(String p_string); extern void print_error(String p_string); extern void print_verbose(String p_string); -#define print_line(...) __print_line(stringify_variants(__VA_ARGS__)) + +inline void print_line(Variant v) { + __print_line(stringify_variants(v)); +} + +template <typename... Args> +void print_line(Variant p_var, Args... p_args) { + __print_line(stringify_variants(p_var, p_args...)); +} #endif // PRINT_STRING_H diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index 11674629fc..9c4fc4e1b7 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -73,11 +73,23 @@ void StringName::cleanup() { d = d->next; } } - print_line("\nStringName Reference Ranking:\n"); + + print_line("\nStringName reference ranking (from most to least referenced):\n"); + data.sort_custom<DebugSortReferences>(); - for (int i = 0; i < MIN(100, data.size()); i++) { + int unreferenced_stringnames = 0; + int rarely_referenced_stringnames = 0; + for (int i = 0; i < data.size(); i++) { print_line(itos(i + 1) + ": " + data[i]->get_name() + " - " + itos(data[i]->debug_references)); + if (data[i]->debug_references == 0) { + unreferenced_stringnames += 1; + } else if (data[i]->debug_references < 5) { + rarely_referenced_stringnames += 1; + } } + + print_line(vformat("\nOut of %d StringNames, %d StringNames were never referenced during this run (0 times) (%.2f%%).", data.size(), unreferenced_stringnames, unreferenced_stringnames / float(data.size()) * 100)); + print_line(vformat("Out of %d StringNames, %d StringNames were rarely referenced during this run (1-4 times) (%.2f%%).", data.size(), rarely_referenced_stringnames, rarely_referenced_stringnames / float(data.size()) * 100)); } #endif int lost_strings = 0; @@ -235,6 +247,7 @@ StringName::StringName(const char *p_name, bool p_static) { _data->cname = nullptr; _data->next = _table[idx]; _data->prev = nullptr; + #ifdef DEBUG_ENABLED if (unlikely(debug_stringname)) { // Keep in memory, force static. diff --git a/core/string/string_name.h b/core/string/string_name.h index 6f08d32981..ff4c41af94 100644 --- a/core/string/string_name.h +++ b/core/string/string_name.h @@ -35,6 +35,8 @@ #include "core/string/ustring.h" #include "core/templates/safe_refcount.h" +#define UNIQUE_NODE_PREFIX "%" + class Main; struct StaticCString { @@ -70,7 +72,7 @@ class StringName { _Data *_data = nullptr; union _HashUnion { - _Data *ptr; + _Data *ptr = nullptr; uint32_t hash; }; @@ -100,6 +102,17 @@ public: bool operator==(const String &p_name) const; bool operator==(const char *p_name) const; bool operator!=(const String &p_name) const; + + _FORCE_INLINE_ bool is_node_unique_name() const { + if (!_data) { + return false; + } + if (_data->cname != nullptr) { + return (char32_t)_data->cname[0] == (char32_t)UNIQUE_NODE_PREFIX[0]; + } else { + return (char32_t)_data->name[0] == (char32_t)UNIQUE_NODE_PREFIX[0]; + } + } _FORCE_INLINE_ bool operator<(const StringName &p_name) const { return _data < p_name._data; } diff --git a/core/string/translation.cpp b/core/string/translation.cpp index c41828de05..cba2f09022 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -95,12 +95,12 @@ StringName Translation::get_message(const StringName &p_src_text, const StringNa WARN_PRINT("Translation class doesn't handle context. Using context in get_message() on a Translation instance is probably a mistake. \nUse a derived Translation class that handles context, such as TranslationPO class"); } - const Map<StringName, StringName>::Element *E = translation_map.find(p_src_text); + HashMap<StringName, StringName>::ConstIterator E = translation_map.find(p_src_text); if (!E) { return StringName(); } - return E->get(); + return E->value; } StringName Translation::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const { @@ -215,12 +215,12 @@ static _character_accent_pair _character_to_accented[] = { Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info; -Map<String, String> TranslationServer::language_map; -Map<String, String> TranslationServer::script_map; -Map<String, String> TranslationServer::locale_rename_map; -Map<String, String> TranslationServer::country_name_map; -Map<String, String> TranslationServer::variant_map; -Map<String, String> TranslationServer::country_rename_map; +HashMap<String, String> TranslationServer::language_map; +HashMap<String, String> TranslationServer::script_map; +HashMap<String, String> TranslationServer::locale_rename_map; +HashMap<String, String> TranslationServer::country_name_map; +HashMap<String, String> TranslationServer::variant_map; +HashMap<String, String> TranslationServer::country_rename_map; void TranslationServer::init_locale_info() { // Init locale info. @@ -452,8 +452,8 @@ String TranslationServer::get_locale_name(const String &p_locale) const { Vector<String> TranslationServer::get_all_languages() const { Vector<String> languages; - for (const Map<String, String>::Element *E = language_map.front(); E; E = E->next()) { - languages.push_back(E->key()); + for (const KeyValue<String, String> &E : language_map) { + languages.push_back(E.key); } return languages; @@ -466,8 +466,8 @@ String TranslationServer::get_language_name(const String &p_language) const { Vector<String> TranslationServer::get_all_scripts() const { Vector<String> scripts; - for (const Map<String, String>::Element *E = script_map.front(); E; E = E->next()) { - scripts.push_back(E->key()); + for (const KeyValue<String, String> &E : script_map) { + scripts.push_back(E.key); } return scripts; @@ -480,8 +480,8 @@ String TranslationServer::get_script_name(const String &p_script) const { Vector<String> TranslationServer::get_all_countries() const { Vector<String> countries; - for (const Map<String, String>::Element *E = country_name_map.front(); E; E = E->next()) { - countries.push_back(E->key()); + for (const KeyValue<String, String> &E : country_name_map) { + countries.push_back(E.key); } return countries; @@ -507,8 +507,8 @@ String TranslationServer::get_locale() const { Array TranslationServer::get_loaded_locales() const { Array locales; - for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) { - const Ref<Translation> &t = E->get(); + for (const Ref<Translation> &E : translations) { + const Ref<Translation> &t = E; ERR_FAIL_COND_V(t.is_null(), Array()); String l = t->get_locale(); @@ -530,8 +530,8 @@ Ref<Translation> TranslationServer::get_translation_object(const String &p_local Ref<Translation> res; int best_score = 0; - for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) { - const Ref<Translation> &t = E->get(); + for (const Ref<Translation> &E : translations) { + const Ref<Translation> &t = E; ERR_FAIL_COND_V(t.is_null(), nullptr); String l = t->get_locale(); @@ -599,8 +599,8 @@ StringName TranslationServer::_get_message_from_translations(const StringName &p StringName res; int best_score = 0; - for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) { - const Ref<Translation> &t = E->get(); + for (const Ref<Translation> &E : translations) { + const Ref<Translation> &t = E; ERR_FAIL_COND_V(t.is_null(), p_message); String l = t->get_locale(); @@ -891,7 +891,7 @@ String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const return res; } -String TranslationServer::add_padding(String &p_message, int p_length) const { +String TranslationServer::add_padding(const String &p_message, int p_length) const { String res; String prefix = pseudolocalization_prefix; String suffix; @@ -921,7 +921,7 @@ const char32_t *TranslationServer::get_accented_version(char32_t p_character) co } bool TranslationServer::is_placeholder(String &p_message, int p_index) const { - return p_message[p_index] == '%' && p_index < p_message.size() - 1 && + return p_index < p_message.size() - 1 && p_message[p_index] == '%' && (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' || p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f'); } diff --git a/core/string/translation.h b/core/string/translation.h index 947ca4c6d8..20c6ebd5a5 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -41,7 +41,7 @@ class Translation : public Resource { RES_BASE_EXTENSION("translation"); String locale = "en"; - Map<StringName, StringName> translation_map; + HashMap<StringName, StringName> translation_map; virtual Vector<String> _get_message_list() const; virtual Dictionary _get_messages() const; @@ -74,7 +74,7 @@ class TranslationServer : public Object { String locale = "en"; String fallback; - Set<Ref<Translation>> translations; + HashSet<Ref<Translation>> translations; Ref<Translation> tool_translation; Ref<Translation> doc_translation; @@ -96,7 +96,7 @@ class TranslationServer : public Object { String double_vowels(String &p_message) const; String replace_with_accented_string(String &p_message) const; String wrap_with_fakebidi_characters(String &p_message) const; - String add_padding(String &p_message, int p_length) const; + String add_padding(const String &p_message, int p_length) const; const char32_t *get_accented_version(char32_t p_character) const; bool is_placeholder(String &p_message, int p_index) const; @@ -111,16 +111,16 @@ class TranslationServer : public Object { String name; String script; String default_country; - Set<String> supported_countries; + HashSet<String> supported_countries; }; static Vector<LocaleScriptInfo> locale_script_info; - static Map<String, String> language_map; - static Map<String, String> script_map; - static Map<String, String> locale_rename_map; - static Map<String, String> country_name_map; - static Map<String, String> country_rename_map; - static Map<String, String> variant_map; + static HashMap<String, String> language_map; + static HashMap<String, String> script_map; + static HashMap<String, String> locale_rename_map; + static HashMap<String, String> country_name_map; + static HashMap<String, String> country_rename_map; + static HashMap<String, String> variant_map; void init_locale_info(); diff --git a/core/string/translation_po.cpp b/core/string/translation_po.cpp index 1c991ee12d..fa656b634d 100644 --- a/core/string/translation_po.cpp +++ b/core/string/translation_po.cpp @@ -35,7 +35,7 @@ #ifdef DEBUG_TRANSLATION_PO void TranslationPO::print_translation_map() { Error err; - FileAccess *file = FileAccess::open("translation_map_print_test.txt", FileAccess::WRITE, &err); + Ref<FileAccess> file = FileAccess::open("translation_map_print_test.txt", FileAccess::WRITE, &err); if (err != OK) { ERR_PRINT("Failed to open translation_map_print_test.txt"); return; @@ -62,7 +62,6 @@ void TranslationPO::print_translation_map() { file->store_line(""); } } - file->close(); } #endif @@ -71,21 +70,14 @@ Dictionary TranslationPO::_get_messages() const { Dictionary d; - List<StringName> context_l; - translation_map.get_key_list(&context_l); - for (const StringName &ctx : context_l) { - const HashMap<StringName, Vector<StringName>> &id_str_map = translation_map[ctx]; - + for (const KeyValue<StringName, HashMap<StringName, Vector<StringName>>> &E : translation_map) { Dictionary d2; - List<StringName> id_l; - id_str_map.get_key_list(&id_l); - // Save list of id and strs associated with a context in a temporary dictionary. - for (List<StringName>::Element *E2 = id_l.front(); E2; E2 = E2->next()) { - StringName id = E2->get(); - d2[id] = id_str_map[id]; + + for (const KeyValue<StringName, Vector<StringName>> &E2 : E.value) { + d2[E2.key] = E2.value; } - d[ctx] = d2; + d[E.key] = d2; } return d; @@ -275,31 +267,24 @@ void TranslationPO::get_message_list(List<StringName> *r_messages) const { // OptimizedTranslation uses this function to get the list of msgid. // Return all the keys of translation_map under "" context. - List<StringName> context_l; - translation_map.get_key_list(&context_l); - - for (const StringName &E : context_l) { - if (String(E) != "") { + for (const KeyValue<StringName, HashMap<StringName, Vector<StringName>>> &E : translation_map) { + if (E.key != StringName()) { continue; } - List<StringName> msgid_l; - translation_map[E].get_key_list(&msgid_l); - - for (List<StringName>::Element *E2 = msgid_l.front(); E2; E2 = E2->next()) { - r_messages->push_back(E2->get()); + for (const KeyValue<StringName, Vector<StringName>> &E2 : E.value) { + r_messages->push_back(E2.key); } } } int TranslationPO::get_message_count() const { - List<StringName> context_l; - translation_map.get_key_list(&context_l); - int count = 0; - for (const StringName &E : context_l) { - count += translation_map[E].size(); + + for (const KeyValue<StringName, HashMap<StringName, Vector<StringName>>> &E : translation_map) { + count += E.value.size(); } + return count; } diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 759c121f29..df1aae5370 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -35,6 +35,7 @@ #include "core/math/math_funcs.h" #include "core/os/memory.h" #include "core/string/print_string.h" +#include "core/string/string_name.h" #include "core/string/translation.h" #include "core/string/ucaps.h" #include "core/variant/variant.h" @@ -463,6 +464,12 @@ String String::operator+(const String &p_str) const { return res; } +String String::operator+(char32_t p_char) const { + String res = *this; + res += p_char; + return res; +} + String operator+(const char *p_chr, const String &p_str) { String tmp = p_chr; tmp += p_str; @@ -2032,7 +2039,7 @@ int64_t String::hex_to_int() const { } // Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error bool overflow = ((hex > INT64_MAX / 16) && (sign == 1 || (sign == -1 && hex != (INT64_MAX >> 4) + 1))) || (sign == -1 && hex == (INT64_MAX >> 4) + 1 && c > '0'); - ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); hex *= 16; hex += n; s++; @@ -2071,7 +2078,7 @@ int64_t String::bin_to_int() const { } // Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error bool overflow = ((binary > INT64_MAX / 2) && (sign == 1 || (sign == -1 && binary != (INT64_MAX >> 1) + 1))) || (sign == -1 && binary == (INT64_MAX >> 1) + 1 && c > '0'); - ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); binary *= 2; binary += n; s++; @@ -2094,7 +2101,7 @@ int64_t String::to_int() const { char32_t c = operator[](i); if (is_digit(c)) { bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); - ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); integer *= 10; integer += c - '0'; @@ -2123,7 +2130,7 @@ int64_t String::to_int(const char *p_str, int p_len) { char c = p_str[i]; if (is_digit(c)) { bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); - ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); integer *= 10; integer += c - '0'; @@ -2154,7 +2161,7 @@ int64_t String::to_int(const wchar_t *p_str, int p_len) { wchar_t c = p_str[i]; if (is_digit(c)) { bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); - ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); integer *= 10; integer += c - '0'; @@ -2476,7 +2483,7 @@ int64_t String::to_int(const char32_t *p_str, int p_len, bool p_clamp) { return INT64_MIN; } } else { - ERR_FAIL_V_MSG(sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + number + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + ERR_FAIL_V_MSG(sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + number + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); } } integer *= 10; @@ -3352,36 +3359,36 @@ String String::repeat(int p_count) const { return new_string; } -String String::left(int p_pos) const { - if (p_pos < 0) { - p_pos = length() + p_pos; +String String::left(int p_len) const { + if (p_len < 0) { + p_len = length() + p_len; } - if (p_pos <= 0) { + if (p_len <= 0) { return ""; } - if (p_pos >= length()) { + if (p_len >= length()) { return *this; } - return substr(0, p_pos); + return substr(0, p_len); } -String String::right(int p_pos) const { - if (p_pos < 0) { - p_pos = length() + p_pos; +String String::right(int p_len) const { + if (p_len < 0) { + p_len = length() + p_len; } - if (p_pos <= 0) { + if (p_len <= 0) { return ""; } - if (p_pos >= length()) { + if (p_len >= length()) { return *this; } - return substr(length() - p_pos); + return substr(length() - p_len); } char32_t String::unicode_at(int p_idx) const { @@ -3648,6 +3655,31 @@ bool String::is_absolute_path() const { } } +static _FORCE_INLINE_ bool _is_valid_identifier_bit(int p_index, char32_t p_char) { + if (p_index == 0 && is_digit(p_char)) { + return false; // No start with number plz. + } + return is_ascii_identifier_char(p_char); +} + +String String::validate_identifier() const { + if (is_empty()) { + return "_"; // Empty string is not a valid identifier; + } + + String result = *this; + int len = result.length(); + char32_t *buffer = result.ptrw(); + + for (int i = 0; i < len; i++) { + if (!_is_valid_identifier_bit(i, buffer[i])) { + buffer[i] = '_'; + } + } + + return result; +} + bool String::is_valid_identifier() const { int len = length(); @@ -3658,15 +3690,7 @@ bool String::is_valid_identifier() const { const char32_t *str = &operator[](0); for (int i = 0; i < len; i++) { - if (i == 0) { - if (is_digit(str[0])) { - return false; // no start with number plz - } - } - - bool valid_char = is_ascii_identifier_char(str[i]); - - if (!valid_char) { + if (!_is_valid_identifier_bit(i, str[i])) { return false; } } @@ -3688,18 +3712,15 @@ String String::uri_encode() const { const CharString temp = utf8(); String res; for (int i = 0; i < temp.length(); ++i) { - char ord = temp[i]; + uint8_t ord = temp[i]; if (ord == '.' || ord == '-' || ord == '~' || is_ascii_identifier_char(ord)) { res += ord; } else { - char h_Val[3]; -#if defined(__GNUC__) || defined(_MSC_VER) - snprintf(h_Val, 3, "%02hhX", ord); -#else - sprintf(h_Val, "%02hhX", ord); -#endif - res += "%"; - res += h_Val; + char p[4] = { '%', 0, 0, 0 }; + static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + p[1] = hex[ord >> 4]; + p[2] = hex[ord & 0xF]; + res += p; } } return res; @@ -4117,15 +4138,11 @@ String String::path_to(const String &p_path) const { dst += "/"; } - String base; - if (src.begins_with("res://") && dst.begins_with("res://")) { - base = "res:/"; src = src.replace("res://", "/"); dst = dst.replace("res://", "/"); } else if (src.begins_with("user://") && dst.begins_with("user://")) { - base = "user:/"; src = src.replace("user://", "/"); dst = dst.replace("user://", "/"); @@ -4140,7 +4157,6 @@ String String::path_to(const String &p_path) const { return p_path; //impossible to do this } - base = src_begin; src = src.substr(src_begin.length(), src.length()); dst = dst.substr(dst_begin.length(), dst.length()); } @@ -4351,7 +4367,7 @@ String String::property_name_encode() const { } // Changes made to the set of invalid characters must also be reflected in the String documentation. -const String String::invalid_node_name_characters = ". : @ / \""; +const String String::invalid_node_name_characters = ". : @ / \" " UNIQUE_NODE_PREFIX; String String::validate_node_name() const { Vector<String> chars = String::invalid_node_name_characters.split(" "); @@ -4425,7 +4441,7 @@ String String::sprintf(const Array &values, bool *error) const { int min_chars = 0; int min_decimals = 0; bool in_decimals = false; - bool pad_with_zeroes = false; + bool pad_with_zeros = false; bool left_justified = false; bool show_sign = false; @@ -4478,7 +4494,7 @@ String String::sprintf(const Array &values, bool *error) const { // Padding. int pad_chars_count = (value < 0 || show_sign) ? min_chars - 1 : min_chars; - String pad_char = pad_with_zeroes ? String("0") : String(" "); + String pad_char = pad_with_zeros ? String("0") : String(" "); if (left_justified) { str = str.rpad(pad_chars_count, pad_char); } else { @@ -4486,10 +4502,13 @@ String String::sprintf(const Array &values, bool *error) const { } // Sign. - if (show_sign && value >= 0) { - str = str.insert(pad_with_zeroes ? 0 : str.length() - number_len, "+"); - } else if (value < 0) { - str = str.insert(pad_with_zeroes ? 0 : str.length() - number_len, "-"); + if (show_sign || value < 0) { + String sign_char = value < 0 ? "-" : "+"; + if (left_justified) { + str = str.insert(0, sign_char); + } else { + str = str.insert(pad_with_zeros ? 0 : str.length() - number_len, sign_char); + } } formatted += str; @@ -4518,13 +4537,9 @@ String String::sprintf(const Array &values, bool *error) const { // Padding. Leave room for sign later if required. int pad_chars_count = (is_negative || show_sign) ? min_chars - 1 : min_chars; - String pad_char = pad_with_zeroes ? String("0") : String(" "); + String pad_char = pad_with_zeros ? String("0") : String(" "); if (left_justified) { - if (pad_with_zeroes) { - return "left justification cannot be used with zeros as the padding"; - } else { - str = str.rpad(pad_chars_count, pad_char); - } + str = str.rpad(pad_chars_count, pad_char); } else { str = str.lpad(pad_chars_count, pad_char); } @@ -4535,7 +4550,7 @@ String String::sprintf(const Array &values, bool *error) const { if (left_justified) { str = str.insert(0, sign_char); } else { - str = str.insert(pad_with_zeroes ? 0 : str.length() - initial_len, sign_char); + str = str.insert(pad_with_zeros ? 0 : str.length() - initial_len, sign_char); } } @@ -4624,7 +4639,11 @@ String String::sprintf(const Array &values, bool *error) const { min_decimals += n; } else { if (c == '0' && min_chars == 0) { - pad_with_zeroes = true; + if (left_justified) { + WARN_PRINT("'0' flag ignored with '-' flag in string format"); + } else { + pad_with_zeros = true; + } } else { min_chars *= 10; min_chars += n; @@ -4673,7 +4692,7 @@ String String::sprintf(const Array &values, bool *error) const { // Back to defaults: min_chars = 0; min_decimals = 6; - pad_with_zeroes = false; + pad_with_zeros = false; left_justified = false; show_sign = false; in_decimals = false; @@ -4774,6 +4793,17 @@ Vector<uint8_t> String::to_utf32_buffer() const { } #ifdef TOOLS_ENABLED +/** + * "Tools TRanslate". Performs string replacement for internationalization + * within the editor. A translation context can optionally be specified to + * disambiguate between identical source strings in translations. When + * placeholders are desired, use `vformat(TTR("Example: %s"), some_string)`. + * If a string mentions a quantity (and may therefore need a dynamic plural form), + * use `TTRN()` instead of `TTR()`. + * + * NOTE: Only use `TTR()` in editor-only code (typically within the `editor/` folder). + * For translations that can be supplied by exported projects, use `RTR()` instead. + */ String TTR(const String &p_text, const String &p_context) { if (TranslationServer::get_singleton()) { return TranslationServer::get_singleton()->tool_translate(p_text, p_context); @@ -4782,6 +4812,18 @@ String TTR(const String &p_text, const String &p_context) { return p_text; } +/** + * "Tools TRanslate for N items". Performs string replacement for + * internationalization within the editor. A translation context can optionally + * be specified to disambiguate between identical source strings in + * translations. Use `TTR()` if the string doesn't need dynamic plural form. + * When placeholders are desired, use + * `vformat(TTRN("%d item", "%d items", some_integer), some_integer)`. + * The placeholder must be present in both strings to avoid run-time warnings in `vformat()`. + * + * NOTE: Only use `TTRN()` in editor-only code (typically within the `editor/` folder). + * For translations that can be supplied by exported projects, use `RTRN()` instead. + */ String TTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context) { if (TranslationServer::get_singleton()) { return TranslationServer::get_singleton()->tool_translate_plural(p_text, p_text_plural, p_n, p_context); @@ -4794,9 +4836,10 @@ String TTRN(const String &p_text, const String &p_text_plural, int p_n, const St return p_text_plural; } -/* DTR and DTRN are used for the documentation, handling descriptions extracted - * from the XML. - * They also replace `$DOCS_URL` with the actual URL to the documentation's branch, +/** + * "Docs TRanslate". Used for the editor class reference documentation, + * handling descriptions extracted from the XML. + * It also replaces `$DOCS_URL` with the actual URL to the documentation's branch, * to allow dehardcoding it in the XML and doing proper substitutions everywhere. */ String DTR(const String &p_text, const String &p_context) { @@ -4810,6 +4853,12 @@ String DTR(const String &p_text, const String &p_context) { return text.replace("$DOCS_URL", VERSION_DOCS_URL); } +/** + * "Docs TRanslate for N items". Used for the editor class reference documentation + * (with support for plurals), handling descriptions extracted from the XML. + * It also replaces `$DOCS_URL` with the actual URL to the documentation's branch, + * to allow dehardcoding it in the XML and doing proper substitutions everywhere. + */ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context) { const String text = p_text.dedent().strip_edges(); const String text_plural = p_text_plural.dedent().strip_edges(); @@ -4826,6 +4875,19 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St } #endif +/** + * "Run-time TRanslate". Performs string replacement for internationalization + * within a running project. The translation string must be supplied by the + * project, as Godot does not provide built-in translations for `RTR()` strings + * to keep binary size low. A translation context can optionally be specified to + * disambiguate between identical source strings in translations. When + * placeholders are desired, use `vformat(RTR("Example: %s"), some_string)`. + * If a string mentions a quantity (and may therefore need a dynamic plural form), + * use `RTRN()` instead of `RTR()`. + * + * NOTE: Do not use `RTR()` in editor-only code (typically within the `editor/` + * folder). For editor translations, use `TTR()` instead. + */ String RTR(const String &p_text, const String &p_context) { if (TranslationServer::get_singleton()) { String rtr = TranslationServer::get_singleton()->tool_translate(p_text, p_context); @@ -4839,6 +4901,20 @@ String RTR(const String &p_text, const String &p_context) { return p_text; } +/** + * "Run-time TRanslate for N items". Performs string replacement for + * internationalization within a running project. The translation string must be + * supplied by the project, as Godot does not provide built-in translations for + * `RTRN()` strings to keep binary size low. A translation context can + * optionally be specified to disambiguate between identical source strings in + * translations. Use `RTR()` if the string doesn't need dynamic plural form. + * When placeholders are desired, use + * `vformat(RTRN("%d item", "%d items", some_integer), some_integer)`. + * The placeholder must be present in both strings to avoid run-time warnings in `vformat()`. + * + * NOTE: Do not use `RTRN()` in editor-only code (typically within the `editor/` + * folder). For editor translations, use `TTRN()` instead. + */ String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context) { if (TranslationServer::get_singleton()) { String rtr = TranslationServer::get_singleton()->tool_translate_plural(p_text, p_text_plural, p_n, p_context); diff --git a/core/string/ustring.h b/core/string/ustring.h index 1d302b65a7..11d0974381 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -225,6 +225,7 @@ public: bool operator==(const String &p_str) const; bool operator!=(const String &p_str) const; String operator+(const String &p_str) const; + String operator+(char32_t p_char) const; String &operator+=(const String &); String &operator+=(char32_t p_char); @@ -355,8 +356,8 @@ public: int count(const String &p_string, int p_from = 0, int p_to = 0) const; int countn(const String &p_string, int p_from = 0, int p_to = 0) const; - String left(int p_pos) const; - String right(int p_pos) const; + String left(int p_len) const; + String right(int p_len) const; String indent(const String &p_prefix) const; String dedent() const; String strip_edges(bool left = true, bool right = true) const; @@ -426,6 +427,7 @@ public: // node functions static const String invalid_node_name_characters; String validate_node_name() const; + String validate_identifier() const; bool is_valid_identifier() const; bool is_valid_int() const; @@ -522,14 +524,20 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St #define TTRGET(m_value) TTR(m_value) #else -#define TTR(m_value) String() -#define TTRN(m_value) String() -#define DTR(m_value) String() -#define DTRN(m_value) String() #define TTRC(m_value) (m_value) #define TTRGET(m_value) (m_value) #endif +// Use this to mark property names for editor translation. +// Often for dynamic properties defined in _get_property_list(). +// Property names defined directly inside EDITOR_DEF, GLOBAL_DEF, and ADD_PROPERTY macros don't need this. +#define PNAME(m_value) (m_value) + +// Similar to PNAME, but to mark groups, i.e. properties with PROPERTY_USAGE_GROUP. +// Groups defined directly inside ADD_GROUP macros don't need this. +// The arguments are the same as ADD_GROUP. m_prefix is only used for extraction. +#define GNAME(m_value, m_prefix) (m_value) + // Runtime translate for the public node API. String RTR(const String &p_text, const String &p_context = ""); String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = ""); |