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