1use gpui::{App, Context, Entity, SharedString};
2use language::Buffer;
3use project::Project;
4use std::ops::Range;
5
6// TODO: Find a better home for `Direction`.
7//
8// This should live in an ancestor crate of `editor` and `inline_completion`,
9// but at time of writing there isn't an obvious spot.
10#[derive(Copy, Clone, PartialEq, Eq)]
11pub enum Direction {
12 Prev,
13 Next,
14}
15
16#[derive(Clone)]
17pub struct InlineCompletion {
18 /// The ID of the completion, if it has one.
19 pub id: Option<SharedString>,
20 pub edits: Vec<(Range<language::Anchor>, String)>,
21 pub edit_preview: Option<language::EditPreview>,
22}
23
24pub enum DataCollectionState {
25 /// The provider doesn't support data collection.
26 Unsupported,
27 /// Data collection is enabled.
28 Enabled { is_project_open_source: bool },
29 /// Data collection is disabled or unanswered.
30 Disabled { is_project_open_source: bool },
31}
32
33impl DataCollectionState {
34 pub fn is_supported(&self) -> bool {
35 !matches!(self, DataCollectionState::Unsupported { .. })
36 }
37
38 pub fn is_enabled(&self) -> bool {
39 matches!(self, DataCollectionState::Enabled { .. })
40 }
41
42 pub fn is_project_open_source(&self) -> bool {
43 match self {
44 Self::Enabled {
45 is_project_open_source,
46 }
47 | Self::Disabled {
48 is_project_open_source,
49 } => *is_project_open_source,
50 _ => false,
51 }
52 }
53}
54
55pub trait EditPredictionProvider: 'static + Sized {
56 fn name() -> &'static str;
57 fn display_name() -> &'static str;
58 fn show_completions_in_menu() -> bool;
59 fn show_tab_accept_marker() -> bool {
60 false
61 }
62 fn data_collection_state(&self, _cx: &App) -> DataCollectionState {
63 DataCollectionState::Unsupported
64 }
65 fn toggle_data_collection(&mut self, _cx: &mut App) {}
66 fn is_enabled(
67 &self,
68 buffer: &Entity<Buffer>,
69 cursor_position: language::Anchor,
70 cx: &App,
71 ) -> bool;
72 fn is_refreshing(&self) -> bool;
73 fn refresh(
74 &mut self,
75 project: Option<Entity<Project>>,
76 buffer: Entity<Buffer>,
77 cursor_position: language::Anchor,
78 debounce: bool,
79 cx: &mut Context<Self>,
80 );
81 fn needs_terms_acceptance(&self, _cx: &App) -> bool {
82 false
83 }
84 fn cycle(
85 &mut self,
86 buffer: Entity<Buffer>,
87 cursor_position: language::Anchor,
88 direction: Direction,
89 cx: &mut Context<Self>,
90 );
91 fn accept(&mut self, cx: &mut Context<Self>);
92 fn discard(&mut self, cx: &mut Context<Self>);
93 fn suggest(
94 &mut self,
95 buffer: &Entity<Buffer>,
96 cursor_position: language::Anchor,
97 cx: &mut Context<Self>,
98 ) -> Option<InlineCompletion>;
99}
100
101pub trait InlineCompletionProviderHandle {
102 fn name(&self) -> &'static str;
103 fn display_name(&self) -> &'static str;
104 fn is_enabled(
105 &self,
106 buffer: &Entity<Buffer>,
107 cursor_position: language::Anchor,
108 cx: &App,
109 ) -> bool;
110 fn show_completions_in_menu(&self) -> bool;
111 fn show_tab_accept_marker(&self) -> bool;
112 fn data_collection_state(&self, cx: &App) -> DataCollectionState;
113 fn toggle_data_collection(&self, cx: &mut App);
114 fn needs_terms_acceptance(&self, cx: &App) -> bool;
115 fn is_refreshing(&self, cx: &App) -> bool;
116 fn refresh(
117 &self,
118 project: Option<Entity<Project>>,
119 buffer: Entity<Buffer>,
120 cursor_position: language::Anchor,
121 debounce: bool,
122 cx: &mut App,
123 );
124 fn cycle(
125 &self,
126 buffer: Entity<Buffer>,
127 cursor_position: language::Anchor,
128 direction: Direction,
129 cx: &mut App,
130 );
131 fn accept(&self, cx: &mut App);
132 fn discard(&self, cx: &mut App);
133 fn suggest(
134 &self,
135 buffer: &Entity<Buffer>,
136 cursor_position: language::Anchor,
137 cx: &mut App,
138 ) -> Option<InlineCompletion>;
139}
140
141impl<T> InlineCompletionProviderHandle for Entity<T>
142where
143 T: EditPredictionProvider,
144{
145 fn name(&self) -> &'static str {
146 T::name()
147 }
148
149 fn display_name(&self) -> &'static str {
150 T::display_name()
151 }
152
153 fn show_completions_in_menu(&self) -> bool {
154 T::show_completions_in_menu()
155 }
156
157 fn show_tab_accept_marker(&self) -> bool {
158 T::show_tab_accept_marker()
159 }
160
161 fn data_collection_state(&self, cx: &App) -> DataCollectionState {
162 self.read(cx).data_collection_state(cx)
163 }
164
165 fn toggle_data_collection(&self, cx: &mut App) {
166 self.update(cx, |this, cx| this.toggle_data_collection(cx))
167 }
168
169 fn is_enabled(
170 &self,
171 buffer: &Entity<Buffer>,
172 cursor_position: language::Anchor,
173 cx: &App,
174 ) -> bool {
175 self.read(cx).is_enabled(buffer, cursor_position, cx)
176 }
177
178 fn needs_terms_acceptance(&self, cx: &App) -> bool {
179 self.read(cx).needs_terms_acceptance(cx)
180 }
181
182 fn is_refreshing(&self, cx: &App) -> bool {
183 self.read(cx).is_refreshing()
184 }
185
186 fn refresh(
187 &self,
188 project: Option<Entity<Project>>,
189 buffer: Entity<Buffer>,
190 cursor_position: language::Anchor,
191 debounce: bool,
192 cx: &mut App,
193 ) {
194 self.update(cx, |this, cx| {
195 this.refresh(project, buffer, cursor_position, debounce, cx)
196 })
197 }
198
199 fn cycle(
200 &self,
201 buffer: Entity<Buffer>,
202 cursor_position: language::Anchor,
203 direction: Direction,
204 cx: &mut App,
205 ) {
206 self.update(cx, |this, cx| {
207 this.cycle(buffer, cursor_position, direction, cx)
208 })
209 }
210
211 fn accept(&self, cx: &mut App) {
212 self.update(cx, |this, cx| this.accept(cx))
213 }
214
215 fn discard(&self, cx: &mut App) {
216 self.update(cx, |this, cx| this.discard(cx))
217 }
218
219 fn suggest(
220 &self,
221 buffer: &Entity<Buffer>,
222 cursor_position: language::Anchor,
223 cx: &mut App,
224 ) -> Option<InlineCompletion> {
225 self.update(cx, |this, cx| this.suggest(buffer, cursor_position, cx))
226 }
227}