active_command.rs

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