1479 lines · 49.9 KB · Rust
RawDownload
use edit_prediction_types::{    EditPredictionDelegate, EditPredictionIconSet, PredictedCursorPosition,};use gpui::{    Entity, KeyBinding, KeybindingKeystroke, Keystroke, Modifiers, NoAction, Task, prelude::*,};use indoc::indoc;use language::EditPredictionsMode;use language::{Buffer, CodeLabel};use multi_buffer::{Anchor, MultiBufferSnapshot, ToPoint};use project::{Completion, CompletionResponse, CompletionSource};use std::{    ops::Range,    rc::Rc,    sync::{        Arc,        atomic::{self, AtomicUsize},    },};use text::{Point, ToOffset};use ui::prelude::*; use crate::{    AcceptEditPrediction, CompletionContext, CompletionProvider, EditPrediction,    EditPredictionKeybindAction, EditPredictionKeybindSurface, MenuEditPredictionsPolicy,    ShowCompletions,    editor_tests::{init_test, update_test_language_settings},    test::editor_test_context::EditorTestContext,};use rpc::proto::PeerId;use workspace::CollaboratorId; #[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_cursor_position_inside_insertion(cx: &mut gpui::TestAppContext) {    init_test(cx, |_| {        eprintln!("");    });     let mut cx = EditorTestContext::new(cx).await;    let provider = cx.new(|_| FakeEditPredictionDelegate::default());     assign_editor_completion_provider(provider.clone(), &mut cx);    // Buffer: "fn foo() {}" - we'll insert text and position cursor inside the insertion    cx.set_state("fn foo() ˇ{}");     // Insert "bar()" at offset 9, with cursor at offset 2 within the insertion (after "ba")    // This tests the case where cursor is inside newly inserted text    propose_edits_with_cursor_position_in_insertion(        &provider,        vec![(9..9, "bar()")],        9, // anchor at the insertion point        2, // offset 2 within "bar()" puts cursor after "ba"        &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(), "bar()");    });     accept_completion(&mut cx);     // Cursor should be inside the inserted text at "baˇr()"    cx.assert_editor_state("fn foo() baˇr(){}");} #[gpui::test]async fn test_edit_prediction_cursor_position_outside_edit(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);    // Buffer: "let x = ;" with cursor before semicolon - we'll insert "42" and position cursor elsewhere    cx.set_state("let x = ˇ;");     // Insert "42" at offset 8, but set cursor_position to offset 4 (the 'x')    // This tests that cursor moves to the predicted position, not the end of the edit    propose_edits_with_cursor_position(        &provider,        vec![(8..8, "42")],        Some(4), // cursor at offset 4 (the 'x'), NOT at the edit location        &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(), "42");    });     accept_completion(&mut cx);     // Cursor should be at offset 4 (the 'x'), not at the end of the inserted "42"    cx.assert_editor_state("let ˇx = 42;");} #[gpui::test]async fn test_edit_prediction_cursor_position_fallback(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 x = ˇ;");     // Propose an edit without a cursor position - should fall back to end of edit    propose_edits(&provider, vec![(8..8, "42")], &mut cx);    cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));     accept_completion(&mut cx);     // Cursor should be at the end of the inserted text (default behavior)    cx.assert_editor_state("let x = 42ˇ;")} #[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_refresh_suppressed_while_following(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 x = ˇ;");     propose_edits(&provider, vec![(8..8, "42")], &mut cx);     cx.update_editor(|editor, window, cx| {        editor.refresh_edit_prediction(false, false, window, cx);        editor.update_visible_edit_prediction(window, cx);    });     assert_eq!(        provider.read_with(&cx.cx, |provider, _| {            provider.refresh_count.load(atomic::Ordering::SeqCst)        }),        1    );    cx.editor(|editor, _, _| {        assert!(editor.active_edit_prediction.is_some());    });     cx.update_editor(|editor, window, cx| {        editor.leader_id = Some(CollaboratorId::PeerId(PeerId::default()));        editor.refresh_edit_prediction(false, false, window, cx);    });     assert_eq!(        provider.read_with(&cx.cx, |provider, _| {            provider.refresh_count.load(atomic::Ordering::SeqCst)        }),        1    );    cx.editor(|editor, _, _| {        assert!(editor.active_edit_prediction.is_none());    });     cx.update_editor(|editor, window, cx| {        editor.leader_id = None;        editor.refresh_edit_prediction(false, false, window, cx);    });     assert_eq!(        provider.read_with(&cx.cx, |provider, _| {            provider.refresh_count.load(atomic::Ordering::SeqCst)        }),        2    );} #[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());    });} #[gpui::test]async fn test_edit_prediction_preview_activates_when_prediction_arrives_with_modifier_held(    cx: &mut gpui::TestAppContext,) {    init_test(cx, |_| {});    load_default_keymap(cx);    update_test_language_settings(cx, &|settings| {        settings.edit_predictions.get_or_insert_default().mode = Some(EditPredictionsMode::Subtle);    });     let mut cx = EditorTestContext::new(cx).await;

Lines 1–500 of 1479.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t