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}