feat: updated engine
This commit is contained in:
parent
cbe99774ff
commit
f4cf6b3999
6607 changed files with 910135 additions and 430025 deletions
|
|
@ -3,6 +3,4 @@ from misc.utility.scons_hints import *
|
|||
|
||||
Import("env")
|
||||
|
||||
env_string = env.Clone()
|
||||
|
||||
env_string.add_source_files(env.core_sources, "*.cpp")
|
||||
env.add_source_files(env.core_sources, "*.cpp")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**************************************************************************/
|
||||
/* char_range.inc */
|
||||
/* char_range.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
|
|
@ -30,19 +30,13 @@
|
|||
|
||||
// This file was generated using the `misc/scripts/char_range_fetch.py` script.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/typedefs.h"
|
||||
#include "core/string/char_utils.h"
|
||||
|
||||
// Unicode Derived Core Properties
|
||||
// Source: https://www.unicode.org/Public/16.0.0/ucd/DerivedCoreProperties.txt
|
||||
// Source: https://www.unicode.org/Public/17.0.0/ucd/DerivedCoreProperties.txt
|
||||
|
||||
struct CharRange {
|
||||
char32_t start;
|
||||
char32_t end;
|
||||
};
|
||||
|
||||
constexpr inline CharRange xid_start[] = {
|
||||
const int xid_start_size = 692;
|
||||
const CharRange xid_start[xid_start_size] = {
|
||||
{ 0x41, 0x5a },
|
||||
{ 0x5f, 0x5f },
|
||||
{ 0x61, 0x7a },
|
||||
|
|
@ -94,7 +88,7 @@ constexpr inline CharRange xid_start[] = {
|
|||
{ 0x840, 0x858 },
|
||||
{ 0x860, 0x86a },
|
||||
{ 0x870, 0x887 },
|
||||
{ 0x889, 0x88e },
|
||||
{ 0x889, 0x88f },
|
||||
{ 0x8a0, 0x8c9 },
|
||||
{ 0x904, 0x939 },
|
||||
{ 0x93d, 0x93d },
|
||||
|
|
@ -160,7 +154,7 @@ constexpr inline CharRange xid_start[] = {
|
|||
{ 0xc2a, 0xc39 },
|
||||
{ 0xc3d, 0xc3d },
|
||||
{ 0xc58, 0xc5a },
|
||||
{ 0xc5d, 0xc5d },
|
||||
{ 0xc5c, 0xc5d },
|
||||
{ 0xc60, 0xc61 },
|
||||
{ 0xc80, 0xc80 },
|
||||
{ 0xc85, 0xc8c },
|
||||
|
|
@ -169,7 +163,7 @@ constexpr inline CharRange xid_start[] = {
|
|||
{ 0xcaa, 0xcb3 },
|
||||
{ 0xcb5, 0xcb9 },
|
||||
{ 0xcbd, 0xcbd },
|
||||
{ 0xcdd, 0xcde },
|
||||
{ 0xcdc, 0xcde },
|
||||
{ 0xce0, 0xce1 },
|
||||
{ 0xcf1, 0xcf2 },
|
||||
{ 0xd04, 0xd0c },
|
||||
|
|
@ -351,11 +345,8 @@ constexpr inline CharRange xid_start[] = {
|
|||
{ 0xa6a0, 0xa6ef },
|
||||
{ 0xa717, 0xa71f },
|
||||
{ 0xa722, 0xa788 },
|
||||
{ 0xa78b, 0xa7cd },
|
||||
{ 0xa7d0, 0xa7d1 },
|
||||
{ 0xa7d3, 0xa7d3 },
|
||||
{ 0xa7d5, 0xa7dc },
|
||||
{ 0xa7f2, 0xa801 },
|
||||
{ 0xa78b, 0xa7dc },
|
||||
{ 0xa7f1, 0xa801 },
|
||||
{ 0xa803, 0xa805 },
|
||||
{ 0xa807, 0xa80a },
|
||||
{ 0xa80c, 0xa822 },
|
||||
|
|
@ -478,6 +469,7 @@ constexpr inline CharRange xid_start[] = {
|
|||
{ 0x108f4, 0x108f5 },
|
||||
{ 0x10900, 0x10915 },
|
||||
{ 0x10920, 0x10939 },
|
||||
{ 0x10940, 0x10959 },
|
||||
{ 0x10980, 0x109b7 },
|
||||
{ 0x109be, 0x109bf },
|
||||
{ 0x10a00, 0x10a00 },
|
||||
|
|
@ -500,7 +492,7 @@ constexpr inline CharRange xid_start[] = {
|
|||
{ 0x10d6f, 0x10d85 },
|
||||
{ 0x10e80, 0x10ea9 },
|
||||
{ 0x10eb0, 0x10eb1 },
|
||||
{ 0x10ec2, 0x10ec4 },
|
||||
{ 0x10ec2, 0x10ec7 },
|
||||
{ 0x10f00, 0x10f1c },
|
||||
{ 0x10f27, 0x10f27 },
|
||||
{ 0x10f30, 0x10f45 },
|
||||
|
|
@ -593,6 +585,7 @@ constexpr inline CharRange xid_start[] = {
|
|||
{ 0x11d67, 0x11d68 },
|
||||
{ 0x11d6a, 0x11d89 },
|
||||
{ 0x11d98, 0x11d98 },
|
||||
{ 0x11db0, 0x11ddb },
|
||||
{ 0x11ee0, 0x11ef2 },
|
||||
{ 0x11f02, 0x11f02 },
|
||||
{ 0x11f04, 0x11f10 },
|
||||
|
|
@ -617,14 +610,17 @@ constexpr inline CharRange xid_start[] = {
|
|||
{ 0x16b7d, 0x16b8f },
|
||||
{ 0x16d40, 0x16d6c },
|
||||
{ 0x16e40, 0x16e7f },
|
||||
{ 0x16ea0, 0x16eb8 },
|
||||
{ 0x16ebb, 0x16ed3 },
|
||||
{ 0x16f00, 0x16f4a },
|
||||
{ 0x16f50, 0x16f50 },
|
||||
{ 0x16f93, 0x16f9f },
|
||||
{ 0x16fe0, 0x16fe1 },
|
||||
{ 0x16fe3, 0x16fe3 },
|
||||
{ 0x17000, 0x187f7 },
|
||||
{ 0x18800, 0x18cd5 },
|
||||
{ 0x18cff, 0x18d08 },
|
||||
{ 0x16ff2, 0x16ff6 },
|
||||
{ 0x17000, 0x18cd5 },
|
||||
{ 0x18cff, 0x18d1e },
|
||||
{ 0x18d80, 0x18df2 },
|
||||
{ 0x1aff0, 0x1aff3 },
|
||||
{ 0x1aff5, 0x1affb },
|
||||
{ 0x1affd, 0x1affe },
|
||||
|
|
@ -679,6 +675,12 @@ constexpr inline CharRange xid_start[] = {
|
|||
{ 0x1e4d0, 0x1e4eb },
|
||||
{ 0x1e5d0, 0x1e5ed },
|
||||
{ 0x1e5f0, 0x1e5f0 },
|
||||
{ 0x1e6c0, 0x1e6de },
|
||||
{ 0x1e6e0, 0x1e6e2 },
|
||||
{ 0x1e6e4, 0x1e6e5 },
|
||||
{ 0x1e6e7, 0x1e6ed },
|
||||
{ 0x1e6f0, 0x1e6f4 },
|
||||
{ 0x1e6fe, 0x1e6ff },
|
||||
{ 0x1e7e0, 0x1e7e6 },
|
||||
{ 0x1e7e8, 0x1e7eb },
|
||||
{ 0x1e7ed, 0x1e7ee },
|
||||
|
|
@ -720,17 +722,17 @@ constexpr inline CharRange xid_start[] = {
|
|||
{ 0x1eea5, 0x1eea9 },
|
||||
{ 0x1eeab, 0x1eebb },
|
||||
{ 0x20000, 0x2a6df },
|
||||
{ 0x2a700, 0x2b739 },
|
||||
{ 0x2b740, 0x2b81d },
|
||||
{ 0x2b820, 0x2cea1 },
|
||||
{ 0x2a700, 0x2b81d },
|
||||
{ 0x2b820, 0x2cead },
|
||||
{ 0x2ceb0, 0x2ebe0 },
|
||||
{ 0x2ebf0, 0x2ee5d },
|
||||
{ 0x2f800, 0x2fa1d },
|
||||
{ 0x30000, 0x3134a },
|
||||
{ 0x31350, 0x323af },
|
||||
{ 0x31350, 0x33479 },
|
||||
};
|
||||
|
||||
constexpr inline CharRange xid_continue[] = {
|
||||
const int xid_continue_size = 806;
|
||||
const CharRange xid_continue[xid_continue_size] = {
|
||||
{ 0x30, 0x39 },
|
||||
{ 0x41, 0x5a },
|
||||
{ 0x5f, 0x5f },
|
||||
|
|
@ -783,7 +785,7 @@ constexpr inline CharRange xid_continue[] = {
|
|||
{ 0x840, 0x85b },
|
||||
{ 0x860, 0x86a },
|
||||
{ 0x870, 0x887 },
|
||||
{ 0x889, 0x88e },
|
||||
{ 0x889, 0x88f },
|
||||
{ 0x897, 0x8e1 },
|
||||
{ 0x8e3, 0x963 },
|
||||
{ 0x966, 0x96f },
|
||||
|
|
@ -873,7 +875,7 @@ constexpr inline CharRange xid_continue[] = {
|
|||
{ 0xc4a, 0xc4d },
|
||||
{ 0xc55, 0xc56 },
|
||||
{ 0xc58, 0xc5a },
|
||||
{ 0xc5d, 0xc5d },
|
||||
{ 0xc5c, 0xc5d },
|
||||
{ 0xc60, 0xc63 },
|
||||
{ 0xc66, 0xc6f },
|
||||
{ 0xc80, 0xc83 },
|
||||
|
|
@ -886,7 +888,7 @@ constexpr inline CharRange xid_continue[] = {
|
|||
{ 0xcc6, 0xcc8 },
|
||||
{ 0xcca, 0xccd },
|
||||
{ 0xcd5, 0xcd6 },
|
||||
{ 0xcdd, 0xcde },
|
||||
{ 0xcdc, 0xcde },
|
||||
{ 0xce0, 0xce3 },
|
||||
{ 0xce6, 0xcef },
|
||||
{ 0xcf1, 0xcf3 },
|
||||
|
|
@ -999,7 +1001,8 @@ constexpr inline CharRange xid_continue[] = {
|
|||
{ 0x1a90, 0x1a99 },
|
||||
{ 0x1aa7, 0x1aa7 },
|
||||
{ 0x1ab0, 0x1abd },
|
||||
{ 0x1abf, 0x1ace },
|
||||
{ 0x1abf, 0x1add },
|
||||
{ 0x1ae0, 0x1aeb },
|
||||
{ 0x1b00, 0x1b4c },
|
||||
{ 0x1b50, 0x1b59 },
|
||||
{ 0x1b6b, 0x1b73 },
|
||||
|
|
@ -1092,11 +1095,8 @@ constexpr inline CharRange xid_continue[] = {
|
|||
{ 0xa67f, 0xa6f1 },
|
||||
{ 0xa717, 0xa71f },
|
||||
{ 0xa722, 0xa788 },
|
||||
{ 0xa78b, 0xa7cd },
|
||||
{ 0xa7d0, 0xa7d1 },
|
||||
{ 0xa7d3, 0xa7d3 },
|
||||
{ 0xa7d5, 0xa7dc },
|
||||
{ 0xa7f2, 0xa827 },
|
||||
{ 0xa78b, 0xa7dc },
|
||||
{ 0xa7f1, 0xa827 },
|
||||
{ 0xa82c, 0xa82c },
|
||||
{ 0xa840, 0xa873 },
|
||||
{ 0xa880, 0xa8c5 },
|
||||
|
|
@ -1218,6 +1218,7 @@ constexpr inline CharRange xid_continue[] = {
|
|||
{ 0x108f4, 0x108f5 },
|
||||
{ 0x10900, 0x10915 },
|
||||
{ 0x10920, 0x10939 },
|
||||
{ 0x10940, 0x10959 },
|
||||
{ 0x10980, 0x109b7 },
|
||||
{ 0x109be, 0x109bf },
|
||||
{ 0x10a00, 0x10a03 },
|
||||
|
|
@ -1246,8 +1247,8 @@ constexpr inline CharRange xid_continue[] = {
|
|||
{ 0x10e80, 0x10ea9 },
|
||||
{ 0x10eab, 0x10eac },
|
||||
{ 0x10eb0, 0x10eb1 },
|
||||
{ 0x10ec2, 0x10ec4 },
|
||||
{ 0x10efc, 0x10f1c },
|
||||
{ 0x10ec2, 0x10ec7 },
|
||||
{ 0x10efa, 0x10f1c },
|
||||
{ 0x10f27, 0x10f27 },
|
||||
{ 0x10f30, 0x10f50 },
|
||||
{ 0x10f70, 0x10f85 },
|
||||
|
|
@ -1341,6 +1342,7 @@ constexpr inline CharRange xid_continue[] = {
|
|||
{ 0x11a50, 0x11a99 },
|
||||
{ 0x11a9d, 0x11a9d },
|
||||
{ 0x11ab0, 0x11af8 },
|
||||
{ 0x11b60, 0x11b67 },
|
||||
{ 0x11bc0, 0x11be0 },
|
||||
{ 0x11bf0, 0x11bf9 },
|
||||
{ 0x11c00, 0x11c08 },
|
||||
|
|
@ -1363,6 +1365,8 @@ constexpr inline CharRange xid_continue[] = {
|
|||
{ 0x11d90, 0x11d91 },
|
||||
{ 0x11d93, 0x11d98 },
|
||||
{ 0x11da0, 0x11da9 },
|
||||
{ 0x11db0, 0x11ddb },
|
||||
{ 0x11de0, 0x11de9 },
|
||||
{ 0x11ee0, 0x11ef6 },
|
||||
{ 0x11f00, 0x11f10 },
|
||||
{ 0x11f12, 0x11f3a },
|
||||
|
|
@ -1393,15 +1397,17 @@ constexpr inline CharRange xid_continue[] = {
|
|||
{ 0x16d40, 0x16d6c },
|
||||
{ 0x16d70, 0x16d79 },
|
||||
{ 0x16e40, 0x16e7f },
|
||||
{ 0x16ea0, 0x16eb8 },
|
||||
{ 0x16ebb, 0x16ed3 },
|
||||
{ 0x16f00, 0x16f4a },
|
||||
{ 0x16f4f, 0x16f87 },
|
||||
{ 0x16f8f, 0x16f9f },
|
||||
{ 0x16fe0, 0x16fe1 },
|
||||
{ 0x16fe3, 0x16fe4 },
|
||||
{ 0x16ff0, 0x16ff1 },
|
||||
{ 0x17000, 0x187f7 },
|
||||
{ 0x18800, 0x18cd5 },
|
||||
{ 0x18cff, 0x18d08 },
|
||||
{ 0x16ff0, 0x16ff6 },
|
||||
{ 0x17000, 0x18cd5 },
|
||||
{ 0x18cff, 0x18d1e },
|
||||
{ 0x18d80, 0x18df2 },
|
||||
{ 0x1aff0, 0x1aff3 },
|
||||
{ 0x1aff5, 0x1affb },
|
||||
{ 0x1affd, 0x1affe },
|
||||
|
|
@ -1479,6 +1485,9 @@ constexpr inline CharRange xid_continue[] = {
|
|||
{ 0x1e2c0, 0x1e2f9 },
|
||||
{ 0x1e4d0, 0x1e4f9 },
|
||||
{ 0x1e5d0, 0x1e5fa },
|
||||
{ 0x1e6c0, 0x1e6de },
|
||||
{ 0x1e6e0, 0x1e6f5 },
|
||||
{ 0x1e6fe, 0x1e6ff },
|
||||
{ 0x1e7e0, 0x1e7e6 },
|
||||
{ 0x1e7e8, 0x1e7eb },
|
||||
{ 0x1e7ed, 0x1e7ee },
|
||||
|
|
@ -1522,18 +1531,18 @@ constexpr inline CharRange xid_continue[] = {
|
|||
{ 0x1eeab, 0x1eebb },
|
||||
{ 0x1fbf0, 0x1fbf9 },
|
||||
{ 0x20000, 0x2a6df },
|
||||
{ 0x2a700, 0x2b739 },
|
||||
{ 0x2b740, 0x2b81d },
|
||||
{ 0x2b820, 0x2cea1 },
|
||||
{ 0x2a700, 0x2b81d },
|
||||
{ 0x2b820, 0x2cead },
|
||||
{ 0x2ceb0, 0x2ebe0 },
|
||||
{ 0x2ebf0, 0x2ee5d },
|
||||
{ 0x2f800, 0x2fa1d },
|
||||
{ 0x30000, 0x3134a },
|
||||
{ 0x31350, 0x323af },
|
||||
{ 0x31350, 0x33479 },
|
||||
{ 0xe0100, 0xe01ef },
|
||||
};
|
||||
|
||||
constexpr inline CharRange uppercase_letter[] = {
|
||||
const int uppercase_letter_size = 660;
|
||||
const CharRange uppercase_letter[uppercase_letter_size] = {
|
||||
{ 0x41, 0x5a },
|
||||
{ 0xc0, 0xd6 },
|
||||
{ 0xd8, 0xde },
|
||||
|
|
@ -2138,7 +2147,10 @@ constexpr inline CharRange uppercase_letter[] = {
|
|||
{ 0xa7c4, 0xa7c7 },
|
||||
{ 0xa7c9, 0xa7c9 },
|
||||
{ 0xa7cb, 0xa7cc },
|
||||
{ 0xa7ce, 0xa7ce },
|
||||
{ 0xa7d0, 0xa7d0 },
|
||||
{ 0xa7d2, 0xa7d2 },
|
||||
{ 0xa7d4, 0xa7d4 },
|
||||
{ 0xa7d6, 0xa7d6 },
|
||||
{ 0xa7d8, 0xa7d8 },
|
||||
{ 0xa7da, 0xa7da },
|
||||
|
|
@ -2155,6 +2167,7 @@ constexpr inline CharRange uppercase_letter[] = {
|
|||
{ 0x10d50, 0x10d65 },
|
||||
{ 0x118a0, 0x118bf },
|
||||
{ 0x16e40, 0x16e5f },
|
||||
{ 0x16ea0, 0x16eb8 },
|
||||
{ 0x1d400, 0x1d419 },
|
||||
{ 0x1d434, 0x1d44d },
|
||||
{ 0x1d468, 0x1d481 },
|
||||
|
|
@ -2192,7 +2205,8 @@ constexpr inline CharRange uppercase_letter[] = {
|
|||
{ 0x1f170, 0x1f189 },
|
||||
};
|
||||
|
||||
constexpr inline CharRange lowercase_letter[] = {
|
||||
const int lowercase_letter_size = 677;
|
||||
const CharRange lowercase_letter[lowercase_letter_size] = {
|
||||
{ 0x61, 0x7a },
|
||||
{ 0xaa, 0xaa },
|
||||
{ 0xb5, 0xb5 },
|
||||
|
|
@ -2340,7 +2354,7 @@ constexpr inline CharRange lowercase_letter[] = {
|
|||
{ 0x24b, 0x24b },
|
||||
{ 0x24d, 0x24d },
|
||||
{ 0x24f, 0x293 },
|
||||
{ 0x295, 0x2b8 },
|
||||
{ 0x296, 0x2b8 },
|
||||
{ 0x2c0, 0x2c1 },
|
||||
{ 0x2e0, 0x2e4 },
|
||||
{ 0x345, 0x345 },
|
||||
|
|
@ -2806,13 +2820,14 @@ constexpr inline CharRange lowercase_letter[] = {
|
|||
{ 0xa7c8, 0xa7c8 },
|
||||
{ 0xa7ca, 0xa7ca },
|
||||
{ 0xa7cd, 0xa7cd },
|
||||
{ 0xa7cf, 0xa7cf },
|
||||
{ 0xa7d1, 0xa7d1 },
|
||||
{ 0xa7d3, 0xa7d3 },
|
||||
{ 0xa7d5, 0xa7d5 },
|
||||
{ 0xa7d7, 0xa7d7 },
|
||||
{ 0xa7d9, 0xa7d9 },
|
||||
{ 0xa7db, 0xa7db },
|
||||
{ 0xa7f2, 0xa7f4 },
|
||||
{ 0xa7f1, 0xa7f4 },
|
||||
{ 0xa7f6, 0xa7f6 },
|
||||
{ 0xa7f8, 0xa7fa },
|
||||
{ 0xab30, 0xab5a },
|
||||
|
|
@ -2835,6 +2850,7 @@ constexpr inline CharRange lowercase_letter[] = {
|
|||
{ 0x10d70, 0x10d85 },
|
||||
{ 0x118c0, 0x118df },
|
||||
{ 0x16e60, 0x16e7f },
|
||||
{ 0x16ebb, 0x16ed3 },
|
||||
{ 0x1d41a, 0x1d433 },
|
||||
{ 0x1d44e, 0x1d454 },
|
||||
{ 0x1d456, 0x1d467 },
|
||||
|
|
@ -2870,7 +2886,8 @@ constexpr inline CharRange lowercase_letter[] = {
|
|||
{ 0x1e922, 0x1e943 },
|
||||
};
|
||||
|
||||
constexpr inline CharRange unicode_letter[] = {
|
||||
const int unicode_letter_size = 761;
|
||||
const CharRange unicode_letter[unicode_letter_size] = {
|
||||
{ 0x41, 0x5a },
|
||||
{ 0x61, 0x7a },
|
||||
{ 0xaa, 0xaa },
|
||||
|
|
@ -2924,7 +2941,7 @@ constexpr inline CharRange unicode_letter[] = {
|
|||
{ 0x840, 0x858 },
|
||||
{ 0x860, 0x86a },
|
||||
{ 0x870, 0x887 },
|
||||
{ 0x889, 0x88e },
|
||||
{ 0x889, 0x88f },
|
||||
{ 0x897, 0x897 },
|
||||
{ 0x8a0, 0x8c9 },
|
||||
{ 0x8d4, 0x8df },
|
||||
|
|
@ -3015,7 +3032,7 @@ constexpr inline CharRange unicode_letter[] = {
|
|||
{ 0xc4a, 0xc4c },
|
||||
{ 0xc55, 0xc56 },
|
||||
{ 0xc58, 0xc5a },
|
||||
{ 0xc5d, 0xc5d },
|
||||
{ 0xc5c, 0xc5d },
|
||||
{ 0xc60, 0xc63 },
|
||||
{ 0xc80, 0xc83 },
|
||||
{ 0xc85, 0xc8c },
|
||||
|
|
@ -3027,7 +3044,7 @@ constexpr inline CharRange unicode_letter[] = {
|
|||
{ 0xcc6, 0xcc8 },
|
||||
{ 0xcca, 0xccc },
|
||||
{ 0xcd5, 0xcd6 },
|
||||
{ 0xcdd, 0xcde },
|
||||
{ 0xcdc, 0xcde },
|
||||
{ 0xce0, 0xce3 },
|
||||
{ 0xcf1, 0xcf3 },
|
||||
{ 0xd00, 0xd0c },
|
||||
|
|
@ -3227,11 +3244,8 @@ constexpr inline CharRange unicode_letter[] = {
|
|||
{ 0xa67f, 0xa6ef },
|
||||
{ 0xa717, 0xa71f },
|
||||
{ 0xa722, 0xa788 },
|
||||
{ 0xa78b, 0xa7cd },
|
||||
{ 0xa7d0, 0xa7d1 },
|
||||
{ 0xa7d3, 0xa7d3 },
|
||||
{ 0xa7d5, 0xa7dc },
|
||||
{ 0xa7f2, 0xa805 },
|
||||
{ 0xa78b, 0xa7dc },
|
||||
{ 0xa7f1, 0xa805 },
|
||||
{ 0xa807, 0xa827 },
|
||||
{ 0xa840, 0xa873 },
|
||||
{ 0xa880, 0xa8c3 },
|
||||
|
|
@ -3340,6 +3354,7 @@ constexpr inline CharRange unicode_letter[] = {
|
|||
{ 0x108f4, 0x108f5 },
|
||||
{ 0x10900, 0x10915 },
|
||||
{ 0x10920, 0x10939 },
|
||||
{ 0x10940, 0x10959 },
|
||||
{ 0x10980, 0x109b7 },
|
||||
{ 0x109be, 0x109bf },
|
||||
{ 0x10a00, 0x10a03 },
|
||||
|
|
@ -3365,8 +3380,8 @@ constexpr inline CharRange unicode_letter[] = {
|
|||
{ 0x10e80, 0x10ea9 },
|
||||
{ 0x10eab, 0x10eac },
|
||||
{ 0x10eb0, 0x10eb1 },
|
||||
{ 0x10ec2, 0x10ec4 },
|
||||
{ 0x10efc, 0x10efc },
|
||||
{ 0x10ec2, 0x10ec7 },
|
||||
{ 0x10efa, 0x10efc },
|
||||
{ 0x10f00, 0x10f1c },
|
||||
{ 0x10f27, 0x10f27 },
|
||||
{ 0x10f30, 0x10f45 },
|
||||
|
|
@ -3459,6 +3474,7 @@ constexpr inline CharRange unicode_letter[] = {
|
|||
{ 0x11a50, 0x11a97 },
|
||||
{ 0x11a9d, 0x11a9d },
|
||||
{ 0x11ab0, 0x11af8 },
|
||||
{ 0x11b60, 0x11b67 },
|
||||
{ 0x11bc0, 0x11be0 },
|
||||
{ 0x11c00, 0x11c08 },
|
||||
{ 0x11c0a, 0x11c36 },
|
||||
|
|
@ -3481,6 +3497,7 @@ constexpr inline CharRange unicode_letter[] = {
|
|||
{ 0x11d90, 0x11d91 },
|
||||
{ 0x11d93, 0x11d96 },
|
||||
{ 0x11d98, 0x11d98 },
|
||||
{ 0x11db0, 0x11ddb },
|
||||
{ 0x11ee0, 0x11ef6 },
|
||||
{ 0x11f00, 0x11f10 },
|
||||
{ 0x11f12, 0x11f3a },
|
||||
|
|
@ -3505,15 +3522,17 @@ constexpr inline CharRange unicode_letter[] = {
|
|||
{ 0x16b7d, 0x16b8f },
|
||||
{ 0x16d40, 0x16d6c },
|
||||
{ 0x16e40, 0x16e7f },
|
||||
{ 0x16ea0, 0x16eb8 },
|
||||
{ 0x16ebb, 0x16ed3 },
|
||||
{ 0x16f00, 0x16f4a },
|
||||
{ 0x16f4f, 0x16f87 },
|
||||
{ 0x16f8f, 0x16f9f },
|
||||
{ 0x16fe0, 0x16fe1 },
|
||||
{ 0x16fe3, 0x16fe3 },
|
||||
{ 0x16ff0, 0x16ff1 },
|
||||
{ 0x17000, 0x187f7 },
|
||||
{ 0x18800, 0x18cd5 },
|
||||
{ 0x18cff, 0x18d08 },
|
||||
{ 0x16ff0, 0x16ff6 },
|
||||
{ 0x17000, 0x18cd5 },
|
||||
{ 0x18cff, 0x18d1e },
|
||||
{ 0x18d80, 0x18df2 },
|
||||
{ 0x1aff0, 0x1aff3 },
|
||||
{ 0x1aff5, 0x1affb },
|
||||
{ 0x1affd, 0x1affe },
|
||||
|
|
@ -3575,6 +3594,9 @@ constexpr inline CharRange unicode_letter[] = {
|
|||
{ 0x1e4d0, 0x1e4eb },
|
||||
{ 0x1e5d0, 0x1e5ed },
|
||||
{ 0x1e5f0, 0x1e5f0 },
|
||||
{ 0x1e6c0, 0x1e6de },
|
||||
{ 0x1e6e0, 0x1e6f5 },
|
||||
{ 0x1e6fe, 0x1e6ff },
|
||||
{ 0x1e7e0, 0x1e7e6 },
|
||||
{ 0x1e7e8, 0x1e7eb },
|
||||
{ 0x1e7ed, 0x1e7ee },
|
||||
|
|
@ -3620,12 +3642,11 @@ constexpr inline CharRange unicode_letter[] = {
|
|||
{ 0x1f150, 0x1f169 },
|
||||
{ 0x1f170, 0x1f189 },
|
||||
{ 0x20000, 0x2a6df },
|
||||
{ 0x2a700, 0x2b739 },
|
||||
{ 0x2b740, 0x2b81d },
|
||||
{ 0x2b820, 0x2cea1 },
|
||||
{ 0x2a700, 0x2b81d },
|
||||
{ 0x2b820, 0x2cead },
|
||||
{ 0x2ceb0, 0x2ebe0 },
|
||||
{ 0x2ebf0, 0x2ee5d },
|
||||
{ 0x2f800, 0x2fa1d },
|
||||
{ 0x30000, 0x3134a },
|
||||
{ 0x31350, 0x323af },
|
||||
{ 0x31350, 0x33479 },
|
||||
};
|
||||
|
|
@ -32,48 +32,62 @@
|
|||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#include "char_range.inc"
|
||||
|
||||
static constexpr char hex_char_table_upper[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
static constexpr char hex_char_table_lower[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
|
||||
#define BSEARCH_CHAR_RANGE(m_array) \
|
||||
int low = 0; \
|
||||
int high = std_size(m_array) - 1; \
|
||||
int middle = (low + high) / 2; \
|
||||
\
|
||||
while (low <= high) { \
|
||||
if (p_char < m_array[middle].start) { \
|
||||
high = middle - 1; \
|
||||
struct CharRange {
|
||||
char32_t start;
|
||||
char32_t end;
|
||||
};
|
||||
|
||||
extern const CharRange xid_start[];
|
||||
extern const int xid_start_size;
|
||||
extern const CharRange xid_continue[];
|
||||
extern const int xid_continue_size;
|
||||
extern const CharRange uppercase_letter[];
|
||||
extern const int uppercase_letter_size;
|
||||
extern const CharRange lowercase_letter[];
|
||||
extern const int lowercase_letter_size;
|
||||
extern const CharRange unicode_letter[];
|
||||
extern const int unicode_letter_size;
|
||||
|
||||
#define BSEARCH_CHAR_RANGE(m_array, m_size) \
|
||||
int low = 0; \
|
||||
int high = m_size - 1; \
|
||||
int middle = (low + high) / 2; \
|
||||
\
|
||||
while (low <= high) { \
|
||||
if (p_char < m_array[middle].start) { \
|
||||
high = middle - 1; \
|
||||
} else if (p_char > m_array[middle].end) { \
|
||||
low = middle + 1; \
|
||||
} else { \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
middle = (low + high) / 2; \
|
||||
} \
|
||||
\
|
||||
low = middle + 1; \
|
||||
} else { \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
middle = (low + high) / 2; \
|
||||
} \
|
||||
\
|
||||
return false
|
||||
|
||||
constexpr bool is_unicode_identifier_start(char32_t p_char) {
|
||||
BSEARCH_CHAR_RANGE(xid_start);
|
||||
inline bool is_unicode_identifier_start(char32_t p_char) {
|
||||
BSEARCH_CHAR_RANGE(xid_start, xid_start_size);
|
||||
}
|
||||
|
||||
constexpr bool is_unicode_identifier_continue(char32_t p_char) {
|
||||
BSEARCH_CHAR_RANGE(xid_continue);
|
||||
inline bool is_unicode_identifier_continue(char32_t p_char) {
|
||||
BSEARCH_CHAR_RANGE(xid_continue, xid_continue_size);
|
||||
}
|
||||
|
||||
constexpr bool is_unicode_upper_case(char32_t p_char) {
|
||||
BSEARCH_CHAR_RANGE(uppercase_letter);
|
||||
inline bool is_unicode_upper_case(char32_t p_char) {
|
||||
BSEARCH_CHAR_RANGE(uppercase_letter, uppercase_letter_size);
|
||||
}
|
||||
|
||||
constexpr bool is_unicode_lower_case(char32_t p_char) {
|
||||
BSEARCH_CHAR_RANGE(lowercase_letter);
|
||||
inline bool is_unicode_lower_case(char32_t p_char) {
|
||||
BSEARCH_CHAR_RANGE(lowercase_letter, lowercase_letter_size);
|
||||
}
|
||||
|
||||
constexpr bool is_unicode_letter(char32_t p_char) {
|
||||
BSEARCH_CHAR_RANGE(unicode_letter);
|
||||
inline bool is_unicode_letter(char32_t p_char) {
|
||||
BSEARCH_CHAR_RANGE(unicode_letter, unicode_letter_size);
|
||||
}
|
||||
|
||||
#undef BSEARCH_CHAR_RANGE
|
||||
|
|
|
|||
|
|
@ -30,9 +30,10 @@
|
|||
|
||||
#include "fuzzy_search.h"
|
||||
|
||||
constexpr float cull_factor = 0.1f;
|
||||
constexpr float cull_cutoff = 30.0f;
|
||||
const String boundary_chars = "/\\-_.";
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/variant/typed_array.h"
|
||||
|
||||
static const String boundary_chars = "/\\-_. ";
|
||||
|
||||
static bool _is_valid_interval(const Vector2i &p_interval) {
|
||||
// Empty intervals are represented as (-1, -1).
|
||||
|
|
@ -117,7 +118,7 @@ bool FuzzyTokenMatch::intersects(const Vector2i &p_other_interval) const {
|
|||
return interval.y >= p_other_interval.x && interval.x <= p_other_interval.y;
|
||||
}
|
||||
|
||||
bool FuzzySearchResult::can_add_token_match(const FuzzyTokenMatch &p_match) const {
|
||||
bool FuzzySearchMatch::_can_add_token_match(const FuzzyTokenMatch &p_match) const {
|
||||
if (p_match.get_miss_count() > miss_budget) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -148,7 +149,7 @@ bool FuzzyTokenMatch::is_case_insensitive(const String &p_original, const String
|
|||
return false;
|
||||
}
|
||||
|
||||
void FuzzySearchResult::score_token_match(FuzzyTokenMatch &p_match, bool p_case_insensitive) const {
|
||||
void FuzzySearchMatch::_score_token_match(FuzzyTokenMatch &p_match, bool p_case_insensitive) const {
|
||||
// This can always be tweaked more. The intuition is that exact matches should almost always
|
||||
// be prioritized over broken up matches, and other criteria more or less act as tie breakers.
|
||||
|
||||
|
|
@ -173,44 +174,80 @@ void FuzzySearchResult::score_token_match(FuzzyTokenMatch &p_match, bool p_case_
|
|||
}
|
||||
}
|
||||
|
||||
void FuzzySearchResult::maybe_apply_score_bonus() {
|
||||
void FuzzySearchMatch::_maybe_apply_token_order_score_bonus() {
|
||||
// This adds a small bonus to results which match tokens in the same order they appear in the query.
|
||||
if (token_matches.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int *token_range_starts = (int *)alloca(sizeof(int) * token_matches.size());
|
||||
|
||||
for (const FuzzyTokenMatch &match : token_matches) {
|
||||
token_range_starts[match.token_idx] = match.interval.x;
|
||||
}
|
||||
|
||||
int last = token_range_starts[0];
|
||||
for (int i = 1; i < token_matches.size(); i++) {
|
||||
if (last > token_range_starts[i]) {
|
||||
// Individual tokens can match without a range if the missed-character budget allows for it. If
|
||||
// the i'th token matches in this manner, skip ahead so we check neither (i-1, i) nor (i, i+1).
|
||||
// It's safe that this skips i=0 since any valid start will be > -1.
|
||||
if (token_range_starts[i] == -1) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (token_range_starts[i - 1] > token_range_starts[i]) {
|
||||
return;
|
||||
}
|
||||
last = token_range_starts[i];
|
||||
}
|
||||
|
||||
score += 1;
|
||||
}
|
||||
|
||||
void FuzzySearchResult::add_token_match(const FuzzyTokenMatch &p_match) {
|
||||
void FuzzySearchMatch::_add_token_match(const FuzzyTokenMatch &p_match) {
|
||||
score += p_match.score;
|
||||
match_interval = _extend_interval(match_interval, p_match.interval);
|
||||
miss_budget -= p_match.get_miss_count();
|
||||
token_matches.append(p_match);
|
||||
}
|
||||
|
||||
void remove_low_scores(Vector<FuzzySearchResult> &p_results, float p_cull_score) {
|
||||
void FuzzySearchMatch::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_target", "target"), &FuzzySearchMatch::set_target);
|
||||
ClassDB::bind_method(D_METHOD("get_target"), &FuzzySearchMatch::get_target);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_score", "score"), &FuzzySearchMatch::set_score);
|
||||
ClassDB::bind_method(D_METHOD("get_score"), &FuzzySearchMatch::get_score);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_original_index", "original_index"), &FuzzySearchMatch::set_original_index);
|
||||
ClassDB::bind_method(D_METHOD("get_original_index"), &FuzzySearchMatch::get_original_index);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_matched_substrings"), &FuzzySearchMatch::get_matched_substrings);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "target"), "set_target", "get_target");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "score"), "set_score", "get_score");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "original_index"), "set_original_index", "get_original_index");
|
||||
}
|
||||
|
||||
TypedArray<Vector2i> FuzzySearchMatch::get_matched_substrings() const {
|
||||
TypedArray<Vector2i> substrings;
|
||||
for (const FuzzyTokenMatch &match : token_matches) {
|
||||
for (const Vector2i &substring : match.substrings) {
|
||||
substrings.append(substring);
|
||||
}
|
||||
}
|
||||
return substrings;
|
||||
}
|
||||
|
||||
static void remove_low_scores(Vector<Ref<FuzzySearchMatch>> &p_results, float p_cull_score) {
|
||||
// Removes all results with score < p_cull_score in-place.
|
||||
int i = 0;
|
||||
int j = p_results.size() - 1;
|
||||
FuzzySearchResult *results = p_results.ptrw();
|
||||
Ref<FuzzySearchMatch> *results = p_results.ptrw();
|
||||
|
||||
while (true) {
|
||||
// Advances i to an element to remove and j to an element to keep.
|
||||
while (j >= i && results[j].score < p_cull_score) {
|
||||
while (j >= i && results[j]->get_score() < p_cull_score) {
|
||||
j--;
|
||||
}
|
||||
while (i < j && results[i].score >= p_cull_score) {
|
||||
while (i < j && results[i]->get_score() >= p_cull_score) {
|
||||
i++;
|
||||
}
|
||||
if (i >= j) {
|
||||
|
|
@ -222,60 +259,13 @@ void remove_low_scores(Vector<FuzzySearchResult> &p_results, float p_cull_score)
|
|||
p_results.resize(j + 1);
|
||||
}
|
||||
|
||||
void FuzzySearch::sort_and_filter(Vector<FuzzySearchResult> &p_results) const {
|
||||
if (p_results.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
float avg_score = 0;
|
||||
float max_score = 0;
|
||||
|
||||
for (const FuzzySearchResult &result : p_results) {
|
||||
avg_score += result.score;
|
||||
max_score = MAX(max_score, result.score);
|
||||
}
|
||||
|
||||
// TODO: Tune scoring and culling here to display fewer subsequence soup matches when good matches
|
||||
// are available.
|
||||
avg_score /= p_results.size();
|
||||
float cull_score = MIN(cull_cutoff, Math::lerp(avg_score, max_score, cull_factor));
|
||||
remove_low_scores(p_results, cull_score);
|
||||
|
||||
struct FuzzySearchResultComparator {
|
||||
bool operator()(const FuzzySearchResult &p_lhs, const FuzzySearchResult &p_rhs) const {
|
||||
// Sort on (score, length, alphanumeric) to ensure consistent ordering.
|
||||
if (p_lhs.score == p_rhs.score) {
|
||||
if (p_lhs.target.length() == p_rhs.target.length()) {
|
||||
return p_lhs.target < p_rhs.target;
|
||||
}
|
||||
return p_lhs.target.length() < p_rhs.target.length();
|
||||
}
|
||||
return p_lhs.score > p_rhs.score;
|
||||
}
|
||||
};
|
||||
|
||||
SortArray<FuzzySearchResult, FuzzySearchResultComparator> sorter;
|
||||
|
||||
if (p_results.size() > max_results) {
|
||||
sorter.partial_sort(0, p_results.size(), max_results, p_results.ptrw());
|
||||
p_results.resize(max_results);
|
||||
} else {
|
||||
sorter.sort(p_results.ptrw(), p_results.size());
|
||||
}
|
||||
}
|
||||
|
||||
void FuzzySearch::set_query(const String &p_query) {
|
||||
set_query(p_query, !p_query.is_lowercase());
|
||||
}
|
||||
|
||||
void FuzzySearch::set_query(const String &p_query, bool p_case_sensitive) {
|
||||
tokens.clear();
|
||||
case_sensitive = p_case_sensitive;
|
||||
Vector<FuzzySearchToken> FuzzySearch::_get_tokens(const String &p_query) const {
|
||||
Vector<FuzzySearchToken> tokens;
|
||||
|
||||
for (const String &string : p_query.split(" ", false)) {
|
||||
tokens.append({
|
||||
static_cast<int>(tokens.size()),
|
||||
p_case_sensitive ? string : string.to_lower(),
|
||||
case_sensitive ? string : string.to_lower(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -290,12 +280,60 @@ void FuzzySearch::set_query(const String &p_query, bool p_case_sensitive) {
|
|||
|
||||
// Prioritize matching longer tokens before shorter ones since match overlaps are not accepted.
|
||||
tokens.sort_custom<TokenComparator>();
|
||||
return tokens;
|
||||
}
|
||||
|
||||
bool FuzzySearch::search(const String &p_target, FuzzySearchResult &p_result) const {
|
||||
p_result.target = p_target;
|
||||
p_result.dir_index = p_target.rfind_char('/');
|
||||
p_result.miss_budget = max_misses;
|
||||
void FuzzySearch::_sort_and_filter(Vector<Ref<FuzzySearchMatch>> &p_results) const {
|
||||
if (p_results.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (filter_low_scores) {
|
||||
float avg_score = 0;
|
||||
float max_score = 0;
|
||||
|
||||
for (const Ref<FuzzySearchMatch> &result : p_results) {
|
||||
avg_score += result->get_score();
|
||||
max_score = MAX(max_score, result->get_score());
|
||||
}
|
||||
|
||||
avg_score /= p_results.size();
|
||||
float cull_score = MIN(filter_cutoff, Math::lerp(avg_score, max_score, filter_factor));
|
||||
remove_low_scores(p_results, cull_score);
|
||||
}
|
||||
|
||||
struct FuzzySearchResultComparator {
|
||||
bool operator()(const Ref<FuzzySearchMatch> &p_lhs, const Ref<FuzzySearchMatch> &p_rhs) const {
|
||||
// Sort on (score, length, alphanumeric) to ensure consistent ordering.
|
||||
if (p_lhs->score == p_rhs->score) {
|
||||
if (p_lhs->target.length() == p_rhs->target.length()) {
|
||||
return p_lhs->target < p_rhs->target;
|
||||
}
|
||||
return p_lhs->target.length() < p_rhs->target.length();
|
||||
}
|
||||
return p_lhs->score > p_rhs->score;
|
||||
}
|
||||
};
|
||||
|
||||
SortArray<Ref<FuzzySearchMatch>, FuzzySearchResultComparator> sorter;
|
||||
|
||||
if (p_results.size() > max_results) {
|
||||
sorter.partial_sort(0, p_results.size(), max_results, p_results.ptrw());
|
||||
p_results.resize(max_results);
|
||||
} else {
|
||||
sorter.sort(p_results.ptrw(), p_results.size());
|
||||
}
|
||||
}
|
||||
|
||||
void FuzzySearch::set_case_sensitive(bool p_case_sensitive) {
|
||||
case_sensitive = p_case_sensitive;
|
||||
}
|
||||
|
||||
bool FuzzySearch::_search_tokens(const Vector<FuzzySearchToken> &p_tokens, const String &p_target, Ref<FuzzySearchMatch> &r_result) const {
|
||||
r_result->target = p_target;
|
||||
r_result->dir_index = p_target.rfind_char('/');
|
||||
r_result->miss_budget = max_misses;
|
||||
r_result->token_matches.reserve(p_tokens.size());
|
||||
|
||||
String adjusted_target = case_sensitive ? p_target : p_target.to_lower();
|
||||
|
||||
|
|
@ -303,23 +341,23 @@ bool FuzzySearch::search(const String &p_target, FuzzySearchResult &p_result) co
|
|||
// which does not conflict with prior token matches. This is not ensured to find the highest scoring
|
||||
// combination of matches, or necessarily the highest scoring single subsequence, as it only considers
|
||||
// eager subsequences for a given index, and likewise eagerly finds matches for each token in sequence.
|
||||
for (const FuzzySearchToken &token : tokens) {
|
||||
for (const FuzzySearchToken &token : p_tokens) {
|
||||
FuzzyTokenMatch best_match;
|
||||
int offset = start_offset;
|
||||
|
||||
while (true) {
|
||||
FuzzyTokenMatch match;
|
||||
if (allow_subsequences) {
|
||||
if (!token.try_fuzzy_match(match, adjusted_target, offset, p_result.miss_budget)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (exact_token_matches) {
|
||||
if (!token.try_exact_match(match, adjusted_target, offset)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!token.try_fuzzy_match(match, adjusted_target, offset, r_result->miss_budget)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (p_result.can_add_token_match(match)) {
|
||||
p_result.score_token_match(match, match.is_case_insensitive(p_target, adjusted_target));
|
||||
if (r_result->_can_add_token_match(match)) {
|
||||
r_result->_score_token_match(match, match.is_case_insensitive(p_target, adjusted_target));
|
||||
if (best_match.token_idx == -1 || best_match.score < match.score) {
|
||||
best_match = match;
|
||||
}
|
||||
|
|
@ -335,23 +373,89 @@ bool FuzzySearch::search(const String &p_target, FuzzySearchResult &p_result) co
|
|||
return false;
|
||||
}
|
||||
|
||||
p_result.add_token_match(best_match);
|
||||
r_result->_add_token_match(best_match);
|
||||
}
|
||||
|
||||
p_result.maybe_apply_score_bonus();
|
||||
if (r_result->match_interval.x == -1) {
|
||||
// Reject matches which rely entirely on misses.
|
||||
return false;
|
||||
}
|
||||
|
||||
r_result->_maybe_apply_token_order_score_bonus();
|
||||
return true;
|
||||
}
|
||||
|
||||
void FuzzySearch::search_all(const PackedStringArray &p_targets, Vector<FuzzySearchResult> &p_results) const {
|
||||
p_results.clear();
|
||||
Ref<FuzzySearchMatch> FuzzySearch::search(const String &p_query, const String &p_target) const {
|
||||
Ref<FuzzySearchMatch> result;
|
||||
result.instantiate();
|
||||
if (_search_tokens(_get_tokens(p_query), p_target, result)) {
|
||||
return result;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Vector<Ref<FuzzySearchMatch>> FuzzySearch::search_all(const String &p_query, const PackedStringArray &p_targets) const {
|
||||
Vector<Ref<FuzzySearchMatch>> results;
|
||||
const Vector<FuzzySearchToken> tokens = _get_tokens(p_query);
|
||||
|
||||
for (int i = 0; i < p_targets.size(); i++) {
|
||||
FuzzySearchResult result;
|
||||
result.original_index = i;
|
||||
if (search(p_targets[i], result)) {
|
||||
p_results.append(result);
|
||||
Ref<FuzzySearchMatch> result;
|
||||
result.instantiate();
|
||||
result->original_index = i;
|
||||
if (_search_tokens(tokens, p_targets[i], result)) {
|
||||
results.append(result);
|
||||
}
|
||||
}
|
||||
|
||||
sort_and_filter(p_results);
|
||||
_sort_and_filter(results);
|
||||
return results;
|
||||
}
|
||||
|
||||
TypedArray<FuzzySearchMatch> FuzzySearch::_search_all_bind(const String &p_query, const PackedStringArray &p_targets) const {
|
||||
Vector<Ref<FuzzySearchMatch>> results = search_all(p_query, p_targets);
|
||||
TypedArray<FuzzySearchMatch> wrapped_results;
|
||||
wrapped_results.reserve(results.size());
|
||||
for (Ref<FuzzySearchMatch> &result : results) {
|
||||
wrapped_results.append(result);
|
||||
}
|
||||
|
||||
return wrapped_results;
|
||||
}
|
||||
|
||||
void FuzzySearch::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_start_offset", "start_offset"), &FuzzySearch::set_start_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_start_offset"), &FuzzySearch::get_start_offset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_max_results", "max_results"), &FuzzySearch::set_max_results);
|
||||
ClassDB::bind_method(D_METHOD("get_max_results"), &FuzzySearch::get_max_results);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_max_misses", "max_misses"), &FuzzySearch::set_max_misses);
|
||||
ClassDB::bind_method(D_METHOD("get_max_misses"), &FuzzySearch::get_max_misses);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_use_exact_tokens", "use_exact_tokens"), &FuzzySearch::set_use_exact_tokens);
|
||||
ClassDB::bind_method(D_METHOD("get_use_exact_tokens"), &FuzzySearch::get_use_exact_tokens);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_case_sensitive", "case_sensitive"), &FuzzySearch::set_case_sensitive);
|
||||
ClassDB::bind_method(D_METHOD("get_case_sensitive"), &FuzzySearch::get_case_sensitive);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_filter_low_scores", "filter_low_scores"), &FuzzySearch::set_filter_low_scores);
|
||||
ClassDB::bind_method(D_METHOD("get_filter_low_scores"), &FuzzySearch::get_filter_low_scores);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_filter_factor", "filter_factor"), &FuzzySearch::set_filter_factor);
|
||||
ClassDB::bind_method(D_METHOD("get_filter_factor"), &FuzzySearch::get_filter_factor);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_filter_cutoff", "filter_cutoff"), &FuzzySearch::set_filter_cutoff);
|
||||
ClassDB::bind_method(D_METHOD("get_filter_cutoff"), &FuzzySearch::get_filter_cutoff);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("search", "query", "target"), &FuzzySearch::search);
|
||||
ClassDB::bind_method(D_METHOD("search_all", "query", "targets"), &FuzzySearch::_search_all_bind);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "start_offset"), "set_start_offset", "get_start_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_results"), "set_max_results", "get_max_results");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_misses"), "set_max_misses", "get_max_misses");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_exact_tokens"), "set_use_exact_tokens", "get_use_exact_tokens");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "case_sensitive"), "set_case_sensitive", "get_case_sensitive");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_low_scores"), "set_filter_low_scores", "get_filter_low_scores");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "filter_factor"), "set_filter_factor", "get_filter_factor");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "filter_cutoff"), "set_filter_cutoff", "get_filter_cutoff");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
class FuzzyTokenMatch;
|
||||
|
|
@ -44,7 +45,7 @@ struct FuzzySearchToken {
|
|||
|
||||
class FuzzyTokenMatch {
|
||||
friend struct FuzzySearchToken;
|
||||
friend class FuzzySearchResult;
|
||||
friend class FuzzySearchMatch;
|
||||
friend class FuzzySearch;
|
||||
|
||||
int matched_length = 0;
|
||||
|
|
@ -62,39 +63,93 @@ public:
|
|||
Vector<Vector2i> substrings; // x is start index, y is length.
|
||||
};
|
||||
|
||||
class FuzzySearchResult {
|
||||
class FuzzySearchMatch : public RefCounted {
|
||||
GDCLASS(FuzzySearchMatch, RefCounted)
|
||||
|
||||
friend class FuzzySearch;
|
||||
|
||||
int miss_budget = 0;
|
||||
Vector2i match_interval = Vector2i(-1, -1);
|
||||
|
||||
bool can_add_token_match(const FuzzyTokenMatch &p_match) const;
|
||||
void score_token_match(FuzzyTokenMatch &p_match, bool p_case_insensitive) const;
|
||||
void add_token_match(const FuzzyTokenMatch &p_match);
|
||||
void maybe_apply_score_bonus();
|
||||
|
||||
public:
|
||||
String target;
|
||||
int score = 0;
|
||||
int original_index = -1;
|
||||
int dir_index = -1;
|
||||
Vector<FuzzyTokenMatch> token_matches;
|
||||
};
|
||||
int miss_budget = 0;
|
||||
Vector2i match_interval = Vector2i(-1, -1);
|
||||
|
||||
class FuzzySearch {
|
||||
Vector<FuzzySearchToken> tokens;
|
||||
bool _can_add_token_match(const FuzzyTokenMatch &p_match) const;
|
||||
void _score_token_match(FuzzyTokenMatch &p_match, bool p_case_insensitive) const;
|
||||
void _add_token_match(const FuzzyTokenMatch &p_match);
|
||||
void _maybe_apply_token_order_score_bonus();
|
||||
|
||||
bool case_sensitive = false;
|
||||
void sort_and_filter(Vector<FuzzySearchResult> &p_results) const;
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_target(const String &p_target) { target = p_target; }
|
||||
String get_target() const { return target; }
|
||||
|
||||
void set_score(int p_score) { score = p_score; }
|
||||
int get_score() const { return score; }
|
||||
|
||||
void set_original_index(int p_original_index) { original_index = p_original_index; }
|
||||
int get_original_index() const { return original_index; }
|
||||
|
||||
void set_dir_index(int p_dir_index) { dir_index = p_dir_index; }
|
||||
int get_dir_index() const { return dir_index; }
|
||||
|
||||
Vector<FuzzyTokenMatch> get_token_matches() { return token_matches; }
|
||||
|
||||
TypedArray<Vector2i> get_matched_substrings() const;
|
||||
};
|
||||
|
||||
class FuzzySearch : public RefCounted {
|
||||
GDCLASS(FuzzySearch, RefCounted)
|
||||
|
||||
int start_offset = 0;
|
||||
int max_results = 100;
|
||||
int max_misses = 2;
|
||||
bool allow_subsequences = true;
|
||||
bool exact_token_matches = false;
|
||||
bool case_sensitive = false;
|
||||
bool filter_low_scores = true;
|
||||
float filter_factor = 0.1f;
|
||||
float filter_cutoff = 30.0f;
|
||||
|
||||
void set_query(const String &p_query);
|
||||
void set_query(const String &p_query, bool p_case_sensitive);
|
||||
bool search(const String &p_target, FuzzySearchResult &p_result) const;
|
||||
void search_all(const PackedStringArray &p_targets, Vector<FuzzySearchResult> &p_results) const;
|
||||
Vector<FuzzySearchToken> _get_tokens(const String &p_query) const;
|
||||
void _sort_and_filter(Vector<Ref<FuzzySearchMatch>> &p_results) const;
|
||||
bool _search_tokens(const Vector<FuzzySearchToken> &p_tokens, const String &p_target, Ref<FuzzySearchMatch> &r_result) const;
|
||||
TypedArray<FuzzySearchMatch> _search_all_bind(const String &p_query, const PackedStringArray &p_targets) const;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_start_offset(int p_offset) { start_offset = p_offset; }
|
||||
int get_start_offset() const { return start_offset; }
|
||||
|
||||
void set_max_results(int p_max_results) { max_results = p_max_results; }
|
||||
int get_max_results() const { return max_results; }
|
||||
|
||||
void set_max_misses(int p_max_misses) { max_misses = p_max_misses; }
|
||||
int get_max_misses() const { return max_misses; }
|
||||
|
||||
void set_use_exact_tokens(bool p_use_exact_tokens) { exact_token_matches = p_use_exact_tokens; }
|
||||
bool get_use_exact_tokens() const { return exact_token_matches; }
|
||||
|
||||
void set_case_sensitive(bool p_case_sensitive);
|
||||
bool get_case_sensitive() const { return case_sensitive; }
|
||||
|
||||
void set_filter_low_scores(bool p_filter_low_scores) { filter_low_scores = p_filter_low_scores; }
|
||||
bool get_filter_low_scores() const { return filter_low_scores; }
|
||||
|
||||
void set_filter_factor(float p_filter_factor) {
|
||||
ERR_FAIL_COND_MSG(p_filter_factor < 0.0f || p_filter_factor > 1.0f, "filter_factor should be in the range [0, 1]");
|
||||
filter_factor = p_filter_factor;
|
||||
}
|
||||
float get_filter_factor() const { return filter_factor; }
|
||||
|
||||
void set_filter_cutoff(float p_filter_cutoff) { filter_cutoff = p_filter_cutoff; }
|
||||
float get_filter_cutoff() const { return filter_cutoff; }
|
||||
|
||||
Ref<FuzzySearchMatch> search(const String &p_query, const String &p_target) const;
|
||||
Vector<Ref<FuzzySearchMatch>> search_all(const String &p_query, const PackedStringArray &p_targets) const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -913,6 +913,7 @@ static const char *language_list[][2] = {
|
|||
{ "tmh", "Tamashek" },
|
||||
{ "tn", "Tswana" },
|
||||
{ "to", "Tongan" },
|
||||
{ "tok", "Toki Pona" },
|
||||
{ "tog", "Nyasa Tonga" },
|
||||
{ "tpi", "Tok Pisin" },
|
||||
{ "tr", "Turkish" },
|
||||
|
|
@ -1537,6 +1538,7 @@ static const char *language_script_list[][2] = {
|
|||
{ "tn", "Latn" },
|
||||
{ "to", "Latn" },
|
||||
{ "tog", "Latn" },
|
||||
{ "tok", "Latn" },
|
||||
{ "tpi", "Latn" },
|
||||
{ "tr", "Latn Arab" },
|
||||
{ "tru", "Latn Syrc" },
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ void NodePath::_update_hash_cache() const {
|
|||
}
|
||||
|
||||
void NodePath::prepend_period() {
|
||||
if (data->path.size() && data->path[0].operator String() != ".") {
|
||||
if (data->path.size() && data->path[0].string() != ".") {
|
||||
_copy_on_write();
|
||||
data->path.insert(0, ".");
|
||||
data->concatenated_path = StringName();
|
||||
data->hash_cache_valid = false;
|
||||
|
|
@ -167,6 +168,29 @@ void NodePath::operator=(const NodePath &p_path) {
|
|||
}
|
||||
}
|
||||
|
||||
void NodePath::_copy_on_write() {
|
||||
if (!data) {
|
||||
return; // No data; nothing to do.
|
||||
}
|
||||
|
||||
if (data->refcount.get() == 1) {
|
||||
return; // Already the only owner of data.
|
||||
}
|
||||
|
||||
// Make a copy.
|
||||
Data *new_data = memnew(Data);
|
||||
new_data->refcount.init();
|
||||
new_data->path = data->path;
|
||||
new_data->subpath = data->subpath;
|
||||
new_data->concatenated_path = data->concatenated_path;
|
||||
new_data->concatenated_subpath = data->concatenated_subpath;
|
||||
new_data->absolute = data->absolute;
|
||||
new_data->hash_cache_valid = data->hash_cache_valid;
|
||||
new_data->hash_cache = data->hash_cache;
|
||||
unref();
|
||||
data = new_data;
|
||||
}
|
||||
|
||||
NodePath::operator String() const {
|
||||
if (!data) {
|
||||
return String();
|
||||
|
|
@ -212,7 +236,7 @@ StringName NodePath::get_concatenated_names() const {
|
|||
if (i > 0) {
|
||||
concatenated += "/";
|
||||
}
|
||||
concatenated += sn[i].operator String();
|
||||
concatenated += sn[i].string();
|
||||
}
|
||||
data->concatenated_path = concatenated;
|
||||
}
|
||||
|
|
@ -230,7 +254,7 @@ StringName NodePath::get_concatenated_subnames() const {
|
|||
if (i > 0) {
|
||||
concatenated += ":";
|
||||
}
|
||||
concatenated += ssn[i].operator String();
|
||||
concatenated += ssn[i].string();
|
||||
}
|
||||
data->concatenated_subpath = concatenated;
|
||||
}
|
||||
|
|
@ -332,14 +356,15 @@ void NodePath::simplify() {
|
|||
if (!data) {
|
||||
return;
|
||||
}
|
||||
_copy_on_write();
|
||||
for (int i = 0; i < data->path.size(); i++) {
|
||||
if (data->path.size() == 1) {
|
||||
break;
|
||||
}
|
||||
if (data->path[i].operator String() == ".") {
|
||||
if (data->path[i].string() == ".") {
|
||||
data->path.remove_at(i);
|
||||
i--;
|
||||
} else if (i > 0 && data->path[i].operator String() == ".." && data->path[i - 1].operator String() != "." && data->path[i - 1].operator String() != "..") {
|
||||
} else if (i > 0 && data->path[i].string() == ".." && data->path[i - 1].string() != "." && data->path[i - 1].string() != "..") {
|
||||
//remove both
|
||||
data->path.remove_at(i - 1);
|
||||
data->path.remove_at(i - 1);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@
|
|||
|
||||
#include <climits>
|
||||
|
||||
// Represents a path to a node or property in a hierarchy of nodes
|
||||
// Note that NodePath is (effectively) const: If you hold a NodePath,
|
||||
// you can expect it to remain unchanged, even if you make copies of
|
||||
// it. This is achieved through copy-on-write (CoW).
|
||||
class [[nodiscard]] NodePath {
|
||||
struct Data {
|
||||
SafeRefCount refcount;
|
||||
|
|
@ -52,6 +56,10 @@ class [[nodiscard]] NodePath {
|
|||
|
||||
void _update_hash_cache() const;
|
||||
|
||||
// Copies the underlying data.
|
||||
// Every non-const function must call this before starting to mutate data.
|
||||
void _copy_on_write();
|
||||
|
||||
public:
|
||||
bool is_absolute() const;
|
||||
int get_name_count() const;
|
||||
|
|
|
|||
45
engine/core/string/optimized_translation.compat.inc
Normal file
45
engine/core/string/optimized_translation.compat.inc
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/**************************************************************************/
|
||||
/* optimized_translation.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
#include "optimized_translation.h"
|
||||
|
||||
#include "core/object/class_db.h"
|
||||
|
||||
void OptimizedTranslation::_generate_bind_compat_119563(const Ref<Translation> &p_from) {
|
||||
generate(p_from);
|
||||
}
|
||||
|
||||
void OptimizedTranslation::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("generate", "from"), &OptimizedTranslation::_generate_bind_compat_119563);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -29,11 +29,13 @@
|
|||
/**************************************************************************/
|
||||
|
||||
#include "optimized_translation.h"
|
||||
#include "optimized_translation.compat.inc"
|
||||
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/templates/pair.h"
|
||||
|
||||
extern "C" {
|
||||
#include "thirdparty/misc/smaz.h"
|
||||
#include <thirdparty/misc/smaz.h>
|
||||
}
|
||||
|
||||
struct CompressedString {
|
||||
|
|
@ -42,11 +44,11 @@ struct CompressedString {
|
|||
int offset = 0;
|
||||
};
|
||||
|
||||
void OptimizedTranslation::generate(const Ref<Translation> &p_from) {
|
||||
bool OptimizedTranslation::generate(const Ref<Translation> &p_from) {
|
||||
// This method compresses a Translation instance.
|
||||
// Right now, it doesn't handle context or plurals.
|
||||
#ifdef TOOLS_ENABLED
|
||||
ERR_FAIL_COND(p_from.is_null());
|
||||
ERR_FAIL_COND_V(p_from.is_null(), false);
|
||||
|
||||
List<StringName> keys;
|
||||
{
|
||||
|
|
@ -54,7 +56,7 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) {
|
|||
p_from->get_message_list(&raw_keys);
|
||||
|
||||
for (const StringName &key : raw_keys) {
|
||||
const String key_str = key.operator String();
|
||||
const String key_str = key.string();
|
||||
int p = key_str.find_char(0x04);
|
||||
if (p == -1) {
|
||||
keys.push_back(key);
|
||||
|
|
@ -83,7 +85,7 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) {
|
|||
|
||||
for (const StringName &E : keys) {
|
||||
//hash string
|
||||
CharString cs = E.operator String().utf8();
|
||||
CharString cs = E.string().utf8();
|
||||
uint32_t h = hash(0, cs.get_data());
|
||||
Pair<int, CharString> p;
|
||||
p.first = idx;
|
||||
|
|
@ -91,7 +93,7 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) {
|
|||
buckets.write[h % size].push_back(p);
|
||||
|
||||
//compress string
|
||||
CharString src_s = p_from->get_message(E).operator String().utf8();
|
||||
CharString src_s = p_from->get_message(E).string().utf8();
|
||||
CompressedString ps;
|
||||
ps.orig_len = src_s.size();
|
||||
ps.offset = total_compression_size;
|
||||
|
|
@ -149,7 +151,7 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) {
|
|||
bucket_table_size += 2 + b.size() * 4;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(bucket_table_size == 0);
|
||||
ERR_FAIL_COND_V(bucket_table_size == 0, false);
|
||||
|
||||
hash_table.resize(size);
|
||||
bucket_table.resize(bucket_table_size);
|
||||
|
|
@ -188,14 +190,17 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) {
|
|||
memcpy(&cw[compressed[i].offset], compressed[i].compressed.get_data(), compressed[i].compressed.size());
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(btindex != bucket_table_size);
|
||||
ERR_FAIL_COND_V(btindex != bucket_table_size, false);
|
||||
set_locale(p_from->get_locale());
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool OptimizedTranslation::_set(const StringName &p_name, const Variant &p_value) {
|
||||
String prop_name = p_name.operator String();
|
||||
String prop_name = p_name.string();
|
||||
if (prop_name == "hash_table") {
|
||||
hash_table = p_value;
|
||||
} else if (prop_name == "bucket_table") {
|
||||
|
|
@ -212,7 +217,7 @@ bool OptimizedTranslation::_set(const StringName &p_name, const Variant &p_value
|
|||
}
|
||||
|
||||
bool OptimizedTranslation::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
String prop_name = p_name.operator String();
|
||||
String prop_name = p_name.string();
|
||||
if (prop_name == "hash_table") {
|
||||
r_ret = hash_table;
|
||||
} else if (prop_name == "bucket_table") {
|
||||
|
|
@ -235,7 +240,7 @@ StringName OptimizedTranslation::get_message(const StringName &p_src_text, const
|
|||
return StringName();
|
||||
}
|
||||
|
||||
CharString str = p_src_text.operator String().utf8();
|
||||
CharString str = p_src_text.string().utf8();
|
||||
uint32_t h = hash(0, str.get_data());
|
||||
|
||||
const int *htr = hash_table.ptr();
|
||||
|
|
@ -332,7 +337,7 @@ void OptimizedTranslation::_get_property_list(List<PropertyInfo> *p_list) const
|
|||
p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "hash_table", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||
p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "bucket_table", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||
p_list->push_back(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "strings", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||
p_list->push_back(PropertyInfo(Variant::OBJECT, "load_from", PROPERTY_HINT_RESOURCE_TYPE, "Translation", PROPERTY_USAGE_EDITOR));
|
||||
p_list->push_back(PropertyInfo(Variant::OBJECT, "load_from", PROPERTY_HINT_RESOURCE_TYPE, Translation::get_class_static(), PROPERTY_USAGE_EDITOR));
|
||||
}
|
||||
|
||||
void OptimizedTranslation::_bind_methods() {
|
||||
|
|
|
|||
|
|
@ -91,11 +91,17 @@ protected:
|
|||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
static void _bind_methods();
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _generate_bind_compat_119563(const Ref<Translation> &p_from);
|
||||
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual StringName get_message(const StringName &p_src_text, const StringName &p_context = "") const override; //overridable for other implementations
|
||||
virtual StringName get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context = "") const override;
|
||||
virtual Vector<String> get_translated_message_list() const override;
|
||||
void generate(const Ref<Translation> &p_from);
|
||||
bool generate(const Ref<Translation> &p_from);
|
||||
|
||||
virtual void get_message_list(List<StringName> *r_messages) const override;
|
||||
virtual int get_message_count() const override;
|
||||
|
|
|
|||
|
|
@ -33,11 +33,17 @@
|
|||
#include "core/core_globals.h"
|
||||
#include "core/os/os.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
static PrintHandlerList *print_handler_list = nullptr;
|
||||
static thread_local bool is_printing = false;
|
||||
|
||||
static void __print_fallback(const String &p_string, bool p_err) {
|
||||
fprintf(p_err ? stderr : stdout, "While attempting to print a message, another message was printed:\n%s\n", p_string.utf8().get_data());
|
||||
static void __print_fallback(const String &p_string, bool p_err, bool p_reentrance) {
|
||||
if (p_reentrance) {
|
||||
fprintf(p_err ? stderr : stdout, "While attempting to print an error, another error was printed:\n");
|
||||
}
|
||||
|
||||
fprintf(p_err ? stderr : stdout, "%s\n", p_string.utf8().get_data());
|
||||
}
|
||||
|
||||
void add_print_handler(PrintHandlerList *p_handler) {
|
||||
|
|
@ -76,8 +82,13 @@ void __print_line(const String &p_string) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!CoreGlobals::print_ready) {
|
||||
__print_fallback(p_string, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_printing) {
|
||||
__print_fallback(p_string, false);
|
||||
__print_fallback(p_string, false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -108,6 +119,7 @@ void __print_line_rich(const String &p_string) {
|
|||
|
||||
String output;
|
||||
int pos = 0;
|
||||
bool in_named_url = false;
|
||||
while (pos <= p_string.length()) {
|
||||
int brk_pos = p_string.find_char('[', pos);
|
||||
|
||||
|
|
@ -156,10 +168,24 @@ void __print_line_rich(const String &p_string) {
|
|||
output += "\u001b[2m";
|
||||
} else if (tag == "/code") {
|
||||
output += "\u001b[22m";
|
||||
} else if (tag.begins_with("url=")) {
|
||||
// Support named URLs using OSC 8 escape codes:
|
||||
// <https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>
|
||||
in_named_url = true;
|
||||
const String url_link = tag.substr(strlen("url="));
|
||||
output += vformat("\u001b]8;;%s\u001b\\", url_link);
|
||||
} else if (tag == "url") {
|
||||
output += "";
|
||||
} else if (tag == "/url") {
|
||||
output += "";
|
||||
if (in_named_url) {
|
||||
output += "\u001b]8;;\u001b\\";
|
||||
in_named_url = false;
|
||||
} else {
|
||||
// While it's legal to close an URL that was never opened using OSC 8 escape codes,
|
||||
// it would result in the code being printed to unsupported terminal emulators
|
||||
// when using unnamed URLs.
|
||||
output += "";
|
||||
}
|
||||
} else if (tag == "center") {
|
||||
output += "\n\t\t\t";
|
||||
} else if (tag == "/center") {
|
||||
|
|
@ -277,8 +303,13 @@ void __print_line_rich(const String &p_string) {
|
|||
}
|
||||
output += "\u001b[0m"; // Reset.
|
||||
|
||||
if (!CoreGlobals::print_ready) {
|
||||
__print_fallback(output, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_printing) {
|
||||
__print_fallback(output, false);
|
||||
__print_fallback(output, false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -299,8 +330,13 @@ void __print_line_rich(const String &p_string) {
|
|||
}
|
||||
|
||||
void print_raw(const String &p_string) {
|
||||
if (!CoreGlobals::print_ready) {
|
||||
__print_fallback(p_string, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_printing) {
|
||||
__print_fallback(p_string, true);
|
||||
__print_fallback(p_string, true, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -316,8 +352,13 @@ void print_error(const String &p_string) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!CoreGlobals::print_ready) {
|
||||
__print_fallback(p_string, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_printing) {
|
||||
__print_fallback(p_string, true);
|
||||
__print_fallback(p_string, true, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,11 +58,11 @@ extern void print_error(const String &p_string);
|
|||
extern bool is_print_verbose_enabled();
|
||||
|
||||
// This version avoids processing the text to be printed until it actually has to be printed, saving some CPU usage.
|
||||
#define print_verbose(m_text) \
|
||||
{ \
|
||||
#define print_verbose(m_text) \
|
||||
{ \
|
||||
if (is_print_verbose_enabled()) { \
|
||||
print_line(m_text); \
|
||||
} \
|
||||
print_line(m_text); \
|
||||
} \
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "core/math/math_funcs_binary.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
|
|
@ -126,7 +127,7 @@ StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::reserve(int p_
|
|||
}
|
||||
|
||||
bool need_copy = string_length > 0 && buffer.is_empty();
|
||||
buffer.resize_uninitialized(next_power_of_2((uint32_t)p_size));
|
||||
buffer.resize_uninitialized(Math::next_power_of_2((uint32_t)p_size));
|
||||
if (need_copy) {
|
||||
memcpy(buffer.ptrw(), short_buffer, string_length * sizeof(char32_t));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "core/os/mutex.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/templates/paged_allocator.h"
|
||||
|
||||
struct StringName::Table {
|
||||
constexpr static uint32_t TABLE_BITS = 16;
|
||||
|
|
|
|||
|
|
@ -125,12 +125,13 @@ public:
|
|||
return (void *)_data;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ operator String() const {
|
||||
if (_data) {
|
||||
return _data->name;
|
||||
}
|
||||
_FORCE_INLINE_ const String &string() const _LIFETIME_BOUND_ {
|
||||
static const String EMPTY;
|
||||
return _data ? _data->name : EMPTY;
|
||||
}
|
||||
|
||||
return String();
|
||||
_FORCE_INLINE_ operator const String &() const _LIFETIME_BOUND_ {
|
||||
return string();
|
||||
}
|
||||
|
||||
struct AlphCompare {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "translation.h"
|
||||
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/os/thread.h"
|
||||
#include "core/string/plural_rules.h"
|
||||
#include "core/string/translation_server.h"
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/io/resource.h"
|
||||
#include "core/object/gdvirtual.gen.inc"
|
||||
#include "core/object/gdvirtual.gen.h"
|
||||
|
||||
class PluralRules;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "translation_domain.h"
|
||||
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/string/translation.h"
|
||||
#include "core/string/translation_server.h"
|
||||
#include "core/variant/typed_array.h"
|
||||
|
|
@ -286,7 +287,7 @@ void TranslationDomain::clear() {
|
|||
}
|
||||
|
||||
const HashSet<Ref<Translation>> TranslationDomain::get_translations() const {
|
||||
return translations;
|
||||
return HashSet<Ref<Translation>>(translations);
|
||||
}
|
||||
|
||||
HashSet<Ref<Translation>> TranslationDomain::find_translations(const String &p_locale, bool p_exact) const {
|
||||
|
|
@ -356,8 +357,9 @@ StringName TranslationDomain::translate(const StringName &p_message, const Strin
|
|||
const String &locale = locale_override.is_empty() ? TranslationServer::get_singleton()->get_locale() : locale_override;
|
||||
StringName res = get_message_from_translations(locale, p_message, p_context);
|
||||
|
||||
const bool &allow_fallback = TranslationServer::get_singleton()->is_fallback_allowed();
|
||||
const String &fallback = TranslationServer::get_singleton()->get_fallback_locale();
|
||||
if (!res && fallback.length() >= 2) {
|
||||
if (!res && allow_fallback && fallback.length() >= 2) {
|
||||
res = get_message_from_translations(fallback, p_message, p_context);
|
||||
}
|
||||
|
||||
|
|
@ -375,8 +377,9 @@ StringName TranslationDomain::translate_plural(const StringName &p_message, cons
|
|||
const String &locale = locale_override.is_empty() ? TranslationServer::get_singleton()->get_locale() : locale_override;
|
||||
StringName res = get_message_from_translations(locale, p_message, p_message_plural, p_n, p_context);
|
||||
|
||||
const bool &allow_fallback = TranslationServer::get_singleton()->is_fallback_allowed();
|
||||
const String &fallback = TranslationServer::get_singleton()->get_fallback_locale();
|
||||
if (!res && fallback.length() >= 2) {
|
||||
if (!res && allow_fallback && fallback.length() >= 2) {
|
||||
res = get_message_from_translations(fallback, p_message, p_message_plural, p_n, p_context);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@
|
|||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
#include "translation_server.h"
|
||||
|
||||
#include "core/object/class_db.h"
|
||||
|
||||
String TranslationServer::_standardize_locale_bind_compat_98972(const String &p_locale) const {
|
||||
return standardize_locale(p_locale, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,8 +31,10 @@
|
|||
#include "translation_server.h"
|
||||
#include "translation_server.compat.inc"
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/os/main_loop.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/locales.h"
|
||||
|
|
@ -496,6 +498,14 @@ String TranslationServer::get_locale() const {
|
|||
return locale;
|
||||
}
|
||||
|
||||
void TranslationServer::set_fallback_allowed(const bool &p_allow) {
|
||||
allow_fallback = p_allow;
|
||||
}
|
||||
|
||||
bool TranslationServer::is_fallback_allowed() const {
|
||||
return allow_fallback;
|
||||
}
|
||||
|
||||
void TranslationServer::set_fallback_locale(const String &p_locale) {
|
||||
fallback = p_locale;
|
||||
}
|
||||
|
|
@ -599,6 +609,7 @@ void TranslationServer::setup() {
|
|||
}
|
||||
|
||||
fallback = GLOBAL_DEF("internationalization/locale/fallback", "en");
|
||||
allow_fallback = GLOBAL_DEF("internationalization/locale/allow_fallback", true);
|
||||
main_domain->set_pseudolocalization_enabled(GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false));
|
||||
main_domain->set_pseudolocalization_accents_enabled(GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true));
|
||||
main_domain->set_pseudolocalization_double_vowels_enabled(GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false));
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class TranslationServer : public Object {
|
|||
GDCLASS(TranslationServer, Object);
|
||||
|
||||
String locale = "en";
|
||||
bool allow_fallback = true;
|
||||
String fallback;
|
||||
|
||||
Ref<TranslationDomain> main_domain;
|
||||
|
|
@ -109,6 +110,8 @@ public:
|
|||
|
||||
void set_locale(const String &p_locale);
|
||||
String get_locale() const;
|
||||
void set_fallback_allowed(const bool &p_allow);
|
||||
bool is_fallback_allowed() const;
|
||||
void set_fallback_locale(const String &p_locale);
|
||||
String get_fallback_locale() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@
|
|||
|
||||
// This file was generated using the `misc/scripts/ucaps_fetch.py` script.
|
||||
|
||||
#define LTU_LEN 1477
|
||||
#define UTL_LEN 1460
|
||||
#define LTU_LEN 1505
|
||||
#define UTL_LEN 1488
|
||||
|
||||
static const int caps_table[LTU_LEN][2] = {
|
||||
{ 0x0061, 0x0041 },
|
||||
|
|
@ -1119,7 +1119,10 @@ static const int caps_table[LTU_LEN][2] = {
|
|||
{ 0xA7C8, 0xA7C7 },
|
||||
{ 0xA7CA, 0xA7C9 },
|
||||
{ 0xA7CD, 0xA7CC },
|
||||
{ 0xA7CF, 0xA7CE },
|
||||
{ 0xA7D1, 0xA7D0 },
|
||||
{ 0xA7D3, 0xA7D2 },
|
||||
{ 0xA7D5, 0xA7D4 },
|
||||
{ 0xA7D7, 0xA7D6 },
|
||||
{ 0xA7D9, 0xA7D8 },
|
||||
{ 0xA7DB, 0xA7DA },
|
||||
|
|
@ -1479,6 +1482,31 @@ static const int caps_table[LTU_LEN][2] = {
|
|||
{ 0x16E7D, 0x16E5D },
|
||||
{ 0x16E7E, 0x16E5E },
|
||||
{ 0x16E7F, 0x16E5F },
|
||||
{ 0x16EBB, 0x16EA0 },
|
||||
{ 0x16EBC, 0x16EA1 },
|
||||
{ 0x16EBD, 0x16EA2 },
|
||||
{ 0x16EBE, 0x16EA3 },
|
||||
{ 0x16EBF, 0x16EA4 },
|
||||
{ 0x16EC0, 0x16EA5 },
|
||||
{ 0x16EC1, 0x16EA6 },
|
||||
{ 0x16EC2, 0x16EA7 },
|
||||
{ 0x16EC3, 0x16EA8 },
|
||||
{ 0x16EC4, 0x16EA9 },
|
||||
{ 0x16EC5, 0x16EAA },
|
||||
{ 0x16EC6, 0x16EAB },
|
||||
{ 0x16EC7, 0x16EAC },
|
||||
{ 0x16EC8, 0x16EAD },
|
||||
{ 0x16EC9, 0x16EAE },
|
||||
{ 0x16ECA, 0x16EAF },
|
||||
{ 0x16ECB, 0x16EB0 },
|
||||
{ 0x16ECC, 0x16EB1 },
|
||||
{ 0x16ECD, 0x16EB2 },
|
||||
{ 0x16ECE, 0x16EB3 },
|
||||
{ 0x16ECF, 0x16EB4 },
|
||||
{ 0x16ED0, 0x16EB5 },
|
||||
{ 0x16ED1, 0x16EB6 },
|
||||
{ 0x16ED2, 0x16EB7 },
|
||||
{ 0x16ED3, 0x16EB8 },
|
||||
{ 0x1E922, 0x1E900 },
|
||||
{ 0x1E923, 0x1E901 },
|
||||
{ 0x1E924, 0x1E902 },
|
||||
|
|
@ -2662,7 +2690,10 @@ static const int reverse_caps_table[UTL_LEN][2] = {
|
|||
{ 0xA7C9, 0xA7CA },
|
||||
{ 0xA7CB, 0x0264 },
|
||||
{ 0xA7CC, 0xA7CD },
|
||||
{ 0xA7CE, 0xA7CF },
|
||||
{ 0xA7D0, 0xA7D1 },
|
||||
{ 0xA7D2, 0xA7D3 },
|
||||
{ 0xA7D4, 0xA7D5 },
|
||||
{ 0xA7D6, 0xA7D7 },
|
||||
{ 0xA7D8, 0xA7D9 },
|
||||
{ 0xA7DA, 0xA7DB },
|
||||
|
|
@ -2942,6 +2973,31 @@ static const int reverse_caps_table[UTL_LEN][2] = {
|
|||
{ 0x16E5D, 0x16E7D },
|
||||
{ 0x16E5E, 0x16E7E },
|
||||
{ 0x16E5F, 0x16E7F },
|
||||
{ 0x16EA0, 0x16EBB },
|
||||
{ 0x16EA1, 0x16EBC },
|
||||
{ 0x16EA2, 0x16EBD },
|
||||
{ 0x16EA3, 0x16EBE },
|
||||
{ 0x16EA4, 0x16EBF },
|
||||
{ 0x16EA5, 0x16EC0 },
|
||||
{ 0x16EA6, 0x16EC1 },
|
||||
{ 0x16EA7, 0x16EC2 },
|
||||
{ 0x16EA8, 0x16EC3 },
|
||||
{ 0x16EA9, 0x16EC4 },
|
||||
{ 0x16EAA, 0x16EC5 },
|
||||
{ 0x16EAB, 0x16EC6 },
|
||||
{ 0x16EAC, 0x16EC7 },
|
||||
{ 0x16EAD, 0x16EC8 },
|
||||
{ 0x16EAE, 0x16EC9 },
|
||||
{ 0x16EAF, 0x16ECA },
|
||||
{ 0x16EB0, 0x16ECB },
|
||||
{ 0x16EB1, 0x16ECC },
|
||||
{ 0x16EB2, 0x16ECD },
|
||||
{ 0x16EB3, 0x16ECE },
|
||||
{ 0x16EB4, 0x16ECF },
|
||||
{ 0x16EB5, 0x16ED0 },
|
||||
{ 0x16EB6, 0x16ED1 },
|
||||
{ 0x16EB7, 0x16ED2 },
|
||||
{ 0x16EB8, 0x16ED3 },
|
||||
{ 0x1E900, 0x1E922 },
|
||||
{ 0x1E901, 0x1E923 },
|
||||
{ 0x1E902, 0x1E924 },
|
||||
|
|
@ -2978,7 +3034,7 @@ static const int reverse_caps_table[UTL_LEN][2] = {
|
|||
{ 0x1E921, 0x1E943 },
|
||||
};
|
||||
|
||||
static int _find_upper(int ch) {
|
||||
inline int _find_upper(int ch) {
|
||||
int low = 0;
|
||||
int high = LTU_LEN - 1;
|
||||
int middle;
|
||||
|
|
@ -2998,7 +3054,7 @@ static int _find_upper(int ch) {
|
|||
return ch;
|
||||
}
|
||||
|
||||
static int _find_lower(int ch) {
|
||||
inline int _find_lower(int ch) {
|
||||
int low = 0;
|
||||
int high = UTL_LEN - 1;
|
||||
int middle;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ STATIC_ASSERT_INCOMPLETE_TYPE(class, Object);
|
|||
#include "core/math/color.h"
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "core/object/object.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/string/string_name.h"
|
||||
|
|
@ -48,7 +47,9 @@ STATIC_ASSERT_INCOMPLETE_TYPE(class, Object);
|
|||
#include "core/variant/variant.h"
|
||||
#include "core/version_generated.gen.h"
|
||||
|
||||
#include "thirdparty/grisu2/grisu2.h"
|
||||
#include <thirdparty/grisu2/grisu2.h>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _CRT_SECURE_NO_WARNINGS // to disable build-time warning which suggested to use strcpy_s instead strcpy
|
||||
|
|
@ -351,7 +352,7 @@ bool operator==(const wchar_t *p_chr, const String &p_str) {
|
|||
// wchar_t is 16-bit
|
||||
return p_str == String::utf16((const char16_t *)p_chr);
|
||||
#else
|
||||
// wchar_t is 32-bi
|
||||
// wchar_t is 32-bit
|
||||
return p_str == (const char32_t *)p_chr;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -365,7 +366,7 @@ bool operator!=(const wchar_t *p_chr, const String &p_str) {
|
|||
// wchar_t is 16-bit
|
||||
return !(p_str == String::utf16((const char16_t *)p_chr));
|
||||
#else
|
||||
// wchar_t is 32-bi
|
||||
// wchar_t is 32-bit
|
||||
return !(p_str == String((const char32_t *)p_chr));
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1657,16 +1658,16 @@ String String::hex_encode_buffer(const uint8_t *p_buffer, int p_len) {
|
|||
Vector<uint8_t> String::hex_decode() const {
|
||||
ERR_FAIL_COND_V_MSG(length() % 2 != 0, Vector<uint8_t>(), "Hexadecimal string of uneven length.");
|
||||
|
||||
#define HEX_TO_BYTE(m_output, m_index) \
|
||||
uint8_t m_output; \
|
||||
c = operator[](m_index); \
|
||||
if (is_digit(c)) { \
|
||||
m_output = c - '0'; \
|
||||
} else if (c >= 'a' && c <= 'f') { \
|
||||
m_output = c - 'a' + 10; \
|
||||
} else if (c >= 'A' && c <= 'F') { \
|
||||
m_output = c - 'A' + 10; \
|
||||
} else { \
|
||||
#define HEX_TO_BYTE(m_output, m_index) \
|
||||
uint8_t m_output; \
|
||||
c = operator[](m_index); \
|
||||
if (is_digit(c)) { \
|
||||
m_output = c - '0'; \
|
||||
} else if (c >= 'a' && c <= 'f') { \
|
||||
m_output = c - 'a' + 10; \
|
||||
} else if (c >= 'A' && c <= 'F') { \
|
||||
m_output = c - 'A' + 10; \
|
||||
} else { \
|
||||
ERR_FAIL_V_MSG(Vector<uint8_t>(), "Invalid hexadecimal character \"" + chr(c) + "\" at index " + m_index + "."); \
|
||||
}
|
||||
|
||||
|
|
@ -2021,11 +2022,7 @@ Error String::append_utf16(const char16_t *p_utf16, int p_len, bool p_default_li
|
|||
int cstr_size = 0;
|
||||
int str_size = 0;
|
||||
|
||||
#ifdef BIG_ENDIAN_ENABLED
|
||||
bool byteswap = p_default_little_endian;
|
||||
#else
|
||||
bool byteswap = !p_default_little_endian;
|
||||
#endif
|
||||
/* HANDLE BOM (Byte Order Mark) */
|
||||
if (p_len < 0 || p_len >= 1) {
|
||||
bool has_bom = false;
|
||||
|
|
@ -5188,10 +5185,13 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
static const String MINUS("-");
|
||||
static const String PLUS("+");
|
||||
|
||||
LocalVector<bool> used_args;
|
||||
used_args.resize_initialized(values.size());
|
||||
String formatted;
|
||||
char32_t *self = (char32_t *)get_data();
|
||||
bool in_format = false;
|
||||
uint64_t value_index = 0;
|
||||
int selected_index = -1;
|
||||
int min_chars = 0;
|
||||
int min_decimals = 0;
|
||||
bool in_decimals = false;
|
||||
|
|
@ -5218,15 +5218,16 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
case 'o': // Octal
|
||||
case 'x': // Hexadecimal (lowercase)
|
||||
case 'X': { // Hexadecimal (uppercase)
|
||||
if (value_index >= values.size()) {
|
||||
uint64_t index = (selected_index >= 0 ? selected_index : value_index);
|
||||
if (index >= values.size()) {
|
||||
return "not enough arguments for format string";
|
||||
}
|
||||
|
||||
if (!values[value_index].is_num()) {
|
||||
if (!values[index].is_num()) {
|
||||
return "a number is required";
|
||||
}
|
||||
|
||||
int64_t value = values[value_index];
|
||||
int64_t value = values[index];
|
||||
int base = 16;
|
||||
bool capitalize = false;
|
||||
switch (c) {
|
||||
|
|
@ -5282,21 +5283,25 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
|
||||
formatted += str;
|
||||
++value_index;
|
||||
if (selected_index == -1) {
|
||||
++value_index;
|
||||
}
|
||||
used_args[index] = true;
|
||||
in_format = false;
|
||||
|
||||
break;
|
||||
}
|
||||
case 'f': { // Float
|
||||
if (value_index >= values.size()) {
|
||||
uint64_t index = (selected_index >= 0 ? selected_index : value_index);
|
||||
if (index >= values.size()) {
|
||||
return "not enough arguments for format string";
|
||||
}
|
||||
|
||||
if (!values[value_index].is_num()) {
|
||||
if (!values[index].is_num()) {
|
||||
return "a number is required";
|
||||
}
|
||||
|
||||
double value = values[value_index];
|
||||
double value = values[index];
|
||||
bool is_negative = std::signbit(value);
|
||||
String str = String::num(Math::abs(value), min_decimals);
|
||||
const bool is_finite = Math::is_finite(value);
|
||||
|
|
@ -5328,17 +5333,21 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
|
||||
formatted += str;
|
||||
++value_index;
|
||||
if (selected_index == -1) {
|
||||
++value_index;
|
||||
}
|
||||
used_args[index] = true;
|
||||
in_format = false;
|
||||
break;
|
||||
}
|
||||
case 'v': { // Vector2/3/4/2i/3i/4i
|
||||
if (value_index >= values.size()) {
|
||||
uint64_t index = (selected_index >= 0 ? selected_index : value_index);
|
||||
if (index >= values.size()) {
|
||||
return "not enough arguments for format string";
|
||||
}
|
||||
|
||||
int count;
|
||||
switch (values[value_index].get_type()) {
|
||||
switch (values[index].get_type()) {
|
||||
case Variant::VECTOR2:
|
||||
case Variant::VECTOR2I: {
|
||||
count = 2;
|
||||
|
|
@ -5356,7 +5365,7 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
}
|
||||
|
||||
Vector4 vec = values[value_index];
|
||||
Vector4 vec = values[index];
|
||||
String str = "(";
|
||||
for (int i = 0; i < count; i++) {
|
||||
double val = vec[i];
|
||||
|
|
@ -5398,16 +5407,20 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
str += ")";
|
||||
|
||||
formatted += str;
|
||||
++value_index;
|
||||
if (selected_index == -1) {
|
||||
++value_index;
|
||||
}
|
||||
used_args[index] = true;
|
||||
in_format = false;
|
||||
break;
|
||||
}
|
||||
case 's': { // String
|
||||
if (value_index >= values.size()) {
|
||||
uint64_t index = (selected_index >= 0 ? selected_index : value_index);
|
||||
if (index >= values.size()) {
|
||||
return "not enough arguments for format string";
|
||||
}
|
||||
|
||||
String str = values[value_index];
|
||||
String str = values[index];
|
||||
// Padding.
|
||||
if (left_justified) {
|
||||
str = str.rpad(min_chars);
|
||||
|
|
@ -5416,19 +5429,23 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
|
||||
formatted += str;
|
||||
++value_index;
|
||||
if (selected_index == -1) {
|
||||
++value_index;
|
||||
}
|
||||
used_args[index] = true;
|
||||
in_format = false;
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
if (value_index >= values.size()) {
|
||||
uint64_t index = (selected_index >= 0 ? selected_index : value_index);
|
||||
if (index >= values.size()) {
|
||||
return "not enough arguments for format string";
|
||||
}
|
||||
|
||||
// Convert to character.
|
||||
String str;
|
||||
if (values[value_index].is_num()) {
|
||||
int value = values[value_index];
|
||||
if (values[index].is_num()) {
|
||||
int value = values[index];
|
||||
if (value < 0) {
|
||||
return "unsigned integer is lower than minimum";
|
||||
} else if (value >= 0xd800 && value <= 0xdfff) {
|
||||
|
|
@ -5436,9 +5453,9 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
} else if (value > 0x10ffff) {
|
||||
return "unsigned integer is greater than maximum";
|
||||
}
|
||||
str = chr(values[value_index]);
|
||||
} else if (values[value_index].get_type() == Variant::STRING) {
|
||||
str = values[value_index];
|
||||
str = chr(values[index]);
|
||||
} else if (values[index].get_type() == Variant::STRING) {
|
||||
str = values[index];
|
||||
if (str.length() != 1) {
|
||||
return "%c requires number or single-character string";
|
||||
}
|
||||
|
|
@ -5454,7 +5471,10 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
|
||||
formatted += str;
|
||||
++value_index;
|
||||
if (selected_index == -1) {
|
||||
++value_index;
|
||||
}
|
||||
used_args[index] = true;
|
||||
in_format = false;
|
||||
break;
|
||||
}
|
||||
|
|
@ -5498,6 +5518,14 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case '$': {
|
||||
if (min_chars > 0) {
|
||||
selected_index = min_chars - 1;
|
||||
}
|
||||
min_chars = 0;
|
||||
pad_with_zeros = false;
|
||||
break;
|
||||
}
|
||||
case '.': { // Float/Vector separator.
|
||||
if (in_decimals) {
|
||||
return "too many decimal points in format";
|
||||
|
|
@ -5508,27 +5536,30 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
|
||||
case '*': { // Dynamic width, based on value.
|
||||
if (value_index >= values.size()) {
|
||||
uint64_t index = (selected_index >= 0 ? selected_index : value_index);
|
||||
if (index >= values.size()) {
|
||||
return "not enough arguments for format string";
|
||||
}
|
||||
|
||||
Variant::Type value_type = values[value_index].get_type();
|
||||
if (!values[value_index].is_num() &&
|
||||
Variant::Type value_type = values[index].get_type();
|
||||
if (!values[index].is_num() &&
|
||||
value_type != Variant::VECTOR2 && value_type != Variant::VECTOR2I &&
|
||||
value_type != Variant::VECTOR3 && value_type != Variant::VECTOR3I &&
|
||||
value_type != Variant::VECTOR4 && value_type != Variant::VECTOR4I) {
|
||||
return "* wants number or vector";
|
||||
}
|
||||
|
||||
int size = values[value_index];
|
||||
int size = values[index];
|
||||
|
||||
if (in_decimals) {
|
||||
min_decimals = size;
|
||||
} else {
|
||||
min_chars = size;
|
||||
}
|
||||
|
||||
++value_index;
|
||||
if (selected_index == -1) {
|
||||
++value_index;
|
||||
}
|
||||
used_args[index] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -5547,6 +5578,7 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
left_justified = false;
|
||||
show_sign = false;
|
||||
in_decimals = false;
|
||||
selected_index = -1;
|
||||
break;
|
||||
default:
|
||||
formatted += c;
|
||||
|
|
@ -5558,8 +5590,10 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
return "incomplete format";
|
||||
}
|
||||
|
||||
if (value_index != values.size()) {
|
||||
return "not all arguments converted during string formatting";
|
||||
for (const bool &b : used_args) {
|
||||
if (!b) {
|
||||
return "not all arguments converted during string formatting";
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
// Note: _GODOT suffix added to header guard to avoid conflict with ICU header.
|
||||
|
||||
#include "core/string/char_utils.h" // IWYU pragma: export
|
||||
#include "core/templates/cowdata.h"
|
||||
#include "core/templates/hashfuncs.h"
|
||||
|
|
@ -175,9 +173,9 @@ class [[nodiscard]] CharStringT {
|
|||
static constexpr T _null = 0;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ T *ptrw() { return _cowdata.ptrw(); }
|
||||
_FORCE_INLINE_ const T *ptr() const { return _cowdata.ptr(); }
|
||||
_FORCE_INLINE_ const T *get_data() const { return size() ? ptr() : &_null; }
|
||||
_FORCE_INLINE_ T *ptrw() _LIFETIME_BOUND_ { return _cowdata.ptrw(); }
|
||||
_FORCE_INLINE_ const T *ptr() const _LIFETIME_BOUND_ { return _cowdata.ptr(); }
|
||||
_FORCE_INLINE_ const T *get_data() const _LIFETIME_BOUND_ { return size() ? ptr() : &_null; }
|
||||
|
||||
// Returns the number of characters in the buffer, including the terminating NUL character.
|
||||
// In most cases, length() should be used instead.
|
||||
|
|
@ -186,8 +184,8 @@ public:
|
|||
_FORCE_INLINE_ int length() const { return size() ? size() - 1 : 0; }
|
||||
_FORCE_INLINE_ bool is_empty() const { return length() == 0; }
|
||||
|
||||
_FORCE_INLINE_ operator Span<T>() const { return Span(ptr(), length()); }
|
||||
_FORCE_INLINE_ Span<T> span() const { return Span(ptr(), length()); }
|
||||
_FORCE_INLINE_ operator Span<T>() const _LIFETIME_BOUND_ { return Span(ptr(), length()); }
|
||||
_FORCE_INLINE_ Span<T> span() const _LIFETIME_BOUND_ { return Span(ptr(), length()); }
|
||||
|
||||
/// Resizes the string. The given size must include the null terminator.
|
||||
/// New characters are not initialized, and should be set by the caller.
|
||||
|
|
@ -295,9 +293,9 @@ public:
|
|||
npos = -1 ///<for "some" compatibility with std::string (npos is a huge value in std::string)
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ char32_t *ptrw() { return _cowdata.ptrw(); }
|
||||
_FORCE_INLINE_ const char32_t *ptr() const { return _cowdata.ptr(); }
|
||||
_FORCE_INLINE_ const char32_t *get_data() const { return size() ? ptr() : &_null; }
|
||||
_FORCE_INLINE_ char32_t *ptrw() _LIFETIME_BOUND_ { return _cowdata.ptrw(); }
|
||||
_FORCE_INLINE_ const char32_t *ptr() const _LIFETIME_BOUND_ { return _cowdata.ptr(); }
|
||||
_FORCE_INLINE_ const char32_t *get_data() const _LIFETIME_BOUND_ { return size() ? ptr() : &_null; }
|
||||
|
||||
// Returns the number of characters in the buffer, including the terminating NUL character.
|
||||
// In most cases, length() should be used instead.
|
||||
|
|
@ -306,8 +304,8 @@ public:
|
|||
_FORCE_INLINE_ int length() const { return size() ? size() - 1 : 0; }
|
||||
_FORCE_INLINE_ bool is_empty() const { return length() == 0; }
|
||||
|
||||
_FORCE_INLINE_ operator Span<char32_t>() const { return Span(ptr(), length()); }
|
||||
_FORCE_INLINE_ Span<char32_t> span() const { return Span(ptr(), length()); }
|
||||
_FORCE_INLINE_ operator Span<char32_t>() const _LIFETIME_BOUND_ { return Span(ptr(), length()); }
|
||||
_FORCE_INLINE_ Span<char32_t> span() const _LIFETIME_BOUND_ { return Span(ptr(), length()); }
|
||||
|
||||
void remove_at(int p_index) { _cowdata.remove_at(p_index); }
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue