use crate::slash_command::file_command::{FileCommandMetadata, FileSlashCommand}; use anyhow::Result; use assistant_slash_command::{ ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection, SlashCommandResult, }; use collections::HashSet; use futures::future; use gpui::{Task, WeakView, WindowContext}; use language::{BufferSnapshot, LspAdapterDelegate}; use std::sync::{atomic::AtomicBool, Arc}; use text::OffsetRangeExt; use workspace::Workspace; pub(crate) struct DeltaSlashCommand; impl SlashCommand for DeltaSlashCommand { fn name(&self) -> String { "delta".into() } fn description(&self) -> String { "Re-insert changed files".into() } fn menu_text(&self) -> String { self.description() } fn requires_argument(&self) -> bool { false } fn complete_argument( self: Arc, _arguments: &[String], _cancellation_flag: Arc, _workspace: Option>, _cx: &mut WindowContext, ) -> Task>> { unimplemented!() } fn run( self: Arc, _arguments: &[String], context_slash_command_output_sections: &[SlashCommandOutputSection], context_buffer: BufferSnapshot, workspace: WeakView, delegate: Option>, cx: &mut WindowContext, ) -> Task { let mut paths = HashSet::default(); let mut file_command_old_outputs = Vec::new(); let mut file_command_new_outputs = Vec::new(); for section in context_slash_command_output_sections.iter().rev() { if let Some(metadata) = section .metadata .as_ref() .and_then(|value| serde_json::from_value::(value.clone()).ok()) { if paths.insert(metadata.path.clone()) { file_command_old_outputs.push( context_buffer .as_rope() .slice(section.range.to_offset(&context_buffer)), ); file_command_new_outputs.push(Arc::new(FileSlashCommand).run( &[metadata.path.clone()], context_slash_command_output_sections, context_buffer.clone(), workspace.clone(), delegate.clone(), cx, )); } } } cx.background_executor().spawn(async move { let mut output = SlashCommandOutput::default(); let file_command_new_outputs = future::join_all(file_command_new_outputs).await; for (old_text, new_output) in file_command_old_outputs .into_iter() .zip(file_command_new_outputs) { if let Ok(new_output) = new_output { if let Ok(new_output) = SlashCommandOutput::from_event_stream(new_output).await { if let Some(file_command_range) = new_output.sections.first() { let new_text = &new_output.text[file_command_range.range.clone()]; if old_text.chars().ne(new_text.chars()) { output.sections.extend(new_output.sections.into_iter().map( |section| SlashCommandOutputSection { range: output.text.len() + section.range.start ..output.text.len() + section.range.end, icon: section.icon, label: section.label, metadata: section.metadata, }, )); output.text.push_str(&new_output.text); } } } } } Ok(output.to_event_stream()) }) } }