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