Browse Source

Merge pull request #52106 from Paulb23/text-edit-optimisation

Optimise TextEdit get line height and width
Gilles Roudière 4 years ago
parent
commit
321ee7bda1

+ 0 - 1
editor/plugins/script_text_editor.cpp

@@ -158,7 +158,6 @@ void ScriptTextEditor::enable_editor() {
 	editor_enabled = true;
 
 	_enable_code_editor();
-	_set_theme_for_script();
 
 	_validate_script();
 }

+ 320 - 280
modules/text_server_adv/text_server_adv.cpp

@@ -440,266 +440,260 @@ bool TextServerAdvanced::is_locale_right_to_left(const String &p_locale) {
 	}
 }
 
-struct FeatureInfo {
-	int32_t tag;
-	String name;
-};
+static Map<StringName, int32_t> feature_sets;
 
-static FeatureInfo feature_set[] = {
+static void _insert_feature_sets() {
 	// Registered OpenType feature tags.
-	{ HB_TAG('a', 'a', 'l', 't'), "access_all_alternates" },
-	{ HB_TAG('a', 'b', 'v', 'f'), "above_base_forms" },
-	{ HB_TAG('a', 'b', 'v', 'm'), "above_base_mark_positioning" },
-	{ HB_TAG('a', 'b', 'v', 's'), "above_base_substitutions" },
-	{ HB_TAG('a', 'f', 'r', 'c'), "alternative_fractions" },
-	{ HB_TAG('a', 'k', 'h', 'n'), "akhands" },
-	{ HB_TAG('b', 'l', 'w', 'f'), "below_base_forms" },
-	{ HB_TAG('b', 'l', 'w', 'm'), "below_base_mark_positioning" },
-	{ HB_TAG('b', 'l', 'w', 's'), "below_base_substitutions" },
-	{ HB_TAG('c', 'a', 'l', 't'), "contextual_alternates" },
-	{ HB_TAG('c', 'a', 's', 'e'), "case_sensitive_forms" },
-	{ HB_TAG('c', 'c', 'm', 'p'), "glyph_composition" },
-	{ HB_TAG('c', 'f', 'a', 'r'), "conjunct_form_after_ro" },
-	{ HB_TAG('c', 'j', 'c', 't'), "conjunct_forms" },
-	{ HB_TAG('c', 'l', 'i', 'g'), "contextual_ligatures" },
-	{ HB_TAG('c', 'p', 'c', 't'), "centered_cjk_punctuation" },
-	{ HB_TAG('c', 'p', 's', 'p'), "capital_spacing" },
-	{ HB_TAG('c', 's', 'w', 'h'), "contextual_swash" },
-	{ HB_TAG('c', 'u', 'r', 's'), "cursive_positioning" },
-	{ HB_TAG('c', 'v', '0', '1'), "character_variant_01" },
-	{ HB_TAG('c', 'v', '0', '2'), "character_variant_02" },
-	{ HB_TAG('c', 'v', '0', '3'), "character_variant_03" },
-	{ HB_TAG('c', 'v', '0', '4'), "character_variant_04" },
-	{ HB_TAG('c', 'v', '0', '5'), "character_variant_05" },
-	{ HB_TAG('c', 'v', '0', '6'), "character_variant_06" },
-	{ HB_TAG('c', 'v', '0', '7'), "character_variant_07" },
-	{ HB_TAG('c', 'v', '0', '8'), "character_variant_08" },
-	{ HB_TAG('c', 'v', '0', '9'), "character_variant_09" },
-	{ HB_TAG('c', 'v', '1', '0'), "character_variant_10" },
-	{ HB_TAG('c', 'v', '1', '1'), "character_variant_11" },
-	{ HB_TAG('c', 'v', '1', '2'), "character_variant_12" },
-	{ HB_TAG('c', 'v', '1', '3'), "character_variant_13" },
-	{ HB_TAG('c', 'v', '1', '4'), "character_variant_14" },
-	{ HB_TAG('c', 'v', '1', '5'), "character_variant_15" },
-	{ HB_TAG('c', 'v', '1', '6'), "character_variant_16" },
-	{ HB_TAG('c', 'v', '1', '7'), "character_variant_17" },
-	{ HB_TAG('c', 'v', '1', '8'), "character_variant_18" },
-	{ HB_TAG('c', 'v', '1', '9'), "character_variant_19" },
-	{ HB_TAG('c', 'v', '2', '0'), "character_variant_20" },
-	{ HB_TAG('c', 'v', '2', '1'), "character_variant_21" },
-	{ HB_TAG('c', 'v', '2', '2'), "character_variant_22" },
-	{ HB_TAG('c', 'v', '2', '3'), "character_variant_23" },
-	{ HB_TAG('c', 'v', '2', '4'), "character_variant_24" },
-	{ HB_TAG('c', 'v', '2', '5'), "character_variant_25" },
-	{ HB_TAG('c', 'v', '2', '6'), "character_variant_26" },
-	{ HB_TAG('c', 'v', '2', '7'), "character_variant_27" },
-	{ HB_TAG('c', 'v', '2', '8'), "character_variant_28" },
-	{ HB_TAG('c', 'v', '2', '9'), "character_variant_29" },
-	{ HB_TAG('c', 'v', '3', '0'), "character_variant_30" },
-	{ HB_TAG('c', 'v', '3', '1'), "character_variant_31" },
-	{ HB_TAG('c', 'v', '3', '2'), "character_variant_32" },
-	{ HB_TAG('c', 'v', '3', '3'), "character_variant_33" },
-	{ HB_TAG('c', 'v', '3', '4'), "character_variant_34" },
-	{ HB_TAG('c', 'v', '3', '5'), "character_variant_35" },
-	{ HB_TAG('c', 'v', '3', '6'), "character_variant_36" },
-	{ HB_TAG('c', 'v', '3', '7'), "character_variant_37" },
-	{ HB_TAG('c', 'v', '3', '8'), "character_variant_38" },
-	{ HB_TAG('c', 'v', '3', '9'), "character_variant_39" },
-	{ HB_TAG('c', 'v', '4', '0'), "character_variant_40" },
-	{ HB_TAG('c', 'v', '4', '1'), "character_variant_41" },
-	{ HB_TAG('c', 'v', '4', '2'), "character_variant_42" },
-	{ HB_TAG('c', 'v', '4', '3'), "character_variant_43" },
-	{ HB_TAG('c', 'v', '4', '4'), "character_variant_44" },
-	{ HB_TAG('c', 'v', '4', '5'), "character_variant_45" },
-	{ HB_TAG('c', 'v', '4', '6'), "character_variant_46" },
-	{ HB_TAG('c', 'v', '4', '7'), "character_variant_47" },
-	{ HB_TAG('c', 'v', '4', '8'), "character_variant_48" },
-	{ HB_TAG('c', 'v', '4', '9'), "character_variant_49" },
-	{ HB_TAG('c', 'v', '5', '0'), "character_variant_50" },
-	{ HB_TAG('c', 'v', '5', '1'), "character_variant_51" },
-	{ HB_TAG('c', 'v', '5', '2'), "character_variant_52" },
-	{ HB_TAG('c', 'v', '5', '3'), "character_variant_53" },
-	{ HB_TAG('c', 'v', '5', '4'), "character_variant_54" },
-	{ HB_TAG('c', 'v', '5', '5'), "character_variant_55" },
-	{ HB_TAG('c', 'v', '5', '6'), "character_variant_56" },
-	{ HB_TAG('c', 'v', '5', '7'), "character_variant_57" },
-	{ HB_TAG('c', 'v', '5', '8'), "character_variant_58" },
-	{ HB_TAG('c', 'v', '5', '9'), "character_variant_59" },
-	{ HB_TAG('c', 'v', '6', '0'), "character_variant_60" },
-	{ HB_TAG('c', 'v', '6', '1'), "character_variant_61" },
-	{ HB_TAG('c', 'v', '6', '2'), "character_variant_62" },
-	{ HB_TAG('c', 'v', '6', '3'), "character_variant_63" },
-	{ HB_TAG('c', 'v', '6', '4'), "character_variant_64" },
-	{ HB_TAG('c', 'v', '6', '5'), "character_variant_65" },
-	{ HB_TAG('c', 'v', '6', '6'), "character_variant_66" },
-	{ HB_TAG('c', 'v', '6', '7'), "character_variant_67" },
-	{ HB_TAG('c', 'v', '6', '8'), "character_variant_68" },
-	{ HB_TAG('c', 'v', '6', '9'), "character_variant_69" },
-	{ HB_TAG('c', 'v', '7', '0'), "character_variant_70" },
-	{ HB_TAG('c', 'v', '7', '1'), "character_variant_71" },
-	{ HB_TAG('c', 'v', '7', '2'), "character_variant_72" },
-	{ HB_TAG('c', 'v', '7', '3'), "character_variant_73" },
-	{ HB_TAG('c', 'v', '7', '4'), "character_variant_74" },
-	{ HB_TAG('c', 'v', '7', '5'), "character_variant_75" },
-	{ HB_TAG('c', 'v', '7', '6'), "character_variant_76" },
-	{ HB_TAG('c', 'v', '7', '7'), "character_variant_77" },
-	{ HB_TAG('c', 'v', '7', '8'), "character_variant_78" },
-	{ HB_TAG('c', 'v', '7', '9'), "character_variant_79" },
-	{ HB_TAG('c', 'v', '8', '0'), "character_variant_80" },
-	{ HB_TAG('c', 'v', '8', '1'), "character_variant_81" },
-	{ HB_TAG('c', 'v', '8', '2'), "character_variant_82" },
-	{ HB_TAG('c', 'v', '8', '3'), "character_variant_83" },
-	{ HB_TAG('c', 'v', '8', '4'), "character_variant_84" },
-	{ HB_TAG('c', 'v', '8', '5'), "character_variant_85" },
-	{ HB_TAG('c', 'v', '8', '6'), "character_variant_86" },
-	{ HB_TAG('c', 'v', '8', '7'), "character_variant_87" },
-	{ HB_TAG('c', 'v', '8', '8'), "character_variant_88" },
-	{ HB_TAG('c', 'v', '8', '9'), "character_variant_89" },
-	{ HB_TAG('c', 'v', '9', '0'), "character_variant_90" },
-	{ HB_TAG('c', 'v', '9', '1'), "character_variant_91" },
-	{ HB_TAG('c', 'v', '9', '2'), "character_variant_92" },
-	{ HB_TAG('c', 'v', '9', '3'), "character_variant_93" },
-	{ HB_TAG('c', 'v', '9', '4'), "character_variant_94" },
-	{ HB_TAG('c', 'v', '9', '5'), "character_variant_95" },
-	{ HB_TAG('c', 'v', '9', '6'), "character_variant_96" },
-	{ HB_TAG('c', 'v', '9', '7'), "character_variant_97" },
-	{ HB_TAG('c', 'v', '9', '8'), "character_variant_98" },
-	{ HB_TAG('c', 'v', '9', '9'), "character_variant_99" },
-	{ HB_TAG('c', '2', 'p', 'c'), "petite_capitals_from_capitals" },
-	{ HB_TAG('c', '2', 's', 'c'), "small_capitals_from_capitals" },
-	{ HB_TAG('d', 'i', 's', 't'), "distances" },
-	{ HB_TAG('d', 'l', 'i', 'g'), "discretionary_ligatures" },
-	{ HB_TAG('d', 'n', 'o', 'm'), "denominators" },
-	{ HB_TAG('d', 't', 'l', 's'), "dotless_forms" },
-	{ HB_TAG('e', 'x', 'p', 't'), "expert_forms" },
-	{ HB_TAG('f', 'a', 'l', 't'), "final_glyph_on_line_alternates" },
-	{ HB_TAG('f', 'i', 'n', '2'), "terminal_forms_2" },
-	{ HB_TAG('f', 'i', 'n', '3'), "terminal_forms_3" },
-	{ HB_TAG('f', 'i', 'n', 'a'), "terminal_forms" },
-	{ HB_TAG('f', 'l', 'a', 'c'), "flattened_accent_forms" },
-	{ HB_TAG('f', 'r', 'a', 'c'), "fractions" },
-	{ HB_TAG('f', 'w', 'i', 'd'), "full_widths" },
-	{ HB_TAG('h', 'a', 'l', 'f'), "half_forms" },
-	{ HB_TAG('h', 'a', 'l', 'n'), "halant_forms" },
-	{ HB_TAG('h', 'a', 'l', 't'), "alternate_half_widths" },
-	{ HB_TAG('h', 'i', 's', 't'), "historical_forms" },
-	{ HB_TAG('h', 'k', 'n', 'a'), "horizontal_kana_alternates" },
-	{ HB_TAG('h', 'l', 'i', 'g'), "historical_ligatures" },
-	{ HB_TAG('h', 'n', 'g', 'l'), "hangul" },
-	{ HB_TAG('h', 'o', 'j', 'o'), "hojo_kanji_forms" },
-	{ HB_TAG('h', 'w', 'i', 'd'), "half_widths" },
-	{ HB_TAG('i', 'n', 'i', 't'), "initial_forms" },
-	{ HB_TAG('i', 's', 'o', 'l'), "isolated_forms" },
-	{ HB_TAG('i', 't', 'a', 'l'), "italics" },
-	{ HB_TAG('j', 'a', 'l', 't'), "justification_alternates" },
-	{ HB_TAG('j', 'p', '7', '8'), "jis78_forms" },
-	{ HB_TAG('j', 'p', '8', '3'), "jis83_forms" },
-	{ HB_TAG('j', 'p', '9', '0'), "jis90_forms" },
-	{ HB_TAG('j', 'p', '0', '4'), "jis2004_forms" },
-	{ HB_TAG('k', 'e', 'r', 'n'), "kerning" },
-	{ HB_TAG('l', 'f', 'b', 'd'), "left_bounds" },
-	{ HB_TAG('l', 'i', 'g', 'a'), "standard_ligatures" },
-	{ HB_TAG('l', 'j', 'm', 'o'), "leading_jamo_forms" },
-	{ HB_TAG('l', 'n', 'u', 'm'), "lining_figures" },
-	{ HB_TAG('l', 'o', 'c', 'l'), "localized_forms" },
-	{ HB_TAG('l', 't', 'r', 'a'), "left_to_right_alternates" },
-	{ HB_TAG('l', 't', 'r', 'm'), "left_to_right_mirrored_forms" },
-	{ HB_TAG('m', 'a', 'r', 'k'), "mark_positioning" },
-	{ HB_TAG('m', 'e', 'd', '2'), "medial_forms_2" },
-	{ HB_TAG('m', 'e', 'd', 'i'), "medial_forms" },
-	{ HB_TAG('m', 'g', 'r', 'k'), "mathematical_greek" },
-	{ HB_TAG('m', 'k', 'm', 'k'), "mark_to_mark_positioning" },
-	{ HB_TAG('m', 's', 'e', 't'), "mark_positioning_via_substitution" },
-	{ HB_TAG('n', 'a', 'l', 't'), "alternate_annotation_forms" },
-	{ HB_TAG('n', 'l', 'c', 'k'), "nlc_kanji_forms" },
-	{ HB_TAG('n', 'u', 'k', 't'), "nukta_forms" },
-	{ HB_TAG('n', 'u', 'm', 'r'), "numerators" },
-	{ HB_TAG('o', 'n', 'u', 'm'), "oldstyle_figures" },
-	{ HB_TAG('o', 'p', 'b', 'd'), "optical_bounds" },
-	{ HB_TAG('o', 'r', 'd', 'n'), "ordinals" },
-	{ HB_TAG('o', 'r', 'n', 'm'), "ornaments" },
-	{ HB_TAG('p', 'a', 'l', 't'), "proportional_alternate_widths" },
-	{ HB_TAG('p', 'c', 'a', 'p'), "petite_capitals" },
-	{ HB_TAG('p', 'k', 'n', 'a'), "proportional_kana" },
-	{ HB_TAG('p', 'n', 'u', 'm'), "proportional_figures" },
-	{ HB_TAG('p', 'r', 'e', 'f'), "pre_base_forms" },
-	{ HB_TAG('p', 'r', 'e', 's'), "pre_base_substitutions" },
-	{ HB_TAG('p', 's', 't', 'f'), "post_base_forms" },
-	{ HB_TAG('p', 's', 't', 's'), "post_base_substitutions" },
-	{ HB_TAG('p', 'w', 'i', 'd'), "proportional_widths" },
-	{ HB_TAG('q', 'w', 'i', 'd'), "quarter_widths" },
-	{ HB_TAG('r', 'a', 'n', 'd'), "randomize" },
-	{ HB_TAG('r', 'c', 'l', 't'), "required_contextual_alternates" },
-	{ HB_TAG('r', 'k', 'r', 'f'), "rakar_forms" },
-	{ HB_TAG('r', 'l', 'i', 'g'), "required_ligatures" },
-	{ HB_TAG('r', 'p', 'h', 'f'), "reph_forms" },
-	{ HB_TAG('r', 't', 'b', 'd'), "right_bounds" },
-	{ HB_TAG('r', 't', 'l', 'a'), "right_to_left_alternates" },
-	{ HB_TAG('r', 't', 'l', 'm'), "right_to_left_mirrored_forms" },
-	{ HB_TAG('r', 'u', 'b', 'y'), "ruby_notation_forms" },
-	{ HB_TAG('r', 'v', 'r', 'n'), "required_variation_alternates" },
-	{ HB_TAG('s', 'a', 'l', 't'), "stylistic_alternates" },
-	{ HB_TAG('s', 'i', 'n', 'f'), "scientific_inferiors" },
-	{ HB_TAG('s', 'i', 'z', 'e'), "optical_size" },
-	{ HB_TAG('s', 'm', 'c', 'p'), "small_capitals" },
-	{ HB_TAG('s', 'm', 'p', 'l'), "simplified_forms" },
-	{ HB_TAG('s', 's', '0', '1'), "stylistic_set_01" },
-	{ HB_TAG('s', 's', '0', '2'), "stylistic_set_02" },
-	{ HB_TAG('s', 's', '0', '3'), "stylistic_set_03" },
-	{ HB_TAG('s', 's', '0', '4'), "stylistic_set_04" },
-	{ HB_TAG('s', 's', '0', '5'), "stylistic_set_05" },
-	{ HB_TAG('s', 's', '0', '6'), "stylistic_set_06" },
-	{ HB_TAG('s', 's', '0', '7'), "stylistic_set_07" },
-	{ HB_TAG('s', 's', '0', '8'), "stylistic_set_08" },
-	{ HB_TAG('s', 's', '0', '9'), "stylistic_set_09" },
-	{ HB_TAG('s', 's', '1', '0'), "stylistic_set_10" },
-	{ HB_TAG('s', 's', '1', '1'), "stylistic_set_11" },
-	{ HB_TAG('s', 's', '1', '2'), "stylistic_set_12" },
-	{ HB_TAG('s', 's', '1', '3'), "stylistic_set_13" },
-	{ HB_TAG('s', 's', '1', '4'), "stylistic_set_14" },
-	{ HB_TAG('s', 's', '1', '5'), "stylistic_set_15" },
-	{ HB_TAG('s', 's', '1', '6'), "stylistic_set_16" },
-	{ HB_TAG('s', 's', '1', '7'), "stylistic_set_17" },
-	{ HB_TAG('s', 's', '1', '8'), "stylistic_set_18" },
-	{ HB_TAG('s', 's', '1', '9'), "stylistic_set_19" },
-	{ HB_TAG('s', 's', '2', '0'), "stylistic_set_20" },
-	{ HB_TAG('s', 's', 't', 'y'), "math_script_style_alternates" },
-	{ HB_TAG('s', 't', 'c', 'h'), "stretching_glyph_decomposition" },
-	{ HB_TAG('s', 'u', 'b', 's'), "subscript" },
-	{ HB_TAG('s', 'u', 'p', 's'), "superscript" },
-	{ HB_TAG('s', 'w', 's', 'h'), "swash" },
-	{ HB_TAG('t', 'i', 't', 'l'), "titling" },
-	{ HB_TAG('t', 'j', 'm', 'o'), "trailing_jamo_forms" },
-	{ HB_TAG('t', 'n', 'a', 'm'), "traditional_name_forms" },
-	{ HB_TAG('t', 'n', 'u', 'm'), "tabular_figures" },
-	{ HB_TAG('t', 'r', 'a', 'd'), "traditional_forms" },
-	{ HB_TAG('t', 'w', 'i', 'd'), "third_widths" },
-	{ HB_TAG('u', 'n', 'i', 'c'), "unicase" },
-	{ HB_TAG('v', 'a', 'l', 't'), "alternate_vertical_metrics" },
-	{ HB_TAG('v', 'a', 't', 'u'), "vattu_variants" },
-	{ HB_TAG('v', 'e', 'r', 't'), "vertical_writing" },
-	{ HB_TAG('v', 'h', 'a', 'l'), "alternate_vertical_half_metrics" },
-	{ HB_TAG('v', 'j', 'm', 'o'), "vowel_jamo_forms" },
-	{ HB_TAG('v', 'k', 'n', 'a'), "vertical_kana_alternates" },
-	{ HB_TAG('v', 'k', 'r', 'n'), "vertical_kerning" },
-	{ HB_TAG('v', 'p', 'a', 'l'), "proportional_alternate_vertical_metrics" },
-	{ HB_TAG('v', 'r', 't', '2'), "vertical_alternates_and_rotation" },
-	{ HB_TAG('v', 'r', 't', 'r'), "vertical_alternates_for_rotation" },
-	{ HB_TAG('z', 'e', 'r', 'o'), "slashed_zero" },
-	// Registered OpenType variation tags.
-	{ HB_TAG('i', 't', 'a', 'l'), "italic" },
-	{ HB_TAG('o', 'p', 's', 'z'), "optical_size" },
-	{ HB_TAG('s', 'l', 'n', 't'), "slant" },
-	{ HB_TAG('w', 'd', 't', 'h'), "width" },
-	{ HB_TAG('w', 'g', 'h', 't'), "weight" },
-	{ 0, String() },
-};
+	feature_sets.insert("access_all_alternates", HB_TAG('a', 'a', 'l', 't'));
+	feature_sets.insert("above_base_forms", HB_TAG('a', 'b', 'v', 'f'));
+	feature_sets.insert("above_base_mark_positioning", HB_TAG('a', 'b', 'v', 'm'));
+	feature_sets.insert("above_base_substitutions", HB_TAG('a', 'b', 'v', 's'));
+	feature_sets.insert("alternative_fractions", HB_TAG('a', 'f', 'r', 'c'));
+	feature_sets.insert("akhands", HB_TAG('a', 'k', 'h', 'n'));
+	feature_sets.insert("below_base_forms", HB_TAG('b', 'l', 'w', 'f'));
+	feature_sets.insert("below_base_mark_positioning", HB_TAG('b', 'l', 'w', 'm'));
+	feature_sets.insert("below_base_substitutions", HB_TAG('b', 'l', 'w', 's'));
+	feature_sets.insert("contextual_alternates", HB_TAG('c', 'a', 'l', 't'));
+	feature_sets.insert("case_sensitive_forms", HB_TAG('c', 'a', 's', 'e'));
+	feature_sets.insert("glyph_composition", HB_TAG('c', 'c', 'm', 'p'));
+	feature_sets.insert("conjunct_form_after_ro", HB_TAG('c', 'f', 'a', 'r'));
+	feature_sets.insert("conjunct_forms", HB_TAG('c', 'j', 'c', 't'));
+	feature_sets.insert("contextual_ligatures", HB_TAG('c', 'l', 'i', 'g'));
+	feature_sets.insert("centered_cjk_punctuation", HB_TAG('c', 'p', 'c', 't'));
+	feature_sets.insert("capital_spacing", HB_TAG('c', 'p', 's', 'p'));
+	feature_sets.insert("contextual_swash", HB_TAG('c', 's', 'w', 'h'));
+	feature_sets.insert("cursive_positioning", HB_TAG('c', 'u', 'r', 's'));
+	feature_sets.insert("character_variant_01", HB_TAG('c', 'v', '0', '1'));
+	feature_sets.insert("character_variant_02", HB_TAG('c', 'v', '0', '2'));
+	feature_sets.insert("character_variant_03", HB_TAG('c', 'v', '0', '3'));
+	feature_sets.insert("character_variant_04", HB_TAG('c', 'v', '0', '4'));
+	feature_sets.insert("character_variant_05", HB_TAG('c', 'v', '0', '5'));
+	feature_sets.insert("character_variant_06", HB_TAG('c', 'v', '0', '6'));
+	feature_sets.insert("character_variant_07", HB_TAG('c', 'v', '0', '7'));
+	feature_sets.insert("character_variant_08", HB_TAG('c', 'v', '0', '8'));
+	feature_sets.insert("character_variant_09", HB_TAG('c', 'v', '0', '9'));
+	feature_sets.insert("character_variant_10", HB_TAG('c', 'v', '1', '0'));
+	feature_sets.insert("character_variant_11", HB_TAG('c', 'v', '1', '1'));
+	feature_sets.insert("character_variant_12", HB_TAG('c', 'v', '1', '2'));
+	feature_sets.insert("character_variant_13", HB_TAG('c', 'v', '1', '3'));
+	feature_sets.insert("character_variant_14", HB_TAG('c', 'v', '1', '4'));
+	feature_sets.insert("character_variant_15", HB_TAG('c', 'v', '1', '5'));
+	feature_sets.insert("character_variant_16", HB_TAG('c', 'v', '1', '6'));
+	feature_sets.insert("character_variant_17", HB_TAG('c', 'v', '1', '7'));
+	feature_sets.insert("character_variant_18", HB_TAG('c', 'v', '1', '8'));
+	feature_sets.insert("character_variant_19", HB_TAG('c', 'v', '1', '9'));
+	feature_sets.insert("character_variant_20", HB_TAG('c', 'v', '2', '0'));
+	feature_sets.insert("character_variant_21", HB_TAG('c', 'v', '2', '1'));
+	feature_sets.insert("character_variant_22", HB_TAG('c', 'v', '2', '2'));
+	feature_sets.insert("character_variant_23", HB_TAG('c', 'v', '2', '3'));
+	feature_sets.insert("character_variant_24", HB_TAG('c', 'v', '2', '4'));
+	feature_sets.insert("character_variant_25", HB_TAG('c', 'v', '2', '5'));
+	feature_sets.insert("character_variant_26", HB_TAG('c', 'v', '2', '6'));
+	feature_sets.insert("character_variant_27", HB_TAG('c', 'v', '2', '7'));
+	feature_sets.insert("character_variant_28", HB_TAG('c', 'v', '2', '8'));
+	feature_sets.insert("character_variant_29", HB_TAG('c', 'v', '2', '9'));
+	feature_sets.insert("character_variant_30", HB_TAG('c', 'v', '3', '0'));
+	feature_sets.insert("character_variant_31", HB_TAG('c', 'v', '3', '1'));
+	feature_sets.insert("character_variant_32", HB_TAG('c', 'v', '3', '2'));
+	feature_sets.insert("character_variant_33", HB_TAG('c', 'v', '3', '3'));
+	feature_sets.insert("character_variant_34", HB_TAG('c', 'v', '3', '4'));
+	feature_sets.insert("character_variant_35", HB_TAG('c', 'v', '3', '5'));
+	feature_sets.insert("character_variant_36", HB_TAG('c', 'v', '3', '6'));
+	feature_sets.insert("character_variant_37", HB_TAG('c', 'v', '3', '7'));
+	feature_sets.insert("character_variant_38", HB_TAG('c', 'v', '3', '8'));
+	feature_sets.insert("character_variant_39", HB_TAG('c', 'v', '3', '9'));
+	feature_sets.insert("character_variant_40", HB_TAG('c', 'v', '4', '0'));
+	feature_sets.insert("character_variant_41", HB_TAG('c', 'v', '4', '1'));
+	feature_sets.insert("character_variant_42", HB_TAG('c', 'v', '4', '2'));
+	feature_sets.insert("character_variant_43", HB_TAG('c', 'v', '4', '3'));
+	feature_sets.insert("character_variant_44", HB_TAG('c', 'v', '4', '4'));
+	feature_sets.insert("character_variant_45", HB_TAG('c', 'v', '4', '5'));
+	feature_sets.insert("character_variant_46", HB_TAG('c', 'v', '4', '6'));
+	feature_sets.insert("character_variant_47", HB_TAG('c', 'v', '4', '7'));
+	feature_sets.insert("character_variant_48", HB_TAG('c', 'v', '4', '8'));
+	feature_sets.insert("character_variant_49", HB_TAG('c', 'v', '4', '9'));
+	feature_sets.insert("character_variant_50", HB_TAG('c', 'v', '5', '0'));
+	feature_sets.insert("character_variant_51", HB_TAG('c', 'v', '5', '1'));
+	feature_sets.insert("character_variant_52", HB_TAG('c', 'v', '5', '2'));
+	feature_sets.insert("character_variant_53", HB_TAG('c', 'v', '5', '3'));
+	feature_sets.insert("character_variant_54", HB_TAG('c', 'v', '5', '4'));
+	feature_sets.insert("character_variant_55", HB_TAG('c', 'v', '5', '5'));
+	feature_sets.insert("character_variant_56", HB_TAG('c', 'v', '5', '6'));
+	feature_sets.insert("character_variant_57", HB_TAG('c', 'v', '5', '7'));
+	feature_sets.insert("character_variant_58", HB_TAG('c', 'v', '5', '8'));
+	feature_sets.insert("character_variant_59", HB_TAG('c', 'v', '5', '9'));
+	feature_sets.insert("character_variant_60", HB_TAG('c', 'v', '6', '0'));
+	feature_sets.insert("character_variant_61", HB_TAG('c', 'v', '6', '1'));
+	feature_sets.insert("character_variant_62", HB_TAG('c', 'v', '6', '2'));
+	feature_sets.insert("character_variant_63", HB_TAG('c', 'v', '6', '3'));
+	feature_sets.insert("character_variant_64", HB_TAG('c', 'v', '6', '4'));
+	feature_sets.insert("character_variant_65", HB_TAG('c', 'v', '6', '5'));
+	feature_sets.insert("character_variant_66", HB_TAG('c', 'v', '6', '6'));
+	feature_sets.insert("character_variant_67", HB_TAG('c', 'v', '6', '7'));
+	feature_sets.insert("character_variant_68", HB_TAG('c', 'v', '6', '8'));
+	feature_sets.insert("character_variant_69", HB_TAG('c', 'v', '6', '9'));
+	feature_sets.insert("character_variant_70", HB_TAG('c', 'v', '7', '0'));
+	feature_sets.insert("character_variant_71", HB_TAG('c', 'v', '7', '1'));
+	feature_sets.insert("character_variant_72", HB_TAG('c', 'v', '7', '2'));
+	feature_sets.insert("character_variant_73", HB_TAG('c', 'v', '7', '3'));
+	feature_sets.insert("character_variant_74", HB_TAG('c', 'v', '7', '4'));
+	feature_sets.insert("character_variant_75", HB_TAG('c', 'v', '7', '5'));
+	feature_sets.insert("character_variant_76", HB_TAG('c', 'v', '7', '6'));
+	feature_sets.insert("character_variant_77", HB_TAG('c', 'v', '7', '7'));
+	feature_sets.insert("character_variant_78", HB_TAG('c', 'v', '7', '8'));
+	feature_sets.insert("character_variant_79", HB_TAG('c', 'v', '7', '9'));
+	feature_sets.insert("character_variant_80", HB_TAG('c', 'v', '8', '0'));
+	feature_sets.insert("character_variant_81", HB_TAG('c', 'v', '8', '1'));
+	feature_sets.insert("character_variant_82", HB_TAG('c', 'v', '8', '2'));
+	feature_sets.insert("character_variant_83", HB_TAG('c', 'v', '8', '3'));
+	feature_sets.insert("character_variant_84", HB_TAG('c', 'v', '8', '4'));
+	feature_sets.insert("character_variant_85", HB_TAG('c', 'v', '8', '5'));
+	feature_sets.insert("character_variant_86", HB_TAG('c', 'v', '8', '6'));
+	feature_sets.insert("character_variant_87", HB_TAG('c', 'v', '8', '7'));
+	feature_sets.insert("character_variant_88", HB_TAG('c', 'v', '8', '8'));
+	feature_sets.insert("character_variant_89", HB_TAG('c', 'v', '8', '9'));
+	feature_sets.insert("character_variant_90", HB_TAG('c', 'v', '9', '0'));
+	feature_sets.insert("character_variant_91", HB_TAG('c', 'v', '9', '1'));
+	feature_sets.insert("character_variant_92", HB_TAG('c', 'v', '9', '2'));
+	feature_sets.insert("character_variant_93", HB_TAG('c', 'v', '9', '3'));
+	feature_sets.insert("character_variant_94", HB_TAG('c', 'v', '9', '4'));
+	feature_sets.insert("character_variant_95", HB_TAG('c', 'v', '9', '5'));
+	feature_sets.insert("character_variant_96", HB_TAG('c', 'v', '9', '6'));
+	feature_sets.insert("character_variant_97", HB_TAG('c', 'v', '9', '7'));
+	feature_sets.insert("character_variant_98", HB_TAG('c', 'v', '9', '8'));
+	feature_sets.insert("character_variant_99", HB_TAG('c', 'v', '9', '9'));
+	feature_sets.insert("petite_capitals_from_capitals", HB_TAG('c', '2', 'p', 'c'));
+	feature_sets.insert("small_capitals_from_capitals", HB_TAG('c', '2', 's', 'c'));
+	feature_sets.insert("distances", HB_TAG('d', 'i', 's', 't'));
+	feature_sets.insert("discretionary_ligatures", HB_TAG('d', 'l', 'i', 'g'));
+	feature_sets.insert("denominators", HB_TAG('d', 'n', 'o', 'm'));
+	feature_sets.insert("dotless_forms", HB_TAG('d', 't', 'l', 's'));
+	feature_sets.insert("expert_forms", HB_TAG('e', 'x', 'p', 't'));
+	feature_sets.insert("final_glyph_on_line_alternates", HB_TAG('f', 'a', 'l', 't'));
+	feature_sets.insert("terminal_forms_2", HB_TAG('f', 'i', 'n', '2'));
+	feature_sets.insert("terminal_forms_3", HB_TAG('f', 'i', 'n', '3'));
+	feature_sets.insert("terminal_forms", HB_TAG('f', 'i', 'n', 'a'));
+	feature_sets.insert("flattened_accent_forms", HB_TAG('f', 'l', 'a', 'c'));
+	feature_sets.insert("fractions", HB_TAG('f', 'r', 'a', 'c'));
+	feature_sets.insert("full_widths", HB_TAG('f', 'w', 'i', 'd'));
+	feature_sets.insert("half_forms", HB_TAG('h', 'a', 'l', 'f'));
+	feature_sets.insert("halant_forms", HB_TAG('h', 'a', 'l', 'n'));
+	feature_sets.insert("alternate_half_widths", HB_TAG('h', 'a', 'l', 't'));
+	feature_sets.insert("historical_forms", HB_TAG('h', 'i', 's', 't'));
+	feature_sets.insert("horizontal_kana_alternates", HB_TAG('h', 'k', 'n', 'a'));
+	feature_sets.insert("historical_ligatures", HB_TAG('h', 'l', 'i', 'g'));
+	feature_sets.insert("hangul", HB_TAG('h', 'n', 'g', 'l'));
+	feature_sets.insert("hojo_kanji_forms", HB_TAG('h', 'o', 'j', 'o'));
+	feature_sets.insert("half_widths", HB_TAG('h', 'w', 'i', 'd'));
+	feature_sets.insert("initial_forms", HB_TAG('i', 'n', 'i', 't'));
+	feature_sets.insert("isolated_forms", HB_TAG('i', 's', 'o', 'l'));
+	feature_sets.insert("italics", HB_TAG('i', 't', 'a', 'l'));
+	feature_sets.insert("justification_alternates", HB_TAG('j', 'a', 'l', 't'));
+	feature_sets.insert("jis78_forms", HB_TAG('j', 'p', '7', '8'));
+	feature_sets.insert("jis83_forms", HB_TAG('j', 'p', '8', '3'));
+	feature_sets.insert("jis90_forms", HB_TAG('j', 'p', '9', '0'));
+	feature_sets.insert("jis2004_forms", HB_TAG('j', 'p', '0', '4'));
+	feature_sets.insert("kerning", HB_TAG('k', 'e', 'r', 'n'));
+	feature_sets.insert("left_bounds", HB_TAG('l', 'f', 'b', 'd'));
+	feature_sets.insert("standard_ligatures", HB_TAG('l', 'i', 'g', 'a'));
+	feature_sets.insert("leading_jamo_forms", HB_TAG('l', 'j', 'm', 'o'));
+	feature_sets.insert("lining_figures", HB_TAG('l', 'n', 'u', 'm'));
+	feature_sets.insert("localized_forms", HB_TAG('l', 'o', 'c', 'l'));
+	feature_sets.insert("left_to_right_alternates", HB_TAG('l', 't', 'r', 'a'));
+	feature_sets.insert("left_to_right_mirrored_forms", HB_TAG('l', 't', 'r', 'm'));
+	feature_sets.insert("mark_positioning", HB_TAG('m', 'a', 'r', 'k'));
+	feature_sets.insert("medial_forms_2", HB_TAG('m', 'e', 'd', '2'));
+	feature_sets.insert("medial_forms", HB_TAG('m', 'e', 'd', 'i'));
+	feature_sets.insert("mathematical_greek", HB_TAG('m', 'g', 'r', 'k'));
+	feature_sets.insert("mark_to_mark_positioning", HB_TAG('m', 'k', 'm', 'k'));
+	feature_sets.insert("mark_positioning_via_substitution", HB_TAG('m', 's', 'e', 't'));
+	feature_sets.insert("alternate_annotation_forms", HB_TAG('n', 'a', 'l', 't'));
+	feature_sets.insert("nlc_kanji_forms", HB_TAG('n', 'l', 'c', 'k'));
+	feature_sets.insert("nukta_forms", HB_TAG('n', 'u', 'k', 't'));
+	feature_sets.insert("numerators", HB_TAG('n', 'u', 'm', 'r'));
+	feature_sets.insert("oldstyle_figures", HB_TAG('o', 'n', 'u', 'm'));
+	feature_sets.insert("optical_bounds", HB_TAG('o', 'p', 'b', 'd'));
+	feature_sets.insert("ordinals", HB_TAG('o', 'r', 'd', 'n'));
+	feature_sets.insert("ornaments", HB_TAG('o', 'r', 'n', 'm'));
+	feature_sets.insert("proportional_alternate_widths", HB_TAG('p', 'a', 'l', 't'));
+	feature_sets.insert("petite_capitals", HB_TAG('p', 'c', 'a', 'p'));
+	feature_sets.insert("proportional_kana", HB_TAG('p', 'k', 'n', 'a'));
+	feature_sets.insert("proportional_figures", HB_TAG('p', 'n', 'u', 'm'));
+	feature_sets.insert("pre_base_forms", HB_TAG('p', 'r', 'e', 'f'));
+	feature_sets.insert("pre_base_substitutions", HB_TAG('p', 'r', 'e', 's'));
+	feature_sets.insert("post_base_forms", HB_TAG('p', 's', 't', 'f'));
+	feature_sets.insert("post_base_substitutions", HB_TAG('p', 's', 't', 's'));
+	feature_sets.insert("proportional_widths", HB_TAG('p', 'w', 'i', 'd'));
+	feature_sets.insert("quarter_widths", HB_TAG('q', 'w', 'i', 'd'));
+	feature_sets.insert("randomize", HB_TAG('r', 'a', 'n', 'd'));
+	feature_sets.insert("required_contextual_alternates", HB_TAG('r', 'c', 'l', 't'));
+	feature_sets.insert("rakar_forms", HB_TAG('r', 'k', 'r', 'f'));
+	feature_sets.insert("required_ligatures", HB_TAG('r', 'l', 'i', 'g'));
+	feature_sets.insert("reph_forms", HB_TAG('r', 'p', 'h', 'f'));
+	feature_sets.insert("right_bounds", HB_TAG('r', 't', 'b', 'd'));
+	feature_sets.insert("right_to_left_alternates", HB_TAG('r', 't', 'l', 'a'));
+	feature_sets.insert("right_to_left_mirrored_forms", HB_TAG('r', 't', 'l', 'm'));
+	feature_sets.insert("ruby_notation_forms", HB_TAG('r', 'u', 'b', 'y'));
+	feature_sets.insert("required_variation_alternates", HB_TAG('r', 'v', 'r', 'n'));
+	feature_sets.insert("stylistic_alternates", HB_TAG('s', 'a', 'l', 't'));
+	feature_sets.insert("scientific_inferiors", HB_TAG('s', 'i', 'n', 'f'));
+	feature_sets.insert("optical_size", HB_TAG('s', 'i', 'z', 'e'));
+	feature_sets.insert("small_capitals", HB_TAG('s', 'm', 'c', 'p'));
+	feature_sets.insert("simplified_forms", HB_TAG('s', 'm', 'p', 'l'));
+	feature_sets.insert("stylistic_set_01", HB_TAG('s', 's', '0', '1'));
+	feature_sets.insert("stylistic_set_02", HB_TAG('s', 's', '0', '2'));
+	feature_sets.insert("stylistic_set_03", HB_TAG('s', 's', '0', '3'));
+	feature_sets.insert("stylistic_set_04", HB_TAG('s', 's', '0', '4'));
+	feature_sets.insert("stylistic_set_05", HB_TAG('s', 's', '0', '5'));
+	feature_sets.insert("stylistic_set_06", HB_TAG('s', 's', '0', '6'));
+	feature_sets.insert("stylistic_set_07", HB_TAG('s', 's', '0', '7'));
+	feature_sets.insert("stylistic_set_08", HB_TAG('s', 's', '0', '8'));
+	feature_sets.insert("stylistic_set_09", HB_TAG('s', 's', '0', '9'));
+	feature_sets.insert("stylistic_set_10", HB_TAG('s', 's', '1', '0'));
+	feature_sets.insert("stylistic_set_11", HB_TAG('s', 's', '1', '1'));
+	feature_sets.insert("stylistic_set_12", HB_TAG('s', 's', '1', '2'));
+	feature_sets.insert("stylistic_set_13", HB_TAG('s', 's', '1', '3'));
+	feature_sets.insert("stylistic_set_14", HB_TAG('s', 's', '1', '4'));
+	feature_sets.insert("stylistic_set_15", HB_TAG('s', 's', '1', '5'));
+	feature_sets.insert("stylistic_set_16", HB_TAG('s', 's', '1', '6'));
+	feature_sets.insert("stylistic_set_17", HB_TAG('s', 's', '1', '7'));
+	feature_sets.insert("stylistic_set_18", HB_TAG('s', 's', '1', '8'));
+	feature_sets.insert("stylistic_set_19", HB_TAG('s', 's', '1', '9'));
+	feature_sets.insert("stylistic_set_20", HB_TAG('s', 's', '2', '0'));
+	feature_sets.insert("math_script_style_alternates", HB_TAG('s', 's', 't', 'y'));
+	feature_sets.insert("stretching_glyph_decomposition", HB_TAG('s', 't', 'c', 'h'));
+	feature_sets.insert("subscript", HB_TAG('s', 'u', 'b', 's'));
+	feature_sets.insert("superscript", HB_TAG('s', 'u', 'p', 's'));
+	feature_sets.insert("swash", HB_TAG('s', 'w', 's', 'h'));
+	feature_sets.insert("titling", HB_TAG('t', 'i', 't', 'l'));
+	feature_sets.insert("trailing_jamo_forms", HB_TAG('t', 'j', 'm', 'o'));
+	feature_sets.insert("traditional_name_forms", HB_TAG('t', 'n', 'a', 'm'));
+	feature_sets.insert("tabular_figures", HB_TAG('t', 'n', 'u', 'm'));
+	feature_sets.insert("traditional_forms", HB_TAG('t', 'r', 'a', 'd'));
+	feature_sets.insert("third_widths", HB_TAG('t', 'w', 'i', 'd'));
+	feature_sets.insert("unicase", HB_TAG('u', 'n', 'i', 'c'));
+	feature_sets.insert("alternate_vertical_metrics", HB_TAG('v', 'a', 'l', 't'));
+	feature_sets.insert("vattu_variants", HB_TAG('v', 'a', 't', 'u'));
+	feature_sets.insert("vertical_writing", HB_TAG('v', 'e', 'r', 't'));
+	feature_sets.insert("alternate_vertical_half_metrics", HB_TAG('v', 'h', 'a', 'l'));
+	feature_sets.insert("vowel_jamo_forms", HB_TAG('v', 'j', 'm', 'o'));
+	feature_sets.insert("vertical_kana_alternates", HB_TAG('v', 'k', 'n', 'a'));
+	feature_sets.insert("vertical_kerning", HB_TAG('v', 'k', 'r', 'n'));
+	feature_sets.insert("proportional_alternate_vertical_metrics", HB_TAG('v', 'p', 'a', 'l'));
+	feature_sets.insert("vertical_alternates_and_rotation", HB_TAG('v', 'r', 't', '2'));
+	feature_sets.insert("vertical_alternates_for_rotation", HB_TAG('v', 'r', 't', 'r'));
+	feature_sets.insert("slashed_zero", HB_TAG('z', 'e', 'r', 'o'));
+	// Registered OpenType variation tag.
+	feature_sets.insert("italic", HB_TAG('i', 't', 'a', 'l'));
+	feature_sets.insert("optical_size", HB_TAG('o', 'p', 's', 'z'));
+	feature_sets.insert("slant", HB_TAG('s', 'l', 'n', 't'));
+	feature_sets.insert("width", HB_TAG('w', 'd', 't', 'h'));
+	feature_sets.insert("weight", HB_TAG('w', 'g', 'h', 't'));
+}
 
 int32_t TextServerAdvanced::name_to_tag(const String &p_name) const {
-	for (int i = 0; feature_set[i].tag != 0; i++) {
-		if (feature_set[i].name == p_name) {
-			return feature_set[i].tag;
-		}
+	if (feature_sets.has(p_name)) {
+		return feature_sets[p_name];
 	}
 
 	// No readable name, use tag string.
@@ -707,9 +701,9 @@ int32_t TextServerAdvanced::name_to_tag(const String &p_name) const {
 }
 
 String TextServerAdvanced::tag_to_name(int32_t p_tag) const {
-	for (int i = 0; feature_set[i].tag != 0; i++) {
-		if (feature_set[i].tag == p_tag) {
-			return feature_set[i].name;
+	for (const KeyValue<StringName, int32_t> &E : feature_sets) {
+		if (E.value == p_tag) {
+			return E.key;
 		}
 	}
 
@@ -4633,30 +4627,76 @@ real_t TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) con
 }
 
 struct num_system_data {
-	String lang;
+	Set<String> lang;
 	String digits;
 	String percent_sign;
 	String exp;
 };
 
 static num_system_data num_systems[]{
-	{ "ar,ar_AR,ar_BH,ar_DJ,ar_EG,ar_ER,ar_IL,ar_IQ,ar_JO,ar_KM,ar_KW,ar_LB,ar_MR,ar_OM,ar_PS,ar_QA,ar_SA,ar_SD,ar_SO,ar_SS,ar_SY,ar_TD,ar_YE", U"٠١٢٣٤٥٦٧٨٩٫", U"٪", U"اس" },
-	{ "fa,ks,pa_Arab,ps,ug,ur_IN,ur,uz_Arab", U"۰۱۲۳۴۵۶۷۸۹٫", U"٪", U"اس" },
-	{ "as,bn,mni", U"০১২৩৪৫৬৭৮৯.", U"%", U"e" },
-	{ "mr,ne", U"०१२३४५६७८९.", U"%", U"e" },
-	{ "dz", U"༠༡༢༣༤༥༦༧༨༩.", U"%", U"e" },
-	{ "sat", U"᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙.", U"%", U"e" },
-	{ "my", U"၀၁၂၃၄၅၆၇၈၉.", U"%", U"e" },
-	{ String(), String(), String(), String() },
+	{ Set<String>(), U"٠١٢٣٤٥٦٧٨٩٫", U"٪", U"اس" },
+	{ Set<String>(), U"۰۱۲۳۴۵۶۷۸۹٫", U"٪", U"اس" },
+	{ Set<String>(), U"০১২৩৪৫৬৭৮৯.", U"%", U"e" },
+	{ Set<String>(), U"०१२३४५६७८९.", U"%", U"e" },
+	{ Set<String>(), U"༠༡༢༣༤༥༦༧༨༩.", U"%", U"e" },
+	{ Set<String>(), U"᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙.", U"%", U"e" },
+	{ Set<String>(), U"၀၁၂၃၄၅၆၇၈၉.", U"%", U"e" },
+	{ Set<String>(), String(), String(), String() },
 };
 
+static void _insert_num_systems_lang() {
+	num_systems[0].lang.insert(StringName("ar"));
+	num_systems[0].lang.insert(StringName("ar_AR"));
+	num_systems[0].lang.insert(StringName("ar_BH"));
+	num_systems[0].lang.insert(StringName("ar_DJ"));
+	num_systems[0].lang.insert(StringName("ar_EG"));
+	num_systems[0].lang.insert(StringName("ar_ER"));
+	num_systems[0].lang.insert(StringName("ar_IL"));
+	num_systems[0].lang.insert(StringName("ar_IQ"));
+	num_systems[0].lang.insert(StringName("ar_JO"));
+	num_systems[0].lang.insert(StringName("ar_KM"));
+	num_systems[0].lang.insert(StringName("ar_KW"));
+	num_systems[0].lang.insert(StringName("ar_LB"));
+	num_systems[0].lang.insert(StringName("ar_MR"));
+	num_systems[0].lang.insert(StringName("ar_OM"));
+	num_systems[0].lang.insert(StringName("ar_PS"));
+	num_systems[0].lang.insert(StringName("ar_QA"));
+	num_systems[0].lang.insert(StringName("ar_SA"));
+	num_systems[0].lang.insert(StringName("ar_SD"));
+	num_systems[0].lang.insert(StringName("ar_SO"));
+	num_systems[0].lang.insert(StringName("ar_SS"));
+	num_systems[0].lang.insert(StringName("ar_SY"));
+	num_systems[0].lang.insert(StringName("ar_TD"));
+	num_systems[0].lang.insert(StringName("ar_YE"));
+
+	num_systems[1].lang.insert(StringName("fa"));
+	num_systems[1].lang.insert(StringName("ks"));
+	num_systems[1].lang.insert(StringName("pa_Arab"));
+	num_systems[1].lang.insert(StringName("ug"));
+	num_systems[1].lang.insert(StringName("ur_IN"));
+	num_systems[1].lang.insert(StringName("ur"));
+	num_systems[1].lang.insert(StringName("uz_Arab"));
+
+	num_systems[2].lang.insert(StringName("as"));
+	num_systems[2].lang.insert(StringName("bn"));
+	num_systems[2].lang.insert(StringName("mni"));
+
+	num_systems[3].lang.insert(StringName("mr"));
+	num_systems[3].lang.insert(StringName("ne"));
+
+	num_systems[4].lang.insert(StringName("dz"));
+
+	num_systems[5].lang.insert(StringName("sat"));
+
+	num_systems[6].lang.insert(StringName("my"));
+}
+
 String TextServerAdvanced::format_number(const String &p_string, const String &p_language) const {
-	String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+	const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
 
 	String res = p_string;
-	for (int i = 0; num_systems[i].lang != String(); i++) {
-		Vector<String> langs = num_systems[i].lang.split(",");
-		if (langs.has(lang)) {
+	for (int i = 0; num_systems[i].lang.size() != 0; i++) {
+		if (num_systems[i].lang.has(lang)) {
 			if (num_systems[i].digits == String()) {
 				return p_string;
 			}
@@ -4677,12 +4717,11 @@ String TextServerAdvanced::format_number(const String &p_string, const String &p
 }
 
 String TextServerAdvanced::parse_number(const String &p_string, const String &p_language) const {
-	String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+	const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
 
 	String res = p_string;
-	for (int i = 0; num_systems[i].lang != String(); i++) {
-		Vector<String> langs = num_systems[i].lang.split(",");
-		if (langs.has(lang)) {
+	for (int i = 0; num_systems[i].lang.size() != 0; i++) {
+		if (num_systems[i].lang.has(lang)) {
 			if (num_systems[i].digits == String()) {
 				return p_string;
 			}
@@ -4706,11 +4745,10 @@ String TextServerAdvanced::parse_number(const String &p_string, const String &p_
 }
 
 String TextServerAdvanced::percent_sign(const String &p_language) const {
-	String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+	const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
 
-	for (int i = 0; num_systems[i].lang != String(); i++) {
-		Vector<String> langs = num_systems[i].lang.split(",");
-		if (langs.has(lang)) {
+	for (int i = 0; num_systems[i].lang.size() != 0; i++) {
+		if (num_systems[i].lang.has(lang)) {
 			if (num_systems[i].percent_sign == String()) {
 				return "%";
 			}
@@ -4730,6 +4768,8 @@ void TextServerAdvanced::register_server() {
 }
 
 TextServerAdvanced::TextServerAdvanced() {
+	_insert_num_systems_lang();
+	_insert_feature_sets();
 	hb_bmp_create_font_funcs();
 }
 

+ 115 - 43
scene/gui/text_edit.cpp

@@ -37,6 +37,7 @@
 #include "core/object/script_language.h"
 #include "core/os/keyboard.h"
 #include "core/os/os.h"
+#include "core/string/string_builder.h"
 #include "core/string/translation.h"
 
 #include "scene/main/window.h"
@@ -78,7 +79,11 @@ void TextEdit::Text::set_font_size(int p_font_size) {
 }
 
 void TextEdit::Text::set_tab_size(int p_tab_size) {
+	if (tab_size == p_tab_size) {
+		return;
+	}
 	tab_size = p_tab_size;
+	tab_size_dirty = true;
 }
 
 int TextEdit::Text::get_tab_size() const {
@@ -118,10 +123,8 @@ int TextEdit::Text::get_line_width(int p_line, int p_wrap_index) const {
 	return text[p_line].data_buf->get_size().x;
 }
 
-int TextEdit::Text::get_line_height(int p_line, int p_wrap_index) const {
-	ERR_FAIL_INDEX_V(p_line, text.size(), 0);
-
-	return text[p_line].data_buf->get_line_size(p_wrap_index).y;
+int TextEdit::Text::get_line_height() const {
+	return line_height;
 }
 
 void TextEdit::Text::set_width(float p_width) {
@@ -153,6 +156,36 @@ _FORCE_INLINE_ const String &TextEdit::Text::operator[](int p_line) const {
 	return text[p_line].data;
 }
 
+void TextEdit::Text::_calculate_line_height() {
+	int height = 0;
+	for (int i = 0; i < text.size(); i++) {
+		// Found another line with the same height...nothing to update.
+		if (text[i].height == line_height) {
+			height = line_height;
+			break;
+		}
+		height = MAX(height, text[i].height);
+	}
+	line_height = height;
+}
+
+void TextEdit::Text::_calculate_max_line_width() {
+	int width = 0;
+	for (int i = 0; i < text.size(); i++) {
+		if (is_hidden(i)) {
+			continue;
+		}
+
+		// Found another line with the same width...nothing to update.
+		if (text[i].width == max_width) {
+			width = max_width;
+			break;
+		}
+		width = MAX(width, text[i].width);
+	}
+	max_width = width;
+}
+
 void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Vector<Vector2i> &p_bidi_override) {
 	ERR_FAIL_INDEX(p_line, text.size());
 
@@ -182,17 +215,54 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_
 		tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size);
 		text.write[p_line].data_buf->tab_align(tabs);
 	}
+
+	// Update height.
+	const int old_height = text.write[p_line].height;
+	const int wrap_amount = get_line_wrap_amount(p_line);
+	int height = font->get_height(font_size);
+	for (int i = 0; i <= wrap_amount; i++) {
+		height = MAX(height, text[p_line].data_buf->get_line_size(i).y);
+	}
+	text.write[p_line].height = height;
+
+	// If this line has shrunk, this may no longer the the tallest line.
+	if (old_height == line_height && height < line_height) {
+		_calculate_line_height();
+	} else {
+		line_height = MAX(height, line_height);
+	}
+
+	// Update width.
+	const int old_width = text.write[p_line].width;
+	int width = get_line_width(p_line);
+	text.write[p_line].width = width;
+
+	// If this line has shrunk, this may no longer the the longest line.
+	if (old_width == max_width && width < max_width) {
+		_calculate_max_line_width();
+	} else if (!is_hidden(p_line)) {
+		max_width = MAX(width, max_width);
+	}
 }
 
 void TextEdit::Text::invalidate_all_lines() {
 	for (int i = 0; i < text.size(); i++) {
 		text.write[i].data_buf->set_width(width);
-		if (tab_size > 0) {
-			Vector<float> tabs;
-			tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size);
-			text.write[i].data_buf->tab_align(tabs);
+		if (tab_size_dirty) {
+			if (tab_size > 0) {
+				Vector<float> tabs;
+				tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size);
+				text.write[i].data_buf->tab_align(tabs);
+			}
+			// Tabs have changes, force width update.
+			text.write[i].width = get_line_width(i);
 		}
 	}
+
+	if (tab_size_dirty) {
+		_calculate_max_line_width();
+		tab_size_dirty = false;
+	}
 }
 
 void TextEdit::Text::invalidate_all() {
@@ -211,16 +281,8 @@ void TextEdit::Text::clear() {
 	insert(0, "", Vector<Vector2i>());
 }
 
-int TextEdit::Text::get_max_width(bool p_exclude_hidden) const {
-	// Quite some work, but should be fast enough.
-
-	int max = 0;
-	for (int i = 0; i < text.size(); i++) {
-		if (!p_exclude_hidden || !is_hidden(i)) {
-			max = MAX(max, get_line_width(i));
-		}
-	}
-	return max;
+int TextEdit::Text::get_max_width() const {
+	return max_width;
 }
 
 void TextEdit::Text::set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override) {
@@ -243,7 +305,20 @@ void TextEdit::Text::insert(int p_at, const String &p_text, const Vector<Vector2
 }
 
 void TextEdit::Text::remove(int p_at) {
+	int height = text[p_at].height;
+	int width = text[p_at].width;
+
 	text.remove(p_at);
+
+	// If this is the tallest line, we need to get the next tallest.
+	if (height == line_height) {
+		_calculate_line_height();
+	}
+
+	// If this is the longest line, we need to get the next longest.
+	if (width == max_width) {
+		_calculate_max_line_width();
+	}
 }
 
 void TextEdit::Text::add_gutter(int p_at) {
@@ -293,7 +368,7 @@ void TextEdit::_notification(int p_what) {
 		case NOTIFICATION_VISIBILITY_CHANGED: {
 			if (is_visible()) {
 				call_deferred(SNAME("_update_scrollbars"));
-				call_deferred(SNAME("_update_wrap_at"));
+				call_deferred(SNAME("_update_wrap_at_column"));
 			}
 		} break;
 		case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
@@ -644,8 +719,9 @@ void TextEdit::_notification(int p_what) {
 						int characters = 0;
 						int tabs = 0;
 						for (int j = 0; j < str.length(); j++) {
-							if (color_map.has(last_wrap_column + j)) {
-								current_color = color_map[last_wrap_column + j].get("color");
+							const Variant *color_data = color_map.getptr(last_wrap_column + j);
+							if (color_data != nullptr) {
+								current_color = (color_data->operator Dictionary()).get("color", font_color);
 								if (!editable) {
 									current_color.a = font_readonly_color.a;
 								}
@@ -1023,8 +1099,9 @@ void TextEdit::_notification(int p_what) {
 						char_ofs = 0;
 					}
 					for (int j = 0; j < gl_size; j++) {
-						if (color_map.has(glyphs[j].start)) {
-							current_color = color_map[glyphs[j].start].get("color");
+						const Variant *color_data = color_map.getptr(glyphs[j].start);
+						if (color_data != nullptr) {
+							current_color = (color_data->operator Dictionary()).get("color", font_color);
 							if (!editable && current_color.a > font_readonly_color.a) {
 								current_color.a = font_readonly_color.a;
 							}
@@ -2548,15 +2625,15 @@ void TextEdit::set_text(const String &p_text) {
 }
 
 String TextEdit::get_text() const {
-	String longthing;
-	int len = text.size();
-	for (int i = 0; i < len; i++) {
-		longthing += text[i];
-		if (i != len - 1) {
-			longthing += "\n";
+	StringBuilder ret_text;
+	const int text_size = text.size();
+	for (int i = 0; i < text_size; i++) {
+		ret_text += text[i];
+		if (i != text_size - 1) {
+			ret_text += "\n";
 		}
 	}
-	return longthing;
+	return ret_text.as_string();
 }
 
 int TextEdit::get_line_count() const {
@@ -2592,13 +2669,7 @@ int TextEdit::get_line_width(int p_line, int p_wrap_index) const {
 }
 
 int TextEdit::get_line_height() const {
-	int height = font->get_height(font_size);
-	for (int i = 0; i < text.size(); i++) {
-		for (int j = 0; j <= text.get_line_wrap_amount(i); j++) {
-			height = MAX(height, text.get_line_height(i, j));
-		}
-	}
-	return height + line_spacing;
+	return text.get_line_height() + line_spacing;
 }
 
 int TextEdit::get_indent_level(int p_line) const {
@@ -4151,6 +4222,9 @@ TextEdit::GutterType TextEdit::get_gutter_type(int p_gutter) const {
 
 void TextEdit::set_gutter_width(int p_gutter, int p_width) {
 	ERR_FAIL_INDEX(p_gutter, gutters.size());
+	if (gutters[p_gutter].width == p_width) {
+		return;
+	}
 	gutters.write[p_gutter].width = p_width;
 	_update_gutter_width();
 }
@@ -4166,6 +4240,9 @@ int TextEdit::get_total_gutter_width() const {
 
 void TextEdit::set_gutter_draw(int p_gutter, bool p_draw) {
 	ERR_FAIL_INDEX(p_gutter, gutters.size());
+	if (gutters[p_gutter].draw == p_draw) {
+		return;
+	}
 	gutters.write[p_gutter].draw = p_draw;
 	_update_gutter_width();
 }
@@ -5458,7 +5535,7 @@ void TextEdit::_update_scrollbars() {
 	}
 
 	int visible_width = size.width - style_normal->get_minimum_size().width;
-	int total_width = text.get_max_width(true) + vmin.x + gutters_width + gutter_padding;
+	int total_width = text.get_max_width() + vmin.x + gutters_width + gutter_padding;
 
 	if (draw_minimap) {
 		total_width += minimap_width;
@@ -5910,8 +5987,6 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
 		text.set_hidden(p_line, false);
 	}
 
-	text.invalidate_cache(p_line);
-
 	r_end_line = p_line + substrings.size() - 1;
 	r_end_column = text[r_end_line].length() - postinsert_text.length();
 
@@ -5968,8 +6043,6 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
 	}
 	text.set(p_from_line, pre_text + post_text, structured_text_parser(st_parser, st_args, pre_text + post_text));
 
-	text.invalidate_cache(p_from_line);
-
 	if (!text_changed_dirty && !setting_text) {
 		if (is_inside_tree()) {
 			MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
@@ -5986,7 +6059,6 @@ TextEdit::TextEdit() {
 	set_default_cursor_shape(CURSOR_IBEAM);
 
 	text.set_tab_size(text.get_tab_size());
-	text.clear();
 
 	h_scroll = memnew(HScrollBar);
 	v_scroll = memnew(VScrollBar);

+ 18 - 3
scene/gui/text_edit.h

@@ -144,6 +144,8 @@ private:
 
 			Color background_color = Color(0, 0, 0, 0);
 			bool hidden = false;
+			int height = 0;
+			int width = 0;
 
 			Line() {
 				data_buf.instantiate();
@@ -152,6 +154,7 @@ private:
 
 	private:
 		bool is_dirty = false;
+		bool tab_size_dirty = false;
 
 		mutable Vector<Line> text;
 		Ref<Font> font;
@@ -162,11 +165,16 @@ private:
 		TextServer::Direction direction = TextServer::DIRECTION_AUTO;
 		bool draw_control_chars = false;
 
+		int line_height = -1;
+		int max_width = -1;
 		int width = -1;
 
 		int tab_size = 4;
 		int gutter_count = 0;
 
+		void _calculate_line_height();
+		void _calculate_max_line_width();
+
 	public:
 		void set_tab_size(int p_tab_size);
 		int get_tab_size() const;
@@ -176,9 +184,9 @@ private:
 		void set_direction_and_language(TextServer::Direction p_direction, const String &p_language);
 		void set_draw_control_chars(bool p_draw_control_chars);
 
-		int get_line_height(int p_line, int p_wrap_index) const;
+		int get_line_height() const;
 		int get_line_width(int p_line, int p_wrap_index = -1) const;
-		int get_max_width(bool p_exclude_hidden = false) const;
+		int get_max_width() const;
 
 		void set_width(float p_width);
 		int get_line_wrap_amount(int p_line) const;
@@ -187,7 +195,14 @@ private:
 		const Ref<TextParagraph> get_line_data(int p_line) const;
 
 		void set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override);
-		void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; }
+		void set_hidden(int p_line, bool p_hidden) {
+			text.write[p_line].hidden = p_hidden;
+			if (!p_hidden && text[p_line].width > max_width) {
+				max_width = text[p_line].width;
+			} else if (p_hidden && text[p_line].width == max_width) {
+				_calculate_max_line_width();
+			}
+		}
 		bool is_hidden(int p_line) const { return text[p_line].hidden; }
 		void insert(int p_at, const String &p_text, const Vector<Vector2i> &p_bidi_override);
 		void remove(int p_at);