Merge /active command into /tabs one (#16154)

Now, tabs have arguments, `active` (default, applied also for no
arguments case) and `all` to insert the active tab only or all tabs.

Release Notes:

- N/A
This commit is contained in:
Kirill Bulatov 2024-08-13 13:15:57 +03:00 committed by GitHub
parent c2b254a67a
commit 081cbcebd9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 93 additions and 137 deletions

View file

@ -3,7 +3,7 @@ use super::{
file_command::{build_entry_output_section, codeblock_fence_for_path},
SlashCommand, SlashCommandOutput,
};
use anyhow::{anyhow, Result};
use anyhow::Result;
use assistant_slash_command::ArgumentCompletion;
use collections::HashMap;
use editor::Editor;
@ -15,6 +15,44 @@ use workspace::Workspace;
pub(crate) struct TabsSlashCommand;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
enum TabsArgument {
#[default]
Active,
All,
}
impl TabsArgument {
fn for_query(mut query: String) -> Vec<Self> {
query.make_ascii_lowercase();
let query = query.trim();
let mut matches = Vec::new();
if Self::Active.name().contains(&query) {
matches.push(Self::Active);
}
if Self::All.name().contains(&query) {
matches.push(Self::All);
}
matches
}
fn name(&self) -> &'static str {
match self {
Self::Active => "active",
Self::All => "all",
}
}
fn from_name(name: &str) -> Option<Self> {
match name {
"active" => Some(Self::Active),
"all" => Some(Self::All),
_ => None,
}
}
}
impl SlashCommand for TabsSlashCommand {
fn name(&self) -> String {
"tabs".into()
@ -29,52 +67,79 @@ impl SlashCommand for TabsSlashCommand {
}
fn requires_argument(&self) -> bool {
false
true
}
fn complete_argument(
self: Arc<Self>,
_query: String,
query: String,
_cancel: Arc<std::sync::atomic::AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Err(anyhow!("this command does not require argument")))
let arguments = TabsArgument::for_query(query);
Task::ready(Ok(arguments
.into_iter()
.map(|arg| ArgumentCompletion {
label: arg.name().to_owned(),
new_text: arg.name().to_owned(),
run_command: true,
})
.collect()))
}
fn run(
self: Arc<Self>,
_argument: Option<&str>,
argument: Option<&str>,
workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let open_buffers = workspace.update(cx, |workspace, cx| {
let mut timestamps_by_entity_id = HashMap::default();
let mut open_buffers = Vec::new();
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 argument = argument
.and_then(TabsArgument::from_name)
.unwrap_or_default();
let open_buffers = workspace.update(cx, |workspace, cx| match argument {
TabsArgument::Active => {
let Some(active_item) = workspace.active_item(cx) else {
anyhow::bail!("no active item")
};
let Some(buffer) = active_item
.downcast::<Editor>()
.and_then(|editor| editor.read(cx).buffer().read(cx).as_singleton())
else {
anyhow::bail!("active item is not an editor")
};
let snapshot = buffer.read(cx).snapshot();
let full_path = snapshot.resolve_file_path(cx, true);
anyhow::Ok(vec![(full_path, snapshot, 0)])
}
TabsArgument::All => {
let mut timestamps_by_entity_id = HashMap::default();
let mut open_buffers = Vec::new();
for editor in workspace.items_of_type::<Editor>(cx) {
if let Some(buffer) = editor.read(cx).buffer().read(cx).as_singleton() {
if let Some(timestamp) = timestamps_by_entity_id.get(&editor.entity_id()) {
let snapshot = buffer.read(cx).snapshot();
let full_path = snapshot.resolve_file_path(cx, true);
open_buffers.push((full_path, snapshot, *timestamp));
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);
}
}
}
open_buffers
for editor in workspace.items_of_type::<Editor>(cx) {
if let Some(buffer) = editor.read(cx).buffer().read(cx).as_singleton() {
if let Some(timestamp) = timestamps_by_entity_id.get(&editor.entity_id()) {
let snapshot = buffer.read(cx).snapshot();
let full_path = snapshot.resolve_file_path(cx, true);
open_buffers.push((full_path, snapshot, *timestamp));
}
}
}
Ok(open_buffers)
}
});
match open_buffers {
Ok(mut open_buffers) => cx.background_executor().spawn(async move {
Ok(Ok(mut open_buffers)) => cx.background_executor().spawn(async move {
open_buffers.sort_by_key(|(_, _, timestamp)| *timestamp);
let mut sections = Vec::new();
@ -112,7 +177,7 @@ impl SlashCommand for TabsSlashCommand {
run_commands_in_text: has_diagnostics,
})
}),
Err(error) => Task::ready(Err(error)),
Ok(Err(error)) | Err(error) => Task::ready(Err(error)),
}
}
}