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