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