From 582b4318f8c6fd8e5000f6804a0bb452f21da4c4 Mon Sep 17 00:00:00 2001 From: rileylyman Date: Tue, 26 May 2020 21:49:05 -0700 Subject: [PATCH] implement generic filename disambiguation A static function is added to EditorNode which allows for filename disambiguation given a list of filenames and the corresponding list of absolute paths for those files. This function is then used to disambiguate scene and script tabs in the editor. (cherry picked from commit 4285211f40cf3519237faf08b8fdbf09bc28e954) --- editor/editor_node.cpp | 117 +++++++++++++++++++++++- editor/editor_node.h | 2 + editor/plugins/script_editor_plugin.cpp | 13 +++ 3 files changed, 131 insertions(+), 1 deletion(-) diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 9746d4be2a5..b19ccbefc20 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -175,12 +175,127 @@ EditorNode *EditorNode::singleton = NULL; +void EditorNode::disambiguate_filenames(const Vector p_full_paths, Vector &r_filenames) { + // Keep track of a list of "index sets," i.e. sets of indices + // within disambiguated_scene_names which contain the same name. + Vector > index_sets; + Map scene_name_to_set_index; + for (int i = 0; i < r_filenames.size(); i++) { + String scene_name = r_filenames[i]; + if (!scene_name_to_set_index.has(scene_name)) { + index_sets.push_back(Set()); + scene_name_to_set_index.insert(r_filenames[i], index_sets.size() - 1); + } + index_sets.write[scene_name_to_set_index[scene_name]].insert(i); + } + + // For each index set with a size > 1, we need to disambiguate + for (int i = 0; i < index_sets.size(); i++) { + Set iset = index_sets[i]; + while (iset.size() > 1) { + // Append the parent folder to each scene name + for (Set::Element *E = iset.front(); E; E = E->next()) { + int set_idx = E->get(); + String scene_name = r_filenames[set_idx]; + String full_path = p_full_paths[set_idx]; + + // Get rid of file extensions and res:// prefixes + if (scene_name.rfind(".") >= 0) { + scene_name = scene_name.substr(0, scene_name.rfind(".")); + } + if (full_path.begins_with("res://")) { + full_path = full_path.substr(6); + } + if (full_path.rfind(".") >= 0) { + full_path = full_path.substr(0, full_path.rfind(".")); + } + + int scene_name_size = scene_name.size(); + int full_path_size = full_path.size(); + int difference = full_path_size - scene_name_size; + + // Find just the parent folder of the current path and append it. + // If the current name is foo.tscn, and the full path is /some/folder/foo.tscn + // then slash_idx is the second '/', so that we select just "folder", and + // append that to yield "folder/foo.tscn". + if (difference > 0) { + String parent = full_path.substr(0, difference); + int slash_idx = parent.rfind("/"); + slash_idx = parent.rfind("/", slash_idx - 1); + parent = slash_idx >= 0 ? parent.substr(slash_idx + 1) : parent; + r_filenames.write[set_idx] = parent + r_filenames[set_idx]; + } + } + + // Loop back through scene names and remove non-ambiguous names + bool can_proceed = false; + Set::Element *E = iset.front(); + while (E) { + String scene_name = r_filenames[E->get()]; + bool duplicate_found = false; + for (Set::Element *F = iset.front(); F; F = F->next()) { + if (E->get() == F->get()) { + continue; + } + String other_scene_name = r_filenames[F->get()]; + if (other_scene_name == scene_name) { + duplicate_found = true; + break; + } + } + + Set::Element *to_erase = duplicate_found ? nullptr : E; + + // We need to check that we could actually append anymore names + // if we wanted to for disambiguation. If we can't, then we have + // to abort even with ambiguous names. We clean the full path + // and the scene name first to remove extensions so that this + // comparison actually works. + String path = p_full_paths[E->get()]; + if (path.begins_with("res://")) { + path = path.substr(6); + } + if (path.rfind(".") >= 0) { + path = path.substr(0, path.rfind(".")); + } + if (scene_name.rfind(".") >= 0) { + scene_name = scene_name.substr(0, scene_name.rfind(".")); + } + + // We can proceed iff the full path is longer than the scene name, + // meaning that there is at least one more parent folder we can + // tack onto the name. + can_proceed = can_proceed || (path.size() - scene_name.size()) >= 1; + + E = E->next(); + if (to_erase) { + iset.erase(to_erase); + } + } + + if (!can_proceed) { + break; + } + } + } +} + void EditorNode::_update_scene_tabs() { bool show_rb = EditorSettings::get_singleton()->get("interface/scene_tabs/show_script_button"); OS::get_singleton()->global_menu_clear("_dock"); + // Get all scene names, which may be ambiguous + Vector disambiguated_scene_names; + Vector full_path_names; + for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { + disambiguated_scene_names.push_back(editor_data.get_scene_title(i)); + full_path_names.push_back(editor_data.get_scene_path(i)); + } + + disambiguate_filenames(full_path_names, disambiguated_scene_names); + scene_tabs->clear_tabs(); Ref script_icon = gui_base->get_icon("Script", "EditorIcons"); for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { @@ -193,7 +308,7 @@ void EditorNode::_update_scene_tabs() { int current = editor_data.get_edited_scene(); bool unsaved = (i == current) ? saved_version != editor_data.get_undo_redo().get_version() : editor_data.get_scene_version(i) != 0; - scene_tabs->add_tab(editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), icon); + scene_tabs->add_tab(disambiguated_scene_names[i] + (unsaved ? "(*)" : ""), icon); OS::get_singleton()->global_menu_add_item("_dock", editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), GLOBAL_SCENE, i); diff --git a/editor/editor_node.h b/editor/editor_node.h index 1ff879ce3c1..e5708a8e212 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -685,6 +685,8 @@ public: static void add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed = false); static void remove_editor_plugin(EditorPlugin *p_editor, bool p_config_changed = false); + static void disambiguate_filenames(const Vector p_full_paths, Vector &r_filenames); + void new_inherited_scene() { _menu_option_confirm(FILE_NEW_INHERITED_SCENE, false); } void set_docks_visible(bool p_show); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 99c3d87168f..65a95d7e289 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -1887,6 +1887,19 @@ void ScriptEditor::_update_script_names() { sedata.push_back(sd); } + Vector disambiguated_script_names; + Vector full_script_paths; + for (int j = 0; j < sedata.size(); j++) { + disambiguated_script_names.push_back(sedata[j].name); + full_script_paths.push_back(sedata[j].tooltip); + } + + EditorNode::disambiguate_filenames(full_script_paths, disambiguated_script_names); + + for (int j = 0; j < sedata.size(); j++) { + sedata.write[j].name = disambiguated_script_names[j]; + } + EditorHelp *eh = Object::cast_to(tab_container->get_child(i)); if (eh) {