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