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 menu_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 _workspace: WeakView<Workspace>,
31 _cx: &mut AppContext,
32 ) -> Task<Result<Vec<String>>> {
33 Task::ready(Err(anyhow!("this command does not require argument")))
34 }
35
36 fn requires_argument(&self) -> bool {
37 false
38 }
39
40 fn run(
41 self: Arc<Self>,
42 _argument: Option<&str>,
43 workspace: WeakView<Workspace>,
44 _delegate: Arc<dyn LspAdapterDelegate>,
45 cx: &mut WindowContext,
46 ) -> Task<Result<SlashCommandOutput>> {
47 let output = workspace.update(cx, |workspace, cx| {
48 let Some(active_item) = workspace.active_item(cx) else {
49 return Task::ready(Err(anyhow!("no active tab")));
50 };
51 let Some(buffer) = active_item
52 .downcast::<Editor>()
53 .and_then(|editor| editor.read(cx).buffer().read(cx).as_singleton())
54 else {
55 return Task::ready(Err(anyhow!("active tab is not an editor")));
56 };
57
58 let snapshot = buffer.read(cx).snapshot();
59 let path = snapshot.resolve_file_path(cx, true);
60 let text = cx.background_executor().spawn({
61 let path = path.clone();
62 async move {
63 let path = path
64 .as_ref()
65 .map(|path| path.to_string_lossy())
66 .unwrap_or_else(|| Cow::Borrowed("untitled"));
67
68 let mut output = String::with_capacity(path.len() + snapshot.len() + 9);
69 output.push_str("```");
70 output.push_str(&path);
71 output.push('\n');
72 for chunk in snapshot.as_rope().chunks() {
73 output.push_str(chunk);
74 }
75 if !output.ends_with('\n') {
76 output.push('\n');
77 }
78 output.push_str("```");
79 output
80 }
81 });
82 cx.foreground_executor().spawn(async move {
83 let text = text.await;
84 let range = 0..text.len();
85 Ok(SlashCommandOutput {
86 text,
87 sections: vec![SlashCommandOutputSection {
88 range,
89 render_placeholder: Arc::new(move |id, unfold, _| {
90 FilePlaceholder {
91 id,
92 path: path.clone(),
93 line_range: None,
94 unfold,
95 }
96 .into_any_element()
97 }),
98 }],
99 run_commands_in_text: false,
100 })
101 })
102 });
103 output.unwrap_or_else(|error| Task::ready(Err(error)))
104 }
105}