Browse Source

[Core] Add case-insensitive `String::containsn`

A Thousand Ships 1 year ago
parent
commit
b4c6cc7d82

+ 2 - 0
core/string/ustring.h

@@ -429,6 +429,8 @@ public:
 	_FORCE_INLINE_ bool is_empty() const { return length() == 0; }
 	_FORCE_INLINE_ bool is_empty() const { return length() == 0; }
 	_FORCE_INLINE_ bool contains(const char *p_str) const { return find(p_str) != -1; }
 	_FORCE_INLINE_ bool contains(const char *p_str) const { return find(p_str) != -1; }
 	_FORCE_INLINE_ bool contains(const String &p_str) const { return find(p_str) != -1; }
 	_FORCE_INLINE_ bool contains(const String &p_str) const { return find(p_str) != -1; }
+	_FORCE_INLINE_ bool containsn(const char *p_str) const { return findn(p_str) != -1; }
+	_FORCE_INLINE_ bool containsn(const String &p_str) const { return findn(p_str) != -1; }
 
 
 	// path functions
 	// path functions
 	bool is_absolute_path() const;
 	bool is_absolute_path() const;

+ 1 - 0
core/variant/variant_call.cpp

@@ -1707,6 +1707,7 @@ static void _register_variant_builtin_methods() {
 	bind_string_method(sha256_buffer, sarray(), varray());
 	bind_string_method(sha256_buffer, sarray(), varray());
 	bind_string_method(is_empty, sarray(), varray());
 	bind_string_method(is_empty, sarray(), varray());
 	bind_string_methodv(contains, static_cast<bool (String::*)(const String &) const>(&String::contains), sarray("what"), varray());
 	bind_string_methodv(contains, static_cast<bool (String::*)(const String &) const>(&String::contains), sarray("what"), varray());
+	bind_string_methodv(containsn, static_cast<bool (String::*)(const String &) const>(&String::containsn), sarray("what"), varray());
 
 
 	bind_string_method(is_absolute_path, sarray(), varray());
 	bind_string_method(is_absolute_path, sarray(), varray());
 	bind_string_method(is_relative_path, sarray(), varray());
 	bind_string_method(is_relative_path, sarray(), varray());

+ 10 - 2
doc/classes/String.xml

@@ -126,7 +126,7 @@
 				[/codeblock]
 				[/codeblock]
 			</description>
 			</description>
 		</method>
 		</method>
-		<method name="contains" qualifiers="const" keywords="includes, has">
+		<method name="contains" qualifiers="const">
 			<return type="bool" />
 			<return type="bool" />
 			<param index="0" name="what" type="String" />
 			<param index="0" name="what" type="String" />
 			<description>
 			<description>
@@ -142,7 +142,15 @@
 				GD.Print("team".Contains("I"));  // Prints false
 				GD.Print("team".Contains("I"));  // Prints false
 				[/csharp]
 				[/csharp]
 				[/codeblocks]
 				[/codeblocks]
-				If you need to know where [param what] is within the string, use [method find].
+				If you need to know where [param what] is within the string, use [method find]. See also [method containsn].
+			</description>
+		</method>
+		<method name="containsn" qualifiers="const">
+			<return type="bool" />
+			<param index="0" name="what" type="String" />
+			<description>
+				Returns [code]true[/code] if the string contains [param what], [b]ignoring case[/b].
+				If you need to know where [param what] is within the string, use [method findn]. See also [method contains].
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="count" qualifiers="const">
 		<method name="count" qualifiers="const">

+ 9 - 1
doc/classes/StringName.xml

@@ -126,7 +126,15 @@
 				GD.Print("team".Contains("I"));  // Prints false
 				GD.Print("team".Contains("I"));  // Prints false
 				[/csharp]
 				[/csharp]
 				[/codeblocks]
 				[/codeblocks]
-				If you need to know where [param what] is within the string, use [method find].
+				If you need to know where [param what] is within the string, use [method find]. See also [method containsn].
+			</description>
+		</method>
+		<method name="containsn" qualifiers="const">
+			<return type="bool" />
+			<param index="0" name="what" type="String" />
+			<description>
+				Returns [code]true[/code] if the string contains [param what], [b]ignoring case[/b].
+				If you need to know where [param what] is within the string, use [method findn]. See also [method contains].
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="count" qualifiers="const">
 		<method name="count" qualifiers="const">

+ 1 - 1
drivers/gles3/storage/config.cpp

@@ -157,7 +157,7 @@ Config::Config() {
 				continue;
 				continue;
 			}
 			}
 
 
