streaming_example_command.rs

  1use std::sync::atomic::AtomicBool;
  2use std::sync::Arc;
  3use std::time::Duration;
  4
  5use anyhow::Result;
  6use assistant_slash_command::{
  7    ArgumentCompletion, SlashCommand, SlashCommandContent, SlashCommandEvent,
  8    SlashCommandOutputSection, SlashCommandResult,
  9};
 10use feature_flags::FeatureFlag;
 11use futures::channel::mpsc;
 12use gpui::{Task, WeakView};
 13use language::{BufferSnapshot, LspAdapterDelegate};
 14use smol::stream::StreamExt;
 15use smol::Timer;
 16use ui::prelude::*;
 17use workspace::Workspace;
 18
 19pub struct StreamingExampleSlashCommandFeatureFlag;
 20
 21impl FeatureFlag for StreamingExampleSlashCommandFeatureFlag {
 22    const NAME: &'static str = "streaming-example-slash-command";
 23}
 24
 25pub(crate) struct StreamingExampleSlashCommand;
 26
 27impl SlashCommand for StreamingExampleSlashCommand {
 28    fn name(&self) -> String {
 29        "streaming-example".into()
 30    }
 31
 32    fn description(&self) -> String {
 33        "An example slash command that showcases streaming.".into()
 34    }
 35
 36    fn menu_text(&self) -> String {
 37        self.description()
 38    }
 39
 40    fn requires_argument(&self) -> bool {
 41        false
 42    }
 43
 44    fn complete_argument(
 45        self: Arc<Self>,
 46        _arguments: &[String],
 47        _cancel: Arc<AtomicBool>,
 48        _workspace: Option<WeakView<Workspace>>,
 49        _cx: &mut WindowContext,
 50    ) -> Task<Result<Vec<ArgumentCompletion>>> {
 51        Task::ready(Ok(Vec::new()))
 52    }
 53
 54    fn run(
 55        self: Arc<Self>,
 56        _arguments: &[String],
 57        _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
 58        _context_buffer: BufferSnapshot,
 59        _workspace: WeakView<Workspace>,
 60        _delegate: Option<Arc<dyn LspAdapterDelegate>>,
 61        cx: &mut WindowContext,
 62    ) -> Task<SlashCommandResult> {
 63        let (events_tx, events_rx) = mpsc::unbounded();
 64        cx.background_executor()
 65            .spawn(async move {
 66                events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection {
 67                    icon: IconName::FileRust,
 68                    label: "Section 1".into(),
 69                    metadata: None,
 70                }))?;
 71                events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
 72                    SlashCommandContent::Text {
 73                        text: "Hello".into(),
 74                        run_commands_in_text: false,
 75                    },
 76                )))?;
 77                events_tx.unbounded_send(Ok(SlashCommandEvent::EndSection))?;
 78
 79                Timer::after(Duration::from_secs(1)).await;
 80
 81                events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection {
 82                    icon: IconName::FileRust,
 83                    label: "Section 2".into(),
 84                    metadata: None,
 85                }))?;
 86                events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
 87                    SlashCommandContent::Text {
 88                        text: "World".into(),
 89                        run_commands_in_text: false,
 90                    },
 91                )))?;
 92                events_tx.unbounded_send(Ok(SlashCommandEvent::EndSection))?;
 93
 94                for n in 1..=10 {
 95                    Timer::after(Duration::from_secs(1)).await;
 96
 97                    events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection {
 98                        icon: IconName::StarFilled,
 99                        label: format!("Section {n}").into(),
100                        metadata: None,
101                    }))?;
102                    events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
103                        SlashCommandContent::Text {
104                            text: "lorem ipsum ".repeat(n).trim().into(),
105                            run_commands_in_text: false,
106                        },
107                    )))?;
108                    events_tx.unbounded_send(Ok(SlashCommandEvent::EndSection))?;
109                }
110
111                anyhow::Ok(())
112            })
113            .detach_and_log_err(cx);
114
115        Task::ready(Ok(events_rx.boxed()))
116    }
117}