1use edit_prediction_types::{
2 EditPredictionDelegate, EditPredictionIconSet, PredictedCursorPosition,
3};
4use gpui::{Entity, KeyBinding, Modifiers, prelude::*};
5use indoc::indoc;
6use multi_buffer::{Anchor, MultiBufferSnapshot, ToPoint};
7use std::{
8 ops::Range,
9 sync::{
10 Arc,
11 atomic::{self, AtomicUsize},
12 },
13};
14use text::{Point, ToOffset};
15use ui::prelude::*;
16
17use crate::{
18 AcceptEditPrediction, EditPrediction, MenuEditPredictionsPolicy, editor_tests::init_test,
19 test::editor_test_context::EditorTestContext,
20};
21use rpc::proto::PeerId;
22use workspace::CollaboratorId;
23
24#[gpui::test]
25async fn test_edit_prediction_insert(cx: &mut gpui::TestAppContext) {
26 init_test(cx, |_| {});
27
28 let mut cx = EditorTestContext::new(cx).await;
29 let provider = cx.new(|_| FakeEditPredictionDelegate::default());
30 assign_editor_completion_provider(provider.clone(), &mut cx);
31 cx.set_state("let absolute_zero_celsius = ˇ;");
32
33 propose_edits(&provider, vec![(28..28, "-273.15")], &mut cx);
34 cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));
35
36 assert_editor_active_edit_completion(&mut cx, |_, edits| {
37 assert_eq!(edits.len(), 1);
38 assert_eq!(edits[0].1.as_ref(), "-273.15");
39 });
40
41 accept_completion(&mut cx);
42
43 cx.assert_editor_state("let absolute_zero_celsius = -273.15ˇ;")
44}
45
46#[gpui::test]
47async fn test_edit_prediction_cursor_position_inside_insertion(cx: &mut gpui::TestAppContext) {
48 init_test(cx, |_| {
49 eprintln!("");
50 });
51
52 let mut cx = EditorTestContext::new(cx).await;
53 let provider = cx.new(|_| FakeEditPredictionDelegate::default());
54
55 assign_editor_completion_provider(provider.clone(), &mut cx);
56 // Buffer: "fn foo() {}" - we'll insert text and position cursor inside the insertion
57 cx.set_state("fn foo() ˇ{}");
58
59 // Insert "bar()" at offset 9, with cursor at offset 2 within the insertion (after "ba")
60 // This tests the case where cursor is inside newly inserted text
61 propose_edits_with_cursor_position_in_insertion(
62 &provider,
63 vec![(9..9, "bar()")],
64 9, // anchor at the insertion point
65 2, // offset 2 within "bar()" puts cursor after "ba"
66 &mut cx,
67 );
68 cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));
69
70 assert_editor_active_edit_completion(&mut cx, |_, edits| {
71 assert_eq!(edits.len(), 1);
72 assert_eq!(edits[0].1.as_ref(), "bar()");
73 });
74
75 accept_completion(&mut cx);
76
77 // Cursor should be inside the inserted text at "baˇr()"
78 cx.assert_editor_state("fn foo() baˇr(){}");
79}
80
81#[gpui::test]
82async fn test_edit_prediction_cursor_position_outside_edit(cx: &mut gpui::TestAppContext) {
83 init_test(cx, |_| {});
84
85 let mut cx = EditorTestContext::new(cx).await;
86 let provider = cx.new(|_| FakeEditPredictionDelegate::default());
87 assign_editor_completion_provider(provider.clone(), &mut cx);
88 // Buffer: "let x = ;" with cursor before semicolon - we'll insert "42" and position cursor elsewhere
89 cx.set_state("let x = ˇ;");
90
91 // Insert "42" at offset 8, but set cursor_position to offset 4 (the 'x')
92 // This tests that cursor moves to the predicted position, not the end of the edit
93 propose_edits_with_cursor_position(
94 &provider,
95 vec![(8..8, "42")],
96 Some(4), // cursor at offset 4 (the 'x'), NOT at the edit location
97 &mut cx,
98 );
99 cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));
100
101 assert_editor_active_edit_completion(&mut cx, |_, edits| {
102 assert_eq!(edits.len(), 1);
103 assert_eq!(edits[0].1.as_ref(), "42");
104 });
105
106 accept_completion(&mut cx);
107
108 // Cursor should be at offset 4 (the 'x'), not at the end of the inserted "42"
109 cx.assert_editor_state("let ˇx = 42;");
110}
111
112#[gpui::test]
113async fn test_edit_prediction_cursor_position_fallback(cx: &mut gpui::TestAppContext) {
114 init_test(cx, |_| {});
115
116 let mut cx = EditorTestContext::new(cx).await;
117 let provider = cx.new(|_| FakeEditPredictionDelegate::default());
118 assign_editor_completion_provider(provider.clone(), &mut cx);
119 cx.set_state("let x = ˇ;");
120
121 // Propose an edit without a cursor position - should fall back to end of edit
122 propose_edits(&provider, vec![(8..8, "42")], &mut cx);
123 cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));
124
125 accept_completion(&mut cx);
126
127 // Cursor should be at the end of the inserted text (default behavior)
128 cx.assert_editor_state("let x = 42ˇ;")
129}
130
131#[gpui::test]
132async fn test_edit_prediction_modification(cx: &mut gpui::TestAppContext) {
133 init_test(cx, |_| {});
134
135 let mut cx = EditorTestContext::new(cx).await;
136 let provider = cx.new(|_| FakeEditPredictionDelegate::default());
137 assign_editor_completion_provider(provider.clone(), &mut cx);
138 cx.set_state("let pi = ˇ\"foo\";");
139
140 propose_edits(&provider, vec![(9..14, "3.14159")], &mut cx);
141 cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));
142
143 assert_editor_active_edit_completion(&mut cx, |_, edits| {
144 assert_eq!(edits.len(), 1);
145 assert_eq!(edits[0].1.as_ref(), "3.14159");
146 });
147
148 accept_completion(&mut cx);
149
150 cx.assert_editor_state("let pi = 3.14159ˇ;")
151}
152
153#[gpui::test]
154async fn test_edit_prediction_jump_button(cx: &mut gpui::TestAppContext) {
155 init_test(cx, |_| {});
156
157 let mut cx = EditorTestContext::new(cx).await;
158 let provider = cx.new(|_| FakeEditPredictionDelegate::default());
159 assign_editor_completion_provider(provider.clone(), &mut cx);
160
161 // Cursor is 2+ lines above the proposed edit
162 cx.set_state(indoc! {"
163 line 0
164 line ˇ1
165 line 2
166 line 3
167 line
168 "});
169
170 propose_edits(
171 &provider,
172 vec![(Point::new(4, 3)..Point::new(4, 3), " 4")],
173 &mut cx,
174 );
175
176 cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));
177 assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {
178 assert_eq!(move_target.to_point(&snapshot), Point::new(4, 3));
179 });
180
181 // When accepting, cursor is moved to the proposed location
182 accept_completion(&mut cx);
183 cx.assert_editor_state(indoc! {"
184 line 0
185 line 1
186 line 2
187 line 3
188 linˇe
189 "});
190
191 // Cursor is 2+ lines below the proposed edit
192 cx.set_state(indoc! {"
193 line 0
194 line
195 line 2
196 line 3
197 line ˇ4
198 "});
199
200 propose_edits(
201 &provider,
202 vec![(Point::new(1, 3)..Point::new(1, 3), " 1")],
203 &mut cx,
204 );
205
206 cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));
207 assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {
208 assert_eq!(move_target.to_point(&snapshot), Point::new(1, 3));
209 });
210
211 // When accepting, cursor is moved to the proposed location
212 accept_completion(&mut cx);
213 cx.assert_editor_state(indoc! {"
214 line 0
215 linˇe
216 line 2
217 line 3
218 line 4
219 "});
220}
221
222#[gpui::test]
223async fn test_edit_prediction_invalidation_range(cx: &mut gpui::TestAppContext) {
224 init_test(cx, |_| {});
225
226 let mut cx = EditorTestContext::new(cx).await;
227 let provider = cx.new(|_| FakeEditPredictionDelegate::default());
228 assign_editor_completion_provider(provider.clone(), &mut cx);
229
230 // Cursor is 3+ lines above the proposed edit
231 cx.set_state(indoc! {"
232 line 0
233 line ˇ1
234 line 2
235 line 3
236 line 4
237 line
238 "});
239 let edit_location = Point::new(5, 3);
240
241 propose_edits(
242 &provider,
243 vec![(edit_location..edit_location, " 5")],
244 &mut cx,
245 );
246
247 cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));
248 assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {
249 assert_eq!(move_target.to_point(&snapshot), edit_location);
250 });
251
252 // If we move *towards* the completion, it stays active
253 cx.set_selections_state(indoc! {"
254 line 0
255 line 1
256 line ˇ2
257 line 3
258 line 4
259 line
260 "});
261 assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {
262 assert_eq!(move_target.to_point(&snapshot), edit_location);
263 });
264
265 // If we move *away* from the completion, it is discarded
266 cx.set_selections_state(indoc! {"
267 line ˇ0
268 line 1
269 line 2
270 line 3
271 line 4
272 line
273 "});
274 cx.editor(|editor, _, _| {
275 assert!(editor.active_edit_prediction.is_none());
276 });
277
278 // Cursor is 3+ lines below the proposed edit
279 cx.set_state(indoc! {"
280 line
281 line 1
282 line 2
283 line 3
284 line ˇ4
285 line 5
286 "});
287 let edit_location = Point::new(0, 3);
288
289 propose_edits(
290 &provider,
291 vec![(edit_location..edit_location, " 0")],
292 &mut cx,
293 );
294
295 cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));
296 assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {
297 assert_eq!(move_target.to_point(&snapshot), edit_location);
298 });
299
300 // If we move *towards* the completion, it stays active
301 cx.set_selections_state(indoc! {"
302 line
303 line 1
304 line 2
305 line ˇ3
306 line 4
307 line 5
308 "});
309 assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {
310 assert_eq!(move_target.to_point(&snapshot), edit_location);
311 });
312
313 // If we move *away* from the completion, it is discarded
314 cx.set_selections_state(indoc! {"
315 line
316 line 1
317 line 2
318 line 3
319 line 4
320 line ˇ5
321 "});
322 cx.editor(|editor, _, _| {
323 assert!(editor.active_edit_prediction.is_none());
324 });
325}
326
327#[gpui::test]
328async fn test_edit_prediction_jump_disabled_for_non_zed_providers(cx: &mut gpui::TestAppContext) {
329 init_test(cx, |_| {});
330
331 let mut cx = EditorTestContext::new(cx).await;
332 let provider = cx.new(|_| FakeNonZedEditPredictionDelegate::default());
333 assign_editor_completion_provider_non_zed(provider.clone(), &mut cx);
334
335 // Cursor is 2+ lines above the proposed edit
336 cx.set_state(indoc! {"
337 line 0
338 line ˇ1
339 line 2
340 line 3
341 line
342 "});
343
344 propose_edits_non_zed(
345 &provider,
346 vec![(Point::new(4, 3)..Point::new(4, 3), " 4")],
347 &mut cx,
348 );
349
350 cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));
351
352 // For non-Zed providers, there should be no move completion (jump functionality disabled)
353 cx.editor(|editor, _, _| {
354 if let Some(completion_state) = &editor.active_edit_prediction {
355 // Should be an Edit prediction, not a Move prediction
356 match &completion_state.completion {
357 EditPrediction::Edit { .. } => {
358 // This is expected for non-Zed providers
359 }
360 EditPrediction::MoveWithin { .. } | EditPrediction::MoveOutside { .. } => {
361 panic!(
362 "Non-Zed providers should not show Move predictions (jump functionality)"
363 );
364 }
365 }
366 }
367 });
368}
369
370#[gpui::test]
371async fn test_edit_prediction_refresh_suppressed_while_following(cx: &mut gpui::TestAppContext) {
372 init_test(cx, |_| {});
373
374 let mut cx = EditorTestContext::new(cx).await;
375 let provider = cx.new(|_| FakeEditPredictionDelegate::default());
376 assign_editor_completion_provider(provider.clone(), &mut cx);
377 cx.set_state("let x = ˇ;");
378
379 propose_edits(&provider, vec![(8..8, "42")], &mut cx);
380
381 cx.update_editor(|editor, window, cx| {
382 editor.refresh_edit_prediction(false, false, window, cx);
383 editor.update_visible_edit_prediction(window, cx);
384 });
385
386 assert_eq!(
387 provider.read_with(&cx.cx, |provider, _| {
388 provider.refresh_count.load(atomic::Ordering::SeqCst)
389 }),
390 1
391 );
392 cx.editor(|editor, _, _| {
393 assert!(editor.active_edit_prediction.is_some());
394 });
395
396 cx.update_editor(|editor, window, cx| {
397 editor.leader_id = Some(CollaboratorId::PeerId(PeerId::default()));
398 editor.refresh_edit_prediction(false, false, window, cx);
399 });
400
401 assert_eq!(
402 provider.read_with(&cx.cx, |provider, _| {
403 provider.refresh_count.load(atomic::Ordering::SeqCst)
404 }),
405 1
406 );
407 cx.editor(|editor, _, _| {
408 assert!(editor.active_edit_prediction.is_none());
409 });
410
411 cx.update_editor(|editor, window, cx| {
412 editor.leader_id = None;
413 editor.refresh_edit_prediction(false, false, window, cx);
414 });
415
416 assert_eq!(
417 provider.read_with(&cx.cx, |provider, _| {
418 provider.refresh_count.load(atomic::Ordering::SeqCst)
419 }),
420 2
421 );
422}
423
424#[gpui::test]
425async fn test_edit_prediction_preview_cleanup_on_toggle_off(cx: &mut gpui::TestAppContext) {
426 init_test(cx, |_| {});
427
428 // Bind `ctrl-shift-a` to accept the provided edit prediction. The actual key
429 // binding here doesn't matter, we simply need to confirm that holding the
430 // binding's modifiers triggers the edit prediction preview.
431 cx.update(|cx| cx.bind_keys([KeyBinding::new("ctrl-shift-a", AcceptEditPrediction, None)]));
432
433 let mut cx = EditorTestContext::new(cx).await;
434 let provider = cx.new(|_| FakeEditPredictionDelegate::default());
435 assign_editor_completion_provider(provider.clone(), &mut cx);
436 cx.set_state("let x = ˇ;");
437
438 propose_edits(&provider, vec![(8..8, "42")], &mut cx);
439 cx.update_editor(|editor, window, cx| {
440 editor.set_menu_edit_predictions_policy(MenuEditPredictionsPolicy::ByProvider);
441 editor.update_visible_edit_prediction(window, cx)
442 });
443
444 cx.editor(|editor, _, _| {
445 assert!(editor.has_active_edit_prediction());
446 });
447
448 // Simulate pressing the modifiers for `AcceptEditPrediction`, namely
449 // `ctrl-shift`, so that we can confirm that the edit prediction preview is
450 // activated.
451 let modifiers = Modifiers::control_shift();
452 cx.simulate_modifiers_change(modifiers);
453 cx.run_until_parked();
454
455 cx.editor(|editor, _, _| {
456 assert!(editor.edit_prediction_preview_is_active());
457 });
458
459 // Disable showing edit predictions without issuing a new modifiers changed
460 // event, to confirm that the edit prediction preview is still active.
461 cx.update_editor(|editor, window, cx| {
462 editor.set_show_edit_predictions(Some(false), window, cx);
463 });
464
465 cx.editor(|editor, _, _| {
466 assert!(!editor.has_active_edit_prediction());
467 assert!(editor.edit_prediction_preview_is_active());
468 });
469
470 // Now release the modifiers
471 // Simulate releasing all modifiers, ensuring that even with edit prediction
472 // disabled, the edit prediction preview is cleaned up.
473 cx.simulate_modifiers_change(Modifiers::none());
474 cx.run_until_parked();
475
476 cx.editor(|editor, _, _| {
477 assert!(!editor.edit_prediction_preview_is_active());
478 });
479}
480
481fn assert_editor_active_edit_completion(
482 cx: &mut EditorTestContext,
483 assert: impl FnOnce(MultiBufferSnapshot, &Vec<(Range<Anchor>, Arc<str>)>),
484) {
485 cx.editor(|editor, _, cx| {
486 let completion_state = editor
487 .active_edit_prediction
488 .as_ref()
489 .expect("editor has no active completion");
490
491 if let EditPrediction::Edit { edits, .. } = &completion_state.completion {
492 assert(editor.buffer().read(cx).snapshot(cx), edits);
493 } else {
494 panic!("expected edit completion");
495 }
496 })
497}
498
499fn assert_editor_active_move_completion(
500 cx: &mut EditorTestContext,
501 assert: impl FnOnce(MultiBufferSnapshot, Anchor),
502) {
503 cx.editor(|editor, _, cx| {
504 let completion_state = editor
505 .active_edit_prediction
506 .as_ref()
507 .expect("editor has no active completion");
508
509 if let EditPrediction::MoveWithin { target, .. } = &completion_state.completion {
510 assert(editor.buffer().read(cx).snapshot(cx), *target);
511 } else {
512 panic!("expected move completion");
513 }
514 })
515}
516
517fn accept_completion(cx: &mut EditorTestContext) {
518 cx.update_editor(|editor, window, cx| {
519 editor.accept_edit_prediction(&crate::AcceptEditPrediction, window, cx)
520 })
521}
522
523fn propose_edits<T: ToOffset>(
524 provider: &Entity<FakeEditPredictionDelegate>,
525 edits: Vec<(Range<T>, &str)>,
526 cx: &mut EditorTestContext,
527) {
528 propose_edits_with_cursor_position(provider, edits, None, cx);
529}
530
531fn propose_edits_with_cursor_position<T: ToOffset>(
532 provider: &Entity<FakeEditPredictionDelegate>,
533 edits: Vec<(Range<T>, &str)>,
534 cursor_offset: Option<usize>,
535 cx: &mut EditorTestContext,
536) {
537 let snapshot = cx.buffer_snapshot();
538 let cursor_position = cursor_offset
539 .map(|offset| PredictedCursorPosition::at_anchor(snapshot.anchor_after(offset)));
540 let edits = edits.into_iter().map(|(range, text)| {
541 let range = snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end);
542 (range, text.into())
543 });
544
545 cx.update(|_, cx| {
546 provider.update(cx, |provider, _| {
547 provider.set_edit_prediction(Some(edit_prediction_types::EditPrediction::Local {
548 id: None,
549 edits: edits.collect(),
550 cursor_position,
551 edit_preview: None,
552 }))
553 })
554 });
555}
556
557fn propose_edits_with_cursor_position_in_insertion<T: ToOffset>(
558 provider: &Entity<FakeEditPredictionDelegate>,
559 edits: Vec<(Range<T>, &str)>,
560 anchor_offset: usize,
561 offset_within_insertion: usize,
562 cx: &mut EditorTestContext,
563) {
564 let snapshot = cx.buffer_snapshot();
565 // Use anchor_before (left bias) so the anchor stays at the insertion point
566 // rather than moving past the inserted text
567 let cursor_position = Some(PredictedCursorPosition::new(
568 snapshot.anchor_before(anchor_offset),
569 offset_within_insertion,
570 ));
571 let edits = edits.into_iter().map(|(range, text)| {
572 let range = snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end);
573 (range, text.into())
574 });
575
576 cx.update(|_, cx| {
577 provider.update(cx, |provider, _| {
578 provider.set_edit_prediction(Some(edit_prediction_types::EditPrediction::Local {
579 id: None,
580 edits: edits.collect(),
581 cursor_position,
582 edit_preview: None,
583 }))
584 })
585 });
586}
587
588fn assign_editor_completion_provider(
589 provider: Entity<FakeEditPredictionDelegate>,
590 cx: &mut EditorTestContext,
591) {
592 cx.update_editor(|editor, window, cx| {
593 editor.set_edit_prediction_provider(Some(provider), window, cx);
594 })
595}
596
597fn propose_edits_non_zed<T: ToOffset>(
598 provider: &Entity<FakeNonZedEditPredictionDelegate>,
599 edits: Vec<(Range<T>, &str)>,
600 cx: &mut EditorTestContext,
601) {
602 let snapshot = cx.buffer_snapshot();
603 let edits = edits.into_iter().map(|(range, text)| {
604 let range = snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end);
605 (range, text.into())
606 });
607
608 cx.update(|_, cx| {
609 provider.update(cx, |provider, _| {
610 provider.set_edit_prediction(Some(edit_prediction_types::EditPrediction::Local {
611 id: None,
612 edits: edits.collect(),
613 cursor_position: None,
614 edit_preview: None,
615 }))
616 })
617 });
618}
619
620fn assign_editor_completion_provider_non_zed(
621 provider: Entity<FakeNonZedEditPredictionDelegate>,
622 cx: &mut EditorTestContext,
623) {
624 cx.update_editor(|editor, window, cx| {
625 editor.set_edit_prediction_provider(Some(provider), window, cx);
626 })
627}
628
629#[derive(Default, Clone)]
630pub struct FakeEditPredictionDelegate {
631 pub completion: Option<edit_prediction_types::EditPrediction>,
632 pub refresh_count: Arc<AtomicUsize>,
633}
634
635impl FakeEditPredictionDelegate {
636 pub fn set_edit_prediction(
637 &mut self,
638 completion: Option<edit_prediction_types::EditPrediction>,
639 ) {
640 self.completion = completion;
641 }
642}
643
644impl EditPredictionDelegate for FakeEditPredictionDelegate {
645 fn name() -> &'static str {
646 "fake-completion-provider"
647 }
648
649 fn display_name() -> &'static str {
650 "Fake Completion Provider"
651 }
652
653 fn show_predictions_in_menu() -> bool {
654 true
655 }
656
657 fn supports_jump_to_edit() -> bool {
658 true
659 }
660
661 fn icons(&self, _cx: &gpui::App) -> EditPredictionIconSet {
662 EditPredictionIconSet::new(IconName::ZedPredict)
663 }
664
665 fn is_enabled(
666 &self,
667 _buffer: &gpui::Entity<language::Buffer>,
668 _cursor_position: language::Anchor,
669 _cx: &gpui::App,
670 ) -> bool {
671 true
672 }
673
674 fn is_refreshing(&self, _cx: &gpui::App) -> bool {
675 false
676 }
677
678 fn refresh(
679 &mut self,
680 _buffer: gpui::Entity<language::Buffer>,
681 _cursor_position: language::Anchor,
682 _debounce: bool,
683 _cx: &mut gpui::Context<Self>,
684 ) {
685 self.refresh_count.fetch_add(1, atomic::Ordering::SeqCst);
686 }
687
688 fn accept(&mut self, _cx: &mut gpui::Context<Self>) {}
689
690 fn discard(
691 &mut self,
692 _reason: edit_prediction_types::EditPredictionDiscardReason,
693 _cx: &mut gpui::Context<Self>,
694 ) {
695 }
696
697 fn suggest<'a>(
698 &mut self,
699 _buffer: &gpui::Entity<language::Buffer>,
700 _cursor_position: language::Anchor,
701 _cx: &mut gpui::Context<Self>,
702 ) -> Option<edit_prediction_types::EditPrediction> {
703 self.completion.clone()
704 }
705}
706
707#[derive(Default, Clone)]
708pub struct FakeNonZedEditPredictionDelegate {
709 pub completion: Option<edit_prediction_types::EditPrediction>,
710}
711
712impl FakeNonZedEditPredictionDelegate {
713 pub fn set_edit_prediction(
714 &mut self,
715 completion: Option<edit_prediction_types::EditPrediction>,
716 ) {
717 self.completion = completion;
718 }
719}
720
721impl EditPredictionDelegate for FakeNonZedEditPredictionDelegate {
722 fn name() -> &'static str {
723 "fake-non-zed-provider"
724 }
725
726 fn display_name() -> &'static str {
727 "Fake Non-Zed Provider"
728 }
729
730 fn show_predictions_in_menu() -> bool {
731 false
732 }
733
734 fn supports_jump_to_edit() -> bool {
735 false
736 }
737
738 fn icons(&self, _cx: &gpui::App) -> EditPredictionIconSet {
739 EditPredictionIconSet::new(IconName::ZedPredict)
740 }
741
742 fn is_enabled(
743 &self,
744 _buffer: &gpui::Entity<language::Buffer>,
745 _cursor_position: language::Anchor,
746 _cx: &gpui::App,
747 ) -> bool {
748 true
749 }
750
751 fn is_refreshing(&self, _cx: &gpui::App) -> bool {
752 false
753 }
754
755 fn refresh(
756 &mut self,
757 _buffer: gpui::Entity<language::Buffer>,
758 _cursor_position: language::Anchor,
759 _debounce: bool,
760 _cx: &mut gpui::Context<Self>,
761 ) {
762 }
763
764 fn accept(&mut self, _cx: &mut gpui::Context<Self>) {}
765
766 fn discard(
767 &mut self,
768 _reason: edit_prediction_types::EditPredictionDiscardReason,
769 _cx: &mut gpui::Context<Self>,
770 ) {
771 }
772
773 fn suggest<'a>(
774 &mut self,
775 _buffer: &gpui::Entity<language::Buffer>,
776 _cursor_position: language::Anchor,
777 _cx: &mut gpui::Context<Self>,
778 ) -> Option<edit_prediction_types::EditPrediction> {
779 self.completion.clone()
780 }
781}