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::{
   13    div, AssetSource, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   14    WindowBounds, WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher, Override,
   24    Point,
   25};
   26use language_settings::IndentGuideSettings;
   27use multi_buffer::MultiBufferIndentGuide;
   28use parking_lot::Mutex;
   29use project::project_settings::{LspSettings, ProjectSettings};
   30use project::FakeFs;
   31use serde_json::{self, json};
   32use std::sync::atomic;
   33use std::sync::atomic::AtomicUsize;
   34use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   35use unindent::Unindent;
   36use util::{
   37    assert_set_eq,
   38    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   39};
   40use workspace::{
   41    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   42    NavigationEntry, ViewId,
   43};
   44
   45#[gpui::test]
   46fn test_edit_events(cx: &mut TestAppContext) {
   47    init_test(cx, |_| {});
   48
   49    let buffer = cx.new_model(|cx| {
   50        let mut buffer = language::Buffer::local("123456", cx);
   51        buffer.set_group_interval(Duration::from_secs(1));
   52        buffer
   53    });
   54
   55    let events = Rc::new(RefCell::new(Vec::new()));
   56    let editor1 = cx.add_window({
   57        let events = events.clone();
   58        |cx| {
   59            let view = cx.view().clone();
   60            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   61                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   62                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   63                _ => {}
   64            })
   65            .detach();
   66            Editor::for_buffer(buffer.clone(), None, cx)
   67        }
   68    });
   69
   70    let editor2 = cx.add_window({
   71        let events = events.clone();
   72        |cx| {
   73            cx.subscribe(
   74                &cx.view().clone(),
   75                move |_, _, event: &EditorEvent, _| match event {
   76                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   77                    EditorEvent::BufferEdited => {
   78                        events.borrow_mut().push(("editor2", "buffer edited"))
   79                    }
   80                    _ => {}
   81                },
   82            )
   83            .detach();
   84            Editor::for_buffer(buffer.clone(), None, cx)
   85        }
   86    });
   87
   88    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   89
   90    // Mutating editor 1 will emit an `Edited` event only for that editor.
   91    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   92    assert_eq!(
   93        mem::take(&mut *events.borrow_mut()),
   94        [
   95            ("editor1", "edited"),
   96            ("editor1", "buffer edited"),
   97            ("editor2", "buffer edited"),
   98        ]
   99    );
  100
  101    // Mutating editor 2 will emit an `Edited` event only for that editor.
  102    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  103    assert_eq!(
  104        mem::take(&mut *events.borrow_mut()),
  105        [
  106            ("editor2", "edited"),
  107            ("editor1", "buffer edited"),
  108            ("editor2", "buffer edited"),
  109        ]
  110    );
  111
  112    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  113    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  114    assert_eq!(
  115        mem::take(&mut *events.borrow_mut()),
  116        [
  117            ("editor1", "edited"),
  118            ("editor1", "buffer edited"),
  119            ("editor2", "buffer edited"),
  120        ]
  121    );
  122
  123    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  124    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  125    assert_eq!(
  126        mem::take(&mut *events.borrow_mut()),
  127        [
  128            ("editor1", "edited"),
  129            ("editor1", "buffer edited"),
  130            ("editor2", "buffer edited"),
  131        ]
  132    );
  133
  134    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  135    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  136    assert_eq!(
  137        mem::take(&mut *events.borrow_mut()),
  138        [
  139            ("editor2", "edited"),
  140            ("editor1", "buffer edited"),
  141            ("editor2", "buffer edited"),
  142        ]
  143    );
  144
  145    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  146    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  147    assert_eq!(
  148        mem::take(&mut *events.borrow_mut()),
  149        [
  150            ("editor2", "edited"),
  151            ("editor1", "buffer edited"),
  152            ("editor2", "buffer edited"),
  153        ]
  154    );
  155
  156    // No event is emitted when the mutation is a no-op.
  157    _ = editor2.update(cx, |editor, cx| {
  158        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  159
  160        editor.backspace(&Backspace, cx);
  161    });
  162    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  163}
  164
  165#[gpui::test]
  166fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  167    init_test(cx, |_| {});
  168
  169    let mut now = Instant::now();
  170    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  171    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  172    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  173    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  174
  175    _ = editor.update(cx, |editor, cx| {
  176        editor.start_transaction_at(now, cx);
  177        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  178
  179        editor.insert("cd", cx);
  180        editor.end_transaction_at(now, cx);
  181        assert_eq!(editor.text(cx), "12cd56");
  182        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  183
  184        editor.start_transaction_at(now, cx);
  185        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  186        editor.insert("e", cx);
  187        editor.end_transaction_at(now, cx);
  188        assert_eq!(editor.text(cx), "12cde6");
  189        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  190
  191        now += group_interval + Duration::from_millis(1);
  192        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  193
  194        // Simulate an edit in another editor
  195        _ = buffer.update(cx, |buffer, cx| {
  196            buffer.start_transaction_at(now, cx);
  197            buffer.edit([(0..1, "a")], None, cx);
  198            buffer.edit([(1..1, "b")], None, cx);
  199            buffer.end_transaction_at(now, cx);
  200        });
  201
  202        assert_eq!(editor.text(cx), "ab2cde6");
  203        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  204
  205        // Last transaction happened past the group interval in a different editor.
  206        // Undo it individually and don't restore selections.
  207        editor.undo(&Undo, cx);
  208        assert_eq!(editor.text(cx), "12cde6");
  209        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  210
  211        // First two transactions happened within the group interval in this editor.
  212        // Undo them together and restore selections.
  213        editor.undo(&Undo, cx);
  214        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  215        assert_eq!(editor.text(cx), "123456");
  216        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  217
  218        // Redo the first two transactions together.
  219        editor.redo(&Redo, cx);
  220        assert_eq!(editor.text(cx), "12cde6");
  221        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  222
  223        // Redo the last transaction on its own.
  224        editor.redo(&Redo, cx);
  225        assert_eq!(editor.text(cx), "ab2cde6");
  226        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  227
  228        // Test empty transactions.
  229        editor.start_transaction_at(now, cx);
  230        editor.end_transaction_at(now, cx);
  231        editor.undo(&Undo, cx);
  232        assert_eq!(editor.text(cx), "12cde6");
  233    });
  234}
  235
  236#[gpui::test]
  237fn test_ime_composition(cx: &mut TestAppContext) {
  238    init_test(cx, |_| {});
  239
  240    let buffer = cx.new_model(|cx| {
  241        let mut buffer = language::Buffer::local("abcde", cx);
  242        // Ensure automatic grouping doesn't occur.
  243        buffer.set_group_interval(Duration::ZERO);
  244        buffer
  245    });
  246
  247    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  248    cx.add_window(|cx| {
  249        let mut editor = build_editor(buffer.clone(), cx);
  250
  251        // Start a new IME composition.
  252        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  253        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  254        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  255        assert_eq!(editor.text(cx), "äbcde");
  256        assert_eq!(
  257            editor.marked_text_ranges(cx),
  258            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  259        );
  260
  261        // Finalize IME composition.
  262        editor.replace_text_in_range(None, "ā", cx);
  263        assert_eq!(editor.text(cx), "ābcde");
  264        assert_eq!(editor.marked_text_ranges(cx), None);
  265
  266        // IME composition edits are grouped and are undone/redone at once.
  267        editor.undo(&Default::default(), cx);
  268        assert_eq!(editor.text(cx), "abcde");
  269        assert_eq!(editor.marked_text_ranges(cx), None);
  270        editor.redo(&Default::default(), cx);
  271        assert_eq!(editor.text(cx), "ābcde");
  272        assert_eq!(editor.marked_text_ranges(cx), None);
  273
  274        // Start a new IME composition.
  275        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  276        assert_eq!(
  277            editor.marked_text_ranges(cx),
  278            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  279        );
  280
  281        // Undoing during an IME composition cancels it.
  282        editor.undo(&Default::default(), cx);
  283        assert_eq!(editor.text(cx), "ābcde");
  284        assert_eq!(editor.marked_text_ranges(cx), None);
  285
  286        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  287        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  288        assert_eq!(editor.text(cx), "ābcdè");
  289        assert_eq!(
  290            editor.marked_text_ranges(cx),
  291            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  292        );
  293
  294        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  295        editor.replace_text_in_range(Some(4..999), "ę", cx);
  296        assert_eq!(editor.text(cx), "ābcdę");
  297        assert_eq!(editor.marked_text_ranges(cx), None);
  298
  299        // Start a new IME composition with multiple cursors.
  300        editor.change_selections(None, cx, |s| {
  301            s.select_ranges([
  302                OffsetUtf16(1)..OffsetUtf16(1),
  303                OffsetUtf16(3)..OffsetUtf16(3),
  304                OffsetUtf16(5)..OffsetUtf16(5),
  305            ])
  306        });
  307        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  308        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  309        assert_eq!(
  310            editor.marked_text_ranges(cx),
  311            Some(vec![
  312                OffsetUtf16(0)..OffsetUtf16(3),
  313                OffsetUtf16(4)..OffsetUtf16(7),
  314                OffsetUtf16(8)..OffsetUtf16(11)
  315            ])
  316        );
  317
  318        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  319        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  320        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  321        assert_eq!(
  322            editor.marked_text_ranges(cx),
  323            Some(vec![
  324                OffsetUtf16(1)..OffsetUtf16(2),
  325                OffsetUtf16(5)..OffsetUtf16(6),
  326                OffsetUtf16(9)..OffsetUtf16(10)
  327            ])
  328        );
  329
  330        // Finalize IME composition with multiple cursors.
  331        editor.replace_text_in_range(Some(9..10), "2", cx);
  332        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  333        assert_eq!(editor.marked_text_ranges(cx), None);
  334
  335        editor
  336    });
  337}
  338
  339#[gpui::test]
  340fn test_selection_with_mouse(cx: &mut TestAppContext) {
  341    init_test(cx, |_| {});
  342
  343    let editor = cx.add_window(|cx| {
  344        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  345        build_editor(buffer, cx)
  346    });
  347
  348    _ = editor.update(cx, |view, cx| {
  349        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  350    });
  351    assert_eq!(
  352        editor
  353            .update(cx, |view, cx| view.selections.display_ranges(cx))
  354            .unwrap(),
  355        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  356    );
  357
  358    _ = editor.update(cx, |view, cx| {
  359        view.update_selection(
  360            DisplayPoint::new(DisplayRow(3), 3),
  361            0,
  362            gpui::Point::<f32>::default(),
  363            cx,
  364        );
  365    });
  366
  367    assert_eq!(
  368        editor
  369            .update(cx, |view, cx| view.selections.display_ranges(cx))
  370            .unwrap(),
  371        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  372    );
  373
  374    _ = editor.update(cx, |view, cx| {
  375        view.update_selection(
  376            DisplayPoint::new(DisplayRow(1), 1),
  377            0,
  378            gpui::Point::<f32>::default(),
  379            cx,
  380        );
  381    });
  382
  383    assert_eq!(
  384        editor
  385            .update(cx, |view, cx| view.selections.display_ranges(cx))
  386            .unwrap(),
  387        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  388    );
  389
  390    _ = editor.update(cx, |view, cx| {
  391        view.end_selection(cx);
  392        view.update_selection(
  393            DisplayPoint::new(DisplayRow(3), 3),
  394            0,
  395            gpui::Point::<f32>::default(),
  396            cx,
  397        );
  398    });
  399
  400    assert_eq!(
  401        editor
  402            .update(cx, |view, cx| view.selections.display_ranges(cx))
  403            .unwrap(),
  404        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  405    );
  406
  407    _ = editor.update(cx, |view, cx| {
  408        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  409        view.update_selection(
  410            DisplayPoint::new(DisplayRow(0), 0),
  411            0,
  412            gpui::Point::<f32>::default(),
  413            cx,
  414        );
  415    });
  416
  417    assert_eq!(
  418        editor
  419            .update(cx, |view, cx| view.selections.display_ranges(cx))
  420            .unwrap(),
  421        [
  422            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  423            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  424        ]
  425    );
  426
  427    _ = editor.update(cx, |view, cx| {
  428        view.end_selection(cx);
  429    });
  430
  431    assert_eq!(
  432        editor
  433            .update(cx, |view, cx| view.selections.display_ranges(cx))
  434            .unwrap(),
  435        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  436    );
  437}
  438
  439#[gpui::test]
  440fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  441    init_test(cx, |_| {});
  442
  443    let view = cx.add_window(|cx| {
  444        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  445        build_editor(buffer, cx)
  446    });
  447
  448    _ = view.update(cx, |view, cx| {
  449        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  450        assert_eq!(
  451            view.selections.display_ranges(cx),
  452            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  453        );
  454    });
  455
  456    _ = view.update(cx, |view, cx| {
  457        view.update_selection(
  458            DisplayPoint::new(DisplayRow(3), 3),
  459            0,
  460            gpui::Point::<f32>::default(),
  461            cx,
  462        );
  463        assert_eq!(
  464            view.selections.display_ranges(cx),
  465            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  466        );
  467    });
  468
  469    _ = view.update(cx, |view, cx| {
  470        view.cancel(&Cancel, cx);
  471        view.update_selection(
  472            DisplayPoint::new(DisplayRow(1), 1),
  473            0,
  474            gpui::Point::<f32>::default(),
  475            cx,
  476        );
  477        assert_eq!(
  478            view.selections.display_ranges(cx),
  479            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  480        );
  481    });
  482}
  483
  484#[gpui::test]
  485fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  486    init_test(cx, |_| {});
  487
  488    let view = cx.add_window(|cx| {
  489        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  490        build_editor(buffer, cx)
  491    });
  492
  493    _ = view.update(cx, |view, cx| {
  494        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  495        assert_eq!(
  496            view.selections.display_ranges(cx),
  497            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  498        );
  499
  500        view.move_down(&Default::default(), cx);
  501        assert_eq!(
  502            view.selections.display_ranges(cx),
  503            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  504        );
  505
  506        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  507        assert_eq!(
  508            view.selections.display_ranges(cx),
  509            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  510        );
  511
  512        view.move_up(&Default::default(), cx);
  513        assert_eq!(
  514            view.selections.display_ranges(cx),
  515            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  516        );
  517    });
  518}
  519
  520#[gpui::test]
  521fn test_clone(cx: &mut TestAppContext) {
  522    init_test(cx, |_| {});
  523
  524    let (text, selection_ranges) = marked_text_ranges(
  525        indoc! {"
  526            one
  527            two
  528            threeˇ
  529            four
  530            fiveˇ
  531        "},
  532        true,
  533    );
  534
  535    let editor = cx.add_window(|cx| {
  536        let buffer = MultiBuffer::build_simple(&text, cx);
  537        build_editor(buffer, cx)
  538    });
  539
  540    _ = editor.update(cx, |editor, cx| {
  541        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  542        editor.fold_ranges(
  543            [
  544                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  545                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  546            ],
  547            true,
  548            cx,
  549        );
  550    });
  551
  552    let cloned_editor = editor
  553        .update(cx, |editor, cx| {
  554            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  555        })
  556        .unwrap()
  557        .unwrap();
  558
  559    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  560    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  561
  562    assert_eq!(
  563        cloned_editor
  564            .update(cx, |e, cx| e.display_text(cx))
  565            .unwrap(),
  566        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  567    );
  568    assert_eq!(
  569        cloned_snapshot
  570            .folds_in_range(0..text.len())
  571            .collect::<Vec<_>>(),
  572        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  573    );
  574    assert_set_eq!(
  575        cloned_editor
  576            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  577            .unwrap(),
  578        editor
  579            .update(cx, |editor, cx| editor.selections.ranges(cx))
  580            .unwrap()
  581    );
  582    assert_set_eq!(
  583        cloned_editor
  584            .update(cx, |e, cx| e.selections.display_ranges(cx))
  585            .unwrap(),
  586        editor
  587            .update(cx, |e, cx| e.selections.display_ranges(cx))
  588            .unwrap()
  589    );
  590}
  591
  592#[gpui::test]
  593async fn test_navigation_history(cx: &mut TestAppContext) {
  594    init_test(cx, |_| {});
  595
  596    use workspace::item::Item;
  597
  598    let fs = FakeFs::new(cx.executor());
  599    let project = Project::test(fs, [], cx).await;
  600    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  601    let pane = workspace
  602        .update(cx, |workspace, _| workspace.active_pane().clone())
  603        .unwrap();
  604
  605    _ = workspace.update(cx, |_v, cx| {
  606        cx.new_view(|cx| {
  607            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  608            let mut editor = build_editor(buffer.clone(), cx);
  609            let handle = cx.view();
  610            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  611
  612            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  613                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  614            }
  615
  616            // Move the cursor a small distance.
  617            // Nothing is added to the navigation history.
  618            editor.change_selections(None, cx, |s| {
  619                s.select_display_ranges([
  620                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  621                ])
  622            });
  623            editor.change_selections(None, cx, |s| {
  624                s.select_display_ranges([
  625                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  626                ])
  627            });
  628            assert!(pop_history(&mut editor, cx).is_none());
  629
  630            // Move the cursor a large distance.
  631            // The history can jump back to the previous position.
  632            editor.change_selections(None, cx, |s| {
  633                s.select_display_ranges([
  634                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  635                ])
  636            });
  637            let nav_entry = pop_history(&mut editor, cx).unwrap();
  638            editor.navigate(nav_entry.data.unwrap(), cx);
  639            assert_eq!(nav_entry.item.id(), cx.entity_id());
  640            assert_eq!(
  641                editor.selections.display_ranges(cx),
  642                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  643            );
  644            assert!(pop_history(&mut editor, cx).is_none());
  645
  646            // Move the cursor a small distance via the mouse.
  647            // Nothing is added to the navigation history.
  648            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  649            editor.end_selection(cx);
  650            assert_eq!(
  651                editor.selections.display_ranges(cx),
  652                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  653            );
  654            assert!(pop_history(&mut editor, cx).is_none());
  655
  656            // Move the cursor a large distance via the mouse.
  657            // The history can jump back to the previous position.
  658            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  659            editor.end_selection(cx);
  660            assert_eq!(
  661                editor.selections.display_ranges(cx),
  662                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  663            );
  664            let nav_entry = pop_history(&mut editor, cx).unwrap();
  665            editor.navigate(nav_entry.data.unwrap(), cx);
  666            assert_eq!(nav_entry.item.id(), cx.entity_id());
  667            assert_eq!(
  668                editor.selections.display_ranges(cx),
  669                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  670            );
  671            assert!(pop_history(&mut editor, cx).is_none());
  672
  673            // Set scroll position to check later
  674            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  675            let original_scroll_position = editor.scroll_manager.anchor();
  676
  677            // Jump to the end of the document and adjust scroll
  678            editor.move_to_end(&MoveToEnd, cx);
  679            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  680            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  681
  682            let nav_entry = pop_history(&mut editor, cx).unwrap();
  683            editor.navigate(nav_entry.data.unwrap(), cx);
  684            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  685
  686            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  687            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  688            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  689            let invalid_point = Point::new(9999, 0);
  690            editor.navigate(
  691                Box::new(NavigationData {
  692                    cursor_anchor: invalid_anchor,
  693                    cursor_position: invalid_point,
  694                    scroll_anchor: ScrollAnchor {
  695                        anchor: invalid_anchor,
  696                        offset: Default::default(),
  697                    },
  698                    scroll_top_row: invalid_point.row,
  699                }),
  700                cx,
  701            );
  702            assert_eq!(
  703                editor.selections.display_ranges(cx),
  704                &[editor.max_point(cx)..editor.max_point(cx)]
  705            );
  706            assert_eq!(
  707                editor.scroll_position(cx),
  708                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  709            );
  710
  711            editor
  712        })
  713    });
  714}
  715
  716#[gpui::test]
  717fn test_cancel(cx: &mut TestAppContext) {
  718    init_test(cx, |_| {});
  719
  720    let view = cx.add_window(|cx| {
  721        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  722        build_editor(buffer, cx)
  723    });
  724
  725    _ = view.update(cx, |view, cx| {
  726        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  727        view.update_selection(
  728            DisplayPoint::new(DisplayRow(1), 1),
  729            0,
  730            gpui::Point::<f32>::default(),
  731            cx,
  732        );
  733        view.end_selection(cx);
  734
  735        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  736        view.update_selection(
  737            DisplayPoint::new(DisplayRow(0), 3),
  738            0,
  739            gpui::Point::<f32>::default(),
  740            cx,
  741        );
  742        view.end_selection(cx);
  743        assert_eq!(
  744            view.selections.display_ranges(cx),
  745            [
  746                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  747                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  748            ]
  749        );
  750    });
  751
  752    _ = view.update(cx, |view, cx| {
  753        view.cancel(&Cancel, cx);
  754        assert_eq!(
  755            view.selections.display_ranges(cx),
  756            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  757        );
  758    });
  759
  760    _ = view.update(cx, |view, cx| {
  761        view.cancel(&Cancel, cx);
  762        assert_eq!(
  763            view.selections.display_ranges(cx),
  764            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  765        );
  766    });
  767}
  768
  769#[gpui::test]
  770fn test_fold_action(cx: &mut TestAppContext) {
  771    init_test(cx, |_| {});
  772
  773    let view = cx.add_window(|cx| {
  774        let buffer = MultiBuffer::build_simple(
  775            &"
  776                impl Foo {
  777                    // Hello!
  778
  779                    fn a() {
  780                        1
  781                    }
  782
  783                    fn b() {
  784                        2
  785                    }
  786
  787                    fn c() {
  788                        3
  789                    }
  790                }
  791            "
  792            .unindent(),
  793            cx,
  794        );
  795        build_editor(buffer.clone(), cx)
  796    });
  797
  798    _ = view.update(cx, |view, cx| {
  799        view.change_selections(None, cx, |s| {
  800            s.select_display_ranges([
  801                DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(12), 0)
  802            ]);
  803        });
  804        view.fold(&Fold, cx);
  805        assert_eq!(
  806            view.display_text(cx),
  807            "
  808                impl Foo {
  809                    // Hello!
  810
  811                    fn a() {
  812                        1
  813                    }
  814
  815                    fn b() {⋯
  816                    }
  817
  818                    fn c() {⋯
  819                    }
  820                }
  821            "
  822            .unindent(),
  823        );
  824
  825        view.fold(&Fold, cx);
  826        assert_eq!(
  827            view.display_text(cx),
  828            "
  829                impl Foo {⋯
  830                }
  831            "
  832            .unindent(),
  833        );
  834
  835        view.unfold_lines(&UnfoldLines, cx);
  836        assert_eq!(
  837            view.display_text(cx),
  838            "
  839                impl Foo {
  840                    // Hello!
  841
  842                    fn a() {
  843                        1
  844                    }
  845
  846                    fn b() {⋯
  847                    }
  848
  849                    fn c() {⋯
  850                    }
  851                }
  852            "
  853            .unindent(),
  854        );
  855
  856        view.unfold_lines(&UnfoldLines, cx);
  857        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  858    });
  859}
  860
  861#[gpui::test]
  862fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  863    init_test(cx, |_| {});
  864
  865    let view = cx.add_window(|cx| {
  866        let buffer = MultiBuffer::build_simple(
  867            &"
  868                class Foo:
  869                    # Hello!
  870
  871                    def a():
  872                        print(1)
  873
  874                    def b():
  875                        print(2)
  876
  877                    def c():
  878                        print(3)
  879            "
  880            .unindent(),
  881            cx,
  882        );
  883        build_editor(buffer.clone(), cx)
  884    });
  885
  886    _ = view.update(cx, |view, cx| {
  887        view.change_selections(None, cx, |s| {
  888            s.select_display_ranges([
  889                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(10), 0)
  890            ]);
  891        });
  892        view.fold(&Fold, cx);
  893        assert_eq!(
  894            view.display_text(cx),
  895            "
  896                class Foo:
  897                    # Hello!
  898
  899                    def a():
  900                        print(1)
  901
  902                    def b():⋯
  903
  904                    def c():⋯
  905            "
  906            .unindent(),
  907        );
  908
  909        view.fold(&Fold, cx);
  910        assert_eq!(
  911            view.display_text(cx),
  912            "
  913                class Foo:⋯
  914            "
  915            .unindent(),
  916        );
  917
  918        view.unfold_lines(&UnfoldLines, cx);
  919        assert_eq!(
  920            view.display_text(cx),
  921            "
  922                class Foo:
  923                    # Hello!
  924
  925                    def a():
  926                        print(1)
  927
  928                    def b():⋯
  929
  930                    def c():⋯
  931            "
  932            .unindent(),
  933        );
  934
  935        view.unfold_lines(&UnfoldLines, cx);
  936        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  937    });
  938}
  939
  940#[gpui::test]
  941fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  942    init_test(cx, |_| {});
  943
  944    let view = cx.add_window(|cx| {
  945        let buffer = MultiBuffer::build_simple(
  946            &"
  947                class Foo:
  948                    # Hello!
  949
  950                    def a():
  951                        print(1)
  952
  953                    def b():
  954                        print(2)
  955
  956
  957                    def c():
  958                        print(3)
  959
  960
  961            "
  962            .unindent(),
  963            cx,
  964        );
  965        build_editor(buffer.clone(), cx)
  966    });
  967
  968    _ = view.update(cx, |view, cx| {
  969        view.change_selections(None, cx, |s| {
  970            s.select_display_ranges([
  971                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(11), 0)
  972            ]);
  973        });
  974        view.fold(&Fold, cx);
  975        assert_eq!(
  976            view.display_text(cx),
  977            "
  978                class Foo:
  979                    # Hello!
  980
  981                    def a():
  982                        print(1)
  983
  984                    def b():⋯
  985
  986
  987                    def c():⋯
  988
  989
  990            "
  991            .unindent(),
  992        );
  993
  994        view.fold(&Fold, cx);
  995        assert_eq!(
  996            view.display_text(cx),
  997            "
  998                class Foo:⋯
  999
 1000
 1001            "
 1002            .unindent(),
 1003        );
 1004
 1005        view.unfold_lines(&UnfoldLines, cx);
 1006        assert_eq!(
 1007            view.display_text(cx),
 1008            "
 1009                class Foo:
 1010                    # Hello!
 1011
 1012                    def a():
 1013                        print(1)
 1014
 1015                    def b():⋯
 1016
 1017
 1018                    def c():⋯
 1019
 1020
 1021            "
 1022            .unindent(),
 1023        );
 1024
 1025        view.unfold_lines(&UnfoldLines, cx);
 1026        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1027    });
 1028}
 1029
 1030#[gpui::test]
 1031fn test_move_cursor(cx: &mut TestAppContext) {
 1032    init_test(cx, |_| {});
 1033
 1034    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1035    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1036
 1037    _ = buffer.update(cx, |buffer, cx| {
 1038        buffer.edit(
 1039            vec![
 1040                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1041                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1042            ],
 1043            None,
 1044            cx,
 1045        );
 1046    });
 1047    _ = view.update(cx, |view, cx| {
 1048        assert_eq!(
 1049            view.selections.display_ranges(cx),
 1050            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1051        );
 1052
 1053        view.move_down(&MoveDown, cx);
 1054        assert_eq!(
 1055            view.selections.display_ranges(cx),
 1056            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1057        );
 1058
 1059        view.move_right(&MoveRight, cx);
 1060        assert_eq!(
 1061            view.selections.display_ranges(cx),
 1062            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1063        );
 1064
 1065        view.move_left(&MoveLeft, cx);
 1066        assert_eq!(
 1067            view.selections.display_ranges(cx),
 1068            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1069        );
 1070
 1071        view.move_up(&MoveUp, cx);
 1072        assert_eq!(
 1073            view.selections.display_ranges(cx),
 1074            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1075        );
 1076
 1077        view.move_to_end(&MoveToEnd, cx);
 1078        assert_eq!(
 1079            view.selections.display_ranges(cx),
 1080            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1081        );
 1082
 1083        view.move_to_beginning(&MoveToBeginning, cx);
 1084        assert_eq!(
 1085            view.selections.display_ranges(cx),
 1086            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1087        );
 1088
 1089        view.change_selections(None, cx, |s| {
 1090            s.select_display_ranges([
 1091                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1092            ]);
 1093        });
 1094        view.select_to_beginning(&SelectToBeginning, cx);
 1095        assert_eq!(
 1096            view.selections.display_ranges(cx),
 1097            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1098        );
 1099
 1100        view.select_to_end(&SelectToEnd, cx);
 1101        assert_eq!(
 1102            view.selections.display_ranges(cx),
 1103            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1104        );
 1105    });
 1106}
 1107
 1108// TODO: Re-enable this test
 1109#[cfg(target_os = "macos")]
 1110#[gpui::test]
 1111fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1112    init_test(cx, |_| {});
 1113
 1114    let view = cx.add_window(|cx| {
 1115        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1116        build_editor(buffer.clone(), cx)
 1117    });
 1118
 1119    assert_eq!('ⓐ'.len_utf8(), 3);
 1120    assert_eq!('α'.len_utf8(), 2);
 1121
 1122    _ = view.update(cx, |view, cx| {
 1123        view.fold_ranges(
 1124            vec![
 1125                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1126                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1127                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1128            ],
 1129            true,
 1130            cx,
 1131        );
 1132        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1133
 1134        view.move_right(&MoveRight, cx);
 1135        assert_eq!(
 1136            view.selections.display_ranges(cx),
 1137            &[empty_range(0, "".len())]
 1138        );
 1139        view.move_right(&MoveRight, cx);
 1140        assert_eq!(
 1141            view.selections.display_ranges(cx),
 1142            &[empty_range(0, "ⓐⓑ".len())]
 1143        );
 1144        view.move_right(&MoveRight, cx);
 1145        assert_eq!(
 1146            view.selections.display_ranges(cx),
 1147            &[empty_range(0, "ⓐⓑ⋯".len())]
 1148        );
 1149
 1150        view.move_down(&MoveDown, cx);
 1151        assert_eq!(
 1152            view.selections.display_ranges(cx),
 1153            &[empty_range(1, "ab⋯e".len())]
 1154        );
 1155        view.move_left(&MoveLeft, cx);
 1156        assert_eq!(
 1157            view.selections.display_ranges(cx),
 1158            &[empty_range(1, "ab⋯".len())]
 1159        );
 1160        view.move_left(&MoveLeft, cx);
 1161        assert_eq!(
 1162            view.selections.display_ranges(cx),
 1163            &[empty_range(1, "ab".len())]
 1164        );
 1165        view.move_left(&MoveLeft, cx);
 1166        assert_eq!(
 1167            view.selections.display_ranges(cx),
 1168            &[empty_range(1, "a".len())]
 1169        );
 1170
 1171        view.move_down(&MoveDown, cx);
 1172        assert_eq!(
 1173            view.selections.display_ranges(cx),
 1174            &[empty_range(2, "α".len())]
 1175        );
 1176        view.move_right(&MoveRight, cx);
 1177        assert_eq!(
 1178            view.selections.display_ranges(cx),
 1179            &[empty_range(2, "αβ".len())]
 1180        );
 1181        view.move_right(&MoveRight, cx);
 1182        assert_eq!(
 1183            view.selections.display_ranges(cx),
 1184            &[empty_range(2, "αβ⋯".len())]
 1185        );
 1186        view.move_right(&MoveRight, cx);
 1187        assert_eq!(
 1188            view.selections.display_ranges(cx),
 1189            &[empty_range(2, "αβ⋯ε".len())]
 1190        );
 1191
 1192        view.move_up(&MoveUp, cx);
 1193        assert_eq!(
 1194            view.selections.display_ranges(cx),
 1195            &[empty_range(1, "ab⋯e".len())]
 1196        );
 1197        view.move_down(&MoveDown, cx);
 1198        assert_eq!(
 1199            view.selections.display_ranges(cx),
 1200            &[empty_range(2, "αβ⋯ε".len())]
 1201        );
 1202        view.move_up(&MoveUp, cx);
 1203        assert_eq!(
 1204            view.selections.display_ranges(cx),
 1205            &[empty_range(1, "ab⋯e".len())]
 1206        );
 1207
 1208        view.move_up(&MoveUp, cx);
 1209        assert_eq!(
 1210            view.selections.display_ranges(cx),
 1211            &[empty_range(0, "ⓐⓑ".len())]
 1212        );
 1213        view.move_left(&MoveLeft, cx);
 1214        assert_eq!(
 1215            view.selections.display_ranges(cx),
 1216            &[empty_range(0, "".len())]
 1217        );
 1218        view.move_left(&MoveLeft, cx);
 1219        assert_eq!(
 1220            view.selections.display_ranges(cx),
 1221            &[empty_range(0, "".len())]
 1222        );
 1223    });
 1224}
 1225
 1226#[gpui::test]
 1227fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1228    init_test(cx, |_| {});
 1229
 1230    let view = cx.add_window(|cx| {
 1231        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1232        build_editor(buffer.clone(), cx)
 1233    });
 1234    _ = view.update(cx, |view, cx| {
 1235        view.change_selections(None, cx, |s| {
 1236            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1237        });
 1238        view.move_down(&MoveDown, cx);
 1239        assert_eq!(
 1240            view.selections.display_ranges(cx),
 1241            &[empty_range(1, "abcd".len())]
 1242        );
 1243
 1244        view.move_down(&MoveDown, cx);
 1245        assert_eq!(
 1246            view.selections.display_ranges(cx),
 1247            &[empty_range(2, "αβγ".len())]
 1248        );
 1249
 1250        view.move_down(&MoveDown, cx);
 1251        assert_eq!(
 1252            view.selections.display_ranges(cx),
 1253            &[empty_range(3, "abcd".len())]
 1254        );
 1255
 1256        view.move_down(&MoveDown, cx);
 1257        assert_eq!(
 1258            view.selections.display_ranges(cx),
 1259            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1260        );
 1261
 1262        view.move_up(&MoveUp, cx);
 1263        assert_eq!(
 1264            view.selections.display_ranges(cx),
 1265            &[empty_range(3, "abcd".len())]
 1266        );
 1267
 1268        view.move_up(&MoveUp, cx);
 1269        assert_eq!(
 1270            view.selections.display_ranges(cx),
 1271            &[empty_range(2, "αβγ".len())]
 1272        );
 1273    });
 1274}
 1275
 1276#[gpui::test]
 1277fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1278    init_test(cx, |_| {});
 1279    let move_to_beg = MoveToBeginningOfLine {
 1280        stop_at_soft_wraps: true,
 1281    };
 1282
 1283    let move_to_end = MoveToEndOfLine {
 1284        stop_at_soft_wraps: true,
 1285    };
 1286
 1287    let view = cx.add_window(|cx| {
 1288        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1289        build_editor(buffer, cx)
 1290    });
 1291    _ = view.update(cx, |view, cx| {
 1292        view.change_selections(None, cx, |s| {
 1293            s.select_display_ranges([
 1294                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1295                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1296            ]);
 1297        });
 1298    });
 1299
 1300    _ = view.update(cx, |view, cx| {
 1301        view.move_to_beginning_of_line(&move_to_beg, cx);
 1302        assert_eq!(
 1303            view.selections.display_ranges(cx),
 1304            &[
 1305                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1306                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1307            ]
 1308        );
 1309    });
 1310
 1311    _ = view.update(cx, |view, cx| {
 1312        view.move_to_beginning_of_line(&move_to_beg, cx);
 1313        assert_eq!(
 1314            view.selections.display_ranges(cx),
 1315            &[
 1316                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1317                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1318            ]
 1319        );
 1320    });
 1321
 1322    _ = view.update(cx, |view, cx| {
 1323        view.move_to_beginning_of_line(&move_to_beg, cx);
 1324        assert_eq!(
 1325            view.selections.display_ranges(cx),
 1326            &[
 1327                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1328                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1329            ]
 1330        );
 1331    });
 1332
 1333    _ = view.update(cx, |view, cx| {
 1334        view.move_to_end_of_line(&move_to_end, cx);
 1335        assert_eq!(
 1336            view.selections.display_ranges(cx),
 1337            &[
 1338                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1339                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1340            ]
 1341        );
 1342    });
 1343
 1344    // Moving to the end of line again is a no-op.
 1345    _ = view.update(cx, |view, cx| {
 1346        view.move_to_end_of_line(&move_to_end, cx);
 1347        assert_eq!(
 1348            view.selections.display_ranges(cx),
 1349            &[
 1350                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1351                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1352            ]
 1353        );
 1354    });
 1355
 1356    _ = view.update(cx, |view, cx| {
 1357        view.move_left(&MoveLeft, cx);
 1358        view.select_to_beginning_of_line(
 1359            &SelectToBeginningOfLine {
 1360                stop_at_soft_wraps: true,
 1361            },
 1362            cx,
 1363        );
 1364        assert_eq!(
 1365            view.selections.display_ranges(cx),
 1366            &[
 1367                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1368                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1369            ]
 1370        );
 1371    });
 1372
 1373    _ = view.update(cx, |view, cx| {
 1374        view.select_to_beginning_of_line(
 1375            &SelectToBeginningOfLine {
 1376                stop_at_soft_wraps: true,
 1377            },
 1378            cx,
 1379        );
 1380        assert_eq!(
 1381            view.selections.display_ranges(cx),
 1382            &[
 1383                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1384                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1385            ]
 1386        );
 1387    });
 1388
 1389    _ = view.update(cx, |view, cx| {
 1390        view.select_to_beginning_of_line(
 1391            &SelectToBeginningOfLine {
 1392                stop_at_soft_wraps: true,
 1393            },
 1394            cx,
 1395        );
 1396        assert_eq!(
 1397            view.selections.display_ranges(cx),
 1398            &[
 1399                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1400                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1401            ]
 1402        );
 1403    });
 1404
 1405    _ = view.update(cx, |view, cx| {
 1406        view.select_to_end_of_line(
 1407            &SelectToEndOfLine {
 1408                stop_at_soft_wraps: true,
 1409            },
 1410            cx,
 1411        );
 1412        assert_eq!(
 1413            view.selections.display_ranges(cx),
 1414            &[
 1415                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1416                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1417            ]
 1418        );
 1419    });
 1420
 1421    _ = view.update(cx, |view, cx| {
 1422        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1423        assert_eq!(view.display_text(cx), "ab\n  de");
 1424        assert_eq!(
 1425            view.selections.display_ranges(cx),
 1426            &[
 1427                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1428                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1429            ]
 1430        );
 1431    });
 1432
 1433    _ = view.update(cx, |view, cx| {
 1434        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1435        assert_eq!(view.display_text(cx), "\n");
 1436        assert_eq!(
 1437            view.selections.display_ranges(cx),
 1438            &[
 1439                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1440                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1441            ]
 1442        );
 1443    });
 1444}
 1445
 1446#[gpui::test]
 1447fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1448    init_test(cx, |_| {});
 1449    let move_to_beg = MoveToBeginningOfLine {
 1450        stop_at_soft_wraps: false,
 1451    };
 1452
 1453    let move_to_end = MoveToEndOfLine {
 1454        stop_at_soft_wraps: false,
 1455    };
 1456
 1457    let view = cx.add_window(|cx| {
 1458        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1459        build_editor(buffer, cx)
 1460    });
 1461
 1462    _ = view.update(cx, |view, cx| {
 1463        view.set_wrap_width(Some(140.0.into()), cx);
 1464
 1465        // We expect the following lines after wrapping
 1466        // ```
 1467        // thequickbrownfox
 1468        // jumpedoverthelazydo
 1469        // gs
 1470        // ```
 1471        // The final `gs` was soft-wrapped onto a new line.
 1472        assert_eq!(
 1473            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1474            view.display_text(cx),
 1475        );
 1476
 1477        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1478        // Start the cursor at the `k` on the first line
 1479        view.change_selections(None, cx, |s| {
 1480            s.select_display_ranges([
 1481                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1482            ]);
 1483        });
 1484
 1485        // Moving to the beginning of the line should put us at the beginning of the line.
 1486        view.move_to_beginning_of_line(&move_to_beg, cx);
 1487        assert_eq!(
 1488            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1489            view.selections.display_ranges(cx)
 1490        );
 1491
 1492        // Moving to the end of the line should put us at the end of the line.
 1493        view.move_to_end_of_line(&move_to_end, cx);
 1494        assert_eq!(
 1495            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1496            view.selections.display_ranges(cx)
 1497        );
 1498
 1499        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1500        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1501        view.change_selections(None, cx, |s| {
 1502            s.select_display_ranges([
 1503                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1504            ]);
 1505        });
 1506
 1507        // Moving to the beginning of the line should put us at the start of the second line of
 1508        // display text, i.e., the `j`.
 1509        view.move_to_beginning_of_line(&move_to_beg, cx);
 1510        assert_eq!(
 1511            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1512            view.selections.display_ranges(cx)
 1513        );
 1514
 1515        // Moving to the beginning of the line again should be a no-op.
 1516        view.move_to_beginning_of_line(&move_to_beg, cx);
 1517        assert_eq!(
 1518            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1519            view.selections.display_ranges(cx)
 1520        );
 1521
 1522        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1523        // next display line.
 1524        view.move_to_end_of_line(&move_to_end, cx);
 1525        assert_eq!(
 1526            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1527            view.selections.display_ranges(cx)
 1528        );
 1529
 1530        // Moving to the end of the line again should be a no-op.
 1531        view.move_to_end_of_line(&move_to_end, cx);
 1532        assert_eq!(
 1533            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1534            view.selections.display_ranges(cx)
 1535        );
 1536    });
 1537}
 1538
 1539#[gpui::test]
 1540fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1541    init_test(cx, |_| {});
 1542
 1543    let view = cx.add_window(|cx| {
 1544        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1545        build_editor(buffer, cx)
 1546    });
 1547    _ = view.update(cx, |view, cx| {
 1548        view.change_selections(None, cx, |s| {
 1549            s.select_display_ranges([
 1550                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1551                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1552            ])
 1553        });
 1554
 1555        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1556        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1557
 1558        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1559        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1560
 1561        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1562        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1563
 1564        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1565        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1566
 1567        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1568        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1569
 1570        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1571        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1572
 1573        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1574        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1575
 1576        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1577        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1578
 1579        view.move_right(&MoveRight, cx);
 1580        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1581        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1582
 1583        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1584        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1585
 1586        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1587        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1588    });
 1589}
 1590
 1591#[gpui::test]
 1592fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1593    init_test(cx, |_| {});
 1594
 1595    let view = cx.add_window(|cx| {
 1596        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1597        build_editor(buffer, cx)
 1598    });
 1599
 1600    _ = view.update(cx, |view, cx| {
 1601        view.set_wrap_width(Some(140.0.into()), cx);
 1602        assert_eq!(
 1603            view.display_text(cx),
 1604            "use one::{\n    two::three::\n    four::five\n};"
 1605        );
 1606
 1607        view.change_selections(None, cx, |s| {
 1608            s.select_display_ranges([
 1609                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1610            ]);
 1611        });
 1612
 1613        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1614        assert_eq!(
 1615            view.selections.display_ranges(cx),
 1616            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1617        );
 1618
 1619        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1620        assert_eq!(
 1621            view.selections.display_ranges(cx),
 1622            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1623        );
 1624
 1625        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1626        assert_eq!(
 1627            view.selections.display_ranges(cx),
 1628            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1629        );
 1630
 1631        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1632        assert_eq!(
 1633            view.selections.display_ranges(cx),
 1634            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1635        );
 1636
 1637        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1638        assert_eq!(
 1639            view.selections.display_ranges(cx),
 1640            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1641        );
 1642
 1643        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1644        assert_eq!(
 1645            view.selections.display_ranges(cx),
 1646            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1647        );
 1648    });
 1649}
 1650
 1651#[gpui::test]
 1652async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1653    init_test(cx, |_| {});
 1654    let mut cx = EditorTestContext::new(cx).await;
 1655
 1656    let line_height = cx.editor(|editor, cx| {
 1657        editor
 1658            .style()
 1659            .unwrap()
 1660            .text
 1661            .line_height_in_pixels(cx.rem_size())
 1662    });
 1663    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1664
 1665    cx.set_state(
 1666        &r#"ˇone
 1667        two
 1668
 1669        three
 1670        fourˇ
 1671        five
 1672
 1673        six"#
 1674            .unindent(),
 1675    );
 1676
 1677    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1678    cx.assert_editor_state(
 1679        &r#"one
 1680        two
 1681        ˇ
 1682        three
 1683        four
 1684        five
 1685        ˇ
 1686        six"#
 1687            .unindent(),
 1688    );
 1689
 1690    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1691    cx.assert_editor_state(
 1692        &r#"one
 1693        two
 1694
 1695        three
 1696        four
 1697        five
 1698        ˇ
 1699        sixˇ"#
 1700            .unindent(),
 1701    );
 1702
 1703    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1704    cx.assert_editor_state(
 1705        &r#"one
 1706        two
 1707
 1708        three
 1709        four
 1710        five
 1711
 1712        sixˇ"#
 1713            .unindent(),
 1714    );
 1715
 1716    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1717    cx.assert_editor_state(
 1718        &r#"one
 1719        two
 1720
 1721        three
 1722        four
 1723        five
 1724        ˇ
 1725        six"#
 1726            .unindent(),
 1727    );
 1728
 1729    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1730    cx.assert_editor_state(
 1731        &r#"one
 1732        two
 1733        ˇ
 1734        three
 1735        four
 1736        five
 1737
 1738        six"#
 1739            .unindent(),
 1740    );
 1741
 1742    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1743    cx.assert_editor_state(
 1744        &r#"ˇone
 1745        two
 1746
 1747        three
 1748        four
 1749        five
 1750
 1751        six"#
 1752            .unindent(),
 1753    );
 1754}
 1755
 1756#[gpui::test]
 1757async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1758    init_test(cx, |_| {});
 1759    let mut cx = EditorTestContext::new(cx).await;
 1760    let line_height = cx.editor(|editor, cx| {
 1761        editor
 1762            .style()
 1763            .unwrap()
 1764            .text
 1765            .line_height_in_pixels(cx.rem_size())
 1766    });
 1767    let window = cx.window;
 1768    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1769
 1770    cx.set_state(
 1771        &r#"ˇone
 1772        two
 1773        three
 1774        four
 1775        five
 1776        six
 1777        seven
 1778        eight
 1779        nine
 1780        ten
 1781        "#,
 1782    );
 1783
 1784    cx.update_editor(|editor, cx| {
 1785        assert_eq!(
 1786            editor.snapshot(cx).scroll_position(),
 1787            gpui::Point::new(0., 0.)
 1788        );
 1789        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1790        assert_eq!(
 1791            editor.snapshot(cx).scroll_position(),
 1792            gpui::Point::new(0., 3.)
 1793        );
 1794        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1795        assert_eq!(
 1796            editor.snapshot(cx).scroll_position(),
 1797            gpui::Point::new(0., 6.)
 1798        );
 1799        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1800        assert_eq!(
 1801            editor.snapshot(cx).scroll_position(),
 1802            gpui::Point::new(0., 3.)
 1803        );
 1804
 1805        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1806        assert_eq!(
 1807            editor.snapshot(cx).scroll_position(),
 1808            gpui::Point::new(0., 1.)
 1809        );
 1810        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1811        assert_eq!(
 1812            editor.snapshot(cx).scroll_position(),
 1813            gpui::Point::new(0., 3.)
 1814        );
 1815    });
 1816}
 1817
 1818#[gpui::test]
 1819async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1820    init_test(cx, |_| {});
 1821    let mut cx = EditorTestContext::new(cx).await;
 1822
 1823    let line_height = cx.update_editor(|editor, cx| {
 1824        editor.set_vertical_scroll_margin(2, cx);
 1825        editor
 1826            .style()
 1827            .unwrap()
 1828            .text
 1829            .line_height_in_pixels(cx.rem_size())
 1830    });
 1831    let window = cx.window;
 1832    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1833
 1834    cx.set_state(
 1835        &r#"ˇone
 1836            two
 1837            three
 1838            four
 1839            five
 1840            six
 1841            seven
 1842            eight
 1843            nine
 1844            ten
 1845        "#,
 1846    );
 1847    cx.update_editor(|editor, cx| {
 1848        assert_eq!(
 1849            editor.snapshot(cx).scroll_position(),
 1850            gpui::Point::new(0., 0.0)
 1851        );
 1852    });
 1853
 1854    // Add a cursor below the visible area. Since both cursors cannot fit
 1855    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1856    // allows the vertical scroll margin below that cursor.
 1857    cx.update_editor(|editor, cx| {
 1858        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1859            selections.select_ranges([
 1860                Point::new(0, 0)..Point::new(0, 0),
 1861                Point::new(6, 0)..Point::new(6, 0),
 1862            ]);
 1863        })
 1864    });
 1865    cx.update_editor(|editor, cx| {
 1866        assert_eq!(
 1867            editor.snapshot(cx).scroll_position(),
 1868            gpui::Point::new(0., 3.0)
 1869        );
 1870    });
 1871
 1872    // Move down. The editor cursor scrolls down to track the newest cursor.
 1873    cx.update_editor(|editor, cx| {
 1874        editor.move_down(&Default::default(), cx);
 1875    });
 1876    cx.update_editor(|editor, cx| {
 1877        assert_eq!(
 1878            editor.snapshot(cx).scroll_position(),
 1879            gpui::Point::new(0., 4.0)
 1880        );
 1881    });
 1882
 1883    // Add a cursor above the visible area. Since both cursors fit on screen,
 1884    // the editor scrolls to show both.
 1885    cx.update_editor(|editor, cx| {
 1886        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1887            selections.select_ranges([
 1888                Point::new(1, 0)..Point::new(1, 0),
 1889                Point::new(6, 0)..Point::new(6, 0),
 1890            ]);
 1891        })
 1892    });
 1893    cx.update_editor(|editor, cx| {
 1894        assert_eq!(
 1895            editor.snapshot(cx).scroll_position(),
 1896            gpui::Point::new(0., 1.0)
 1897        );
 1898    });
 1899}
 1900
 1901#[gpui::test]
 1902async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1903    init_test(cx, |_| {});
 1904    let mut cx = EditorTestContext::new(cx).await;
 1905
 1906    let line_height = cx.editor(|editor, cx| {
 1907        editor
 1908            .style()
 1909            .unwrap()
 1910            .text
 1911            .line_height_in_pixels(cx.rem_size())
 1912    });
 1913    let window = cx.window;
 1914    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1915    cx.set_state(
 1916        &r#"
 1917        ˇone
 1918        two
 1919        threeˇ
 1920        four
 1921        five
 1922        six
 1923        seven
 1924        eight
 1925        nine
 1926        ten
 1927        "#
 1928        .unindent(),
 1929    );
 1930
 1931    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1932    cx.assert_editor_state(
 1933        &r#"
 1934        one
 1935        two
 1936        three
 1937        ˇfour
 1938        five
 1939        sixˇ
 1940        seven
 1941        eight
 1942        nine
 1943        ten
 1944        "#
 1945        .unindent(),
 1946    );
 1947
 1948    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1949    cx.assert_editor_state(
 1950        &r#"
 1951        one
 1952        two
 1953        three
 1954        four
 1955        five
 1956        six
 1957        ˇseven
 1958        eight
 1959        nineˇ
 1960        ten
 1961        "#
 1962        .unindent(),
 1963    );
 1964
 1965    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 1966    cx.assert_editor_state(
 1967        &r#"
 1968        one
 1969        two
 1970        three
 1971        ˇfour
 1972        five
 1973        sixˇ
 1974        seven
 1975        eight
 1976        nine
 1977        ten
 1978        "#
 1979        .unindent(),
 1980    );
 1981
 1982    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 1983    cx.assert_editor_state(
 1984        &r#"
 1985        ˇone
 1986        two
 1987        threeˇ
 1988        four
 1989        five
 1990        six
 1991        seven
 1992        eight
 1993        nine
 1994        ten
 1995        "#
 1996        .unindent(),
 1997    );
 1998
 1999    // Test select collapsing
 2000    cx.update_editor(|editor, cx| {
 2001        editor.move_page_down(&MovePageDown::default(), cx);
 2002        editor.move_page_down(&MovePageDown::default(), cx);
 2003        editor.move_page_down(&MovePageDown::default(), cx);
 2004    });
 2005    cx.assert_editor_state(
 2006        &r#"
 2007        one
 2008        two
 2009        three
 2010        four
 2011        five
 2012        six
 2013        seven
 2014        eight
 2015        nine
 2016        ˇten
 2017        ˇ"#
 2018        .unindent(),
 2019    );
 2020}
 2021
 2022#[gpui::test]
 2023async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2024    init_test(cx, |_| {});
 2025    let mut cx = EditorTestContext::new(cx).await;
 2026    cx.set_state("one «two threeˇ» four");
 2027    cx.update_editor(|editor, cx| {
 2028        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2029        assert_eq!(editor.text(cx), " four");
 2030    });
 2031}
 2032
 2033#[gpui::test]
 2034fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2035    init_test(cx, |_| {});
 2036
 2037    let view = cx.add_window(|cx| {
 2038        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2039        build_editor(buffer.clone(), cx)
 2040    });
 2041
 2042    _ = view.update(cx, |view, cx| {
 2043        view.change_selections(None, cx, |s| {
 2044            s.select_display_ranges([
 2045                // an empty selection - the preceding word fragment is deleted
 2046                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2047                // characters selected - they are deleted
 2048                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2049            ])
 2050        });
 2051        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
 2052        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2053    });
 2054
 2055    _ = view.update(cx, |view, cx| {
 2056        view.change_selections(None, cx, |s| {
 2057            s.select_display_ranges([
 2058                // an empty selection - the following word fragment is deleted
 2059                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2060                // characters selected - they are deleted
 2061                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2062            ])
 2063        });
 2064        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
 2065        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2066    });
 2067}
 2068
 2069#[gpui::test]
 2070fn test_newline(cx: &mut TestAppContext) {
 2071    init_test(cx, |_| {});
 2072
 2073    let view = cx.add_window(|cx| {
 2074        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2075        build_editor(buffer.clone(), cx)
 2076    });
 2077
 2078    _ = view.update(cx, |view, cx| {
 2079        view.change_selections(None, cx, |s| {
 2080            s.select_display_ranges([
 2081                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2082                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2083                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2084            ])
 2085        });
 2086
 2087        view.newline(&Newline, cx);
 2088        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2089    });
 2090}
 2091
 2092#[gpui::test]
 2093fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2094    init_test(cx, |_| {});
 2095
 2096    let editor = cx.add_window(|cx| {
 2097        let buffer = MultiBuffer::build_simple(
 2098            "
 2099                a
 2100                b(
 2101                    X
 2102                )
 2103                c(
 2104                    X
 2105                )
 2106            "
 2107            .unindent()
 2108            .as_str(),
 2109            cx,
 2110        );
 2111        let mut editor = build_editor(buffer.clone(), cx);
 2112        editor.change_selections(None, cx, |s| {
 2113            s.select_ranges([
 2114                Point::new(2, 4)..Point::new(2, 5),
 2115                Point::new(5, 4)..Point::new(5, 5),
 2116            ])
 2117        });
 2118        editor
 2119    });
 2120
 2121    _ = editor.update(cx, |editor, cx| {
 2122        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2123        editor.buffer.update(cx, |buffer, cx| {
 2124            buffer.edit(
 2125                [
 2126                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2127                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2128                ],
 2129                None,
 2130                cx,
 2131            );
 2132            assert_eq!(
 2133                buffer.read(cx).text(),
 2134                "
 2135                    a
 2136                    b()
 2137                    c()
 2138                "
 2139                .unindent()
 2140            );
 2141        });
 2142        assert_eq!(
 2143            editor.selections.ranges(cx),
 2144            &[
 2145                Point::new(1, 2)..Point::new(1, 2),
 2146                Point::new(2, 2)..Point::new(2, 2),
 2147            ],
 2148        );
 2149
 2150        editor.newline(&Newline, cx);
 2151        assert_eq!(
 2152            editor.text(cx),
 2153            "
 2154                a
 2155                b(
 2156                )
 2157                c(
 2158                )
 2159            "
 2160            .unindent()
 2161        );
 2162
 2163        // The selections are moved after the inserted newlines
 2164        assert_eq!(
 2165            editor.selections.ranges(cx),
 2166            &[
 2167                Point::new(2, 0)..Point::new(2, 0),
 2168                Point::new(4, 0)..Point::new(4, 0),
 2169            ],
 2170        );
 2171    });
 2172}
 2173
 2174#[gpui::test]
 2175async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2176    init_test(cx, |settings| {
 2177        settings.defaults.tab_size = NonZeroU32::new(4)
 2178    });
 2179
 2180    let language = Arc::new(
 2181        Language::new(
 2182            LanguageConfig::default(),
 2183            Some(tree_sitter_rust::language()),
 2184        )
 2185        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2186        .unwrap(),
 2187    );
 2188
 2189    let mut cx = EditorTestContext::new(cx).await;
 2190    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2191    cx.set_state(indoc! {"
 2192        const a: ˇA = (
 2193 2194                «const_functionˇ»(ˇ),
 2195                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2196 2197        ˇ);ˇ
 2198    "});
 2199
 2200    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2201    cx.assert_editor_state(indoc! {"
 2202        ˇ
 2203        const a: A = (
 2204            ˇ
 2205            (
 2206                ˇ
 2207                ˇ
 2208                const_function(),
 2209                ˇ
 2210                ˇ
 2211                ˇ
 2212                ˇ
 2213                something_else,
 2214                ˇ
 2215            )
 2216            ˇ
 2217            ˇ
 2218        );
 2219    "});
 2220}
 2221
 2222#[gpui::test]
 2223async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2224    init_test(cx, |settings| {
 2225        settings.defaults.tab_size = NonZeroU32::new(4)
 2226    });
 2227
 2228    let language = Arc::new(
 2229        Language::new(
 2230            LanguageConfig::default(),
 2231            Some(tree_sitter_rust::language()),
 2232        )
 2233        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2234        .unwrap(),
 2235    );
 2236
 2237    let mut cx = EditorTestContext::new(cx).await;
 2238    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2239    cx.set_state(indoc! {"
 2240        const a: ˇA = (
 2241 2242                «const_functionˇ»(ˇ),
 2243                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2244 2245        ˇ);ˇ
 2246    "});
 2247
 2248    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2249    cx.assert_editor_state(indoc! {"
 2250        const a: A = (
 2251            ˇ
 2252            (
 2253                ˇ
 2254                const_function(),
 2255                ˇ
 2256                ˇ
 2257                something_else,
 2258                ˇ
 2259                ˇ
 2260                ˇ
 2261                ˇ
 2262            )
 2263            ˇ
 2264        );
 2265        ˇ
 2266        ˇ
 2267    "});
 2268}
 2269
 2270#[gpui::test]
 2271async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2272    init_test(cx, |settings| {
 2273        settings.defaults.tab_size = NonZeroU32::new(4)
 2274    });
 2275
 2276    let language = Arc::new(Language::new(
 2277        LanguageConfig {
 2278            line_comments: vec!["//".into()],
 2279            ..LanguageConfig::default()
 2280        },
 2281        None,
 2282    ));
 2283    {
 2284        let mut cx = EditorTestContext::new(cx).await;
 2285        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2286        cx.set_state(indoc! {"
 2287        // Fooˇ
 2288    "});
 2289
 2290        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2291        cx.assert_editor_state(indoc! {"
 2292        // Foo
 2293        //ˇ
 2294    "});
 2295        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2296        cx.set_state(indoc! {"
 2297        ˇ// Foo
 2298    "});
 2299        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2300        cx.assert_editor_state(indoc! {"
 2301
 2302        ˇ// Foo
 2303    "});
 2304    }
 2305    // Ensure that comment continuations can be disabled.
 2306    update_test_language_settings(cx, |settings| {
 2307        settings.defaults.extend_comment_on_newline = Some(false);
 2308    });
 2309    let mut cx = EditorTestContext::new(cx).await;
 2310    cx.set_state(indoc! {"
 2311        // Fooˇ
 2312    "});
 2313    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2314    cx.assert_editor_state(indoc! {"
 2315        // Foo
 2316        ˇ
 2317    "});
 2318}
 2319
 2320#[gpui::test]
 2321fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2322    init_test(cx, |_| {});
 2323
 2324    let editor = cx.add_window(|cx| {
 2325        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2326        let mut editor = build_editor(buffer.clone(), cx);
 2327        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2328        editor
 2329    });
 2330
 2331    _ = editor.update(cx, |editor, cx| {
 2332        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2333        editor.buffer.update(cx, |buffer, cx| {
 2334            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2335            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2336        });
 2337        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2338
 2339        editor.insert("Z", cx);
 2340        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2341
 2342        // The selections are moved after the inserted characters
 2343        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2344    });
 2345}
 2346
 2347#[gpui::test]
 2348async fn test_tab(cx: &mut gpui::TestAppContext) {
 2349    init_test(cx, |settings| {
 2350        settings.defaults.tab_size = NonZeroU32::new(3)
 2351    });
 2352
 2353    let mut cx = EditorTestContext::new(cx).await;
 2354    cx.set_state(indoc! {"
 2355        ˇabˇc
 2356        ˇ🏀ˇ🏀ˇefg
 2357 2358    "});
 2359    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2360    cx.assert_editor_state(indoc! {"
 2361           ˇab ˇc
 2362           ˇ🏀  ˇ🏀  ˇefg
 2363        d  ˇ
 2364    "});
 2365
 2366    cx.set_state(indoc! {"
 2367        a
 2368        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2369    "});
 2370    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2371    cx.assert_editor_state(indoc! {"
 2372        a
 2373           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2374    "});
 2375}
 2376
 2377#[gpui::test]
 2378async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2379    init_test(cx, |_| {});
 2380
 2381    let mut cx = EditorTestContext::new(cx).await;
 2382    let language = Arc::new(
 2383        Language::new(
 2384            LanguageConfig::default(),
 2385            Some(tree_sitter_rust::language()),
 2386        )
 2387        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2388        .unwrap(),
 2389    );
 2390    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2391
 2392    // cursors that are already at the suggested indent level insert
 2393    // a soft tab. cursors that are to the left of the suggested indent
 2394    // auto-indent their line.
 2395    cx.set_state(indoc! {"
 2396        ˇ
 2397        const a: B = (
 2398            c(
 2399                d(
 2400        ˇ
 2401                )
 2402        ˇ
 2403        ˇ    )
 2404        );
 2405    "});
 2406    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2407    cx.assert_editor_state(indoc! {"
 2408            ˇ
 2409        const a: B = (
 2410            c(
 2411                d(
 2412                    ˇ
 2413                )
 2414                ˇ
 2415            ˇ)
 2416        );
 2417    "});
 2418
 2419    // handle auto-indent when there are multiple cursors on the same line
 2420    cx.set_state(indoc! {"
 2421        const a: B = (
 2422            c(
 2423        ˇ    ˇ
 2424        ˇ    )
 2425        );
 2426    "});
 2427    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2428    cx.assert_editor_state(indoc! {"
 2429        const a: B = (
 2430            c(
 2431                ˇ
 2432            ˇ)
 2433        );
 2434    "});
 2435}
 2436
 2437#[gpui::test]
 2438async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2439    init_test(cx, |settings| {
 2440        settings.defaults.tab_size = NonZeroU32::new(4)
 2441    });
 2442
 2443    let language = Arc::new(
 2444        Language::new(
 2445            LanguageConfig::default(),
 2446            Some(tree_sitter_rust::language()),
 2447        )
 2448        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2449        .unwrap(),
 2450    );
 2451
 2452    let mut cx = EditorTestContext::new(cx).await;
 2453    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2454    cx.set_state(indoc! {"
 2455        fn a() {
 2456            if b {
 2457        \t ˇc
 2458            }
 2459        }
 2460    "});
 2461
 2462    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2463    cx.assert_editor_state(indoc! {"
 2464        fn a() {
 2465            if b {
 2466                ˇc
 2467            }
 2468        }
 2469    "});
 2470}
 2471
 2472#[gpui::test]
 2473async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2474    init_test(cx, |settings| {
 2475        settings.defaults.tab_size = NonZeroU32::new(4);
 2476    });
 2477
 2478    let mut cx = EditorTestContext::new(cx).await;
 2479
 2480    cx.set_state(indoc! {"
 2481          «oneˇ» «twoˇ»
 2482        three
 2483         four
 2484    "});
 2485    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2486    cx.assert_editor_state(indoc! {"
 2487            «oneˇ» «twoˇ»
 2488        three
 2489         four
 2490    "});
 2491
 2492    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2493    cx.assert_editor_state(indoc! {"
 2494        «oneˇ» «twoˇ»
 2495        three
 2496         four
 2497    "});
 2498
 2499    // select across line ending
 2500    cx.set_state(indoc! {"
 2501        one two
 2502        t«hree
 2503        ˇ» four
 2504    "});
 2505    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2506    cx.assert_editor_state(indoc! {"
 2507        one two
 2508            t«hree
 2509        ˇ» four
 2510    "});
 2511
 2512    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2513    cx.assert_editor_state(indoc! {"
 2514        one two
 2515        t«hree
 2516        ˇ» four
 2517    "});
 2518
 2519    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2520    cx.set_state(indoc! {"
 2521        one two
 2522        ˇthree
 2523            four
 2524    "});
 2525    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2526    cx.assert_editor_state(indoc! {"
 2527        one two
 2528            ˇthree
 2529            four
 2530    "});
 2531
 2532    cx.set_state(indoc! {"
 2533        one two
 2534        ˇ    three
 2535            four
 2536    "});
 2537    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2538    cx.assert_editor_state(indoc! {"
 2539        one two
 2540        ˇthree
 2541            four
 2542    "});
 2543}
 2544
 2545#[gpui::test]
 2546async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2547    init_test(cx, |settings| {
 2548        settings.defaults.hard_tabs = Some(true);
 2549    });
 2550
 2551    let mut cx = EditorTestContext::new(cx).await;
 2552
 2553    // select two ranges on one line
 2554    cx.set_state(indoc! {"
 2555        «oneˇ» «twoˇ»
 2556        three
 2557        four
 2558    "});
 2559    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2560    cx.assert_editor_state(indoc! {"
 2561        \t«oneˇ» «twoˇ»
 2562        three
 2563        four
 2564    "});
 2565    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2566    cx.assert_editor_state(indoc! {"
 2567        \t\t«oneˇ» «twoˇ»
 2568        three
 2569        four
 2570    "});
 2571    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2572    cx.assert_editor_state(indoc! {"
 2573        \t«oneˇ» «twoˇ»
 2574        three
 2575        four
 2576    "});
 2577    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2578    cx.assert_editor_state(indoc! {"
 2579        «oneˇ» «twoˇ»
 2580        three
 2581        four
 2582    "});
 2583
 2584    // select across a line ending
 2585    cx.set_state(indoc! {"
 2586        one two
 2587        t«hree
 2588        ˇ»four
 2589    "});
 2590    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2591    cx.assert_editor_state(indoc! {"
 2592        one two
 2593        \tt«hree
 2594        ˇ»four
 2595    "});
 2596    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2597    cx.assert_editor_state(indoc! {"
 2598        one two
 2599        \t\tt«hree
 2600        ˇ»four
 2601    "});
 2602    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2603    cx.assert_editor_state(indoc! {"
 2604        one two
 2605        \tt«hree
 2606        ˇ»four
 2607    "});
 2608    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2609    cx.assert_editor_state(indoc! {"
 2610        one two
 2611        t«hree
 2612        ˇ»four
 2613    "});
 2614
 2615    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2616    cx.set_state(indoc! {"
 2617        one two
 2618        ˇthree
 2619        four
 2620    "});
 2621    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2622    cx.assert_editor_state(indoc! {"
 2623        one two
 2624        ˇthree
 2625        four
 2626    "});
 2627    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2628    cx.assert_editor_state(indoc! {"
 2629        one two
 2630        \tˇthree
 2631        four
 2632    "});
 2633    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2634    cx.assert_editor_state(indoc! {"
 2635        one two
 2636        ˇthree
 2637        four
 2638    "});
 2639}
 2640
 2641#[gpui::test]
 2642fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2643    init_test(cx, |settings| {
 2644        settings.languages.extend([
 2645            (
 2646                "TOML".into(),
 2647                LanguageSettingsContent {
 2648                    tab_size: NonZeroU32::new(2),
 2649                    ..Default::default()
 2650                },
 2651            ),
 2652            (
 2653                "Rust".into(),
 2654                LanguageSettingsContent {
 2655                    tab_size: NonZeroU32::new(4),
 2656                    ..Default::default()
 2657                },
 2658            ),
 2659        ]);
 2660    });
 2661
 2662    let toml_language = Arc::new(Language::new(
 2663        LanguageConfig {
 2664            name: "TOML".into(),
 2665            ..Default::default()
 2666        },
 2667        None,
 2668    ));
 2669    let rust_language = Arc::new(Language::new(
 2670        LanguageConfig {
 2671            name: "Rust".into(),
 2672            ..Default::default()
 2673        },
 2674        None,
 2675    ));
 2676
 2677    let toml_buffer =
 2678        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2679    let rust_buffer = cx.new_model(|cx| {
 2680        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2681    });
 2682    let multibuffer = cx.new_model(|cx| {
 2683        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 2684        multibuffer.push_excerpts(
 2685            toml_buffer.clone(),
 2686            [ExcerptRange {
 2687                context: Point::new(0, 0)..Point::new(2, 0),
 2688                primary: None,
 2689            }],
 2690            cx,
 2691        );
 2692        multibuffer.push_excerpts(
 2693            rust_buffer.clone(),
 2694            [ExcerptRange {
 2695                context: Point::new(0, 0)..Point::new(1, 0),
 2696                primary: None,
 2697            }],
 2698            cx,
 2699        );
 2700        multibuffer
 2701    });
 2702
 2703    cx.add_window(|cx| {
 2704        let mut editor = build_editor(multibuffer, cx);
 2705
 2706        assert_eq!(
 2707            editor.text(cx),
 2708            indoc! {"
 2709                a = 1
 2710                b = 2
 2711
 2712                const c: usize = 3;
 2713            "}
 2714        );
 2715
 2716        select_ranges(
 2717            &mut editor,
 2718            indoc! {"
 2719                «aˇ» = 1
 2720                b = 2
 2721
 2722                «const c:ˇ» usize = 3;
 2723            "},
 2724            cx,
 2725        );
 2726
 2727        editor.tab(&Tab, cx);
 2728        assert_text_with_selections(
 2729            &mut editor,
 2730            indoc! {"
 2731                  «aˇ» = 1
 2732                b = 2
 2733
 2734                    «const c:ˇ» usize = 3;
 2735            "},
 2736            cx,
 2737        );
 2738        editor.tab_prev(&TabPrev, cx);
 2739        assert_text_with_selections(
 2740            &mut editor,
 2741            indoc! {"
 2742                «aˇ» = 1
 2743                b = 2
 2744
 2745                «const c:ˇ» usize = 3;
 2746            "},
 2747            cx,
 2748        );
 2749
 2750        editor
 2751    });
 2752}
 2753
 2754#[gpui::test]
 2755async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2756    init_test(cx, |_| {});
 2757
 2758    let mut cx = EditorTestContext::new(cx).await;
 2759
 2760    // Basic backspace
 2761    cx.set_state(indoc! {"
 2762        onˇe two three
 2763        fou«rˇ» five six
 2764        seven «ˇeight nine
 2765        »ten
 2766    "});
 2767    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2768    cx.assert_editor_state(indoc! {"
 2769        oˇe two three
 2770        fouˇ five six
 2771        seven ˇten
 2772    "});
 2773
 2774    // Test backspace inside and around indents
 2775    cx.set_state(indoc! {"
 2776        zero
 2777            ˇone
 2778                ˇtwo
 2779            ˇ ˇ ˇ  three
 2780        ˇ  ˇ  four
 2781    "});
 2782    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2783    cx.assert_editor_state(indoc! {"
 2784        zero
 2785        ˇone
 2786            ˇtwo
 2787        ˇ  threeˇ  four
 2788    "});
 2789
 2790    // Test backspace with line_mode set to true
 2791    cx.update_editor(|e, _| e.selections.line_mode = true);
 2792    cx.set_state(indoc! {"
 2793        The ˇquick ˇbrown
 2794        fox jumps over
 2795        the lazy dog
 2796        ˇThe qu«ick bˇ»rown"});
 2797    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2798    cx.assert_editor_state(indoc! {"
 2799        ˇfox jumps over
 2800        the lazy dogˇ"});
 2801}
 2802
 2803#[gpui::test]
 2804async fn test_delete(cx: &mut gpui::TestAppContext) {
 2805    init_test(cx, |_| {});
 2806
 2807    let mut cx = EditorTestContext::new(cx).await;
 2808    cx.set_state(indoc! {"
 2809        onˇe two three
 2810        fou«rˇ» five six
 2811        seven «ˇeight nine
 2812        »ten
 2813    "});
 2814    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2815    cx.assert_editor_state(indoc! {"
 2816        onˇ two three
 2817        fouˇ five six
 2818        seven ˇten
 2819    "});
 2820
 2821    // Test backspace with line_mode set to true
 2822    cx.update_editor(|e, _| e.selections.line_mode = true);
 2823    cx.set_state(indoc! {"
 2824        The ˇquick ˇbrown
 2825        fox «ˇjum»ps over
 2826        the lazy dog
 2827        ˇThe qu«ick bˇ»rown"});
 2828    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2829    cx.assert_editor_state("ˇthe lazy dogˇ");
 2830}
 2831
 2832#[gpui::test]
 2833fn test_delete_line(cx: &mut TestAppContext) {
 2834    init_test(cx, |_| {});
 2835
 2836    let view = cx.add_window(|cx| {
 2837        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2838        build_editor(buffer, cx)
 2839    });
 2840    _ = view.update(cx, |view, cx| {
 2841        view.change_selections(None, cx, |s| {
 2842            s.select_display_ranges([
 2843                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2844                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2845                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2846            ])
 2847        });
 2848        view.delete_line(&DeleteLine, cx);
 2849        assert_eq!(view.display_text(cx), "ghi");
 2850        assert_eq!(
 2851            view.selections.display_ranges(cx),
 2852            vec![
 2853                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2854                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2855            ]
 2856        );
 2857    });
 2858
 2859    let view = cx.add_window(|cx| {
 2860        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2861        build_editor(buffer, cx)
 2862    });
 2863    _ = view.update(cx, |view, cx| {
 2864        view.change_selections(None, cx, |s| {
 2865            s.select_display_ranges([
 2866                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 2867            ])
 2868        });
 2869        view.delete_line(&DeleteLine, cx);
 2870        assert_eq!(view.display_text(cx), "ghi\n");
 2871        assert_eq!(
 2872            view.selections.display_ranges(cx),
 2873            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 2874        );
 2875    });
 2876}
 2877
 2878#[gpui::test]
 2879fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 2880    init_test(cx, |_| {});
 2881
 2882    cx.add_window(|cx| {
 2883        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 2884        let mut editor = build_editor(buffer.clone(), cx);
 2885        let buffer = buffer.read(cx).as_singleton().unwrap();
 2886
 2887        assert_eq!(
 2888            editor.selections.ranges::<Point>(cx),
 2889            &[Point::new(0, 0)..Point::new(0, 0)]
 2890        );
 2891
 2892        // When on single line, replace newline at end by space
 2893        editor.join_lines(&JoinLines, cx);
 2894        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2895        assert_eq!(
 2896            editor.selections.ranges::<Point>(cx),
 2897            &[Point::new(0, 3)..Point::new(0, 3)]
 2898        );
 2899
 2900        // When multiple lines are selected, remove newlines that are spanned by the selection
 2901        editor.change_selections(None, cx, |s| {
 2902            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 2903        });
 2904        editor.join_lines(&JoinLines, cx);
 2905        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 2906        assert_eq!(
 2907            editor.selections.ranges::<Point>(cx),
 2908            &[Point::new(0, 11)..Point::new(0, 11)]
 2909        );
 2910
 2911        // Undo should be transactional
 2912        editor.undo(&Undo, cx);
 2913        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2914        assert_eq!(
 2915            editor.selections.ranges::<Point>(cx),
 2916            &[Point::new(0, 5)..Point::new(2, 2)]
 2917        );
 2918
 2919        // When joining an empty line don't insert a space
 2920        editor.change_selections(None, cx, |s| {
 2921            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 2922        });
 2923        editor.join_lines(&JoinLines, cx);
 2924        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 2925        assert_eq!(
 2926            editor.selections.ranges::<Point>(cx),
 2927            [Point::new(2, 3)..Point::new(2, 3)]
 2928        );
 2929
 2930        // We can remove trailing newlines
 2931        editor.join_lines(&JoinLines, cx);
 2932        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2933        assert_eq!(
 2934            editor.selections.ranges::<Point>(cx),
 2935            [Point::new(2, 3)..Point::new(2, 3)]
 2936        );
 2937
 2938        // We don't blow up on the last line
 2939        editor.join_lines(&JoinLines, cx);
 2940        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2941        assert_eq!(
 2942            editor.selections.ranges::<Point>(cx),
 2943            [Point::new(2, 3)..Point::new(2, 3)]
 2944        );
 2945
 2946        // reset to test indentation
 2947        editor.buffer.update(cx, |buffer, cx| {
 2948            buffer.edit(
 2949                [
 2950                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 2951                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 2952                ],
 2953                None,
 2954                cx,
 2955            )
 2956        });
 2957
 2958        // We remove any leading spaces
 2959        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 2960        editor.change_selections(None, cx, |s| {
 2961            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 2962        });
 2963        editor.join_lines(&JoinLines, cx);
 2964        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 2965
 2966        // We don't insert a space for a line containing only spaces
 2967        editor.join_lines(&JoinLines, cx);
 2968        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 2969
 2970        // We ignore any leading tabs
 2971        editor.join_lines(&JoinLines, cx);
 2972        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 2973
 2974        editor
 2975    });
 2976}
 2977
 2978#[gpui::test]
 2979fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 2980    init_test(cx, |_| {});
 2981
 2982    cx.add_window(|cx| {
 2983        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 2984        let mut editor = build_editor(buffer.clone(), cx);
 2985        let buffer = buffer.read(cx).as_singleton().unwrap();
 2986
 2987        editor.change_selections(None, cx, |s| {
 2988            s.select_ranges([
 2989                Point::new(0, 2)..Point::new(1, 1),
 2990                Point::new(1, 2)..Point::new(1, 2),
 2991                Point::new(3, 1)..Point::new(3, 2),
 2992            ])
 2993        });
 2994
 2995        editor.join_lines(&JoinLines, cx);
 2996        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 2997
 2998        assert_eq!(
 2999            editor.selections.ranges::<Point>(cx),
 3000            [
 3001                Point::new(0, 7)..Point::new(0, 7),
 3002                Point::new(1, 3)..Point::new(1, 3)
 3003            ]
 3004        );
 3005        editor
 3006    });
 3007}
 3008
 3009#[gpui::test]
 3010async fn test_join_lines_with_git_diff_base(
 3011    executor: BackgroundExecutor,
 3012    cx: &mut gpui::TestAppContext,
 3013) {
 3014    init_test(cx, |_| {});
 3015
 3016    let mut cx = EditorTestContext::new(cx).await;
 3017
 3018    let diff_base = r#"
 3019        Line 0
 3020        Line 1
 3021        Line 2
 3022        Line 3
 3023        "#
 3024    .unindent();
 3025
 3026    cx.set_state(
 3027        &r#"
 3028        ˇLine 0
 3029        Line 1
 3030        Line 2
 3031        Line 3
 3032        "#
 3033        .unindent(),
 3034    );
 3035
 3036    cx.set_diff_base(Some(&diff_base));
 3037    executor.run_until_parked();
 3038
 3039    // Join lines
 3040    cx.update_editor(|editor, cx| {
 3041        editor.join_lines(&JoinLines, cx);
 3042    });
 3043    executor.run_until_parked();
 3044
 3045    cx.assert_editor_state(
 3046        &r#"
 3047        Line 0ˇ Line 1
 3048        Line 2
 3049        Line 3
 3050        "#
 3051        .unindent(),
 3052    );
 3053    // Join again
 3054    cx.update_editor(|editor, cx| {
 3055        editor.join_lines(&JoinLines, cx);
 3056    });
 3057    executor.run_until_parked();
 3058
 3059    cx.assert_editor_state(
 3060        &r#"
 3061        Line 0 Line 1ˇ Line 2
 3062        Line 3
 3063        "#
 3064        .unindent(),
 3065    );
 3066}
 3067
 3068#[gpui::test]
 3069async fn test_custom_newlines_cause_no_false_positive_diffs(
 3070    executor: BackgroundExecutor,
 3071    cx: &mut gpui::TestAppContext,
 3072) {
 3073    init_test(cx, |_| {});
 3074    let mut cx = EditorTestContext::new(cx).await;
 3075    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3076    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3077    executor.run_until_parked();
 3078
 3079    cx.update_editor(|editor, cx| {
 3080        assert_eq!(
 3081            editor
 3082                .buffer()
 3083                .read(cx)
 3084                .snapshot(cx)
 3085                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3086                .collect::<Vec<_>>(),
 3087            Vec::new(),
 3088            "Should not have any diffs for files with custom newlines"
 3089        );
 3090    });
 3091}
 3092
 3093#[gpui::test]
 3094async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3095    init_test(cx, |_| {});
 3096
 3097    let mut cx = EditorTestContext::new(cx).await;
 3098
 3099    // Test sort_lines_case_insensitive()
 3100    cx.set_state(indoc! {"
 3101        «z
 3102        y
 3103        x
 3104        Z
 3105        Y
 3106        Xˇ»
 3107    "});
 3108    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3109    cx.assert_editor_state(indoc! {"
 3110        «x
 3111        X
 3112        y
 3113        Y
 3114        z
 3115        Zˇ»
 3116    "});
 3117
 3118    // Test reverse_lines()
 3119    cx.set_state(indoc! {"
 3120        «5
 3121        4
 3122        3
 3123        2
 3124        1ˇ»
 3125    "});
 3126    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3127    cx.assert_editor_state(indoc! {"
 3128        «1
 3129        2
 3130        3
 3131        4
 3132        5ˇ»
 3133    "});
 3134
 3135    // Skip testing shuffle_line()
 3136
 3137    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3138    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3139
 3140    // Don't manipulate when cursor is on single line, but expand the selection
 3141    cx.set_state(indoc! {"
 3142        ddˇdd
 3143        ccc
 3144        bb
 3145        a
 3146    "});
 3147    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3148    cx.assert_editor_state(indoc! {"
 3149        «ddddˇ»
 3150        ccc
 3151        bb
 3152        a
 3153    "});
 3154
 3155    // Basic manipulate case
 3156    // Start selection moves to column 0
 3157    // End of selection shrinks to fit shorter line
 3158    cx.set_state(indoc! {"
 3159        dd«d
 3160        ccc
 3161        bb
 3162        aaaaaˇ»
 3163    "});
 3164    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3165    cx.assert_editor_state(indoc! {"
 3166        «aaaaa
 3167        bb
 3168        ccc
 3169        dddˇ»
 3170    "});
 3171
 3172    // Manipulate case with newlines
 3173    cx.set_state(indoc! {"
 3174        dd«d
 3175        ccc
 3176
 3177        bb
 3178        aaaaa
 3179
 3180        ˇ»
 3181    "});
 3182    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3183    cx.assert_editor_state(indoc! {"
 3184        «
 3185
 3186        aaaaa
 3187        bb
 3188        ccc
 3189        dddˇ»
 3190
 3191    "});
 3192
 3193    // Adding new line
 3194    cx.set_state(indoc! {"
 3195        aa«a
 3196        bbˇ»b
 3197    "});
 3198    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3199    cx.assert_editor_state(indoc! {"
 3200        «aaa
 3201        bbb
 3202        added_lineˇ»
 3203    "});
 3204
 3205    // Removing line
 3206    cx.set_state(indoc! {"
 3207        aa«a
 3208        bbbˇ»
 3209    "});
 3210    cx.update_editor(|e, cx| {
 3211        e.manipulate_lines(cx, |lines| {
 3212            lines.pop();
 3213        })
 3214    });
 3215    cx.assert_editor_state(indoc! {"
 3216        «aaaˇ»
 3217    "});
 3218
 3219    // Removing all lines
 3220    cx.set_state(indoc! {"
 3221        aa«a
 3222        bbbˇ»
 3223    "});
 3224    cx.update_editor(|e, cx| {
 3225        e.manipulate_lines(cx, |lines| {
 3226            lines.drain(..);
 3227        })
 3228    });
 3229    cx.assert_editor_state(indoc! {"
 3230        ˇ
 3231    "});
 3232}
 3233
 3234#[gpui::test]
 3235async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3236    init_test(cx, |_| {});
 3237
 3238    let mut cx = EditorTestContext::new(cx).await;
 3239
 3240    // Consider continuous selection as single selection
 3241    cx.set_state(indoc! {"
 3242        Aaa«aa
 3243        cˇ»c«c
 3244        bb
 3245        aaaˇ»aa
 3246    "});
 3247    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3248    cx.assert_editor_state(indoc! {"
 3249        «Aaaaa
 3250        ccc
 3251        bb
 3252        aaaaaˇ»
 3253    "});
 3254
 3255    cx.set_state(indoc! {"
 3256        Aaa«aa
 3257        cˇ»c«c
 3258        bb
 3259        aaaˇ»aa
 3260    "});
 3261    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3262    cx.assert_editor_state(indoc! {"
 3263        «Aaaaa
 3264        ccc
 3265        bbˇ»
 3266    "});
 3267
 3268    // Consider non continuous selection as distinct dedup operations
 3269    cx.set_state(indoc! {"
 3270        «aaaaa
 3271        bb
 3272        aaaaa
 3273        aaaaaˇ»
 3274
 3275        aaa«aaˇ»
 3276    "});
 3277    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3278    cx.assert_editor_state(indoc! {"
 3279        «aaaaa
 3280        bbˇ»
 3281
 3282        «aaaaaˇ»
 3283    "});
 3284}
 3285
 3286#[gpui::test]
 3287async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3288    init_test(cx, |_| {});
 3289
 3290    let mut cx = EditorTestContext::new(cx).await;
 3291
 3292    cx.set_state(indoc! {"
 3293        «Aaa
 3294        aAa
 3295        Aaaˇ»
 3296    "});
 3297    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3298    cx.assert_editor_state(indoc! {"
 3299        «Aaa
 3300        aAaˇ»
 3301    "});
 3302
 3303    cx.set_state(indoc! {"
 3304        «Aaa
 3305        aAa
 3306        aaAˇ»
 3307    "});
 3308    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3309    cx.assert_editor_state(indoc! {"
 3310        «Aaaˇ»
 3311    "});
 3312}
 3313
 3314#[gpui::test]
 3315async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3316    init_test(cx, |_| {});
 3317
 3318    let mut cx = EditorTestContext::new(cx).await;
 3319
 3320    // Manipulate with multiple selections on a single line
 3321    cx.set_state(indoc! {"
 3322        dd«dd
 3323        cˇ»c«c
 3324        bb
 3325        aaaˇ»aa
 3326    "});
 3327    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3328    cx.assert_editor_state(indoc! {"
 3329        «aaaaa
 3330        bb
 3331        ccc
 3332        ddddˇ»
 3333    "});
 3334
 3335    // Manipulate with multiple disjoin selections
 3336    cx.set_state(indoc! {"
 3337 3338        4
 3339        3
 3340        2
 3341        1ˇ»
 3342
 3343        dd«dd
 3344        ccc
 3345        bb
 3346        aaaˇ»aa
 3347    "});
 3348    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3349    cx.assert_editor_state(indoc! {"
 3350        «1
 3351        2
 3352        3
 3353        4
 3354        5ˇ»
 3355
 3356        «aaaaa
 3357        bb
 3358        ccc
 3359        ddddˇ»
 3360    "});
 3361
 3362    // Adding lines on each selection
 3363    cx.set_state(indoc! {"
 3364 3365        1ˇ»
 3366
 3367        bb«bb
 3368        aaaˇ»aa
 3369    "});
 3370    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3371    cx.assert_editor_state(indoc! {"
 3372        «2
 3373        1
 3374        added lineˇ»
 3375
 3376        «bbbb
 3377        aaaaa
 3378        added lineˇ»
 3379    "});
 3380
 3381    // Removing lines on each selection
 3382    cx.set_state(indoc! {"
 3383 3384        1ˇ»
 3385
 3386        bb«bb
 3387        aaaˇ»aa
 3388    "});
 3389    cx.update_editor(|e, cx| {
 3390        e.manipulate_lines(cx, |lines| {
 3391            lines.pop();
 3392        })
 3393    });
 3394    cx.assert_editor_state(indoc! {"
 3395        «2ˇ»
 3396
 3397        «bbbbˇ»
 3398    "});
 3399}
 3400
 3401#[gpui::test]
 3402async fn test_manipulate_text(cx: &mut TestAppContext) {
 3403    init_test(cx, |_| {});
 3404
 3405    let mut cx = EditorTestContext::new(cx).await;
 3406
 3407    // Test convert_to_upper_case()
 3408    cx.set_state(indoc! {"
 3409        «hello worldˇ»
 3410    "});
 3411    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3412    cx.assert_editor_state(indoc! {"
 3413        «HELLO WORLDˇ»
 3414    "});
 3415
 3416    // Test convert_to_lower_case()
 3417    cx.set_state(indoc! {"
 3418        «HELLO WORLDˇ»
 3419    "});
 3420    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3421    cx.assert_editor_state(indoc! {"
 3422        «hello worldˇ»
 3423    "});
 3424
 3425    // Test multiple line, single selection case
 3426    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3427    cx.set_state(indoc! {"
 3428        «The quick brown
 3429        fox jumps over
 3430        the lazy dogˇ»
 3431    "});
 3432    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3433    cx.assert_editor_state(indoc! {"
 3434        «The Quick Brown
 3435        Fox Jumps Over
 3436        The Lazy Dogˇ»
 3437    "});
 3438
 3439    // Test multiple line, single selection case
 3440    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3441    cx.set_state(indoc! {"
 3442        «The quick brown
 3443        fox jumps over
 3444        the lazy dogˇ»
 3445    "});
 3446    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3447    cx.assert_editor_state(indoc! {"
 3448        «TheQuickBrown
 3449        FoxJumpsOver
 3450        TheLazyDogˇ»
 3451    "});
 3452
 3453    // From here on out, test more complex cases of manipulate_text()
 3454
 3455    // Test no selection case - should affect words cursors are in
 3456    // Cursor at beginning, middle, and end of word
 3457    cx.set_state(indoc! {"
 3458        ˇhello big beauˇtiful worldˇ
 3459    "});
 3460    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3461    cx.assert_editor_state(indoc! {"
 3462        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3463    "});
 3464
 3465    // Test multiple selections on a single line and across multiple lines
 3466    cx.set_state(indoc! {"
 3467        «Theˇ» quick «brown
 3468        foxˇ» jumps «overˇ»
 3469        the «lazyˇ» dog
 3470    "});
 3471    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3472    cx.assert_editor_state(indoc! {"
 3473        «THEˇ» quick «BROWN
 3474        FOXˇ» jumps «OVERˇ»
 3475        the «LAZYˇ» dog
 3476    "});
 3477
 3478    // Test case where text length grows
 3479    cx.set_state(indoc! {"
 3480        «tschüߡ»
 3481    "});
 3482    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3483    cx.assert_editor_state(indoc! {"
 3484        «TSCHÜSSˇ»
 3485    "});
 3486
 3487    // Test to make sure we don't crash when text shrinks
 3488    cx.set_state(indoc! {"
 3489        aaa_bbbˇ
 3490    "});
 3491    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3492    cx.assert_editor_state(indoc! {"
 3493        «aaaBbbˇ»
 3494    "});
 3495
 3496    // Test to make sure we all aware of the fact that each word can grow and shrink
 3497    // Final selections should be aware of this fact
 3498    cx.set_state(indoc! {"
 3499        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3500    "});
 3501    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3502    cx.assert_editor_state(indoc! {"
 3503        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3504    "});
 3505
 3506    cx.set_state(indoc! {"
 3507        «hElLo, WoRld!ˇ»
 3508    "});
 3509    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3510    cx.assert_editor_state(indoc! {"
 3511        «HeLlO, wOrLD!ˇ»
 3512    "});
 3513}
 3514
 3515#[gpui::test]
 3516fn test_duplicate_line(cx: &mut TestAppContext) {
 3517    init_test(cx, |_| {});
 3518
 3519    let view = cx.add_window(|cx| {
 3520        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3521        build_editor(buffer, cx)
 3522    });
 3523    _ = view.update(cx, |view, cx| {
 3524        view.change_selections(None, cx, |s| {
 3525            s.select_display_ranges([
 3526                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3527                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3528                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3529                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3530            ])
 3531        });
 3532        view.duplicate_line_down(&DuplicateLineDown, cx);
 3533        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3534        assert_eq!(
 3535            view.selections.display_ranges(cx),
 3536            vec![
 3537                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3538                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3539                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3540                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3541            ]
 3542        );
 3543    });
 3544
 3545    let view = cx.add_window(|cx| {
 3546        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3547        build_editor(buffer, cx)
 3548    });
 3549    _ = view.update(cx, |view, cx| {
 3550        view.change_selections(None, cx, |s| {
 3551            s.select_display_ranges([
 3552                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3553                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3554            ])
 3555        });
 3556        view.duplicate_line_down(&DuplicateLineDown, cx);
 3557        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3558        assert_eq!(
 3559            view.selections.display_ranges(cx),
 3560            vec![
 3561                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3562                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3563            ]
 3564        );
 3565    });
 3566
 3567    // With `move_upwards` the selections stay in place, except for
 3568    // the lines inserted above them
 3569    let view = cx.add_window(|cx| {
 3570        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3571        build_editor(buffer, cx)
 3572    });
 3573    _ = view.update(cx, |view, cx| {
 3574        view.change_selections(None, cx, |s| {
 3575            s.select_display_ranges([
 3576                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3577                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3578                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3579                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3580            ])
 3581        });
 3582        view.duplicate_line_up(&DuplicateLineUp, cx);
 3583        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3584        assert_eq!(
 3585            view.selections.display_ranges(cx),
 3586            vec![
 3587                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3588                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3589                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3590                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3591            ]
 3592        );
 3593    });
 3594
 3595    let view = cx.add_window(|cx| {
 3596        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3597        build_editor(buffer, cx)
 3598    });
 3599    _ = view.update(cx, |view, cx| {
 3600        view.change_selections(None, cx, |s| {
 3601            s.select_display_ranges([
 3602                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3603                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3604            ])
 3605        });
 3606        view.duplicate_line_up(&DuplicateLineUp, cx);
 3607        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3608        assert_eq!(
 3609            view.selections.display_ranges(cx),
 3610            vec![
 3611                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3612                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3613            ]
 3614        );
 3615    });
 3616}
 3617
 3618#[gpui::test]
 3619fn test_move_line_up_down(cx: &mut TestAppContext) {
 3620    init_test(cx, |_| {});
 3621
 3622    let view = cx.add_window(|cx| {
 3623        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3624        build_editor(buffer, cx)
 3625    });
 3626    _ = view.update(cx, |view, cx| {
 3627        view.fold_ranges(
 3628            vec![
 3629                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3630                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3631                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3632            ],
 3633            true,
 3634            cx,
 3635        );
 3636        view.change_selections(None, cx, |s| {
 3637            s.select_display_ranges([
 3638                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3639                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3640                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3641                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3642            ])
 3643        });
 3644        assert_eq!(
 3645            view.display_text(cx),
 3646            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3647        );
 3648
 3649        view.move_line_up(&MoveLineUp, cx);
 3650        assert_eq!(
 3651            view.display_text(cx),
 3652            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3653        );
 3654        assert_eq!(
 3655            view.selections.display_ranges(cx),
 3656            vec![
 3657                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3658                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3659                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3660                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3661            ]
 3662        );
 3663    });
 3664
 3665    _ = view.update(cx, |view, cx| {
 3666        view.move_line_down(&MoveLineDown, cx);
 3667        assert_eq!(
 3668            view.display_text(cx),
 3669            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3670        );
 3671        assert_eq!(
 3672            view.selections.display_ranges(cx),
 3673            vec![
 3674                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3675                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3676                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3677                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3678            ]
 3679        );
 3680    });
 3681
 3682    _ = view.update(cx, |view, cx| {
 3683        view.move_line_down(&MoveLineDown, cx);
 3684        assert_eq!(
 3685            view.display_text(cx),
 3686            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3687        );
 3688        assert_eq!(
 3689            view.selections.display_ranges(cx),
 3690            vec![
 3691                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3692                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3693                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3694                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3695            ]
 3696        );
 3697    });
 3698
 3699    _ = view.update(cx, |view, cx| {
 3700        view.move_line_up(&MoveLineUp, cx);
 3701        assert_eq!(
 3702            view.display_text(cx),
 3703            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3704        );
 3705        assert_eq!(
 3706            view.selections.display_ranges(cx),
 3707            vec![
 3708                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3709                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3710                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3711                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3712            ]
 3713        );
 3714    });
 3715}
 3716
 3717#[gpui::test]
 3718fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3719    init_test(cx, |_| {});
 3720
 3721    let editor = cx.add_window(|cx| {
 3722        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3723        build_editor(buffer, cx)
 3724    });
 3725    _ = editor.update(cx, |editor, cx| {
 3726        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3727        editor.insert_blocks(
 3728            [BlockProperties {
 3729                style: BlockStyle::Fixed,
 3730                position: snapshot.anchor_after(Point::new(2, 0)),
 3731                disposition: BlockDisposition::Below,
 3732                height: 1,
 3733                render: Box::new(|_| div().into_any()),
 3734            }],
 3735            Some(Autoscroll::fit()),
 3736            cx,
 3737        );
 3738        editor.change_selections(None, cx, |s| {
 3739            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3740        });
 3741        editor.move_line_down(&MoveLineDown, cx);
 3742    });
 3743}
 3744
 3745#[gpui::test]
 3746fn test_transpose(cx: &mut TestAppContext) {
 3747    init_test(cx, |_| {});
 3748
 3749    _ = cx.add_window(|cx| {
 3750        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3751        editor.set_style(EditorStyle::default(), cx);
 3752        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3753        editor.transpose(&Default::default(), cx);
 3754        assert_eq!(editor.text(cx), "bac");
 3755        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3756
 3757        editor.transpose(&Default::default(), cx);
 3758        assert_eq!(editor.text(cx), "bca");
 3759        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3760
 3761        editor.transpose(&Default::default(), cx);
 3762        assert_eq!(editor.text(cx), "bac");
 3763        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3764
 3765        editor
 3766    });
 3767
 3768    _ = cx.add_window(|cx| {
 3769        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3770        editor.set_style(EditorStyle::default(), cx);
 3771        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3772        editor.transpose(&Default::default(), cx);
 3773        assert_eq!(editor.text(cx), "acb\nde");
 3774        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3775
 3776        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3777        editor.transpose(&Default::default(), cx);
 3778        assert_eq!(editor.text(cx), "acbd\ne");
 3779        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3780
 3781        editor.transpose(&Default::default(), cx);
 3782        assert_eq!(editor.text(cx), "acbde\n");
 3783        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3784
 3785        editor.transpose(&Default::default(), cx);
 3786        assert_eq!(editor.text(cx), "acbd\ne");
 3787        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3788
 3789        editor
 3790    });
 3791
 3792    _ = cx.add_window(|cx| {
 3793        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3794        editor.set_style(EditorStyle::default(), cx);
 3795        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3796        editor.transpose(&Default::default(), cx);
 3797        assert_eq!(editor.text(cx), "bacd\ne");
 3798        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3799
 3800        editor.transpose(&Default::default(), cx);
 3801        assert_eq!(editor.text(cx), "bcade\n");
 3802        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3803
 3804        editor.transpose(&Default::default(), cx);
 3805        assert_eq!(editor.text(cx), "bcda\ne");
 3806        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3807
 3808        editor.transpose(&Default::default(), cx);
 3809        assert_eq!(editor.text(cx), "bcade\n");
 3810        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3811
 3812        editor.transpose(&Default::default(), cx);
 3813        assert_eq!(editor.text(cx), "bcaed\n");
 3814        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3815
 3816        editor
 3817    });
 3818
 3819    _ = cx.add_window(|cx| {
 3820        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3821        editor.set_style(EditorStyle::default(), cx);
 3822        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3823        editor.transpose(&Default::default(), cx);
 3824        assert_eq!(editor.text(cx), "🏀🍐✋");
 3825        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3826
 3827        editor.transpose(&Default::default(), cx);
 3828        assert_eq!(editor.text(cx), "🏀✋🍐");
 3829        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3830
 3831        editor.transpose(&Default::default(), cx);
 3832        assert_eq!(editor.text(cx), "🏀🍐✋");
 3833        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3834
 3835        editor
 3836    });
 3837}
 3838
 3839#[gpui::test]
 3840async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 3841    init_test(cx, |_| {});
 3842
 3843    let mut cx = EditorTestContext::new(cx).await;
 3844
 3845    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 3846    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3847    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 3848
 3849    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 3850    cx.set_state("two ˇfour ˇsix ˇ");
 3851    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3852    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 3853
 3854    // Paste again but with only two cursors. Since the number of cursors doesn't
 3855    // match the number of slices in the clipboard, the entire clipboard text
 3856    // is pasted at each cursor.
 3857    cx.set_state("ˇtwo one✅ four three six five ˇ");
 3858    cx.update_editor(|e, cx| {
 3859        e.handle_input("( ", cx);
 3860        e.paste(&Paste, cx);
 3861        e.handle_input(") ", cx);
 3862    });
 3863    cx.assert_editor_state(
 3864        &([
 3865            "( one✅ ",
 3866            "three ",
 3867            "five ) ˇtwo one✅ four three six five ( one✅ ",
 3868            "three ",
 3869            "five ) ˇ",
 3870        ]
 3871        .join("\n")),
 3872    );
 3873
 3874    // Cut with three selections, one of which is full-line.
 3875    cx.set_state(indoc! {"
 3876        1«2ˇ»3
 3877        4ˇ567
 3878        «8ˇ»9"});
 3879    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3880    cx.assert_editor_state(indoc! {"
 3881        1ˇ3
 3882        ˇ9"});
 3883
 3884    // Paste with three selections, noticing how the copied selection that was full-line
 3885    // gets inserted before the second cursor.
 3886    cx.set_state(indoc! {"
 3887        1ˇ3
 3888 3889        «oˇ»ne"});
 3890    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3891    cx.assert_editor_state(indoc! {"
 3892        12ˇ3
 3893        4567
 3894 3895        8ˇne"});
 3896
 3897    // Copy with a single cursor only, which writes the whole line into the clipboard.
 3898    cx.set_state(indoc! {"
 3899        The quick brown
 3900        fox juˇmps over
 3901        the lazy dog"});
 3902    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 3903    assert_eq!(
 3904        cx.read_from_clipboard().map(|item| item.text().to_owned()),
 3905        Some("fox jumps over\n".to_owned())
 3906    );
 3907
 3908    // Paste with three selections, noticing how the copied full-line selection is inserted
 3909    // before the empty selections but replaces the selection that is non-empty.
 3910    cx.set_state(indoc! {"
 3911        Tˇhe quick brown
 3912        «foˇ»x jumps over
 3913        tˇhe lazy dog"});
 3914    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3915    cx.assert_editor_state(indoc! {"
 3916        fox jumps over
 3917        Tˇhe quick brown
 3918        fox jumps over
 3919        ˇx jumps over
 3920        fox jumps over
 3921        tˇhe lazy dog"});
 3922}
 3923
 3924#[gpui::test]
 3925async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 3926    init_test(cx, |_| {});
 3927
 3928    let mut cx = EditorTestContext::new(cx).await;
 3929    let language = Arc::new(Language::new(
 3930        LanguageConfig::default(),
 3931        Some(tree_sitter_rust::language()),
 3932    ));
 3933    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3934
 3935    // Cut an indented block, without the leading whitespace.
 3936    cx.set_state(indoc! {"
 3937        const a: B = (
 3938            c(),
 3939            «d(
 3940                e,
 3941                f
 3942            )ˇ»
 3943        );
 3944    "});
 3945    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3946    cx.assert_editor_state(indoc! {"
 3947        const a: B = (
 3948            c(),
 3949            ˇ
 3950        );
 3951    "});
 3952
 3953    // Paste it at the same position.
 3954    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3955    cx.assert_editor_state(indoc! {"
 3956        const a: B = (
 3957            c(),
 3958            d(
 3959                e,
 3960                f
 3961 3962        );
 3963    "});
 3964
 3965    // Paste it at a line with a lower indent level.
 3966    cx.set_state(indoc! {"
 3967        ˇ
 3968        const a: B = (
 3969            c(),
 3970        );
 3971    "});
 3972    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3973    cx.assert_editor_state(indoc! {"
 3974        d(
 3975            e,
 3976            f
 3977 3978        const a: B = (
 3979            c(),
 3980        );
 3981    "});
 3982
 3983    // Cut an indented block, with the leading whitespace.
 3984    cx.set_state(indoc! {"
 3985        const a: B = (
 3986            c(),
 3987        «    d(
 3988                e,
 3989                f
 3990            )
 3991        ˇ»);
 3992    "});
 3993    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3994    cx.assert_editor_state(indoc! {"
 3995        const a: B = (
 3996            c(),
 3997        ˇ);
 3998    "});
 3999
 4000    // Paste it at the same position.
 4001    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4002    cx.assert_editor_state(indoc! {"
 4003        const a: B = (
 4004            c(),
 4005            d(
 4006                e,
 4007                f
 4008            )
 4009        ˇ);
 4010    "});
 4011
 4012    // Paste it at a line with a higher indent level.
 4013    cx.set_state(indoc! {"
 4014        const a: B = (
 4015            c(),
 4016            d(
 4017                e,
 4018 4019            )
 4020        );
 4021    "});
 4022    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4023    cx.assert_editor_state(indoc! {"
 4024        const a: B = (
 4025            c(),
 4026            d(
 4027                e,
 4028                f    d(
 4029                    e,
 4030                    f
 4031                )
 4032        ˇ
 4033            )
 4034        );
 4035    "});
 4036}
 4037
 4038#[gpui::test]
 4039fn test_select_all(cx: &mut TestAppContext) {
 4040    init_test(cx, |_| {});
 4041
 4042    let view = cx.add_window(|cx| {
 4043        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4044        build_editor(buffer, cx)
 4045    });
 4046    _ = view.update(cx, |view, cx| {
 4047        view.select_all(&SelectAll, cx);
 4048        assert_eq!(
 4049            view.selections.display_ranges(cx),
 4050            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4051        );
 4052    });
 4053}
 4054
 4055#[gpui::test]
 4056fn test_select_line(cx: &mut TestAppContext) {
 4057    init_test(cx, |_| {});
 4058
 4059    let view = cx.add_window(|cx| {
 4060        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4061        build_editor(buffer, cx)
 4062    });
 4063    _ = view.update(cx, |view, cx| {
 4064        view.change_selections(None, cx, |s| {
 4065            s.select_display_ranges([
 4066                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4067                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4068                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4069                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4070            ])
 4071        });
 4072        view.select_line(&SelectLine, cx);
 4073        assert_eq!(
 4074            view.selections.display_ranges(cx),
 4075            vec![
 4076                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4077                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4078            ]
 4079        );
 4080    });
 4081
 4082    _ = view.update(cx, |view, cx| {
 4083        view.select_line(&SelectLine, cx);
 4084        assert_eq!(
 4085            view.selections.display_ranges(cx),
 4086            vec![
 4087                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4088                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4089            ]
 4090        );
 4091    });
 4092
 4093    _ = view.update(cx, |view, cx| {
 4094        view.select_line(&SelectLine, cx);
 4095        assert_eq!(
 4096            view.selections.display_ranges(cx),
 4097            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4098        );
 4099    });
 4100}
 4101
 4102#[gpui::test]
 4103fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4104    init_test(cx, |_| {});
 4105
 4106    let view = cx.add_window(|cx| {
 4107        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4108        build_editor(buffer, cx)
 4109    });
 4110    _ = view.update(cx, |view, cx| {
 4111        view.fold_ranges(
 4112            vec![
 4113                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4114                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4115                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4116            ],
 4117            true,
 4118            cx,
 4119        );
 4120        view.change_selections(None, cx, |s| {
 4121            s.select_display_ranges([
 4122                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4123                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4124                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4125                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4126            ])
 4127        });
 4128        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4129    });
 4130
 4131    _ = view.update(cx, |view, cx| {
 4132        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4133        assert_eq!(
 4134            view.display_text(cx),
 4135            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4136        );
 4137        assert_eq!(
 4138            view.selections.display_ranges(cx),
 4139            [
 4140                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4141                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4142                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4143                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4144            ]
 4145        );
 4146    });
 4147
 4148    _ = view.update(cx, |view, cx| {
 4149        view.change_selections(None, cx, |s| {
 4150            s.select_display_ranges([
 4151                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4152            ])
 4153        });
 4154        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4155        assert_eq!(
 4156            view.display_text(cx),
 4157            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4158        );
 4159        assert_eq!(
 4160            view.selections.display_ranges(cx),
 4161            [
 4162                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4163                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4164                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4165                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4166                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4167                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4168                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4169                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4170            ]
 4171        );
 4172    });
 4173}
 4174
 4175#[gpui::test]
 4176async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4177    init_test(cx, |_| {});
 4178
 4179    let mut cx = EditorTestContext::new(cx).await;
 4180
 4181    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4182    cx.set_state(indoc!(
 4183        r#"abc
 4184           defˇghi
 4185
 4186           jk
 4187           nlmo
 4188           "#
 4189    ));
 4190
 4191    cx.update_editor(|editor, cx| {
 4192        editor.add_selection_above(&Default::default(), cx);
 4193    });
 4194
 4195    cx.assert_editor_state(indoc!(
 4196        r#"abcˇ
 4197           defˇghi
 4198
 4199           jk
 4200           nlmo
 4201           "#
 4202    ));
 4203
 4204    cx.update_editor(|editor, cx| {
 4205        editor.add_selection_above(&Default::default(), cx);
 4206    });
 4207
 4208    cx.assert_editor_state(indoc!(
 4209        r#"abcˇ
 4210            defˇghi
 4211
 4212            jk
 4213            nlmo
 4214            "#
 4215    ));
 4216
 4217    cx.update_editor(|view, cx| {
 4218        view.add_selection_below(&Default::default(), cx);
 4219    });
 4220
 4221    cx.assert_editor_state(indoc!(
 4222        r#"abc
 4223           defˇghi
 4224
 4225           jk
 4226           nlmo
 4227           "#
 4228    ));
 4229
 4230    cx.update_editor(|view, cx| {
 4231        view.undo_selection(&Default::default(), cx);
 4232    });
 4233
 4234    cx.assert_editor_state(indoc!(
 4235        r#"abcˇ
 4236           defˇghi
 4237
 4238           jk
 4239           nlmo
 4240           "#
 4241    ));
 4242
 4243    cx.update_editor(|view, cx| {
 4244        view.redo_selection(&Default::default(), cx);
 4245    });
 4246
 4247    cx.assert_editor_state(indoc!(
 4248        r#"abc
 4249           defˇghi
 4250
 4251           jk
 4252           nlmo
 4253           "#
 4254    ));
 4255
 4256    cx.update_editor(|view, cx| {
 4257        view.add_selection_below(&Default::default(), cx);
 4258    });
 4259
 4260    cx.assert_editor_state(indoc!(
 4261        r#"abc
 4262           defˇghi
 4263
 4264           jk
 4265           nlmˇo
 4266           "#
 4267    ));
 4268
 4269    cx.update_editor(|view, cx| {
 4270        view.add_selection_below(&Default::default(), cx);
 4271    });
 4272
 4273    cx.assert_editor_state(indoc!(
 4274        r#"abc
 4275           defˇghi
 4276
 4277           jk
 4278           nlmˇo
 4279           "#
 4280    ));
 4281
 4282    // change selections
 4283    cx.set_state(indoc!(
 4284        r#"abc
 4285           def«ˇg»hi
 4286
 4287           jk
 4288           nlmo
 4289           "#
 4290    ));
 4291
 4292    cx.update_editor(|view, cx| {
 4293        view.add_selection_below(&Default::default(), cx);
 4294    });
 4295
 4296    cx.assert_editor_state(indoc!(
 4297        r#"abc
 4298           def«ˇg»hi
 4299
 4300           jk
 4301           nlm«ˇo»
 4302           "#
 4303    ));
 4304
 4305    cx.update_editor(|view, cx| {
 4306        view.add_selection_below(&Default::default(), cx);
 4307    });
 4308
 4309    cx.assert_editor_state(indoc!(
 4310        r#"abc
 4311           def«ˇg»hi
 4312
 4313           jk
 4314           nlm«ˇo»
 4315           "#
 4316    ));
 4317
 4318    cx.update_editor(|view, cx| {
 4319        view.add_selection_above(&Default::default(), cx);
 4320    });
 4321
 4322    cx.assert_editor_state(indoc!(
 4323        r#"abc
 4324           def«ˇg»hi
 4325
 4326           jk
 4327           nlmo
 4328           "#
 4329    ));
 4330
 4331    cx.update_editor(|view, cx| {
 4332        view.add_selection_above(&Default::default(), cx);
 4333    });
 4334
 4335    cx.assert_editor_state(indoc!(
 4336        r#"abc
 4337           def«ˇg»hi
 4338
 4339           jk
 4340           nlmo
 4341           "#
 4342    ));
 4343
 4344    // Change selections again
 4345    cx.set_state(indoc!(
 4346        r#"a«bc
 4347           defgˇ»hi
 4348
 4349           jk
 4350           nlmo
 4351           "#
 4352    ));
 4353
 4354    cx.update_editor(|view, cx| {
 4355        view.add_selection_below(&Default::default(), cx);
 4356    });
 4357
 4358    cx.assert_editor_state(indoc!(
 4359        r#"a«bcˇ»
 4360           d«efgˇ»hi
 4361
 4362           j«kˇ»
 4363           nlmo
 4364           "#
 4365    ));
 4366
 4367    cx.update_editor(|view, cx| {
 4368        view.add_selection_below(&Default::default(), cx);
 4369    });
 4370    cx.assert_editor_state(indoc!(
 4371        r#"a«bcˇ»
 4372           d«efgˇ»hi
 4373
 4374           j«kˇ»
 4375           n«lmoˇ»
 4376           "#
 4377    ));
 4378    cx.update_editor(|view, cx| {
 4379        view.add_selection_above(&Default::default(), cx);
 4380    });
 4381
 4382    cx.assert_editor_state(indoc!(
 4383        r#"a«bcˇ»
 4384           d«efgˇ»hi
 4385
 4386           j«kˇ»
 4387           nlmo
 4388           "#
 4389    ));
 4390
 4391    // Change selections again
 4392    cx.set_state(indoc!(
 4393        r#"abc
 4394           d«ˇefghi
 4395
 4396           jk
 4397           nlm»o
 4398           "#
 4399    ));
 4400
 4401    cx.update_editor(|view, cx| {
 4402        view.add_selection_above(&Default::default(), cx);
 4403    });
 4404
 4405    cx.assert_editor_state(indoc!(
 4406        r#"a«ˇbc»
 4407           d«ˇef»ghi
 4408
 4409           j«ˇk»
 4410           n«ˇlm»o
 4411           "#
 4412    ));
 4413
 4414    cx.update_editor(|view, cx| {
 4415        view.add_selection_below(&Default::default(), cx);
 4416    });
 4417
 4418    cx.assert_editor_state(indoc!(
 4419        r#"abc
 4420           d«ˇef»ghi
 4421
 4422           j«ˇk»
 4423           n«ˇlm»o
 4424           "#
 4425    ));
 4426}
 4427
 4428#[gpui::test]
 4429async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4430    init_test(cx, |_| {});
 4431
 4432    let mut cx = EditorTestContext::new(cx).await;
 4433    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4434
 4435    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4436        .unwrap();
 4437    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4438
 4439    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4440        .unwrap();
 4441    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4442
 4443    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4444    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4445
 4446    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4447    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4448
 4449    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4450        .unwrap();
 4451    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4452
 4453    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4454        .unwrap();
 4455    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4456}
 4457
 4458#[gpui::test]
 4459async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4460    init_test(cx, |_| {});
 4461
 4462    let mut cx = EditorTestContext::new(cx).await;
 4463    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4464
 4465    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4466        .unwrap();
 4467    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4468}
 4469
 4470#[gpui::test]
 4471async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4472    init_test(cx, |_| {});
 4473
 4474    let mut cx = EditorTestContext::new(cx).await;
 4475    cx.set_state(
 4476        r#"let foo = 2;
 4477lˇet foo = 2;
 4478let fooˇ = 2;
 4479let foo = 2;
 4480let foo = ˇ2;"#,
 4481    );
 4482
 4483    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4484        .unwrap();
 4485    cx.assert_editor_state(
 4486        r#"let foo = 2;
 4487«letˇ» foo = 2;
 4488let «fooˇ» = 2;
 4489let foo = 2;
 4490let foo = «2ˇ»;"#,
 4491    );
 4492
 4493    // noop for multiple selections with different contents
 4494    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4495        .unwrap();
 4496    cx.assert_editor_state(
 4497        r#"let foo = 2;
 4498«letˇ» foo = 2;
 4499let «fooˇ» = 2;
 4500let foo = 2;
 4501let foo = «2ˇ»;"#,
 4502    );
 4503}
 4504
 4505#[gpui::test]
 4506async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4507    init_test(cx, |_| {});
 4508
 4509    let mut cx = EditorTestContext::new_multibuffer(
 4510        cx,
 4511        [
 4512            &indoc! {
 4513                "aaa\n«bbb\nccc\n»ddd"
 4514            },
 4515            &indoc! {
 4516                "aaa\n«bbb\nccc\n»ddd"
 4517            },
 4518        ],
 4519    );
 4520
 4521    cx.assert_editor_state(indoc! {"
 4522        ˇbbb
 4523        ccc
 4524
 4525        bbb
 4526        ccc
 4527        "});
 4528    cx.dispatch_action(SelectPrevious::default());
 4529    cx.assert_editor_state(indoc! {"
 4530                «bbbˇ»
 4531                ccc
 4532
 4533                bbb
 4534                ccc
 4535                "});
 4536    cx.dispatch_action(SelectPrevious::default());
 4537    cx.assert_editor_state(indoc! {"
 4538                «bbbˇ»
 4539                ccc
 4540
 4541                «bbbˇ»
 4542                ccc
 4543                "});
 4544}
 4545
 4546#[gpui::test]
 4547async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 4548    init_test(cx, |_| {});
 4549
 4550    let mut cx = EditorTestContext::new(cx).await;
 4551    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4552
 4553    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4554        .unwrap();
 4555    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4556
 4557    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4558        .unwrap();
 4559    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4560
 4561    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4562    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4563
 4564    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4565    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4566
 4567    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4568        .unwrap();
 4569    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 4570
 4571    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4572        .unwrap();
 4573    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 4574
 4575    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4576        .unwrap();
 4577    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4578}
 4579
 4580#[gpui::test]
 4581async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4582    init_test(cx, |_| {});
 4583
 4584    let mut cx = EditorTestContext::new(cx).await;
 4585    cx.set_state(
 4586        r#"let foo = 2;
 4587lˇet foo = 2;
 4588let fooˇ = 2;
 4589let foo = 2;
 4590let foo = ˇ2;"#,
 4591    );
 4592
 4593    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4594        .unwrap();
 4595    cx.assert_editor_state(
 4596        r#"let foo = 2;
 4597«letˇ» foo = 2;
 4598let «fooˇ» = 2;
 4599let foo = 2;
 4600let foo = «2ˇ»;"#,
 4601    );
 4602
 4603    // noop for multiple selections with different contents
 4604    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4605        .unwrap();
 4606    cx.assert_editor_state(
 4607        r#"let foo = 2;
 4608«letˇ» foo = 2;
 4609let «fooˇ» = 2;
 4610let foo = 2;
 4611let foo = «2ˇ»;"#,
 4612    );
 4613}
 4614
 4615#[gpui::test]
 4616async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 4617    init_test(cx, |_| {});
 4618
 4619    let mut cx = EditorTestContext::new(cx).await;
 4620    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 4621
 4622    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4623        .unwrap();
 4624    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4625
 4626    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4627        .unwrap();
 4628    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4629
 4630    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4631    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4632
 4633    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4634    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4635
 4636    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4637        .unwrap();
 4638    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 4639
 4640    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4641        .unwrap();
 4642    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4643}
 4644
 4645#[gpui::test]
 4646async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 4647    init_test(cx, |_| {});
 4648
 4649    let language = Arc::new(Language::new(
 4650        LanguageConfig::default(),
 4651        Some(tree_sitter_rust::language()),
 4652    ));
 4653
 4654    let text = r#"
 4655        use mod1::mod2::{mod3, mod4};
 4656
 4657        fn fn_1(param1: bool, param2: &str) {
 4658            let var1 = "text";
 4659        }
 4660    "#
 4661    .unindent();
 4662
 4663    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4664    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4665    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4666
 4667    view.condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 4668        .await;
 4669
 4670    _ = view.update(cx, |view, cx| {
 4671        view.change_selections(None, cx, |s| {
 4672            s.select_display_ranges([
 4673                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4674                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4675                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4676            ]);
 4677        });
 4678        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4679    });
 4680    assert_eq!(
 4681        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
 4682        &[
 4683            DisplayPoint::new(DisplayRow(0), 23)..DisplayPoint::new(DisplayRow(0), 27),
 4684            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4685            DisplayPoint::new(DisplayRow(3), 15)..DisplayPoint::new(DisplayRow(3), 21),
 4686        ]
 4687    );
 4688
 4689    _ = view.update(cx, |view, cx| {
 4690        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4691    });
 4692    assert_eq!(
 4693        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4694        &[
 4695            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4696            DisplayPoint::new(DisplayRow(4), 1)..DisplayPoint::new(DisplayRow(2), 0),
 4697        ]
 4698    );
 4699
 4700    _ = view.update(cx, |view, cx| {
 4701        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4702    });
 4703    assert_eq!(
 4704        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4705        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4706    );
 4707
 4708    // Trying to expand the selected syntax node one more time has no effect.
 4709    _ = view.update(cx, |view, cx| {
 4710        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4711    });
 4712    assert_eq!(
 4713        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4714        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4715    );
 4716
 4717    _ = view.update(cx, |view, cx| {
 4718        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4719    });
 4720    assert_eq!(
 4721        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4722        &[
 4723            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4724            DisplayPoint::new(DisplayRow(4), 1)..DisplayPoint::new(DisplayRow(2), 0),
 4725        ]
 4726    );
 4727
 4728    _ = view.update(cx, |view, cx| {
 4729        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4730    });
 4731    assert_eq!(
 4732        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4733        &[
 4734            DisplayPoint::new(DisplayRow(0), 23)..DisplayPoint::new(DisplayRow(0), 27),
 4735            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4736            DisplayPoint::new(DisplayRow(3), 15)..DisplayPoint::new(DisplayRow(3), 21),
 4737        ]
 4738    );
 4739
 4740    _ = view.update(cx, |view, cx| {
 4741        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4742    });
 4743    assert_eq!(
 4744        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4745        &[
 4746            DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4747            DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4748            DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4749        ]
 4750    );
 4751
 4752    // Trying to shrink the selected syntax node one more time has no effect.
 4753    _ = view.update(cx, |view, cx| {
 4754        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4755    });
 4756    assert_eq!(
 4757        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4758        &[
 4759            DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4760            DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4761            DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4762        ]
 4763    );
 4764
 4765    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 4766    // a fold.
 4767    _ = view.update(cx, |view, cx| {
 4768        view.fold_ranges(
 4769            vec![
 4770                (
 4771                    Point::new(0, 21)..Point::new(0, 24),
 4772                    FoldPlaceholder::test(),
 4773                ),
 4774                (
 4775                    Point::new(3, 20)..Point::new(3, 22),
 4776                    FoldPlaceholder::test(),
 4777                ),
 4778            ],
 4779            true,
 4780            cx,
 4781        );
 4782        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4783    });
 4784    assert_eq!(
 4785        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4786        &[
 4787            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4788            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4789            DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(3), 23),
 4790        ]
 4791    );
 4792}
 4793
 4794#[gpui::test]
 4795async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 4796    init_test(cx, |_| {});
 4797
 4798    let language = Arc::new(
 4799        Language::new(
 4800            LanguageConfig {
 4801                brackets: BracketPairConfig {
 4802                    pairs: vec![
 4803                        BracketPair {
 4804                            start: "{".to_string(),
 4805                            end: "}".to_string(),
 4806                            close: false,
 4807                            surround: false,
 4808                            newline: true,
 4809                        },
 4810                        BracketPair {
 4811                            start: "(".to_string(),
 4812                            end: ")".to_string(),
 4813                            close: false,
 4814                            surround: false,
 4815                            newline: true,
 4816                        },
 4817                    ],
 4818                    ..Default::default()
 4819                },
 4820                ..Default::default()
 4821            },
 4822            Some(tree_sitter_rust::language()),
 4823        )
 4824        .with_indents_query(
 4825            r#"
 4826                (_ "(" ")" @end) @indent
 4827                (_ "{" "}" @end) @indent
 4828            "#,
 4829        )
 4830        .unwrap(),
 4831    );
 4832
 4833    let text = "fn a() {}";
 4834
 4835    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4836    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4837    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4838    editor
 4839        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 4840        .await;
 4841
 4842    _ = editor.update(cx, |editor, cx| {
 4843        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 4844        editor.newline(&Newline, cx);
 4845        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 4846        assert_eq!(
 4847            editor.selections.ranges(cx),
 4848            &[
 4849                Point::new(1, 4)..Point::new(1, 4),
 4850                Point::new(3, 4)..Point::new(3, 4),
 4851                Point::new(5, 0)..Point::new(5, 0)
 4852            ]
 4853        );
 4854    });
 4855}
 4856
 4857#[gpui::test]
 4858async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 4859    init_test(cx, |_| {});
 4860
 4861    let mut cx = EditorTestContext::new(cx).await;
 4862
 4863    let language = Arc::new(Language::new(
 4864        LanguageConfig {
 4865            brackets: BracketPairConfig {
 4866                pairs: vec![
 4867                    BracketPair {
 4868                        start: "{".to_string(),
 4869                        end: "}".to_string(),
 4870                        close: true,
 4871                        surround: true,
 4872                        newline: true,
 4873                    },
 4874                    BracketPair {
 4875                        start: "(".to_string(),
 4876                        end: ")".to_string(),
 4877                        close: true,
 4878                        surround: true,
 4879                        newline: true,
 4880                    },
 4881                    BracketPair {
 4882                        start: "/*".to_string(),
 4883                        end: " */".to_string(),
 4884                        close: true,
 4885                        surround: true,
 4886                        newline: true,
 4887                    },
 4888                    BracketPair {
 4889                        start: "[".to_string(),
 4890                        end: "]".to_string(),
 4891                        close: false,
 4892                        surround: false,
 4893                        newline: true,
 4894                    },
 4895                    BracketPair {
 4896                        start: "\"".to_string(),
 4897                        end: "\"".to_string(),
 4898                        close: true,
 4899                        surround: true,
 4900                        newline: false,
 4901                    },
 4902                    BracketPair {
 4903                        start: "<".to_string(),
 4904                        end: ">".to_string(),
 4905                        close: false,
 4906                        surround: true,
 4907                        newline: true,
 4908                    },
 4909                ],
 4910                ..Default::default()
 4911            },
 4912            autoclose_before: "})]".to_string(),
 4913            ..Default::default()
 4914        },
 4915        Some(tree_sitter_rust::language()),
 4916    ));
 4917
 4918    cx.language_registry().add(language.clone());
 4919    cx.update_buffer(|buffer, cx| {
 4920        buffer.set_language(Some(language), cx);
 4921    });
 4922
 4923    cx.set_state(
 4924        &r#"
 4925            🏀ˇ
 4926            εˇ
 4927            ❤️ˇ
 4928        "#
 4929        .unindent(),
 4930    );
 4931
 4932    // autoclose multiple nested brackets at multiple cursors
 4933    cx.update_editor(|view, cx| {
 4934        view.handle_input("{", cx);
 4935        view.handle_input("{", cx);
 4936        view.handle_input("{", cx);
 4937    });
 4938    cx.assert_editor_state(
 4939        &"
 4940            🏀{{{ˇ}}}
 4941            ε{{{ˇ}}}
 4942            ❤️{{{ˇ}}}
 4943        "
 4944        .unindent(),
 4945    );
 4946
 4947    // insert a different closing bracket
 4948    cx.update_editor(|view, cx| {
 4949        view.handle_input(")", cx);
 4950    });
 4951    cx.assert_editor_state(
 4952        &"
 4953            🏀{{{)ˇ}}}
 4954            ε{{{)ˇ}}}
 4955            ❤️{{{)ˇ}}}
 4956        "
 4957        .unindent(),
 4958    );
 4959
 4960    // skip over the auto-closed brackets when typing a closing bracket
 4961    cx.update_editor(|view, cx| {
 4962        view.move_right(&MoveRight, cx);
 4963        view.handle_input("}", cx);
 4964        view.handle_input("}", cx);
 4965        view.handle_input("}", cx);
 4966    });
 4967    cx.assert_editor_state(
 4968        &"
 4969            🏀{{{)}}}}ˇ
 4970            ε{{{)}}}}ˇ
 4971            ❤️{{{)}}}}ˇ
 4972        "
 4973        .unindent(),
 4974    );
 4975
 4976    // autoclose multi-character pairs
 4977    cx.set_state(
 4978        &"
 4979            ˇ
 4980            ˇ
 4981        "
 4982        .unindent(),
 4983    );
 4984    cx.update_editor(|view, cx| {
 4985        view.handle_input("/", cx);
 4986        view.handle_input("*", cx);
 4987    });
 4988    cx.assert_editor_state(
 4989        &"
 4990            /*ˇ */
 4991            /*ˇ */
 4992        "
 4993        .unindent(),
 4994    );
 4995
 4996    // one cursor autocloses a multi-character pair, one cursor
 4997    // does not autoclose.
 4998    cx.set_state(
 4999        &"
 5000 5001            ˇ
 5002        "
 5003        .unindent(),
 5004    );
 5005    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5006    cx.assert_editor_state(
 5007        &"
 5008            /*ˇ */
 5009 5010        "
 5011        .unindent(),
 5012    );
 5013
 5014    // Don't autoclose if the next character isn't whitespace and isn't
 5015    // listed in the language's "autoclose_before" section.
 5016    cx.set_state("ˇa b");
 5017    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5018    cx.assert_editor_state("{ˇa b");
 5019
 5020    // Don't autoclose if `close` is false for the bracket pair
 5021    cx.set_state("ˇ");
 5022    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5023    cx.assert_editor_state("");
 5024
 5025    // Surround with brackets if text is selected
 5026    cx.set_state("«aˇ» b");
 5027    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5028    cx.assert_editor_state("{«aˇ»} b");
 5029
 5030    // Autclose pair where the start and end characters are the same
 5031    cx.set_state("");
 5032    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5033    cx.assert_editor_state("a\"ˇ\"");
 5034    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5035    cx.assert_editor_state("a\"\"ˇ");
 5036
 5037    // Don't autoclose pair if autoclose is disabled
 5038    cx.set_state("ˇ");
 5039    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5040    cx.assert_editor_state("");
 5041
 5042    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5043    cx.set_state("«aˇ» b");
 5044    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5045    cx.assert_editor_state("<«aˇ»> b");
 5046}
 5047
 5048#[gpui::test]
 5049async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5050    init_test(cx, |settings| {
 5051        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5052    });
 5053
 5054    let mut cx = EditorTestContext::new(cx).await;
 5055
 5056    let language = Arc::new(Language::new(
 5057        LanguageConfig {
 5058            brackets: BracketPairConfig {
 5059                pairs: vec![
 5060                    BracketPair {
 5061                        start: "{".to_string(),
 5062                        end: "}".to_string(),
 5063                        close: true,
 5064                        surround: true,
 5065                        newline: true,
 5066                    },
 5067                    BracketPair {
 5068                        start: "(".to_string(),
 5069                        end: ")".to_string(),
 5070                        close: true,
 5071                        surround: true,
 5072                        newline: true,
 5073                    },
 5074                    BracketPair {
 5075                        start: "[".to_string(),
 5076                        end: "]".to_string(),
 5077                        close: false,
 5078                        surround: false,
 5079                        newline: true,
 5080                    },
 5081                ],
 5082                ..Default::default()
 5083            },
 5084            autoclose_before: "})]".to_string(),
 5085            ..Default::default()
 5086        },
 5087        Some(tree_sitter_rust::language()),
 5088    ));
 5089
 5090    cx.language_registry().add(language.clone());
 5091    cx.update_buffer(|buffer, cx| {
 5092        buffer.set_language(Some(language), cx);
 5093    });
 5094
 5095    cx.set_state(
 5096        &"
 5097            ˇ
 5098            ˇ
 5099            ˇ
 5100        "
 5101        .unindent(),
 5102    );
 5103
 5104    // ensure only matching closing brackets are skipped over
 5105    cx.update_editor(|view, cx| {
 5106        view.handle_input("}", cx);
 5107        view.move_left(&MoveLeft, cx);
 5108        view.handle_input(")", cx);
 5109        view.move_left(&MoveLeft, cx);
 5110    });
 5111    cx.assert_editor_state(
 5112        &"
 5113            ˇ)}
 5114            ˇ)}
 5115            ˇ)}
 5116        "
 5117        .unindent(),
 5118    );
 5119
 5120    // skip-over closing brackets at multiple cursors
 5121    cx.update_editor(|view, cx| {
 5122        view.handle_input(")", cx);
 5123        view.handle_input("}", cx);
 5124    });
 5125    cx.assert_editor_state(
 5126        &"
 5127            )}ˇ
 5128            )}ˇ
 5129            )}ˇ
 5130        "
 5131        .unindent(),
 5132    );
 5133
 5134    // ignore non-close brackets
 5135    cx.update_editor(|view, cx| {
 5136        view.handle_input("]", cx);
 5137        view.move_left(&MoveLeft, cx);
 5138        view.handle_input("]", cx);
 5139    });
 5140    cx.assert_editor_state(
 5141        &"
 5142            )}]ˇ]
 5143            )}]ˇ]
 5144            )}]ˇ]
 5145        "
 5146        .unindent(),
 5147    );
 5148}
 5149
 5150#[gpui::test]
 5151async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5152    init_test(cx, |_| {});
 5153
 5154    let mut cx = EditorTestContext::new(cx).await;
 5155
 5156    let html_language = Arc::new(
 5157        Language::new(
 5158            LanguageConfig {
 5159                name: "HTML".into(),
 5160                brackets: BracketPairConfig {
 5161                    pairs: vec![
 5162                        BracketPair {
 5163                            start: "<".into(),
 5164                            end: ">".into(),
 5165                            close: true,
 5166                            ..Default::default()
 5167                        },
 5168                        BracketPair {
 5169                            start: "{".into(),
 5170                            end: "}".into(),
 5171                            close: true,
 5172                            ..Default::default()
 5173                        },
 5174                        BracketPair {
 5175                            start: "(".into(),
 5176                            end: ")".into(),
 5177                            close: true,
 5178                            ..Default::default()
 5179                        },
 5180                    ],
 5181                    ..Default::default()
 5182                },
 5183                autoclose_before: "})]>".into(),
 5184                ..Default::default()
 5185            },
 5186            Some(tree_sitter_html::language()),
 5187        )
 5188        .with_injection_query(
 5189            r#"
 5190            (script_element
 5191                (raw_text) @content
 5192                (#set! "language" "javascript"))
 5193            "#,
 5194        )
 5195        .unwrap(),
 5196    );
 5197
 5198    let javascript_language = Arc::new(Language::new(
 5199        LanguageConfig {
 5200            name: "JavaScript".into(),
 5201            brackets: BracketPairConfig {
 5202                pairs: vec![
 5203                    BracketPair {
 5204                        start: "/*".into(),
 5205                        end: " */".into(),
 5206                        close: true,
 5207                        ..Default::default()
 5208                    },
 5209                    BracketPair {
 5210                        start: "{".into(),
 5211                        end: "}".into(),
 5212                        close: true,
 5213                        ..Default::default()
 5214                    },
 5215                    BracketPair {
 5216                        start: "(".into(),
 5217                        end: ")".into(),
 5218                        close: true,
 5219                        ..Default::default()
 5220                    },
 5221                ],
 5222                ..Default::default()
 5223            },
 5224            autoclose_before: "})]>".into(),
 5225            ..Default::default()
 5226        },
 5227        Some(tree_sitter_typescript::language_tsx()),
 5228    ));
 5229
 5230    cx.language_registry().add(html_language.clone());
 5231    cx.language_registry().add(javascript_language.clone());
 5232
 5233    cx.update_buffer(|buffer, cx| {
 5234        buffer.set_language(Some(html_language), cx);
 5235    });
 5236
 5237    cx.set_state(
 5238        &r#"
 5239            <body>ˇ
 5240                <script>
 5241                    var x = 1;ˇ
 5242                </script>
 5243            </body>ˇ
 5244        "#
 5245        .unindent(),
 5246    );
 5247
 5248    // Precondition: different languages are active at different locations.
 5249    cx.update_editor(|editor, cx| {
 5250        let snapshot = editor.snapshot(cx);
 5251        let cursors = editor.selections.ranges::<usize>(cx);
 5252        let languages = cursors
 5253            .iter()
 5254            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5255            .collect::<Vec<_>>();
 5256        assert_eq!(
 5257            languages,
 5258            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5259        );
 5260    });
 5261
 5262    // Angle brackets autoclose in HTML, but not JavaScript.
 5263    cx.update_editor(|editor, cx| {
 5264        editor.handle_input("<", cx);
 5265        editor.handle_input("a", cx);
 5266    });
 5267    cx.assert_editor_state(
 5268        &r#"
 5269            <body><aˇ>
 5270                <script>
 5271                    var x = 1;<aˇ
 5272                </script>
 5273            </body><aˇ>
 5274        "#
 5275        .unindent(),
 5276    );
 5277
 5278    // Curly braces and parens autoclose in both HTML and JavaScript.
 5279    cx.update_editor(|editor, cx| {
 5280        editor.handle_input(" b=", cx);
 5281        editor.handle_input("{", cx);
 5282        editor.handle_input("c", cx);
 5283        editor.handle_input("(", cx);
 5284    });
 5285    cx.assert_editor_state(
 5286        &r#"
 5287            <body><a b={c(ˇ)}>
 5288                <script>
 5289                    var x = 1;<a b={c(ˇ)}
 5290                </script>
 5291            </body><a b={c(ˇ)}>
 5292        "#
 5293        .unindent(),
 5294    );
 5295
 5296    // Brackets that were already autoclosed are skipped.
 5297    cx.update_editor(|editor, cx| {
 5298        editor.handle_input(")", cx);
 5299        editor.handle_input("d", cx);
 5300        editor.handle_input("}", cx);
 5301    });
 5302    cx.assert_editor_state(
 5303        &r#"
 5304            <body><a b={c()d}ˇ>
 5305                <script>
 5306                    var x = 1;<a b={c()d}ˇ
 5307                </script>
 5308            </body><a b={c()d}ˇ>
 5309        "#
 5310        .unindent(),
 5311    );
 5312    cx.update_editor(|editor, cx| {
 5313        editor.handle_input(">", cx);
 5314    });
 5315    cx.assert_editor_state(
 5316        &r#"
 5317            <body><a b={c()d}>ˇ
 5318                <script>
 5319                    var x = 1;<a b={c()d}>ˇ
 5320                </script>
 5321            </body><a b={c()d}>ˇ
 5322        "#
 5323        .unindent(),
 5324    );
 5325
 5326    // Reset
 5327    cx.set_state(
 5328        &r#"
 5329            <body>ˇ
 5330                <script>
 5331                    var x = 1;ˇ
 5332                </script>
 5333            </body>ˇ
 5334        "#
 5335        .unindent(),
 5336    );
 5337
 5338    cx.update_editor(|editor, cx| {
 5339        editor.handle_input("<", cx);
 5340    });
 5341    cx.assert_editor_state(
 5342        &r#"
 5343            <body><ˇ>
 5344                <script>
 5345                    var x = 1;<ˇ
 5346                </script>
 5347            </body><ˇ>
 5348        "#
 5349        .unindent(),
 5350    );
 5351
 5352    // When backspacing, the closing angle brackets are removed.
 5353    cx.update_editor(|editor, cx| {
 5354        editor.backspace(&Backspace, cx);
 5355    });
 5356    cx.assert_editor_state(
 5357        &r#"
 5358            <body>ˇ
 5359                <script>
 5360                    var x = 1;ˇ
 5361                </script>
 5362            </body>ˇ
 5363        "#
 5364        .unindent(),
 5365    );
 5366
 5367    // Block comments autoclose in JavaScript, but not HTML.
 5368    cx.update_editor(|editor, cx| {
 5369        editor.handle_input("/", cx);
 5370        editor.handle_input("*", cx);
 5371    });
 5372    cx.assert_editor_state(
 5373        &r#"
 5374            <body>/*ˇ
 5375                <script>
 5376                    var x = 1;/*ˇ */
 5377                </script>
 5378            </body>/*ˇ
 5379        "#
 5380        .unindent(),
 5381    );
 5382}
 5383
 5384#[gpui::test]
 5385async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5386    init_test(cx, |_| {});
 5387
 5388    let mut cx = EditorTestContext::new(cx).await;
 5389
 5390    let rust_language = Arc::new(
 5391        Language::new(
 5392            LanguageConfig {
 5393                name: "Rust".into(),
 5394                brackets: serde_json::from_value(json!([
 5395                    { "start": "{", "end": "}", "close": true, "newline": true },
 5396                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5397                ]))
 5398                .unwrap(),
 5399                autoclose_before: "})]>".into(),
 5400                ..Default::default()
 5401            },
 5402            Some(tree_sitter_rust::language()),
 5403        )
 5404        .with_override_query("(string_literal) @string")
 5405        .unwrap(),
 5406    );
 5407
 5408    cx.language_registry().add(rust_language.clone());
 5409    cx.update_buffer(|buffer, cx| {
 5410        buffer.set_language(Some(rust_language), cx);
 5411    });
 5412
 5413    cx.set_state(
 5414        &r#"
 5415            let x = ˇ
 5416        "#
 5417        .unindent(),
 5418    );
 5419
 5420    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5421    cx.update_editor(|editor, cx| {
 5422        editor.handle_input("\"", cx);
 5423    });
 5424    cx.assert_editor_state(
 5425        &r#"
 5426            let x = "ˇ"
 5427        "#
 5428        .unindent(),
 5429    );
 5430
 5431    // Inserting another quotation mark. The cursor moves across the existing
 5432    // automatically-inserted quotation mark.
 5433    cx.update_editor(|editor, cx| {
 5434        editor.handle_input("\"", cx);
 5435    });
 5436    cx.assert_editor_state(
 5437        &r#"
 5438            let x = ""ˇ
 5439        "#
 5440        .unindent(),
 5441    );
 5442
 5443    // Reset
 5444    cx.set_state(
 5445        &r#"
 5446            let x = ˇ
 5447        "#
 5448        .unindent(),
 5449    );
 5450
 5451    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5452    cx.update_editor(|editor, cx| {
 5453        editor.handle_input("\"", cx);
 5454        editor.handle_input(" ", cx);
 5455        editor.move_left(&Default::default(), cx);
 5456        editor.handle_input("\\", cx);
 5457        editor.handle_input("\"", cx);
 5458    });
 5459    cx.assert_editor_state(
 5460        &r#"
 5461            let x = "\"ˇ "
 5462        "#
 5463        .unindent(),
 5464    );
 5465
 5466    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5467    // mark. Nothing is inserted.
 5468    cx.update_editor(|editor, cx| {
 5469        editor.move_right(&Default::default(), cx);
 5470        editor.handle_input("\"", cx);
 5471    });
 5472    cx.assert_editor_state(
 5473        &r#"
 5474            let x = "\" "ˇ
 5475        "#
 5476        .unindent(),
 5477    );
 5478}
 5479
 5480#[gpui::test]
 5481async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5482    init_test(cx, |_| {});
 5483
 5484    let language = Arc::new(Language::new(
 5485        LanguageConfig {
 5486            brackets: BracketPairConfig {
 5487                pairs: vec![
 5488                    BracketPair {
 5489                        start: "{".to_string(),
 5490                        end: "}".to_string(),
 5491                        close: true,
 5492                        surround: true,
 5493                        newline: true,
 5494                    },
 5495                    BracketPair {
 5496                        start: "/* ".to_string(),
 5497                        end: "*/".to_string(),
 5498                        close: true,
 5499                        surround: true,
 5500                        ..Default::default()
 5501                    },
 5502                ],
 5503                ..Default::default()
 5504            },
 5505            ..Default::default()
 5506        },
 5507        Some(tree_sitter_rust::language()),
 5508    ));
 5509
 5510    let text = r#"
 5511        a
 5512        b
 5513        c
 5514    "#
 5515    .unindent();
 5516
 5517    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5518    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5519    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5520    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5521        .await;
 5522
 5523    _ = view.update(cx, |view, cx| {
 5524        view.change_selections(None, cx, |s| {
 5525            s.select_display_ranges([
 5526                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5527                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5528                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5529            ])
 5530        });
 5531
 5532        view.handle_input("{", cx);
 5533        view.handle_input("{", cx);
 5534        view.handle_input("{", cx);
 5535        assert_eq!(
 5536            view.text(cx),
 5537            "
 5538                {{{a}}}
 5539                {{{b}}}
 5540                {{{c}}}
 5541            "
 5542            .unindent()
 5543        );
 5544        assert_eq!(
 5545            view.selections.display_ranges(cx),
 5546            [
 5547                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5548                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5549                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5550            ]
 5551        );
 5552
 5553        view.undo(&Undo, cx);
 5554        view.undo(&Undo, cx);
 5555        view.undo(&Undo, cx);
 5556        assert_eq!(
 5557            view.text(cx),
 5558            "
 5559                a
 5560                b
 5561                c
 5562            "
 5563            .unindent()
 5564        );
 5565        assert_eq!(
 5566            view.selections.display_ranges(cx),
 5567            [
 5568                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5569                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5570                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5571            ]
 5572        );
 5573
 5574        // Ensure inserting the first character of a multi-byte bracket pair
 5575        // doesn't surround the selections with the bracket.
 5576        view.handle_input("/", cx);
 5577        assert_eq!(
 5578            view.text(cx),
 5579            "
 5580                /
 5581                /
 5582                /
 5583            "
 5584            .unindent()
 5585        );
 5586        assert_eq!(
 5587            view.selections.display_ranges(cx),
 5588            [
 5589                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5590                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5591                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5592            ]
 5593        );
 5594
 5595        view.undo(&Undo, cx);
 5596        assert_eq!(
 5597            view.text(cx),
 5598            "
 5599                a
 5600                b
 5601                c
 5602            "
 5603            .unindent()
 5604        );
 5605        assert_eq!(
 5606            view.selections.display_ranges(cx),
 5607            [
 5608                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5609                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5610                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5611            ]
 5612        );
 5613
 5614        // Ensure inserting the last character of a multi-byte bracket pair
 5615        // doesn't surround the selections with the bracket.
 5616        view.handle_input("*", cx);
 5617        assert_eq!(
 5618            view.text(cx),
 5619            "
 5620                *
 5621                *
 5622                *
 5623            "
 5624            .unindent()
 5625        );
 5626        assert_eq!(
 5627            view.selections.display_ranges(cx),
 5628            [
 5629                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5630                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5631                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5632            ]
 5633        );
 5634    });
 5635}
 5636
 5637#[gpui::test]
 5638async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 5639    init_test(cx, |_| {});
 5640
 5641    let language = Arc::new(Language::new(
 5642        LanguageConfig {
 5643            brackets: BracketPairConfig {
 5644                pairs: vec![BracketPair {
 5645                    start: "{".to_string(),
 5646                    end: "}".to_string(),
 5647                    close: true,
 5648                    surround: true,
 5649                    newline: true,
 5650                }],
 5651                ..Default::default()
 5652            },
 5653            autoclose_before: "}".to_string(),
 5654            ..Default::default()
 5655        },
 5656        Some(tree_sitter_rust::language()),
 5657    ));
 5658
 5659    let text = r#"
 5660        a
 5661        b
 5662        c
 5663    "#
 5664    .unindent();
 5665
 5666    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5667    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5668    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5669    editor
 5670        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5671        .await;
 5672
 5673    _ = editor.update(cx, |editor, cx| {
 5674        editor.change_selections(None, cx, |s| {
 5675            s.select_ranges([
 5676                Point::new(0, 1)..Point::new(0, 1),
 5677                Point::new(1, 1)..Point::new(1, 1),
 5678                Point::new(2, 1)..Point::new(2, 1),
 5679            ])
 5680        });
 5681
 5682        editor.handle_input("{", cx);
 5683        editor.handle_input("{", cx);
 5684        editor.handle_input("_", cx);
 5685        assert_eq!(
 5686            editor.text(cx),
 5687            "
 5688                a{{_}}
 5689                b{{_}}
 5690                c{{_}}
 5691            "
 5692            .unindent()
 5693        );
 5694        assert_eq!(
 5695            editor.selections.ranges::<Point>(cx),
 5696            [
 5697                Point::new(0, 4)..Point::new(0, 4),
 5698                Point::new(1, 4)..Point::new(1, 4),
 5699                Point::new(2, 4)..Point::new(2, 4)
 5700            ]
 5701        );
 5702
 5703        editor.backspace(&Default::default(), cx);
 5704        editor.backspace(&Default::default(), cx);
 5705        assert_eq!(
 5706            editor.text(cx),
 5707            "
 5708                a{}
 5709                b{}
 5710                c{}
 5711            "
 5712            .unindent()
 5713        );
 5714        assert_eq!(
 5715            editor.selections.ranges::<Point>(cx),
 5716            [
 5717                Point::new(0, 2)..Point::new(0, 2),
 5718                Point::new(1, 2)..Point::new(1, 2),
 5719                Point::new(2, 2)..Point::new(2, 2)
 5720            ]
 5721        );
 5722
 5723        editor.delete_to_previous_word_start(&Default::default(), cx);
 5724        assert_eq!(
 5725            editor.text(cx),
 5726            "
 5727                a
 5728                b
 5729                c
 5730            "
 5731            .unindent()
 5732        );
 5733        assert_eq!(
 5734            editor.selections.ranges::<Point>(cx),
 5735            [
 5736                Point::new(0, 1)..Point::new(0, 1),
 5737                Point::new(1, 1)..Point::new(1, 1),
 5738                Point::new(2, 1)..Point::new(2, 1)
 5739            ]
 5740        );
 5741    });
 5742}
 5743
 5744#[gpui::test]
 5745async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 5746    init_test(cx, |settings| {
 5747        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5748    });
 5749
 5750    let mut cx = EditorTestContext::new(cx).await;
 5751
 5752    let language = Arc::new(Language::new(
 5753        LanguageConfig {
 5754            brackets: BracketPairConfig {
 5755                pairs: vec![
 5756                    BracketPair {
 5757                        start: "{".to_string(),
 5758                        end: "}".to_string(),
 5759                        close: true,
 5760                        surround: true,
 5761                        newline: true,
 5762                    },
 5763                    BracketPair {
 5764                        start: "(".to_string(),
 5765                        end: ")".to_string(),
 5766                        close: true,
 5767                        surround: true,
 5768                        newline: true,
 5769                    },
 5770                    BracketPair {
 5771                        start: "[".to_string(),
 5772                        end: "]".to_string(),
 5773                        close: false,
 5774                        surround: true,
 5775                        newline: true,
 5776                    },
 5777                ],
 5778                ..Default::default()
 5779            },
 5780            autoclose_before: "})]".to_string(),
 5781            ..Default::default()
 5782        },
 5783        Some(tree_sitter_rust::language()),
 5784    ));
 5785
 5786    cx.language_registry().add(language.clone());
 5787    cx.update_buffer(|buffer, cx| {
 5788        buffer.set_language(Some(language), cx);
 5789    });
 5790
 5791    cx.set_state(
 5792        &"
 5793            {(ˇ)}
 5794            [[ˇ]]
 5795            {(ˇ)}
 5796        "
 5797        .unindent(),
 5798    );
 5799
 5800    cx.update_editor(|view, cx| {
 5801        view.backspace(&Default::default(), cx);
 5802        view.backspace(&Default::default(), cx);
 5803    });
 5804
 5805    cx.assert_editor_state(
 5806        &"
 5807            ˇ
 5808            ˇ]]
 5809            ˇ
 5810        "
 5811        .unindent(),
 5812    );
 5813
 5814    cx.update_editor(|view, cx| {
 5815        view.handle_input("{", cx);
 5816        view.handle_input("{", cx);
 5817        view.move_right(&MoveRight, cx);
 5818        view.move_right(&MoveRight, cx);
 5819        view.move_left(&MoveLeft, cx);
 5820        view.move_left(&MoveLeft, cx);
 5821        view.backspace(&Default::default(), cx);
 5822    });
 5823
 5824    cx.assert_editor_state(
 5825        &"
 5826            {ˇ}
 5827            {ˇ}]]
 5828            {ˇ}
 5829        "
 5830        .unindent(),
 5831    );
 5832
 5833    cx.update_editor(|view, cx| {
 5834        view.backspace(&Default::default(), cx);
 5835    });
 5836
 5837    cx.assert_editor_state(
 5838        &"
 5839            ˇ
 5840            ˇ]]
 5841            ˇ
 5842        "
 5843        .unindent(),
 5844    );
 5845}
 5846
 5847#[gpui::test]
 5848async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 5849    init_test(cx, |_| {});
 5850
 5851    let language = Arc::new(Language::new(
 5852        LanguageConfig::default(),
 5853        Some(tree_sitter_rust::language()),
 5854    ));
 5855
 5856    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 5857    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5858    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5859    editor
 5860        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5861        .await;
 5862
 5863    _ = editor.update(cx, |editor, cx| {
 5864        editor.set_auto_replace_emoji_shortcode(true);
 5865
 5866        editor.handle_input("Hello ", cx);
 5867        editor.handle_input(":wave", cx);
 5868        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 5869
 5870        editor.handle_input(":", cx);
 5871        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 5872
 5873        editor.handle_input(" :smile", cx);
 5874        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 5875
 5876        editor.handle_input(":", cx);
 5877        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 5878
 5879        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 5880        editor.handle_input(":wave", cx);
 5881        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 5882
 5883        editor.handle_input(":", cx);
 5884        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 5885
 5886        editor.handle_input(":1", cx);
 5887        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 5888
 5889        editor.handle_input(":", cx);
 5890        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 5891
 5892        // Ensure shortcode does not get replaced when it is part of a word
 5893        editor.handle_input(" Test:wave", cx);
 5894        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 5895
 5896        editor.handle_input(":", cx);
 5897        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 5898
 5899        editor.set_auto_replace_emoji_shortcode(false);
 5900
 5901        // Ensure shortcode does not get replaced when auto replace is off
 5902        editor.handle_input(" :wave", cx);
 5903        assert_eq!(
 5904            editor.text(cx),
 5905            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 5906        );
 5907
 5908        editor.handle_input(":", cx);
 5909        assert_eq!(
 5910            editor.text(cx),
 5911            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 5912        );
 5913    });
 5914}
 5915
 5916#[gpui::test]
 5917async fn test_snippets(cx: &mut gpui::TestAppContext) {
 5918    init_test(cx, |_| {});
 5919
 5920    let (text, insertion_ranges) = marked_text_ranges(
 5921        indoc! {"
 5922            a.ˇ b
 5923            a.ˇ b
 5924            a.ˇ b
 5925        "},
 5926        false,
 5927    );
 5928
 5929    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 5930    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5931
 5932    _ = editor.update(cx, |editor, cx| {
 5933        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 5934
 5935        editor
 5936            .insert_snippet(&insertion_ranges, snippet, cx)
 5937            .unwrap();
 5938
 5939        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 5940            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 5941            assert_eq!(editor.text(cx), expected_text);
 5942            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 5943        }
 5944
 5945        assert(
 5946            editor,
 5947            cx,
 5948            indoc! {"
 5949                a.f(«one», two, «three») b
 5950                a.f(«one», two, «three») b
 5951                a.f(«one», two, «three») b
 5952            "},
 5953        );
 5954
 5955        // Can't move earlier than the first tab stop
 5956        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 5957        assert(
 5958            editor,
 5959            cx,
 5960            indoc! {"
 5961                a.f(«one», two, «three») b
 5962                a.f(«one», two, «three») b
 5963                a.f(«one», two, «three») b
 5964            "},
 5965        );
 5966
 5967        assert!(editor.move_to_next_snippet_tabstop(cx));
 5968        assert(
 5969            editor,
 5970            cx,
 5971            indoc! {"
 5972                a.f(one, «two», three) b
 5973                a.f(one, «two», three) b
 5974                a.f(one, «two», three) b
 5975            "},
 5976        );
 5977
 5978        editor.move_to_prev_snippet_tabstop(cx);
 5979        assert(
 5980            editor,
 5981            cx,
 5982            indoc! {"
 5983                a.f(«one», two, «three») b
 5984                a.f(«one», two, «three») b
 5985                a.f(«one», two, «three») b
 5986            "},
 5987        );
 5988
 5989        assert!(editor.move_to_next_snippet_tabstop(cx));
 5990        assert(
 5991            editor,
 5992            cx,
 5993            indoc! {"
 5994                a.f(one, «two», three) b
 5995                a.f(one, «two», three) b
 5996                a.f(one, «two», three) b
 5997            "},
 5998        );
 5999        assert!(editor.move_to_next_snippet_tabstop(cx));
 6000        assert(
 6001            editor,
 6002            cx,
 6003            indoc! {"
 6004                a.f(one, two, three)ˇ b
 6005                a.f(one, two, three)ˇ b
 6006                a.f(one, two, three)ˇ b
 6007            "},
 6008        );
 6009
 6010        // As soon as the last tab stop is reached, snippet state is gone
 6011        editor.move_to_prev_snippet_tabstop(cx);
 6012        assert(
 6013            editor,
 6014            cx,
 6015            indoc! {"
 6016                a.f(one, two, three)ˇ b
 6017                a.f(one, two, three)ˇ b
 6018                a.f(one, two, three)ˇ b
 6019            "},
 6020        );
 6021    });
 6022}
 6023
 6024#[gpui::test]
 6025async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6026    init_test(cx, |_| {});
 6027
 6028    let fs = FakeFs::new(cx.executor());
 6029    fs.insert_file("/file.rs", Default::default()).await;
 6030
 6031    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6032
 6033    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6034    language_registry.add(rust_lang());
 6035    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6036        "Rust",
 6037        FakeLspAdapter {
 6038            capabilities: lsp::ServerCapabilities {
 6039                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6040                ..Default::default()
 6041            },
 6042            ..Default::default()
 6043        },
 6044    );
 6045
 6046    let buffer = project
 6047        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6048        .await
 6049        .unwrap();
 6050
 6051    cx.executor().start_waiting();
 6052    let fake_server = fake_servers.next().await.unwrap();
 6053
 6054    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6055    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6056    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6057    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6058
 6059    let save = editor
 6060        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6061        .unwrap();
 6062    fake_server
 6063        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6064            assert_eq!(
 6065                params.text_document.uri,
 6066                lsp::Url::from_file_path("/file.rs").unwrap()
 6067            );
 6068            assert_eq!(params.options.tab_size, 4);
 6069            Ok(Some(vec![lsp::TextEdit::new(
 6070                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6071                ", ".to_string(),
 6072            )]))
 6073        })
 6074        .next()
 6075        .await;
 6076    cx.executor().start_waiting();
 6077    save.await;
 6078
 6079    assert_eq!(
 6080        editor.update(cx, |editor, cx| editor.text(cx)),
 6081        "one, two\nthree\n"
 6082    );
 6083    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6084
 6085    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6086    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6087
 6088    // Ensure we can still save even if formatting hangs.
 6089    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6090        assert_eq!(
 6091            params.text_document.uri,
 6092            lsp::Url::from_file_path("/file.rs").unwrap()
 6093        );
 6094        futures::future::pending::<()>().await;
 6095        unreachable!()
 6096    });
 6097    let save = editor
 6098        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6099        .unwrap();
 6100    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6101    cx.executor().start_waiting();
 6102    save.await;
 6103    assert_eq!(
 6104        editor.update(cx, |editor, cx| editor.text(cx)),
 6105        "one\ntwo\nthree\n"
 6106    );
 6107    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6108
 6109    // For non-dirty buffer, no formatting request should be sent
 6110    let save = editor
 6111        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6112        .unwrap();
 6113    let _pending_format_request = fake_server
 6114        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6115            panic!("Should not be invoked on non-dirty buffer");
 6116        })
 6117        .next();
 6118    cx.executor().start_waiting();
 6119    save.await;
 6120
 6121    // Set rust language override and assert overridden tabsize is sent to language server
 6122    update_test_language_settings(cx, |settings| {
 6123        settings.languages.insert(
 6124            "Rust".into(),
 6125            LanguageSettingsContent {
 6126                tab_size: NonZeroU32::new(8),
 6127                ..Default::default()
 6128            },
 6129        );
 6130    });
 6131
 6132    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6133    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6134    let save = editor
 6135        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6136        .unwrap();
 6137    fake_server
 6138        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6139            assert_eq!(
 6140                params.text_document.uri,
 6141                lsp::Url::from_file_path("/file.rs").unwrap()
 6142            );
 6143            assert_eq!(params.options.tab_size, 8);
 6144            Ok(Some(vec![]))
 6145        })
 6146        .next()
 6147        .await;
 6148    cx.executor().start_waiting();
 6149    save.await;
 6150}
 6151
 6152#[gpui::test]
 6153async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6154    init_test(cx, |_| {});
 6155
 6156    let cols = 4;
 6157    let rows = 10;
 6158    let sample_text_1 = sample_text(rows, cols, 'a');
 6159    assert_eq!(
 6160        sample_text_1,
 6161        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6162    );
 6163    let sample_text_2 = sample_text(rows, cols, 'l');
 6164    assert_eq!(
 6165        sample_text_2,
 6166        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6167    );
 6168    let sample_text_3 = sample_text(rows, cols, 'v');
 6169    assert_eq!(
 6170        sample_text_3,
 6171        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6172    );
 6173
 6174    let fs = FakeFs::new(cx.executor());
 6175    fs.insert_tree(
 6176        "/a",
 6177        json!({
 6178            "main.rs": sample_text_1,
 6179            "other.rs": sample_text_2,
 6180            "lib.rs": sample_text_3,
 6181        }),
 6182    )
 6183    .await;
 6184
 6185    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6186    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6187    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6188
 6189    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6190    language_registry.add(rust_lang());
 6191    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6192        "Rust",
 6193        FakeLspAdapter {
 6194            capabilities: lsp::ServerCapabilities {
 6195                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6196                ..Default::default()
 6197            },
 6198            ..Default::default()
 6199        },
 6200    );
 6201
 6202    let worktree = project.update(cx, |project, _| {
 6203        let mut worktrees = project.worktrees().collect::<Vec<_>>();
 6204        assert_eq!(worktrees.len(), 1);
 6205        worktrees.pop().unwrap()
 6206    });
 6207    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6208
 6209    let buffer_1 = project
 6210        .update(cx, |project, cx| {
 6211            project.open_buffer((worktree_id, "main.rs"), cx)
 6212        })
 6213        .await
 6214        .unwrap();
 6215    let buffer_2 = project
 6216        .update(cx, |project, cx| {
 6217            project.open_buffer((worktree_id, "other.rs"), cx)
 6218        })
 6219        .await
 6220        .unwrap();
 6221    let buffer_3 = project
 6222        .update(cx, |project, cx| {
 6223            project.open_buffer((worktree_id, "lib.rs"), cx)
 6224        })
 6225        .await
 6226        .unwrap();
 6227
 6228    let multi_buffer = cx.new_model(|cx| {
 6229        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 6230        multi_buffer.push_excerpts(
 6231            buffer_1.clone(),
 6232            [
 6233                ExcerptRange {
 6234                    context: Point::new(0, 0)..Point::new(3, 0),
 6235                    primary: None,
 6236                },
 6237                ExcerptRange {
 6238                    context: Point::new(5, 0)..Point::new(7, 0),
 6239                    primary: None,
 6240                },
 6241                ExcerptRange {
 6242                    context: Point::new(9, 0)..Point::new(10, 4),
 6243                    primary: None,
 6244                },
 6245            ],
 6246            cx,
 6247        );
 6248        multi_buffer.push_excerpts(
 6249            buffer_2.clone(),
 6250            [
 6251                ExcerptRange {
 6252                    context: Point::new(0, 0)..Point::new(3, 0),
 6253                    primary: None,
 6254                },
 6255                ExcerptRange {
 6256                    context: Point::new(5, 0)..Point::new(7, 0),
 6257                    primary: None,
 6258                },
 6259                ExcerptRange {
 6260                    context: Point::new(9, 0)..Point::new(10, 4),
 6261                    primary: None,
 6262                },
 6263            ],
 6264            cx,
 6265        );
 6266        multi_buffer.push_excerpts(
 6267            buffer_3.clone(),
 6268            [
 6269                ExcerptRange {
 6270                    context: Point::new(0, 0)..Point::new(3, 0),
 6271                    primary: None,
 6272                },
 6273                ExcerptRange {
 6274                    context: Point::new(5, 0)..Point::new(7, 0),
 6275                    primary: None,
 6276                },
 6277                ExcerptRange {
 6278                    context: Point::new(9, 0)..Point::new(10, 4),
 6279                    primary: None,
 6280                },
 6281            ],
 6282            cx,
 6283        );
 6284        multi_buffer
 6285    });
 6286    let multi_buffer_editor = cx.new_view(|cx| {
 6287        Editor::new(
 6288            EditorMode::Full,
 6289            multi_buffer,
 6290            Some(project.clone()),
 6291            true,
 6292            cx,
 6293        )
 6294    });
 6295
 6296    multi_buffer_editor.update(cx, |editor, cx| {
 6297        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6298        editor.insert("|one|two|three|", cx);
 6299    });
 6300    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6301    multi_buffer_editor.update(cx, |editor, cx| {
 6302        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6303            s.select_ranges(Some(60..70))
 6304        });
 6305        editor.insert("|four|five|six|", cx);
 6306    });
 6307    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6308
 6309    // First two buffers should be edited, but not the third one.
 6310    assert_eq!(
 6311        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6312        "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}",
 6313    );
 6314    buffer_1.update(cx, |buffer, _| {
 6315        assert!(buffer.is_dirty());
 6316        assert_eq!(
 6317            buffer.text(),
 6318            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6319        )
 6320    });
 6321    buffer_2.update(cx, |buffer, _| {
 6322        assert!(buffer.is_dirty());
 6323        assert_eq!(
 6324            buffer.text(),
 6325            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6326        )
 6327    });
 6328    buffer_3.update(cx, |buffer, _| {
 6329        assert!(!buffer.is_dirty());
 6330        assert_eq!(buffer.text(), sample_text_3,)
 6331    });
 6332
 6333    cx.executor().start_waiting();
 6334    let save = multi_buffer_editor
 6335        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6336        .unwrap();
 6337
 6338    let fake_server = fake_servers.next().await.unwrap();
 6339    fake_server
 6340        .server
 6341        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6342            Ok(Some(vec![lsp::TextEdit::new(
 6343                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6344                format!("[{} formatted]", params.text_document.uri),
 6345            )]))
 6346        })
 6347        .detach();
 6348    save.await;
 6349
 6350    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6351    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6352    assert_eq!(
 6353        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6354        "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}",
 6355    );
 6356    buffer_1.update(cx, |buffer, _| {
 6357        assert!(!buffer.is_dirty());
 6358        assert_eq!(
 6359            buffer.text(),
 6360            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6361        )
 6362    });
 6363    buffer_2.update(cx, |buffer, _| {
 6364        assert!(!buffer.is_dirty());
 6365        assert_eq!(
 6366            buffer.text(),
 6367            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6368        )
 6369    });
 6370    buffer_3.update(cx, |buffer, _| {
 6371        assert!(!buffer.is_dirty());
 6372        assert_eq!(buffer.text(), sample_text_3,)
 6373    });
 6374}
 6375
 6376#[gpui::test]
 6377async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6378    init_test(cx, |_| {});
 6379
 6380    let fs = FakeFs::new(cx.executor());
 6381    fs.insert_file("/file.rs", Default::default()).await;
 6382
 6383    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6384
 6385    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6386    language_registry.add(rust_lang());
 6387    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6388        "Rust",
 6389        FakeLspAdapter {
 6390            capabilities: lsp::ServerCapabilities {
 6391                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6392                ..Default::default()
 6393            },
 6394            ..Default::default()
 6395        },
 6396    );
 6397
 6398    let buffer = project
 6399        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6400        .await
 6401        .unwrap();
 6402
 6403    cx.executor().start_waiting();
 6404    let fake_server = fake_servers.next().await.unwrap();
 6405
 6406    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6407    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6408    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6409    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6410
 6411    let save = editor
 6412        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6413        .unwrap();
 6414    fake_server
 6415        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6416            assert_eq!(
 6417                params.text_document.uri,
 6418                lsp::Url::from_file_path("/file.rs").unwrap()
 6419            );
 6420            assert_eq!(params.options.tab_size, 4);
 6421            Ok(Some(vec![lsp::TextEdit::new(
 6422                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6423                ", ".to_string(),
 6424            )]))
 6425        })
 6426        .next()
 6427        .await;
 6428    cx.executor().start_waiting();
 6429    save.await;
 6430    assert_eq!(
 6431        editor.update(cx, |editor, cx| editor.text(cx)),
 6432        "one, two\nthree\n"
 6433    );
 6434    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6435
 6436    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6437    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6438
 6439    // Ensure we can still save even if formatting hangs.
 6440    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6441        move |params, _| async move {
 6442            assert_eq!(
 6443                params.text_document.uri,
 6444                lsp::Url::from_file_path("/file.rs").unwrap()
 6445            );
 6446            futures::future::pending::<()>().await;
 6447            unreachable!()
 6448        },
 6449    );
 6450    let save = editor
 6451        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6452        .unwrap();
 6453    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6454    cx.executor().start_waiting();
 6455    save.await;
 6456    assert_eq!(
 6457        editor.update(cx, |editor, cx| editor.text(cx)),
 6458        "one\ntwo\nthree\n"
 6459    );
 6460    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6461
 6462    // For non-dirty buffer, no formatting request should be sent
 6463    let save = editor
 6464        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6465        .unwrap();
 6466    let _pending_format_request = fake_server
 6467        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6468            panic!("Should not be invoked on non-dirty buffer");
 6469        })
 6470        .next();
 6471    cx.executor().start_waiting();
 6472    save.await;
 6473
 6474    // Set Rust language override and assert overridden tabsize is sent to language server
 6475    update_test_language_settings(cx, |settings| {
 6476        settings.languages.insert(
 6477            "Rust".into(),
 6478            LanguageSettingsContent {
 6479                tab_size: NonZeroU32::new(8),
 6480                ..Default::default()
 6481            },
 6482        );
 6483    });
 6484
 6485    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6486    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6487    let save = editor
 6488        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6489        .unwrap();
 6490    fake_server
 6491        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6492            assert_eq!(
 6493                params.text_document.uri,
 6494                lsp::Url::from_file_path("/file.rs").unwrap()
 6495            );
 6496            assert_eq!(params.options.tab_size, 8);
 6497            Ok(Some(vec![]))
 6498        })
 6499        .next()
 6500        .await;
 6501    cx.executor().start_waiting();
 6502    save.await;
 6503}
 6504
 6505#[gpui::test]
 6506async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6507    init_test(cx, |settings| {
 6508        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
 6509    });
 6510
 6511    let fs = FakeFs::new(cx.executor());
 6512    fs.insert_file("/file.rs", Default::default()).await;
 6513
 6514    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6515
 6516    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6517    language_registry.add(Arc::new(Language::new(
 6518        LanguageConfig {
 6519            name: "Rust".into(),
 6520            matcher: LanguageMatcher {
 6521                path_suffixes: vec!["rs".to_string()],
 6522                ..Default::default()
 6523            },
 6524            ..LanguageConfig::default()
 6525        },
 6526        Some(tree_sitter_rust::language()),
 6527    )));
 6528    update_test_language_settings(cx, |settings| {
 6529        // Enable Prettier formatting for the same buffer, and ensure
 6530        // LSP is called instead of Prettier.
 6531        settings.defaults.prettier = Some(PrettierSettings {
 6532            allowed: true,
 6533            ..PrettierSettings::default()
 6534        });
 6535    });
 6536    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6537        "Rust",
 6538        FakeLspAdapter {
 6539            capabilities: lsp::ServerCapabilities {
 6540                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6541                ..Default::default()
 6542            },
 6543            ..Default::default()
 6544        },
 6545    );
 6546
 6547    let buffer = project
 6548        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6549        .await
 6550        .unwrap();
 6551
 6552    cx.executor().start_waiting();
 6553    let fake_server = fake_servers.next().await.unwrap();
 6554
 6555    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6556    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6557    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6558
 6559    let format = editor
 6560        .update(cx, |editor, cx| {
 6561            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6562        })
 6563        .unwrap();
 6564    fake_server
 6565        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6566            assert_eq!(
 6567                params.text_document.uri,
 6568                lsp::Url::from_file_path("/file.rs").unwrap()
 6569            );
 6570            assert_eq!(params.options.tab_size, 4);
 6571            Ok(Some(vec![lsp::TextEdit::new(
 6572                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6573                ", ".to_string(),
 6574            )]))
 6575        })
 6576        .next()
 6577        .await;
 6578    cx.executor().start_waiting();
 6579    format.await;
 6580    assert_eq!(
 6581        editor.update(cx, |editor, cx| editor.text(cx)),
 6582        "one, two\nthree\n"
 6583    );
 6584
 6585    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6586    // Ensure we don't lock if formatting hangs.
 6587    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6588        assert_eq!(
 6589            params.text_document.uri,
 6590            lsp::Url::from_file_path("/file.rs").unwrap()
 6591        );
 6592        futures::future::pending::<()>().await;
 6593        unreachable!()
 6594    });
 6595    let format = editor
 6596        .update(cx, |editor, cx| {
 6597            editor.perform_format(project, FormatTrigger::Manual, cx)
 6598        })
 6599        .unwrap();
 6600    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6601    cx.executor().start_waiting();
 6602    format.await;
 6603    assert_eq!(
 6604        editor.update(cx, |editor, cx| editor.text(cx)),
 6605        "one\ntwo\nthree\n"
 6606    );
 6607}
 6608
 6609#[gpui::test]
 6610async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6611    init_test(cx, |_| {});
 6612
 6613    let mut cx = EditorLspTestContext::new_rust(
 6614        lsp::ServerCapabilities {
 6615            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6616            ..Default::default()
 6617        },
 6618        cx,
 6619    )
 6620    .await;
 6621
 6622    cx.set_state(indoc! {"
 6623        one.twoˇ
 6624    "});
 6625
 6626    // The format request takes a long time. When it completes, it inserts
 6627    // a newline and an indent before the `.`
 6628    cx.lsp
 6629        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6630            let executor = cx.background_executor().clone();
 6631            async move {
 6632                executor.timer(Duration::from_millis(100)).await;
 6633                Ok(Some(vec![lsp::TextEdit {
 6634                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6635                    new_text: "\n    ".into(),
 6636                }]))
 6637            }
 6638        });
 6639
 6640    // Submit a format request.
 6641    let format_1 = cx
 6642        .update_editor(|editor, cx| editor.format(&Format, cx))
 6643        .unwrap();
 6644    cx.executor().run_until_parked();
 6645
 6646    // Submit a second format request.
 6647    let format_2 = cx
 6648        .update_editor(|editor, cx| editor.format(&Format, cx))
 6649        .unwrap();
 6650    cx.executor().run_until_parked();
 6651
 6652    // Wait for both format requests to complete
 6653    cx.executor().advance_clock(Duration::from_millis(200));
 6654    cx.executor().start_waiting();
 6655    format_1.await.unwrap();
 6656    cx.executor().start_waiting();
 6657    format_2.await.unwrap();
 6658
 6659    // The formatting edits only happens once.
 6660    cx.assert_editor_state(indoc! {"
 6661        one
 6662            .twoˇ
 6663    "});
 6664}
 6665
 6666#[gpui::test]
 6667async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6668    init_test(cx, |settings| {
 6669        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 6670    });
 6671
 6672    let mut cx = EditorLspTestContext::new_rust(
 6673        lsp::ServerCapabilities {
 6674            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6675            ..Default::default()
 6676        },
 6677        cx,
 6678    )
 6679    .await;
 6680
 6681    // Set up a buffer white some trailing whitespace and no trailing newline.
 6682    cx.set_state(
 6683        &[
 6684            "one ",   //
 6685            "twoˇ",   //
 6686            "three ", //
 6687            "four",   //
 6688        ]
 6689        .join("\n"),
 6690    );
 6691
 6692    // Submit a format request.
 6693    let format = cx
 6694        .update_editor(|editor, cx| editor.format(&Format, cx))
 6695        .unwrap();
 6696
 6697    // Record which buffer changes have been sent to the language server
 6698    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6699    cx.lsp
 6700        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6701            let buffer_changes = buffer_changes.clone();
 6702            move |params, _| {
 6703                buffer_changes.lock().extend(
 6704                    params
 6705                        .content_changes
 6706                        .into_iter()
 6707                        .map(|e| (e.range.unwrap(), e.text)),
 6708                );
 6709            }
 6710        });
 6711
 6712    // Handle formatting requests to the language server.
 6713    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6714        let buffer_changes = buffer_changes.clone();
 6715        move |_, _| {
 6716            // When formatting is requested, trailing whitespace has already been stripped,
 6717            // and the trailing newline has already been added.
 6718            assert_eq!(
 6719                &buffer_changes.lock()[1..],
 6720                &[
 6721                    (
 6722                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6723                        "".into()
 6724                    ),
 6725                    (
 6726                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6727                        "".into()
 6728                    ),
 6729                    (
 6730                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6731                        "\n".into()
 6732                    ),
 6733                ]
 6734            );
 6735
 6736            // Insert blank lines between each line of the buffer.
 6737            async move {
 6738                Ok(Some(vec![
 6739                    lsp::TextEdit {
 6740                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6741                        new_text: "\n".into(),
 6742                    },
 6743                    lsp::TextEdit {
 6744                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6745                        new_text: "\n".into(),
 6746                    },
 6747                ]))
 6748            }
 6749        }
 6750    });
 6751
 6752    // After formatting the buffer, the trailing whitespace is stripped,
 6753    // a newline is appended, and the edits provided by the language server
 6754    // have been applied.
 6755    format.await.unwrap();
 6756    cx.assert_editor_state(
 6757        &[
 6758            "one",   //
 6759            "",      //
 6760            "twoˇ",  //
 6761            "",      //
 6762            "three", //
 6763            "four",  //
 6764            "",      //
 6765        ]
 6766        .join("\n"),
 6767    );
 6768
 6769    // Undoing the formatting undoes the trailing whitespace removal, the
 6770    // trailing newline, and the LSP edits.
 6771    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6772    cx.assert_editor_state(
 6773        &[
 6774            "one ",   //
 6775            "twoˇ",   //
 6776            "three ", //
 6777            "four",   //
 6778        ]
 6779        .join("\n"),
 6780    );
 6781}
 6782
 6783#[gpui::test]
 6784async fn test_completion(cx: &mut gpui::TestAppContext) {
 6785    init_test(cx, |_| {});
 6786
 6787    let mut cx = EditorLspTestContext::new_rust(
 6788        lsp::ServerCapabilities {
 6789            completion_provider: Some(lsp::CompletionOptions {
 6790                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 6791                resolve_provider: Some(true),
 6792                ..Default::default()
 6793            }),
 6794            ..Default::default()
 6795        },
 6796        cx,
 6797    )
 6798    .await;
 6799    let counter = Arc::new(AtomicUsize::new(0));
 6800
 6801    cx.set_state(indoc! {"
 6802        oneˇ
 6803        two
 6804        three
 6805    "});
 6806    cx.simulate_keystroke(".");
 6807    handle_completion_request(
 6808        &mut cx,
 6809        indoc! {"
 6810            one.|<>
 6811            two
 6812            three
 6813        "},
 6814        vec!["first_completion", "second_completion"],
 6815        counter.clone(),
 6816    )
 6817    .await;
 6818    cx.condition(|editor, _| editor.context_menu_visible())
 6819        .await;
 6820    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 6821
 6822    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6823        editor.context_menu_next(&Default::default(), cx);
 6824        editor
 6825            .confirm_completion(&ConfirmCompletion::default(), cx)
 6826            .unwrap()
 6827    });
 6828    cx.assert_editor_state(indoc! {"
 6829        one.second_completionˇ
 6830        two
 6831        three
 6832    "});
 6833
 6834    handle_resolve_completion_request(
 6835        &mut cx,
 6836        Some(vec![
 6837            (
 6838                //This overlaps with the primary completion edit which is
 6839                //misbehavior from the LSP spec, test that we filter it out
 6840                indoc! {"
 6841                    one.second_ˇcompletion
 6842                    two
 6843                    threeˇ
 6844                "},
 6845                "overlapping additional edit",
 6846            ),
 6847            (
 6848                indoc! {"
 6849                    one.second_completion
 6850                    two
 6851                    threeˇ
 6852                "},
 6853                "\nadditional edit",
 6854            ),
 6855        ]),
 6856    )
 6857    .await;
 6858    apply_additional_edits.await.unwrap();
 6859    cx.assert_editor_state(indoc! {"
 6860        one.second_completionˇ
 6861        two
 6862        three
 6863        additional edit
 6864    "});
 6865
 6866    cx.set_state(indoc! {"
 6867        one.second_completion
 6868        twoˇ
 6869        threeˇ
 6870        additional edit
 6871    "});
 6872    cx.simulate_keystroke(" ");
 6873    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6874    cx.simulate_keystroke("s");
 6875    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6876
 6877    cx.assert_editor_state(indoc! {"
 6878        one.second_completion
 6879        two sˇ
 6880        three sˇ
 6881        additional edit
 6882    "});
 6883    handle_completion_request(
 6884        &mut cx,
 6885        indoc! {"
 6886            one.second_completion
 6887            two s
 6888            three <s|>
 6889            additional edit
 6890        "},
 6891        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 6892        counter.clone(),
 6893    )
 6894    .await;
 6895    cx.condition(|editor, _| editor.context_menu_visible())
 6896        .await;
 6897    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 6898
 6899    cx.simulate_keystroke("i");
 6900
 6901    handle_completion_request(
 6902        &mut cx,
 6903        indoc! {"
 6904            one.second_completion
 6905            two si
 6906            three <si|>
 6907            additional edit
 6908        "},
 6909        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 6910        counter.clone(),
 6911    )
 6912    .await;
 6913    cx.condition(|editor, _| editor.context_menu_visible())
 6914        .await;
 6915    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 6916
 6917    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6918        editor
 6919            .confirm_completion(&ConfirmCompletion::default(), cx)
 6920            .unwrap()
 6921    });
 6922    cx.assert_editor_state(indoc! {"
 6923        one.second_completion
 6924        two sixth_completionˇ
 6925        three sixth_completionˇ
 6926        additional edit
 6927    "});
 6928
 6929    handle_resolve_completion_request(&mut cx, None).await;
 6930    apply_additional_edits.await.unwrap();
 6931
 6932    _ = cx.update(|cx| {
 6933        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6934            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6935                settings.show_completions_on_input = Some(false);
 6936            });
 6937        })
 6938    });
 6939    cx.set_state("editorˇ");
 6940    cx.simulate_keystroke(".");
 6941    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6942    cx.simulate_keystroke("c");
 6943    cx.simulate_keystroke("l");
 6944    cx.simulate_keystroke("o");
 6945    cx.assert_editor_state("editor.cloˇ");
 6946    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6947    cx.update_editor(|editor, cx| {
 6948        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 6949    });
 6950    handle_completion_request(
 6951        &mut cx,
 6952        "editor.<clo|>",
 6953        vec!["close", "clobber"],
 6954        counter.clone(),
 6955    )
 6956    .await;
 6957    cx.condition(|editor, _| editor.context_menu_visible())
 6958        .await;
 6959    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 6960
 6961    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6962        editor
 6963            .confirm_completion(&ConfirmCompletion::default(), cx)
 6964            .unwrap()
 6965    });
 6966    cx.assert_editor_state("editor.closeˇ");
 6967    handle_resolve_completion_request(&mut cx, None).await;
 6968    apply_additional_edits.await.unwrap();
 6969}
 6970
 6971#[gpui::test]
 6972async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 6973    init_test(cx, |_| {});
 6974
 6975    let mut cx = EditorLspTestContext::new_rust(
 6976        lsp::ServerCapabilities {
 6977            completion_provider: Some(lsp::CompletionOptions {
 6978                trigger_characters: Some(vec![".".to_string()]),
 6979                resolve_provider: Some(true),
 6980                ..Default::default()
 6981            }),
 6982            ..Default::default()
 6983        },
 6984        cx,
 6985    )
 6986    .await;
 6987
 6988    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 6989    cx.simulate_keystroke(".");
 6990    let completion_item = lsp::CompletionItem {
 6991        label: "Some".into(),
 6992        kind: Some(lsp::CompletionItemKind::SNIPPET),
 6993        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 6994        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 6995            kind: lsp::MarkupKind::Markdown,
 6996            value: "```rust\nSome(2)\n```".to_string(),
 6997        })),
 6998        deprecated: Some(false),
 6999        sort_text: Some("Some".to_string()),
 7000        filter_text: Some("Some".to_string()),
 7001        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 7002        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 7003            range: lsp::Range {
 7004                start: lsp::Position {
 7005                    line: 0,
 7006                    character: 22,
 7007                },
 7008                end: lsp::Position {
 7009                    line: 0,
 7010                    character: 22,
 7011                },
 7012            },
 7013            new_text: "Some(2)".to_string(),
 7014        })),
 7015        additional_text_edits: Some(vec![lsp::TextEdit {
 7016            range: lsp::Range {
 7017                start: lsp::Position {
 7018                    line: 0,
 7019                    character: 20,
 7020                },
 7021                end: lsp::Position {
 7022                    line: 0,
 7023                    character: 22,
 7024                },
 7025            },
 7026            new_text: "".to_string(),
 7027        }]),
 7028        ..Default::default()
 7029    };
 7030
 7031    let closure_completion_item = completion_item.clone();
 7032    let counter = Arc::new(AtomicUsize::new(0));
 7033    let counter_clone = counter.clone();
 7034    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 7035        let task_completion_item = closure_completion_item.clone();
 7036        counter_clone.fetch_add(1, atomic::Ordering::Release);
 7037        async move {
 7038            Ok(Some(lsp::CompletionResponse::Array(vec![
 7039                task_completion_item,
 7040            ])))
 7041        }
 7042    });
 7043
 7044    cx.condition(|editor, _| editor.context_menu_visible())
 7045        .await;
 7046    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 7047    assert!(request.next().await.is_some());
 7048    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7049
 7050    cx.simulate_keystroke("S");
 7051    cx.simulate_keystroke("o");
 7052    cx.simulate_keystroke("m");
 7053    cx.condition(|editor, _| editor.context_menu_visible())
 7054        .await;
 7055    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 7056    assert!(request.next().await.is_some());
 7057    assert!(request.next().await.is_some());
 7058    assert!(request.next().await.is_some());
 7059    request.close();
 7060    assert!(request.next().await.is_none());
 7061    assert_eq!(
 7062        counter.load(atomic::Ordering::Acquire),
 7063        4,
 7064        "With the completions menu open, only one LSP request should happen per input"
 7065    );
 7066}
 7067
 7068#[gpui::test]
 7069async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 7070    init_test(cx, |_| {});
 7071    let mut cx = EditorTestContext::new(cx).await;
 7072    let language = Arc::new(Language::new(
 7073        LanguageConfig {
 7074            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 7075            ..Default::default()
 7076        },
 7077        Some(tree_sitter_rust::language()),
 7078    ));
 7079    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 7080
 7081    // If multiple selections intersect a line, the line is only toggled once.
 7082    cx.set_state(indoc! {"
 7083        fn a() {
 7084            «//b();
 7085            ˇ»// «c();
 7086            //ˇ»  d();
 7087        }
 7088    "});
 7089
 7090    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7091
 7092    cx.assert_editor_state(indoc! {"
 7093        fn a() {
 7094            «b();
 7095            c();
 7096            ˇ» d();
 7097        }
 7098    "});
 7099
 7100    // The comment prefix is inserted at the same column for every line in a
 7101    // selection.
 7102    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7103
 7104    cx.assert_editor_state(indoc! {"
 7105        fn a() {
 7106            // «b();
 7107            // c();
 7108            ˇ»//  d();
 7109        }
 7110    "});
 7111
 7112    // If a selection ends at the beginning of a line, that line is not toggled.
 7113    cx.set_selections_state(indoc! {"
 7114        fn a() {
 7115            // b();
 7116            «// c();
 7117        ˇ»    //  d();
 7118        }
 7119    "});
 7120
 7121    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7122
 7123    cx.assert_editor_state(indoc! {"
 7124        fn a() {
 7125            // b();
 7126            «c();
 7127        ˇ»    //  d();
 7128        }
 7129    "});
 7130
 7131    // If a selection span a single line and is empty, the line is toggled.
 7132    cx.set_state(indoc! {"
 7133        fn a() {
 7134            a();
 7135            b();
 7136        ˇ
 7137        }
 7138    "});
 7139
 7140    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7141
 7142    cx.assert_editor_state(indoc! {"
 7143        fn a() {
 7144            a();
 7145            b();
 7146        //•ˇ
 7147        }
 7148    "});
 7149
 7150    // If a selection span multiple lines, empty lines are not toggled.
 7151    cx.set_state(indoc! {"
 7152        fn a() {
 7153            «a();
 7154
 7155            c();ˇ»
 7156        }
 7157    "});
 7158
 7159    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7160
 7161    cx.assert_editor_state(indoc! {"
 7162        fn a() {
 7163            // «a();
 7164
 7165            // c();ˇ»
 7166        }
 7167    "});
 7168
 7169    // If a selection includes multiple comment prefixes, all lines are uncommented.
 7170    cx.set_state(indoc! {"
 7171        fn a() {
 7172            «// a();
 7173            /// b();
 7174            //! c();ˇ»
 7175        }
 7176    "});
 7177
 7178    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7179
 7180    cx.assert_editor_state(indoc! {"
 7181        fn a() {
 7182            «a();
 7183            b();
 7184            c();ˇ»
 7185        }
 7186    "});
 7187}
 7188
 7189#[gpui::test]
 7190async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 7191    init_test(cx, |_| {});
 7192
 7193    let language = Arc::new(Language::new(
 7194        LanguageConfig {
 7195            line_comments: vec!["// ".into()],
 7196            ..Default::default()
 7197        },
 7198        Some(tree_sitter_rust::language()),
 7199    ));
 7200
 7201    let mut cx = EditorTestContext::new(cx).await;
 7202
 7203    cx.language_registry().add(language.clone());
 7204    cx.update_buffer(|buffer, cx| {
 7205        buffer.set_language(Some(language), cx);
 7206    });
 7207
 7208    let toggle_comments = &ToggleComments {
 7209        advance_downwards: true,
 7210    };
 7211
 7212    // Single cursor on one line -> advance
 7213    // Cursor moves horizontally 3 characters as well on non-blank line
 7214    cx.set_state(indoc!(
 7215        "fn a() {
 7216             ˇdog();
 7217             cat();
 7218        }"
 7219    ));
 7220    cx.update_editor(|editor, cx| {
 7221        editor.toggle_comments(toggle_comments, cx);
 7222    });
 7223    cx.assert_editor_state(indoc!(
 7224        "fn a() {
 7225             // dog();
 7226             catˇ();
 7227        }"
 7228    ));
 7229
 7230    // Single selection on one line -> don't advance
 7231    cx.set_state(indoc!(
 7232        "fn a() {
 7233             «dog()ˇ»;
 7234             cat();
 7235        }"
 7236    ));
 7237    cx.update_editor(|editor, cx| {
 7238        editor.toggle_comments(toggle_comments, cx);
 7239    });
 7240    cx.assert_editor_state(indoc!(
 7241        "fn a() {
 7242             // «dog()ˇ»;
 7243             cat();
 7244        }"
 7245    ));
 7246
 7247    // Multiple cursors on one line -> advance
 7248    cx.set_state(indoc!(
 7249        "fn a() {
 7250             ˇdˇog();
 7251             cat();
 7252        }"
 7253    ));
 7254    cx.update_editor(|editor, cx| {
 7255        editor.toggle_comments(toggle_comments, cx);
 7256    });
 7257    cx.assert_editor_state(indoc!(
 7258        "fn a() {
 7259             // dog();
 7260             catˇ(ˇ);
 7261        }"
 7262    ));
 7263
 7264    // Multiple cursors on one line, with selection -> don't advance
 7265    cx.set_state(indoc!(
 7266        "fn a() {
 7267             ˇdˇog«()ˇ»;
 7268             cat();
 7269        }"
 7270    ));
 7271    cx.update_editor(|editor, cx| {
 7272        editor.toggle_comments(toggle_comments, cx);
 7273    });
 7274    cx.assert_editor_state(indoc!(
 7275        "fn a() {
 7276             // ˇdˇog«()ˇ»;
 7277             cat();
 7278        }"
 7279    ));
 7280
 7281    // Single cursor on one line -> advance
 7282    // Cursor moves to column 0 on blank line
 7283    cx.set_state(indoc!(
 7284        "fn a() {
 7285             ˇdog();
 7286
 7287             cat();
 7288        }"
 7289    ));
 7290    cx.update_editor(|editor, cx| {
 7291        editor.toggle_comments(toggle_comments, cx);
 7292    });
 7293    cx.assert_editor_state(indoc!(
 7294        "fn a() {
 7295             // dog();
 7296        ˇ
 7297             cat();
 7298        }"
 7299    ));
 7300
 7301    // Single cursor on one line -> advance
 7302    // Cursor starts and ends at column 0
 7303    cx.set_state(indoc!(
 7304        "fn a() {
 7305         ˇ    dog();
 7306             cat();
 7307        }"
 7308    ));
 7309    cx.update_editor(|editor, cx| {
 7310        editor.toggle_comments(toggle_comments, cx);
 7311    });
 7312    cx.assert_editor_state(indoc!(
 7313        "fn a() {
 7314             // dog();
 7315         ˇ    cat();
 7316        }"
 7317    ));
 7318}
 7319
 7320#[gpui::test]
 7321async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 7322    init_test(cx, |_| {});
 7323
 7324    let mut cx = EditorTestContext::new(cx).await;
 7325
 7326    let html_language = Arc::new(
 7327        Language::new(
 7328            LanguageConfig {
 7329                name: "HTML".into(),
 7330                block_comment: Some(("<!-- ".into(), " -->".into())),
 7331                ..Default::default()
 7332            },
 7333            Some(tree_sitter_html::language()),
 7334        )
 7335        .with_injection_query(
 7336            r#"
 7337            (script_element
 7338                (raw_text) @content
 7339                (#set! "language" "javascript"))
 7340            "#,
 7341        )
 7342        .unwrap(),
 7343    );
 7344
 7345    let javascript_language = Arc::new(Language::new(
 7346        LanguageConfig {
 7347            name: "JavaScript".into(),
 7348            line_comments: vec!["// ".into()],
 7349            ..Default::default()
 7350        },
 7351        Some(tree_sitter_typescript::language_tsx()),
 7352    ));
 7353
 7354    cx.language_registry().add(html_language.clone());
 7355    cx.language_registry().add(javascript_language.clone());
 7356    cx.update_buffer(|buffer, cx| {
 7357        buffer.set_language(Some(html_language), cx);
 7358    });
 7359
 7360    // Toggle comments for empty selections
 7361    cx.set_state(
 7362        &r#"
 7363            <p>A</p>ˇ
 7364            <p>B</p>ˇ
 7365            <p>C</p>ˇ
 7366        "#
 7367        .unindent(),
 7368    );
 7369    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7370    cx.assert_editor_state(
 7371        &r#"
 7372            <!-- <p>A</p>ˇ -->
 7373            <!-- <p>B</p>ˇ -->
 7374            <!-- <p>C</p>ˇ -->
 7375        "#
 7376        .unindent(),
 7377    );
 7378    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7379    cx.assert_editor_state(
 7380        &r#"
 7381            <p>A</p>ˇ
 7382            <p>B</p>ˇ
 7383            <p>C</p>ˇ
 7384        "#
 7385        .unindent(),
 7386    );
 7387
 7388    // Toggle comments for mixture of empty and non-empty selections, where
 7389    // multiple selections occupy a given line.
 7390    cx.set_state(
 7391        &r#"
 7392            <p>A«</p>
 7393            <p>ˇ»B</p>ˇ
 7394            <p>C«</p>
 7395            <p>ˇ»D</p>ˇ
 7396        "#
 7397        .unindent(),
 7398    );
 7399
 7400    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7401    cx.assert_editor_state(
 7402        &r#"
 7403            <!-- <p>A«</p>
 7404            <p>ˇ»B</p>ˇ -->
 7405            <!-- <p>C«</p>
 7406            <p>ˇ»D</p>ˇ -->
 7407        "#
 7408        .unindent(),
 7409    );
 7410    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7411    cx.assert_editor_state(
 7412        &r#"
 7413            <p>A«</p>
 7414            <p>ˇ»B</p>ˇ
 7415            <p>C«</p>
 7416            <p>ˇ»D</p>ˇ
 7417        "#
 7418        .unindent(),
 7419    );
 7420
 7421    // Toggle comments when different languages are active for different
 7422    // selections.
 7423    cx.set_state(
 7424        &r#"
 7425            ˇ<script>
 7426                ˇvar x = new Y();
 7427            ˇ</script>
 7428        "#
 7429        .unindent(),
 7430    );
 7431    cx.executor().run_until_parked();
 7432    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7433    cx.assert_editor_state(
 7434        &r#"
 7435            <!-- ˇ<script> -->
 7436                // ˇvar x = new Y();
 7437            <!-- ˇ</script> -->
 7438        "#
 7439        .unindent(),
 7440    );
 7441}
 7442
 7443#[gpui::test]
 7444fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 7445    init_test(cx, |_| {});
 7446
 7447    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7448    let multibuffer = cx.new_model(|cx| {
 7449        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7450        multibuffer.push_excerpts(
 7451            buffer.clone(),
 7452            [
 7453                ExcerptRange {
 7454                    context: Point::new(0, 0)..Point::new(0, 4),
 7455                    primary: None,
 7456                },
 7457                ExcerptRange {
 7458                    context: Point::new(1, 0)..Point::new(1, 4),
 7459                    primary: None,
 7460                },
 7461            ],
 7462            cx,
 7463        );
 7464        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 7465        multibuffer
 7466    });
 7467
 7468    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 7469    _ = view.update(cx, |view, cx| {
 7470        assert_eq!(view.text(cx), "aaaa\nbbbb");
 7471        view.change_selections(None, cx, |s| {
 7472            s.select_ranges([
 7473                Point::new(0, 0)..Point::new(0, 0),
 7474                Point::new(1, 0)..Point::new(1, 0),
 7475            ])
 7476        });
 7477
 7478        view.handle_input("X", cx);
 7479        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 7480        assert_eq!(
 7481            view.selections.ranges(cx),
 7482            [
 7483                Point::new(0, 1)..Point::new(0, 1),
 7484                Point::new(1, 1)..Point::new(1, 1),
 7485            ]
 7486        );
 7487
 7488        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 7489        view.change_selections(None, cx, |s| {
 7490            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 7491        });
 7492        view.backspace(&Default::default(), cx);
 7493        assert_eq!(view.text(cx), "Xa\nbbb");
 7494        assert_eq!(
 7495            view.selections.ranges(cx),
 7496            [Point::new(1, 0)..Point::new(1, 0)]
 7497        );
 7498
 7499        view.change_selections(None, cx, |s| {
 7500            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 7501        });
 7502        view.backspace(&Default::default(), cx);
 7503        assert_eq!(view.text(cx), "X\nbb");
 7504        assert_eq!(
 7505            view.selections.ranges(cx),
 7506            [Point::new(0, 1)..Point::new(0, 1)]
 7507        );
 7508    });
 7509}
 7510
 7511#[gpui::test]
 7512fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 7513    init_test(cx, |_| {});
 7514
 7515    let markers = vec![('[', ']').into(), ('(', ')').into()];
 7516    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 7517        indoc! {"
 7518            [aaaa
 7519            (bbbb]
 7520            cccc)",
 7521        },
 7522        markers.clone(),
 7523    );
 7524    let excerpt_ranges = markers.into_iter().map(|marker| {
 7525        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 7526        ExcerptRange {
 7527            context,
 7528            primary: None,
 7529        }
 7530    });
 7531    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 7532    let multibuffer = cx.new_model(|cx| {
 7533        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7534        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 7535        multibuffer
 7536    });
 7537
 7538    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 7539    _ = view.update(cx, |view, cx| {
 7540        let (expected_text, selection_ranges) = marked_text_ranges(
 7541            indoc! {"
 7542                aaaa
 7543                bˇbbb
 7544                bˇbbˇb
 7545                cccc"
 7546            },
 7547            true,
 7548        );
 7549        assert_eq!(view.text(cx), expected_text);
 7550        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 7551
 7552        view.handle_input("X", cx);
 7553
 7554        let (expected_text, expected_selections) = marked_text_ranges(
 7555            indoc! {"
 7556                aaaa
 7557                bXˇbbXb
 7558                bXˇbbXˇb
 7559                cccc"
 7560            },
 7561            false,
 7562        );
 7563        assert_eq!(view.text(cx), expected_text);
 7564        assert_eq!(view.selections.ranges(cx), expected_selections);
 7565
 7566        view.newline(&Newline, cx);
 7567        let (expected_text, expected_selections) = marked_text_ranges(
 7568            indoc! {"
 7569                aaaa
 7570                bX
 7571                ˇbbX
 7572                b
 7573                bX
 7574                ˇbbX
 7575                ˇb
 7576                cccc"
 7577            },
 7578            false,
 7579        );
 7580        assert_eq!(view.text(cx), expected_text);
 7581        assert_eq!(view.selections.ranges(cx), expected_selections);
 7582    });
 7583}
 7584
 7585#[gpui::test]
 7586fn test_refresh_selections(cx: &mut TestAppContext) {
 7587    init_test(cx, |_| {});
 7588
 7589    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7590    let mut excerpt1_id = None;
 7591    let multibuffer = cx.new_model(|cx| {
 7592        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7593        excerpt1_id = multibuffer
 7594            .push_excerpts(
 7595                buffer.clone(),
 7596                [
 7597                    ExcerptRange {
 7598                        context: Point::new(0, 0)..Point::new(1, 4),
 7599                        primary: None,
 7600                    },
 7601                    ExcerptRange {
 7602                        context: Point::new(1, 0)..Point::new(2, 4),
 7603                        primary: None,
 7604                    },
 7605                ],
 7606                cx,
 7607            )
 7608            .into_iter()
 7609            .next();
 7610        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 7611        multibuffer
 7612    });
 7613
 7614    let editor = cx.add_window(|cx| {
 7615        let mut editor = build_editor(multibuffer.clone(), cx);
 7616        let snapshot = editor.snapshot(cx);
 7617        editor.change_selections(None, cx, |s| {
 7618            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 7619        });
 7620        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 7621        assert_eq!(
 7622            editor.selections.ranges(cx),
 7623            [
 7624                Point::new(1, 3)..Point::new(1, 3),
 7625                Point::new(2, 1)..Point::new(2, 1),
 7626            ]
 7627        );
 7628        editor
 7629    });
 7630
 7631    // Refreshing selections is a no-op when excerpts haven't changed.
 7632    _ = editor.update(cx, |editor, cx| {
 7633        editor.change_selections(None, cx, |s| s.refresh());
 7634        assert_eq!(
 7635            editor.selections.ranges(cx),
 7636            [
 7637                Point::new(1, 3)..Point::new(1, 3),
 7638                Point::new(2, 1)..Point::new(2, 1),
 7639            ]
 7640        );
 7641    });
 7642
 7643    _ = multibuffer.update(cx, |multibuffer, cx| {
 7644        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 7645    });
 7646    _ = editor.update(cx, |editor, cx| {
 7647        // Removing an excerpt causes the first selection to become degenerate.
 7648        assert_eq!(
 7649            editor.selections.ranges(cx),
 7650            [
 7651                Point::new(0, 0)..Point::new(0, 0),
 7652                Point::new(0, 1)..Point::new(0, 1)
 7653            ]
 7654        );
 7655
 7656        // Refreshing selections will relocate the first selection to the original buffer
 7657        // location.
 7658        editor.change_selections(None, cx, |s| s.refresh());
 7659        assert_eq!(
 7660            editor.selections.ranges(cx),
 7661            [
 7662                Point::new(0, 1)..Point::new(0, 1),
 7663                Point::new(0, 3)..Point::new(0, 3)
 7664            ]
 7665        );
 7666        assert!(editor.selections.pending_anchor().is_some());
 7667    });
 7668}
 7669
 7670#[gpui::test]
 7671fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 7672    init_test(cx, |_| {});
 7673
 7674    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7675    let mut excerpt1_id = None;
 7676    let multibuffer = cx.new_model(|cx| {
 7677        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7678        excerpt1_id = multibuffer
 7679            .push_excerpts(
 7680                buffer.clone(),
 7681                [
 7682                    ExcerptRange {
 7683                        context: Point::new(0, 0)..Point::new(1, 4),
 7684                        primary: None,
 7685                    },
 7686                    ExcerptRange {
 7687                        context: Point::new(1, 0)..Point::new(2, 4),
 7688                        primary: None,
 7689                    },
 7690                ],
 7691                cx,
 7692            )
 7693            .into_iter()
 7694            .next();
 7695        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 7696        multibuffer
 7697    });
 7698
 7699    let editor = cx.add_window(|cx| {
 7700        let mut editor = build_editor(multibuffer.clone(), cx);
 7701        let snapshot = editor.snapshot(cx);
 7702        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 7703        assert_eq!(
 7704            editor.selections.ranges(cx),
 7705            [Point::new(1, 3)..Point::new(1, 3)]
 7706        );
 7707        editor
 7708    });
 7709
 7710    _ = multibuffer.update(cx, |multibuffer, cx| {
 7711        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 7712    });
 7713    _ = editor.update(cx, |editor, cx| {
 7714        assert_eq!(
 7715            editor.selections.ranges(cx),
 7716            [Point::new(0, 0)..Point::new(0, 0)]
 7717        );
 7718
 7719        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 7720        editor.change_selections(None, cx, |s| s.refresh());
 7721        assert_eq!(
 7722            editor.selections.ranges(cx),
 7723            [Point::new(0, 3)..Point::new(0, 3)]
 7724        );
 7725        assert!(editor.selections.pending_anchor().is_some());
 7726    });
 7727}
 7728
 7729#[gpui::test]
 7730async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 7731    init_test(cx, |_| {});
 7732
 7733    let language = Arc::new(
 7734        Language::new(
 7735            LanguageConfig {
 7736                brackets: BracketPairConfig {
 7737                    pairs: vec![
 7738                        BracketPair {
 7739                            start: "{".to_string(),
 7740                            end: "}".to_string(),
 7741                            close: true,
 7742                            surround: true,
 7743                            newline: true,
 7744                        },
 7745                        BracketPair {
 7746                            start: "/* ".to_string(),
 7747                            end: " */".to_string(),
 7748                            close: true,
 7749                            surround: true,
 7750                            newline: true,
 7751                        },
 7752                    ],
 7753                    ..Default::default()
 7754                },
 7755                ..Default::default()
 7756            },
 7757            Some(tree_sitter_rust::language()),
 7758        )
 7759        .with_indents_query("")
 7760        .unwrap(),
 7761    );
 7762
 7763    let text = concat!(
 7764        "{   }\n",     //
 7765        "  x\n",       //
 7766        "  /*   */\n", //
 7767        "x\n",         //
 7768        "{{} }\n",     //
 7769    );
 7770
 7771    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 7772    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7773    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7774    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 7775        .await;
 7776
 7777    _ = view.update(cx, |view, cx| {
 7778        view.change_selections(None, cx, |s| {
 7779            s.select_display_ranges([
 7780                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 7781                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 7782                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 7783            ])
 7784        });
 7785        view.newline(&Newline, cx);
 7786
 7787        assert_eq!(
 7788            view.buffer().read(cx).read(cx).text(),
 7789            concat!(
 7790                "{ \n",    // Suppress rustfmt
 7791                "\n",      //
 7792                "}\n",     //
 7793                "  x\n",   //
 7794                "  /* \n", //
 7795                "  \n",    //
 7796                "  */\n",  //
 7797                "x\n",     //
 7798                "{{} \n",  //
 7799                "}\n",     //
 7800            )
 7801        );
 7802    });
 7803}
 7804
 7805#[gpui::test]
 7806fn test_highlighted_ranges(cx: &mut TestAppContext) {
 7807    init_test(cx, |_| {});
 7808
 7809    let editor = cx.add_window(|cx| {
 7810        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 7811        build_editor(buffer.clone(), cx)
 7812    });
 7813
 7814    _ = editor.update(cx, |editor, cx| {
 7815        struct Type1;
 7816        struct Type2;
 7817
 7818        let buffer = editor.buffer.read(cx).snapshot(cx);
 7819
 7820        let anchor_range =
 7821            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 7822
 7823        editor.highlight_background::<Type1>(
 7824            &[
 7825                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 7826                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 7827                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 7828                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 7829            ],
 7830            |_| Hsla::red(),
 7831            cx,
 7832        );
 7833        editor.highlight_background::<Type2>(
 7834            &[
 7835                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 7836                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 7837                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 7838                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 7839            ],
 7840            |_| Hsla::green(),
 7841            cx,
 7842        );
 7843
 7844        let snapshot = editor.snapshot(cx);
 7845        let mut highlighted_ranges = editor.background_highlights_in_range(
 7846            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 7847            &snapshot,
 7848            cx.theme().colors(),
 7849        );
 7850        // Enforce a consistent ordering based on color without relying on the ordering of the
 7851        // highlight's `TypeId` which is non-executor.
 7852        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 7853        assert_eq!(
 7854            highlighted_ranges,
 7855            &[
 7856                (
 7857                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 7858                    Hsla::red(),
 7859                ),
 7860                (
 7861                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 7862                    Hsla::red(),
 7863                ),
 7864                (
 7865                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 7866                    Hsla::green(),
 7867                ),
 7868                (
 7869                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 7870                    Hsla::green(),
 7871                ),
 7872            ]
 7873        );
 7874        assert_eq!(
 7875            editor.background_highlights_in_range(
 7876                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 7877                &snapshot,
 7878                cx.theme().colors(),
 7879            ),
 7880            &[(
 7881                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 7882                Hsla::red(),
 7883            )]
 7884        );
 7885    });
 7886}
 7887
 7888#[gpui::test]
 7889async fn test_following(cx: &mut gpui::TestAppContext) {
 7890    init_test(cx, |_| {});
 7891
 7892    let fs = FakeFs::new(cx.executor());
 7893    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7894
 7895    let buffer = project.update(cx, |project, cx| {
 7896        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 7897        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 7898    });
 7899    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 7900    let follower = cx.update(|cx| {
 7901        cx.open_window(
 7902            WindowOptions {
 7903                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 7904                    gpui::Point::new(px(0.), px(0.)),
 7905                    gpui::Point::new(px(10.), px(80.)),
 7906                ))),
 7907                ..Default::default()
 7908            },
 7909            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 7910        )
 7911        .unwrap()
 7912    });
 7913
 7914    let is_still_following = Rc::new(RefCell::new(true));
 7915    let follower_edit_event_count = Rc::new(RefCell::new(0));
 7916    let pending_update = Rc::new(RefCell::new(None));
 7917    _ = follower.update(cx, {
 7918        let update = pending_update.clone();
 7919        let is_still_following = is_still_following.clone();
 7920        let follower_edit_event_count = follower_edit_event_count.clone();
 7921        |_, cx| {
 7922            cx.subscribe(
 7923                &leader.root_view(cx).unwrap(),
 7924                move |_, leader, event, cx| {
 7925                    leader
 7926                        .read(cx)
 7927                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 7928                },
 7929            )
 7930            .detach();
 7931
 7932            cx.subscribe(
 7933                &follower.root_view(cx).unwrap(),
 7934                move |_, _, event: &EditorEvent, _cx| {
 7935                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 7936                        *is_still_following.borrow_mut() = false;
 7937                    }
 7938
 7939                    if let EditorEvent::BufferEdited = event {
 7940                        *follower_edit_event_count.borrow_mut() += 1;
 7941                    }
 7942                },
 7943            )
 7944            .detach();
 7945        }
 7946    });
 7947
 7948    // Update the selections only
 7949    _ = leader.update(cx, |leader, cx| {
 7950        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 7951    });
 7952    follower
 7953        .update(cx, |follower, cx| {
 7954            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7955        })
 7956        .unwrap()
 7957        .await
 7958        .unwrap();
 7959    _ = follower.update(cx, |follower, cx| {
 7960        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 7961    });
 7962    assert_eq!(*is_still_following.borrow(), true);
 7963    assert_eq!(*follower_edit_event_count.borrow(), 0);
 7964
 7965    // Update the scroll position only
 7966    _ = leader.update(cx, |leader, cx| {
 7967        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 7968    });
 7969    follower
 7970        .update(cx, |follower, cx| {
 7971            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7972        })
 7973        .unwrap()
 7974        .await
 7975        .unwrap();
 7976    assert_eq!(
 7977        follower
 7978            .update(cx, |follower, cx| follower.scroll_position(cx))
 7979            .unwrap(),
 7980        gpui::Point::new(1.5, 3.5)
 7981    );
 7982    assert_eq!(*is_still_following.borrow(), true);
 7983    assert_eq!(*follower_edit_event_count.borrow(), 0);
 7984
 7985    // Update the selections and scroll position. The follower's scroll position is updated
 7986    // via autoscroll, not via the leader's exact scroll position.
 7987    _ = leader.update(cx, |leader, cx| {
 7988        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7989        leader.request_autoscroll(Autoscroll::newest(), cx);
 7990        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 7991    });
 7992    follower
 7993        .update(cx, |follower, cx| {
 7994            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7995        })
 7996        .unwrap()
 7997        .await
 7998        .unwrap();
 7999    _ = follower.update(cx, |follower, cx| {
 8000        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 8001        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 8002    });
 8003    assert_eq!(*is_still_following.borrow(), true);
 8004
 8005    // Creating a pending selection that precedes another selection
 8006    _ = leader.update(cx, |leader, cx| {
 8007        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8008        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 8009    });
 8010    follower
 8011        .update(cx, |follower, cx| {
 8012            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8013        })
 8014        .unwrap()
 8015        .await
 8016        .unwrap();
 8017    _ = follower.update(cx, |follower, cx| {
 8018        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 8019    });
 8020    assert_eq!(*is_still_following.borrow(), true);
 8021
 8022    // Extend the pending selection so that it surrounds another selection
 8023    _ = leader.update(cx, |leader, cx| {
 8024        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 8025    });
 8026    follower
 8027        .update(cx, |follower, cx| {
 8028            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8029        })
 8030        .unwrap()
 8031        .await
 8032        .unwrap();
 8033    _ = follower.update(cx, |follower, cx| {
 8034        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 8035    });
 8036
 8037    // Scrolling locally breaks the follow
 8038    _ = follower.update(cx, |follower, cx| {
 8039        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 8040        follower.set_scroll_anchor(
 8041            ScrollAnchor {
 8042                anchor: top_anchor,
 8043                offset: gpui::Point::new(0.0, 0.5),
 8044            },
 8045            cx,
 8046        );
 8047    });
 8048    assert_eq!(*is_still_following.borrow(), false);
 8049}
 8050
 8051#[gpui::test]
 8052async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 8053    init_test(cx, |_| {});
 8054
 8055    let fs = FakeFs::new(cx.executor());
 8056    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8057    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8058    let pane = workspace
 8059        .update(cx, |workspace, _| workspace.active_pane().clone())
 8060        .unwrap();
 8061
 8062    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8063
 8064    let leader = pane.update(cx, |_, cx| {
 8065        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 8066        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 8067    });
 8068
 8069    // Start following the editor when it has no excerpts.
 8070    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8071    let follower_1 = cx
 8072        .update_window(*workspace.deref(), |_, cx| {
 8073            Editor::from_state_proto(
 8074                pane.clone(),
 8075                workspace.root_view(cx).unwrap(),
 8076                ViewId {
 8077                    creator: Default::default(),
 8078                    id: 0,
 8079                },
 8080                &mut state_message,
 8081                cx,
 8082            )
 8083        })
 8084        .unwrap()
 8085        .unwrap()
 8086        .await
 8087        .unwrap();
 8088
 8089    let update_message = Rc::new(RefCell::new(None));
 8090    follower_1.update(cx, {
 8091        let update = update_message.clone();
 8092        |_, cx| {
 8093            cx.subscribe(&leader, move |_, leader, event, cx| {
 8094                leader
 8095                    .read(cx)
 8096                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8097            })
 8098            .detach();
 8099        }
 8100    });
 8101
 8102    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 8103        (
 8104            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 8105            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 8106        )
 8107    });
 8108
 8109    // Insert some excerpts.
 8110    _ = leader.update(cx, |leader, cx| {
 8111        leader.buffer.update(cx, |multibuffer, cx| {
 8112            let excerpt_ids = multibuffer.push_excerpts(
 8113                buffer_1.clone(),
 8114                [
 8115                    ExcerptRange {
 8116                        context: 1..6,
 8117                        primary: None,
 8118                    },
 8119                    ExcerptRange {
 8120                        context: 12..15,
 8121                        primary: None,
 8122                    },
 8123                    ExcerptRange {
 8124                        context: 0..3,
 8125                        primary: None,
 8126                    },
 8127                ],
 8128                cx,
 8129            );
 8130            multibuffer.insert_excerpts_after(
 8131                excerpt_ids[0],
 8132                buffer_2.clone(),
 8133                [
 8134                    ExcerptRange {
 8135                        context: 8..12,
 8136                        primary: None,
 8137                    },
 8138                    ExcerptRange {
 8139                        context: 0..6,
 8140                        primary: None,
 8141                    },
 8142                ],
 8143                cx,
 8144            );
 8145        });
 8146    });
 8147
 8148    // Apply the update of adding the excerpts.
 8149    follower_1
 8150        .update(cx, |follower, cx| {
 8151            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8152        })
 8153        .await
 8154        .unwrap();
 8155    assert_eq!(
 8156        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8157        leader.update(cx, |editor, cx| editor.text(cx))
 8158    );
 8159    update_message.borrow_mut().take();
 8160
 8161    // Start following separately after it already has excerpts.
 8162    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8163    let follower_2 = cx
 8164        .update_window(*workspace.deref(), |_, cx| {
 8165            Editor::from_state_proto(
 8166                pane.clone(),
 8167                workspace.root_view(cx).unwrap().clone(),
 8168                ViewId {
 8169                    creator: Default::default(),
 8170                    id: 0,
 8171                },
 8172                &mut state_message,
 8173                cx,
 8174            )
 8175        })
 8176        .unwrap()
 8177        .unwrap()
 8178        .await
 8179        .unwrap();
 8180    assert_eq!(
 8181        follower_2.update(cx, |editor, cx| editor.text(cx)),
 8182        leader.update(cx, |editor, cx| editor.text(cx))
 8183    );
 8184
 8185    // Remove some excerpts.
 8186    _ = leader.update(cx, |leader, cx| {
 8187        leader.buffer.update(cx, |multibuffer, cx| {
 8188            let excerpt_ids = multibuffer.excerpt_ids();
 8189            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 8190            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 8191        });
 8192    });
 8193
 8194    // Apply the update of removing the excerpts.
 8195    follower_1
 8196        .update(cx, |follower, cx| {
 8197            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8198        })
 8199        .await
 8200        .unwrap();
 8201    follower_2
 8202        .update(cx, |follower, cx| {
 8203            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8204        })
 8205        .await
 8206        .unwrap();
 8207    update_message.borrow_mut().take();
 8208    assert_eq!(
 8209        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8210        leader.update(cx, |editor, cx| editor.text(cx))
 8211    );
 8212}
 8213
 8214#[gpui::test]
 8215async fn go_to_prev_overlapping_diagnostic(
 8216    executor: BackgroundExecutor,
 8217    cx: &mut gpui::TestAppContext,
 8218) {
 8219    init_test(cx, |_| {});
 8220
 8221    let mut cx = EditorTestContext::new(cx).await;
 8222    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 8223
 8224    cx.set_state(indoc! {"
 8225        ˇfn func(abc def: i32) -> u32 {
 8226        }
 8227    "});
 8228
 8229    _ = cx.update(|cx| {
 8230        _ = project.update(cx, |project, cx| {
 8231            project
 8232                .update_diagnostics(
 8233                    LanguageServerId(0),
 8234                    lsp::PublishDiagnosticsParams {
 8235                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 8236                        version: None,
 8237                        diagnostics: vec![
 8238                            lsp::Diagnostic {
 8239                                range: lsp::Range::new(
 8240                                    lsp::Position::new(0, 11),
 8241                                    lsp::Position::new(0, 12),
 8242                                ),
 8243                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8244                                ..Default::default()
 8245                            },
 8246                            lsp::Diagnostic {
 8247                                range: lsp::Range::new(
 8248                                    lsp::Position::new(0, 12),
 8249                                    lsp::Position::new(0, 15),
 8250                                ),
 8251                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8252                                ..Default::default()
 8253                            },
 8254                            lsp::Diagnostic {
 8255                                range: lsp::Range::new(
 8256                                    lsp::Position::new(0, 25),
 8257                                    lsp::Position::new(0, 28),
 8258                                ),
 8259                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8260                                ..Default::default()
 8261                            },
 8262                        ],
 8263                    },
 8264                    &[],
 8265                    cx,
 8266                )
 8267                .unwrap()
 8268        });
 8269    });
 8270
 8271    executor.run_until_parked();
 8272
 8273    cx.update_editor(|editor, cx| {
 8274        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 8275    });
 8276
 8277    cx.assert_editor_state(indoc! {"
 8278        fn func(abc def: i32) -> ˇu32 {
 8279        }
 8280    "});
 8281
 8282    cx.update_editor(|editor, cx| {
 8283        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 8284    });
 8285
 8286    cx.assert_editor_state(indoc! {"
 8287        fn func(abc ˇdef: i32) -> u32 {
 8288        }
 8289    "});
 8290
 8291    cx.update_editor(|editor, cx| {
 8292        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 8293    });
 8294
 8295    cx.assert_editor_state(indoc! {"
 8296        fn func(abcˇ def: i32) -> u32 {
 8297        }
 8298    "});
 8299
 8300    cx.update_editor(|editor, cx| {
 8301        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 8302    });
 8303
 8304    cx.assert_editor_state(indoc! {"
 8305        fn func(abc def: i32) -> ˇu32 {
 8306        }
 8307    "});
 8308}
 8309
 8310#[gpui::test]
 8311async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 8312    init_test(cx, |_| {});
 8313
 8314    let mut cx = EditorTestContext::new(cx).await;
 8315
 8316    let diff_base = r#"
 8317        use some::mod;
 8318
 8319        const A: u32 = 42;
 8320
 8321        fn main() {
 8322            println!("hello");
 8323
 8324            println!("world");
 8325        }
 8326        "#
 8327    .unindent();
 8328
 8329    // Edits are modified, removed, modified, added
 8330    cx.set_state(
 8331        &r#"
 8332        use some::modified;
 8333
 8334        ˇ
 8335        fn main() {
 8336            println!("hello there");
 8337
 8338            println!("around the");
 8339            println!("world");
 8340        }
 8341        "#
 8342        .unindent(),
 8343    );
 8344
 8345    cx.set_diff_base(Some(&diff_base));
 8346    executor.run_until_parked();
 8347
 8348    cx.update_editor(|editor, cx| {
 8349        //Wrap around the bottom of the buffer
 8350        for _ in 0..3 {
 8351            editor.go_to_hunk(&GoToHunk, cx);
 8352        }
 8353    });
 8354
 8355    cx.assert_editor_state(
 8356        &r#"
 8357        ˇuse some::modified;
 8358
 8359
 8360        fn main() {
 8361            println!("hello there");
 8362
 8363            println!("around the");
 8364            println!("world");
 8365        }
 8366        "#
 8367        .unindent(),
 8368    );
 8369
 8370    cx.update_editor(|editor, cx| {
 8371        //Wrap around the top of the buffer
 8372        for _ in 0..2 {
 8373            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 8374        }
 8375    });
 8376
 8377    cx.assert_editor_state(
 8378        &r#"
 8379        use some::modified;
 8380
 8381
 8382        fn main() {
 8383        ˇ    println!("hello there");
 8384
 8385            println!("around the");
 8386            println!("world");
 8387        }
 8388        "#
 8389        .unindent(),
 8390    );
 8391
 8392    cx.update_editor(|editor, cx| {
 8393        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 8394    });
 8395
 8396    cx.assert_editor_state(
 8397        &r#"
 8398        use some::modified;
 8399
 8400        ˇ
 8401        fn main() {
 8402            println!("hello there");
 8403
 8404            println!("around the");
 8405            println!("world");
 8406        }
 8407        "#
 8408        .unindent(),
 8409    );
 8410
 8411    cx.update_editor(|editor, cx| {
 8412        for _ in 0..3 {
 8413            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 8414        }
 8415    });
 8416
 8417    cx.assert_editor_state(
 8418        &r#"
 8419        use some::modified;
 8420
 8421
 8422        fn main() {
 8423        ˇ    println!("hello there");
 8424
 8425            println!("around the");
 8426            println!("world");
 8427        }
 8428        "#
 8429        .unindent(),
 8430    );
 8431
 8432    cx.update_editor(|editor, cx| {
 8433        editor.fold(&Fold, cx);
 8434
 8435        //Make sure that the fold only gets one hunk
 8436        for _ in 0..4 {
 8437            editor.go_to_hunk(&GoToHunk, cx);
 8438        }
 8439    });
 8440
 8441    cx.assert_editor_state(
 8442        &r#"
 8443        ˇuse some::modified;
 8444
 8445
 8446        fn main() {
 8447            println!("hello there");
 8448
 8449            println!("around the");
 8450            println!("world");
 8451        }
 8452        "#
 8453        .unindent(),
 8454    );
 8455}
 8456
 8457#[test]
 8458fn test_split_words() {
 8459    fn split(text: &str) -> Vec<&str> {
 8460        split_words(text).collect()
 8461    }
 8462
 8463    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 8464    assert_eq!(split("hello_world"), &["hello_", "world"]);
 8465    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 8466    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 8467    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 8468    assert_eq!(split("helloworld"), &["helloworld"]);
 8469
 8470    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 8471}
 8472
 8473#[gpui::test]
 8474async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 8475    init_test(cx, |_| {});
 8476
 8477    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 8478    let mut assert = |before, after| {
 8479        let _state_context = cx.set_state(before);
 8480        cx.update_editor(|editor, cx| {
 8481            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 8482        });
 8483        cx.assert_editor_state(after);
 8484    };
 8485
 8486    // Outside bracket jumps to outside of matching bracket
 8487    assert("console.logˇ(var);", "console.log(var)ˇ;");
 8488    assert("console.log(var)ˇ;", "console.logˇ(var);");
 8489
 8490    // Inside bracket jumps to inside of matching bracket
 8491    assert("console.log(ˇvar);", "console.log(varˇ);");
 8492    assert("console.log(varˇ);", "console.log(ˇvar);");
 8493
 8494    // When outside a bracket and inside, favor jumping to the inside bracket
 8495    assert(
 8496        "console.log('foo', [1, 2, 3]ˇ);",
 8497        "console.log(ˇ'foo', [1, 2, 3]);",
 8498    );
 8499    assert(
 8500        "console.log(ˇ'foo', [1, 2, 3]);",
 8501        "console.log('foo', [1, 2, 3]ˇ);",
 8502    );
 8503
 8504    // Bias forward if two options are equally likely
 8505    assert(
 8506        "let result = curried_fun()ˇ();",
 8507        "let result = curried_fun()()ˇ;",
 8508    );
 8509
 8510    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 8511    assert(
 8512        indoc! {"
 8513            function test() {
 8514                console.log('test')ˇ
 8515            }"},
 8516        indoc! {"
 8517            function test() {
 8518                console.logˇ('test')
 8519            }"},
 8520    );
 8521}
 8522
 8523#[gpui::test]
 8524async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 8525    init_test(cx, |_| {});
 8526
 8527    let fs = FakeFs::new(cx.executor());
 8528    fs.insert_tree(
 8529        "/a",
 8530        json!({
 8531            "main.rs": "fn main() { let a = 5; }",
 8532            "other.rs": "// Test file",
 8533        }),
 8534    )
 8535    .await;
 8536    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8537
 8538    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8539    language_registry.add(Arc::new(Language::new(
 8540        LanguageConfig {
 8541            name: "Rust".into(),
 8542            matcher: LanguageMatcher {
 8543                path_suffixes: vec!["rs".to_string()],
 8544                ..Default::default()
 8545            },
 8546            brackets: BracketPairConfig {
 8547                pairs: vec![BracketPair {
 8548                    start: "{".to_string(),
 8549                    end: "}".to_string(),
 8550                    close: true,
 8551                    surround: true,
 8552                    newline: true,
 8553                }],
 8554                disabled_scopes_by_bracket_ix: Vec::new(),
 8555            },
 8556            ..Default::default()
 8557        },
 8558        Some(tree_sitter_rust::language()),
 8559    )));
 8560    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 8561        "Rust",
 8562        FakeLspAdapter {
 8563            capabilities: lsp::ServerCapabilities {
 8564                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 8565                    first_trigger_character: "{".to_string(),
 8566                    more_trigger_character: None,
 8567                }),
 8568                ..Default::default()
 8569            },
 8570            ..Default::default()
 8571        },
 8572    );
 8573
 8574    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8575
 8576    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8577
 8578    let worktree_id = workspace
 8579        .update(cx, |workspace, cx| {
 8580            workspace.project().update(cx, |project, cx| {
 8581                project.worktrees().next().unwrap().read(cx).id()
 8582            })
 8583        })
 8584        .unwrap();
 8585
 8586    let buffer = project
 8587        .update(cx, |project, cx| {
 8588            project.open_local_buffer("/a/main.rs", cx)
 8589        })
 8590        .await
 8591        .unwrap();
 8592    cx.executor().run_until_parked();
 8593    cx.executor().start_waiting();
 8594    let fake_server = fake_servers.next().await.unwrap();
 8595    let editor_handle = workspace
 8596        .update(cx, |workspace, cx| {
 8597            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 8598        })
 8599        .unwrap()
 8600        .await
 8601        .unwrap()
 8602        .downcast::<Editor>()
 8603        .unwrap();
 8604
 8605    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 8606        assert_eq!(
 8607            params.text_document_position.text_document.uri,
 8608            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 8609        );
 8610        assert_eq!(
 8611            params.text_document_position.position,
 8612            lsp::Position::new(0, 21),
 8613        );
 8614
 8615        Ok(Some(vec![lsp::TextEdit {
 8616            new_text: "]".to_string(),
 8617            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 8618        }]))
 8619    });
 8620
 8621    editor_handle.update(cx, |editor, cx| {
 8622        editor.focus(cx);
 8623        editor.change_selections(None, cx, |s| {
 8624            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 8625        });
 8626        editor.handle_input("{", cx);
 8627    });
 8628
 8629    cx.executor().run_until_parked();
 8630
 8631    _ = buffer.update(cx, |buffer, _| {
 8632        assert_eq!(
 8633            buffer.text(),
 8634            "fn main() { let a = {5}; }",
 8635            "No extra braces from on type formatting should appear in the buffer"
 8636        )
 8637    });
 8638}
 8639
 8640#[gpui::test]
 8641async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 8642    init_test(cx, |_| {});
 8643
 8644    let fs = FakeFs::new(cx.executor());
 8645    fs.insert_tree(
 8646        "/a",
 8647        json!({
 8648            "main.rs": "fn main() { let a = 5; }",
 8649            "other.rs": "// Test file",
 8650        }),
 8651    )
 8652    .await;
 8653
 8654    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8655
 8656    let server_restarts = Arc::new(AtomicUsize::new(0));
 8657    let closure_restarts = Arc::clone(&server_restarts);
 8658    let language_server_name = "test language server";
 8659    let language_name: Arc<str> = "Rust".into();
 8660
 8661    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8662    language_registry.add(Arc::new(Language::new(
 8663        LanguageConfig {
 8664            name: Arc::clone(&language_name),
 8665            matcher: LanguageMatcher {
 8666                path_suffixes: vec!["rs".to_string()],
 8667                ..Default::default()
 8668            },
 8669            ..Default::default()
 8670        },
 8671        Some(tree_sitter_rust::language()),
 8672    )));
 8673    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 8674        "Rust",
 8675        FakeLspAdapter {
 8676            name: language_server_name,
 8677            initialization_options: Some(json!({
 8678                "testOptionValue": true
 8679            })),
 8680            initializer: Some(Box::new(move |fake_server| {
 8681                let task_restarts = Arc::clone(&closure_restarts);
 8682                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 8683                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 8684                    futures::future::ready(Ok(()))
 8685                });
 8686            })),
 8687            ..Default::default()
 8688        },
 8689    );
 8690
 8691    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8692    let _buffer = project
 8693        .update(cx, |project, cx| {
 8694            project.open_local_buffer("/a/main.rs", cx)
 8695        })
 8696        .await
 8697        .unwrap();
 8698    let _fake_server = fake_servers.next().await.unwrap();
 8699    update_test_language_settings(cx, |language_settings| {
 8700        language_settings.languages.insert(
 8701            Arc::clone(&language_name),
 8702            LanguageSettingsContent {
 8703                tab_size: NonZeroU32::new(8),
 8704                ..Default::default()
 8705            },
 8706        );
 8707    });
 8708    cx.executor().run_until_parked();
 8709    assert_eq!(
 8710        server_restarts.load(atomic::Ordering::Acquire),
 8711        0,
 8712        "Should not restart LSP server on an unrelated change"
 8713    );
 8714
 8715    update_test_project_settings(cx, |project_settings| {
 8716        project_settings.lsp.insert(
 8717            "Some other server name".into(),
 8718            LspSettings {
 8719                binary: None,
 8720                settings: None,
 8721                initialization_options: Some(json!({
 8722                    "some other init value": false
 8723                })),
 8724            },
 8725        );
 8726    });
 8727    cx.executor().run_until_parked();
 8728    assert_eq!(
 8729        server_restarts.load(atomic::Ordering::Acquire),
 8730        0,
 8731        "Should not restart LSP server on an unrelated LSP settings change"
 8732    );
 8733
 8734    update_test_project_settings(cx, |project_settings| {
 8735        project_settings.lsp.insert(
 8736            language_server_name.into(),
 8737            LspSettings {
 8738                binary: None,
 8739                settings: None,
 8740                initialization_options: Some(json!({
 8741                    "anotherInitValue": false
 8742                })),
 8743            },
 8744        );
 8745    });
 8746    cx.executor().run_until_parked();
 8747    assert_eq!(
 8748        server_restarts.load(atomic::Ordering::Acquire),
 8749        1,
 8750        "Should restart LSP server on a related LSP settings change"
 8751    );
 8752
 8753    update_test_project_settings(cx, |project_settings| {
 8754        project_settings.lsp.insert(
 8755            language_server_name.into(),
 8756            LspSettings {
 8757                binary: None,
 8758                settings: None,
 8759                initialization_options: Some(json!({
 8760                    "anotherInitValue": false
 8761                })),
 8762            },
 8763        );
 8764    });
 8765    cx.executor().run_until_parked();
 8766    assert_eq!(
 8767        server_restarts.load(atomic::Ordering::Acquire),
 8768        1,
 8769        "Should not restart LSP server on a related LSP settings change that is the same"
 8770    );
 8771
 8772    update_test_project_settings(cx, |project_settings| {
 8773        project_settings.lsp.insert(
 8774            language_server_name.into(),
 8775            LspSettings {
 8776                binary: None,
 8777                settings: None,
 8778                initialization_options: None,
 8779            },
 8780        );
 8781    });
 8782    cx.executor().run_until_parked();
 8783    assert_eq!(
 8784        server_restarts.load(atomic::Ordering::Acquire),
 8785        2,
 8786        "Should restart LSP server on another related LSP settings change"
 8787    );
 8788}
 8789
 8790#[gpui::test]
 8791async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 8792    init_test(cx, |_| {});
 8793
 8794    let mut cx = EditorLspTestContext::new_rust(
 8795        lsp::ServerCapabilities {
 8796            completion_provider: Some(lsp::CompletionOptions {
 8797                trigger_characters: Some(vec![".".to_string()]),
 8798                resolve_provider: Some(true),
 8799                ..Default::default()
 8800            }),
 8801            ..Default::default()
 8802        },
 8803        cx,
 8804    )
 8805    .await;
 8806
 8807    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8808    cx.simulate_keystroke(".");
 8809    let completion_item = lsp::CompletionItem {
 8810        label: "some".into(),
 8811        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8812        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8813        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8814            kind: lsp::MarkupKind::Markdown,
 8815            value: "```rust\nSome(2)\n```".to_string(),
 8816        })),
 8817        deprecated: Some(false),
 8818        sort_text: Some("fffffff2".to_string()),
 8819        filter_text: Some("some".to_string()),
 8820        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8821        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8822            range: lsp::Range {
 8823                start: lsp::Position {
 8824                    line: 0,
 8825                    character: 22,
 8826                },
 8827                end: lsp::Position {
 8828                    line: 0,
 8829                    character: 22,
 8830                },
 8831            },
 8832            new_text: "Some(2)".to_string(),
 8833        })),
 8834        additional_text_edits: Some(vec![lsp::TextEdit {
 8835            range: lsp::Range {
 8836                start: lsp::Position {
 8837                    line: 0,
 8838                    character: 20,
 8839                },
 8840                end: lsp::Position {
 8841                    line: 0,
 8842                    character: 22,
 8843                },
 8844            },
 8845            new_text: "".to_string(),
 8846        }]),
 8847        ..Default::default()
 8848    };
 8849
 8850    let closure_completion_item = completion_item.clone();
 8851    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8852        let task_completion_item = closure_completion_item.clone();
 8853        async move {
 8854            Ok(Some(lsp::CompletionResponse::Array(vec![
 8855                task_completion_item,
 8856            ])))
 8857        }
 8858    });
 8859
 8860    request.next().await;
 8861
 8862    cx.condition(|editor, _| editor.context_menu_visible())
 8863        .await;
 8864    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8865        editor
 8866            .confirm_completion(&ConfirmCompletion::default(), cx)
 8867            .unwrap()
 8868    });
 8869    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 8870
 8871    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 8872        let task_completion_item = completion_item.clone();
 8873        async move { Ok(task_completion_item) }
 8874    })
 8875    .next()
 8876    .await
 8877    .unwrap();
 8878    apply_additional_edits.await.unwrap();
 8879    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 8880}
 8881
 8882#[gpui::test]
 8883async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 8884    init_test(cx, |_| {});
 8885
 8886    let mut cx = EditorLspTestContext::new(
 8887        Language::new(
 8888            LanguageConfig {
 8889                matcher: LanguageMatcher {
 8890                    path_suffixes: vec!["jsx".into()],
 8891                    ..Default::default()
 8892                },
 8893                overrides: [(
 8894                    "element".into(),
 8895                    LanguageConfigOverride {
 8896                        word_characters: Override::Set(['-'].into_iter().collect()),
 8897                        ..Default::default()
 8898                    },
 8899                )]
 8900                .into_iter()
 8901                .collect(),
 8902                ..Default::default()
 8903            },
 8904            Some(tree_sitter_typescript::language_tsx()),
 8905        )
 8906        .with_override_query("(jsx_self_closing_element) @element")
 8907        .unwrap(),
 8908        lsp::ServerCapabilities {
 8909            completion_provider: Some(lsp::CompletionOptions {
 8910                trigger_characters: Some(vec![":".to_string()]),
 8911                ..Default::default()
 8912            }),
 8913            ..Default::default()
 8914        },
 8915        cx,
 8916    )
 8917    .await;
 8918
 8919    cx.lsp
 8920        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8921            Ok(Some(lsp::CompletionResponse::Array(vec![
 8922                lsp::CompletionItem {
 8923                    label: "bg-blue".into(),
 8924                    ..Default::default()
 8925                },
 8926                lsp::CompletionItem {
 8927                    label: "bg-red".into(),
 8928                    ..Default::default()
 8929                },
 8930                lsp::CompletionItem {
 8931                    label: "bg-yellow".into(),
 8932                    ..Default::default()
 8933                },
 8934            ])))
 8935        });
 8936
 8937    cx.set_state(r#"<p class="bgˇ" />"#);
 8938
 8939    // Trigger completion when typing a dash, because the dash is an extra
 8940    // word character in the 'element' scope, which contains the cursor.
 8941    cx.simulate_keystroke("-");
 8942    cx.executor().run_until_parked();
 8943    cx.update_editor(|editor, _| {
 8944        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8945            assert_eq!(
 8946                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8947                &["bg-red", "bg-blue", "bg-yellow"]
 8948            );
 8949        } else {
 8950            panic!("expected completion menu to be open");
 8951        }
 8952    });
 8953
 8954    cx.simulate_keystroke("l");
 8955    cx.executor().run_until_parked();
 8956    cx.update_editor(|editor, _| {
 8957        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8958            assert_eq!(
 8959                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8960                &["bg-blue", "bg-yellow"]
 8961            );
 8962        } else {
 8963            panic!("expected completion menu to be open");
 8964        }
 8965    });
 8966
 8967    // When filtering completions, consider the character after the '-' to
 8968    // be the start of a subword.
 8969    cx.set_state(r#"<p class="yelˇ" />"#);
 8970    cx.simulate_keystroke("l");
 8971    cx.executor().run_until_parked();
 8972    cx.update_editor(|editor, _| {
 8973        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8974            assert_eq!(
 8975                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8976                &["bg-yellow"]
 8977            );
 8978        } else {
 8979            panic!("expected completion menu to be open");
 8980        }
 8981    });
 8982}
 8983
 8984#[gpui::test]
 8985async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 8986    init_test(cx, |settings| {
 8987        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
 8988    });
 8989
 8990    let fs = FakeFs::new(cx.executor());
 8991    fs.insert_file("/file.ts", Default::default()).await;
 8992
 8993    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
 8994    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8995
 8996    language_registry.add(Arc::new(Language::new(
 8997        LanguageConfig {
 8998            name: "TypeScript".into(),
 8999            matcher: LanguageMatcher {
 9000                path_suffixes: vec!["ts".to_string()],
 9001                ..Default::default()
 9002            },
 9003            ..Default::default()
 9004        },
 9005        Some(tree_sitter_rust::language()),
 9006    )));
 9007    update_test_language_settings(cx, |settings| {
 9008        settings.defaults.prettier = Some(PrettierSettings {
 9009            allowed: true,
 9010            ..PrettierSettings::default()
 9011        });
 9012    });
 9013
 9014    let test_plugin = "test_plugin";
 9015    let _ = language_registry.register_fake_lsp_adapter(
 9016        "TypeScript",
 9017        FakeLspAdapter {
 9018            prettier_plugins: vec![test_plugin],
 9019            ..Default::default()
 9020        },
 9021    );
 9022
 9023    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 9024    let buffer = project
 9025        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
 9026        .await
 9027        .unwrap();
 9028
 9029    let buffer_text = "one\ntwo\nthree\n";
 9030    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9031    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9032    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 9033
 9034    editor
 9035        .update(cx, |editor, cx| {
 9036            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9037        })
 9038        .unwrap()
 9039        .await;
 9040    assert_eq!(
 9041        editor.update(cx, |editor, cx| editor.text(cx)),
 9042        buffer_text.to_string() + prettier_format_suffix,
 9043        "Test prettier formatting was not applied to the original buffer text",
 9044    );
 9045
 9046    update_test_language_settings(cx, |settings| {
 9047        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 9048    });
 9049    let format = editor.update(cx, |editor, cx| {
 9050        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9051    });
 9052    format.await.unwrap();
 9053    assert_eq!(
 9054        editor.update(cx, |editor, cx| editor.text(cx)),
 9055        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 9056        "Autoformatting (via test prettier) was not applied to the original buffer text",
 9057    );
 9058}
 9059
 9060#[gpui::test]
 9061async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 9062    init_test(cx, |_| {});
 9063    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9064    let base_text = indoc! {r#"struct Row;
 9065struct Row1;
 9066struct Row2;
 9067
 9068struct Row4;
 9069struct Row5;
 9070struct Row6;
 9071
 9072struct Row8;
 9073struct Row9;
 9074struct Row10;"#};
 9075
 9076    // When addition hunks are not adjacent to carets, no hunk revert is performed
 9077    assert_hunk_revert(
 9078        indoc! {r#"struct Row;
 9079                   struct Row1;
 9080                   struct Row1.1;
 9081                   struct Row1.2;
 9082                   struct Row2;ˇ
 9083
 9084                   struct Row4;
 9085                   struct Row5;
 9086                   struct Row6;
 9087
 9088                   struct Row8;
 9089                   ˇstruct Row9;
 9090                   struct Row9.1;
 9091                   struct Row9.2;
 9092                   struct Row9.3;
 9093                   struct Row10;"#},
 9094        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9095        indoc! {r#"struct Row;
 9096                   struct Row1;
 9097                   struct Row1.1;
 9098                   struct Row1.2;
 9099                   struct Row2;ˇ
 9100
 9101                   struct Row4;
 9102                   struct Row5;
 9103                   struct Row6;
 9104
 9105                   struct Row8;
 9106                   ˇstruct Row9;
 9107                   struct Row9.1;
 9108                   struct Row9.2;
 9109                   struct Row9.3;
 9110                   struct Row10;"#},
 9111        base_text,
 9112        &mut cx,
 9113    );
 9114    // Same for selections
 9115    assert_hunk_revert(
 9116        indoc! {r#"struct Row;
 9117                   struct Row1;
 9118                   struct Row2;
 9119                   struct Row2.1;
 9120                   struct Row2.2;
 9121                   «ˇ
 9122                   struct Row4;
 9123                   struct» Row5;
 9124                   «struct Row6;
 9125                   ˇ»
 9126                   struct Row9.1;
 9127                   struct Row9.2;
 9128                   struct Row9.3;
 9129                   struct Row8;
 9130                   struct Row9;
 9131                   struct Row10;"#},
 9132        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9133        indoc! {r#"struct Row;
 9134                   struct Row1;
 9135                   struct Row2;
 9136                   struct Row2.1;
 9137                   struct Row2.2;
 9138                   «ˇ
 9139                   struct Row4;
 9140                   struct» Row5;
 9141                   «struct Row6;
 9142                   ˇ»
 9143                   struct Row9.1;
 9144                   struct Row9.2;
 9145                   struct Row9.3;
 9146                   struct Row8;
 9147                   struct Row9;
 9148                   struct Row10;"#},
 9149        base_text,
 9150        &mut cx,
 9151    );
 9152
 9153    // When carets and selections intersect the addition hunks, those are reverted.
 9154    // Adjacent carets got merged.
 9155    assert_hunk_revert(
 9156        indoc! {r#"struct Row;
 9157                   ˇ// something on the top
 9158                   struct Row1;
 9159                   struct Row2;
 9160                   struct Roˇw3.1;
 9161                   struct Row2.2;
 9162                   struct Row2.3;ˇ
 9163
 9164                   struct Row4;
 9165                   struct ˇRow5.1;
 9166                   struct Row5.2;
 9167                   struct «Rowˇ»5.3;
 9168                   struct Row5;
 9169                   struct Row6;
 9170                   ˇ
 9171                   struct Row9.1;
 9172                   struct «Rowˇ»9.2;
 9173                   struct «ˇRow»9.3;
 9174                   struct Row8;
 9175                   struct Row9;
 9176                   «ˇ// something on bottom»
 9177                   struct Row10;"#},
 9178        vec![
 9179            DiffHunkStatus::Added,
 9180            DiffHunkStatus::Added,
 9181            DiffHunkStatus::Added,
 9182            DiffHunkStatus::Added,
 9183            DiffHunkStatus::Added,
 9184        ],
 9185        indoc! {r#"struct Row;
 9186                   ˇstruct Row1;
 9187                   struct Row2;
 9188                   ˇ
 9189                   struct Row4;
 9190                   ˇstruct Row5;
 9191                   struct Row6;
 9192                   ˇ
 9193                   ˇstruct Row8;
 9194                   struct Row9;
 9195                   ˇstruct Row10;"#},
 9196        base_text,
 9197        &mut cx,
 9198    );
 9199}
 9200
 9201#[gpui::test]
 9202async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
 9203    init_test(cx, |_| {});
 9204    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9205    let base_text = indoc! {r#"struct Row;
 9206struct Row1;
 9207struct Row2;
 9208
 9209struct Row4;
 9210struct Row5;
 9211struct Row6;
 9212
 9213struct Row8;
 9214struct Row9;
 9215struct Row10;"#};
 9216
 9217    // Modification hunks behave the same as the addition ones.
 9218    assert_hunk_revert(
 9219        indoc! {r#"struct Row;
 9220                   struct Row1;
 9221                   struct Row33;
 9222                   ˇ
 9223                   struct Row4;
 9224                   struct Row5;
 9225                   struct Row6;
 9226                   ˇ
 9227                   struct Row99;
 9228                   struct Row9;
 9229                   struct Row10;"#},
 9230        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 9231        indoc! {r#"struct Row;
 9232                   struct Row1;
 9233                   struct Row33;
 9234                   ˇ
 9235                   struct Row4;
 9236                   struct Row5;
 9237                   struct Row6;
 9238                   ˇ
 9239                   struct Row99;
 9240                   struct Row9;
 9241                   struct Row10;"#},
 9242        base_text,
 9243        &mut cx,
 9244    );
 9245    assert_hunk_revert(
 9246        indoc! {r#"struct Row;
 9247                   struct Row1;
 9248                   struct Row33;
 9249                   «ˇ
 9250                   struct Row4;
 9251                   struct» Row5;
 9252                   «struct Row6;
 9253                   ˇ»
 9254                   struct Row99;
 9255                   struct Row9;
 9256                   struct Row10;"#},
 9257        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 9258        indoc! {r#"struct Row;
 9259                   struct Row1;
 9260                   struct Row33;
 9261                   «ˇ
 9262                   struct Row4;
 9263                   struct» Row5;
 9264                   «struct Row6;
 9265                   ˇ»
 9266                   struct Row99;
 9267                   struct Row9;
 9268                   struct Row10;"#},
 9269        base_text,
 9270        &mut cx,
 9271    );
 9272
 9273    assert_hunk_revert(
 9274        indoc! {r#"ˇstruct Row1.1;
 9275                   struct Row1;
 9276                   «ˇstr»uct Row22;
 9277
 9278                   struct ˇRow44;
 9279                   struct Row5;
 9280                   struct «Rˇ»ow66;ˇ
 9281
 9282                   «struˇ»ct Row88;
 9283                   struct Row9;
 9284                   struct Row1011;ˇ"#},
 9285        vec![
 9286            DiffHunkStatus::Modified,
 9287            DiffHunkStatus::Modified,
 9288            DiffHunkStatus::Modified,
 9289            DiffHunkStatus::Modified,
 9290            DiffHunkStatus::Modified,
 9291            DiffHunkStatus::Modified,
 9292        ],
 9293        indoc! {r#"struct Row;
 9294                   ˇstruct Row1;
 9295                   struct Row2;
 9296                   ˇ
 9297                   struct Row4;
 9298                   ˇstruct Row5;
 9299                   struct Row6;
 9300                   ˇ
 9301                   struct Row8;
 9302                   ˇstruct Row9;
 9303                   struct Row10;ˇ"#},
 9304        base_text,
 9305        &mut cx,
 9306    );
 9307}
 9308
 9309#[gpui::test]
 9310async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
 9311    init_test(cx, |_| {});
 9312    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9313    let base_text = indoc! {r#"struct Row;
 9314struct Row1;
 9315struct Row2;
 9316
 9317struct Row4;
 9318struct Row5;
 9319struct Row6;
 9320
 9321struct Row8;
 9322struct Row9;
 9323struct Row10;"#};
 9324
 9325    // Deletion hunks trigger with carets on ajacent rows, so carets and selections have to stay farther to avoid the revert
 9326    assert_hunk_revert(
 9327        indoc! {r#"struct Row;
 9328                   struct Row2;
 9329
 9330                   ˇstruct Row4;
 9331                   struct Row5;
 9332                   struct Row6;
 9333                   ˇ
 9334                   struct Row8;
 9335                   struct Row10;"#},
 9336        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 9337        indoc! {r#"struct Row;
 9338                   struct Row2;
 9339
 9340                   ˇstruct Row4;
 9341                   struct Row5;
 9342                   struct Row6;
 9343                   ˇ
 9344                   struct Row8;
 9345                   struct Row10;"#},
 9346        base_text,
 9347        &mut cx,
 9348    );
 9349    assert_hunk_revert(
 9350        indoc! {r#"struct Row;
 9351                   struct Row2;
 9352
 9353                   «ˇstruct Row4;
 9354                   struct» Row5;
 9355                   «struct Row6;
 9356                   ˇ»
 9357                   struct Row8;
 9358                   struct Row10;"#},
 9359        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 9360        indoc! {r#"struct Row;
 9361                   struct Row2;
 9362
 9363                   «ˇstruct Row4;
 9364                   struct» Row5;
 9365                   «struct Row6;
 9366                   ˇ»
 9367                   struct Row8;
 9368                   struct Row10;"#},
 9369        base_text,
 9370        &mut cx,
 9371    );
 9372
 9373    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
 9374    assert_hunk_revert(
 9375        indoc! {r#"struct Row;
 9376                   ˇstruct Row2;
 9377
 9378                   struct Row4;
 9379                   struct Row5;
 9380                   struct Row6;
 9381
 9382                   struct Row8;ˇ
 9383                   struct Row10;"#},
 9384        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 9385        indoc! {r#"struct Row;
 9386                   struct Row1;
 9387                   ˇstruct Row2;
 9388
 9389                   struct Row4;
 9390                   struct Row5;
 9391                   struct Row6;
 9392
 9393                   struct Row8;ˇ
 9394                   struct Row9;
 9395                   struct Row10;"#},
 9396        base_text,
 9397        &mut cx,
 9398    );
 9399    assert_hunk_revert(
 9400        indoc! {r#"struct Row;
 9401                   struct Row2«ˇ;
 9402                   struct Row4;
 9403                   struct» Row5;
 9404                   «struct Row6;
 9405
 9406                   struct Row8;ˇ»
 9407                   struct Row10;"#},
 9408        vec![
 9409            DiffHunkStatus::Removed,
 9410            DiffHunkStatus::Removed,
 9411            DiffHunkStatus::Removed,
 9412        ],
 9413        indoc! {r#"struct Row;
 9414                   struct Row1;
 9415                   struct Row2«ˇ;
 9416
 9417                   struct Row4;
 9418                   struct» Row5;
 9419                   «struct Row6;
 9420
 9421                   struct Row8;ˇ»
 9422                   struct Row9;
 9423                   struct Row10;"#},
 9424        base_text,
 9425        &mut cx,
 9426    );
 9427}
 9428
 9429#[gpui::test]
 9430async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
 9431    init_test(cx, |_| {});
 9432
 9433    let cols = 4;
 9434    let rows = 10;
 9435    let sample_text_1 = sample_text(rows, cols, 'a');
 9436    assert_eq!(
 9437        sample_text_1,
 9438        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 9439    );
 9440    let sample_text_2 = sample_text(rows, cols, 'l');
 9441    assert_eq!(
 9442        sample_text_2,
 9443        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 9444    );
 9445    let sample_text_3 = sample_text(rows, cols, 'v');
 9446    assert_eq!(
 9447        sample_text_3,
 9448        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 9449    );
 9450
 9451    fn diff_every_buffer_row(
 9452        buffer: &Model<Buffer>,
 9453        sample_text: String,
 9454        cols: usize,
 9455        cx: &mut gpui::TestAppContext,
 9456    ) {
 9457        // revert first character in each row, creating one large diff hunk per buffer
 9458        let is_first_char = |offset: usize| offset % cols == 0;
 9459        buffer.update(cx, |buffer, cx| {
 9460            buffer.set_text(
 9461                sample_text
 9462                    .chars()
 9463                    .enumerate()
 9464                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
 9465                    .collect::<String>(),
 9466                cx,
 9467            );
 9468            buffer.set_diff_base(Some(sample_text), cx);
 9469        });
 9470        cx.executor().run_until_parked();
 9471    }
 9472
 9473    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
 9474    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
 9475
 9476    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
 9477    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
 9478
 9479    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
 9480    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
 9481
 9482    let multibuffer = cx.new_model(|cx| {
 9483        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 9484        multibuffer.push_excerpts(
 9485            buffer_1.clone(),
 9486            [
 9487                ExcerptRange {
 9488                    context: Point::new(0, 0)..Point::new(3, 0),
 9489                    primary: None,
 9490                },
 9491                ExcerptRange {
 9492                    context: Point::new(5, 0)..Point::new(7, 0),
 9493                    primary: None,
 9494                },
 9495                ExcerptRange {
 9496                    context: Point::new(9, 0)..Point::new(10, 4),
 9497                    primary: None,
 9498                },
 9499            ],
 9500            cx,
 9501        );
 9502        multibuffer.push_excerpts(
 9503            buffer_2.clone(),
 9504            [
 9505                ExcerptRange {
 9506                    context: Point::new(0, 0)..Point::new(3, 0),
 9507                    primary: None,
 9508                },
 9509                ExcerptRange {
 9510                    context: Point::new(5, 0)..Point::new(7, 0),
 9511                    primary: None,
 9512                },
 9513                ExcerptRange {
 9514                    context: Point::new(9, 0)..Point::new(10, 4),
 9515                    primary: None,
 9516                },
 9517            ],
 9518            cx,
 9519        );
 9520        multibuffer.push_excerpts(
 9521            buffer_3.clone(),
 9522            [
 9523                ExcerptRange {
 9524                    context: Point::new(0, 0)..Point::new(3, 0),
 9525                    primary: None,
 9526                },
 9527                ExcerptRange {
 9528                    context: Point::new(5, 0)..Point::new(7, 0),
 9529                    primary: None,
 9530                },
 9531                ExcerptRange {
 9532                    context: Point::new(9, 0)..Point::new(10, 4),
 9533                    primary: None,
 9534                },
 9535            ],
 9536            cx,
 9537        );
 9538        multibuffer
 9539    });
 9540
 9541    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9542    editor.update(cx, |editor, cx| {
 9543        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");
 9544        editor.select_all(&SelectAll, cx);
 9545        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
 9546    });
 9547    cx.executor().run_until_parked();
 9548    // When all ranges are selected, all buffer hunks are reverted.
 9549    editor.update(cx, |editor, cx| {
 9550        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");
 9551    });
 9552    buffer_1.update(cx, |buffer, _| {
 9553        assert_eq!(buffer.text(), sample_text_1);
 9554    });
 9555    buffer_2.update(cx, |buffer, _| {
 9556        assert_eq!(buffer.text(), sample_text_2);
 9557    });
 9558    buffer_3.update(cx, |buffer, _| {
 9559        assert_eq!(buffer.text(), sample_text_3);
 9560    });
 9561
 9562    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
 9563    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
 9564    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
 9565    editor.update(cx, |editor, cx| {
 9566        editor.change_selections(None, cx, |s| {
 9567            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
 9568        });
 9569        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
 9570    });
 9571    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
 9572    // but not affect buffer_2 and its related excerpts.
 9573    editor.update(cx, |editor, cx| {
 9574        assert_eq!(
 9575            editor.text(cx),
 9576            "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"
 9577        );
 9578    });
 9579    buffer_1.update(cx, |buffer, _| {
 9580        assert_eq!(buffer.text(), sample_text_1);
 9581    });
 9582    buffer_2.update(cx, |buffer, _| {
 9583        assert_eq!(
 9584            buffer.text(),
 9585            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
 9586        );
 9587    });
 9588    buffer_3.update(cx, |buffer, _| {
 9589        assert_eq!(
 9590            buffer.text(),
 9591            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
 9592        );
 9593    });
 9594}
 9595
 9596#[gpui::test]
 9597async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
 9598    init_test(cx, |_| {});
 9599
 9600    let cols = 4;
 9601    let rows = 10;
 9602    let sample_text_1 = sample_text(rows, cols, 'a');
 9603    assert_eq!(
 9604        sample_text_1,
 9605        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 9606    );
 9607    let sample_text_2 = sample_text(rows, cols, 'l');
 9608    assert_eq!(
 9609        sample_text_2,
 9610        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 9611    );
 9612    let sample_text_3 = sample_text(rows, cols, 'v');
 9613    assert_eq!(
 9614        sample_text_3,
 9615        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 9616    );
 9617
 9618    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
 9619    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
 9620    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
 9621
 9622    let multi_buffer = cx.new_model(|cx| {
 9623        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 9624        multibuffer.push_excerpts(
 9625            buffer_1.clone(),
 9626            [
 9627                ExcerptRange {
 9628                    context: Point::new(0, 0)..Point::new(3, 0),
 9629                    primary: None,
 9630                },
 9631                ExcerptRange {
 9632                    context: Point::new(5, 0)..Point::new(7, 0),
 9633                    primary: None,
 9634                },
 9635                ExcerptRange {
 9636                    context: Point::new(9, 0)..Point::new(10, 4),
 9637                    primary: None,
 9638                },
 9639            ],
 9640            cx,
 9641        );
 9642        multibuffer.push_excerpts(
 9643            buffer_2.clone(),
 9644            [
 9645                ExcerptRange {
 9646                    context: Point::new(0, 0)..Point::new(3, 0),
 9647                    primary: None,
 9648                },
 9649                ExcerptRange {
 9650                    context: Point::new(5, 0)..Point::new(7, 0),
 9651                    primary: None,
 9652                },
 9653                ExcerptRange {
 9654                    context: Point::new(9, 0)..Point::new(10, 4),
 9655                    primary: None,
 9656                },
 9657            ],
 9658            cx,
 9659        );
 9660        multibuffer.push_excerpts(
 9661            buffer_3.clone(),
 9662            [
 9663                ExcerptRange {
 9664                    context: Point::new(0, 0)..Point::new(3, 0),
 9665                    primary: None,
 9666                },
 9667                ExcerptRange {
 9668                    context: Point::new(5, 0)..Point::new(7, 0),
 9669                    primary: None,
 9670                },
 9671                ExcerptRange {
 9672                    context: Point::new(9, 0)..Point::new(10, 4),
 9673                    primary: None,
 9674                },
 9675            ],
 9676            cx,
 9677        );
 9678        multibuffer
 9679    });
 9680
 9681    let fs = FakeFs::new(cx.executor());
 9682    fs.insert_tree(
 9683        "/a",
 9684        json!({
 9685            "main.rs": sample_text_1,
 9686            "other.rs": sample_text_2,
 9687            "lib.rs": sample_text_3,
 9688        }),
 9689    )
 9690    .await;
 9691    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9692    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9693    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9694    let multi_buffer_editor = cx.new_view(|cx| {
 9695        Editor::new(
 9696            EditorMode::Full,
 9697            multi_buffer,
 9698            Some(project.clone()),
 9699            true,
 9700            cx,
 9701        )
 9702    });
 9703    let multibuffer_item_id = workspace
 9704        .update(cx, |workspace, cx| {
 9705            assert!(
 9706                workspace.active_item(cx).is_none(),
 9707                "active item should be None before the first item is added"
 9708            );
 9709            workspace.add_item_to_active_pane(Box::new(multi_buffer_editor.clone()), None, cx);
 9710            let active_item = workspace
 9711                .active_item(cx)
 9712                .expect("should have an active item after adding the multi buffer");
 9713            assert!(
 9714                !active_item.is_singleton(cx),
 9715                "A multi buffer was expected to active after adding"
 9716            );
 9717            active_item.item_id()
 9718        })
 9719        .unwrap();
 9720    cx.executor().run_until_parked();
 9721
 9722    multi_buffer_editor.update(cx, |editor, cx| {
 9723        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 9724        editor.open_excerpts(&OpenExcerpts, cx);
 9725    });
 9726    cx.executor().run_until_parked();
 9727    let first_item_id = workspace
 9728        .update(cx, |workspace, cx| {
 9729            let active_item = workspace
 9730                .active_item(cx)
 9731                .expect("should have an active item after navigating into the 1st buffer");
 9732            let first_item_id = active_item.item_id();
 9733            assert_ne!(
 9734                first_item_id, multibuffer_item_id,
 9735                "Should navigate into the 1st buffer and activate it"
 9736            );
 9737            assert!(
 9738                active_item.is_singleton(cx),
 9739                "New active item should be a singleton buffer"
 9740            );
 9741            assert_eq!(
 9742                active_item
 9743                    .act_as::<Editor>(cx)
 9744                    .expect("should have navigated into an editor for the 1st buffer")
 9745                    .read(cx)
 9746                    .text(cx),
 9747                sample_text_1
 9748            );
 9749
 9750            workspace
 9751                .go_back(workspace.active_pane().downgrade(), cx)
 9752                .detach_and_log_err(cx);
 9753
 9754            first_item_id
 9755        })
 9756        .unwrap();
 9757    cx.executor().run_until_parked();
 9758    workspace
 9759        .update(cx, |workspace, cx| {
 9760            let active_item = workspace
 9761                .active_item(cx)
 9762                .expect("should have an active item after navigating back");
 9763            assert_eq!(
 9764                active_item.item_id(),
 9765                multibuffer_item_id,
 9766                "Should navigate back to the multi buffer"
 9767            );
 9768            assert!(!active_item.is_singleton(cx));
 9769        })
 9770        .unwrap();
 9771
 9772    multi_buffer_editor.update(cx, |editor, cx| {
 9773        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 9774            s.select_ranges(Some(39..40))
 9775        });
 9776        editor.open_excerpts(&OpenExcerpts, cx);
 9777    });
 9778    cx.executor().run_until_parked();
 9779    let second_item_id = workspace
 9780        .update(cx, |workspace, cx| {
 9781            let active_item = workspace
 9782                .active_item(cx)
 9783                .expect("should have an active item after navigating into the 2nd buffer");
 9784            let second_item_id = active_item.item_id();
 9785            assert_ne!(
 9786                second_item_id, multibuffer_item_id,
 9787                "Should navigate away from the multibuffer"
 9788            );
 9789            assert_ne!(
 9790                second_item_id, first_item_id,
 9791                "Should navigate into the 2nd buffer and activate it"
 9792            );
 9793            assert!(
 9794                active_item.is_singleton(cx),
 9795                "New active item should be a singleton buffer"
 9796            );
 9797            assert_eq!(
 9798                active_item
 9799                    .act_as::<Editor>(cx)
 9800                    .expect("should have navigated into an editor")
 9801                    .read(cx)
 9802                    .text(cx),
 9803                sample_text_2
 9804            );
 9805
 9806            workspace
 9807                .go_back(workspace.active_pane().downgrade(), cx)
 9808                .detach_and_log_err(cx);
 9809
 9810            second_item_id
 9811        })
 9812        .unwrap();
 9813    cx.executor().run_until_parked();
 9814    workspace
 9815        .update(cx, |workspace, cx| {
 9816            let active_item = workspace
 9817                .active_item(cx)
 9818                .expect("should have an active item after navigating back from the 2nd buffer");
 9819            assert_eq!(
 9820                active_item.item_id(),
 9821                multibuffer_item_id,
 9822                "Should navigate back from the 2nd buffer to the multi buffer"
 9823            );
 9824            assert!(!active_item.is_singleton(cx));
 9825        })
 9826        .unwrap();
 9827
 9828    multi_buffer_editor.update(cx, |editor, cx| {
 9829        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 9830            s.select_ranges(Some(60..70))
 9831        });
 9832        editor.open_excerpts(&OpenExcerpts, cx);
 9833    });
 9834    cx.executor().run_until_parked();
 9835    workspace
 9836        .update(cx, |workspace, cx| {
 9837            let active_item = workspace
 9838                .active_item(cx)
 9839                .expect("should have an active item after navigating into the 3rd buffer");
 9840            let third_item_id = active_item.item_id();
 9841            assert_ne!(
 9842                third_item_id, multibuffer_item_id,
 9843                "Should navigate into the 3rd buffer and activate it"
 9844            );
 9845            assert_ne!(third_item_id, first_item_id);
 9846            assert_ne!(third_item_id, second_item_id);
 9847            assert!(
 9848                active_item.is_singleton(cx),
 9849                "New active item should be a singleton buffer"
 9850            );
 9851            assert_eq!(
 9852                active_item
 9853                    .act_as::<Editor>(cx)
 9854                    .expect("should have navigated into an editor")
 9855                    .read(cx)
 9856                    .text(cx),
 9857                sample_text_3
 9858            );
 9859
 9860            workspace
 9861                .go_back(workspace.active_pane().downgrade(), cx)
 9862                .detach_and_log_err(cx);
 9863        })
 9864        .unwrap();
 9865    cx.executor().run_until_parked();
 9866    workspace
 9867        .update(cx, |workspace, cx| {
 9868            let active_item = workspace
 9869                .active_item(cx)
 9870                .expect("should have an active item after navigating back from the 3rd buffer");
 9871            assert_eq!(
 9872                active_item.item_id(),
 9873                multibuffer_item_id,
 9874                "Should navigate back from the 3rd buffer to the multi buffer"
 9875            );
 9876            assert!(!active_item.is_singleton(cx));
 9877        })
 9878        .unwrap();
 9879}
 9880
 9881#[gpui::test]
 9882async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9883    init_test(cx, |_| {});
 9884
 9885    let mut cx = EditorTestContext::new(cx).await;
 9886
 9887    let diff_base = r#"
 9888        use some::mod;
 9889
 9890        const A: u32 = 42;
 9891
 9892        fn main() {
 9893            println!("hello");
 9894
 9895            println!("world");
 9896        }
 9897        "#
 9898    .unindent();
 9899
 9900    cx.set_state(
 9901        &r#"
 9902        use some::modified;
 9903
 9904        ˇ
 9905        fn main() {
 9906            println!("hello there");
 9907
 9908            println!("around the");
 9909            println!("world");
 9910        }
 9911        "#
 9912        .unindent(),
 9913    );
 9914
 9915    cx.set_diff_base(Some(&diff_base));
 9916    executor.run_until_parked();
 9917    let unexpanded_hunks = vec![
 9918        (
 9919            "use some::mod;\n".to_string(),
 9920            DiffHunkStatus::Modified,
 9921            DisplayRow(0)..DisplayRow(1),
 9922        ),
 9923        (
 9924            "const A: u32 = 42;\n".to_string(),
 9925            DiffHunkStatus::Removed,
 9926            DisplayRow(2)..DisplayRow(2),
 9927        ),
 9928        (
 9929            "    println!(\"hello\");\n".to_string(),
 9930            DiffHunkStatus::Modified,
 9931            DisplayRow(4)..DisplayRow(5),
 9932        ),
 9933        (
 9934            "".to_string(),
 9935            DiffHunkStatus::Added,
 9936            DisplayRow(6)..DisplayRow(7),
 9937        ),
 9938    ];
 9939    cx.update_editor(|editor, cx| {
 9940        let snapshot = editor.snapshot(cx);
 9941        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9942        assert_eq!(all_hunks, unexpanded_hunks);
 9943    });
 9944
 9945    cx.update_editor(|editor, cx| {
 9946        for _ in 0..4 {
 9947            editor.go_to_hunk(&GoToHunk, cx);
 9948            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
 9949        }
 9950    });
 9951    executor.run_until_parked();
 9952    cx.assert_editor_state(
 9953        &r#"
 9954        use some::modified;
 9955
 9956        ˇ
 9957        fn main() {
 9958            println!("hello there");
 9959
 9960            println!("around the");
 9961            println!("world");
 9962        }
 9963        "#
 9964        .unindent(),
 9965    );
 9966    cx.update_editor(|editor, cx| {
 9967        let snapshot = editor.snapshot(cx);
 9968        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9969        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
 9970        assert_eq!(
 9971            expanded_hunks_background_highlights(editor, cx),
 9972            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
 9973            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
 9974        );
 9975        assert_eq!(
 9976            all_hunks,
 9977            vec![
 9978                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
 9979                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
 9980                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
 9981                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
 9982            ],
 9983            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
 9984            (from modified and removed hunks)"
 9985        );
 9986        assert_eq!(
 9987            all_hunks, all_expanded_hunks,
 9988            "Editor hunks should not change and all be expanded"
 9989        );
 9990    });
 9991
 9992    cx.update_editor(|editor, cx| {
 9993        editor.cancel(&Cancel, cx);
 9994
 9995        let snapshot = editor.snapshot(cx);
 9996        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9997        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
 9998        assert_eq!(
 9999            expanded_hunks_background_highlights(editor, cx),
10000            Vec::new(),
10001            "After cancelling in editor, no git highlights should be left"
10002        );
10003        assert_eq!(
10004            all_expanded_hunks,
10005            Vec::new(),
10006            "After cancelling in editor, no hunks should be expanded"
10007        );
10008        assert_eq!(
10009            all_hunks, unexpanded_hunks,
10010            "After cancelling in editor, regular hunks' coordinates should get back to normal"
10011        );
10012    });
10013}
10014
10015#[gpui::test]
10016async fn test_toggled_diff_base_change(
10017    executor: BackgroundExecutor,
10018    cx: &mut gpui::TestAppContext,
10019) {
10020    init_test(cx, |_| {});
10021
10022    let mut cx = EditorTestContext::new(cx).await;
10023
10024    let diff_base = r#"
10025        use some::mod1;
10026        use some::mod2;
10027
10028        const A: u32 = 42;
10029        const B: u32 = 42;
10030        const C: u32 = 42;
10031
10032        fn main(ˇ) {
10033            println!("hello");
10034
10035            println!("world");
10036        }
10037        "#
10038    .unindent();
10039
10040    cx.set_state(
10041        &r#"
10042        use some::mod2;
10043
10044        const A: u32 = 42;
10045        const C: u32 = 42;
10046
10047        fn main(ˇ) {
10048            //println!("hello");
10049
10050            println!("world");
10051            //
10052            //
10053        }
10054        "#
10055        .unindent(),
10056    );
10057
10058    cx.set_diff_base(Some(&diff_base));
10059    executor.run_until_parked();
10060    cx.update_editor(|editor, cx| {
10061        let snapshot = editor.snapshot(cx);
10062        let all_hunks = editor_hunks(editor, &snapshot, cx);
10063        assert_eq!(
10064            all_hunks,
10065            vec![
10066                (
10067                    "use some::mod1;\n".to_string(),
10068                    DiffHunkStatus::Removed,
10069                    DisplayRow(0)..DisplayRow(0)
10070                ),
10071                (
10072                    "const B: u32 = 42;\n".to_string(),
10073                    DiffHunkStatus::Removed,
10074                    DisplayRow(3)..DisplayRow(3)
10075                ),
10076                (
10077                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10078                    DiffHunkStatus::Modified,
10079                    DisplayRow(5)..DisplayRow(7)
10080                ),
10081                (
10082                    "".to_string(),
10083                    DiffHunkStatus::Added,
10084                    DisplayRow(9)..DisplayRow(11)
10085                ),
10086            ]
10087        );
10088    });
10089
10090    cx.update_editor(|editor, cx| {
10091        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10092    });
10093    executor.run_until_parked();
10094    cx.assert_editor_state(
10095        &r#"
10096        use some::mod2;
10097
10098        const A: u32 = 42;
10099        const C: u32 = 42;
10100
10101        fn main(ˇ) {
10102            //println!("hello");
10103
10104            println!("world");
10105            //
10106            //
10107        }
10108        "#
10109        .unindent(),
10110    );
10111    cx.update_editor(|editor, cx| {
10112        let snapshot = editor.snapshot(cx);
10113        let all_hunks = editor_hunks(editor, &snapshot, cx);
10114        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10115        assert_eq!(
10116            expanded_hunks_background_highlights(editor, cx),
10117            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
10118            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10119        );
10120        assert_eq!(
10121            all_hunks,
10122            vec![
10123                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
10124                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
10125                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
10126                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
10127            ],
10128            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10129            (from modified and removed hunks)"
10130        );
10131        assert_eq!(
10132            all_hunks, all_expanded_hunks,
10133            "Editor hunks should not change and all be expanded"
10134        );
10135    });
10136
10137    cx.set_diff_base(Some("new diff base!"));
10138    executor.run_until_parked();
10139
10140    cx.update_editor(|editor, cx| {
10141        let snapshot = editor.snapshot(cx);
10142        let all_hunks = editor_hunks(editor, &snapshot, cx);
10143        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10144        assert_eq!(
10145            expanded_hunks_background_highlights(editor, cx),
10146            Vec::new(),
10147            "After diff base is changed, old git highlights should be removed"
10148        );
10149        assert_eq!(
10150            all_expanded_hunks,
10151            Vec::new(),
10152            "After diff base is changed, old git hunk expansions should be removed"
10153        );
10154        assert_eq!(
10155            all_hunks,
10156            vec![(
10157                "new diff base!".to_string(),
10158                DiffHunkStatus::Modified,
10159                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
10160            )],
10161            "After diff base is changed, hunks should update"
10162        );
10163    });
10164}
10165
10166#[gpui::test]
10167async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10168    init_test(cx, |_| {});
10169
10170    let mut cx = EditorTestContext::new(cx).await;
10171
10172    let diff_base = r#"
10173        use some::mod1;
10174        use some::mod2;
10175
10176        const A: u32 = 42;
10177        const B: u32 = 42;
10178        const C: u32 = 42;
10179
10180        fn main(ˇ) {
10181            println!("hello");
10182
10183            println!("world");
10184        }
10185
10186        fn another() {
10187            println!("another");
10188        }
10189
10190        fn another2() {
10191            println!("another2");
10192        }
10193        "#
10194    .unindent();
10195
10196    cx.set_state(
10197        &r#"
10198        «use some::mod2;
10199
10200        const A: u32 = 42;
10201        const C: u32 = 42;
10202
10203        fn main() {
10204            //println!("hello");
10205
10206            println!("world");
10207            //
10208            //ˇ»
10209        }
10210
10211        fn another() {
10212            println!("another");
10213            println!("another");
10214        }
10215
10216            println!("another2");
10217        }
10218        "#
10219        .unindent(),
10220    );
10221
10222    cx.set_diff_base(Some(&diff_base));
10223    executor.run_until_parked();
10224    cx.update_editor(|editor, cx| {
10225        let snapshot = editor.snapshot(cx);
10226        let all_hunks = editor_hunks(editor, &snapshot, cx);
10227        assert_eq!(
10228            all_hunks,
10229            vec![
10230                (
10231                    "use some::mod1;\n".to_string(),
10232                    DiffHunkStatus::Removed,
10233                    DisplayRow(0)..DisplayRow(0)
10234                ),
10235                (
10236                    "const B: u32 = 42;\n".to_string(),
10237                    DiffHunkStatus::Removed,
10238                    DisplayRow(3)..DisplayRow(3)
10239                ),
10240                (
10241                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10242                    DiffHunkStatus::Modified,
10243                    DisplayRow(5)..DisplayRow(7)
10244                ),
10245                (
10246                    "".to_string(),
10247                    DiffHunkStatus::Added,
10248                    DisplayRow(9)..DisplayRow(11)
10249                ),
10250                (
10251                    "".to_string(),
10252                    DiffHunkStatus::Added,
10253                    DisplayRow(15)..DisplayRow(16)
10254                ),
10255                (
10256                    "fn another2() {\n".to_string(),
10257                    DiffHunkStatus::Removed,
10258                    DisplayRow(18)..DisplayRow(18)
10259                ),
10260            ]
10261        );
10262    });
10263
10264    cx.update_editor(|editor, cx| {
10265        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10266    });
10267    executor.run_until_parked();
10268    cx.assert_editor_state(
10269        &r#"
10270        «use some::mod2;
10271
10272        const A: u32 = 42;
10273        const C: u32 = 42;
10274
10275        fn main() {
10276            //println!("hello");
10277
10278            println!("world");
10279            //
10280            //ˇ»
10281        }
10282
10283        fn another() {
10284            println!("another");
10285            println!("another");
10286        }
10287
10288            println!("another2");
10289        }
10290        "#
10291        .unindent(),
10292    );
10293    cx.update_editor(|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(9)..=DisplayRow(10),
10301                DisplayRow(13)..=DisplayRow(14),
10302                DisplayRow(19)..=DisplayRow(19)
10303            ]
10304        );
10305        assert_eq!(
10306            all_hunks,
10307            vec![
10308                (
10309                    "use some::mod1;\n".to_string(),
10310                    DiffHunkStatus::Removed,
10311                    DisplayRow(1)..DisplayRow(1)
10312                ),
10313                (
10314                    "const B: u32 = 42;\n".to_string(),
10315                    DiffHunkStatus::Removed,
10316                    DisplayRow(5)..DisplayRow(5)
10317                ),
10318                (
10319                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10320                    DiffHunkStatus::Modified,
10321                    DisplayRow(9)..DisplayRow(11)
10322                ),
10323                (
10324                    "".to_string(),
10325                    DiffHunkStatus::Added,
10326                    DisplayRow(13)..DisplayRow(15)
10327                ),
10328                (
10329                    "".to_string(),
10330                    DiffHunkStatus::Added,
10331                    DisplayRow(19)..DisplayRow(20)
10332                ),
10333                (
10334                    "fn another2() {\n".to_string(),
10335                    DiffHunkStatus::Removed,
10336                    DisplayRow(23)..DisplayRow(23)
10337                ),
10338            ],
10339        );
10340        assert_eq!(all_hunks, all_expanded_hunks);
10341    });
10342
10343    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
10344    cx.executor().run_until_parked();
10345    cx.assert_editor_state(
10346        &r#"
10347        «use some::mod2;
10348
10349        const A: u32 = 42;
10350        const C: u32 = 42;
10351
10352        fn main() {
10353            //println!("hello");
10354
10355            println!("world");
10356            //
10357            //ˇ»
10358        }
10359
10360        fn another() {
10361            println!("another");
10362            println!("another");
10363        }
10364
10365            println!("another2");
10366        }
10367        "#
10368        .unindent(),
10369    );
10370    cx.update_editor(|editor, cx| {
10371        let snapshot = editor.snapshot(cx);
10372        let all_hunks = editor_hunks(editor, &snapshot, cx);
10373        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10374        assert_eq!(
10375            expanded_hunks_background_highlights(editor, cx),
10376            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
10377            "Only one hunk is left not folded, its highlight should be visible"
10378        );
10379        assert_eq!(
10380            all_hunks,
10381            vec![
10382                (
10383                    "use some::mod1;\n".to_string(),
10384                    DiffHunkStatus::Removed,
10385                    DisplayRow(0)..DisplayRow(0)
10386                ),
10387                (
10388                    "const B: u32 = 42;\n".to_string(),
10389                    DiffHunkStatus::Removed,
10390                    DisplayRow(0)..DisplayRow(0)
10391                ),
10392                (
10393                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10394                    DiffHunkStatus::Modified,
10395                    DisplayRow(0)..DisplayRow(0)
10396                ),
10397                (
10398                    "".to_string(),
10399                    DiffHunkStatus::Added,
10400                    DisplayRow(0)..DisplayRow(1)
10401                ),
10402                (
10403                    "".to_string(),
10404                    DiffHunkStatus::Added,
10405                    DisplayRow(5)..DisplayRow(6)
10406                ),
10407                (
10408                    "fn another2() {\n".to_string(),
10409                    DiffHunkStatus::Removed,
10410                    DisplayRow(9)..DisplayRow(9)
10411                ),
10412            ],
10413            "Hunk list should still return shifted folded hunks"
10414        );
10415        assert_eq!(
10416            all_expanded_hunks,
10417            vec![
10418                (
10419                    "".to_string(),
10420                    DiffHunkStatus::Added,
10421                    DisplayRow(5)..DisplayRow(6)
10422                ),
10423                (
10424                    "fn another2() {\n".to_string(),
10425                    DiffHunkStatus::Removed,
10426                    DisplayRow(9)..DisplayRow(9)
10427                ),
10428            ],
10429            "Only non-folded hunks should be left expanded"
10430        );
10431    });
10432
10433    cx.update_editor(|editor, cx| {
10434        editor.select_all(&SelectAll, cx);
10435        editor.unfold_lines(&UnfoldLines, cx);
10436    });
10437    cx.executor().run_until_parked();
10438    cx.assert_editor_state(
10439        &r#"
10440        «use some::mod2;
10441
10442        const A: u32 = 42;
10443        const C: u32 = 42;
10444
10445        fn main() {
10446            //println!("hello");
10447
10448            println!("world");
10449            //
10450            //
10451        }
10452
10453        fn another() {
10454            println!("another");
10455            println!("another");
10456        }
10457
10458            println!("another2");
10459        }
10460        ˇ»"#
10461        .unindent(),
10462    );
10463    cx.update_editor(|editor, cx| {
10464        let snapshot = editor.snapshot(cx);
10465        let all_hunks = editor_hunks(editor, &snapshot, cx);
10466        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10467        assert_eq!(
10468            expanded_hunks_background_highlights(editor, cx),
10469            vec![
10470                DisplayRow(9)..=DisplayRow(10),
10471                DisplayRow(13)..=DisplayRow(14),
10472                DisplayRow(19)..=DisplayRow(19)
10473            ],
10474            "After unfolding, all hunk diffs should be visible again"
10475        );
10476        assert_eq!(
10477            all_hunks,
10478            vec![
10479                (
10480                    "use some::mod1;\n".to_string(),
10481                    DiffHunkStatus::Removed,
10482                    DisplayRow(1)..DisplayRow(1)
10483                ),
10484                (
10485                    "const B: u32 = 42;\n".to_string(),
10486                    DiffHunkStatus::Removed,
10487                    DisplayRow(5)..DisplayRow(5)
10488                ),
10489                (
10490                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10491                    DiffHunkStatus::Modified,
10492                    DisplayRow(9)..DisplayRow(11)
10493                ),
10494                (
10495                    "".to_string(),
10496                    DiffHunkStatus::Added,
10497                    DisplayRow(13)..DisplayRow(15)
10498                ),
10499                (
10500                    "".to_string(),
10501                    DiffHunkStatus::Added,
10502                    DisplayRow(19)..DisplayRow(20)
10503                ),
10504                (
10505                    "fn another2() {\n".to_string(),
10506                    DiffHunkStatus::Removed,
10507                    DisplayRow(23)..DisplayRow(23)
10508                ),
10509            ],
10510        );
10511        assert_eq!(all_hunks, all_expanded_hunks);
10512    });
10513}
10514
10515#[gpui::test]
10516async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
10517    init_test(cx, |_| {});
10518
10519    let cols = 4;
10520    let rows = 10;
10521    let sample_text_1 = sample_text(rows, cols, 'a');
10522    assert_eq!(
10523        sample_text_1,
10524        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10525    );
10526    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
10527    let sample_text_2 = sample_text(rows, cols, 'l');
10528    assert_eq!(
10529        sample_text_2,
10530        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10531    );
10532    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
10533    let sample_text_3 = sample_text(rows, cols, 'v');
10534    assert_eq!(
10535        sample_text_3,
10536        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10537    );
10538    let modified_sample_text_3 =
10539        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
10540    let buffer_1 = cx.new_model(|cx| {
10541        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
10542        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
10543        buffer
10544    });
10545    let buffer_2 = cx.new_model(|cx| {
10546        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
10547        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
10548        buffer
10549    });
10550    let buffer_3 = cx.new_model(|cx| {
10551        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
10552        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
10553        buffer
10554    });
10555
10556    let multi_buffer = cx.new_model(|cx| {
10557        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10558        multibuffer.push_excerpts(
10559            buffer_1.clone(),
10560            [
10561                ExcerptRange {
10562                    context: Point::new(0, 0)..Point::new(3, 0),
10563                    primary: None,
10564                },
10565                ExcerptRange {
10566                    context: Point::new(5, 0)..Point::new(7, 0),
10567                    primary: None,
10568                },
10569                ExcerptRange {
10570                    context: Point::new(9, 0)..Point::new(10, 4),
10571                    primary: None,
10572                },
10573            ],
10574            cx,
10575        );
10576        multibuffer.push_excerpts(
10577            buffer_2.clone(),
10578            [
10579                ExcerptRange {
10580                    context: Point::new(0, 0)..Point::new(3, 0),
10581                    primary: None,
10582                },
10583                ExcerptRange {
10584                    context: Point::new(5, 0)..Point::new(7, 0),
10585                    primary: None,
10586                },
10587                ExcerptRange {
10588                    context: Point::new(9, 0)..Point::new(10, 4),
10589                    primary: None,
10590                },
10591            ],
10592            cx,
10593        );
10594        multibuffer.push_excerpts(
10595            buffer_3.clone(),
10596            [
10597                ExcerptRange {
10598                    context: Point::new(0, 0)..Point::new(3, 0),
10599                    primary: None,
10600                },
10601                ExcerptRange {
10602                    context: Point::new(5, 0)..Point::new(7, 0),
10603                    primary: None,
10604                },
10605                ExcerptRange {
10606                    context: Point::new(9, 0)..Point::new(10, 4),
10607                    primary: None,
10608                },
10609            ],
10610            cx,
10611        );
10612        multibuffer
10613    });
10614
10615    let fs = FakeFs::new(cx.executor());
10616    fs.insert_tree(
10617        "/a",
10618        json!({
10619            "main.rs": modified_sample_text_1,
10620            "other.rs": modified_sample_text_2,
10621            "lib.rs": modified_sample_text_3,
10622        }),
10623    )
10624    .await;
10625
10626    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10627    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10628    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10629    let multi_buffer_editor = cx.new_view(|cx| {
10630        Editor::new(
10631            EditorMode::Full,
10632            multi_buffer,
10633            Some(project.clone()),
10634            true,
10635            cx,
10636        )
10637    });
10638    cx.executor().run_until_parked();
10639
10640    let expected_all_hunks = vec![
10641        (
10642            "bbbb\n".to_string(),
10643            DiffHunkStatus::Removed,
10644            DisplayRow(4)..DisplayRow(4),
10645        ),
10646        (
10647            "nnnn\n".to_string(),
10648            DiffHunkStatus::Modified,
10649            DisplayRow(21)..DisplayRow(22),
10650        ),
10651        (
10652            "".to_string(),
10653            DiffHunkStatus::Added,
10654            DisplayRow(41)..DisplayRow(42),
10655        ),
10656    ];
10657    let expected_all_hunks_shifted = vec![
10658        (
10659            "bbbb\n".to_string(),
10660            DiffHunkStatus::Removed,
10661            DisplayRow(5)..DisplayRow(5),
10662        ),
10663        (
10664            "nnnn\n".to_string(),
10665            DiffHunkStatus::Modified,
10666            DisplayRow(23)..DisplayRow(24),
10667        ),
10668        (
10669            "".to_string(),
10670            DiffHunkStatus::Added,
10671            DisplayRow(43)..DisplayRow(44),
10672        ),
10673    ];
10674
10675    multi_buffer_editor.update(cx, |editor, cx| {
10676        let snapshot = editor.snapshot(cx);
10677        let all_hunks = editor_hunks(editor, &snapshot, cx);
10678        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10679        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10680        assert_eq!(all_hunks, expected_all_hunks);
10681        assert_eq!(all_expanded_hunks, Vec::new());
10682    });
10683
10684    multi_buffer_editor.update(cx, |editor, cx| {
10685        editor.select_all(&SelectAll, cx);
10686        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10687    });
10688    cx.executor().run_until_parked();
10689    multi_buffer_editor.update(cx, |editor, cx| {
10690        let snapshot = editor.snapshot(cx);
10691        let all_hunks = editor_hunks(editor, &snapshot, cx);
10692        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10693        assert_eq!(
10694            expanded_hunks_background_highlights(editor, cx),
10695            vec![
10696                DisplayRow(23)..=DisplayRow(23),
10697                DisplayRow(43)..=DisplayRow(43)
10698            ],
10699        );
10700        assert_eq!(all_hunks, expected_all_hunks_shifted);
10701        assert_eq!(all_hunks, all_expanded_hunks);
10702    });
10703
10704    multi_buffer_editor.update(cx, |editor, cx| {
10705        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10706    });
10707    cx.executor().run_until_parked();
10708    multi_buffer_editor.update(cx, |editor, cx| {
10709        let snapshot = editor.snapshot(cx);
10710        let all_hunks = editor_hunks(editor, &snapshot, cx);
10711        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10712        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10713        assert_eq!(all_hunks, expected_all_hunks);
10714        assert_eq!(all_expanded_hunks, Vec::new());
10715    });
10716
10717    multi_buffer_editor.update(cx, |editor, cx| {
10718        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10719    });
10720    cx.executor().run_until_parked();
10721    multi_buffer_editor.update(cx, |editor, cx| {
10722        let snapshot = editor.snapshot(cx);
10723        let all_hunks = editor_hunks(editor, &snapshot, cx);
10724        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10725        assert_eq!(
10726            expanded_hunks_background_highlights(editor, cx),
10727            vec![
10728                DisplayRow(23)..=DisplayRow(23),
10729                DisplayRow(43)..=DisplayRow(43)
10730            ],
10731        );
10732        assert_eq!(all_hunks, expected_all_hunks_shifted);
10733        assert_eq!(all_hunks, all_expanded_hunks);
10734    });
10735
10736    multi_buffer_editor.update(cx, |editor, cx| {
10737        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10738    });
10739    cx.executor().run_until_parked();
10740    multi_buffer_editor.update(cx, |editor, cx| {
10741        let snapshot = editor.snapshot(cx);
10742        let all_hunks = editor_hunks(editor, &snapshot, cx);
10743        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10744        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10745        assert_eq!(all_hunks, expected_all_hunks);
10746        assert_eq!(all_expanded_hunks, Vec::new());
10747    });
10748}
10749
10750#[gpui::test]
10751async fn test_edits_around_toggled_additions(
10752    executor: BackgroundExecutor,
10753    cx: &mut gpui::TestAppContext,
10754) {
10755    init_test(cx, |_| {});
10756
10757    let mut cx = EditorTestContext::new(cx).await;
10758
10759    let diff_base = r#"
10760        use some::mod1;
10761        use some::mod2;
10762
10763        const A: u32 = 42;
10764
10765        fn main() {
10766            println!("hello");
10767
10768            println!("world");
10769        }
10770        "#
10771    .unindent();
10772    executor.run_until_parked();
10773    cx.set_state(
10774        &r#"
10775        use some::mod1;
10776        use some::mod2;
10777
10778        const A: u32 = 42;
10779        const B: u32 = 42;
10780        const C: u32 = 42;
10781        ˇ
10782
10783        fn main() {
10784            println!("hello");
10785
10786            println!("world");
10787        }
10788        "#
10789        .unindent(),
10790    );
10791
10792    cx.set_diff_base(Some(&diff_base));
10793    executor.run_until_parked();
10794    cx.update_editor(|editor, cx| {
10795        let snapshot = editor.snapshot(cx);
10796        let all_hunks = editor_hunks(editor, &snapshot, cx);
10797        assert_eq!(
10798            all_hunks,
10799            vec![(
10800                "".to_string(),
10801                DiffHunkStatus::Added,
10802                DisplayRow(4)..DisplayRow(7)
10803            )]
10804        );
10805    });
10806    cx.update_editor(|editor, cx| {
10807        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10808    });
10809    executor.run_until_parked();
10810    cx.assert_editor_state(
10811        &r#"
10812        use some::mod1;
10813        use some::mod2;
10814
10815        const A: u32 = 42;
10816        const B: u32 = 42;
10817        const C: u32 = 42;
10818        ˇ
10819
10820        fn main() {
10821            println!("hello");
10822
10823            println!("world");
10824        }
10825        "#
10826        .unindent(),
10827    );
10828    cx.update_editor(|editor, cx| {
10829        let snapshot = editor.snapshot(cx);
10830        let all_hunks = editor_hunks(editor, &snapshot, cx);
10831        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10832        assert_eq!(
10833            all_hunks,
10834            vec![(
10835                "".to_string(),
10836                DiffHunkStatus::Added,
10837                DisplayRow(4)..DisplayRow(7)
10838            )]
10839        );
10840        assert_eq!(
10841            expanded_hunks_background_highlights(editor, cx),
10842            vec![DisplayRow(4)..=DisplayRow(6)]
10843        );
10844        assert_eq!(all_hunks, all_expanded_hunks);
10845    });
10846
10847    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
10848    executor.run_until_parked();
10849    cx.assert_editor_state(
10850        &r#"
10851        use some::mod1;
10852        use some::mod2;
10853
10854        const A: u32 = 42;
10855        const B: u32 = 42;
10856        const C: u32 = 42;
10857        const D: u32 = 42;
10858        ˇ
10859
10860        fn main() {
10861            println!("hello");
10862
10863            println!("world");
10864        }
10865        "#
10866        .unindent(),
10867    );
10868    cx.update_editor(|editor, cx| {
10869        let snapshot = editor.snapshot(cx);
10870        let all_hunks = editor_hunks(editor, &snapshot, cx);
10871        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10872        assert_eq!(
10873            all_hunks,
10874            vec![(
10875                "".to_string(),
10876                DiffHunkStatus::Added,
10877                DisplayRow(4)..DisplayRow(8)
10878            )]
10879        );
10880        assert_eq!(
10881            expanded_hunks_background_highlights(editor, cx),
10882            vec![DisplayRow(4)..=DisplayRow(6)],
10883            "Edited hunk should have one more line added"
10884        );
10885        assert_eq!(
10886            all_hunks, all_expanded_hunks,
10887            "Expanded hunk should also grow with the addition"
10888        );
10889    });
10890
10891    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
10892    executor.run_until_parked();
10893    cx.assert_editor_state(
10894        &r#"
10895        use some::mod1;
10896        use some::mod2;
10897
10898        const A: u32 = 42;
10899        const B: u32 = 42;
10900        const C: u32 = 42;
10901        const D: u32 = 42;
10902        const E: u32 = 42;
10903        ˇ
10904
10905        fn main() {
10906            println!("hello");
10907
10908            println!("world");
10909        }
10910        "#
10911        .unindent(),
10912    );
10913    cx.update_editor(|editor, cx| {
10914        let snapshot = editor.snapshot(cx);
10915        let all_hunks = editor_hunks(editor, &snapshot, cx);
10916        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10917        assert_eq!(
10918            all_hunks,
10919            vec![(
10920                "".to_string(),
10921                DiffHunkStatus::Added,
10922                DisplayRow(4)..DisplayRow(9)
10923            )]
10924        );
10925        assert_eq!(
10926            expanded_hunks_background_highlights(editor, cx),
10927            vec![DisplayRow(4)..=DisplayRow(6)],
10928            "Edited hunk should have one more line added"
10929        );
10930        assert_eq!(all_hunks, all_expanded_hunks);
10931    });
10932
10933    cx.update_editor(|editor, cx| {
10934        editor.move_up(&MoveUp, cx);
10935        editor.delete_line(&DeleteLine, cx);
10936    });
10937    executor.run_until_parked();
10938    cx.assert_editor_state(
10939        &r#"
10940        use some::mod1;
10941        use some::mod2;
10942
10943        const A: u32 = 42;
10944        const B: u32 = 42;
10945        const C: u32 = 42;
10946        const D: u32 = 42;
10947        ˇ
10948
10949        fn main() {
10950            println!("hello");
10951
10952            println!("world");
10953        }
10954        "#
10955        .unindent(),
10956    );
10957    cx.update_editor(|editor, cx| {
10958        let snapshot = editor.snapshot(cx);
10959        let all_hunks = editor_hunks(editor, &snapshot, cx);
10960        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10961        assert_eq!(
10962            all_hunks,
10963            vec![(
10964                "".to_string(),
10965                DiffHunkStatus::Added,
10966                DisplayRow(4)..DisplayRow(8)
10967            )]
10968        );
10969        assert_eq!(
10970            expanded_hunks_background_highlights(editor, cx),
10971            vec![DisplayRow(4)..=DisplayRow(6)],
10972            "Deleting a line should shrint the hunk"
10973        );
10974        assert_eq!(
10975            all_hunks, all_expanded_hunks,
10976            "Expanded hunk should also shrink with the addition"
10977        );
10978    });
10979
10980    cx.update_editor(|editor, cx| {
10981        editor.move_up(&MoveUp, cx);
10982        editor.delete_line(&DeleteLine, cx);
10983        editor.move_up(&MoveUp, cx);
10984        editor.delete_line(&DeleteLine, cx);
10985        editor.move_up(&MoveUp, cx);
10986        editor.delete_line(&DeleteLine, cx);
10987    });
10988    executor.run_until_parked();
10989    cx.assert_editor_state(
10990        &r#"
10991        use some::mod1;
10992        use some::mod2;
10993
10994        const A: u32 = 42;
10995        ˇ
10996
10997        fn main() {
10998            println!("hello");
10999
11000            println!("world");
11001        }
11002        "#
11003        .unindent(),
11004    );
11005    cx.update_editor(|editor, cx| {
11006        let snapshot = editor.snapshot(cx);
11007        let all_hunks = editor_hunks(editor, &snapshot, cx);
11008        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11009        assert_eq!(
11010            all_hunks,
11011            vec![(
11012                "".to_string(),
11013                DiffHunkStatus::Added,
11014                DisplayRow(5)..DisplayRow(6)
11015            )]
11016        );
11017        assert_eq!(
11018            expanded_hunks_background_highlights(editor, cx),
11019            vec![DisplayRow(5)..=DisplayRow(5)]
11020        );
11021        assert_eq!(all_hunks, all_expanded_hunks);
11022    });
11023
11024    cx.update_editor(|editor, cx| {
11025        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
11026        editor.delete_line(&DeleteLine, cx);
11027    });
11028    executor.run_until_parked();
11029    cx.assert_editor_state(
11030        &r#"
11031        ˇ
11032
11033        fn main() {
11034            println!("hello");
11035
11036            println!("world");
11037        }
11038        "#
11039        .unindent(),
11040    );
11041    cx.update_editor(|editor, cx| {
11042        let snapshot = editor.snapshot(cx);
11043        let all_hunks = editor_hunks(editor, &snapshot, cx);
11044        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11045        assert_eq!(
11046            all_hunks,
11047            vec![
11048                (
11049                    "use some::mod1;\nuse some::mod2;\n".to_string(),
11050                    DiffHunkStatus::Removed,
11051                    DisplayRow(0)..DisplayRow(0)
11052                ),
11053                (
11054                    "const A: u32 = 42;\n".to_string(),
11055                    DiffHunkStatus::Removed,
11056                    DisplayRow(2)..DisplayRow(2)
11057                )
11058            ]
11059        );
11060        assert_eq!(
11061            expanded_hunks_background_highlights(editor, cx),
11062            Vec::new(),
11063            "Should close all stale expanded addition hunks"
11064        );
11065        assert_eq!(
11066            all_expanded_hunks,
11067            vec![(
11068                "const A: u32 = 42;\n".to_string(),
11069                DiffHunkStatus::Removed,
11070                DisplayRow(2)..DisplayRow(2)
11071            )],
11072            "Should open hunks that were adjacent to the stale addition one"
11073        );
11074    });
11075}
11076
11077#[gpui::test]
11078async fn test_edits_around_toggled_deletions(
11079    executor: BackgroundExecutor,
11080    cx: &mut gpui::TestAppContext,
11081) {
11082    init_test(cx, |_| {});
11083
11084    let mut cx = EditorTestContext::new(cx).await;
11085
11086    let diff_base = r#"
11087        use some::mod1;
11088        use some::mod2;
11089
11090        const A: u32 = 42;
11091        const B: u32 = 42;
11092        const C: u32 = 42;
11093
11094
11095        fn main() {
11096            println!("hello");
11097
11098            println!("world");
11099        }
11100        "#
11101    .unindent();
11102    executor.run_until_parked();
11103    cx.set_state(
11104        &r#"
11105        use some::mod1;
11106        use some::mod2;
11107
11108        ˇconst B: u32 = 42;
11109        const C: u32 = 42;
11110
11111
11112        fn main() {
11113            println!("hello");
11114
11115            println!("world");
11116        }
11117        "#
11118        .unindent(),
11119    );
11120
11121    cx.set_diff_base(Some(&diff_base));
11122    executor.run_until_parked();
11123    cx.update_editor(|editor, cx| {
11124        let snapshot = editor.snapshot(cx);
11125        let all_hunks = editor_hunks(editor, &snapshot, cx);
11126        assert_eq!(
11127            all_hunks,
11128            vec![(
11129                "const A: u32 = 42;\n".to_string(),
11130                DiffHunkStatus::Removed,
11131                DisplayRow(3)..DisplayRow(3)
11132            )]
11133        );
11134    });
11135    cx.update_editor(|editor, cx| {
11136        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11137    });
11138    executor.run_until_parked();
11139    cx.assert_editor_state(
11140        &r#"
11141        use some::mod1;
11142        use some::mod2;
11143
11144        ˇconst B: u32 = 42;
11145        const C: u32 = 42;
11146
11147
11148        fn main() {
11149            println!("hello");
11150
11151            println!("world");
11152        }
11153        "#
11154        .unindent(),
11155    );
11156    cx.update_editor(|editor, cx| {
11157        let snapshot = editor.snapshot(cx);
11158        let all_hunks = editor_hunks(editor, &snapshot, cx);
11159        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11160        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11161        assert_eq!(
11162            all_hunks,
11163            vec![(
11164                "const A: u32 = 42;\n".to_string(),
11165                DiffHunkStatus::Removed,
11166                DisplayRow(4)..DisplayRow(4)
11167            )]
11168        );
11169        assert_eq!(all_hunks, all_expanded_hunks);
11170    });
11171
11172    cx.update_editor(|editor, 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        ˇconst C: u32 = 42;
11182
11183
11184        fn main() {
11185            println!("hello");
11186
11187            println!("world");
11188        }
11189        "#
11190        .unindent(),
11191    );
11192    cx.update_editor(|editor, cx| {
11193        let snapshot = editor.snapshot(cx);
11194        let all_hunks = editor_hunks(editor, &snapshot, cx);
11195        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11196        assert_eq!(
11197            expanded_hunks_background_highlights(editor, cx),
11198            Vec::new(),
11199            "Deleted hunks do not highlight current editor's background"
11200        );
11201        assert_eq!(
11202            all_hunks,
11203            vec![(
11204                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
11205                DiffHunkStatus::Removed,
11206                DisplayRow(5)..DisplayRow(5)
11207            )]
11208        );
11209        assert_eq!(all_hunks, all_expanded_hunks);
11210    });
11211
11212    cx.update_editor(|editor, cx| {
11213        editor.delete_line(&DeleteLine, cx);
11214    });
11215    executor.run_until_parked();
11216    cx.assert_editor_state(
11217        &r#"
11218        use some::mod1;
11219        use some::mod2;
11220
11221        ˇ
11222
11223        fn main() {
11224            println!("hello");
11225
11226            println!("world");
11227        }
11228        "#
11229        .unindent(),
11230    );
11231    cx.update_editor(|editor, cx| {
11232        let snapshot = editor.snapshot(cx);
11233        let all_hunks = editor_hunks(editor, &snapshot, cx);
11234        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11235        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11236        assert_eq!(
11237            all_hunks,
11238            vec![(
11239                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11240                DiffHunkStatus::Removed,
11241                DisplayRow(6)..DisplayRow(6)
11242            )]
11243        );
11244        assert_eq!(all_hunks, all_expanded_hunks);
11245    });
11246
11247    cx.update_editor(|editor, cx| {
11248        editor.handle_input("replacement", cx);
11249    });
11250    executor.run_until_parked();
11251    cx.assert_editor_state(
11252        &r#"
11253        use some::mod1;
11254        use some::mod2;
11255
11256        replacementˇ
11257
11258        fn main() {
11259            println!("hello");
11260
11261            println!("world");
11262        }
11263        "#
11264        .unindent(),
11265    );
11266    cx.update_editor(|editor, cx| {
11267        let snapshot = editor.snapshot(cx);
11268        let all_hunks = editor_hunks(editor, &snapshot, cx);
11269        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11270        assert_eq!(
11271            all_hunks,
11272            vec![(
11273                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
11274                DiffHunkStatus::Modified,
11275                DisplayRow(7)..DisplayRow(8)
11276            )]
11277        );
11278        assert_eq!(
11279            expanded_hunks_background_highlights(editor, cx),
11280            vec![DisplayRow(7)..=DisplayRow(7)],
11281            "Modified expanded hunks should display additions and highlight their background"
11282        );
11283        assert_eq!(all_hunks, all_expanded_hunks);
11284    });
11285}
11286
11287#[gpui::test]
11288async fn test_edits_around_toggled_modifications(
11289    executor: BackgroundExecutor,
11290    cx: &mut gpui::TestAppContext,
11291) {
11292    init_test(cx, |_| {});
11293
11294    let mut cx = EditorTestContext::new(cx).await;
11295
11296    let diff_base = r#"
11297        use some::mod1;
11298        use some::mod2;
11299
11300        const A: u32 = 42;
11301        const B: u32 = 42;
11302        const C: u32 = 42;
11303        const D: u32 = 42;
11304
11305
11306        fn main() {
11307            println!("hello");
11308
11309            println!("world");
11310        }"#
11311    .unindent();
11312    executor.run_until_parked();
11313    cx.set_state(
11314        &r#"
11315        use some::mod1;
11316        use some::mod2;
11317
11318        const A: u32 = 42;
11319        const B: u32 = 42;
11320        const C: u32 = 43ˇ
11321        const D: u32 = 42;
11322
11323
11324        fn main() {
11325            println!("hello");
11326
11327            println!("world");
11328        }"#
11329        .unindent(),
11330    );
11331
11332    cx.set_diff_base(Some(&diff_base));
11333    executor.run_until_parked();
11334    cx.update_editor(|editor, cx| {
11335        let snapshot = editor.snapshot(cx);
11336        let all_hunks = editor_hunks(editor, &snapshot, cx);
11337        assert_eq!(
11338            all_hunks,
11339            vec![(
11340                "const C: u32 = 42;\n".to_string(),
11341                DiffHunkStatus::Modified,
11342                DisplayRow(5)..DisplayRow(6)
11343            )]
11344        );
11345    });
11346    cx.update_editor(|editor, cx| {
11347        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11348    });
11349    executor.run_until_parked();
11350    cx.assert_editor_state(
11351        &r#"
11352        use some::mod1;
11353        use some::mod2;
11354
11355        const A: u32 = 42;
11356        const B: u32 = 42;
11357        const C: u32 = 43ˇ
11358        const D: u32 = 42;
11359
11360
11361        fn main() {
11362            println!("hello");
11363
11364            println!("world");
11365        }"#
11366        .unindent(),
11367    );
11368    cx.update_editor(|editor, cx| {
11369        let snapshot = editor.snapshot(cx);
11370        let all_hunks = editor_hunks(editor, &snapshot, cx);
11371        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11372        assert_eq!(
11373            expanded_hunks_background_highlights(editor, cx),
11374            vec![DisplayRow(6)..=DisplayRow(6)],
11375        );
11376        assert_eq!(
11377            all_hunks,
11378            vec![(
11379                "const C: u32 = 42;\n".to_string(),
11380                DiffHunkStatus::Modified,
11381                DisplayRow(6)..DisplayRow(7)
11382            )]
11383        );
11384        assert_eq!(all_hunks, all_expanded_hunks);
11385    });
11386
11387    cx.update_editor(|editor, cx| {
11388        editor.handle_input("\nnew_line\n", cx);
11389    });
11390    executor.run_until_parked();
11391    cx.assert_editor_state(
11392        &r#"
11393            use some::mod1;
11394            use some::mod2;
11395
11396            const A: u32 = 42;
11397            const B: u32 = 42;
11398            const C: u32 = 43
11399            new_line
11400            ˇ
11401            const D: u32 = 42;
11402
11403
11404            fn main() {
11405                println!("hello");
11406
11407                println!("world");
11408            }"#
11409        .unindent(),
11410    );
11411    cx.update_editor(|editor, cx| {
11412        let snapshot = editor.snapshot(cx);
11413        let all_hunks = editor_hunks(editor, &snapshot, cx);
11414        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11415        assert_eq!(
11416            expanded_hunks_background_highlights(editor, cx),
11417            vec![DisplayRow(6)..=DisplayRow(6)],
11418            "Modified hunk should grow highlighted lines on more text additions"
11419        );
11420        assert_eq!(
11421            all_hunks,
11422            vec![(
11423                "const C: u32 = 42;\n".to_string(),
11424                DiffHunkStatus::Modified,
11425                DisplayRow(6)..DisplayRow(9)
11426            )]
11427        );
11428        assert_eq!(all_hunks, all_expanded_hunks);
11429    });
11430
11431    cx.update_editor(|editor, cx| {
11432        editor.move_up(&MoveUp, cx);
11433        editor.move_up(&MoveUp, cx);
11434        editor.move_up(&MoveUp, cx);
11435        editor.delete_line(&DeleteLine, cx);
11436    });
11437    executor.run_until_parked();
11438    cx.assert_editor_state(
11439        &r#"
11440            use some::mod1;
11441            use some::mod2;
11442
11443            const A: u32 = 42;
11444            ˇconst C: u32 = 43
11445            new_line
11446
11447            const D: u32 = 42;
11448
11449
11450            fn main() {
11451                println!("hello");
11452
11453                println!("world");
11454            }"#
11455        .unindent(),
11456    );
11457    cx.update_editor(|editor, cx| {
11458        let snapshot = editor.snapshot(cx);
11459        let all_hunks = editor_hunks(editor, &snapshot, cx);
11460        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11461        assert_eq!(
11462            expanded_hunks_background_highlights(editor, cx),
11463            vec![DisplayRow(6)..=DisplayRow(8)],
11464        );
11465        assert_eq!(
11466            all_hunks,
11467            vec![(
11468                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11469                DiffHunkStatus::Modified,
11470                DisplayRow(6)..DisplayRow(9)
11471            )],
11472            "Modified hunk should grow deleted lines on text deletions above"
11473        );
11474        assert_eq!(all_hunks, all_expanded_hunks);
11475    });
11476
11477    cx.update_editor(|editor, cx| {
11478        editor.move_up(&MoveUp, cx);
11479        editor.handle_input("v", cx);
11480    });
11481    executor.run_until_parked();
11482    cx.assert_editor_state(
11483        &r#"
11484            use some::mod1;
11485            use some::mod2;
11486
11487            vˇconst A: u32 = 42;
11488            const C: u32 = 43
11489            new_line
11490
11491            const D: u32 = 42;
11492
11493
11494            fn main() {
11495                println!("hello");
11496
11497                println!("world");
11498            }"#
11499        .unindent(),
11500    );
11501    cx.update_editor(|editor, cx| {
11502        let snapshot = editor.snapshot(cx);
11503        let all_hunks = editor_hunks(editor, &snapshot, cx);
11504        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11505        assert_eq!(
11506            expanded_hunks_background_highlights(editor, cx),
11507            vec![DisplayRow(6)..=DisplayRow(9)],
11508            "Modified hunk should grow deleted lines on text modifications above"
11509        );
11510        assert_eq!(
11511            all_hunks,
11512            vec![(
11513                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11514                DiffHunkStatus::Modified,
11515                DisplayRow(6)..DisplayRow(10)
11516            )]
11517        );
11518        assert_eq!(all_hunks, all_expanded_hunks);
11519    });
11520
11521    cx.update_editor(|editor, cx| {
11522        editor.move_down(&MoveDown, cx);
11523        editor.move_down(&MoveDown, cx);
11524        editor.delete_line(&DeleteLine, cx)
11525    });
11526    executor.run_until_parked();
11527    cx.assert_editor_state(
11528        &r#"
11529            use some::mod1;
11530            use some::mod2;
11531
11532            vconst A: u32 = 42;
11533            const C: u32 = 43
11534            ˇ
11535            const D: u32 = 42;
11536
11537
11538            fn main() {
11539                println!("hello");
11540
11541                println!("world");
11542            }"#
11543        .unindent(),
11544    );
11545    cx.update_editor(|editor, cx| {
11546        let snapshot = editor.snapshot(cx);
11547        let all_hunks = editor_hunks(editor, &snapshot, cx);
11548        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11549        assert_eq!(
11550            expanded_hunks_background_highlights(editor, cx),
11551            vec![DisplayRow(6)..=DisplayRow(8)],
11552            "Modified hunk should grow shrink lines on modification lines removal"
11553        );
11554        assert_eq!(
11555            all_hunks,
11556            vec![(
11557                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11558                DiffHunkStatus::Modified,
11559                DisplayRow(6)..DisplayRow(9)
11560            )]
11561        );
11562        assert_eq!(all_hunks, all_expanded_hunks);
11563    });
11564
11565    cx.update_editor(|editor, cx| {
11566        editor.move_up(&MoveUp, cx);
11567        editor.move_up(&MoveUp, cx);
11568        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
11569        editor.delete_line(&DeleteLine, cx)
11570    });
11571    executor.run_until_parked();
11572    cx.assert_editor_state(
11573        &r#"
11574            use some::mod1;
11575            use some::mod2;
11576
11577            ˇ
11578
11579            fn main() {
11580                println!("hello");
11581
11582                println!("world");
11583            }"#
11584        .unindent(),
11585    );
11586    cx.update_editor(|editor, cx| {
11587        let snapshot = editor.snapshot(cx);
11588        let all_hunks = editor_hunks(editor, &snapshot, cx);
11589        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11590        assert_eq!(
11591            expanded_hunks_background_highlights(editor, cx),
11592            Vec::new(),
11593            "Modified hunk should turn into a removed one on all modified lines removal"
11594        );
11595        assert_eq!(
11596            all_hunks,
11597            vec![(
11598                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
11599                    .to_string(),
11600                DiffHunkStatus::Removed,
11601                DisplayRow(7)..DisplayRow(7)
11602            )]
11603        );
11604        assert_eq!(all_hunks, all_expanded_hunks);
11605    });
11606}
11607
11608#[gpui::test]
11609async fn test_multiple_expanded_hunks_merge(
11610    executor: BackgroundExecutor,
11611    cx: &mut gpui::TestAppContext,
11612) {
11613    init_test(cx, |_| {});
11614
11615    let mut cx = EditorTestContext::new(cx).await;
11616
11617    let diff_base = r#"
11618        use some::mod1;
11619        use some::mod2;
11620
11621        const A: u32 = 42;
11622        const B: u32 = 42;
11623        const C: u32 = 42;
11624        const D: u32 = 42;
11625
11626
11627        fn main() {
11628            println!("hello");
11629
11630            println!("world");
11631        }"#
11632    .unindent();
11633    executor.run_until_parked();
11634    cx.set_state(
11635        &r#"
11636        use some::mod1;
11637        use some::mod2;
11638
11639        const A: u32 = 42;
11640        const B: u32 = 42;
11641        const C: u32 = 43ˇ
11642        const D: u32 = 42;
11643
11644
11645        fn main() {
11646            println!("hello");
11647
11648            println!("world");
11649        }"#
11650        .unindent(),
11651    );
11652
11653    cx.set_diff_base(Some(&diff_base));
11654    executor.run_until_parked();
11655    cx.update_editor(|editor, cx| {
11656        let snapshot = editor.snapshot(cx);
11657        let all_hunks = editor_hunks(editor, &snapshot, cx);
11658        assert_eq!(
11659            all_hunks,
11660            vec![(
11661                "const C: u32 = 42;\n".to_string(),
11662                DiffHunkStatus::Modified,
11663                DisplayRow(5)..DisplayRow(6)
11664            )]
11665        );
11666    });
11667    cx.update_editor(|editor, cx| {
11668        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11669    });
11670    executor.run_until_parked();
11671    cx.assert_editor_state(
11672        &r#"
11673        use some::mod1;
11674        use some::mod2;
11675
11676        const A: u32 = 42;
11677        const B: u32 = 42;
11678        const C: u32 = 43ˇ
11679        const D: u32 = 42;
11680
11681
11682        fn main() {
11683            println!("hello");
11684
11685            println!("world");
11686        }"#
11687        .unindent(),
11688    );
11689    cx.update_editor(|editor, cx| {
11690        let snapshot = editor.snapshot(cx);
11691        let all_hunks = editor_hunks(editor, &snapshot, cx);
11692        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11693        assert_eq!(
11694            expanded_hunks_background_highlights(editor, cx),
11695            vec![DisplayRow(6)..=DisplayRow(6)],
11696        );
11697        assert_eq!(
11698            all_hunks,
11699            vec![(
11700                "const C: u32 = 42;\n".to_string(),
11701                DiffHunkStatus::Modified,
11702                DisplayRow(6)..DisplayRow(7)
11703            )]
11704        );
11705        assert_eq!(all_hunks, all_expanded_hunks);
11706    });
11707
11708    cx.update_editor(|editor, cx| {
11709        editor.handle_input("\nnew_line\n", cx);
11710    });
11711    executor.run_until_parked();
11712    cx.assert_editor_state(
11713        &r#"
11714            use some::mod1;
11715            use some::mod2;
11716
11717            const A: u32 = 42;
11718            const B: u32 = 42;
11719            const C: u32 = 43
11720            new_line
11721            ˇ
11722            const D: u32 = 42;
11723
11724
11725            fn main() {
11726                println!("hello");
11727
11728                println!("world");
11729            }"#
11730        .unindent(),
11731    );
11732}
11733
11734async fn setup_indent_guides_editor(
11735    text: &str,
11736    cx: &mut gpui::TestAppContext,
11737) -> (BufferId, EditorTestContext) {
11738    init_test(cx, |_| {});
11739
11740    let mut cx = EditorTestContext::new(cx).await;
11741
11742    let buffer_id = cx.update_editor(|editor, cx| {
11743        editor.set_text(text, cx);
11744        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
11745        let buffer_id = buffer_ids[0];
11746        buffer_id
11747    });
11748
11749    (buffer_id, cx)
11750}
11751
11752fn assert_indent_guides(
11753    range: Range<u32>,
11754    expected: Vec<IndentGuide>,
11755    active_indices: Option<Vec<usize>>,
11756    cx: &mut EditorTestContext,
11757) {
11758    let indent_guides = cx.update_editor(|editor, cx| {
11759        let snapshot = editor.snapshot(cx).display_snapshot;
11760        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
11761            MultiBufferRow(range.start)..MultiBufferRow(range.end),
11762            true,
11763            &snapshot,
11764            cx,
11765        );
11766
11767        indent_guides.sort_by(|a, b| {
11768            a.depth.cmp(&b.depth).then(
11769                a.start_row
11770                    .cmp(&b.start_row)
11771                    .then(a.end_row.cmp(&b.end_row)),
11772            )
11773        });
11774        indent_guides
11775    });
11776
11777    if let Some(expected) = active_indices {
11778        let active_indices = cx.update_editor(|editor, cx| {
11779            let snapshot = editor.snapshot(cx).display_snapshot;
11780            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
11781        });
11782
11783        assert_eq!(
11784            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
11785            expected,
11786            "Active indent guide indices do not match"
11787        );
11788    }
11789
11790    let expected: Vec<_> = expected
11791        .into_iter()
11792        .map(|guide| MultiBufferIndentGuide {
11793            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
11794            buffer: guide,
11795        })
11796        .collect();
11797
11798    assert_eq!(indent_guides, expected, "Indent guides do not match");
11799}
11800
11801fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
11802    IndentGuide {
11803        buffer_id,
11804        start_row,
11805        end_row,
11806        depth,
11807        tab_size: 4,
11808        settings: IndentGuideSettings {
11809            enabled: true,
11810            line_width: 1,
11811            active_line_width: 1,
11812            ..Default::default()
11813        },
11814    }
11815}
11816
11817#[gpui::test]
11818async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
11819    let (buffer_id, mut cx) = setup_indent_guides_editor(
11820        &"
11821    fn main() {
11822        let a = 1;
11823    }"
11824        .unindent(),
11825        cx,
11826    )
11827    .await;
11828
11829    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
11830}
11831
11832#[gpui::test]
11833async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
11834    let (buffer_id, mut cx) = setup_indent_guides_editor(
11835        &"
11836    fn main() {
11837        let a = 1;
11838        let b = 2;
11839    }"
11840        .unindent(),
11841        cx,
11842    )
11843    .await;
11844
11845    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
11846}
11847
11848#[gpui::test]
11849async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
11850    let (buffer_id, mut cx) = setup_indent_guides_editor(
11851        &"
11852    fn main() {
11853        let a = 1;
11854        if a == 3 {
11855            let b = 2;
11856        } else {
11857            let c = 3;
11858        }
11859    }"
11860        .unindent(),
11861        cx,
11862    )
11863    .await;
11864
11865    assert_indent_guides(
11866        0..8,
11867        vec![
11868            indent_guide(buffer_id, 1, 6, 0),
11869            indent_guide(buffer_id, 3, 3, 1),
11870            indent_guide(buffer_id, 5, 5, 1),
11871        ],
11872        None,
11873        &mut cx,
11874    );
11875}
11876
11877#[gpui::test]
11878async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
11879    let (buffer_id, mut cx) = setup_indent_guides_editor(
11880        &"
11881    fn main() {
11882        let a = 1;
11883            let b = 2;
11884        let c = 3;
11885    }"
11886        .unindent(),
11887        cx,
11888    )
11889    .await;
11890
11891    assert_indent_guides(
11892        0..5,
11893        vec![
11894            indent_guide(buffer_id, 1, 3, 0),
11895            indent_guide(buffer_id, 2, 2, 1),
11896        ],
11897        None,
11898        &mut cx,
11899    );
11900}
11901
11902#[gpui::test]
11903async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
11904    let (buffer_id, mut cx) = setup_indent_guides_editor(
11905        &"
11906        fn main() {
11907            let a = 1;
11908
11909            let c = 3;
11910        }"
11911        .unindent(),
11912        cx,
11913    )
11914    .await;
11915
11916    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
11917}
11918
11919#[gpui::test]
11920async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
11921    let (buffer_id, mut cx) = setup_indent_guides_editor(
11922        &"
11923        fn main() {
11924            let a = 1;
11925
11926            let c = 3;
11927
11928            if a == 3 {
11929                let b = 2;
11930            } else {
11931                let c = 3;
11932            }
11933        }"
11934        .unindent(),
11935        cx,
11936    )
11937    .await;
11938
11939    assert_indent_guides(
11940        0..11,
11941        vec![
11942            indent_guide(buffer_id, 1, 9, 0),
11943            indent_guide(buffer_id, 6, 6, 1),
11944            indent_guide(buffer_id, 8, 8, 1),
11945        ],
11946        None,
11947        &mut cx,
11948    );
11949}
11950
11951#[gpui::test]
11952async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
11953    let (buffer_id, mut cx) = setup_indent_guides_editor(
11954        &"
11955        fn main() {
11956            let a = 1;
11957
11958            let c = 3;
11959
11960            if a == 3 {
11961                let b = 2;
11962            } else {
11963                let c = 3;
11964            }
11965        }"
11966        .unindent(),
11967        cx,
11968    )
11969    .await;
11970
11971    assert_indent_guides(
11972        1..11,
11973        vec![
11974            indent_guide(buffer_id, 1, 9, 0),
11975            indent_guide(buffer_id, 6, 6, 1),
11976            indent_guide(buffer_id, 8, 8, 1),
11977        ],
11978        None,
11979        &mut cx,
11980    );
11981}
11982
11983#[gpui::test]
11984async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
11985    let (buffer_id, mut cx) = setup_indent_guides_editor(
11986        &"
11987        fn main() {
11988            let a = 1;
11989
11990            let c = 3;
11991
11992            if a == 3 {
11993                let b = 2;
11994            } else {
11995                let c = 3;
11996            }
11997        }"
11998        .unindent(),
11999        cx,
12000    )
12001    .await;
12002
12003    assert_indent_guides(
12004        1..10,
12005        vec![
12006            indent_guide(buffer_id, 1, 9, 0),
12007            indent_guide(buffer_id, 6, 6, 1),
12008            indent_guide(buffer_id, 8, 8, 1),
12009        ],
12010        None,
12011        &mut cx,
12012    );
12013}
12014
12015#[gpui::test]
12016async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12017    let (buffer_id, mut cx) = setup_indent_guides_editor(
12018        &"
12019        block1
12020            block2
12021                block3
12022                    block4
12023            block2
12024        block1
12025        block1"
12026            .unindent(),
12027        cx,
12028    )
12029    .await;
12030
12031    assert_indent_guides(
12032        1..10,
12033        vec![
12034            indent_guide(buffer_id, 1, 4, 0),
12035            indent_guide(buffer_id, 2, 3, 1),
12036            indent_guide(buffer_id, 3, 3, 2),
12037        ],
12038        None,
12039        &mut cx,
12040    );
12041}
12042
12043#[gpui::test]
12044async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12045    let (buffer_id, mut cx) = setup_indent_guides_editor(
12046        &"
12047        block1
12048            block2
12049                block3
12050
12051        block1
12052        block1"
12053            .unindent(),
12054        cx,
12055    )
12056    .await;
12057
12058    assert_indent_guides(
12059        0..6,
12060        vec![
12061            indent_guide(buffer_id, 1, 2, 0),
12062            indent_guide(buffer_id, 2, 2, 1),
12063        ],
12064        None,
12065        &mut cx,
12066    );
12067}
12068
12069#[gpui::test]
12070async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12071    let (buffer_id, mut cx) = setup_indent_guides_editor(
12072        &"
12073        block1
12074
12075
12076
12077            block2
12078        "
12079        .unindent(),
12080        cx,
12081    )
12082    .await;
12083
12084    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12085}
12086
12087#[gpui::test]
12088async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12089    let (buffer_id, mut cx) = setup_indent_guides_editor(
12090        &"
12091        def a:
12092        \tb = 3
12093        \tif True:
12094        \t\tc = 4
12095        \t\td = 5
12096        \tprint(b)
12097        "
12098        .unindent(),
12099        cx,
12100    )
12101    .await;
12102
12103    assert_indent_guides(
12104        0..6,
12105        vec![
12106            indent_guide(buffer_id, 1, 6, 0),
12107            indent_guide(buffer_id, 3, 4, 1),
12108        ],
12109        None,
12110        &mut cx,
12111    );
12112}
12113
12114#[gpui::test]
12115async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12116    let (buffer_id, mut cx) = setup_indent_guides_editor(
12117        &"
12118    fn main() {
12119        let a = 1;
12120    }"
12121        .unindent(),
12122        cx,
12123    )
12124    .await;
12125
12126    cx.update_editor(|editor, cx| {
12127        editor.change_selections(None, cx, |s| {
12128            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12129        });
12130    });
12131
12132    assert_indent_guides(
12133        0..3,
12134        vec![indent_guide(buffer_id, 1, 1, 0)],
12135        Some(vec![0]),
12136        &mut cx,
12137    );
12138}
12139
12140#[gpui::test]
12141async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12142    let (buffer_id, mut cx) = setup_indent_guides_editor(
12143        &"
12144    fn main() {
12145        if 1 == 2 {
12146            let a = 1;
12147        }
12148    }"
12149        .unindent(),
12150        cx,
12151    )
12152    .await;
12153
12154    cx.update_editor(|editor, cx| {
12155        editor.change_selections(None, cx, |s| {
12156            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12157        });
12158    });
12159
12160    assert_indent_guides(
12161        0..4,
12162        vec![
12163            indent_guide(buffer_id, 1, 3, 0),
12164            indent_guide(buffer_id, 2, 2, 1),
12165        ],
12166        Some(vec![1]),
12167        &mut cx,
12168    );
12169
12170    cx.update_editor(|editor, cx| {
12171        editor.change_selections(None, cx, |s| {
12172            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12173        });
12174    });
12175
12176    assert_indent_guides(
12177        0..4,
12178        vec![
12179            indent_guide(buffer_id, 1, 3, 0),
12180            indent_guide(buffer_id, 2, 2, 1),
12181        ],
12182        Some(vec![1]),
12183        &mut cx,
12184    );
12185
12186    cx.update_editor(|editor, cx| {
12187        editor.change_selections(None, cx, |s| {
12188            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
12189        });
12190    });
12191
12192    assert_indent_guides(
12193        0..4,
12194        vec![
12195            indent_guide(buffer_id, 1, 3, 0),
12196            indent_guide(buffer_id, 2, 2, 1),
12197        ],
12198        Some(vec![0]),
12199        &mut cx,
12200    );
12201}
12202
12203#[gpui::test]
12204async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
12205    let (buffer_id, mut cx) = setup_indent_guides_editor(
12206        &"
12207    fn main() {
12208        let a = 1;
12209
12210        let b = 2;
12211    }"
12212        .unindent(),
12213        cx,
12214    )
12215    .await;
12216
12217    cx.update_editor(|editor, cx| {
12218        editor.change_selections(None, cx, |s| {
12219            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12220        });
12221    });
12222
12223    assert_indent_guides(
12224        0..5,
12225        vec![indent_guide(buffer_id, 1, 3, 0)],
12226        Some(vec![0]),
12227        &mut cx,
12228    );
12229}
12230
12231#[gpui::test]
12232async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
12233    let (buffer_id, mut cx) = setup_indent_guides_editor(
12234        &"
12235    def m:
12236        a = 1
12237        pass"
12238            .unindent(),
12239        cx,
12240    )
12241    .await;
12242
12243    cx.update_editor(|editor, cx| {
12244        editor.change_selections(None, cx, |s| {
12245            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12246        });
12247    });
12248
12249    assert_indent_guides(
12250        0..3,
12251        vec![indent_guide(buffer_id, 1, 2, 0)],
12252        Some(vec![0]),
12253        &mut cx,
12254    );
12255}
12256
12257#[gpui::test]
12258fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
12259    init_test(cx, |_| {});
12260
12261    let editor = cx.add_window(|cx| {
12262        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
12263        build_editor(buffer, cx)
12264    });
12265
12266    let render_args = Arc::new(Mutex::new(None));
12267    let snapshot = editor
12268        .update(cx, |editor, cx| {
12269            let snapshot = editor.buffer().read(cx).snapshot(cx);
12270            let range =
12271                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
12272
12273            struct RenderArgs {
12274                row: MultiBufferRow,
12275                folded: bool,
12276                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
12277            }
12278
12279            let crease = Crease::new(
12280                range,
12281                FoldPlaceholder::test(),
12282                {
12283                    let toggle_callback = render_args.clone();
12284                    move |row, folded, callback, _cx| {
12285                        *toggle_callback.lock() = Some(RenderArgs {
12286                            row,
12287                            folded,
12288                            callback,
12289                        });
12290                        div()
12291                    }
12292                },
12293                |_row, _folded, _cx| div(),
12294            );
12295
12296            editor.insert_creases(Some(crease), cx);
12297            let snapshot = editor.snapshot(cx);
12298            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
12299            snapshot
12300        })
12301        .unwrap();
12302
12303    let render_args = render_args.lock().take().unwrap();
12304    assert_eq!(render_args.row, MultiBufferRow(1));
12305    assert_eq!(render_args.folded, false);
12306    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12307
12308    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
12309        .unwrap();
12310    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12311    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
12312
12313    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
12314        .unwrap();
12315    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12316    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12317}
12318
12319fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
12320    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
12321    point..point
12322}
12323
12324fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
12325    let (text, ranges) = marked_text_ranges(marked_text, true);
12326    assert_eq!(view.text(cx), text);
12327    assert_eq!(
12328        view.selections.ranges(cx),
12329        ranges,
12330        "Assert selections are {}",
12331        marked_text
12332    );
12333}
12334
12335/// Handle completion request passing a marked string specifying where the completion
12336/// should be triggered from using '|' character, what range should be replaced, and what completions
12337/// should be returned using '<' and '>' to delimit the range
12338pub fn handle_completion_request(
12339    cx: &mut EditorLspTestContext,
12340    marked_string: &str,
12341    completions: Vec<&'static str>,
12342    counter: Arc<AtomicUsize>,
12343) -> impl Future<Output = ()> {
12344    let complete_from_marker: TextRangeMarker = '|'.into();
12345    let replace_range_marker: TextRangeMarker = ('<', '>').into();
12346    let (_, mut marked_ranges) = marked_text_ranges_by(
12347        marked_string,
12348        vec![complete_from_marker.clone(), replace_range_marker.clone()],
12349    );
12350
12351    let complete_from_position =
12352        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
12353    let replace_range =
12354        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
12355
12356    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
12357        let completions = completions.clone();
12358        counter.fetch_add(1, atomic::Ordering::Release);
12359        async move {
12360            assert_eq!(params.text_document_position.text_document.uri, url.clone());
12361            assert_eq!(
12362                params.text_document_position.position,
12363                complete_from_position
12364            );
12365            Ok(Some(lsp::CompletionResponse::Array(
12366                completions
12367                    .iter()
12368                    .map(|completion_text| lsp::CompletionItem {
12369                        label: completion_text.to_string(),
12370                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12371                            range: replace_range,
12372                            new_text: completion_text.to_string(),
12373                        })),
12374                        ..Default::default()
12375                    })
12376                    .collect(),
12377            )))
12378        }
12379    });
12380
12381    async move {
12382        request.next().await;
12383    }
12384}
12385
12386fn handle_resolve_completion_request(
12387    cx: &mut EditorLspTestContext,
12388    edits: Option<Vec<(&'static str, &'static str)>>,
12389) -> impl Future<Output = ()> {
12390    let edits = edits.map(|edits| {
12391        edits
12392            .iter()
12393            .map(|(marked_string, new_text)| {
12394                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
12395                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
12396                lsp::TextEdit::new(replace_range, new_text.to_string())
12397            })
12398            .collect::<Vec<_>>()
12399    });
12400
12401    let mut request =
12402        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12403            let edits = edits.clone();
12404            async move {
12405                Ok(lsp::CompletionItem {
12406                    additional_text_edits: edits,
12407                    ..Default::default()
12408                })
12409            }
12410        });
12411
12412    async move {
12413        request.next().await;
12414    }
12415}
12416
12417pub(crate) fn update_test_language_settings(
12418    cx: &mut TestAppContext,
12419    f: impl Fn(&mut AllLanguageSettingsContent),
12420) {
12421    _ = cx.update(|cx| {
12422        SettingsStore::update_global(cx, |store, cx| {
12423            store.update_user_settings::<AllLanguageSettings>(cx, f);
12424        });
12425    });
12426}
12427
12428pub(crate) fn update_test_project_settings(
12429    cx: &mut TestAppContext,
12430    f: impl Fn(&mut ProjectSettings),
12431) {
12432    _ = cx.update(|cx| {
12433        SettingsStore::update_global(cx, |store, cx| {
12434            store.update_user_settings::<ProjectSettings>(cx, f);
12435        });
12436    });
12437}
12438
12439pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
12440    _ = cx.update(|cx| {
12441        cx.text_system()
12442            .add_fonts(vec![assets::Assets
12443                .load("fonts/zed-mono/zed-mono-extended.ttf")
12444                .unwrap()
12445                .unwrap()])
12446            .unwrap();
12447        let store = SettingsStore::test(cx);
12448        cx.set_global(store);
12449        theme::init(theme::LoadThemes::JustBase, cx);
12450        release_channel::init(SemanticVersion::default(), cx);
12451        client::init_settings(cx);
12452        language::init(cx);
12453        Project::init_settings(cx);
12454        workspace::init_settings(cx);
12455        crate::init(cx);
12456    });
12457
12458    update_test_language_settings(cx, f);
12459}
12460
12461pub(crate) fn rust_lang() -> Arc<Language> {
12462    Arc::new(Language::new(
12463        LanguageConfig {
12464            name: "Rust".into(),
12465            matcher: LanguageMatcher {
12466                path_suffixes: vec!["rs".to_string()],
12467                ..Default::default()
12468            },
12469            ..Default::default()
12470        },
12471        Some(tree_sitter_rust::language()),
12472    ))
12473}
12474
12475#[track_caller]
12476fn assert_hunk_revert(
12477    not_reverted_text_with_selections: &str,
12478    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
12479    expected_reverted_text_with_selections: &str,
12480    base_text: &str,
12481    cx: &mut EditorLspTestContext,
12482) {
12483    cx.set_state(not_reverted_text_with_selections);
12484    cx.update_editor(|editor, cx| {
12485        editor
12486            .buffer()
12487            .read(cx)
12488            .as_singleton()
12489            .unwrap()
12490            .update(cx, |buffer, cx| {
12491                buffer.set_diff_base(Some(base_text.into()), cx);
12492            });
12493    });
12494    cx.executor().run_until_parked();
12495
12496    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
12497        let snapshot = editor.buffer().read(cx).snapshot(cx);
12498        let reverted_hunk_statuses = snapshot
12499            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
12500            .map(|hunk| hunk_status(&hunk))
12501            .collect::<Vec<_>>();
12502
12503        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
12504        reverted_hunk_statuses
12505    });
12506    cx.executor().run_until_parked();
12507    cx.assert_editor_state(expected_reverted_text_with_selections);
12508    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
12509}