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