active_command.rs

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