delta_command.rs

  1use crate::slash_command::file_command::{FileCommandMetadata, FileSlashCommand};
  2use anyhow::Result;
  3use assistant_slash_command::{
  4    ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
  5};
  6use collections::HashSet;
  7use futures::future;
  8use gpui::{Task, WeakView, WindowContext};
  9use language::{BufferSnapshot, LspAdapterDelegate};
 10use std::sync::{atomic::AtomicBool, Arc};
 11use text::OffsetRangeExt;
 12use workspace::Workspace;
 13
 14pub(crate) struct DeltaSlashCommand;
 15
 16impl SlashCommand for DeltaSlashCommand {
 17    fn name(&self) -> String {
 18        "delta".into()
 19    }
 20
 21    fn description(&self) -> String {
 22        "Re-insert changed files".into()
 23    }
 24
 25    fn menu_text(&self) -> String {
 26        self.description()
 27    }
 28
 29    fn requires_argument(&self) -> bool {
 30        false
 31    }
 32
 33    fn complete_argument(
 34        self: Arc<Self>,
 35        _arguments: &[String],
 36        _cancellation_flag: Arc<AtomicBool>,
 37        _workspace: Option<WeakView<Workspace>>,
 38        _cx: &mut WindowContext,
 39    ) -> Task<Result<Vec<ArgumentCompletion>>> {
 40        unimplemented!()
 41    }
 42
 43    fn run(
 44        self: Arc<Self>,
 45        _arguments: &[String],
 46        context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
 47        context_buffer: BufferSnapshot,
 48        workspace: WeakView<Workspace>,
 49        delegate: Option<Arc<dyn LspAdapterDelegate>>,
 50        cx: &mut WindowContext,
 51    ) -> Task<Result<SlashCommandOutput>> {
 52        let mut paths = HashSet::default();
 53        let mut file_command_old_outputs = Vec::new();
 54        let mut file_command_new_outputs = Vec::new();
 55        for section in context_slash_command_output_sections.iter().rev() {
 56            if let Some(metadata) = section
 57                .metadata
 58                .as_ref()
 59                .and_then(|value| serde_json::from_value::<FileCommandMetadata>(value.clone()).ok())
 60            {
 61                if paths.insert(metadata.path.clone()) {
 62                    file_command_old_outputs.push(
 63                        context_buffer
 64                            .as_rope()
 65                            .slice(section.range.to_offset(&context_buffer)),
 66                    );
 67                    file_command_new_outputs.push(Arc::new(FileSlashCommand).run(
 68                        &[metadata.path.clone()],
 69                        context_slash_command_output_sections,
 70                        context_buffer.clone(),
 71                        workspace.clone(),
 72                        delegate.clone(),
 73                        cx,
 74                    ));
 75                }
 76            }
 77        }
 78
 79        cx.background_executor().spawn(async move {
 80            let mut output = SlashCommandOutput::default();
 81
 82            let file_command_new_outputs = future::join_all(file_command_new_outputs).await;
 83            for (old_text, new_output) in file_command_old_outputs
 84                .into_iter()
 85                .zip(file_command_new_outputs)
 86            {
 87                if let Ok(new_output) = new_output {
 88                    if let Some(file_command_range) = new_output.sections.first() {
 89                        let new_text = &new_output.text[file_command_range.range.clone()];
 90                        if old_text.chars().ne(new_text.chars()) {
 91                            output.sections.extend(new_output.sections.into_iter().map(
 92                                |section| SlashCommandOutputSection {
 93                                    range: output.text.len() + section.range.start
 94                                        ..output.text.len() + section.range.end,
 95                                    icon: section.icon,
 96                                    label: section.label,
 97                                    metadata: section.metadata,
 98                                },
 99                            ));
100                            output.text.push_str(&new_output.text);
101                        }
102                    }
103                }
104            }
105
106            Ok(output)
107        })
108    }
109}