23248 lines · 703.8 KB · Rust
RawDownload
use super::*;use crate::{    JoinLines,    code_context_menus::CodeContextMenu,    inline_completion_tests::FakeInlineCompletionProvider,    linked_editing_ranges::LinkedEditingRanges,    scroll::scroll_amount::ScrollAmount,    test::{        assert_text_with_selections, build_editor,        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},        editor_test_context::EditorTestContext,        select_ranges,    },};use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};use futures::StreamExt;use gpui::{    BackgroundExecutor, DismissEvent, Rgba, SemanticVersion, TestAppContext, UpdateGlobal,    VisualTestContext, WindowBounds, WindowOptions, div,};use indoc::indoc;use language::{    BracketPairConfig,    Capability::ReadWrite,    DiagnosticSourceKind, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher,    LanguageName, Override, Point,    language_settings::{        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings, FormatterList,        LanguageSettingsContent, LspInsertMode, PrettierSettings, SelectedFormatter,    },    tree_sitter_python,};use language_settings::{Formatter, IndentGuideSettings};use lsp::CompletionParams;use multi_buffer::{IndentGuide, PathKey};use parking_lot::Mutex;use pretty_assertions::{assert_eq, assert_ne};use project::{    FakeFs,    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},    project_settings::{LspSettings, ProjectSettings},};use serde_json::{self, json};use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};use std::{    iter,    sync::atomic::{self, AtomicUsize},};use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};use text::ToPoint as _;use unindent::Unindent;use util::{    assert_set_eq, path,    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},    uri,};use workspace::{    CloseActiveItem, CloseAllItems, CloseInactiveItems, MoveItemToPaneInDirection, NavigationEntry,    OpenOptions, ViewId,    item::{FollowEvent, FollowableItem, Item, ItemHandle, SaveOptions},}; #[gpui::test]fn test_edit_events(cx: &mut TestAppContext) {    init_test(cx, |_| {});     let buffer = cx.new(|cx| {        let mut buffer = language::Buffer::local("123456", cx);        buffer.set_group_interval(Duration::from_secs(1));        buffer    });     let events = Rc::new(RefCell::new(Vec::new()));    let editor1 = cx.add_window({        let events = events.clone();        |window, cx| {            let entity = cx.entity().clone();            cx.subscribe_in(                &entity,                window,                move |_, _, event: &EditorEvent, _, _| match event {                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),                    EditorEvent::BufferEdited => {                        events.borrow_mut().push(("editor1", "buffer edited"))                    }                    _ => {}                },            )            .detach();            Editor::for_buffer(buffer.clone(), None, window, cx)        }    });     let editor2 = cx.add_window({        let events = events.clone();        |window, cx| {            cx.subscribe_in(                &cx.entity().clone(),                window,                move |_, _, event: &EditorEvent, _, _| match event {                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),                    EditorEvent::BufferEdited => {                        events.borrow_mut().push(("editor2", "buffer edited"))                    }                    _ => {}                },            )            .detach();            Editor::for_buffer(buffer.clone(), None, window, cx)        }    });     assert_eq!(mem::take(&mut *events.borrow_mut()), []);     // Mutating editor 1 will emit an `Edited` event only for that editor.    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));    assert_eq!(        mem::take(&mut *events.borrow_mut()),        [            ("editor1", "edited"),            ("editor1", "buffer edited"),            ("editor2", "buffer edited"),        ]    );     // Mutating editor 2 will emit an `Edited` event only for that editor.    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));    assert_eq!(        mem::take(&mut *events.borrow_mut()),        [            ("editor2", "edited"),            ("editor1", "buffer edited"),            ("editor2", "buffer edited"),        ]    );     // Undoing on editor 1 will emit an `Edited` event only for that editor.    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));    assert_eq!(        mem::take(&mut *events.borrow_mut()),        [            ("editor1", "edited"),            ("editor1", "buffer edited"),            ("editor2", "buffer edited"),        ]    );     // Redoing on editor 1 will emit an `Edited` event only for that editor.    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));    assert_eq!(        mem::take(&mut *events.borrow_mut()),        [            ("editor1", "edited"),            ("editor1", "buffer edited"),            ("editor2", "buffer edited"),        ]    );     // Undoing on editor 2 will emit an `Edited` event only for that editor.    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));    assert_eq!(        mem::take(&mut *events.borrow_mut()),        [            ("editor2", "edited"),            ("editor1", "buffer edited"),            ("editor2", "buffer edited"),        ]    );     // Redoing on editor 2 will emit an `Edited` event only for that editor.    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));    assert_eq!(        mem::take(&mut *events.borrow_mut()),        [            ("editor2", "edited"),            ("editor1", "buffer edited"),            ("editor2", "buffer edited"),        ]    );     // No event is emitted when the mutation is a no-op.    _ = editor2.update(cx, |editor, window, cx| {        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {            s.select_ranges([0..0])        });         editor.backspace(&Backspace, window, cx);    });    assert_eq!(mem::take(&mut *events.borrow_mut()), []);} #[gpui::test]fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {    init_test(cx, |_| {});     let mut now = Instant::now();    let group_interval = Duration::from_millis(1);    let buffer = cx.new(|cx| {        let mut buf = language::Buffer::local("123456", cx);        buf.set_group_interval(group_interval);        buf    });    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));     _ = editor.update(cx, |editor, window, cx| {        editor.start_transaction_at(now, window, cx);        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {            s.select_ranges([2..4])        });         editor.insert("cd", window, cx);        editor.end_transaction_at(now, cx);        assert_eq!(editor.text(cx), "12cd56");        assert_eq!(editor.selections.ranges(cx), vec![4..4]);         editor.start_transaction_at(now, window, cx);        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {            s.select_ranges([4..5])        });        editor.insert("e", window, cx);        editor.end_transaction_at(now, cx);        assert_eq!(editor.text(cx), "12cde6");        assert_eq!(editor.selections.ranges(cx), vec![5..5]);         now += group_interval + Duration::from_millis(1);        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {            s.select_ranges([2..2])        });         // Simulate an edit in another editor        buffer.update(cx, |buffer, cx| {            buffer.start_transaction_at(now, cx);            buffer.edit([(0..1, "a")], None, cx);            buffer.edit([(1..1, "b")], None, cx);            buffer.end_transaction_at(now, cx);        });         assert_eq!(editor.text(cx), "ab2cde6");        assert_eq!(editor.selections.ranges(cx), vec![3..3]);         // Last transaction happened past the group interval in a different editor.        // Undo it individually and don't restore selections.        editor.undo(&Undo, window, cx);        assert_eq!(editor.text(cx), "12cde6");        assert_eq!(editor.selections.ranges(cx), vec![2..2]);         // First two transactions happened within the group interval in this editor.        // Undo them together and restore selections.        editor.undo(&Undo, window, cx);        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.        assert_eq!(editor.text(cx), "123456");        assert_eq!(editor.selections.ranges(cx), vec![0..0]);         // Redo the first two transactions together.        editor.redo(&Redo, window, cx);        assert_eq!(editor.text(cx), "12cde6");        assert_eq!(editor.selections.ranges(cx), vec![5..5]);         // Redo the last transaction on its own.        editor.redo(&Redo, window, cx);        assert_eq!(editor.text(cx), "ab2cde6");        assert_eq!(editor.selections.ranges(cx), vec![6..6]);         // Test empty transactions.        editor.start_transaction_at(now, window, cx);        editor.end_transaction_at(now, cx);        editor.undo(&Undo, window, cx);        assert_eq!(editor.text(cx), "12cde6");    });} #[gpui::test]fn test_ime_composition(cx: &mut TestAppContext) {    init_test(cx, |_| {});     let buffer = cx.new(|cx| {        let mut buffer = language::Buffer::local("abcde", cx);        // Ensure automatic grouping doesn't occur.        buffer.set_group_interval(Duration::ZERO);        buffer    });     let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));    cx.add_window(|window, cx| {        let mut editor = build_editor(buffer.clone(), window, cx);         // Start a new IME composition.        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);        assert_eq!(editor.text(cx), "äbcde");        assert_eq!(            editor.marked_text_ranges(cx),            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])        );         // Finalize IME composition.        editor.replace_text_in_range(None, "ā", window, cx);        assert_eq!(editor.text(cx), "ābcde");        assert_eq!(editor.marked_text_ranges(cx), None);         // IME composition edits are grouped and are undone/redone at once.        editor.undo(&Default::default(), window, cx);        assert_eq!(editor.text(cx), "abcde");        assert_eq!(editor.marked_text_ranges(cx), None);        editor.redo(&Default::default(), window, cx);        assert_eq!(editor.text(cx), "ābcde");        assert_eq!(editor.marked_text_ranges(cx), None);         // Start a new IME composition.        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);        assert_eq!(            editor.marked_text_ranges(cx),            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])        );         // Undoing during an IME composition cancels it.        editor.undo(&Default::default(), window, cx);        assert_eq!(editor.text(cx), "ābcde");        assert_eq!(editor.marked_text_ranges(cx), None);         // Start a new IME composition with an invalid marked range, ensuring it gets clipped.        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);        assert_eq!(editor.text(cx), "ābcdè");        assert_eq!(            editor.marked_text_ranges(cx),            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])        );         // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.        editor.replace_text_in_range(Some(4..999), "ę", window, cx);        assert_eq!(editor.text(cx), "ābcdę");        assert_eq!(editor.marked_text_ranges(cx), None);         // Start a new IME composition with multiple cursors.        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {            s.select_ranges([                OffsetUtf16(1)..OffsetUtf16(1),                OffsetUtf16(3)..OffsetUtf16(3),                OffsetUtf16(5)..OffsetUtf16(5),            ])        });        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");        assert_eq!(            editor.marked_text_ranges(cx),            Some(vec![                OffsetUtf16(0)..OffsetUtf16(3),                OffsetUtf16(4)..OffsetUtf16(7),                OffsetUtf16(8)..OffsetUtf16(11)            ])        );         // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");        assert_eq!(            editor.marked_text_ranges(cx),            Some(vec![                OffsetUtf16(1)..OffsetUtf16(2),                OffsetUtf16(5)..OffsetUtf16(6),                OffsetUtf16(9)..OffsetUtf16(10)            ])        );         // Finalize IME composition with multiple cursors.        editor.replace_text_in_range(Some(9..10), "2", window, cx);        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");        assert_eq!(editor.marked_text_ranges(cx), None);         editor    });} #[gpui::test]fn test_selection_with_mouse(cx: &mut TestAppContext) {    init_test(cx, |_| {});     let editor = cx.add_window(|window, cx| {        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);        build_editor(buffer, window, cx)    });     _ = editor.update(cx, |editor, window, cx| {        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);    });    assert_eq!(        editor            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))            .unwrap(),        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]    );     _ = editor.update(cx, |editor, window, cx| {        editor.update_selection(            DisplayPoint::new(DisplayRow(3), 3),            0,            gpui::Point::<f32>::default(),            window,            cx,        );    });     assert_eq!(        editor            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))            .unwrap(),        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]    );     _ = editor.update(cx, |editor, window, cx| {        editor.update_selection(            DisplayPoint::new(DisplayRow(1), 1),            0,            gpui::Point::<f32>::default(),            window,            cx,        );    });     assert_eq!(        editor            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))            .unwrap(),        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]    );     _ = editor.update(cx, |editor, window, cx| {        editor.end_selection(window, cx);        editor.update_selection(            DisplayPoint::new(DisplayRow(3), 3),            0,            gpui::Point::<f32>::default(),            window,            cx,        );    });     assert_eq!(        editor            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))            .unwrap(),        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]    );     _ = editor.update(cx, |editor, window, cx| {        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);        editor.update_selection(            DisplayPoint::new(DisplayRow(0), 0),            0,            gpui::Point::<f32>::default(),            window,            cx,        );    });     assert_eq!(        editor            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))            .unwrap(),        [            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)        ]    );     _ = editor.update(cx, |editor, window, cx| {        editor.end_selection(window, cx);    });     assert_eq!(        editor            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))            .unwrap(),        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]    );} #[gpui::test]fn test_multiple_cursor_removal(cx: &mut TestAppContext) {    init_test(cx, |_| {});     let editor = cx.add_window(|window, cx| {        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);        build_editor(buffer, window, cx)    });     _ = editor.update(cx, |editor, window, cx| {        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);    });     _ = editor.update(cx, |editor, window, cx| {        editor.end_selection(window, cx);    });     _ = editor.update(cx, |editor, window, cx| {        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);    }); 

Lines 1–500 of 23248.