567 lines · 16.1 KB · Rust
RawDownload
use edit_prediction_types::EditPredictionDelegate;use gpui::{Entity, KeyBinding, Modifiers, prelude::*};use indoc::indoc;use multi_buffer::{Anchor, MultiBufferSnapshot, ToPoint};use std::{ops::Range, sync::Arc};use text::{Point, ToOffset}; use crate::{    AcceptEditPrediction, EditPrediction, MenuEditPredictionsPolicy, editor_tests::init_test,    test::editor_test_context::EditorTestContext,}; #[gpui::test]async fn test_edit_prediction_insert(cx: &mut gpui::TestAppContext) {    init_test(cx, |_| {});     let mut cx = EditorTestContext::new(cx).await;    let provider = cx.new(|_| FakeEditPredictionDelegate::default());    assign_editor_completion_provider(provider.clone(), &mut cx);    cx.set_state("let absolute_zero_celsius = ˇ;");     propose_edits(&provider, vec![(28..28, "-273.15")], &mut cx);    cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));     assert_editor_active_edit_completion(&mut cx, |_, edits| {        assert_eq!(edits.len(), 1);        assert_eq!(edits[0].1.as_ref(), "-273.15");    });     accept_completion(&mut cx);     cx.assert_editor_state("let absolute_zero_celsius = -273.15ˇ;")} #[gpui::test]async fn test_edit_prediction_modification(cx: &mut gpui::TestAppContext) {    init_test(cx, |_| {});     let mut cx = EditorTestContext::new(cx).await;    let provider = cx.new(|_| FakeEditPredictionDelegate::default());    assign_editor_completion_provider(provider.clone(), &mut cx);    cx.set_state("let pi = ˇ\"foo\";");     propose_edits(&provider, vec![(9..14, "3.14159")], &mut cx);    cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));     assert_editor_active_edit_completion(&mut cx, |_, edits| {        assert_eq!(edits.len(), 1);        assert_eq!(edits[0].1.as_ref(), "3.14159");    });     accept_completion(&mut cx);     cx.assert_editor_state("let pi = 3.14159ˇ;")} #[gpui::test]async fn test_edit_prediction_jump_button(cx: &mut gpui::TestAppContext) {    init_test(cx, |_| {});     let mut cx = EditorTestContext::new(cx).await;    let provider = cx.new(|_| FakeEditPredictionDelegate::default());    assign_editor_completion_provider(provider.clone(), &mut cx);     // Cursor is 2+ lines above the proposed edit    cx.set_state(indoc! {"        line 0        line ˇ1        line 2        line 3        line    "});     propose_edits(        &provider,        vec![(Point::new(4, 3)..Point::new(4, 3), " 4")],        &mut cx,    );     cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));    assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {        assert_eq!(move_target.to_point(&snapshot), Point::new(4, 3));    });     // When accepting, cursor is moved to the proposed location    accept_completion(&mut cx);    cx.assert_editor_state(indoc! {"        line 0        line 1        line 2        line 3        linˇe    "});     // Cursor is 2+ lines below the proposed edit    cx.set_state(indoc! {"        line 0        line        line 2        line 3        line ˇ4    "});     propose_edits(        &provider,        vec![(Point::new(1, 3)..Point::new(1, 3), " 1")],        &mut cx,    );     cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));    assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {        assert_eq!(move_target.to_point(&snapshot), Point::new(1, 3));    });     // When accepting, cursor is moved to the proposed location    accept_completion(&mut cx);    cx.assert_editor_state(indoc! {"        line 0        linˇe        line 2        line 3        line 4    "});} #[gpui::test]async fn test_edit_prediction_invalidation_range(cx: &mut gpui::TestAppContext) {    init_test(cx, |_| {});     let mut cx = EditorTestContext::new(cx).await;    let provider = cx.new(|_| FakeEditPredictionDelegate::default());    assign_editor_completion_provider(provider.clone(), &mut cx);     // Cursor is 3+ lines above the proposed edit    cx.set_state(indoc! {"        line 0        line ˇ1        line 2        line 3        line 4        line    "});    let edit_location = Point::new(5, 3);     propose_edits(        &provider,        vec![(edit_location..edit_location, " 5")],        &mut cx,    );     cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));    assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {        assert_eq!(move_target.to_point(&snapshot), edit_location);    });     // If we move *towards* the completion, it stays active    cx.set_selections_state(indoc! {"        line 0        line 1        line ˇ2        line 3        line 4        line    "});    assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {        assert_eq!(move_target.to_point(&snapshot), edit_location);    });     // If we move *away* from the completion, it is discarded    cx.set_selections_state(indoc! {"        line ˇ0        line 1        line 2        line 3        line 4        line    "});    cx.editor(|editor, _, _| {        assert!(editor.active_edit_prediction.is_none());    });     // Cursor is 3+ lines below the proposed edit    cx.set_state(indoc! {"        line        line 1        line 2        line 3        line ˇ4        line 5    "});    let edit_location = Point::new(0, 3);     propose_edits(        &provider,        vec![(edit_location..edit_location, " 0")],        &mut cx,    );     cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));    assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {        assert_eq!(move_target.to_point(&snapshot), edit_location);    });     // If we move *towards* the completion, it stays active    cx.set_selections_state(indoc! {"        line        line 1        line 2        line ˇ3        line 4        line 5    "});    assert_editor_active_move_completion(&mut cx, |snapshot, move_target| {        assert_eq!(move_target.to_point(&snapshot), edit_location);    });     // If we move *away* from the completion, it is discarded    cx.set_selections_state(indoc! {"        line        line 1        line 2        line 3        line 4        line ˇ5    "});    cx.editor(|editor, _, _| {        assert!(editor.active_edit_prediction.is_none());    });} #[gpui::test]async fn test_edit_prediction_jump_disabled_for_non_zed_providers(cx: &mut gpui::TestAppContext) {    init_test(cx, |_| {});     let mut cx = EditorTestContext::new(cx).await;    let provider = cx.new(|_| FakeNonZedEditPredictionDelegate::default());    assign_editor_completion_provider_non_zed(provider.clone(), &mut cx);     // Cursor is 2+ lines above the proposed edit    cx.set_state(indoc! {"        line 0        line ˇ1        line 2        line 3        line    "});     propose_edits_non_zed(        &provider,        vec![(Point::new(4, 3)..Point::new(4, 3), " 4")],        &mut cx,    );     cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));     // For non-Zed providers, there should be no move completion (jump functionality disabled)    cx.editor(|editor, _, _| {        if let Some(completion_state) = &editor.active_edit_prediction {            // Should be an Edit prediction, not a Move prediction            match &completion_state.completion {                EditPrediction::Edit { .. } => {                    // This is expected for non-Zed providers                }                EditPrediction::MoveWithin { .. } | EditPrediction::MoveOutside { .. } => {                    panic!(                        "Non-Zed providers should not show Move predictions (jump functionality)"                    );                }            }        }    });} #[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(|_| FakeEditPredictionDelegate::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<Anchor>, Arc<str>)>),) {    cx.editor(|editor, _, cx| {        let completion_state = editor            .active_edit_prediction            .as_ref()            .expect("editor has no active completion");         if let EditPrediction::Edit { edits, .. } = &completion_state.completion {            assert(editor.buffer().read(cx).snapshot(cx), edits);        } else {            panic!("expected edit completion");        }    })} fn assert_editor_active_move_completion(    cx: &mut EditorTestContext,    assert: impl FnOnce(MultiBufferSnapshot, Anchor),) {    cx.editor(|editor, _, cx| {        let completion_state = editor            .active_edit_prediction            .as_ref()            .expect("editor has no active completion");         if let EditPrediction::MoveWithin { target, .. } = &completion_state.completion {            assert(editor.buffer().read(cx).snapshot(cx), *target);        } else {            panic!("expected move completion");        }    })} fn accept_completion(cx: &mut EditorTestContext) {    cx.update_editor(|editor, window, cx| {        editor.accept_edit_prediction(&crate::AcceptEditPrediction, window, cx)    })} fn propose_edits<T: ToOffset>(    provider: &Entity<FakeEditPredictionDelegate>,    edits: Vec<(Range<T>, &str)>,    cx: &mut EditorTestContext,) {    let snapshot = cx.buffer_snapshot();    let edits = edits.into_iter().map(|(range, text)| {        let range = snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end);        (range, text.into())    });     cx.update(|_, cx| {        provider.update(cx, |provider, _| {            provider.set_edit_prediction(Some(edit_prediction_types::EditPrediction::Local {                id: None,                edits: edits.collect(),                edit_preview: None,            }))        })    });} fn assign_editor_completion_provider(    provider: Entity<FakeEditPredictionDelegate>,    cx: &mut EditorTestContext,) {    cx.update_editor(|editor, window, cx| {        editor.set_edit_prediction_provider(Some(provider), window, cx);    })} fn propose_edits_non_zed<T: ToOffset>(    provider: &Entity<FakeNonZedEditPredictionDelegate>,    edits: Vec<(Range<T>, &str)>,    cx: &mut EditorTestContext,) {    let snapshot = cx.buffer_snapshot();    let edits = edits.into_iter().map(|(range, text)| {        let range = snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end);        (range, text.into())    });     cx.update(|_, cx| {        provider.update(cx, |provider, _| {            provider.set_edit_prediction(Some(edit_prediction_types::EditPrediction::Local {                id: None,                edits: edits.collect(),                edit_preview: None,            }))        })    });} fn assign_editor_completion_provider_non_zed(    provider: Entity<FakeNonZedEditPredictionDelegate>,    cx: &mut EditorTestContext,) {    cx.update_editor(|editor, window, cx| {        editor.set_edit_prediction_provider(Some(provider), window, cx);    })} #[derive(Default, Clone)]pub struct FakeEditPredictionDelegate {    pub completion: Option<edit_prediction_types::EditPrediction>,} impl FakeEditPredictionDelegate {    pub fn set_edit_prediction(        &mut self,        completion: Option<edit_prediction_types::EditPrediction>,    ) {        self.completion = completion;    }} impl EditPredictionDelegate for FakeEditPredictionDelegate {    fn name() -> &'static str {        "fake-completion-provider"    }     fn display_name() -> &'static str {        "Fake Completion Provider"    }     fn show_predictions_in_menu() -> bool {        true    }     fn supports_jump_to_edit() -> bool {        true    }     fn is_enabled(        &self,        _buffer: &gpui::Entity<language::Buffer>,        _cursor_position: language::Anchor,        _cx: &gpui::App,    ) -> bool {        true    }     fn is_refreshing(&self, _cx: &gpui::App) -> bool {        false    }     fn refresh(        &mut self,        _buffer: gpui::Entity<language::Buffer>,        _cursor_position: language::Anchor,        _debounce: bool,        _cx: &mut gpui::Context<Self>,    ) {    }     fn accept(&mut self, _cx: &mut gpui::Context<Self>) {}     fn discard(&mut self, _cx: &mut gpui::Context<Self>) {}     fn suggest<'a>(        &mut self,        _buffer: &gpui::Entity<language::Buffer>,        _cursor_position: language::Anchor,        _cx: &mut gpui::Context<Self>,    ) -> Option<edit_prediction_types::EditPrediction> {        self.completion.clone()    }}

Lines 1–500 of 567.