From dca8c81a07eb5f981b6555adbd2180086d08af3d Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Wed, 17 Jun 2026 21:31:30 -0700 Subject: [PATCH 1/2] Fix "Untitled Document {N}" numbering progression when another doc includes that substring --- .../portfolio/portfolio_message_handler.rs | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index cad6354eca..cc10791e6d 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -1891,30 +1891,25 @@ impl PortfolioMessageHandler { if trimmed.is_empty() { self.generate_new_document_name(exclude) } else { trimmed.to_string() } } - /// `exclude` lets a renaming caller skip its own current name so a document can rename back to its - /// existing slot rather than colliding with itself and getting bumped to the next number. + /// `exclude` lets a renaming caller skip its own current name so a document can rename back to + /// its existing slot rather than colliding with itself and getting bumped to the next number. pub fn generate_new_document_name(&self, exclude: Option) -> String { - let mut doc_title_numbers = self + let untitled = DEFAULT_DOCUMENT_NAME; + + // Collect the numbers already used by existing default-named documents, skipping the excluded one + let taken_numbers = self .document_ids .iter() - .filter(|id| exclude != Some(**id)) - .filter_map(|id| self.document_details(*id)) - .filter_map(|doc| { - doc.name - .rsplit_once(DEFAULT_DOCUMENT_NAME) - .map(|(prefix, number)| (prefix.is_empty()).then(|| number.trim().parse::().ok()).flatten().unwrap_or(1)) - }) - .collect::>(); + .filter(|&&id| exclude != Some(id)) + .filter_map(|&id| self.document_details(id)) + .filter_map(|doc| doc.name.strip_prefix(untitled).map(|rest| rest.trim().parse::().unwrap_or(1))) + .collect::>(); - doc_title_numbers.sort_unstable(); - doc_title_numbers.iter_mut().enumerate().for_each(|(i, number)| *number = *number - i as isize - 2); - // Uses binary search to find the index of the element where number is bigger than i - let new_doc_title_num = doc_title_numbers.binary_search(&0).unwrap_or_else(|e| e) + 1; + // Pick the lowest number not already in use (a match always exists since the range is unbounded) + let new_number = (1..).find(|number| !taken_numbers.contains(number)).unwrap_or(1); - match new_doc_title_num { - 1 => DEFAULT_DOCUMENT_NAME.to_string(), - _ => format!("{DEFAULT_DOCUMENT_NAME} {new_doc_title_num}"), - } + // Return "Untitled Document" for the first, then "Untitled Document {N}" for subsequent ones + if new_number == 1 { untitled.to_string() } else { format!("{untitled} {new_number}") } } // TODO: Eventually remove this document upgrade code From 6f057478d4ce449a8886f54acbe2623171fdc42e Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Wed, 17 Jun 2026 21:50:55 -0700 Subject: [PATCH 2/2] Fix names with non-numeric suffixes blocking the base name --- editor/src/messages/portfolio/portfolio_message_handler.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index cc10791e6d..357fc69d91 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -1902,7 +1902,10 @@ impl PortfolioMessageHandler { .iter() .filter(|&&id| exclude != Some(id)) .filter_map(|&id| self.document_details(id)) - .filter_map(|doc| doc.name.strip_prefix(untitled).map(|rest| rest.trim().parse::().unwrap_or(1))) + .filter_map(|doc| { + let rest = doc.name.strip_prefix(untitled)?.trim(); + if rest.is_empty() { Some(1) } else { rest.parse::().ok() } + }) .collect::>(); // Pick the lowest number not already in use (a match always exists since the range is unbounded)