active_command.rs

  1use super::{file_command::FilePlaceholder, SlashCommand, SlashCommandOutput};
  2use anyhow::{anyhow, Result};
  3use assistant_slash_command::SlashCommandOutputSection;
  4use editor::Editor;
  5use gpui::{AppContext, Task, WeakView};
  6use language::LspAdapterDelegate;
  7use std::{borrow::Cow, sync::Arc};
  8use ui::{IntoElement, WindowContext};
  9use workspace::Workspace;
 10
 11pub(crate) struct ActiveSlashCommand;
 12
 13impl SlashCommand for ActiveSlashCommand {
 14    fn name(&self) -> String {
 15        "active".into()
 16    }
 17
 18    fn description(&self) -> String {
 19        "insert active tab".into()
 20    }
 21
 22    fn tooltip_text(&self) -> String {
 23        "insert active tab".into()
 24    }
 25
 26    fn complete_argument(
 27        &self,
 28        _query: String,
 29        _cancel: std::sync::Arc<std::sync::atomic::AtomicBool>,
 30        _cx: &mut AppContext,
 31    ) -> Task<Result<Vec<String>>> {
 32        Task::ready(Err(anyhow!("this command does not require argument")))
 33    }
 34
 35    fn requires_argument(&self) -> bool {
 36        false
 37    }
 38
 39    fn run(
 40        self: Arc<Self>,
 41        _argument: Option<&str>,
 42        workspace: WeakView<Workspace>,
 43        _delegate: Arc<dyn LspAdapterDelegate>,
 44        cx: &mut WindowContext,
 45    ) -> Task<Result<SlashCommandOutput>> {
 46        let output = workspace.update(cx, |workspace, cx| {
 47            let Some(active_item) = workspace.active_item(cx) else {
 48                return Task::ready(Err(anyhow!("no active tab")));
 49            };
 50            let Some(buffer) = active_item
 51                .downcast::<Editor>()
 52                .and_then(|editor| editor.read(cx).buffer().read(cx).as_singleton())
 53            else {
 54                return Task::ready(Err(anyhow!("active tab is not an editor")));
 55            };
 56
 57            let snapshot = buffer.read(cx).snapshot();
 58            let path = snapshot.resolve_file_path(cx, true);
 59            let text = cx.background_executor().spawn({
 60                let path = path.clone();
 61                async move {
 62                    let path = path
 63                        .as_ref()
 64                        .map(|path| path.to_string_lossy())
 65                        .unwrap_or_else(|| Cow::Borrowed("untitled"));
 66
 67                    let mut output = String::with_capacity(path.len() + snapshot.len() + 9);
 68                    output.push_str("```");
 69                    output.push_str(&path);
 70                    output.push('\n');
 71                    for chunk in snapshot.as_rope().chunks() {
 72                        output.push_str(chunk);
 73                    }
 74                    if !output.ends_with('\n') {
 75                        output.push('\n');
 76                    }
 77                    output.push_str("```");
 78                    output
 79                }
 80            });
 81            cx.foreground_executor().spawn(async move {
 82                let text = text.await;
 83                let range = 0..text.len();
 84                Ok(SlashCommandOutput {
 85                    text,
 86                    sections: vec![SlashCommandOutputSection {
 87                        range,
 88                        render_placeholder: Arc::new(move |id, unfold, _| {
 89                            FilePlaceholder {
 90                                id,
 91                                path: path.clone(),
 92                                line_range: None,
 93                                unfold,
 94                            }
 95                            .into_any_element()
 96                        }),
 97                    }],
 98                })
 99            })
100        });
101        output.unwrap_or_else(|error| Task::ready(Err(error)))
102    }
103}