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 supports_jump_to_edit() -> bool {
65 true
66 }
67
68 fn data_collection_state(&self, _cx: &App) -> DataCollectionState {
69 DataCollectionState::Unsupported
70 }
71
72 fn usage(&self, _cx: &App) -> Option<EditPredictionUsage> {
73 None
74 }
75
76 fn toggle_data_collection(&mut self, _cx: &mut App) {}
77 fn is_enabled(
78 &self,
79 buffer: &Entity<Buffer>,
80 cursor_position: language::Anchor,
81 cx: &App,
82 ) -> bool;
83 fn is_refreshing(&self) -> bool;
84 fn refresh(
85 &mut self,
86 project: Option<Entity<Project>>,
87 buffer: Entity<Buffer>,
88 cursor_position: language::Anchor,
89 debounce: bool,
90 cx: &mut Context<Self>,
91 );
92 fn needs_terms_acceptance(&self, _cx: &App) -> bool {
93 false
94 }
95 fn cycle(
96 &mut self,
97 buffer: Entity<Buffer>,
98 cursor_position: language::Anchor,
99 direction: Direction,
100 cx: &mut Context<Self>,
101 );
102 fn accept(&mut self, cx: &mut Context<Self>);
103 fn discard(&mut self, cx: &mut Context<Self>);
104 fn suggest(
105 &mut self,
106 buffer: &Entity<Buffer>,
107 cursor_position: language::Anchor,
108 cx: &mut Context<Self>,
109 ) -> Option<EditPrediction>;
110}
111
112pub trait EditPredictionProviderHandle {
113 fn name(&self) -> &'static str;
114 fn display_name(&self) -> &'static str;
115 fn is_enabled(
116 &self,
117 buffer: &Entity<Buffer>,
118 cursor_position: language::Anchor,
119 cx: &App,
120 ) -> bool;
121 fn show_completions_in_menu(&self) -> bool;
122 fn show_tab_accept_marker(&self) -> bool;
123 fn supports_jump_to_edit(&self) -> bool;
124 fn data_collection_state(&self, cx: &App) -> DataCollectionState;
125 fn usage(&self, cx: &App) -> Option<EditPredictionUsage>;
126 fn toggle_data_collection(&self, cx: &mut App);
127 fn needs_terms_acceptance(&self, cx: &App) -> bool;
128 fn is_refreshing(&self, cx: &App) -> bool;
129 fn refresh(
130 &self,
131 project: Option<Entity<Project>>,
132 buffer: Entity<Buffer>,
133 cursor_position: language::Anchor,
134 debounce: bool,
135 cx: &mut App,
136 );
137 fn cycle(
138 &self,
139 buffer: Entity<Buffer>,
140 cursor_position: language::Anchor,
141 direction: Direction,
142 cx: &mut App,
143 );
144 fn accept(&self, cx: &mut App);
145 fn discard(&self, cx: &mut App);
146 fn suggest(
147 &self,
148 buffer: &Entity<Buffer>,
149 cursor_position: language::Anchor,
150 cx: &mut App,
151 ) -> Option<EditPrediction>;
152}
153
154impl<T> EditPredictionProviderHandle for Entity<T>
155where
156 T: EditPredictionProvider,
157{
158 fn name(&self) -> &'static str {
159 T::name()
160 }
161
162 fn display_name(&self) -> &'static str {
163 T::display_name()
164 }
165
166 fn show_completions_in_menu(&self) -> bool {
167 T::show_completions_in_menu()
168 }
169
170 fn show_tab_accept_marker(&self) -> bool {
171 T::show_tab_accept_marker()
172 }
173
174 fn supports_jump_to_edit(&self) -> bool {
175 T::supports_jump_to_edit()
176 }
177
178 fn data_collection_state(&self, cx: &App) -> DataCollectionState {
179 self.read(cx).data_collection_state(cx)
180 }
181
182 fn usage(&self, cx: &App) -> Option<EditPredictionUsage> {
183 self.read(cx).usage(cx)
184 }
185
186 fn toggle_data_collection(&self, cx: &mut App) {
187 self.update(cx, |this, cx| this.toggle_data_collection(cx))
188 }
189
190 fn is_enabled(
191 &self,
192 buffer: &Entity<Buffer>,
193 cursor_position: language::Anchor,
194 cx: &App,
195 ) -> bool {
196 self.read(cx).is_enabled(buffer, cursor_position, cx)
197 }
198
199 fn needs_terms_acceptance(&self, cx: &App) -> bool {
200 self.read(cx).needs_terms_acceptance(cx)
201 }
202
203 fn is_refreshing(&self, cx: &App) -> bool {
204 self.read(cx).is_refreshing()
205 }
206
207 fn refresh(
208 &self,
209 project: Option<Entity<Project>>,
210 buffer: Entity<Buffer>,
211 cursor_position: language::Anchor,
212 debounce: bool,
213 cx: &mut App,
214 ) {
215 self.update(cx, |this, cx| {
216 this.refresh(project, buffer, cursor_position, debounce, cx)
217 })
218 }
219
220 fn cycle(
221 &self,
222 buffer: Entity<Buffer>,
223 cursor_position: language::Anchor,
224 direction: Direction,
225 cx: &mut App,
226 ) {
227 self.update(cx, |this, cx| {
228 this.cycle(buffer, cursor_position, direction, cx)
229 })
230 }
231
232 fn accept(&self, cx: &mut App) {
233 self.update(cx, |this, cx| this.accept(cx))
234 }
235
236 fn discard(&self, cx: &mut App) {
237 self.update(cx, |this, cx| this.discard(cx))
238 }
239
240 fn suggest(
241 &self,
242 buffer: &Entity<Buffer>,
243 cursor_position: language::Anchor,
244 cx: &mut App,
245 ) -> Option<EditPrediction> {
246 self.update(cx, |this, cx| this.suggest(buffer, cursor_position, cx))
247 }
248}