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