editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_hunks,
    6        editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
    7        expanded_hunks, expanded_hunks_background_highlights, select_ranges,
    8    },
    9    JoinLines,
   10};
   11use futures::StreamExt;
   12use gpui::{div, TestAppContext, VisualTestContext, WindowBounds, WindowOptions};
   13use indoc::indoc;
   14use language::{
   15    language_settings::{
   16        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   17    },
   18    BracketPairConfig,
   19    Capability::ReadWrite,
   20    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, Override, Point,
   21};
   22use parking_lot::Mutex;
   23use project::project_settings::{LspSettings, ProjectSettings};
   24use project::FakeFs;
   25use serde_json::{self, json};
   26use std::sync::atomic;
   27use std::sync::atomic::AtomicUsize;
   28use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   29use unindent::Unindent;
   30use util::{
   31    assert_set_eq,
   32    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   33};
   34use workspace::{
   35    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   36    NavigationEntry, ViewId,
   37};
   38
   39#[gpui::test]
   40fn test_edit_events(cx: &mut TestAppContext) {
   41    init_test(cx, |_| {});
   42
   43    let buffer = cx.new_model(|cx| {
   44        let mut buffer = language::Buffer::local("123456", cx);
   45        buffer.set_group_interval(Duration::from_secs(1));
   46        buffer
   47    });
   48
   49    let events = Rc::new(RefCell::new(Vec::new()));
   50    let editor1 = cx.add_window({
   51        let events = events.clone();
   52        |cx| {
   53            let view = cx.view().clone();
   54            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| {
   55                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
   56                    events.borrow_mut().push(("editor1", event.clone()));
   57                }
   58            })
   59            .detach();
   60            Editor::for_buffer(buffer.clone(), None, cx)
   61        }
   62    });
   63
   64    let editor2 = cx.add_window({
   65        let events = events.clone();
   66        |cx| {
   67            cx.subscribe(&cx.view().clone(), move |_, _, event: &EditorEvent, _| {
   68                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
   69                    events.borrow_mut().push(("editor2", event.clone()));
   70                }
   71            })
   72            .detach();
   73            Editor::for_buffer(buffer.clone(), None, cx)
   74        }
   75    });
   76
   77    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   78
   79    // Mutating editor 1 will emit an `Edited` event only for that editor.
   80    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   81    assert_eq!(
   82        mem::take(&mut *events.borrow_mut()),
   83        [
   84            ("editor1", EditorEvent::Edited),
   85            ("editor1", EditorEvent::BufferEdited),
   86            ("editor2", EditorEvent::BufferEdited),
   87        ]
   88    );
   89
   90    // Mutating editor 2 will emit an `Edited` event only for that editor.
   91    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
   92    assert_eq!(
   93        mem::take(&mut *events.borrow_mut()),
   94        [
   95            ("editor2", EditorEvent::Edited),
   96            ("editor1", EditorEvent::BufferEdited),
   97            ("editor2", EditorEvent::BufferEdited),
   98        ]
   99    );
  100
  101    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  102    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  103    assert_eq!(
  104        mem::take(&mut *events.borrow_mut()),
  105        [
  106            ("editor1", EditorEvent::Edited),
  107            ("editor1", EditorEvent::BufferEdited),
  108            ("editor2", EditorEvent::BufferEdited),
  109        ]
  110    );
  111
  112    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  113    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  114    assert_eq!(
  115        mem::take(&mut *events.borrow_mut()),
  116        [
  117            ("editor1", EditorEvent::Edited),
  118            ("editor1", EditorEvent::BufferEdited),
  119            ("editor2", EditorEvent::BufferEdited),
  120        ]
  121    );
  122
  123    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  124    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  125    assert_eq!(
  126        mem::take(&mut *events.borrow_mut()),
  127        [
  128            ("editor2", EditorEvent::Edited),
  129            ("editor1", EditorEvent::BufferEdited),
  130            ("editor2", EditorEvent::BufferEdited),
  131        ]
  132    );
  133
  134    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  135    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  136    assert_eq!(
  137        mem::take(&mut *events.borrow_mut()),
  138        [
  139            ("editor2", EditorEvent::Edited),
  140            ("editor1", EditorEvent::BufferEdited),
  141            ("editor2", EditorEvent::BufferEdited),
  142        ]
  143    );
  144
  145    // No event is emitted when the mutation is a no-op.
  146    _ = editor2.update(cx, |editor, cx| {
  147        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  148
  149        editor.backspace(&Backspace, cx);
  150    });
  151    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  152}
  153
  154#[gpui::test]
  155fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  156    init_test(cx, |_| {});
  157
  158    let mut now = Instant::now();
  159    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  160    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  161    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  162    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  163
  164    _ = editor.update(cx, |editor, cx| {
  165        editor.start_transaction_at(now, cx);
  166        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  167
  168        editor.insert("cd", cx);
  169        editor.end_transaction_at(now, cx);
  170        assert_eq!(editor.text(cx), "12cd56");
  171        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  172
  173        editor.start_transaction_at(now, cx);
  174        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  175        editor.insert("e", cx);
  176        editor.end_transaction_at(now, cx);
  177        assert_eq!(editor.text(cx), "12cde6");
  178        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  179
  180        now += group_interval + Duration::from_millis(1);
  181        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  182
  183        // Simulate an edit in another editor
  184        _ = buffer.update(cx, |buffer, cx| {
  185            buffer.start_transaction_at(now, cx);
  186            buffer.edit([(0..1, "a")], None, cx);
  187            buffer.edit([(1..1, "b")], None, cx);
  188            buffer.end_transaction_at(now, cx);
  189        });
  190
  191        assert_eq!(editor.text(cx), "ab2cde6");
  192        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  193
  194        // Last transaction happened past the group interval in a different editor.
  195        // Undo it individually and don't restore selections.
  196        editor.undo(&Undo, cx);
  197        assert_eq!(editor.text(cx), "12cde6");
  198        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  199
  200        // First two transactions happened within the group interval in this editor.
  201        // Undo them together and restore selections.
  202        editor.undo(&Undo, cx);
  203        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  204        assert_eq!(editor.text(cx), "123456");
  205        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  206
  207        // Redo the first two transactions together.
  208        editor.redo(&Redo, cx);
  209        assert_eq!(editor.text(cx), "12cde6");
  210        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  211
  212        // Redo the last transaction on its own.
  213        editor.redo(&Redo, cx);
  214        assert_eq!(editor.text(cx), "ab2cde6");
  215        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  216
  217        // Test empty transactions.
  218        editor.start_transaction_at(now, cx);
  219        editor.end_transaction_at(now, cx);
  220        editor.undo(&Undo, cx);
  221        assert_eq!(editor.text(cx), "12cde6");
  222    });
  223}
  224
  225#[gpui::test]
  226fn test_ime_composition(cx: &mut TestAppContext) {
  227    init_test(cx, |_| {});
  228
  229    let buffer = cx.new_model(|cx| {
  230        let mut buffer = language::Buffer::local("abcde", cx);
  231        // Ensure automatic grouping doesn't occur.
  232        buffer.set_group_interval(Duration::ZERO);
  233        buffer
  234    });
  235
  236    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  237    cx.add_window(|cx| {
  238        let mut editor = build_editor(buffer.clone(), cx);
  239
  240        // Start a new IME composition.
  241        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  242        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  243        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  244        assert_eq!(editor.text(cx), "äbcde");
  245        assert_eq!(
  246            editor.marked_text_ranges(cx),
  247            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  248        );
  249
  250        // Finalize IME composition.
  251        editor.replace_text_in_range(None, "ā", cx);
  252        assert_eq!(editor.text(cx), "ābcde");
  253        assert_eq!(editor.marked_text_ranges(cx), None);
  254
  255        // IME composition edits are grouped and are undone/redone at once.
  256        editor.undo(&Default::default(), cx);
  257        assert_eq!(editor.text(cx), "abcde");
  258        assert_eq!(editor.marked_text_ranges(cx), None);
  259        editor.redo(&Default::default(), cx);
  260        assert_eq!(editor.text(cx), "ābcde");
  261        assert_eq!(editor.marked_text_ranges(cx), None);
  262
  263        // Start a new IME composition.
  264        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  265        assert_eq!(
  266            editor.marked_text_ranges(cx),
  267            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  268        );
  269
  270        // Undoing during an IME composition cancels it.
  271        editor.undo(&Default::default(), cx);
  272        assert_eq!(editor.text(cx), "ābcde");
  273        assert_eq!(editor.marked_text_ranges(cx), None);
  274
  275        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  276        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  277        assert_eq!(editor.text(cx), "ābcdè");
  278        assert_eq!(
  279            editor.marked_text_ranges(cx),
  280            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  281        );
  282
  283        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  284        editor.replace_text_in_range(Some(4..999), "ę", cx);
  285        assert_eq!(editor.text(cx), "ābcdę");
  286        assert_eq!(editor.marked_text_ranges(cx), None);
  287
  288        // Start a new IME composition with multiple cursors.
  289        editor.change_selections(None, cx, |s| {
  290            s.select_ranges([
  291                OffsetUtf16(1)..OffsetUtf16(1),
  292                OffsetUtf16(3)..OffsetUtf16(3),
  293                OffsetUtf16(5)..OffsetUtf16(5),
  294            ])
  295        });
  296        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  297        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  298        assert_eq!(
  299            editor.marked_text_ranges(cx),
  300            Some(vec![
  301                OffsetUtf16(0)..OffsetUtf16(3),
  302                OffsetUtf16(4)..OffsetUtf16(7),
  303                OffsetUtf16(8)..OffsetUtf16(11)
  304            ])
  305        );
  306
  307        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  308        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  309        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  310        assert_eq!(
  311            editor.marked_text_ranges(cx),
  312            Some(vec![
  313                OffsetUtf16(1)..OffsetUtf16(2),
  314                OffsetUtf16(5)..OffsetUtf16(6),
  315                OffsetUtf16(9)..OffsetUtf16(10)
  316            ])
  317        );
  318
  319        // Finalize IME composition with multiple cursors.
  320        editor.replace_text_in_range(Some(9..10), "2", cx);
  321        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  322        assert_eq!(editor.marked_text_ranges(cx), None);
  323
  324        editor
  325    });
  326}
  327
  328#[gpui::test]
  329fn test_selection_with_mouse(cx: &mut TestAppContext) {
  330    init_test(cx, |_| {});
  331
  332    let editor = cx.add_window(|cx| {
  333        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  334        build_editor(buffer, cx)
  335    });
  336
  337    _ = editor.update(cx, |view, cx| {
  338        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  339    });
  340    assert_eq!(
  341        editor
  342            .update(cx, |view, cx| view.selections.display_ranges(cx))
  343            .unwrap(),
  344        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  345    );
  346
  347    _ = editor.update(cx, |view, cx| {
  348        view.update_selection(
  349            DisplayPoint::new(DisplayRow(3), 3),
  350            0,
  351            gpui::Point::<f32>::default(),
  352            cx,
  353        );
  354    });
  355
  356    assert_eq!(
  357        editor
  358            .update(cx, |view, cx| view.selections.display_ranges(cx))
  359            .unwrap(),
  360        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  361    );
  362
  363    _ = editor.update(cx, |view, cx| {
  364        view.update_selection(
  365            DisplayPoint::new(DisplayRow(1), 1),
  366            0,
  367            gpui::Point::<f32>::default(),
  368            cx,
  369        );
  370    });
  371
  372    assert_eq!(
  373        editor
  374            .update(cx, |view, cx| view.selections.display_ranges(cx))
  375            .unwrap(),
  376        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  377    );
  378
  379    _ = editor.update(cx, |view, cx| {
  380        view.end_selection(cx);
  381        view.update_selection(
  382            DisplayPoint::new(DisplayRow(3), 3),
  383            0,
  384            gpui::Point::<f32>::default(),
  385            cx,
  386        );
  387    });
  388
  389    assert_eq!(
  390        editor
  391            .update(cx, |view, cx| view.selections.display_ranges(cx))
  392            .unwrap(),
  393        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  394    );
  395
  396    _ = editor.update(cx, |view, cx| {
  397        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  398        view.update_selection(
  399            DisplayPoint::new(DisplayRow(0), 0),
  400            0,
  401            gpui::Point::<f32>::default(),
  402            cx,
  403        );
  404    });
  405
  406    assert_eq!(
  407        editor
  408            .update(cx, |view, cx| view.selections.display_ranges(cx))
  409            .unwrap(),
  410        [
  411            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  412            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  413        ]
  414    );
  415
  416    _ = editor.update(cx, |view, cx| {
  417        view.end_selection(cx);
  418    });
  419
  420    assert_eq!(
  421        editor
  422            .update(cx, |view, cx| view.selections.display_ranges(cx))
  423            .unwrap(),
  424        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  425    );
  426}
  427
  428#[gpui::test]
  429fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  430    init_test(cx, |_| {});
  431
  432    let view = cx.add_window(|cx| {
  433        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  434        build_editor(buffer, cx)
  435    });
  436
  437    _ = view.update(cx, |view, cx| {
  438        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  439        assert_eq!(
  440            view.selections.display_ranges(cx),
  441            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  442        );
  443    });
  444
  445    _ = view.update(cx, |view, cx| {
  446        view.update_selection(
  447            DisplayPoint::new(DisplayRow(3), 3),
  448            0,
  449            gpui::Point::<f32>::default(),
  450            cx,
  451        );
  452        assert_eq!(
  453            view.selections.display_ranges(cx),
  454            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  455        );
  456    });
  457
  458    _ = view.update(cx, |view, cx| {
  459        view.cancel(&Cancel, cx);
  460        view.update_selection(
  461            DisplayPoint::new(DisplayRow(1), 1),
  462            0,
  463            gpui::Point::<f32>::default(),
  464            cx,
  465        );
  466        assert_eq!(
  467            view.selections.display_ranges(cx),
  468            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  469        );
  470    });
  471}
  472
  473#[gpui::test]
  474fn test_clone(cx: &mut TestAppContext) {
  475    init_test(cx, |_| {});
  476
  477    let (text, selection_ranges) = marked_text_ranges(
  478        indoc! {"
  479            one
  480            two
  481            threeˇ
  482            four
  483            fiveˇ
  484        "},
  485        true,
  486    );
  487
  488    let editor = cx.add_window(|cx| {
  489        let buffer = MultiBuffer::build_simple(&text, cx);
  490        build_editor(buffer, cx)
  491    });
  492
  493    _ = editor.update(cx, |editor, cx| {
  494        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  495        editor.fold_ranges(
  496            [
  497                Point::new(1, 0)..Point::new(2, 0),
  498                Point::new(3, 0)..Point::new(4, 0),
  499            ],
  500            true,
  501            cx,
  502        );
  503    });
  504
  505    let cloned_editor = editor
  506        .update(cx, |editor, cx| {
  507            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  508        })
  509        .unwrap();
  510
  511    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  512    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  513
  514    assert_eq!(
  515        cloned_editor
  516            .update(cx, |e, cx| e.display_text(cx))
  517            .unwrap(),
  518        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  519    );
  520    assert_eq!(
  521        cloned_snapshot
  522            .folds_in_range(0..text.len())
  523            .collect::<Vec<_>>(),
  524        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  525    );
  526    assert_set_eq!(
  527        cloned_editor
  528            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  529            .unwrap(),
  530        editor
  531            .update(cx, |editor, cx| editor.selections.ranges(cx))
  532            .unwrap()
  533    );
  534    assert_set_eq!(
  535        cloned_editor
  536            .update(cx, |e, cx| e.selections.display_ranges(cx))
  537            .unwrap(),
  538        editor
  539            .update(cx, |e, cx| e.selections.display_ranges(cx))
  540            .unwrap()
  541    );
  542}
  543
  544#[gpui::test]
  545async fn test_navigation_history(cx: &mut TestAppContext) {
  546    init_test(cx, |_| {});
  547
  548    use workspace::item::Item;
  549
  550    let fs = FakeFs::new(cx.executor());
  551    let project = Project::test(fs, [], cx).await;
  552    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  553    let pane = workspace
  554        .update(cx, |workspace, _| workspace.active_pane().clone())
  555        .unwrap();
  556
  557    _ = workspace.update(cx, |_v, cx| {
  558        cx.new_view(|cx| {
  559            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  560            let mut editor = build_editor(buffer.clone(), cx);
  561            let handle = cx.view();
  562            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  563
  564            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  565                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  566            }
  567
  568            // Move the cursor a small distance.
  569            // Nothing is added to the navigation history.
  570            editor.change_selections(None, cx, |s| {
  571                s.select_display_ranges([
  572                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  573                ])
  574            });
  575            editor.change_selections(None, cx, |s| {
  576                s.select_display_ranges([
  577                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  578                ])
  579            });
  580            assert!(pop_history(&mut editor, cx).is_none());
  581
  582            // Move the cursor a large distance.
  583            // The history can jump back to the previous position.
  584            editor.change_selections(None, cx, |s| {
  585                s.select_display_ranges([
  586                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  587                ])
  588            });
  589            let nav_entry = pop_history(&mut editor, cx).unwrap();
  590            editor.navigate(nav_entry.data.unwrap(), cx);
  591            assert_eq!(nav_entry.item.id(), cx.entity_id());
  592            assert_eq!(
  593                editor.selections.display_ranges(cx),
  594                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  595            );
  596            assert!(pop_history(&mut editor, cx).is_none());
  597
  598            // Move the cursor a small distance via the mouse.
  599            // Nothing is added to the navigation history.
  600            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  601            editor.end_selection(cx);
  602            assert_eq!(
  603                editor.selections.display_ranges(cx),
  604                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  605            );
  606            assert!(pop_history(&mut editor, cx).is_none());
  607
  608            // Move the cursor a large distance via the mouse.
  609            // The history can jump back to the previous position.
  610            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  611            editor.end_selection(cx);
  612            assert_eq!(
  613                editor.selections.display_ranges(cx),
  614                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  615            );
  616            let nav_entry = pop_history(&mut editor, cx).unwrap();
  617            editor.navigate(nav_entry.data.unwrap(), cx);
  618            assert_eq!(nav_entry.item.id(), cx.entity_id());
  619            assert_eq!(
  620                editor.selections.display_ranges(cx),
  621                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  622            );
  623            assert!(pop_history(&mut editor, cx).is_none());
  624
  625            // Set scroll position to check later
  626            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  627            let original_scroll_position = editor.scroll_manager.anchor();
  628
  629            // Jump to the end of the document and adjust scroll
  630            editor.move_to_end(&MoveToEnd, cx);
  631            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  632            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  633
  634            let nav_entry = pop_history(&mut editor, cx).unwrap();
  635            editor.navigate(nav_entry.data.unwrap(), cx);
  636            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  637
  638            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  639            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  640            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  641            let invalid_point = Point::new(9999, 0);
  642            editor.navigate(
  643                Box::new(NavigationData {
  644                    cursor_anchor: invalid_anchor,
  645                    cursor_position: invalid_point,
  646                    scroll_anchor: ScrollAnchor {
  647                        anchor: invalid_anchor,
  648                        offset: Default::default(),
  649                    },
  650                    scroll_top_row: invalid_point.row,
  651                }),
  652                cx,
  653            );
  654            assert_eq!(
  655                editor.selections.display_ranges(cx),
  656                &[editor.max_point(cx)..editor.max_point(cx)]
  657            );
  658            assert_eq!(
  659                editor.scroll_position(cx),
  660                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  661            );
  662
  663            editor
  664        })
  665    });
  666}
  667
  668#[gpui::test]
  669fn test_cancel(cx: &mut TestAppContext) {
  670    init_test(cx, |_| {});
  671
  672    let view = cx.add_window(|cx| {
  673        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  674        build_editor(buffer, cx)
  675    });
  676
  677    _ = view.update(cx, |view, cx| {
  678        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  679        view.update_selection(
  680            DisplayPoint::new(DisplayRow(1), 1),
  681            0,
  682            gpui::Point::<f32>::default(),
  683            cx,
  684        );
  685        view.end_selection(cx);
  686
  687        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  688        view.update_selection(
  689            DisplayPoint::new(DisplayRow(0), 3),
  690            0,
  691            gpui::Point::<f32>::default(),
  692            cx,
  693        );
  694        view.end_selection(cx);
  695        assert_eq!(
  696            view.selections.display_ranges(cx),
  697            [
  698                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  699                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  700            ]
  701        );
  702    });
  703
  704    _ = view.update(cx, |view, cx| {
  705        view.cancel(&Cancel, cx);
  706        assert_eq!(
  707            view.selections.display_ranges(cx),
  708            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  709        );
  710    });
  711
  712    _ = view.update(cx, |view, cx| {
  713        view.cancel(&Cancel, cx);
  714        assert_eq!(
  715            view.selections.display_ranges(cx),
  716            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  717        );
  718    });
  719}
  720
  721#[gpui::test]
  722fn test_fold_action(cx: &mut TestAppContext) {
  723    init_test(cx, |_| {});
  724
  725    let view = cx.add_window(|cx| {
  726        let buffer = MultiBuffer::build_simple(
  727            &"
  728                impl Foo {
  729                    // Hello!
  730
  731                    fn a() {
  732                        1
  733                    }
  734
  735                    fn b() {
  736                        2
  737                    }
  738
  739                    fn c() {
  740                        3
  741                    }
  742                }
  743            "
  744            .unindent(),
  745            cx,
  746        );
  747        build_editor(buffer.clone(), cx)
  748    });
  749
  750    _ = view.update(cx, |view, cx| {
  751        view.change_selections(None, cx, |s| {
  752            s.select_display_ranges([
  753                DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(12), 0)
  754            ]);
  755        });
  756        view.fold(&Fold, cx);
  757        assert_eq!(
  758            view.display_text(cx),
  759            "
  760                impl Foo {
  761                    // Hello!
  762
  763                    fn a() {
  764                        1
  765                    }
  766
  767                    fn b() {⋯
  768                    }
  769
  770                    fn c() {⋯
  771                    }
  772                }
  773            "
  774            .unindent(),
  775        );
  776
  777        view.fold(&Fold, cx);
  778        assert_eq!(
  779            view.display_text(cx),
  780            "
  781                impl Foo {⋯
  782                }
  783            "
  784            .unindent(),
  785        );
  786
  787        view.unfold_lines(&UnfoldLines, cx);
  788        assert_eq!(
  789            view.display_text(cx),
  790            "
  791                impl Foo {
  792                    // Hello!
  793
  794                    fn a() {
  795                        1
  796                    }
  797
  798                    fn b() {⋯
  799                    }
  800
  801                    fn c() {⋯
  802                    }
  803                }
  804            "
  805            .unindent(),
  806        );
  807
  808        view.unfold_lines(&UnfoldLines, cx);
  809        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  810    });
  811}
  812
  813#[gpui::test]
  814fn test_move_cursor(cx: &mut TestAppContext) {
  815    init_test(cx, |_| {});
  816
  817    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
  818    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  819
  820    _ = buffer.update(cx, |buffer, cx| {
  821        buffer.edit(
  822            vec![
  823                (Point::new(1, 0)..Point::new(1, 0), "\t"),
  824                (Point::new(1, 1)..Point::new(1, 1), "\t"),
  825            ],
  826            None,
  827            cx,
  828        );
  829    });
  830    _ = view.update(cx, |view, cx| {
  831        assert_eq!(
  832            view.selections.display_ranges(cx),
  833            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
  834        );
  835
  836        view.move_down(&MoveDown, cx);
  837        assert_eq!(
  838            view.selections.display_ranges(cx),
  839            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
  840        );
  841
  842        view.move_right(&MoveRight, cx);
  843        assert_eq!(
  844            view.selections.display_ranges(cx),
  845            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
  846        );
  847
  848        view.move_left(&MoveLeft, cx);
  849        assert_eq!(
  850            view.selections.display_ranges(cx),
  851            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
  852        );
  853
  854        view.move_up(&MoveUp, cx);
  855        assert_eq!(
  856            view.selections.display_ranges(cx),
  857            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
  858        );
  859
  860        view.move_to_end(&MoveToEnd, cx);
  861        assert_eq!(
  862            view.selections.display_ranges(cx),
  863            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
  864        );
  865
  866        view.move_to_beginning(&MoveToBeginning, cx);
  867        assert_eq!(
  868            view.selections.display_ranges(cx),
  869            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
  870        );
  871
  872        view.change_selections(None, cx, |s| {
  873            s.select_display_ranges([
  874                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
  875            ]);
  876        });
  877        view.select_to_beginning(&SelectToBeginning, cx);
  878        assert_eq!(
  879            view.selections.display_ranges(cx),
  880            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
  881        );
  882
  883        view.select_to_end(&SelectToEnd, cx);
  884        assert_eq!(
  885            view.selections.display_ranges(cx),
  886            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
  887        );
  888    });
  889}
  890
  891#[gpui::test]
  892fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
  893    init_test(cx, |_| {});
  894
  895    let view = cx.add_window(|cx| {
  896        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
  897        build_editor(buffer.clone(), cx)
  898    });
  899
  900    assert_eq!('ⓐ'.len_utf8(), 3);
  901    assert_eq!('α'.len_utf8(), 2);
  902
  903    _ = view.update(cx, |view, cx| {
  904        view.fold_ranges(
  905            vec![
  906                Point::new(0, 6)..Point::new(0, 12),
  907                Point::new(1, 2)..Point::new(1, 4),
  908                Point::new(2, 4)..Point::new(2, 8),
  909            ],
  910            true,
  911            cx,
  912        );
  913        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
  914
  915        view.move_right(&MoveRight, cx);
  916        assert_eq!(
  917            view.selections.display_ranges(cx),
  918            &[empty_range(0, "".len())]
  919        );
  920        view.move_right(&MoveRight, cx);
  921        assert_eq!(
  922            view.selections.display_ranges(cx),
  923            &[empty_range(0, "ⓐⓑ".len())]
  924        );
  925        view.move_right(&MoveRight, cx);
  926        assert_eq!(
  927            view.selections.display_ranges(cx),
  928            &[empty_range(0, "ⓐⓑ⋯".len())]
  929        );
  930
  931        view.move_down(&MoveDown, cx);
  932        assert_eq!(
  933            view.selections.display_ranges(cx),
  934            &[empty_range(1, "ab⋯e".len())]
  935        );
  936        view.move_left(&MoveLeft, cx);
  937        assert_eq!(
  938            view.selections.display_ranges(cx),
  939            &[empty_range(1, "ab⋯".len())]
  940        );
  941        view.move_left(&MoveLeft, cx);
  942        assert_eq!(
  943            view.selections.display_ranges(cx),
  944            &[empty_range(1, "ab".len())]
  945        );
  946        view.move_left(&MoveLeft, cx);
  947        assert_eq!(
  948            view.selections.display_ranges(cx),
  949            &[empty_range(1, "a".len())]
  950        );
  951
  952        view.move_down(&MoveDown, cx);
  953        assert_eq!(
  954            view.selections.display_ranges(cx),
  955            &[empty_range(2, "α".len())]
  956        );
  957        view.move_right(&MoveRight, cx);
  958        assert_eq!(
  959            view.selections.display_ranges(cx),
  960            &[empty_range(2, "αβ".len())]
  961        );
  962        view.move_right(&MoveRight, cx);
  963        assert_eq!(
  964            view.selections.display_ranges(cx),
  965            &[empty_range(2, "αβ⋯".len())]
  966        );
  967        view.move_right(&MoveRight, cx);
  968        assert_eq!(
  969            view.selections.display_ranges(cx),
  970            &[empty_range(2, "αβ⋯ε".len())]
  971        );
  972
  973        view.move_up(&MoveUp, cx);
  974        assert_eq!(
  975            view.selections.display_ranges(cx),
  976            &[empty_range(1, "ab⋯e".len())]
  977        );
  978        view.move_down(&MoveDown, cx);
  979        assert_eq!(
  980            view.selections.display_ranges(cx),
  981            &[empty_range(2, "αβ⋯ε".len())]
  982        );
  983        view.move_up(&MoveUp, cx);
  984        assert_eq!(
  985            view.selections.display_ranges(cx),
  986            &[empty_range(1, "ab⋯e".len())]
  987        );
  988
  989        view.move_up(&MoveUp, cx);
  990        assert_eq!(
  991            view.selections.display_ranges(cx),
  992            &[empty_range(0, "ⓐⓑ".len())]
  993        );
  994        view.move_left(&MoveLeft, cx);
  995        assert_eq!(
  996            view.selections.display_ranges(cx),
  997            &[empty_range(0, "".len())]
  998        );
  999        view.move_left(&MoveLeft, cx);
 1000        assert_eq!(
 1001            view.selections.display_ranges(cx),
 1002            &[empty_range(0, "".len())]
 1003        );
 1004    });
 1005}
 1006
 1007#[gpui::test]
 1008fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1009    init_test(cx, |_| {});
 1010
 1011    let view = cx.add_window(|cx| {
 1012        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1013        build_editor(buffer.clone(), cx)
 1014    });
 1015    _ = view.update(cx, |view, cx| {
 1016        view.change_selections(None, cx, |s| {
 1017            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1018        });
 1019        view.move_down(&MoveDown, cx);
 1020        assert_eq!(
 1021            view.selections.display_ranges(cx),
 1022            &[empty_range(1, "abcd".len())]
 1023        );
 1024
 1025        view.move_down(&MoveDown, cx);
 1026        assert_eq!(
 1027            view.selections.display_ranges(cx),
 1028            &[empty_range(2, "αβγ".len())]
 1029        );
 1030
 1031        view.move_down(&MoveDown, cx);
 1032        assert_eq!(
 1033            view.selections.display_ranges(cx),
 1034            &[empty_range(3, "abcd".len())]
 1035        );
 1036
 1037        view.move_down(&MoveDown, cx);
 1038        assert_eq!(
 1039            view.selections.display_ranges(cx),
 1040            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1041        );
 1042
 1043        view.move_up(&MoveUp, cx);
 1044        assert_eq!(
 1045            view.selections.display_ranges(cx),
 1046            &[empty_range(3, "abcd".len())]
 1047        );
 1048
 1049        view.move_up(&MoveUp, cx);
 1050        assert_eq!(
 1051            view.selections.display_ranges(cx),
 1052            &[empty_range(2, "αβγ".len())]
 1053        );
 1054    });
 1055}
 1056
 1057#[gpui::test]
 1058fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1059    init_test(cx, |_| {});
 1060    let move_to_beg = MoveToBeginningOfLine {
 1061        stop_at_soft_wraps: true,
 1062    };
 1063
 1064    let move_to_end = MoveToEndOfLine {
 1065        stop_at_soft_wraps: true,
 1066    };
 1067
 1068    let view = cx.add_window(|cx| {
 1069        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1070        build_editor(buffer, cx)
 1071    });
 1072    _ = view.update(cx, |view, cx| {
 1073        view.change_selections(None, cx, |s| {
 1074            s.select_display_ranges([
 1075                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1076                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1077            ]);
 1078        });
 1079    });
 1080
 1081    _ = view.update(cx, |view, cx| {
 1082        view.move_to_beginning_of_line(&move_to_beg, cx);
 1083        assert_eq!(
 1084            view.selections.display_ranges(cx),
 1085            &[
 1086                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1087                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1088            ]
 1089        );
 1090    });
 1091
 1092    _ = view.update(cx, |view, cx| {
 1093        view.move_to_beginning_of_line(&move_to_beg, cx);
 1094        assert_eq!(
 1095            view.selections.display_ranges(cx),
 1096            &[
 1097                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1098                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1099            ]
 1100        );
 1101    });
 1102
 1103    _ = view.update(cx, |view, cx| {
 1104        view.move_to_beginning_of_line(&move_to_beg, cx);
 1105        assert_eq!(
 1106            view.selections.display_ranges(cx),
 1107            &[
 1108                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1109                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1110            ]
 1111        );
 1112    });
 1113
 1114    _ = view.update(cx, |view, cx| {
 1115        view.move_to_end_of_line(&move_to_end, cx);
 1116        assert_eq!(
 1117            view.selections.display_ranges(cx),
 1118            &[
 1119                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1120                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1121            ]
 1122        );
 1123    });
 1124
 1125    // Moving to the end of line again is a no-op.
 1126    _ = view.update(cx, |view, cx| {
 1127        view.move_to_end_of_line(&move_to_end, cx);
 1128        assert_eq!(
 1129            view.selections.display_ranges(cx),
 1130            &[
 1131                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1132                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1133            ]
 1134        );
 1135    });
 1136
 1137    _ = view.update(cx, |view, cx| {
 1138        view.move_left(&MoveLeft, cx);
 1139        view.select_to_beginning_of_line(
 1140            &SelectToBeginningOfLine {
 1141                stop_at_soft_wraps: true,
 1142            },
 1143            cx,
 1144        );
 1145        assert_eq!(
 1146            view.selections.display_ranges(cx),
 1147            &[
 1148                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1149                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1150            ]
 1151        );
 1152    });
 1153
 1154    _ = view.update(cx, |view, cx| {
 1155        view.select_to_beginning_of_line(
 1156            &SelectToBeginningOfLine {
 1157                stop_at_soft_wraps: true,
 1158            },
 1159            cx,
 1160        );
 1161        assert_eq!(
 1162            view.selections.display_ranges(cx),
 1163            &[
 1164                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1165                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1166            ]
 1167        );
 1168    });
 1169
 1170    _ = view.update(cx, |view, cx| {
 1171        view.select_to_beginning_of_line(
 1172            &SelectToBeginningOfLine {
 1173                stop_at_soft_wraps: true,
 1174            },
 1175            cx,
 1176        );
 1177        assert_eq!(
 1178            view.selections.display_ranges(cx),
 1179            &[
 1180                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1181                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1182            ]
 1183        );
 1184    });
 1185
 1186    _ = view.update(cx, |view, cx| {
 1187        view.select_to_end_of_line(
 1188            &SelectToEndOfLine {
 1189                stop_at_soft_wraps: true,
 1190            },
 1191            cx,
 1192        );
 1193        assert_eq!(
 1194            view.selections.display_ranges(cx),
 1195            &[
 1196                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1197                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1198            ]
 1199        );
 1200    });
 1201
 1202    _ = view.update(cx, |view, cx| {
 1203        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1204        assert_eq!(view.display_text(cx), "ab\n  de");
 1205        assert_eq!(
 1206            view.selections.display_ranges(cx),
 1207            &[
 1208                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1209                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1210            ]
 1211        );
 1212    });
 1213
 1214    _ = view.update(cx, |view, cx| {
 1215        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1216        assert_eq!(view.display_text(cx), "\n");
 1217        assert_eq!(
 1218            view.selections.display_ranges(cx),
 1219            &[
 1220                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1221                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1222            ]
 1223        );
 1224    });
 1225}
 1226
 1227#[gpui::test]
 1228fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1229    init_test(cx, |_| {});
 1230    let move_to_beg = MoveToBeginningOfLine {
 1231        stop_at_soft_wraps: false,
 1232    };
 1233
 1234    let move_to_end = MoveToEndOfLine {
 1235        stop_at_soft_wraps: false,
 1236    };
 1237
 1238    let view = cx.add_window(|cx| {
 1239        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1240        build_editor(buffer, cx)
 1241    });
 1242
 1243    _ = view.update(cx, |view, cx| {
 1244        view.set_wrap_width(Some(140.0.into()), cx);
 1245
 1246        // We expect the following lines after wrapping
 1247        // ```
 1248        // thequickbrownfox
 1249        // jumpedoverthelazydo
 1250        // gs
 1251        // ```
 1252        // The final `gs` was soft-wrapped onto a new line.
 1253        assert_eq!(
 1254            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1255            view.display_text(cx),
 1256        );
 1257
 1258        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1259        // Start the cursor at the `k` on the first line
 1260        view.change_selections(None, cx, |s| {
 1261            s.select_display_ranges([
 1262                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1263            ]);
 1264        });
 1265
 1266        // Moving to the beginning of the line should put us at the beginning of the line.
 1267        view.move_to_beginning_of_line(&move_to_beg, cx);
 1268        assert_eq!(
 1269            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1270            view.selections.display_ranges(cx)
 1271        );
 1272
 1273        // Moving to the end of the line should put us at the end of the line.
 1274        view.move_to_end_of_line(&move_to_end, cx);
 1275        assert_eq!(
 1276            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1277            view.selections.display_ranges(cx)
 1278        );
 1279
 1280        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1281        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1282        view.change_selections(None, cx, |s| {
 1283            s.select_display_ranges([
 1284                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1285            ]);
 1286        });
 1287
 1288        // Moving to the beginning of the line should put us at the start of the second line of
 1289        // display text, i.e., the `j`.
 1290        view.move_to_beginning_of_line(&move_to_beg, cx);
 1291        assert_eq!(
 1292            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1293            view.selections.display_ranges(cx)
 1294        );
 1295
 1296        // Moving to the beginning of the line again should be a no-op.
 1297        view.move_to_beginning_of_line(&move_to_beg, cx);
 1298        assert_eq!(
 1299            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1300            view.selections.display_ranges(cx)
 1301        );
 1302
 1303        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1304        // next display line.
 1305        view.move_to_end_of_line(&move_to_end, cx);
 1306        assert_eq!(
 1307            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1308            view.selections.display_ranges(cx)
 1309        );
 1310
 1311        // Moving to the end of the line again should be a no-op.
 1312        view.move_to_end_of_line(&move_to_end, cx);
 1313        assert_eq!(
 1314            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1315            view.selections.display_ranges(cx)
 1316        );
 1317    });
 1318}
 1319
 1320#[gpui::test]
 1321fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1322    init_test(cx, |_| {});
 1323
 1324    let view = cx.add_window(|cx| {
 1325        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1326        build_editor(buffer, cx)
 1327    });
 1328    _ = view.update(cx, |view, cx| {
 1329        view.change_selections(None, cx, |s| {
 1330            s.select_display_ranges([
 1331                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1332                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1333            ])
 1334        });
 1335
 1336        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1337        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1338
 1339        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1340        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1341
 1342        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1343        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1344
 1345        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1346        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1347
 1348        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1349        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1350
 1351        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1352        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1353
 1354        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1355        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1356
 1357        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1358        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1359
 1360        view.move_right(&MoveRight, cx);
 1361        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1362        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1363
 1364        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1365        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1366
 1367        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1368        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1369    });
 1370}
 1371
 1372#[gpui::test]
 1373fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1374    init_test(cx, |_| {});
 1375
 1376    let view = cx.add_window(|cx| {
 1377        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1378        build_editor(buffer, cx)
 1379    });
 1380
 1381    _ = view.update(cx, |view, cx| {
 1382        view.set_wrap_width(Some(140.0.into()), cx);
 1383        assert_eq!(
 1384            view.display_text(cx),
 1385            "use one::{\n    two::three::\n    four::five\n};"
 1386        );
 1387
 1388        view.change_selections(None, cx, |s| {
 1389            s.select_display_ranges([
 1390                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1391            ]);
 1392        });
 1393
 1394        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1395        assert_eq!(
 1396            view.selections.display_ranges(cx),
 1397            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1398        );
 1399
 1400        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1401        assert_eq!(
 1402            view.selections.display_ranges(cx),
 1403            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1404        );
 1405
 1406        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1407        assert_eq!(
 1408            view.selections.display_ranges(cx),
 1409            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1410        );
 1411
 1412        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1413        assert_eq!(
 1414            view.selections.display_ranges(cx),
 1415            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1416        );
 1417
 1418        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1419        assert_eq!(
 1420            view.selections.display_ranges(cx),
 1421            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1422        );
 1423
 1424        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1425        assert_eq!(
 1426            view.selections.display_ranges(cx),
 1427            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1428        );
 1429    });
 1430}
 1431
 1432#[gpui::test]
 1433async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1434    init_test(cx, |_| {});
 1435    let mut cx = EditorTestContext::new(cx).await;
 1436
 1437    let line_height = cx.editor(|editor, cx| {
 1438        editor
 1439            .style()
 1440            .unwrap()
 1441            .text
 1442            .line_height_in_pixels(cx.rem_size())
 1443    });
 1444    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1445
 1446    cx.set_state(
 1447        &r#"ˇone
 1448        two
 1449
 1450        three
 1451        fourˇ
 1452        five
 1453
 1454        six"#
 1455            .unindent(),
 1456    );
 1457
 1458    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1459    cx.assert_editor_state(
 1460        &r#"one
 1461        two
 1462        ˇ
 1463        three
 1464        four
 1465        five
 1466        ˇ
 1467        six"#
 1468            .unindent(),
 1469    );
 1470
 1471    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1472    cx.assert_editor_state(
 1473        &r#"one
 1474        two
 1475
 1476        three
 1477        four
 1478        five
 1479        ˇ
 1480        sixˇ"#
 1481            .unindent(),
 1482    );
 1483
 1484    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1485    cx.assert_editor_state(
 1486        &r#"one
 1487        two
 1488
 1489        three
 1490        four
 1491        five
 1492
 1493        sixˇ"#
 1494            .unindent(),
 1495    );
 1496
 1497    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1498    cx.assert_editor_state(
 1499        &r#"one
 1500        two
 1501
 1502        three
 1503        four
 1504        five
 1505        ˇ
 1506        six"#
 1507            .unindent(),
 1508    );
 1509
 1510    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1511    cx.assert_editor_state(
 1512        &r#"one
 1513        two
 1514        ˇ
 1515        three
 1516        four
 1517        five
 1518
 1519        six"#
 1520            .unindent(),
 1521    );
 1522
 1523    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1524    cx.assert_editor_state(
 1525        &r#"ˇone
 1526        two
 1527
 1528        three
 1529        four
 1530        five
 1531
 1532        six"#
 1533            .unindent(),
 1534    );
 1535}
 1536
 1537#[gpui::test]
 1538async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1539    init_test(cx, |_| {});
 1540    let mut cx = EditorTestContext::new(cx).await;
 1541    let line_height = cx.editor(|editor, cx| {
 1542        editor
 1543            .style()
 1544            .unwrap()
 1545            .text
 1546            .line_height_in_pixels(cx.rem_size())
 1547    });
 1548    let window = cx.window;
 1549    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1550
 1551    cx.set_state(
 1552        &r#"ˇone
 1553        two
 1554        three
 1555        four
 1556        five
 1557        six
 1558        seven
 1559        eight
 1560        nine
 1561        ten
 1562        "#,
 1563    );
 1564
 1565    cx.update_editor(|editor, cx| {
 1566        assert_eq!(
 1567            editor.snapshot(cx).scroll_position(),
 1568            gpui::Point::new(0., 0.)
 1569        );
 1570        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1571        assert_eq!(
 1572            editor.snapshot(cx).scroll_position(),
 1573            gpui::Point::new(0., 3.)
 1574        );
 1575        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1576        assert_eq!(
 1577            editor.snapshot(cx).scroll_position(),
 1578            gpui::Point::new(0., 6.)
 1579        );
 1580        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1581        assert_eq!(
 1582            editor.snapshot(cx).scroll_position(),
 1583            gpui::Point::new(0., 3.)
 1584        );
 1585
 1586        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1587        assert_eq!(
 1588            editor.snapshot(cx).scroll_position(),
 1589            gpui::Point::new(0., 1.)
 1590        );
 1591        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1592        assert_eq!(
 1593            editor.snapshot(cx).scroll_position(),
 1594            gpui::Point::new(0., 3.)
 1595        );
 1596    });
 1597}
 1598
 1599#[gpui::test]
 1600async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1601    init_test(cx, |_| {});
 1602    let mut cx = EditorTestContext::new(cx).await;
 1603
 1604    let line_height = cx.update_editor(|editor, cx| {
 1605        editor.set_vertical_scroll_margin(2, cx);
 1606        editor
 1607            .style()
 1608            .unwrap()
 1609            .text
 1610            .line_height_in_pixels(cx.rem_size())
 1611    });
 1612    let window = cx.window;
 1613    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1614
 1615    cx.set_state(
 1616        &r#"ˇone
 1617            two
 1618            three
 1619            four
 1620            five
 1621            six
 1622            seven
 1623            eight
 1624            nine
 1625            ten
 1626        "#,
 1627    );
 1628    cx.update_editor(|editor, cx| {
 1629        assert_eq!(
 1630            editor.snapshot(cx).scroll_position(),
 1631            gpui::Point::new(0., 0.0)
 1632        );
 1633    });
 1634
 1635    // Add a cursor below the visible area. Since both cursors cannot fit
 1636    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1637    // allows the vertical scroll margin below that cursor.
 1638    cx.update_editor(|editor, cx| {
 1639        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1640            selections.select_ranges([
 1641                Point::new(0, 0)..Point::new(0, 0),
 1642                Point::new(6, 0)..Point::new(6, 0),
 1643            ]);
 1644        })
 1645    });
 1646    cx.update_editor(|editor, cx| {
 1647        assert_eq!(
 1648            editor.snapshot(cx).scroll_position(),
 1649            gpui::Point::new(0., 3.0)
 1650        );
 1651    });
 1652
 1653    // Move down. The editor cursor scrolls down to track the newest cursor.
 1654    cx.update_editor(|editor, cx| {
 1655        editor.move_down(&Default::default(), cx);
 1656    });
 1657    cx.update_editor(|editor, cx| {
 1658        assert_eq!(
 1659            editor.snapshot(cx).scroll_position(),
 1660            gpui::Point::new(0., 4.0)
 1661        );
 1662    });
 1663
 1664    // Add a cursor above the visible area. Since both cursors fit on screen,
 1665    // the editor scrolls to show both.
 1666    cx.update_editor(|editor, cx| {
 1667        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1668            selections.select_ranges([
 1669                Point::new(1, 0)..Point::new(1, 0),
 1670                Point::new(6, 0)..Point::new(6, 0),
 1671            ]);
 1672        })
 1673    });
 1674    cx.update_editor(|editor, cx| {
 1675        assert_eq!(
 1676            editor.snapshot(cx).scroll_position(),
 1677            gpui::Point::new(0., 1.0)
 1678        );
 1679    });
 1680}
 1681
 1682#[gpui::test]
 1683async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1684    init_test(cx, |_| {});
 1685    let mut cx = EditorTestContext::new(cx).await;
 1686
 1687    let line_height = cx.editor(|editor, cx| {
 1688        editor
 1689            .style()
 1690            .unwrap()
 1691            .text
 1692            .line_height_in_pixels(cx.rem_size())
 1693    });
 1694    let window = cx.window;
 1695    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1696    cx.set_state(
 1697        &r#"
 1698        ˇone
 1699        two
 1700        threeˇ
 1701        four
 1702        five
 1703        six
 1704        seven
 1705        eight
 1706        nine
 1707        ten
 1708        "#
 1709        .unindent(),
 1710    );
 1711
 1712    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1713    cx.assert_editor_state(
 1714        &r#"
 1715        one
 1716        two
 1717        three
 1718        ˇfour
 1719        five
 1720        sixˇ
 1721        seven
 1722        eight
 1723        nine
 1724        ten
 1725        "#
 1726        .unindent(),
 1727    );
 1728
 1729    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1730    cx.assert_editor_state(
 1731        &r#"
 1732        one
 1733        two
 1734        three
 1735        four
 1736        five
 1737        six
 1738        ˇseven
 1739        eight
 1740        nineˇ
 1741        ten
 1742        "#
 1743        .unindent(),
 1744    );
 1745
 1746    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 1747    cx.assert_editor_state(
 1748        &r#"
 1749        one
 1750        two
 1751        three
 1752        ˇfour
 1753        five
 1754        sixˇ
 1755        seven
 1756        eight
 1757        nine
 1758        ten
 1759        "#
 1760        .unindent(),
 1761    );
 1762
 1763    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 1764    cx.assert_editor_state(
 1765        &r#"
 1766        ˇone
 1767        two
 1768        threeˇ
 1769        four
 1770        five
 1771        six
 1772        seven
 1773        eight
 1774        nine
 1775        ten
 1776        "#
 1777        .unindent(),
 1778    );
 1779
 1780    // Test select collapsing
 1781    cx.update_editor(|editor, cx| {
 1782        editor.move_page_down(&MovePageDown::default(), cx);
 1783        editor.move_page_down(&MovePageDown::default(), cx);
 1784        editor.move_page_down(&MovePageDown::default(), cx);
 1785    });
 1786    cx.assert_editor_state(
 1787        &r#"
 1788        one
 1789        two
 1790        three
 1791        four
 1792        five
 1793        six
 1794        seven
 1795        eight
 1796        nine
 1797        ˇten
 1798        ˇ"#
 1799        .unindent(),
 1800    );
 1801}
 1802
 1803#[gpui::test]
 1804async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 1805    init_test(cx, |_| {});
 1806    let mut cx = EditorTestContext::new(cx).await;
 1807    cx.set_state("one «two threeˇ» four");
 1808    cx.update_editor(|editor, cx| {
 1809        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1810        assert_eq!(editor.text(cx), " four");
 1811    });
 1812}
 1813
 1814#[gpui::test]
 1815fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 1816    init_test(cx, |_| {});
 1817
 1818    let view = cx.add_window(|cx| {
 1819        let buffer = MultiBuffer::build_simple("one two three four", cx);
 1820        build_editor(buffer.clone(), cx)
 1821    });
 1822
 1823    _ = view.update(cx, |view, cx| {
 1824        view.change_selections(None, cx, |s| {
 1825            s.select_display_ranges([
 1826                // an empty selection - the preceding word fragment is deleted
 1827                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1828                // characters selected - they are deleted
 1829                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 1830            ])
 1831        });
 1832        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
 1833        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 1834    });
 1835
 1836    _ = view.update(cx, |view, cx| {
 1837        view.change_selections(None, cx, |s| {
 1838            s.select_display_ranges([
 1839                // an empty selection - the following word fragment is deleted
 1840                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1841                // characters selected - they are deleted
 1842                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 1843            ])
 1844        });
 1845        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
 1846        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 1847    });
 1848}
 1849
 1850#[gpui::test]
 1851fn test_newline(cx: &mut TestAppContext) {
 1852    init_test(cx, |_| {});
 1853
 1854    let view = cx.add_window(|cx| {
 1855        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 1856        build_editor(buffer.clone(), cx)
 1857    });
 1858
 1859    _ = view.update(cx, |view, cx| {
 1860        view.change_selections(None, cx, |s| {
 1861            s.select_display_ranges([
 1862                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1863                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1864                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 1865            ])
 1866        });
 1867
 1868        view.newline(&Newline, cx);
 1869        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 1870    });
 1871}
 1872
 1873#[gpui::test]
 1874fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 1875    init_test(cx, |_| {});
 1876
 1877    let editor = cx.add_window(|cx| {
 1878        let buffer = MultiBuffer::build_simple(
 1879            "
 1880                a
 1881                b(
 1882                    X
 1883                )
 1884                c(
 1885                    X
 1886                )
 1887            "
 1888            .unindent()
 1889            .as_str(),
 1890            cx,
 1891        );
 1892        let mut editor = build_editor(buffer.clone(), cx);
 1893        editor.change_selections(None, cx, |s| {
 1894            s.select_ranges([
 1895                Point::new(2, 4)..Point::new(2, 5),
 1896                Point::new(5, 4)..Point::new(5, 5),
 1897            ])
 1898        });
 1899        editor
 1900    });
 1901
 1902    _ = editor.update(cx, |editor, cx| {
 1903        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 1904        editor.buffer.update(cx, |buffer, cx| {
 1905            buffer.edit(
 1906                [
 1907                    (Point::new(1, 2)..Point::new(3, 0), ""),
 1908                    (Point::new(4, 2)..Point::new(6, 0), ""),
 1909                ],
 1910                None,
 1911                cx,
 1912            );
 1913            assert_eq!(
 1914                buffer.read(cx).text(),
 1915                "
 1916                    a
 1917                    b()
 1918                    c()
 1919                "
 1920                .unindent()
 1921            );
 1922        });
 1923        assert_eq!(
 1924            editor.selections.ranges(cx),
 1925            &[
 1926                Point::new(1, 2)..Point::new(1, 2),
 1927                Point::new(2, 2)..Point::new(2, 2),
 1928            ],
 1929        );
 1930
 1931        editor.newline(&Newline, cx);
 1932        assert_eq!(
 1933            editor.text(cx),
 1934            "
 1935                a
 1936                b(
 1937                )
 1938                c(
 1939                )
 1940            "
 1941            .unindent()
 1942        );
 1943
 1944        // The selections are moved after the inserted newlines
 1945        assert_eq!(
 1946            editor.selections.ranges(cx),
 1947            &[
 1948                Point::new(2, 0)..Point::new(2, 0),
 1949                Point::new(4, 0)..Point::new(4, 0),
 1950            ],
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 1957    init_test(cx, |settings| {
 1958        settings.defaults.tab_size = NonZeroU32::new(4)
 1959    });
 1960
 1961    let language = Arc::new(
 1962        Language::new(
 1963            LanguageConfig::default(),
 1964            Some(tree_sitter_rust::language()),
 1965        )
 1966        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 1967        .unwrap(),
 1968    );
 1969
 1970    let mut cx = EditorTestContext::new(cx).await;
 1971    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 1972    cx.set_state(indoc! {"
 1973        const a: ˇA = (
 1974 1975                «const_functionˇ»(ˇ),
 1976                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 1977 1978        ˇ);ˇ
 1979    "});
 1980
 1981    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 1982    cx.assert_editor_state(indoc! {"
 1983        ˇ
 1984        const a: A = (
 1985            ˇ
 1986            (
 1987                ˇ
 1988                ˇ
 1989                const_function(),
 1990                ˇ
 1991                ˇ
 1992                ˇ
 1993                ˇ
 1994                something_else,
 1995                ˇ
 1996            )
 1997            ˇ
 1998            ˇ
 1999        );
 2000    "});
 2001}
 2002
 2003#[gpui::test]
 2004async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2005    init_test(cx, |settings| {
 2006        settings.defaults.tab_size = NonZeroU32::new(4)
 2007    });
 2008
 2009    let language = Arc::new(
 2010        Language::new(
 2011            LanguageConfig::default(),
 2012            Some(tree_sitter_rust::language()),
 2013        )
 2014        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2015        .unwrap(),
 2016    );
 2017
 2018    let mut cx = EditorTestContext::new(cx).await;
 2019    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2020    cx.set_state(indoc! {"
 2021        const a: ˇA = (
 2022 2023                «const_functionˇ»(ˇ),
 2024                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2025 2026        ˇ);ˇ
 2027    "});
 2028
 2029    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2030    cx.assert_editor_state(indoc! {"
 2031        const a: A = (
 2032            ˇ
 2033            (
 2034                ˇ
 2035                const_function(),
 2036                ˇ
 2037                ˇ
 2038                something_else,
 2039                ˇ
 2040                ˇ
 2041                ˇ
 2042                ˇ
 2043            )
 2044            ˇ
 2045        );
 2046        ˇ
 2047        ˇ
 2048    "});
 2049}
 2050
 2051#[gpui::test]
 2052async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2053    init_test(cx, |settings| {
 2054        settings.defaults.tab_size = NonZeroU32::new(4)
 2055    });
 2056
 2057    let language = Arc::new(Language::new(
 2058        LanguageConfig {
 2059            line_comments: vec!["//".into()],
 2060            ..LanguageConfig::default()
 2061        },
 2062        None,
 2063    ));
 2064    {
 2065        let mut cx = EditorTestContext::new(cx).await;
 2066        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2067        cx.set_state(indoc! {"
 2068        // Fooˇ
 2069    "});
 2070
 2071        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2072        cx.assert_editor_state(indoc! {"
 2073        // Foo
 2074        //ˇ
 2075    "});
 2076        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2077        cx.set_state(indoc! {"
 2078        ˇ// Foo
 2079    "});
 2080        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2081        cx.assert_editor_state(indoc! {"
 2082
 2083        ˇ// Foo
 2084    "});
 2085    }
 2086    // Ensure that comment continuations can be disabled.
 2087    update_test_language_settings(cx, |settings| {
 2088        settings.defaults.extend_comment_on_newline = Some(false);
 2089    });
 2090    let mut cx = EditorTestContext::new(cx).await;
 2091    cx.set_state(indoc! {"
 2092        // Fooˇ
 2093    "});
 2094    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2095    cx.assert_editor_state(indoc! {"
 2096        // Foo
 2097        ˇ
 2098    "});
 2099}
 2100
 2101#[gpui::test]
 2102fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2103    init_test(cx, |_| {});
 2104
 2105    let editor = cx.add_window(|cx| {
 2106        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2107        let mut editor = build_editor(buffer.clone(), cx);
 2108        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2109        editor
 2110    });
 2111
 2112    _ = editor.update(cx, |editor, cx| {
 2113        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2114        editor.buffer.update(cx, |buffer, cx| {
 2115            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2116            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2117        });
 2118        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2119
 2120        editor.insert("Z", cx);
 2121        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2122
 2123        // The selections are moved after the inserted characters
 2124        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2125    });
 2126}
 2127
 2128#[gpui::test]
 2129async fn test_tab(cx: &mut gpui::TestAppContext) {
 2130    init_test(cx, |settings| {
 2131        settings.defaults.tab_size = NonZeroU32::new(3)
 2132    });
 2133
 2134    let mut cx = EditorTestContext::new(cx).await;
 2135    cx.set_state(indoc! {"
 2136        ˇabˇc
 2137        ˇ🏀ˇ🏀ˇefg
 2138 2139    "});
 2140    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2141    cx.assert_editor_state(indoc! {"
 2142           ˇab ˇc
 2143           ˇ🏀  ˇ🏀  ˇefg
 2144        d  ˇ
 2145    "});
 2146
 2147    cx.set_state(indoc! {"
 2148        a
 2149        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2150    "});
 2151    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2152    cx.assert_editor_state(indoc! {"
 2153        a
 2154           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2155    "});
 2156}
 2157
 2158#[gpui::test]
 2159async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2160    init_test(cx, |_| {});
 2161
 2162    let mut cx = EditorTestContext::new(cx).await;
 2163    let language = Arc::new(
 2164        Language::new(
 2165            LanguageConfig::default(),
 2166            Some(tree_sitter_rust::language()),
 2167        )
 2168        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2169        .unwrap(),
 2170    );
 2171    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2172
 2173    // cursors that are already at the suggested indent level insert
 2174    // a soft tab. cursors that are to the left of the suggested indent
 2175    // auto-indent their line.
 2176    cx.set_state(indoc! {"
 2177        ˇ
 2178        const a: B = (
 2179            c(
 2180                d(
 2181        ˇ
 2182                )
 2183        ˇ
 2184        ˇ    )
 2185        );
 2186    "});
 2187    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2188    cx.assert_editor_state(indoc! {"
 2189            ˇ
 2190        const a: B = (
 2191            c(
 2192                d(
 2193                    ˇ
 2194                )
 2195                ˇ
 2196            ˇ)
 2197        );
 2198    "});
 2199
 2200    // handle auto-indent when there are multiple cursors on the same line
 2201    cx.set_state(indoc! {"
 2202        const a: B = (
 2203            c(
 2204        ˇ    ˇ
 2205        ˇ    )
 2206        );
 2207    "});
 2208    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2209    cx.assert_editor_state(indoc! {"
 2210        const a: B = (
 2211            c(
 2212                ˇ
 2213            ˇ)
 2214        );
 2215    "});
 2216}
 2217
 2218#[gpui::test]
 2219async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2220    init_test(cx, |settings| {
 2221        settings.defaults.tab_size = NonZeroU32::new(4)
 2222    });
 2223
 2224    let language = Arc::new(
 2225        Language::new(
 2226            LanguageConfig::default(),
 2227            Some(tree_sitter_rust::language()),
 2228        )
 2229        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2230        .unwrap(),
 2231    );
 2232
 2233    let mut cx = EditorTestContext::new(cx).await;
 2234    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2235    cx.set_state(indoc! {"
 2236        fn a() {
 2237            if b {
 2238        \t ˇc
 2239            }
 2240        }
 2241    "});
 2242
 2243    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2244    cx.assert_editor_state(indoc! {"
 2245        fn a() {
 2246            if b {
 2247                ˇc
 2248            }
 2249        }
 2250    "});
 2251}
 2252
 2253#[gpui::test]
 2254async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2255    init_test(cx, |settings| {
 2256        settings.defaults.tab_size = NonZeroU32::new(4);
 2257    });
 2258
 2259    let mut cx = EditorTestContext::new(cx).await;
 2260
 2261    cx.set_state(indoc! {"
 2262          «oneˇ» «twoˇ»
 2263        three
 2264         four
 2265    "});
 2266    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2267    cx.assert_editor_state(indoc! {"
 2268            «oneˇ» «twoˇ»
 2269        three
 2270         four
 2271    "});
 2272
 2273    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2274    cx.assert_editor_state(indoc! {"
 2275        «oneˇ» «twoˇ»
 2276        three
 2277         four
 2278    "});
 2279
 2280    // select across line ending
 2281    cx.set_state(indoc! {"
 2282        one two
 2283        t«hree
 2284        ˇ» four
 2285    "});
 2286    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2287    cx.assert_editor_state(indoc! {"
 2288        one two
 2289            t«hree
 2290        ˇ» four
 2291    "});
 2292
 2293    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2294    cx.assert_editor_state(indoc! {"
 2295        one two
 2296        t«hree
 2297        ˇ» four
 2298    "});
 2299
 2300    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2301    cx.set_state(indoc! {"
 2302        one two
 2303        ˇthree
 2304            four
 2305    "});
 2306    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2307    cx.assert_editor_state(indoc! {"
 2308        one two
 2309            ˇthree
 2310            four
 2311    "});
 2312
 2313    cx.set_state(indoc! {"
 2314        one two
 2315        ˇ    three
 2316            four
 2317    "});
 2318    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2319    cx.assert_editor_state(indoc! {"
 2320        one two
 2321        ˇthree
 2322            four
 2323    "});
 2324}
 2325
 2326#[gpui::test]
 2327async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2328    init_test(cx, |settings| {
 2329        settings.defaults.hard_tabs = Some(true);
 2330    });
 2331
 2332    let mut cx = EditorTestContext::new(cx).await;
 2333
 2334    // select two ranges on one line
 2335    cx.set_state(indoc! {"
 2336        «oneˇ» «twoˇ»
 2337        three
 2338        four
 2339    "});
 2340    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2341    cx.assert_editor_state(indoc! {"
 2342        \t«oneˇ» «twoˇ»
 2343        three
 2344        four
 2345    "});
 2346    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2347    cx.assert_editor_state(indoc! {"
 2348        \t\t«oneˇ» «twoˇ»
 2349        three
 2350        four
 2351    "});
 2352    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2353    cx.assert_editor_state(indoc! {"
 2354        \t«oneˇ» «twoˇ»
 2355        three
 2356        four
 2357    "});
 2358    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2359    cx.assert_editor_state(indoc! {"
 2360        «oneˇ» «twoˇ»
 2361        three
 2362        four
 2363    "});
 2364
 2365    // select across a line ending
 2366    cx.set_state(indoc! {"
 2367        one two
 2368        t«hree
 2369        ˇ»four
 2370    "});
 2371    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2372    cx.assert_editor_state(indoc! {"
 2373        one two
 2374        \tt«hree
 2375        ˇ»four
 2376    "});
 2377    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2378    cx.assert_editor_state(indoc! {"
 2379        one two
 2380        \t\tt«hree
 2381        ˇ»four
 2382    "});
 2383    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2384    cx.assert_editor_state(indoc! {"
 2385        one two
 2386        \tt«hree
 2387        ˇ»four
 2388    "});
 2389    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2390    cx.assert_editor_state(indoc! {"
 2391        one two
 2392        t«hree
 2393        ˇ»four
 2394    "});
 2395
 2396    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2397    cx.set_state(indoc! {"
 2398        one two
 2399        ˇthree
 2400        four
 2401    "});
 2402    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2403    cx.assert_editor_state(indoc! {"
 2404        one two
 2405        ˇthree
 2406        four
 2407    "});
 2408    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2409    cx.assert_editor_state(indoc! {"
 2410        one two
 2411        \tˇthree
 2412        four
 2413    "});
 2414    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2415    cx.assert_editor_state(indoc! {"
 2416        one two
 2417        ˇthree
 2418        four
 2419    "});
 2420}
 2421
 2422#[gpui::test]
 2423fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2424    init_test(cx, |settings| {
 2425        settings.languages.extend([
 2426            (
 2427                "TOML".into(),
 2428                LanguageSettingsContent {
 2429                    tab_size: NonZeroU32::new(2),
 2430                    ..Default::default()
 2431                },
 2432            ),
 2433            (
 2434                "Rust".into(),
 2435                LanguageSettingsContent {
 2436                    tab_size: NonZeroU32::new(4),
 2437                    ..Default::default()
 2438                },
 2439            ),
 2440        ]);
 2441    });
 2442
 2443    let toml_language = Arc::new(Language::new(
 2444        LanguageConfig {
 2445            name: "TOML".into(),
 2446            ..Default::default()
 2447        },
 2448        None,
 2449    ));
 2450    let rust_language = Arc::new(Language::new(
 2451        LanguageConfig {
 2452            name: "Rust".into(),
 2453            ..Default::default()
 2454        },
 2455        None,
 2456    ));
 2457
 2458    let toml_buffer =
 2459        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2460    let rust_buffer = cx.new_model(|cx| {
 2461        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2462    });
 2463    let multibuffer = cx.new_model(|cx| {
 2464        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 2465        multibuffer.push_excerpts(
 2466            toml_buffer.clone(),
 2467            [ExcerptRange {
 2468                context: Point::new(0, 0)..Point::new(2, 0),
 2469                primary: None,
 2470            }],
 2471            cx,
 2472        );
 2473        multibuffer.push_excerpts(
 2474            rust_buffer.clone(),
 2475            [ExcerptRange {
 2476                context: Point::new(0, 0)..Point::new(1, 0),
 2477                primary: None,
 2478            }],
 2479            cx,
 2480        );
 2481        multibuffer
 2482    });
 2483
 2484    cx.add_window(|cx| {
 2485        let mut editor = build_editor(multibuffer, cx);
 2486
 2487        assert_eq!(
 2488            editor.text(cx),
 2489            indoc! {"
 2490                a = 1
 2491                b = 2
 2492
 2493                const c: usize = 3;
 2494            "}
 2495        );
 2496
 2497        select_ranges(
 2498            &mut editor,
 2499            indoc! {"
 2500                «aˇ» = 1
 2501                b = 2
 2502
 2503                «const c:ˇ» usize = 3;
 2504            "},
 2505            cx,
 2506        );
 2507
 2508        editor.tab(&Tab, cx);
 2509        assert_text_with_selections(
 2510            &mut editor,
 2511            indoc! {"
 2512                  «aˇ» = 1
 2513                b = 2
 2514
 2515                    «const c:ˇ» usize = 3;
 2516            "},
 2517            cx,
 2518        );
 2519        editor.tab_prev(&TabPrev, cx);
 2520        assert_text_with_selections(
 2521            &mut editor,
 2522            indoc! {"
 2523                «aˇ» = 1
 2524                b = 2
 2525
 2526                «const c:ˇ» usize = 3;
 2527            "},
 2528            cx,
 2529        );
 2530
 2531        editor
 2532    });
 2533}
 2534
 2535#[gpui::test]
 2536async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2537    init_test(cx, |_| {});
 2538
 2539    let mut cx = EditorTestContext::new(cx).await;
 2540
 2541    // Basic backspace
 2542    cx.set_state(indoc! {"
 2543        onˇe two three
 2544        fou«rˇ» five six
 2545        seven «ˇeight nine
 2546        »ten
 2547    "});
 2548    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2549    cx.assert_editor_state(indoc! {"
 2550        oˇe two three
 2551        fouˇ five six
 2552        seven ˇten
 2553    "});
 2554
 2555    // Test backspace inside and around indents
 2556    cx.set_state(indoc! {"
 2557        zero
 2558            ˇone
 2559                ˇtwo
 2560            ˇ ˇ ˇ  three
 2561        ˇ  ˇ  four
 2562    "});
 2563    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2564    cx.assert_editor_state(indoc! {"
 2565        zero
 2566        ˇone
 2567            ˇtwo
 2568        ˇ  threeˇ  four
 2569    "});
 2570
 2571    // Test backspace with line_mode set to true
 2572    cx.update_editor(|e, _| e.selections.line_mode = true);
 2573    cx.set_state(indoc! {"
 2574        The ˇquick ˇbrown
 2575        fox jumps over
 2576        the lazy dog
 2577        ˇThe qu«ick bˇ»rown"});
 2578    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2579    cx.assert_editor_state(indoc! {"
 2580        ˇfox jumps over
 2581        the lazy dogˇ"});
 2582}
 2583
 2584#[gpui::test]
 2585async fn test_delete(cx: &mut gpui::TestAppContext) {
 2586    init_test(cx, |_| {});
 2587
 2588    let mut cx = EditorTestContext::new(cx).await;
 2589    cx.set_state(indoc! {"
 2590        onˇe two three
 2591        fou«rˇ» five six
 2592        seven «ˇeight nine
 2593        »ten
 2594    "});
 2595    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2596    cx.assert_editor_state(indoc! {"
 2597        onˇ two three
 2598        fouˇ five six
 2599        seven ˇten
 2600    "});
 2601
 2602    // Test backspace with line_mode set to true
 2603    cx.update_editor(|e, _| e.selections.line_mode = true);
 2604    cx.set_state(indoc! {"
 2605        The ˇquick ˇbrown
 2606        fox «ˇjum»ps over
 2607        the lazy dog
 2608        ˇThe qu«ick bˇ»rown"});
 2609    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2610    cx.assert_editor_state("ˇthe lazy dogˇ");
 2611}
 2612
 2613#[gpui::test]
 2614fn test_delete_line(cx: &mut TestAppContext) {
 2615    init_test(cx, |_| {});
 2616
 2617    let view = cx.add_window(|cx| {
 2618        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2619        build_editor(buffer, cx)
 2620    });
 2621    _ = view.update(cx, |view, cx| {
 2622        view.change_selections(None, cx, |s| {
 2623            s.select_display_ranges([
 2624                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2625                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2626                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2627            ])
 2628        });
 2629        view.delete_line(&DeleteLine, cx);
 2630        assert_eq!(view.display_text(cx), "ghi");
 2631        assert_eq!(
 2632            view.selections.display_ranges(cx),
 2633            vec![
 2634                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2635                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2636            ]
 2637        );
 2638    });
 2639
 2640    let view = cx.add_window(|cx| {
 2641        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2642        build_editor(buffer, cx)
 2643    });
 2644    _ = view.update(cx, |view, cx| {
 2645        view.change_selections(None, cx, |s| {
 2646            s.select_display_ranges([
 2647                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 2648            ])
 2649        });
 2650        view.delete_line(&DeleteLine, cx);
 2651        assert_eq!(view.display_text(cx), "ghi\n");
 2652        assert_eq!(
 2653            view.selections.display_ranges(cx),
 2654            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 2655        );
 2656    });
 2657}
 2658
 2659#[gpui::test]
 2660fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 2661    init_test(cx, |_| {});
 2662
 2663    cx.add_window(|cx| {
 2664        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 2665        let mut editor = build_editor(buffer.clone(), cx);
 2666        let buffer = buffer.read(cx).as_singleton().unwrap();
 2667
 2668        assert_eq!(
 2669            editor.selections.ranges::<Point>(cx),
 2670            &[Point::new(0, 0)..Point::new(0, 0)]
 2671        );
 2672
 2673        // When on single line, replace newline at end by space
 2674        editor.join_lines(&JoinLines, cx);
 2675        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2676        assert_eq!(
 2677            editor.selections.ranges::<Point>(cx),
 2678            &[Point::new(0, 3)..Point::new(0, 3)]
 2679        );
 2680
 2681        // When multiple lines are selected, remove newlines that are spanned by the selection
 2682        editor.change_selections(None, cx, |s| {
 2683            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 2684        });
 2685        editor.join_lines(&JoinLines, cx);
 2686        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 2687        assert_eq!(
 2688            editor.selections.ranges::<Point>(cx),
 2689            &[Point::new(0, 11)..Point::new(0, 11)]
 2690        );
 2691
 2692        // Undo should be transactional
 2693        editor.undo(&Undo, cx);
 2694        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2695        assert_eq!(
 2696            editor.selections.ranges::<Point>(cx),
 2697            &[Point::new(0, 5)..Point::new(2, 2)]
 2698        );
 2699
 2700        // When joining an empty line don't insert a space
 2701        editor.change_selections(None, cx, |s| {
 2702            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 2703        });
 2704        editor.join_lines(&JoinLines, cx);
 2705        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 2706        assert_eq!(
 2707            editor.selections.ranges::<Point>(cx),
 2708            [Point::new(2, 3)..Point::new(2, 3)]
 2709        );
 2710
 2711        // We can remove trailing newlines
 2712        editor.join_lines(&JoinLines, cx);
 2713        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2714        assert_eq!(
 2715            editor.selections.ranges::<Point>(cx),
 2716            [Point::new(2, 3)..Point::new(2, 3)]
 2717        );
 2718
 2719        // We don't blow up on the last line
 2720        editor.join_lines(&JoinLines, cx);
 2721        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2722        assert_eq!(
 2723            editor.selections.ranges::<Point>(cx),
 2724            [Point::new(2, 3)..Point::new(2, 3)]
 2725        );
 2726
 2727        // reset to test indentation
 2728        editor.buffer.update(cx, |buffer, cx| {
 2729            buffer.edit(
 2730                [
 2731                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 2732                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 2733                ],
 2734                None,
 2735                cx,
 2736            )
 2737        });
 2738
 2739        // We remove any leading spaces
 2740        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 2741        editor.change_selections(None, cx, |s| {
 2742            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 2743        });
 2744        editor.join_lines(&JoinLines, cx);
 2745        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 2746
 2747        // We don't insert a space for a line containing only spaces
 2748        editor.join_lines(&JoinLines, cx);
 2749        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 2750
 2751        // We ignore any leading tabs
 2752        editor.join_lines(&JoinLines, cx);
 2753        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 2754
 2755        editor
 2756    });
 2757}
 2758
 2759#[gpui::test]
 2760fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 2761    init_test(cx, |_| {});
 2762
 2763    cx.add_window(|cx| {
 2764        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 2765        let mut editor = build_editor(buffer.clone(), cx);
 2766        let buffer = buffer.read(cx).as_singleton().unwrap();
 2767
 2768        editor.change_selections(None, cx, |s| {
 2769            s.select_ranges([
 2770                Point::new(0, 2)..Point::new(1, 1),
 2771                Point::new(1, 2)..Point::new(1, 2),
 2772                Point::new(3, 1)..Point::new(3, 2),
 2773            ])
 2774        });
 2775
 2776        editor.join_lines(&JoinLines, cx);
 2777        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 2778
 2779        assert_eq!(
 2780            editor.selections.ranges::<Point>(cx),
 2781            [
 2782                Point::new(0, 7)..Point::new(0, 7),
 2783                Point::new(1, 3)..Point::new(1, 3)
 2784            ]
 2785        );
 2786        editor
 2787    });
 2788}
 2789
 2790#[gpui::test]
 2791async fn test_join_lines_with_git_diff_base(
 2792    executor: BackgroundExecutor,
 2793    cx: &mut gpui::TestAppContext,
 2794) {
 2795    init_test(cx, |_| {});
 2796
 2797    let mut cx = EditorTestContext::new(cx).await;
 2798
 2799    let diff_base = r#"
 2800        Line 0
 2801        Line 1
 2802        Line 2
 2803        Line 3
 2804        "#
 2805    .unindent();
 2806
 2807    cx.set_state(
 2808        &r#"
 2809        ˇLine 0
 2810        Line 1
 2811        Line 2
 2812        Line 3
 2813        "#
 2814        .unindent(),
 2815    );
 2816
 2817    cx.set_diff_base(Some(&diff_base));
 2818    executor.run_until_parked();
 2819
 2820    // Join lines
 2821    cx.update_editor(|editor, cx| {
 2822        editor.join_lines(&JoinLines, cx);
 2823    });
 2824    executor.run_until_parked();
 2825
 2826    cx.assert_editor_state(
 2827        &r#"
 2828        Line 0ˇ Line 1
 2829        Line 2
 2830        Line 3
 2831        "#
 2832        .unindent(),
 2833    );
 2834    // Join again
 2835    cx.update_editor(|editor, cx| {
 2836        editor.join_lines(&JoinLines, cx);
 2837    });
 2838    executor.run_until_parked();
 2839
 2840    cx.assert_editor_state(
 2841        &r#"
 2842        Line 0 Line 1ˇ Line 2
 2843        Line 3
 2844        "#
 2845        .unindent(),
 2846    );
 2847}
 2848
 2849#[gpui::test]
 2850async fn test_custom_newlines_cause_no_false_positive_diffs(
 2851    executor: BackgroundExecutor,
 2852    cx: &mut gpui::TestAppContext,
 2853) {
 2854    init_test(cx, |_| {});
 2855    let mut cx = EditorTestContext::new(cx).await;
 2856    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 2857    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 2858    executor.run_until_parked();
 2859
 2860    cx.update_editor(|editor, cx| {
 2861        assert_eq!(
 2862            editor
 2863                .buffer()
 2864                .read(cx)
 2865                .snapshot(cx)
 2866                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 2867                .collect::<Vec<_>>(),
 2868            Vec::new(),
 2869            "Should not have any diffs for files with custom newlines"
 2870        );
 2871    });
 2872}
 2873
 2874#[gpui::test]
 2875async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 2876    init_test(cx, |_| {});
 2877
 2878    let mut cx = EditorTestContext::new(cx).await;
 2879
 2880    // Test sort_lines_case_insensitive()
 2881    cx.set_state(indoc! {"
 2882        «z
 2883        y
 2884        x
 2885        Z
 2886        Y
 2887        Xˇ»
 2888    "});
 2889    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 2890    cx.assert_editor_state(indoc! {"
 2891        «x
 2892        X
 2893        y
 2894        Y
 2895        z
 2896        Zˇ»
 2897    "});
 2898
 2899    // Test reverse_lines()
 2900    cx.set_state(indoc! {"
 2901        «5
 2902        4
 2903        3
 2904        2
 2905        1ˇ»
 2906    "});
 2907    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 2908    cx.assert_editor_state(indoc! {"
 2909        «1
 2910        2
 2911        3
 2912        4
 2913        5ˇ»
 2914    "});
 2915
 2916    // Skip testing shuffle_line()
 2917
 2918    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 2919    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 2920
 2921    // Don't manipulate when cursor is on single line, but expand the selection
 2922    cx.set_state(indoc! {"
 2923        ddˇdd
 2924        ccc
 2925        bb
 2926        a
 2927    "});
 2928    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 2929    cx.assert_editor_state(indoc! {"
 2930        «ddddˇ»
 2931        ccc
 2932        bb
 2933        a
 2934    "});
 2935
 2936    // Basic manipulate case
 2937    // Start selection moves to column 0
 2938    // End of selection shrinks to fit shorter line
 2939    cx.set_state(indoc! {"
 2940        dd«d
 2941        ccc
 2942        bb
 2943        aaaaaˇ»
 2944    "});
 2945    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 2946    cx.assert_editor_state(indoc! {"
 2947        «aaaaa
 2948        bb
 2949        ccc
 2950        dddˇ»
 2951    "});
 2952
 2953    // Manipulate case with newlines
 2954    cx.set_state(indoc! {"
 2955        dd«d
 2956        ccc
 2957
 2958        bb
 2959        aaaaa
 2960
 2961        ˇ»
 2962    "});
 2963    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 2964    cx.assert_editor_state(indoc! {"
 2965        «
 2966
 2967        aaaaa
 2968        bb
 2969        ccc
 2970        dddˇ»
 2971
 2972    "});
 2973
 2974    // Adding new line
 2975    cx.set_state(indoc! {"
 2976        aa«a
 2977        bbˇ»b
 2978    "});
 2979    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 2980    cx.assert_editor_state(indoc! {"
 2981        «aaa
 2982        bbb
 2983        added_lineˇ»
 2984    "});
 2985
 2986    // Removing line
 2987    cx.set_state(indoc! {"
 2988        aa«a
 2989        bbbˇ»
 2990    "});
 2991    cx.update_editor(|e, cx| {
 2992        e.manipulate_lines(cx, |lines| {
 2993            lines.pop();
 2994        })
 2995    });
 2996    cx.assert_editor_state(indoc! {"
 2997        «aaaˇ»
 2998    "});
 2999
 3000    // Removing all lines
 3001    cx.set_state(indoc! {"
 3002        aa«a
 3003        bbbˇ»
 3004    "});
 3005    cx.update_editor(|e, cx| {
 3006        e.manipulate_lines(cx, |lines| {
 3007            lines.drain(..);
 3008        })
 3009    });
 3010    cx.assert_editor_state(indoc! {"
 3011        ˇ
 3012    "});
 3013}
 3014
 3015#[gpui::test]
 3016async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3017    init_test(cx, |_| {});
 3018
 3019    let mut cx = EditorTestContext::new(cx).await;
 3020
 3021    // Consider continuous selection as single selection
 3022    cx.set_state(indoc! {"
 3023        Aaa«aa
 3024        cˇ»c«c
 3025        bb
 3026        aaaˇ»aa
 3027    "});
 3028    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3029    cx.assert_editor_state(indoc! {"
 3030        «Aaaaa
 3031        ccc
 3032        bb
 3033        aaaaaˇ»
 3034    "});
 3035
 3036    cx.set_state(indoc! {"
 3037        Aaa«aa
 3038        cˇ»c«c
 3039        bb
 3040        aaaˇ»aa
 3041    "});
 3042    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3043    cx.assert_editor_state(indoc! {"
 3044        «Aaaaa
 3045        ccc
 3046        bbˇ»
 3047    "});
 3048
 3049    // Consider non continuous selection as distinct dedup operations
 3050    cx.set_state(indoc! {"
 3051        «aaaaa
 3052        bb
 3053        aaaaa
 3054        aaaaaˇ»
 3055
 3056        aaa«aaˇ»
 3057    "});
 3058    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3059    cx.assert_editor_state(indoc! {"
 3060        «aaaaa
 3061        bbˇ»
 3062
 3063        «aaaaaˇ»
 3064    "});
 3065}
 3066
 3067#[gpui::test]
 3068async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3069    init_test(cx, |_| {});
 3070
 3071    let mut cx = EditorTestContext::new(cx).await;
 3072
 3073    cx.set_state(indoc! {"
 3074        «Aaa
 3075        aAa
 3076        Aaaˇ»
 3077    "});
 3078    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3079    cx.assert_editor_state(indoc! {"
 3080        «Aaa
 3081        aAaˇ»
 3082    "});
 3083
 3084    cx.set_state(indoc! {"
 3085        «Aaa
 3086        aAa
 3087        aaAˇ»
 3088    "});
 3089    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3090    cx.assert_editor_state(indoc! {"
 3091        «Aaaˇ»
 3092    "});
 3093}
 3094
 3095#[gpui::test]
 3096async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3097    init_test(cx, |_| {});
 3098
 3099    let mut cx = EditorTestContext::new(cx).await;
 3100
 3101    // Manipulate with multiple selections on a single line
 3102    cx.set_state(indoc! {"
 3103        dd«dd
 3104        cˇ»c«c
 3105        bb
 3106        aaaˇ»aa
 3107    "});
 3108    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3109    cx.assert_editor_state(indoc! {"
 3110        «aaaaa
 3111        bb
 3112        ccc
 3113        ddddˇ»
 3114    "});
 3115
 3116    // Manipulate with multiple disjoin selections
 3117    cx.set_state(indoc! {"
 3118 3119        4
 3120        3
 3121        2
 3122        1ˇ»
 3123
 3124        dd«dd
 3125        ccc
 3126        bb
 3127        aaaˇ»aa
 3128    "});
 3129    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3130    cx.assert_editor_state(indoc! {"
 3131        «1
 3132        2
 3133        3
 3134        4
 3135        5ˇ»
 3136
 3137        «aaaaa
 3138        bb
 3139        ccc
 3140        ddddˇ»
 3141    "});
 3142
 3143    // Adding lines on each selection
 3144    cx.set_state(indoc! {"
 3145 3146        1ˇ»
 3147
 3148        bb«bb
 3149        aaaˇ»aa
 3150    "});
 3151    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3152    cx.assert_editor_state(indoc! {"
 3153        «2
 3154        1
 3155        added lineˇ»
 3156
 3157        «bbbb
 3158        aaaaa
 3159        added lineˇ»
 3160    "});
 3161
 3162    // Removing lines on each selection
 3163    cx.set_state(indoc! {"
 3164 3165        1ˇ»
 3166
 3167        bb«bb
 3168        aaaˇ»aa
 3169    "});
 3170    cx.update_editor(|e, cx| {
 3171        e.manipulate_lines(cx, |lines| {
 3172            lines.pop();
 3173        })
 3174    });
 3175    cx.assert_editor_state(indoc! {"
 3176        «2ˇ»
 3177
 3178        «bbbbˇ»
 3179    "});
 3180}
 3181
 3182#[gpui::test]
 3183async fn test_manipulate_text(cx: &mut TestAppContext) {
 3184    init_test(cx, |_| {});
 3185
 3186    let mut cx = EditorTestContext::new(cx).await;
 3187
 3188    // Test convert_to_upper_case()
 3189    cx.set_state(indoc! {"
 3190        «hello worldˇ»
 3191    "});
 3192    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3193    cx.assert_editor_state(indoc! {"
 3194        «HELLO WORLDˇ»
 3195    "});
 3196
 3197    // Test convert_to_lower_case()
 3198    cx.set_state(indoc! {"
 3199        «HELLO WORLDˇ»
 3200    "});
 3201    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3202    cx.assert_editor_state(indoc! {"
 3203        «hello worldˇ»
 3204    "});
 3205
 3206    // Test multiple line, single selection case
 3207    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3208    cx.set_state(indoc! {"
 3209        «The quick brown
 3210        fox jumps over
 3211        the lazy dogˇ»
 3212    "});
 3213    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3214    cx.assert_editor_state(indoc! {"
 3215        «The Quick Brown
 3216        Fox Jumps Over
 3217        The Lazy Dogˇ»
 3218    "});
 3219
 3220    // Test multiple line, single selection case
 3221    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3222    cx.set_state(indoc! {"
 3223        «The quick brown
 3224        fox jumps over
 3225        the lazy dogˇ»
 3226    "});
 3227    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3228    cx.assert_editor_state(indoc! {"
 3229        «TheQuickBrown
 3230        FoxJumpsOver
 3231        TheLazyDogˇ»
 3232    "});
 3233
 3234    // From here on out, test more complex cases of manipulate_text()
 3235
 3236    // Test no selection case - should affect words cursors are in
 3237    // Cursor at beginning, middle, and end of word
 3238    cx.set_state(indoc! {"
 3239        ˇhello big beauˇtiful worldˇ
 3240    "});
 3241    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3242    cx.assert_editor_state(indoc! {"
 3243        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3244    "});
 3245
 3246    // Test multiple selections on a single line and across multiple lines
 3247    cx.set_state(indoc! {"
 3248        «Theˇ» quick «brown
 3249        foxˇ» jumps «overˇ»
 3250        the «lazyˇ» dog
 3251    "});
 3252    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3253    cx.assert_editor_state(indoc! {"
 3254        «THEˇ» quick «BROWN
 3255        FOXˇ» jumps «OVERˇ»
 3256        the «LAZYˇ» dog
 3257    "});
 3258
 3259    // Test case where text length grows
 3260    cx.set_state(indoc! {"
 3261        «tschüߡ»
 3262    "});
 3263    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3264    cx.assert_editor_state(indoc! {"
 3265        «TSCHÜSSˇ»
 3266    "});
 3267
 3268    // Test to make sure we don't crash when text shrinks
 3269    cx.set_state(indoc! {"
 3270        aaa_bbbˇ
 3271    "});
 3272    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3273    cx.assert_editor_state(indoc! {"
 3274        «aaaBbbˇ»
 3275    "});
 3276
 3277    // Test to make sure we all aware of the fact that each word can grow and shrink
 3278    // Final selections should be aware of this fact
 3279    cx.set_state(indoc! {"
 3280        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3281    "});
 3282    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3283    cx.assert_editor_state(indoc! {"
 3284        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3285    "});
 3286
 3287    cx.set_state(indoc! {"
 3288        «hElLo, WoRld!ˇ»
 3289    "});
 3290    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3291    cx.assert_editor_state(indoc! {"
 3292        «HeLlO, wOrLD!ˇ»
 3293    "});
 3294}
 3295
 3296#[gpui::test]
 3297fn test_duplicate_line(cx: &mut TestAppContext) {
 3298    init_test(cx, |_| {});
 3299
 3300    let view = cx.add_window(|cx| {
 3301        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3302        build_editor(buffer, cx)
 3303    });
 3304    _ = view.update(cx, |view, cx| {
 3305        view.change_selections(None, cx, |s| {
 3306            s.select_display_ranges([
 3307                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3308                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3309                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3310                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3311            ])
 3312        });
 3313        view.duplicate_line_down(&DuplicateLineDown, cx);
 3314        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3315        assert_eq!(
 3316            view.selections.display_ranges(cx),
 3317            vec![
 3318                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3319                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3320                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3321                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3322            ]
 3323        );
 3324    });
 3325
 3326    let view = cx.add_window(|cx| {
 3327        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3328        build_editor(buffer, cx)
 3329    });
 3330    _ = view.update(cx, |view, cx| {
 3331        view.change_selections(None, cx, |s| {
 3332            s.select_display_ranges([
 3333                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3334                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3335            ])
 3336        });
 3337        view.duplicate_line_down(&DuplicateLineDown, cx);
 3338        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3339        assert_eq!(
 3340            view.selections.display_ranges(cx),
 3341            vec![
 3342                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3343                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3344            ]
 3345        );
 3346    });
 3347
 3348    // With `move_upwards` the selections stay in place, except for
 3349    // the lines inserted above them
 3350    let view = cx.add_window(|cx| {
 3351        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3352        build_editor(buffer, cx)
 3353    });
 3354    _ = view.update(cx, |view, cx| {
 3355        view.change_selections(None, cx, |s| {
 3356            s.select_display_ranges([
 3357                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3358                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3359                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3360                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3361            ])
 3362        });
 3363        view.duplicate_line_up(&DuplicateLineUp, cx);
 3364        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3365        assert_eq!(
 3366            view.selections.display_ranges(cx),
 3367            vec![
 3368                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3369                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3370                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3371                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3372            ]
 3373        );
 3374    });
 3375
 3376    let view = cx.add_window(|cx| {
 3377        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3378        build_editor(buffer, cx)
 3379    });
 3380    _ = view.update(cx, |view, cx| {
 3381        view.change_selections(None, cx, |s| {
 3382            s.select_display_ranges([
 3383                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3384                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3385            ])
 3386        });
 3387        view.duplicate_line_up(&DuplicateLineUp, cx);
 3388        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3389        assert_eq!(
 3390            view.selections.display_ranges(cx),
 3391            vec![
 3392                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3393                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3394            ]
 3395        );
 3396    });
 3397}
 3398
 3399#[gpui::test]
 3400fn test_move_line_up_down(cx: &mut TestAppContext) {
 3401    init_test(cx, |_| {});
 3402
 3403    let view = cx.add_window(|cx| {
 3404        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3405        build_editor(buffer, cx)
 3406    });
 3407    _ = view.update(cx, |view, cx| {
 3408        view.fold_ranges(
 3409            vec![
 3410                Point::new(0, 2)..Point::new(1, 2),
 3411                Point::new(2, 3)..Point::new(4, 1),
 3412                Point::new(7, 0)..Point::new(8, 4),
 3413            ],
 3414            true,
 3415            cx,
 3416        );
 3417        view.change_selections(None, cx, |s| {
 3418            s.select_display_ranges([
 3419                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3420                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3421                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3422                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3423            ])
 3424        });
 3425        assert_eq!(
 3426            view.display_text(cx),
 3427            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3428        );
 3429
 3430        view.move_line_up(&MoveLineUp, cx);
 3431        assert_eq!(
 3432            view.display_text(cx),
 3433            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3434        );
 3435        assert_eq!(
 3436            view.selections.display_ranges(cx),
 3437            vec![
 3438                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3439                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3440                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3441                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3442            ]
 3443        );
 3444    });
 3445
 3446    _ = view.update(cx, |view, cx| {
 3447        view.move_line_down(&MoveLineDown, cx);
 3448        assert_eq!(
 3449            view.display_text(cx),
 3450            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3451        );
 3452        assert_eq!(
 3453            view.selections.display_ranges(cx),
 3454            vec![
 3455                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3456                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3457                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3458                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3459            ]
 3460        );
 3461    });
 3462
 3463    _ = view.update(cx, |view, cx| {
 3464        view.move_line_down(&MoveLineDown, cx);
 3465        assert_eq!(
 3466            view.display_text(cx),
 3467            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3468        );
 3469        assert_eq!(
 3470            view.selections.display_ranges(cx),
 3471            vec![
 3472                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3473                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3474                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3475                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3476            ]
 3477        );
 3478    });
 3479
 3480    _ = view.update(cx, |view, cx| {
 3481        view.move_line_up(&MoveLineUp, cx);
 3482        assert_eq!(
 3483            view.display_text(cx),
 3484            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3485        );
 3486        assert_eq!(
 3487            view.selections.display_ranges(cx),
 3488            vec![
 3489                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3490                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3491                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3492                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3493            ]
 3494        );
 3495    });
 3496}
 3497
 3498#[gpui::test]
 3499fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3500    init_test(cx, |_| {});
 3501
 3502    let editor = cx.add_window(|cx| {
 3503        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3504        build_editor(buffer, cx)
 3505    });
 3506    _ = editor.update(cx, |editor, cx| {
 3507        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3508        editor.insert_blocks(
 3509            [BlockProperties {
 3510                style: BlockStyle::Fixed,
 3511                position: snapshot.anchor_after(Point::new(2, 0)),
 3512                disposition: BlockDisposition::Below,
 3513                height: 1,
 3514                render: Box::new(|_| div().into_any()),
 3515            }],
 3516            Some(Autoscroll::fit()),
 3517            cx,
 3518        );
 3519        editor.change_selections(None, cx, |s| {
 3520            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3521        });
 3522        editor.move_line_down(&MoveLineDown, cx);
 3523    });
 3524}
 3525
 3526#[gpui::test]
 3527fn test_transpose(cx: &mut TestAppContext) {
 3528    init_test(cx, |_| {});
 3529
 3530    _ = cx.add_window(|cx| {
 3531        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3532        editor.set_style(EditorStyle::default(), cx);
 3533        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3534        editor.transpose(&Default::default(), cx);
 3535        assert_eq!(editor.text(cx), "bac");
 3536        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3537
 3538        editor.transpose(&Default::default(), cx);
 3539        assert_eq!(editor.text(cx), "bca");
 3540        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3541
 3542        editor.transpose(&Default::default(), cx);
 3543        assert_eq!(editor.text(cx), "bac");
 3544        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3545
 3546        editor
 3547    });
 3548
 3549    _ = cx.add_window(|cx| {
 3550        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3551        editor.set_style(EditorStyle::default(), cx);
 3552        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3553        editor.transpose(&Default::default(), cx);
 3554        assert_eq!(editor.text(cx), "acb\nde");
 3555        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3556
 3557        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3558        editor.transpose(&Default::default(), cx);
 3559        assert_eq!(editor.text(cx), "acbd\ne");
 3560        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3561
 3562        editor.transpose(&Default::default(), cx);
 3563        assert_eq!(editor.text(cx), "acbde\n");
 3564        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3565
 3566        editor.transpose(&Default::default(), cx);
 3567        assert_eq!(editor.text(cx), "acbd\ne");
 3568        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3569
 3570        editor
 3571    });
 3572
 3573    _ = cx.add_window(|cx| {
 3574        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3575        editor.set_style(EditorStyle::default(), cx);
 3576        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3577        editor.transpose(&Default::default(), cx);
 3578        assert_eq!(editor.text(cx), "bacd\ne");
 3579        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3580
 3581        editor.transpose(&Default::default(), cx);
 3582        assert_eq!(editor.text(cx), "bcade\n");
 3583        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3584
 3585        editor.transpose(&Default::default(), cx);
 3586        assert_eq!(editor.text(cx), "bcda\ne");
 3587        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3588
 3589        editor.transpose(&Default::default(), cx);
 3590        assert_eq!(editor.text(cx), "bcade\n");
 3591        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3592
 3593        editor.transpose(&Default::default(), cx);
 3594        assert_eq!(editor.text(cx), "bcaed\n");
 3595        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3596
 3597        editor
 3598    });
 3599
 3600    _ = cx.add_window(|cx| {
 3601        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3602        editor.set_style(EditorStyle::default(), cx);
 3603        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3604        editor.transpose(&Default::default(), cx);
 3605        assert_eq!(editor.text(cx), "🏀🍐✋");
 3606        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3607
 3608        editor.transpose(&Default::default(), cx);
 3609        assert_eq!(editor.text(cx), "🏀✋🍐");
 3610        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3611
 3612        editor.transpose(&Default::default(), cx);
 3613        assert_eq!(editor.text(cx), "🏀🍐✋");
 3614        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3615
 3616        editor
 3617    });
 3618}
 3619
 3620#[gpui::test]
 3621async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 3622    init_test(cx, |_| {});
 3623
 3624    let mut cx = EditorTestContext::new(cx).await;
 3625
 3626    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 3627    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3628    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 3629
 3630    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 3631    cx.set_state("two ˇfour ˇsix ˇ");
 3632    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3633    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 3634
 3635    // Paste again but with only two cursors. Since the number of cursors doesn't
 3636    // match the number of slices in the clipboard, the entire clipboard text
 3637    // is pasted at each cursor.
 3638    cx.set_state("ˇtwo one✅ four three six five ˇ");
 3639    cx.update_editor(|e, cx| {
 3640        e.handle_input("( ", cx);
 3641        e.paste(&Paste, cx);
 3642        e.handle_input(") ", cx);
 3643    });
 3644    cx.assert_editor_state(
 3645        &([
 3646            "( one✅ ",
 3647            "three ",
 3648            "five ) ˇtwo one✅ four three six five ( one✅ ",
 3649            "three ",
 3650            "five ) ˇ",
 3651        ]
 3652        .join("\n")),
 3653    );
 3654
 3655    // Cut with three selections, one of which is full-line.
 3656    cx.set_state(indoc! {"
 3657        1«2ˇ»3
 3658        4ˇ567
 3659        «8ˇ»9"});
 3660    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3661    cx.assert_editor_state(indoc! {"
 3662        1ˇ3
 3663        ˇ9"});
 3664
 3665    // Paste with three selections, noticing how the copied selection that was full-line
 3666    // gets inserted before the second cursor.
 3667    cx.set_state(indoc! {"
 3668        1ˇ3
 3669 3670        «oˇ»ne"});
 3671    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3672    cx.assert_editor_state(indoc! {"
 3673        12ˇ3
 3674        4567
 3675 3676        8ˇne"});
 3677
 3678    // Copy with a single cursor only, which writes the whole line into the clipboard.
 3679    cx.set_state(indoc! {"
 3680        The quick brown
 3681        fox juˇmps over
 3682        the lazy dog"});
 3683    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 3684    assert_eq!(
 3685        cx.read_from_clipboard().map(|item| item.text().to_owned()),
 3686        Some("fox jumps over\n".to_owned())
 3687    );
 3688
 3689    // Paste with three selections, noticing how the copied full-line selection is inserted
 3690    // before the empty selections but replaces the selection that is non-empty.
 3691    cx.set_state(indoc! {"
 3692        Tˇhe quick brown
 3693        «foˇ»x jumps over
 3694        tˇhe lazy dog"});
 3695    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3696    cx.assert_editor_state(indoc! {"
 3697        fox jumps over
 3698        Tˇhe quick brown
 3699        fox jumps over
 3700        ˇx jumps over
 3701        fox jumps over
 3702        tˇhe lazy dog"});
 3703}
 3704
 3705#[gpui::test]
 3706async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 3707    init_test(cx, |_| {});
 3708
 3709    let mut cx = EditorTestContext::new(cx).await;
 3710    let language = Arc::new(Language::new(
 3711        LanguageConfig::default(),
 3712        Some(tree_sitter_rust::language()),
 3713    ));
 3714    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3715
 3716    // Cut an indented block, without the leading whitespace.
 3717    cx.set_state(indoc! {"
 3718        const a: B = (
 3719            c(),
 3720            «d(
 3721                e,
 3722                f
 3723            )ˇ»
 3724        );
 3725    "});
 3726    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3727    cx.assert_editor_state(indoc! {"
 3728        const a: B = (
 3729            c(),
 3730            ˇ
 3731        );
 3732    "});
 3733
 3734    // Paste it at the same position.
 3735    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3736    cx.assert_editor_state(indoc! {"
 3737        const a: B = (
 3738            c(),
 3739            d(
 3740                e,
 3741                f
 3742 3743        );
 3744    "});
 3745
 3746    // Paste it at a line with a lower indent level.
 3747    cx.set_state(indoc! {"
 3748        ˇ
 3749        const a: B = (
 3750            c(),
 3751        );
 3752    "});
 3753    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3754    cx.assert_editor_state(indoc! {"
 3755        d(
 3756            e,
 3757            f
 3758 3759        const a: B = (
 3760            c(),
 3761        );
 3762    "});
 3763
 3764    // Cut an indented block, with the leading whitespace.
 3765    cx.set_state(indoc! {"
 3766        const a: B = (
 3767            c(),
 3768        «    d(
 3769                e,
 3770                f
 3771            )
 3772        ˇ»);
 3773    "});
 3774    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3775    cx.assert_editor_state(indoc! {"
 3776        const a: B = (
 3777            c(),
 3778        ˇ);
 3779    "});
 3780
 3781    // Paste it at the same position.
 3782    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3783    cx.assert_editor_state(indoc! {"
 3784        const a: B = (
 3785            c(),
 3786            d(
 3787                e,
 3788                f
 3789            )
 3790        ˇ);
 3791    "});
 3792
 3793    // Paste it at a line with a higher indent level.
 3794    cx.set_state(indoc! {"
 3795        const a: B = (
 3796            c(),
 3797            d(
 3798                e,
 3799 3800            )
 3801        );
 3802    "});
 3803    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3804    cx.assert_editor_state(indoc! {"
 3805        const a: B = (
 3806            c(),
 3807            d(
 3808                e,
 3809                f    d(
 3810                    e,
 3811                    f
 3812                )
 3813        ˇ
 3814            )
 3815        );
 3816    "});
 3817}
 3818
 3819#[gpui::test]
 3820fn test_select_all(cx: &mut TestAppContext) {
 3821    init_test(cx, |_| {});
 3822
 3823    let view = cx.add_window(|cx| {
 3824        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 3825        build_editor(buffer, cx)
 3826    });
 3827    _ = view.update(cx, |view, cx| {
 3828        view.select_all(&SelectAll, cx);
 3829        assert_eq!(
 3830            view.selections.display_ranges(cx),
 3831            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 3832        );
 3833    });
 3834}
 3835
 3836#[gpui::test]
 3837fn test_select_line(cx: &mut TestAppContext) {
 3838    init_test(cx, |_| {});
 3839
 3840    let view = cx.add_window(|cx| {
 3841        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 3842        build_editor(buffer, cx)
 3843    });
 3844    _ = view.update(cx, |view, cx| {
 3845        view.change_selections(None, cx, |s| {
 3846            s.select_display_ranges([
 3847                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3848                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3849                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3850                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 3851            ])
 3852        });
 3853        view.select_line(&SelectLine, cx);
 3854        assert_eq!(
 3855            view.selections.display_ranges(cx),
 3856            vec![
 3857                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3858                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 3859            ]
 3860        );
 3861    });
 3862
 3863    _ = view.update(cx, |view, cx| {
 3864        view.select_line(&SelectLine, cx);
 3865        assert_eq!(
 3866            view.selections.display_ranges(cx),
 3867            vec![
 3868                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3869                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 3870            ]
 3871        );
 3872    });
 3873
 3874    _ = view.update(cx, |view, cx| {
 3875        view.select_line(&SelectLine, cx);
 3876        assert_eq!(
 3877            view.selections.display_ranges(cx),
 3878            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 3879        );
 3880    });
 3881}
 3882
 3883#[gpui::test]
 3884fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 3885    init_test(cx, |_| {});
 3886
 3887    let view = cx.add_window(|cx| {
 3888        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 3889        build_editor(buffer, cx)
 3890    });
 3891    _ = view.update(cx, |view, cx| {
 3892        view.fold_ranges(
 3893            vec![
 3894                Point::new(0, 2)..Point::new(1, 2),
 3895                Point::new(2, 3)..Point::new(4, 1),
 3896                Point::new(7, 0)..Point::new(8, 4),
 3897            ],
 3898            true,
 3899            cx,
 3900        );
 3901        view.change_selections(None, cx, |s| {
 3902            s.select_display_ranges([
 3903                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3904                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3905                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3906                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 3907            ])
 3908        });
 3909        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 3910    });
 3911
 3912    _ = view.update(cx, |view, cx| {
 3913        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 3914        assert_eq!(
 3915            view.display_text(cx),
 3916            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 3917        );
 3918        assert_eq!(
 3919            view.selections.display_ranges(cx),
 3920            [
 3921                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3922                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3923                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3924                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 3925            ]
 3926        );
 3927    });
 3928
 3929    _ = view.update(cx, |view, cx| {
 3930        view.change_selections(None, cx, |s| {
 3931            s.select_display_ranges([
 3932                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3933            ])
 3934        });
 3935        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 3936        assert_eq!(
 3937            view.display_text(cx),
 3938            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 3939        );
 3940        assert_eq!(
 3941            view.selections.display_ranges(cx),
 3942            [
 3943                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 3944                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 3945                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 3946                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 3947                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 3948                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 3949                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 3950                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 3951            ]
 3952        );
 3953    });
 3954}
 3955
 3956#[gpui::test]
 3957async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 3958    init_test(cx, |_| {});
 3959
 3960    let mut cx = EditorTestContext::new(cx).await;
 3961
 3962    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 3963    cx.set_state(indoc!(
 3964        r#"abc
 3965           defˇghi
 3966
 3967           jk
 3968           nlmo
 3969           "#
 3970    ));
 3971
 3972    cx.update_editor(|editor, cx| {
 3973        editor.add_selection_above(&Default::default(), cx);
 3974    });
 3975
 3976    cx.assert_editor_state(indoc!(
 3977        r#"abcˇ
 3978           defˇghi
 3979
 3980           jk
 3981           nlmo
 3982           "#
 3983    ));
 3984
 3985    cx.update_editor(|editor, cx| {
 3986        editor.add_selection_above(&Default::default(), cx);
 3987    });
 3988
 3989    cx.assert_editor_state(indoc!(
 3990        r#"abcˇ
 3991            defˇghi
 3992
 3993            jk
 3994            nlmo
 3995            "#
 3996    ));
 3997
 3998    cx.update_editor(|view, cx| {
 3999        view.add_selection_below(&Default::default(), cx);
 4000    });
 4001
 4002    cx.assert_editor_state(indoc!(
 4003        r#"abc
 4004           defˇghi
 4005
 4006           jk
 4007           nlmo
 4008           "#
 4009    ));
 4010
 4011    cx.update_editor(|view, cx| {
 4012        view.undo_selection(&Default::default(), cx);
 4013    });
 4014
 4015    cx.assert_editor_state(indoc!(
 4016        r#"abcˇ
 4017           defˇghi
 4018
 4019           jk
 4020           nlmo
 4021           "#
 4022    ));
 4023
 4024    cx.update_editor(|view, cx| {
 4025        view.redo_selection(&Default::default(), cx);
 4026    });
 4027
 4028    cx.assert_editor_state(indoc!(
 4029        r#"abc
 4030           defˇghi
 4031
 4032           jk
 4033           nlmo
 4034           "#
 4035    ));
 4036
 4037    cx.update_editor(|view, cx| {
 4038        view.add_selection_below(&Default::default(), cx);
 4039    });
 4040
 4041    cx.assert_editor_state(indoc!(
 4042        r#"abc
 4043           defˇghi
 4044
 4045           jk
 4046           nlmˇo
 4047           "#
 4048    ));
 4049
 4050    cx.update_editor(|view, cx| {
 4051        view.add_selection_below(&Default::default(), cx);
 4052    });
 4053
 4054    cx.assert_editor_state(indoc!(
 4055        r#"abc
 4056           defˇghi
 4057
 4058           jk
 4059           nlmˇo
 4060           "#
 4061    ));
 4062
 4063    // change selections
 4064    cx.set_state(indoc!(
 4065        r#"abc
 4066           def«ˇg»hi
 4067
 4068           jk
 4069           nlmo
 4070           "#
 4071    ));
 4072
 4073    cx.update_editor(|view, cx| {
 4074        view.add_selection_below(&Default::default(), cx);
 4075    });
 4076
 4077    cx.assert_editor_state(indoc!(
 4078        r#"abc
 4079           def«ˇg»hi
 4080
 4081           jk
 4082           nlm«ˇo»
 4083           "#
 4084    ));
 4085
 4086    cx.update_editor(|view, cx| {
 4087        view.add_selection_below(&Default::default(), cx);
 4088    });
 4089
 4090    cx.assert_editor_state(indoc!(
 4091        r#"abc
 4092           def«ˇg»hi
 4093
 4094           jk
 4095           nlm«ˇo»
 4096           "#
 4097    ));
 4098
 4099    cx.update_editor(|view, cx| {
 4100        view.add_selection_above(&Default::default(), cx);
 4101    });
 4102
 4103    cx.assert_editor_state(indoc!(
 4104        r#"abc
 4105           def«ˇg»hi
 4106
 4107           jk
 4108           nlmo
 4109           "#
 4110    ));
 4111
 4112    cx.update_editor(|view, cx| {
 4113        view.add_selection_above(&Default::default(), cx);
 4114    });
 4115
 4116    cx.assert_editor_state(indoc!(
 4117        r#"abc
 4118           def«ˇg»hi
 4119
 4120           jk
 4121           nlmo
 4122           "#
 4123    ));
 4124
 4125    // Change selections again
 4126    cx.set_state(indoc!(
 4127        r#"a«bc
 4128           defgˇ»hi
 4129
 4130           jk
 4131           nlmo
 4132           "#
 4133    ));
 4134
 4135    cx.update_editor(|view, cx| {
 4136        view.add_selection_below(&Default::default(), cx);
 4137    });
 4138
 4139    cx.assert_editor_state(indoc!(
 4140        r#"a«bcˇ»
 4141           d«efgˇ»hi
 4142
 4143           j«kˇ»
 4144           nlmo
 4145           "#
 4146    ));
 4147
 4148    cx.update_editor(|view, cx| {
 4149        view.add_selection_below(&Default::default(), cx);
 4150    });
 4151    cx.assert_editor_state(indoc!(
 4152        r#"a«bcˇ»
 4153           d«efgˇ»hi
 4154
 4155           j«kˇ»
 4156           n«lmoˇ»
 4157           "#
 4158    ));
 4159    cx.update_editor(|view, cx| {
 4160        view.add_selection_above(&Default::default(), cx);
 4161    });
 4162
 4163    cx.assert_editor_state(indoc!(
 4164        r#"a«bcˇ»
 4165           d«efgˇ»hi
 4166
 4167           j«kˇ»
 4168           nlmo
 4169           "#
 4170    ));
 4171
 4172    // Change selections again
 4173    cx.set_state(indoc!(
 4174        r#"abc
 4175           d«ˇefghi
 4176
 4177           jk
 4178           nlm»o
 4179           "#
 4180    ));
 4181
 4182    cx.update_editor(|view, cx| {
 4183        view.add_selection_above(&Default::default(), cx);
 4184    });
 4185
 4186    cx.assert_editor_state(indoc!(
 4187        r#"a«ˇbc»
 4188           d«ˇef»ghi
 4189
 4190           j«ˇk»
 4191           n«ˇlm»o
 4192           "#
 4193    ));
 4194
 4195    cx.update_editor(|view, cx| {
 4196        view.add_selection_below(&Default::default(), cx);
 4197    });
 4198
 4199    cx.assert_editor_state(indoc!(
 4200        r#"abc
 4201           d«ˇef»ghi
 4202
 4203           j«ˇk»
 4204           n«ˇlm»o
 4205           "#
 4206    ));
 4207}
 4208
 4209#[gpui::test]
 4210async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4211    init_test(cx, |_| {});
 4212
 4213    let mut cx = EditorTestContext::new(cx).await;
 4214    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4215
 4216    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4217        .unwrap();
 4218    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4219
 4220    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4221        .unwrap();
 4222    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4223
 4224    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4225    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4226
 4227    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4228    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4229
 4230    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4231        .unwrap();
 4232    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4233
 4234    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4235        .unwrap();
 4236    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4237}
 4238
 4239#[gpui::test]
 4240async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4241    init_test(cx, |_| {});
 4242
 4243    let mut cx = EditorTestContext::new(cx).await;
 4244    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4245
 4246    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4247        .unwrap();
 4248    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4249}
 4250
 4251#[gpui::test]
 4252async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4253    init_test(cx, |_| {});
 4254
 4255    let mut cx = EditorTestContext::new(cx).await;
 4256    cx.set_state(
 4257        r#"let foo = 2;
 4258lˇet foo = 2;
 4259let fooˇ = 2;
 4260let foo = 2;
 4261let foo = ˇ2;"#,
 4262    );
 4263
 4264    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4265        .unwrap();
 4266    cx.assert_editor_state(
 4267        r#"let foo = 2;
 4268«letˇ» foo = 2;
 4269let «fooˇ» = 2;
 4270let foo = 2;
 4271let foo = «2ˇ»;"#,
 4272    );
 4273
 4274    // noop for multiple selections with different contents
 4275    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4276        .unwrap();
 4277    cx.assert_editor_state(
 4278        r#"let foo = 2;
 4279«letˇ» foo = 2;
 4280let «fooˇ» = 2;
 4281let foo = 2;
 4282let foo = «2ˇ»;"#,
 4283    );
 4284}
 4285
 4286#[gpui::test]
 4287async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4288    init_test(cx, |_| {});
 4289
 4290    let mut cx = EditorTestContext::new_multibuffer(
 4291        cx,
 4292        [
 4293            indoc! {
 4294                "aaa\n«bbb\nccc\n»ddd"
 4295            },
 4296            indoc! {
 4297                "aaa\n«bbb\nccc\n»ddd"
 4298            },
 4299        ],
 4300    );
 4301
 4302    cx.assert_editor_state(indoc! {"
 4303        ˇbbb
 4304        ccc
 4305
 4306        bbb
 4307        ccc
 4308        "});
 4309    cx.dispatch_action(SelectPrevious::default());
 4310    cx.assert_editor_state(indoc! {"
 4311                «bbbˇ»
 4312                ccc
 4313
 4314                bbb
 4315                ccc
 4316                "});
 4317    cx.dispatch_action(SelectPrevious::default());
 4318    cx.assert_editor_state(indoc! {"
 4319                «bbbˇ»
 4320                ccc
 4321
 4322                «bbbˇ»
 4323                ccc
 4324                "});
 4325}
 4326
 4327#[gpui::test]
 4328async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 4329    init_test(cx, |_| {});
 4330
 4331    let mut cx = EditorTestContext::new(cx).await;
 4332    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4333
 4334    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4335        .unwrap();
 4336    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4337
 4338    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4339        .unwrap();
 4340    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4341
 4342    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4343    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4344
 4345    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4346    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4347
 4348    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4349        .unwrap();
 4350    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 4351
 4352    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4353        .unwrap();
 4354    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 4355
 4356    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4357        .unwrap();
 4358    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4359}
 4360
 4361#[gpui::test]
 4362async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4363    init_test(cx, |_| {});
 4364
 4365    let mut cx = EditorTestContext::new(cx).await;
 4366    cx.set_state(
 4367        r#"let foo = 2;
 4368lˇet foo = 2;
 4369let fooˇ = 2;
 4370let foo = 2;
 4371let foo = ˇ2;"#,
 4372    );
 4373
 4374    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4375        .unwrap();
 4376    cx.assert_editor_state(
 4377        r#"let foo = 2;
 4378«letˇ» foo = 2;
 4379let «fooˇ» = 2;
 4380let foo = 2;
 4381let foo = «2ˇ»;"#,
 4382    );
 4383
 4384    // noop for multiple selections with different contents
 4385    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4386        .unwrap();
 4387    cx.assert_editor_state(
 4388        r#"let foo = 2;
 4389«letˇ» foo = 2;
 4390let «fooˇ» = 2;
 4391let foo = 2;
 4392let foo = «2ˇ»;"#,
 4393    );
 4394}
 4395
 4396#[gpui::test]
 4397async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 4398    init_test(cx, |_| {});
 4399
 4400    let mut cx = EditorTestContext::new(cx).await;
 4401    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 4402
 4403    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4404        .unwrap();
 4405    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4406
 4407    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4408        .unwrap();
 4409    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4410
 4411    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4412    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4413
 4414    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4415    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4416
 4417    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4418        .unwrap();
 4419    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 4420
 4421    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4422        .unwrap();
 4423    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4424}
 4425
 4426#[gpui::test]
 4427async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 4428    init_test(cx, |_| {});
 4429
 4430    let language = Arc::new(Language::new(
 4431        LanguageConfig::default(),
 4432        Some(tree_sitter_rust::language()),
 4433    ));
 4434
 4435    let text = r#"
 4436        use mod1::mod2::{mod3, mod4};
 4437
 4438        fn fn_1(param1: bool, param2: &str) {
 4439            let var1 = "text";
 4440        }
 4441    "#
 4442    .unindent();
 4443
 4444    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4445    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4446    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4447
 4448    view.condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 4449        .await;
 4450
 4451    _ = view.update(cx, |view, cx| {
 4452        view.change_selections(None, cx, |s| {
 4453            s.select_display_ranges([
 4454                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4455                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4456                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4457            ]);
 4458        });
 4459        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4460    });
 4461    assert_eq!(
 4462        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
 4463        &[
 4464            DisplayPoint::new(DisplayRow(0), 23)..DisplayPoint::new(DisplayRow(0), 27),
 4465            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4466            DisplayPoint::new(DisplayRow(3), 15)..DisplayPoint::new(DisplayRow(3), 21),
 4467        ]
 4468    );
 4469
 4470    _ = view.update(cx, |view, cx| {
 4471        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4472    });
 4473    assert_eq!(
 4474        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4475        &[
 4476            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4477            DisplayPoint::new(DisplayRow(4), 1)..DisplayPoint::new(DisplayRow(2), 0),
 4478        ]
 4479    );
 4480
 4481    _ = view.update(cx, |view, cx| {
 4482        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4483    });
 4484    assert_eq!(
 4485        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4486        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4487    );
 4488
 4489    // Trying to expand the selected syntax node one more time has no effect.
 4490    _ = view.update(cx, |view, cx| {
 4491        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4492    });
 4493    assert_eq!(
 4494        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4495        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4496    );
 4497
 4498    _ = view.update(cx, |view, cx| {
 4499        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4500    });
 4501    assert_eq!(
 4502        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4503        &[
 4504            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4505            DisplayPoint::new(DisplayRow(4), 1)..DisplayPoint::new(DisplayRow(2), 0),
 4506        ]
 4507    );
 4508
 4509    _ = view.update(cx, |view, cx| {
 4510        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4511    });
 4512    assert_eq!(
 4513        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4514        &[
 4515            DisplayPoint::new(DisplayRow(0), 23)..DisplayPoint::new(DisplayRow(0), 27),
 4516            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4517            DisplayPoint::new(DisplayRow(3), 15)..DisplayPoint::new(DisplayRow(3), 21),
 4518        ]
 4519    );
 4520
 4521    _ = view.update(cx, |view, cx| {
 4522        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4523    });
 4524    assert_eq!(
 4525        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4526        &[
 4527            DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4528            DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4529            DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4530        ]
 4531    );
 4532
 4533    // Trying to shrink the selected syntax node one more time has no effect.
 4534    _ = view.update(cx, |view, cx| {
 4535        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4536    });
 4537    assert_eq!(
 4538        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4539        &[
 4540            DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4541            DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4542            DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4543        ]
 4544    );
 4545
 4546    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 4547    // a fold.
 4548    _ = view.update(cx, |view, cx| {
 4549        view.fold_ranges(
 4550            vec![
 4551                Point::new(0, 21)..Point::new(0, 24),
 4552                Point::new(3, 20)..Point::new(3, 22),
 4553            ],
 4554            true,
 4555            cx,
 4556        );
 4557        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4558    });
 4559    assert_eq!(
 4560        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4561        &[
 4562            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4563            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4564            DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(3), 23),
 4565        ]
 4566    );
 4567}
 4568
 4569#[gpui::test]
 4570async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 4571    init_test(cx, |_| {});
 4572
 4573    let language = Arc::new(
 4574        Language::new(
 4575            LanguageConfig {
 4576                brackets: BracketPairConfig {
 4577                    pairs: vec![
 4578                        BracketPair {
 4579                            start: "{".to_string(),
 4580                            end: "}".to_string(),
 4581                            close: false,
 4582                            newline: true,
 4583                        },
 4584                        BracketPair {
 4585                            start: "(".to_string(),
 4586                            end: ")".to_string(),
 4587                            close: false,
 4588                            newline: true,
 4589                        },
 4590                    ],
 4591                    ..Default::default()
 4592                },
 4593                ..Default::default()
 4594            },
 4595            Some(tree_sitter_rust::language()),
 4596        )
 4597        .with_indents_query(
 4598            r#"
 4599                (_ "(" ")" @end) @indent
 4600                (_ "{" "}" @end) @indent
 4601            "#,
 4602        )
 4603        .unwrap(),
 4604    );
 4605
 4606    let text = "fn a() {}";
 4607
 4608    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4609    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4610    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4611    editor
 4612        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 4613        .await;
 4614
 4615    _ = editor.update(cx, |editor, cx| {
 4616        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 4617        editor.newline(&Newline, cx);
 4618        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 4619        assert_eq!(
 4620            editor.selections.ranges(cx),
 4621            &[
 4622                Point::new(1, 4)..Point::new(1, 4),
 4623                Point::new(3, 4)..Point::new(3, 4),
 4624                Point::new(5, 0)..Point::new(5, 0)
 4625            ]
 4626        );
 4627    });
 4628}
 4629
 4630#[gpui::test]
 4631async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
 4632    init_test(cx, |_| {});
 4633
 4634    let mut cx = EditorTestContext::new(cx).await;
 4635
 4636    let language = Arc::new(Language::new(
 4637        LanguageConfig {
 4638            brackets: BracketPairConfig {
 4639                pairs: vec![
 4640                    BracketPair {
 4641                        start: "{".to_string(),
 4642                        end: "}".to_string(),
 4643                        close: true,
 4644                        newline: true,
 4645                    },
 4646                    BracketPair {
 4647                        start: "(".to_string(),
 4648                        end: ")".to_string(),
 4649                        close: true,
 4650                        newline: true,
 4651                    },
 4652                    BracketPair {
 4653                        start: "/*".to_string(),
 4654                        end: " */".to_string(),
 4655                        close: true,
 4656                        newline: true,
 4657                    },
 4658                    BracketPair {
 4659                        start: "[".to_string(),
 4660                        end: "]".to_string(),
 4661                        close: false,
 4662                        newline: true,
 4663                    },
 4664                    BracketPair {
 4665                        start: "\"".to_string(),
 4666                        end: "\"".to_string(),
 4667                        close: true,
 4668                        newline: false,
 4669                    },
 4670                ],
 4671                ..Default::default()
 4672            },
 4673            autoclose_before: "})]".to_string(),
 4674            ..Default::default()
 4675        },
 4676        Some(tree_sitter_rust::language()),
 4677    ));
 4678
 4679    cx.language_registry().add(language.clone());
 4680    cx.update_buffer(|buffer, cx| {
 4681        buffer.set_language(Some(language), cx);
 4682    });
 4683
 4684    cx.set_state(
 4685        &r#"
 4686            🏀ˇ
 4687            εˇ
 4688            ❤️ˇ
 4689        "#
 4690        .unindent(),
 4691    );
 4692
 4693    // autoclose multiple nested brackets at multiple cursors
 4694    cx.update_editor(|view, cx| {
 4695        view.handle_input("{", cx);
 4696        view.handle_input("{", cx);
 4697        view.handle_input("{", cx);
 4698    });
 4699    cx.assert_editor_state(
 4700        &"
 4701            🏀{{{ˇ}}}
 4702            ε{{{ˇ}}}
 4703            ❤️{{{ˇ}}}
 4704        "
 4705        .unindent(),
 4706    );
 4707
 4708    // insert a different closing bracket
 4709    cx.update_editor(|view, cx| {
 4710        view.handle_input(")", cx);
 4711    });
 4712    cx.assert_editor_state(
 4713        &"
 4714            🏀{{{)ˇ}}}
 4715            ε{{{)ˇ}}}
 4716            ❤️{{{)ˇ}}}
 4717        "
 4718        .unindent(),
 4719    );
 4720
 4721    // skip over the auto-closed brackets when typing a closing bracket
 4722    cx.update_editor(|view, cx| {
 4723        view.move_right(&MoveRight, cx);
 4724        view.handle_input("}", cx);
 4725        view.handle_input("}", cx);
 4726        view.handle_input("}", cx);
 4727    });
 4728    cx.assert_editor_state(
 4729        &"
 4730            🏀{{{)}}}}ˇ
 4731            ε{{{)}}}}ˇ
 4732            ❤️{{{)}}}}ˇ
 4733        "
 4734        .unindent(),
 4735    );
 4736
 4737    // autoclose multi-character pairs
 4738    cx.set_state(
 4739        &"
 4740            ˇ
 4741            ˇ
 4742        "
 4743        .unindent(),
 4744    );
 4745    cx.update_editor(|view, cx| {
 4746        view.handle_input("/", cx);
 4747        view.handle_input("*", cx);
 4748    });
 4749    cx.assert_editor_state(
 4750        &"
 4751            /*ˇ */
 4752            /*ˇ */
 4753        "
 4754        .unindent(),
 4755    );
 4756
 4757    // one cursor autocloses a multi-character pair, one cursor
 4758    // does not autoclose.
 4759    cx.set_state(
 4760        &"
 4761 4762            ˇ
 4763        "
 4764        .unindent(),
 4765    );
 4766    cx.update_editor(|view, cx| view.handle_input("*", cx));
 4767    cx.assert_editor_state(
 4768        &"
 4769            /*ˇ */
 4770 4771        "
 4772        .unindent(),
 4773    );
 4774
 4775    // Don't autoclose if the next character isn't whitespace and isn't
 4776    // listed in the language's "autoclose_before" section.
 4777    cx.set_state("ˇa b");
 4778    cx.update_editor(|view, cx| view.handle_input("{", cx));
 4779    cx.assert_editor_state("{ˇa b");
 4780
 4781    // Don't autoclose if `close` is false for the bracket pair
 4782    cx.set_state("ˇ");
 4783    cx.update_editor(|view, cx| view.handle_input("[", cx));
 4784    cx.assert_editor_state("");
 4785
 4786    // Surround with brackets if text is selected
 4787    cx.set_state("«aˇ» b");
 4788    cx.update_editor(|view, cx| view.handle_input("{", cx));
 4789    cx.assert_editor_state("{«aˇ»} b");
 4790
 4791    // Autclose pair where the start and end characters are the same
 4792    cx.set_state("");
 4793    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 4794    cx.assert_editor_state("a\"ˇ\"");
 4795    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 4796    cx.assert_editor_state("a\"\"ˇ");
 4797}
 4798
 4799#[gpui::test]
 4800async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 4801    init_test(cx, |settings| {
 4802        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 4803    });
 4804
 4805    let mut cx = EditorTestContext::new(cx).await;
 4806
 4807    let language = Arc::new(Language::new(
 4808        LanguageConfig {
 4809            brackets: BracketPairConfig {
 4810                pairs: vec![
 4811                    BracketPair {
 4812                        start: "{".to_string(),
 4813                        end: "}".to_string(),
 4814                        close: true,
 4815                        newline: true,
 4816                    },
 4817                    BracketPair {
 4818                        start: "(".to_string(),
 4819                        end: ")".to_string(),
 4820                        close: true,
 4821                        newline: true,
 4822                    },
 4823                    BracketPair {
 4824                        start: "[".to_string(),
 4825                        end: "]".to_string(),
 4826                        close: false,
 4827                        newline: true,
 4828                    },
 4829                ],
 4830                ..Default::default()
 4831            },
 4832            autoclose_before: "})]".to_string(),
 4833            ..Default::default()
 4834        },
 4835        Some(tree_sitter_rust::language()),
 4836    ));
 4837
 4838    cx.language_registry().add(language.clone());
 4839    cx.update_buffer(|buffer, cx| {
 4840        buffer.set_language(Some(language), cx);
 4841    });
 4842
 4843    cx.set_state(
 4844        &"
 4845            ˇ
 4846            ˇ
 4847            ˇ
 4848        "
 4849        .unindent(),
 4850    );
 4851
 4852    // ensure only matching closing brackets are skipped over
 4853    cx.update_editor(|view, cx| {
 4854        view.handle_input("}", cx);
 4855        view.move_left(&MoveLeft, cx);
 4856        view.handle_input(")", cx);
 4857        view.move_left(&MoveLeft, cx);
 4858    });
 4859    cx.assert_editor_state(
 4860        &"
 4861            ˇ)}
 4862            ˇ)}
 4863            ˇ)}
 4864        "
 4865        .unindent(),
 4866    );
 4867
 4868    // skip-over closing brackets at multiple cursors
 4869    cx.update_editor(|view, cx| {
 4870        view.handle_input(")", cx);
 4871        view.handle_input("}", cx);
 4872    });
 4873    cx.assert_editor_state(
 4874        &"
 4875            )}ˇ
 4876            )}ˇ
 4877            )}ˇ
 4878        "
 4879        .unindent(),
 4880    );
 4881
 4882    // ignore non-close brackets
 4883    cx.update_editor(|view, cx| {
 4884        view.handle_input("]", cx);
 4885        view.move_left(&MoveLeft, cx);
 4886        view.handle_input("]", cx);
 4887    });
 4888    cx.assert_editor_state(
 4889        &"
 4890            )}]ˇ]
 4891            )}]ˇ]
 4892            )}]ˇ]
 4893        "
 4894        .unindent(),
 4895    );
 4896}
 4897
 4898#[gpui::test]
 4899async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 4900    init_test(cx, |_| {});
 4901
 4902    let mut cx = EditorTestContext::new(cx).await;
 4903
 4904    let html_language = Arc::new(
 4905        Language::new(
 4906            LanguageConfig {
 4907                name: "HTML".into(),
 4908                brackets: BracketPairConfig {
 4909                    pairs: vec![
 4910                        BracketPair {
 4911                            start: "<".into(),
 4912                            end: ">".into(),
 4913                            close: true,
 4914                            ..Default::default()
 4915                        },
 4916                        BracketPair {
 4917                            start: "{".into(),
 4918                            end: "}".into(),
 4919                            close: true,
 4920                            ..Default::default()
 4921                        },
 4922                        BracketPair {
 4923                            start: "(".into(),
 4924                            end: ")".into(),
 4925                            close: true,
 4926                            ..Default::default()
 4927                        },
 4928                    ],
 4929                    ..Default::default()
 4930                },
 4931                autoclose_before: "})]>".into(),
 4932                ..Default::default()
 4933            },
 4934            Some(tree_sitter_html::language()),
 4935        )
 4936        .with_injection_query(
 4937            r#"
 4938            (script_element
 4939                (raw_text) @content
 4940                (#set! "language" "javascript"))
 4941            "#,
 4942        )
 4943        .unwrap(),
 4944    );
 4945
 4946    let javascript_language = Arc::new(Language::new(
 4947        LanguageConfig {
 4948            name: "JavaScript".into(),
 4949            brackets: BracketPairConfig {
 4950                pairs: vec![
 4951                    BracketPair {
 4952                        start: "/*".into(),
 4953                        end: " */".into(),
 4954                        close: true,
 4955                        ..Default::default()
 4956                    },
 4957                    BracketPair {
 4958                        start: "{".into(),
 4959                        end: "}".into(),
 4960                        close: true,
 4961                        ..Default::default()
 4962                    },
 4963                    BracketPair {
 4964                        start: "(".into(),
 4965                        end: ")".into(),
 4966                        close: true,
 4967                        ..Default::default()
 4968                    },
 4969                ],
 4970                ..Default::default()
 4971            },
 4972            autoclose_before: "})]>".into(),
 4973            ..Default::default()
 4974        },
 4975        Some(tree_sitter_typescript::language_tsx()),
 4976    ));
 4977
 4978    cx.language_registry().add(html_language.clone());
 4979    cx.language_registry().add(javascript_language.clone());
 4980
 4981    cx.update_buffer(|buffer, cx| {
 4982        buffer.set_language(Some(html_language), cx);
 4983    });
 4984
 4985    cx.set_state(
 4986        &r#"
 4987            <body>ˇ
 4988                <script>
 4989                    var x = 1;ˇ
 4990                </script>
 4991            </body>ˇ
 4992        "#
 4993        .unindent(),
 4994    );
 4995
 4996    // Precondition: different languages are active at different locations.
 4997    cx.update_editor(|editor, cx| {
 4998        let snapshot = editor.snapshot(cx);
 4999        let cursors = editor.selections.ranges::<usize>(cx);
 5000        let languages = cursors
 5001            .iter()
 5002            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5003            .collect::<Vec<_>>();
 5004        assert_eq!(
 5005            languages,
 5006            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5007        );
 5008    });
 5009
 5010    // Angle brackets autoclose in HTML, but not JavaScript.
 5011    cx.update_editor(|editor, cx| {
 5012        editor.handle_input("<", cx);
 5013        editor.handle_input("a", cx);
 5014    });
 5015    cx.assert_editor_state(
 5016        &r#"
 5017            <body><aˇ>
 5018                <script>
 5019                    var x = 1;<aˇ
 5020                </script>
 5021            </body><aˇ>
 5022        "#
 5023        .unindent(),
 5024    );
 5025
 5026    // Curly braces and parens autoclose in both HTML and JavaScript.
 5027    cx.update_editor(|editor, cx| {
 5028        editor.handle_input(" b=", cx);
 5029        editor.handle_input("{", cx);
 5030        editor.handle_input("c", cx);
 5031        editor.handle_input("(", cx);
 5032    });
 5033    cx.assert_editor_state(
 5034        &r#"
 5035            <body><a b={c(ˇ)}>
 5036                <script>
 5037                    var x = 1;<a b={c(ˇ)}
 5038                </script>
 5039            </body><a b={c(ˇ)}>
 5040        "#
 5041        .unindent(),
 5042    );
 5043
 5044    // Brackets that were already autoclosed are skipped.
 5045    cx.update_editor(|editor, cx| {
 5046        editor.handle_input(")", cx);
 5047        editor.handle_input("d", cx);
 5048        editor.handle_input("}", cx);
 5049    });
 5050    cx.assert_editor_state(
 5051        &r#"
 5052            <body><a b={c()d}ˇ>
 5053                <script>
 5054                    var x = 1;<a b={c()d}ˇ
 5055                </script>
 5056            </body><a b={c()d}ˇ>
 5057        "#
 5058        .unindent(),
 5059    );
 5060    cx.update_editor(|editor, cx| {
 5061        editor.handle_input(">", cx);
 5062    });
 5063    cx.assert_editor_state(
 5064        &r#"
 5065            <body><a b={c()d}>ˇ
 5066                <script>
 5067                    var x = 1;<a b={c()d}>ˇ
 5068                </script>
 5069            </body><a b={c()d}>ˇ
 5070        "#
 5071        .unindent(),
 5072    );
 5073
 5074    // Reset
 5075    cx.set_state(
 5076        &r#"
 5077            <body>ˇ
 5078                <script>
 5079                    var x = 1;ˇ
 5080                </script>
 5081            </body>ˇ
 5082        "#
 5083        .unindent(),
 5084    );
 5085
 5086    cx.update_editor(|editor, cx| {
 5087        editor.handle_input("<", cx);
 5088    });
 5089    cx.assert_editor_state(
 5090        &r#"
 5091            <body><ˇ>
 5092                <script>
 5093                    var x = 1;<ˇ
 5094                </script>
 5095            </body><ˇ>
 5096        "#
 5097        .unindent(),
 5098    );
 5099
 5100    // When backspacing, the closing angle brackets are removed.
 5101    cx.update_editor(|editor, cx| {
 5102        editor.backspace(&Backspace, cx);
 5103    });
 5104    cx.assert_editor_state(
 5105        &r#"
 5106            <body>ˇ
 5107                <script>
 5108                    var x = 1;ˇ
 5109                </script>
 5110            </body>ˇ
 5111        "#
 5112        .unindent(),
 5113    );
 5114
 5115    // Block comments autoclose in JavaScript, but not HTML.
 5116    cx.update_editor(|editor, cx| {
 5117        editor.handle_input("/", cx);
 5118        editor.handle_input("*", cx);
 5119    });
 5120    cx.assert_editor_state(
 5121        &r#"
 5122            <body>/*ˇ
 5123                <script>
 5124                    var x = 1;/*ˇ */
 5125                </script>
 5126            </body>/*ˇ
 5127        "#
 5128        .unindent(),
 5129    );
 5130}
 5131
 5132#[gpui::test]
 5133async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5134    init_test(cx, |_| {});
 5135
 5136    let mut cx = EditorTestContext::new(cx).await;
 5137
 5138    let rust_language = Arc::new(
 5139        Language::new(
 5140            LanguageConfig {
 5141                name: "Rust".into(),
 5142                brackets: serde_json::from_value(json!([
 5143                    { "start": "{", "end": "}", "close": true, "newline": true },
 5144                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5145                ]))
 5146                .unwrap(),
 5147                autoclose_before: "})]>".into(),
 5148                ..Default::default()
 5149            },
 5150            Some(tree_sitter_rust::language()),
 5151        )
 5152        .with_override_query("(string_literal) @string")
 5153        .unwrap(),
 5154    );
 5155
 5156    cx.language_registry().add(rust_language.clone());
 5157    cx.update_buffer(|buffer, cx| {
 5158        buffer.set_language(Some(rust_language), cx);
 5159    });
 5160
 5161    cx.set_state(
 5162        &r#"
 5163            let x = ˇ
 5164        "#
 5165        .unindent(),
 5166    );
 5167
 5168    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5169    cx.update_editor(|editor, cx| {
 5170        editor.handle_input("\"", cx);
 5171    });
 5172    cx.assert_editor_state(
 5173        &r#"
 5174            let x = "ˇ"
 5175        "#
 5176        .unindent(),
 5177    );
 5178
 5179    // Inserting another quotation mark. The cursor moves across the existing
 5180    // automatically-inserted quotation mark.
 5181    cx.update_editor(|editor, cx| {
 5182        editor.handle_input("\"", cx);
 5183    });
 5184    cx.assert_editor_state(
 5185        &r#"
 5186            let x = ""ˇ
 5187        "#
 5188        .unindent(),
 5189    );
 5190
 5191    // Reset
 5192    cx.set_state(
 5193        &r#"
 5194            let x = ˇ
 5195        "#
 5196        .unindent(),
 5197    );
 5198
 5199    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5200    cx.update_editor(|editor, cx| {
 5201        editor.handle_input("\"", cx);
 5202        editor.handle_input(" ", cx);
 5203        editor.move_left(&Default::default(), cx);
 5204        editor.handle_input("\\", cx);
 5205        editor.handle_input("\"", cx);
 5206    });
 5207    cx.assert_editor_state(
 5208        &r#"
 5209            let x = "\"ˇ "
 5210        "#
 5211        .unindent(),
 5212    );
 5213
 5214    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5215    // mark. Nothing is inserted.
 5216    cx.update_editor(|editor, cx| {
 5217        editor.move_right(&Default::default(), cx);
 5218        editor.handle_input("\"", cx);
 5219    });
 5220    cx.assert_editor_state(
 5221        &r#"
 5222            let x = "\" "ˇ
 5223        "#
 5224        .unindent(),
 5225    );
 5226}
 5227
 5228#[gpui::test]
 5229async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5230    init_test(cx, |_| {});
 5231
 5232    let language = Arc::new(Language::new(
 5233        LanguageConfig {
 5234            brackets: BracketPairConfig {
 5235                pairs: vec![
 5236                    BracketPair {
 5237                        start: "{".to_string(),
 5238                        end: "}".to_string(),
 5239                        close: true,
 5240                        newline: true,
 5241                    },
 5242                    BracketPair {
 5243                        start: "/* ".to_string(),
 5244                        end: "*/".to_string(),
 5245                        close: true,
 5246                        ..Default::default()
 5247                    },
 5248                ],
 5249                ..Default::default()
 5250            },
 5251            ..Default::default()
 5252        },
 5253        Some(tree_sitter_rust::language()),
 5254    ));
 5255
 5256    let text = r#"
 5257        a
 5258        b
 5259        c
 5260    "#
 5261    .unindent();
 5262
 5263    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5264    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5265    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5266    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5267        .await;
 5268
 5269    _ = view.update(cx, |view, cx| {
 5270        view.change_selections(None, cx, |s| {
 5271            s.select_display_ranges([
 5272                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5273                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5274                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5275            ])
 5276        });
 5277
 5278        view.handle_input("{", cx);
 5279        view.handle_input("{", cx);
 5280        view.handle_input("{", cx);
 5281        assert_eq!(
 5282            view.text(cx),
 5283            "
 5284                {{{a}}}
 5285                {{{b}}}
 5286                {{{c}}}
 5287            "
 5288            .unindent()
 5289        );
 5290        assert_eq!(
 5291            view.selections.display_ranges(cx),
 5292            [
 5293                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5294                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5295                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5296            ]
 5297        );
 5298
 5299        view.undo(&Undo, cx);
 5300        view.undo(&Undo, cx);
 5301        view.undo(&Undo, cx);
 5302        assert_eq!(
 5303            view.text(cx),
 5304            "
 5305                a
 5306                b
 5307                c
 5308            "
 5309            .unindent()
 5310        );
 5311        assert_eq!(
 5312            view.selections.display_ranges(cx),
 5313            [
 5314                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5315                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5316                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5317            ]
 5318        );
 5319
 5320        // Ensure inserting the first character of a multi-byte bracket pair
 5321        // doesn't surround the selections with the bracket.
 5322        view.handle_input("/", cx);
 5323        assert_eq!(
 5324            view.text(cx),
 5325            "
 5326                /
 5327                /
 5328                /
 5329            "
 5330            .unindent()
 5331        );
 5332        assert_eq!(
 5333            view.selections.display_ranges(cx),
 5334            [
 5335                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5336                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5337                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5338            ]
 5339        );
 5340
 5341        view.undo(&Undo, cx);
 5342        assert_eq!(
 5343            view.text(cx),
 5344            "
 5345                a
 5346                b
 5347                c
 5348            "
 5349            .unindent()
 5350        );
 5351        assert_eq!(
 5352            view.selections.display_ranges(cx),
 5353            [
 5354                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5355                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5356                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5357            ]
 5358        );
 5359
 5360        // Ensure inserting the last character of a multi-byte bracket pair
 5361        // doesn't surround the selections with the bracket.
 5362        view.handle_input("*", cx);
 5363        assert_eq!(
 5364            view.text(cx),
 5365            "
 5366                *
 5367                *
 5368                *
 5369            "
 5370            .unindent()
 5371        );
 5372        assert_eq!(
 5373            view.selections.display_ranges(cx),
 5374            [
 5375                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5376                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5377                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5378            ]
 5379        );
 5380    });
 5381}
 5382
 5383#[gpui::test]
 5384async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 5385    init_test(cx, |_| {});
 5386
 5387    let language = Arc::new(Language::new(
 5388        LanguageConfig {
 5389            brackets: BracketPairConfig {
 5390                pairs: vec![BracketPair {
 5391                    start: "{".to_string(),
 5392                    end: "}".to_string(),
 5393                    close: true,
 5394                    newline: true,
 5395                }],
 5396                ..Default::default()
 5397            },
 5398            autoclose_before: "}".to_string(),
 5399            ..Default::default()
 5400        },
 5401        Some(tree_sitter_rust::language()),
 5402    ));
 5403
 5404    let text = r#"
 5405        a
 5406        b
 5407        c
 5408    "#
 5409    .unindent();
 5410
 5411    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5412    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5413    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5414    editor
 5415        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5416        .await;
 5417
 5418    _ = editor.update(cx, |editor, cx| {
 5419        editor.change_selections(None, cx, |s| {
 5420            s.select_ranges([
 5421                Point::new(0, 1)..Point::new(0, 1),
 5422                Point::new(1, 1)..Point::new(1, 1),
 5423                Point::new(2, 1)..Point::new(2, 1),
 5424            ])
 5425        });
 5426
 5427        editor.handle_input("{", cx);
 5428        editor.handle_input("{", cx);
 5429        editor.handle_input("_", cx);
 5430        assert_eq!(
 5431            editor.text(cx),
 5432            "
 5433                a{{_}}
 5434                b{{_}}
 5435                c{{_}}
 5436            "
 5437            .unindent()
 5438        );
 5439        assert_eq!(
 5440            editor.selections.ranges::<Point>(cx),
 5441            [
 5442                Point::new(0, 4)..Point::new(0, 4),
 5443                Point::new(1, 4)..Point::new(1, 4),
 5444                Point::new(2, 4)..Point::new(2, 4)
 5445            ]
 5446        );
 5447
 5448        editor.backspace(&Default::default(), cx);
 5449        editor.backspace(&Default::default(), cx);
 5450        assert_eq!(
 5451            editor.text(cx),
 5452            "
 5453                a{}
 5454                b{}
 5455                c{}
 5456            "
 5457            .unindent()
 5458        );
 5459        assert_eq!(
 5460            editor.selections.ranges::<Point>(cx),
 5461            [
 5462                Point::new(0, 2)..Point::new(0, 2),
 5463                Point::new(1, 2)..Point::new(1, 2),
 5464                Point::new(2, 2)..Point::new(2, 2)
 5465            ]
 5466        );
 5467
 5468        editor.delete_to_previous_word_start(&Default::default(), cx);
 5469        assert_eq!(
 5470            editor.text(cx),
 5471            "
 5472                a
 5473                b
 5474                c
 5475            "
 5476            .unindent()
 5477        );
 5478        assert_eq!(
 5479            editor.selections.ranges::<Point>(cx),
 5480            [
 5481                Point::new(0, 1)..Point::new(0, 1),
 5482                Point::new(1, 1)..Point::new(1, 1),
 5483                Point::new(2, 1)..Point::new(2, 1)
 5484            ]
 5485        );
 5486    });
 5487}
 5488
 5489#[gpui::test]
 5490async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 5491    init_test(cx, |settings| {
 5492        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5493    });
 5494
 5495    let mut cx = EditorTestContext::new(cx).await;
 5496
 5497    let language = Arc::new(Language::new(
 5498        LanguageConfig {
 5499            brackets: BracketPairConfig {
 5500                pairs: vec![
 5501                    BracketPair {
 5502                        start: "{".to_string(),
 5503                        end: "}".to_string(),
 5504                        close: true,
 5505                        newline: true,
 5506                    },
 5507                    BracketPair {
 5508                        start: "(".to_string(),
 5509                        end: ")".to_string(),
 5510                        close: true,
 5511                        newline: true,
 5512                    },
 5513                    BracketPair {
 5514                        start: "[".to_string(),
 5515                        end: "]".to_string(),
 5516                        close: false,
 5517                        newline: true,
 5518                    },
 5519                ],
 5520                ..Default::default()
 5521            },
 5522            autoclose_before: "})]".to_string(),
 5523            ..Default::default()
 5524        },
 5525        Some(tree_sitter_rust::language()),
 5526    ));
 5527
 5528    cx.language_registry().add(language.clone());
 5529    cx.update_buffer(|buffer, cx| {
 5530        buffer.set_language(Some(language), cx);
 5531    });
 5532
 5533    cx.set_state(
 5534        &"
 5535            {(ˇ)}
 5536            [[ˇ]]
 5537            {(ˇ)}
 5538        "
 5539        .unindent(),
 5540    );
 5541
 5542    cx.update_editor(|view, cx| {
 5543        view.backspace(&Default::default(), cx);
 5544        view.backspace(&Default::default(), cx);
 5545    });
 5546
 5547    cx.assert_editor_state(
 5548        &"
 5549            ˇ
 5550            ˇ]]
 5551            ˇ
 5552        "
 5553        .unindent(),
 5554    );
 5555
 5556    cx.update_editor(|view, cx| {
 5557        view.handle_input("{", cx);
 5558        view.handle_input("{", cx);
 5559        view.move_right(&MoveRight, cx);
 5560        view.move_right(&MoveRight, cx);
 5561        view.move_left(&MoveLeft, cx);
 5562        view.move_left(&MoveLeft, cx);
 5563        view.backspace(&Default::default(), cx);
 5564    });
 5565
 5566    cx.assert_editor_state(
 5567        &"
 5568            {ˇ}
 5569            {ˇ}]]
 5570            {ˇ}
 5571        "
 5572        .unindent(),
 5573    );
 5574
 5575    cx.update_editor(|view, cx| {
 5576        view.backspace(&Default::default(), cx);
 5577    });
 5578
 5579    cx.assert_editor_state(
 5580        &"
 5581            ˇ
 5582            ˇ]]
 5583            ˇ
 5584        "
 5585        .unindent(),
 5586    );
 5587}
 5588
 5589#[gpui::test]
 5590async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 5591    init_test(cx, |_| {});
 5592
 5593    let language = Arc::new(Language::new(
 5594        LanguageConfig::default(),
 5595        Some(tree_sitter_rust::language()),
 5596    ));
 5597
 5598    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 5599    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5600    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5601    editor
 5602        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5603        .await;
 5604
 5605    _ = editor.update(cx, |editor, cx| {
 5606        editor.set_auto_replace_emoji_shortcode(true);
 5607
 5608        editor.handle_input("Hello ", cx);
 5609        editor.handle_input(":wave", cx);
 5610        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 5611
 5612        editor.handle_input(":", cx);
 5613        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 5614
 5615        editor.handle_input(" :smile", cx);
 5616        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 5617
 5618        editor.handle_input(":", cx);
 5619        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 5620
 5621        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 5622        editor.handle_input(":wave", cx);
 5623        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 5624
 5625        editor.handle_input(":", cx);
 5626        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 5627
 5628        editor.handle_input(":1", cx);
 5629        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 5630
 5631        editor.handle_input(":", cx);
 5632        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 5633
 5634        // Ensure shortcode does not get replaced when it is part of a word
 5635        editor.handle_input(" Test:wave", cx);
 5636        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 5637
 5638        editor.handle_input(":", cx);
 5639        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 5640
 5641        editor.set_auto_replace_emoji_shortcode(false);
 5642
 5643        // Ensure shortcode does not get replaced when auto replace is off
 5644        editor.handle_input(" :wave", cx);
 5645        assert_eq!(
 5646            editor.text(cx),
 5647            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 5648        );
 5649
 5650        editor.handle_input(":", cx);
 5651        assert_eq!(
 5652            editor.text(cx),
 5653            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 5654        );
 5655    });
 5656}
 5657
 5658#[gpui::test]
 5659async fn test_snippets(cx: &mut gpui::TestAppContext) {
 5660    init_test(cx, |_| {});
 5661
 5662    let (text, insertion_ranges) = marked_text_ranges(
 5663        indoc! {"
 5664            a.ˇ b
 5665            a.ˇ b
 5666            a.ˇ b
 5667        "},
 5668        false,
 5669    );
 5670
 5671    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 5672    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5673
 5674    _ = editor.update(cx, |editor, cx| {
 5675        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 5676
 5677        editor
 5678            .insert_snippet(&insertion_ranges, snippet, cx)
 5679            .unwrap();
 5680
 5681        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 5682            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 5683            assert_eq!(editor.text(cx), expected_text);
 5684            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 5685        }
 5686
 5687        assert(
 5688            editor,
 5689            cx,
 5690            indoc! {"
 5691                a.f(«one», two, «three») b
 5692                a.f(«one», two, «three») b
 5693                a.f(«one», two, «three») b
 5694            "},
 5695        );
 5696
 5697        // Can't move earlier than the first tab stop
 5698        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 5699        assert(
 5700            editor,
 5701            cx,
 5702            indoc! {"
 5703                a.f(«one», two, «three») b
 5704                a.f(«one», two, «three») b
 5705                a.f(«one», two, «three») b
 5706            "},
 5707        );
 5708
 5709        assert!(editor.move_to_next_snippet_tabstop(cx));
 5710        assert(
 5711            editor,
 5712            cx,
 5713            indoc! {"
 5714                a.f(one, «two», three) b
 5715                a.f(one, «two», three) b
 5716                a.f(one, «two», three) b
 5717            "},
 5718        );
 5719
 5720        editor.move_to_prev_snippet_tabstop(cx);
 5721        assert(
 5722            editor,
 5723            cx,
 5724            indoc! {"
 5725                a.f(«one», two, «three») b
 5726                a.f(«one», two, «three») b
 5727                a.f(«one», two, «three») b
 5728            "},
 5729        );
 5730
 5731        assert!(editor.move_to_next_snippet_tabstop(cx));
 5732        assert(
 5733            editor,
 5734            cx,
 5735            indoc! {"
 5736                a.f(one, «two», three) b
 5737                a.f(one, «two», three) b
 5738                a.f(one, «two», three) b
 5739            "},
 5740        );
 5741        assert!(editor.move_to_next_snippet_tabstop(cx));
 5742        assert(
 5743            editor,
 5744            cx,
 5745            indoc! {"
 5746                a.f(one, two, three)ˇ b
 5747                a.f(one, two, three)ˇ b
 5748                a.f(one, two, three)ˇ b
 5749            "},
 5750        );
 5751
 5752        // As soon as the last tab stop is reached, snippet state is gone
 5753        editor.move_to_prev_snippet_tabstop(cx);
 5754        assert(
 5755            editor,
 5756            cx,
 5757            indoc! {"
 5758                a.f(one, two, three)ˇ b
 5759                a.f(one, two, three)ˇ b
 5760                a.f(one, two, three)ˇ b
 5761            "},
 5762        );
 5763    });
 5764}
 5765
 5766#[gpui::test]
 5767async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 5768    init_test(cx, |_| {});
 5769
 5770    let fs = FakeFs::new(cx.executor());
 5771    fs.insert_file("/file.rs", Default::default()).await;
 5772
 5773    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 5774
 5775    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 5776    language_registry.add(rust_lang());
 5777    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 5778        "Rust",
 5779        FakeLspAdapter {
 5780            capabilities: lsp::ServerCapabilities {
 5781                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5782                ..Default::default()
 5783            },
 5784            ..Default::default()
 5785        },
 5786    );
 5787
 5788    let buffer = project
 5789        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 5790        .await
 5791        .unwrap();
 5792
 5793    cx.executor().start_waiting();
 5794    let fake_server = fake_servers.next().await.unwrap();
 5795
 5796    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5797    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5798    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 5799    assert!(cx.read(|cx| editor.is_dirty(cx)));
 5800
 5801    let save = editor
 5802        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5803        .unwrap();
 5804    fake_server
 5805        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 5806            assert_eq!(
 5807                params.text_document.uri,
 5808                lsp::Url::from_file_path("/file.rs").unwrap()
 5809            );
 5810            assert_eq!(params.options.tab_size, 4);
 5811            Ok(Some(vec![lsp::TextEdit::new(
 5812                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 5813                ", ".to_string(),
 5814            )]))
 5815        })
 5816        .next()
 5817        .await;
 5818    cx.executor().start_waiting();
 5819    save.await;
 5820
 5821    assert_eq!(
 5822        editor.update(cx, |editor, cx| editor.text(cx)),
 5823        "one, two\nthree\n"
 5824    );
 5825    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 5826
 5827    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 5828    assert!(cx.read(|cx| editor.is_dirty(cx)));
 5829
 5830    // Ensure we can still save even if formatting hangs.
 5831    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 5832        assert_eq!(
 5833            params.text_document.uri,
 5834            lsp::Url::from_file_path("/file.rs").unwrap()
 5835        );
 5836        futures::future::pending::<()>().await;
 5837        unreachable!()
 5838    });
 5839    let save = editor
 5840        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5841        .unwrap();
 5842    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 5843    cx.executor().start_waiting();
 5844    save.await;
 5845    assert_eq!(
 5846        editor.update(cx, |editor, cx| editor.text(cx)),
 5847        "one\ntwo\nthree\n"
 5848    );
 5849    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 5850
 5851    // For non-dirty buffer, no formatting request should be sent
 5852    let save = editor
 5853        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5854        .unwrap();
 5855    let _pending_format_request = fake_server
 5856        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 5857            panic!("Should not be invoked on non-dirty buffer");
 5858        })
 5859        .next();
 5860    cx.executor().start_waiting();
 5861    save.await;
 5862
 5863    // Set rust language override and assert overridden tabsize is sent to language server
 5864    update_test_language_settings(cx, |settings| {
 5865        settings.languages.insert(
 5866            "Rust".into(),
 5867            LanguageSettingsContent {
 5868                tab_size: NonZeroU32::new(8),
 5869                ..Default::default()
 5870            },
 5871        );
 5872    });
 5873
 5874    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 5875    assert!(cx.read(|cx| editor.is_dirty(cx)));
 5876    let save = editor
 5877        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5878        .unwrap();
 5879    fake_server
 5880        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 5881            assert_eq!(
 5882                params.text_document.uri,
 5883                lsp::Url::from_file_path("/file.rs").unwrap()
 5884            );
 5885            assert_eq!(params.options.tab_size, 8);
 5886            Ok(Some(vec![]))
 5887        })
 5888        .next()
 5889        .await;
 5890    cx.executor().start_waiting();
 5891    save.await;
 5892}
 5893
 5894#[gpui::test]
 5895async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 5896    init_test(cx, |_| {});
 5897
 5898    let cols = 4;
 5899    let rows = 10;
 5900    let sample_text_1 = sample_text(rows, cols, 'a');
 5901    assert_eq!(
 5902        sample_text_1,
 5903        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 5904    );
 5905    let sample_text_2 = sample_text(rows, cols, 'l');
 5906    assert_eq!(
 5907        sample_text_2,
 5908        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 5909    );
 5910    let sample_text_3 = sample_text(rows, cols, 'v');
 5911    assert_eq!(
 5912        sample_text_3,
 5913        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 5914    );
 5915
 5916    let fs = FakeFs::new(cx.executor());
 5917    fs.insert_tree(
 5918        "/a",
 5919        json!({
 5920            "main.rs": sample_text_1,
 5921            "other.rs": sample_text_2,
 5922            "lib.rs": sample_text_3,
 5923        }),
 5924    )
 5925    .await;
 5926
 5927    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 5928    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 5929    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 5930
 5931    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 5932    language_registry.add(rust_lang());
 5933    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 5934        "Rust",
 5935        FakeLspAdapter {
 5936            capabilities: lsp::ServerCapabilities {
 5937                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5938                ..Default::default()
 5939            },
 5940            ..Default::default()
 5941        },
 5942    );
 5943
 5944    let worktree = project.update(cx, |project, _| {
 5945        let mut worktrees = project.worktrees().collect::<Vec<_>>();
 5946        assert_eq!(worktrees.len(), 1);
 5947        worktrees.pop().unwrap()
 5948    });
 5949    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 5950
 5951    let buffer_1 = project
 5952        .update(cx, |project, cx| {
 5953            project.open_buffer((worktree_id, "main.rs"), cx)
 5954        })
 5955        .await
 5956        .unwrap();
 5957    let buffer_2 = project
 5958        .update(cx, |project, cx| {
 5959            project.open_buffer((worktree_id, "other.rs"), cx)
 5960        })
 5961        .await
 5962        .unwrap();
 5963    let buffer_3 = project
 5964        .update(cx, |project, cx| {
 5965            project.open_buffer((worktree_id, "lib.rs"), cx)
 5966        })
 5967        .await
 5968        .unwrap();
 5969
 5970    let multi_buffer = cx.new_model(|cx| {
 5971        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 5972        multi_buffer.push_excerpts(
 5973            buffer_1.clone(),
 5974            [
 5975                ExcerptRange {
 5976                    context: Point::new(0, 0)..Point::new(3, 0),
 5977                    primary: None,
 5978                },
 5979                ExcerptRange {
 5980                    context: Point::new(5, 0)..Point::new(7, 0),
 5981                    primary: None,
 5982                },
 5983                ExcerptRange {
 5984                    context: Point::new(9, 0)..Point::new(10, 4),
 5985                    primary: None,
 5986                },
 5987            ],
 5988            cx,
 5989        );
 5990        multi_buffer.push_excerpts(
 5991            buffer_2.clone(),
 5992            [
 5993                ExcerptRange {
 5994                    context: Point::new(0, 0)..Point::new(3, 0),
 5995                    primary: None,
 5996                },
 5997                ExcerptRange {
 5998                    context: Point::new(5, 0)..Point::new(7, 0),
 5999                    primary: None,
 6000                },
 6001                ExcerptRange {
 6002                    context: Point::new(9, 0)..Point::new(10, 4),
 6003                    primary: None,
 6004                },
 6005            ],
 6006            cx,
 6007        );
 6008        multi_buffer.push_excerpts(
 6009            buffer_3.clone(),
 6010            [
 6011                ExcerptRange {
 6012                    context: Point::new(0, 0)..Point::new(3, 0),
 6013                    primary: None,
 6014                },
 6015                ExcerptRange {
 6016                    context: Point::new(5, 0)..Point::new(7, 0),
 6017                    primary: None,
 6018                },
 6019                ExcerptRange {
 6020                    context: Point::new(9, 0)..Point::new(10, 4),
 6021                    primary: None,
 6022                },
 6023            ],
 6024            cx,
 6025        );
 6026        multi_buffer
 6027    });
 6028    let multi_buffer_editor =
 6029        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
 6030
 6031    multi_buffer_editor.update(cx, |editor, cx| {
 6032        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6033        editor.insert("|one|two|three|", cx);
 6034    });
 6035    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6036    multi_buffer_editor.update(cx, |editor, cx| {
 6037        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6038            s.select_ranges(Some(60..70))
 6039        });
 6040        editor.insert("|four|five|six|", cx);
 6041    });
 6042    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6043
 6044    // First two buffers should be edited, but not the third one.
 6045    assert_eq!(
 6046        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6047        "a|one|two|three|aa\nbbbb\ncccc\n\nffff\ngggg\n\njjjj\nllll\nmmmm\nnnnn|four|five|six|\nr\n\nuuuu\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 6048    );
 6049    buffer_1.update(cx, |buffer, _| {
 6050        assert!(buffer.is_dirty());
 6051        assert_eq!(
 6052            buffer.text(),
 6053            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6054        )
 6055    });
 6056    buffer_2.update(cx, |buffer, _| {
 6057        assert!(buffer.is_dirty());
 6058        assert_eq!(
 6059            buffer.text(),
 6060            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6061        )
 6062    });
 6063    buffer_3.update(cx, |buffer, _| {
 6064        assert!(!buffer.is_dirty());
 6065        assert_eq!(buffer.text(), sample_text_3,)
 6066    });
 6067
 6068    cx.executor().start_waiting();
 6069    let save = multi_buffer_editor
 6070        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6071        .unwrap();
 6072
 6073    let fake_server = fake_servers.next().await.unwrap();
 6074    fake_server
 6075        .server
 6076        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6077            Ok(Some(vec![lsp::TextEdit::new(
 6078                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6079                format!("[{} formatted]", params.text_document.uri),
 6080            )]))
 6081        })
 6082        .detach();
 6083    save.await;
 6084
 6085    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6086    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6087    assert_eq!(
 6088        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6089        "a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 6090    );
 6091    buffer_1.update(cx, |buffer, _| {
 6092        assert!(!buffer.is_dirty());
 6093        assert_eq!(
 6094            buffer.text(),
 6095            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6096        )
 6097    });
 6098    buffer_2.update(cx, |buffer, _| {
 6099        assert!(!buffer.is_dirty());
 6100        assert_eq!(
 6101            buffer.text(),
 6102            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6103        )
 6104    });
 6105    buffer_3.update(cx, |buffer, _| {
 6106        assert!(!buffer.is_dirty());
 6107        assert_eq!(buffer.text(), sample_text_3,)
 6108    });
 6109}
 6110
 6111#[gpui::test]
 6112async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6113    init_test(cx, |_| {});
 6114
 6115    let fs = FakeFs::new(cx.executor());
 6116    fs.insert_file("/file.rs", Default::default()).await;
 6117
 6118    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6119
 6120    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6121    language_registry.add(rust_lang());
 6122    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6123        "Rust",
 6124        FakeLspAdapter {
 6125            capabilities: lsp::ServerCapabilities {
 6126                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6127                ..Default::default()
 6128            },
 6129            ..Default::default()
 6130        },
 6131    );
 6132
 6133    let buffer = project
 6134        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6135        .await
 6136        .unwrap();
 6137
 6138    cx.executor().start_waiting();
 6139    let fake_server = fake_servers.next().await.unwrap();
 6140
 6141    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6142    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6143    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6144    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6145
 6146    let save = editor
 6147        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6148        .unwrap();
 6149    fake_server
 6150        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6151            assert_eq!(
 6152                params.text_document.uri,
 6153                lsp::Url::from_file_path("/file.rs").unwrap()
 6154            );
 6155            assert_eq!(params.options.tab_size, 4);
 6156            Ok(Some(vec![lsp::TextEdit::new(
 6157                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6158                ", ".to_string(),
 6159            )]))
 6160        })
 6161        .next()
 6162        .await;
 6163    cx.executor().start_waiting();
 6164    save.await;
 6165    assert_eq!(
 6166        editor.update(cx, |editor, cx| editor.text(cx)),
 6167        "one, two\nthree\n"
 6168    );
 6169    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6170
 6171    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6172    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6173
 6174    // Ensure we can still save even if formatting hangs.
 6175    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6176        move |params, _| async move {
 6177            assert_eq!(
 6178                params.text_document.uri,
 6179                lsp::Url::from_file_path("/file.rs").unwrap()
 6180            );
 6181            futures::future::pending::<()>().await;
 6182            unreachable!()
 6183        },
 6184    );
 6185    let save = editor
 6186        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6187        .unwrap();
 6188    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6189    cx.executor().start_waiting();
 6190    save.await;
 6191    assert_eq!(
 6192        editor.update(cx, |editor, cx| editor.text(cx)),
 6193        "one\ntwo\nthree\n"
 6194    );
 6195    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6196
 6197    // For non-dirty buffer, no formatting request should be sent
 6198    let save = editor
 6199        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6200        .unwrap();
 6201    let _pending_format_request = fake_server
 6202        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6203            panic!("Should not be invoked on non-dirty buffer");
 6204        })
 6205        .next();
 6206    cx.executor().start_waiting();
 6207    save.await;
 6208
 6209    // Set Rust language override and assert overridden tabsize is sent to language server
 6210    update_test_language_settings(cx, |settings| {
 6211        settings.languages.insert(
 6212            "Rust".into(),
 6213            LanguageSettingsContent {
 6214                tab_size: NonZeroU32::new(8),
 6215                ..Default::default()
 6216            },
 6217        );
 6218    });
 6219
 6220    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6221    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6222    let save = editor
 6223        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6224        .unwrap();
 6225    fake_server
 6226        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6227            assert_eq!(
 6228                params.text_document.uri,
 6229                lsp::Url::from_file_path("/file.rs").unwrap()
 6230            );
 6231            assert_eq!(params.options.tab_size, 8);
 6232            Ok(Some(vec![]))
 6233        })
 6234        .next()
 6235        .await;
 6236    cx.executor().start_waiting();
 6237    save.await;
 6238}
 6239
 6240#[gpui::test]
 6241async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6242    init_test(cx, |settings| {
 6243        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
 6244    });
 6245
 6246    let fs = FakeFs::new(cx.executor());
 6247    fs.insert_file("/file.rs", Default::default()).await;
 6248
 6249    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6250
 6251    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6252    language_registry.add(Arc::new(Language::new(
 6253        LanguageConfig {
 6254            name: "Rust".into(),
 6255            matcher: LanguageMatcher {
 6256                path_suffixes: vec!["rs".to_string()],
 6257                ..Default::default()
 6258            },
 6259            ..LanguageConfig::default()
 6260        },
 6261        Some(tree_sitter_rust::language()),
 6262    )));
 6263    update_test_language_settings(cx, |settings| {
 6264        // Enable Prettier formatting for the same buffer, and ensure
 6265        // LSP is called instead of Prettier.
 6266        settings.defaults.prettier = Some(PrettierSettings {
 6267            allowed: true,
 6268            ..PrettierSettings::default()
 6269        });
 6270    });
 6271    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6272        "Rust",
 6273        FakeLspAdapter {
 6274            capabilities: lsp::ServerCapabilities {
 6275                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6276                ..Default::default()
 6277            },
 6278            ..Default::default()
 6279        },
 6280    );
 6281
 6282    let buffer = project
 6283        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6284        .await
 6285        .unwrap();
 6286
 6287    cx.executor().start_waiting();
 6288    let fake_server = fake_servers.next().await.unwrap();
 6289
 6290    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6291    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6292    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6293
 6294    let format = editor
 6295        .update(cx, |editor, cx| {
 6296            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6297        })
 6298        .unwrap();
 6299    fake_server
 6300        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6301            assert_eq!(
 6302                params.text_document.uri,
 6303                lsp::Url::from_file_path("/file.rs").unwrap()
 6304            );
 6305            assert_eq!(params.options.tab_size, 4);
 6306            Ok(Some(vec![lsp::TextEdit::new(
 6307                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6308                ", ".to_string(),
 6309            )]))
 6310        })
 6311        .next()
 6312        .await;
 6313    cx.executor().start_waiting();
 6314    format.await;
 6315    assert_eq!(
 6316        editor.update(cx, |editor, cx| editor.text(cx)),
 6317        "one, two\nthree\n"
 6318    );
 6319
 6320    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6321    // Ensure we don't lock if formatting hangs.
 6322    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6323        assert_eq!(
 6324            params.text_document.uri,
 6325            lsp::Url::from_file_path("/file.rs").unwrap()
 6326        );
 6327        futures::future::pending::<()>().await;
 6328        unreachable!()
 6329    });
 6330    let format = editor
 6331        .update(cx, |editor, cx| {
 6332            editor.perform_format(project, FormatTrigger::Manual, cx)
 6333        })
 6334        .unwrap();
 6335    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6336    cx.executor().start_waiting();
 6337    format.await;
 6338    assert_eq!(
 6339        editor.update(cx, |editor, cx| editor.text(cx)),
 6340        "one\ntwo\nthree\n"
 6341    );
 6342}
 6343
 6344#[gpui::test]
 6345async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6346    init_test(cx, |_| {});
 6347
 6348    let mut cx = EditorLspTestContext::new_rust(
 6349        lsp::ServerCapabilities {
 6350            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6351            ..Default::default()
 6352        },
 6353        cx,
 6354    )
 6355    .await;
 6356
 6357    cx.set_state(indoc! {"
 6358        one.twoˇ
 6359    "});
 6360
 6361    // The format request takes a long time. When it completes, it inserts
 6362    // a newline and an indent before the `.`
 6363    cx.lsp
 6364        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6365            let executor = cx.background_executor().clone();
 6366            async move {
 6367                executor.timer(Duration::from_millis(100)).await;
 6368                Ok(Some(vec![lsp::TextEdit {
 6369                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6370                    new_text: "\n    ".into(),
 6371                }]))
 6372            }
 6373        });
 6374
 6375    // Submit a format request.
 6376    let format_1 = cx
 6377        .update_editor(|editor, cx| editor.format(&Format, cx))
 6378        .unwrap();
 6379    cx.executor().run_until_parked();
 6380
 6381    // Submit a second format request.
 6382    let format_2 = cx
 6383        .update_editor(|editor, cx| editor.format(&Format, cx))
 6384        .unwrap();
 6385    cx.executor().run_until_parked();
 6386
 6387    // Wait for both format requests to complete
 6388    cx.executor().advance_clock(Duration::from_millis(200));
 6389    cx.executor().start_waiting();
 6390    format_1.await.unwrap();
 6391    cx.executor().start_waiting();
 6392    format_2.await.unwrap();
 6393
 6394    // The formatting edits only happens once.
 6395    cx.assert_editor_state(indoc! {"
 6396        one
 6397            .twoˇ
 6398    "});
 6399}
 6400
 6401#[gpui::test]
 6402async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6403    init_test(cx, |settings| {
 6404        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 6405    });
 6406
 6407    let mut cx = EditorLspTestContext::new_rust(
 6408        lsp::ServerCapabilities {
 6409            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6410            ..Default::default()
 6411        },
 6412        cx,
 6413    )
 6414    .await;
 6415
 6416    // Set up a buffer white some trailing whitespace and no trailing newline.
 6417    cx.set_state(
 6418        &[
 6419            "one ",   //
 6420            "twoˇ",   //
 6421            "three ", //
 6422            "four",   //
 6423        ]
 6424        .join("\n"),
 6425    );
 6426
 6427    // Submit a format request.
 6428    let format = cx
 6429        .update_editor(|editor, cx| editor.format(&Format, cx))
 6430        .unwrap();
 6431
 6432    // Record which buffer changes have been sent to the language server
 6433    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6434    cx.lsp
 6435        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6436            let buffer_changes = buffer_changes.clone();
 6437            move |params, _| {
 6438                buffer_changes.lock().extend(
 6439                    params
 6440                        .content_changes
 6441                        .into_iter()
 6442                        .map(|e| (e.range.unwrap(), e.text)),
 6443                );
 6444            }
 6445        });
 6446
 6447    // Handle formatting requests to the language server.
 6448    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6449        let buffer_changes = buffer_changes.clone();
 6450        move |_, _| {
 6451            // When formatting is requested, trailing whitespace has already been stripped,
 6452            // and the trailing newline has already been added.
 6453            assert_eq!(
 6454                &buffer_changes.lock()[1..],
 6455                &[
 6456                    (
 6457                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6458                        "".into()
 6459                    ),
 6460                    (
 6461                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6462                        "".into()
 6463                    ),
 6464                    (
 6465                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6466                        "\n".into()
 6467                    ),
 6468                ]
 6469            );
 6470
 6471            // Insert blank lines between each line of the buffer.
 6472            async move {
 6473                Ok(Some(vec![
 6474                    lsp::TextEdit {
 6475                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6476                        new_text: "\n".into(),
 6477                    },
 6478                    lsp::TextEdit {
 6479                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6480                        new_text: "\n".into(),
 6481                    },
 6482                ]))
 6483            }
 6484        }
 6485    });
 6486
 6487    // After formatting the buffer, the trailing whitespace is stripped,
 6488    // a newline is appended, and the edits provided by the language server
 6489    // have been applied.
 6490    format.await.unwrap();
 6491    cx.assert_editor_state(
 6492        &[
 6493            "one",   //
 6494            "",      //
 6495            "twoˇ",  //
 6496            "",      //
 6497            "three", //
 6498            "four",  //
 6499            "",      //
 6500        ]
 6501        .join("\n"),
 6502    );
 6503
 6504    // Undoing the formatting undoes the trailing whitespace removal, the
 6505    // trailing newline, and the LSP edits.
 6506    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6507    cx.assert_editor_state(
 6508        &[
 6509            "one ",   //
 6510            "twoˇ",   //
 6511            "three ", //
 6512            "four",   //
 6513        ]
 6514        .join("\n"),
 6515    );
 6516}
 6517
 6518#[gpui::test]
 6519async fn test_completion(cx: &mut gpui::TestAppContext) {
 6520    init_test(cx, |_| {});
 6521
 6522    let mut cx = EditorLspTestContext::new_rust(
 6523        lsp::ServerCapabilities {
 6524            completion_provider: Some(lsp::CompletionOptions {
 6525                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 6526                resolve_provider: Some(true),
 6527                ..Default::default()
 6528            }),
 6529            ..Default::default()
 6530        },
 6531        cx,
 6532    )
 6533    .await;
 6534
 6535    cx.set_state(indoc! {"
 6536        oneˇ
 6537        two
 6538        three
 6539    "});
 6540    cx.simulate_keystroke(".");
 6541    handle_completion_request(
 6542        &mut cx,
 6543        indoc! {"
 6544            one.|<>
 6545            two
 6546            three
 6547        "},
 6548        vec!["first_completion", "second_completion"],
 6549    )
 6550    .await;
 6551    cx.condition(|editor, _| editor.context_menu_visible())
 6552        .await;
 6553    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6554        editor.context_menu_next(&Default::default(), cx);
 6555        editor
 6556            .confirm_completion(&ConfirmCompletion::default(), cx)
 6557            .unwrap()
 6558    });
 6559    cx.assert_editor_state(indoc! {"
 6560        one.second_completionˇ
 6561        two
 6562        three
 6563    "});
 6564
 6565    handle_resolve_completion_request(
 6566        &mut cx,
 6567        Some(vec![
 6568            (
 6569                //This overlaps with the primary completion edit which is
 6570                //misbehavior from the LSP spec, test that we filter it out
 6571                indoc! {"
 6572                    one.second_ˇcompletion
 6573                    two
 6574                    threeˇ
 6575                "},
 6576                "overlapping additional edit",
 6577            ),
 6578            (
 6579                indoc! {"
 6580                    one.second_completion
 6581                    two
 6582                    threeˇ
 6583                "},
 6584                "\nadditional edit",
 6585            ),
 6586        ]),
 6587    )
 6588    .await;
 6589    apply_additional_edits.await.unwrap();
 6590    cx.assert_editor_state(indoc! {"
 6591        one.second_completionˇ
 6592        two
 6593        three
 6594        additional edit
 6595    "});
 6596
 6597    cx.set_state(indoc! {"
 6598        one.second_completion
 6599        twoˇ
 6600        threeˇ
 6601        additional edit
 6602    "});
 6603    cx.simulate_keystroke(" ");
 6604    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6605    cx.simulate_keystroke("s");
 6606    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6607
 6608    cx.assert_editor_state(indoc! {"
 6609        one.second_completion
 6610        two sˇ
 6611        three sˇ
 6612        additional edit
 6613    "});
 6614    handle_completion_request(
 6615        &mut cx,
 6616        indoc! {"
 6617            one.second_completion
 6618            two s
 6619            three <s|>
 6620            additional edit
 6621        "},
 6622        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 6623    )
 6624    .await;
 6625    cx.condition(|editor, _| editor.context_menu_visible())
 6626        .await;
 6627
 6628    cx.simulate_keystroke("i");
 6629
 6630    handle_completion_request(
 6631        &mut cx,
 6632        indoc! {"
 6633            one.second_completion
 6634            two si
 6635            three <si|>
 6636            additional edit
 6637        "},
 6638        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 6639    )
 6640    .await;
 6641    cx.condition(|editor, _| editor.context_menu_visible())
 6642        .await;
 6643
 6644    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6645        editor
 6646            .confirm_completion(&ConfirmCompletion::default(), cx)
 6647            .unwrap()
 6648    });
 6649    cx.assert_editor_state(indoc! {"
 6650        one.second_completion
 6651        two sixth_completionˇ
 6652        three sixth_completionˇ
 6653        additional edit
 6654    "});
 6655
 6656    handle_resolve_completion_request(&mut cx, None).await;
 6657    apply_additional_edits.await.unwrap();
 6658
 6659    _ = cx.update(|cx| {
 6660        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6661            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6662                settings.show_completions_on_input = Some(false);
 6663            });
 6664        })
 6665    });
 6666    cx.set_state("editorˇ");
 6667    cx.simulate_keystroke(".");
 6668    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6669    cx.simulate_keystroke("c");
 6670    cx.simulate_keystroke("l");
 6671    cx.simulate_keystroke("o");
 6672    cx.assert_editor_state("editor.cloˇ");
 6673    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6674    cx.update_editor(|editor, cx| {
 6675        editor.show_completions(&ShowCompletions, cx);
 6676    });
 6677    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
 6678    cx.condition(|editor, _| editor.context_menu_visible())
 6679        .await;
 6680    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6681        editor
 6682            .confirm_completion(&ConfirmCompletion::default(), cx)
 6683            .unwrap()
 6684    });
 6685    cx.assert_editor_state("editor.closeˇ");
 6686    handle_resolve_completion_request(&mut cx, None).await;
 6687    apply_additional_edits.await.unwrap();
 6688}
 6689
 6690#[gpui::test]
 6691async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 6692    init_test(cx, |_| {});
 6693    let mut cx = EditorTestContext::new(cx).await;
 6694    let language = Arc::new(Language::new(
 6695        LanguageConfig {
 6696            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 6697            ..Default::default()
 6698        },
 6699        Some(tree_sitter_rust::language()),
 6700    ));
 6701    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 6702
 6703    // If multiple selections intersect a line, the line is only toggled once.
 6704    cx.set_state(indoc! {"
 6705        fn a() {
 6706            «//b();
 6707            ˇ»// «c();
 6708            //ˇ»  d();
 6709        }
 6710    "});
 6711
 6712    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6713
 6714    cx.assert_editor_state(indoc! {"
 6715        fn a() {
 6716            «b();
 6717            c();
 6718            ˇ» d();
 6719        }
 6720    "});
 6721
 6722    // The comment prefix is inserted at the same column for every line in a
 6723    // selection.
 6724    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6725
 6726    cx.assert_editor_state(indoc! {"
 6727        fn a() {
 6728            // «b();
 6729            // c();
 6730            ˇ»//  d();
 6731        }
 6732    "});
 6733
 6734    // If a selection ends at the beginning of a line, that line is not toggled.
 6735    cx.set_selections_state(indoc! {"
 6736        fn a() {
 6737            // b();
 6738            «// c();
 6739        ˇ»    //  d();
 6740        }
 6741    "});
 6742
 6743    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6744
 6745    cx.assert_editor_state(indoc! {"
 6746        fn a() {
 6747            // b();
 6748            «c();
 6749        ˇ»    //  d();
 6750        }
 6751    "});
 6752
 6753    // If a selection span a single line and is empty, the line is toggled.
 6754    cx.set_state(indoc! {"
 6755        fn a() {
 6756            a();
 6757            b();
 6758        ˇ
 6759        }
 6760    "});
 6761
 6762    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6763
 6764    cx.assert_editor_state(indoc! {"
 6765        fn a() {
 6766            a();
 6767            b();
 6768        //•ˇ
 6769        }
 6770    "});
 6771
 6772    // If a selection span multiple lines, empty lines are not toggled.
 6773    cx.set_state(indoc! {"
 6774        fn a() {
 6775            «a();
 6776
 6777            c();ˇ»
 6778        }
 6779    "});
 6780
 6781    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6782
 6783    cx.assert_editor_state(indoc! {"
 6784        fn a() {
 6785            // «a();
 6786
 6787            // c();ˇ»
 6788        }
 6789    "});
 6790
 6791    // If a selection includes multiple comment prefixes, all lines are uncommented.
 6792    cx.set_state(indoc! {"
 6793        fn a() {
 6794            «// a();
 6795            /// b();
 6796            //! c();ˇ»
 6797        }
 6798    "});
 6799
 6800    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6801
 6802    cx.assert_editor_state(indoc! {"
 6803        fn a() {
 6804            «a();
 6805            b();
 6806            c();ˇ»
 6807        }
 6808    "});
 6809}
 6810
 6811#[gpui::test]
 6812async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 6813    init_test(cx, |_| {});
 6814
 6815    let language = Arc::new(Language::new(
 6816        LanguageConfig {
 6817            line_comments: vec!["// ".into()],
 6818            ..Default::default()
 6819        },
 6820        Some(tree_sitter_rust::language()),
 6821    ));
 6822
 6823    let mut cx = EditorTestContext::new(cx).await;
 6824
 6825    cx.language_registry().add(language.clone());
 6826    cx.update_buffer(|buffer, cx| {
 6827        buffer.set_language(Some(language), cx);
 6828    });
 6829
 6830    let toggle_comments = &ToggleComments {
 6831        advance_downwards: true,
 6832    };
 6833
 6834    // Single cursor on one line -> advance
 6835    // Cursor moves horizontally 3 characters as well on non-blank line
 6836    cx.set_state(indoc!(
 6837        "fn a() {
 6838             ˇdog();
 6839             cat();
 6840        }"
 6841    ));
 6842    cx.update_editor(|editor, cx| {
 6843        editor.toggle_comments(toggle_comments, cx);
 6844    });
 6845    cx.assert_editor_state(indoc!(
 6846        "fn a() {
 6847             // dog();
 6848             catˇ();
 6849        }"
 6850    ));
 6851
 6852    // Single selection on one line -> don't advance
 6853    cx.set_state(indoc!(
 6854        "fn a() {
 6855             «dog()ˇ»;
 6856             cat();
 6857        }"
 6858    ));
 6859    cx.update_editor(|editor, cx| {
 6860        editor.toggle_comments(toggle_comments, cx);
 6861    });
 6862    cx.assert_editor_state(indoc!(
 6863        "fn a() {
 6864             // «dog()ˇ»;
 6865             cat();
 6866        }"
 6867    ));
 6868
 6869    // Multiple cursors on one line -> advance
 6870    cx.set_state(indoc!(
 6871        "fn a() {
 6872             ˇdˇog();
 6873             cat();
 6874        }"
 6875    ));
 6876    cx.update_editor(|editor, cx| {
 6877        editor.toggle_comments(toggle_comments, cx);
 6878    });
 6879    cx.assert_editor_state(indoc!(
 6880        "fn a() {
 6881             // dog();
 6882             catˇ(ˇ);
 6883        }"
 6884    ));
 6885
 6886    // Multiple cursors on one line, with selection -> don't advance
 6887    cx.set_state(indoc!(
 6888        "fn a() {
 6889             ˇdˇog«()ˇ»;
 6890             cat();
 6891        }"
 6892    ));
 6893    cx.update_editor(|editor, cx| {
 6894        editor.toggle_comments(toggle_comments, cx);
 6895    });
 6896    cx.assert_editor_state(indoc!(
 6897        "fn a() {
 6898             // ˇdˇog«()ˇ»;
 6899             cat();
 6900        }"
 6901    ));
 6902
 6903    // Single cursor on one line -> advance
 6904    // Cursor moves to column 0 on blank line
 6905    cx.set_state(indoc!(
 6906        "fn a() {
 6907             ˇdog();
 6908
 6909             cat();
 6910        }"
 6911    ));
 6912    cx.update_editor(|editor, cx| {
 6913        editor.toggle_comments(toggle_comments, cx);
 6914    });
 6915    cx.assert_editor_state(indoc!(
 6916        "fn a() {
 6917             // dog();
 6918        ˇ
 6919             cat();
 6920        }"
 6921    ));
 6922
 6923    // Single cursor on one line -> advance
 6924    // Cursor starts and ends at column 0
 6925    cx.set_state(indoc!(
 6926        "fn a() {
 6927         ˇ    dog();
 6928             cat();
 6929        }"
 6930    ));
 6931    cx.update_editor(|editor, cx| {
 6932        editor.toggle_comments(toggle_comments, cx);
 6933    });
 6934    cx.assert_editor_state(indoc!(
 6935        "fn a() {
 6936             // dog();
 6937         ˇ    cat();
 6938        }"
 6939    ));
 6940}
 6941
 6942#[gpui::test]
 6943async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 6944    init_test(cx, |_| {});
 6945
 6946    let mut cx = EditorTestContext::new(cx).await;
 6947
 6948    let html_language = Arc::new(
 6949        Language::new(
 6950            LanguageConfig {
 6951                name: "HTML".into(),
 6952                block_comment: Some(("<!-- ".into(), " -->".into())),
 6953                ..Default::default()
 6954            },
 6955            Some(tree_sitter_html::language()),
 6956        )
 6957        .with_injection_query(
 6958            r#"
 6959            (script_element
 6960                (raw_text) @content
 6961                (#set! "language" "javascript"))
 6962            "#,
 6963        )
 6964        .unwrap(),
 6965    );
 6966
 6967    let javascript_language = Arc::new(Language::new(
 6968        LanguageConfig {
 6969            name: "JavaScript".into(),
 6970            line_comments: vec!["// ".into()],
 6971            ..Default::default()
 6972        },
 6973        Some(tree_sitter_typescript::language_tsx()),
 6974    ));
 6975
 6976    cx.language_registry().add(html_language.clone());
 6977    cx.language_registry().add(javascript_language.clone());
 6978    cx.update_buffer(|buffer, cx| {
 6979        buffer.set_language(Some(html_language), cx);
 6980    });
 6981
 6982    // Toggle comments for empty selections
 6983    cx.set_state(
 6984        &r#"
 6985            <p>A</p>ˇ
 6986            <p>B</p>ˇ
 6987            <p>C</p>ˇ
 6988        "#
 6989        .unindent(),
 6990    );
 6991    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 6992    cx.assert_editor_state(
 6993        &r#"
 6994            <!-- <p>A</p>ˇ -->
 6995            <!-- <p>B</p>ˇ -->
 6996            <!-- <p>C</p>ˇ -->
 6997        "#
 6998        .unindent(),
 6999    );
 7000    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7001    cx.assert_editor_state(
 7002        &r#"
 7003            <p>A</p>ˇ
 7004            <p>B</p>ˇ
 7005            <p>C</p>ˇ
 7006        "#
 7007        .unindent(),
 7008    );
 7009
 7010    // Toggle comments for mixture of empty and non-empty selections, where
 7011    // multiple selections occupy a given line.
 7012    cx.set_state(
 7013        &r#"
 7014            <p>A«</p>
 7015            <p>ˇ»B</p>ˇ
 7016            <p>C«</p>
 7017            <p>ˇ»D</p>ˇ
 7018        "#
 7019        .unindent(),
 7020    );
 7021
 7022    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7023    cx.assert_editor_state(
 7024        &r#"
 7025            <!-- <p>A«</p>
 7026            <p>ˇ»B</p>ˇ -->
 7027            <!-- <p>C«</p>
 7028            <p>ˇ»D</p>ˇ -->
 7029        "#
 7030        .unindent(),
 7031    );
 7032    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7033    cx.assert_editor_state(
 7034        &r#"
 7035            <p>A«</p>
 7036            <p>ˇ»B</p>ˇ
 7037            <p>C«</p>
 7038            <p>ˇ»D</p>ˇ
 7039        "#
 7040        .unindent(),
 7041    );
 7042
 7043    // Toggle comments when different languages are active for different
 7044    // selections.
 7045    cx.set_state(
 7046        &r#"
 7047            ˇ<script>
 7048                ˇvar x = new Y();
 7049            ˇ</script>
 7050        "#
 7051        .unindent(),
 7052    );
 7053    cx.executor().run_until_parked();
 7054    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7055    cx.assert_editor_state(
 7056        &r#"
 7057            <!-- ˇ<script> -->
 7058                // ˇvar x = new Y();
 7059            <!-- ˇ</script> -->
 7060        "#
 7061        .unindent(),
 7062    );
 7063}
 7064
 7065#[gpui::test]
 7066fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 7067    init_test(cx, |_| {});
 7068
 7069    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7070    let multibuffer = cx.new_model(|cx| {
 7071        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7072        multibuffer.push_excerpts(
 7073            buffer.clone(),
 7074            [
 7075                ExcerptRange {
 7076                    context: Point::new(0, 0)..Point::new(0, 4),
 7077                    primary: None,
 7078                },
 7079                ExcerptRange {
 7080                    context: Point::new(1, 0)..Point::new(1, 4),
 7081                    primary: None,
 7082                },
 7083            ],
 7084            cx,
 7085        );
 7086        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 7087        multibuffer
 7088    });
 7089
 7090    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 7091    _ = view.update(cx, |view, cx| {
 7092        assert_eq!(view.text(cx), "aaaa\nbbbb");
 7093        view.change_selections(None, cx, |s| {
 7094            s.select_ranges([
 7095                Point::new(0, 0)..Point::new(0, 0),
 7096                Point::new(1, 0)..Point::new(1, 0),
 7097            ])
 7098        });
 7099
 7100        view.handle_input("X", cx);
 7101        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 7102        assert_eq!(
 7103            view.selections.ranges(cx),
 7104            [
 7105                Point::new(0, 1)..Point::new(0, 1),
 7106                Point::new(1, 1)..Point::new(1, 1),
 7107            ]
 7108        );
 7109
 7110        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 7111        view.change_selections(None, cx, |s| {
 7112            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 7113        });
 7114        view.backspace(&Default::default(), cx);
 7115        assert_eq!(view.text(cx), "Xa\nbbb");
 7116        assert_eq!(
 7117            view.selections.ranges(cx),
 7118            [Point::new(1, 0)..Point::new(1, 0)]
 7119        );
 7120
 7121        view.change_selections(None, cx, |s| {
 7122            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 7123        });
 7124        view.backspace(&Default::default(), cx);
 7125        assert_eq!(view.text(cx), "X\nbb");
 7126        assert_eq!(
 7127            view.selections.ranges(cx),
 7128            [Point::new(0, 1)..Point::new(0, 1)]
 7129        );
 7130    });
 7131}
 7132
 7133#[gpui::test]
 7134fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 7135    init_test(cx, |_| {});
 7136
 7137    let markers = vec![('[', ']').into(), ('(', ')').into()];
 7138    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 7139        indoc! {"
 7140            [aaaa
 7141            (bbbb]
 7142            cccc)",
 7143        },
 7144        markers.clone(),
 7145    );
 7146    let excerpt_ranges = markers.into_iter().map(|marker| {
 7147        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 7148        ExcerptRange {
 7149            context,
 7150            primary: None,
 7151        }
 7152    });
 7153    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 7154    let multibuffer = cx.new_model(|cx| {
 7155        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7156        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 7157        multibuffer
 7158    });
 7159
 7160    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 7161    _ = view.update(cx, |view, cx| {
 7162        let (expected_text, selection_ranges) = marked_text_ranges(
 7163            indoc! {"
 7164                aaaa
 7165                bˇbbb
 7166                bˇbbˇb
 7167                cccc"
 7168            },
 7169            true,
 7170        );
 7171        assert_eq!(view.text(cx), expected_text);
 7172        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 7173
 7174        view.handle_input("X", cx);
 7175
 7176        let (expected_text, expected_selections) = marked_text_ranges(
 7177            indoc! {"
 7178                aaaa
 7179                bXˇbbXb
 7180                bXˇbbXˇb
 7181                cccc"
 7182            },
 7183            false,
 7184        );
 7185        assert_eq!(view.text(cx), expected_text);
 7186        assert_eq!(view.selections.ranges(cx), expected_selections);
 7187
 7188        view.newline(&Newline, cx);
 7189        let (expected_text, expected_selections) = marked_text_ranges(
 7190            indoc! {"
 7191                aaaa
 7192                bX
 7193                ˇbbX
 7194                b
 7195                bX
 7196                ˇbbX
 7197                ˇb
 7198                cccc"
 7199            },
 7200            false,
 7201        );
 7202        assert_eq!(view.text(cx), expected_text);
 7203        assert_eq!(view.selections.ranges(cx), expected_selections);
 7204    });
 7205}
 7206
 7207#[gpui::test]
 7208fn test_refresh_selections(cx: &mut TestAppContext) {
 7209    init_test(cx, |_| {});
 7210
 7211    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7212    let mut excerpt1_id = None;
 7213    let multibuffer = cx.new_model(|cx| {
 7214        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7215        excerpt1_id = multibuffer
 7216            .push_excerpts(
 7217                buffer.clone(),
 7218                [
 7219                    ExcerptRange {
 7220                        context: Point::new(0, 0)..Point::new(1, 4),
 7221                        primary: None,
 7222                    },
 7223                    ExcerptRange {
 7224                        context: Point::new(1, 0)..Point::new(2, 4),
 7225                        primary: None,
 7226                    },
 7227                ],
 7228                cx,
 7229            )
 7230            .into_iter()
 7231            .next();
 7232        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 7233        multibuffer
 7234    });
 7235
 7236    let editor = cx.add_window(|cx| {
 7237        let mut editor = build_editor(multibuffer.clone(), cx);
 7238        let snapshot = editor.snapshot(cx);
 7239        editor.change_selections(None, cx, |s| {
 7240            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 7241        });
 7242        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 7243        assert_eq!(
 7244            editor.selections.ranges(cx),
 7245            [
 7246                Point::new(1, 3)..Point::new(1, 3),
 7247                Point::new(2, 1)..Point::new(2, 1),
 7248            ]
 7249        );
 7250        editor
 7251    });
 7252
 7253    // Refreshing selections is a no-op when excerpts haven't changed.
 7254    _ = editor.update(cx, |editor, cx| {
 7255        editor.change_selections(None, cx, |s| s.refresh());
 7256        assert_eq!(
 7257            editor.selections.ranges(cx),
 7258            [
 7259                Point::new(1, 3)..Point::new(1, 3),
 7260                Point::new(2, 1)..Point::new(2, 1),
 7261            ]
 7262        );
 7263    });
 7264
 7265    _ = multibuffer.update(cx, |multibuffer, cx| {
 7266        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 7267    });
 7268    _ = editor.update(cx, |editor, cx| {
 7269        // Removing an excerpt causes the first selection to become degenerate.
 7270        assert_eq!(
 7271            editor.selections.ranges(cx),
 7272            [
 7273                Point::new(0, 0)..Point::new(0, 0),
 7274                Point::new(0, 1)..Point::new(0, 1)
 7275            ]
 7276        );
 7277
 7278        // Refreshing selections will relocate the first selection to the original buffer
 7279        // location.
 7280        editor.change_selections(None, cx, |s| s.refresh());
 7281        assert_eq!(
 7282            editor.selections.ranges(cx),
 7283            [
 7284                Point::new(0, 1)..Point::new(0, 1),
 7285                Point::new(0, 3)..Point::new(0, 3)
 7286            ]
 7287        );
 7288        assert!(editor.selections.pending_anchor().is_some());
 7289    });
 7290}
 7291
 7292#[gpui::test]
 7293fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 7294    init_test(cx, |_| {});
 7295
 7296    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7297    let mut excerpt1_id = None;
 7298    let multibuffer = cx.new_model(|cx| {
 7299        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7300        excerpt1_id = multibuffer
 7301            .push_excerpts(
 7302                buffer.clone(),
 7303                [
 7304                    ExcerptRange {
 7305                        context: Point::new(0, 0)..Point::new(1, 4),
 7306                        primary: None,
 7307                    },
 7308                    ExcerptRange {
 7309                        context: Point::new(1, 0)..Point::new(2, 4),
 7310                        primary: None,
 7311                    },
 7312                ],
 7313                cx,
 7314            )
 7315            .into_iter()
 7316            .next();
 7317        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 7318        multibuffer
 7319    });
 7320
 7321    let editor = cx.add_window(|cx| {
 7322        let mut editor = build_editor(multibuffer.clone(), cx);
 7323        let snapshot = editor.snapshot(cx);
 7324        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 7325        assert_eq!(
 7326            editor.selections.ranges(cx),
 7327            [Point::new(1, 3)..Point::new(1, 3)]
 7328        );
 7329        editor
 7330    });
 7331
 7332    _ = multibuffer.update(cx, |multibuffer, cx| {
 7333        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 7334    });
 7335    _ = editor.update(cx, |editor, cx| {
 7336        assert_eq!(
 7337            editor.selections.ranges(cx),
 7338            [Point::new(0, 0)..Point::new(0, 0)]
 7339        );
 7340
 7341        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 7342        editor.change_selections(None, cx, |s| s.refresh());
 7343        assert_eq!(
 7344            editor.selections.ranges(cx),
 7345            [Point::new(0, 3)..Point::new(0, 3)]
 7346        );
 7347        assert!(editor.selections.pending_anchor().is_some());
 7348    });
 7349}
 7350
 7351#[gpui::test]
 7352async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 7353    init_test(cx, |_| {});
 7354
 7355    let language = Arc::new(
 7356        Language::new(
 7357            LanguageConfig {
 7358                brackets: BracketPairConfig {
 7359                    pairs: vec![
 7360                        BracketPair {
 7361                            start: "{".to_string(),
 7362                            end: "}".to_string(),
 7363                            close: true,
 7364                            newline: true,
 7365                        },
 7366                        BracketPair {
 7367                            start: "/* ".to_string(),
 7368                            end: " */".to_string(),
 7369                            close: true,
 7370                            newline: true,
 7371                        },
 7372                    ],
 7373                    ..Default::default()
 7374                },
 7375                ..Default::default()
 7376            },
 7377            Some(tree_sitter_rust::language()),
 7378        )
 7379        .with_indents_query("")
 7380        .unwrap(),
 7381    );
 7382
 7383    let text = concat!(
 7384        "{   }\n",     //
 7385        "  x\n",       //
 7386        "  /*   */\n", //
 7387        "x\n",         //
 7388        "{{} }\n",     //
 7389    );
 7390
 7391    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 7392    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7393    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7394    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 7395        .await;
 7396
 7397    _ = view.update(cx, |view, cx| {
 7398        view.change_selections(None, cx, |s| {
 7399            s.select_display_ranges([
 7400                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 7401                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 7402                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 7403            ])
 7404        });
 7405        view.newline(&Newline, cx);
 7406
 7407        assert_eq!(
 7408            view.buffer().read(cx).read(cx).text(),
 7409            concat!(
 7410                "{ \n",    // Suppress rustfmt
 7411                "\n",      //
 7412                "}\n",     //
 7413                "  x\n",   //
 7414                "  /* \n", //
 7415                "  \n",    //
 7416                "  */\n",  //
 7417                "x\n",     //
 7418                "{{} \n",  //
 7419                "}\n",     //
 7420            )
 7421        );
 7422    });
 7423}
 7424
 7425#[gpui::test]
 7426fn test_highlighted_ranges(cx: &mut TestAppContext) {
 7427    init_test(cx, |_| {});
 7428
 7429    let editor = cx.add_window(|cx| {
 7430        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 7431        build_editor(buffer.clone(), cx)
 7432    });
 7433
 7434    _ = editor.update(cx, |editor, cx| {
 7435        struct Type1;
 7436        struct Type2;
 7437
 7438        let buffer = editor.buffer.read(cx).snapshot(cx);
 7439
 7440        let anchor_range =
 7441            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 7442
 7443        editor.highlight_background::<Type1>(
 7444            &[
 7445                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 7446                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 7447                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 7448                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 7449            ],
 7450            |_| Hsla::red(),
 7451            cx,
 7452        );
 7453        editor.highlight_background::<Type2>(
 7454            &[
 7455                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 7456                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 7457                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 7458                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 7459            ],
 7460            |_| Hsla::green(),
 7461            cx,
 7462        );
 7463
 7464        let snapshot = editor.snapshot(cx);
 7465        let mut highlighted_ranges = editor.background_highlights_in_range(
 7466            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 7467            &snapshot,
 7468            cx.theme().colors(),
 7469        );
 7470        // Enforce a consistent ordering based on color without relying on the ordering of the
 7471        // highlight's `TypeId` which is non-executor.
 7472        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 7473        assert_eq!(
 7474            highlighted_ranges,
 7475            &[
 7476                (
 7477                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 7478                    Hsla::red(),
 7479                ),
 7480                (
 7481                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 7482                    Hsla::red(),
 7483                ),
 7484                (
 7485                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 7486                    Hsla::green(),
 7487                ),
 7488                (
 7489                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 7490                    Hsla::green(),
 7491                ),
 7492            ]
 7493        );
 7494        assert_eq!(
 7495            editor.background_highlights_in_range(
 7496                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 7497                &snapshot,
 7498                cx.theme().colors(),
 7499            ),
 7500            &[(
 7501                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 7502                Hsla::red(),
 7503            )]
 7504        );
 7505    });
 7506}
 7507
 7508#[gpui::test]
 7509async fn test_following(cx: &mut gpui::TestAppContext) {
 7510    init_test(cx, |_| {});
 7511
 7512    let fs = FakeFs::new(cx.executor());
 7513    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7514
 7515    let buffer = project.update(cx, |project, cx| {
 7516        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 7517        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 7518    });
 7519    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 7520    let follower = cx.update(|cx| {
 7521        cx.open_window(
 7522            WindowOptions {
 7523                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 7524                    gpui::Point::new(0.into(), 0.into()),
 7525                    gpui::Point::new(10.into(), 80.into()),
 7526                ))),
 7527                ..Default::default()
 7528            },
 7529            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 7530        )
 7531    });
 7532
 7533    let is_still_following = Rc::new(RefCell::new(true));
 7534    let follower_edit_event_count = Rc::new(RefCell::new(0));
 7535    let pending_update = Rc::new(RefCell::new(None));
 7536    _ = follower.update(cx, {
 7537        let update = pending_update.clone();
 7538        let is_still_following = is_still_following.clone();
 7539        let follower_edit_event_count = follower_edit_event_count.clone();
 7540        |_, cx| {
 7541            cx.subscribe(
 7542                &leader.root_view(cx).unwrap(),
 7543                move |_, leader, event, cx| {
 7544                    leader
 7545                        .read(cx)
 7546                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 7547                },
 7548            )
 7549            .detach();
 7550
 7551            cx.subscribe(
 7552                &follower.root_view(cx).unwrap(),
 7553                move |_, _, event: &EditorEvent, _cx| {
 7554                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 7555                        *is_still_following.borrow_mut() = false;
 7556                    }
 7557
 7558                    if let EditorEvent::BufferEdited = event {
 7559                        *follower_edit_event_count.borrow_mut() += 1;
 7560                    }
 7561                },
 7562            )
 7563            .detach();
 7564        }
 7565    });
 7566
 7567    // Update the selections only
 7568    _ = leader.update(cx, |leader, cx| {
 7569        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 7570    });
 7571    follower
 7572        .update(cx, |follower, cx| {
 7573            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7574        })
 7575        .unwrap()
 7576        .await
 7577        .unwrap();
 7578    _ = follower.update(cx, |follower, cx| {
 7579        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 7580    });
 7581    assert_eq!(*is_still_following.borrow(), true);
 7582    assert_eq!(*follower_edit_event_count.borrow(), 0);
 7583
 7584    // Update the scroll position only
 7585    _ = leader.update(cx, |leader, cx| {
 7586        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 7587    });
 7588    follower
 7589        .update(cx, |follower, cx| {
 7590            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7591        })
 7592        .unwrap()
 7593        .await
 7594        .unwrap();
 7595    assert_eq!(
 7596        follower
 7597            .update(cx, |follower, cx| follower.scroll_position(cx))
 7598            .unwrap(),
 7599        gpui::Point::new(1.5, 3.5)
 7600    );
 7601    assert_eq!(*is_still_following.borrow(), true);
 7602    assert_eq!(*follower_edit_event_count.borrow(), 0);
 7603
 7604    // Update the selections and scroll position. The follower's scroll position is updated
 7605    // via autoscroll, not via the leader's exact scroll position.
 7606    _ = leader.update(cx, |leader, cx| {
 7607        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7608        leader.request_autoscroll(Autoscroll::newest(), cx);
 7609        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 7610    });
 7611    follower
 7612        .update(cx, |follower, cx| {
 7613            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7614        })
 7615        .unwrap()
 7616        .await
 7617        .unwrap();
 7618    _ = follower.update(cx, |follower, cx| {
 7619        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 7620        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 7621    });
 7622    assert_eq!(*is_still_following.borrow(), true);
 7623
 7624    // Creating a pending selection that precedes another selection
 7625    _ = leader.update(cx, |leader, cx| {
 7626        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 7627        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 7628    });
 7629    follower
 7630        .update(cx, |follower, cx| {
 7631            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7632        })
 7633        .unwrap()
 7634        .await
 7635        .unwrap();
 7636    _ = follower.update(cx, |follower, cx| {
 7637        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 7638    });
 7639    assert_eq!(*is_still_following.borrow(), true);
 7640
 7641    // Extend the pending selection so that it surrounds another selection
 7642    _ = leader.update(cx, |leader, cx| {
 7643        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 7644    });
 7645    follower
 7646        .update(cx, |follower, cx| {
 7647            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7648        })
 7649        .unwrap()
 7650        .await
 7651        .unwrap();
 7652    _ = follower.update(cx, |follower, cx| {
 7653        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 7654    });
 7655
 7656    // Scrolling locally breaks the follow
 7657    _ = follower.update(cx, |follower, cx| {
 7658        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 7659        follower.set_scroll_anchor(
 7660            ScrollAnchor {
 7661                anchor: top_anchor,
 7662                offset: gpui::Point::new(0.0, 0.5),
 7663            },
 7664            cx,
 7665        );
 7666    });
 7667    assert_eq!(*is_still_following.borrow(), false);
 7668}
 7669
 7670#[gpui::test]
 7671async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 7672    init_test(cx, |_| {});
 7673
 7674    let fs = FakeFs::new(cx.executor());
 7675    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7676    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 7677    let pane = workspace
 7678        .update(cx, |workspace, _| workspace.active_pane().clone())
 7679        .unwrap();
 7680
 7681    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7682
 7683    let leader = pane.update(cx, |_, cx| {
 7684        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 7685        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 7686    });
 7687
 7688    // Start following the editor when it has no excerpts.
 7689    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 7690    let follower_1 = cx
 7691        .update_window(*workspace.deref(), |_, cx| {
 7692            Editor::from_state_proto(
 7693                pane.clone(),
 7694                workspace.root_view(cx).unwrap(),
 7695                ViewId {
 7696                    creator: Default::default(),
 7697                    id: 0,
 7698                },
 7699                &mut state_message,
 7700                cx,
 7701            )
 7702        })
 7703        .unwrap()
 7704        .unwrap()
 7705        .await
 7706        .unwrap();
 7707
 7708    let update_message = Rc::new(RefCell::new(None));
 7709    follower_1.update(cx, {
 7710        let update = update_message.clone();
 7711        |_, cx| {
 7712            cx.subscribe(&leader, move |_, leader, event, cx| {
 7713                leader
 7714                    .read(cx)
 7715                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 7716            })
 7717            .detach();
 7718        }
 7719    });
 7720
 7721    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 7722        (
 7723            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 7724            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 7725        )
 7726    });
 7727
 7728    // Insert some excerpts.
 7729    _ = leader.update(cx, |leader, cx| {
 7730        leader.buffer.update(cx, |multibuffer, cx| {
 7731            let excerpt_ids = multibuffer.push_excerpts(
 7732                buffer_1.clone(),
 7733                [
 7734                    ExcerptRange {
 7735                        context: 1..6,
 7736                        primary: None,
 7737                    },
 7738                    ExcerptRange {
 7739                        context: 12..15,
 7740                        primary: None,
 7741                    },
 7742                    ExcerptRange {
 7743                        context: 0..3,
 7744                        primary: None,
 7745                    },
 7746                ],
 7747                cx,
 7748            );
 7749            multibuffer.insert_excerpts_after(
 7750                excerpt_ids[0],
 7751                buffer_2.clone(),
 7752                [
 7753                    ExcerptRange {
 7754                        context: 8..12,
 7755                        primary: None,
 7756                    },
 7757                    ExcerptRange {
 7758                        context: 0..6,
 7759                        primary: None,
 7760                    },
 7761                ],
 7762                cx,
 7763            );
 7764        });
 7765    });
 7766
 7767    // Apply the update of adding the excerpts.
 7768    follower_1
 7769        .update(cx, |follower, cx| {
 7770            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 7771        })
 7772        .await
 7773        .unwrap();
 7774    assert_eq!(
 7775        follower_1.update(cx, |editor, cx| editor.text(cx)),
 7776        leader.update(cx, |editor, cx| editor.text(cx))
 7777    );
 7778    update_message.borrow_mut().take();
 7779
 7780    // Start following separately after it already has excerpts.
 7781    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 7782    let follower_2 = cx
 7783        .update_window(*workspace.deref(), |_, cx| {
 7784            Editor::from_state_proto(
 7785                pane.clone(),
 7786                workspace.root_view(cx).unwrap().clone(),
 7787                ViewId {
 7788                    creator: Default::default(),
 7789                    id: 0,
 7790                },
 7791                &mut state_message,
 7792                cx,
 7793            )
 7794        })
 7795        .unwrap()
 7796        .unwrap()
 7797        .await
 7798        .unwrap();
 7799    assert_eq!(
 7800        follower_2.update(cx, |editor, cx| editor.text(cx)),
 7801        leader.update(cx, |editor, cx| editor.text(cx))
 7802    );
 7803
 7804    // Remove some excerpts.
 7805    _ = leader.update(cx, |leader, cx| {
 7806        leader.buffer.update(cx, |multibuffer, cx| {
 7807            let excerpt_ids = multibuffer.excerpt_ids();
 7808            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 7809            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 7810        });
 7811    });
 7812
 7813    // Apply the update of removing the excerpts.
 7814    follower_1
 7815        .update(cx, |follower, cx| {
 7816            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 7817        })
 7818        .await
 7819        .unwrap();
 7820    follower_2
 7821        .update(cx, |follower, cx| {
 7822            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 7823        })
 7824        .await
 7825        .unwrap();
 7826    update_message.borrow_mut().take();
 7827    assert_eq!(
 7828        follower_1.update(cx, |editor, cx| editor.text(cx)),
 7829        leader.update(cx, |editor, cx| editor.text(cx))
 7830    );
 7831}
 7832
 7833#[gpui::test]
 7834async fn go_to_prev_overlapping_diagnostic(
 7835    executor: BackgroundExecutor,
 7836    cx: &mut gpui::TestAppContext,
 7837) {
 7838    init_test(cx, |_| {});
 7839
 7840    let mut cx = EditorTestContext::new(cx).await;
 7841    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 7842
 7843    cx.set_state(indoc! {"
 7844        ˇfn func(abc def: i32) -> u32 {
 7845        }
 7846    "});
 7847
 7848    _ = cx.update(|cx| {
 7849        _ = project.update(cx, |project, cx| {
 7850            project
 7851                .update_diagnostics(
 7852                    LanguageServerId(0),
 7853                    lsp::PublishDiagnosticsParams {
 7854                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 7855                        version: None,
 7856                        diagnostics: vec![
 7857                            lsp::Diagnostic {
 7858                                range: lsp::Range::new(
 7859                                    lsp::Position::new(0, 11),
 7860                                    lsp::Position::new(0, 12),
 7861                                ),
 7862                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 7863                                ..Default::default()
 7864                            },
 7865                            lsp::Diagnostic {
 7866                                range: lsp::Range::new(
 7867                                    lsp::Position::new(0, 12),
 7868                                    lsp::Position::new(0, 15),
 7869                                ),
 7870                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 7871                                ..Default::default()
 7872                            },
 7873                            lsp::Diagnostic {
 7874                                range: lsp::Range::new(
 7875                                    lsp::Position::new(0, 25),
 7876                                    lsp::Position::new(0, 28),
 7877                                ),
 7878                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 7879                                ..Default::default()
 7880                            },
 7881                        ],
 7882                    },
 7883                    &[],
 7884                    cx,
 7885                )
 7886                .unwrap()
 7887        });
 7888    });
 7889
 7890    executor.run_until_parked();
 7891
 7892    cx.update_editor(|editor, cx| {
 7893        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 7894    });
 7895
 7896    cx.assert_editor_state(indoc! {"
 7897        fn func(abc def: i32) -> ˇu32 {
 7898        }
 7899    "});
 7900
 7901    cx.update_editor(|editor, cx| {
 7902        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 7903    });
 7904
 7905    cx.assert_editor_state(indoc! {"
 7906        fn func(abc ˇdef: i32) -> u32 {
 7907        }
 7908    "});
 7909
 7910    cx.update_editor(|editor, cx| {
 7911        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 7912    });
 7913
 7914    cx.assert_editor_state(indoc! {"
 7915        fn func(abcˇ def: i32) -> u32 {
 7916        }
 7917    "});
 7918
 7919    cx.update_editor(|editor, cx| {
 7920        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 7921    });
 7922
 7923    cx.assert_editor_state(indoc! {"
 7924        fn func(abc def: i32) -> ˇu32 {
 7925        }
 7926    "});
 7927}
 7928
 7929#[gpui::test]
 7930async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 7931    init_test(cx, |_| {});
 7932
 7933    let mut cx = EditorTestContext::new(cx).await;
 7934
 7935    let diff_base = r#"
 7936        use some::mod;
 7937
 7938        const A: u32 = 42;
 7939
 7940        fn main() {
 7941            println!("hello");
 7942
 7943            println!("world");
 7944        }
 7945        "#
 7946    .unindent();
 7947
 7948    // Edits are modified, removed, modified, added
 7949    cx.set_state(
 7950        &r#"
 7951        use some::modified;
 7952
 7953        ˇ
 7954        fn main() {
 7955            println!("hello there");
 7956
 7957            println!("around the");
 7958            println!("world");
 7959        }
 7960        "#
 7961        .unindent(),
 7962    );
 7963
 7964    cx.set_diff_base(Some(&diff_base));
 7965    executor.run_until_parked();
 7966
 7967    cx.update_editor(|editor, cx| {
 7968        //Wrap around the bottom of the buffer
 7969        for _ in 0..3 {
 7970            editor.go_to_hunk(&GoToHunk, cx);
 7971        }
 7972    });
 7973
 7974    cx.assert_editor_state(
 7975        &r#"
 7976        ˇuse some::modified;
 7977
 7978
 7979        fn main() {
 7980            println!("hello there");
 7981
 7982            println!("around the");
 7983            println!("world");
 7984        }
 7985        "#
 7986        .unindent(),
 7987    );
 7988
 7989    cx.update_editor(|editor, cx| {
 7990        //Wrap around the top of the buffer
 7991        for _ in 0..2 {
 7992            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 7993        }
 7994    });
 7995
 7996    cx.assert_editor_state(
 7997        &r#"
 7998        use some::modified;
 7999
 8000
 8001        fn main() {
 8002        ˇ    println!("hello there");
 8003
 8004            println!("around the");
 8005            println!("world");
 8006        }
 8007        "#
 8008        .unindent(),
 8009    );
 8010
 8011    cx.update_editor(|editor, cx| {
 8012        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 8013    });
 8014
 8015    cx.assert_editor_state(
 8016        &r#"
 8017        use some::modified;
 8018
 8019        ˇ
 8020        fn main() {
 8021            println!("hello there");
 8022
 8023            println!("around the");
 8024            println!("world");
 8025        }
 8026        "#
 8027        .unindent(),
 8028    );
 8029
 8030    cx.update_editor(|editor, cx| {
 8031        for _ in 0..3 {
 8032            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 8033        }
 8034    });
 8035
 8036    cx.assert_editor_state(
 8037        &r#"
 8038        use some::modified;
 8039
 8040
 8041        fn main() {
 8042        ˇ    println!("hello there");
 8043
 8044            println!("around the");
 8045            println!("world");
 8046        }
 8047        "#
 8048        .unindent(),
 8049    );
 8050
 8051    cx.update_editor(|editor, cx| {
 8052        editor.fold(&Fold, cx);
 8053
 8054        //Make sure that the fold only gets one hunk
 8055        for _ in 0..4 {
 8056            editor.go_to_hunk(&GoToHunk, cx);
 8057        }
 8058    });
 8059
 8060    cx.assert_editor_state(
 8061        &r#"
 8062        ˇuse some::modified;
 8063
 8064
 8065        fn main() {
 8066            println!("hello there");
 8067
 8068            println!("around the");
 8069            println!("world");
 8070        }
 8071        "#
 8072        .unindent(),
 8073    );
 8074}
 8075
 8076#[test]
 8077fn test_split_words() {
 8078    fn split(text: &str) -> Vec<&str> {
 8079        split_words(text).collect()
 8080    }
 8081
 8082    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 8083    assert_eq!(split("hello_world"), &["hello_", "world"]);
 8084    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 8085    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 8086    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 8087    assert_eq!(split("helloworld"), &["helloworld"]);
 8088
 8089    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 8090}
 8091
 8092#[gpui::test]
 8093async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 8094    init_test(cx, |_| {});
 8095
 8096    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 8097    let mut assert = |before, after| {
 8098        let _state_context = cx.set_state(before);
 8099        cx.update_editor(|editor, cx| {
 8100            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 8101        });
 8102        cx.assert_editor_state(after);
 8103    };
 8104
 8105    // Outside bracket jumps to outside of matching bracket
 8106    assert("console.logˇ(var);", "console.log(var)ˇ;");
 8107    assert("console.log(var)ˇ;", "console.logˇ(var);");
 8108
 8109    // Inside bracket jumps to inside of matching bracket
 8110    assert("console.log(ˇvar);", "console.log(varˇ);");
 8111    assert("console.log(varˇ);", "console.log(ˇvar);");
 8112
 8113    // When outside a bracket and inside, favor jumping to the inside bracket
 8114    assert(
 8115        "console.log('foo', [1, 2, 3]ˇ);",
 8116        "console.log(ˇ'foo', [1, 2, 3]);",
 8117    );
 8118    assert(
 8119        "console.log(ˇ'foo', [1, 2, 3]);",
 8120        "console.log('foo', [1, 2, 3]ˇ);",
 8121    );
 8122
 8123    // Bias forward if two options are equally likely
 8124    assert(
 8125        "let result = curried_fun()ˇ();",
 8126        "let result = curried_fun()()ˇ;",
 8127    );
 8128
 8129    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 8130    assert(
 8131        indoc! {"
 8132            function test() {
 8133                console.log('test')ˇ
 8134            }"},
 8135        indoc! {"
 8136            function test() {
 8137                console.logˇ('test')
 8138            }"},
 8139    );
 8140}
 8141
 8142#[gpui::test]
 8143async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 8144    init_test(cx, |_| {});
 8145
 8146    let fs = FakeFs::new(cx.executor());
 8147    fs.insert_tree(
 8148        "/a",
 8149        json!({
 8150            "main.rs": "fn main() { let a = 5; }",
 8151            "other.rs": "// Test file",
 8152        }),
 8153    )
 8154    .await;
 8155    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8156
 8157    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8158    language_registry.add(Arc::new(Language::new(
 8159        LanguageConfig {
 8160            name: "Rust".into(),
 8161            matcher: LanguageMatcher {
 8162                path_suffixes: vec!["rs".to_string()],
 8163                ..Default::default()
 8164            },
 8165            brackets: BracketPairConfig {
 8166                pairs: vec![BracketPair {
 8167                    start: "{".to_string(),
 8168                    end: "}".to_string(),
 8169                    close: true,
 8170                    newline: true,
 8171                }],
 8172                disabled_scopes_by_bracket_ix: Vec::new(),
 8173            },
 8174            ..Default::default()
 8175        },
 8176        Some(tree_sitter_rust::language()),
 8177    )));
 8178    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 8179        "Rust",
 8180        FakeLspAdapter {
 8181            capabilities: lsp::ServerCapabilities {
 8182                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 8183                    first_trigger_character: "{".to_string(),
 8184                    more_trigger_character: None,
 8185                }),
 8186                ..Default::default()
 8187            },
 8188            ..Default::default()
 8189        },
 8190    );
 8191
 8192    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8193
 8194    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8195
 8196    let worktree_id = workspace
 8197        .update(cx, |workspace, cx| {
 8198            workspace.project().update(cx, |project, cx| {
 8199                project.worktrees().next().unwrap().read(cx).id()
 8200            })
 8201        })
 8202        .unwrap();
 8203
 8204    let buffer = project
 8205        .update(cx, |project, cx| {
 8206            project.open_local_buffer("/a/main.rs", cx)
 8207        })
 8208        .await
 8209        .unwrap();
 8210    cx.executor().run_until_parked();
 8211    cx.executor().start_waiting();
 8212    let fake_server = fake_servers.next().await.unwrap();
 8213    let editor_handle = workspace
 8214        .update(cx, |workspace, cx| {
 8215            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 8216        })
 8217        .unwrap()
 8218        .await
 8219        .unwrap()
 8220        .downcast::<Editor>()
 8221        .unwrap();
 8222
 8223    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 8224        assert_eq!(
 8225            params.text_document_position.text_document.uri,
 8226            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 8227        );
 8228        assert_eq!(
 8229            params.text_document_position.position,
 8230            lsp::Position::new(0, 21),
 8231        );
 8232
 8233        Ok(Some(vec![lsp::TextEdit {
 8234            new_text: "]".to_string(),
 8235            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 8236        }]))
 8237    });
 8238
 8239    editor_handle.update(cx, |editor, cx| {
 8240        editor.focus(cx);
 8241        editor.change_selections(None, cx, |s| {
 8242            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 8243        });
 8244        editor.handle_input("{", cx);
 8245    });
 8246
 8247    cx.executor().run_until_parked();
 8248
 8249    _ = buffer.update(cx, |buffer, _| {
 8250        assert_eq!(
 8251            buffer.text(),
 8252            "fn main() { let a = {5}; }",
 8253            "No extra braces from on type formatting should appear in the buffer"
 8254        )
 8255    });
 8256}
 8257
 8258#[gpui::test]
 8259async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 8260    init_test(cx, |_| {});
 8261
 8262    let fs = FakeFs::new(cx.executor());
 8263    fs.insert_tree(
 8264        "/a",
 8265        json!({
 8266            "main.rs": "fn main() { let a = 5; }",
 8267            "other.rs": "// Test file",
 8268        }),
 8269    )
 8270    .await;
 8271
 8272    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8273
 8274    let server_restarts = Arc::new(AtomicUsize::new(0));
 8275    let closure_restarts = Arc::clone(&server_restarts);
 8276    let language_server_name = "test language server";
 8277    let language_name: Arc<str> = "Rust".into();
 8278
 8279    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8280    language_registry.add(Arc::new(Language::new(
 8281        LanguageConfig {
 8282            name: Arc::clone(&language_name),
 8283            matcher: LanguageMatcher {
 8284                path_suffixes: vec!["rs".to_string()],
 8285                ..Default::default()
 8286            },
 8287            ..Default::default()
 8288        },
 8289        Some(tree_sitter_rust::language()),
 8290    )));
 8291    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 8292        "Rust",
 8293        FakeLspAdapter {
 8294            name: language_server_name,
 8295            initialization_options: Some(json!({
 8296                "testOptionValue": true
 8297            })),
 8298            initializer: Some(Box::new(move |fake_server| {
 8299                let task_restarts = Arc::clone(&closure_restarts);
 8300                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 8301                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 8302                    futures::future::ready(Ok(()))
 8303                });
 8304            })),
 8305            ..Default::default()
 8306        },
 8307    );
 8308
 8309    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8310    let _buffer = project
 8311        .update(cx, |project, cx| {
 8312            project.open_local_buffer("/a/main.rs", cx)
 8313        })
 8314        .await
 8315        .unwrap();
 8316    let _fake_server = fake_servers.next().await.unwrap();
 8317    update_test_language_settings(cx, |language_settings| {
 8318        language_settings.languages.insert(
 8319            Arc::clone(&language_name),
 8320            LanguageSettingsContent {
 8321                tab_size: NonZeroU32::new(8),
 8322                ..Default::default()
 8323            },
 8324        );
 8325    });
 8326    cx.executor().run_until_parked();
 8327    assert_eq!(
 8328        server_restarts.load(atomic::Ordering::Acquire),
 8329        0,
 8330        "Should not restart LSP server on an unrelated change"
 8331    );
 8332
 8333    update_test_project_settings(cx, |project_settings| {
 8334        project_settings.lsp.insert(
 8335            "Some other server name".into(),
 8336            LspSettings {
 8337                binary: None,
 8338                settings: None,
 8339                initialization_options: Some(json!({
 8340                    "some other init value": false
 8341                })),
 8342            },
 8343        );
 8344    });
 8345    cx.executor().run_until_parked();
 8346    assert_eq!(
 8347        server_restarts.load(atomic::Ordering::Acquire),
 8348        0,
 8349        "Should not restart LSP server on an unrelated LSP settings change"
 8350    );
 8351
 8352    update_test_project_settings(cx, |project_settings| {
 8353        project_settings.lsp.insert(
 8354            language_server_name.into(),
 8355            LspSettings {
 8356                binary: None,
 8357                settings: None,
 8358                initialization_options: Some(json!({
 8359                    "anotherInitValue": false
 8360                })),
 8361            },
 8362        );
 8363    });
 8364    cx.executor().run_until_parked();
 8365    assert_eq!(
 8366        server_restarts.load(atomic::Ordering::Acquire),
 8367        1,
 8368        "Should restart LSP server on a related LSP settings change"
 8369    );
 8370
 8371    update_test_project_settings(cx, |project_settings| {
 8372        project_settings.lsp.insert(
 8373            language_server_name.into(),
 8374            LspSettings {
 8375                binary: None,
 8376                settings: None,
 8377                initialization_options: Some(json!({
 8378                    "anotherInitValue": false
 8379                })),
 8380            },
 8381        );
 8382    });
 8383    cx.executor().run_until_parked();
 8384    assert_eq!(
 8385        server_restarts.load(atomic::Ordering::Acquire),
 8386        1,
 8387        "Should not restart LSP server on a related LSP settings change that is the same"
 8388    );
 8389
 8390    update_test_project_settings(cx, |project_settings| {
 8391        project_settings.lsp.insert(
 8392            language_server_name.into(),
 8393            LspSettings {
 8394                binary: None,
 8395                settings: None,
 8396                initialization_options: None,
 8397            },
 8398        );
 8399    });
 8400    cx.executor().run_until_parked();
 8401    assert_eq!(
 8402        server_restarts.load(atomic::Ordering::Acquire),
 8403        2,
 8404        "Should restart LSP server on another related LSP settings change"
 8405    );
 8406}
 8407
 8408#[gpui::test]
 8409async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 8410    init_test(cx, |_| {});
 8411
 8412    let mut cx = EditorLspTestContext::new_rust(
 8413        lsp::ServerCapabilities {
 8414            completion_provider: Some(lsp::CompletionOptions {
 8415                trigger_characters: Some(vec![".".to_string()]),
 8416                resolve_provider: Some(true),
 8417                ..Default::default()
 8418            }),
 8419            ..Default::default()
 8420        },
 8421        cx,
 8422    )
 8423    .await;
 8424
 8425    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8426    cx.simulate_keystroke(".");
 8427    let completion_item = lsp::CompletionItem {
 8428        label: "some".into(),
 8429        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8430        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8431        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8432            kind: lsp::MarkupKind::Markdown,
 8433            value: "```rust\nSome(2)\n```".to_string(),
 8434        })),
 8435        deprecated: Some(false),
 8436        sort_text: Some("fffffff2".to_string()),
 8437        filter_text: Some("some".to_string()),
 8438        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8439        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8440            range: lsp::Range {
 8441                start: lsp::Position {
 8442                    line: 0,
 8443                    character: 22,
 8444                },
 8445                end: lsp::Position {
 8446                    line: 0,
 8447                    character: 22,
 8448                },
 8449            },
 8450            new_text: "Some(2)".to_string(),
 8451        })),
 8452        additional_text_edits: Some(vec![lsp::TextEdit {
 8453            range: lsp::Range {
 8454                start: lsp::Position {
 8455                    line: 0,
 8456                    character: 20,
 8457                },
 8458                end: lsp::Position {
 8459                    line: 0,
 8460                    character: 22,
 8461                },
 8462            },
 8463            new_text: "".to_string(),
 8464        }]),
 8465        ..Default::default()
 8466    };
 8467
 8468    let closure_completion_item = completion_item.clone();
 8469    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8470        let task_completion_item = closure_completion_item.clone();
 8471        async move {
 8472            Ok(Some(lsp::CompletionResponse::Array(vec![
 8473                task_completion_item,
 8474            ])))
 8475        }
 8476    });
 8477
 8478    request.next().await;
 8479
 8480    cx.condition(|editor, _| editor.context_menu_visible())
 8481        .await;
 8482    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8483        editor
 8484            .confirm_completion(&ConfirmCompletion::default(), cx)
 8485            .unwrap()
 8486    });
 8487    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 8488
 8489    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 8490        let task_completion_item = completion_item.clone();
 8491        async move { Ok(task_completion_item) }
 8492    })
 8493    .next()
 8494    .await
 8495    .unwrap();
 8496    apply_additional_edits.await.unwrap();
 8497    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 8498}
 8499
 8500#[gpui::test]
 8501async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 8502    init_test(cx, |_| {});
 8503
 8504    let mut cx = EditorLspTestContext::new(
 8505        Language::new(
 8506            LanguageConfig {
 8507                matcher: LanguageMatcher {
 8508                    path_suffixes: vec!["jsx".into()],
 8509                    ..Default::default()
 8510                },
 8511                overrides: [(
 8512                    "element".into(),
 8513                    LanguageConfigOverride {
 8514                        word_characters: Override::Set(['-'].into_iter().collect()),
 8515                        ..Default::default()
 8516                    },
 8517                )]
 8518                .into_iter()
 8519                .collect(),
 8520                ..Default::default()
 8521            },
 8522            Some(tree_sitter_typescript::language_tsx()),
 8523        )
 8524        .with_override_query("(jsx_self_closing_element) @element")
 8525        .unwrap(),
 8526        lsp::ServerCapabilities {
 8527            completion_provider: Some(lsp::CompletionOptions {
 8528                trigger_characters: Some(vec![":".to_string()]),
 8529                ..Default::default()
 8530            }),
 8531            ..Default::default()
 8532        },
 8533        cx,
 8534    )
 8535    .await;
 8536
 8537    cx.lsp
 8538        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8539            Ok(Some(lsp::CompletionResponse::Array(vec![
 8540                lsp::CompletionItem {
 8541                    label: "bg-blue".into(),
 8542                    ..Default::default()
 8543                },
 8544                lsp::CompletionItem {
 8545                    label: "bg-red".into(),
 8546                    ..Default::default()
 8547                },
 8548                lsp::CompletionItem {
 8549                    label: "bg-yellow".into(),
 8550                    ..Default::default()
 8551                },
 8552            ])))
 8553        });
 8554
 8555    cx.set_state(r#"<p class="bgˇ" />"#);
 8556
 8557    // Trigger completion when typing a dash, because the dash is an extra
 8558    // word character in the 'element' scope, which contains the cursor.
 8559    cx.simulate_keystroke("-");
 8560    cx.executor().run_until_parked();
 8561    cx.update_editor(|editor, _| {
 8562        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8563            assert_eq!(
 8564                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8565                &["bg-red", "bg-blue", "bg-yellow"]
 8566            );
 8567        } else {
 8568            panic!("expected completion menu to be open");
 8569        }
 8570    });
 8571
 8572    cx.simulate_keystroke("l");
 8573    cx.executor().run_until_parked();
 8574    cx.update_editor(|editor, _| {
 8575        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8576            assert_eq!(
 8577                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8578                &["bg-blue", "bg-yellow"]
 8579            );
 8580        } else {
 8581            panic!("expected completion menu to be open");
 8582        }
 8583    });
 8584
 8585    // When filtering completions, consider the character after the '-' to
 8586    // be the start of a subword.
 8587    cx.set_state(r#"<p class="yelˇ" />"#);
 8588    cx.simulate_keystroke("l");
 8589    cx.executor().run_until_parked();
 8590    cx.update_editor(|editor, _| {
 8591        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8592            assert_eq!(
 8593                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8594                &["bg-yellow"]
 8595            );
 8596        } else {
 8597            panic!("expected completion menu to be open");
 8598        }
 8599    });
 8600}
 8601
 8602#[gpui::test]
 8603async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 8604    init_test(cx, |settings| {
 8605        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
 8606    });
 8607
 8608    let fs = FakeFs::new(cx.executor());
 8609    fs.insert_file("/file.ts", Default::default()).await;
 8610
 8611    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
 8612    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8613
 8614    language_registry.add(Arc::new(Language::new(
 8615        LanguageConfig {
 8616            name: "TypeScript".into(),
 8617            matcher: LanguageMatcher {
 8618                path_suffixes: vec!["ts".to_string()],
 8619                ..Default::default()
 8620            },
 8621            ..Default::default()
 8622        },
 8623        Some(tree_sitter_rust::language()),
 8624    )));
 8625    update_test_language_settings(cx, |settings| {
 8626        settings.defaults.prettier = Some(PrettierSettings {
 8627            allowed: true,
 8628            ..PrettierSettings::default()
 8629        });
 8630    });
 8631
 8632    let test_plugin = "test_plugin";
 8633    let _ = language_registry.register_fake_lsp_adapter(
 8634        "TypeScript",
 8635        FakeLspAdapter {
 8636            prettier_plugins: vec![test_plugin],
 8637            ..Default::default()
 8638        },
 8639    );
 8640
 8641    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 8642    let buffer = project
 8643        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
 8644        .await
 8645        .unwrap();
 8646
 8647    let buffer_text = "one\ntwo\nthree\n";
 8648    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8649    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8650    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 8651
 8652    editor
 8653        .update(cx, |editor, cx| {
 8654            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 8655        })
 8656        .unwrap()
 8657        .await;
 8658    assert_eq!(
 8659        editor.update(cx, |editor, cx| editor.text(cx)),
 8660        buffer_text.to_string() + prettier_format_suffix,
 8661        "Test prettier formatting was not applied to the original buffer text",
 8662    );
 8663
 8664    update_test_language_settings(cx, |settings| {
 8665        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 8666    });
 8667    let format = editor.update(cx, |editor, cx| {
 8668        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 8669    });
 8670    format.await.unwrap();
 8671    assert_eq!(
 8672        editor.update(cx, |editor, cx| editor.text(cx)),
 8673        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 8674        "Autoformatting (via test prettier) was not applied to the original buffer text",
 8675    );
 8676}
 8677
 8678#[gpui::test]
 8679async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 8680    init_test(cx, |_| {});
 8681    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 8682    let base_text = indoc! {r#"struct Row;
 8683struct Row1;
 8684struct Row2;
 8685
 8686struct Row4;
 8687struct Row5;
 8688struct Row6;
 8689
 8690struct Row8;
 8691struct Row9;
 8692struct Row10;"#};
 8693
 8694    // When addition hunks are not adjacent to carets, no hunk revert is performed
 8695    assert_hunk_revert(
 8696        indoc! {r#"struct Row;
 8697                   struct Row1;
 8698                   struct Row1.1;
 8699                   struct Row1.2;
 8700                   struct Row2;ˇ
 8701
 8702                   struct Row4;
 8703                   struct Row5;
 8704                   struct Row6;
 8705
 8706                   struct Row8;
 8707                   ˇstruct Row9;
 8708                   struct Row9.1;
 8709                   struct Row9.2;
 8710                   struct Row9.3;
 8711                   struct Row10;"#},
 8712        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 8713        indoc! {r#"struct Row;
 8714                   struct Row1;
 8715                   struct Row1.1;
 8716                   struct Row1.2;
 8717                   struct Row2;ˇ
 8718
 8719                   struct Row4;
 8720                   struct Row5;
 8721                   struct Row6;
 8722
 8723                   struct Row8;
 8724                   ˇstruct Row9;
 8725                   struct Row9.1;
 8726                   struct Row9.2;
 8727                   struct Row9.3;
 8728                   struct Row10;"#},
 8729        base_text,
 8730        &mut cx,
 8731    );
 8732    // Same for selections
 8733    assert_hunk_revert(
 8734        indoc! {r#"struct Row;
 8735                   struct Row1;
 8736                   struct Row2;
 8737                   struct Row2.1;
 8738                   struct Row2.2;
 8739                   «ˇ
 8740                   struct Row4;
 8741                   struct» Row5;
 8742                   «struct Row6;
 8743                   ˇ»
 8744                   struct Row9.1;
 8745                   struct Row9.2;
 8746                   struct Row9.3;
 8747                   struct Row8;
 8748                   struct Row9;
 8749                   struct Row10;"#},
 8750        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 8751        indoc! {r#"struct Row;
 8752                   struct Row1;
 8753                   struct Row2;
 8754                   struct Row2.1;
 8755                   struct Row2.2;
 8756                   «ˇ
 8757                   struct Row4;
 8758                   struct» Row5;
 8759                   «struct Row6;
 8760                   ˇ»
 8761                   struct Row9.1;
 8762                   struct Row9.2;
 8763                   struct Row9.3;
 8764                   struct Row8;
 8765                   struct Row9;
 8766                   struct Row10;"#},
 8767        base_text,
 8768        &mut cx,
 8769    );
 8770
 8771    // When carets and selections intersect the addition hunks, those are reverted.
 8772    // Adjacent carets got merged.
 8773    assert_hunk_revert(
 8774        indoc! {r#"struct Row;
 8775                   ˇ// something on the top
 8776                   struct Row1;
 8777                   struct Row2;
 8778                   struct Roˇw3.1;
 8779                   struct Row2.2;
 8780                   struct Row2.3;ˇ
 8781
 8782                   struct Row4;
 8783                   struct ˇRow5.1;
 8784                   struct Row5.2;
 8785                   struct «Rowˇ»5.3;
 8786                   struct Row5;
 8787                   struct Row6;
 8788                   ˇ
 8789                   struct Row9.1;
 8790                   struct «Rowˇ»9.2;
 8791                   struct «ˇRow»9.3;
 8792                   struct Row8;
 8793                   struct Row9;
 8794                   «ˇ// something on bottom»
 8795                   struct Row10;"#},
 8796        vec![
 8797            DiffHunkStatus::Added,
 8798            DiffHunkStatus::Added,
 8799            DiffHunkStatus::Added,
 8800            DiffHunkStatus::Added,
 8801            DiffHunkStatus::Added,
 8802        ],
 8803        indoc! {r#"struct Row;
 8804                   ˇstruct Row1;
 8805                   struct Row2;
 8806                   ˇ
 8807                   struct Row4;
 8808                   ˇstruct Row5;
 8809                   struct Row6;
 8810                   ˇ
 8811                   ˇstruct Row8;
 8812                   struct Row9;
 8813                   ˇstruct Row10;"#},
 8814        base_text,
 8815        &mut cx,
 8816    );
 8817}
 8818
 8819#[gpui::test]
 8820async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
 8821    init_test(cx, |_| {});
 8822    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 8823    let base_text = indoc! {r#"struct Row;
 8824struct Row1;
 8825struct Row2;
 8826
 8827struct Row4;
 8828struct Row5;
 8829struct Row6;
 8830
 8831struct Row8;
 8832struct Row9;
 8833struct Row10;"#};
 8834
 8835    // Modification hunks behave the same as the addition ones.
 8836    assert_hunk_revert(
 8837        indoc! {r#"struct Row;
 8838                   struct Row1;
 8839                   struct Row33;
 8840                   ˇ
 8841                   struct Row4;
 8842                   struct Row5;
 8843                   struct Row6;
 8844                   ˇ
 8845                   struct Row99;
 8846                   struct Row9;
 8847                   struct Row10;"#},
 8848        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 8849        indoc! {r#"struct Row;
 8850                   struct Row1;
 8851                   struct Row33;
 8852                   ˇ
 8853                   struct Row4;
 8854                   struct Row5;
 8855                   struct Row6;
 8856                   ˇ
 8857                   struct Row99;
 8858                   struct Row9;
 8859                   struct Row10;"#},
 8860        base_text,
 8861        &mut cx,
 8862    );
 8863    assert_hunk_revert(
 8864        indoc! {r#"struct Row;
 8865                   struct Row1;
 8866                   struct Row33;
 8867                   «ˇ
 8868                   struct Row4;
 8869                   struct» Row5;
 8870                   «struct Row6;
 8871                   ˇ»
 8872                   struct Row99;
 8873                   struct Row9;
 8874                   struct Row10;"#},
 8875        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 8876        indoc! {r#"struct Row;
 8877                   struct Row1;
 8878                   struct Row33;
 8879                   «ˇ
 8880                   struct Row4;
 8881                   struct» Row5;
 8882                   «struct Row6;
 8883                   ˇ»
 8884                   struct Row99;
 8885                   struct Row9;
 8886                   struct Row10;"#},
 8887        base_text,
 8888        &mut cx,
 8889    );
 8890
 8891    assert_hunk_revert(
 8892        indoc! {r#"ˇstruct Row1.1;
 8893                   struct Row1;
 8894                   «ˇstr»uct Row22;
 8895
 8896                   struct ˇRow44;
 8897                   struct Row5;
 8898                   struct «Rˇ»ow66;ˇ
 8899
 8900                   «struˇ»ct Row88;
 8901                   struct Row9;
 8902                   struct Row1011;ˇ"#},
 8903        vec![
 8904            DiffHunkStatus::Modified,
 8905            DiffHunkStatus::Modified,
 8906            DiffHunkStatus::Modified,
 8907            DiffHunkStatus::Modified,
 8908            DiffHunkStatus::Modified,
 8909            DiffHunkStatus::Modified,
 8910        ],
 8911        indoc! {r#"struct Row;
 8912                   ˇstruct Row1;
 8913                   struct Row2;
 8914                   ˇ
 8915                   struct Row4;
 8916                   ˇstruct Row5;
 8917                   struct Row6;
 8918                   ˇ
 8919                   struct Row8;
 8920                   ˇstruct Row9;
 8921                   struct Row10;ˇ"#},
 8922        base_text,
 8923        &mut cx,
 8924    );
 8925}
 8926
 8927#[gpui::test]
 8928async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
 8929    init_test(cx, |_| {});
 8930    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 8931    let base_text = indoc! {r#"struct Row;
 8932struct Row1;
 8933struct Row2;
 8934
 8935struct Row4;
 8936struct Row5;
 8937struct Row6;
 8938
 8939struct Row8;
 8940struct Row9;
 8941struct Row10;"#};
 8942
 8943    // Deletion hunks trigger with carets on ajacent rows, so carets and selections have to stay farther to avoid the revert
 8944    assert_hunk_revert(
 8945        indoc! {r#"struct Row;
 8946                   struct Row2;
 8947
 8948                   ˇstruct Row4;
 8949                   struct Row5;
 8950                   struct Row6;
 8951                   ˇ
 8952                   struct Row8;
 8953                   struct Row10;"#},
 8954        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 8955        indoc! {r#"struct Row;
 8956                   struct Row2;
 8957
 8958                   ˇstruct Row4;
 8959                   struct Row5;
 8960                   struct Row6;
 8961                   ˇ
 8962                   struct Row8;
 8963                   struct Row10;"#},
 8964        base_text,
 8965        &mut cx,
 8966    );
 8967    assert_hunk_revert(
 8968        indoc! {r#"struct Row;
 8969                   struct Row2;
 8970
 8971                   «ˇstruct Row4;
 8972                   struct» Row5;
 8973                   «struct Row6;
 8974                   ˇ»
 8975                   struct Row8;
 8976                   struct Row10;"#},
 8977        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 8978        indoc! {r#"struct Row;
 8979                   struct Row2;
 8980
 8981                   «ˇstruct Row4;
 8982                   struct» Row5;
 8983                   «struct Row6;
 8984                   ˇ»
 8985                   struct Row8;
 8986                   struct Row10;"#},
 8987        base_text,
 8988        &mut cx,
 8989    );
 8990
 8991    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
 8992    assert_hunk_revert(
 8993        indoc! {r#"struct Row;
 8994                   ˇstruct Row2;
 8995
 8996                   struct Row4;
 8997                   struct Row5;
 8998                   struct Row6;
 8999
 9000                   struct Row8;ˇ
 9001                   struct Row10;"#},
 9002        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 9003        indoc! {r#"struct Row;
 9004                   struct Row1;
 9005                   ˇstruct Row2;
 9006
 9007                   struct Row4;
 9008                   struct Row5;
 9009                   struct Row6;
 9010
 9011                   struct Row8;ˇ
 9012                   struct Row9;
 9013                   struct Row10;"#},
 9014        base_text,
 9015        &mut cx,
 9016    );
 9017    assert_hunk_revert(
 9018        indoc! {r#"struct Row;
 9019                   struct Row2«ˇ;
 9020                   struct Row4;
 9021                   struct» Row5;
 9022                   «struct Row6;
 9023
 9024                   struct Row8;ˇ»
 9025                   struct Row10;"#},
 9026        vec![
 9027            DiffHunkStatus::Removed,
 9028            DiffHunkStatus::Removed,
 9029            DiffHunkStatus::Removed,
 9030        ],
 9031        indoc! {r#"struct Row;
 9032                   struct Row1;
 9033                   struct Row2«ˇ;
 9034
 9035                   struct Row4;
 9036                   struct» Row5;
 9037                   «struct Row6;
 9038
 9039                   struct Row8;ˇ»
 9040                   struct Row9;
 9041                   struct Row10;"#},
 9042        base_text,
 9043        &mut cx,
 9044    );
 9045}
 9046
 9047#[gpui::test]
 9048async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
 9049    init_test(cx, |_| {});
 9050
 9051    let cols = 4;
 9052    let rows = 10;
 9053    let sample_text_1 = sample_text(rows, cols, 'a');
 9054    assert_eq!(
 9055        sample_text_1,
 9056        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 9057    );
 9058    let sample_text_2 = sample_text(rows, cols, 'l');
 9059    assert_eq!(
 9060        sample_text_2,
 9061        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 9062    );
 9063    let sample_text_3 = sample_text(rows, cols, 'v');
 9064    assert_eq!(
 9065        sample_text_3,
 9066        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 9067    );
 9068
 9069    fn diff_every_buffer_row(
 9070        buffer: &Model<Buffer>,
 9071        sample_text: String,
 9072        cols: usize,
 9073        cx: &mut gpui::TestAppContext,
 9074    ) {
 9075        // revert first character in each row, creating one large diff hunk per buffer
 9076        let is_first_char = |offset: usize| offset % cols == 0;
 9077        buffer.update(cx, |buffer, cx| {
 9078            buffer.set_text(
 9079                sample_text
 9080                    .chars()
 9081                    .enumerate()
 9082                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
 9083                    .collect::<String>(),
 9084                cx,
 9085            );
 9086            buffer.set_diff_base(Some(sample_text), cx);
 9087        });
 9088        cx.executor().run_until_parked();
 9089    }
 9090
 9091    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
 9092    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
 9093
 9094    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
 9095    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
 9096
 9097    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
 9098    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
 9099
 9100    let multibuffer = cx.new_model(|cx| {
 9101        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 9102        multibuffer.push_excerpts(
 9103            buffer_1.clone(),
 9104            [
 9105                ExcerptRange {
 9106                    context: Point::new(0, 0)..Point::new(3, 0),
 9107                    primary: None,
 9108                },
 9109                ExcerptRange {
 9110                    context: Point::new(5, 0)..Point::new(7, 0),
 9111                    primary: None,
 9112                },
 9113                ExcerptRange {
 9114                    context: Point::new(9, 0)..Point::new(10, 4),
 9115                    primary: None,
 9116                },
 9117            ],
 9118            cx,
 9119        );
 9120        multibuffer.push_excerpts(
 9121            buffer_2.clone(),
 9122            [
 9123                ExcerptRange {
 9124                    context: Point::new(0, 0)..Point::new(3, 0),
 9125                    primary: None,
 9126                },
 9127                ExcerptRange {
 9128                    context: Point::new(5, 0)..Point::new(7, 0),
 9129                    primary: None,
 9130                },
 9131                ExcerptRange {
 9132                    context: Point::new(9, 0)..Point::new(10, 4),
 9133                    primary: None,
 9134                },
 9135            ],
 9136            cx,
 9137        );
 9138        multibuffer.push_excerpts(
 9139            buffer_3.clone(),
 9140            [
 9141                ExcerptRange {
 9142                    context: Point::new(0, 0)..Point::new(3, 0),
 9143                    primary: None,
 9144                },
 9145                ExcerptRange {
 9146                    context: Point::new(5, 0)..Point::new(7, 0),
 9147                    primary: None,
 9148                },
 9149                ExcerptRange {
 9150                    context: Point::new(9, 0)..Point::new(10, 4),
 9151                    primary: None,
 9152                },
 9153            ],
 9154            cx,
 9155        );
 9156        multibuffer
 9157    });
 9158
 9159    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9160    editor.update(cx, |editor, cx| {
 9161        assert_eq!(editor.text(cx), "XaaaXbbbX\nccXc\ndXdd\n\nhXhh\nXiiiXjjjX\n\nXlllXmmmX\nnnXn\noXoo\n\nsXss\nXtttXuuuX\n\nXvvvXwwwX\nxxXx\nyXyy\n\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n");
 9162        editor.select_all(&SelectAll, cx);
 9163        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
 9164    });
 9165    cx.executor().run_until_parked();
 9166    // When all ranges are selected, all buffer hunks are reverted.
 9167    editor.update(cx, |editor, cx| {
 9168        assert_eq!(editor.text(cx), "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nllll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu\n\n\nvvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}\n\n");
 9169    });
 9170    buffer_1.update(cx, |buffer, _| {
 9171        assert_eq!(buffer.text(), sample_text_1);
 9172    });
 9173    buffer_2.update(cx, |buffer, _| {
 9174        assert_eq!(buffer.text(), sample_text_2);
 9175    });
 9176    buffer_3.update(cx, |buffer, _| {
 9177        assert_eq!(buffer.text(), sample_text_3);
 9178    });
 9179
 9180    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
 9181    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
 9182    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
 9183    editor.update(cx, |editor, cx| {
 9184        editor.change_selections(None, cx, |s| {
 9185            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
 9186        });
 9187        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
 9188    });
 9189    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
 9190    // but not affect buffer_2 and its related excerpts.
 9191    editor.update(cx, |editor, cx| {
 9192        assert_eq!(
 9193            editor.text(cx),
 9194            "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nXlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX\n\n\nXvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n\n"
 9195        );
 9196    });
 9197    buffer_1.update(cx, |buffer, _| {
 9198        assert_eq!(buffer.text(), sample_text_1);
 9199    });
 9200    buffer_2.update(cx, |buffer, _| {
 9201        assert_eq!(
 9202            buffer.text(),
 9203            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
 9204        );
 9205    });
 9206    buffer_3.update(cx, |buffer, _| {
 9207        assert_eq!(
 9208            buffer.text(),
 9209            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
 9210        );
 9211    });
 9212}
 9213
 9214#[gpui::test]
 9215async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
 9216    init_test(cx, |_| {});
 9217
 9218    let cols = 4;
 9219    let rows = 10;
 9220    let sample_text_1 = sample_text(rows, cols, 'a');
 9221    assert_eq!(
 9222        sample_text_1,
 9223        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 9224    );
 9225    let sample_text_2 = sample_text(rows, cols, 'l');
 9226    assert_eq!(
 9227        sample_text_2,
 9228        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 9229    );
 9230    let sample_text_3 = sample_text(rows, cols, 'v');
 9231    assert_eq!(
 9232        sample_text_3,
 9233        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 9234    );
 9235
 9236    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
 9237    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
 9238    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
 9239
 9240    let multi_buffer = cx.new_model(|cx| {
 9241        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 9242        multibuffer.push_excerpts(
 9243            buffer_1.clone(),
 9244            [
 9245                ExcerptRange {
 9246                    context: Point::new(0, 0)..Point::new(3, 0),
 9247                    primary: None,
 9248                },
 9249                ExcerptRange {
 9250                    context: Point::new(5, 0)..Point::new(7, 0),
 9251                    primary: None,
 9252                },
 9253                ExcerptRange {
 9254                    context: Point::new(9, 0)..Point::new(10, 4),
 9255                    primary: None,
 9256                },
 9257            ],
 9258            cx,
 9259        );
 9260        multibuffer.push_excerpts(
 9261            buffer_2.clone(),
 9262            [
 9263                ExcerptRange {
 9264                    context: Point::new(0, 0)..Point::new(3, 0),
 9265                    primary: None,
 9266                },
 9267                ExcerptRange {
 9268                    context: Point::new(5, 0)..Point::new(7, 0),
 9269                    primary: None,
 9270                },
 9271                ExcerptRange {
 9272                    context: Point::new(9, 0)..Point::new(10, 4),
 9273                    primary: None,
 9274                },
 9275            ],
 9276            cx,
 9277        );
 9278        multibuffer.push_excerpts(
 9279            buffer_3.clone(),
 9280            [
 9281                ExcerptRange {
 9282                    context: Point::new(0, 0)..Point::new(3, 0),
 9283                    primary: None,
 9284                },
 9285                ExcerptRange {
 9286                    context: Point::new(5, 0)..Point::new(7, 0),
 9287                    primary: None,
 9288                },
 9289                ExcerptRange {
 9290                    context: Point::new(9, 0)..Point::new(10, 4),
 9291                    primary: None,
 9292                },
 9293            ],
 9294            cx,
 9295        );
 9296        multibuffer
 9297    });
 9298
 9299    let fs = FakeFs::new(cx.executor());
 9300    fs.insert_tree(
 9301        "/a",
 9302        json!({
 9303            "main.rs": sample_text_1,
 9304            "other.rs": sample_text_2,
 9305            "lib.rs": sample_text_3,
 9306        }),
 9307    )
 9308    .await;
 9309    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9310    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9311    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9312    let multi_buffer_editor =
 9313        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
 9314    let multibuffer_item_id = workspace
 9315        .update(cx, |workspace, cx| {
 9316            assert!(
 9317                workspace.active_item(cx).is_none(),
 9318                "active item should be None before the first item is added"
 9319            );
 9320            workspace.add_item_to_active_pane(Box::new(multi_buffer_editor.clone()), None, cx);
 9321            let active_item = workspace
 9322                .active_item(cx)
 9323                .expect("should have an active item after adding the multi buffer");
 9324            assert!(
 9325                !active_item.is_singleton(cx),
 9326                "A multi buffer was expected to active after adding"
 9327            );
 9328            active_item.item_id()
 9329        })
 9330        .unwrap();
 9331    cx.executor().run_until_parked();
 9332
 9333    multi_buffer_editor.update(cx, |editor, cx| {
 9334        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 9335        editor.open_excerpts(&OpenExcerpts, cx);
 9336    });
 9337    cx.executor().run_until_parked();
 9338    let first_item_id = workspace
 9339        .update(cx, |workspace, cx| {
 9340            let active_item = workspace
 9341                .active_item(cx)
 9342                .expect("should have an active item after navigating into the 1st buffer");
 9343            let first_item_id = active_item.item_id();
 9344            assert_ne!(
 9345                first_item_id, multibuffer_item_id,
 9346                "Should navigate into the 1st buffer and activate it"
 9347            );
 9348            assert!(
 9349                active_item.is_singleton(cx),
 9350                "New active item should be a singleton buffer"
 9351            );
 9352            assert_eq!(
 9353                active_item
 9354                    .act_as::<Editor>(cx)
 9355                    .expect("should have navigated into an editor for the 1st buffer")
 9356                    .read(cx)
 9357                    .text(cx),
 9358                sample_text_1
 9359            );
 9360
 9361            workspace
 9362                .go_back(workspace.active_pane().downgrade(), cx)
 9363                .detach_and_log_err(cx);
 9364
 9365            first_item_id
 9366        })
 9367        .unwrap();
 9368    cx.executor().run_until_parked();
 9369    workspace
 9370        .update(cx, |workspace, cx| {
 9371            let active_item = workspace
 9372                .active_item(cx)
 9373                .expect("should have an active item after navigating back");
 9374            assert_eq!(
 9375                active_item.item_id(),
 9376                multibuffer_item_id,
 9377                "Should navigate back to the multi buffer"
 9378            );
 9379            assert!(!active_item.is_singleton(cx));
 9380        })
 9381        .unwrap();
 9382
 9383    multi_buffer_editor.update(cx, |editor, cx| {
 9384        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 9385            s.select_ranges(Some(39..40))
 9386        });
 9387        editor.open_excerpts(&OpenExcerpts, cx);
 9388    });
 9389    cx.executor().run_until_parked();
 9390    let second_item_id = workspace
 9391        .update(cx, |workspace, cx| {
 9392            let active_item = workspace
 9393                .active_item(cx)
 9394                .expect("should have an active item after navigating into the 2nd buffer");
 9395            let second_item_id = active_item.item_id();
 9396            assert_ne!(
 9397                second_item_id, multibuffer_item_id,
 9398                "Should navigate away from the multibuffer"
 9399            );
 9400            assert_ne!(
 9401                second_item_id, first_item_id,
 9402                "Should navigate into the 2nd buffer and activate it"
 9403            );
 9404            assert!(
 9405                active_item.is_singleton(cx),
 9406                "New active item should be a singleton buffer"
 9407            );
 9408            assert_eq!(
 9409                active_item
 9410                    .act_as::<Editor>(cx)
 9411                    .expect("should have navigated into an editor")
 9412                    .read(cx)
 9413                    .text(cx),
 9414                sample_text_2
 9415            );
 9416
 9417            workspace
 9418                .go_back(workspace.active_pane().downgrade(), cx)
 9419                .detach_and_log_err(cx);
 9420
 9421            second_item_id
 9422        })
 9423        .unwrap();
 9424    cx.executor().run_until_parked();
 9425    workspace
 9426        .update(cx, |workspace, cx| {
 9427            let active_item = workspace
 9428                .active_item(cx)
 9429                .expect("should have an active item after navigating back from the 2nd buffer");
 9430            assert_eq!(
 9431                active_item.item_id(),
 9432                multibuffer_item_id,
 9433                "Should navigate back from the 2nd buffer to the multi buffer"
 9434            );
 9435            assert!(!active_item.is_singleton(cx));
 9436        })
 9437        .unwrap();
 9438
 9439    multi_buffer_editor.update(cx, |editor, cx| {
 9440        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 9441            s.select_ranges(Some(60..70))
 9442        });
 9443        editor.open_excerpts(&OpenExcerpts, cx);
 9444    });
 9445    cx.executor().run_until_parked();
 9446    workspace
 9447        .update(cx, |workspace, cx| {
 9448            let active_item = workspace
 9449                .active_item(cx)
 9450                .expect("should have an active item after navigating into the 3rd buffer");
 9451            let third_item_id = active_item.item_id();
 9452            assert_ne!(
 9453                third_item_id, multibuffer_item_id,
 9454                "Should navigate into the 3rd buffer and activate it"
 9455            );
 9456            assert_ne!(third_item_id, first_item_id);
 9457            assert_ne!(third_item_id, second_item_id);
 9458            assert!(
 9459                active_item.is_singleton(cx),
 9460                "New active item should be a singleton buffer"
 9461            );
 9462            assert_eq!(
 9463                active_item
 9464                    .act_as::<Editor>(cx)
 9465                    .expect("should have navigated into an editor")
 9466                    .read(cx)
 9467                    .text(cx),
 9468                sample_text_3
 9469            );
 9470
 9471            workspace
 9472                .go_back(workspace.active_pane().downgrade(), cx)
 9473                .detach_and_log_err(cx);
 9474        })
 9475        .unwrap();
 9476    cx.executor().run_until_parked();
 9477    workspace
 9478        .update(cx, |workspace, cx| {
 9479            let active_item = workspace
 9480                .active_item(cx)
 9481                .expect("should have an active item after navigating back from the 3rd buffer");
 9482            assert_eq!(
 9483                active_item.item_id(),
 9484                multibuffer_item_id,
 9485                "Should navigate back from the 3rd buffer to the multi buffer"
 9486            );
 9487            assert!(!active_item.is_singleton(cx));
 9488        })
 9489        .unwrap();
 9490}
 9491
 9492#[gpui::test]
 9493async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9494    init_test(cx, |_| {});
 9495
 9496    let mut cx = EditorTestContext::new(cx).await;
 9497
 9498    let diff_base = r#"
 9499        use some::mod;
 9500
 9501        const A: u32 = 42;
 9502
 9503        fn main() {
 9504            println!("hello");
 9505
 9506            println!("world");
 9507        }
 9508        "#
 9509    .unindent();
 9510
 9511    cx.set_state(
 9512        &r#"
 9513        use some::modified;
 9514
 9515        ˇ
 9516        fn main() {
 9517            println!("hello there");
 9518
 9519            println!("around the");
 9520            println!("world");
 9521        }
 9522        "#
 9523        .unindent(),
 9524    );
 9525
 9526    cx.set_diff_base(Some(&diff_base));
 9527    executor.run_until_parked();
 9528    let unexpanded_hunks = vec![
 9529        (
 9530            "use some::mod;\n".to_string(),
 9531            DiffHunkStatus::Modified,
 9532            DisplayRow(0)..DisplayRow(1),
 9533        ),
 9534        (
 9535            "const A: u32 = 42;\n".to_string(),
 9536            DiffHunkStatus::Removed,
 9537            DisplayRow(2)..DisplayRow(2),
 9538        ),
 9539        (
 9540            "    println!(\"hello\");\n".to_string(),
 9541            DiffHunkStatus::Modified,
 9542            DisplayRow(4)..DisplayRow(5),
 9543        ),
 9544        (
 9545            "".to_string(),
 9546            DiffHunkStatus::Added,
 9547            DisplayRow(6)..DisplayRow(7),
 9548        ),
 9549    ];
 9550    cx.update_editor(|editor, cx| {
 9551        let snapshot = editor.snapshot(cx);
 9552        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9553        assert_eq!(all_hunks, unexpanded_hunks);
 9554    });
 9555
 9556    cx.update_editor(|editor, cx| {
 9557        for _ in 0..4 {
 9558            editor.go_to_hunk(&GoToHunk, cx);
 9559            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
 9560        }
 9561    });
 9562    executor.run_until_parked();
 9563    cx.assert_editor_state(
 9564        &r#"
 9565        use some::modified;
 9566
 9567        ˇ
 9568        fn main() {
 9569            println!("hello there");
 9570
 9571            println!("around the");
 9572            println!("world");
 9573        }
 9574        "#
 9575        .unindent(),
 9576    );
 9577    cx.update_editor(|editor, cx| {
 9578        let snapshot = editor.snapshot(cx);
 9579        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9580        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
 9581        assert_eq!(
 9582            expanded_hunks_background_highlights(editor, cx),
 9583            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
 9584            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
 9585        );
 9586        assert_eq!(
 9587            all_hunks,
 9588            vec![
 9589                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
 9590                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
 9591                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
 9592                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
 9593            ],
 9594            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
 9595            (from modified and removed hunks)"
 9596        );
 9597        assert_eq!(
 9598            all_hunks, all_expanded_hunks,
 9599            "Editor hunks should not change and all be expanded"
 9600        );
 9601    });
 9602
 9603    cx.update_editor(|editor, cx| {
 9604        editor.cancel(&Cancel, cx);
 9605
 9606        let snapshot = editor.snapshot(cx);
 9607        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9608        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
 9609        assert_eq!(
 9610            expanded_hunks_background_highlights(editor, cx),
 9611            Vec::new(),
 9612            "After cancelling in editor, no git highlights should be left"
 9613        );
 9614        assert_eq!(
 9615            all_expanded_hunks,
 9616            Vec::new(),
 9617            "After cancelling in editor, no hunks should be expanded"
 9618        );
 9619        assert_eq!(
 9620            all_hunks, unexpanded_hunks,
 9621            "After cancelling in editor, regular hunks' coordinates should get back to normal"
 9622        );
 9623    });
 9624}
 9625
 9626#[gpui::test]
 9627async fn test_toggled_diff_base_change(
 9628    executor: BackgroundExecutor,
 9629    cx: &mut gpui::TestAppContext,
 9630) {
 9631    init_test(cx, |_| {});
 9632
 9633    let mut cx = EditorTestContext::new(cx).await;
 9634
 9635    let diff_base = r#"
 9636        use some::mod1;
 9637        use some::mod2;
 9638
 9639        const A: u32 = 42;
 9640        const B: u32 = 42;
 9641        const C: u32 = 42;
 9642
 9643        fn main(ˇ) {
 9644            println!("hello");
 9645
 9646            println!("world");
 9647        }
 9648        "#
 9649    .unindent();
 9650
 9651    cx.set_state(
 9652        &r#"
 9653        use some::mod2;
 9654
 9655        const A: u32 = 42;
 9656        const C: u32 = 42;
 9657
 9658        fn main(ˇ) {
 9659            //println!("hello");
 9660
 9661            println!("world");
 9662            //
 9663            //
 9664        }
 9665        "#
 9666        .unindent(),
 9667    );
 9668
 9669    cx.set_diff_base(Some(&diff_base));
 9670    executor.run_until_parked();
 9671    cx.update_editor(|editor, cx| {
 9672        let snapshot = editor.snapshot(cx);
 9673        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9674        assert_eq!(
 9675            all_hunks,
 9676            vec![
 9677                (
 9678                    "use some::mod1;\n".to_string(),
 9679                    DiffHunkStatus::Removed,
 9680                    DisplayRow(0)..DisplayRow(0)
 9681                ),
 9682                (
 9683                    "const B: u32 = 42;\n".to_string(),
 9684                    DiffHunkStatus::Removed,
 9685                    DisplayRow(3)..DisplayRow(3)
 9686                ),
 9687                (
 9688                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
 9689                    DiffHunkStatus::Modified,
 9690                    DisplayRow(5)..DisplayRow(7)
 9691                ),
 9692                (
 9693                    "".to_string(),
 9694                    DiffHunkStatus::Added,
 9695                    DisplayRow(9)..DisplayRow(11)
 9696                ),
 9697            ]
 9698        );
 9699    });
 9700
 9701    cx.update_editor(|editor, cx| {
 9702        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
 9703    });
 9704    executor.run_until_parked();
 9705    cx.assert_editor_state(
 9706        &r#"
 9707        use some::mod2;
 9708
 9709        const A: u32 = 42;
 9710        const C: u32 = 42;
 9711
 9712        fn main(ˇ) {
 9713            //println!("hello");
 9714
 9715            println!("world");
 9716            //
 9717            //
 9718        }
 9719        "#
 9720        .unindent(),
 9721    );
 9722    cx.update_editor(|editor, cx| {
 9723        let snapshot = editor.snapshot(cx);
 9724        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9725        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
 9726        assert_eq!(
 9727            expanded_hunks_background_highlights(editor, cx),
 9728            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
 9729            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
 9730        );
 9731        assert_eq!(
 9732            all_hunks,
 9733            vec![
 9734                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
 9735                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
 9736                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
 9737                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
 9738            ],
 9739            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
 9740            (from modified and removed hunks)"
 9741        );
 9742        assert_eq!(
 9743            all_hunks, all_expanded_hunks,
 9744            "Editor hunks should not change and all be expanded"
 9745        );
 9746    });
 9747
 9748    cx.set_diff_base(Some("new diff base!"));
 9749    executor.run_until_parked();
 9750
 9751    cx.update_editor(|editor, cx| {
 9752        let snapshot = editor.snapshot(cx);
 9753        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9754        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
 9755        assert_eq!(
 9756            expanded_hunks_background_highlights(editor, cx),
 9757            Vec::new(),
 9758            "After diff base is changed, old git highlights should be removed"
 9759        );
 9760        assert_eq!(
 9761            all_expanded_hunks,
 9762            Vec::new(),
 9763            "After diff base is changed, old git hunk expansions should be removed"
 9764        );
 9765        assert_eq!(
 9766            all_hunks,
 9767            vec![(
 9768                "new diff base!".to_string(),
 9769                DiffHunkStatus::Modified,
 9770                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
 9771            )],
 9772            "After diff base is changed, hunks should update"
 9773        );
 9774    });
 9775}
 9776
 9777#[gpui::test]
 9778async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9779    init_test(cx, |_| {});
 9780
 9781    let mut cx = EditorTestContext::new(cx).await;
 9782
 9783    let diff_base = r#"
 9784        use some::mod1;
 9785        use some::mod2;
 9786
 9787        const A: u32 = 42;
 9788        const B: u32 = 42;
 9789        const C: u32 = 42;
 9790
 9791        fn main(ˇ) {
 9792            println!("hello");
 9793
 9794            println!("world");
 9795        }
 9796
 9797        fn another() {
 9798            println!("another");
 9799        }
 9800
 9801        fn another2() {
 9802            println!("another2");
 9803        }
 9804        "#
 9805    .unindent();
 9806
 9807    cx.set_state(
 9808        &r#"
 9809        «use some::mod2;
 9810
 9811        const A: u32 = 42;
 9812        const C: u32 = 42;
 9813
 9814        fn main() {
 9815            //println!("hello");
 9816
 9817            println!("world");
 9818            //
 9819            //ˇ»
 9820        }
 9821
 9822        fn another() {
 9823            println!("another");
 9824            println!("another");
 9825        }
 9826
 9827            println!("another2");
 9828        }
 9829        "#
 9830        .unindent(),
 9831    );
 9832
 9833    cx.set_diff_base(Some(&diff_base));
 9834    executor.run_until_parked();
 9835    cx.update_editor(|editor, cx| {
 9836        let snapshot = editor.snapshot(cx);
 9837        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9838        assert_eq!(
 9839            all_hunks,
 9840            vec![
 9841                (
 9842                    "use some::mod1;\n".to_string(),
 9843                    DiffHunkStatus::Removed,
 9844                    DisplayRow(0)..DisplayRow(0)
 9845                ),
 9846                (
 9847                    "const B: u32 = 42;\n".to_string(),
 9848                    DiffHunkStatus::Removed,
 9849                    DisplayRow(3)..DisplayRow(3)
 9850                ),
 9851                (
 9852                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
 9853                    DiffHunkStatus::Modified,
 9854                    DisplayRow(5)..DisplayRow(7)
 9855                ),
 9856                (
 9857                    "".to_string(),
 9858                    DiffHunkStatus::Added,
 9859                    DisplayRow(9)..DisplayRow(11)
 9860                ),
 9861                (
 9862                    "".to_string(),
 9863                    DiffHunkStatus::Added,
 9864                    DisplayRow(15)..DisplayRow(16)
 9865                ),
 9866                (
 9867                    "fn another2() {\n".to_string(),
 9868                    DiffHunkStatus::Removed,
 9869                    DisplayRow(18)..DisplayRow(18)
 9870                ),
 9871            ]
 9872        );
 9873    });
 9874
 9875    cx.update_editor(|editor, cx| {
 9876        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
 9877    });
 9878    executor.run_until_parked();
 9879    cx.assert_editor_state(
 9880        &r#"
 9881        «use some::mod2;
 9882
 9883        const A: u32 = 42;
 9884        const C: u32 = 42;
 9885
 9886        fn main() {
 9887            //println!("hello");
 9888
 9889            println!("world");
 9890            //
 9891            //ˇ»
 9892        }
 9893
 9894        fn another() {
 9895            println!("another");
 9896            println!("another");
 9897        }
 9898
 9899            println!("another2");
 9900        }
 9901        "#
 9902        .unindent(),
 9903    );
 9904    cx.update_editor(|editor, cx| {
 9905        let snapshot = editor.snapshot(cx);
 9906        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9907        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
 9908        assert_eq!(
 9909            expanded_hunks_background_highlights(editor, cx),
 9910            vec![
 9911                DisplayRow(9)..=DisplayRow(10),
 9912                DisplayRow(13)..=DisplayRow(14),
 9913                DisplayRow(19)..=DisplayRow(19)
 9914            ]
 9915        );
 9916        assert_eq!(
 9917            all_hunks,
 9918            vec![
 9919                (
 9920                    "use some::mod1;\n".to_string(),
 9921                    DiffHunkStatus::Removed,
 9922                    DisplayRow(1)..DisplayRow(1)
 9923                ),
 9924                (
 9925                    "const B: u32 = 42;\n".to_string(),
 9926                    DiffHunkStatus::Removed,
 9927                    DisplayRow(5)..DisplayRow(5)
 9928                ),
 9929                (
 9930                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
 9931                    DiffHunkStatus::Modified,
 9932                    DisplayRow(9)..DisplayRow(11)
 9933                ),
 9934                (
 9935                    "".to_string(),
 9936                    DiffHunkStatus::Added,
 9937                    DisplayRow(13)..DisplayRow(15)
 9938                ),
 9939                (
 9940                    "".to_string(),
 9941                    DiffHunkStatus::Added,
 9942                    DisplayRow(19)..DisplayRow(20)
 9943                ),
 9944                (
 9945                    "fn another2() {\n".to_string(),
 9946                    DiffHunkStatus::Removed,
 9947                    DisplayRow(23)..DisplayRow(23)
 9948                ),
 9949            ],
 9950        );
 9951        assert_eq!(all_hunks, all_expanded_hunks);
 9952    });
 9953
 9954    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
 9955    cx.executor().run_until_parked();
 9956    cx.assert_editor_state(
 9957        &r#"
 9958        «use some::mod2;
 9959
 9960        const A: u32 = 42;
 9961        const C: u32 = 42;
 9962
 9963        fn main() {
 9964            //println!("hello");
 9965
 9966            println!("world");
 9967            //
 9968            //ˇ»
 9969        }
 9970
 9971        fn another() {
 9972            println!("another");
 9973            println!("another");
 9974        }
 9975
 9976            println!("another2");
 9977        }
 9978        "#
 9979        .unindent(),
 9980    );
 9981    cx.update_editor(|editor, cx| {
 9982        let snapshot = editor.snapshot(cx);
 9983        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9984        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
 9985        assert_eq!(
 9986            expanded_hunks_background_highlights(editor, cx),
 9987            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
 9988            "Only one hunk is left not folded, its highlight should be visible"
 9989        );
 9990        assert_eq!(
 9991            all_hunks,
 9992            vec![
 9993                (
 9994                    "use some::mod1;\n".to_string(),
 9995                    DiffHunkStatus::Removed,
 9996                    DisplayRow(0)..DisplayRow(0)
 9997                ),
 9998                (
 9999                    "const B: u32 = 42;\n".to_string(),
10000                    DiffHunkStatus::Removed,
10001                    DisplayRow(0)..DisplayRow(0)
10002                ),
10003                (
10004                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10005                    DiffHunkStatus::Modified,
10006                    DisplayRow(0)..DisplayRow(0)
10007                ),
10008                (
10009                    "".to_string(),
10010                    DiffHunkStatus::Added,
10011                    DisplayRow(0)..DisplayRow(1)
10012                ),
10013                (
10014                    "".to_string(),
10015                    DiffHunkStatus::Added,
10016                    DisplayRow(5)..DisplayRow(6)
10017                ),
10018                (
10019                    "fn another2() {\n".to_string(),
10020                    DiffHunkStatus::Removed,
10021                    DisplayRow(9)..DisplayRow(9)
10022                ),
10023            ],
10024            "Hunk list should still return shifted folded hunks"
10025        );
10026        assert_eq!(
10027            all_expanded_hunks,
10028            vec![
10029                (
10030                    "".to_string(),
10031                    DiffHunkStatus::Added,
10032                    DisplayRow(5)..DisplayRow(6)
10033                ),
10034                (
10035                    "fn another2() {\n".to_string(),
10036                    DiffHunkStatus::Removed,
10037                    DisplayRow(9)..DisplayRow(9)
10038                ),
10039            ],
10040            "Only non-folded hunks should be left expanded"
10041        );
10042    });
10043
10044    cx.update_editor(|editor, cx| {
10045        editor.select_all(&SelectAll, cx);
10046        editor.unfold_lines(&UnfoldLines, cx);
10047    });
10048    cx.executor().run_until_parked();
10049    cx.assert_editor_state(
10050        &r#"
10051        «use some::mod2;
10052
10053        const A: u32 = 42;
10054        const C: u32 = 42;
10055
10056        fn main() {
10057            //println!("hello");
10058
10059            println!("world");
10060            //
10061            //
10062        }
10063
10064        fn another() {
10065            println!("another");
10066            println!("another");
10067        }
10068
10069            println!("another2");
10070        }
10071        ˇ»"#
10072        .unindent(),
10073    );
10074    cx.update_editor(|editor, cx| {
10075        let snapshot = editor.snapshot(cx);
10076        let all_hunks = editor_hunks(editor, &snapshot, cx);
10077        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10078        assert_eq!(
10079            expanded_hunks_background_highlights(editor, cx),
10080            vec![
10081                DisplayRow(9)..=DisplayRow(10),
10082                DisplayRow(13)..=DisplayRow(14),
10083                DisplayRow(19)..=DisplayRow(19)
10084            ],
10085            "After unfolding, all hunk diffs should be visible again"
10086        );
10087        assert_eq!(
10088            all_hunks,
10089            vec![
10090                (
10091                    "use some::mod1;\n".to_string(),
10092                    DiffHunkStatus::Removed,
10093                    DisplayRow(1)..DisplayRow(1)
10094                ),
10095                (
10096                    "const B: u32 = 42;\n".to_string(),
10097                    DiffHunkStatus::Removed,
10098                    DisplayRow(5)..DisplayRow(5)
10099                ),
10100                (
10101                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10102                    DiffHunkStatus::Modified,
10103                    DisplayRow(9)..DisplayRow(11)
10104                ),
10105                (
10106                    "".to_string(),
10107                    DiffHunkStatus::Added,
10108                    DisplayRow(13)..DisplayRow(15)
10109                ),
10110                (
10111                    "".to_string(),
10112                    DiffHunkStatus::Added,
10113                    DisplayRow(19)..DisplayRow(20)
10114                ),
10115                (
10116                    "fn another2() {\n".to_string(),
10117                    DiffHunkStatus::Removed,
10118                    DisplayRow(23)..DisplayRow(23)
10119                ),
10120            ],
10121        );
10122        assert_eq!(all_hunks, all_expanded_hunks);
10123    });
10124}
10125
10126#[gpui::test]
10127async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
10128    init_test(cx, |_| {});
10129
10130    let cols = 4;
10131    let rows = 10;
10132    let sample_text_1 = sample_text(rows, cols, 'a');
10133    assert_eq!(
10134        sample_text_1,
10135        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10136    );
10137    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
10138    let sample_text_2 = sample_text(rows, cols, 'l');
10139    assert_eq!(
10140        sample_text_2,
10141        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10142    );
10143    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
10144    let sample_text_3 = sample_text(rows, cols, 'v');
10145    assert_eq!(
10146        sample_text_3,
10147        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10148    );
10149    let modified_sample_text_3 =
10150        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
10151    let buffer_1 = cx.new_model(|cx| {
10152        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
10153        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
10154        buffer
10155    });
10156    let buffer_2 = cx.new_model(|cx| {
10157        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
10158        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
10159        buffer
10160    });
10161    let buffer_3 = cx.new_model(|cx| {
10162        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
10163        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
10164        buffer
10165    });
10166
10167    let multi_buffer = cx.new_model(|cx| {
10168        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10169        multibuffer.push_excerpts(
10170            buffer_1.clone(),
10171            [
10172                ExcerptRange {
10173                    context: Point::new(0, 0)..Point::new(3, 0),
10174                    primary: None,
10175                },
10176                ExcerptRange {
10177                    context: Point::new(5, 0)..Point::new(7, 0),
10178                    primary: None,
10179                },
10180                ExcerptRange {
10181                    context: Point::new(9, 0)..Point::new(10, 4),
10182                    primary: None,
10183                },
10184            ],
10185            cx,
10186        );
10187        multibuffer.push_excerpts(
10188            buffer_2.clone(),
10189            [
10190                ExcerptRange {
10191                    context: Point::new(0, 0)..Point::new(3, 0),
10192                    primary: None,
10193                },
10194                ExcerptRange {
10195                    context: Point::new(5, 0)..Point::new(7, 0),
10196                    primary: None,
10197                },
10198                ExcerptRange {
10199                    context: Point::new(9, 0)..Point::new(10, 4),
10200                    primary: None,
10201                },
10202            ],
10203            cx,
10204        );
10205        multibuffer.push_excerpts(
10206            buffer_3.clone(),
10207            [
10208                ExcerptRange {
10209                    context: Point::new(0, 0)..Point::new(3, 0),
10210                    primary: None,
10211                },
10212                ExcerptRange {
10213                    context: Point::new(5, 0)..Point::new(7, 0),
10214                    primary: None,
10215                },
10216                ExcerptRange {
10217                    context: Point::new(9, 0)..Point::new(10, 4),
10218                    primary: None,
10219                },
10220            ],
10221            cx,
10222        );
10223        multibuffer
10224    });
10225
10226    let fs = FakeFs::new(cx.executor());
10227    fs.insert_tree(
10228        "/a",
10229        json!({
10230            "main.rs": modified_sample_text_1,
10231            "other.rs": modified_sample_text_2,
10232            "lib.rs": modified_sample_text_3,
10233        }),
10234    )
10235    .await;
10236
10237    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10238    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10239    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10240    let multi_buffer_editor =
10241        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
10242    cx.executor().run_until_parked();
10243
10244    let expected_all_hunks = vec![
10245        (
10246            "bbbb\n".to_string(),
10247            DiffHunkStatus::Removed,
10248            DisplayRow(3)..DisplayRow(3),
10249        ),
10250        (
10251            "nnnn\n".to_string(),
10252            DiffHunkStatus::Modified,
10253            DisplayRow(16)..DisplayRow(17),
10254        ),
10255        (
10256            "".to_string(),
10257            DiffHunkStatus::Added,
10258            DisplayRow(31)..DisplayRow(32),
10259        ),
10260    ];
10261    let expected_all_hunks_shifted = vec![
10262        (
10263            "bbbb\n".to_string(),
10264            DiffHunkStatus::Removed,
10265            DisplayRow(4)..DisplayRow(4),
10266        ),
10267        (
10268            "nnnn\n".to_string(),
10269            DiffHunkStatus::Modified,
10270            DisplayRow(18)..DisplayRow(19),
10271        ),
10272        (
10273            "".to_string(),
10274            DiffHunkStatus::Added,
10275            DisplayRow(33)..DisplayRow(34),
10276        ),
10277    ];
10278
10279    multi_buffer_editor.update(cx, |editor, cx| {
10280        let snapshot = editor.snapshot(cx);
10281        let all_hunks = editor_hunks(editor, &snapshot, cx);
10282        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10283        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10284        assert_eq!(all_hunks, expected_all_hunks);
10285        assert_eq!(all_expanded_hunks, Vec::new());
10286    });
10287
10288    multi_buffer_editor.update(cx, |editor, cx| {
10289        editor.select_all(&SelectAll, cx);
10290        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10291    });
10292    cx.executor().run_until_parked();
10293    multi_buffer_editor.update(cx, |editor, cx| {
10294        let snapshot = editor.snapshot(cx);
10295        let all_hunks = editor_hunks(editor, &snapshot, cx);
10296        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10297        assert_eq!(
10298            expanded_hunks_background_highlights(editor, cx),
10299            vec![
10300                DisplayRow(18)..=DisplayRow(18),
10301                DisplayRow(33)..=DisplayRow(33)
10302            ],
10303        );
10304        assert_eq!(all_hunks, expected_all_hunks_shifted);
10305        assert_eq!(all_hunks, all_expanded_hunks);
10306    });
10307
10308    multi_buffer_editor.update(cx, |editor, cx| {
10309        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10310    });
10311    cx.executor().run_until_parked();
10312    multi_buffer_editor.update(cx, |editor, cx| {
10313        let snapshot = editor.snapshot(cx);
10314        let all_hunks = editor_hunks(editor, &snapshot, cx);
10315        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10316        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10317        assert_eq!(all_hunks, expected_all_hunks);
10318        assert_eq!(all_expanded_hunks, Vec::new());
10319    });
10320
10321    multi_buffer_editor.update(cx, |editor, cx| {
10322        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10323    });
10324    cx.executor().run_until_parked();
10325    multi_buffer_editor.update(cx, |editor, cx| {
10326        let snapshot = editor.snapshot(cx);
10327        let all_hunks = editor_hunks(editor, &snapshot, cx);
10328        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10329        assert_eq!(
10330            expanded_hunks_background_highlights(editor, cx),
10331            vec![
10332                DisplayRow(18)..=DisplayRow(18),
10333                DisplayRow(33)..=DisplayRow(33)
10334            ],
10335        );
10336        assert_eq!(all_hunks, expected_all_hunks_shifted);
10337        assert_eq!(all_hunks, all_expanded_hunks);
10338    });
10339
10340    multi_buffer_editor.update(cx, |editor, cx| {
10341        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10342    });
10343    cx.executor().run_until_parked();
10344    multi_buffer_editor.update(cx, |editor, cx| {
10345        let snapshot = editor.snapshot(cx);
10346        let all_hunks = editor_hunks(editor, &snapshot, cx);
10347        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10348        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10349        assert_eq!(all_hunks, expected_all_hunks);
10350        assert_eq!(all_expanded_hunks, Vec::new());
10351    });
10352}
10353
10354#[gpui::test]
10355async fn test_edits_around_toggled_additions(
10356    executor: BackgroundExecutor,
10357    cx: &mut gpui::TestAppContext,
10358) {
10359    init_test(cx, |_| {});
10360
10361    let mut cx = EditorTestContext::new(cx).await;
10362
10363    let diff_base = r#"
10364        use some::mod1;
10365        use some::mod2;
10366
10367        const A: u32 = 42;
10368
10369        fn main() {
10370            println!("hello");
10371
10372            println!("world");
10373        }
10374        "#
10375    .unindent();
10376    executor.run_until_parked();
10377    cx.set_state(
10378        &r#"
10379        use some::mod1;
10380        use some::mod2;
10381
10382        const A: u32 = 42;
10383        const B: u32 = 42;
10384        const C: u32 = 42;
10385        ˇ
10386
10387        fn main() {
10388            println!("hello");
10389
10390            println!("world");
10391        }
10392        "#
10393        .unindent(),
10394    );
10395
10396    cx.set_diff_base(Some(&diff_base));
10397    executor.run_until_parked();
10398    cx.update_editor(|editor, cx| {
10399        let snapshot = editor.snapshot(cx);
10400        let all_hunks = editor_hunks(editor, &snapshot, cx);
10401        assert_eq!(
10402            all_hunks,
10403            vec![(
10404                "".to_string(),
10405                DiffHunkStatus::Added,
10406                DisplayRow(4)..DisplayRow(7)
10407            )]
10408        );
10409    });
10410    cx.update_editor(|editor, cx| {
10411        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10412    });
10413    executor.run_until_parked();
10414    cx.assert_editor_state(
10415        &r#"
10416        use some::mod1;
10417        use some::mod2;
10418
10419        const A: u32 = 42;
10420        const B: u32 = 42;
10421        const C: u32 = 42;
10422        ˇ
10423
10424        fn main() {
10425            println!("hello");
10426
10427            println!("world");
10428        }
10429        "#
10430        .unindent(),
10431    );
10432    cx.update_editor(|editor, cx| {
10433        let snapshot = editor.snapshot(cx);
10434        let all_hunks = editor_hunks(editor, &snapshot, cx);
10435        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10436        assert_eq!(
10437            all_hunks,
10438            vec![(
10439                "".to_string(),
10440                DiffHunkStatus::Added,
10441                DisplayRow(4)..DisplayRow(7)
10442            )]
10443        );
10444        assert_eq!(
10445            expanded_hunks_background_highlights(editor, cx),
10446            vec![DisplayRow(4)..=DisplayRow(6)]
10447        );
10448        assert_eq!(all_hunks, all_expanded_hunks);
10449    });
10450
10451    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
10452    executor.run_until_parked();
10453    cx.assert_editor_state(
10454        &r#"
10455        use some::mod1;
10456        use some::mod2;
10457
10458        const A: u32 = 42;
10459        const B: u32 = 42;
10460        const C: u32 = 42;
10461        const D: u32 = 42;
10462        ˇ
10463
10464        fn main() {
10465            println!("hello");
10466
10467            println!("world");
10468        }
10469        "#
10470        .unindent(),
10471    );
10472    cx.update_editor(|editor, cx| {
10473        let snapshot = editor.snapshot(cx);
10474        let all_hunks = editor_hunks(editor, &snapshot, cx);
10475        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10476        assert_eq!(
10477            all_hunks,
10478            vec![(
10479                "".to_string(),
10480                DiffHunkStatus::Added,
10481                DisplayRow(4)..DisplayRow(8)
10482            )]
10483        );
10484        assert_eq!(
10485            expanded_hunks_background_highlights(editor, cx),
10486            vec![DisplayRow(4)..=DisplayRow(6)],
10487            "Edited hunk should have one more line added"
10488        );
10489        assert_eq!(
10490            all_hunks, all_expanded_hunks,
10491            "Expanded hunk should also grow with the addition"
10492        );
10493    });
10494
10495    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
10496    executor.run_until_parked();
10497    cx.assert_editor_state(
10498        &r#"
10499        use some::mod1;
10500        use some::mod2;
10501
10502        const A: u32 = 42;
10503        const B: u32 = 42;
10504        const C: u32 = 42;
10505        const D: u32 = 42;
10506        const E: u32 = 42;
10507        ˇ
10508
10509        fn main() {
10510            println!("hello");
10511
10512            println!("world");
10513        }
10514        "#
10515        .unindent(),
10516    );
10517    cx.update_editor(|editor, cx| {
10518        let snapshot = editor.snapshot(cx);
10519        let all_hunks = editor_hunks(editor, &snapshot, cx);
10520        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10521        assert_eq!(
10522            all_hunks,
10523            vec![(
10524                "".to_string(),
10525                DiffHunkStatus::Added,
10526                DisplayRow(4)..DisplayRow(9)
10527            )]
10528        );
10529        assert_eq!(
10530            expanded_hunks_background_highlights(editor, cx),
10531            vec![DisplayRow(4)..=DisplayRow(6)],
10532            "Edited hunk should have one more line added"
10533        );
10534        assert_eq!(all_hunks, all_expanded_hunks);
10535    });
10536
10537    cx.update_editor(|editor, cx| {
10538        editor.move_up(&MoveUp, cx);
10539        editor.delete_line(&DeleteLine, cx);
10540    });
10541    executor.run_until_parked();
10542    cx.assert_editor_state(
10543        &r#"
10544        use some::mod1;
10545        use some::mod2;
10546
10547        const A: u32 = 42;
10548        const B: u32 = 42;
10549        const C: u32 = 42;
10550        const D: u32 = 42;
10551        ˇ
10552
10553        fn main() {
10554            println!("hello");
10555
10556            println!("world");
10557        }
10558        "#
10559        .unindent(),
10560    );
10561    cx.update_editor(|editor, cx| {
10562        let snapshot = editor.snapshot(cx);
10563        let all_hunks = editor_hunks(editor, &snapshot, cx);
10564        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10565        assert_eq!(
10566            all_hunks,
10567            vec![(
10568                "".to_string(),
10569                DiffHunkStatus::Added,
10570                DisplayRow(4)..DisplayRow(8)
10571            )]
10572        );
10573        assert_eq!(
10574            expanded_hunks_background_highlights(editor, cx),
10575            vec![DisplayRow(4)..=DisplayRow(6)],
10576            "Deleting a line should shrint the hunk"
10577        );
10578        assert_eq!(
10579            all_hunks, all_expanded_hunks,
10580            "Expanded hunk should also shrink with the addition"
10581        );
10582    });
10583
10584    cx.update_editor(|editor, cx| {
10585        editor.move_up(&MoveUp, cx);
10586        editor.delete_line(&DeleteLine, cx);
10587        editor.move_up(&MoveUp, cx);
10588        editor.delete_line(&DeleteLine, cx);
10589        editor.move_up(&MoveUp, cx);
10590        editor.delete_line(&DeleteLine, cx);
10591    });
10592    executor.run_until_parked();
10593    cx.assert_editor_state(
10594        &r#"
10595        use some::mod1;
10596        use some::mod2;
10597
10598        const A: u32 = 42;
10599        ˇ
10600
10601        fn main() {
10602            println!("hello");
10603
10604            println!("world");
10605        }
10606        "#
10607        .unindent(),
10608    );
10609    cx.update_editor(|editor, cx| {
10610        let snapshot = editor.snapshot(cx);
10611        let all_hunks = editor_hunks(editor, &snapshot, cx);
10612        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10613        assert_eq!(
10614            all_hunks,
10615            vec![(
10616                "".to_string(),
10617                DiffHunkStatus::Added,
10618                DisplayRow(5)..DisplayRow(6)
10619            )]
10620        );
10621        assert_eq!(
10622            expanded_hunks_background_highlights(editor, cx),
10623            vec![DisplayRow(5)..=DisplayRow(5)]
10624        );
10625        assert_eq!(all_hunks, all_expanded_hunks);
10626    });
10627
10628    cx.update_editor(|editor, cx| {
10629        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
10630        editor.delete_line(&DeleteLine, cx);
10631    });
10632    executor.run_until_parked();
10633    cx.assert_editor_state(
10634        &r#"
10635        ˇ
10636
10637        fn main() {
10638            println!("hello");
10639
10640            println!("world");
10641        }
10642        "#
10643        .unindent(),
10644    );
10645    cx.update_editor(|editor, cx| {
10646        let snapshot = editor.snapshot(cx);
10647        let all_hunks = editor_hunks(editor, &snapshot, cx);
10648        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10649        assert_eq!(
10650            all_hunks,
10651            vec![
10652                (
10653                    "use some::mod1;\nuse some::mod2;\n".to_string(),
10654                    DiffHunkStatus::Removed,
10655                    DisplayRow(0)..DisplayRow(0)
10656                ),
10657                (
10658                    "const A: u32 = 42;\n".to_string(),
10659                    DiffHunkStatus::Removed,
10660                    DisplayRow(2)..DisplayRow(2)
10661                )
10662            ]
10663        );
10664        assert_eq!(
10665            expanded_hunks_background_highlights(editor, cx),
10666            Vec::new(),
10667            "Should close all stale expanded addition hunks"
10668        );
10669        assert_eq!(
10670            all_expanded_hunks,
10671            vec![(
10672                "const A: u32 = 42;\n".to_string(),
10673                DiffHunkStatus::Removed,
10674                DisplayRow(2)..DisplayRow(2)
10675            )],
10676            "Should open hunks that were adjacent to the stale addition one"
10677        );
10678    });
10679}
10680
10681#[gpui::test]
10682async fn test_edits_around_toggled_deletions(
10683    executor: BackgroundExecutor,
10684    cx: &mut gpui::TestAppContext,
10685) {
10686    init_test(cx, |_| {});
10687
10688    let mut cx = EditorTestContext::new(cx).await;
10689
10690    let diff_base = r#"
10691        use some::mod1;
10692        use some::mod2;
10693
10694        const A: u32 = 42;
10695        const B: u32 = 42;
10696        const C: u32 = 42;
10697
10698
10699        fn main() {
10700            println!("hello");
10701
10702            println!("world");
10703        }
10704        "#
10705    .unindent();
10706    executor.run_until_parked();
10707    cx.set_state(
10708        &r#"
10709        use some::mod1;
10710        use some::mod2;
10711
10712        ˇconst B: u32 = 42;
10713        const C: u32 = 42;
10714
10715
10716        fn main() {
10717            println!("hello");
10718
10719            println!("world");
10720        }
10721        "#
10722        .unindent(),
10723    );
10724
10725    cx.set_diff_base(Some(&diff_base));
10726    executor.run_until_parked();
10727    cx.update_editor(|editor, cx| {
10728        let snapshot = editor.snapshot(cx);
10729        let all_hunks = editor_hunks(editor, &snapshot, cx);
10730        assert_eq!(
10731            all_hunks,
10732            vec![(
10733                "const A: u32 = 42;\n".to_string(),
10734                DiffHunkStatus::Removed,
10735                DisplayRow(3)..DisplayRow(3)
10736            )]
10737        );
10738    });
10739    cx.update_editor(|editor, cx| {
10740        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10741    });
10742    executor.run_until_parked();
10743    cx.assert_editor_state(
10744        &r#"
10745        use some::mod1;
10746        use some::mod2;
10747
10748        ˇconst B: u32 = 42;
10749        const C: u32 = 42;
10750
10751
10752        fn main() {
10753            println!("hello");
10754
10755            println!("world");
10756        }
10757        "#
10758        .unindent(),
10759    );
10760    cx.update_editor(|editor, cx| {
10761        let snapshot = editor.snapshot(cx);
10762        let all_hunks = editor_hunks(editor, &snapshot, cx);
10763        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10764        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10765        assert_eq!(
10766            all_hunks,
10767            vec![(
10768                "const A: u32 = 42;\n".to_string(),
10769                DiffHunkStatus::Removed,
10770                DisplayRow(4)..DisplayRow(4)
10771            )]
10772        );
10773        assert_eq!(all_hunks, all_expanded_hunks);
10774    });
10775
10776    cx.update_editor(|editor, cx| {
10777        editor.delete_line(&DeleteLine, cx);
10778    });
10779    executor.run_until_parked();
10780    cx.assert_editor_state(
10781        &r#"
10782        use some::mod1;
10783        use some::mod2;
10784
10785        ˇconst C: u32 = 42;
10786
10787
10788        fn main() {
10789            println!("hello");
10790
10791            println!("world");
10792        }
10793        "#
10794        .unindent(),
10795    );
10796    cx.update_editor(|editor, cx| {
10797        let snapshot = editor.snapshot(cx);
10798        let all_hunks = editor_hunks(editor, &snapshot, cx);
10799        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10800        assert_eq!(
10801            expanded_hunks_background_highlights(editor, cx),
10802            Vec::new(),
10803            "Deleted hunks do not highlight current editor's background"
10804        );
10805        assert_eq!(
10806            all_hunks,
10807            vec![(
10808                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
10809                DiffHunkStatus::Removed,
10810                DisplayRow(5)..DisplayRow(5)
10811            )]
10812        );
10813        assert_eq!(all_hunks, all_expanded_hunks);
10814    });
10815
10816    cx.update_editor(|editor, cx| {
10817        editor.delete_line(&DeleteLine, cx);
10818    });
10819    executor.run_until_parked();
10820    cx.assert_editor_state(
10821        &r#"
10822        use some::mod1;
10823        use some::mod2;
10824
10825        ˇ
10826
10827        fn main() {
10828            println!("hello");
10829
10830            println!("world");
10831        }
10832        "#
10833        .unindent(),
10834    );
10835    cx.update_editor(|editor, cx| {
10836        let snapshot = editor.snapshot(cx);
10837        let all_hunks = editor_hunks(editor, &snapshot, cx);
10838        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10839        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10840        assert_eq!(
10841            all_hunks,
10842            vec![(
10843                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
10844                DiffHunkStatus::Removed,
10845                DisplayRow(6)..DisplayRow(6)
10846            )]
10847        );
10848        assert_eq!(all_hunks, all_expanded_hunks);
10849    });
10850
10851    cx.update_editor(|editor, cx| {
10852        editor.handle_input("replacement", cx);
10853    });
10854    executor.run_until_parked();
10855    cx.assert_editor_state(
10856        &r#"
10857        use some::mod1;
10858        use some::mod2;
10859
10860        replacementˇ
10861
10862        fn main() {
10863            println!("hello");
10864
10865            println!("world");
10866        }
10867        "#
10868        .unindent(),
10869    );
10870    cx.update_editor(|editor, cx| {
10871        let snapshot = editor.snapshot(cx);
10872        let all_hunks = editor_hunks(editor, &snapshot, cx);
10873        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10874        assert_eq!(
10875            all_hunks,
10876            vec![(
10877                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
10878                DiffHunkStatus::Modified,
10879                DisplayRow(7)..DisplayRow(8)
10880            )]
10881        );
10882        assert_eq!(
10883            expanded_hunks_background_highlights(editor, cx),
10884            vec![DisplayRow(7)..=DisplayRow(7)],
10885            "Modified expanded hunks should display additions and highlight their background"
10886        );
10887        assert_eq!(all_hunks, all_expanded_hunks);
10888    });
10889}
10890
10891#[gpui::test]
10892async fn test_edits_around_toggled_modifications(
10893    executor: BackgroundExecutor,
10894    cx: &mut gpui::TestAppContext,
10895) {
10896    init_test(cx, |_| {});
10897
10898    let mut cx = EditorTestContext::new(cx).await;
10899
10900    let diff_base = r#"
10901        use some::mod1;
10902        use some::mod2;
10903
10904        const A: u32 = 42;
10905        const B: u32 = 42;
10906        const C: u32 = 42;
10907        const D: u32 = 42;
10908
10909
10910        fn main() {
10911            println!("hello");
10912
10913            println!("world");
10914        }"#
10915    .unindent();
10916    executor.run_until_parked();
10917    cx.set_state(
10918        &r#"
10919        use some::mod1;
10920        use some::mod2;
10921
10922        const A: u32 = 42;
10923        const B: u32 = 42;
10924        const C: u32 = 43ˇ
10925        const D: u32 = 42;
10926
10927
10928        fn main() {
10929            println!("hello");
10930
10931            println!("world");
10932        }"#
10933        .unindent(),
10934    );
10935
10936    cx.set_diff_base(Some(&diff_base));
10937    executor.run_until_parked();
10938    cx.update_editor(|editor, cx| {
10939        let snapshot = editor.snapshot(cx);
10940        let all_hunks = editor_hunks(editor, &snapshot, cx);
10941        assert_eq!(
10942            all_hunks,
10943            vec![(
10944                "const C: u32 = 42;\n".to_string(),
10945                DiffHunkStatus::Modified,
10946                DisplayRow(5)..DisplayRow(6)
10947            )]
10948        );
10949    });
10950    cx.update_editor(|editor, cx| {
10951        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10952    });
10953    executor.run_until_parked();
10954    cx.assert_editor_state(
10955        &r#"
10956        use some::mod1;
10957        use some::mod2;
10958
10959        const A: u32 = 42;
10960        const B: u32 = 42;
10961        const C: u32 = 43ˇ
10962        const D: u32 = 42;
10963
10964
10965        fn main() {
10966            println!("hello");
10967
10968            println!("world");
10969        }"#
10970        .unindent(),
10971    );
10972    cx.update_editor(|editor, cx| {
10973        let snapshot = editor.snapshot(cx);
10974        let all_hunks = editor_hunks(editor, &snapshot, cx);
10975        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10976        assert_eq!(
10977            expanded_hunks_background_highlights(editor, cx),
10978            vec![DisplayRow(6)..=DisplayRow(6)],
10979        );
10980        assert_eq!(
10981            all_hunks,
10982            vec![(
10983                "const C: u32 = 42;\n".to_string(),
10984                DiffHunkStatus::Modified,
10985                DisplayRow(6)..DisplayRow(7)
10986            )]
10987        );
10988        assert_eq!(all_hunks, all_expanded_hunks);
10989    });
10990
10991    cx.update_editor(|editor, cx| {
10992        editor.handle_input("\nnew_line\n", cx);
10993    });
10994    executor.run_until_parked();
10995    cx.assert_editor_state(
10996        &r#"
10997            use some::mod1;
10998            use some::mod2;
10999
11000            const A: u32 = 42;
11001            const B: u32 = 42;
11002            const C: u32 = 43
11003            new_line
11004            ˇ
11005            const D: u32 = 42;
11006
11007
11008            fn main() {
11009                println!("hello");
11010
11011                println!("world");
11012            }"#
11013        .unindent(),
11014    );
11015    cx.update_editor(|editor, cx| {
11016        let snapshot = editor.snapshot(cx);
11017        let all_hunks = editor_hunks(editor, &snapshot, cx);
11018        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11019        assert_eq!(
11020            expanded_hunks_background_highlights(editor, cx),
11021            vec![DisplayRow(6)..=DisplayRow(6)],
11022            "Modified hunk should grow highlighted lines on more text additions"
11023        );
11024        assert_eq!(
11025            all_hunks,
11026            vec![(
11027                "const C: u32 = 42;\n".to_string(),
11028                DiffHunkStatus::Modified,
11029                DisplayRow(6)..DisplayRow(9)
11030            )]
11031        );
11032        assert_eq!(all_hunks, all_expanded_hunks);
11033    });
11034
11035    cx.update_editor(|editor, cx| {
11036        editor.move_up(&MoveUp, cx);
11037        editor.move_up(&MoveUp, cx);
11038        editor.move_up(&MoveUp, cx);
11039        editor.delete_line(&DeleteLine, cx);
11040    });
11041    executor.run_until_parked();
11042    cx.assert_editor_state(
11043        &r#"
11044            use some::mod1;
11045            use some::mod2;
11046
11047            const A: u32 = 42;
11048            ˇconst C: u32 = 43
11049            new_line
11050
11051            const D: u32 = 42;
11052
11053
11054            fn main() {
11055                println!("hello");
11056
11057                println!("world");
11058            }"#
11059        .unindent(),
11060    );
11061    cx.update_editor(|editor, cx| {
11062        let snapshot = editor.snapshot(cx);
11063        let all_hunks = editor_hunks(editor, &snapshot, cx);
11064        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11065        assert_eq!(
11066            expanded_hunks_background_highlights(editor, cx),
11067            vec![DisplayRow(6)..=DisplayRow(8)],
11068        );
11069        assert_eq!(
11070            all_hunks,
11071            vec![(
11072                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11073                DiffHunkStatus::Modified,
11074                DisplayRow(6)..DisplayRow(9)
11075            )],
11076            "Modified hunk should grow deleted lines on text deletions above"
11077        );
11078        assert_eq!(all_hunks, all_expanded_hunks);
11079    });
11080
11081    cx.update_editor(|editor, cx| {
11082        editor.move_up(&MoveUp, cx);
11083        editor.handle_input("v", cx);
11084    });
11085    executor.run_until_parked();
11086    cx.assert_editor_state(
11087        &r#"
11088            use some::mod1;
11089            use some::mod2;
11090
11091            vˇconst A: u32 = 42;
11092            const C: u32 = 43
11093            new_line
11094
11095            const D: u32 = 42;
11096
11097
11098            fn main() {
11099                println!("hello");
11100
11101                println!("world");
11102            }"#
11103        .unindent(),
11104    );
11105    cx.update_editor(|editor, cx| {
11106        let snapshot = editor.snapshot(cx);
11107        let all_hunks = editor_hunks(editor, &snapshot, cx);
11108        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11109        assert_eq!(
11110            expanded_hunks_background_highlights(editor, cx),
11111            vec![DisplayRow(6)..=DisplayRow(9)],
11112            "Modified hunk should grow deleted lines on text modifications above"
11113        );
11114        assert_eq!(
11115            all_hunks,
11116            vec![(
11117                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11118                DiffHunkStatus::Modified,
11119                DisplayRow(6)..DisplayRow(10)
11120            )]
11121        );
11122        assert_eq!(all_hunks, all_expanded_hunks);
11123    });
11124
11125    cx.update_editor(|editor, cx| {
11126        editor.move_down(&MoveDown, cx);
11127        editor.move_down(&MoveDown, cx);
11128        editor.delete_line(&DeleteLine, cx)
11129    });
11130    executor.run_until_parked();
11131    cx.assert_editor_state(
11132        &r#"
11133            use some::mod1;
11134            use some::mod2;
11135
11136            vconst A: u32 = 42;
11137            const C: u32 = 43
11138            ˇ
11139            const D: u32 = 42;
11140
11141
11142            fn main() {
11143                println!("hello");
11144
11145                println!("world");
11146            }"#
11147        .unindent(),
11148    );
11149    cx.update_editor(|editor, cx| {
11150        let snapshot = editor.snapshot(cx);
11151        let all_hunks = editor_hunks(editor, &snapshot, cx);
11152        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11153        assert_eq!(
11154            expanded_hunks_background_highlights(editor, cx),
11155            vec![DisplayRow(6)..=DisplayRow(8)],
11156            "Modified hunk should grow shrink lines on modification lines removal"
11157        );
11158        assert_eq!(
11159            all_hunks,
11160            vec![(
11161                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11162                DiffHunkStatus::Modified,
11163                DisplayRow(6)..DisplayRow(9)
11164            )]
11165        );
11166        assert_eq!(all_hunks, all_expanded_hunks);
11167    });
11168
11169    cx.update_editor(|editor, cx| {
11170        editor.move_up(&MoveUp, cx);
11171        editor.move_up(&MoveUp, cx);
11172        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
11173        editor.delete_line(&DeleteLine, cx)
11174    });
11175    executor.run_until_parked();
11176    cx.assert_editor_state(
11177        &r#"
11178            use some::mod1;
11179            use some::mod2;
11180
11181            ˇ
11182
11183            fn main() {
11184                println!("hello");
11185
11186                println!("world");
11187            }"#
11188        .unindent(),
11189    );
11190    cx.update_editor(|editor, cx| {
11191        let snapshot = editor.snapshot(cx);
11192        let all_hunks = editor_hunks(editor, &snapshot, cx);
11193        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11194        assert_eq!(
11195            expanded_hunks_background_highlights(editor, cx),
11196            Vec::new(),
11197            "Modified hunk should turn into a removed one on all modified lines removal"
11198        );
11199        assert_eq!(
11200            all_hunks,
11201            vec![(
11202                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
11203                    .to_string(),
11204                DiffHunkStatus::Removed,
11205                DisplayRow(7)..DisplayRow(7)
11206            )]
11207        );
11208        assert_eq!(all_hunks, all_expanded_hunks);
11209    });
11210}
11211
11212#[gpui::test]
11213async fn test_multiple_expanded_hunks_merge(
11214    executor: BackgroundExecutor,
11215    cx: &mut gpui::TestAppContext,
11216) {
11217    init_test(cx, |_| {});
11218
11219    let mut cx = EditorTestContext::new(cx).await;
11220
11221    let diff_base = r#"
11222        use some::mod1;
11223        use some::mod2;
11224
11225        const A: u32 = 42;
11226        const B: u32 = 42;
11227        const C: u32 = 42;
11228        const D: u32 = 42;
11229
11230
11231        fn main() {
11232            println!("hello");
11233
11234            println!("world");
11235        }"#
11236    .unindent();
11237    executor.run_until_parked();
11238    cx.set_state(
11239        &r#"
11240        use some::mod1;
11241        use some::mod2;
11242
11243        const A: u32 = 42;
11244        const B: u32 = 42;
11245        const C: u32 = 43ˇ
11246        const D: u32 = 42;
11247
11248
11249        fn main() {
11250            println!("hello");
11251
11252            println!("world");
11253        }"#
11254        .unindent(),
11255    );
11256
11257    cx.set_diff_base(Some(&diff_base));
11258    executor.run_until_parked();
11259    cx.update_editor(|editor, cx| {
11260        let snapshot = editor.snapshot(cx);
11261        let all_hunks = editor_hunks(editor, &snapshot, cx);
11262        assert_eq!(
11263            all_hunks,
11264            vec![(
11265                "const C: u32 = 42;\n".to_string(),
11266                DiffHunkStatus::Modified,
11267                DisplayRow(5)..DisplayRow(6)
11268            )]
11269        );
11270    });
11271    cx.update_editor(|editor, cx| {
11272        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11273    });
11274    executor.run_until_parked();
11275    cx.assert_editor_state(
11276        &r#"
11277        use some::mod1;
11278        use some::mod2;
11279
11280        const A: u32 = 42;
11281        const B: u32 = 42;
11282        const C: u32 = 43ˇ
11283        const D: u32 = 42;
11284
11285
11286        fn main() {
11287            println!("hello");
11288
11289            println!("world");
11290        }"#
11291        .unindent(),
11292    );
11293    cx.update_editor(|editor, cx| {
11294        let snapshot = editor.snapshot(cx);
11295        let all_hunks = editor_hunks(editor, &snapshot, cx);
11296        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11297        assert_eq!(
11298            expanded_hunks_background_highlights(editor, cx),
11299            vec![DisplayRow(6)..=DisplayRow(6)],
11300        );
11301        assert_eq!(
11302            all_hunks,
11303            vec![(
11304                "const C: u32 = 42;\n".to_string(),
11305                DiffHunkStatus::Modified,
11306                DisplayRow(6)..DisplayRow(7)
11307            )]
11308        );
11309        assert_eq!(all_hunks, all_expanded_hunks);
11310    });
11311
11312    cx.update_editor(|editor, cx| {
11313        editor.handle_input("\nnew_line\n", cx);
11314    });
11315    executor.run_until_parked();
11316    cx.assert_editor_state(
11317        &r#"
11318            use some::mod1;
11319            use some::mod2;
11320
11321            const A: u32 = 42;
11322            const B: u32 = 42;
11323            const C: u32 = 43
11324            new_line
11325            ˇ
11326            const D: u32 = 42;
11327
11328
11329            fn main() {
11330                println!("hello");
11331
11332                println!("world");
11333            }"#
11334        .unindent(),
11335    );
11336}
11337
11338fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
11339    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
11340    point..point
11341}
11342
11343fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
11344    let (text, ranges) = marked_text_ranges(marked_text, true);
11345    assert_eq!(view.text(cx), text);
11346    assert_eq!(
11347        view.selections.ranges(cx),
11348        ranges,
11349        "Assert selections are {}",
11350        marked_text
11351    );
11352}
11353
11354/// Handle completion request passing a marked string specifying where the completion
11355/// should be triggered from using '|' character, what range should be replaced, and what completions
11356/// should be returned using '<' and '>' to delimit the range
11357pub fn handle_completion_request(
11358    cx: &mut EditorLspTestContext,
11359    marked_string: &str,
11360    completions: Vec<&'static str>,
11361) -> impl Future<Output = ()> {
11362    let complete_from_marker: TextRangeMarker = '|'.into();
11363    let replace_range_marker: TextRangeMarker = ('<', '>').into();
11364    let (_, mut marked_ranges) = marked_text_ranges_by(
11365        marked_string,
11366        vec![complete_from_marker.clone(), replace_range_marker.clone()],
11367    );
11368
11369    let complete_from_position =
11370        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
11371    let replace_range =
11372        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
11373
11374    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
11375        let completions = completions.clone();
11376        async move {
11377            assert_eq!(params.text_document_position.text_document.uri, url.clone());
11378            assert_eq!(
11379                params.text_document_position.position,
11380                complete_from_position
11381            );
11382            Ok(Some(lsp::CompletionResponse::Array(
11383                completions
11384                    .iter()
11385                    .map(|completion_text| lsp::CompletionItem {
11386                        label: completion_text.to_string(),
11387                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11388                            range: replace_range,
11389                            new_text: completion_text.to_string(),
11390                        })),
11391                        ..Default::default()
11392                    })
11393                    .collect(),
11394            )))
11395        }
11396    });
11397
11398    async move {
11399        request.next().await;
11400    }
11401}
11402
11403fn handle_resolve_completion_request(
11404    cx: &mut EditorLspTestContext,
11405    edits: Option<Vec<(&'static str, &'static str)>>,
11406) -> impl Future<Output = ()> {
11407    let edits = edits.map(|edits| {
11408        edits
11409            .iter()
11410            .map(|(marked_string, new_text)| {
11411                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
11412                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
11413                lsp::TextEdit::new(replace_range, new_text.to_string())
11414            })
11415            .collect::<Vec<_>>()
11416    });
11417
11418    let mut request =
11419        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11420            let edits = edits.clone();
11421            async move {
11422                Ok(lsp::CompletionItem {
11423                    additional_text_edits: edits,
11424                    ..Default::default()
11425                })
11426            }
11427        });
11428
11429    async move {
11430        request.next().await;
11431    }
11432}
11433
11434pub(crate) fn update_test_language_settings(
11435    cx: &mut TestAppContext,
11436    f: impl Fn(&mut AllLanguageSettingsContent),
11437) {
11438    _ = cx.update(|cx| {
11439        cx.update_global(|store: &mut SettingsStore, cx| {
11440            store.update_user_settings::<AllLanguageSettings>(cx, f);
11441        });
11442    });
11443}
11444
11445pub(crate) fn update_test_project_settings(
11446    cx: &mut TestAppContext,
11447    f: impl Fn(&mut ProjectSettings),
11448) {
11449    _ = cx.update(|cx| {
11450        cx.update_global(|store: &mut SettingsStore, cx| {
11451            store.update_user_settings::<ProjectSettings>(cx, f);
11452        });
11453    });
11454}
11455
11456pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
11457    _ = cx.update(|cx| {
11458        let store = SettingsStore::test(cx);
11459        cx.set_global(store);
11460        theme::init(theme::LoadThemes::JustBase, cx);
11461        release_channel::init("0.0.0", cx);
11462        client::init_settings(cx);
11463        language::init(cx);
11464        Project::init_settings(cx);
11465        workspace::init_settings(cx);
11466        crate::init(cx);
11467    });
11468
11469    update_test_language_settings(cx, f);
11470}
11471
11472pub(crate) fn rust_lang() -> Arc<Language> {
11473    Arc::new(Language::new(
11474        LanguageConfig {
11475            name: "Rust".into(),
11476            matcher: LanguageMatcher {
11477                path_suffixes: vec!["rs".to_string()],
11478                ..Default::default()
11479            },
11480            ..Default::default()
11481        },
11482        Some(tree_sitter_rust::language()),
11483    ))
11484}
11485
11486#[track_caller]
11487fn assert_hunk_revert(
11488    not_reverted_text_with_selections: &str,
11489    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
11490    expected_reverted_text_with_selections: &str,
11491    base_text: &str,
11492    cx: &mut EditorLspTestContext,
11493) {
11494    cx.set_state(not_reverted_text_with_selections);
11495    cx.update_editor(|editor, cx| {
11496        editor
11497            .buffer()
11498            .read(cx)
11499            .as_singleton()
11500            .unwrap()
11501            .update(cx, |buffer, cx| {
11502                buffer.set_diff_base(Some(base_text.into()), cx);
11503            });
11504    });
11505    cx.executor().run_until_parked();
11506
11507    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
11508        let snapshot = editor.buffer().read(cx).snapshot(cx);
11509        let reverted_hunk_statuses = snapshot
11510            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
11511            .map(|hunk| hunk_status(&hunk))
11512            .collect::<Vec<_>>();
11513
11514        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11515        reverted_hunk_statuses
11516    });
11517    cx.executor().run_until_parked();
11518    cx.assert_editor_state(expected_reverted_text_with_selections);
11519    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
11520}