Introduce dynamic tab titles for unsaved files based on buffer content (#32353)
https://github.com/user-attachments/assets/0bb08784-251c-4221-890a-2d6b3fb94e0f For new, unsaved files: - If a buffer has no content, or contains only whitespace, use `untitled` - If a buffer has content, take the first 40 chars of the first line | Sublime | VS Code | Zed | |---------|---------|-----| | <img width="227" alt="SCR-20250608-ouux" src="https://github.com/user-attachments/assets/d02b1e50-5775-4252-86e6-6c9d3f6c72fb" /> | <img width="230" alt="SCR-20250608-ousn" src="https://github.com/user-attachments/assets/7c9c016b-642f-4a80-9bc1-8c9bdc7bbd32" /> | <img width="242" alt="SCR-20250608-ovbg" src="https://github.com/user-attachments/assets/c7f4be5c-5bba-4a2a-b477-1392ca938cd5" /> | Note that this implementation also trims all leading whitespace, so that if the buffer has any non-whitespace content, we use it. VS Code and Sublime do not do this. | Sublime | VS Code | Zed | |---------|---------|-----| | <img width="233" alt="SCR-20250608-oviq" src="https://github.com/user-attachments/assets/ccffecc6-0f46-4d1b-8739-740240bc067b" /> | <img width="198" alt="SCR-20250608-ovkq" src="https://github.com/user-attachments/assets/35c20149-f898-417b-aff3-dda22b8cc1f3" /> | <img width="233" alt="SCR-20250608-ovns" src="https://github.com/user-attachments/assets/2509e8f6-254b-4fcb-a0ea-e18e95bb685b" /> | Release Notes: - Introduced dynamic tab titles for unsaved files based on buffer content
This commit is contained in:
parent
23adff6ff2
commit
b15aef4310
4 changed files with 82 additions and 7 deletions
|
@ -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");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue