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.
This commit is contained in:
parent
bcf7e51689
commit
4285211f40
|
@ -178,6 +178,111 @@
|
||||||
|
|
||||||
EditorNode *EditorNode::singleton = nullptr;
|
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() {
|
void EditorNode::_update_scene_tabs() {
|
||||||
bool show_rb = EditorSettings::get_singleton()->get("interface/scene_tabs/show_script_button");
|
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");
|
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();
|
scene_tabs->clear_tabs();
|
||||||
Ref<Texture2D> script_icon = gui_base->get_theme_icon("Script", "EditorIcons");
|
Ref<Texture2D> script_icon = gui_base->get_theme_icon("Script", "EditorIcons");
|
||||||
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
|
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();
|
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;
|
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)) {
|
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);
|
DisplayServer::get_singleton()->global_menu_add_item("_dock", editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), callable_mp(this, &EditorNode::_global_menu_scene), i);
|
||||||
|
|
|
@ -687,6 +687,8 @@ public:
|
||||||
static void add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed = false);
|
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 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 new_inherited_scene() { _menu_option_confirm(FILE_NEW_INHERITED_SCENE, false); }
|
||||||
|
|
||||||
void set_docks_visible(bool p_show);
|
void set_docks_visible(bool p_show);
|
||||||
|
|
|
@ -1731,6 +1731,19 @@ void ScriptEditor::_update_script_names() {
|
||||||
sedata.push_back(sd);
|
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));
|
EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(i));
|
||||||
if (eh) {
|
if (eh) {
|
||||||
String name = eh->get_class();
|
String name = eh->get_class();
|
||||||
|
|
Loading…
Reference in New Issue