assistant_slash_command.rs

  1mod slash_command_registry;
  2
  3use anyhow::Result;
  4use gpui::{AnyElement, AppContext, ElementId, SharedString, Task, WeakView, WindowContext};
  5use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate, OffsetRangeExt};
  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        context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
 81        context_buffer: BufferSnapshot,
 82        workspace: WeakView<Workspace>,
 83        // TODO: We're just using the `LspAdapterDelegate` here because that is
 84        // what the extension API is already expecting.
 85        //
 86        // It may be that `LspAdapterDelegate` needs a more general name, or
 87        // perhaps another kind of delegate is needed here.
 88        delegate: Option<Arc<dyn LspAdapterDelegate>>,
 89        cx: &mut WindowContext,
 90    ) -> Task<Result<SlashCommandOutput>>;
 91}
 92
 93pub type RenderFoldPlaceholder = Arc<
 94    dyn Send
 95        + Sync
 96        + Fn(ElementId, Arc<dyn Fn(&mut WindowContext)>, &mut WindowContext) -> AnyElement,
 97>;
 98
 99#[derive(Debug, Default, PartialEq)]
100pub struct SlashCommandOutput {
101    pub text: String,
102    pub sections: Vec<SlashCommandOutputSection<usize>>,
103    pub run_commands_in_text: bool,
104}
105
106#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
107pub struct SlashCommandOutputSection<T> {
108    pub range: Range<T>,
109    pub icon: IconName,
110    pub label: SharedString,
111    pub metadata: Option<serde_json::Value>,
112}
113
114impl SlashCommandOutputSection<language::Anchor> {
115    pub fn is_valid(&self, buffer: &language::TextBuffer) -> bool {
116        self.range.start.is_valid(buffer) && !self.range.to_offset(buffer).is_empty()
117    }
118}