editor_tests.rs

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