Browse Source

Merge pull request #77471 from AThousandShips/help_search_index

Cache `TreeItem`s between runs in `EditorHelpSearch`
Yuri Sizov 1 year ago
parent
commit
039bc52df9
2 changed files with 122 additions and 42 deletions
  1. 107 41
      editor/editor_help_search.cpp
  2. 15 1
      editor/editor_help_search.h

+ 107 - 41
editor/editor_help_search.cpp

@@ -48,7 +48,7 @@ void EditorHelpSearch::_update_results() {
 		search_flags |= SEARCH_SHOW_HIERARCHY;
 	}
 
-	search = Ref<Runner>(memnew(Runner(results_tree, results_tree, term, search_flags)));
+	search = Ref<Runner>(memnew(Runner(results_tree, results_tree, &tree_cache, term, search_flags)));
 	set_process(true);
 }
 
@@ -96,6 +96,7 @@ void EditorHelpSearch::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_VISIBILITY_CHANGED: {
 			if (!is_visible()) {
+				tree_cache.clear();
 				callable_mp(results_tree, &Tree::clear).call_deferred(); // Wait for the Tree's mouse event propagation.
 				get_ok_button()->set_disabled(true);
 				EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "search_help", Rect2(get_position(), get_size()));
@@ -258,6 +259,13 @@ EditorHelpSearch::EditorHelpSearch() {
 	vbox->add_child(results_tree, true);
 }
 
+void EditorHelpSearch::TreeCache::clear() {
+	for (const KeyValue<String, TreeItem *> &E : item_cache) {
+		memdelete(E.value);
+	}
+	item_cache.clear();
+}
+
 bool EditorHelpSearch::Runner::_is_class_disabled_by_feature_profile(const StringName &p_class) {
 	Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile();
 	if (profile.is_null()) {
@@ -436,11 +444,42 @@ bool EditorHelpSearch::Runner::_phase_match_classes() {
 	return iterator_stack.is_empty();
 }
 
+void EditorHelpSearch::Runner::_populate_cache() {
+	root_item = results_tree->get_root();
+
+	if (root_item) {
+		LocalVector<TreeItem *> stack;
+
+		// Add children of root item to stack.
+		for (TreeItem *child = root_item->get_first_child(); child; child = child->get_next()) {
+			stack.push_back(child);
+		}
+
+		// Traverse stack and cache items.
+		while (!stack.is_empty()) {
+			TreeItem *cur_item = stack[stack.size() - 1];
+			stack.resize(stack.size() - 1);
+
+			// Add to the cache.
+			tree_cache->item_cache.insert(cur_item->get_metadata(0).operator String(), cur_item);
+
+			// Add any children to the stack.
+			for (TreeItem *child = cur_item->get_first_child(); child; child = child->get_next()) {
+				stack.push_back(child);
+			}
+
+			// Remove from parent.
+			cur_item->get_parent()->remove_child(cur_item);
+		}
+	} else {
+		root_item = results_tree->create_item();
+	}
+}
+
 bool EditorHelpSearch::Runner::_phase_class_items_init() {
 	iterator_match = matches.begin();
 
-	results_tree->clear();
-	root_item = results_tree->create_item();
+	_populate_cache();
 	class_items.clear();
 
 	return true;
@@ -618,27 +657,54 @@ TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const ClassMatch &p_
 	return class_item;
 }
 
+bool EditorHelpSearch::Runner::_find_or_create_item(TreeItem *p_parent, const String &p_item_meta, TreeItem *&r_item) {
+	// Attempt to find in cache.
+	if (tree_cache->item_cache.has(p_item_meta)) {
+		r_item = tree_cache->item_cache[p_item_meta];
+
+		// Remove from cache.
+		tree_cache->item_cache.erase(p_item_meta);
+
+		// Add to tree.
+		p_parent->add_child(r_item);
+
+		return false;
+	} else {
+		// Otherwise create item.
+		r_item = results_tree->create_item(p_parent);
+
+		return true;
+	}
+}
+
 TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray) {
 	String tooltip = DTR(p_doc->brief_description.strip_edges());
 
-	TreeItem *item = results_tree->create_item(p_parent);
-	item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_doc->name));
-	item->set_text(0, p_doc->name);
-	item->set_text(1, TTR("Class"));
-	item->set_tooltip_text(0, tooltip);
-	item->set_tooltip_text(1, tooltip);
-	item->set_metadata(0, "class_name:" + p_doc->name);
+	const String item_meta = "class_name:" + p_doc->name;
+
+	TreeItem *item = nullptr;
+	if (_find_or_create_item(p_parent, item_meta, item)) {
+		item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_doc->name));
+		item->set_text(0, p_doc->name);
+		item->set_text(1, TTR("Class"));
+		item->set_tooltip_text(0, tooltip);
+		item->set_tooltip_text(1, tooltip);
+		item->set_metadata(0, item_meta);
+		if (p_doc->is_deprecated) {
+			Ref<Texture2D> error_icon = ui_service->get_editor_theme_icon(SNAME("StatusError"));
+			item->add_button(0, error_icon, 0, false, TTR("This class is marked as deprecated."));
+		} else if (p_doc->is_experimental) {
+			Ref<Texture2D> warning_icon = ui_service->get_editor_theme_icon(SNAME("NodeWarning"));
+			item->add_button(0, warning_icon, 0, false, TTR("This class is marked as experimental."));
+		}
+	}
+
 	if (p_gray) {
 		item->set_custom_color(0, disabled_color);
 		item->set_custom_color(1, disabled_color);
-	}
-
-	if (p_doc->is_deprecated) {
-		Ref<Texture2D> error_icon = ui_service->get_editor_theme_icon("StatusError");
-		item->add_button(0, error_icon, 0, false, TTR("This class is marked as deprecated."));
-	} else if (p_doc->is_experimental) {
-		Ref<Texture2D> warning_icon = ui_service->get_editor_theme_icon("NodeWarning");
-		item->add_button(0, warning_icon, 0, false, TTR("This class is marked as experimental."));
+	} else {
+		item->clear_custom_color(0);
+		item->clear_custom_color(1);
 	}
 
 	_match_item(item, p_doc->name);
