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                            newline: true,
 4639                        },
 4640                        BracketPair {
 4641                            start: "(".to_string(),
 4642                            end: ")".to_string(),
 4643                            close: false,
 4644                            newline: true,
 4645                        },
 4646                    ],
 4647                    ..Default::default()
 4648                },
 4649                ..Default::default()
 4650            },
 4651            Some(tree_sitter_rust::language()),
 4652        )
 4653        .with_indents_query(
 4654            r#"
 4655                (_ "(" ")" @end) @indent
 4656                (_ "{" "}" @end) @indent
 4657            "#,
 4658        )
 4659        .unwrap(),
 4660    );
 4661
 4662    let text = "fn a() {}";
 4663
 4664    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4665    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4666    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4667    editor
 4668        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 4669        .await;
 4670
 4671    _ = editor.update(cx, |editor, cx| {
 4672        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 4673        editor.newline(&Newline, cx);
 4674        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 4675        assert_eq!(
 4676            editor.selections.ranges(cx),
 4677            &[
 4678                Point::new(1, 4)..Point::new(1, 4),
 4679                Point::new(3, 4)..Point::new(3, 4),
 4680                Point::new(5, 0)..Point::new(5, 0)
 4681            ]
 4682        );
 4683    });
 4684}
 4685
 4686#[gpui::test]
 4687async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
 4688    init_test(cx, |_| {});
 4689
 4690    let mut cx = EditorTestContext::new(cx).await;
 4691
 4692    let language = Arc::new(Language::new(
 4693        LanguageConfig {
 4694            brackets: BracketPairConfig {
 4695                pairs: vec![
 4696                    BracketPair {
 4697                        start: "{".to_string(),
 4698                        end: "}".to_string(),
 4699                        close: true,
 4700                        newline: true,
 4701                    },
 4702                    BracketPair {
 4703                        start: "(".to_string(),
 4704                        end: ")".to_string(),
 4705                        close: true,
 4706                        newline: true,
 4707                    },
 4708                    BracketPair {
 4709                        start: "/*".to_string(),
 4710                        end: " */".to_string(),
 4711                        close: true,
 4712                        newline: true,
 4713                    },
 4714                    BracketPair {
 4715                        start: "[".to_string(),
 4716                        end: "]".to_string(),
 4717                        close: false,
 4718                        newline: true,
 4719                    },
 4720                    BracketPair {
 4721                        start: "\"".to_string(),
 4722                        end: "\"".to_string(),
 4723                        close: true,
 4724                        newline: false,
 4725                    },
 4726                ],
 4727                ..Default::default()
 4728            },
 4729            autoclose_before: "})]".to_string(),
 4730            ..Default::default()
 4731        },
 4732        Some(tree_sitter_rust::language()),
 4733    ));
 4734
 4735    cx.language_registry().add(language.clone());
 4736    cx.update_buffer(|buffer, cx| {
 4737        buffer.set_language(Some(language), cx);
 4738    });
 4739
 4740    cx.set_state(
 4741        &r#"
 4742            🏀ˇ
 4743            εˇ
 4744            ❤️ˇ
 4745        "#
 4746        .unindent(),
 4747    );
 4748
 4749    // autoclose multiple nested brackets at multiple cursors
 4750    cx.update_editor(|view, cx| {
 4751        view.handle_input("{", cx);
 4752        view.handle_input("{", cx);
 4753        view.handle_input("{", cx);
 4754    });
 4755    cx.assert_editor_state(
 4756        &"
 4757            🏀{{{ˇ}}}
 4758            ε{{{ˇ}}}
 4759            ❤️{{{ˇ}}}
 4760        "
 4761        .unindent(),
 4762    );
 4763
 4764    // insert a different closing bracket
 4765    cx.update_editor(|view, cx| {
 4766        view.handle_input(")", cx);
 4767    });
 4768    cx.assert_editor_state(
 4769        &"
 4770            🏀{{{)ˇ}}}
 4771            ε{{{)ˇ}}}
 4772            ❤️{{{)ˇ}}}
 4773        "
 4774        .unindent(),
 4775    );
 4776
 4777    // skip over the auto-closed brackets when typing a closing bracket
 4778    cx.update_editor(|view, cx| {
 4779        view.move_right(&MoveRight, cx);
 4780        view.handle_input("}", cx);
 4781        view.handle_input("}", cx);
 4782        view.handle_input("}", cx);
 4783    });
 4784    cx.assert_editor_state(
 4785        &"
 4786            🏀{{{)}}}}ˇ
 4787            ε{{{)}}}}ˇ
 4788            ❤️{{{)}}}}ˇ
 4789        "
 4790        .unindent(),
 4791    );
 4792
 4793    // autoclose multi-character pairs
 4794    cx.set_state(
 4795        &"
 4796            ˇ
 4797            ˇ
 4798        "
 4799        .unindent(),
 4800    );
 4801    cx.update_editor(|view, cx| {
 4802        view.handle_input("/", cx);
 4803        view.handle_input("*", cx);
 4804    });
 4805    cx.assert_editor_state(
 4806        &"
 4807            /*ˇ */
 4808            /*ˇ */
 4809        "
 4810        .unindent(),
 4811    );
 4812
 4813    // one cursor autocloses a multi-character pair, one cursor
 4814    // does not autoclose.
 4815    cx.set_state(
 4816        &"
 4817 4818            ˇ
 4819        "
 4820        .unindent(),
 4821    );
 4822    cx.update_editor(|view, cx| view.handle_input("*", cx));
 4823    cx.assert_editor_state(
 4824        &"
 4825            /*ˇ */
 4826 4827        "
 4828        .unindent(),
 4829    );
 4830
 4831    // Don't autoclose if the next character isn't whitespace and isn't
 4832    // listed in the language's "autoclose_before" section.
 4833    cx.set_state("ˇa b");
 4834    cx.update_editor(|view, cx| view.handle_input("{", cx));
 4835    cx.assert_editor_state("{ˇa b");
 4836
 4837    // Don't autoclose if `close` is false for the bracket pair
 4838    cx.set_state("ˇ");
 4839    cx.update_editor(|view, cx| view.handle_input("[", cx));
 4840    cx.assert_editor_state("");
 4841
 4842    // Surround with brackets if text is selected
 4843    cx.set_state("«aˇ» b");
 4844    cx.update_editor(|view, cx| view.handle_input("{", cx));
 4845    cx.assert_editor_state("{«aˇ»} b");
 4846
 4847    // Autclose pair where the start and end characters are the same
 4848    cx.set_state("");
 4849    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 4850    cx.assert_editor_state("a\"ˇ\"");
 4851    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 4852    cx.assert_editor_state("a\"\"ˇ");
 4853}
 4854
 4855#[gpui::test]
 4856async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 4857    init_test(cx, |settings| {
 4858        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 4859    });
 4860
 4861    let mut cx = EditorTestContext::new(cx).await;
 4862
 4863    let language = Arc::new(Language::new(
 4864        LanguageConfig {
 4865            brackets: BracketPairConfig {
 4866                pairs: vec![
 4867                    BracketPair {
 4868                        start: "{".to_string(),
 4869                        end: "}".to_string(),
 4870                        close: true,
 4871                        newline: true,
 4872                    },
 4873                    BracketPair {
 4874                        start: "(".to_string(),
 4875                        end: ")".to_string(),
 4876                        close: true,
 4877                        newline: true,
 4878                    },
 4879                    BracketPair {
 4880                        start: "[".to_string(),
 4881                        end: "]".to_string(),
 4882                        close: false,
 4883                        newline: true,
 4884                    },
 4885                ],
 4886                ..Default::default()
 4887            },
 4888            autoclose_before: "})]".to_string(),
 4889            ..Default::default()
 4890        },
 4891        Some(tree_sitter_rust::language()),
 4892    ));
 4893
 4894    cx.language_registry().add(language.clone());
 4895    cx.update_buffer(|buffer, cx| {
 4896        buffer.set_language(Some(language), cx);
 4897    });
 4898
 4899    cx.set_state(
 4900        &"
 4901            ˇ
 4902            ˇ
 4903            ˇ
 4904        "
 4905        .unindent(),
 4906    );
 4907
 4908    // ensure only matching closing brackets are skipped over
 4909    cx.update_editor(|view, cx| {
 4910        view.handle_input("}", cx);
 4911        view.move_left(&MoveLeft, cx);
 4912        view.handle_input(")", cx);
 4913        view.move_left(&MoveLeft, cx);
 4914    });
 4915    cx.assert_editor_state(
 4916        &"
 4917            ˇ)}
 4918            ˇ)}
 4919            ˇ)}
 4920        "
 4921        .unindent(),
 4922    );
 4923
 4924    // skip-over closing brackets at multiple cursors
 4925    cx.update_editor(|view, cx| {
 4926        view.handle_input(")", cx);
 4927        view.handle_input("}", cx);
 4928    });
 4929    cx.assert_editor_state(
 4930        &"
 4931            )}ˇ
 4932            )}ˇ
 4933            )}ˇ
 4934        "
 4935        .unindent(),
 4936    );
 4937
 4938    // ignore non-close brackets
 4939    cx.update_editor(|view, cx| {
 4940        view.handle_input("]", cx);
 4941        view.move_left(&MoveLeft, cx);
 4942        view.handle_input("]", cx);
 4943    });
 4944    cx.assert_editor_state(
 4945        &"
 4946            )}]ˇ]
 4947            )}]ˇ]
 4948            )}]ˇ]
 4949        "
 4950        .unindent(),
 4951    );
 4952}
 4953
 4954#[gpui::test]
 4955async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 4956    init_test(cx, |_| {});
 4957
 4958    let mut cx = EditorTestContext::new(cx).await;
 4959
 4960    let html_language = Arc::new(
 4961        Language::new(
 4962            LanguageConfig {
 4963                name: "HTML".into(),
 4964                brackets: BracketPairConfig {
 4965                    pairs: vec![
 4966                        BracketPair {
 4967                            start: "<".into(),
 4968                            end: ">".into(),
 4969                            close: true,
 4970                            ..Default::default()
 4971                        },
 4972                        BracketPair {
 4973                            start: "{".into(),
 4974                            end: "}".into(),
 4975                            close: true,
 4976                            ..Default::default()
 4977                        },
 4978                        BracketPair {
 4979                            start: "(".into(),
 4980                            end: ")".into(),
 4981                            close: true,
 4982                            ..Default::default()
 4983                        },
 4984                    ],
 4985                    ..Default::default()
 4986                },
 4987                autoclose_before: "})]>".into(),
 4988                ..Default::default()
 4989            },
 4990            Some(tree_sitter_html::language()),
 4991        )
 4992        .with_injection_query(
 4993            r#"
 4994            (script_element
 4995                (raw_text) @content
 4996                (#set! "language" "javascript"))
 4997            "#,
 4998        )
 4999        .unwrap(),
 5000    );
 5001
 5002    let javascript_language = Arc::new(Language::new(
 5003        LanguageConfig {
 5004            name: "JavaScript".into(),
 5005            brackets: BracketPairConfig {
 5006                pairs: vec![
 5007                    BracketPair {
 5008                        start: "/*".into(),
 5009                        end: " */".into(),
 5010                        close: true,
 5011                        ..Default::default()
 5012                    },
 5013                    BracketPair {
 5014                        start: "{".into(),
 5015                        end: "}".into(),
 5016                        close: true,
 5017                        ..Default::default()
 5018                    },
 5019                    BracketPair {
 5020                        start: "(".into(),
 5021                        end: ")".into(),
 5022                        close: true,
 5023                        ..Default::default()
 5024                    },
 5025                ],
 5026                ..Default::default()
 5027            },
 5028            autoclose_before: "})]>".into(),
 5029            ..Default::default()
 5030        },
 5031        Some(tree_sitter_typescript::language_tsx()),
 5032    ));
 5033
 5034    cx.language_registry().add(html_language.clone());
 5035    cx.language_registry().add(javascript_language.clone());
 5036
 5037    cx.update_buffer(|buffer, cx| {
 5038        buffer.set_language(Some(html_language), cx);
 5039    });
 5040
 5041    cx.set_state(
 5042        &r#"
 5043            <body>ˇ
 5044                <script>
 5045                    var x = 1;ˇ
 5046                </script>
 5047            </body>ˇ
 5048        "#
 5049        .unindent(),
 5050    );
 5051
 5052    // Precondition: different languages are active at different locations.
 5053    cx.update_editor(|editor, cx| {
 5054        let snapshot = editor.snapshot(cx);
 5055        let cursors = editor.selections.ranges::<usize>(cx);
 5056        let languages = cursors
 5057            .iter()
 5058            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5059            .collect::<Vec<_>>();
 5060        assert_eq!(
 5061            languages,
 5062            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5063        );
 5064    });
 5065
 5066    // Angle brackets autoclose in HTML, but not JavaScript.
 5067    cx.update_editor(|editor, cx| {
 5068        editor.handle_input("<", cx);
 5069        editor.handle_input("a", cx);
 5070    });
 5071    cx.assert_editor_state(
 5072        &r#"
 5073            <body><aˇ>
 5074                <script>
 5075                    var x = 1;<aˇ
 5076                </script>
 5077            </body><aˇ>
 5078        "#
 5079        .unindent(),
 5080    );
 5081
 5082    // Curly braces and parens autoclose in both HTML and JavaScript.
 5083    cx.update_editor(|editor, cx| {
 5084        editor.handle_input(" b=", cx);
 5085        editor.handle_input("{", cx);
 5086        editor.handle_input("c", cx);
 5087        editor.handle_input("(", cx);
 5088    });
 5089    cx.assert_editor_state(
 5090        &r#"
 5091            <body><a b={c(ˇ)}>
 5092                <script>
 5093                    var x = 1;<a b={c(ˇ)}
 5094                </script>
 5095            </body><a b={c(ˇ)}>
 5096        "#
 5097        .unindent(),
 5098    );
 5099
 5100    // Brackets that were already autoclosed are skipped.
 5101    cx.update_editor(|editor, cx| {
 5102        editor.handle_input(")", cx);
 5103        editor.handle_input("d", cx);
 5104        editor.handle_input("}", cx);
 5105    });
 5106    cx.assert_editor_state(
 5107        &r#"
 5108            <body><a b={c()d}ˇ>
 5109                <script>
 5110                    var x = 1;<a b={c()d}ˇ
 5111                </script>
 5112            </body><a b={c()d}ˇ>
 5113        "#
 5114        .unindent(),
 5115    );
 5116    cx.update_editor(|editor, cx| {
 5117        editor.handle_input(">", cx);
 5118    });
 5119    cx.assert_editor_state(
 5120        &r#"
 5121            <body><a b={c()d}>ˇ
 5122                <script>
 5123                    var x = 1;<a b={c()d}>ˇ
 5124                </script>
 5125            </body><a b={c()d}>ˇ
 5126        "#
 5127        .unindent(),
 5128    );
 5129
 5130    // Reset
 5131    cx.set_state(
 5132        &r#"
 5133            <body>ˇ
 5134                <script>
 5135                    var x = 1;ˇ
 5136                </script>
 5137            </body>ˇ
 5138        "#
 5139        .unindent(),
 5140    );
 5141
 5142    cx.update_editor(|editor, cx| {
 5143        editor.handle_input("<", cx);
 5144    });
 5145    cx.assert_editor_state(
 5146        &r#"
 5147            <body><ˇ>
 5148                <script>
 5149                    var x = 1;<ˇ
 5150                </script>
 5151            </body><ˇ>
 5152        "#
 5153        .unindent(),
 5154    );
 5155
 5156    // When backspacing, the closing angle brackets are removed.
 5157    cx.update_editor(|editor, cx| {
 5158        editor.backspace(&Backspace, cx);
 5159    });
 5160    cx.assert_editor_state(
 5161        &r#"
 5162            <body>ˇ
 5163                <script>
 5164                    var x = 1;ˇ
 5165                </script>
 5166            </body>ˇ
 5167        "#
 5168        .unindent(),
 5169    );
 5170
 5171    // Block comments autoclose in JavaScript, but not HTML.
 5172    cx.update_editor(|editor, cx| {
 5173        editor.handle_input("/", cx);
 5174        editor.handle_input("*", cx);
 5175    });
 5176    cx.assert_editor_state(
 5177        &r#"
 5178            <body>/*ˇ
 5179                <script>
 5180                    var x = 1;/*ˇ */
 5181                </script>
 5182            </body>/*ˇ
 5183        "#
 5184        .unindent(),
 5185    );
 5186}
 5187
 5188#[gpui::test]
 5189async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5190    init_test(cx, |_| {});
 5191
 5192    let mut cx = EditorTestContext::new(cx).await;
 5193
 5194    let rust_language = Arc::new(
 5195        Language::new(
 5196            LanguageConfig {
 5197                name: "Rust".into(),
 5198                brackets: serde_json::from_value(json!([
 5199                    { "start": "{", "end": "}", "close": true, "newline": true },
 5200                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5201                ]))
 5202                .unwrap(),
 5203                autoclose_before: "})]>".into(),
 5204                ..Default::default()
 5205            },
 5206            Some(tree_sitter_rust::language()),
 5207        )
 5208        .with_override_query("(string_literal) @string")
 5209        .unwrap(),
 5210    );
 5211
 5212    cx.language_registry().add(rust_language.clone());
 5213    cx.update_buffer(|buffer, cx| {
 5214        buffer.set_language(Some(rust_language), cx);
 5215    });
 5216
 5217    cx.set_state(
 5218        &r#"
 5219            let x = ˇ
 5220        "#
 5221        .unindent(),
 5222    );
 5223
 5224    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5225    cx.update_editor(|editor, cx| {
 5226        editor.handle_input("\"", cx);
 5227    });
 5228    cx.assert_editor_state(
 5229        &r#"
 5230            let x = "ˇ"
 5231        "#
 5232        .unindent(),
 5233    );
 5234
 5235    // Inserting another quotation mark. The cursor moves across the existing
 5236    // automatically-inserted quotation mark.
 5237    cx.update_editor(|editor, cx| {
 5238        editor.handle_input("\"", cx);
 5239    });
 5240    cx.assert_editor_state(
 5241        &r#"
 5242            let x = ""ˇ
 5243        "#
 5244        .unindent(),
 5245    );
 5246
 5247    // Reset
 5248    cx.set_state(
 5249        &r#"
 5250            let x = ˇ
 5251        "#
 5252        .unindent(),
 5253    );
 5254
 5255    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5256    cx.update_editor(|editor, cx| {
 5257        editor.handle_input("\"", cx);
 5258        editor.handle_input(" ", cx);
 5259        editor.move_left(&Default::default(), cx);
 5260        editor.handle_input("\\", cx);
 5261        editor.handle_input("\"", cx);
 5262    });
 5263    cx.assert_editor_state(
 5264        &r#"
 5265            let x = "\"ˇ "
 5266        "#
 5267        .unindent(),
 5268    );
 5269
 5270    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5271    // mark. Nothing is inserted.
 5272    cx.update_editor(|editor, cx| {
 5273        editor.move_right(&Default::default(), cx);
 5274        editor.handle_input("\"", cx);
 5275    });
 5276    cx.assert_editor_state(
 5277        &r#"
 5278            let x = "\" "ˇ
 5279        "#
 5280        .unindent(),
 5281    );
 5282}
 5283
 5284#[gpui::test]
 5285async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5286    init_test(cx, |_| {});
 5287
 5288    let language = Arc::new(Language::new(
 5289        LanguageConfig {
 5290            brackets: BracketPairConfig {
 5291                pairs: vec![
 5292                    BracketPair {
 5293                        start: "{".to_string(),
 5294                        end: "}".to_string(),
 5295                        close: true,
 5296                        newline: true,
 5297                    },
 5298                    BracketPair {
 5299                        start: "/* ".to_string(),
 5300                        end: "*/".to_string(),
 5301                        close: true,
 5302                        ..Default::default()
 5303                    },
 5304                ],
 5305                ..Default::default()
 5306            },
 5307            ..Default::default()
 5308        },
 5309        Some(tree_sitter_rust::language()),
 5310    ));
 5311
 5312    let text = r#"
 5313        a
 5314        b
 5315        c
 5316    "#
 5317    .unindent();
 5318
 5319    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5320    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5321    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5322    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5323        .await;
 5324
 5325    _ = view.update(cx, |view, cx| {
 5326        view.change_selections(None, cx, |s| {
 5327            s.select_display_ranges([
 5328                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5329                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5330                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5331            ])
 5332        });
 5333
 5334        view.handle_input("{", cx);
 5335        view.handle_input("{", cx);
 5336        view.handle_input("{", cx);
 5337        assert_eq!(
 5338            view.text(cx),
 5339            "
 5340                {{{a}}}
 5341                {{{b}}}
 5342                {{{c}}}
 5343            "
 5344            .unindent()
 5345        );
 5346        assert_eq!(
 5347            view.selections.display_ranges(cx),
 5348            [
 5349                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5350                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5351                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5352            ]
 5353        );
 5354
 5355        view.undo(&Undo, cx);
 5356        view.undo(&Undo, cx);
 5357        view.undo(&Undo, cx);
 5358        assert_eq!(
 5359            view.text(cx),
 5360            "
 5361                a
 5362                b
 5363                c
 5364            "
 5365            .unindent()
 5366        );
 5367        assert_eq!(
 5368            view.selections.display_ranges(cx),
 5369            [
 5370                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5371                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5372                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5373            ]
 5374        );
 5375
 5376        // Ensure inserting the first character of a multi-byte bracket pair
 5377        // doesn't surround the selections with the bracket.
 5378        view.handle_input("/", cx);
 5379        assert_eq!(
 5380            view.text(cx),
 5381            "
 5382                /
 5383                /
 5384                /
 5385            "
 5386            .unindent()
 5387        );
 5388        assert_eq!(
 5389            view.selections.display_ranges(cx),
 5390            [
 5391                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5392                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5393                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5394            ]
 5395        );
 5396
 5397        view.undo(&Undo, cx);
 5398        assert_eq!(
 5399            view.text(cx),
 5400            "
 5401                a
 5402                b
 5403                c
 5404            "
 5405            .unindent()
 5406        );
 5407        assert_eq!(
 5408            view.selections.display_ranges(cx),
 5409            [
 5410                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5411                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5412                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5413            ]
 5414        );
 5415
 5416        // Ensure inserting the last character of a multi-byte bracket pair
 5417        // doesn't surround the selections with the bracket.
 5418        view.handle_input("*", cx);
 5419        assert_eq!(
 5420            view.text(cx),
 5421            "
 5422                *
 5423                *
 5424                *
 5425            "
 5426            .unindent()
 5427        );
 5428        assert_eq!(
 5429            view.selections.display_ranges(cx),
 5430            [
 5431                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5432                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5433                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5434            ]
 5435        );
 5436    });
 5437}
 5438
 5439#[gpui::test]
 5440async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 5441    init_test(cx, |_| {});
 5442
 5443    let language = Arc::new(Language::new(
 5444        LanguageConfig {
 5445            brackets: BracketPairConfig {
 5446                pairs: vec![BracketPair {
 5447                    start: "{".to_string(),
 5448                    end: "}".to_string(),
 5449                    close: true,
 5450                    newline: true,
 5451                }],
 5452                ..Default::default()
 5453            },
 5454            autoclose_before: "}".to_string(),
 5455            ..Default::default()
 5456        },
 5457        Some(tree_sitter_rust::language()),
 5458    ));
 5459
 5460    let text = r#"
 5461        a
 5462        b
 5463        c
 5464    "#
 5465    .unindent();
 5466
 5467    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5468    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5469    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5470    editor
 5471        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5472        .await;
 5473
 5474    _ = editor.update(cx, |editor, cx| {
 5475        editor.change_selections(None, cx, |s| {
 5476            s.select_ranges([
 5477                Point::new(0, 1)..Point::new(0, 1),
 5478                Point::new(1, 1)..Point::new(1, 1),
 5479                Point::new(2, 1)..Point::new(2, 1),
 5480            ])
 5481        });
 5482
 5483        editor.handle_input("{", cx);
 5484        editor.handle_input("{", cx);
 5485        editor.handle_input("_", cx);
 5486        assert_eq!(
 5487            editor.text(cx),
 5488            "
 5489                a{{_}}
 5490                b{{_}}
 5491                c{{_}}
 5492            "
 5493            .unindent()
 5494        );
 5495        assert_eq!(
 5496            editor.selections.ranges::<Point>(cx),
 5497            [
 5498                Point::new(0, 4)..Point::new(0, 4),
 5499                Point::new(1, 4)..Point::new(1, 4),
 5500                Point::new(2, 4)..Point::new(2, 4)
 5501            ]
 5502        );
 5503
 5504        editor.backspace(&Default::default(), cx);
 5505        editor.backspace(&Default::default(), cx);
 5506        assert_eq!(
 5507            editor.text(cx),
 5508            "
 5509                a{}
 5510                b{}
 5511                c{}
 5512            "
 5513            .unindent()
 5514        );
 5515        assert_eq!(
 5516            editor.selections.ranges::<Point>(cx),
 5517            [
 5518                Point::new(0, 2)..Point::new(0, 2),
 5519                Point::new(1, 2)..Point::new(1, 2),
 5520                Point::new(2, 2)..Point::new(2, 2)
 5521            ]
 5522        );
 5523
 5524        editor.delete_to_previous_word_start(&Default::default(), cx);
 5525        assert_eq!(
 5526            editor.text(cx),
 5527            "
 5528                a
 5529                b
 5530                c
 5531            "
 5532            .unindent()
 5533        );
 5534        assert_eq!(
 5535            editor.selections.ranges::<Point>(cx),
 5536            [
 5537                Point::new(0, 1)..Point::new(0, 1),
 5538                Point::new(1, 1)..Point::new(1, 1),
 5539                Point::new(2, 1)..Point::new(2, 1)
 5540            ]
 5541        );
 5542    });
 5543}
 5544
 5545#[gpui::test]
 5546async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 5547    init_test(cx, |settings| {
 5548        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5549    });
 5550
 5551    let mut cx = EditorTestContext::new(cx).await;
 5552
 5553    let language = Arc::new(Language::new(
 5554        LanguageConfig {
 5555            brackets: BracketPairConfig {
 5556                pairs: vec![
 5557                    BracketPair {
 5558                        start: "{".to_string(),
 5559                        end: "}".to_string(),
 5560                        close: true,
 5561                        newline: true,
 5562                    },
 5563                    BracketPair {
 5564                        start: "(".to_string(),
 5565                        end: ")".to_string(),
 5566                        close: true,
 5567                        newline: true,
 5568                    },
 5569                    BracketPair {
 5570                        start: "[".to_string(),
 5571                        end: "]".to_string(),
 5572                        close: false,
 5573                        newline: true,
 5574                    },
 5575                ],
 5576                ..Default::default()
 5577            },
 5578            autoclose_before: "})]".to_string(),
 5579            ..Default::default()
 5580        },
 5581        Some(tree_sitter_rust::language()),
 5582    ));
 5583
 5584    cx.language_registry().add(language.clone());
 5585    cx.update_buffer(|buffer, cx| {
 5586        buffer.set_language(Some(language), cx);
 5587    });
 5588
 5589    cx.set_state(
 5590        &"
 5591            {(ˇ)}
 5592            [[ˇ]]
 5593            {(ˇ)}
 5594        "
 5595        .unindent(),
 5596    );
 5597
 5598    cx.update_editor(|view, cx| {
 5599        view.backspace(&Default::default(), cx);
 5600        view.backspace(&Default::default(), cx);
 5601    });
 5602
 5603    cx.assert_editor_state(
 5604        &"
 5605            ˇ
 5606            ˇ]]
 5607            ˇ
 5608        "
 5609        .unindent(),
 5610    );
 5611
 5612    cx.update_editor(|view, cx| {
 5613        view.handle_input("{", cx);
 5614        view.handle_input("{", cx);
 5615        view.move_right(&MoveRight, cx);
 5616        view.move_right(&MoveRight, cx);
 5617        view.move_left(&MoveLeft, cx);
 5618        view.move_left(&MoveLeft, cx);
 5619        view.backspace(&Default::default(), cx);
 5620    });
 5621
 5622    cx.assert_editor_state(
 5623        &"
 5624            {ˇ}
 5625            {ˇ}]]
 5626            {ˇ}
 5627        "
 5628        .unindent(),
 5629    );
 5630
 5631    cx.update_editor(|view, cx| {
 5632        view.backspace(&Default::default(), cx);
 5633    });
 5634
 5635    cx.assert_editor_state(
 5636        &"
 5637            ˇ
 5638            ˇ]]
 5639            ˇ
 5640        "
 5641        .unindent(),
 5642    );
 5643}
 5644
 5645#[gpui::test]
 5646async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 5647    init_test(cx, |_| {});
 5648
 5649    let language = Arc::new(Language::new(
 5650        LanguageConfig::default(),
 5651        Some(tree_sitter_rust::language()),
 5652    ));
 5653
 5654    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 5655    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5656    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5657    editor
 5658        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5659        .await;
 5660
 5661    _ = editor.update(cx, |editor, cx| {
 5662        editor.set_auto_replace_emoji_shortcode(true);
 5663
 5664        editor.handle_input("Hello ", cx);
 5665        editor.handle_input(":wave", cx);
 5666        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 5667
 5668        editor.handle_input(":", cx);
 5669        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 5670
 5671        editor.handle_input(" :smile", cx);
 5672        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 5673
 5674        editor.handle_input(":", cx);
 5675        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 5676
 5677        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 5678        editor.handle_input(":wave", cx);
 5679        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 5680
 5681        editor.handle_input(":", cx);
 5682        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 5683
 5684        editor.handle_input(":1", cx);
 5685        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 5686
 5687        editor.handle_input(":", cx);
 5688        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 5689
 5690        // Ensure shortcode does not get replaced when it is part of a word
 5691        editor.handle_input(" Test:wave", cx);
 5692        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 5693
 5694        editor.handle_input(":", cx);
 5695        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 5696
 5697        editor.set_auto_replace_emoji_shortcode(false);
 5698
 5699        // Ensure shortcode does not get replaced when auto replace is off
 5700        editor.handle_input(" :wave", cx);
 5701        assert_eq!(
 5702            editor.text(cx),
 5703            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 5704        );
 5705
 5706        editor.handle_input(":", cx);
 5707        assert_eq!(
 5708            editor.text(cx),
 5709            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 5710        );
 5711    });
 5712}
 5713
 5714#[gpui::test]
 5715async fn test_snippets(cx: &mut gpui::TestAppContext) {
 5716    init_test(cx, |_| {});
 5717
 5718    let (text, insertion_ranges) = marked_text_ranges(
 5719        indoc! {"
 5720            a.ˇ b
 5721            a.ˇ b
 5722            a.ˇ b
 5723        "},
 5724        false,
 5725    );
 5726
 5727    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 5728    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5729
 5730    _ = editor.update(cx, |editor, cx| {
 5731        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 5732
 5733        editor
 5734            .insert_snippet(&insertion_ranges, snippet, cx)
 5735            .unwrap();
 5736
 5737        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 5738            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 5739            assert_eq!(editor.text(cx), expected_text);
 5740            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 5741        }
 5742
 5743        assert(
 5744            editor,
 5745            cx,
 5746            indoc! {"
 5747                a.f(«one», two, «three») b
 5748                a.f(«one», two, «three») b
 5749                a.f(«one», two, «three») b
 5750            "},
 5751        );
 5752
 5753        // Can't move earlier than the first tab stop
 5754        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 5755        assert(
 5756            editor,
 5757            cx,
 5758            indoc! {"
 5759                a.f(«one», two, «three») b
 5760                a.f(«one», two, «three») b
 5761                a.f(«one», two, «three») b
 5762            "},
 5763        );
 5764
 5765        assert!(editor.move_to_next_snippet_tabstop(cx));
 5766        assert(
 5767            editor,
 5768            cx,
 5769            indoc! {"
 5770                a.f(one, «two», three) b
 5771                a.f(one, «two», three) b
 5772                a.f(one, «two», three) b
 5773            "},
 5774        );
 5775
 5776        editor.move_to_prev_snippet_tabstop(cx);
 5777        assert(
 5778            editor,
 5779            cx,
 5780            indoc! {"
 5781                a.f(«one», two, «three») b
 5782                a.f(«one», two, «three») b
 5783                a.f(«one», two, «three») b
 5784            "},
 5785        );
 5786
 5787        assert!(editor.move_to_next_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        assert!(editor.move_to_next_snippet_tabstop(cx));
 5798        assert(
 5799            editor,
 5800            cx,
 5801            indoc! {"
 5802                a.f(one, two, three)ˇ b
 5803                a.f(one, two, three)ˇ b
 5804                a.f(one, two, three)ˇ b
 5805            "},
 5806        );
 5807
 5808        // As soon as the last tab stop is reached, snippet state is gone
 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}
 5821
 5822#[gpui::test]
 5823async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 5824    init_test(cx, |_| {});
 5825
 5826    let fs = FakeFs::new(cx.executor());
 5827    fs.insert_file("/file.rs", Default::default()).await;
 5828
 5829    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 5830
 5831    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 5832    language_registry.add(rust_lang());
 5833    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 5834        "Rust",
 5835        FakeLspAdapter {
 5836            capabilities: lsp::ServerCapabilities {
 5837                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5838                ..Default::default()
 5839            },
 5840            ..Default::default()
 5841        },
 5842    );
 5843
 5844    let buffer = project
 5845        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 5846        .await
 5847        .unwrap();
 5848
 5849    cx.executor().start_waiting();
 5850    let fake_server = fake_servers.next().await.unwrap();
 5851
 5852    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5853    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5854    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 5855    assert!(cx.read(|cx| editor.is_dirty(cx)));
 5856
 5857    let save = editor
 5858        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5859        .unwrap();
 5860    fake_server
 5861        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 5862            assert_eq!(
 5863                params.text_document.uri,
 5864                lsp::Url::from_file_path("/file.rs").unwrap()
 5865            );
 5866            assert_eq!(params.options.tab_size, 4);
 5867            Ok(Some(vec![lsp::TextEdit::new(
 5868                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 5869                ", ".to_string(),
 5870            )]))
 5871        })
 5872        .next()
 5873        .await;
 5874    cx.executor().start_waiting();
 5875    save.await;
 5876
 5877    assert_eq!(
 5878        editor.update(cx, |editor, cx| editor.text(cx)),
 5879        "one, two\nthree\n"
 5880    );
 5881    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 5882
 5883    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 5884    assert!(cx.read(|cx| editor.is_dirty(cx)));
 5885
 5886    // Ensure we can still save even if formatting hangs.
 5887    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 5888        assert_eq!(
 5889            params.text_document.uri,
 5890            lsp::Url::from_file_path("/file.rs").unwrap()
 5891        );
 5892        futures::future::pending::<()>().await;
 5893        unreachable!()
 5894    });
 5895    let save = editor
 5896        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5897        .unwrap();
 5898    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 5899    cx.executor().start_waiting();
 5900    save.await;
 5901    assert_eq!(
 5902        editor.update(cx, |editor, cx| editor.text(cx)),
 5903        "one\ntwo\nthree\n"
 5904    );
 5905    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 5906
 5907    // For non-dirty buffer, no formatting request should be sent
 5908    let save = editor
 5909        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5910        .unwrap();
 5911    let _pending_format_request = fake_server
 5912        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 5913            panic!("Should not be invoked on non-dirty buffer");
 5914        })
 5915        .next();
 5916    cx.executor().start_waiting();
 5917    save.await;
 5918
 5919    // Set rust language override and assert overridden tabsize is sent to language server
 5920    update_test_language_settings(cx, |settings| {
 5921        settings.languages.insert(
 5922            "Rust".into(),
 5923            LanguageSettingsContent {
 5924                tab_size: NonZeroU32::new(8),
 5925                ..Default::default()
 5926            },
 5927        );
 5928    });
 5929
 5930    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 5931    assert!(cx.read(|cx| editor.is_dirty(cx)));
 5932    let save = editor
 5933        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5934        .unwrap();
 5935    fake_server
 5936        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 5937            assert_eq!(
 5938                params.text_document.uri,
 5939                lsp::Url::from_file_path("/file.rs").unwrap()
 5940            );
 5941            assert_eq!(params.options.tab_size, 8);
 5942            Ok(Some(vec![]))
 5943        })
 5944        .next()
 5945        .await;
 5946    cx.executor().start_waiting();
 5947    save.await;
 5948}
 5949
 5950#[gpui::test]
 5951async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 5952    init_test(cx, |_| {});
 5953
 5954    let cols = 4;
 5955    let rows = 10;
 5956    let sample_text_1 = sample_text(rows, cols, 'a');
 5957    assert_eq!(
 5958        sample_text_1,
 5959        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 5960    );
 5961    let sample_text_2 = sample_text(rows, cols, 'l');
 5962    assert_eq!(
 5963        sample_text_2,
 5964        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 5965    );
 5966    let sample_text_3 = sample_text(rows, cols, 'v');
 5967    assert_eq!(
 5968        sample_text_3,
 5969        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 5970    );
 5971
 5972    let fs = FakeFs::new(cx.executor());
 5973    fs.insert_tree(
 5974        "/a",
 5975        json!({
 5976            "main.rs": sample_text_1,
 5977            "other.rs": sample_text_2,
 5978            "lib.rs": sample_text_3,
 5979        }),
 5980    )
 5981    .await;
 5982
 5983    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 5984    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 5985    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 5986
 5987    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 5988    language_registry.add(rust_lang());
 5989    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 5990        "Rust",
 5991        FakeLspAdapter {
 5992            capabilities: lsp::ServerCapabilities {
 5993                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5994                ..Default::default()
 5995            },
 5996            ..Default::default()
 5997        },
 5998    );
 5999
 6000    let worktree = project.update(cx, |project, _| {
 6001        let mut worktrees = project.worktrees().collect::<Vec<_>>();
 6002        assert_eq!(worktrees.len(), 1);
 6003        worktrees.pop().unwrap()
 6004    });
 6005    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6006
 6007    let buffer_1 = project
 6008        .update(cx, |project, cx| {
 6009            project.open_buffer((worktree_id, "main.rs"), cx)
 6010        })
 6011        .await
 6012        .unwrap();
 6013    let buffer_2 = project
 6014        .update(cx, |project, cx| {
 6015            project.open_buffer((worktree_id, "other.rs"), cx)
 6016        })
 6017        .await
 6018        .unwrap();
 6019    let buffer_3 = project
 6020        .update(cx, |project, cx| {
 6021            project.open_buffer((worktree_id, "lib.rs"), cx)
 6022        })
 6023        .await
 6024        .unwrap();
 6025
 6026    let multi_buffer = cx.new_model(|cx| {
 6027        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 6028        multi_buffer.push_excerpts(
 6029            buffer_1.clone(),
 6030            [
 6031                ExcerptRange {
 6032                    context: Point::new(0, 0)..Point::new(3, 0),
 6033                    primary: None,
 6034                },
 6035                ExcerptRange {
 6036                    context: Point::new(5, 0)..Point::new(7, 0),
 6037                    primary: None,
 6038                },
 6039                ExcerptRange {
 6040                    context: Point::new(9, 0)..Point::new(10, 4),
 6041                    primary: None,
 6042                },
 6043            ],
 6044            cx,
 6045        );
 6046        multi_buffer.push_excerpts(
 6047            buffer_2.clone(),
 6048            [
 6049                ExcerptRange {
 6050                    context: Point::new(0, 0)..Point::new(3, 0),
 6051                    primary: None,
 6052                },
 6053                ExcerptRange {
 6054                    context: Point::new(5, 0)..Point::new(7, 0),
 6055                    primary: None,
 6056                },
 6057                ExcerptRange {
 6058                    context: Point::new(9, 0)..Point::new(10, 4),
 6059                    primary: None,
 6060                },
 6061            ],
 6062            cx,
 6063        );
 6064        multi_buffer.push_excerpts(
 6065            buffer_3.clone(),
 6066            [
 6067                ExcerptRange {
 6068                    context: Point::new(0, 0)..Point::new(3, 0),
 6069                    primary: None,
 6070                },
 6071                ExcerptRange {
 6072                    context: Point::new(5, 0)..Point::new(7, 0),
 6073                    primary: None,
 6074                },
 6075                ExcerptRange {
 6076                    context: Point::new(9, 0)..Point::new(10, 4),
 6077                    primary: None,
 6078                },
 6079            ],
 6080            cx,
 6081        );
 6082        multi_buffer
 6083    });
 6084    let multi_buffer_editor = cx.new_view(|cx| {
 6085        Editor::new(
 6086            EditorMode::Full,
 6087            multi_buffer,
 6088            Some(project.clone()),
 6089            true,
 6090            cx,
 6091        )
 6092    });
 6093
 6094    multi_buffer_editor.update(cx, |editor, cx| {
 6095        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6096        editor.insert("|one|two|three|", cx);
 6097    });
 6098    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6099    multi_buffer_editor.update(cx, |editor, cx| {
 6100        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6101            s.select_ranges(Some(60..70))
 6102        });
 6103        editor.insert("|four|five|six|", cx);
 6104    });
 6105    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6106
 6107    // First two buffers should be edited, but not the third one.
 6108    assert_eq!(
 6109        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6110        "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}",
 6111    );
 6112    buffer_1.update(cx, |buffer, _| {
 6113        assert!(buffer.is_dirty());
 6114        assert_eq!(
 6115            buffer.text(),
 6116            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6117        )
 6118    });
 6119    buffer_2.update(cx, |buffer, _| {
 6120        assert!(buffer.is_dirty());
 6121        assert_eq!(
 6122            buffer.text(),
 6123            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6124        )
 6125    });
 6126    buffer_3.update(cx, |buffer, _| {
 6127        assert!(!buffer.is_dirty());
 6128        assert_eq!(buffer.text(), sample_text_3,)
 6129    });
 6130
 6131    cx.executor().start_waiting();
 6132    let save = multi_buffer_editor
 6133        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6134        .unwrap();
 6135
 6136    let fake_server = fake_servers.next().await.unwrap();
 6137    fake_server
 6138        .server
 6139        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6140            Ok(Some(vec![lsp::TextEdit::new(
 6141                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6142                format!("[{} formatted]", params.text_document.uri),
 6143            )]))
 6144        })
 6145        .detach();
 6146    save.await;
 6147
 6148    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6149    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6150    assert_eq!(
 6151        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6152        "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}",
 6153    );
 6154    buffer_1.update(cx, |buffer, _| {
 6155        assert!(!buffer.is_dirty());
 6156        assert_eq!(
 6157            buffer.text(),
 6158            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6159        )
 6160    });
 6161    buffer_2.update(cx, |buffer, _| {
 6162        assert!(!buffer.is_dirty());
 6163        assert_eq!(
 6164            buffer.text(),
 6165            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6166        )
 6167    });
 6168    buffer_3.update(cx, |buffer, _| {
 6169        assert!(!buffer.is_dirty());
 6170        assert_eq!(buffer.text(), sample_text_3,)
 6171    });
 6172}
 6173
 6174#[gpui::test]
 6175async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6176    init_test(cx, |_| {});
 6177
 6178    let fs = FakeFs::new(cx.executor());
 6179    fs.insert_file("/file.rs", Default::default()).await;
 6180
 6181    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6182
 6183    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6184    language_registry.add(rust_lang());
 6185    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6186        "Rust",
 6187        FakeLspAdapter {
 6188            capabilities: lsp::ServerCapabilities {
 6189                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6190                ..Default::default()
 6191            },
 6192            ..Default::default()
 6193        },
 6194    );
 6195
 6196    let buffer = project
 6197        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6198        .await
 6199        .unwrap();
 6200
 6201    cx.executor().start_waiting();
 6202    let fake_server = fake_servers.next().await.unwrap();
 6203
 6204    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6205    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6206    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6207    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6208
 6209    let save = editor
 6210        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6211        .unwrap();
 6212    fake_server
 6213        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6214            assert_eq!(
 6215                params.text_document.uri,
 6216                lsp::Url::from_file_path("/file.rs").unwrap()
 6217            );
 6218            assert_eq!(params.options.tab_size, 4);
 6219            Ok(Some(vec![lsp::TextEdit::new(
 6220                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6221                ", ".to_string(),
 6222            )]))
 6223        })
 6224        .next()
 6225        .await;
 6226    cx.executor().start_waiting();
 6227    save.await;
 6228    assert_eq!(
 6229        editor.update(cx, |editor, cx| editor.text(cx)),
 6230        "one, two\nthree\n"
 6231    );
 6232    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6233
 6234    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6235    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6236
 6237    // Ensure we can still save even if formatting hangs.
 6238    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6239        move |params, _| async move {
 6240            assert_eq!(
 6241                params.text_document.uri,
 6242                lsp::Url::from_file_path("/file.rs").unwrap()
 6243            );
 6244            futures::future::pending::<()>().await;
 6245            unreachable!()
 6246        },
 6247    );
 6248    let save = editor
 6249        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6250        .unwrap();
 6251    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6252    cx.executor().start_waiting();
 6253    save.await;
 6254    assert_eq!(
 6255        editor.update(cx, |editor, cx| editor.text(cx)),
 6256        "one\ntwo\nthree\n"
 6257    );
 6258    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6259
 6260    // For non-dirty buffer, no formatting request should be sent
 6261    let save = editor
 6262        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6263        .unwrap();
 6264    let _pending_format_request = fake_server
 6265        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6266            panic!("Should not be invoked on non-dirty buffer");
 6267        })
 6268        .next();
 6269    cx.executor().start_waiting();
 6270    save.await;
 6271
 6272    // Set Rust language override and assert overridden tabsize is sent to language server
 6273    update_test_language_settings(cx, |settings| {
 6274        settings.languages.insert(
 6275            "Rust".into(),
 6276            LanguageSettingsContent {
 6277                tab_size: NonZeroU32::new(8),
 6278                ..Default::default()
 6279            },
 6280        );
 6281    });
 6282
 6283    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6284    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6285    let save = editor
 6286        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6287        .unwrap();
 6288    fake_server
 6289        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6290            assert_eq!(
 6291                params.text_document.uri,
 6292                lsp::Url::from_file_path("/file.rs").unwrap()
 6293            );
 6294            assert_eq!(params.options.tab_size, 8);
 6295            Ok(Some(vec![]))
 6296        })
 6297        .next()
 6298        .await;
 6299    cx.executor().start_waiting();
 6300    save.await;
 6301}
 6302
 6303#[gpui::test]
 6304async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6305    init_test(cx, |settings| {
 6306        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
 6307    });
 6308
 6309    let fs = FakeFs::new(cx.executor());
 6310    fs.insert_file("/file.rs", Default::default()).await;
 6311
 6312    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6313
 6314    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6315    language_registry.add(Arc::new(Language::new(
 6316        LanguageConfig {
 6317            name: "Rust".into(),
 6318            matcher: LanguageMatcher {
 6319                path_suffixes: vec!["rs".to_string()],
 6320                ..Default::default()
 6321            },
 6322            ..LanguageConfig::default()
 6323        },
 6324        Some(tree_sitter_rust::language()),
 6325    )));
 6326    update_test_language_settings(cx, |settings| {
 6327        // Enable Prettier formatting for the same buffer, and ensure
 6328        // LSP is called instead of Prettier.
 6329        settings.defaults.prettier = Some(PrettierSettings {
 6330            allowed: true,
 6331            ..PrettierSettings::default()
 6332        });
 6333    });
 6334    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6335        "Rust",
 6336        FakeLspAdapter {
 6337            capabilities: lsp::ServerCapabilities {
 6338                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6339                ..Default::default()
 6340            },
 6341            ..Default::default()
 6342        },
 6343    );
 6344
 6345    let buffer = project
 6346        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6347        .await
 6348        .unwrap();
 6349
 6350    cx.executor().start_waiting();
 6351    let fake_server = fake_servers.next().await.unwrap();
 6352
 6353    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6354    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6355    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6356
 6357    let format = editor
 6358        .update(cx, |editor, cx| {
 6359            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6360        })
 6361        .unwrap();
 6362    fake_server
 6363        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6364            assert_eq!(
 6365                params.text_document.uri,
 6366                lsp::Url::from_file_path("/file.rs").unwrap()
 6367            );
 6368            assert_eq!(params.options.tab_size, 4);
 6369            Ok(Some(vec![lsp::TextEdit::new(
 6370                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6371                ", ".to_string(),
 6372            )]))
 6373        })
 6374        .next()
 6375        .await;
 6376    cx.executor().start_waiting();
 6377    format.await;
 6378    assert_eq!(
 6379        editor.update(cx, |editor, cx| editor.text(cx)),
 6380        "one, two\nthree\n"
 6381    );
 6382
 6383    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6384    // Ensure we don't lock if formatting hangs.
 6385    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6386        assert_eq!(
 6387            params.text_document.uri,
 6388            lsp::Url::from_file_path("/file.rs").unwrap()
 6389        );
 6390        futures::future::pending::<()>().await;
 6391        unreachable!()
 6392    });
 6393    let format = editor
 6394        .update(cx, |editor, cx| {
 6395            editor.perform_format(project, FormatTrigger::Manual, cx)
 6396        })
 6397        .unwrap();
 6398    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6399    cx.executor().start_waiting();
 6400    format.await;
 6401    assert_eq!(
 6402        editor.update(cx, |editor, cx| editor.text(cx)),
 6403        "one\ntwo\nthree\n"
 6404    );
 6405}
 6406
 6407#[gpui::test]
 6408async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6409    init_test(cx, |_| {});
 6410
 6411    let mut cx = EditorLspTestContext::new_rust(
 6412        lsp::ServerCapabilities {
 6413            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6414            ..Default::default()
 6415        },
 6416        cx,
 6417    )
 6418    .await;
 6419
 6420    cx.set_state(indoc! {"
 6421        one.twoˇ
 6422    "});
 6423
 6424    // The format request takes a long time. When it completes, it inserts
 6425    // a newline and an indent before the `.`
 6426    cx.lsp
 6427        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6428            let executor = cx.background_executor().clone();
 6429            async move {
 6430                executor.timer(Duration::from_millis(100)).await;
 6431                Ok(Some(vec![lsp::TextEdit {
 6432                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6433                    new_text: "\n    ".into(),
 6434                }]))
 6435            }
 6436        });
 6437
 6438    // Submit a format request.
 6439    let format_1 = cx
 6440        .update_editor(|editor, cx| editor.format(&Format, cx))
 6441        .unwrap();
 6442    cx.executor().run_until_parked();
 6443
 6444    // Submit a second format request.
 6445    let format_2 = cx
 6446        .update_editor(|editor, cx| editor.format(&Format, cx))
 6447        .unwrap();
 6448    cx.executor().run_until_parked();
 6449
 6450    // Wait for both format requests to complete
 6451    cx.executor().advance_clock(Duration::from_millis(200));
 6452    cx.executor().start_waiting();
 6453    format_1.await.unwrap();
 6454    cx.executor().start_waiting();
 6455    format_2.await.unwrap();
 6456
 6457    // The formatting edits only happens once.
 6458    cx.assert_editor_state(indoc! {"
 6459        one
 6460            .twoˇ
 6461    "});
 6462}
 6463
 6464#[gpui::test]
 6465async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6466    init_test(cx, |settings| {
 6467        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 6468    });
 6469
 6470    let mut cx = EditorLspTestContext::new_rust(
 6471        lsp::ServerCapabilities {
 6472            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6473            ..Default::default()
 6474        },
 6475        cx,
 6476    )
 6477    .await;
 6478
 6479    // Set up a buffer white some trailing whitespace and no trailing newline.
 6480    cx.set_state(
 6481        &[
 6482            "one ",   //
 6483            "twoˇ",   //
 6484            "three ", //
 6485            "four",   //
 6486        ]
 6487        .join("\n"),
 6488    );
 6489
 6490    // Submit a format request.
 6491    let format = cx
 6492        .update_editor(|editor, cx| editor.format(&Format, cx))
 6493        .unwrap();
 6494
 6495    // Record which buffer changes have been sent to the language server
 6496    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6497    cx.lsp
 6498        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6499            let buffer_changes = buffer_changes.clone();
 6500            move |params, _| {
 6501                buffer_changes.lock().extend(
 6502                    params
 6503                        .content_changes
 6504                        .into_iter()
 6505                        .map(|e| (e.range.unwrap(), e.text)),
 6506                );
 6507            }
 6508        });
 6509
 6510    // Handle formatting requests to the language server.
 6511    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6512        let buffer_changes = buffer_changes.clone();
 6513        move |_, _| {
 6514            // When formatting is requested, trailing whitespace has already been stripped,
 6515            // and the trailing newline has already been added.
 6516            assert_eq!(
 6517                &buffer_changes.lock()[1..],
 6518                &[
 6519                    (
 6520                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6521                        "".into()
 6522                    ),
 6523                    (
 6524                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6525                        "".into()
 6526                    ),
 6527                    (
 6528                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6529                        "\n".into()
 6530                    ),
 6531                ]
 6532            );
 6533
 6534            // Insert blank lines between each line of the buffer.
 6535            async move {
 6536                Ok(Some(vec![
 6537                    lsp::TextEdit {
 6538                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6539                        new_text: "\n".into(),
 6540                    },
 6541                    lsp::TextEdit {
 6542                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6543                        new_text: "\n".into(),
 6544                    },
 6545                ]))
 6546            }
 6547        }
 6548    });
 6549
 6550    // After formatting the buffer, the trailing whitespace is stripped,
 6551    // a newline is appended, and the edits provided by the language server
 6552    // have been applied.
 6553    format.await.unwrap();
 6554    cx.assert_editor_state(
 6555        &[
 6556            "one",   //
 6557            "",      //
 6558            "twoˇ",  //
 6559            "",      //
 6560            "three", //
 6561            "four",  //
 6562            "",      //
 6563        ]
 6564        .join("\n"),
 6565    );
 6566
 6567    // Undoing the formatting undoes the trailing whitespace removal, the
 6568    // trailing newline, and the LSP edits.
 6569    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6570    cx.assert_editor_state(
 6571        &[
 6572            "one ",   //
 6573            "twoˇ",   //
 6574            "three ", //
 6575            "four",   //
 6576        ]
 6577        .join("\n"),
 6578    );
 6579}
 6580
 6581#[gpui::test]
 6582async fn test_completion(cx: &mut gpui::TestAppContext) {
 6583    init_test(cx, |_| {});
 6584
 6585    let mut cx = EditorLspTestContext::new_rust(
 6586        lsp::ServerCapabilities {
 6587            completion_provider: Some(lsp::CompletionOptions {
 6588                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 6589                resolve_provider: Some(true),
 6590                ..Default::default()
 6591            }),
 6592            ..Default::default()
 6593        },
 6594        cx,
 6595    )
 6596    .await;
 6597    let counter = Arc::new(AtomicUsize::new(0));
 6598
 6599    cx.set_state(indoc! {"
 6600        oneˇ
 6601        two
 6602        three
 6603    "});
 6604    cx.simulate_keystroke(".");
 6605    handle_completion_request(
 6606        &mut cx,
 6607        indoc! {"
 6608            one.|<>
 6609            two
 6610            three
 6611        "},
 6612        vec!["first_completion", "second_completion"],
 6613        counter.clone(),
 6614    )
 6615    .await;
 6616    cx.condition(|editor, _| editor.context_menu_visible())
 6617        .await;
 6618    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 6619
 6620    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6621        editor.context_menu_next(&Default::default(), cx);
 6622        editor
 6623            .confirm_completion(&ConfirmCompletion::default(), cx)
 6624            .unwrap()
 6625    });
 6626    cx.assert_editor_state(indoc! {"
 6627        one.second_completionˇ
 6628        two
 6629        three
 6630    "});
 6631
 6632    handle_resolve_completion_request(
 6633        &mut cx,
 6634        Some(vec![
 6635            (
 6636                //This overlaps with the primary completion edit which is
 6637                //misbehavior from the LSP spec, test that we filter it out
 6638                indoc! {"
 6639                    one.second_ˇcompletion
 6640                    two
 6641                    threeˇ
 6642                "},
 6643                "overlapping additional edit",
 6644            ),
 6645            (
 6646                indoc! {"
 6647                    one.second_completion
 6648                    two
 6649                    threeˇ
 6650                "},
 6651                "\nadditional edit",
 6652            ),
 6653        ]),
 6654    )
 6655    .await;
 6656    apply_additional_edits.await.unwrap();
 6657    cx.assert_editor_state(indoc! {"
 6658        one.second_completionˇ
 6659        two
 6660        three
 6661        additional edit
 6662    "});
 6663
 6664    cx.set_state(indoc! {"
 6665        one.second_completion
 6666        twoˇ
 6667        threeˇ
 6668        additional edit
 6669    "});
 6670    cx.simulate_keystroke(" ");
 6671    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6672    cx.simulate_keystroke("s");
 6673    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6674
 6675    cx.assert_editor_state(indoc! {"
 6676        one.second_completion
 6677        two sˇ
 6678        three sˇ
 6679        additional edit
 6680    "});
 6681    handle_completion_request(
 6682        &mut cx,
 6683        indoc! {"
 6684            one.second_completion
 6685            two s
 6686            three <s|>
 6687            additional edit
 6688        "},
 6689        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 6690        counter.clone(),
 6691    )
 6692    .await;
 6693    cx.condition(|editor, _| editor.context_menu_visible())
 6694        .await;
 6695    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 6696
 6697    cx.simulate_keystroke("i");
 6698
 6699    handle_completion_request(
 6700        &mut cx,
 6701        indoc! {"
 6702            one.second_completion
 6703            two si
 6704            three <si|>
 6705            additional edit
 6706        "},
 6707        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 6708        counter.clone(),
 6709    )
 6710    .await;
 6711    cx.condition(|editor, _| editor.context_menu_visible())
 6712        .await;
 6713    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 6714
 6715    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6716        editor
 6717            .confirm_completion(&ConfirmCompletion::default(), cx)
 6718            .unwrap()
 6719    });
 6720    cx.assert_editor_state(indoc! {"
 6721        one.second_completion
 6722        two sixth_completionˇ
 6723        three sixth_completionˇ
 6724        additional edit
 6725    "});
 6726
 6727    handle_resolve_completion_request(&mut cx, None).await;
 6728    apply_additional_edits.await.unwrap();
 6729
 6730    _ = cx.update(|cx| {
 6731        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6732            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6733                settings.show_completions_on_input = Some(false);
 6734            });
 6735        })
 6736    });
 6737    cx.set_state("editorˇ");
 6738    cx.simulate_keystroke(".");
 6739    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6740    cx.simulate_keystroke("c");
 6741    cx.simulate_keystroke("l");
 6742    cx.simulate_keystroke("o");
 6743    cx.assert_editor_state("editor.cloˇ");
 6744    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6745    cx.update_editor(|editor, cx| {
 6746        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 6747    });
 6748    handle_completion_request(
 6749        &mut cx,
 6750        "editor.<clo|>",
 6751        vec!["close", "clobber"],
 6752        counter.clone(),
 6753    )
 6754    .await;
 6755    cx.condition(|editor, _| editor.context_menu_visible())
 6756        .await;
 6757    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 6758
 6759    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6760        editor
 6761            .confirm_completion(&ConfirmCompletion::default(), cx)
 6762            .unwrap()
 6763    });
 6764    cx.assert_editor_state("editor.closeˇ");
 6765    handle_resolve_completion_request(&mut cx, None).await;
 6766    apply_additional_edits.await.unwrap();
 6767}
 6768
 6769#[gpui::test]
 6770async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 6771    init_test(cx, |_| {});
 6772
 6773    let mut cx = EditorLspTestContext::new_rust(
 6774        lsp::ServerCapabilities {
 6775            completion_provider: Some(lsp::CompletionOptions {
 6776                trigger_characters: Some(vec![".".to_string()]),
 6777                resolve_provider: Some(true),
 6778                ..Default::default()
 6779            }),
 6780            ..Default::default()
 6781        },
 6782        cx,
 6783    )
 6784    .await;
 6785
 6786    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 6787    cx.simulate_keystroke(".");
 6788    let completion_item = lsp::CompletionItem {
 6789        label: "Some".into(),
 6790        kind: Some(lsp::CompletionItemKind::SNIPPET),
 6791        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 6792        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 6793            kind: lsp::MarkupKind::Markdown,
 6794            value: "```rust\nSome(2)\n```".to_string(),
 6795        })),
 6796        deprecated: Some(false),
 6797        sort_text: Some("Some".to_string()),
 6798        filter_text: Some("Some".to_string()),
 6799        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 6800        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 6801            range: lsp::Range {
 6802                start: lsp::Position {
 6803                    line: 0,
 6804                    character: 22,
 6805                },
 6806                end: lsp::Position {
 6807                    line: 0,
 6808                    character: 22,
 6809                },
 6810            },
 6811            new_text: "Some(2)".to_string(),
 6812        })),
 6813        additional_text_edits: Some(vec![lsp::TextEdit {
 6814            range: lsp::Range {
 6815                start: lsp::Position {
 6816                    line: 0,
 6817                    character: 20,
 6818                },
 6819                end: lsp::Position {
 6820                    line: 0,
 6821                    character: 22,
 6822                },
 6823            },
 6824            new_text: "".to_string(),
 6825        }]),
 6826        ..Default::default()
 6827    };
 6828
 6829    let closure_completion_item = completion_item.clone();
 6830    let counter = Arc::new(AtomicUsize::new(0));
 6831    let counter_clone = counter.clone();
 6832    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 6833        let task_completion_item = closure_completion_item.clone();
 6834        counter_clone.fetch_add(1, atomic::Ordering::Release);
 6835        async move {
 6836            Ok(Some(lsp::CompletionResponse::Array(vec![
 6837                task_completion_item,
 6838            ])))
 6839        }
 6840    });
 6841
 6842    cx.condition(|editor, _| editor.context_menu_visible())
 6843        .await;
 6844    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 6845    assert!(request.next().await.is_some());
 6846    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 6847
 6848    cx.simulate_keystroke("S");
 6849    cx.simulate_keystroke("o");
 6850    cx.simulate_keystroke("m");
 6851    cx.condition(|editor, _| editor.context_menu_visible())
 6852        .await;
 6853    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 6854    assert!(request.next().await.is_some());
 6855    assert!(request.next().await.is_some());
 6856    assert!(request.next().await.is_some());
 6857    request.close();
 6858    assert!(request.next().await.is_none());
 6859    assert_eq!(
 6860        counter.load(atomic::Ordering::Acquire),
 6861        4,
 6862        "With the completions menu open, only one LSP request should happen per input"
 6863    );
 6864}
 6865
 6866#[gpui::test]
 6867async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 6868    init_test(cx, |_| {});
 6869    let mut cx = EditorTestContext::new(cx).await;
 6870    let language = Arc::new(Language::new(
 6871        LanguageConfig {
 6872            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 6873            ..Default::default()
 6874        },
 6875        Some(tree_sitter_rust::language()),
 6876    ));
 6877    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 6878
 6879    // If multiple selections intersect a line, the line is only toggled once.
 6880    cx.set_state(indoc! {"
 6881        fn a() {
 6882            «//b();
 6883            ˇ»// «c();
 6884            //ˇ»  d();
 6885        }
 6886    "});
 6887
 6888    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6889
 6890    cx.assert_editor_state(indoc! {"
 6891        fn a() {
 6892            «b();
 6893            c();
 6894            ˇ» d();
 6895        }
 6896    "});
 6897
 6898    // The comment prefix is inserted at the same column for every line in a
 6899    // selection.
 6900    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6901
 6902    cx.assert_editor_state(indoc! {"
 6903        fn a() {
 6904            // «b();
 6905            // c();
 6906            ˇ»//  d();
 6907        }
 6908    "});
 6909
 6910    // If a selection ends at the beginning of a line, that line is not toggled.
 6911    cx.set_selections_state(indoc! {"
 6912        fn a() {
 6913            // b();
 6914            «// c();
 6915        ˇ»    //  d();
 6916        }
 6917    "});
 6918
 6919    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6920
 6921    cx.assert_editor_state(indoc! {"
 6922        fn a() {
 6923            // b();
 6924            «c();
 6925        ˇ»    //  d();
 6926        }
 6927    "});
 6928
 6929    // If a selection span a single line and is empty, the line is toggled.
 6930    cx.set_state(indoc! {"
 6931        fn a() {
 6932            a();
 6933            b();
 6934        ˇ
 6935        }
 6936    "});
 6937
 6938    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6939
 6940    cx.assert_editor_state(indoc! {"
 6941        fn a() {
 6942            a();
 6943            b();
 6944        //•ˇ
 6945        }
 6946    "});
 6947
 6948    // If a selection span multiple lines, empty lines are not toggled.
 6949    cx.set_state(indoc! {"
 6950        fn a() {
 6951            «a();
 6952
 6953            c();ˇ»
 6954        }
 6955    "});
 6956
 6957    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6958
 6959    cx.assert_editor_state(indoc! {"
 6960        fn a() {
 6961            // «a();
 6962
 6963            // c();ˇ»
 6964        }
 6965    "});
 6966
 6967    // If a selection includes multiple comment prefixes, all lines are uncommented.
 6968    cx.set_state(indoc! {"
 6969        fn a() {
 6970            «// a();
 6971            /// b();
 6972            //! c();ˇ»
 6973        }
 6974    "});
 6975
 6976    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6977
 6978    cx.assert_editor_state(indoc! {"
 6979        fn a() {
 6980            «a();
 6981            b();
 6982            c();ˇ»
 6983        }
 6984    "});
 6985}
 6986
 6987#[gpui::test]
 6988async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 6989    init_test(cx, |_| {});
 6990
 6991    let language = Arc::new(Language::new(
 6992        LanguageConfig {
 6993            line_comments: vec!["// ".into()],
 6994            ..Default::default()
 6995        },
 6996        Some(tree_sitter_rust::language()),
 6997    ));
 6998
 6999    let mut cx = EditorTestContext::new(cx).await;
 7000
 7001    cx.language_registry().add(language.clone());
 7002    cx.update_buffer(|buffer, cx| {
 7003        buffer.set_language(Some(language), cx);
 7004    });
 7005
 7006    let toggle_comments = &ToggleComments {
 7007        advance_downwards: true,
 7008    };
 7009
 7010    // Single cursor on one line -> advance
 7011    // Cursor moves horizontally 3 characters as well on non-blank line
 7012    cx.set_state(indoc!(
 7013        "fn a() {
 7014             ˇdog();
 7015             cat();
 7016        }"
 7017    ));
 7018    cx.update_editor(|editor, cx| {
 7019        editor.toggle_comments(toggle_comments, cx);
 7020    });
 7021    cx.assert_editor_state(indoc!(
 7022        "fn a() {
 7023             // dog();
 7024             catˇ();
 7025        }"
 7026    ));
 7027
 7028    // Single selection on one line -> don't advance
 7029    cx.set_state(indoc!(
 7030        "fn a() {
 7031             «dog()ˇ»;
 7032             cat();
 7033        }"
 7034    ));
 7035    cx.update_editor(|editor, cx| {
 7036        editor.toggle_comments(toggle_comments, cx);
 7037    });
 7038    cx.assert_editor_state(indoc!(
 7039        "fn a() {
 7040             // «dog()ˇ»;
 7041             cat();
 7042        }"
 7043    ));
 7044
 7045    // Multiple cursors on one line -> advance
 7046    cx.set_state(indoc!(
 7047        "fn a() {
 7048             ˇdˇog();
 7049             cat();
 7050        }"
 7051    ));
 7052    cx.update_editor(|editor, cx| {
 7053        editor.toggle_comments(toggle_comments, cx);
 7054    });
 7055    cx.assert_editor_state(indoc!(
 7056        "fn a() {
 7057             // dog();
 7058             catˇ(ˇ);
 7059        }"
 7060    ));
 7061
 7062    // Multiple cursors on one line, with selection -> don't advance
 7063    cx.set_state(indoc!(
 7064        "fn a() {
 7065             ˇdˇog«()ˇ»;
 7066             cat();
 7067        }"
 7068    ));
 7069    cx.update_editor(|editor, cx| {
 7070        editor.toggle_comments(toggle_comments, cx);
 7071    });
 7072    cx.assert_editor_state(indoc!(
 7073        "fn a() {
 7074             // ˇdˇog«()ˇ»;
 7075             cat();
 7076        }"
 7077    ));
 7078
 7079    // Single cursor on one line -> advance
 7080    // Cursor moves to column 0 on blank line
 7081    cx.set_state(indoc!(
 7082        "fn a() {
 7083             ˇdog();
 7084
 7085             cat();
 7086        }"
 7087    ));
 7088    cx.update_editor(|editor, cx| {
 7089        editor.toggle_comments(toggle_comments, cx);
 7090    });
 7091    cx.assert_editor_state(indoc!(
 7092        "fn a() {
 7093             // dog();
 7094        ˇ
 7095             cat();
 7096        }"
 7097    ));
 7098
 7099    // Single cursor on one line -> advance
 7100    // Cursor starts and ends at column 0
 7101    cx.set_state(indoc!(
 7102        "fn a() {
 7103         ˇ    dog();
 7104             cat();
 7105        }"
 7106    ));
 7107    cx.update_editor(|editor, cx| {
 7108        editor.toggle_comments(toggle_comments, cx);
 7109    });
 7110    cx.assert_editor_state(indoc!(
 7111        "fn a() {
 7112             // dog();
 7113         ˇ    cat();
 7114        }"
 7115    ));
 7116}
 7117
 7118#[gpui::test]
 7119async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 7120    init_test(cx, |_| {});
 7121
 7122    let mut cx = EditorTestContext::new(cx).await;
 7123
 7124    let html_language = Arc::new(
 7125        Language::new(
 7126            LanguageConfig {
 7127                name: "HTML".into(),
 7128                block_comment: Some(("<!-- ".into(), " -->".into())),
 7129                ..Default::default()
 7130            },
 7131            Some(tree_sitter_html::language()),
 7132        )
 7133        .with_injection_query(
 7134            r#"
 7135            (script_element
 7136                (raw_text) @content
 7137                (#set! "language" "javascript"))
 7138            "#,
 7139        )
 7140        .unwrap(),
 7141    );
 7142
 7143    let javascript_language = Arc::new(Language::new(
 7144        LanguageConfig {
 7145            name: "JavaScript".into(),
 7146            line_comments: vec!["// ".into()],
 7147            ..Default::default()
 7148        },
 7149        Some(tree_sitter_typescript::language_tsx()),
 7150    ));
 7151
 7152    cx.language_registry().add(html_language.clone());
 7153    cx.language_registry().add(javascript_language.clone());
 7154    cx.update_buffer(|buffer, cx| {
 7155        buffer.set_language(Some(html_language), cx);
 7156    });
 7157
 7158    // Toggle comments for empty selections
 7159    cx.set_state(
 7160        &r#"
 7161            <p>A</p>ˇ
 7162            <p>B</p>ˇ
 7163            <p>C</p>ˇ
 7164        "#
 7165        .unindent(),
 7166    );
 7167    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7168    cx.assert_editor_state(
 7169        &r#"
 7170            <!-- <p>A</p>ˇ -->
 7171            <!-- <p>B</p>ˇ -->
 7172            <!-- <p>C</p>ˇ -->
 7173        "#
 7174        .unindent(),
 7175    );
 7176    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7177    cx.assert_editor_state(
 7178        &r#"
 7179            <p>A</p>ˇ
 7180            <p>B</p>ˇ
 7181            <p>C</p>ˇ
 7182        "#
 7183        .unindent(),
 7184    );
 7185
 7186    // Toggle comments for mixture of empty and non-empty selections, where
 7187    // multiple selections occupy a given line.
 7188    cx.set_state(
 7189        &r#"
 7190            <p>A«</p>
 7191            <p>ˇ»B</p>ˇ
 7192            <p>C«</p>
 7193            <p>ˇ»D</p>ˇ
 7194        "#
 7195        .unindent(),
 7196    );
 7197
 7198    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7199    cx.assert_editor_state(
 7200        &r#"
 7201            <!-- <p>A«</p>
 7202            <p>ˇ»B</p>ˇ -->
 7203            <!-- <p>C«</p>
 7204            <p>ˇ»D</p>ˇ -->
 7205        "#
 7206        .unindent(),
 7207    );
 7208    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7209    cx.assert_editor_state(
 7210        &r#"
 7211            <p>A«</p>
 7212            <p>ˇ»B</p>ˇ
 7213            <p>C«</p>
 7214            <p>ˇ»D</p>ˇ
 7215        "#
 7216        .unindent(),
 7217    );
 7218
 7219    // Toggle comments when different languages are active for different
 7220    // selections.
 7221    cx.set_state(
 7222        &r#"
 7223            ˇ<script>
 7224                ˇvar x = new Y();
 7225            ˇ</script>
 7226        "#
 7227        .unindent(),
 7228    );
 7229    cx.executor().run_until_parked();
 7230    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7231    cx.assert_editor_state(
 7232        &r#"
 7233            <!-- ˇ<script> -->
 7234                // ˇvar x = new Y();
 7235            <!-- ˇ</script> -->
 7236        "#
 7237        .unindent(),
 7238    );
 7239}
 7240
 7241#[gpui::test]
 7242fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 7243    init_test(cx, |_| {});
 7244
 7245    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7246    let multibuffer = cx.new_model(|cx| {
 7247        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7248        multibuffer.push_excerpts(
 7249            buffer.clone(),
 7250            [
 7251                ExcerptRange {
 7252                    context: Point::new(0, 0)..Point::new(0, 4),
 7253                    primary: None,
 7254                },
 7255                ExcerptRange {
 7256                    context: Point::new(1, 0)..Point::new(1, 4),
 7257                    primary: None,
 7258                },
 7259            ],
 7260            cx,
 7261        );
 7262        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 7263        multibuffer
 7264    });
 7265
 7266    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 7267    _ = view.update(cx, |view, cx| {
 7268        assert_eq!(view.text(cx), "aaaa\nbbbb");
 7269        view.change_selections(None, cx, |s| {
 7270            s.select_ranges([
 7271                Point::new(0, 0)..Point::new(0, 0),
 7272                Point::new(1, 0)..Point::new(1, 0),
 7273            ])
 7274        });
 7275
 7276        view.handle_input("X", cx);
 7277        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 7278        assert_eq!(
 7279            view.selections.ranges(cx),
 7280            [
 7281                Point::new(0, 1)..Point::new(0, 1),
 7282                Point::new(1, 1)..Point::new(1, 1),
 7283            ]
 7284        );
 7285
 7286        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 7287        view.change_selections(None, cx, |s| {
 7288            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 7289        });
 7290        view.backspace(&Default::default(), cx);
 7291        assert_eq!(view.text(cx), "Xa\nbbb");
 7292        assert_eq!(
 7293            view.selections.ranges(cx),
 7294            [Point::new(1, 0)..Point::new(1, 0)]
 7295        );
 7296
 7297        view.change_selections(None, cx, |s| {
 7298            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 7299        });
 7300        view.backspace(&Default::default(), cx);
 7301        assert_eq!(view.text(cx), "X\nbb");
 7302        assert_eq!(
 7303            view.selections.ranges(cx),
 7304            [Point::new(0, 1)..Point::new(0, 1)]
 7305        );
 7306    });
 7307}
 7308
 7309#[gpui::test]
 7310fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 7311    init_test(cx, |_| {});
 7312
 7313    let markers = vec![('[', ']').into(), ('(', ')').into()];
 7314    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 7315        indoc! {"
 7316            [aaaa
 7317            (bbbb]
 7318            cccc)",
 7319        },
 7320        markers.clone(),
 7321    );
 7322    let excerpt_ranges = markers.into_iter().map(|marker| {
 7323        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 7324        ExcerptRange {
 7325            context,
 7326            primary: None,
 7327        }
 7328    });
 7329    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 7330    let multibuffer = cx.new_model(|cx| {
 7331        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7332        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 7333        multibuffer
 7334    });
 7335
 7336    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 7337    _ = view.update(cx, |view, cx| {
 7338        let (expected_text, selection_ranges) = marked_text_ranges(
 7339            indoc! {"
 7340                aaaa
 7341                bˇbbb
 7342                bˇbbˇb
 7343                cccc"
 7344            },
 7345            true,
 7346        );
 7347        assert_eq!(view.text(cx), expected_text);
 7348        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 7349
 7350        view.handle_input("X", cx);
 7351
 7352        let (expected_text, expected_selections) = marked_text_ranges(
 7353            indoc! {"
 7354                aaaa
 7355                bXˇbbXb
 7356                bXˇbbXˇb
 7357                cccc"
 7358            },
 7359            false,
 7360        );
 7361        assert_eq!(view.text(cx), expected_text);
 7362        assert_eq!(view.selections.ranges(cx), expected_selections);
 7363
 7364        view.newline(&Newline, cx);
 7365        let (expected_text, expected_selections) = marked_text_ranges(
 7366            indoc! {"
 7367                aaaa
 7368                bX
 7369                ˇbbX
 7370                b
 7371                bX
 7372                ˇbbX
 7373                ˇb
 7374                cccc"
 7375            },
 7376            false,
 7377        );
 7378        assert_eq!(view.text(cx), expected_text);
 7379        assert_eq!(view.selections.ranges(cx), expected_selections);
 7380    });
 7381}
 7382
 7383#[gpui::test]
 7384fn test_refresh_selections(cx: &mut TestAppContext) {
 7385    init_test(cx, |_| {});
 7386
 7387    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7388    let mut excerpt1_id = None;
 7389    let multibuffer = cx.new_model(|cx| {
 7390        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7391        excerpt1_id = multibuffer
 7392            .push_excerpts(
 7393                buffer.clone(),
 7394                [
 7395                    ExcerptRange {
 7396                        context: Point::new(0, 0)..Point::new(1, 4),
 7397                        primary: None,
 7398                    },
 7399                    ExcerptRange {
 7400                        context: Point::new(1, 0)..Point::new(2, 4),
 7401                        primary: None,
 7402                    },
 7403                ],
 7404                cx,
 7405            )
 7406            .into_iter()
 7407            .next();
 7408        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 7409        multibuffer
 7410    });
 7411
 7412    let editor = cx.add_window(|cx| {
 7413        let mut editor = build_editor(multibuffer.clone(), cx);
 7414        let snapshot = editor.snapshot(cx);
 7415        editor.change_selections(None, cx, |s| {
 7416            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 7417        });
 7418        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 7419        assert_eq!(
 7420            editor.selections.ranges(cx),
 7421            [
 7422                Point::new(1, 3)..Point::new(1, 3),
 7423                Point::new(2, 1)..Point::new(2, 1),
 7424            ]
 7425        );
 7426        editor
 7427    });
 7428
 7429    // Refreshing selections is a no-op when excerpts haven't changed.
 7430    _ = editor.update(cx, |editor, cx| {
 7431        editor.change_selections(None, cx, |s| s.refresh());
 7432        assert_eq!(
 7433            editor.selections.ranges(cx),
 7434            [
 7435                Point::new(1, 3)..Point::new(1, 3),
 7436                Point::new(2, 1)..Point::new(2, 1),
 7437            ]
 7438        );
 7439    });
 7440
 7441    _ = multibuffer.update(cx, |multibuffer, cx| {
 7442        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 7443    });
 7444    _ = editor.update(cx, |editor, cx| {
 7445        // Removing an excerpt causes the first selection to become degenerate.
 7446        assert_eq!(
 7447            editor.selections.ranges(cx),
 7448            [
 7449                Point::new(0, 0)..Point::new(0, 0),
 7450                Point::new(0, 1)..Point::new(0, 1)
 7451            ]
 7452        );
 7453
 7454        // Refreshing selections will relocate the first selection to the original buffer
 7455        // location.
 7456        editor.change_selections(None, cx, |s| s.refresh());
 7457        assert_eq!(
 7458            editor.selections.ranges(cx),
 7459            [
 7460                Point::new(0, 1)..Point::new(0, 1),
 7461                Point::new(0, 3)..Point::new(0, 3)
 7462            ]
 7463        );
 7464        assert!(editor.selections.pending_anchor().is_some());
 7465    });
 7466}
 7467
 7468#[gpui::test]
 7469fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 7470    init_test(cx, |_| {});
 7471
 7472    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7473    let mut excerpt1_id = None;
 7474    let multibuffer = cx.new_model(|cx| {
 7475        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7476        excerpt1_id = multibuffer
 7477            .push_excerpts(
 7478                buffer.clone(),
 7479                [
 7480                    ExcerptRange {
 7481                        context: Point::new(0, 0)..Point::new(1, 4),
 7482                        primary: None,
 7483                    },
 7484                    ExcerptRange {
 7485                        context: Point::new(1, 0)..Point::new(2, 4),
 7486                        primary: None,
 7487                    },
 7488                ],
 7489                cx,
 7490            )
 7491            .into_iter()
 7492            .next();
 7493        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 7494        multibuffer
 7495    });
 7496
 7497    let editor = cx.add_window(|cx| {
 7498        let mut editor = build_editor(multibuffer.clone(), cx);
 7499        let snapshot = editor.snapshot(cx);
 7500        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 7501        assert_eq!(
 7502            editor.selections.ranges(cx),
 7503            [Point::new(1, 3)..Point::new(1, 3)]
 7504        );
 7505        editor
 7506    });
 7507
 7508    _ = multibuffer.update(cx, |multibuffer, cx| {
 7509        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 7510    });
 7511    _ = editor.update(cx, |editor, cx| {
 7512        assert_eq!(
 7513            editor.selections.ranges(cx),
 7514            [Point::new(0, 0)..Point::new(0, 0)]
 7515        );
 7516
 7517        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 7518        editor.change_selections(None, cx, |s| s.refresh());
 7519        assert_eq!(
 7520            editor.selections.ranges(cx),
 7521            [Point::new(0, 3)..Point::new(0, 3)]
 7522        );
 7523        assert!(editor.selections.pending_anchor().is_some());
 7524    });
 7525}
 7526
 7527#[gpui::test]
 7528async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 7529    init_test(cx, |_| {});
 7530
 7531    let language = Arc::new(
 7532        Language::new(
 7533            LanguageConfig {
 7534                brackets: BracketPairConfig {
 7535                    pairs: vec![
 7536                        BracketPair {
 7537                            start: "{".to_string(),
 7538                            end: "}".to_string(),
 7539                            close: true,
 7540                            newline: true,
 7541                        },
 7542                        BracketPair {
 7543                            start: "/* ".to_string(),
 7544                            end: " */".to_string(),
 7545                            close: true,
 7546                            newline: true,
 7547                        },
 7548                    ],
 7549                    ..Default::default()
 7550                },
 7551                ..Default::default()
 7552            },
 7553            Some(tree_sitter_rust::language()),
 7554        )
 7555        .with_indents_query("")
 7556        .unwrap(),
 7557    );
 7558
 7559    let text = concat!(
 7560        "{   }\n",     //
 7561        "  x\n",       //
 7562        "  /*   */\n", //
 7563        "x\n",         //
 7564        "{{} }\n",     //
 7565    );
 7566
 7567    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 7568    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7569    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7570    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 7571        .await;
 7572
 7573    _ = view.update(cx, |view, cx| {
 7574        view.change_selections(None, cx, |s| {
 7575            s.select_display_ranges([
 7576                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 7577                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 7578                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 7579            ])
 7580        });
 7581        view.newline(&Newline, cx);
 7582
 7583        assert_eq!(
 7584            view.buffer().read(cx).read(cx).text(),
 7585            concat!(
 7586                "{ \n",    // Suppress rustfmt
 7587                "\n",      //
 7588                "}\n",     //
 7589                "  x\n",   //
 7590                "  /* \n", //
 7591                "  \n",    //
 7592                "  */\n",  //
 7593                "x\n",     //
 7594                "{{} \n",  //
 7595                "}\n",     //
 7596            )
 7597        );
 7598    });
 7599}
 7600
 7601#[gpui::test]
 7602fn test_highlighted_ranges(cx: &mut TestAppContext) {
 7603    init_test(cx, |_| {});
 7604
 7605    let editor = cx.add_window(|cx| {
 7606        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 7607        build_editor(buffer.clone(), cx)
 7608    });
 7609
 7610    _ = editor.update(cx, |editor, cx| {
 7611        struct Type1;
 7612        struct Type2;
 7613
 7614        let buffer = editor.buffer.read(cx).snapshot(cx);
 7615
 7616        let anchor_range =
 7617            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 7618
 7619        editor.highlight_background::<Type1>(
 7620            &[
 7621                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 7622                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 7623                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 7624                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 7625            ],
 7626            |_| Hsla::red(),
 7627            cx,
 7628        );
 7629        editor.highlight_background::<Type2>(
 7630            &[
 7631                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 7632                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 7633                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 7634                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 7635            ],
 7636            |_| Hsla::green(),
 7637            cx,
 7638        );
 7639
 7640        let snapshot = editor.snapshot(cx);
 7641        let mut highlighted_ranges = editor.background_highlights_in_range(
 7642            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 7643            &snapshot,
 7644            cx.theme().colors(),
 7645        );
 7646        // Enforce a consistent ordering based on color without relying on the ordering of the
 7647        // highlight's `TypeId` which is non-executor.
 7648        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 7649        assert_eq!(
 7650            highlighted_ranges,
 7651            &[
 7652                (
 7653                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 7654                    Hsla::red(),
 7655                ),
 7656                (
 7657                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 7658                    Hsla::red(),
 7659                ),
 7660                (
 7661                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 7662                    Hsla::green(),
 7663                ),
 7664                (
 7665                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 7666                    Hsla::green(),
 7667                ),
 7668            ]
 7669        );
 7670        assert_eq!(
 7671            editor.background_highlights_in_range(
 7672                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 7673                &snapshot,
 7674                cx.theme().colors(),
 7675            ),
 7676            &[(
 7677                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 7678                Hsla::red(),
 7679            )]
 7680        );
 7681    });
 7682}
 7683
 7684#[gpui::test]
 7685async fn test_following(cx: &mut gpui::TestAppContext) {
 7686    init_test(cx, |_| {});
 7687
 7688    let fs = FakeFs::new(cx.executor());
 7689    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7690
 7691    let buffer = project.update(cx, |project, cx| {
 7692        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 7693        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 7694    });
 7695    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 7696    let follower = cx.update(|cx| {
 7697        cx.open_window(
 7698            WindowOptions {
 7699                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 7700                    gpui::Point::new(px(0.), px(0.)),
 7701                    gpui::Point::new(px(10.), px(80.)),
 7702                ))),
 7703                ..Default::default()
 7704            },
 7705            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 7706        )
 7707        .unwrap()
 7708    });
 7709
 7710    let is_still_following = Rc::new(RefCell::new(true));
 7711    let follower_edit_event_count = Rc::new(RefCell::new(0));
 7712    let pending_update = Rc::new(RefCell::new(None));
 7713    _ = follower.update(cx, {
 7714        let update = pending_update.clone();
 7715        let is_still_following = is_still_following.clone();
 7716        let follower_edit_event_count = follower_edit_event_count.clone();
 7717        |_, cx| {
 7718            cx.subscribe(
 7719                &leader.root_view(cx).unwrap(),
 7720                move |_, leader, event, cx| {
 7721                    leader
 7722                        .read(cx)
 7723                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 7724                },
 7725            )
 7726            .detach();
 7727
 7728            cx.subscribe(
 7729                &follower.root_view(cx).unwrap(),
 7730                move |_, _, event: &EditorEvent, _cx| {
 7731                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 7732                        *is_still_following.borrow_mut() = false;
 7733                    }
 7734
 7735                    if let EditorEvent::BufferEdited = event {
 7736                        *follower_edit_event_count.borrow_mut() += 1;
 7737                    }
 7738                },
 7739            )
 7740            .detach();
 7741        }
 7742    });
 7743
 7744    // Update the selections only
 7745    _ = leader.update(cx, |leader, cx| {
 7746        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 7747    });
 7748    follower
 7749        .update(cx, |follower, cx| {
 7750            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7751        })
 7752        .unwrap()
 7753        .await
 7754        .unwrap();
 7755    _ = follower.update(cx, |follower, cx| {
 7756        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 7757    });
 7758    assert_eq!(*is_still_following.borrow(), true);
 7759    assert_eq!(*follower_edit_event_count.borrow(), 0);
 7760
 7761    // Update the scroll position only
 7762    _ = leader.update(cx, |leader, cx| {
 7763        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 7764    });
 7765    follower
 7766        .update(cx, |follower, cx| {
 7767            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7768        })
 7769        .unwrap()
 7770        .await
 7771        .unwrap();
 7772    assert_eq!(
 7773        follower
 7774            .update(cx, |follower, cx| follower.scroll_position(cx))
 7775            .unwrap(),
 7776        gpui::Point::new(1.5, 3.5)
 7777    );
 7778    assert_eq!(*is_still_following.borrow(), true);
 7779    assert_eq!(*follower_edit_event_count.borrow(), 0);
 7780
 7781    // Update the selections and scroll position. The follower's scroll position is updated
 7782    // via autoscroll, not via the leader's exact scroll position.
 7783    _ = leader.update(cx, |leader, cx| {
 7784        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7785        leader.request_autoscroll(Autoscroll::newest(), cx);
 7786        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 7787    });
 7788    follower
 7789        .update(cx, |follower, cx| {
 7790            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7791        })
 7792        .unwrap()
 7793        .await
 7794        .unwrap();
 7795    _ = follower.update(cx, |follower, cx| {
 7796        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 7797        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 7798    });
 7799    assert_eq!(*is_still_following.borrow(), true);
 7800
 7801    // Creating a pending selection that precedes another selection
 7802    _ = leader.update(cx, |leader, cx| {
 7803        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 7804        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 7805    });
 7806    follower
 7807        .update(cx, |follower, cx| {
 7808            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7809        })
 7810        .unwrap()
 7811        .await
 7812        .unwrap();
 7813    _ = follower.update(cx, |follower, cx| {
 7814        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 7815    });
 7816    assert_eq!(*is_still_following.borrow(), true);
 7817
 7818    // Extend the pending selection so that it surrounds another selection
 7819    _ = leader.update(cx, |leader, cx| {
 7820        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 7821    });
 7822    follower
 7823        .update(cx, |follower, cx| {
 7824            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7825        })
 7826        .unwrap()
 7827        .await
 7828        .unwrap();
 7829    _ = follower.update(cx, |follower, cx| {
 7830        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 7831    });
 7832
 7833    // Scrolling locally breaks the follow
 7834    _ = follower.update(cx, |follower, cx| {
 7835        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 7836        follower.set_scroll_anchor(
 7837            ScrollAnchor {
 7838                anchor: top_anchor,
 7839                offset: gpui::Point::new(0.0, 0.5),
 7840            },
 7841            cx,
 7842        );
 7843    });
 7844    assert_eq!(*is_still_following.borrow(), false);
 7845}
 7846
 7847#[gpui::test]
 7848async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 7849    init_test(cx, |_| {});
 7850
 7851    let fs = FakeFs::new(cx.executor());
 7852    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7853    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 7854    let pane = workspace
 7855        .update(cx, |workspace, _| workspace.active_pane().clone())
 7856        .unwrap();
 7857
 7858    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7859
 7860    let leader = pane.update(cx, |_, cx| {
 7861        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 7862        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 7863    });
 7864
 7865    // Start following the editor when it has no excerpts.
 7866    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 7867    let follower_1 = cx
 7868        .update_window(*workspace.deref(), |_, cx| {
 7869            Editor::from_state_proto(
 7870                pane.clone(),
 7871                workspace.root_view(cx).unwrap(),
 7872                ViewId {
 7873                    creator: Default::default(),
 7874                    id: 0,
 7875                },
 7876                &mut state_message,
 7877                cx,
 7878            )
 7879        })
 7880        .unwrap()
 7881        .unwrap()
 7882        .await
 7883        .unwrap();
 7884
 7885    let update_message = Rc::new(RefCell::new(None));
 7886    follower_1.update(cx, {
 7887        let update = update_message.clone();
 7888        |_, cx| {
 7889            cx.subscribe(&leader, move |_, leader, event, cx| {
 7890                leader
 7891                    .read(cx)
 7892                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 7893            })
 7894            .detach();
 7895        }
 7896    });
 7897
 7898    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 7899        (
 7900            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 7901            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 7902        )
 7903    });
 7904
 7905    // Insert some excerpts.
 7906    _ = leader.update(cx, |leader, cx| {
 7907        leader.buffer.update(cx, |multibuffer, cx| {
 7908            let excerpt_ids = multibuffer.push_excerpts(
 7909                buffer_1.clone(),
 7910                [
 7911                    ExcerptRange {
 7912                        context: 1..6,
 7913                        primary: None,
 7914                    },
 7915                    ExcerptRange {
 7916                        context: 12..15,
 7917                        primary: None,
 7918                    },
 7919                    ExcerptRange {
 7920                        context: 0..3,
 7921                        primary: None,
 7922                    },
 7923                ],
 7924                cx,
 7925            );
 7926            multibuffer.insert_excerpts_after(
 7927                excerpt_ids[0],
 7928                buffer_2.clone(),
 7929                [
 7930                    ExcerptRange {
 7931                        context: 8..12,
 7932                        primary: None,
 7933                    },
 7934                    ExcerptRange {
 7935                        context: 0..6,
 7936                        primary: None,
 7937                    },
 7938                ],
 7939                cx,
 7940            );
 7941        });
 7942    });
 7943
 7944    // Apply the update of adding the excerpts.
 7945    follower_1
 7946        .update(cx, |follower, cx| {
 7947            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 7948        })
 7949        .await
 7950        .unwrap();
 7951    assert_eq!(
 7952        follower_1.update(cx, |editor, cx| editor.text(cx)),
 7953        leader.update(cx, |editor, cx| editor.text(cx))
 7954    );
 7955    update_message.borrow_mut().take();
 7956
 7957    // Start following separately after it already has excerpts.
 7958    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 7959    let follower_2 = cx
 7960        .update_window(*workspace.deref(), |_, cx| {
 7961            Editor::from_state_proto(
 7962                pane.clone(),
 7963                workspace.root_view(cx).unwrap().clone(),
 7964                ViewId {
 7965                    creator: Default::default(),
 7966                    id: 0,
 7967                },
 7968                &mut state_message,
 7969                cx,
 7970            )
 7971        })
 7972        .unwrap()
 7973        .unwrap()
 7974        .await
 7975        .unwrap();
 7976    assert_eq!(
 7977        follower_2.update(cx, |editor, cx| editor.text(cx)),
 7978        leader.update(cx, |editor, cx| editor.text(cx))
 7979    );
 7980
 7981    // Remove some excerpts.
 7982    _ = leader.update(cx, |leader, cx| {
 7983        leader.buffer.update(cx, |multibuffer, cx| {
 7984            let excerpt_ids = multibuffer.excerpt_ids();
 7985            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 7986            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 7987        });
 7988    });
 7989
 7990    // Apply the update of removing the excerpts.
 7991    follower_1
 7992        .update(cx, |follower, cx| {
 7993            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 7994        })
 7995        .await
 7996        .unwrap();
 7997    follower_2
 7998        .update(cx, |follower, cx| {
 7999            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8000        })
 8001        .await
 8002        .unwrap();
 8003    update_message.borrow_mut().take();
 8004    assert_eq!(
 8005        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8006        leader.update(cx, |editor, cx| editor.text(cx))
 8007    );
 8008}
 8009
 8010#[gpui::test]
 8011async fn go_to_prev_overlapping_diagnostic(
 8012    executor: BackgroundExecutor,
 8013    cx: &mut gpui::TestAppContext,
 8014) {
 8015    init_test(cx, |_| {});
 8016
 8017    let mut cx = EditorTestContext::new(cx).await;
 8018    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 8019
 8020    cx.set_state(indoc! {"
 8021        ˇfn func(abc def: i32) -> u32 {
 8022        }
 8023    "});
 8024
 8025    _ = cx.update(|cx| {
 8026        _ = project.update(cx, |project, cx| {
 8027            project
 8028                .update_diagnostics(
 8029                    LanguageServerId(0),
 8030                    lsp::PublishDiagnosticsParams {
 8031                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 8032                        version: None,
 8033                        diagnostics: vec![
 8034                            lsp::Diagnostic {
 8035                                range: lsp::Range::new(
 8036                                    lsp::Position::new(0, 11),
 8037                                    lsp::Position::new(0, 12),
 8038                                ),
 8039                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8040                                ..Default::default()
 8041                            },
 8042                            lsp::Diagnostic {
 8043                                range: lsp::Range::new(
 8044                                    lsp::Position::new(0, 12),
 8045                                    lsp::Position::new(0, 15),
 8046                                ),
 8047                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8048                                ..Default::default()
 8049                            },
 8050                            lsp::Diagnostic {
 8051                                range: lsp::Range::new(
 8052                                    lsp::Position::new(0, 25),
 8053                                    lsp::Position::new(0, 28),
 8054                                ),
 8055                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8056                                ..Default::default()
 8057                            },
 8058                        ],
 8059                    },
 8060                    &[],
 8061                    cx,
 8062                )
 8063                .unwrap()
 8064        });
 8065    });
 8066
 8067    executor.run_until_parked();
 8068
 8069    cx.update_editor(|editor, cx| {
 8070        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 8071    });
 8072
 8073    cx.assert_editor_state(indoc! {"
 8074        fn func(abc def: i32) -> ˇu32 {
 8075        }
 8076    "});
 8077
 8078    cx.update_editor(|editor, cx| {
 8079        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 8080    });
 8081
 8082    cx.assert_editor_state(indoc! {"
 8083        fn func(abc ˇdef: i32) -> u32 {
 8084        }
 8085    "});
 8086
 8087    cx.update_editor(|editor, cx| {
 8088        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 8089    });
 8090
 8091    cx.assert_editor_state(indoc! {"
 8092        fn func(abcˇ def: i32) -> u32 {
 8093        }
 8094    "});
 8095
 8096    cx.update_editor(|editor, cx| {
 8097        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 8098    });
 8099
 8100    cx.assert_editor_state(indoc! {"
 8101        fn func(abc def: i32) -> ˇu32 {
 8102        }
 8103    "});
 8104}
 8105
 8106#[gpui::test]
 8107async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 8108    init_test(cx, |_| {});
 8109
 8110    let mut cx = EditorTestContext::new(cx).await;
 8111
 8112    let diff_base = r#"
 8113        use some::mod;
 8114
 8115        const A: u32 = 42;
 8116
 8117        fn main() {
 8118            println!("hello");
 8119
 8120            println!("world");
 8121        }
 8122        "#
 8123    .unindent();
 8124
 8125    // Edits are modified, removed, modified, added
 8126    cx.set_state(
 8127        &r#"
 8128        use some::modified;
 8129
 8130        ˇ
 8131        fn main() {
 8132            println!("hello there");
 8133
 8134            println!("around the");
 8135            println!("world");
 8136        }
 8137        "#
 8138        .unindent(),
 8139    );
 8140
 8141    cx.set_diff_base(Some(&diff_base));
 8142    executor.run_until_parked();
 8143
 8144    cx.update_editor(|editor, cx| {
 8145        //Wrap around the bottom of the buffer
 8146        for _ in 0..3 {
 8147            editor.go_to_hunk(&GoToHunk, cx);
 8148        }
 8149    });
 8150
 8151    cx.assert_editor_state(
 8152        &r#"
 8153        ˇuse some::modified;
 8154
 8155
 8156        fn main() {
 8157            println!("hello there");
 8158
 8159            println!("around the");
 8160            println!("world");
 8161        }
 8162        "#
 8163        .unindent(),
 8164    );
 8165
 8166    cx.update_editor(|editor, cx| {
 8167        //Wrap around the top of the buffer
 8168        for _ in 0..2 {
 8169            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 8170        }
 8171    });
 8172
 8173    cx.assert_editor_state(
 8174        &r#"
 8175        use some::modified;
 8176
 8177
 8178        fn main() {
 8179        ˇ    println!("hello there");
 8180
 8181            println!("around the");
 8182            println!("world");
 8183        }
 8184        "#
 8185        .unindent(),
 8186    );
 8187
 8188    cx.update_editor(|editor, cx| {
 8189        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 8190    });
 8191
 8192    cx.assert_editor_state(
 8193        &r#"
 8194        use some::modified;
 8195
 8196        ˇ
 8197        fn main() {
 8198            println!("hello there");
 8199
 8200            println!("around the");
 8201            println!("world");
 8202        }
 8203        "#
 8204        .unindent(),
 8205    );
 8206
 8207    cx.update_editor(|editor, cx| {
 8208        for _ in 0..3 {
 8209            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 8210        }
 8211    });
 8212
 8213    cx.assert_editor_state(
 8214        &r#"
 8215        use some::modified;
 8216
 8217
 8218        fn main() {
 8219        ˇ    println!("hello there");
 8220
 8221            println!("around the");
 8222            println!("world");
 8223        }
 8224        "#
 8225        .unindent(),
 8226    );
 8227
 8228    cx.update_editor(|editor, cx| {
 8229        editor.fold(&Fold, cx);
 8230
 8231        //Make sure that the fold only gets one hunk
 8232        for _ in 0..4 {
 8233            editor.go_to_hunk(&GoToHunk, cx);
 8234        }
 8235    });
 8236
 8237    cx.assert_editor_state(
 8238        &r#"
 8239        ˇuse some::modified;
 8240
 8241
 8242        fn main() {
 8243            println!("hello there");
 8244
 8245            println!("around the");
 8246            println!("world");
 8247        }
 8248        "#
 8249        .unindent(),
 8250    );
 8251}
 8252
 8253#[test]
 8254fn test_split_words() {
 8255    fn split(text: &str) -> Vec<&str> {
 8256        split_words(text).collect()
 8257    }
 8258
 8259    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 8260    assert_eq!(split("hello_world"), &["hello_", "world"]);
 8261    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 8262    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 8263    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 8264    assert_eq!(split("helloworld"), &["helloworld"]);
 8265
 8266    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 8267}
 8268
 8269#[gpui::test]
 8270async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 8271    init_test(cx, |_| {});
 8272
 8273    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 8274    let mut assert = |before, after| {
 8275        let _state_context = cx.set_state(before);
 8276        cx.update_editor(|editor, cx| {
 8277            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 8278        });
 8279        cx.assert_editor_state(after);
 8280    };
 8281
 8282    // Outside bracket jumps to outside of matching bracket
 8283    assert("console.logˇ(var);", "console.log(var)ˇ;");
 8284    assert("console.log(var)ˇ;", "console.logˇ(var);");
 8285
 8286    // Inside bracket jumps to inside of matching bracket
 8287    assert("console.log(ˇvar);", "console.log(varˇ);");
 8288    assert("console.log(varˇ);", "console.log(ˇvar);");
 8289
 8290    // When outside a bracket and inside, favor jumping to the inside bracket
 8291    assert(
 8292        "console.log('foo', [1, 2, 3]ˇ);",
 8293        "console.log(ˇ'foo', [1, 2, 3]);",
 8294    );
 8295    assert(
 8296        "console.log(ˇ'foo', [1, 2, 3]);",
 8297        "console.log('foo', [1, 2, 3]ˇ);",
 8298    );
 8299
 8300    // Bias forward if two options are equally likely
 8301    assert(
 8302        "let result = curried_fun()ˇ();",
 8303        "let result = curried_fun()()ˇ;",
 8304    );
 8305
 8306    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 8307    assert(
 8308        indoc! {"
 8309            function test() {
 8310                console.log('test')ˇ
 8311            }"},
 8312        indoc! {"
 8313            function test() {
 8314                console.logˇ('test')
 8315            }"},
 8316    );
 8317}
 8318
 8319#[gpui::test]
 8320async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 8321    init_test(cx, |_| {});
 8322
 8323    let fs = FakeFs::new(cx.executor());
 8324    fs.insert_tree(
 8325        "/a",
 8326        json!({
 8327            "main.rs": "fn main() { let a = 5; }",
 8328            "other.rs": "// Test file",
 8329        }),
 8330    )
 8331    .await;
 8332    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8333
 8334    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8335    language_registry.add(Arc::new(Language::new(
 8336        LanguageConfig {
 8337            name: "Rust".into(),
 8338            matcher: LanguageMatcher {
 8339                path_suffixes: vec!["rs".to_string()],
 8340                ..Default::default()
 8341            },
 8342            brackets: BracketPairConfig {
 8343                pairs: vec![BracketPair {
 8344                    start: "{".to_string(),
 8345                    end: "}".to_string(),
 8346                    close: true,
 8347                    newline: true,
 8348                }],
 8349                disabled_scopes_by_bracket_ix: Vec::new(),
 8350            },
 8351            ..Default::default()
 8352        },
 8353        Some(tree_sitter_rust::language()),
 8354    )));
 8355    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 8356        "Rust",
 8357        FakeLspAdapter {
 8358            capabilities: lsp::ServerCapabilities {
 8359                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 8360                    first_trigger_character: "{".to_string(),
 8361                    more_trigger_character: None,
 8362                }),
 8363                ..Default::default()
 8364            },
 8365            ..Default::default()
 8366        },
 8367    );
 8368
 8369    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8370
 8371    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8372
 8373    let worktree_id = workspace
 8374        .update(cx, |workspace, cx| {
 8375            workspace.project().update(cx, |project, cx| {
 8376                project.worktrees().next().unwrap().read(cx).id()
 8377            })
 8378        })
 8379        .unwrap();
 8380
 8381    let buffer = project
 8382        .update(cx, |project, cx| {
 8383            project.open_local_buffer("/a/main.rs", cx)
 8384        })
 8385        .await
 8386        .unwrap();
 8387    cx.executor().run_until_parked();
 8388    cx.executor().start_waiting();
 8389    let fake_server = fake_servers.next().await.unwrap();
 8390    let editor_handle = workspace
 8391        .update(cx, |workspace, cx| {
 8392            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 8393        })
 8394        .unwrap()
 8395        .await
 8396        .unwrap()
 8397        .downcast::<Editor>()
 8398        .unwrap();
 8399
 8400    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 8401        assert_eq!(
 8402            params.text_document_position.text_document.uri,
 8403            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 8404        );
 8405        assert_eq!(
 8406            params.text_document_position.position,
 8407            lsp::Position::new(0, 21),
 8408        );
 8409
 8410        Ok(Some(vec![lsp::TextEdit {
 8411            new_text: "]".to_string(),
 8412            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 8413        }]))
 8414    });
 8415
 8416    editor_handle.update(cx, |editor, cx| {
 8417        editor.focus(cx);
 8418        editor.change_selections(None, cx, |s| {
 8419            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 8420        });
 8421        editor.handle_input("{", cx);
 8422    });
 8423
 8424    cx.executor().run_until_parked();
 8425
 8426    _ = buffer.update(cx, |buffer, _| {
 8427        assert_eq!(
 8428            buffer.text(),
 8429            "fn main() { let a = {5}; }",
 8430            "No extra braces from on type formatting should appear in the buffer"
 8431        )
 8432    });
 8433}
 8434
 8435#[gpui::test]
 8436async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 8437    init_test(cx, |_| {});
 8438
 8439    let fs = FakeFs::new(cx.executor());
 8440    fs.insert_tree(
 8441        "/a",
 8442        json!({
 8443            "main.rs": "fn main() { let a = 5; }",
 8444            "other.rs": "// Test file",
 8445        }),
 8446    )
 8447    .await;
 8448
 8449    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8450
 8451    let server_restarts = Arc::new(AtomicUsize::new(0));
 8452    let closure_restarts = Arc::clone(&server_restarts);
 8453    let language_server_name = "test language server";
 8454    let language_name: Arc<str> = "Rust".into();
 8455
 8456    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8457    language_registry.add(Arc::new(Language::new(
 8458        LanguageConfig {
 8459            name: Arc::clone(&language_name),
 8460            matcher: LanguageMatcher {
 8461                path_suffixes: vec!["rs".to_string()],
 8462                ..Default::default()
 8463            },
 8464            ..Default::default()
 8465        },
 8466        Some(tree_sitter_rust::language()),
 8467    )));
 8468    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 8469        "Rust",
 8470        FakeLspAdapter {
 8471            name: language_server_name,
 8472            initialization_options: Some(json!({
 8473                "testOptionValue": true
 8474            })),
 8475            initializer: Some(Box::new(move |fake_server| {
 8476                let task_restarts = Arc::clone(&closure_restarts);
 8477                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 8478                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 8479                    futures::future::ready(Ok(()))
 8480                });
 8481            })),
 8482            ..Default::default()
 8483        },
 8484    );
 8485
 8486    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8487    let _buffer = project
 8488        .update(cx, |project, cx| {
 8489            project.open_local_buffer("/a/main.rs", cx)
 8490        })
 8491        .await
 8492        .unwrap();
 8493    let _fake_server = fake_servers.next().await.unwrap();
 8494    update_test_language_settings(cx, |language_settings| {
 8495        language_settings.languages.insert(
 8496            Arc::clone(&language_name),
 8497            LanguageSettingsContent {
 8498                tab_size: NonZeroU32::new(8),
 8499                ..Default::default()
 8500            },
 8501        );
 8502    });
 8503    cx.executor().run_until_parked();
 8504    assert_eq!(
 8505        server_restarts.load(atomic::Ordering::Acquire),
 8506        0,
 8507        "Should not restart LSP server on an unrelated change"
 8508    );
 8509
 8510    update_test_project_settings(cx, |project_settings| {
 8511        project_settings.lsp.insert(
 8512            "Some other server name".into(),
 8513            LspSettings {
 8514                binary: None,
 8515                settings: None,
 8516                initialization_options: Some(json!({
 8517                    "some other init value": false
 8518                })),
 8519            },
 8520        );
 8521    });
 8522    cx.executor().run_until_parked();
 8523    assert_eq!(
 8524        server_restarts.load(atomic::Ordering::Acquire),
 8525        0,
 8526        "Should not restart LSP server on an unrelated LSP settings change"
 8527    );
 8528
 8529    update_test_project_settings(cx, |project_settings| {
 8530        project_settings.lsp.insert(
 8531            language_server_name.into(),
 8532            LspSettings {
 8533                binary: None,
 8534                settings: None,
 8535                initialization_options: Some(json!({
 8536                    "anotherInitValue": false
 8537                })),
 8538            },
 8539        );
 8540    });
 8541    cx.executor().run_until_parked();
 8542    assert_eq!(
 8543        server_restarts.load(atomic::Ordering::Acquire),
 8544        1,
 8545        "Should restart LSP server on a related LSP settings change"
 8546    );
 8547
 8548    update_test_project_settings(cx, |project_settings| {
 8549        project_settings.lsp.insert(
 8550            language_server_name.into(),
 8551            LspSettings {
 8552                binary: None,
 8553                settings: None,
 8554                initialization_options: Some(json!({
 8555                    "anotherInitValue": false
 8556                })),
 8557            },
 8558        );
 8559    });
 8560    cx.executor().run_until_parked();
 8561    assert_eq!(
 8562        server_restarts.load(atomic::Ordering::Acquire),
 8563        1,
 8564        "Should not restart LSP server on a related LSP settings change that is the same"
 8565    );
 8566
 8567    update_test_project_settings(cx, |project_settings| {
 8568        project_settings.lsp.insert(
 8569            language_server_name.into(),
 8570            LspSettings {
 8571                binary: None,
 8572                settings: None,
 8573                initialization_options: None,
 8574            },
 8575        );
 8576    });
 8577    cx.executor().run_until_parked();
 8578    assert_eq!(
 8579        server_restarts.load(atomic::Ordering::Acquire),
 8580        2,
 8581        "Should restart LSP server on another related LSP settings change"
 8582    );
 8583}
 8584
 8585#[gpui::test]
 8586async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 8587    init_test(cx, |_| {});
 8588
 8589    let mut cx = EditorLspTestContext::new_rust(
 8590        lsp::ServerCapabilities {
 8591            completion_provider: Some(lsp::CompletionOptions {
 8592                trigger_characters: Some(vec![".".to_string()]),
 8593                resolve_provider: Some(true),
 8594                ..Default::default()
 8595            }),
 8596            ..Default::default()
 8597        },
 8598        cx,
 8599    )
 8600    .await;
 8601
 8602    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8603    cx.simulate_keystroke(".");
 8604    let completion_item = lsp::CompletionItem {
 8605        label: "some".into(),
 8606        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8607        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8608        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8609            kind: lsp::MarkupKind::Markdown,
 8610            value: "```rust\nSome(2)\n```".to_string(),
 8611        })),
 8612        deprecated: Some(false),
 8613        sort_text: Some("fffffff2".to_string()),
 8614        filter_text: Some("some".to_string()),
 8615        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8616        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8617            range: lsp::Range {
 8618                start: lsp::Position {
 8619                    line: 0,
 8620                    character: 22,
 8621                },
 8622                end: lsp::Position {
 8623                    line: 0,
 8624                    character: 22,
 8625                },
 8626            },
 8627            new_text: "Some(2)".to_string(),
 8628        })),
 8629        additional_text_edits: Some(vec![lsp::TextEdit {
 8630            range: lsp::Range {
 8631                start: lsp::Position {
 8632                    line: 0,
 8633                    character: 20,
 8634                },
 8635                end: lsp::Position {
 8636                    line: 0,
 8637                    character: 22,
 8638                },
 8639            },
 8640            new_text: "".to_string(),
 8641        }]),
 8642        ..Default::default()
 8643    };
 8644
 8645    let closure_completion_item = completion_item.clone();
 8646    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8647        let task_completion_item = closure_completion_item.clone();
 8648        async move {
 8649            Ok(Some(lsp::CompletionResponse::Array(vec![
 8650                task_completion_item,
 8651            ])))
 8652        }
 8653    });
 8654
 8655    request.next().await;
 8656
 8657    cx.condition(|editor, _| editor.context_menu_visible())
 8658        .await;
 8659    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8660        editor
 8661            .confirm_completion(&ConfirmCompletion::default(), cx)
 8662            .unwrap()
 8663    });
 8664    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 8665
 8666    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 8667        let task_completion_item = completion_item.clone();
 8668        async move { Ok(task_completion_item) }
 8669    })
 8670    .next()
 8671    .await
 8672    .unwrap();
 8673    apply_additional_edits.await.unwrap();
 8674    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 8675}
 8676
 8677#[gpui::test]
 8678async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 8679    init_test(cx, |_| {});
 8680
 8681    let mut cx = EditorLspTestContext::new(
 8682        Language::new(
 8683            LanguageConfig {
 8684                matcher: LanguageMatcher {
 8685                    path_suffixes: vec!["jsx".into()],
 8686                    ..Default::default()
 8687                },
 8688                overrides: [(
 8689                    "element".into(),
 8690                    LanguageConfigOverride {
 8691                        word_characters: Override::Set(['-'].into_iter().collect()),
 8692                        ..Default::default()
 8693                    },
 8694                )]
 8695                .into_iter()
 8696                .collect(),
 8697                ..Default::default()
 8698            },
 8699            Some(tree_sitter_typescript::language_tsx()),
 8700        )
 8701        .with_override_query("(jsx_self_closing_element) @element")
 8702        .unwrap(),
 8703        lsp::ServerCapabilities {
 8704            completion_provider: Some(lsp::CompletionOptions {
 8705                trigger_characters: Some(vec![":".to_string()]),
 8706                ..Default::default()
 8707            }),
 8708            ..Default::default()
 8709        },
 8710        cx,
 8711    )
 8712    .await;
 8713
 8714    cx.lsp
 8715        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8716            Ok(Some(lsp::CompletionResponse::Array(vec![
 8717                lsp::CompletionItem {
 8718                    label: "bg-blue".into(),
 8719                    ..Default::default()
 8720                },
 8721                lsp::CompletionItem {
 8722                    label: "bg-red".into(),
 8723                    ..Default::default()
 8724                },
 8725                lsp::CompletionItem {
 8726                    label: "bg-yellow".into(),
 8727                    ..Default::default()
 8728                },
 8729            ])))
 8730        });
 8731
 8732    cx.set_state(r#"<p class="bgˇ" />"#);
 8733
 8734    // Trigger completion when typing a dash, because the dash is an extra
 8735    // word character in the 'element' scope, which contains the cursor.
 8736    cx.simulate_keystroke("-");
 8737    cx.executor().run_until_parked();
 8738    cx.update_editor(|editor, _| {
 8739        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8740            assert_eq!(
 8741                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8742                &["bg-red", "bg-blue", "bg-yellow"]
 8743            );
 8744        } else {
 8745            panic!("expected completion menu to be open");
 8746        }
 8747    });
 8748
 8749    cx.simulate_keystroke("l");
 8750    cx.executor().run_until_parked();
 8751    cx.update_editor(|editor, _| {
 8752        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8753            assert_eq!(
 8754                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8755                &["bg-blue", "bg-yellow"]
 8756            );
 8757        } else {
 8758            panic!("expected completion menu to be open");
 8759        }
 8760    });
 8761
 8762    // When filtering completions, consider the character after the '-' to
 8763    // be the start of a subword.
 8764    cx.set_state(r#"<p class="yelˇ" />"#);
 8765    cx.simulate_keystroke("l");
 8766    cx.executor().run_until_parked();
 8767    cx.update_editor(|editor, _| {
 8768        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8769            assert_eq!(
 8770                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8771                &["bg-yellow"]
 8772            );
 8773        } else {
 8774            panic!("expected completion menu to be open");
 8775        }
 8776    });
 8777}
 8778
 8779#[gpui::test]
 8780async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 8781    init_test(cx, |settings| {
 8782        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
 8783    });
 8784
 8785    let fs = FakeFs::new(cx.executor());
 8786    fs.insert_file("/file.ts", Default::default()).await;
 8787
 8788    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
 8789    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8790
 8791    language_registry.add(Arc::new(Language::new(
 8792        LanguageConfig {
 8793            name: "TypeScript".into(),
 8794            matcher: LanguageMatcher {
 8795                path_suffixes: vec!["ts".to_string()],
 8796                ..Default::default()
 8797            },
 8798            ..Default::default()
 8799        },
 8800        Some(tree_sitter_rust::language()),
 8801    )));
 8802    update_test_language_settings(cx, |settings| {
 8803        settings.defaults.prettier = Some(PrettierSettings {
 8804            allowed: true,
 8805            ..PrettierSettings::default()
 8806        });
 8807    });
 8808
 8809    let test_plugin = "test_plugin";
 8810    let _ = language_registry.register_fake_lsp_adapter(
 8811        "TypeScript",
 8812        FakeLspAdapter {
 8813            prettier_plugins: vec![test_plugin],
 8814            ..Default::default()
 8815        },
 8816    );
 8817
 8818    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 8819    let buffer = project
 8820        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
 8821        .await
 8822        .unwrap();
 8823
 8824    let buffer_text = "one\ntwo\nthree\n";
 8825    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8826    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8827    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 8828
 8829    editor
 8830        .update(cx, |editor, cx| {
 8831            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 8832        })
 8833        .unwrap()
 8834        .await;
 8835    assert_eq!(
 8836        editor.update(cx, |editor, cx| editor.text(cx)),
 8837        buffer_text.to_string() + prettier_format_suffix,
 8838        "Test prettier formatting was not applied to the original buffer text",
 8839    );
 8840
 8841    update_test_language_settings(cx, |settings| {
 8842        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 8843    });
 8844    let format = editor.update(cx, |editor, cx| {
 8845        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 8846    });
 8847    format.await.unwrap();
 8848    assert_eq!(
 8849        editor.update(cx, |editor, cx| editor.text(cx)),
 8850        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 8851        "Autoformatting (via test prettier) was not applied to the original buffer text",
 8852    );
 8853}
 8854
 8855#[gpui::test]
 8856async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 8857    init_test(cx, |_| {});
 8858    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 8859    let base_text = indoc! {r#"struct Row;
 8860struct Row1;
 8861struct Row2;
 8862
 8863struct Row4;
 8864struct Row5;
 8865struct Row6;
 8866
 8867struct Row8;
 8868struct Row9;
 8869struct Row10;"#};
 8870
 8871    // When addition hunks are not adjacent to carets, no hunk revert is performed
 8872    assert_hunk_revert(
 8873        indoc! {r#"struct Row;
 8874                   struct Row1;
 8875                   struct Row1.1;
 8876                   struct Row1.2;
 8877                   struct Row2;ˇ
 8878
 8879                   struct Row4;
 8880                   struct Row5;
 8881                   struct Row6;
 8882
 8883                   struct Row8;
 8884                   ˇstruct Row9;
 8885                   struct Row9.1;
 8886                   struct Row9.2;
 8887                   struct Row9.3;
 8888                   struct Row10;"#},
 8889        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 8890        indoc! {r#"struct Row;
 8891                   struct Row1;
 8892                   struct Row1.1;
 8893                   struct Row1.2;
 8894                   struct Row2;ˇ
 8895
 8896                   struct Row4;
 8897                   struct Row5;
 8898                   struct Row6;
 8899
 8900                   struct Row8;
 8901                   ˇstruct Row9;
 8902                   struct Row9.1;
 8903                   struct Row9.2;
 8904                   struct Row9.3;
 8905                   struct Row10;"#},
 8906        base_text,
 8907        &mut cx,
 8908    );
 8909    // Same for selections
 8910    assert_hunk_revert(
 8911        indoc! {r#"struct Row;
 8912                   struct Row1;
 8913                   struct Row2;
 8914                   struct Row2.1;
 8915                   struct Row2.2;
 8916                   «ˇ
 8917                   struct Row4;
 8918                   struct» Row5;
 8919                   «struct Row6;
 8920                   ˇ»
 8921                   struct Row9.1;
 8922                   struct Row9.2;
 8923                   struct Row9.3;
 8924                   struct Row8;
 8925                   struct Row9;
 8926                   struct Row10;"#},
 8927        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 8928        indoc! {r#"struct Row;
 8929                   struct Row1;
 8930                   struct Row2;
 8931                   struct Row2.1;
 8932                   struct Row2.2;
 8933                   «ˇ
 8934                   struct Row4;
 8935                   struct» Row5;
 8936                   «struct Row6;
 8937                   ˇ»
 8938                   struct Row9.1;
 8939                   struct Row9.2;
 8940                   struct Row9.3;
 8941                   struct Row8;
 8942                   struct Row9;
 8943                   struct Row10;"#},
 8944        base_text,
 8945        &mut cx,
 8946    );
 8947
 8948    // When carets and selections intersect the addition hunks, those are reverted.
 8949    // Adjacent carets got merged.
 8950    assert_hunk_revert(
 8951        indoc! {r#"struct Row;
 8952                   ˇ// something on the top
 8953                   struct Row1;
 8954                   struct Row2;
 8955                   struct Roˇw3.1;
 8956                   struct Row2.2;
 8957                   struct Row2.3;ˇ
 8958
 8959                   struct Row4;
 8960                   struct ˇRow5.1;
 8961                   struct Row5.2;
 8962                   struct «Rowˇ»5.3;
 8963                   struct Row5;
 8964                   struct Row6;
 8965                   ˇ
 8966                   struct Row9.1;
 8967                   struct «Rowˇ»9.2;
 8968                   struct «ˇRow»9.3;
 8969                   struct Row8;
 8970                   struct Row9;
 8971                   «ˇ// something on bottom»
 8972                   struct Row10;"#},
 8973        vec![
 8974            DiffHunkStatus::Added,
 8975            DiffHunkStatus::Added,
 8976            DiffHunkStatus::Added,
 8977            DiffHunkStatus::Added,
 8978            DiffHunkStatus::Added,
 8979        ],
 8980        indoc! {r#"struct Row;
 8981                   ˇstruct Row1;
 8982                   struct Row2;
 8983                   ˇ
 8984                   struct Row4;
 8985                   ˇstruct Row5;
 8986                   struct Row6;
 8987                   ˇ
 8988                   ˇstruct Row8;
 8989                   struct Row9;
 8990                   ˇstruct Row10;"#},
 8991        base_text,
 8992        &mut cx,
 8993    );
 8994}
 8995
 8996#[gpui::test]
 8997async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
 8998    init_test(cx, |_| {});
 8999    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9000    let base_text = indoc! {r#"struct Row;
 9001struct Row1;
 9002struct Row2;
 9003
 9004struct Row4;
 9005struct Row5;
 9006struct Row6;
 9007
 9008struct Row8;
 9009struct Row9;
 9010struct Row10;"#};
 9011
 9012    // Modification hunks behave the same as the addition ones.
 9013    assert_hunk_revert(
 9014        indoc! {r#"struct Row;
 9015                   struct Row1;
 9016                   struct Row33;
 9017                   ˇ
 9018                   struct Row4;
 9019                   struct Row5;
 9020                   struct Row6;
 9021                   ˇ
 9022                   struct Row99;
 9023                   struct Row9;
 9024                   struct Row10;"#},
 9025        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 9026        indoc! {r#"struct Row;
 9027                   struct Row1;
 9028                   struct Row33;
 9029                   ˇ
 9030                   struct Row4;
 9031                   struct Row5;
 9032                   struct Row6;
 9033                   ˇ
 9034                   struct Row99;
 9035                   struct Row9;
 9036                   struct Row10;"#},
 9037        base_text,
 9038        &mut cx,
 9039    );
 9040    assert_hunk_revert(
 9041        indoc! {r#"struct Row;
 9042                   struct Row1;
 9043                   struct Row33;
 9044                   «ˇ
 9045                   struct Row4;
 9046                   struct» Row5;
 9047                   «struct Row6;
 9048                   ˇ»
 9049                   struct Row99;
 9050                   struct Row9;
 9051                   struct Row10;"#},
 9052        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 9053        indoc! {r#"struct Row;
 9054                   struct Row1;
 9055                   struct Row33;
 9056                   «ˇ
 9057                   struct Row4;
 9058                   struct» Row5;
 9059                   «struct Row6;
 9060                   ˇ»
 9061                   struct Row99;
 9062                   struct Row9;
 9063                   struct Row10;"#},
 9064        base_text,
 9065        &mut cx,
 9066    );
 9067
 9068    assert_hunk_revert(
 9069        indoc! {r#"ˇstruct Row1.1;
 9070                   struct Row1;
 9071                   «ˇstr»uct Row22;
 9072
 9073                   struct ˇRow44;
 9074                   struct Row5;
 9075                   struct «Rˇ»ow66;ˇ
 9076
 9077                   «struˇ»ct Row88;
 9078                   struct Row9;
 9079                   struct Row1011;ˇ"#},
 9080        vec![
 9081            DiffHunkStatus::Modified,
 9082            DiffHunkStatus::Modified,
 9083            DiffHunkStatus::Modified,
 9084            DiffHunkStatus::Modified,
 9085            DiffHunkStatus::Modified,
 9086            DiffHunkStatus::Modified,
 9087        ],
 9088        indoc! {r#"struct Row;
 9089                   ˇstruct Row1;
 9090                   struct Row2;
 9091                   ˇ
 9092                   struct Row4;
 9093                   ˇstruct Row5;
 9094                   struct Row6;
 9095                   ˇ
 9096                   struct Row8;
 9097                   ˇstruct Row9;
 9098                   struct Row10;ˇ"#},
 9099        base_text,
 9100        &mut cx,
 9101    );
 9102}
 9103
 9104#[gpui::test]
 9105async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
 9106    init_test(cx, |_| {});
 9107    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9108    let base_text = indoc! {r#"struct Row;
 9109struct Row1;
 9110struct Row2;
 9111
 9112struct Row4;
 9113struct Row5;
 9114struct Row6;
 9115
 9116struct Row8;
 9117struct Row9;
 9118struct Row10;"#};
 9119
 9120    // Deletion hunks trigger with carets on ajacent rows, so carets and selections have to stay farther to avoid the revert
 9121    assert_hunk_revert(
 9122        indoc! {r#"struct Row;
 9123                   struct Row2;
 9124
 9125                   ˇstruct Row4;
 9126                   struct Row5;
 9127                   struct Row6;
 9128                   ˇ
 9129                   struct Row8;
 9130                   struct Row10;"#},
 9131        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 9132        indoc! {r#"struct Row;
 9133                   struct Row2;
 9134
 9135                   ˇstruct Row4;
 9136                   struct Row5;
 9137                   struct Row6;
 9138                   ˇ
 9139                   struct Row8;
 9140                   struct Row10;"#},
 9141        base_text,
 9142        &mut cx,
 9143    );
 9144    assert_hunk_revert(
 9145        indoc! {r#"struct Row;
 9146                   struct Row2;
 9147
 9148                   «ˇstruct Row4;
 9149                   struct» Row5;
 9150                   «struct Row6;
 9151                   ˇ»
 9152                   struct Row8;
 9153                   struct Row10;"#},
 9154        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 9155        indoc! {r#"struct Row;
 9156                   struct Row2;
 9157
 9158                   «ˇstruct Row4;
 9159                   struct» Row5;
 9160                   «struct Row6;
 9161                   ˇ»
 9162                   struct Row8;
 9163                   struct Row10;"#},
 9164        base_text,
 9165        &mut cx,
 9166    );
 9167
 9168    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
 9169    assert_hunk_revert(
 9170        indoc! {r#"struct Row;
 9171                   ˇstruct Row2;
 9172
 9173                   struct Row4;
 9174                   struct Row5;
 9175                   struct Row6;
 9176
 9177                   struct Row8;ˇ
 9178                   struct Row10;"#},
 9179        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 9180        indoc! {r#"struct Row;
 9181                   struct Row1;
 9182                   ˇstruct Row2;
 9183
 9184                   struct Row4;
 9185                   struct Row5;
 9186                   struct Row6;
 9187
 9188                   struct Row8;ˇ
 9189                   struct Row9;
 9190                   struct Row10;"#},
 9191        base_text,
 9192        &mut cx,
 9193    );
 9194    assert_hunk_revert(
 9195        indoc! {r#"struct Row;
 9196                   struct Row2«ˇ;
 9197                   struct Row4;
 9198                   struct» Row5;
 9199                   «struct Row6;
 9200
 9201                   struct Row8;ˇ»
 9202                   struct Row10;"#},
 9203        vec![
 9204            DiffHunkStatus::Removed,
 9205            DiffHunkStatus::Removed,
 9206            DiffHunkStatus::Removed,
 9207        ],
 9208        indoc! {r#"struct Row;
 9209                   struct Row1;
 9210                   struct Row2«ˇ;
 9211
 9212                   struct Row4;
 9213                   struct» Row5;
 9214                   «struct Row6;
 9215
 9216                   struct Row8;ˇ»
 9217                   struct Row9;
 9218                   struct Row10;"#},
 9219        base_text,
 9220        &mut cx,
 9221    );
 9222}
 9223
 9224#[gpui::test]
 9225async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
 9226    init_test(cx, |_| {});
 9227
 9228    let cols = 4;
 9229    let rows = 10;
 9230    let sample_text_1 = sample_text(rows, cols, 'a');
 9231    assert_eq!(
 9232        sample_text_1,
 9233        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 9234    );
 9235    let sample_text_2 = sample_text(rows, cols, 'l');
 9236    assert_eq!(
 9237        sample_text_2,
 9238        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 9239    );
 9240    let sample_text_3 = sample_text(rows, cols, 'v');
 9241    assert_eq!(
 9242        sample_text_3,
 9243        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 9244    );
 9245
 9246    fn diff_every_buffer_row(
 9247        buffer: &Model<Buffer>,
 9248        sample_text: String,
 9249        cols: usize,
 9250        cx: &mut gpui::TestAppContext,
 9251    ) {
 9252        // revert first character in each row, creating one large diff hunk per buffer
 9253        let is_first_char = |offset: usize| offset % cols == 0;
 9254        buffer.update(cx, |buffer, cx| {
 9255            buffer.set_text(
 9256                sample_text
 9257                    .chars()
 9258                    .enumerate()
 9259                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
 9260                    .collect::<String>(),
 9261                cx,
 9262            );
 9263            buffer.set_diff_base(Some(sample_text), cx);
 9264        });
 9265        cx.executor().run_until_parked();
 9266    }
 9267
 9268    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
 9269    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
 9270
 9271    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
 9272    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
 9273
 9274    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
 9275    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
 9276
 9277    let multibuffer = cx.new_model(|cx| {
 9278        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 9279        multibuffer.push_excerpts(
 9280            buffer_1.clone(),
 9281            [
 9282                ExcerptRange {
 9283                    context: Point::new(0, 0)..Point::new(3, 0),
 9284                    primary: None,
 9285                },
 9286                ExcerptRange {
 9287                    context: Point::new(5, 0)..Point::new(7, 0),
 9288                    primary: None,
 9289                },
 9290                ExcerptRange {
 9291                    context: Point::new(9, 0)..Point::new(10, 4),
 9292                    primary: None,
 9293                },
 9294            ],
 9295            cx,
 9296        );
 9297        multibuffer.push_excerpts(
 9298            buffer_2.clone(),
 9299            [
 9300                ExcerptRange {
 9301                    context: Point::new(0, 0)..Point::new(3, 0),
 9302                    primary: None,
 9303                },
 9304                ExcerptRange {
 9305                    context: Point::new(5, 0)..Point::new(7, 0),
 9306                    primary: None,
 9307                },
 9308                ExcerptRange {
 9309                    context: Point::new(9, 0)..Point::new(10, 4),
 9310                    primary: None,
 9311                },
 9312            ],
 9313            cx,
 9314        );
 9315        multibuffer.push_excerpts(
 9316            buffer_3.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
 9334    });
 9335
 9336    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9337    editor.update(cx, |editor, cx| {
 9338        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");
 9339        editor.select_all(&SelectAll, cx);
 9340        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
 9341    });
 9342    cx.executor().run_until_parked();
 9343    // When all ranges are selected, all buffer hunks are reverted.
 9344    editor.update(cx, |editor, cx| {
 9345        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");
 9346    });
 9347    buffer_1.update(cx, |buffer, _| {
 9348        assert_eq!(buffer.text(), sample_text_1);
 9349    });
 9350    buffer_2.update(cx, |buffer, _| {
 9351        assert_eq!(buffer.text(), sample_text_2);
 9352    });
 9353    buffer_3.update(cx, |buffer, _| {
 9354        assert_eq!(buffer.text(), sample_text_3);
 9355    });
 9356
 9357    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
 9358    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
 9359    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
 9360    editor.update(cx, |editor, cx| {
 9361        editor.change_selections(None, cx, |s| {
 9362            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
 9363        });
 9364        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
 9365    });
 9366    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
 9367    // but not affect buffer_2 and its related excerpts.
 9368    editor.update(cx, |editor, cx| {
 9369        assert_eq!(
 9370            editor.text(cx),
 9371            "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"
 9372        );
 9373    });
 9374    buffer_1.update(cx, |buffer, _| {
 9375        assert_eq!(buffer.text(), sample_text_1);
 9376    });
 9377    buffer_2.update(cx, |buffer, _| {
 9378        assert_eq!(
 9379            buffer.text(),
 9380            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
 9381        );
 9382    });
 9383    buffer_3.update(cx, |buffer, _| {
 9384        assert_eq!(
 9385            buffer.text(),
 9386            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
 9387        );
 9388    });
 9389}
 9390
 9391#[gpui::test]
 9392async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
 9393    init_test(cx, |_| {});
 9394
 9395    let cols = 4;
 9396    let rows = 10;
 9397    let sample_text_1 = sample_text(rows, cols, 'a');
 9398    assert_eq!(
 9399        sample_text_1,
 9400        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 9401    );
 9402    let sample_text_2 = sample_text(rows, cols, 'l');
 9403    assert_eq!(
 9404        sample_text_2,
 9405        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 9406    );
 9407    let sample_text_3 = sample_text(rows, cols, 'v');
 9408    assert_eq!(
 9409        sample_text_3,
 9410        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 9411    );
 9412
 9413    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
 9414    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
 9415    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
 9416
 9417    let multi_buffer = cx.new_model(|cx| {
 9418        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 9419        multibuffer.push_excerpts(
 9420            buffer_1.clone(),
 9421            [
 9422                ExcerptRange {
 9423                    context: Point::new(0, 0)..Point::new(3, 0),
 9424                    primary: None,
 9425                },
 9426                ExcerptRange {
 9427                    context: Point::new(5, 0)..Point::new(7, 0),
 9428                    primary: None,
 9429                },
 9430                ExcerptRange {
 9431                    context: Point::new(9, 0)..Point::new(10, 4),
 9432                    primary: None,
 9433                },
 9434            ],
 9435            cx,
 9436        );
 9437        multibuffer.push_excerpts(
 9438            buffer_2.clone(),
 9439            [
 9440                ExcerptRange {
 9441                    context: Point::new(0, 0)..Point::new(3, 0),
 9442                    primary: None,
 9443                },
 9444                ExcerptRange {
 9445                    context: Point::new(5, 0)..Point::new(7, 0),
 9446                    primary: None,
 9447                },
 9448                ExcerptRange {
 9449                    context: Point::new(9, 0)..Point::new(10, 4),
 9450                    primary: None,
 9451                },
 9452            ],
 9453            cx,
 9454        );
 9455        multibuffer.push_excerpts(
 9456            buffer_3.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
 9474    });
 9475
 9476    let fs = FakeFs::new(cx.executor());
 9477    fs.insert_tree(
 9478        "/a",
 9479        json!({
 9480            "main.rs": sample_text_1,
 9481            "other.rs": sample_text_2,
 9482            "lib.rs": sample_text_3,
 9483        }),
 9484    )
 9485    .await;
 9486    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9487    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9488    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9489    let multi_buffer_editor = cx.new_view(|cx| {
 9490        Editor::new(
 9491            EditorMode::Full,
 9492            multi_buffer,
 9493            Some(project.clone()),
 9494            true,
 9495            cx,
 9496        )
 9497    });
 9498    let multibuffer_item_id = workspace
 9499        .update(cx, |workspace, cx| {
 9500            assert!(
 9501                workspace.active_item(cx).is_none(),
 9502                "active item should be None before the first item is added"
 9503            );
 9504            workspace.add_item_to_active_pane(Box::new(multi_buffer_editor.clone()), None, cx);
 9505            let active_item = workspace
 9506                .active_item(cx)
 9507                .expect("should have an active item after adding the multi buffer");
 9508            assert!(
 9509                !active_item.is_singleton(cx),
 9510                "A multi buffer was expected to active after adding"
 9511            );
 9512            active_item.item_id()
 9513        })
 9514        .unwrap();
 9515    cx.executor().run_until_parked();
 9516
 9517    multi_buffer_editor.update(cx, |editor, cx| {
 9518        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 9519        editor.open_excerpts(&OpenExcerpts, cx);
 9520    });
 9521    cx.executor().run_until_parked();
 9522    let first_item_id = workspace
 9523        .update(cx, |workspace, cx| {
 9524            let active_item = workspace
 9525                .active_item(cx)
 9526                .expect("should have an active item after navigating into the 1st buffer");
 9527            let first_item_id = active_item.item_id();
 9528            assert_ne!(
 9529                first_item_id, multibuffer_item_id,
 9530                "Should navigate into the 1st buffer and activate it"
 9531            );
 9532            assert!(
 9533                active_item.is_singleton(cx),
 9534                "New active item should be a singleton buffer"
 9535            );
 9536            assert_eq!(
 9537                active_item
 9538                    .act_as::<Editor>(cx)
 9539                    .expect("should have navigated into an editor for the 1st buffer")
 9540                    .read(cx)
 9541                    .text(cx),
 9542                sample_text_1
 9543            );
 9544
 9545            workspace
 9546                .go_back(workspace.active_pane().downgrade(), cx)
 9547                .detach_and_log_err(cx);
 9548
 9549            first_item_id
 9550        })
 9551        .unwrap();
 9552    cx.executor().run_until_parked();
 9553    workspace
 9554        .update(cx, |workspace, cx| {
 9555            let active_item = workspace
 9556                .active_item(cx)
 9557                .expect("should have an active item after navigating back");
 9558            assert_eq!(
 9559                active_item.item_id(),
 9560                multibuffer_item_id,
 9561                "Should navigate back to the multi buffer"
 9562            );
 9563            assert!(!active_item.is_singleton(cx));
 9564        })
 9565        .unwrap();
 9566
 9567    multi_buffer_editor.update(cx, |editor, cx| {
 9568        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 9569            s.select_ranges(Some(39..40))
 9570        });
 9571        editor.open_excerpts(&OpenExcerpts, cx);
 9572    });
 9573    cx.executor().run_until_parked();
 9574    let second_item_id = workspace
 9575        .update(cx, |workspace, cx| {
 9576            let active_item = workspace
 9577                .active_item(cx)
 9578                .expect("should have an active item after navigating into the 2nd buffer");
 9579            let second_item_id = active_item.item_id();
 9580            assert_ne!(
 9581                second_item_id, multibuffer_item_id,
 9582                "Should navigate away from the multibuffer"
 9583            );
 9584            assert_ne!(
 9585                second_item_id, first_item_id,
 9586                "Should navigate into the 2nd buffer and activate it"
 9587            );
 9588            assert!(
 9589                active_item.is_singleton(cx),
 9590                "New active item should be a singleton buffer"
 9591            );
 9592            assert_eq!(
 9593                active_item
 9594                    .act_as::<Editor>(cx)
 9595                    .expect("should have navigated into an editor")
 9596                    .read(cx)
 9597                    .text(cx),
 9598                sample_text_2
 9599            );
 9600
 9601            workspace
 9602                .go_back(workspace.active_pane().downgrade(), cx)
 9603                .detach_and_log_err(cx);
 9604
 9605            second_item_id
 9606        })
 9607        .unwrap();
 9608    cx.executor().run_until_parked();
 9609    workspace
 9610        .update(cx, |workspace, cx| {
 9611            let active_item = workspace
 9612                .active_item(cx)
 9613                .expect("should have an active item after navigating back from the 2nd buffer");
 9614            assert_eq!(
 9615                active_item.item_id(),
 9616                multibuffer_item_id,
 9617                "Should navigate back from the 2nd buffer to the multi buffer"
 9618            );
 9619            assert!(!active_item.is_singleton(cx));
 9620        })
 9621        .unwrap();
 9622
 9623    multi_buffer_editor.update(cx, |editor, cx| {
 9624        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 9625            s.select_ranges(Some(60..70))
 9626        });
 9627        editor.open_excerpts(&OpenExcerpts, cx);
 9628    });
 9629    cx.executor().run_until_parked();
 9630    workspace
 9631        .update(cx, |workspace, cx| {
 9632            let active_item = workspace
 9633                .active_item(cx)
 9634                .expect("should have an active item after navigating into the 3rd buffer");
 9635            let third_item_id = active_item.item_id();
 9636            assert_ne!(
 9637                third_item_id, multibuffer_item_id,
 9638                "Should navigate into the 3rd buffer and activate it"
 9639            );
 9640            assert_ne!(third_item_id, first_item_id);
 9641            assert_ne!(third_item_id, second_item_id);
 9642            assert!(
 9643                active_item.is_singleton(cx),
 9644                "New active item should be a singleton buffer"
 9645            );
 9646            assert_eq!(
 9647                active_item
 9648                    .act_as::<Editor>(cx)
 9649                    .expect("should have navigated into an editor")
 9650                    .read(cx)
 9651                    .text(cx),
 9652                sample_text_3
 9653            );
 9654
 9655            workspace
 9656                .go_back(workspace.active_pane().downgrade(), cx)
 9657                .detach_and_log_err(cx);
 9658        })
 9659        .unwrap();
 9660    cx.executor().run_until_parked();
 9661    workspace
 9662        .update(cx, |workspace, cx| {
 9663            let active_item = workspace
 9664                .active_item(cx)
 9665                .expect("should have an active item after navigating back from the 3rd buffer");
 9666            assert_eq!(
 9667                active_item.item_id(),
 9668                multibuffer_item_id,
 9669                "Should navigate back from the 3rd buffer to the multi buffer"
 9670            );
 9671            assert!(!active_item.is_singleton(cx));
 9672        })
 9673        .unwrap();
 9674}
 9675
 9676#[gpui::test]
 9677async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9678    init_test(cx, |_| {});
 9679
 9680    let mut cx = EditorTestContext::new(cx).await;
 9681
 9682    let diff_base = r#"
 9683        use some::mod;
 9684
 9685        const A: u32 = 42;
 9686
 9687        fn main() {
 9688            println!("hello");
 9689
 9690            println!("world");
 9691        }
 9692        "#
 9693    .unindent();
 9694
 9695    cx.set_state(
 9696        &r#"
 9697        use some::modified;
 9698
 9699        ˇ
 9700        fn main() {
 9701            println!("hello there");
 9702
 9703            println!("around the");
 9704            println!("world");
 9705        }
 9706        "#
 9707        .unindent(),
 9708    );
 9709
 9710    cx.set_diff_base(Some(&diff_base));
 9711    executor.run_until_parked();
 9712    let unexpanded_hunks = vec![
 9713        (
 9714            "use some::mod;\n".to_string(),
 9715            DiffHunkStatus::Modified,
 9716            DisplayRow(0)..DisplayRow(1),
 9717        ),
 9718        (
 9719            "const A: u32 = 42;\n".to_string(),
 9720            DiffHunkStatus::Removed,
 9721            DisplayRow(2)..DisplayRow(2),
 9722        ),
 9723        (
 9724            "    println!(\"hello\");\n".to_string(),
 9725            DiffHunkStatus::Modified,
 9726            DisplayRow(4)..DisplayRow(5),
 9727        ),
 9728        (
 9729            "".to_string(),
 9730            DiffHunkStatus::Added,
 9731            DisplayRow(6)..DisplayRow(7),
 9732        ),
 9733    ];
 9734    cx.update_editor(|editor, cx| {
 9735        let snapshot = editor.snapshot(cx);
 9736        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9737        assert_eq!(all_hunks, unexpanded_hunks);
 9738    });
 9739
 9740    cx.update_editor(|editor, cx| {
 9741        for _ in 0..4 {
 9742            editor.go_to_hunk(&GoToHunk, cx);
 9743            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
 9744        }
 9745    });
 9746    executor.run_until_parked();
 9747    cx.assert_editor_state(
 9748        &r#"
 9749        use some::modified;
 9750
 9751        ˇ
 9752        fn main() {
 9753            println!("hello there");
 9754
 9755            println!("around the");
 9756            println!("world");
 9757        }
 9758        "#
 9759        .unindent(),
 9760    );
 9761    cx.update_editor(|editor, cx| {
 9762        let snapshot = editor.snapshot(cx);
 9763        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9764        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
 9765        assert_eq!(
 9766            expanded_hunks_background_highlights(editor, cx),
 9767            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
 9768            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
 9769        );
 9770        assert_eq!(
 9771            all_hunks,
 9772            vec![
 9773                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
 9774                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
 9775                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
 9776                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
 9777            ],
 9778            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
 9779            (from modified and removed hunks)"
 9780        );
 9781        assert_eq!(
 9782            all_hunks, all_expanded_hunks,
 9783            "Editor hunks should not change and all be expanded"
 9784        );
 9785    });
 9786
 9787    cx.update_editor(|editor, cx| {
 9788        editor.cancel(&Cancel, cx);
 9789
 9790        let snapshot = editor.snapshot(cx);
 9791        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9792        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
 9793        assert_eq!(
 9794            expanded_hunks_background_highlights(editor, cx),
 9795            Vec::new(),
 9796            "After cancelling in editor, no git highlights should be left"
 9797        );
 9798        assert_eq!(
 9799            all_expanded_hunks,
 9800            Vec::new(),
 9801            "After cancelling in editor, no hunks should be expanded"
 9802        );
 9803        assert_eq!(
 9804            all_hunks, unexpanded_hunks,
 9805            "After cancelling in editor, regular hunks' coordinates should get back to normal"
 9806        );
 9807    });
 9808}
 9809
 9810#[gpui::test]
 9811async fn test_toggled_diff_base_change(
 9812    executor: BackgroundExecutor,
 9813    cx: &mut gpui::TestAppContext,
 9814) {
 9815    init_test(cx, |_| {});
 9816
 9817    let mut cx = EditorTestContext::new(cx).await;
 9818
 9819    let diff_base = r#"
 9820        use some::mod1;
 9821        use some::mod2;
 9822
 9823        const A: u32 = 42;
 9824        const B: u32 = 42;
 9825        const C: u32 = 42;
 9826
 9827        fn main(ˇ) {
 9828            println!("hello");
 9829
 9830            println!("world");
 9831        }
 9832        "#
 9833    .unindent();
 9834
 9835    cx.set_state(
 9836        &r#"
 9837        use some::mod2;
 9838
 9839        const A: u32 = 42;
 9840        const C: u32 = 42;
 9841
 9842        fn main(ˇ) {
 9843            //println!("hello");
 9844
 9845            println!("world");
 9846            //
 9847            //
 9848        }
 9849        "#
 9850        .unindent(),
 9851    );
 9852
 9853    cx.set_diff_base(Some(&diff_base));
 9854    executor.run_until_parked();
 9855    cx.update_editor(|editor, cx| {
 9856        let snapshot = editor.snapshot(cx);
 9857        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9858        assert_eq!(
 9859            all_hunks,
 9860            vec![
 9861                (
 9862                    "use some::mod1;\n".to_string(),
 9863                    DiffHunkStatus::Removed,
 9864                    DisplayRow(0)..DisplayRow(0)
 9865                ),
 9866                (
 9867                    "const B: u32 = 42;\n".to_string(),
 9868                    DiffHunkStatus::Removed,
 9869                    DisplayRow(3)..DisplayRow(3)
 9870                ),
 9871                (
 9872                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
 9873                    DiffHunkStatus::Modified,
 9874                    DisplayRow(5)..DisplayRow(7)
 9875                ),
 9876                (
 9877                    "".to_string(),
 9878                    DiffHunkStatus::Added,
 9879                    DisplayRow(9)..DisplayRow(11)
 9880                ),
 9881            ]
 9882        );
 9883    });
 9884
 9885    cx.update_editor(|editor, cx| {
 9886        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
 9887    });
 9888    executor.run_until_parked();
 9889    cx.assert_editor_state(
 9890        &r#"
 9891        use some::mod2;
 9892
 9893        const A: u32 = 42;
 9894        const C: u32 = 42;
 9895
 9896        fn main(ˇ) {
 9897            //println!("hello");
 9898
 9899            println!("world");
 9900            //
 9901            //
 9902        }
 9903        "#
 9904        .unindent(),
 9905    );
 9906    cx.update_editor(|editor, cx| {
 9907        let snapshot = editor.snapshot(cx);
 9908        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9909        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
 9910        assert_eq!(
 9911            expanded_hunks_background_highlights(editor, cx),
 9912            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
 9913            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
 9914        );
 9915        assert_eq!(
 9916            all_hunks,
 9917            vec![
 9918                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
 9919                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
 9920                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
 9921                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
 9922            ],
 9923            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
 9924            (from modified and removed hunks)"
 9925        );
 9926        assert_eq!(
 9927            all_hunks, all_expanded_hunks,
 9928            "Editor hunks should not change and all be expanded"
 9929        );
 9930    });
 9931
 9932    cx.set_diff_base(Some("new diff base!"));
 9933    executor.run_until_parked();
 9934
 9935    cx.update_editor(|editor, cx| {
 9936        let snapshot = editor.snapshot(cx);
 9937        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9938        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
 9939        assert_eq!(
 9940            expanded_hunks_background_highlights(editor, cx),
 9941            Vec::new(),
 9942            "After diff base is changed, old git highlights should be removed"
 9943        );
 9944        assert_eq!(
 9945            all_expanded_hunks,
 9946            Vec::new(),
 9947            "After diff base is changed, old git hunk expansions should be removed"
 9948        );
 9949        assert_eq!(
 9950            all_hunks,
 9951            vec![(
 9952                "new diff base!".to_string(),
 9953                DiffHunkStatus::Modified,
 9954                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
 9955            )],
 9956            "After diff base is changed, hunks should update"
 9957        );
 9958    });
 9959}
 9960
 9961#[gpui::test]
 9962async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9963    init_test(cx, |_| {});
 9964
 9965    let mut cx = EditorTestContext::new(cx).await;
 9966
 9967    let diff_base = r#"
 9968        use some::mod1;
 9969        use some::mod2;
 9970
 9971        const A: u32 = 42;
 9972        const B: u32 = 42;
 9973        const C: u32 = 42;
 9974
 9975        fn main(ˇ) {
 9976            println!("hello");
 9977
 9978            println!("world");
 9979        }
 9980
 9981        fn another() {
 9982            println!("another");
 9983        }
 9984
 9985        fn another2() {
 9986            println!("another2");
 9987        }
 9988        "#
 9989    .unindent();
 9990
 9991    cx.set_state(
 9992        &r#"
 9993        «use some::mod2;
 9994
 9995        const A: u32 = 42;
 9996        const C: u32 = 42;
 9997
 9998        fn main() {
 9999            //println!("hello");
10000
10001            println!("world");
10002            //
10003            //ˇ»
10004        }
10005
10006        fn another() {
10007            println!("another");
10008            println!("another");
10009        }
10010
10011            println!("another2");
10012        }
10013        "#
10014        .unindent(),
10015    );
10016
10017    cx.set_diff_base(Some(&diff_base));
10018    executor.run_until_parked();
10019    cx.update_editor(|editor, cx| {
10020        let snapshot = editor.snapshot(cx);
10021        let all_hunks = editor_hunks(editor, &snapshot, cx);
10022        assert_eq!(
10023            all_hunks,
10024            vec![
10025                (
10026                    "use some::mod1;\n".to_string(),
10027                    DiffHunkStatus::Removed,
10028                    DisplayRow(0)..DisplayRow(0)
10029                ),
10030                (
10031                    "const B: u32 = 42;\n".to_string(),
10032                    DiffHunkStatus::Removed,
10033                    DisplayRow(3)..DisplayRow(3)
10034                ),
10035                (
10036                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10037                    DiffHunkStatus::Modified,
10038                    DisplayRow(5)..DisplayRow(7)
10039                ),
10040                (
10041                    "".to_string(),
10042                    DiffHunkStatus::Added,
10043                    DisplayRow(9)..DisplayRow(11)
10044                ),
10045                (
10046                    "".to_string(),
10047                    DiffHunkStatus::Added,
10048                    DisplayRow(15)..DisplayRow(16)
10049                ),
10050                (
10051                    "fn another2() {\n".to_string(),
10052                    DiffHunkStatus::Removed,
10053                    DisplayRow(18)..DisplayRow(18)
10054                ),
10055            ]
10056        );
10057    });
10058
10059    cx.update_editor(|editor, cx| {
10060        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10061    });
10062    executor.run_until_parked();
10063    cx.assert_editor_state(
10064        &r#"
10065        «use some::mod2;
10066
10067        const A: u32 = 42;
10068        const C: u32 = 42;
10069
10070        fn main() {
10071            //println!("hello");
10072
10073            println!("world");
10074            //
10075            //ˇ»
10076        }
10077
10078        fn another() {
10079            println!("another");
10080            println!("another");
10081        }
10082
10083            println!("another2");
10084        }
10085        "#
10086        .unindent(),
10087    );
10088    cx.update_editor(|editor, cx| {
10089        let snapshot = editor.snapshot(cx);
10090        let all_hunks = editor_hunks(editor, &snapshot, cx);
10091        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10092        assert_eq!(
10093            expanded_hunks_background_highlights(editor, cx),
10094            vec![
10095                DisplayRow(9)..=DisplayRow(10),
10096                DisplayRow(13)..=DisplayRow(14),
10097                DisplayRow(19)..=DisplayRow(19)
10098            ]
10099        );
10100        assert_eq!(
10101            all_hunks,
10102            vec![
10103                (
10104                    "use some::mod1;\n".to_string(),
10105                    DiffHunkStatus::Removed,
10106                    DisplayRow(1)..DisplayRow(1)
10107                ),
10108                (
10109                    "const B: u32 = 42;\n".to_string(),
10110                    DiffHunkStatus::Removed,
10111                    DisplayRow(5)..DisplayRow(5)
10112                ),
10113                (
10114                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10115                    DiffHunkStatus::Modified,
10116                    DisplayRow(9)..DisplayRow(11)
10117                ),
10118                (
10119                    "".to_string(),
10120                    DiffHunkStatus::Added,
10121                    DisplayRow(13)..DisplayRow(15)
10122                ),
10123                (
10124                    "".to_string(),
10125                    DiffHunkStatus::Added,
10126                    DisplayRow(19)..DisplayRow(20)
10127                ),
10128                (
10129                    "fn another2() {\n".to_string(),
10130                    DiffHunkStatus::Removed,
10131                    DisplayRow(23)..DisplayRow(23)
10132                ),
10133            ],
10134        );
10135        assert_eq!(all_hunks, all_expanded_hunks);
10136    });
10137
10138    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
10139    cx.executor().run_until_parked();
10140    cx.assert_editor_state(
10141        &r#"
10142        «use some::mod2;
10143
10144        const A: u32 = 42;
10145        const C: u32 = 42;
10146
10147        fn main() {
10148            //println!("hello");
10149
10150            println!("world");
10151            //
10152            //ˇ»
10153        }
10154
10155        fn another() {
10156            println!("another");
10157            println!("another");
10158        }
10159
10160            println!("another2");
10161        }
10162        "#
10163        .unindent(),
10164    );
10165    cx.update_editor(|editor, cx| {
10166        let snapshot = editor.snapshot(cx);
10167        let all_hunks = editor_hunks(editor, &snapshot, cx);
10168        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10169        assert_eq!(
10170            expanded_hunks_background_highlights(editor, cx),
10171            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
10172            "Only one hunk is left not folded, its highlight should be visible"
10173        );
10174        assert_eq!(
10175            all_hunks,
10176            vec![
10177                (
10178                    "use some::mod1;\n".to_string(),
10179                    DiffHunkStatus::Removed,
10180                    DisplayRow(0)..DisplayRow(0)
10181                ),
10182                (
10183                    "const B: u32 = 42;\n".to_string(),
10184                    DiffHunkStatus::Removed,
10185                    DisplayRow(0)..DisplayRow(0)
10186                ),
10187                (
10188                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10189                    DiffHunkStatus::Modified,
10190                    DisplayRow(0)..DisplayRow(0)
10191                ),
10192                (
10193                    "".to_string(),
10194                    DiffHunkStatus::Added,
10195                    DisplayRow(0)..DisplayRow(1)
10196                ),
10197                (
10198                    "".to_string(),
10199                    DiffHunkStatus::Added,
10200                    DisplayRow(5)..DisplayRow(6)
10201                ),
10202                (
10203                    "fn another2() {\n".to_string(),
10204                    DiffHunkStatus::Removed,
10205                    DisplayRow(9)..DisplayRow(9)
10206                ),
10207            ],
10208            "Hunk list should still return shifted folded hunks"
10209        );
10210        assert_eq!(
10211            all_expanded_hunks,
10212            vec![
10213                (
10214                    "".to_string(),
10215                    DiffHunkStatus::Added,
10216                    DisplayRow(5)..DisplayRow(6)
10217                ),
10218                (
10219                    "fn another2() {\n".to_string(),
10220                    DiffHunkStatus::Removed,
10221                    DisplayRow(9)..DisplayRow(9)
10222                ),
10223            ],
10224            "Only non-folded hunks should be left expanded"
10225        );
10226    });
10227
10228    cx.update_editor(|editor, cx| {
10229        editor.select_all(&SelectAll, cx);
10230        editor.unfold_lines(&UnfoldLines, cx);
10231    });
10232    cx.executor().run_until_parked();
10233    cx.assert_editor_state(
10234        &r#"
10235        «use some::mod2;
10236
10237        const A: u32 = 42;
10238        const C: u32 = 42;
10239
10240        fn main() {
10241            //println!("hello");
10242
10243            println!("world");
10244            //
10245            //
10246        }
10247
10248        fn another() {
10249            println!("another");
10250            println!("another");
10251        }
10252
10253            println!("another2");
10254        }
10255        ˇ»"#
10256        .unindent(),
10257    );
10258    cx.update_editor(|editor, cx| {
10259        let snapshot = editor.snapshot(cx);
10260        let all_hunks = editor_hunks(editor, &snapshot, cx);
10261        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10262        assert_eq!(
10263            expanded_hunks_background_highlights(editor, cx),
10264            vec![
10265                DisplayRow(9)..=DisplayRow(10),
10266                DisplayRow(13)..=DisplayRow(14),
10267                DisplayRow(19)..=DisplayRow(19)
10268            ],
10269            "After unfolding, all hunk diffs should be visible again"
10270        );
10271        assert_eq!(
10272            all_hunks,
10273            vec![
10274                (
10275                    "use some::mod1;\n".to_string(),
10276                    DiffHunkStatus::Removed,
10277                    DisplayRow(1)..DisplayRow(1)
10278                ),
10279                (
10280                    "const B: u32 = 42;\n".to_string(),
10281                    DiffHunkStatus::Removed,
10282                    DisplayRow(5)..DisplayRow(5)
10283                ),
10284                (
10285                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10286                    DiffHunkStatus::Modified,
10287                    DisplayRow(9)..DisplayRow(11)
10288                ),
10289                (
10290                    "".to_string(),
10291                    DiffHunkStatus::Added,
10292                    DisplayRow(13)..DisplayRow(15)
10293                ),
10294                (
10295                    "".to_string(),
10296                    DiffHunkStatus::Added,
10297                    DisplayRow(19)..DisplayRow(20)
10298                ),
10299                (
10300                    "fn another2() {\n".to_string(),
10301                    DiffHunkStatus::Removed,
10302                    DisplayRow(23)..DisplayRow(23)
10303                ),
10304            ],
10305        );
10306        assert_eq!(all_hunks, all_expanded_hunks);
10307    });
10308}
10309
10310#[gpui::test]
10311async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
10312    init_test(cx, |_| {});
10313
10314    let cols = 4;
10315    let rows = 10;
10316    let sample_text_1 = sample_text(rows, cols, 'a');
10317    assert_eq!(
10318        sample_text_1,
10319        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10320    );
10321    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
10322    let sample_text_2 = sample_text(rows, cols, 'l');
10323    assert_eq!(
10324        sample_text_2,
10325        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10326    );
10327    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
10328    let sample_text_3 = sample_text(rows, cols, 'v');
10329    assert_eq!(
10330        sample_text_3,
10331        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10332    );
10333    let modified_sample_text_3 =
10334        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
10335    let buffer_1 = cx.new_model(|cx| {
10336        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
10337        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
10338        buffer
10339    });
10340    let buffer_2 = cx.new_model(|cx| {
10341        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
10342        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
10343        buffer
10344    });
10345    let buffer_3 = cx.new_model(|cx| {
10346        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
10347        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
10348        buffer
10349    });
10350
10351    let multi_buffer = cx.new_model(|cx| {
10352        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10353        multibuffer.push_excerpts(
10354            buffer_1.clone(),
10355            [
10356                ExcerptRange {
10357                    context: Point::new(0, 0)..Point::new(3, 0),
10358                    primary: None,
10359                },
10360                ExcerptRange {
10361                    context: Point::new(5, 0)..Point::new(7, 0),
10362                    primary: None,
10363                },
10364                ExcerptRange {
10365                    context: Point::new(9, 0)..Point::new(10, 4),
10366                    primary: None,
10367                },
10368            ],
10369            cx,
10370        );
10371        multibuffer.push_excerpts(
10372            buffer_2.clone(),
10373            [
10374                ExcerptRange {
10375                    context: Point::new(0, 0)..Point::new(3, 0),
10376                    primary: None,
10377                },
10378                ExcerptRange {
10379                    context: Point::new(5, 0)..Point::new(7, 0),
10380                    primary: None,
10381                },
10382                ExcerptRange {
10383                    context: Point::new(9, 0)..Point::new(10, 4),
10384                    primary: None,
10385                },
10386            ],
10387            cx,
10388        );
10389        multibuffer.push_excerpts(
10390            buffer_3.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
10408    });
10409
10410    let fs = FakeFs::new(cx.executor());
10411    fs.insert_tree(
10412        "/a",
10413        json!({
10414            "main.rs": modified_sample_text_1,
10415            "other.rs": modified_sample_text_2,
10416            "lib.rs": modified_sample_text_3,
10417        }),
10418    )
10419    .await;
10420
10421    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10422    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10423    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10424    let multi_buffer_editor = cx.new_view(|cx| {
10425        Editor::new(
10426            EditorMode::Full,
10427            multi_buffer,
10428            Some(project.clone()),
10429            true,
10430            cx,
10431        )
10432    });
10433    cx.executor().run_until_parked();
10434
10435    let expected_all_hunks = vec![
10436        (
10437            "bbbb\n".to_string(),
10438            DiffHunkStatus::Removed,
10439            DisplayRow(4)..DisplayRow(4),
10440        ),
10441        (
10442            "nnnn\n".to_string(),
10443            DiffHunkStatus::Modified,
10444            DisplayRow(21)..DisplayRow(22),
10445        ),
10446        (
10447            "".to_string(),
10448            DiffHunkStatus::Added,
10449            DisplayRow(41)..DisplayRow(42),
10450        ),
10451    ];
10452    let expected_all_hunks_shifted = vec![
10453        (
10454            "bbbb\n".to_string(),
10455            DiffHunkStatus::Removed,
10456            DisplayRow(5)..DisplayRow(5),
10457        ),
10458        (
10459            "nnnn\n".to_string(),
10460            DiffHunkStatus::Modified,
10461            DisplayRow(23)..DisplayRow(24),
10462        ),
10463        (
10464            "".to_string(),
10465            DiffHunkStatus::Added,
10466            DisplayRow(43)..DisplayRow(44),
10467        ),
10468    ];
10469
10470    multi_buffer_editor.update(cx, |editor, cx| {
10471        let snapshot = editor.snapshot(cx);
10472        let all_hunks = editor_hunks(editor, &snapshot, cx);
10473        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10474        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10475        assert_eq!(all_hunks, expected_all_hunks);
10476        assert_eq!(all_expanded_hunks, Vec::new());
10477    });
10478
10479    multi_buffer_editor.update(cx, |editor, cx| {
10480        editor.select_all(&SelectAll, cx);
10481        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10482    });
10483    cx.executor().run_until_parked();
10484    multi_buffer_editor.update(cx, |editor, cx| {
10485        let snapshot = editor.snapshot(cx);
10486        let all_hunks = editor_hunks(editor, &snapshot, cx);
10487        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10488        assert_eq!(
10489            expanded_hunks_background_highlights(editor, cx),
10490            vec![
10491                DisplayRow(23)..=DisplayRow(23),
10492                DisplayRow(43)..=DisplayRow(43)
10493            ],
10494        );
10495        assert_eq!(all_hunks, expected_all_hunks_shifted);
10496        assert_eq!(all_hunks, all_expanded_hunks);
10497    });
10498
10499    multi_buffer_editor.update(cx, |editor, cx| {
10500        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10501    });
10502    cx.executor().run_until_parked();
10503    multi_buffer_editor.update(cx, |editor, cx| {
10504        let snapshot = editor.snapshot(cx);
10505        let all_hunks = editor_hunks(editor, &snapshot, cx);
10506        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10507        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10508        assert_eq!(all_hunks, expected_all_hunks);
10509        assert_eq!(all_expanded_hunks, Vec::new());
10510    });
10511
10512    multi_buffer_editor.update(cx, |editor, cx| {
10513        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10514    });
10515    cx.executor().run_until_parked();
10516    multi_buffer_editor.update(cx, |editor, cx| {
10517        let snapshot = editor.snapshot(cx);
10518        let all_hunks = editor_hunks(editor, &snapshot, cx);
10519        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10520        assert_eq!(
10521            expanded_hunks_background_highlights(editor, cx),
10522            vec![
10523                DisplayRow(23)..=DisplayRow(23),
10524                DisplayRow(43)..=DisplayRow(43)
10525            ],
10526        );
10527        assert_eq!(all_hunks, expected_all_hunks_shifted);
10528        assert_eq!(all_hunks, all_expanded_hunks);
10529    });
10530
10531    multi_buffer_editor.update(cx, |editor, cx| {
10532        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10533    });
10534    cx.executor().run_until_parked();
10535    multi_buffer_editor.update(cx, |editor, cx| {
10536        let snapshot = editor.snapshot(cx);
10537        let all_hunks = editor_hunks(editor, &snapshot, cx);
10538        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10539        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10540        assert_eq!(all_hunks, expected_all_hunks);
10541        assert_eq!(all_expanded_hunks, Vec::new());
10542    });
10543}
10544
10545#[gpui::test]
10546async fn test_edits_around_toggled_additions(
10547    executor: BackgroundExecutor,
10548    cx: &mut gpui::TestAppContext,
10549) {
10550    init_test(cx, |_| {});
10551
10552    let mut cx = EditorTestContext::new(cx).await;
10553
10554    let diff_base = r#"
10555        use some::mod1;
10556        use some::mod2;
10557
10558        const A: u32 = 42;
10559
10560        fn main() {
10561            println!("hello");
10562
10563            println!("world");
10564        }
10565        "#
10566    .unindent();
10567    executor.run_until_parked();
10568    cx.set_state(
10569        &r#"
10570        use some::mod1;
10571        use some::mod2;
10572
10573        const A: u32 = 42;
10574        const B: u32 = 42;
10575        const C: u32 = 42;
10576        ˇ
10577
10578        fn main() {
10579            println!("hello");
10580
10581            println!("world");
10582        }
10583        "#
10584        .unindent(),
10585    );
10586
10587    cx.set_diff_base(Some(&diff_base));
10588    executor.run_until_parked();
10589    cx.update_editor(|editor, cx| {
10590        let snapshot = editor.snapshot(cx);
10591        let all_hunks = editor_hunks(editor, &snapshot, cx);
10592        assert_eq!(
10593            all_hunks,
10594            vec![(
10595                "".to_string(),
10596                DiffHunkStatus::Added,
10597                DisplayRow(4)..DisplayRow(7)
10598            )]
10599        );
10600    });
10601    cx.update_editor(|editor, cx| {
10602        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10603    });
10604    executor.run_until_parked();
10605    cx.assert_editor_state(
10606        &r#"
10607        use some::mod1;
10608        use some::mod2;
10609
10610        const A: u32 = 42;
10611        const B: u32 = 42;
10612        const C: u32 = 42;
10613        ˇ
10614
10615        fn main() {
10616            println!("hello");
10617
10618            println!("world");
10619        }
10620        "#
10621        .unindent(),
10622    );
10623    cx.update_editor(|editor, cx| {
10624        let snapshot = editor.snapshot(cx);
10625        let all_hunks = editor_hunks(editor, &snapshot, cx);
10626        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10627        assert_eq!(
10628            all_hunks,
10629            vec![(
10630                "".to_string(),
10631                DiffHunkStatus::Added,
10632                DisplayRow(4)..DisplayRow(7)
10633            )]
10634        );
10635        assert_eq!(
10636            expanded_hunks_background_highlights(editor, cx),
10637            vec![DisplayRow(4)..=DisplayRow(6)]
10638        );
10639        assert_eq!(all_hunks, all_expanded_hunks);
10640    });
10641
10642    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
10643    executor.run_until_parked();
10644    cx.assert_editor_state(
10645        &r#"
10646        use some::mod1;
10647        use some::mod2;
10648
10649        const A: u32 = 42;
10650        const B: u32 = 42;
10651        const C: u32 = 42;
10652        const D: u32 = 42;
10653        ˇ
10654
10655        fn main() {
10656            println!("hello");
10657
10658            println!("world");
10659        }
10660        "#
10661        .unindent(),
10662    );
10663    cx.update_editor(|editor, cx| {
10664        let snapshot = editor.snapshot(cx);
10665        let all_hunks = editor_hunks(editor, &snapshot, cx);
10666        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10667        assert_eq!(
10668            all_hunks,
10669            vec![(
10670                "".to_string(),
10671                DiffHunkStatus::Added,
10672                DisplayRow(4)..DisplayRow(8)
10673            )]
10674        );
10675        assert_eq!(
10676            expanded_hunks_background_highlights(editor, cx),
10677            vec![DisplayRow(4)..=DisplayRow(6)],
10678            "Edited hunk should have one more line added"
10679        );
10680        assert_eq!(
10681            all_hunks, all_expanded_hunks,
10682            "Expanded hunk should also grow with the addition"
10683        );
10684    });
10685
10686    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
10687    executor.run_until_parked();
10688    cx.assert_editor_state(
10689        &r#"
10690        use some::mod1;
10691        use some::mod2;
10692
10693        const A: u32 = 42;
10694        const B: u32 = 42;
10695        const C: u32 = 42;
10696        const D: u32 = 42;
10697        const E: u32 = 42;
10698        ˇ
10699
10700        fn main() {
10701            println!("hello");
10702
10703            println!("world");
10704        }
10705        "#
10706        .unindent(),
10707    );
10708    cx.update_editor(|editor, cx| {
10709        let snapshot = editor.snapshot(cx);
10710        let all_hunks = editor_hunks(editor, &snapshot, cx);
10711        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10712        assert_eq!(
10713            all_hunks,
10714            vec![(
10715                "".to_string(),
10716                DiffHunkStatus::Added,
10717                DisplayRow(4)..DisplayRow(9)
10718            )]
10719        );
10720        assert_eq!(
10721            expanded_hunks_background_highlights(editor, cx),
10722            vec![DisplayRow(4)..=DisplayRow(6)],
10723            "Edited hunk should have one more line added"
10724        );
10725        assert_eq!(all_hunks, all_expanded_hunks);
10726    });
10727
10728    cx.update_editor(|editor, cx| {
10729        editor.move_up(&MoveUp, cx);
10730        editor.delete_line(&DeleteLine, cx);
10731    });
10732    executor.run_until_parked();
10733    cx.assert_editor_state(
10734        &r#"
10735        use some::mod1;
10736        use some::mod2;
10737
10738        const A: u32 = 42;
10739        const B: u32 = 42;
10740        const C: u32 = 42;
10741        const D: u32 = 42;
10742        ˇ
10743
10744        fn main() {
10745            println!("hello");
10746
10747            println!("world");
10748        }
10749        "#
10750        .unindent(),
10751    );
10752    cx.update_editor(|editor, cx| {
10753        let snapshot = editor.snapshot(cx);
10754        let all_hunks = editor_hunks(editor, &snapshot, cx);
10755        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10756        assert_eq!(
10757            all_hunks,
10758            vec![(
10759                "".to_string(),
10760                DiffHunkStatus::Added,
10761                DisplayRow(4)..DisplayRow(8)
10762            )]
10763        );
10764        assert_eq!(
10765            expanded_hunks_background_highlights(editor, cx),
10766            vec![DisplayRow(4)..=DisplayRow(6)],
10767            "Deleting a line should shrint the hunk"
10768        );
10769        assert_eq!(
10770            all_hunks, all_expanded_hunks,
10771            "Expanded hunk should also shrink with the addition"
10772        );
10773    });
10774
10775    cx.update_editor(|editor, cx| {
10776        editor.move_up(&MoveUp, cx);
10777        editor.delete_line(&DeleteLine, cx);
10778        editor.move_up(&MoveUp, cx);
10779        editor.delete_line(&DeleteLine, cx);
10780        editor.move_up(&MoveUp, cx);
10781        editor.delete_line(&DeleteLine, cx);
10782    });
10783    executor.run_until_parked();
10784    cx.assert_editor_state(
10785        &r#"
10786        use some::mod1;
10787        use some::mod2;
10788
10789        const A: u32 = 42;
10790        ˇ
10791
10792        fn main() {
10793            println!("hello");
10794
10795            println!("world");
10796        }
10797        "#
10798        .unindent(),
10799    );
10800    cx.update_editor(|editor, cx| {
10801        let snapshot = editor.snapshot(cx);
10802        let all_hunks = editor_hunks(editor, &snapshot, cx);
10803        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10804        assert_eq!(
10805            all_hunks,
10806            vec![(
10807                "".to_string(),
10808                DiffHunkStatus::Added,
10809                DisplayRow(5)..DisplayRow(6)
10810            )]
10811        );
10812        assert_eq!(
10813            expanded_hunks_background_highlights(editor, cx),
10814            vec![DisplayRow(5)..=DisplayRow(5)]
10815        );
10816        assert_eq!(all_hunks, all_expanded_hunks);
10817    });
10818
10819    cx.update_editor(|editor, cx| {
10820        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
10821        editor.delete_line(&DeleteLine, cx);
10822    });
10823    executor.run_until_parked();
10824    cx.assert_editor_state(
10825        &r#"
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                (
10844                    "use some::mod1;\nuse some::mod2;\n".to_string(),
10845                    DiffHunkStatus::Removed,
10846                    DisplayRow(0)..DisplayRow(0)
10847                ),
10848                (
10849                    "const A: u32 = 42;\n".to_string(),
10850                    DiffHunkStatus::Removed,
10851                    DisplayRow(2)..DisplayRow(2)
10852                )
10853            ]
10854        );
10855        assert_eq!(
10856            expanded_hunks_background_highlights(editor, cx),
10857            Vec::new(),
10858            "Should close all stale expanded addition hunks"
10859        );
10860        assert_eq!(
10861            all_expanded_hunks,
10862            vec![(
10863                "const A: u32 = 42;\n".to_string(),
10864                DiffHunkStatus::Removed,
10865                DisplayRow(2)..DisplayRow(2)
10866            )],
10867            "Should open hunks that were adjacent to the stale addition one"
10868        );
10869    });
10870}
10871
10872#[gpui::test]
10873async fn test_edits_around_toggled_deletions(
10874    executor: BackgroundExecutor,
10875    cx: &mut gpui::TestAppContext,
10876) {
10877    init_test(cx, |_| {});
10878
10879    let mut cx = EditorTestContext::new(cx).await;
10880
10881    let diff_base = r#"
10882        use some::mod1;
10883        use some::mod2;
10884
10885        const A: u32 = 42;
10886        const B: u32 = 42;
10887        const C: u32 = 42;
10888
10889
10890        fn main() {
10891            println!("hello");
10892
10893            println!("world");
10894        }
10895        "#
10896    .unindent();
10897    executor.run_until_parked();
10898    cx.set_state(
10899        &r#"
10900        use some::mod1;
10901        use some::mod2;
10902
10903        ˇconst B: u32 = 42;
10904        const C: u32 = 42;
10905
10906
10907        fn main() {
10908            println!("hello");
10909
10910            println!("world");
10911        }
10912        "#
10913        .unindent(),
10914    );
10915
10916    cx.set_diff_base(Some(&diff_base));
10917    executor.run_until_parked();
10918    cx.update_editor(|editor, cx| {
10919        let snapshot = editor.snapshot(cx);
10920        let all_hunks = editor_hunks(editor, &snapshot, cx);
10921        assert_eq!(
10922            all_hunks,
10923            vec![(
10924                "const A: u32 = 42;\n".to_string(),
10925                DiffHunkStatus::Removed,
10926                DisplayRow(3)..DisplayRow(3)
10927            )]
10928        );
10929    });
10930    cx.update_editor(|editor, cx| {
10931        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10932    });
10933    executor.run_until_parked();
10934    cx.assert_editor_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    cx.update_editor(|editor, cx| {
10952        let snapshot = editor.snapshot(cx);
10953        let all_hunks = editor_hunks(editor, &snapshot, cx);
10954        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10955        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10956        assert_eq!(
10957            all_hunks,
10958            vec![(
10959                "const A: u32 = 42;\n".to_string(),
10960                DiffHunkStatus::Removed,
10961                DisplayRow(4)..DisplayRow(4)
10962            )]
10963        );
10964        assert_eq!(all_hunks, all_expanded_hunks);
10965    });
10966
10967    cx.update_editor(|editor, cx| {
10968        editor.delete_line(&DeleteLine, cx);
10969    });
10970    executor.run_until_parked();
10971    cx.assert_editor_state(
10972        &r#"
10973        use some::mod1;
10974        use some::mod2;
10975
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!(
10992            expanded_hunks_background_highlights(editor, cx),
10993            Vec::new(),
10994            "Deleted hunks do not highlight current editor's background"
10995        );
10996        assert_eq!(
10997            all_hunks,
10998            vec![(
10999                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
11000                DiffHunkStatus::Removed,
11001                DisplayRow(5)..DisplayRow(5)
11002            )]
11003        );
11004        assert_eq!(all_hunks, all_expanded_hunks);
11005    });
11006
11007    cx.update_editor(|editor, cx| {
11008        editor.delete_line(&DeleteLine, cx);
11009    });
11010    executor.run_until_parked();
11011    cx.assert_editor_state(
11012        &r#"
11013        use some::mod1;
11014        use some::mod2;
11015
11016        ˇ
11017
11018        fn main() {
11019            println!("hello");
11020
11021            println!("world");
11022        }
11023        "#
11024        .unindent(),
11025    );
11026    cx.update_editor(|editor, cx| {
11027        let snapshot = editor.snapshot(cx);
11028        let all_hunks = editor_hunks(editor, &snapshot, cx);
11029        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11030        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11031        assert_eq!(
11032            all_hunks,
11033            vec![(
11034                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11035                DiffHunkStatus::Removed,
11036                DisplayRow(6)..DisplayRow(6)
11037            )]
11038        );
11039        assert_eq!(all_hunks, all_expanded_hunks);
11040    });
11041
11042    cx.update_editor(|editor, cx| {
11043        editor.handle_input("replacement", cx);
11044    });
11045    executor.run_until_parked();
11046    cx.assert_editor_state(
11047        &r#"
11048        use some::mod1;
11049        use some::mod2;
11050
11051        replacementˇ
11052
11053        fn main() {
11054            println!("hello");
11055
11056            println!("world");
11057        }
11058        "#
11059        .unindent(),
11060    );
11061    cx.update_editor(|editor, cx| {
11062        let snapshot = editor.snapshot(cx);
11063        let all_hunks = editor_hunks(editor, &snapshot, cx);
11064        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11065        assert_eq!(
11066            all_hunks,
11067            vec![(
11068                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
11069                DiffHunkStatus::Modified,
11070                DisplayRow(7)..DisplayRow(8)
11071            )]
11072        );
11073        assert_eq!(
11074            expanded_hunks_background_highlights(editor, cx),
11075            vec![DisplayRow(7)..=DisplayRow(7)],
11076            "Modified expanded hunks should display additions and highlight their background"
11077        );
11078        assert_eq!(all_hunks, all_expanded_hunks);
11079    });
11080}
11081
11082#[gpui::test]
11083async fn test_edits_around_toggled_modifications(
11084    executor: BackgroundExecutor,
11085    cx: &mut gpui::TestAppContext,
11086) {
11087    init_test(cx, |_| {});
11088
11089    let mut cx = EditorTestContext::new(cx).await;
11090
11091    let diff_base = r#"
11092        use some::mod1;
11093        use some::mod2;
11094
11095        const A: u32 = 42;
11096        const B: u32 = 42;
11097        const C: u32 = 42;
11098        const D: u32 = 42;
11099
11100
11101        fn main() {
11102            println!("hello");
11103
11104            println!("world");
11105        }"#
11106    .unindent();
11107    executor.run_until_parked();
11108    cx.set_state(
11109        &r#"
11110        use some::mod1;
11111        use some::mod2;
11112
11113        const A: u32 = 42;
11114        const B: u32 = 42;
11115        const C: u32 = 43ˇ
11116        const D: u32 = 42;
11117
11118
11119        fn main() {
11120            println!("hello");
11121
11122            println!("world");
11123        }"#
11124        .unindent(),
11125    );
11126
11127    cx.set_diff_base(Some(&diff_base));
11128    executor.run_until_parked();
11129    cx.update_editor(|editor, cx| {
11130        let snapshot = editor.snapshot(cx);
11131        let all_hunks = editor_hunks(editor, &snapshot, cx);
11132        assert_eq!(
11133            all_hunks,
11134            vec![(
11135                "const C: u32 = 42;\n".to_string(),
11136                DiffHunkStatus::Modified,
11137                DisplayRow(5)..DisplayRow(6)
11138            )]
11139        );
11140    });
11141    cx.update_editor(|editor, cx| {
11142        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11143    });
11144    executor.run_until_parked();
11145    cx.assert_editor_state(
11146        &r#"
11147        use some::mod1;
11148        use some::mod2;
11149
11150        const A: u32 = 42;
11151        const B: u32 = 42;
11152        const C: u32 = 43ˇ
11153        const D: u32 = 42;
11154
11155
11156        fn main() {
11157            println!("hello");
11158
11159            println!("world");
11160        }"#
11161        .unindent(),
11162    );
11163    cx.update_editor(|editor, cx| {
11164        let snapshot = editor.snapshot(cx);
11165        let all_hunks = editor_hunks(editor, &snapshot, cx);
11166        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11167        assert_eq!(
11168            expanded_hunks_background_highlights(editor, cx),
11169            vec![DisplayRow(6)..=DisplayRow(6)],
11170        );
11171        assert_eq!(
11172            all_hunks,
11173            vec![(
11174                "const C: u32 = 42;\n".to_string(),
11175                DiffHunkStatus::Modified,
11176                DisplayRow(6)..DisplayRow(7)
11177            )]
11178        );
11179        assert_eq!(all_hunks, all_expanded_hunks);
11180    });
11181
11182    cx.update_editor(|editor, cx| {
11183        editor.handle_input("\nnew_line\n", cx);
11184    });
11185    executor.run_until_parked();
11186    cx.assert_editor_state(
11187        &r#"
11188            use some::mod1;
11189            use some::mod2;
11190
11191            const A: u32 = 42;
11192            const B: u32 = 42;
11193            const C: u32 = 43
11194            new_line
11195            ˇ
11196            const D: u32 = 42;
11197
11198
11199            fn main() {
11200                println!("hello");
11201
11202                println!("world");
11203            }"#
11204        .unindent(),
11205    );
11206    cx.update_editor(|editor, cx| {
11207        let snapshot = editor.snapshot(cx);
11208        let all_hunks = editor_hunks(editor, &snapshot, cx);
11209        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11210        assert_eq!(
11211            expanded_hunks_background_highlights(editor, cx),
11212            vec![DisplayRow(6)..=DisplayRow(6)],
11213            "Modified hunk should grow highlighted lines on more text additions"
11214        );
11215        assert_eq!(
11216            all_hunks,
11217            vec![(
11218                "const C: u32 = 42;\n".to_string(),
11219                DiffHunkStatus::Modified,
11220                DisplayRow(6)..DisplayRow(9)
11221            )]
11222        );
11223        assert_eq!(all_hunks, all_expanded_hunks);
11224    });
11225
11226    cx.update_editor(|editor, cx| {
11227        editor.move_up(&MoveUp, cx);
11228        editor.move_up(&MoveUp, cx);
11229        editor.move_up(&MoveUp, cx);
11230        editor.delete_line(&DeleteLine, cx);
11231    });
11232    executor.run_until_parked();
11233    cx.assert_editor_state(
11234        &r#"
11235            use some::mod1;
11236            use some::mod2;
11237
11238            const A: u32 = 42;
11239            ˇconst C: u32 = 43
11240            new_line
11241
11242            const D: u32 = 42;
11243
11244
11245            fn main() {
11246                println!("hello");
11247
11248                println!("world");
11249            }"#
11250        .unindent(),
11251    );
11252    cx.update_editor(|editor, cx| {
11253        let snapshot = editor.snapshot(cx);
11254        let all_hunks = editor_hunks(editor, &snapshot, cx);
11255        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11256        assert_eq!(
11257            expanded_hunks_background_highlights(editor, cx),
11258            vec![DisplayRow(6)..=DisplayRow(8)],
11259        );
11260        assert_eq!(
11261            all_hunks,
11262            vec![(
11263                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11264                DiffHunkStatus::Modified,
11265                DisplayRow(6)..DisplayRow(9)
11266            )],
11267            "Modified hunk should grow deleted lines on text deletions above"
11268        );
11269        assert_eq!(all_hunks, all_expanded_hunks);
11270    });
11271
11272    cx.update_editor(|editor, cx| {
11273        editor.move_up(&MoveUp, cx);
11274        editor.handle_input("v", cx);
11275    });
11276    executor.run_until_parked();
11277    cx.assert_editor_state(
11278        &r#"
11279            use some::mod1;
11280            use some::mod2;
11281
11282            vˇconst A: u32 = 42;
11283            const C: u32 = 43
11284            new_line
11285
11286            const D: u32 = 42;
11287
11288
11289            fn main() {
11290                println!("hello");
11291
11292                println!("world");
11293            }"#
11294        .unindent(),
11295    );
11296    cx.update_editor(|editor, cx| {
11297        let snapshot = editor.snapshot(cx);
11298        let all_hunks = editor_hunks(editor, &snapshot, cx);
11299        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11300        assert_eq!(
11301            expanded_hunks_background_highlights(editor, cx),
11302            vec![DisplayRow(6)..=DisplayRow(9)],
11303            "Modified hunk should grow deleted lines on text modifications above"
11304        );
11305        assert_eq!(
11306            all_hunks,
11307            vec![(
11308                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11309                DiffHunkStatus::Modified,
11310                DisplayRow(6)..DisplayRow(10)
11311            )]
11312        );
11313        assert_eq!(all_hunks, all_expanded_hunks);
11314    });
11315
11316    cx.update_editor(|editor, cx| {
11317        editor.move_down(&MoveDown, cx);
11318        editor.move_down(&MoveDown, cx);
11319        editor.delete_line(&DeleteLine, cx)
11320    });
11321    executor.run_until_parked();
11322    cx.assert_editor_state(
11323        &r#"
11324            use some::mod1;
11325            use some::mod2;
11326
11327            vconst A: u32 = 42;
11328            const C: u32 = 43
11329            ˇ
11330            const D: u32 = 42;
11331
11332
11333            fn main() {
11334                println!("hello");
11335
11336                println!("world");
11337            }"#
11338        .unindent(),
11339    );
11340    cx.update_editor(|editor, cx| {
11341        let snapshot = editor.snapshot(cx);
11342        let all_hunks = editor_hunks(editor, &snapshot, cx);
11343        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11344        assert_eq!(
11345            expanded_hunks_background_highlights(editor, cx),
11346            vec![DisplayRow(6)..=DisplayRow(8)],
11347            "Modified hunk should grow shrink lines on modification lines removal"
11348        );
11349        assert_eq!(
11350            all_hunks,
11351            vec![(
11352                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11353                DiffHunkStatus::Modified,
11354                DisplayRow(6)..DisplayRow(9)
11355            )]
11356        );
11357        assert_eq!(all_hunks, all_expanded_hunks);
11358    });
11359
11360    cx.update_editor(|editor, cx| {
11361        editor.move_up(&MoveUp, cx);
11362        editor.move_up(&MoveUp, cx);
11363        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
11364        editor.delete_line(&DeleteLine, cx)
11365    });
11366    executor.run_until_parked();
11367    cx.assert_editor_state(
11368        &r#"
11369            use some::mod1;
11370            use some::mod2;
11371
11372            ˇ
11373
11374            fn main() {
11375                println!("hello");
11376
11377                println!("world");
11378            }"#
11379        .unindent(),
11380    );
11381    cx.update_editor(|editor, cx| {
11382        let snapshot = editor.snapshot(cx);
11383        let all_hunks = editor_hunks(editor, &snapshot, cx);
11384        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11385        assert_eq!(
11386            expanded_hunks_background_highlights(editor, cx),
11387            Vec::new(),
11388            "Modified hunk should turn into a removed one on all modified lines removal"
11389        );
11390        assert_eq!(
11391            all_hunks,
11392            vec![(
11393                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
11394                    .to_string(),
11395                DiffHunkStatus::Removed,
11396                DisplayRow(7)..DisplayRow(7)
11397            )]
11398        );
11399        assert_eq!(all_hunks, all_expanded_hunks);
11400    });
11401}
11402
11403#[gpui::test]
11404async fn test_multiple_expanded_hunks_merge(
11405    executor: BackgroundExecutor,
11406    cx: &mut gpui::TestAppContext,
11407) {
11408    init_test(cx, |_| {});
11409
11410    let mut cx = EditorTestContext::new(cx).await;
11411
11412    let diff_base = r#"
11413        use some::mod1;
11414        use some::mod2;
11415
11416        const A: u32 = 42;
11417        const B: u32 = 42;
11418        const C: u32 = 42;
11419        const D: u32 = 42;
11420
11421
11422        fn main() {
11423            println!("hello");
11424
11425            println!("world");
11426        }"#
11427    .unindent();
11428    executor.run_until_parked();
11429    cx.set_state(
11430        &r#"
11431        use some::mod1;
11432        use some::mod2;
11433
11434        const A: u32 = 42;
11435        const B: u32 = 42;
11436        const C: u32 = 43ˇ
11437        const D: u32 = 42;
11438
11439
11440        fn main() {
11441            println!("hello");
11442
11443            println!("world");
11444        }"#
11445        .unindent(),
11446    );
11447
11448    cx.set_diff_base(Some(&diff_base));
11449    executor.run_until_parked();
11450    cx.update_editor(|editor, cx| {
11451        let snapshot = editor.snapshot(cx);
11452        let all_hunks = editor_hunks(editor, &snapshot, cx);
11453        assert_eq!(
11454            all_hunks,
11455            vec![(
11456                "const C: u32 = 42;\n".to_string(),
11457                DiffHunkStatus::Modified,
11458                DisplayRow(5)..DisplayRow(6)
11459            )]
11460        );
11461    });
11462    cx.update_editor(|editor, cx| {
11463        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11464    });
11465    executor.run_until_parked();
11466    cx.assert_editor_state(
11467        &r#"
11468        use some::mod1;
11469        use some::mod2;
11470
11471        const A: u32 = 42;
11472        const B: u32 = 42;
11473        const C: u32 = 43ˇ
11474        const D: u32 = 42;
11475
11476
11477        fn main() {
11478            println!("hello");
11479
11480            println!("world");
11481        }"#
11482        .unindent(),
11483    );
11484    cx.update_editor(|editor, cx| {
11485        let snapshot = editor.snapshot(cx);
11486        let all_hunks = editor_hunks(editor, &snapshot, cx);
11487        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11488        assert_eq!(
11489            expanded_hunks_background_highlights(editor, cx),
11490            vec![DisplayRow(6)..=DisplayRow(6)],
11491        );
11492        assert_eq!(
11493            all_hunks,
11494            vec![(
11495                "const C: u32 = 42;\n".to_string(),
11496                DiffHunkStatus::Modified,
11497                DisplayRow(6)..DisplayRow(7)
11498            )]
11499        );
11500        assert_eq!(all_hunks, all_expanded_hunks);
11501    });
11502
11503    cx.update_editor(|editor, cx| {
11504        editor.handle_input("\nnew_line\n", cx);
11505    });
11506    executor.run_until_parked();
11507    cx.assert_editor_state(
11508        &r#"
11509            use some::mod1;
11510            use some::mod2;
11511
11512            const A: u32 = 42;
11513            const B: u32 = 42;
11514            const C: u32 = 43
11515            new_line
11516            ˇ
11517            const D: u32 = 42;
11518
11519
11520            fn main() {
11521                println!("hello");
11522
11523                println!("world");
11524            }"#
11525        .unindent(),
11526    );
11527}
11528
11529async fn setup_indent_guides_editor(
11530    text: &str,
11531    cx: &mut gpui::TestAppContext,
11532) -> (BufferId, EditorTestContext) {
11533    init_test(cx, |_| {});
11534
11535    let mut cx = EditorTestContext::new(cx).await;
11536
11537    let buffer_id = cx.update_editor(|editor, cx| {
11538        editor.set_text(text, cx);
11539        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
11540        let buffer_id = buffer_ids[0];
11541        buffer_id
11542    });
11543
11544    (buffer_id, cx)
11545}
11546
11547fn assert_indent_guides(
11548    range: Range<u32>,
11549    expected: Vec<IndentGuide>,
11550    active_indices: Option<Vec<usize>>,
11551    cx: &mut EditorTestContext,
11552) {
11553    let indent_guides = cx.update_editor(|editor, cx| {
11554        let snapshot = editor.snapshot(cx).display_snapshot;
11555        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
11556            MultiBufferRow(range.start)..MultiBufferRow(range.end),
11557            true,
11558            &snapshot,
11559            cx,
11560        );
11561
11562        indent_guides.sort_by(|a, b| {
11563            a.depth.cmp(&b.depth).then(
11564                a.start_row
11565                    .cmp(&b.start_row)
11566                    .then(a.end_row.cmp(&b.end_row)),
11567            )
11568        });
11569        indent_guides
11570    });
11571
11572    if let Some(expected) = active_indices {
11573        let active_indices = cx.update_editor(|editor, cx| {
11574            let snapshot = editor.snapshot(cx).display_snapshot;
11575            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
11576        });
11577
11578        assert_eq!(
11579            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
11580            expected,
11581            "Active indent guide indices do not match"
11582        );
11583    }
11584
11585    let expected: Vec<_> = expected
11586        .into_iter()
11587        .map(|guide| MultiBufferIndentGuide {
11588            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
11589            buffer: guide,
11590        })
11591        .collect();
11592
11593    assert_eq!(indent_guides, expected, "Indent guides do not match");
11594}
11595
11596fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
11597    IndentGuide {
11598        buffer_id,
11599        start_row,
11600        end_row,
11601        depth,
11602        tab_size: 4,
11603        settings: IndentGuideSettings {
11604            enabled: true,
11605            line_width: 1,
11606            ..Default::default()
11607        },
11608    }
11609}
11610
11611#[gpui::test]
11612async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
11613    let (buffer_id, mut cx) = setup_indent_guides_editor(
11614        &"
11615    fn main() {
11616        let a = 1;
11617    }"
11618        .unindent(),
11619        cx,
11620    )
11621    .await;
11622
11623    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
11624}
11625
11626#[gpui::test]
11627async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
11628    let (buffer_id, mut cx) = setup_indent_guides_editor(
11629        &"
11630    fn main() {
11631        let a = 1;
11632        let b = 2;
11633    }"
11634        .unindent(),
11635        cx,
11636    )
11637    .await;
11638
11639    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
11640}
11641
11642#[gpui::test]
11643async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
11644    let (buffer_id, mut cx) = setup_indent_guides_editor(
11645        &"
11646    fn main() {
11647        let a = 1;
11648        if a == 3 {
11649            let b = 2;
11650        } else {
11651            let c = 3;
11652        }
11653    }"
11654        .unindent(),
11655        cx,
11656    )
11657    .await;
11658
11659    assert_indent_guides(
11660        0..8,
11661        vec![
11662            indent_guide(buffer_id, 1, 6, 0),
11663            indent_guide(buffer_id, 3, 3, 1),
11664            indent_guide(buffer_id, 5, 5, 1),
11665        ],
11666        None,
11667        &mut cx,
11668    );
11669}
11670
11671#[gpui::test]
11672async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
11673    let (buffer_id, mut cx) = setup_indent_guides_editor(
11674        &"
11675    fn main() {
11676        let a = 1;
11677            let b = 2;
11678        let c = 3;
11679    }"
11680        .unindent(),
11681        cx,
11682    )
11683    .await;
11684
11685    assert_indent_guides(
11686        0..5,
11687        vec![
11688            indent_guide(buffer_id, 1, 3, 0),
11689            indent_guide(buffer_id, 2, 2, 1),
11690        ],
11691        None,
11692        &mut cx,
11693    );
11694}
11695
11696#[gpui::test]
11697async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
11698    let (buffer_id, mut cx) = setup_indent_guides_editor(
11699        &"
11700        fn main() {
11701            let a = 1;
11702
11703            let c = 3;
11704        }"
11705        .unindent(),
11706        cx,
11707    )
11708    .await;
11709
11710    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
11711}
11712
11713#[gpui::test]
11714async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
11715    let (buffer_id, mut cx) = setup_indent_guides_editor(
11716        &"
11717        fn main() {
11718            let a = 1;
11719
11720            let c = 3;
11721
11722            if a == 3 {
11723                let b = 2;
11724            } else {
11725                let c = 3;
11726            }
11727        }"
11728        .unindent(),
11729        cx,
11730    )
11731    .await;
11732
11733    assert_indent_guides(
11734        0..11,
11735        vec![
11736            indent_guide(buffer_id, 1, 9, 0),
11737            indent_guide(buffer_id, 6, 6, 1),
11738            indent_guide(buffer_id, 8, 8, 1),
11739        ],
11740        None,
11741        &mut cx,
11742    );
11743}
11744
11745#[gpui::test]
11746async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
11747    let (buffer_id, mut cx) = setup_indent_guides_editor(
11748        &"
11749        fn main() {
11750            let a = 1;
11751
11752            let c = 3;
11753
11754            if a == 3 {
11755                let b = 2;
11756            } else {
11757                let c = 3;
11758            }
11759        }"
11760        .unindent(),
11761        cx,
11762    )
11763    .await;
11764
11765    assert_indent_guides(
11766        1..11,
11767        vec![
11768            indent_guide(buffer_id, 1, 9, 0),
11769            indent_guide(buffer_id, 6, 6, 1),
11770            indent_guide(buffer_id, 8, 8, 1),
11771        ],
11772        None,
11773        &mut cx,
11774    );
11775}
11776
11777#[gpui::test]
11778async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
11779    let (buffer_id, mut cx) = setup_indent_guides_editor(
11780        &"
11781        fn main() {
11782            let a = 1;
11783
11784            let c = 3;
11785
11786            if a == 3 {
11787                let b = 2;
11788            } else {
11789                let c = 3;
11790            }
11791        }"
11792        .unindent(),
11793        cx,
11794    )
11795    .await;
11796
11797    assert_indent_guides(
11798        1..10,
11799        vec![
11800            indent_guide(buffer_id, 1, 9, 0),
11801            indent_guide(buffer_id, 6, 6, 1),
11802            indent_guide(buffer_id, 8, 8, 1),
11803        ],
11804        None,
11805        &mut cx,
11806    );
11807}
11808
11809#[gpui::test]
11810async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
11811    let (buffer_id, mut cx) = setup_indent_guides_editor(
11812        &"
11813        block1
11814            block2
11815                block3
11816                    block4
11817            block2
11818        block1
11819        block1"
11820            .unindent(),
11821        cx,
11822    )
11823    .await;
11824
11825    assert_indent_guides(
11826        1..10,
11827        vec![
11828            indent_guide(buffer_id, 1, 4, 0),
11829            indent_guide(buffer_id, 2, 3, 1),
11830            indent_guide(buffer_id, 3, 3, 2),
11831        ],
11832        None,
11833        &mut cx,
11834    );
11835}
11836
11837#[gpui::test]
11838async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
11839    let (buffer_id, mut cx) = setup_indent_guides_editor(
11840        &"
11841        block1
11842            block2
11843                block3
11844
11845        block1
11846        block1"
11847            .unindent(),
11848        cx,
11849    )
11850    .await;
11851
11852    assert_indent_guides(
11853        0..6,
11854        vec![
11855            indent_guide(buffer_id, 1, 2, 0),
11856            indent_guide(buffer_id, 2, 2, 1),
11857        ],
11858        None,
11859        &mut cx,
11860    );
11861}
11862
11863#[gpui::test]
11864async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
11865    let (buffer_id, mut cx) = setup_indent_guides_editor(
11866        &"
11867        block1
11868
11869
11870
11871            block2
11872        "
11873        .unindent(),
11874        cx,
11875    )
11876    .await;
11877
11878    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
11879}
11880
11881#[gpui::test]
11882async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
11883    let (buffer_id, mut cx) = setup_indent_guides_editor(
11884        &"
11885        def a:
11886        \tb = 3
11887        \tif True:
11888        \t\tc = 4
11889        \t\td = 5
11890        \tprint(b)
11891        "
11892        .unindent(),
11893        cx,
11894    )
11895    .await;
11896
11897    assert_indent_guides(
11898        0..6,
11899        vec![
11900            indent_guide(buffer_id, 1, 6, 0),
11901            indent_guide(buffer_id, 3, 4, 1),
11902        ],
11903        None,
11904        &mut cx,
11905    );
11906}
11907
11908#[gpui::test]
11909async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
11910    let (buffer_id, mut cx) = setup_indent_guides_editor(
11911        &"
11912    fn main() {
11913        let a = 1;
11914    }"
11915        .unindent(),
11916        cx,
11917    )
11918    .await;
11919
11920    cx.update_editor(|editor, cx| {
11921        editor.change_selections(None, cx, |s| {
11922            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
11923        });
11924    });
11925
11926    assert_indent_guides(
11927        0..3,
11928        vec![indent_guide(buffer_id, 1, 1, 0)],
11929        Some(vec![0]),
11930        &mut cx,
11931    );
11932}
11933
11934#[gpui::test]
11935async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
11936    let (buffer_id, mut cx) = setup_indent_guides_editor(
11937        &"
11938    fn main() {
11939        if 1 == 2 {
11940            let a = 1;
11941        }
11942    }"
11943        .unindent(),
11944        cx,
11945    )
11946    .await;
11947
11948    cx.update_editor(|editor, cx| {
11949        editor.change_selections(None, cx, |s| {
11950            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
11951        });
11952    });
11953
11954    assert_indent_guides(
11955        0..4,
11956        vec![
11957            indent_guide(buffer_id, 1, 3, 0),
11958            indent_guide(buffer_id, 2, 2, 1),
11959        ],
11960        Some(vec![1]),
11961        &mut cx,
11962    );
11963
11964    cx.update_editor(|editor, cx| {
11965        editor.change_selections(None, cx, |s| {
11966            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
11967        });
11968    });
11969
11970    assert_indent_guides(
11971        0..4,
11972        vec![
11973            indent_guide(buffer_id, 1, 3, 0),
11974            indent_guide(buffer_id, 2, 2, 1),
11975        ],
11976        Some(vec![1]),
11977        &mut cx,
11978    );
11979
11980    cx.update_editor(|editor, cx| {
11981        editor.change_selections(None, cx, |s| {
11982            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
11983        });
11984    });
11985
11986    assert_indent_guides(
11987        0..4,
11988        vec![
11989            indent_guide(buffer_id, 1, 3, 0),
11990            indent_guide(buffer_id, 2, 2, 1),
11991        ],
11992        Some(vec![0]),
11993        &mut cx,
11994    );
11995}
11996
11997#[gpui::test]
11998async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
11999    let (buffer_id, mut cx) = setup_indent_guides_editor(
12000        &"
12001    fn main() {
12002        let a = 1;
12003
12004        let b = 2;
12005    }"
12006        .unindent(),
12007        cx,
12008    )
12009    .await;
12010
12011    cx.update_editor(|editor, cx| {
12012        editor.change_selections(None, cx, |s| {
12013            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12014        });
12015    });
12016
12017    assert_indent_guides(
12018        0..5,
12019        vec![indent_guide(buffer_id, 1, 3, 0)],
12020        Some(vec![0]),
12021        &mut cx,
12022    );
12023}
12024
12025#[gpui::test]
12026async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
12027    let (buffer_id, mut cx) = setup_indent_guides_editor(
12028        &"
12029    def m:
12030        a = 1
12031        pass"
12032            .unindent(),
12033        cx,
12034    )
12035    .await;
12036
12037    cx.update_editor(|editor, cx| {
12038        editor.change_selections(None, cx, |s| {
12039            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12040        });
12041    });
12042
12043    assert_indent_guides(
12044        0..3,
12045        vec![indent_guide(buffer_id, 1, 2, 0)],
12046        Some(vec![0]),
12047        &mut cx,
12048    );
12049}
12050
12051#[gpui::test]
12052fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
12053    init_test(cx, |_| {});
12054
12055    let editor = cx.add_window(|cx| {
12056        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
12057        build_editor(buffer, cx)
12058    });
12059
12060    let render_args = Arc::new(Mutex::new(None));
12061    let snapshot = editor
12062        .update(cx, |editor, cx| {
12063            let snapshot = editor.buffer().read(cx).snapshot(cx);
12064            let range =
12065                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
12066
12067            struct RenderArgs {
12068                row: MultiBufferRow,
12069                folded: bool,
12070                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
12071            }
12072
12073            let crease = Crease::new(
12074                range,
12075                FoldPlaceholder::test(),
12076                {
12077                    let toggle_callback = render_args.clone();
12078                    move |row, folded, callback, _cx| {
12079                        *toggle_callback.lock() = Some(RenderArgs {
12080                            row,
12081                            folded,
12082                            callback,
12083                        });
12084                        div()
12085                    }
12086                },
12087                |_row, _folded, _cx| div(),
12088            );
12089
12090            editor.insert_creases(Some(crease), cx);
12091            let snapshot = editor.snapshot(cx);
12092            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
12093            snapshot
12094        })
12095        .unwrap();
12096
12097    let render_args = render_args.lock().take().unwrap();
12098    assert_eq!(render_args.row, MultiBufferRow(1));
12099    assert_eq!(render_args.folded, false);
12100    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12101
12102    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
12103        .unwrap();
12104    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12105    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
12106
12107    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
12108        .unwrap();
12109    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12110    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12111}
12112
12113fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
12114    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
12115    point..point
12116}
12117
12118fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
12119    let (text, ranges) = marked_text_ranges(marked_text, true);
12120    assert_eq!(view.text(cx), text);
12121    assert_eq!(
12122        view.selections.ranges(cx),
12123        ranges,
12124        "Assert selections are {}",
12125        marked_text
12126    );
12127}
12128
12129/// Handle completion request passing a marked string specifying where the completion
12130/// should be triggered from using '|' character, what range should be replaced, and what completions
12131/// should be returned using '<' and '>' to delimit the range
12132pub fn handle_completion_request(
12133    cx: &mut EditorLspTestContext,
12134    marked_string: &str,
12135    completions: Vec<&'static str>,
12136    counter: Arc<AtomicUsize>,
12137) -> impl Future<Output = ()> {
12138    let complete_from_marker: TextRangeMarker = '|'.into();
12139    let replace_range_marker: TextRangeMarker = ('<', '>').into();
12140    let (_, mut marked_ranges) = marked_text_ranges_by(
12141        marked_string,
12142        vec![complete_from_marker.clone(), replace_range_marker.clone()],
12143    );
12144
12145    let complete_from_position =
12146        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
12147    let replace_range =
12148        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
12149
12150    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
12151        let completions = completions.clone();
12152        counter.fetch_add(1, atomic::Ordering::Release);
12153        async move {
12154            assert_eq!(params.text_document_position.text_document.uri, url.clone());
12155            assert_eq!(
12156                params.text_document_position.position,
12157                complete_from_position
12158            );
12159            Ok(Some(lsp::CompletionResponse::Array(
12160                completions
12161                    .iter()
12162                    .map(|completion_text| lsp::CompletionItem {
12163                        label: completion_text.to_string(),
12164                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12165                            range: replace_range,
12166                            new_text: completion_text.to_string(),
12167                        })),
12168                        ..Default::default()
12169                    })
12170                    .collect(),
12171            )))
12172        }
12173    });
12174
12175    async move {
12176        request.next().await;
12177    }
12178}
12179
12180fn handle_resolve_completion_request(
12181    cx: &mut EditorLspTestContext,
12182    edits: Option<Vec<(&'static str, &'static str)>>,
12183) -> impl Future<Output = ()> {
12184    let edits = edits.map(|edits| {
12185        edits
12186            .iter()
12187            .map(|(marked_string, new_text)| {
12188                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
12189                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
12190                lsp::TextEdit::new(replace_range, new_text.to_string())
12191            })
12192            .collect::<Vec<_>>()
12193    });
12194
12195    let mut request =
12196        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12197            let edits = edits.clone();
12198            async move {
12199                Ok(lsp::CompletionItem {
12200                    additional_text_edits: edits,
12201                    ..Default::default()
12202                })
12203            }
12204        });
12205
12206    async move {
12207        request.next().await;
12208    }
12209}
12210
12211pub(crate) fn update_test_language_settings(
12212    cx: &mut TestAppContext,
12213    f: impl Fn(&mut AllLanguageSettingsContent),
12214) {
12215    _ = cx.update(|cx| {
12216        SettingsStore::update_global(cx, |store, cx| {
12217            store.update_user_settings::<AllLanguageSettings>(cx, f);
12218        });
12219    });
12220}
12221
12222pub(crate) fn update_test_project_settings(
12223    cx: &mut TestAppContext,
12224    f: impl Fn(&mut ProjectSettings),
12225) {
12226    _ = cx.update(|cx| {
12227        SettingsStore::update_global(cx, |store, cx| {
12228            store.update_user_settings::<ProjectSettings>(cx, f);
12229        });
12230    });
12231}
12232
12233pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
12234    _ = cx.update(|cx| {
12235        cx.text_system()
12236            .add_fonts(vec![assets::Assets
12237                .load("fonts/zed-mono/zed-mono-extended.ttf")
12238                .unwrap()
12239                .unwrap()])
12240            .unwrap();
12241        let store = SettingsStore::test(cx);
12242        cx.set_global(store);
12243        theme::init(theme::LoadThemes::JustBase, cx);
12244        release_channel::init(SemanticVersion::default(), cx);
12245        client::init_settings(cx);
12246        language::init(cx);
12247        Project::init_settings(cx);
12248        workspace::init_settings(cx);
12249        crate::init(cx);
12250    });
12251
12252    update_test_language_settings(cx, f);
12253}
12254
12255pub(crate) fn rust_lang() -> Arc<Language> {
12256    Arc::new(Language::new(
12257        LanguageConfig {
12258            name: "Rust".into(),
12259            matcher: LanguageMatcher {
12260                path_suffixes: vec!["rs".to_string()],
12261                ..Default::default()
12262            },
12263            ..Default::default()
12264        },
12265        Some(tree_sitter_rust::language()),
12266    ))
12267}
12268
12269#[track_caller]
12270fn assert_hunk_revert(
12271    not_reverted_text_with_selections: &str,
12272    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
12273    expected_reverted_text_with_selections: &str,
12274    base_text: &str,
12275    cx: &mut EditorLspTestContext,
12276) {
12277    cx.set_state(not_reverted_text_with_selections);
12278    cx.update_editor(|editor, cx| {
12279        editor
12280            .buffer()
12281            .read(cx)
12282            .as_singleton()
12283            .unwrap()
12284            .update(cx, |buffer, cx| {
12285                buffer.set_diff_base(Some(base_text.into()), cx);
12286            });
12287    });
12288    cx.executor().run_until_parked();
12289
12290    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
12291        let snapshot = editor.buffer().read(cx).snapshot(cx);
12292        let reverted_hunk_statuses = snapshot
12293            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
12294            .map(|hunk| hunk_status(&hunk))
12295            .collect::<Vec<_>>();
12296
12297        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
12298        reverted_hunk_statuses
12299    });
12300    cx.executor().run_until_parked();
12301    cx.assert_editor_state(expected_reverted_text_with_selections);
12302    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
12303}