@@ -18791,6 +18791,11 @@ impl Editor {
cx.emit(EditorEvent::BufferEdited);
cx.emit(SearchEvent::MatchesInvalidated);
if *singleton_buffer_edited {
+ if let Some(buffer) = multibuffer.read(cx).as_singleton() {
+ if buffer.read(cx).file().is_none() {
+ cx.emit(EditorEvent::TitleChanged);
+ }
+ }
if let Some(project) = &self.project {
#[allow(clippy::mutable_key_type)]
let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
@@ -2600,13 +2600,27 @@ impl MultiBuffer {
return title.into();
}
- if let Some(buffer) = self.as_singleton() {
- if let Some(file) = buffer.read(cx).file() {
- return file.file_name(cx).to_string_lossy();
- }
- }
+ self.as_singleton()
+ .and_then(|buffer| {
+ let buffer = buffer.read(cx);
- "untitled".into()
+ if let Some(file) = buffer.file() {
+ return Some(file.file_name(cx).to_string_lossy());
+ }
+
+ let title = buffer
+ .snapshot()
+ .chars()
+ .skip_while(|ch| ch.is_whitespace())
+ .take_while(|&ch| ch != '\n')
+ .take(40)
+ .collect::<String>()
+ .trim_end()
+ .to_string();
+
+ (!title.is_empty()).then(|| title.into())
+ })
+ .unwrap_or("untitled".into())
}
pub fn set_title(&mut self, title: String, cx: &mut Context<Self>) {
@@ -3651,3 +3651,59 @@ fn assert_line_indents(snapshot: &MultiBufferSnapshot) {
"reversed_line_indents({max_row})"
);
}
+
+#[gpui::test]
+fn test_new_empty_buffer_uses_untitled_title(cx: &mut App) {
+ let buffer = cx.new(|cx| Buffer::local("", cx));
+ let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
+
+ assert_eq!(multibuffer.read(cx).title(cx), "untitled");
+}
+
+#[gpui::test]
+fn test_new_empty_buffer_uses_untitled_title_when_only_contains_whitespace(cx: &mut App) {
+ let buffer = cx.new(|cx| Buffer::local("\n ", cx));
+ let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
+
+ assert_eq!(multibuffer.read(cx).title(cx), "untitled");
+}
+
+#[gpui::test]
+fn test_new_empty_buffer_takes_first_line_for_title(cx: &mut App) {
+ let buffer = cx.new(|cx| Buffer::local("Hello World\nSecond line", cx));
+ let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
+
+ assert_eq!(multibuffer.read(cx).title(cx), "Hello World");
+}
+
+#[gpui::test]
+fn test_new_empty_buffer_takes_trimmed_first_line_for_title(cx: &mut App) {
+ let buffer = cx.new(|cx| Buffer::local("\nHello, World ", cx));
+ let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
+
+ assert_eq!(multibuffer.read(cx).title(cx), "Hello, World");
+}
+
+#[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 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_buffers_title_can_be_set(cx: &mut App) {
+ let buffer = cx.new(|cx| Buffer::local("Hello World", cx));
+ let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
+ assert_eq!(multibuffer.read(cx).title(cx), "Hello World");
+
+ multibuffer.update(cx, |multibuffer, cx| {
+ multibuffer.set_title("Hey".into(), cx)
+ });
+ assert_eq!(multibuffer.read(cx).title(cx), "Hey");
+}
@@ -3027,7 +3027,7 @@ mod tests {
});
cx.read(|cx| {
assert!(editor.is_dirty(cx));
- assert_eq!(editor.read(cx).title(cx), "untitled");
+ assert_eq!(editor.read(cx).title(cx), "hi");
});
// When the save completes, the buffer's title is updated and the language is assigned based