assistant_slash_command.rs

  1mod slash_command_registry;
  2
  3use anyhow::Result;
  4use gpui::{AnyElement, AppContext, ElementId, SharedString, Task, WeakView, WindowContext};
  5use language::{CodeLabel, LspAdapterDelegate};
  6use serde::{Deserialize, Serialize};
  7pub use slash_command_registry::*;
  8use std::{
  9    ops::Range,
 10    sync::{atomic::AtomicBool, Arc},
 11};
 12use workspace::{ui::IconName, Workspace};
 13
 14pub fn init(cx: &mut AppContext) {
 15    SlashCommandRegistry::default_global(cx);
 16}
 17
 18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 19pub enum AfterCompletion {
 20    /// Run the command
 21    Run,
 22    /// Continue composing the current argument, doesn't add a space
 23    Compose,
 24    /// Continue the command composition, adds a space
 25    Continue,
 26}
 27
 28impl From<bool> for AfterCompletion {
 29    fn from(value: bool) -> Self {
 30        if value {
 31            AfterCompletion::Run
 32        } else {
 33            AfterCompletion::Continue
 34        }
 35    }
 36}
 37
 38impl AfterCompletion {
 39    pub fn run(&self) -> bool {
 40        match self {
 41            AfterCompletion::Run => true,
 42            AfterCompletion::Compose | AfterCompletion::Continue => false,
 43        }
 44    }
 45}
 46
 47#[derive(Debug)]
 48pub struct ArgumentCompletion {
 49    /// The label to display for this completion.
 50    pub label: CodeLabel,
 51    /// The new text that should be inserted into the command when this completion is accepted.
 52    pub new_text: String,
 53    /// Whether the command should be run when accepting this completion.
 54    pub after_completion: AfterCompletion,
 55    /// Whether to replace the all arguments, or whether to treat this as an independent argument.
 56    pub replace_previous_arguments: bool,
 57}
 58
 59pub trait SlashCommand: 'static + Send + Sync {
 60    fn name(&self) -> String;
 61    fn label(&self, _cx: &AppContext) -> CodeLabel {
 62        CodeLabel::plain(self.name(), None)
 63    }
 64    fn description(&self) -> String;
 65    fn menu_text(&self) -> String;
 66    fn complete_argument(
 67        self: Arc<Self>,
 68        arguments: &[String],
 69        cancel: Arc<AtomicBool>,
 70        workspace: Option<WeakView<Workspace>>,
 71        cx: &mut WindowContext,
 72    ) -> Task<Result<Vec<ArgumentCompletion>>>;
 73    fn requires_argument(&self) -> bool;
 74    fn accepts_arguments(&self) -> bool {
 75        self.requires_argument()
 76    }
 77    fn run(
 78        self: Arc<Self>,
 79        arguments: &[String],
 80        workspace: WeakView<Workspace>,
 81        // TODO: We're just using the `LspAdapterDelegate` here because that is
 82        // what the extension API is already expecting.
 83        //
 84        // It may be that `LspAdapterDelegate` needs a more general name, or
 85        // perhaps another kind of delegate is needed here.
 86        delegate: Option<Arc<dyn LspAdapterDelegate>>,
 87        cx: &mut WindowContext,
 88    ) -> Task<Result<SlashCommandOutput>>;
 89}
 90
 91pub type RenderFoldPlaceholder = Arc<
 92    dyn Send
 93        + Sync
 94        + Fn(ElementId, Arc<dyn Fn(&mut WindowContext)>, &mut WindowContext) -> AnyElement,
 95>;
 96
 97#[derive(Debug, Default)]
 98pub struct SlashCommandOutput {
 99    pub text: String,
100    pub sections: Vec<SlashCommandOutputSection<usize>>,
101    pub run_commands_in_text: bool,
102}
103
104#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
105pub struct SlashCommandOutputSection<T> {
106    pub range: Range<T>,
107    pub icon: IconName,
108    pub label: SharedString,
109}