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