diff --git a/crates/editor/src/edit_prediction_tests.rs b/crates/editor/src/edit_prediction_tests.rs index 7d64dd9749c68cb0e436c1cfcb04e3458d052872..d897a670674cb23b075646c64f22e8b9bf0e4f90 100644 --- a/crates/editor/src/edit_prediction_tests.rs +++ b/crates/editor/src/edit_prediction_tests.rs @@ -1,12 +1,13 @@ use edit_prediction::EditPredictionProvider; -use gpui::{Entity, prelude::*}; +use gpui::{Entity, KeyBinding, Modifiers, prelude::*}; use indoc::indoc; use multi_buffer::{Anchor, MultiBufferSnapshot, ToPoint}; use std::ops::Range; use text::{Point, ToOffset}; use crate::{ - EditPrediction, editor_tests::init_test, test::editor_test_context::EditorTestContext, + AcceptEditPrediction, EditPrediction, MenuEditPredictionsPolicy, editor_tests::init_test, + test::editor_test_context::EditorTestContext, }; #[gpui::test] @@ -270,6 +271,63 @@ async fn test_edit_prediction_jump_disabled_for_non_zed_providers(cx: &mut gpui: }); } +#[gpui::test] +async fn test_edit_prediction_preview_cleanup_on_toggle_off(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + + // Bind `ctrl-shift-a` to accept the provided edit prediction. The actual key + // binding here doesn't matter, we simply need to confirm that holding the + // binding's modifiers triggers the edit prediction preview. + cx.update(|cx| cx.bind_keys([KeyBinding::new("ctrl-shift-a", AcceptEditPrediction, None)])); + + let mut cx = EditorTestContext::new(cx).await; + let provider = cx.new(|_| FakeEditPredictionProvider::default()); + assign_editor_completion_provider(provider.clone(), &mut cx); + cx.set_state("let x = ˇ;"); + + propose_edits(&provider, vec![(8..8, "42")], &mut cx); + cx.update_editor(|editor, window, cx| { + editor.set_menu_edit_predictions_policy(MenuEditPredictionsPolicy::ByProvider); + editor.update_visible_edit_prediction(window, cx) + }); + + cx.editor(|editor, _, _| { + assert!(editor.has_active_edit_prediction()); + }); + + // Simulate pressing the modifiers for `AcceptEditPrediction`, namely + // `ctrl-shift`, so that we can confirm that the edit prediction preview is + // activated. + let modifiers = Modifiers::control_shift(); + cx.simulate_modifiers_change(modifiers); + cx.run_until_parked(); + + cx.editor(|editor, _, _| { + assert!(editor.edit_prediction_preview_is_active()); + }); + + // Disable showing edit predictions without issuing a new modifiers changed + // event, to confirm that the edit prediction preview is still active. + cx.update_editor(|editor, window, cx| { + editor.set_show_edit_predictions(Some(false), window, cx); + }); + + cx.editor(|editor, _, _| { + assert!(!editor.has_active_edit_prediction()); + assert!(editor.edit_prediction_preview_is_active()); + }); + + // Now release the modifiers + // Simulate releasing all modifiers, ensuring that even with edit prediction + // disabled, the edit prediction preview is cleaned up. + cx.simulate_modifiers_change(Modifiers::none()); + cx.run_until_parked(); + + cx.editor(|editor, _, _| { + assert!(!editor.edit_prediction_preview_is_active()); + }); +} + fn assert_editor_active_edit_completion( cx: &mut EditorTestContext, assert: impl FnOnce(MultiBufferSnapshot, &Vec<(Range, String)>), @@ -395,7 +453,7 @@ impl EditPredictionProvider for FakeEditPredictionProvider { } fn show_completions_in_menu() -> bool { - false + true } fn supports_jump_to_edit() -> bool { diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index efd41e7465a0d375d957553d5c4cc283a78db276..9d2e2fc741c2ccce21a41fee4f9f32f20b1d33ab 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7576,7 +7576,14 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if self.show_edit_predictions_in_menu() { + // Ensure that the edit prediction preview is updated, even when not + // enabled, if there's an active edit prediction preview. + if self.show_edit_predictions_in_menu() + || matches!( + self.edit_prediction_preview, + EditPredictionPreview::Active { .. } + ) + { self.update_edit_prediction_preview(&modifiers, window, cx); }