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, WeakEntity};
 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 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<WeakEntity<Workspace>>,
 49        _window: &mut Window,
 50        _cx: &mut App,
 51    ) -> Task<Result<Vec<ArgumentCompletion>>> {
 52        Task::ready(Ok(Vec::new()))
 53    }
 54
 55    fn run(
 56        self: Arc<Self>,
 57        _arguments: &[String],
 58        _context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
 59        _context_buffer: BufferSnapshot,
 60        _workspace: WeakEntity<Workspace>,
 61        _delegate: Option<Arc<dyn LspAdapterDelegate>>,
 62        _: &mut Window,
 63        cx: &mut App,
 64    ) -> Task<SlashCommandResult> {
 65        let (events_tx, events_rx) = mpsc::unbounded();
 66        cx.background_executor()
 67            .spawn(async move {
 68                events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection {
 69                    icon: IconName::FileRust,
 70                    label: "Section 1".into(),
 71                    metadata: None,
 72                }))?;
 73                events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
 74                    SlashCommandContent::Text {
 75                        text: "Hello".into(),
 76                        run_commands_in_text: false,
 77                    },
 78                )))?;
 79                events_tx.unbounded_send(Ok(SlashCommandEvent::EndSection))?;
 80
 81                Timer::after(Duration::from_secs(1)).await;
 82
 83                events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection {
 84                    icon: IconName::FileRust,
 85                    label: "Section 2".into(),
 86                    metadata: None,
 87                }))?;
 88                events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
 89                    SlashCommandContent::Text {
 90                        text: "World".into(),
 91                        run_commands_in_text: false,
 92                    },
 93                )))?;
 94                events_tx.unbounded_send(Ok(SlashCommandEvent::EndSection))?;
 95
 96                for n in 1..=10 {
 97                    Timer::after(Duration::from_secs(1)).await;
 98
 99                    events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection {
100                        icon: IconName::StarFilled,
101                        label: format!("Section {n}").into(),
102                        metadata: None,
103                    }))?;
104                    events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
105                        SlashCommandContent::Text {
106                            text: "lorem ipsum ".repeat(n).trim().into(),
107                            run_commands_in_text: false,
108                        },
109                    )))?;
110                    events_tx.unbounded_send(Ok(SlashCommandEvent::EndSection))?;
111                }
112
113                anyhow::Ok(())
114            })
115            .detach_and_log_err(cx);
116
117        Task::ready(Ok(events_rx.boxed()))
118    }
119}