15049 lines · 448.1 KB · Rust
RawDownload
use super::*;use crate::{    scroll::scroll_amount::ScrollAmount,    test::{        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,        editor_test_context::EditorTestContext, select_ranges,    },    JoinLines,};use futures::StreamExt;use gpui::{    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,    WindowBounds, WindowOptions,};use indoc::indoc;use language::{    language_settings::{        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,    },    BracketPairConfig,    Capability::ReadWrite,    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher,    LanguageName, Override, ParsedMarkdown, Point,};use language_settings::{Formatter, FormatterList, IndentGuideSettings};use multi_buffer::MultiBufferIndentGuide;use parking_lot::Mutex;use pretty_assertions::{assert_eq, assert_ne};use project::{buffer_store::BufferChangeSet, FakeFs};use project::{    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,    project_settings::{LspSettings, ProjectSettings},};use serde_json::{self, json};use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};use std::{    iter,    sync::atomic::{self, AtomicUsize},};use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};use unindent::Unindent;use util::{    assert_set_eq,    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},};use workspace::{    item::{FollowEvent, FollowableItem, Item, ItemHandle},    NavigationEntry, ViewId,}; #[gpui::test]fn test_edit_events(cx: &mut TestAppContext) {    init_test(cx, |_| {});     let buffer = cx.new_model(|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();        |cx| {            let view = cx.view().clone();            cx.subscribe(&view, 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, cx)        }    });     let editor2 = cx.add_window({        let events = events.clone();        |cx| {            cx.subscribe(                &cx.view().clone(),                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, 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, cx| editor.insert("X", 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, cx| editor.delete(&Delete, 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, cx| editor.undo(&Undo, 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, cx| editor.redo(&Redo, 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, cx| editor.undo(&Undo, 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, cx| editor.redo(&Redo, 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, cx| {        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));         editor.backspace(&Backspace, 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_model(|cx| {        let mut buf = language::Buffer::local("123456", cx);        buf.set_group_interval(group_interval);        buf    });    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));     _ = editor.update(cx, |editor, cx| {        editor.start_transaction_at(now, cx);        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));         editor.insert("cd", 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, cx);        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));        editor.insert("e", 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(None, 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, 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, cx);        editor.undo(&Undo, 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, 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, cx);        assert_eq!(editor.text(cx), "ab2cde6");        assert_eq!(editor.selections.ranges(cx), vec![6..6]);         // Test empty transactions.        editor.start_transaction_at(now, cx);        editor.end_transaction_at(now, cx);        editor.undo(&Undo, cx);        assert_eq!(editor.text(cx), "12cde6");    });} #[gpui::test]fn test_ime_composition(cx: &mut TestAppContext) {    init_test(cx, |_| {});     let buffer = cx.new_model(|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_model(|cx| MultiBuffer::singleton(buffer, cx));    cx.add_window(|cx| {        let mut editor = build_editor(buffer.clone(), cx);         // Start a new IME composition.        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, 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, "ā", 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(), cx);        assert_eq!(editor.text(cx), "abcde");        assert_eq!(editor.marked_text_ranges(cx), None);        editor.redo(&Default::default(), 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, 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(), 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, 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), "ę", 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(None, 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, 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, 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", 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(|cx| {        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);        build_editor(buffer, cx)    });     _ = editor.update(cx, |view, cx| {        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);    });    assert_eq!(        editor            .update(cx, |view, cx| view.selections.display_ranges(cx))            .unwrap(),        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]    );     _ = editor.update(cx, |view, cx| {        view.update_selection(            DisplayPoint::new(DisplayRow(3), 3),            0,            gpui::Point::<f32>::default(),            cx,        );    });     assert_eq!(        editor            .update(cx, |view, cx| view.selections.display_ranges(cx))            .unwrap(),        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]    );     _ = editor.update(cx, |view, cx| {        view.update_selection(            DisplayPoint::new(DisplayRow(1), 1),            0,            gpui::Point::<f32>::default(),            cx,        );    });     assert_eq!(        editor            .update(cx, |view, cx| view.selections.display_ranges(cx))            .unwrap(),        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]    );     _ = editor.update(cx, |view, cx| {        view.end_selection(cx);        view.update_selection(            DisplayPoint::new(DisplayRow(3), 3),            0,            gpui::Point::<f32>::default(),            cx,        );    });     assert_eq!(        editor            .update(cx, |view, cx| view.selections.display_ranges(cx))            .unwrap(),        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]    );     _ = editor.update(cx, |view, cx| {        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);        view.update_selection(            DisplayPoint::new(DisplayRow(0), 0),            0,            gpui::Point::<f32>::default(),            cx,        );    });     assert_eq!(        editor            .update(cx, |view, cx| view.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, |view, cx| {        view.end_selection(cx);    });     assert_eq!(        editor            .update(cx, |view, cx| view.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(|cx| {        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);        build_editor(buffer, cx)    });     _ = editor.update(cx, |view, cx| {        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);    });     _ = editor.update(cx, |view, cx| {        view.end_selection(cx);    });     _ = editor.update(cx, |view, cx| {        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);    });     _ = editor.update(cx, |view, cx| {        view.end_selection(cx);    });     assert_eq!(        editor            .update(cx, |view, cx| view.selections.display_ranges(cx))            .unwrap(),        [            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)        ]    );     _ = editor.update(cx, |view, cx| {        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);    });     _ = editor.update(cx, |view, cx| {        view.end_selection(cx);    });     assert_eq!(        editor            .update(cx, |view, cx| view.selections.display_ranges(cx))            .unwrap(),        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]    );} #[gpui::test]

Lines 1–500 of 15049.