Introduce a new /tabs command (#12382)

This inserts the content of the open tabs sorted by recency.

Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra
2024-05-28 17:17:34 +02:00
committed by GitHub
co-authored by Nathan
parent b466a8b828
commit 371abd37f6
4 changed files with 174 additions and 77 deletions
@@ -1,9 +1,8 @@
use super::{file_command::FilePlaceholder, SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Result};
use assistant_slash_command::SlashCommandOutputSection;
use collections::HashMap;
use editor::Editor;
use gpui::{AppContext, Entity, Task, WeakView};
use gpui::{AppContext, Task, WeakView};
use language::LspAdapterDelegate;
use std::{borrow::Cow, sync::Arc};
use ui::{IntoElement, WindowContext};
@@ -45,79 +44,59 @@ impl SlashCommand for ActiveSlashCommand {
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let output = workspace.update(cx, |workspace, cx| {
let mut timestamps_by_entity_id = HashMap::default();
for pane in workspace.panes() {
let pane = pane.read(cx);
for entry in pane.activation_history() {
timestamps_by_entity_id.insert(entry.entity_id, entry.timestamp);
}
}
let Some(active_item) = workspace.active_item(cx) else {
return Task::ready(Err(anyhow!("no active tab")));
};
let Some(buffer) = active_item
.downcast::<Editor>()
.and_then(|editor| editor.read(cx).buffer().read(cx).as_singleton())
else {
return Task::ready(Err(anyhow!("active tab is not an editor")));
};
let mut most_recent_buffer = None;
for editor in workspace.items_of_type::<Editor>(cx) {
let Some(buffer) = editor.read(cx).buffer().read(cx).as_singleton() else {
continue;
};
let snapshot = buffer.read(cx).snapshot();
let path = snapshot.resolve_file_path(cx, true);
let text = cx.background_executor().spawn({
let path = path.clone();
async move {
let path = path
.as_ref()
.map(|path| path.to_string_lossy())
.unwrap_or_else(|| Cow::Borrowed("untitled"));
let timestamp = timestamps_by_entity_id
.get(&editor.entity_id())
.copied()
.unwrap_or_default();
if most_recent_buffer
.as_ref()
.map_or(true, |(_, prev_timestamp)| timestamp > *prev_timestamp)
{
most_recent_buffer = Some((buffer, timestamp));
}
}
if let Some((buffer, _)) = most_recent_buffer {
let snapshot = buffer.read(cx).snapshot();
let path = snapshot.resolve_file_path(cx, true);
let text = cx.background_executor().spawn({
let path = path.clone();
async move {
let path = path
.as_ref()
.map(|path| path.to_string_lossy())
.unwrap_or_else(|| Cow::Borrowed("untitled"));
let mut output = String::with_capacity(path.len() + snapshot.len() + 9);
output.push_str("```");
output.push_str(&path);
output.push('\n');
for chunk in snapshot.as_rope().chunks() {
output.push_str(chunk);
}
if !output.ends_with('\n') {
output.push('\n');
}
output.push_str("```");
output
let mut output = String::with_capacity(path.len() + snapshot.len() + 9);
output.push_str("```");
output.push_str(&path);
output.push('\n');
for chunk in snapshot.as_rope().chunks() {
output.push_str(chunk);
}
});
cx.foreground_executor().spawn(async move {
let text = text.await;
let range = 0..text.len();
Ok(SlashCommandOutput {
text,
sections: vec![SlashCommandOutputSection {
range,
render_placeholder: Arc::new(move |id, unfold, _| {
FilePlaceholder {
id,
path: path.clone(),
line_range: None,
unfold,
}
.into_any_element()
}),
}],
})
if !output.ends_with('\n') {
output.push('\n');
}
output.push_str("```");
output
}
});
cx.foreground_executor().spawn(async move {
let text = text.await;
let range = 0..text.len();
Ok(SlashCommandOutput {
text,
sections: vec![SlashCommandOutputSection {
range,
render_placeholder: Arc::new(move |id, unfold, _| {
FilePlaceholder {
id,
path: path.clone(),
line_range: None,
unfold,
}
.into_any_element()
}),
}],
})
} else {
Task::ready(Err(anyhow!("no recent buffer found")))
}
})
});
output.unwrap_or_else(|error| Task::ready(Err(error)))
}