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