-			if (renderer.findn(v) != -1) {
+			if (renderer.containsn(v)) {
 				use_depth_prepass = false;
 				use_depth_prepass = false;
 			}
 			}
 		}
 		}

+ 1 - 1
editor/animation_track_editor.cpp

@@ -7122,7 +7122,7 @@ void AnimationTrackEditor::_pick_track_select_recursive(TreeItem *p_item, const
 	NodePath np = p_item->get_metadata(0);
 	NodePath np = p_item->get_metadata(0);
 	Node *node = get_node_or_null(np);
 	Node *node = get_node_or_null(np);
 
 
-	if (node && !p_filter.is_empty() && ((String)node->get_name()).findn(p_filter) != -1) {
+	if (node && !p_filter.is_empty() && ((String)node->get_name()).containsn(p_filter)) {
 		p_select_candidates.push_back(node);
 		p_select_candidates.push_back(node);
 	}
 	}
 
 

+ 1 - 1
editor/connections_dialog.cpp

@@ -282,7 +282,7 @@ List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_me
 	List<MethodInfo> ret;
 	List<MethodInfo> ret;
 
 
 	for (const MethodInfo &mi : p_methods) {
 	for (const MethodInfo &mi : p_methods) {
-		if (!p_search_string.is_empty() && mi.name.findn(p_search_string) == -1) {
+		if (!p_search_string.is_empty() && !mi.name.containsn(p_search_string)) {
 			continue;
 			continue;
 		}
 		}
 
 

+ 1 - 1
editor/debugger/debug_adapter/debug_adapter_parser.h

@@ -50,7 +50,7 @@ private:
 		if (p_path.contains("\\")) {
 		if (p_path.contains("\\")) {
 			String project_path = ProjectSettings::get_singleton()->get_resource_path();
 			String project_path = ProjectSettings::get_singleton()->get_resource_path();
 			String path = p_path.replace("\\", "/");
 			String path = p_path.replace("\\", "/");
-			return path.findn(project_path) != -1;
+			return path.containsn(project_path);
 		}
 		}
 		return p_path.begins_with(ProjectSettings::get_singleton()->get_resource_path());
 		return p_path.begins_with(ProjectSettings::get_singleton()->get_resource_path());
 	}
 	}

+ 3 - 3
editor/editor_help_search.cpp

@@ -40,7 +40,7 @@
 
 
 bool EditorHelpSearch::_all_terms_in_name(const Vector<String> &p_terms, const String &p_name) const {
 bool EditorHelpSearch::_all_terms_in_name(const Vector<String> &p_terms, const String &p_name) const {
 	for (int i = 0; i < p_terms.size(); i++) {
 	for (int i = 0; i < p_terms.size(); i++) {
-		if (p_name.findn(p_terms[i]) < 0) {
+		if (!p_name.containsn(p_terms[i])) {
 			return false;
 			return false;
 		}
 		}
 	}
 	}
@@ -109,7 +109,7 @@ Dictionary EditorHelpSearch::_native_search_cb(const String &p_search_string, in
 		if (class_doc.name.is_empty()) {
 		if (class_doc.name.is_empty()) {
 			continue;
 			continue;
 		}
 		}
-		if (class_doc.name.findn(term) > -1) {
+		if (class_doc.name.containsn(term)) {
 			ret[vformat("class_name:%s", class_doc.name)] = class_doc.name;
 			ret[vformat("class_name:%s", class_doc.name)] = class_doc.name;
 		}
 		}
 		if (term.length() > 1 || term == "@") {
 		if (term.length() > 1 || term == "@") {
@@ -746,7 +746,7 @@ bool EditorHelpSearch::Runner::_match_string(const String &p_term, const String
 	if (search_flags & SEARCH_CASE_SENSITIVE) {
 	if (search_flags & SEARCH_CASE_SENSITIVE) {
 		return p_string.find(p_term) > -1;
 		return p_string.find(p_term) > -1;
 	} else {
 	} else {
-		return p_string.findn(p_term) > -1;
+		return p_string.containsn(p_term);
 	}
 	}
 }
 }
 
 

+ 1 - 1
editor/editor_inspector.cpp

@@ -52,7 +52,7 @@
 #include "scene/resources/style_box_flat.h"
 #include "scene/resources/style_box_flat.h"
 
 
 bool EditorInspector::_property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) {
 bool EditorInspector::_property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) {
-	if (p_property_path.findn(p_filter) != -1) {
+	if (p_property_path.containsn(p_filter)) {
 		return true;
 		return true;
 	}
 	}
 
 

+ 1 - 1
editor/editor_log.cpp

@@ -322,7 +322,7 @@ void EditorLog::_rebuild_log() {
 bool EditorLog::_check_display_message(LogMessage &p_message) {
 bool EditorLog::_check_display_message(LogMessage &p_message) {
 	bool filter_active = type_filter_map[p_message.type]->is_active();
 	bool filter_active = type_filter_map[p_message.type]->is_active();
 	String search_text = search_box->get_text();
 	String search_text = search_box->get_text();
-	bool search_match = search_text.is_empty() || p_message.text.findn(search_text) > -1;
+	bool search_match = search_text.is_empty() || p_message.text.containsn(search_text);
 	return filter_active && search_match;
 	return filter_active && search_match;
 }
 }
 
 

+ 1 - 1
editor/editor_sectioned_inspector.cpp

@@ -36,7 +36,7 @@
 #include "editor/themes/editor_scale.h"
 #include "editor/themes/editor_scale.h"
 
 
 static bool _property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) {
 static bool _property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) {
-	if (p_property_path.findn(p_filter) != -1) {
+	if (p_property_path.containsn(p_filter)) {
 		return true;
 		return true;
 	}
 	}
 
 

+ 2 - 2
editor/import/3d/resource_importer_scene.cpp

@@ -387,7 +387,7 @@ static bool _teststr(const String &p_what, const String &p_str) {
 		what = what.substr(0, what.length() - 1);
 		what = what.substr(0, what.length() - 1);
 	}
 	}
 
 
-	if (what.findn("$" + p_str) != -1) { //blender and other stuff
+	if (what.containsn("$" + p_str)) { // Blender and other stuff.
 		return true;
 		return true;
 	}
 	}
 	if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters
 	if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters
@@ -410,7 +410,7 @@ static String _fixstr(const String &p_what, const String &p_str) {
 
 
 	String end = p_what.substr(what.length(), p_what.length() - what.length());
 	String end = p_what.substr(what.length(), p_what.length() - what.length());
 
 
-	if (what.findn("$" + p_str) != -1) { //blender and other stuff
+	if (what.containsn("$" + p_str)) { // Blender and other stuff.
 		return what.replace("$" + p_str, "") + end;
 		return what.replace("$" + p_str, "") + end;
 	}
 	}
 	if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters
 	if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters

+ 4 - 4
editor/input_event_configuration_dialog.cpp

@@ -285,7 +285,7 @@ void InputEventConfigurationDialog::_update_input_list() {
 		for (int i = 0; i < keycode_get_count(); i++) {
 		for (int i = 0; i < keycode_get_count(); i++) {
 			String name = keycode_get_name_by_index(i);
 			String name = keycode_get_name_by_index(i);
 
 
-			if (!search_term.is_empty() && name.findn(search_term) == -1) {
+			if (!search_term.is_empty() && !name.containsn(search_term)) {
 				continue;
 				continue;
 			}
 			}
 
 
@@ -309,7 +309,7 @@ void InputEventConfigurationDialog::_update_input_list() {
 			mb->set_button_index(mouse_buttons[i]);
 			mb->set_button_index(mouse_buttons[i]);
 			String desc = EventListenerLineEdit::get_event_text(mb, false);
 			String desc = EventListenerLineEdit::get_event_text(mb, false);
 
 
-			if (!search_term.is_empty() && desc.findn(search_term) == -1) {
+			if (!search_term.is_empty() && !desc.containsn(search_term)) {
 				continue;
 				continue;
 			}
 			}
 
 
@@ -332,7 +332,7 @@ void InputEventConfigurationDialog::_update_input_list() {
 			joyb->set_button_index((JoyButton)i);
 			joyb->set_button_index((JoyButton)i);
 			String desc = EventListenerLineEdit::get_event_text(joyb, false);
 			String desc = EventListenerLineEdit::get_event_text(joyb, false);
 
 
-			if (!search_term.is_empty() && desc.findn(search_term) == -1) {
+			if (!search_term.is_empty() && !desc.containsn(search_term)) {
 				continue;
 				continue;
 			}
 			}
 
 
@@ -358,7 +358,7 @@ void InputEventConfigurationDialog::_update_input_list() {
 			joym->set_axis_value(direction);
 			joym->set_axis_value(direction);
 			String desc = EventListenerLineEdit::get_event_text(joym, false);
 			String desc = EventListenerLineEdit::get_event_text(joym, false);
 
 
-			if (!search_term.is_empty() && desc.findn(search_term) == -1) {
+			if (!search_term.is_empty() && !desc.containsn(search_term)) {
 				continue;
 				continue;
 			}
 			}
 
 

+ 1 - 1
editor/plugins/script_editor_plugin.cpp

@@ -374,7 +374,7 @@ void ScriptEditorQuickOpen::_update_search() {
 
 
 	for (int i = 0; i < functions.size(); i++) {
 	for (int i = 0; i < functions.size(); i++) {
 		String file = functions[i];
 		String file = functions[i];
-		if ((search_box->get_text().is_empty() || file.findn(search_box->get_text()) != -1)) {
+		if ((search_box->get_text().is_empty() || file.containsn(search_box->get_text()))) {
 			TreeItem *ti = search_options->create_item(root);
 			TreeItem *ti = search_options->create_item(root);
 			ti->set_text(0, file);
 			ti->set_text(0, file);
 			if (root->get_first_child() == ti) {
 			if (root->get_first_child() == ti) {

+ 2 - 2
editor/plugins/theme_editor_plugin.cpp

@@ -106,7 +106,7 @@ void ThemeItemImportTree::_update_items_tree() {
 		type_node->set_checked(IMPORT_ITEM_DATA, false);
 		type_node->set_checked(IMPORT_ITEM_DATA, false);
 		type_node->set_editable(IMPORT_ITEM_DATA, true);
 		type_node->set_editable(IMPORT_ITEM_DATA, true);
 
 
-		bool is_matching_filter = (filter_text.is_empty() || type_name.findn(filter_text) > -1);
+		bool is_matching_filter = (filter_text.is_empty() || type_name.containsn(filter_text));
 		bool has_filtered_items = false;
 		bool has_filtered_items = false;
 
 
 		for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
 		for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
@@ -120,7 +120,7 @@ void ThemeItemImportTree::_update_items_tree() {
 
 
 			for (const StringName &F : names) {
 			for (const StringName &F : names) {
 				String item_name = (String)F;
 				String item_name = (String)F;
-				bool is_item_matching_filter = (item_name.findn(filter_text) > -1);
+				bool is_item_matching_filter = item_name.containsn(filter_text);
 				if (!filter_text.is_empty() && !is_matching_filter && !is_item_matching_filter) {
 				if (!filter_text.is_empty() && !is_matching_filter && !is_item_matching_filter) {
 					continue;
 					continue;
 				}
 				}

+ 1 - 1
editor/plugins/visual_shader_editor_plugin.cpp

@@ -1957,7 +1957,7 @@ void VisualShaderEditor::_update_options_menu() {
 	}
 	}
 
 
 	for (int i = 0; i < add_options.size(); i++) {
 	for (int i = 0; i < add_options.size(); i++) {
-		if (!use_filter || add_options[i].name.findn(filter) != -1) {
+		if (!use_filter || add_options[i].name.containsn(filter)) {
 			// port type filtering
 			// port type filtering
 			if (members_output_port_type != VisualShaderNode::PORT_TYPE_MAX || members_input_port_type != VisualShaderNode::PORT_TYPE_MAX) {
 			if (members_output_port_type != VisualShaderNode::PORT_TYPE_MAX || members_input_port_type != VisualShaderNode::PORT_TYPE_MAX) {
 				Ref<VisualShaderNode> vsn;
 				Ref<VisualShaderNode> vsn;

+ 1 - 1
editor/project_manager/project_list.cpp

@@ -545,7 +545,7 @@ void ProjectList::sort_projects() {
 			}
 			}
 
 
 			// When searching, display projects whose name or path contain the search term and whose tags match the searched tags.
 			// When searching, display projects whose name or path contain the search term and whose tags match the searched tags.
-			item_visible = !missing_tags && (search_term.is_empty() || item.project_name.findn(search_term) != -1 || search_path.findn(search_term) != -1);
+			item_visible = !missing_tags && (search_term.is_empty() || item.project_name.containsn(search_term) || search_path.containsn(search_term));
 		}
 		}
 
 
 		item.control->set_visible(item_visible);
 		item.control->set_visible(item_visible);

+ 4 - 4
editor/property_selector.cpp

@@ -190,7 +190,7 @@ void PropertySelector::_update_search() {
 				continue;
 				continue;
 			}
 			}
 
 
-			if (!search_box->get_text().is_empty() && E.name.findn(search_text) == -1) {
+			if (!search_box->get_text().is_empty() && !E.name.containsn(search_text)) {
 				continue;
 				continue;
 			}
 			}
 
 
@@ -203,7 +203,7 @@ void PropertySelector::_update_search() {
 			item->set_metadata(0, E.name);
 			item->set_metadata(0, E.name);
 			item->set_icon(0, type_icons[E.type]);
 			item->set_icon(0, type_icons[E.type]);
 
 
-			if (!found && !search_box->get_text().is_empty() && E.name.findn(search_text) != -1) {
+			if (!found && !search_box->get_text().is_empty() && E.name.containsn(search_text)) {
 				item->select(0);
 				item->select(0);
 				found = true;
 				found = true;
 			}
 			}
@@ -281,7 +281,7 @@ void PropertySelector::_update_search() {
 				continue;
 				continue;
 			}
 			}
 
 
-			if (!search_box->get_text().is_empty() && name.findn(search_text) == -1) {
+			if (!search_box->get_text().is_empty() && !name.containsn(search_text)) {
 				continue;
 				continue;
 			}
 			}
 
 
@@ -330,7 +330,7 @@ void PropertySelector::_update_search() {
 			item->set_metadata(0, name);
 			item->set_metadata(0, name);
 			item->set_selectable(0, true);
 			item->set_selectable(0, true);
 
 
-			if (!found && !search_box->get_text().is_empty() && name.findn(search_text) != -1) {
+			if (!found && !search_box->get_text().is_empty() && name.containsn(search_text)) {
 				item->select(0);
 				item->select(0);
 				found = true;
 				found = true;
 			}
 			}

+ 1 - 1
modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp

@@ -77,7 +77,7 @@ void EditorSceneExporterGLTFSettings::_get_property_list(List<PropertyInfo> *p_l
 	for (PropertyInfo prop : _property_list) {
 	for (PropertyInfo prop : _property_list) {
 		if (prop.name == "lossy_quality") {
 		if (prop.name == "lossy_quality") {
 			String image_format = get("image_format");
 			String image_format = get("image_format");
-			bool is_image_format_lossy = image_format == "JPEG" || image_format.findn("Lossy") != -1;
+			bool is_image_format_lossy = image_format == "JPEG" || image_format.containsn("Lossy");
 			prop.usage = is_image_format_lossy ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_STORAGE;
 			prop.usage = is_image_format_lossy ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_STORAGE;
 		}
 		}
 		p_list->push_back(prop);
 		p_list->push_back(prop);

+ 1 - 1
modules/multiplayer/editor/replication_editor.cpp

@@ -81,7 +81,7 @@ void ReplicationEditor::_pick_node_select_recursive(TreeItem *p_item, const Stri
 	NodePath np = p_item->get_metadata(0);
 	NodePath np = p_item->get_metadata(0);
 	Node *node = get_node(np);
 	Node *node = get_node(np);
 
 
-	if (!p_filter.is_empty() && ((String)node->get_name()).findn(p_filter) != -1) {
+	if (!p_filter.is_empty() && ((String)node->get_name()).containsn(p_filter)) {
 		p_select_candidates.push_back(node);
 		p_select_candidates.push_back(node);
 	}
 	}
 
 

+ 1 - 1
scene/main/http_request.cpp

@@ -239,7 +239,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
 		String new_request;
 		String new_request;
 
 
 		for (const String &E : rheaders) {
 		for (const String &E : rheaders) {
-			if (E.findn("Location: ") != -1) {
+			if (E.containsn("Location: ")) {
 				new_request = E.substr(9, E.length()).strip_edges();
 				new_request = E.substr(9, E.length()).strip_edges();
 			}
 			}
 		}
 		}

+ 14 - 1
tests/core/string/test_string.h

@@ -297,6 +297,19 @@ TEST_CASE("[String] Contains") {
 	CHECK(!s.contains(String("\\char_test.tscn")));
 	CHECK(!s.contains(String("\\char_test.tscn")));
 }
 }
 
 
+TEST_CASE("[String] Contains case insensitive") {
+	String s = "C:\\Godot\\project\\string_test.tscn";
+	CHECK(s.containsn("Godot"));
+	CHECK(s.containsn("godot"));
+	CHECK(s.containsn(String("Project\\string_test")));
+	CHECK(s.containsn(String("\\string_Test.tscn")));
+
+	CHECK(!s.containsn("Godoh"));
+	CHECK(!s.containsn("godoh"));
+	CHECK(!s.containsn(String("project\\string test")));
+	CHECK(!s.containsn(String("\\char_test.tscn")));
+}
+
 TEST_CASE("[String] Test chr") {
 TEST_CASE("[String] Test chr") {
 	CHECK(String::chr('H') == "H");
 	CHECK(String::chr('H') == "H");
 	CHECK(String::chr(0x3012)[0] == 0x3012);
 	CHECK(String::chr(0x3012)[0] == 0x3012);
@@ -376,7 +389,7 @@ TEST_CASE("[String] Find") {
 	MULTICHECK_STRING_INT_EQ(s, rfind, "", 15, -1);
 	MULTICHECK_STRING_INT_EQ(s, rfind, "", 15, -1);
 }
 }
 
 
-TEST_CASE("[String] Find no case") {
+TEST_CASE("[String] Find case insensitive") {
 	String s = "Pretty Whale Whale";
 	String s = "Pretty Whale Whale";
 	MULTICHECK_STRING_EQ(s, findn, "WHA", 7);
 	MULTICHECK_STRING_EQ(s, findn, "WHA", 7);
 	MULTICHECK_STRING_INT_EQ(s, findn, "WHA", 9, 13);
 	MULTICHECK_STRING_INT_EQ(s, findn, "WHA", 9, 13);