diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index db37a64e16..dcacb20e49 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -18858,7 +18858,7 @@ impl Editor { cx.emit(EditorEvent::BufferEdited); cx.emit(SearchEvent::MatchesInvalidated); if *singleton_buffer_edited { - if let Some(buffer) = multibuffer.read(cx).as_singleton() { + if let Some(buffer) = edited_buffer { if buffer.read(cx).file().is_none() { cx.emit(EditorEvent::TitleChanged); } diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 53b3b53de8..1815f3dd10 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -2600,27 +2600,58 @@ impl MultiBuffer { return title.into(); } - self.as_singleton() - .and_then(|buffer| { - let buffer = buffer.read(cx); + if let Some(buffer) = self.as_singleton() { + let buffer = buffer.read(cx); - if let Some(file) = buffer.file() { - return Some(file.file_name(cx).to_string_lossy()); + if let Some(file) = buffer.file() { + return file.file_name(cx).to_string_lossy(); + } + + if let Some(title) = self.buffer_based_title(buffer) { + return title; + } + }; + + "untitled".into() + } + + fn buffer_based_title(&self, buffer: &Buffer) -> Option> { + let mut is_leading_whitespace = true; + let mut count = 0; + let mut prev_was_space = false; + let mut title = String::new(); + + for ch in buffer.snapshot().chars() { + if is_leading_whitespace && ch.is_whitespace() { + continue; + } + + is_leading_whitespace = false; + + if ch == '\n' || count >= 40 { + break; + } + + if ch.is_whitespace() { + if !prev_was_space { + title.push(' '); + count += 1; + prev_was_space = true; } + } else { + title.push(ch); + count += 1; + prev_was_space = false; + } + } - let title = buffer - .snapshot() - .chars() - .skip_while(|ch| ch.is_whitespace()) - .take_while(|&ch| ch != '\n') - .take(40) - .collect::() - .trim_end() - .to_string(); + let title = title.trim_end().to_string(); - (!title.is_empty()).then(|| title.into()) - }) - .unwrap_or("untitled".into()) + if !title.is_empty() { + return Some(title.into()); + } + + None } pub fn set_title(&mut self, title: String, cx: &mut Context) { diff --git a/crates/multi_buffer/src/multi_buffer_tests.rs b/crates/multi_buffer/src/multi_buffer_tests.rs index 65ea1189cb..824efa559f 100644 --- a/crates/multi_buffer/src/multi_buffer_tests.rs +++ b/crates/multi_buffer/src/multi_buffer_tests.rs @@ -3686,10 +3686,20 @@ fn test_new_empty_buffer_takes_trimmed_first_line_for_title(cx: &mut App) { #[gpui::test] fn test_new_empty_buffer_uses_truncated_first_line_for_title(cx: &mut App) { - let title_after = ["a", "b", "c", "d"] - .map(|letter| letter.repeat(10)) - .join(""); - let title = format!("{}{}", title_after, "e".repeat(10)); + let title = "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee"; + let title_after = "aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd"; + let buffer = cx.new(|cx| Buffer::local(title, cx)); + let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx)); + + assert_eq!(multibuffer.read(cx).title(cx), title_after); +} + +#[gpui::test] +fn test_new_empty_buffer_uses_truncated_first_line_for_title_after_merging_adjacent_spaces( + cx: &mut App, +) { + let title = "aaaaaaaaaabbbbbbbbbb ccccccccccddddddddddeeeeeeeeee"; + let title_after = "aaaaaaaaaabbbbbbbbbb ccccccccccddddddddd"; let buffer = cx.new(|cx| Buffer::local(title, cx)); let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));