@@ -679,30 +745,29 @@ TreeItem *EditorHelpSearch::Runner::_create_theme_property_item(TreeItem *p_pare
 }
 
 TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, bool is_deprecated, bool is_experimental) {
-	Ref<Texture2D> icon;
-	String text;
-	if (search_flags & SEARCH_SHOW_HIERARCHY) {
-		icon = ui_service->get_editor_theme_icon(p_icon);
-		text = p_text;
-	} else {
-		icon = ui_service->get_editor_theme_icon(p_icon);
-		text = p_class_name + "." + p_text;
+	const String item_meta = "class_" + p_metatype + ":" + p_class_name + ":" + p_name;
+
+	TreeItem *item = nullptr;
+	if (_find_or_create_item(p_parent, item_meta, item)) {
+		item->set_icon(0, ui_service->get_editor_theme_icon(p_icon));
+		item->set_text(1, TTRGET(p_type));
+		item->set_tooltip_text(0, p_tooltip);
+		item->set_tooltip_text(1, p_tooltip);
+		item->set_metadata(0, item_meta);
+
+		if (is_deprecated) {
+			Ref<Texture2D> error_icon = ui_service->get_editor_theme_icon(SNAME("StatusError"));
+			item->add_button(0, error_icon, 0, false, TTR("This member is marked as deprecated."));
+		} else if (is_experimental) {
+			Ref<Texture2D> warning_icon = ui_service->get_editor_theme_icon(SNAME("NodeWarning"));
+			item->add_button(0, warning_icon, 0, false, TTR("This member is marked as experimental."));
+		}
 	}
 
-	TreeItem *item = results_tree->create_item(p_parent);
-	item->set_icon(0, icon);
-	item->set_text(0, text);
-	item->set_text(1, TTRGET(p_type));
-	item->set_tooltip_text(0, p_tooltip);
-	item->set_tooltip_text(1, p_tooltip);
-	item->set_metadata(0, "class_" + p_metatype + ":" + p_class_name + ":" + p_name);
-
-	if (is_deprecated) {
-		Ref<Texture2D> error_icon = ui_service->get_editor_theme_icon("StatusError");
-		item->add_button(0, error_icon, 0, false, TTR("This member is marked as deprecated."));
-	} else if (is_experimental) {
-		Ref<Texture2D> warning_icon = ui_service->get_editor_theme_icon("NodeWarning");
-		item->add_button(0, warning_icon, 0, false, TTR("This member is marked as experimental."));
+	if (search_flags & SEARCH_SHOW_HIERARCHY) {
+		item->set_text(0, p_text);
+	} else {
+		item->set_text(0, p_class_name + "." + p_text);
 	}
 
 	_match_item(item, p_name);
@@ -721,9 +786,10 @@ bool EditorHelpSearch::Runner::work(uint64_t slot) {
 	return true;
 }
 
-EditorHelpSearch::Runner::Runner(Control *p_icon_service, Tree *p_results_tree, const String &p_term, int p_search_flags) :
+EditorHelpSearch::Runner::Runner(Control *p_icon_service, Tree *p_results_tree, TreeCache *p_tree_cache, const String &p_term, int p_search_flags) :
 		ui_service(p_icon_service),
 		results_tree(p_results_tree),
+		tree_cache(p_tree_cache),
 		term((p_search_flags & SEARCH_CASE_SENSITIVE) == 0 ? p_term.strip_edges().to_lower() : p_term.strip_edges()),
 		search_flags(p_search_flags),
 		disabled_color(ui_service->get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor))) {

+ 15 - 1
editor/editor_help_search.h

@@ -67,6 +67,16 @@ class EditorHelpSearch : public ConfirmationDialog {
 	class Runner;
 	Ref<Runner> search;
 
+	struct TreeCache {
+		HashMap<String, TreeItem *> item_cache;
+
+		void clear();
+
+		~TreeCache() {
+			clear();
+		}
+	} tree_cache;
+
 	void _update_results();
 
 	void _search_box_gui_input(const Ref<InputEvent> &p_event);
@@ -117,6 +127,7 @@ class EditorHelpSearch::Runner : public RefCounted {
 
 	Control *ui_service = nullptr;
 	Tree *results_tree = nullptr;
+	TreeCache *tree_cache = nullptr;
 	String term;
 	Vector<String> terms;
 	int search_flags;
@@ -134,6 +145,9 @@ class EditorHelpSearch::Runner : public RefCounted {
 
 	bool _is_class_disabled_by_feature_profile(const StringName &p_class);
 
+	void _populate_cache();
+	bool _find_or_create_item(TreeItem *p_parent, const String &p_item_meta, TreeItem *&r_item);
+
 	bool _slice();
 	bool _phase_match_classes_init();
 	bool _phase_match_classes();
@@ -162,7 +176,7 @@ class EditorHelpSearch::Runner : public RefCounted {
 public:
 	bool work(uint64_t slot = 100000);
 
-	Runner(Control *p_icon_service, Tree *p_results_tree, const String &p_term, int p_search_flags);
+	Runner(Control *p_icon_service, Tree *p_results_tree, TreeCache *p_tree_cache, const String &p_term, int p_search_flags);
 };
 
 #endif // EDITOR_HELP_SEARCH_H