summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
authorrileylyman <rileylyman@berkeley.edu>2020-05-26 21:49:05 -0700
committerrileylyman <rileylyman@gmail.com>2020-06-26 16:12:08 -0700
commit4285211f40cf3519237faf08b8fdbf09bc28e954 (patch)
tree94b36fb11e90a0f6f33b50e72f785733993cf0d3 /editor
parentbcf7e51689442f7fdb369509ccf0738dcc736e96 (diff)
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.
Diffstat (limited to 'editor')
-rw-r--r--editor/editor_node.cpp117
-rw-r--r--editor/editor_node.h2
-rw-r--r--editor/plugins/script_editor_plugin.cpp13
3 files changed, 131 insertions, 1 deletions
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 30aa24695c..26042631d5 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -178,6 +178,111 @@
EditorNode *EditorNode::singleton = nullptr;
+void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &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<Set<int>> index_sets;
+ Map<String, int> 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.append(Set<int>());
+ 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<int> iset = index_sets[i];
+ while (iset.size() > 1) {
+ // Append the parent folder to each scene name
+ for (Set<int>::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<int>::Element *E = iset.front();
+ while (E) {
+ String scene_name = r_filenames[E->get()];
+ bool duplicate_found = false;
+ for (Set<int>::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<int>::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");
@@ -185,6 +290,16 @@ void EditorNode::_update_scene_tabs() {
DisplayServer::get_singleton()->global_menu_clear("_dock");
}
+ // Get all scene names, which may be ambiguous
+ Vector<String> disambiguated_scene_names;
+ Vector<String> full_path_names;
+ for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
+ disambiguated_scene_names.append(editor_data.get_scene_title(i));
+ full_path_names.append(editor_data.get_scene_path(i));
+ }
+
+ disambiguate_filenames(full_path_names, disambiguated_scene_names);
+
scene_tabs->clear_tabs();
Ref<Texture2D> script_icon = gui_base->get_theme_icon("Script", "EditorIcons");
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
@@ -196,7 +311,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);
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) {
DisplayServer::get_singleton()->global_menu_add_item("_dock", editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), callable_mp(this, &EditorNode::_global_menu_scene), i);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index b0e0c5614c..413e228e2a 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -687,6 +687,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<String> p_full_paths, Vector<String> &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 96079d5418..3e643c3e8d 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -1731,6 +1731,19 @@ void ScriptEditor::_update_script_names() {
sedata.push_back(sd);
}
+ Vector<String> disambiguated_script_names;
+ Vector<String> full_script_paths;
+ for (int j = 0; j < sedata.size(); j++) {
+ disambiguated_script_names.append(sedata[j].name);
+ full_script_paths.append(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<EditorHelp>(tab_container->get_child(i));
if (eh) {
String name = eh->get_class();