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, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   14    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    ParsedMarkdown, Point,
   25};
   26use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   27use multi_buffer::MultiBufferIndentGuide;
   28use parking_lot::Mutex;
   29use project::FakeFs;
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::sync::atomic;
   36use std::sync::atomic::AtomicUsize;
   37use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   38use unindent::Unindent;
   39use util::{
   40    assert_set_eq,
   41    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   42};
   43use workspace::{
   44    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   45    NavigationEntry, ViewId,
   46};
   47
   48#[gpui::test]
   49fn test_edit_events(cx: &mut TestAppContext) {
   50    init_test(cx, |_| {});
   51
   52    let buffer = cx.new_model(|cx| {
   53        let mut buffer = language::Buffer::local("123456", cx);
   54        buffer.set_group_interval(Duration::from_secs(1));
   55        buffer
   56    });
   57
   58    let events = Rc::new(RefCell::new(Vec::new()));
   59    let editor1 = cx.add_window({
   60        let events = events.clone();
   61        |cx| {
   62            let view = cx.view().clone();
   63            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   64                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   65                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   66                _ => {}
   67            })
   68            .detach();
   69            Editor::for_buffer(buffer.clone(), None, cx)
   70        }
   71    });
   72
   73    let editor2 = cx.add_window({
   74        let events = events.clone();
   75        |cx| {
   76            cx.subscribe(
   77                &cx.view().clone(),
   78                move |_, _, event: &EditorEvent, _| match event {
   79                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   80                    EditorEvent::BufferEdited => {
   81                        events.borrow_mut().push(("editor2", "buffer edited"))
   82                    }
   83                    _ => {}
   84                },
   85            )
   86            .detach();
   87            Editor::for_buffer(buffer.clone(), None, cx)
   88        }
   89    });
   90
   91    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   92
   93    // Mutating editor 1 will emit an `Edited` event only for that editor.
   94    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   95    assert_eq!(
   96        mem::take(&mut *events.borrow_mut()),
   97        [
   98            ("editor1", "edited"),
   99            ("editor1", "buffer edited"),
  100            ("editor2", "buffer edited"),
  101        ]
  102    );
  103
  104    // Mutating editor 2 will emit an `Edited` event only for that editor.
  105    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  106    assert_eq!(
  107        mem::take(&mut *events.borrow_mut()),
  108        [
  109            ("editor2", "edited"),
  110            ("editor1", "buffer edited"),
  111            ("editor2", "buffer edited"),
  112        ]
  113    );
  114
  115    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  116    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  117    assert_eq!(
  118        mem::take(&mut *events.borrow_mut()),
  119        [
  120            ("editor1", "edited"),
  121            ("editor1", "buffer edited"),
  122            ("editor2", "buffer edited"),
  123        ]
  124    );
  125
  126    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  127    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  128    assert_eq!(
  129        mem::take(&mut *events.borrow_mut()),
  130        [
  131            ("editor1", "edited"),
  132            ("editor1", "buffer edited"),
  133            ("editor2", "buffer edited"),
  134        ]
  135    );
  136
  137    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  138    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  139    assert_eq!(
  140        mem::take(&mut *events.borrow_mut()),
  141        [
  142            ("editor2", "edited"),
  143            ("editor1", "buffer edited"),
  144            ("editor2", "buffer edited"),
  145        ]
  146    );
  147
  148    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  149    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  150    assert_eq!(
  151        mem::take(&mut *events.borrow_mut()),
  152        [
  153            ("editor2", "edited"),
  154            ("editor1", "buffer edited"),
  155            ("editor2", "buffer edited"),
  156        ]
  157    );
  158
  159    // No event is emitted when the mutation is a no-op.
  160    _ = editor2.update(cx, |editor, cx| {
  161        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  162
  163        editor.backspace(&Backspace, cx);
  164    });
  165    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  166}
  167
  168#[gpui::test]
  169fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  170    init_test(cx, |_| {});
  171
  172    let mut now = Instant::now();
  173    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  174    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  175    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  176    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  177
  178    _ = editor.update(cx, |editor, cx| {
  179        editor.start_transaction_at(now, cx);
  180        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  181
  182        editor.insert("cd", cx);
  183        editor.end_transaction_at(now, cx);
  184        assert_eq!(editor.text(cx), "12cd56");
  185        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  186
  187        editor.start_transaction_at(now, cx);
  188        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  189        editor.insert("e", cx);
  190        editor.end_transaction_at(now, cx);
  191        assert_eq!(editor.text(cx), "12cde6");
  192        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  193
  194        now += group_interval + Duration::from_millis(1);
  195        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  196
  197        // Simulate an edit in another editor
  198        _ = buffer.update(cx, |buffer, cx| {
  199            buffer.start_transaction_at(now, cx);
  200            buffer.edit([(0..1, "a")], None, cx);
  201            buffer.edit([(1..1, "b")], None, cx);
  202            buffer.end_transaction_at(now, cx);
  203        });
  204
  205        assert_eq!(editor.text(cx), "ab2cde6");
  206        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  207
  208        // Last transaction happened past the group interval in a different editor.
  209        // Undo it individually and don't restore selections.
  210        editor.undo(&Undo, cx);
  211        assert_eq!(editor.text(cx), "12cde6");
  212        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  213
  214        // First two transactions happened within the group interval in this editor.
  215        // Undo them together and restore selections.
  216        editor.undo(&Undo, cx);
  217        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  218        assert_eq!(editor.text(cx), "123456");
  219        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  220
  221        // Redo the first two transactions together.
  222        editor.redo(&Redo, cx);
  223        assert_eq!(editor.text(cx), "12cde6");
  224        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  225
  226        // Redo the last transaction on its own.
  227        editor.redo(&Redo, cx);
  228        assert_eq!(editor.text(cx), "ab2cde6");
  229        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  230
  231        // Test empty transactions.
  232        editor.start_transaction_at(now, cx);
  233        editor.end_transaction_at(now, cx);
  234        editor.undo(&Undo, cx);
  235        assert_eq!(editor.text(cx), "12cde6");
  236    });
  237}
  238
  239#[gpui::test]
  240fn test_ime_composition(cx: &mut TestAppContext) {
  241    init_test(cx, |_| {});
  242
  243    let buffer = cx.new_model(|cx| {
  244        let mut buffer = language::Buffer::local("abcde", cx);
  245        // Ensure automatic grouping doesn't occur.
  246        buffer.set_group_interval(Duration::ZERO);
  247        buffer
  248    });
  249
  250    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  251    cx.add_window(|cx| {
  252        let mut editor = build_editor(buffer.clone(), cx);
  253
  254        // Start a new IME composition.
  255        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  256        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  257        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  258        assert_eq!(editor.text(cx), "äbcde");
  259        assert_eq!(
  260            editor.marked_text_ranges(cx),
  261            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  262        );
  263
  264        // Finalize IME composition.
  265        editor.replace_text_in_range(None, "ā", cx);
  266        assert_eq!(editor.text(cx), "ābcde");
  267        assert_eq!(editor.marked_text_ranges(cx), None);
  268
  269        // IME composition edits are grouped and are undone/redone at once.
  270        editor.undo(&Default::default(), cx);
  271        assert_eq!(editor.text(cx), "abcde");
  272        assert_eq!(editor.marked_text_ranges(cx), None);
  273        editor.redo(&Default::default(), cx);
  274        assert_eq!(editor.text(cx), "ābcde");
  275        assert_eq!(editor.marked_text_ranges(cx), None);
  276
  277        // Start a new IME composition.
  278        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  279        assert_eq!(
  280            editor.marked_text_ranges(cx),
  281            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  282        );
  283
  284        // Undoing during an IME composition cancels it.
  285        editor.undo(&Default::default(), cx);
  286        assert_eq!(editor.text(cx), "ābcde");
  287        assert_eq!(editor.marked_text_ranges(cx), None);
  288
  289        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  290        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  291        assert_eq!(editor.text(cx), "ābcdè");
  292        assert_eq!(
  293            editor.marked_text_ranges(cx),
  294            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  295        );
  296
  297        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  298        editor.replace_text_in_range(Some(4..999), "ę", cx);
  299        assert_eq!(editor.text(cx), "ābcdę");
  300        assert_eq!(editor.marked_text_ranges(cx), None);
  301
  302        // Start a new IME composition with multiple cursors.
  303        editor.change_selections(None, cx, |s| {
  304            s.select_ranges([
  305                OffsetUtf16(1)..OffsetUtf16(1),
  306                OffsetUtf16(3)..OffsetUtf16(3),
  307                OffsetUtf16(5)..OffsetUtf16(5),
  308            ])
  309        });
  310        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  311        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  312        assert_eq!(
  313            editor.marked_text_ranges(cx),
  314            Some(vec![
  315                OffsetUtf16(0)..OffsetUtf16(3),
  316                OffsetUtf16(4)..OffsetUtf16(7),
  317                OffsetUtf16(8)..OffsetUtf16(11)
  318            ])
  319        );
  320
  321        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  322        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  323        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  324        assert_eq!(
  325            editor.marked_text_ranges(cx),
  326            Some(vec![
  327                OffsetUtf16(1)..OffsetUtf16(2),
  328                OffsetUtf16(5)..OffsetUtf16(6),
  329                OffsetUtf16(9)..OffsetUtf16(10)
  330            ])
  331        );
  332
  333        // Finalize IME composition with multiple cursors.
  334        editor.replace_text_in_range(Some(9..10), "2", cx);
  335        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  336        assert_eq!(editor.marked_text_ranges(cx), None);
  337
  338        editor
  339    });
  340}
  341
  342#[gpui::test]
  343fn test_selection_with_mouse(cx: &mut TestAppContext) {
  344    init_test(cx, |_| {});
  345
  346    let editor = cx.add_window(|cx| {
  347        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  348        build_editor(buffer, cx)
  349    });
  350
  351    _ = editor.update(cx, |view, cx| {
  352        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  353    });
  354    assert_eq!(
  355        editor
  356            .update(cx, |view, cx| view.selections.display_ranges(cx))
  357            .unwrap(),
  358        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  359    );
  360
  361    _ = editor.update(cx, |view, cx| {
  362        view.update_selection(
  363            DisplayPoint::new(DisplayRow(3), 3),
  364            0,
  365            gpui::Point::<f32>::default(),
  366            cx,
  367        );
  368    });
  369
  370    assert_eq!(
  371        editor
  372            .update(cx, |view, cx| view.selections.display_ranges(cx))
  373            .unwrap(),
  374        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  375    );
  376
  377    _ = editor.update(cx, |view, cx| {
  378        view.update_selection(
  379            DisplayPoint::new(DisplayRow(1), 1),
  380            0,
  381            gpui::Point::<f32>::default(),
  382            cx,
  383        );
  384    });
  385
  386    assert_eq!(
  387        editor
  388            .update(cx, |view, cx| view.selections.display_ranges(cx))
  389            .unwrap(),
  390        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  391    );
  392
  393    _ = editor.update(cx, |view, cx| {
  394        view.end_selection(cx);
  395        view.update_selection(
  396            DisplayPoint::new(DisplayRow(3), 3),
  397            0,
  398            gpui::Point::<f32>::default(),
  399            cx,
  400        );
  401    });
  402
  403    assert_eq!(
  404        editor
  405            .update(cx, |view, cx| view.selections.display_ranges(cx))
  406            .unwrap(),
  407        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  408    );
  409
  410    _ = editor.update(cx, |view, cx| {
  411        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  412        view.update_selection(
  413            DisplayPoint::new(DisplayRow(0), 0),
  414            0,
  415            gpui::Point::<f32>::default(),
  416            cx,
  417        );
  418    });
  419
  420    assert_eq!(
  421        editor
  422            .update(cx, |view, cx| view.selections.display_ranges(cx))
  423            .unwrap(),
  424        [
  425            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  426            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  427        ]
  428    );
  429
  430    _ = editor.update(cx, |view, cx| {
  431        view.end_selection(cx);
  432    });
  433
  434    assert_eq!(
  435        editor
  436            .update(cx, |view, cx| view.selections.display_ranges(cx))
  437            .unwrap(),
  438        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  439    );
  440}
  441
  442#[gpui::test]
  443fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  444    init_test(cx, |_| {});
  445
  446    let editor = cx.add_window(|cx| {
  447        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  448        build_editor(buffer, cx)
  449    });
  450
  451    _ = editor.update(cx, |view, cx| {
  452        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  453    });
  454
  455    _ = editor.update(cx, |view, cx| {
  456        view.end_selection(cx);
  457    });
  458
  459    _ = editor.update(cx, |view, cx| {
  460        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  461    });
  462
  463    _ = editor.update(cx, |view, cx| {
  464        view.end_selection(cx);
  465    });
  466
  467    assert_eq!(
  468        editor
  469            .update(cx, |view, cx| view.selections.display_ranges(cx))
  470            .unwrap(),
  471        [
  472            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  473            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  474        ]
  475    );
  476
  477    _ = editor.update(cx, |view, cx| {
  478        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  479    });
  480
  481    _ = editor.update(cx, |view, cx| {
  482        view.end_selection(cx);
  483    });
  484
  485    assert_eq!(
  486        editor
  487            .update(cx, |view, cx| view.selections.display_ranges(cx))
  488            .unwrap(),
  489        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  490    );
  491}
  492
  493#[gpui::test]
  494fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  495    init_test(cx, |_| {});
  496
  497    let view = cx.add_window(|cx| {
  498        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  499        build_editor(buffer, cx)
  500    });
  501
  502    _ = view.update(cx, |view, cx| {
  503        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  504        assert_eq!(
  505            view.selections.display_ranges(cx),
  506            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  507        );
  508    });
  509
  510    _ = view.update(cx, |view, cx| {
  511        view.update_selection(
  512            DisplayPoint::new(DisplayRow(3), 3),
  513            0,
  514            gpui::Point::<f32>::default(),
  515            cx,
  516        );
  517        assert_eq!(
  518            view.selections.display_ranges(cx),
  519            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  520        );
  521    });
  522
  523    _ = view.update(cx, |view, cx| {
  524        view.cancel(&Cancel, cx);
  525        view.update_selection(
  526            DisplayPoint::new(DisplayRow(1), 1),
  527            0,
  528            gpui::Point::<f32>::default(),
  529            cx,
  530        );
  531        assert_eq!(
  532            view.selections.display_ranges(cx),
  533            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  534        );
  535    });
  536}
  537
  538#[gpui::test]
  539fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  540    init_test(cx, |_| {});
  541
  542    let view = cx.add_window(|cx| {
  543        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  544        build_editor(buffer, cx)
  545    });
  546
  547    _ = view.update(cx, |view, cx| {
  548        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  549        assert_eq!(
  550            view.selections.display_ranges(cx),
  551            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  552        );
  553
  554        view.move_down(&Default::default(), cx);
  555        assert_eq!(
  556            view.selections.display_ranges(cx),
  557            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  558        );
  559
  560        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  561        assert_eq!(
  562            view.selections.display_ranges(cx),
  563            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  564        );
  565
  566        view.move_up(&Default::default(), cx);
  567        assert_eq!(
  568            view.selections.display_ranges(cx),
  569            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  570        );
  571    });
  572}
  573
  574#[gpui::test]
  575fn test_clone(cx: &mut TestAppContext) {
  576    init_test(cx, |_| {});
  577
  578    let (text, selection_ranges) = marked_text_ranges(
  579        indoc! {"
  580            one
  581            two
  582            threeˇ
  583            four
  584            fiveˇ
  585        "},
  586        true,
  587    );
  588
  589    let editor = cx.add_window(|cx| {
  590        let buffer = MultiBuffer::build_simple(&text, cx);
  591        build_editor(buffer, cx)
  592    });
  593
  594    _ = editor.update(cx, |editor, cx| {
  595        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  596        editor.fold_ranges(
  597            [
  598                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  599                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  600            ],
  601            true,
  602            cx,
  603        );
  604    });
  605
  606    let cloned_editor = editor
  607        .update(cx, |editor, cx| {
  608            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  609        })
  610        .unwrap()
  611        .unwrap();
  612
  613    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  614    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  615
  616    assert_eq!(
  617        cloned_editor
  618            .update(cx, |e, cx| e.display_text(cx))
  619            .unwrap(),
  620        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  621    );
  622    assert_eq!(
  623        cloned_snapshot
  624            .folds_in_range(0..text.len())
  625            .collect::<Vec<_>>(),
  626        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  627    );
  628    assert_set_eq!(
  629        cloned_editor
  630            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  631            .unwrap(),
  632        editor
  633            .update(cx, |editor, cx| editor.selections.ranges(cx))
  634            .unwrap()
  635    );
  636    assert_set_eq!(
  637        cloned_editor
  638            .update(cx, |e, cx| e.selections.display_ranges(cx))
  639            .unwrap(),
  640        editor
  641            .update(cx, |e, cx| e.selections.display_ranges(cx))
  642            .unwrap()
  643    );
  644}
  645
  646#[gpui::test]
  647async fn test_navigation_history(cx: &mut TestAppContext) {
  648    init_test(cx, |_| {});
  649
  650    use workspace::item::Item;
  651
  652    let fs = FakeFs::new(cx.executor());
  653    let project = Project::test(fs, [], cx).await;
  654    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  655    let pane = workspace
  656        .update(cx, |workspace, _| workspace.active_pane().clone())
  657        .unwrap();
  658
  659    _ = workspace.update(cx, |_v, cx| {
  660        cx.new_view(|cx| {
  661            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  662            let mut editor = build_editor(buffer.clone(), cx);
  663            let handle = cx.view();
  664            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  665
  666            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  667                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  668            }
  669
  670            // Move the cursor a small distance.
  671            // Nothing is added to the navigation history.
  672            editor.change_selections(None, cx, |s| {
  673                s.select_display_ranges([
  674                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  675                ])
  676            });
  677            editor.change_selections(None, cx, |s| {
  678                s.select_display_ranges([
  679                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  680                ])
  681            });
  682            assert!(pop_history(&mut editor, cx).is_none());
  683
  684            // Move the cursor a large distance.
  685            // The history can jump back to the previous position.
  686            editor.change_selections(None, cx, |s| {
  687                s.select_display_ranges([
  688                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  689                ])
  690            });
  691            let nav_entry = pop_history(&mut editor, cx).unwrap();
  692            editor.navigate(nav_entry.data.unwrap(), cx);
  693            assert_eq!(nav_entry.item.id(), cx.entity_id());
  694            assert_eq!(
  695                editor.selections.display_ranges(cx),
  696                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  697            );
  698            assert!(pop_history(&mut editor, cx).is_none());
  699
  700            // Move the cursor a small distance via the mouse.
  701            // Nothing is added to the navigation history.
  702            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  703            editor.end_selection(cx);
  704            assert_eq!(
  705                editor.selections.display_ranges(cx),
  706                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  707            );
  708            assert!(pop_history(&mut editor, cx).is_none());
  709
  710            // Move the cursor a large distance via the mouse.
  711            // The history can jump back to the previous position.
  712            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  713            editor.end_selection(cx);
  714            assert_eq!(
  715                editor.selections.display_ranges(cx),
  716                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  717            );
  718            let nav_entry = pop_history(&mut editor, cx).unwrap();
  719            editor.navigate(nav_entry.data.unwrap(), cx);
  720            assert_eq!(nav_entry.item.id(), cx.entity_id());
  721            assert_eq!(
  722                editor.selections.display_ranges(cx),
  723                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  724            );
  725            assert!(pop_history(&mut editor, cx).is_none());
  726
  727            // Set scroll position to check later
  728            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  729            let original_scroll_position = editor.scroll_manager.anchor();
  730
  731            // Jump to the end of the document and adjust scroll
  732            editor.move_to_end(&MoveToEnd, cx);
  733            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  734            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  735
  736            let nav_entry = pop_history(&mut editor, cx).unwrap();
  737            editor.navigate(nav_entry.data.unwrap(), cx);
  738            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  739
  740            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  741            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  742            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  743            let invalid_point = Point::new(9999, 0);
  744            editor.navigate(
  745                Box::new(NavigationData {
  746                    cursor_anchor: invalid_anchor,
  747                    cursor_position: invalid_point,
  748                    scroll_anchor: ScrollAnchor {
  749                        anchor: invalid_anchor,
  750                        offset: Default::default(),
  751                    },
  752                    scroll_top_row: invalid_point.row,
  753                }),
  754                cx,
  755            );
  756            assert_eq!(
  757                editor.selections.display_ranges(cx),
  758                &[editor.max_point(cx)..editor.max_point(cx)]
  759            );
  760            assert_eq!(
  761                editor.scroll_position(cx),
  762                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  763            );
  764
  765            editor
  766        })
  767    });
  768}
  769
  770#[gpui::test]
  771fn test_cancel(cx: &mut TestAppContext) {
  772    init_test(cx, |_| {});
  773
  774    let view = cx.add_window(|cx| {
  775        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  776        build_editor(buffer, cx)
  777    });
  778
  779    _ = view.update(cx, |view, cx| {
  780        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  781        view.update_selection(
  782            DisplayPoint::new(DisplayRow(1), 1),
  783            0,
  784            gpui::Point::<f32>::default(),
  785            cx,
  786        );
  787        view.end_selection(cx);
  788
  789        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  790        view.update_selection(
  791            DisplayPoint::new(DisplayRow(0), 3),
  792            0,
  793            gpui::Point::<f32>::default(),
  794            cx,
  795        );
  796        view.end_selection(cx);
  797        assert_eq!(
  798            view.selections.display_ranges(cx),
  799            [
  800                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  801                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  802            ]
  803        );
  804    });
  805
  806    _ = view.update(cx, |view, cx| {
  807        view.cancel(&Cancel, cx);
  808        assert_eq!(
  809            view.selections.display_ranges(cx),
  810            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  811        );
  812    });
  813
  814    _ = view.update(cx, |view, cx| {
  815        view.cancel(&Cancel, cx);
  816        assert_eq!(
  817            view.selections.display_ranges(cx),
  818            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  819        );
  820    });
  821}
  822
  823#[gpui::test]
  824fn test_fold_action(cx: &mut TestAppContext) {
  825    init_test(cx, |_| {});
  826
  827    let view = cx.add_window(|cx| {
  828        let buffer = MultiBuffer::build_simple(
  829            &"
  830                impl Foo {
  831                    // Hello!
  832
  833                    fn a() {
  834                        1
  835                    }
  836
  837                    fn b() {
  838                        2
  839                    }
  840
  841                    fn c() {
  842                        3
  843                    }
  844                }
  845            "
  846            .unindent(),
  847            cx,
  848        );
  849        build_editor(buffer.clone(), cx)
  850    });
  851
  852    _ = view.update(cx, |view, cx| {
  853        view.change_selections(None, cx, |s| {
  854            s.select_display_ranges([
  855                DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(12), 0)
  856            ]);
  857        });
  858        view.fold(&Fold, cx);
  859        assert_eq!(
  860            view.display_text(cx),
  861            "
  862                impl Foo {
  863                    // Hello!
  864
  865                    fn a() {
  866                        1
  867                    }
  868
  869                    fn b() {⋯
  870                    }
  871
  872                    fn c() {⋯
  873                    }
  874                }
  875            "
  876            .unindent(),
  877        );
  878
  879        view.fold(&Fold, cx);
  880        assert_eq!(
  881            view.display_text(cx),
  882            "
  883                impl Foo {⋯
  884                }
  885            "
  886            .unindent(),
  887        );
  888
  889        view.unfold_lines(&UnfoldLines, cx);
  890        assert_eq!(
  891            view.display_text(cx),
  892            "
  893                impl Foo {
  894                    // Hello!
  895
  896                    fn a() {
  897                        1
  898                    }
  899
  900                    fn b() {⋯
  901                    }
  902
  903                    fn c() {⋯
  904                    }
  905                }
  906            "
  907            .unindent(),
  908        );
  909
  910        view.unfold_lines(&UnfoldLines, cx);
  911        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  912    });
  913}
  914
  915#[gpui::test]
  916fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  917    init_test(cx, |_| {});
  918
  919    let view = cx.add_window(|cx| {
  920        let buffer = MultiBuffer::build_simple(
  921            &"
  922                class Foo:
  923                    # Hello!
  924
  925                    def a():
  926                        print(1)
  927
  928                    def b():
  929                        print(2)
  930
  931                    def c():
  932                        print(3)
  933            "
  934            .unindent(),
  935            cx,
  936        );
  937        build_editor(buffer.clone(), cx)
  938    });
  939
  940    _ = view.update(cx, |view, cx| {
  941        view.change_selections(None, cx, |s| {
  942            s.select_display_ranges([
  943                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(10), 0)
  944            ]);
  945        });
  946        view.fold(&Fold, cx);
  947        assert_eq!(
  948            view.display_text(cx),
  949            "
  950                class Foo:
  951                    # Hello!
  952
  953                    def a():
  954                        print(1)
  955
  956                    def b():⋯
  957
  958                    def c():⋯
  959            "
  960            .unindent(),
  961        );
  962
  963        view.fold(&Fold, cx);
  964        assert_eq!(
  965            view.display_text(cx),
  966            "
  967                class Foo:⋯
  968            "
  969            .unindent(),
  970        );
  971
  972        view.unfold_lines(&UnfoldLines, cx);
  973        assert_eq!(
  974            view.display_text(cx),
  975            "
  976                class Foo:
  977                    # Hello!
  978
  979                    def a():
  980                        print(1)
  981
  982                    def b():⋯
  983
  984                    def c():⋯
  985            "
  986            .unindent(),
  987        );
  988
  989        view.unfold_lines(&UnfoldLines, cx);
  990        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  991    });
  992}
  993
  994#[gpui::test]
  995fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  996    init_test(cx, |_| {});
  997
  998    let view = cx.add_window(|cx| {
  999        let buffer = MultiBuffer::build_simple(
 1000            &"
 1001                class Foo:
 1002                    # Hello!
 1003
 1004                    def a():
 1005                        print(1)
 1006
 1007                    def b():
 1008                        print(2)
 1009
 1010
 1011                    def c():
 1012                        print(3)
 1013
 1014
 1015            "
 1016            .unindent(),
 1017            cx,
 1018        );
 1019        build_editor(buffer.clone(), cx)
 1020    });
 1021
 1022    _ = view.update(cx, |view, cx| {
 1023        view.change_selections(None, cx, |s| {
 1024            s.select_display_ranges([
 1025                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1026            ]);
 1027        });
 1028        view.fold(&Fold, cx);
 1029        assert_eq!(
 1030            view.display_text(cx),
 1031            "
 1032                class Foo:
 1033                    # Hello!
 1034
 1035                    def a():
 1036                        print(1)
 1037
 1038                    def b():⋯
 1039
 1040
 1041                    def c():⋯
 1042
 1043
 1044            "
 1045            .unindent(),
 1046        );
 1047
 1048        view.fold(&Fold, cx);
 1049        assert_eq!(
 1050            view.display_text(cx),
 1051            "
 1052                class Foo:⋯
 1053
 1054
 1055            "
 1056            .unindent(),
 1057        );
 1058
 1059        view.unfold_lines(&UnfoldLines, cx);
 1060        assert_eq!(
 1061            view.display_text(cx),
 1062            "
 1063                class Foo:
 1064                    # Hello!
 1065
 1066                    def a():
 1067                        print(1)
 1068
 1069                    def b():⋯
 1070
 1071
 1072                    def c():⋯
 1073
 1074
 1075            "
 1076            .unindent(),
 1077        );
 1078
 1079        view.unfold_lines(&UnfoldLines, cx);
 1080        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1081    });
 1082}
 1083
 1084#[gpui::test]
 1085fn test_move_cursor(cx: &mut TestAppContext) {
 1086    init_test(cx, |_| {});
 1087
 1088    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1089    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1090
 1091    _ = buffer.update(cx, |buffer, cx| {
 1092        buffer.edit(
 1093            vec![
 1094                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1095                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1096            ],
 1097            None,
 1098            cx,
 1099        );
 1100    });
 1101    _ = view.update(cx, |view, cx| {
 1102        assert_eq!(
 1103            view.selections.display_ranges(cx),
 1104            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1105        );
 1106
 1107        view.move_down(&MoveDown, cx);
 1108        assert_eq!(
 1109            view.selections.display_ranges(cx),
 1110            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1111        );
 1112
 1113        view.move_right(&MoveRight, cx);
 1114        assert_eq!(
 1115            view.selections.display_ranges(cx),
 1116            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1117        );
 1118
 1119        view.move_left(&MoveLeft, cx);
 1120        assert_eq!(
 1121            view.selections.display_ranges(cx),
 1122            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1123        );
 1124
 1125        view.move_up(&MoveUp, cx);
 1126        assert_eq!(
 1127            view.selections.display_ranges(cx),
 1128            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1129        );
 1130
 1131        view.move_to_end(&MoveToEnd, cx);
 1132        assert_eq!(
 1133            view.selections.display_ranges(cx),
 1134            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1135        );
 1136
 1137        view.move_to_beginning(&MoveToBeginning, cx);
 1138        assert_eq!(
 1139            view.selections.display_ranges(cx),
 1140            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1141        );
 1142
 1143        view.change_selections(None, cx, |s| {
 1144            s.select_display_ranges([
 1145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1146            ]);
 1147        });
 1148        view.select_to_beginning(&SelectToBeginning, cx);
 1149        assert_eq!(
 1150            view.selections.display_ranges(cx),
 1151            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1152        );
 1153
 1154        view.select_to_end(&SelectToEnd, cx);
 1155        assert_eq!(
 1156            view.selections.display_ranges(cx),
 1157            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1158        );
 1159    });
 1160}
 1161
 1162// TODO: Re-enable this test
 1163#[cfg(target_os = "macos")]
 1164#[gpui::test]
 1165fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1166    init_test(cx, |_| {});
 1167
 1168    let view = cx.add_window(|cx| {
 1169        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1170        build_editor(buffer.clone(), cx)
 1171    });
 1172
 1173    assert_eq!('ⓐ'.len_utf8(), 3);
 1174    assert_eq!('α'.len_utf8(), 2);
 1175
 1176    _ = view.update(cx, |view, cx| {
 1177        view.fold_ranges(
 1178            vec![
 1179                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1180                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1181                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1182            ],
 1183            true,
 1184            cx,
 1185        );
 1186        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1187
 1188        view.move_right(&MoveRight, cx);
 1189        assert_eq!(
 1190            view.selections.display_ranges(cx),
 1191            &[empty_range(0, "".len())]
 1192        );
 1193        view.move_right(&MoveRight, cx);
 1194        assert_eq!(
 1195            view.selections.display_ranges(cx),
 1196            &[empty_range(0, "ⓐⓑ".len())]
 1197        );
 1198        view.move_right(&MoveRight, cx);
 1199        assert_eq!(
 1200            view.selections.display_ranges(cx),
 1201            &[empty_range(0, "ⓐⓑ⋯".len())]
 1202        );
 1203
 1204        view.move_down(&MoveDown, cx);
 1205        assert_eq!(
 1206            view.selections.display_ranges(cx),
 1207            &[empty_range(1, "ab⋯e".len())]
 1208        );
 1209        view.move_left(&MoveLeft, cx);
 1210        assert_eq!(
 1211            view.selections.display_ranges(cx),
 1212            &[empty_range(1, "ab⋯".len())]
 1213        );
 1214        view.move_left(&MoveLeft, cx);
 1215        assert_eq!(
 1216            view.selections.display_ranges(cx),
 1217            &[empty_range(1, "ab".len())]
 1218        );
 1219        view.move_left(&MoveLeft, cx);
 1220        assert_eq!(
 1221            view.selections.display_ranges(cx),
 1222            &[empty_range(1, "a".len())]
 1223        );
 1224
 1225        view.move_down(&MoveDown, cx);
 1226        assert_eq!(
 1227            view.selections.display_ranges(cx),
 1228            &[empty_range(2, "α".len())]
 1229        );
 1230        view.move_right(&MoveRight, cx);
 1231        assert_eq!(
 1232            view.selections.display_ranges(cx),
 1233            &[empty_range(2, "αβ".len())]
 1234        );
 1235        view.move_right(&MoveRight, cx);
 1236        assert_eq!(
 1237            view.selections.display_ranges(cx),
 1238            &[empty_range(2, "αβ⋯".len())]
 1239        );
 1240        view.move_right(&MoveRight, cx);
 1241        assert_eq!(
 1242            view.selections.display_ranges(cx),
 1243            &[empty_range(2, "αβ⋯ε".len())]
 1244        );
 1245
 1246        view.move_up(&MoveUp, cx);
 1247        assert_eq!(
 1248            view.selections.display_ranges(cx),
 1249            &[empty_range(1, "ab⋯e".len())]
 1250        );
 1251        view.move_down(&MoveDown, cx);
 1252        assert_eq!(
 1253            view.selections.display_ranges(cx),
 1254            &[empty_range(2, "αβ⋯ε".len())]
 1255        );
 1256        view.move_up(&MoveUp, cx);
 1257        assert_eq!(
 1258            view.selections.display_ranges(cx),
 1259            &[empty_range(1, "ab⋯e".len())]
 1260        );
 1261
 1262        view.move_up(&MoveUp, cx);
 1263        assert_eq!(
 1264            view.selections.display_ranges(cx),
 1265            &[empty_range(0, "ⓐⓑ".len())]
 1266        );
 1267        view.move_left(&MoveLeft, cx);
 1268        assert_eq!(
 1269            view.selections.display_ranges(cx),
 1270            &[empty_range(0, "".len())]
 1271        );
 1272        view.move_left(&MoveLeft, cx);
 1273        assert_eq!(
 1274            view.selections.display_ranges(cx),
 1275            &[empty_range(0, "".len())]
 1276        );
 1277    });
 1278}
 1279
 1280#[gpui::test]
 1281fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1282    init_test(cx, |_| {});
 1283
 1284    let view = cx.add_window(|cx| {
 1285        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1286        build_editor(buffer.clone(), cx)
 1287    });
 1288    _ = view.update(cx, |view, cx| {
 1289        view.change_selections(None, cx, |s| {
 1290            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1291        });
 1292        view.move_down(&MoveDown, cx);
 1293        assert_eq!(
 1294            view.selections.display_ranges(cx),
 1295            &[empty_range(1, "abcd".len())]
 1296        );
 1297
 1298        view.move_down(&MoveDown, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(2, "αβγ".len())]
 1302        );
 1303
 1304        view.move_down(&MoveDown, cx);
 1305        assert_eq!(
 1306            view.selections.display_ranges(cx),
 1307            &[empty_range(3, "abcd".len())]
 1308        );
 1309
 1310        view.move_down(&MoveDown, cx);
 1311        assert_eq!(
 1312            view.selections.display_ranges(cx),
 1313            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1314        );
 1315
 1316        view.move_up(&MoveUp, cx);
 1317        assert_eq!(
 1318            view.selections.display_ranges(cx),
 1319            &[empty_range(3, "abcd".len())]
 1320        );
 1321
 1322        view.move_up(&MoveUp, cx);
 1323        assert_eq!(
 1324            view.selections.display_ranges(cx),
 1325            &[empty_range(2, "αβγ".len())]
 1326        );
 1327    });
 1328}
 1329
 1330#[gpui::test]
 1331fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1332    init_test(cx, |_| {});
 1333    let move_to_beg = MoveToBeginningOfLine {
 1334        stop_at_soft_wraps: true,
 1335    };
 1336
 1337    let move_to_end = MoveToEndOfLine {
 1338        stop_at_soft_wraps: true,
 1339    };
 1340
 1341    let view = cx.add_window(|cx| {
 1342        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1343        build_editor(buffer, cx)
 1344    });
 1345    _ = view.update(cx, |view, cx| {
 1346        view.change_selections(None, cx, |s| {
 1347            s.select_display_ranges([
 1348                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1349                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1350            ]);
 1351        });
 1352    });
 1353
 1354    _ = view.update(cx, |view, cx| {
 1355        view.move_to_beginning_of_line(&move_to_beg, cx);
 1356        assert_eq!(
 1357            view.selections.display_ranges(cx),
 1358            &[
 1359                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1360                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1361            ]
 1362        );
 1363    });
 1364
 1365    _ = view.update(cx, |view, cx| {
 1366        view.move_to_beginning_of_line(&move_to_beg, cx);
 1367        assert_eq!(
 1368            view.selections.display_ranges(cx),
 1369            &[
 1370                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1371                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1372            ]
 1373        );
 1374    });
 1375
 1376    _ = view.update(cx, |view, cx| {
 1377        view.move_to_beginning_of_line(&move_to_beg, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[
 1381                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1382                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1383            ]
 1384        );
 1385    });
 1386
 1387    _ = view.update(cx, |view, cx| {
 1388        view.move_to_end_of_line(&move_to_end, cx);
 1389        assert_eq!(
 1390            view.selections.display_ranges(cx),
 1391            &[
 1392                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1393                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1394            ]
 1395        );
 1396    });
 1397
 1398    // Moving to the end of line again is a no-op.
 1399    _ = view.update(cx, |view, cx| {
 1400        view.move_to_end_of_line(&move_to_end, cx);
 1401        assert_eq!(
 1402            view.selections.display_ranges(cx),
 1403            &[
 1404                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1405                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1406            ]
 1407        );
 1408    });
 1409
 1410    _ = view.update(cx, |view, cx| {
 1411        view.move_left(&MoveLeft, cx);
 1412        view.select_to_beginning_of_line(
 1413            &SelectToBeginningOfLine {
 1414                stop_at_soft_wraps: true,
 1415            },
 1416            cx,
 1417        );
 1418        assert_eq!(
 1419            view.selections.display_ranges(cx),
 1420            &[
 1421                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1422                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1423            ]
 1424        );
 1425    });
 1426
 1427    _ = view.update(cx, |view, cx| {
 1428        view.select_to_beginning_of_line(
 1429            &SelectToBeginningOfLine {
 1430                stop_at_soft_wraps: true,
 1431            },
 1432            cx,
 1433        );
 1434        assert_eq!(
 1435            view.selections.display_ranges(cx),
 1436            &[
 1437                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1438                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1439            ]
 1440        );
 1441    });
 1442
 1443    _ = view.update(cx, |view, cx| {
 1444        view.select_to_beginning_of_line(
 1445            &SelectToBeginningOfLine {
 1446                stop_at_soft_wraps: true,
 1447            },
 1448            cx,
 1449        );
 1450        assert_eq!(
 1451            view.selections.display_ranges(cx),
 1452            &[
 1453                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1454                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1455            ]
 1456        );
 1457    });
 1458
 1459    _ = view.update(cx, |view, cx| {
 1460        view.select_to_end_of_line(
 1461            &SelectToEndOfLine {
 1462                stop_at_soft_wraps: true,
 1463            },
 1464            cx,
 1465        );
 1466        assert_eq!(
 1467            view.selections.display_ranges(cx),
 1468            &[
 1469                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1470                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1471            ]
 1472        );
 1473    });
 1474
 1475    _ = view.update(cx, |view, cx| {
 1476        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1477        assert_eq!(view.display_text(cx), "ab\n  de");
 1478        assert_eq!(
 1479            view.selections.display_ranges(cx),
 1480            &[
 1481                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1482                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1483            ]
 1484        );
 1485    });
 1486
 1487    _ = view.update(cx, |view, cx| {
 1488        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1489        assert_eq!(view.display_text(cx), "\n");
 1490        assert_eq!(
 1491            view.selections.display_ranges(cx),
 1492            &[
 1493                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1494                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1495            ]
 1496        );
 1497    });
 1498}
 1499
 1500#[gpui::test]
 1501fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1502    init_test(cx, |_| {});
 1503    let move_to_beg = MoveToBeginningOfLine {
 1504        stop_at_soft_wraps: false,
 1505    };
 1506
 1507    let move_to_end = MoveToEndOfLine {
 1508        stop_at_soft_wraps: false,
 1509    };
 1510
 1511    let view = cx.add_window(|cx| {
 1512        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1513        build_editor(buffer, cx)
 1514    });
 1515
 1516    _ = view.update(cx, |view, cx| {
 1517        view.set_wrap_width(Some(140.0.into()), cx);
 1518
 1519        // We expect the following lines after wrapping
 1520        // ```
 1521        // thequickbrownfox
 1522        // jumpedoverthelazydo
 1523        // gs
 1524        // ```
 1525        // The final `gs` was soft-wrapped onto a new line.
 1526        assert_eq!(
 1527            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1528            view.display_text(cx),
 1529        );
 1530
 1531        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1532        // Start the cursor at the `k` on the first line
 1533        view.change_selections(None, cx, |s| {
 1534            s.select_display_ranges([
 1535                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1536            ]);
 1537        });
 1538
 1539        // Moving to the beginning of the line should put us at the beginning of the line.
 1540        view.move_to_beginning_of_line(&move_to_beg, cx);
 1541        assert_eq!(
 1542            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1543            view.selections.display_ranges(cx)
 1544        );
 1545
 1546        // Moving to the end of the line should put us at the end of the line.
 1547        view.move_to_end_of_line(&move_to_end, cx);
 1548        assert_eq!(
 1549            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1550            view.selections.display_ranges(cx)
 1551        );
 1552
 1553        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1554        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1555        view.change_selections(None, cx, |s| {
 1556            s.select_display_ranges([
 1557                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1558            ]);
 1559        });
 1560
 1561        // Moving to the beginning of the line should put us at the start of the second line of
 1562        // display text, i.e., the `j`.
 1563        view.move_to_beginning_of_line(&move_to_beg, cx);
 1564        assert_eq!(
 1565            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1566            view.selections.display_ranges(cx)
 1567        );
 1568
 1569        // Moving to the beginning of the line again should be a no-op.
 1570        view.move_to_beginning_of_line(&move_to_beg, cx);
 1571        assert_eq!(
 1572            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1573            view.selections.display_ranges(cx)
 1574        );
 1575
 1576        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1577        // next display line.
 1578        view.move_to_end_of_line(&move_to_end, cx);
 1579        assert_eq!(
 1580            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1581            view.selections.display_ranges(cx)
 1582        );
 1583
 1584        // Moving to the end of the line again should be a no-op.
 1585        view.move_to_end_of_line(&move_to_end, cx);
 1586        assert_eq!(
 1587            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1588            view.selections.display_ranges(cx)
 1589        );
 1590    });
 1591}
 1592
 1593#[gpui::test]
 1594fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1595    init_test(cx, |_| {});
 1596
 1597    let view = cx.add_window(|cx| {
 1598        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1599        build_editor(buffer, cx)
 1600    });
 1601    _ = view.update(cx, |view, cx| {
 1602        view.change_selections(None, cx, |s| {
 1603            s.select_display_ranges([
 1604                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1605                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1606            ])
 1607        });
 1608
 1609        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1610        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1611
 1612        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1613        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1614
 1615        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1616        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1617
 1618        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1619        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1620
 1621        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1622        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1623
 1624        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1625        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1626
 1627        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1628        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1629
 1630        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1631        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1632
 1633        view.move_right(&MoveRight, cx);
 1634        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1635        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1636
 1637        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1638        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1639
 1640        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1641        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1642    });
 1643}
 1644
 1645#[gpui::test]
 1646fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1647    init_test(cx, |_| {});
 1648
 1649    let view = cx.add_window(|cx| {
 1650        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1651        build_editor(buffer, cx)
 1652    });
 1653
 1654    _ = view.update(cx, |view, cx| {
 1655        view.set_wrap_width(Some(140.0.into()), cx);
 1656        assert_eq!(
 1657            view.display_text(cx),
 1658            "use one::{\n    two::three::\n    four::five\n};"
 1659        );
 1660
 1661        view.change_selections(None, cx, |s| {
 1662            s.select_display_ranges([
 1663                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1664            ]);
 1665        });
 1666
 1667        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1668        assert_eq!(
 1669            view.selections.display_ranges(cx),
 1670            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1671        );
 1672
 1673        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1674        assert_eq!(
 1675            view.selections.display_ranges(cx),
 1676            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1677        );
 1678
 1679        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1680        assert_eq!(
 1681            view.selections.display_ranges(cx),
 1682            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1683        );
 1684
 1685        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1686        assert_eq!(
 1687            view.selections.display_ranges(cx),
 1688            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1689        );
 1690
 1691        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1692        assert_eq!(
 1693            view.selections.display_ranges(cx),
 1694            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1695        );
 1696
 1697        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1698        assert_eq!(
 1699            view.selections.display_ranges(cx),
 1700            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1701        );
 1702    });
 1703}
 1704
 1705#[gpui::test]
 1706async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1707    init_test(cx, |_| {});
 1708    let mut cx = EditorTestContext::new(cx).await;
 1709
 1710    let line_height = cx.editor(|editor, cx| {
 1711        editor
 1712            .style()
 1713            .unwrap()
 1714            .text
 1715            .line_height_in_pixels(cx.rem_size())
 1716    });
 1717    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1718
 1719    cx.set_state(
 1720        &r#"ˇone
 1721        two
 1722
 1723        three
 1724        fourˇ
 1725        five
 1726
 1727        six"#
 1728            .unindent(),
 1729    );
 1730
 1731    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1732    cx.assert_editor_state(
 1733        &r#"one
 1734        two
 1735        ˇ
 1736        three
 1737        four
 1738        five
 1739        ˇ
 1740        six"#
 1741            .unindent(),
 1742    );
 1743
 1744    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1745    cx.assert_editor_state(
 1746        &r#"one
 1747        two
 1748
 1749        three
 1750        four
 1751        five
 1752        ˇ
 1753        sixˇ"#
 1754            .unindent(),
 1755    );
 1756
 1757    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1758    cx.assert_editor_state(
 1759        &r#"one
 1760        two
 1761
 1762        three
 1763        four
 1764        five
 1765
 1766        sixˇ"#
 1767            .unindent(),
 1768    );
 1769
 1770    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1771    cx.assert_editor_state(
 1772        &r#"one
 1773        two
 1774
 1775        three
 1776        four
 1777        five
 1778        ˇ
 1779        six"#
 1780            .unindent(),
 1781    );
 1782
 1783    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1784    cx.assert_editor_state(
 1785        &r#"one
 1786        two
 1787        ˇ
 1788        three
 1789        four
 1790        five
 1791
 1792        six"#
 1793            .unindent(),
 1794    );
 1795
 1796    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1797    cx.assert_editor_state(
 1798        &r#"ˇone
 1799        two
 1800
 1801        three
 1802        four
 1803        five
 1804
 1805        six"#
 1806            .unindent(),
 1807    );
 1808}
 1809
 1810#[gpui::test]
 1811async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1812    init_test(cx, |_| {});
 1813    let mut cx = EditorTestContext::new(cx).await;
 1814    let line_height = cx.editor(|editor, cx| {
 1815        editor
 1816            .style()
 1817            .unwrap()
 1818            .text
 1819            .line_height_in_pixels(cx.rem_size())
 1820    });
 1821    let window = cx.window;
 1822    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1823
 1824    cx.set_state(
 1825        &r#"ˇone
 1826        two
 1827        three
 1828        four
 1829        five
 1830        six
 1831        seven
 1832        eight
 1833        nine
 1834        ten
 1835        "#,
 1836    );
 1837
 1838    cx.update_editor(|editor, cx| {
 1839        assert_eq!(
 1840            editor.snapshot(cx).scroll_position(),
 1841            gpui::Point::new(0., 0.)
 1842        );
 1843        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1844        assert_eq!(
 1845            editor.snapshot(cx).scroll_position(),
 1846            gpui::Point::new(0., 3.)
 1847        );
 1848        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1849        assert_eq!(
 1850            editor.snapshot(cx).scroll_position(),
 1851            gpui::Point::new(0., 6.)
 1852        );
 1853        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1854        assert_eq!(
 1855            editor.snapshot(cx).scroll_position(),
 1856            gpui::Point::new(0., 3.)
 1857        );
 1858
 1859        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1860        assert_eq!(
 1861            editor.snapshot(cx).scroll_position(),
 1862            gpui::Point::new(0., 1.)
 1863        );
 1864        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1865        assert_eq!(
 1866            editor.snapshot(cx).scroll_position(),
 1867            gpui::Point::new(0., 3.)
 1868        );
 1869    });
 1870}
 1871
 1872#[gpui::test]
 1873async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1874    init_test(cx, |_| {});
 1875    let mut cx = EditorTestContext::new(cx).await;
 1876
 1877    let line_height = cx.update_editor(|editor, cx| {
 1878        editor.set_vertical_scroll_margin(2, cx);
 1879        editor
 1880            .style()
 1881            .unwrap()
 1882            .text
 1883            .line_height_in_pixels(cx.rem_size())
 1884    });
 1885    let window = cx.window;
 1886    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1887
 1888    cx.set_state(
 1889        &r#"ˇone
 1890            two
 1891            three
 1892            four
 1893            five
 1894            six
 1895            seven
 1896            eight
 1897            nine
 1898            ten
 1899        "#,
 1900    );
 1901    cx.update_editor(|editor, cx| {
 1902        assert_eq!(
 1903            editor.snapshot(cx).scroll_position(),
 1904            gpui::Point::new(0., 0.0)
 1905        );
 1906    });
 1907
 1908    // Add a cursor below the visible area. Since both cursors cannot fit
 1909    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1910    // allows the vertical scroll margin below that cursor.
 1911    cx.update_editor(|editor, cx| {
 1912        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1913            selections.select_ranges([
 1914                Point::new(0, 0)..Point::new(0, 0),
 1915                Point::new(6, 0)..Point::new(6, 0),
 1916            ]);
 1917        })
 1918    });
 1919    cx.update_editor(|editor, cx| {
 1920        assert_eq!(
 1921            editor.snapshot(cx).scroll_position(),
 1922            gpui::Point::new(0., 3.0)
 1923        );
 1924    });
 1925
 1926    // Move down. The editor cursor scrolls down to track the newest cursor.
 1927    cx.update_editor(|editor, cx| {
 1928        editor.move_down(&Default::default(), cx);
 1929    });
 1930    cx.update_editor(|editor, cx| {
 1931        assert_eq!(
 1932            editor.snapshot(cx).scroll_position(),
 1933            gpui::Point::new(0., 4.0)
 1934        );
 1935    });
 1936
 1937    // Add a cursor above the visible area. Since both cursors fit on screen,
 1938    // the editor scrolls to show both.
 1939    cx.update_editor(|editor, cx| {
 1940        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1941            selections.select_ranges([
 1942                Point::new(1, 0)..Point::new(1, 0),
 1943                Point::new(6, 0)..Point::new(6, 0),
 1944            ]);
 1945        })
 1946    });
 1947    cx.update_editor(|editor, cx| {
 1948        assert_eq!(
 1949            editor.snapshot(cx).scroll_position(),
 1950            gpui::Point::new(0., 1.0)
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1957    init_test(cx, |_| {});
 1958    let mut cx = EditorTestContext::new(cx).await;
 1959
 1960    let line_height = cx.editor(|editor, cx| {
 1961        editor
 1962            .style()
 1963            .unwrap()
 1964            .text
 1965            .line_height_in_pixels(cx.rem_size())
 1966    });
 1967    let window = cx.window;
 1968    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1969    cx.set_state(
 1970        &r#"
 1971        ˇone
 1972        two
 1973        threeˇ
 1974        four
 1975        five
 1976        six
 1977        seven
 1978        eight
 1979        nine
 1980        ten
 1981        "#
 1982        .unindent(),
 1983    );
 1984
 1985    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1986    cx.assert_editor_state(
 1987        &r#"
 1988        one
 1989        two
 1990        three
 1991        ˇfour
 1992        five
 1993        sixˇ
 1994        seven
 1995        eight
 1996        nine
 1997        ten
 1998        "#
 1999        .unindent(),
 2000    );
 2001
 2002    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2003    cx.assert_editor_state(
 2004        &r#"
 2005        one
 2006        two
 2007        three
 2008        four
 2009        five
 2010        six
 2011        ˇseven
 2012        eight
 2013        nineˇ
 2014        ten
 2015        "#
 2016        .unindent(),
 2017    );
 2018
 2019    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2020    cx.assert_editor_state(
 2021        &r#"
 2022        one
 2023        two
 2024        three
 2025        ˇfour
 2026        five
 2027        sixˇ
 2028        seven
 2029        eight
 2030        nine
 2031        ten
 2032        "#
 2033        .unindent(),
 2034    );
 2035
 2036    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2037    cx.assert_editor_state(
 2038        &r#"
 2039        ˇone
 2040        two
 2041        threeˇ
 2042        four
 2043        five
 2044        six
 2045        seven
 2046        eight
 2047        nine
 2048        ten
 2049        "#
 2050        .unindent(),
 2051    );
 2052
 2053    // Test select collapsing
 2054    cx.update_editor(|editor, cx| {
 2055        editor.move_page_down(&MovePageDown::default(), cx);
 2056        editor.move_page_down(&MovePageDown::default(), cx);
 2057        editor.move_page_down(&MovePageDown::default(), cx);
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"
 2061        one
 2062        two
 2063        three
 2064        four
 2065        five
 2066        six
 2067        seven
 2068        eight
 2069        nine
 2070        ˇten
 2071        ˇ"#
 2072        .unindent(),
 2073    );
 2074}
 2075
 2076#[gpui::test]
 2077async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2078    init_test(cx, |_| {});
 2079    let mut cx = EditorTestContext::new(cx).await;
 2080    cx.set_state("one «two threeˇ» four");
 2081    cx.update_editor(|editor, cx| {
 2082        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2083        assert_eq!(editor.text(cx), " four");
 2084    });
 2085}
 2086
 2087#[gpui::test]
 2088fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2089    init_test(cx, |_| {});
 2090
 2091    let view = cx.add_window(|cx| {
 2092        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2093        build_editor(buffer.clone(), cx)
 2094    });
 2095
 2096    _ = view.update(cx, |view, cx| {
 2097        view.change_selections(None, cx, |s| {
 2098            s.select_display_ranges([
 2099                // an empty selection - the preceding word fragment is deleted
 2100                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2101                // characters selected - they are deleted
 2102                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2103            ])
 2104        });
 2105        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
 2106        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2107    });
 2108
 2109    _ = view.update(cx, |view, cx| {
 2110        view.change_selections(None, cx, |s| {
 2111            s.select_display_ranges([
 2112                // an empty selection - the following word fragment is deleted
 2113                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2114                // characters selected - they are deleted
 2115                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2116            ])
 2117        });
 2118        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
 2119        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2120    });
 2121}
 2122
 2123#[gpui::test]
 2124fn test_newline(cx: &mut TestAppContext) {
 2125    init_test(cx, |_| {});
 2126
 2127    let view = cx.add_window(|cx| {
 2128        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2129        build_editor(buffer.clone(), cx)
 2130    });
 2131
 2132    _ = view.update(cx, |view, cx| {
 2133        view.change_selections(None, cx, |s| {
 2134            s.select_display_ranges([
 2135                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2136                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2137                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2138            ])
 2139        });
 2140
 2141        view.newline(&Newline, cx);
 2142        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2143    });
 2144}
 2145
 2146#[gpui::test]
 2147fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2148    init_test(cx, |_| {});
 2149
 2150    let editor = cx.add_window(|cx| {
 2151        let buffer = MultiBuffer::build_simple(
 2152            "
 2153                a
 2154                b(
 2155                    X
 2156                )
 2157                c(
 2158                    X
 2159                )
 2160            "
 2161            .unindent()
 2162            .as_str(),
 2163            cx,
 2164        );
 2165        let mut editor = build_editor(buffer.clone(), cx);
 2166        editor.change_selections(None, cx, |s| {
 2167            s.select_ranges([
 2168                Point::new(2, 4)..Point::new(2, 5),
 2169                Point::new(5, 4)..Point::new(5, 5),
 2170            ])
 2171        });
 2172        editor
 2173    });
 2174
 2175    _ = editor.update(cx, |editor, cx| {
 2176        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2177        editor.buffer.update(cx, |buffer, cx| {
 2178            buffer.edit(
 2179                [
 2180                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2181                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2182                ],
 2183                None,
 2184                cx,
 2185            );
 2186            assert_eq!(
 2187                buffer.read(cx).text(),
 2188                "
 2189                    a
 2190                    b()
 2191                    c()
 2192                "
 2193                .unindent()
 2194            );
 2195        });
 2196        assert_eq!(
 2197            editor.selections.ranges(cx),
 2198            &[
 2199                Point::new(1, 2)..Point::new(1, 2),
 2200                Point::new(2, 2)..Point::new(2, 2),
 2201            ],
 2202        );
 2203
 2204        editor.newline(&Newline, cx);
 2205        assert_eq!(
 2206            editor.text(cx),
 2207            "
 2208                a
 2209                b(
 2210                )
 2211                c(
 2212                )
 2213            "
 2214            .unindent()
 2215        );
 2216
 2217        // The selections are moved after the inserted newlines
 2218        assert_eq!(
 2219            editor.selections.ranges(cx),
 2220            &[
 2221                Point::new(2, 0)..Point::new(2, 0),
 2222                Point::new(4, 0)..Point::new(4, 0),
 2223            ],
 2224        );
 2225    });
 2226}
 2227
 2228#[gpui::test]
 2229async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2230    init_test(cx, |settings| {
 2231        settings.defaults.tab_size = NonZeroU32::new(4)
 2232    });
 2233
 2234    let language = Arc::new(
 2235        Language::new(
 2236            LanguageConfig::default(),
 2237            Some(tree_sitter_rust::language()),
 2238        )
 2239        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2240        .unwrap(),
 2241    );
 2242
 2243    let mut cx = EditorTestContext::new(cx).await;
 2244    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2245    cx.set_state(indoc! {"
 2246        const a: ˇA = (
 2247 2248                «const_functionˇ»(ˇ),
 2249                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2250 2251        ˇ);ˇ
 2252    "});
 2253
 2254    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2255    cx.assert_editor_state(indoc! {"
 2256        ˇ
 2257        const a: A = (
 2258            ˇ
 2259            (
 2260                ˇ
 2261                ˇ
 2262                const_function(),
 2263                ˇ
 2264                ˇ
 2265                ˇ
 2266                ˇ
 2267                something_else,
 2268                ˇ
 2269            )
 2270            ˇ
 2271            ˇ
 2272        );
 2273    "});
 2274}
 2275
 2276#[gpui::test]
 2277async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2278    init_test(cx, |settings| {
 2279        settings.defaults.tab_size = NonZeroU32::new(4)
 2280    });
 2281
 2282    let language = Arc::new(
 2283        Language::new(
 2284            LanguageConfig::default(),
 2285            Some(tree_sitter_rust::language()),
 2286        )
 2287        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2288        .unwrap(),
 2289    );
 2290
 2291    let mut cx = EditorTestContext::new(cx).await;
 2292    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2293    cx.set_state(indoc! {"
 2294        const a: ˇA = (
 2295 2296                «const_functionˇ»(ˇ),
 2297                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2298 2299        ˇ);ˇ
 2300    "});
 2301
 2302    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2303    cx.assert_editor_state(indoc! {"
 2304        const a: A = (
 2305            ˇ
 2306            (
 2307                ˇ
 2308                const_function(),
 2309                ˇ
 2310                ˇ
 2311                something_else,
 2312                ˇ
 2313                ˇ
 2314                ˇ
 2315                ˇ
 2316            )
 2317            ˇ
 2318        );
 2319        ˇ
 2320        ˇ
 2321    "});
 2322}
 2323
 2324#[gpui::test]
 2325async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2326    init_test(cx, |settings| {
 2327        settings.defaults.tab_size = NonZeroU32::new(4)
 2328    });
 2329
 2330    let language = Arc::new(Language::new(
 2331        LanguageConfig {
 2332            line_comments: vec!["//".into()],
 2333            ..LanguageConfig::default()
 2334        },
 2335        None,
 2336    ));
 2337    {
 2338        let mut cx = EditorTestContext::new(cx).await;
 2339        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2340        cx.set_state(indoc! {"
 2341        // Fooˇ
 2342    "});
 2343
 2344        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2345        cx.assert_editor_state(indoc! {"
 2346        // Foo
 2347        //ˇ
 2348    "});
 2349        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2350        cx.set_state(indoc! {"
 2351        ˇ// Foo
 2352    "});
 2353        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2354        cx.assert_editor_state(indoc! {"
 2355
 2356        ˇ// Foo
 2357    "});
 2358    }
 2359    // Ensure that comment continuations can be disabled.
 2360    update_test_language_settings(cx, |settings| {
 2361        settings.defaults.extend_comment_on_newline = Some(false);
 2362    });
 2363    let mut cx = EditorTestContext::new(cx).await;
 2364    cx.set_state(indoc! {"
 2365        // Fooˇ
 2366    "});
 2367    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2368    cx.assert_editor_state(indoc! {"
 2369        // Foo
 2370        ˇ
 2371    "});
 2372}
 2373
 2374#[gpui::test]
 2375fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2376    init_test(cx, |_| {});
 2377
 2378    let editor = cx.add_window(|cx| {
 2379        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2380        let mut editor = build_editor(buffer.clone(), cx);
 2381        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2382        editor
 2383    });
 2384
 2385    _ = editor.update(cx, |editor, cx| {
 2386        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2387        editor.buffer.update(cx, |buffer, cx| {
 2388            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2389            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2390        });
 2391        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2392
 2393        editor.insert("Z", cx);
 2394        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2395
 2396        // The selections are moved after the inserted characters
 2397        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2398    });
 2399}
 2400
 2401#[gpui::test]
 2402async fn test_tab(cx: &mut gpui::TestAppContext) {
 2403    init_test(cx, |settings| {
 2404        settings.defaults.tab_size = NonZeroU32::new(3)
 2405    });
 2406
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state(indoc! {"
 2409        ˇabˇc
 2410        ˇ🏀ˇ🏀ˇefg
 2411 2412    "});
 2413    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2414    cx.assert_editor_state(indoc! {"
 2415           ˇab ˇc
 2416           ˇ🏀  ˇ🏀  ˇefg
 2417        d  ˇ
 2418    "});
 2419
 2420    cx.set_state(indoc! {"
 2421        a
 2422        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2423    "});
 2424    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2425    cx.assert_editor_state(indoc! {"
 2426        a
 2427           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2428    "});
 2429}
 2430
 2431#[gpui::test]
 2432async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2433    init_test(cx, |_| {});
 2434
 2435    let mut cx = EditorTestContext::new(cx).await;
 2436    let language = Arc::new(
 2437        Language::new(
 2438            LanguageConfig::default(),
 2439            Some(tree_sitter_rust::language()),
 2440        )
 2441        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2442        .unwrap(),
 2443    );
 2444    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2445
 2446    // cursors that are already at the suggested indent level insert
 2447    // a soft tab. cursors that are to the left of the suggested indent
 2448    // auto-indent their line.
 2449    cx.set_state(indoc! {"
 2450        ˇ
 2451        const a: B = (
 2452            c(
 2453                d(
 2454        ˇ
 2455                )
 2456        ˇ
 2457        ˇ    )
 2458        );
 2459    "});
 2460    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2461    cx.assert_editor_state(indoc! {"
 2462            ˇ
 2463        const a: B = (
 2464            c(
 2465                d(
 2466                    ˇ
 2467                )
 2468                ˇ
 2469            ˇ)
 2470        );
 2471    "});
 2472
 2473    // handle auto-indent when there are multiple cursors on the same line
 2474    cx.set_state(indoc! {"
 2475        const a: B = (
 2476            c(
 2477        ˇ    ˇ
 2478        ˇ    )
 2479        );
 2480    "});
 2481    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2482    cx.assert_editor_state(indoc! {"
 2483        const a: B = (
 2484            c(
 2485                ˇ
 2486            ˇ)
 2487        );
 2488    "});
 2489}
 2490
 2491#[gpui::test]
 2492async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2493    init_test(cx, |settings| {
 2494        settings.defaults.tab_size = NonZeroU32::new(4)
 2495    });
 2496
 2497    let language = Arc::new(
 2498        Language::new(
 2499            LanguageConfig::default(),
 2500            Some(tree_sitter_rust::language()),
 2501        )
 2502        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2503        .unwrap(),
 2504    );
 2505
 2506    let mut cx = EditorTestContext::new(cx).await;
 2507    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2508    cx.set_state(indoc! {"
 2509        fn a() {
 2510            if b {
 2511        \t ˇc
 2512            }
 2513        }
 2514    "});
 2515
 2516    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2517    cx.assert_editor_state(indoc! {"
 2518        fn a() {
 2519            if b {
 2520                ˇc
 2521            }
 2522        }
 2523    "});
 2524}
 2525
 2526#[gpui::test]
 2527async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2528    init_test(cx, |settings| {
 2529        settings.defaults.tab_size = NonZeroU32::new(4);
 2530    });
 2531
 2532    let mut cx = EditorTestContext::new(cx).await;
 2533
 2534    cx.set_state(indoc! {"
 2535          «oneˇ» «twoˇ»
 2536        three
 2537         four
 2538    "});
 2539    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2540    cx.assert_editor_state(indoc! {"
 2541            «oneˇ» «twoˇ»
 2542        three
 2543         four
 2544    "});
 2545
 2546    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2547    cx.assert_editor_state(indoc! {"
 2548        «oneˇ» «twoˇ»
 2549        three
 2550         four
 2551    "});
 2552
 2553    // select across line ending
 2554    cx.set_state(indoc! {"
 2555        one two
 2556        t«hree
 2557        ˇ» four
 2558    "});
 2559    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2560    cx.assert_editor_state(indoc! {"
 2561        one two
 2562            t«hree
 2563        ˇ» four
 2564    "});
 2565
 2566    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2567    cx.assert_editor_state(indoc! {"
 2568        one two
 2569        t«hree
 2570        ˇ» four
 2571    "});
 2572
 2573    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2574    cx.set_state(indoc! {"
 2575        one two
 2576        ˇthree
 2577            four
 2578    "});
 2579    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2580    cx.assert_editor_state(indoc! {"
 2581        one two
 2582            ˇthree
 2583            four
 2584    "});
 2585
 2586    cx.set_state(indoc! {"
 2587        one two
 2588        ˇ    three
 2589            four
 2590    "});
 2591    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2592    cx.assert_editor_state(indoc! {"
 2593        one two
 2594        ˇthree
 2595            four
 2596    "});
 2597}
 2598
 2599#[gpui::test]
 2600async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2601    init_test(cx, |settings| {
 2602        settings.defaults.hard_tabs = Some(true);
 2603    });
 2604
 2605    let mut cx = EditorTestContext::new(cx).await;
 2606
 2607    // select two ranges on one line
 2608    cx.set_state(indoc! {"
 2609        «oneˇ» «twoˇ»
 2610        three
 2611        four
 2612    "});
 2613    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2614    cx.assert_editor_state(indoc! {"
 2615        \t«oneˇ» «twoˇ»
 2616        three
 2617        four
 2618    "});
 2619    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2620    cx.assert_editor_state(indoc! {"
 2621        \t\t«oneˇ» «twoˇ»
 2622        three
 2623        four
 2624    "});
 2625    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2626    cx.assert_editor_state(indoc! {"
 2627        \t«oneˇ» «twoˇ»
 2628        three
 2629        four
 2630    "});
 2631    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2632    cx.assert_editor_state(indoc! {"
 2633        «oneˇ» «twoˇ»
 2634        three
 2635        four
 2636    "});
 2637
 2638    // select across a line ending
 2639    cx.set_state(indoc! {"
 2640        one two
 2641        t«hree
 2642        ˇ»four
 2643    "});
 2644    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2645    cx.assert_editor_state(indoc! {"
 2646        one two
 2647        \tt«hree
 2648        ˇ»four
 2649    "});
 2650    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2651    cx.assert_editor_state(indoc! {"
 2652        one two
 2653        \t\tt«hree
 2654        ˇ»four
 2655    "});
 2656    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2657    cx.assert_editor_state(indoc! {"
 2658        one two
 2659        \tt«hree
 2660        ˇ»four
 2661    "});
 2662    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2663    cx.assert_editor_state(indoc! {"
 2664        one two
 2665        t«hree
 2666        ˇ»four
 2667    "});
 2668
 2669    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2670    cx.set_state(indoc! {"
 2671        one two
 2672        ˇthree
 2673        four
 2674    "});
 2675    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2676    cx.assert_editor_state(indoc! {"
 2677        one two
 2678        ˇthree
 2679        four
 2680    "});
 2681    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        one two
 2684        \tˇthree
 2685        four
 2686    "});
 2687    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2688    cx.assert_editor_state(indoc! {"
 2689        one two
 2690        ˇthree
 2691        four
 2692    "});
 2693}
 2694
 2695#[gpui::test]
 2696fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2697    init_test(cx, |settings| {
 2698        settings.languages.extend([
 2699            (
 2700                "TOML".into(),
 2701                LanguageSettingsContent {
 2702                    tab_size: NonZeroU32::new(2),
 2703                    ..Default::default()
 2704                },
 2705            ),
 2706            (
 2707                "Rust".into(),
 2708                LanguageSettingsContent {
 2709                    tab_size: NonZeroU32::new(4),
 2710                    ..Default::default()
 2711                },
 2712            ),
 2713        ]);
 2714    });
 2715
 2716    let toml_language = Arc::new(Language::new(
 2717        LanguageConfig {
 2718            name: "TOML".into(),
 2719            ..Default::default()
 2720        },
 2721        None,
 2722    ));
 2723    let rust_language = Arc::new(Language::new(
 2724        LanguageConfig {
 2725            name: "Rust".into(),
 2726            ..Default::default()
 2727        },
 2728        None,
 2729    ));
 2730
 2731    let toml_buffer =
 2732        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2733    let rust_buffer = cx.new_model(|cx| {
 2734        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2735    });
 2736    let multibuffer = cx.new_model(|cx| {
 2737        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 2738        multibuffer.push_excerpts(
 2739            toml_buffer.clone(),
 2740            [ExcerptRange {
 2741                context: Point::new(0, 0)..Point::new(2, 0),
 2742                primary: None,
 2743            }],
 2744            cx,
 2745        );
 2746        multibuffer.push_excerpts(
 2747            rust_buffer.clone(),
 2748            [ExcerptRange {
 2749                context: Point::new(0, 0)..Point::new(1, 0),
 2750                primary: None,
 2751            }],
 2752            cx,
 2753        );
 2754        multibuffer
 2755    });
 2756
 2757    cx.add_window(|cx| {
 2758        let mut editor = build_editor(multibuffer, cx);
 2759
 2760        assert_eq!(
 2761            editor.text(cx),
 2762            indoc! {"
 2763                a = 1
 2764                b = 2
 2765
 2766                const c: usize = 3;
 2767            "}
 2768        );
 2769
 2770        select_ranges(
 2771            &mut editor,
 2772            indoc! {"
 2773                «aˇ» = 1
 2774                b = 2
 2775
 2776                «const c:ˇ» usize = 3;
 2777            "},
 2778            cx,
 2779        );
 2780
 2781        editor.tab(&Tab, cx);
 2782        assert_text_with_selections(
 2783            &mut editor,
 2784            indoc! {"
 2785                  «aˇ» = 1
 2786                b = 2
 2787
 2788                    «const c:ˇ» usize = 3;
 2789            "},
 2790            cx,
 2791        );
 2792        editor.tab_prev(&TabPrev, cx);
 2793        assert_text_with_selections(
 2794            &mut editor,
 2795            indoc! {"
 2796                «aˇ» = 1
 2797                b = 2
 2798
 2799                «const c:ˇ» usize = 3;
 2800            "},
 2801            cx,
 2802        );
 2803
 2804        editor
 2805    });
 2806}
 2807
 2808#[gpui::test]
 2809async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2810    init_test(cx, |_| {});
 2811
 2812    let mut cx = EditorTestContext::new(cx).await;
 2813
 2814    // Basic backspace
 2815    cx.set_state(indoc! {"
 2816        onˇe two three
 2817        fou«rˇ» five six
 2818        seven «ˇeight nine
 2819        »ten
 2820    "});
 2821    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2822    cx.assert_editor_state(indoc! {"
 2823        oˇe two three
 2824        fouˇ five six
 2825        seven ˇten
 2826    "});
 2827
 2828    // Test backspace inside and around indents
 2829    cx.set_state(indoc! {"
 2830        zero
 2831            ˇone
 2832                ˇtwo
 2833            ˇ ˇ ˇ  three
 2834        ˇ  ˇ  four
 2835    "});
 2836    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2837    cx.assert_editor_state(indoc! {"
 2838        zero
 2839        ˇone
 2840            ˇtwo
 2841        ˇ  threeˇ  four
 2842    "});
 2843
 2844    // Test backspace with line_mode set to true
 2845    cx.update_editor(|e, _| e.selections.line_mode = true);
 2846    cx.set_state(indoc! {"
 2847        The ˇquick ˇbrown
 2848        fox jumps over
 2849        the lazy dog
 2850        ˇThe qu«ick bˇ»rown"});
 2851    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        ˇfox jumps over
 2854        the lazy dogˇ"});
 2855}
 2856
 2857#[gpui::test]
 2858async fn test_delete(cx: &mut gpui::TestAppContext) {
 2859    init_test(cx, |_| {});
 2860
 2861    let mut cx = EditorTestContext::new(cx).await;
 2862    cx.set_state(indoc! {"
 2863        onˇe two three
 2864        fou«rˇ» five six
 2865        seven «ˇeight nine
 2866        »ten
 2867    "});
 2868    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2869    cx.assert_editor_state(indoc! {"
 2870        onˇ two three
 2871        fouˇ five six
 2872        seven ˇten
 2873    "});
 2874
 2875    // Test backspace with line_mode set to true
 2876    cx.update_editor(|e, _| e.selections.line_mode = true);
 2877    cx.set_state(indoc! {"
 2878        The ˇquick ˇbrown
 2879        fox «ˇjum»ps over
 2880        the lazy dog
 2881        ˇThe qu«ick bˇ»rown"});
 2882    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2883    cx.assert_editor_state("ˇthe lazy dogˇ");
 2884}
 2885
 2886#[gpui::test]
 2887fn test_delete_line(cx: &mut TestAppContext) {
 2888    init_test(cx, |_| {});
 2889
 2890    let view = cx.add_window(|cx| {
 2891        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2892        build_editor(buffer, cx)
 2893    });
 2894    _ = view.update(cx, |view, cx| {
 2895        view.change_selections(None, cx, |s| {
 2896            s.select_display_ranges([
 2897                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2898                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2899                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2900            ])
 2901        });
 2902        view.delete_line(&DeleteLine, cx);
 2903        assert_eq!(view.display_text(cx), "ghi");
 2904        assert_eq!(
 2905            view.selections.display_ranges(cx),
 2906            vec![
 2907                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2908                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2909            ]
 2910        );
 2911    });
 2912
 2913    let view = cx.add_window(|cx| {
 2914        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2915        build_editor(buffer, cx)
 2916    });
 2917    _ = view.update(cx, |view, cx| {
 2918        view.change_selections(None, cx, |s| {
 2919            s.select_display_ranges([
 2920                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 2921            ])
 2922        });
 2923        view.delete_line(&DeleteLine, cx);
 2924        assert_eq!(view.display_text(cx), "ghi\n");
 2925        assert_eq!(
 2926            view.selections.display_ranges(cx),
 2927            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 2928        );
 2929    });
 2930}
 2931
 2932#[gpui::test]
 2933fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 2934    init_test(cx, |_| {});
 2935
 2936    cx.add_window(|cx| {
 2937        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 2938        let mut editor = build_editor(buffer.clone(), cx);
 2939        let buffer = buffer.read(cx).as_singleton().unwrap();
 2940
 2941        assert_eq!(
 2942            editor.selections.ranges::<Point>(cx),
 2943            &[Point::new(0, 0)..Point::new(0, 0)]
 2944        );
 2945
 2946        // When on single line, replace newline at end by space
 2947        editor.join_lines(&JoinLines, cx);
 2948        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2949        assert_eq!(
 2950            editor.selections.ranges::<Point>(cx),
 2951            &[Point::new(0, 3)..Point::new(0, 3)]
 2952        );
 2953
 2954        // When multiple lines are selected, remove newlines that are spanned by the selection
 2955        editor.change_selections(None, cx, |s| {
 2956            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 2957        });
 2958        editor.join_lines(&JoinLines, cx);
 2959        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 2960        assert_eq!(
 2961            editor.selections.ranges::<Point>(cx),
 2962            &[Point::new(0, 11)..Point::new(0, 11)]
 2963        );
 2964
 2965        // Undo should be transactional
 2966        editor.undo(&Undo, cx);
 2967        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2968        assert_eq!(
 2969            editor.selections.ranges::<Point>(cx),
 2970            &[Point::new(0, 5)..Point::new(2, 2)]
 2971        );
 2972
 2973        // When joining an empty line don't insert a space
 2974        editor.change_selections(None, cx, |s| {
 2975            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 2976        });
 2977        editor.join_lines(&JoinLines, cx);
 2978        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 2979        assert_eq!(
 2980            editor.selections.ranges::<Point>(cx),
 2981            [Point::new(2, 3)..Point::new(2, 3)]
 2982        );
 2983
 2984        // We can remove trailing newlines
 2985        editor.join_lines(&JoinLines, cx);
 2986        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2987        assert_eq!(
 2988            editor.selections.ranges::<Point>(cx),
 2989            [Point::new(2, 3)..Point::new(2, 3)]
 2990        );
 2991
 2992        // We don't blow up on the last line
 2993        editor.join_lines(&JoinLines, cx);
 2994        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2995        assert_eq!(
 2996            editor.selections.ranges::<Point>(cx),
 2997            [Point::new(2, 3)..Point::new(2, 3)]
 2998        );
 2999
 3000        // reset to test indentation
 3001        editor.buffer.update(cx, |buffer, cx| {
 3002            buffer.edit(
 3003                [
 3004                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3005                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3006                ],
 3007                None,
 3008                cx,
 3009            )
 3010        });
 3011
 3012        // We remove any leading spaces
 3013        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3014        editor.change_selections(None, cx, |s| {
 3015            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3016        });
 3017        editor.join_lines(&JoinLines, cx);
 3018        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3019
 3020        // We don't insert a space for a line containing only spaces
 3021        editor.join_lines(&JoinLines, cx);
 3022        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3023
 3024        // We ignore any leading tabs
 3025        editor.join_lines(&JoinLines, cx);
 3026        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3027
 3028        editor
 3029    });
 3030}
 3031
 3032#[gpui::test]
 3033fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3034    init_test(cx, |_| {});
 3035
 3036    cx.add_window(|cx| {
 3037        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3038        let mut editor = build_editor(buffer.clone(), cx);
 3039        let buffer = buffer.read(cx).as_singleton().unwrap();
 3040
 3041        editor.change_selections(None, cx, |s| {
 3042            s.select_ranges([
 3043                Point::new(0, 2)..Point::new(1, 1),
 3044                Point::new(1, 2)..Point::new(1, 2),
 3045                Point::new(3, 1)..Point::new(3, 2),
 3046            ])
 3047        });
 3048
 3049        editor.join_lines(&JoinLines, cx);
 3050        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3051
 3052        assert_eq!(
 3053            editor.selections.ranges::<Point>(cx),
 3054            [
 3055                Point::new(0, 7)..Point::new(0, 7),
 3056                Point::new(1, 3)..Point::new(1, 3)
 3057            ]
 3058        );
 3059        editor
 3060    });
 3061}
 3062
 3063#[gpui::test]
 3064async fn test_join_lines_with_git_diff_base(
 3065    executor: BackgroundExecutor,
 3066    cx: &mut gpui::TestAppContext,
 3067) {
 3068    init_test(cx, |_| {});
 3069
 3070    let mut cx = EditorTestContext::new(cx).await;
 3071
 3072    let diff_base = r#"
 3073        Line 0
 3074        Line 1
 3075        Line 2
 3076        Line 3
 3077        "#
 3078    .unindent();
 3079
 3080    cx.set_state(
 3081        &r#"
 3082        ˇLine 0
 3083        Line 1
 3084        Line 2
 3085        Line 3
 3086        "#
 3087        .unindent(),
 3088    );
 3089
 3090    cx.set_diff_base(Some(&diff_base));
 3091    executor.run_until_parked();
 3092
 3093    // Join lines
 3094    cx.update_editor(|editor, cx| {
 3095        editor.join_lines(&JoinLines, cx);
 3096    });
 3097    executor.run_until_parked();
 3098
 3099    cx.assert_editor_state(
 3100        &r#"
 3101        Line 0ˇ Line 1
 3102        Line 2
 3103        Line 3
 3104        "#
 3105        .unindent(),
 3106    );
 3107    // Join again
 3108    cx.update_editor(|editor, cx| {
 3109        editor.join_lines(&JoinLines, cx);
 3110    });
 3111    executor.run_until_parked();
 3112
 3113    cx.assert_editor_state(
 3114        &r#"
 3115        Line 0 Line 1ˇ Line 2
 3116        Line 3
 3117        "#
 3118        .unindent(),
 3119    );
 3120}
 3121
 3122#[gpui::test]
 3123async fn test_custom_newlines_cause_no_false_positive_diffs(
 3124    executor: BackgroundExecutor,
 3125    cx: &mut gpui::TestAppContext,
 3126) {
 3127    init_test(cx, |_| {});
 3128    let mut cx = EditorTestContext::new(cx).await;
 3129    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3130    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3131    executor.run_until_parked();
 3132
 3133    cx.update_editor(|editor, cx| {
 3134        assert_eq!(
 3135            editor
 3136                .buffer()
 3137                .read(cx)
 3138                .snapshot(cx)
 3139                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3140                .collect::<Vec<_>>(),
 3141            Vec::new(),
 3142            "Should not have any diffs for files with custom newlines"
 3143        );
 3144    });
 3145}
 3146
 3147#[gpui::test]
 3148async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3149    init_test(cx, |_| {});
 3150
 3151    let mut cx = EditorTestContext::new(cx).await;
 3152
 3153    // Test sort_lines_case_insensitive()
 3154    cx.set_state(indoc! {"
 3155        «z
 3156        y
 3157        x
 3158        Z
 3159        Y
 3160        Xˇ»
 3161    "});
 3162    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3163    cx.assert_editor_state(indoc! {"
 3164        «x
 3165        X
 3166        y
 3167        Y
 3168        z
 3169        Zˇ»
 3170    "});
 3171
 3172    // Test reverse_lines()
 3173    cx.set_state(indoc! {"
 3174        «5
 3175        4
 3176        3
 3177        2
 3178        1ˇ»
 3179    "});
 3180    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3181    cx.assert_editor_state(indoc! {"
 3182        «1
 3183        2
 3184        3
 3185        4
 3186        5ˇ»
 3187    "});
 3188
 3189    // Skip testing shuffle_line()
 3190
 3191    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3192    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3193
 3194    // Don't manipulate when cursor is on single line, but expand the selection
 3195    cx.set_state(indoc! {"
 3196        ddˇdd
 3197        ccc
 3198        bb
 3199        a
 3200    "});
 3201    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3202    cx.assert_editor_state(indoc! {"
 3203        «ddddˇ»
 3204        ccc
 3205        bb
 3206        a
 3207    "});
 3208
 3209    // Basic manipulate case
 3210    // Start selection moves to column 0
 3211    // End of selection shrinks to fit shorter line
 3212    cx.set_state(indoc! {"
 3213        dd«d
 3214        ccc
 3215        bb
 3216        aaaaaˇ»
 3217    "});
 3218    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3219    cx.assert_editor_state(indoc! {"
 3220        «aaaaa
 3221        bb
 3222        ccc
 3223        dddˇ»
 3224    "});
 3225
 3226    // Manipulate case with newlines
 3227    cx.set_state(indoc! {"
 3228        dd«d
 3229        ccc
 3230
 3231        bb
 3232        aaaaa
 3233
 3234        ˇ»
 3235    "});
 3236    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3237    cx.assert_editor_state(indoc! {"
 3238        «
 3239
 3240        aaaaa
 3241        bb
 3242        ccc
 3243        dddˇ»
 3244
 3245    "});
 3246
 3247    // Adding new line
 3248    cx.set_state(indoc! {"
 3249        aa«a
 3250        bbˇ»b
 3251    "});
 3252    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3253    cx.assert_editor_state(indoc! {"
 3254        «aaa
 3255        bbb
 3256        added_lineˇ»
 3257    "});
 3258
 3259    // Removing line
 3260    cx.set_state(indoc! {"
 3261        aa«a
 3262        bbbˇ»
 3263    "});
 3264    cx.update_editor(|e, cx| {
 3265        e.manipulate_lines(cx, |lines| {
 3266            lines.pop();
 3267        })
 3268    });
 3269    cx.assert_editor_state(indoc! {"
 3270        «aaaˇ»
 3271    "});
 3272
 3273    // Removing all lines
 3274    cx.set_state(indoc! {"
 3275        aa«a
 3276        bbbˇ»
 3277    "});
 3278    cx.update_editor(|e, cx| {
 3279        e.manipulate_lines(cx, |lines| {
 3280            lines.drain(..);
 3281        })
 3282    });
 3283    cx.assert_editor_state(indoc! {"
 3284        ˇ
 3285    "});
 3286}
 3287
 3288#[gpui::test]
 3289async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3290    init_test(cx, |_| {});
 3291
 3292    let mut cx = EditorTestContext::new(cx).await;
 3293
 3294    // Consider continuous selection as single selection
 3295    cx.set_state(indoc! {"
 3296        Aaa«aa
 3297        cˇ»c«c
 3298        bb
 3299        aaaˇ»aa
 3300    "});
 3301    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3302    cx.assert_editor_state(indoc! {"
 3303        «Aaaaa
 3304        ccc
 3305        bb
 3306        aaaaaˇ»
 3307    "});
 3308
 3309    cx.set_state(indoc! {"
 3310        Aaa«aa
 3311        cˇ»c«c
 3312        bb
 3313        aaaˇ»aa
 3314    "});
 3315    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3316    cx.assert_editor_state(indoc! {"
 3317        «Aaaaa
 3318        ccc
 3319        bbˇ»
 3320    "});
 3321
 3322    // Consider non continuous selection as distinct dedup operations
 3323    cx.set_state(indoc! {"
 3324        «aaaaa
 3325        bb
 3326        aaaaa
 3327        aaaaaˇ»
 3328
 3329        aaa«aaˇ»
 3330    "});
 3331    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3332    cx.assert_editor_state(indoc! {"
 3333        «aaaaa
 3334        bbˇ»
 3335
 3336        «aaaaaˇ»
 3337    "});
 3338}
 3339
 3340#[gpui::test]
 3341async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3342    init_test(cx, |_| {});
 3343
 3344    let mut cx = EditorTestContext::new(cx).await;
 3345
 3346    cx.set_state(indoc! {"
 3347        «Aaa
 3348        aAa
 3349        Aaaˇ»
 3350    "});
 3351    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3352    cx.assert_editor_state(indoc! {"
 3353        «Aaa
 3354        aAaˇ»
 3355    "});
 3356
 3357    cx.set_state(indoc! {"
 3358        «Aaa
 3359        aAa
 3360        aaAˇ»
 3361    "});
 3362    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3363    cx.assert_editor_state(indoc! {"
 3364        «Aaaˇ»
 3365    "});
 3366}
 3367
 3368#[gpui::test]
 3369async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3370    init_test(cx, |_| {});
 3371
 3372    let mut cx = EditorTestContext::new(cx).await;
 3373
 3374    // Manipulate with multiple selections on a single line
 3375    cx.set_state(indoc! {"
 3376        dd«dd
 3377        cˇ»c«c
 3378        bb
 3379        aaaˇ»aa
 3380    "});
 3381    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3382    cx.assert_editor_state(indoc! {"
 3383        «aaaaa
 3384        bb
 3385        ccc
 3386        ddddˇ»
 3387    "});
 3388
 3389    // Manipulate with multiple disjoin selections
 3390    cx.set_state(indoc! {"
 3391 3392        4
 3393        3
 3394        2
 3395        1ˇ»
 3396
 3397        dd«dd
 3398        ccc
 3399        bb
 3400        aaaˇ»aa
 3401    "});
 3402    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3403    cx.assert_editor_state(indoc! {"
 3404        «1
 3405        2
 3406        3
 3407        4
 3408        5ˇ»
 3409
 3410        «aaaaa
 3411        bb
 3412        ccc
 3413        ddddˇ»
 3414    "});
 3415
 3416    // Adding lines on each selection
 3417    cx.set_state(indoc! {"
 3418 3419        1ˇ»
 3420
 3421        bb«bb
 3422        aaaˇ»aa
 3423    "});
 3424    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3425    cx.assert_editor_state(indoc! {"
 3426        «2
 3427        1
 3428        added lineˇ»
 3429
 3430        «bbbb
 3431        aaaaa
 3432        added lineˇ»
 3433    "});
 3434
 3435    // Removing lines on each selection
 3436    cx.set_state(indoc! {"
 3437 3438        1ˇ»
 3439
 3440        bb«bb
 3441        aaaˇ»aa
 3442    "});
 3443    cx.update_editor(|e, cx| {
 3444        e.manipulate_lines(cx, |lines| {
 3445            lines.pop();
 3446        })
 3447    });
 3448    cx.assert_editor_state(indoc! {"
 3449        «2ˇ»
 3450
 3451        «bbbbˇ»
 3452    "});
 3453}
 3454
 3455#[gpui::test]
 3456async fn test_manipulate_text(cx: &mut TestAppContext) {
 3457    init_test(cx, |_| {});
 3458
 3459    let mut cx = EditorTestContext::new(cx).await;
 3460
 3461    // Test convert_to_upper_case()
 3462    cx.set_state(indoc! {"
 3463        «hello worldˇ»
 3464    "});
 3465    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3466    cx.assert_editor_state(indoc! {"
 3467        «HELLO WORLDˇ»
 3468    "});
 3469
 3470    // Test convert_to_lower_case()
 3471    cx.set_state(indoc! {"
 3472        «HELLO WORLDˇ»
 3473    "});
 3474    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3475    cx.assert_editor_state(indoc! {"
 3476        «hello worldˇ»
 3477    "});
 3478
 3479    // Test multiple line, single selection case
 3480    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3481    cx.set_state(indoc! {"
 3482        «The quick brown
 3483        fox jumps over
 3484        the lazy dogˇ»
 3485    "});
 3486    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3487    cx.assert_editor_state(indoc! {"
 3488        «The Quick Brown
 3489        Fox Jumps Over
 3490        The Lazy Dogˇ»
 3491    "});
 3492
 3493    // Test multiple line, single selection case
 3494    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3495    cx.set_state(indoc! {"
 3496        «The quick brown
 3497        fox jumps over
 3498        the lazy dogˇ»
 3499    "});
 3500    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3501    cx.assert_editor_state(indoc! {"
 3502        «TheQuickBrown
 3503        FoxJumpsOver
 3504        TheLazyDogˇ»
 3505    "});
 3506
 3507    // From here on out, test more complex cases of manipulate_text()
 3508
 3509    // Test no selection case - should affect words cursors are in
 3510    // Cursor at beginning, middle, and end of word
 3511    cx.set_state(indoc! {"
 3512        ˇhello big beauˇtiful worldˇ
 3513    "});
 3514    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3515    cx.assert_editor_state(indoc! {"
 3516        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3517    "});
 3518
 3519    // Test multiple selections on a single line and across multiple lines
 3520    cx.set_state(indoc! {"
 3521        «Theˇ» quick «brown
 3522        foxˇ» jumps «overˇ»
 3523        the «lazyˇ» dog
 3524    "});
 3525    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3526    cx.assert_editor_state(indoc! {"
 3527        «THEˇ» quick «BROWN
 3528        FOXˇ» jumps «OVERˇ»
 3529        the «LAZYˇ» dog
 3530    "});
 3531
 3532    // Test case where text length grows
 3533    cx.set_state(indoc! {"
 3534        «tschüߡ»
 3535    "});
 3536    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3537    cx.assert_editor_state(indoc! {"
 3538        «TSCHÜSSˇ»
 3539    "});
 3540
 3541    // Test to make sure we don't crash when text shrinks
 3542    cx.set_state(indoc! {"
 3543        aaa_bbbˇ
 3544    "});
 3545    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3546    cx.assert_editor_state(indoc! {"
 3547        «aaaBbbˇ»
 3548    "});
 3549
 3550    // Test to make sure we all aware of the fact that each word can grow and shrink
 3551    // Final selections should be aware of this fact
 3552    cx.set_state(indoc! {"
 3553        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3554    "});
 3555    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3556    cx.assert_editor_state(indoc! {"
 3557        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3558    "});
 3559
 3560    cx.set_state(indoc! {"
 3561        «hElLo, WoRld!ˇ»
 3562    "});
 3563    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3564    cx.assert_editor_state(indoc! {"
 3565        «HeLlO, wOrLD!ˇ»
 3566    "});
 3567}
 3568
 3569#[gpui::test]
 3570fn test_duplicate_line(cx: &mut TestAppContext) {
 3571    init_test(cx, |_| {});
 3572
 3573    let view = cx.add_window(|cx| {
 3574        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3575        build_editor(buffer, cx)
 3576    });
 3577    _ = view.update(cx, |view, cx| {
 3578        view.change_selections(None, cx, |s| {
 3579            s.select_display_ranges([
 3580                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3581                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3582                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3583                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3584            ])
 3585        });
 3586        view.duplicate_line_down(&DuplicateLineDown, cx);
 3587        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3588        assert_eq!(
 3589            view.selections.display_ranges(cx),
 3590            vec![
 3591                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3592                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3593                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3594                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3595            ]
 3596        );
 3597    });
 3598
 3599    let view = cx.add_window(|cx| {
 3600        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3601        build_editor(buffer, cx)
 3602    });
 3603    _ = view.update(cx, |view, cx| {
 3604        view.change_selections(None, cx, |s| {
 3605            s.select_display_ranges([
 3606                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3607                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3608            ])
 3609        });
 3610        view.duplicate_line_down(&DuplicateLineDown, cx);
 3611        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3612        assert_eq!(
 3613            view.selections.display_ranges(cx),
 3614            vec![
 3615                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3616                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3617            ]
 3618        );
 3619    });
 3620
 3621    // With `move_upwards` the selections stay in place, except for
 3622    // the lines inserted above them
 3623    let view = cx.add_window(|cx| {
 3624        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3625        build_editor(buffer, cx)
 3626    });
 3627    _ = view.update(cx, |view, cx| {
 3628        view.change_selections(None, cx, |s| {
 3629            s.select_display_ranges([
 3630                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3631                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3632                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3633                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3634            ])
 3635        });
 3636        view.duplicate_line_up(&DuplicateLineUp, cx);
 3637        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3638        assert_eq!(
 3639            view.selections.display_ranges(cx),
 3640            vec![
 3641                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3642                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3643                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3644                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3645            ]
 3646        );
 3647    });
 3648
 3649    let view = cx.add_window(|cx| {
 3650        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3651        build_editor(buffer, cx)
 3652    });
 3653    _ = view.update(cx, |view, cx| {
 3654        view.change_selections(None, cx, |s| {
 3655            s.select_display_ranges([
 3656                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3657                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3658            ])
 3659        });
 3660        view.duplicate_line_up(&DuplicateLineUp, cx);
 3661        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3662        assert_eq!(
 3663            view.selections.display_ranges(cx),
 3664            vec![
 3665                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3666                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3667            ]
 3668        );
 3669    });
 3670}
 3671
 3672#[gpui::test]
 3673fn test_move_line_up_down(cx: &mut TestAppContext) {
 3674    init_test(cx, |_| {});
 3675
 3676    let view = cx.add_window(|cx| {
 3677        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3678        build_editor(buffer, cx)
 3679    });
 3680    _ = view.update(cx, |view, cx| {
 3681        view.fold_ranges(
 3682            vec![
 3683                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3684                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3685                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3686            ],
 3687            true,
 3688            cx,
 3689        );
 3690        view.change_selections(None, cx, |s| {
 3691            s.select_display_ranges([
 3692                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3693                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3694                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3695                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3696            ])
 3697        });
 3698        assert_eq!(
 3699            view.display_text(cx),
 3700            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3701        );
 3702
 3703        view.move_line_up(&MoveLineUp, cx);
 3704        assert_eq!(
 3705            view.display_text(cx),
 3706            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3707        );
 3708        assert_eq!(
 3709            view.selections.display_ranges(cx),
 3710            vec![
 3711                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3712                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3713                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3714                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3715            ]
 3716        );
 3717    });
 3718
 3719    _ = view.update(cx, |view, cx| {
 3720        view.move_line_down(&MoveLineDown, cx);
 3721        assert_eq!(
 3722            view.display_text(cx),
 3723            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3724        );
 3725        assert_eq!(
 3726            view.selections.display_ranges(cx),
 3727            vec![
 3728                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3729                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3730                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3731                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3732            ]
 3733        );
 3734    });
 3735
 3736    _ = view.update(cx, |view, cx| {
 3737        view.move_line_down(&MoveLineDown, cx);
 3738        assert_eq!(
 3739            view.display_text(cx),
 3740            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3741        );
 3742        assert_eq!(
 3743            view.selections.display_ranges(cx),
 3744            vec![
 3745                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3746                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3747                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3748                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3749            ]
 3750        );
 3751    });
 3752
 3753    _ = view.update(cx, |view, cx| {
 3754        view.move_line_up(&MoveLineUp, cx);
 3755        assert_eq!(
 3756            view.display_text(cx),
 3757            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3758        );
 3759        assert_eq!(
 3760            view.selections.display_ranges(cx),
 3761            vec![
 3762                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3763                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3764                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3765                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3766            ]
 3767        );
 3768    });
 3769}
 3770
 3771#[gpui::test]
 3772fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3773    init_test(cx, |_| {});
 3774
 3775    let editor = cx.add_window(|cx| {
 3776        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3777        build_editor(buffer, cx)
 3778    });
 3779    _ = editor.update(cx, |editor, cx| {
 3780        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3781        editor.insert_blocks(
 3782            [BlockProperties {
 3783                style: BlockStyle::Fixed,
 3784                position: snapshot.anchor_after(Point::new(2, 0)),
 3785                disposition: BlockDisposition::Below,
 3786                height: 1,
 3787                render: Box::new(|_| div().into_any()),
 3788                priority: 0,
 3789            }],
 3790            Some(Autoscroll::fit()),
 3791            cx,
 3792        );
 3793        editor.change_selections(None, cx, |s| {
 3794            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3795        });
 3796        editor.move_line_down(&MoveLineDown, cx);
 3797    });
 3798}
 3799
 3800#[gpui::test]
 3801fn test_transpose(cx: &mut TestAppContext) {
 3802    init_test(cx, |_| {});
 3803
 3804    _ = cx.add_window(|cx| {
 3805        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3806        editor.set_style(EditorStyle::default(), cx);
 3807        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3808        editor.transpose(&Default::default(), cx);
 3809        assert_eq!(editor.text(cx), "bac");
 3810        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3811
 3812        editor.transpose(&Default::default(), cx);
 3813        assert_eq!(editor.text(cx), "bca");
 3814        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3815
 3816        editor.transpose(&Default::default(), cx);
 3817        assert_eq!(editor.text(cx), "bac");
 3818        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3819
 3820        editor
 3821    });
 3822
 3823    _ = cx.add_window(|cx| {
 3824        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3825        editor.set_style(EditorStyle::default(), cx);
 3826        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3827        editor.transpose(&Default::default(), cx);
 3828        assert_eq!(editor.text(cx), "acb\nde");
 3829        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3830
 3831        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3832        editor.transpose(&Default::default(), cx);
 3833        assert_eq!(editor.text(cx), "acbd\ne");
 3834        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3835
 3836        editor.transpose(&Default::default(), cx);
 3837        assert_eq!(editor.text(cx), "acbde\n");
 3838        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3839
 3840        editor.transpose(&Default::default(), cx);
 3841        assert_eq!(editor.text(cx), "acbd\ne");
 3842        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3843
 3844        editor
 3845    });
 3846
 3847    _ = cx.add_window(|cx| {
 3848        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3849        editor.set_style(EditorStyle::default(), cx);
 3850        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3851        editor.transpose(&Default::default(), cx);
 3852        assert_eq!(editor.text(cx), "bacd\ne");
 3853        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3854
 3855        editor.transpose(&Default::default(), cx);
 3856        assert_eq!(editor.text(cx), "bcade\n");
 3857        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3858
 3859        editor.transpose(&Default::default(), cx);
 3860        assert_eq!(editor.text(cx), "bcda\ne");
 3861        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3862
 3863        editor.transpose(&Default::default(), cx);
 3864        assert_eq!(editor.text(cx), "bcade\n");
 3865        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3866
 3867        editor.transpose(&Default::default(), cx);
 3868        assert_eq!(editor.text(cx), "bcaed\n");
 3869        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3870
 3871        editor
 3872    });
 3873
 3874    _ = cx.add_window(|cx| {
 3875        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3876        editor.set_style(EditorStyle::default(), cx);
 3877        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3878        editor.transpose(&Default::default(), cx);
 3879        assert_eq!(editor.text(cx), "🏀🍐✋");
 3880        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3881
 3882        editor.transpose(&Default::default(), cx);
 3883        assert_eq!(editor.text(cx), "🏀✋🍐");
 3884        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3885
 3886        editor.transpose(&Default::default(), cx);
 3887        assert_eq!(editor.text(cx), "🏀🍐✋");
 3888        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3889
 3890        editor
 3891    });
 3892}
 3893
 3894#[gpui::test]
 3895async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 3896    init_test(cx, |_| {});
 3897
 3898    let mut cx = EditorTestContext::new(cx).await;
 3899
 3900    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 3901    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3902    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 3903
 3904    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 3905    cx.set_state("two ˇfour ˇsix ˇ");
 3906    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3907    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 3908
 3909    // Paste again but with only two cursors. Since the number of cursors doesn't
 3910    // match the number of slices in the clipboard, the entire clipboard text
 3911    // is pasted at each cursor.
 3912    cx.set_state("ˇtwo one✅ four three six five ˇ");
 3913    cx.update_editor(|e, cx| {
 3914        e.handle_input("( ", cx);
 3915        e.paste(&Paste, cx);
 3916        e.handle_input(") ", cx);
 3917    });
 3918    cx.assert_editor_state(
 3919        &([
 3920            "( one✅ ",
 3921            "three ",
 3922            "five ) ˇtwo one✅ four three six five ( one✅ ",
 3923            "three ",
 3924            "five ) ˇ",
 3925        ]
 3926        .join("\n")),
 3927    );
 3928
 3929    // Cut with three selections, one of which is full-line.
 3930    cx.set_state(indoc! {"
 3931        1«2ˇ»3
 3932        4ˇ567
 3933        «8ˇ»9"});
 3934    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3935    cx.assert_editor_state(indoc! {"
 3936        1ˇ3
 3937        ˇ9"});
 3938
 3939    // Paste with three selections, noticing how the copied selection that was full-line
 3940    // gets inserted before the second cursor.
 3941    cx.set_state(indoc! {"
 3942        1ˇ3
 3943 3944        «oˇ»ne"});
 3945    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3946    cx.assert_editor_state(indoc! {"
 3947        12ˇ3
 3948        4567
 3949 3950        8ˇne"});
 3951
 3952    // Copy with a single cursor only, which writes the whole line into the clipboard.
 3953    cx.set_state(indoc! {"
 3954        The quick brown
 3955        fox juˇmps over
 3956        the lazy dog"});
 3957    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 3958    assert_eq!(
 3959        cx.read_from_clipboard()
 3960            .and_then(|item| item.text().as_deref().map(str::to_string)),
 3961        Some("fox jumps over\n".to_string())
 3962    );
 3963
 3964    // Paste with three selections, noticing how the copied full-line selection is inserted
 3965    // before the empty selections but replaces the selection that is non-empty.
 3966    cx.set_state(indoc! {"
 3967        Tˇhe quick brown
 3968        «foˇ»x jumps over
 3969        tˇhe lazy dog"});
 3970    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3971    cx.assert_editor_state(indoc! {"
 3972        fox jumps over
 3973        Tˇhe quick brown
 3974        fox jumps over
 3975        ˇx jumps over
 3976        fox jumps over
 3977        tˇhe lazy dog"});
 3978}
 3979
 3980#[gpui::test]
 3981async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 3982    init_test(cx, |_| {});
 3983
 3984    let mut cx = EditorTestContext::new(cx).await;
 3985    let language = Arc::new(Language::new(
 3986        LanguageConfig::default(),
 3987        Some(tree_sitter_rust::language()),
 3988    ));
 3989    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3990
 3991    // Cut an indented block, without the leading whitespace.
 3992    cx.set_state(indoc! {"
 3993        const a: B = (
 3994            c(),
 3995            «d(
 3996                e,
 3997                f
 3998            )ˇ»
 3999        );
 4000    "});
 4001    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4002    cx.assert_editor_state(indoc! {"
 4003        const a: B = (
 4004            c(),
 4005            ˇ
 4006        );
 4007    "});
 4008
 4009    // Paste it at the same position.
 4010    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4011    cx.assert_editor_state(indoc! {"
 4012        const a: B = (
 4013            c(),
 4014            d(
 4015                e,
 4016                f
 4017 4018        );
 4019    "});
 4020
 4021    // Paste it at a line with a lower indent level.
 4022    cx.set_state(indoc! {"
 4023        ˇ
 4024        const a: B = (
 4025            c(),
 4026        );
 4027    "});
 4028    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4029    cx.assert_editor_state(indoc! {"
 4030        d(
 4031            e,
 4032            f
 4033 4034        const a: B = (
 4035            c(),
 4036        );
 4037    "});
 4038
 4039    // Cut an indented block, with the leading whitespace.
 4040    cx.set_state(indoc! {"
 4041        const a: B = (
 4042            c(),
 4043        «    d(
 4044                e,
 4045                f
 4046            )
 4047        ˇ»);
 4048    "});
 4049    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4050    cx.assert_editor_state(indoc! {"
 4051        const a: B = (
 4052            c(),
 4053        ˇ);
 4054    "});
 4055
 4056    // Paste it at the same position.
 4057    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4058    cx.assert_editor_state(indoc! {"
 4059        const a: B = (
 4060            c(),
 4061            d(
 4062                e,
 4063                f
 4064            )
 4065        ˇ);
 4066    "});
 4067
 4068    // Paste it at a line with a higher indent level.
 4069    cx.set_state(indoc! {"
 4070        const a: B = (
 4071            c(),
 4072            d(
 4073                e,
 4074 4075            )
 4076        );
 4077    "});
 4078    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4079    cx.assert_editor_state(indoc! {"
 4080        const a: B = (
 4081            c(),
 4082            d(
 4083                e,
 4084                f    d(
 4085                    e,
 4086                    f
 4087                )
 4088        ˇ
 4089            )
 4090        );
 4091    "});
 4092}
 4093
 4094#[gpui::test]
 4095fn test_select_all(cx: &mut TestAppContext) {
 4096    init_test(cx, |_| {});
 4097
 4098    let view = cx.add_window(|cx| {
 4099        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4100        build_editor(buffer, cx)
 4101    });
 4102    _ = view.update(cx, |view, cx| {
 4103        view.select_all(&SelectAll, cx);
 4104        assert_eq!(
 4105            view.selections.display_ranges(cx),
 4106            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4107        );
 4108    });
 4109}
 4110
 4111#[gpui::test]
 4112fn test_select_line(cx: &mut TestAppContext) {
 4113    init_test(cx, |_| {});
 4114
 4115    let view = cx.add_window(|cx| {
 4116        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4117        build_editor(buffer, cx)
 4118    });
 4119    _ = view.update(cx, |view, cx| {
 4120        view.change_selections(None, cx, |s| {
 4121            s.select_display_ranges([
 4122                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4123                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4124                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4125                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4126            ])
 4127        });
 4128        view.select_line(&SelectLine, cx);
 4129        assert_eq!(
 4130            view.selections.display_ranges(cx),
 4131            vec![
 4132                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4133                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4134            ]
 4135        );
 4136    });
 4137
 4138    _ = view.update(cx, |view, cx| {
 4139        view.select_line(&SelectLine, cx);
 4140        assert_eq!(
 4141            view.selections.display_ranges(cx),
 4142            vec![
 4143                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4144                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4145            ]
 4146        );
 4147    });
 4148
 4149    _ = view.update(cx, |view, cx| {
 4150        view.select_line(&SelectLine, cx);
 4151        assert_eq!(
 4152            view.selections.display_ranges(cx),
 4153            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4154        );
 4155    });
 4156}
 4157
 4158#[gpui::test]
 4159fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4160    init_test(cx, |_| {});
 4161
 4162    let view = cx.add_window(|cx| {
 4163        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4164        build_editor(buffer, cx)
 4165    });
 4166    _ = view.update(cx, |view, cx| {
 4167        view.fold_ranges(
 4168            vec![
 4169                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4170                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4171                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4172            ],
 4173            true,
 4174            cx,
 4175        );
 4176        view.change_selections(None, cx, |s| {
 4177            s.select_display_ranges([
 4178                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4179                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4180                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4181                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4182            ])
 4183        });
 4184        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4185    });
 4186
 4187    _ = view.update(cx, |view, cx| {
 4188        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4189        assert_eq!(
 4190            view.display_text(cx),
 4191            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4192        );
 4193        assert_eq!(
 4194            view.selections.display_ranges(cx),
 4195            [
 4196                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4197                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4198                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4199                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4200            ]
 4201        );
 4202    });
 4203
 4204    _ = view.update(cx, |view, cx| {
 4205        view.change_selections(None, cx, |s| {
 4206            s.select_display_ranges([
 4207                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4208            ])
 4209        });
 4210        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4211        assert_eq!(
 4212            view.display_text(cx),
 4213            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4214        );
 4215        assert_eq!(
 4216            view.selections.display_ranges(cx),
 4217            [
 4218                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4219                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4220                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4221                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4222                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4223                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4224                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4225                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4226            ]
 4227        );
 4228    });
 4229}
 4230
 4231#[gpui::test]
 4232async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4233    init_test(cx, |_| {});
 4234
 4235    let mut cx = EditorTestContext::new(cx).await;
 4236
 4237    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4238    cx.set_state(indoc!(
 4239        r#"abc
 4240           defˇghi
 4241
 4242           jk
 4243           nlmo
 4244           "#
 4245    ));
 4246
 4247    cx.update_editor(|editor, cx| {
 4248        editor.add_selection_above(&Default::default(), cx);
 4249    });
 4250
 4251    cx.assert_editor_state(indoc!(
 4252        r#"abcˇ
 4253           defˇghi
 4254
 4255           jk
 4256           nlmo
 4257           "#
 4258    ));
 4259
 4260    cx.update_editor(|editor, cx| {
 4261        editor.add_selection_above(&Default::default(), cx);
 4262    });
 4263
 4264    cx.assert_editor_state(indoc!(
 4265        r#"abcˇ
 4266            defˇghi
 4267
 4268            jk
 4269            nlmo
 4270            "#
 4271    ));
 4272
 4273    cx.update_editor(|view, cx| {
 4274        view.add_selection_below(&Default::default(), cx);
 4275    });
 4276
 4277    cx.assert_editor_state(indoc!(
 4278        r#"abc
 4279           defˇghi
 4280
 4281           jk
 4282           nlmo
 4283           "#
 4284    ));
 4285
 4286    cx.update_editor(|view, cx| {
 4287        view.undo_selection(&Default::default(), cx);
 4288    });
 4289
 4290    cx.assert_editor_state(indoc!(
 4291        r#"abcˇ
 4292           defˇghi
 4293
 4294           jk
 4295           nlmo
 4296           "#
 4297    ));
 4298
 4299    cx.update_editor(|view, cx| {
 4300        view.redo_selection(&Default::default(), cx);
 4301    });
 4302
 4303    cx.assert_editor_state(indoc!(
 4304        r#"abc
 4305           defˇghi
 4306
 4307           jk
 4308           nlmo
 4309           "#
 4310    ));
 4311
 4312    cx.update_editor(|view, cx| {
 4313        view.add_selection_below(&Default::default(), cx);
 4314    });
 4315
 4316    cx.assert_editor_state(indoc!(
 4317        r#"abc
 4318           defˇghi
 4319
 4320           jk
 4321           nlmˇo
 4322           "#
 4323    ));
 4324
 4325    cx.update_editor(|view, cx| {
 4326        view.add_selection_below(&Default::default(), cx);
 4327    });
 4328
 4329    cx.assert_editor_state(indoc!(
 4330        r#"abc
 4331           defˇghi
 4332
 4333           jk
 4334           nlmˇo
 4335           "#
 4336    ));
 4337
 4338    // change selections
 4339    cx.set_state(indoc!(
 4340        r#"abc
 4341           def«ˇg»hi
 4342
 4343           jk
 4344           nlmo
 4345           "#
 4346    ));
 4347
 4348    cx.update_editor(|view, cx| {
 4349        view.add_selection_below(&Default::default(), cx);
 4350    });
 4351
 4352    cx.assert_editor_state(indoc!(
 4353        r#"abc
 4354           def«ˇg»hi
 4355
 4356           jk
 4357           nlm«ˇo»
 4358           "#
 4359    ));
 4360
 4361    cx.update_editor(|view, cx| {
 4362        view.add_selection_below(&Default::default(), cx);
 4363    });
 4364
 4365    cx.assert_editor_state(indoc!(
 4366        r#"abc
 4367           def«ˇg»hi
 4368
 4369           jk
 4370           nlm«ˇo»
 4371           "#
 4372    ));
 4373
 4374    cx.update_editor(|view, cx| {
 4375        view.add_selection_above(&Default::default(), cx);
 4376    });
 4377
 4378    cx.assert_editor_state(indoc!(
 4379        r#"abc
 4380           def«ˇg»hi
 4381
 4382           jk
 4383           nlmo
 4384           "#
 4385    ));
 4386
 4387    cx.update_editor(|view, cx| {
 4388        view.add_selection_above(&Default::default(), cx);
 4389    });
 4390
 4391    cx.assert_editor_state(indoc!(
 4392        r#"abc
 4393           def«ˇg»hi
 4394
 4395           jk
 4396           nlmo
 4397           "#
 4398    ));
 4399
 4400    // Change selections again
 4401    cx.set_state(indoc!(
 4402        r#"a«bc
 4403           defgˇ»hi
 4404
 4405           jk
 4406           nlmo
 4407           "#
 4408    ));
 4409
 4410    cx.update_editor(|view, cx| {
 4411        view.add_selection_below(&Default::default(), cx);
 4412    });
 4413
 4414    cx.assert_editor_state(indoc!(
 4415        r#"a«bcˇ»
 4416           d«efgˇ»hi
 4417
 4418           j«kˇ»
 4419           nlmo
 4420           "#
 4421    ));
 4422
 4423    cx.update_editor(|view, cx| {
 4424        view.add_selection_below(&Default::default(), cx);
 4425    });
 4426    cx.assert_editor_state(indoc!(
 4427        r#"a«bcˇ»
 4428           d«efgˇ»hi
 4429
 4430           j«kˇ»
 4431           n«lmoˇ»
 4432           "#
 4433    ));
 4434    cx.update_editor(|view, cx| {
 4435        view.add_selection_above(&Default::default(), cx);
 4436    });
 4437
 4438    cx.assert_editor_state(indoc!(
 4439        r#"a«bcˇ»
 4440           d«efgˇ»hi
 4441
 4442           j«kˇ»
 4443           nlmo
 4444           "#
 4445    ));
 4446
 4447    // Change selections again
 4448    cx.set_state(indoc!(
 4449        r#"abc
 4450           d«ˇefghi
 4451
 4452           jk
 4453           nlm»o
 4454           "#
 4455    ));
 4456
 4457    cx.update_editor(|view, cx| {
 4458        view.add_selection_above(&Default::default(), cx);
 4459    });
 4460
 4461    cx.assert_editor_state(indoc!(
 4462        r#"a«ˇbc»
 4463           d«ˇef»ghi
 4464
 4465           j«ˇk»
 4466           n«ˇlm»o
 4467           "#
 4468    ));
 4469
 4470    cx.update_editor(|view, cx| {
 4471        view.add_selection_below(&Default::default(), cx);
 4472    });
 4473
 4474    cx.assert_editor_state(indoc!(
 4475        r#"abc
 4476           d«ˇef»ghi
 4477
 4478           j«ˇk»
 4479           n«ˇlm»o
 4480           "#
 4481    ));
 4482}
 4483
 4484#[gpui::test]
 4485async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4486    init_test(cx, |_| {});
 4487
 4488    let mut cx = EditorTestContext::new(cx).await;
 4489    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4490
 4491    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4492        .unwrap();
 4493    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4494
 4495    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4496        .unwrap();
 4497    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4498
 4499    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4500    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4501
 4502    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4503    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4504
 4505    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4506        .unwrap();
 4507    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4508
 4509    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4510        .unwrap();
 4511    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4512}
 4513
 4514#[gpui::test]
 4515async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4516    init_test(cx, |_| {});
 4517
 4518    let mut cx = EditorTestContext::new(cx).await;
 4519    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4520
 4521    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4522        .unwrap();
 4523    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4524}
 4525
 4526#[gpui::test]
 4527async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4528    init_test(cx, |_| {});
 4529
 4530    let mut cx = EditorTestContext::new(cx).await;
 4531    cx.set_state(
 4532        r#"let foo = 2;
 4533lˇet foo = 2;
 4534let fooˇ = 2;
 4535let foo = 2;
 4536let foo = ˇ2;"#,
 4537    );
 4538
 4539    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4540        .unwrap();
 4541    cx.assert_editor_state(
 4542        r#"let foo = 2;
 4543«letˇ» foo = 2;
 4544let «fooˇ» = 2;
 4545let foo = 2;
 4546let foo = «2ˇ»;"#,
 4547    );
 4548
 4549    // noop for multiple selections with different contents
 4550    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4551        .unwrap();
 4552    cx.assert_editor_state(
 4553        r#"let foo = 2;
 4554«letˇ» foo = 2;
 4555let «fooˇ» = 2;
 4556let foo = 2;
 4557let foo = «2ˇ»;"#,
 4558    );
 4559}
 4560
 4561#[gpui::test]
 4562async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4563    init_test(cx, |_| {});
 4564
 4565    let mut cx = EditorTestContext::new_multibuffer(
 4566        cx,
 4567        [
 4568            &indoc! {
 4569                "aaa\n«bbb\nccc\n»ddd"
 4570            },
 4571            &indoc! {
 4572                "aaa\n«bbb\nccc\n»ddd"
 4573            },
 4574        ],
 4575    );
 4576
 4577    cx.assert_editor_state(indoc! {"
 4578        ˇbbb
 4579        ccc
 4580
 4581        bbb
 4582        ccc
 4583        "});
 4584    cx.dispatch_action(SelectPrevious::default());
 4585    cx.assert_editor_state(indoc! {"
 4586                «bbbˇ»
 4587                ccc
 4588
 4589                bbb
 4590                ccc
 4591                "});
 4592    cx.dispatch_action(SelectPrevious::default());
 4593    cx.assert_editor_state(indoc! {"
 4594                «bbbˇ»
 4595                ccc
 4596
 4597                «bbbˇ»
 4598                ccc
 4599                "});
 4600}
 4601
 4602#[gpui::test]
 4603async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 4604    init_test(cx, |_| {});
 4605
 4606    let mut cx = EditorTestContext::new(cx).await;
 4607    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4608
 4609    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4610        .unwrap();
 4611    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4612
 4613    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4614        .unwrap();
 4615    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4616
 4617    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4618    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4619
 4620    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4621    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4622
 4623    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4624        .unwrap();
 4625    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 4626
 4627    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4628        .unwrap();
 4629    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 4630
 4631    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4632        .unwrap();
 4633    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4634}
 4635
 4636#[gpui::test]
 4637async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4638    init_test(cx, |_| {});
 4639
 4640    let mut cx = EditorTestContext::new(cx).await;
 4641    cx.set_state(
 4642        r#"let foo = 2;
 4643lˇet foo = 2;
 4644let fooˇ = 2;
 4645let foo = 2;
 4646let foo = ˇ2;"#,
 4647    );
 4648
 4649    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4650        .unwrap();
 4651    cx.assert_editor_state(
 4652        r#"let foo = 2;
 4653«letˇ» foo = 2;
 4654let «fooˇ» = 2;
 4655let foo = 2;
 4656let foo = «2ˇ»;"#,
 4657    );
 4658
 4659    // noop for multiple selections with different contents
 4660    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4661        .unwrap();
 4662    cx.assert_editor_state(
 4663        r#"let foo = 2;
 4664«letˇ» foo = 2;
 4665let «fooˇ» = 2;
 4666let foo = 2;
 4667let foo = «2ˇ»;"#,
 4668    );
 4669}
 4670
 4671#[gpui::test]
 4672async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 4673    init_test(cx, |_| {});
 4674
 4675    let mut cx = EditorTestContext::new(cx).await;
 4676    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 4677
 4678    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4679        .unwrap();
 4680    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4681
 4682    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4683        .unwrap();
 4684    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4685
 4686    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4687    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4688
 4689    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4690    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4691
 4692    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4693        .unwrap();
 4694    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 4695
 4696    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4697        .unwrap();
 4698    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4699}
 4700
 4701#[gpui::test]
 4702async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 4703    init_test(cx, |_| {});
 4704
 4705    let language = Arc::new(Language::new(
 4706        LanguageConfig::default(),
 4707        Some(tree_sitter_rust::language()),
 4708    ));
 4709
 4710    let text = r#"
 4711        use mod1::mod2::{mod3, mod4};
 4712
 4713        fn fn_1(param1: bool, param2: &str) {
 4714            let var1 = "text";
 4715        }
 4716    "#
 4717    .unindent();
 4718
 4719    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4720    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4721    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4722
 4723    editor
 4724        .condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 4725        .await;
 4726
 4727    editor.update(cx, |view, cx| {
 4728        view.change_selections(None, cx, |s| {
 4729            s.select_display_ranges([
 4730                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4731                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4732                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4733            ]);
 4734        });
 4735        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4736    });
 4737    editor.update(cx, |editor, cx| {
 4738        assert_text_with_selections(
 4739            editor,
 4740            indoc! {r#"
 4741                use mod1::mod2::{mod3, «mod4ˇ»};
 4742
 4743                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4744                    let var1 = "«textˇ»";
 4745                }
 4746            "#},
 4747            cx,
 4748        );
 4749    });
 4750
 4751    editor.update(cx, |view, cx| {
 4752        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4753    });
 4754    editor.update(cx, |editor, cx| {
 4755        assert_text_with_selections(
 4756            editor,
 4757            indoc! {r#"
 4758                use mod1::mod2::«{mod3, mod4}ˇ»;
 4759
 4760                «ˇfn fn_1(param1: bool, param2: &str) {
 4761                    let var1 = "text";
 4762 4763            "#},
 4764            cx,
 4765        );
 4766    });
 4767
 4768    editor.update(cx, |view, cx| {
 4769        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4770    });
 4771    assert_eq!(
 4772        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4773        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4774    );
 4775
 4776    // Trying to expand the selected syntax node one more time has no effect.
 4777    editor.update(cx, |view, cx| {
 4778        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4779    });
 4780    assert_eq!(
 4781        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4782        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4783    );
 4784
 4785    editor.update(cx, |view, cx| {
 4786        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4787    });
 4788    editor.update(cx, |editor, cx| {
 4789        assert_text_with_selections(
 4790            editor,
 4791            indoc! {r#"
 4792                use mod1::mod2::«{mod3, mod4}ˇ»;
 4793
 4794                «ˇfn fn_1(param1: bool, param2: &str) {
 4795                    let var1 = "text";
 4796 4797            "#},
 4798            cx,
 4799        );
 4800    });
 4801
 4802    editor.update(cx, |view, cx| {
 4803        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4804    });
 4805    editor.update(cx, |editor, cx| {
 4806        assert_text_with_selections(
 4807            editor,
 4808            indoc! {r#"
 4809                use mod1::mod2::{mod3, «mod4ˇ»};
 4810
 4811                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4812                    let var1 = "«textˇ»";
 4813                }
 4814            "#},
 4815            cx,
 4816        );
 4817    });
 4818
 4819    editor.update(cx, |view, cx| {
 4820        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4821    });
 4822    editor.update(cx, |editor, cx| {
 4823        assert_text_with_selections(
 4824            editor,
 4825            indoc! {r#"
 4826                use mod1::mod2::{mod3, mo«ˇ»d4};
 4827
 4828                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 4829                    let var1 = "te«ˇ»xt";
 4830                }
 4831            "#},
 4832            cx,
 4833        );
 4834    });
 4835
 4836    // Trying to shrink the selected syntax node one more time has no effect.
 4837    editor.update(cx, |view, cx| {
 4838        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4839    });
 4840    editor.update(cx, |editor, cx| {
 4841        assert_text_with_selections(
 4842            editor,
 4843            indoc! {r#"
 4844                use mod1::mod2::{mod3, mo«ˇ»d4};
 4845
 4846                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 4847                    let var1 = "te«ˇ»xt";
 4848                }
 4849            "#},
 4850            cx,
 4851        );
 4852    });
 4853
 4854    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 4855    // a fold.
 4856    editor.update(cx, |view, cx| {
 4857        view.fold_ranges(
 4858            vec![
 4859                (
 4860                    Point::new(0, 21)..Point::new(0, 24),
 4861                    FoldPlaceholder::test(),
 4862                ),
 4863                (
 4864                    Point::new(3, 20)..Point::new(3, 22),
 4865                    FoldPlaceholder::test(),
 4866                ),
 4867            ],
 4868            true,
 4869            cx,
 4870        );
 4871        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4872    });
 4873    editor.update(cx, |editor, cx| {
 4874        assert_text_with_selections(
 4875            editor,
 4876            indoc! {r#"
 4877                use mod1::mod2::«{mod3, mod4}ˇ»;
 4878
 4879                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4880                    «let var1 = "text";ˇ»
 4881                }
 4882            "#},
 4883            cx,
 4884        );
 4885    });
 4886}
 4887
 4888#[gpui::test]
 4889async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 4890    init_test(cx, |_| {});
 4891
 4892    let language = Arc::new(
 4893        Language::new(
 4894            LanguageConfig {
 4895                brackets: BracketPairConfig {
 4896                    pairs: vec![
 4897                        BracketPair {
 4898                            start: "{".to_string(),
 4899                            end: "}".to_string(),
 4900                            close: false,
 4901                            surround: false,
 4902                            newline: true,
 4903                        },
 4904                        BracketPair {
 4905                            start: "(".to_string(),
 4906                            end: ")".to_string(),
 4907                            close: false,
 4908                            surround: false,
 4909                            newline: true,
 4910                        },
 4911                    ],
 4912                    ..Default::default()
 4913                },
 4914                ..Default::default()
 4915            },
 4916            Some(tree_sitter_rust::language()),
 4917        )
 4918        .with_indents_query(
 4919            r#"
 4920                (_ "(" ")" @end) @indent
 4921                (_ "{" "}" @end) @indent
 4922            "#,
 4923        )
 4924        .unwrap(),
 4925    );
 4926
 4927    let text = "fn a() {}";
 4928
 4929    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4930    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4931    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4932    editor
 4933        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 4934        .await;
 4935
 4936    _ = editor.update(cx, |editor, cx| {
 4937        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 4938        editor.newline(&Newline, cx);
 4939        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 4940        assert_eq!(
 4941            editor.selections.ranges(cx),
 4942            &[
 4943                Point::new(1, 4)..Point::new(1, 4),
 4944                Point::new(3, 4)..Point::new(3, 4),
 4945                Point::new(5, 0)..Point::new(5, 0)
 4946            ]
 4947        );
 4948    });
 4949}
 4950
 4951#[gpui::test]
 4952async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 4953    init_test(cx, |_| {});
 4954
 4955    let mut cx = EditorTestContext::new(cx).await;
 4956
 4957    let language = Arc::new(Language::new(
 4958        LanguageConfig {
 4959            brackets: BracketPairConfig {
 4960                pairs: vec![
 4961                    BracketPair {
 4962                        start: "{".to_string(),
 4963                        end: "}".to_string(),
 4964                        close: true,
 4965                        surround: true,
 4966                        newline: true,
 4967                    },
 4968                    BracketPair {
 4969                        start: "(".to_string(),
 4970                        end: ")".to_string(),
 4971                        close: true,
 4972                        surround: true,
 4973                        newline: true,
 4974                    },
 4975                    BracketPair {
 4976                        start: "/*".to_string(),
 4977                        end: " */".to_string(),
 4978                        close: true,
 4979                        surround: true,
 4980                        newline: true,
 4981                    },
 4982                    BracketPair {
 4983                        start: "[".to_string(),
 4984                        end: "]".to_string(),
 4985                        close: false,
 4986                        surround: false,
 4987                        newline: true,
 4988                    },
 4989                    BracketPair {
 4990                        start: "\"".to_string(),
 4991                        end: "\"".to_string(),
 4992                        close: true,
 4993                        surround: true,
 4994                        newline: false,
 4995                    },
 4996                    BracketPair {
 4997                        start: "<".to_string(),
 4998                        end: ">".to_string(),
 4999                        close: false,
 5000                        surround: true,
 5001                        newline: true,
 5002                    },
 5003                ],
 5004                ..Default::default()
 5005            },
 5006            autoclose_before: "})]".to_string(),
 5007            ..Default::default()
 5008        },
 5009        Some(tree_sitter_rust::language()),
 5010    ));
 5011
 5012    cx.language_registry().add(language.clone());
 5013    cx.update_buffer(|buffer, cx| {
 5014        buffer.set_language(Some(language), cx);
 5015    });
 5016
 5017    cx.set_state(
 5018        &r#"
 5019            🏀ˇ
 5020            εˇ
 5021            ❤️ˇ
 5022        "#
 5023        .unindent(),
 5024    );
 5025
 5026    // autoclose multiple nested brackets at multiple cursors
 5027    cx.update_editor(|view, cx| {
 5028        view.handle_input("{", cx);
 5029        view.handle_input("{", cx);
 5030        view.handle_input("{", cx);
 5031    });
 5032    cx.assert_editor_state(
 5033        &"
 5034            🏀{{{ˇ}}}
 5035            ε{{{ˇ}}}
 5036            ❤️{{{ˇ}}}
 5037        "
 5038        .unindent(),
 5039    );
 5040
 5041    // insert a different closing bracket
 5042    cx.update_editor(|view, cx| {
 5043        view.handle_input(")", cx);
 5044    });
 5045    cx.assert_editor_state(
 5046        &"
 5047            🏀{{{)ˇ}}}
 5048            ε{{{)ˇ}}}
 5049            ❤️{{{)ˇ}}}
 5050        "
 5051        .unindent(),
 5052    );
 5053
 5054    // skip over the auto-closed brackets when typing a closing bracket
 5055    cx.update_editor(|view, cx| {
 5056        view.move_right(&MoveRight, cx);
 5057        view.handle_input("}", cx);
 5058        view.handle_input("}", cx);
 5059        view.handle_input("}", cx);
 5060    });
 5061    cx.assert_editor_state(
 5062        &"
 5063            🏀{{{)}}}}ˇ
 5064            ε{{{)}}}}ˇ
 5065            ❤️{{{)}}}}ˇ
 5066        "
 5067        .unindent(),
 5068    );
 5069
 5070    // autoclose multi-character pairs
 5071    cx.set_state(
 5072        &"
 5073            ˇ
 5074            ˇ
 5075        "
 5076        .unindent(),
 5077    );
 5078    cx.update_editor(|view, cx| {
 5079        view.handle_input("/", cx);
 5080        view.handle_input("*", cx);
 5081    });
 5082    cx.assert_editor_state(
 5083        &"
 5084            /*ˇ */
 5085            /*ˇ */
 5086        "
 5087        .unindent(),
 5088    );
 5089
 5090    // one cursor autocloses a multi-character pair, one cursor
 5091    // does not autoclose.
 5092    cx.set_state(
 5093        &"
 5094 5095            ˇ
 5096        "
 5097        .unindent(),
 5098    );
 5099    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5100    cx.assert_editor_state(
 5101        &"
 5102            /*ˇ */
 5103 5104        "
 5105        .unindent(),
 5106    );
 5107
 5108    // Don't autoclose if the next character isn't whitespace and isn't
 5109    // listed in the language's "autoclose_before" section.
 5110    cx.set_state("ˇa b");
 5111    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5112    cx.assert_editor_state("{ˇa b");
 5113
 5114    // Don't autoclose if `close` is false for the bracket pair
 5115    cx.set_state("ˇ");
 5116    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5117    cx.assert_editor_state("");
 5118
 5119    // Surround with brackets if text is selected
 5120    cx.set_state("«aˇ» b");
 5121    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5122    cx.assert_editor_state("{«aˇ»} b");
 5123
 5124    // Autclose pair where the start and end characters are the same
 5125    cx.set_state("");
 5126    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5127    cx.assert_editor_state("a\"ˇ\"");
 5128    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5129    cx.assert_editor_state("a\"\"ˇ");
 5130
 5131    // Don't autoclose pair if autoclose is disabled
 5132    cx.set_state("ˇ");
 5133    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5134    cx.assert_editor_state("");
 5135
 5136    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5137    cx.set_state("«aˇ» b");
 5138    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5139    cx.assert_editor_state("<«aˇ»> b");
 5140}
 5141
 5142#[gpui::test]
 5143async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5144    init_test(cx, |settings| {
 5145        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5146    });
 5147
 5148    let mut cx = EditorTestContext::new(cx).await;
 5149
 5150    let language = Arc::new(Language::new(
 5151        LanguageConfig {
 5152            brackets: BracketPairConfig {
 5153                pairs: vec![
 5154                    BracketPair {
 5155                        start: "{".to_string(),
 5156                        end: "}".to_string(),
 5157                        close: true,
 5158                        surround: true,
 5159                        newline: true,
 5160                    },
 5161                    BracketPair {
 5162                        start: "(".to_string(),
 5163                        end: ")".to_string(),
 5164                        close: true,
 5165                        surround: true,
 5166                        newline: true,
 5167                    },
 5168                    BracketPair {
 5169                        start: "[".to_string(),
 5170                        end: "]".to_string(),
 5171                        close: false,
 5172                        surround: false,
 5173                        newline: true,
 5174                    },
 5175                ],
 5176                ..Default::default()
 5177            },
 5178            autoclose_before: "})]".to_string(),
 5179            ..Default::default()
 5180        },
 5181        Some(tree_sitter_rust::language()),
 5182    ));
 5183
 5184    cx.language_registry().add(language.clone());
 5185    cx.update_buffer(|buffer, cx| {
 5186        buffer.set_language(Some(language), cx);
 5187    });
 5188
 5189    cx.set_state(
 5190        &"
 5191            ˇ
 5192            ˇ
 5193            ˇ
 5194        "
 5195        .unindent(),
 5196    );
 5197
 5198    // ensure only matching closing brackets are skipped over
 5199    cx.update_editor(|view, cx| {
 5200        view.handle_input("}", cx);
 5201        view.move_left(&MoveLeft, cx);
 5202        view.handle_input(")", cx);
 5203        view.move_left(&MoveLeft, cx);
 5204    });
 5205    cx.assert_editor_state(
 5206        &"
 5207            ˇ)}
 5208            ˇ)}
 5209            ˇ)}
 5210        "
 5211        .unindent(),
 5212    );
 5213
 5214    // skip-over closing brackets at multiple cursors
 5215    cx.update_editor(|view, cx| {
 5216        view.handle_input(")", cx);
 5217        view.handle_input("}", cx);
 5218    });
 5219    cx.assert_editor_state(
 5220        &"
 5221            )}ˇ
 5222            )}ˇ
 5223            )}ˇ
 5224        "
 5225        .unindent(),
 5226    );
 5227
 5228    // ignore non-close brackets
 5229    cx.update_editor(|view, cx| {
 5230        view.handle_input("]", cx);
 5231        view.move_left(&MoveLeft, cx);
 5232        view.handle_input("]", cx);
 5233    });
 5234    cx.assert_editor_state(
 5235        &"
 5236            )}]ˇ]
 5237            )}]ˇ]
 5238            )}]ˇ]
 5239        "
 5240        .unindent(),
 5241    );
 5242}
 5243
 5244#[gpui::test]
 5245async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5246    init_test(cx, |_| {});
 5247
 5248    let mut cx = EditorTestContext::new(cx).await;
 5249
 5250    let html_language = Arc::new(
 5251        Language::new(
 5252            LanguageConfig {
 5253                name: "HTML".into(),
 5254                brackets: BracketPairConfig {
 5255                    pairs: vec![
 5256                        BracketPair {
 5257                            start: "<".into(),
 5258                            end: ">".into(),
 5259                            close: true,
 5260                            ..Default::default()
 5261                        },
 5262                        BracketPair {
 5263                            start: "{".into(),
 5264                            end: "}".into(),
 5265                            close: true,
 5266                            ..Default::default()
 5267                        },
 5268                        BracketPair {
 5269                            start: "(".into(),
 5270                            end: ")".into(),
 5271                            close: true,
 5272                            ..Default::default()
 5273                        },
 5274                    ],
 5275                    ..Default::default()
 5276                },
 5277                autoclose_before: "})]>".into(),
 5278                ..Default::default()
 5279            },
 5280            Some(tree_sitter_html::language()),
 5281        )
 5282        .with_injection_query(
 5283            r#"
 5284            (script_element
 5285                (raw_text) @content
 5286                (#set! "language" "javascript"))
 5287            "#,
 5288        )
 5289        .unwrap(),
 5290    );
 5291
 5292    let javascript_language = Arc::new(Language::new(
 5293        LanguageConfig {
 5294            name: "JavaScript".into(),
 5295            brackets: BracketPairConfig {
 5296                pairs: vec![
 5297                    BracketPair {
 5298                        start: "/*".into(),
 5299                        end: " */".into(),
 5300                        close: true,
 5301                        ..Default::default()
 5302                    },
 5303                    BracketPair {
 5304                        start: "{".into(),
 5305                        end: "}".into(),
 5306                        close: true,
 5307                        ..Default::default()
 5308                    },
 5309                    BracketPair {
 5310                        start: "(".into(),
 5311                        end: ")".into(),
 5312                        close: true,
 5313                        ..Default::default()
 5314                    },
 5315                ],
 5316                ..Default::default()
 5317            },
 5318            autoclose_before: "})]>".into(),
 5319            ..Default::default()
 5320        },
 5321        Some(tree_sitter_typescript::language_tsx()),
 5322    ));
 5323
 5324    cx.language_registry().add(html_language.clone());
 5325    cx.language_registry().add(javascript_language.clone());
 5326
 5327    cx.update_buffer(|buffer, cx| {
 5328        buffer.set_language(Some(html_language), cx);
 5329    });
 5330
 5331    cx.set_state(
 5332        &r#"
 5333            <body>ˇ
 5334                <script>
 5335                    var x = 1;ˇ
 5336                </script>
 5337            </body>ˇ
 5338        "#
 5339        .unindent(),
 5340    );
 5341
 5342    // Precondition: different languages are active at different locations.
 5343    cx.update_editor(|editor, cx| {
 5344        let snapshot = editor.snapshot(cx);
 5345        let cursors = editor.selections.ranges::<usize>(cx);
 5346        let languages = cursors
 5347            .iter()
 5348            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5349            .collect::<Vec<_>>();
 5350        assert_eq!(
 5351            languages,
 5352            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5353        );
 5354    });
 5355
 5356    // Angle brackets autoclose in HTML, but not JavaScript.
 5357    cx.update_editor(|editor, cx| {
 5358        editor.handle_input("<", cx);
 5359        editor.handle_input("a", cx);
 5360    });
 5361    cx.assert_editor_state(
 5362        &r#"
 5363            <body><aˇ>
 5364                <script>
 5365                    var x = 1;<aˇ
 5366                </script>
 5367            </body><aˇ>
 5368        "#
 5369        .unindent(),
 5370    );
 5371
 5372    // Curly braces and parens autoclose in both HTML and JavaScript.
 5373    cx.update_editor(|editor, cx| {
 5374        editor.handle_input(" b=", cx);
 5375        editor.handle_input("{", cx);
 5376        editor.handle_input("c", cx);
 5377        editor.handle_input("(", cx);
 5378    });
 5379    cx.assert_editor_state(
 5380        &r#"
 5381            <body><a b={c(ˇ)}>
 5382                <script>
 5383                    var x = 1;<a b={c(ˇ)}
 5384                </script>
 5385            </body><a b={c(ˇ)}>
 5386        "#
 5387        .unindent(),
 5388    );
 5389
 5390    // Brackets that were already autoclosed are skipped.
 5391    cx.update_editor(|editor, cx| {
 5392        editor.handle_input(")", cx);
 5393        editor.handle_input("d", cx);
 5394        editor.handle_input("}", cx);
 5395    });
 5396    cx.assert_editor_state(
 5397        &r#"
 5398            <body><a b={c()d}ˇ>
 5399                <script>
 5400                    var x = 1;<a b={c()d}ˇ
 5401                </script>
 5402            </body><a b={c()d}ˇ>
 5403        "#
 5404        .unindent(),
 5405    );
 5406    cx.update_editor(|editor, cx| {
 5407        editor.handle_input(">", cx);
 5408    });
 5409    cx.assert_editor_state(
 5410        &r#"
 5411            <body><a b={c()d}>ˇ
 5412                <script>
 5413                    var x = 1;<a b={c()d}>ˇ
 5414                </script>
 5415            </body><a b={c()d}>ˇ
 5416        "#
 5417        .unindent(),
 5418    );
 5419
 5420    // Reset
 5421    cx.set_state(
 5422        &r#"
 5423            <body>ˇ
 5424                <script>
 5425                    var x = 1;ˇ
 5426                </script>
 5427            </body>ˇ
 5428        "#
 5429        .unindent(),
 5430    );
 5431
 5432    cx.update_editor(|editor, cx| {
 5433        editor.handle_input("<", cx);
 5434    });
 5435    cx.assert_editor_state(
 5436        &r#"
 5437            <body><ˇ>
 5438                <script>
 5439                    var x = 1;<ˇ
 5440                </script>
 5441            </body><ˇ>
 5442        "#
 5443        .unindent(),
 5444    );
 5445
 5446    // When backspacing, the closing angle brackets are removed.
 5447    cx.update_editor(|editor, cx| {
 5448        editor.backspace(&Backspace, cx);
 5449    });
 5450    cx.assert_editor_state(
 5451        &r#"
 5452            <body>ˇ
 5453                <script>
 5454                    var x = 1;ˇ
 5455                </script>
 5456            </body>ˇ
 5457        "#
 5458        .unindent(),
 5459    );
 5460
 5461    // Block comments autoclose in JavaScript, but not HTML.
 5462    cx.update_editor(|editor, cx| {
 5463        editor.handle_input("/", cx);
 5464        editor.handle_input("*", cx);
 5465    });
 5466    cx.assert_editor_state(
 5467        &r#"
 5468            <body>/*ˇ
 5469                <script>
 5470                    var x = 1;/*ˇ */
 5471                </script>
 5472            </body>/*ˇ
 5473        "#
 5474        .unindent(),
 5475    );
 5476}
 5477
 5478#[gpui::test]
 5479async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5480    init_test(cx, |_| {});
 5481
 5482    let mut cx = EditorTestContext::new(cx).await;
 5483
 5484    let rust_language = Arc::new(
 5485        Language::new(
 5486            LanguageConfig {
 5487                name: "Rust".into(),
 5488                brackets: serde_json::from_value(json!([
 5489                    { "start": "{", "end": "}", "close": true, "newline": true },
 5490                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5491                ]))
 5492                .unwrap(),
 5493                autoclose_before: "})]>".into(),
 5494                ..Default::default()
 5495            },
 5496            Some(tree_sitter_rust::language()),
 5497        )
 5498        .with_override_query("(string_literal) @string")
 5499        .unwrap(),
 5500    );
 5501
 5502    cx.language_registry().add(rust_language.clone());
 5503    cx.update_buffer(|buffer, cx| {
 5504        buffer.set_language(Some(rust_language), cx);
 5505    });
 5506
 5507    cx.set_state(
 5508        &r#"
 5509            let x = ˇ
 5510        "#
 5511        .unindent(),
 5512    );
 5513
 5514    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5515    cx.update_editor(|editor, cx| {
 5516        editor.handle_input("\"", cx);
 5517    });
 5518    cx.assert_editor_state(
 5519        &r#"
 5520            let x = "ˇ"
 5521        "#
 5522        .unindent(),
 5523    );
 5524
 5525    // Inserting another quotation mark. The cursor moves across the existing
 5526    // automatically-inserted quotation mark.
 5527    cx.update_editor(|editor, cx| {
 5528        editor.handle_input("\"", cx);
 5529    });
 5530    cx.assert_editor_state(
 5531        &r#"
 5532            let x = ""ˇ
 5533        "#
 5534        .unindent(),
 5535    );
 5536
 5537    // Reset
 5538    cx.set_state(
 5539        &r#"
 5540            let x = ˇ
 5541        "#
 5542        .unindent(),
 5543    );
 5544
 5545    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5546    cx.update_editor(|editor, cx| {
 5547        editor.handle_input("\"", cx);
 5548        editor.handle_input(" ", cx);
 5549        editor.move_left(&Default::default(), cx);
 5550        editor.handle_input("\\", cx);
 5551        editor.handle_input("\"", cx);
 5552    });
 5553    cx.assert_editor_state(
 5554        &r#"
 5555            let x = "\"ˇ "
 5556        "#
 5557        .unindent(),
 5558    );
 5559
 5560    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5561    // mark. Nothing is inserted.
 5562    cx.update_editor(|editor, cx| {
 5563        editor.move_right(&Default::default(), cx);
 5564        editor.handle_input("\"", cx);
 5565    });
 5566    cx.assert_editor_state(
 5567        &r#"
 5568            let x = "\" "ˇ
 5569        "#
 5570        .unindent(),
 5571    );
 5572}
 5573
 5574#[gpui::test]
 5575async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5576    init_test(cx, |_| {});
 5577
 5578    let language = Arc::new(Language::new(
 5579        LanguageConfig {
 5580            brackets: BracketPairConfig {
 5581                pairs: vec![
 5582                    BracketPair {
 5583                        start: "{".to_string(),
 5584                        end: "}".to_string(),
 5585                        close: true,
 5586                        surround: true,
 5587                        newline: true,
 5588                    },
 5589                    BracketPair {
 5590                        start: "/* ".to_string(),
 5591                        end: "*/".to_string(),
 5592                        close: true,
 5593                        surround: true,
 5594                        ..Default::default()
 5595                    },
 5596                ],
 5597                ..Default::default()
 5598            },
 5599            ..Default::default()
 5600        },
 5601        Some(tree_sitter_rust::language()),
 5602    ));
 5603
 5604    let text = r#"
 5605        a
 5606        b
 5607        c
 5608    "#
 5609    .unindent();
 5610
 5611    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5612    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5613    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5614    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5615        .await;
 5616
 5617    _ = view.update(cx, |view, cx| {
 5618        view.change_selections(None, cx, |s| {
 5619            s.select_display_ranges([
 5620                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5621                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5622                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5623            ])
 5624        });
 5625
 5626        view.handle_input("{", cx);
 5627        view.handle_input("{", cx);
 5628        view.handle_input("{", cx);
 5629        assert_eq!(
 5630            view.text(cx),
 5631            "
 5632                {{{a}}}
 5633                {{{b}}}
 5634                {{{c}}}
 5635            "
 5636            .unindent()
 5637        );
 5638        assert_eq!(
 5639            view.selections.display_ranges(cx),
 5640            [
 5641                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5642                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5643                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5644            ]
 5645        );
 5646
 5647        view.undo(&Undo, cx);
 5648        view.undo(&Undo, cx);
 5649        view.undo(&Undo, cx);
 5650        assert_eq!(
 5651            view.text(cx),
 5652            "
 5653                a
 5654                b
 5655                c
 5656            "
 5657            .unindent()
 5658        );
 5659        assert_eq!(
 5660            view.selections.display_ranges(cx),
 5661            [
 5662                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5663                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5664                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5665            ]
 5666        );
 5667
 5668        // Ensure inserting the first character of a multi-byte bracket pair
 5669        // doesn't surround the selections with the bracket.
 5670        view.handle_input("/", cx);
 5671        assert_eq!(
 5672            view.text(cx),
 5673            "
 5674                /
 5675                /
 5676                /
 5677            "
 5678            .unindent()
 5679        );
 5680        assert_eq!(
 5681            view.selections.display_ranges(cx),
 5682            [
 5683                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5684                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5685                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5686            ]
 5687        );
 5688
 5689        view.undo(&Undo, cx);
 5690        assert_eq!(
 5691            view.text(cx),
 5692            "
 5693                a
 5694                b
 5695                c
 5696            "
 5697            .unindent()
 5698        );
 5699        assert_eq!(
 5700            view.selections.display_ranges(cx),
 5701            [
 5702                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5703                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5704                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5705            ]
 5706        );
 5707
 5708        // Ensure inserting the last character of a multi-byte bracket pair
 5709        // doesn't surround the selections with the bracket.
 5710        view.handle_input("*", cx);
 5711        assert_eq!(
 5712            view.text(cx),
 5713            "
 5714                *
 5715                *
 5716                *
 5717            "
 5718            .unindent()
 5719        );
 5720        assert_eq!(
 5721            view.selections.display_ranges(cx),
 5722            [
 5723                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5724                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5725                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5726            ]
 5727        );
 5728    });
 5729}
 5730
 5731#[gpui::test]
 5732async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 5733    init_test(cx, |_| {});
 5734
 5735    let language = Arc::new(Language::new(
 5736        LanguageConfig {
 5737            brackets: BracketPairConfig {
 5738                pairs: vec![BracketPair {
 5739                    start: "{".to_string(),
 5740                    end: "}".to_string(),
 5741                    close: true,
 5742                    surround: true,
 5743                    newline: true,
 5744                }],
 5745                ..Default::default()
 5746            },
 5747            autoclose_before: "}".to_string(),
 5748            ..Default::default()
 5749        },
 5750        Some(tree_sitter_rust::language()),
 5751    ));
 5752
 5753    let text = r#"
 5754        a
 5755        b
 5756        c
 5757    "#
 5758    .unindent();
 5759
 5760    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5761    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5762    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5763    editor
 5764        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5765        .await;
 5766
 5767    _ = editor.update(cx, |editor, cx| {
 5768        editor.change_selections(None, cx, |s| {
 5769            s.select_ranges([
 5770                Point::new(0, 1)..Point::new(0, 1),
 5771                Point::new(1, 1)..Point::new(1, 1),
 5772                Point::new(2, 1)..Point::new(2, 1),
 5773            ])
 5774        });
 5775
 5776        editor.handle_input("{", cx);
 5777        editor.handle_input("{", cx);
 5778        editor.handle_input("_", cx);
 5779        assert_eq!(
 5780            editor.text(cx),
 5781            "
 5782                a{{_}}
 5783                b{{_}}
 5784                c{{_}}
 5785            "
 5786            .unindent()
 5787        );
 5788        assert_eq!(
 5789            editor.selections.ranges::<Point>(cx),
 5790            [
 5791                Point::new(0, 4)..Point::new(0, 4),
 5792                Point::new(1, 4)..Point::new(1, 4),
 5793                Point::new(2, 4)..Point::new(2, 4)
 5794            ]
 5795        );
 5796
 5797        editor.backspace(&Default::default(), cx);
 5798        editor.backspace(&Default::default(), cx);
 5799        assert_eq!(
 5800            editor.text(cx),
 5801            "
 5802                a{}
 5803                b{}
 5804                c{}
 5805            "
 5806            .unindent()
 5807        );
 5808        assert_eq!(
 5809            editor.selections.ranges::<Point>(cx),
 5810            [
 5811                Point::new(0, 2)..Point::new(0, 2),
 5812                Point::new(1, 2)..Point::new(1, 2),
 5813                Point::new(2, 2)..Point::new(2, 2)
 5814            ]
 5815        );
 5816
 5817        editor.delete_to_previous_word_start(&Default::default(), cx);
 5818        assert_eq!(
 5819            editor.text(cx),
 5820            "
 5821                a
 5822                b
 5823                c
 5824            "
 5825            .unindent()
 5826        );
 5827        assert_eq!(
 5828            editor.selections.ranges::<Point>(cx),
 5829            [
 5830                Point::new(0, 1)..Point::new(0, 1),
 5831                Point::new(1, 1)..Point::new(1, 1),
 5832                Point::new(2, 1)..Point::new(2, 1)
 5833            ]
 5834        );
 5835    });
 5836}
 5837
 5838#[gpui::test]
 5839async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 5840    init_test(cx, |settings| {
 5841        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5842    });
 5843
 5844    let mut cx = EditorTestContext::new(cx).await;
 5845
 5846    let language = Arc::new(Language::new(
 5847        LanguageConfig {
 5848            brackets: BracketPairConfig {
 5849                pairs: vec![
 5850                    BracketPair {
 5851                        start: "{".to_string(),
 5852                        end: "}".to_string(),
 5853                        close: true,
 5854                        surround: true,
 5855                        newline: true,
 5856                    },
 5857                    BracketPair {
 5858                        start: "(".to_string(),
 5859                        end: ")".to_string(),
 5860                        close: true,
 5861                        surround: true,
 5862                        newline: true,
 5863                    },
 5864                    BracketPair {
 5865                        start: "[".to_string(),
 5866                        end: "]".to_string(),
 5867                        close: false,
 5868                        surround: true,
 5869                        newline: true,
 5870                    },
 5871                ],
 5872                ..Default::default()
 5873            },
 5874            autoclose_before: "})]".to_string(),
 5875            ..Default::default()
 5876        },
 5877        Some(tree_sitter_rust::language()),
 5878    ));
 5879
 5880    cx.language_registry().add(language.clone());
 5881    cx.update_buffer(|buffer, cx| {
 5882        buffer.set_language(Some(language), cx);
 5883    });
 5884
 5885    cx.set_state(
 5886        &"
 5887            {(ˇ)}
 5888            [[ˇ]]
 5889            {(ˇ)}
 5890        "
 5891        .unindent(),
 5892    );
 5893
 5894    cx.update_editor(|view, cx| {
 5895        view.backspace(&Default::default(), cx);
 5896        view.backspace(&Default::default(), cx);
 5897    });
 5898
 5899    cx.assert_editor_state(
 5900        &"
 5901            ˇ
 5902            ˇ]]
 5903            ˇ
 5904        "
 5905        .unindent(),
 5906    );
 5907
 5908    cx.update_editor(|view, cx| {
 5909        view.handle_input("{", cx);
 5910        view.handle_input("{", cx);
 5911        view.move_right(&MoveRight, cx);
 5912        view.move_right(&MoveRight, cx);
 5913        view.move_left(&MoveLeft, cx);
 5914        view.move_left(&MoveLeft, cx);
 5915        view.backspace(&Default::default(), cx);
 5916    });
 5917
 5918    cx.assert_editor_state(
 5919        &"
 5920            {ˇ}
 5921            {ˇ}]]
 5922            {ˇ}
 5923        "
 5924        .unindent(),
 5925    );
 5926
 5927    cx.update_editor(|view, cx| {
 5928        view.backspace(&Default::default(), cx);
 5929    });
 5930
 5931    cx.assert_editor_state(
 5932        &"
 5933            ˇ
 5934            ˇ]]
 5935            ˇ
 5936        "
 5937        .unindent(),
 5938    );
 5939}
 5940
 5941#[gpui::test]
 5942async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 5943    init_test(cx, |_| {});
 5944
 5945    let language = Arc::new(Language::new(
 5946        LanguageConfig::default(),
 5947        Some(tree_sitter_rust::language()),
 5948    ));
 5949
 5950    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 5951    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5952    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5953    editor
 5954        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5955        .await;
 5956
 5957    _ = editor.update(cx, |editor, cx| {
 5958        editor.set_auto_replace_emoji_shortcode(true);
 5959
 5960        editor.handle_input("Hello ", cx);
 5961        editor.handle_input(":wave", cx);
 5962        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 5963
 5964        editor.handle_input(":", cx);
 5965        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 5966
 5967        editor.handle_input(" :smile", cx);
 5968        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 5969
 5970        editor.handle_input(":", cx);
 5971        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 5972
 5973        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 5974        editor.handle_input(":wave", cx);
 5975        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 5976
 5977        editor.handle_input(":", cx);
 5978        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 5979
 5980        editor.handle_input(":1", cx);
 5981        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 5982
 5983        editor.handle_input(":", cx);
 5984        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 5985
 5986        // Ensure shortcode does not get replaced when it is part of a word
 5987        editor.handle_input(" Test:wave", cx);
 5988        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 5989
 5990        editor.handle_input(":", cx);
 5991        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 5992
 5993        editor.set_auto_replace_emoji_shortcode(false);
 5994
 5995        // Ensure shortcode does not get replaced when auto replace is off
 5996        editor.handle_input(" :wave", cx);
 5997        assert_eq!(
 5998            editor.text(cx),
 5999            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6000        );
 6001
 6002        editor.handle_input(":", cx);
 6003        assert_eq!(
 6004            editor.text(cx),
 6005            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6006        );
 6007    });
 6008}
 6009
 6010#[gpui::test]
 6011async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6012    init_test(cx, |_| {});
 6013
 6014    let (text, insertion_ranges) = marked_text_ranges(
 6015        indoc! {"
 6016            a.ˇ b
 6017            a.ˇ b
 6018            a.ˇ b
 6019        "},
 6020        false,
 6021    );
 6022
 6023    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6024    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6025
 6026    _ = editor.update(cx, |editor, cx| {
 6027        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6028
 6029        editor
 6030            .insert_snippet(&insertion_ranges, snippet, cx)
 6031            .unwrap();
 6032
 6033        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6034            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6035            assert_eq!(editor.text(cx), expected_text);
 6036            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6037        }
 6038
 6039        assert(
 6040            editor,
 6041            cx,
 6042            indoc! {"
 6043                a.f(«one», two, «three») b
 6044                a.f(«one», two, «three») b
 6045                a.f(«one», two, «three») b
 6046            "},
 6047        );
 6048
 6049        // Can't move earlier than the first tab stop
 6050        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6051        assert(
 6052            editor,
 6053            cx,
 6054            indoc! {"
 6055                a.f(«one», two, «three») b
 6056                a.f(«one», two, «three») b
 6057                a.f(«one», two, «three») b
 6058            "},
 6059        );
 6060
 6061        assert!(editor.move_to_next_snippet_tabstop(cx));
 6062        assert(
 6063            editor,
 6064            cx,
 6065            indoc! {"
 6066                a.f(one, «two», three) b
 6067                a.f(one, «two», three) b
 6068                a.f(one, «two», three) b
 6069            "},
 6070        );
 6071
 6072        editor.move_to_prev_snippet_tabstop(cx);
 6073        assert(
 6074            editor,
 6075            cx,
 6076            indoc! {"
 6077                a.f(«one», two, «three») b
 6078                a.f(«one», two, «three») b
 6079                a.f(«one», two, «three») b
 6080            "},
 6081        );
 6082
 6083        assert!(editor.move_to_next_snippet_tabstop(cx));
 6084        assert(
 6085            editor,
 6086            cx,
 6087            indoc! {"
 6088                a.f(one, «two», three) b
 6089                a.f(one, «two», three) b
 6090                a.f(one, «two», three) b
 6091            "},
 6092        );
 6093        assert!(editor.move_to_next_snippet_tabstop(cx));
 6094        assert(
 6095            editor,
 6096            cx,
 6097            indoc! {"
 6098                a.f(one, two, three)ˇ b
 6099                a.f(one, two, three)ˇ b
 6100                a.f(one, two, three)ˇ b
 6101            "},
 6102        );
 6103
 6104        // As soon as the last tab stop is reached, snippet state is gone
 6105        editor.move_to_prev_snippet_tabstop(cx);
 6106        assert(
 6107            editor,
 6108            cx,
 6109            indoc! {"
 6110                a.f(one, two, three)ˇ b
 6111                a.f(one, two, three)ˇ b
 6112                a.f(one, two, three)ˇ b
 6113            "},
 6114        );
 6115    });
 6116}
 6117
 6118#[gpui::test]
 6119async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6120    init_test(cx, |_| {});
 6121
 6122    let fs = FakeFs::new(cx.executor());
 6123    fs.insert_file("/file.rs", Default::default()).await;
 6124
 6125    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6126
 6127    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6128    language_registry.add(rust_lang());
 6129    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6130        "Rust",
 6131        FakeLspAdapter {
 6132            capabilities: lsp::ServerCapabilities {
 6133                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6134                ..Default::default()
 6135            },
 6136            ..Default::default()
 6137        },
 6138    );
 6139
 6140    let buffer = project
 6141        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6142        .await
 6143        .unwrap();
 6144
 6145    cx.executor().start_waiting();
 6146    let fake_server = fake_servers.next().await.unwrap();
 6147
 6148    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6149    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6150    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6151    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6152
 6153    let save = editor
 6154        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6155        .unwrap();
 6156    fake_server
 6157        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6158            assert_eq!(
 6159                params.text_document.uri,
 6160                lsp::Url::from_file_path("/file.rs").unwrap()
 6161            );
 6162            assert_eq!(params.options.tab_size, 4);
 6163            Ok(Some(vec![lsp::TextEdit::new(
 6164                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6165                ", ".to_string(),
 6166            )]))
 6167        })
 6168        .next()
 6169        .await;
 6170    cx.executor().start_waiting();
 6171    save.await;
 6172
 6173    assert_eq!(
 6174        editor.update(cx, |editor, cx| editor.text(cx)),
 6175        "one, two\nthree\n"
 6176    );
 6177    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6178
 6179    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6180    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6181
 6182    // Ensure we can still save even if formatting hangs.
 6183    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6184        assert_eq!(
 6185            params.text_document.uri,
 6186            lsp::Url::from_file_path("/file.rs").unwrap()
 6187        );
 6188        futures::future::pending::<()>().await;
 6189        unreachable!()
 6190    });
 6191    let save = editor
 6192        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6193        .unwrap();
 6194    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6195    cx.executor().start_waiting();
 6196    save.await;
 6197    assert_eq!(
 6198        editor.update(cx, |editor, cx| editor.text(cx)),
 6199        "one\ntwo\nthree\n"
 6200    );
 6201    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6202
 6203    // For non-dirty buffer, no formatting request should be sent
 6204    let save = editor
 6205        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6206        .unwrap();
 6207    let _pending_format_request = fake_server
 6208        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6209            panic!("Should not be invoked on non-dirty buffer");
 6210        })
 6211        .next();
 6212    cx.executor().start_waiting();
 6213    save.await;
 6214
 6215    // Set rust language override and assert overridden tabsize is sent to language server
 6216    update_test_language_settings(cx, |settings| {
 6217        settings.languages.insert(
 6218            "Rust".into(),
 6219            LanguageSettingsContent {
 6220                tab_size: NonZeroU32::new(8),
 6221                ..Default::default()
 6222            },
 6223        );
 6224    });
 6225
 6226    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6227    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6228    let save = editor
 6229        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6230        .unwrap();
 6231    fake_server
 6232        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6233            assert_eq!(
 6234                params.text_document.uri,
 6235                lsp::Url::from_file_path("/file.rs").unwrap()
 6236            );
 6237            assert_eq!(params.options.tab_size, 8);
 6238            Ok(Some(vec![]))
 6239        })
 6240        .next()
 6241        .await;
 6242    cx.executor().start_waiting();
 6243    save.await;
 6244}
 6245
 6246#[gpui::test]
 6247async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6248    init_test(cx, |_| {});
 6249
 6250    let cols = 4;
 6251    let rows = 10;
 6252    let sample_text_1 = sample_text(rows, cols, 'a');
 6253    assert_eq!(
 6254        sample_text_1,
 6255        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6256    );
 6257    let sample_text_2 = sample_text(rows, cols, 'l');
 6258    assert_eq!(
 6259        sample_text_2,
 6260        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6261    );
 6262    let sample_text_3 = sample_text(rows, cols, 'v');
 6263    assert_eq!(
 6264        sample_text_3,
 6265        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6266    );
 6267
 6268    let fs = FakeFs::new(cx.executor());
 6269    fs.insert_tree(
 6270        "/a",
 6271        json!({
 6272            "main.rs": sample_text_1,
 6273            "other.rs": sample_text_2,
 6274            "lib.rs": sample_text_3,
 6275        }),
 6276    )
 6277    .await;
 6278
 6279    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6280    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6281    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6282
 6283    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6284    language_registry.add(rust_lang());
 6285    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6286        "Rust",
 6287        FakeLspAdapter {
 6288            capabilities: lsp::ServerCapabilities {
 6289                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6290                ..Default::default()
 6291            },
 6292            ..Default::default()
 6293        },
 6294    );
 6295
 6296    let worktree = project.update(cx, |project, cx| {
 6297        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6298        assert_eq!(worktrees.len(), 1);
 6299        worktrees.pop().unwrap()
 6300    });
 6301    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6302
 6303    let buffer_1 = project
 6304        .update(cx, |project, cx| {
 6305            project.open_buffer((worktree_id, "main.rs"), cx)
 6306        })
 6307        .await
 6308        .unwrap();
 6309    let buffer_2 = project
 6310        .update(cx, |project, cx| {
 6311            project.open_buffer((worktree_id, "other.rs"), cx)
 6312        })
 6313        .await
 6314        .unwrap();
 6315    let buffer_3 = project
 6316        .update(cx, |project, cx| {
 6317            project.open_buffer((worktree_id, "lib.rs"), cx)
 6318        })
 6319        .await
 6320        .unwrap();
 6321
 6322    let multi_buffer = cx.new_model(|cx| {
 6323        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 6324        multi_buffer.push_excerpts(
 6325            buffer_1.clone(),
 6326            [
 6327                ExcerptRange {
 6328                    context: Point::new(0, 0)..Point::new(3, 0),
 6329                    primary: None,
 6330                },
 6331                ExcerptRange {
 6332                    context: Point::new(5, 0)..Point::new(7, 0),
 6333                    primary: None,
 6334                },
 6335                ExcerptRange {
 6336                    context: Point::new(9, 0)..Point::new(10, 4),
 6337                    primary: None,
 6338                },
 6339            ],
 6340            cx,
 6341        );
 6342        multi_buffer.push_excerpts(
 6343            buffer_2.clone(),
 6344            [
 6345                ExcerptRange {
 6346                    context: Point::new(0, 0)..Point::new(3, 0),
 6347                    primary: None,
 6348                },
 6349                ExcerptRange {
 6350                    context: Point::new(5, 0)..Point::new(7, 0),
 6351                    primary: None,
 6352                },
 6353                ExcerptRange {
 6354                    context: Point::new(9, 0)..Point::new(10, 4),
 6355                    primary: None,
 6356                },
 6357            ],
 6358            cx,
 6359        );
 6360        multi_buffer.push_excerpts(
 6361            buffer_3.clone(),
 6362            [
 6363                ExcerptRange {
 6364                    context: Point::new(0, 0)..Point::new(3, 0),
 6365                    primary: None,
 6366                },
 6367                ExcerptRange {
 6368                    context: Point::new(5, 0)..Point::new(7, 0),
 6369                    primary: None,
 6370                },
 6371                ExcerptRange {
 6372                    context: Point::new(9, 0)..Point::new(10, 4),
 6373                    primary: None,
 6374                },
 6375            ],
 6376            cx,
 6377        );
 6378        multi_buffer
 6379    });
 6380    let multi_buffer_editor = cx.new_view(|cx| {
 6381        Editor::new(
 6382            EditorMode::Full,
 6383            multi_buffer,
 6384            Some(project.clone()),
 6385            true,
 6386            cx,
 6387        )
 6388    });
 6389
 6390    multi_buffer_editor.update(cx, |editor, cx| {
 6391        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6392        editor.insert("|one|two|three|", cx);
 6393    });
 6394    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6395    multi_buffer_editor.update(cx, |editor, cx| {
 6396        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6397            s.select_ranges(Some(60..70))
 6398        });
 6399        editor.insert("|four|five|six|", cx);
 6400    });
 6401    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6402
 6403    // First two buffers should be edited, but not the third one.
 6404    assert_eq!(
 6405        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6406        "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}",
 6407    );
 6408    buffer_1.update(cx, |buffer, _| {
 6409        assert!(buffer.is_dirty());
 6410        assert_eq!(
 6411            buffer.text(),
 6412            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6413        )
 6414    });
 6415    buffer_2.update(cx, |buffer, _| {
 6416        assert!(buffer.is_dirty());
 6417        assert_eq!(
 6418            buffer.text(),
 6419            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6420        )
 6421    });
 6422    buffer_3.update(cx, |buffer, _| {
 6423        assert!(!buffer.is_dirty());
 6424        assert_eq!(buffer.text(), sample_text_3,)
 6425    });
 6426
 6427    cx.executor().start_waiting();
 6428    let save = multi_buffer_editor
 6429        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6430        .unwrap();
 6431
 6432    let fake_server = fake_servers.next().await.unwrap();
 6433    fake_server
 6434        .server
 6435        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6436            Ok(Some(vec![lsp::TextEdit::new(
 6437                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6438                format!("[{} formatted]", params.text_document.uri),
 6439            )]))
 6440        })
 6441        .detach();
 6442    save.await;
 6443
 6444    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6445    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6446    assert_eq!(
 6447        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6448        "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}",
 6449    );
 6450    buffer_1.update(cx, |buffer, _| {
 6451        assert!(!buffer.is_dirty());
 6452        assert_eq!(
 6453            buffer.text(),
 6454            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6455        )
 6456    });
 6457    buffer_2.update(cx, |buffer, _| {
 6458        assert!(!buffer.is_dirty());
 6459        assert_eq!(
 6460            buffer.text(),
 6461            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6462        )
 6463    });
 6464    buffer_3.update(cx, |buffer, _| {
 6465        assert!(!buffer.is_dirty());
 6466        assert_eq!(buffer.text(), sample_text_3,)
 6467    });
 6468}
 6469
 6470#[gpui::test]
 6471async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6472    init_test(cx, |_| {});
 6473
 6474    let fs = FakeFs::new(cx.executor());
 6475    fs.insert_file("/file.rs", Default::default()).await;
 6476
 6477    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6478
 6479    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6480    language_registry.add(rust_lang());
 6481    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6482        "Rust",
 6483        FakeLspAdapter {
 6484            capabilities: lsp::ServerCapabilities {
 6485                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6486                ..Default::default()
 6487            },
 6488            ..Default::default()
 6489        },
 6490    );
 6491
 6492    let buffer = project
 6493        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6494        .await
 6495        .unwrap();
 6496
 6497    cx.executor().start_waiting();
 6498    let fake_server = fake_servers.next().await.unwrap();
 6499
 6500    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6501    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6502    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6503    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6504
 6505    let save = editor
 6506        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6507        .unwrap();
 6508    fake_server
 6509        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6510            assert_eq!(
 6511                params.text_document.uri,
 6512                lsp::Url::from_file_path("/file.rs").unwrap()
 6513            );
 6514            assert_eq!(params.options.tab_size, 4);
 6515            Ok(Some(vec![lsp::TextEdit::new(
 6516                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6517                ", ".to_string(),
 6518            )]))
 6519        })
 6520        .next()
 6521        .await;
 6522    cx.executor().start_waiting();
 6523    save.await;
 6524    assert_eq!(
 6525        editor.update(cx, |editor, cx| editor.text(cx)),
 6526        "one, two\nthree\n"
 6527    );
 6528    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6529
 6530    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6531    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6532
 6533    // Ensure we can still save even if formatting hangs.
 6534    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6535        move |params, _| async move {
 6536            assert_eq!(
 6537                params.text_document.uri,
 6538                lsp::Url::from_file_path("/file.rs").unwrap()
 6539            );
 6540            futures::future::pending::<()>().await;
 6541            unreachable!()
 6542        },
 6543    );
 6544    let save = editor
 6545        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6546        .unwrap();
 6547    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6548    cx.executor().start_waiting();
 6549    save.await;
 6550    assert_eq!(
 6551        editor.update(cx, |editor, cx| editor.text(cx)),
 6552        "one\ntwo\nthree\n"
 6553    );
 6554    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6555
 6556    // For non-dirty buffer, no formatting request should be sent
 6557    let save = editor
 6558        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6559        .unwrap();
 6560    let _pending_format_request = fake_server
 6561        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6562            panic!("Should not be invoked on non-dirty buffer");
 6563        })
 6564        .next();
 6565    cx.executor().start_waiting();
 6566    save.await;
 6567
 6568    // Set Rust language override and assert overridden tabsize is sent to language server
 6569    update_test_language_settings(cx, |settings| {
 6570        settings.languages.insert(
 6571            "Rust".into(),
 6572            LanguageSettingsContent {
 6573                tab_size: NonZeroU32::new(8),
 6574                ..Default::default()
 6575            },
 6576        );
 6577    });
 6578
 6579    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6580    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6581    let save = editor
 6582        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6583        .unwrap();
 6584    fake_server
 6585        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6586            assert_eq!(
 6587                params.text_document.uri,
 6588                lsp::Url::from_file_path("/file.rs").unwrap()
 6589            );
 6590            assert_eq!(params.options.tab_size, 8);
 6591            Ok(Some(vec![]))
 6592        })
 6593        .next()
 6594        .await;
 6595    cx.executor().start_waiting();
 6596    save.await;
 6597}
 6598
 6599#[gpui::test]
 6600async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6601    init_test(cx, |settings| {
 6602        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 6603            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 6604        ))
 6605    });
 6606
 6607    let fs = FakeFs::new(cx.executor());
 6608    fs.insert_file("/file.rs", Default::default()).await;
 6609
 6610    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6611
 6612    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6613    language_registry.add(Arc::new(Language::new(
 6614        LanguageConfig {
 6615            name: "Rust".into(),
 6616            matcher: LanguageMatcher {
 6617                path_suffixes: vec!["rs".to_string()],
 6618                ..Default::default()
 6619            },
 6620            ..LanguageConfig::default()
 6621        },
 6622        Some(tree_sitter_rust::language()),
 6623    )));
 6624    update_test_language_settings(cx, |settings| {
 6625        // Enable Prettier formatting for the same buffer, and ensure
 6626        // LSP is called instead of Prettier.
 6627        settings.defaults.prettier = Some(PrettierSettings {
 6628            allowed: true,
 6629            ..PrettierSettings::default()
 6630        });
 6631    });
 6632    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6633        "Rust",
 6634        FakeLspAdapter {
 6635            capabilities: lsp::ServerCapabilities {
 6636                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6637                ..Default::default()
 6638            },
 6639            ..Default::default()
 6640        },
 6641    );
 6642
 6643    let buffer = project
 6644        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6645        .await
 6646        .unwrap();
 6647
 6648    cx.executor().start_waiting();
 6649    let fake_server = fake_servers.next().await.unwrap();
 6650
 6651    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6652    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6653    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6654
 6655    let format = editor
 6656        .update(cx, |editor, cx| {
 6657            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6658        })
 6659        .unwrap();
 6660    fake_server
 6661        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6662            assert_eq!(
 6663                params.text_document.uri,
 6664                lsp::Url::from_file_path("/file.rs").unwrap()
 6665            );
 6666            assert_eq!(params.options.tab_size, 4);
 6667            Ok(Some(vec![lsp::TextEdit::new(
 6668                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6669                ", ".to_string(),
 6670            )]))
 6671        })
 6672        .next()
 6673        .await;
 6674    cx.executor().start_waiting();
 6675    format.await;
 6676    assert_eq!(
 6677        editor.update(cx, |editor, cx| editor.text(cx)),
 6678        "one, two\nthree\n"
 6679    );
 6680
 6681    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6682    // Ensure we don't lock if formatting hangs.
 6683    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6684        assert_eq!(
 6685            params.text_document.uri,
 6686            lsp::Url::from_file_path("/file.rs").unwrap()
 6687        );
 6688        futures::future::pending::<()>().await;
 6689        unreachable!()
 6690    });
 6691    let format = editor
 6692        .update(cx, |editor, cx| {
 6693            editor.perform_format(project, FormatTrigger::Manual, cx)
 6694        })
 6695        .unwrap();
 6696    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6697    cx.executor().start_waiting();
 6698    format.await;
 6699    assert_eq!(
 6700        editor.update(cx, |editor, cx| editor.text(cx)),
 6701        "one\ntwo\nthree\n"
 6702    );
 6703}
 6704
 6705#[gpui::test]
 6706async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6707    init_test(cx, |_| {});
 6708
 6709    let mut cx = EditorLspTestContext::new_rust(
 6710        lsp::ServerCapabilities {
 6711            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6712            ..Default::default()
 6713        },
 6714        cx,
 6715    )
 6716    .await;
 6717
 6718    cx.set_state(indoc! {"
 6719        one.twoˇ
 6720    "});
 6721
 6722    // The format request takes a long time. When it completes, it inserts
 6723    // a newline and an indent before the `.`
 6724    cx.lsp
 6725        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6726            let executor = cx.background_executor().clone();
 6727            async move {
 6728                executor.timer(Duration::from_millis(100)).await;
 6729                Ok(Some(vec![lsp::TextEdit {
 6730                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6731                    new_text: "\n    ".into(),
 6732                }]))
 6733            }
 6734        });
 6735
 6736    // Submit a format request.
 6737    let format_1 = cx
 6738        .update_editor(|editor, cx| editor.format(&Format, cx))
 6739        .unwrap();
 6740    cx.executor().run_until_parked();
 6741
 6742    // Submit a second format request.
 6743    let format_2 = cx
 6744        .update_editor(|editor, cx| editor.format(&Format, cx))
 6745        .unwrap();
 6746    cx.executor().run_until_parked();
 6747
 6748    // Wait for both format requests to complete
 6749    cx.executor().advance_clock(Duration::from_millis(200));
 6750    cx.executor().start_waiting();
 6751    format_1.await.unwrap();
 6752    cx.executor().start_waiting();
 6753    format_2.await.unwrap();
 6754
 6755    // The formatting edits only happens once.
 6756    cx.assert_editor_state(indoc! {"
 6757        one
 6758            .twoˇ
 6759    "});
 6760}
 6761
 6762#[gpui::test]
 6763async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6764    init_test(cx, |settings| {
 6765        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 6766    });
 6767
 6768    let mut cx = EditorLspTestContext::new_rust(
 6769        lsp::ServerCapabilities {
 6770            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6771            ..Default::default()
 6772        },
 6773        cx,
 6774    )
 6775    .await;
 6776
 6777    // Set up a buffer white some trailing whitespace and no trailing newline.
 6778    cx.set_state(
 6779        &[
 6780            "one ",   //
 6781            "twoˇ",   //
 6782            "three ", //
 6783            "four",   //
 6784        ]
 6785        .join("\n"),
 6786    );
 6787
 6788    // Submit a format request.
 6789    let format = cx
 6790        .update_editor(|editor, cx| editor.format(&Format, cx))
 6791        .unwrap();
 6792
 6793    // Record which buffer changes have been sent to the language server
 6794    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6795    cx.lsp
 6796        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6797            let buffer_changes = buffer_changes.clone();
 6798            move |params, _| {
 6799                buffer_changes.lock().extend(
 6800                    params
 6801                        .content_changes
 6802                        .into_iter()
 6803                        .map(|e| (e.range.unwrap(), e.text)),
 6804                );
 6805            }
 6806        });
 6807
 6808    // Handle formatting requests to the language server.
 6809    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6810        let buffer_changes = buffer_changes.clone();
 6811        move |_, _| {
 6812            // When formatting is requested, trailing whitespace has already been stripped,
 6813            // and the trailing newline has already been added.
 6814            assert_eq!(
 6815                &buffer_changes.lock()[1..],
 6816                &[
 6817                    (
 6818                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6819                        "".into()
 6820                    ),
 6821                    (
 6822                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6823                        "".into()
 6824                    ),
 6825                    (
 6826                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6827                        "\n".into()
 6828                    ),
 6829                ]
 6830            );
 6831
 6832            // Insert blank lines between each line of the buffer.
 6833            async move {
 6834                Ok(Some(vec![
 6835                    lsp::TextEdit {
 6836                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6837                        new_text: "\n".into(),
 6838                    },
 6839                    lsp::TextEdit {
 6840                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6841                        new_text: "\n".into(),
 6842                    },
 6843                ]))
 6844            }
 6845        }
 6846    });
 6847
 6848    // After formatting the buffer, the trailing whitespace is stripped,
 6849    // a newline is appended, and the edits provided by the language server
 6850    // have been applied.
 6851    format.await.unwrap();
 6852    cx.assert_editor_state(
 6853        &[
 6854            "one",   //
 6855            "",      //
 6856            "twoˇ",  //
 6857            "",      //
 6858            "three", //
 6859            "four",  //
 6860            "",      //
 6861        ]
 6862        .join("\n"),
 6863    );
 6864
 6865    // Undoing the formatting undoes the trailing whitespace removal, the
 6866    // trailing newline, and the LSP edits.
 6867    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6868    cx.assert_editor_state(
 6869        &[
 6870            "one ",   //
 6871            "twoˇ",   //
 6872            "three ", //
 6873            "four",   //
 6874        ]
 6875        .join("\n"),
 6876    );
 6877}
 6878
 6879#[gpui::test]
 6880async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 6881    cx: &mut gpui::TestAppContext,
 6882) {
 6883    init_test(cx, |_| {});
 6884
 6885    cx.update(|cx| {
 6886        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6887            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6888                settings.auto_signature_help = Some(true);
 6889            });
 6890        });
 6891    });
 6892
 6893    let mut cx = EditorLspTestContext::new_rust(
 6894        lsp::ServerCapabilities {
 6895            signature_help_provider: Some(lsp::SignatureHelpOptions {
 6896                ..Default::default()
 6897            }),
 6898            ..Default::default()
 6899        },
 6900        cx,
 6901    )
 6902    .await;
 6903
 6904    let language = Language::new(
 6905        LanguageConfig {
 6906            name: "Rust".into(),
 6907            brackets: BracketPairConfig {
 6908                pairs: vec![
 6909                    BracketPair {
 6910                        start: "{".to_string(),
 6911                        end: "}".to_string(),
 6912                        close: true,
 6913                        surround: true,
 6914                        newline: true,
 6915                    },
 6916                    BracketPair {
 6917                        start: "(".to_string(),
 6918                        end: ")".to_string(),
 6919                        close: true,
 6920                        surround: true,
 6921                        newline: true,
 6922                    },
 6923                    BracketPair {
 6924                        start: "/*".to_string(),
 6925                        end: " */".to_string(),
 6926                        close: true,
 6927                        surround: true,
 6928                        newline: true,
 6929                    },
 6930                    BracketPair {
 6931                        start: "[".to_string(),
 6932                        end: "]".to_string(),
 6933                        close: false,
 6934                        surround: false,
 6935                        newline: true,
 6936                    },
 6937                    BracketPair {
 6938                        start: "\"".to_string(),
 6939                        end: "\"".to_string(),
 6940                        close: true,
 6941                        surround: true,
 6942                        newline: false,
 6943                    },
 6944                    BracketPair {
 6945                        start: "<".to_string(),
 6946                        end: ">".to_string(),
 6947                        close: false,
 6948                        surround: true,
 6949                        newline: true,
 6950                    },
 6951                ],
 6952                ..Default::default()
 6953            },
 6954            autoclose_before: "})]".to_string(),
 6955            ..Default::default()
 6956        },
 6957        Some(tree_sitter_rust::language()),
 6958    );
 6959    let language = Arc::new(language);
 6960
 6961    cx.language_registry().add(language.clone());
 6962    cx.update_buffer(|buffer, cx| {
 6963        buffer.set_language(Some(language), cx);
 6964    });
 6965
 6966    cx.set_state(
 6967        &r#"
 6968            fn main() {
 6969                sampleˇ
 6970            }
 6971        "#
 6972        .unindent(),
 6973    );
 6974
 6975    cx.update_editor(|view, cx| {
 6976        view.handle_input("(", cx);
 6977    });
 6978    cx.assert_editor_state(
 6979        &"
 6980            fn main() {
 6981                sample(ˇ)
 6982            }
 6983        "
 6984        .unindent(),
 6985    );
 6986
 6987    let mocked_response = lsp::SignatureHelp {
 6988        signatures: vec![lsp::SignatureInformation {
 6989            label: "fn sample(param1: u8, param2: u8)".to_string(),
 6990            documentation: None,
 6991            parameters: Some(vec![
 6992                lsp::ParameterInformation {
 6993                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 6994                    documentation: None,
 6995                },
 6996                lsp::ParameterInformation {
 6997                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 6998                    documentation: None,
 6999                },
 7000            ]),
 7001            active_parameter: None,
 7002        }],
 7003        active_signature: Some(0),
 7004        active_parameter: Some(0),
 7005    };
 7006    handle_signature_help_request(&mut cx, mocked_response).await;
 7007
 7008    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7009        .await;
 7010
 7011    cx.editor(|editor, _| {
 7012        let signature_help_state = editor.signature_help_state.popover().cloned();
 7013        assert!(signature_help_state.is_some());
 7014        let ParsedMarkdown {
 7015            text, highlights, ..
 7016        } = signature_help_state.unwrap().parsed_content;
 7017        assert_eq!(text, "param1: u8, param2: u8");
 7018        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7019    });
 7020}
 7021
 7022#[gpui::test]
 7023async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7024    init_test(cx, |_| {});
 7025
 7026    cx.update(|cx| {
 7027        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7028            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7029                settings.auto_signature_help = Some(false);
 7030                settings.show_signature_help_after_edits = Some(false);
 7031            });
 7032        });
 7033    });
 7034
 7035    let mut cx = EditorLspTestContext::new_rust(
 7036        lsp::ServerCapabilities {
 7037            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7038                ..Default::default()
 7039            }),
 7040            ..Default::default()
 7041        },
 7042        cx,
 7043    )
 7044    .await;
 7045
 7046    let language = Language::new(
 7047        LanguageConfig {
 7048            name: "Rust".into(),
 7049            brackets: BracketPairConfig {
 7050                pairs: vec![
 7051                    BracketPair {
 7052                        start: "{".to_string(),
 7053                        end: "}".to_string(),
 7054                        close: true,
 7055                        surround: true,
 7056                        newline: true,
 7057                    },
 7058                    BracketPair {
 7059                        start: "(".to_string(),
 7060                        end: ")".to_string(),
 7061                        close: true,
 7062                        surround: true,
 7063                        newline: true,
 7064                    },
 7065                    BracketPair {
 7066                        start: "/*".to_string(),
 7067                        end: " */".to_string(),
 7068                        close: true,
 7069                        surround: true,
 7070                        newline: true,
 7071                    },
 7072                    BracketPair {
 7073                        start: "[".to_string(),
 7074                        end: "]".to_string(),
 7075                        close: false,
 7076                        surround: false,
 7077                        newline: true,
 7078                    },
 7079                    BracketPair {
 7080                        start: "\"".to_string(),
 7081                        end: "\"".to_string(),
 7082                        close: true,
 7083                        surround: true,
 7084                        newline: false,
 7085                    },
 7086                    BracketPair {
 7087                        start: "<".to_string(),
 7088                        end: ">".to_string(),
 7089                        close: false,
 7090                        surround: true,
 7091                        newline: true,
 7092                    },
 7093                ],
 7094                ..Default::default()
 7095            },
 7096            autoclose_before: "})]".to_string(),
 7097            ..Default::default()
 7098        },
 7099        Some(tree_sitter_rust::language()),
 7100    );
 7101    let language = Arc::new(language);
 7102
 7103    cx.language_registry().add(language.clone());
 7104    cx.update_buffer(|buffer, cx| {
 7105        buffer.set_language(Some(language), cx);
 7106    });
 7107
 7108    // Ensure that signature_help is not called when no signature help is enabled.
 7109    cx.set_state(
 7110        &r#"
 7111            fn main() {
 7112                sampleˇ
 7113            }
 7114        "#
 7115        .unindent(),
 7116    );
 7117    cx.update_editor(|view, cx| {
 7118        view.handle_input("(", cx);
 7119    });
 7120    cx.assert_editor_state(
 7121        &"
 7122            fn main() {
 7123                sample(ˇ)
 7124            }
 7125        "
 7126        .unindent(),
 7127    );
 7128    cx.editor(|editor, _| {
 7129        assert!(editor.signature_help_state.task().is_none());
 7130    });
 7131
 7132    let mocked_response = lsp::SignatureHelp {
 7133        signatures: vec![lsp::SignatureInformation {
 7134            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7135            documentation: None,
 7136            parameters: Some(vec![
 7137                lsp::ParameterInformation {
 7138                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7139                    documentation: None,
 7140                },
 7141                lsp::ParameterInformation {
 7142                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7143                    documentation: None,
 7144                },
 7145            ]),
 7146            active_parameter: None,
 7147        }],
 7148        active_signature: Some(0),
 7149        active_parameter: Some(0),
 7150    };
 7151
 7152    // Ensure that signature_help is called when enabled afte edits
 7153    cx.update(|cx| {
 7154        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7155            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7156                settings.auto_signature_help = Some(false);
 7157                settings.show_signature_help_after_edits = Some(true);
 7158            });
 7159        });
 7160    });
 7161    cx.set_state(
 7162        &r#"
 7163            fn main() {
 7164                sampleˇ
 7165            }
 7166        "#
 7167        .unindent(),
 7168    );
 7169    cx.update_editor(|view, cx| {
 7170        view.handle_input("(", cx);
 7171    });
 7172    cx.assert_editor_state(
 7173        &"
 7174            fn main() {
 7175                sample(ˇ)
 7176            }
 7177        "
 7178        .unindent(),
 7179    );
 7180    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7181    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7182        .await;
 7183    cx.update_editor(|editor, _| {
 7184        let signature_help_state = editor.signature_help_state.popover().cloned();
 7185        assert!(signature_help_state.is_some());
 7186        let ParsedMarkdown {
 7187            text, highlights, ..
 7188        } = signature_help_state.unwrap().parsed_content;
 7189        assert_eq!(text, "param1: u8, param2: u8");
 7190        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7191        editor.signature_help_state = SignatureHelpState::default();
 7192    });
 7193
 7194    // Ensure that signature_help is called when auto signature help override is enabled
 7195    cx.update(|cx| {
 7196        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7197            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7198                settings.auto_signature_help = Some(true);
 7199                settings.show_signature_help_after_edits = Some(false);
 7200            });
 7201        });
 7202    });
 7203    cx.set_state(
 7204        &r#"
 7205            fn main() {
 7206                sampleˇ
 7207            }
 7208        "#
 7209        .unindent(),
 7210    );
 7211    cx.update_editor(|view, cx| {
 7212        view.handle_input("(", cx);
 7213    });
 7214    cx.assert_editor_state(
 7215        &"
 7216            fn main() {
 7217                sample(ˇ)
 7218            }
 7219        "
 7220        .unindent(),
 7221    );
 7222    handle_signature_help_request(&mut cx, mocked_response).await;
 7223    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7224        .await;
 7225    cx.editor(|editor, _| {
 7226        let signature_help_state = editor.signature_help_state.popover().cloned();
 7227        assert!(signature_help_state.is_some());
 7228        let ParsedMarkdown {
 7229            text, highlights, ..
 7230        } = signature_help_state.unwrap().parsed_content;
 7231        assert_eq!(text, "param1: u8, param2: u8");
 7232        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7233    });
 7234}
 7235
 7236#[gpui::test]
 7237async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7238    init_test(cx, |_| {});
 7239    cx.update(|cx| {
 7240        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7241            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7242                settings.auto_signature_help = Some(true);
 7243            });
 7244        });
 7245    });
 7246
 7247    let mut cx = EditorLspTestContext::new_rust(
 7248        lsp::ServerCapabilities {
 7249            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7250                ..Default::default()
 7251            }),
 7252            ..Default::default()
 7253        },
 7254        cx,
 7255    )
 7256    .await;
 7257
 7258    // A test that directly calls `show_signature_help`
 7259    cx.update_editor(|editor, cx| {
 7260        editor.show_signature_help(&ShowSignatureHelp, cx);
 7261    });
 7262
 7263    let mocked_response = lsp::SignatureHelp {
 7264        signatures: vec![lsp::SignatureInformation {
 7265            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7266            documentation: None,
 7267            parameters: Some(vec![
 7268                lsp::ParameterInformation {
 7269                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7270                    documentation: None,
 7271                },
 7272                lsp::ParameterInformation {
 7273                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7274                    documentation: None,
 7275                },
 7276            ]),
 7277            active_parameter: None,
 7278        }],
 7279        active_signature: Some(0),
 7280        active_parameter: Some(0),
 7281    };
 7282    handle_signature_help_request(&mut cx, mocked_response).await;
 7283
 7284    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7285        .await;
 7286
 7287    cx.editor(|editor, _| {
 7288        let signature_help_state = editor.signature_help_state.popover().cloned();
 7289        assert!(signature_help_state.is_some());
 7290        let ParsedMarkdown {
 7291            text, highlights, ..
 7292        } = signature_help_state.unwrap().parsed_content;
 7293        assert_eq!(text, "param1: u8, param2: u8");
 7294        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7295    });
 7296
 7297    // When exiting outside from inside the brackets, `signature_help` is closed.
 7298    cx.set_state(indoc! {"
 7299        fn main() {
 7300            sample(ˇ);
 7301        }
 7302
 7303        fn sample(param1: u8, param2: u8) {}
 7304    "});
 7305
 7306    cx.update_editor(|editor, cx| {
 7307        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7308    });
 7309
 7310    let mocked_response = lsp::SignatureHelp {
 7311        signatures: Vec::new(),
 7312        active_signature: None,
 7313        active_parameter: None,
 7314    };
 7315    handle_signature_help_request(&mut cx, mocked_response).await;
 7316
 7317    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7318        .await;
 7319
 7320    cx.editor(|editor, _| {
 7321        assert!(!editor.signature_help_state.is_shown());
 7322    });
 7323
 7324    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7325    cx.set_state(indoc! {"
 7326        fn main() {
 7327            sample(ˇ);
 7328        }
 7329
 7330        fn sample(param1: u8, param2: u8) {}
 7331    "});
 7332
 7333    let mocked_response = lsp::SignatureHelp {
 7334        signatures: vec![lsp::SignatureInformation {
 7335            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7336            documentation: None,
 7337            parameters: Some(vec![
 7338                lsp::ParameterInformation {
 7339                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7340                    documentation: None,
 7341                },
 7342                lsp::ParameterInformation {
 7343                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7344                    documentation: None,
 7345                },
 7346            ]),
 7347            active_parameter: None,
 7348        }],
 7349        active_signature: Some(0),
 7350        active_parameter: Some(0),
 7351    };
 7352    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7353    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7354        .await;
 7355    cx.editor(|editor, _| {
 7356        assert!(editor.signature_help_state.is_shown());
 7357    });
 7358
 7359    // Restore the popover with more parameter input
 7360    cx.set_state(indoc! {"
 7361        fn main() {
 7362            sample(param1, param2ˇ);
 7363        }
 7364
 7365        fn sample(param1: u8, param2: u8) {}
 7366    "});
 7367
 7368    let mocked_response = lsp::SignatureHelp {
 7369        signatures: vec![lsp::SignatureInformation {
 7370            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7371            documentation: None,
 7372            parameters: Some(vec![
 7373                lsp::ParameterInformation {
 7374                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7375                    documentation: None,
 7376                },
 7377                lsp::ParameterInformation {
 7378                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7379                    documentation: None,
 7380                },
 7381            ]),
 7382            active_parameter: None,
 7383        }],
 7384        active_signature: Some(0),
 7385        active_parameter: Some(1),
 7386    };
 7387    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7388    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7389        .await;
 7390
 7391    // When selecting a range, the popover is gone.
 7392    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7393    cx.update_editor(|editor, cx| {
 7394        editor.change_selections(None, cx, |s| {
 7395            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7396        })
 7397    });
 7398    cx.assert_editor_state(indoc! {"
 7399        fn main() {
 7400            sample(param1, «ˇparam2»);
 7401        }
 7402
 7403        fn sample(param1: u8, param2: u8) {}
 7404    "});
 7405    cx.editor(|editor, _| {
 7406        assert!(!editor.signature_help_state.is_shown());
 7407    });
 7408
 7409    // When unselecting again, the popover is back if within the brackets.
 7410    cx.update_editor(|editor, cx| {
 7411        editor.change_selections(None, cx, |s| {
 7412            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7413        })
 7414    });
 7415    cx.assert_editor_state(indoc! {"
 7416        fn main() {
 7417            sample(param1, ˇparam2);
 7418        }
 7419
 7420        fn sample(param1: u8, param2: u8) {}
 7421    "});
 7422    handle_signature_help_request(&mut cx, mocked_response).await;
 7423    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7424        .await;
 7425    cx.editor(|editor, _| {
 7426        assert!(editor.signature_help_state.is_shown());
 7427    });
 7428
 7429    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7430    cx.update_editor(|editor, cx| {
 7431        editor.change_selections(None, cx, |s| {
 7432            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7433            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7434        })
 7435    });
 7436    cx.assert_editor_state(indoc! {"
 7437        fn main() {
 7438            sample(param1, ˇparam2);
 7439        }
 7440
 7441        fn sample(param1: u8, param2: u8) {}
 7442    "});
 7443
 7444    let mocked_response = lsp::SignatureHelp {
 7445        signatures: vec![lsp::SignatureInformation {
 7446            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7447            documentation: None,
 7448            parameters: Some(vec![
 7449                lsp::ParameterInformation {
 7450                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7451                    documentation: None,
 7452                },
 7453                lsp::ParameterInformation {
 7454                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7455                    documentation: None,
 7456                },
 7457            ]),
 7458            active_parameter: None,
 7459        }],
 7460        active_signature: Some(0),
 7461        active_parameter: Some(1),
 7462    };
 7463    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7464    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7465        .await;
 7466    cx.update_editor(|editor, cx| {
 7467        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7468    });
 7469    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7470        .await;
 7471    cx.update_editor(|editor, cx| {
 7472        editor.change_selections(None, cx, |s| {
 7473            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7474        })
 7475    });
 7476    cx.assert_editor_state(indoc! {"
 7477        fn main() {
 7478            sample(param1, «ˇparam2»);
 7479        }
 7480
 7481        fn sample(param1: u8, param2: u8) {}
 7482    "});
 7483    cx.update_editor(|editor, cx| {
 7484        editor.change_selections(None, cx, |s| {
 7485            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7486        })
 7487    });
 7488    cx.assert_editor_state(indoc! {"
 7489        fn main() {
 7490            sample(param1, ˇparam2);
 7491        }
 7492
 7493        fn sample(param1: u8, param2: u8) {}
 7494    "});
 7495    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7496        .await;
 7497}
 7498
 7499#[gpui::test]
 7500async fn test_completion(cx: &mut gpui::TestAppContext) {
 7501    init_test(cx, |_| {});
 7502
 7503    let mut cx = EditorLspTestContext::new_rust(
 7504        lsp::ServerCapabilities {
 7505            completion_provider: Some(lsp::CompletionOptions {
 7506                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7507                resolve_provider: Some(true),
 7508                ..Default::default()
 7509            }),
 7510            ..Default::default()
 7511        },
 7512        cx,
 7513    )
 7514    .await;
 7515    let counter = Arc::new(AtomicUsize::new(0));
 7516
 7517    cx.set_state(indoc! {"
 7518        oneˇ
 7519        two
 7520        three
 7521    "});
 7522    cx.simulate_keystroke(".");
 7523    handle_completion_request(
 7524        &mut cx,
 7525        indoc! {"
 7526            one.|<>
 7527            two
 7528            three
 7529        "},
 7530        vec!["first_completion", "second_completion"],
 7531        counter.clone(),
 7532    )
 7533    .await;
 7534    cx.condition(|editor, _| editor.context_menu_visible())
 7535        .await;
 7536    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7537
 7538    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7539        editor.context_menu_next(&Default::default(), cx);
 7540        editor
 7541            .confirm_completion(&ConfirmCompletion::default(), cx)
 7542            .unwrap()
 7543    });
 7544    cx.assert_editor_state(indoc! {"
 7545        one.second_completionˇ
 7546        two
 7547        three
 7548    "});
 7549
 7550    handle_resolve_completion_request(
 7551        &mut cx,
 7552        Some(vec![
 7553            (
 7554                //This overlaps with the primary completion edit which is
 7555                //misbehavior from the LSP spec, test that we filter it out
 7556                indoc! {"
 7557                    one.second_ˇcompletion
 7558                    two
 7559                    threeˇ
 7560                "},
 7561                "overlapping additional edit",
 7562            ),
 7563            (
 7564                indoc! {"
 7565                    one.second_completion
 7566                    two
 7567                    threeˇ
 7568                "},
 7569                "\nadditional edit",
 7570            ),
 7571        ]),
 7572    )
 7573    .await;
 7574    apply_additional_edits.await.unwrap();
 7575    cx.assert_editor_state(indoc! {"
 7576        one.second_completionˇ
 7577        two
 7578        three
 7579        additional edit
 7580    "});
 7581
 7582    cx.set_state(indoc! {"
 7583        one.second_completion
 7584        twoˇ
 7585        threeˇ
 7586        additional edit
 7587    "});
 7588    cx.simulate_keystroke(" ");
 7589    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7590    cx.simulate_keystroke("s");
 7591    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7592
 7593    cx.assert_editor_state(indoc! {"
 7594        one.second_completion
 7595        two sˇ
 7596        three sˇ
 7597        additional edit
 7598    "});
 7599    handle_completion_request(
 7600        &mut cx,
 7601        indoc! {"
 7602            one.second_completion
 7603            two s
 7604            three <s|>
 7605            additional edit
 7606        "},
 7607        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7608        counter.clone(),
 7609    )
 7610    .await;
 7611    cx.condition(|editor, _| editor.context_menu_visible())
 7612        .await;
 7613    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 7614
 7615    cx.simulate_keystroke("i");
 7616
 7617    handle_completion_request(
 7618        &mut cx,
 7619        indoc! {"
 7620            one.second_completion
 7621            two si
 7622            three <si|>
 7623            additional edit
 7624        "},
 7625        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7626        counter.clone(),
 7627    )
 7628    .await;
 7629    cx.condition(|editor, _| editor.context_menu_visible())
 7630        .await;
 7631    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 7632
 7633    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7634        editor
 7635            .confirm_completion(&ConfirmCompletion::default(), cx)
 7636            .unwrap()
 7637    });
 7638    cx.assert_editor_state(indoc! {"
 7639        one.second_completion
 7640        two sixth_completionˇ
 7641        three sixth_completionˇ
 7642        additional edit
 7643    "});
 7644
 7645    handle_resolve_completion_request(&mut cx, None).await;
 7646    apply_additional_edits.await.unwrap();
 7647
 7648    _ = cx.update(|cx| {
 7649        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7650            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7651                settings.show_completions_on_input = Some(false);
 7652            });
 7653        })
 7654    });
 7655    cx.set_state("editorˇ");
 7656    cx.simulate_keystroke(".");
 7657    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7658    cx.simulate_keystroke("c");
 7659    cx.simulate_keystroke("l");
 7660    cx.simulate_keystroke("o");
 7661    cx.assert_editor_state("editor.cloˇ");
 7662    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7663    cx.update_editor(|editor, cx| {
 7664        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 7665    });
 7666    handle_completion_request(
 7667        &mut cx,
 7668        "editor.<clo|>",
 7669        vec!["close", "clobber"],
 7670        counter.clone(),
 7671    )
 7672    .await;
 7673    cx.condition(|editor, _| editor.context_menu_visible())
 7674        .await;
 7675    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 7676
 7677    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7678        editor
 7679            .confirm_completion(&ConfirmCompletion::default(), cx)
 7680            .unwrap()
 7681    });
 7682    cx.assert_editor_state("editor.closeˇ");
 7683    handle_resolve_completion_request(&mut cx, None).await;
 7684    apply_additional_edits.await.unwrap();
 7685}
 7686
 7687#[gpui::test]
 7688async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 7689    init_test(cx, |_| {});
 7690    let mut cx = EditorLspTestContext::new_rust(
 7691        lsp::ServerCapabilities {
 7692            completion_provider: Some(lsp::CompletionOptions {
 7693                trigger_characters: Some(vec![".".to_string()]),
 7694                ..Default::default()
 7695            }),
 7696            ..Default::default()
 7697        },
 7698        cx,
 7699    )
 7700    .await;
 7701    cx.lsp
 7702        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 7703            Ok(Some(lsp::CompletionResponse::Array(vec![
 7704                lsp::CompletionItem {
 7705                    label: "first".into(),
 7706                    ..Default::default()
 7707                },
 7708                lsp::CompletionItem {
 7709                    label: "last".into(),
 7710                    ..Default::default()
 7711                },
 7712            ])))
 7713        });
 7714    cx.set_state("variableˇ");
 7715    cx.simulate_keystroke(".");
 7716    cx.executor().run_until_parked();
 7717
 7718    cx.update_editor(|editor, _| {
 7719        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7720            assert_eq!(
 7721                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 7722                &["first", "last"]
 7723            );
 7724        } else {
 7725            panic!("expected completion menu to be open");
 7726        }
 7727    });
 7728
 7729    cx.update_editor(|editor, cx| {
 7730        editor.move_page_down(&MovePageDown::default(), cx);
 7731        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7732            assert!(
 7733                menu.selected_item == 1,
 7734                "expected PageDown to select the last item from the context menu"
 7735            );
 7736        } else {
 7737            panic!("expected completion menu to stay open after PageDown");
 7738        }
 7739    });
 7740
 7741    cx.update_editor(|editor, cx| {
 7742        editor.move_page_up(&MovePageUp::default(), cx);
 7743        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7744            assert!(
 7745                menu.selected_item == 0,
 7746                "expected PageUp to select the first item from the context menu"
 7747            );
 7748        } else {
 7749            panic!("expected completion menu to stay open after PageUp");
 7750        }
 7751    });
 7752}
 7753
 7754#[gpui::test]
 7755async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 7756    init_test(cx, |_| {});
 7757
 7758    let mut cx = EditorLspTestContext::new_rust(
 7759        lsp::ServerCapabilities {
 7760            completion_provider: Some(lsp::CompletionOptions {
 7761                trigger_characters: Some(vec![".".to_string()]),
 7762                resolve_provider: Some(true),
 7763                ..Default::default()
 7764            }),
 7765            ..Default::default()
 7766        },
 7767        cx,
 7768    )
 7769    .await;
 7770
 7771    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 7772    cx.simulate_keystroke(".");
 7773    let completion_item = lsp::CompletionItem {
 7774        label: "Some".into(),
 7775        kind: Some(lsp::CompletionItemKind::SNIPPET),
 7776        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 7777        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 7778            kind: lsp::MarkupKind::Markdown,
 7779            value: "```rust\nSome(2)\n```".to_string(),
 7780        })),
 7781        deprecated: Some(false),
 7782        sort_text: Some("Some".to_string()),
 7783        filter_text: Some("Some".to_string()),
 7784        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 7785        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 7786            range: lsp::Range {
 7787                start: lsp::Position {
 7788                    line: 0,
 7789                    character: 22,
 7790                },
 7791                end: lsp::Position {
 7792                    line: 0,
 7793                    character: 22,
 7794                },
 7795            },
 7796            new_text: "Some(2)".to_string(),
 7797        })),
 7798        additional_text_edits: Some(vec![lsp::TextEdit {
 7799            range: lsp::Range {
 7800                start: lsp::Position {
 7801                    line: 0,
 7802                    character: 20,
 7803                },
 7804                end: lsp::Position {
 7805                    line: 0,
 7806                    character: 22,
 7807                },
 7808            },
 7809            new_text: "".to_string(),
 7810        }]),
 7811        ..Default::default()
 7812    };
 7813
 7814    let closure_completion_item = completion_item.clone();
 7815    let counter = Arc::new(AtomicUsize::new(0));
 7816    let counter_clone = counter.clone();
 7817    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 7818        let task_completion_item = closure_completion_item.clone();
 7819        counter_clone.fetch_add(1, atomic::Ordering::Release);
 7820        async move {
 7821            Ok(Some(lsp::CompletionResponse::Array(vec![
 7822                task_completion_item,
 7823            ])))
 7824        }
 7825    });
 7826
 7827    cx.condition(|editor, _| editor.context_menu_visible())
 7828        .await;
 7829    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 7830    assert!(request.next().await.is_some());
 7831    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7832
 7833    cx.simulate_keystroke("S");
 7834    cx.simulate_keystroke("o");
 7835    cx.simulate_keystroke("m");
 7836    cx.condition(|editor, _| editor.context_menu_visible())
 7837        .await;
 7838    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 7839    assert!(request.next().await.is_some());
 7840    assert!(request.next().await.is_some());
 7841    assert!(request.next().await.is_some());
 7842    request.close();
 7843    assert!(request.next().await.is_none());
 7844    assert_eq!(
 7845        counter.load(atomic::Ordering::Acquire),
 7846        4,
 7847        "With the completions menu open, only one LSP request should happen per input"
 7848    );
 7849}
 7850
 7851#[gpui::test]
 7852async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 7853    init_test(cx, |_| {});
 7854    let mut cx = EditorTestContext::new(cx).await;
 7855    let language = Arc::new(Language::new(
 7856        LanguageConfig {
 7857            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 7858            ..Default::default()
 7859        },
 7860        Some(tree_sitter_rust::language()),
 7861    ));
 7862    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 7863
 7864    // If multiple selections intersect a line, the line is only toggled once.
 7865    cx.set_state(indoc! {"
 7866        fn a() {
 7867            «//b();
 7868            ˇ»// «c();
 7869            //ˇ»  d();
 7870        }
 7871    "});
 7872
 7873    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7874
 7875    cx.assert_editor_state(indoc! {"
 7876        fn a() {
 7877            «b();
 7878            c();
 7879            ˇ» d();
 7880        }
 7881    "});
 7882
 7883    // The comment prefix is inserted at the same column for every line in a
 7884    // selection.
 7885    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7886
 7887    cx.assert_editor_state(indoc! {"
 7888        fn a() {
 7889            // «b();
 7890            // c();
 7891            ˇ»//  d();
 7892        }
 7893    "});
 7894
 7895    // If a selection ends at the beginning of a line, that line is not toggled.
 7896    cx.set_selections_state(indoc! {"
 7897        fn a() {
 7898            // b();
 7899            «// c();
 7900        ˇ»    //  d();
 7901        }
 7902    "});
 7903
 7904    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7905
 7906    cx.assert_editor_state(indoc! {"
 7907        fn a() {
 7908            // b();
 7909            «c();
 7910        ˇ»    //  d();
 7911        }
 7912    "});
 7913
 7914    // If a selection span a single line and is empty, the line is toggled.
 7915    cx.set_state(indoc! {"
 7916        fn a() {
 7917            a();
 7918            b();
 7919        ˇ
 7920        }
 7921    "});
 7922
 7923    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7924
 7925    cx.assert_editor_state(indoc! {"
 7926        fn a() {
 7927            a();
 7928            b();
 7929        //•ˇ
 7930        }
 7931    "});
 7932
 7933    // If a selection span multiple lines, empty lines are not toggled.
 7934    cx.set_state(indoc! {"
 7935        fn a() {
 7936            «a();
 7937
 7938            c();ˇ»
 7939        }
 7940    "});
 7941
 7942    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7943
 7944    cx.assert_editor_state(indoc! {"
 7945        fn a() {
 7946            // «a();
 7947
 7948            // c();ˇ»
 7949        }
 7950    "});
 7951
 7952    // If a selection includes multiple comment prefixes, all lines are uncommented.
 7953    cx.set_state(indoc! {"
 7954        fn a() {
 7955            «// a();
 7956            /// b();
 7957            //! c();ˇ»
 7958        }
 7959    "});
 7960
 7961    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7962
 7963    cx.assert_editor_state(indoc! {"
 7964        fn a() {
 7965            «a();
 7966            b();
 7967            c();ˇ»
 7968        }
 7969    "});
 7970}
 7971
 7972#[gpui::test]
 7973async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 7974    init_test(cx, |_| {});
 7975
 7976    let language = Arc::new(Language::new(
 7977        LanguageConfig {
 7978            line_comments: vec!["// ".into()],
 7979            ..Default::default()
 7980        },
 7981        Some(tree_sitter_rust::language()),
 7982    ));
 7983
 7984    let mut cx = EditorTestContext::new(cx).await;
 7985
 7986    cx.language_registry().add(language.clone());
 7987    cx.update_buffer(|buffer, cx| {
 7988        buffer.set_language(Some(language), cx);
 7989    });
 7990
 7991    let toggle_comments = &ToggleComments {
 7992        advance_downwards: true,
 7993    };
 7994
 7995    // Single cursor on one line -> advance
 7996    // Cursor moves horizontally 3 characters as well on non-blank line
 7997    cx.set_state(indoc!(
 7998        "fn a() {
 7999             ˇdog();
 8000             cat();
 8001        }"
 8002    ));
 8003    cx.update_editor(|editor, cx| {
 8004        editor.toggle_comments(toggle_comments, cx);
 8005    });
 8006    cx.assert_editor_state(indoc!(
 8007        "fn a() {
 8008             // dog();
 8009             catˇ();
 8010        }"
 8011    ));
 8012
 8013    // Single selection on one line -> don't advance
 8014    cx.set_state(indoc!(
 8015        "fn a() {
 8016             «dog()ˇ»;
 8017             cat();
 8018        }"
 8019    ));
 8020    cx.update_editor(|editor, cx| {
 8021        editor.toggle_comments(toggle_comments, cx);
 8022    });
 8023    cx.assert_editor_state(indoc!(
 8024        "fn a() {
 8025             // «dog()ˇ»;
 8026             cat();
 8027        }"
 8028    ));
 8029
 8030    // Multiple cursors on one line -> advance
 8031    cx.set_state(indoc!(
 8032        "fn a() {
 8033             ˇdˇog();
 8034             cat();
 8035        }"
 8036    ));
 8037    cx.update_editor(|editor, cx| {
 8038        editor.toggle_comments(toggle_comments, cx);
 8039    });
 8040    cx.assert_editor_state(indoc!(
 8041        "fn a() {
 8042             // dog();
 8043             catˇ(ˇ);
 8044        }"
 8045    ));
 8046
 8047    // Multiple cursors on one line, with selection -> don't advance
 8048    cx.set_state(indoc!(
 8049        "fn a() {
 8050             ˇdˇog«()ˇ»;
 8051             cat();
 8052        }"
 8053    ));
 8054    cx.update_editor(|editor, cx| {
 8055        editor.toggle_comments(toggle_comments, cx);
 8056    });
 8057    cx.assert_editor_state(indoc!(
 8058        "fn a() {
 8059             // ˇdˇog«()ˇ»;
 8060             cat();
 8061        }"
 8062    ));
 8063
 8064    // Single cursor on one line -> advance
 8065    // Cursor moves to column 0 on blank line
 8066    cx.set_state(indoc!(
 8067        "fn a() {
 8068             ˇdog();
 8069
 8070             cat();
 8071        }"
 8072    ));
 8073    cx.update_editor(|editor, cx| {
 8074        editor.toggle_comments(toggle_comments, cx);
 8075    });
 8076    cx.assert_editor_state(indoc!(
 8077        "fn a() {
 8078             // dog();
 8079        ˇ
 8080             cat();
 8081        }"
 8082    ));
 8083
 8084    // Single cursor on one line -> advance
 8085    // Cursor starts and ends at column 0
 8086    cx.set_state(indoc!(
 8087        "fn a() {
 8088         ˇ    dog();
 8089             cat();
 8090        }"
 8091    ));
 8092    cx.update_editor(|editor, cx| {
 8093        editor.toggle_comments(toggle_comments, cx);
 8094    });
 8095    cx.assert_editor_state(indoc!(
 8096        "fn a() {
 8097             // dog();
 8098         ˇ    cat();
 8099        }"
 8100    ));
 8101}
 8102
 8103#[gpui::test]
 8104async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8105    init_test(cx, |_| {});
 8106
 8107    let mut cx = EditorTestContext::new(cx).await;
 8108
 8109    let html_language = Arc::new(
 8110        Language::new(
 8111            LanguageConfig {
 8112                name: "HTML".into(),
 8113                block_comment: Some(("<!-- ".into(), " -->".into())),
 8114                ..Default::default()
 8115            },
 8116            Some(tree_sitter_html::language()),
 8117        )
 8118        .with_injection_query(
 8119            r#"
 8120            (script_element
 8121                (raw_text) @content
 8122                (#set! "language" "javascript"))
 8123            "#,
 8124        )
 8125        .unwrap(),
 8126    );
 8127
 8128    let javascript_language = Arc::new(Language::new(
 8129        LanguageConfig {
 8130            name: "JavaScript".into(),
 8131            line_comments: vec!["// ".into()],
 8132            ..Default::default()
 8133        },
 8134        Some(tree_sitter_typescript::language_tsx()),
 8135    ));
 8136
 8137    cx.language_registry().add(html_language.clone());
 8138    cx.language_registry().add(javascript_language.clone());
 8139    cx.update_buffer(|buffer, cx| {
 8140        buffer.set_language(Some(html_language), cx);
 8141    });
 8142
 8143    // Toggle comments for empty selections
 8144    cx.set_state(
 8145        &r#"
 8146            <p>A</p>ˇ
 8147            <p>B</p>ˇ
 8148            <p>C</p>ˇ
 8149        "#
 8150        .unindent(),
 8151    );
 8152    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8153    cx.assert_editor_state(
 8154        &r#"
 8155            <!-- <p>A</p>ˇ -->
 8156            <!-- <p>B</p>ˇ -->
 8157            <!-- <p>C</p>ˇ -->
 8158        "#
 8159        .unindent(),
 8160    );
 8161    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8162    cx.assert_editor_state(
 8163        &r#"
 8164            <p>A</p>ˇ
 8165            <p>B</p>ˇ
 8166            <p>C</p>ˇ
 8167        "#
 8168        .unindent(),
 8169    );
 8170
 8171    // Toggle comments for mixture of empty and non-empty selections, where
 8172    // multiple selections occupy a given line.
 8173    cx.set_state(
 8174        &r#"
 8175            <p>A«</p>
 8176            <p>ˇ»B</p>ˇ
 8177            <p>C«</p>
 8178            <p>ˇ»D</p>ˇ
 8179        "#
 8180        .unindent(),
 8181    );
 8182
 8183    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8184    cx.assert_editor_state(
 8185        &r#"
 8186            <!-- <p>A«</p>
 8187            <p>ˇ»B</p>ˇ -->
 8188            <!-- <p>C«</p>
 8189            <p>ˇ»D</p>ˇ -->
 8190        "#
 8191        .unindent(),
 8192    );
 8193    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8194    cx.assert_editor_state(
 8195        &r#"
 8196            <p>A«</p>
 8197            <p>ˇ»B</p>ˇ
 8198            <p>C«</p>
 8199            <p>ˇ»D</p>ˇ
 8200        "#
 8201        .unindent(),
 8202    );
 8203
 8204    // Toggle comments when different languages are active for different
 8205    // selections.
 8206    cx.set_state(
 8207        &r#"
 8208            ˇ<script>
 8209                ˇvar x = new Y();
 8210            ˇ</script>
 8211        "#
 8212        .unindent(),
 8213    );
 8214    cx.executor().run_until_parked();
 8215    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8216    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8217    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8218    cx.assert_editor_state(
 8219        &r#"
 8220            <!-- ˇ<script> -->
 8221                // ˇvar x = new Y();
 8222            // ˇ</script>
 8223        "#
 8224        .unindent(),
 8225    );
 8226}
 8227
 8228#[gpui::test]
 8229fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8230    init_test(cx, |_| {});
 8231
 8232    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8233    let multibuffer = cx.new_model(|cx| {
 8234        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8235        multibuffer.push_excerpts(
 8236            buffer.clone(),
 8237            [
 8238                ExcerptRange {
 8239                    context: Point::new(0, 0)..Point::new(0, 4),
 8240                    primary: None,
 8241                },
 8242                ExcerptRange {
 8243                    context: Point::new(1, 0)..Point::new(1, 4),
 8244                    primary: None,
 8245                },
 8246            ],
 8247            cx,
 8248        );
 8249        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8250        multibuffer
 8251    });
 8252
 8253    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8254    _ = view.update(cx, |view, cx| {
 8255        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8256        view.change_selections(None, cx, |s| {
 8257            s.select_ranges([
 8258                Point::new(0, 0)..Point::new(0, 0),
 8259                Point::new(1, 0)..Point::new(1, 0),
 8260            ])
 8261        });
 8262
 8263        view.handle_input("X", cx);
 8264        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8265        assert_eq!(
 8266            view.selections.ranges(cx),
 8267            [
 8268                Point::new(0, 1)..Point::new(0, 1),
 8269                Point::new(1, 1)..Point::new(1, 1),
 8270            ]
 8271        );
 8272
 8273        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8274        view.change_selections(None, cx, |s| {
 8275            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8276        });
 8277        view.backspace(&Default::default(), cx);
 8278        assert_eq!(view.text(cx), "Xa\nbbb");
 8279        assert_eq!(
 8280            view.selections.ranges(cx),
 8281            [Point::new(1, 0)..Point::new(1, 0)]
 8282        );
 8283
 8284        view.change_selections(None, cx, |s| {
 8285            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8286        });
 8287        view.backspace(&Default::default(), cx);
 8288        assert_eq!(view.text(cx), "X\nbb");
 8289        assert_eq!(
 8290            view.selections.ranges(cx),
 8291            [Point::new(0, 1)..Point::new(0, 1)]
 8292        );
 8293    });
 8294}
 8295
 8296#[gpui::test]
 8297fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8298    init_test(cx, |_| {});
 8299
 8300    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8301    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8302        indoc! {"
 8303            [aaaa
 8304            (bbbb]
 8305            cccc)",
 8306        },
 8307        markers.clone(),
 8308    );
 8309    let excerpt_ranges = markers.into_iter().map(|marker| {
 8310        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8311        ExcerptRange {
 8312            context,
 8313            primary: None,
 8314        }
 8315    });
 8316    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8317    let multibuffer = cx.new_model(|cx| {
 8318        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8319        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8320        multibuffer
 8321    });
 8322
 8323    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8324    _ = view.update(cx, |view, cx| {
 8325        let (expected_text, selection_ranges) = marked_text_ranges(
 8326            indoc! {"
 8327                aaaa
 8328                bˇbbb
 8329                bˇbbˇb
 8330                cccc"
 8331            },
 8332            true,
 8333        );
 8334        assert_eq!(view.text(cx), expected_text);
 8335        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8336
 8337        view.handle_input("X", cx);
 8338
 8339        let (expected_text, expected_selections) = marked_text_ranges(
 8340            indoc! {"
 8341                aaaa
 8342                bXˇbbXb
 8343                bXˇbbXˇb
 8344                cccc"
 8345            },
 8346            false,
 8347        );
 8348        assert_eq!(view.text(cx), expected_text);
 8349        assert_eq!(view.selections.ranges(cx), expected_selections);
 8350
 8351        view.newline(&Newline, cx);
 8352        let (expected_text, expected_selections) = marked_text_ranges(
 8353            indoc! {"
 8354                aaaa
 8355                bX
 8356                ˇbbX
 8357                b
 8358                bX
 8359                ˇbbX
 8360                ˇb
 8361                cccc"
 8362            },
 8363            false,
 8364        );
 8365        assert_eq!(view.text(cx), expected_text);
 8366        assert_eq!(view.selections.ranges(cx), expected_selections);
 8367    });
 8368}
 8369
 8370#[gpui::test]
 8371fn test_refresh_selections(cx: &mut TestAppContext) {
 8372    init_test(cx, |_| {});
 8373
 8374    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8375    let mut excerpt1_id = None;
 8376    let multibuffer = cx.new_model(|cx| {
 8377        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8378        excerpt1_id = multibuffer
 8379            .push_excerpts(
 8380                buffer.clone(),
 8381                [
 8382                    ExcerptRange {
 8383                        context: Point::new(0, 0)..Point::new(1, 4),
 8384                        primary: None,
 8385                    },
 8386                    ExcerptRange {
 8387                        context: Point::new(1, 0)..Point::new(2, 4),
 8388                        primary: None,
 8389                    },
 8390                ],
 8391                cx,
 8392            )
 8393            .into_iter()
 8394            .next();
 8395        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8396        multibuffer
 8397    });
 8398
 8399    let editor = cx.add_window(|cx| {
 8400        let mut editor = build_editor(multibuffer.clone(), cx);
 8401        let snapshot = editor.snapshot(cx);
 8402        editor.change_selections(None, cx, |s| {
 8403            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8404        });
 8405        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8406        assert_eq!(
 8407            editor.selections.ranges(cx),
 8408            [
 8409                Point::new(1, 3)..Point::new(1, 3),
 8410                Point::new(2, 1)..Point::new(2, 1),
 8411            ]
 8412        );
 8413        editor
 8414    });
 8415
 8416    // Refreshing selections is a no-op when excerpts haven't changed.
 8417    _ = editor.update(cx, |editor, cx| {
 8418        editor.change_selections(None, cx, |s| s.refresh());
 8419        assert_eq!(
 8420            editor.selections.ranges(cx),
 8421            [
 8422                Point::new(1, 3)..Point::new(1, 3),
 8423                Point::new(2, 1)..Point::new(2, 1),
 8424            ]
 8425        );
 8426    });
 8427
 8428    _ = multibuffer.update(cx, |multibuffer, cx| {
 8429        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8430    });
 8431    _ = editor.update(cx, |editor, cx| {
 8432        // Removing an excerpt causes the first selection to become degenerate.
 8433        assert_eq!(
 8434            editor.selections.ranges(cx),
 8435            [
 8436                Point::new(0, 0)..Point::new(0, 0),
 8437                Point::new(0, 1)..Point::new(0, 1)
 8438            ]
 8439        );
 8440
 8441        // Refreshing selections will relocate the first selection to the original buffer
 8442        // location.
 8443        editor.change_selections(None, cx, |s| s.refresh());
 8444        assert_eq!(
 8445            editor.selections.ranges(cx),
 8446            [
 8447                Point::new(0, 1)..Point::new(0, 1),
 8448                Point::new(0, 3)..Point::new(0, 3)
 8449            ]
 8450        );
 8451        assert!(editor.selections.pending_anchor().is_some());
 8452    });
 8453}
 8454
 8455#[gpui::test]
 8456fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8457    init_test(cx, |_| {});
 8458
 8459    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8460    let mut excerpt1_id = None;
 8461    let multibuffer = cx.new_model(|cx| {
 8462        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8463        excerpt1_id = multibuffer
 8464            .push_excerpts(
 8465                buffer.clone(),
 8466                [
 8467                    ExcerptRange {
 8468                        context: Point::new(0, 0)..Point::new(1, 4),
 8469                        primary: None,
 8470                    },
 8471                    ExcerptRange {
 8472                        context: Point::new(1, 0)..Point::new(2, 4),
 8473                        primary: None,
 8474                    },
 8475                ],
 8476                cx,
 8477            )
 8478            .into_iter()
 8479            .next();
 8480        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8481        multibuffer
 8482    });
 8483
 8484    let editor = cx.add_window(|cx| {
 8485        let mut editor = build_editor(multibuffer.clone(), cx);
 8486        let snapshot = editor.snapshot(cx);
 8487        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8488        assert_eq!(
 8489            editor.selections.ranges(cx),
 8490            [Point::new(1, 3)..Point::new(1, 3)]
 8491        );
 8492        editor
 8493    });
 8494
 8495    _ = multibuffer.update(cx, |multibuffer, cx| {
 8496        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8497    });
 8498    _ = editor.update(cx, |editor, cx| {
 8499        assert_eq!(
 8500            editor.selections.ranges(cx),
 8501            [Point::new(0, 0)..Point::new(0, 0)]
 8502        );
 8503
 8504        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8505        editor.change_selections(None, cx, |s| s.refresh());
 8506        assert_eq!(
 8507            editor.selections.ranges(cx),
 8508            [Point::new(0, 3)..Point::new(0, 3)]
 8509        );
 8510        assert!(editor.selections.pending_anchor().is_some());
 8511    });
 8512}
 8513
 8514#[gpui::test]
 8515async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8516    init_test(cx, |_| {});
 8517
 8518    let language = Arc::new(
 8519        Language::new(
 8520            LanguageConfig {
 8521                brackets: BracketPairConfig {
 8522                    pairs: vec![
 8523                        BracketPair {
 8524                            start: "{".to_string(),
 8525                            end: "}".to_string(),
 8526                            close: true,
 8527                            surround: true,
 8528                            newline: true,
 8529                        },
 8530                        BracketPair {
 8531                            start: "/* ".to_string(),
 8532                            end: " */".to_string(),
 8533                            close: true,
 8534                            surround: true,
 8535                            newline: true,
 8536                        },
 8537                    ],
 8538                    ..Default::default()
 8539                },
 8540                ..Default::default()
 8541            },
 8542            Some(tree_sitter_rust::language()),
 8543        )
 8544        .with_indents_query("")
 8545        .unwrap(),
 8546    );
 8547
 8548    let text = concat!(
 8549        "{   }\n",     //
 8550        "  x\n",       //
 8551        "  /*   */\n", //
 8552        "x\n",         //
 8553        "{{} }\n",     //
 8554    );
 8555
 8556    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 8557    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8558    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8559    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 8560        .await;
 8561
 8562    _ = view.update(cx, |view, cx| {
 8563        view.change_selections(None, cx, |s| {
 8564            s.select_display_ranges([
 8565                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 8566                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 8567                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 8568            ])
 8569        });
 8570        view.newline(&Newline, cx);
 8571
 8572        assert_eq!(
 8573            view.buffer().read(cx).read(cx).text(),
 8574            concat!(
 8575                "{ \n",    // Suppress rustfmt
 8576                "\n",      //
 8577                "}\n",     //
 8578                "  x\n",   //
 8579                "  /* \n", //
 8580                "  \n",    //
 8581                "  */\n",  //
 8582                "x\n",     //
 8583                "{{} \n",  //
 8584                "}\n",     //
 8585            )
 8586        );
 8587    });
 8588}
 8589
 8590#[gpui::test]
 8591fn test_highlighted_ranges(cx: &mut TestAppContext) {
 8592    init_test(cx, |_| {});
 8593
 8594    let editor = cx.add_window(|cx| {
 8595        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 8596        build_editor(buffer.clone(), cx)
 8597    });
 8598
 8599    _ = editor.update(cx, |editor, cx| {
 8600        struct Type1;
 8601        struct Type2;
 8602
 8603        let buffer = editor.buffer.read(cx).snapshot(cx);
 8604
 8605        let anchor_range =
 8606            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 8607
 8608        editor.highlight_background::<Type1>(
 8609            &[
 8610                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 8611                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 8612                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 8613                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 8614            ],
 8615            |_| Hsla::red(),
 8616            cx,
 8617        );
 8618        editor.highlight_background::<Type2>(
 8619            &[
 8620                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 8621                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 8622                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 8623                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 8624            ],
 8625            |_| Hsla::green(),
 8626            cx,
 8627        );
 8628
 8629        let snapshot = editor.snapshot(cx);
 8630        let mut highlighted_ranges = editor.background_highlights_in_range(
 8631            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 8632            &snapshot,
 8633            cx.theme().colors(),
 8634        );
 8635        // Enforce a consistent ordering based on color without relying on the ordering of the
 8636        // highlight's `TypeId` which is non-executor.
 8637        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 8638        assert_eq!(
 8639            highlighted_ranges,
 8640            &[
 8641                (
 8642                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 8643                    Hsla::red(),
 8644                ),
 8645                (
 8646                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8647                    Hsla::red(),
 8648                ),
 8649                (
 8650                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 8651                    Hsla::green(),
 8652                ),
 8653                (
 8654                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 8655                    Hsla::green(),
 8656                ),
 8657            ]
 8658        );
 8659        assert_eq!(
 8660            editor.background_highlights_in_range(
 8661                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 8662                &snapshot,
 8663                cx.theme().colors(),
 8664            ),
 8665            &[(
 8666                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8667                Hsla::red(),
 8668            )]
 8669        );
 8670    });
 8671}
 8672
 8673#[gpui::test]
 8674async fn test_following(cx: &mut gpui::TestAppContext) {
 8675    init_test(cx, |_| {});
 8676
 8677    let fs = FakeFs::new(cx.executor());
 8678    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8679
 8680    let buffer = project.update(cx, |project, cx| {
 8681        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 8682        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 8683    });
 8684    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 8685    let follower = cx.update(|cx| {
 8686        cx.open_window(
 8687            WindowOptions {
 8688                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 8689                    gpui::Point::new(px(0.), px(0.)),
 8690                    gpui::Point::new(px(10.), px(80.)),
 8691                ))),
 8692                ..Default::default()
 8693            },
 8694            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 8695        )
 8696        .unwrap()
 8697    });
 8698
 8699    let is_still_following = Rc::new(RefCell::new(true));
 8700    let follower_edit_event_count = Rc::new(RefCell::new(0));
 8701    let pending_update = Rc::new(RefCell::new(None));
 8702    _ = follower.update(cx, {
 8703        let update = pending_update.clone();
 8704        let is_still_following = is_still_following.clone();
 8705        let follower_edit_event_count = follower_edit_event_count.clone();
 8706        |_, cx| {
 8707            cx.subscribe(
 8708                &leader.root_view(cx).unwrap(),
 8709                move |_, leader, event, cx| {
 8710                    leader
 8711                        .read(cx)
 8712                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8713                },
 8714            )
 8715            .detach();
 8716
 8717            cx.subscribe(
 8718                &follower.root_view(cx).unwrap(),
 8719                move |_, _, event: &EditorEvent, _cx| {
 8720                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 8721                        *is_still_following.borrow_mut() = false;
 8722                    }
 8723
 8724                    if let EditorEvent::BufferEdited = event {
 8725                        *follower_edit_event_count.borrow_mut() += 1;
 8726                    }
 8727                },
 8728            )
 8729            .detach();
 8730        }
 8731    });
 8732
 8733    // Update the selections only
 8734    _ = leader.update(cx, |leader, cx| {
 8735        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8736    });
 8737    follower
 8738        .update(cx, |follower, cx| {
 8739            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8740        })
 8741        .unwrap()
 8742        .await
 8743        .unwrap();
 8744    _ = follower.update(cx, |follower, cx| {
 8745        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 8746    });
 8747    assert_eq!(*is_still_following.borrow(), true);
 8748    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8749
 8750    // Update the scroll position only
 8751    _ = leader.update(cx, |leader, cx| {
 8752        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8753    });
 8754    follower
 8755        .update(cx, |follower, cx| {
 8756            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8757        })
 8758        .unwrap()
 8759        .await
 8760        .unwrap();
 8761    assert_eq!(
 8762        follower
 8763            .update(cx, |follower, cx| follower.scroll_position(cx))
 8764            .unwrap(),
 8765        gpui::Point::new(1.5, 3.5)
 8766    );
 8767    assert_eq!(*is_still_following.borrow(), true);
 8768    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8769
 8770    // Update the selections and scroll position. The follower's scroll position is updated
 8771    // via autoscroll, not via the leader's exact scroll position.
 8772    _ = leader.update(cx, |leader, cx| {
 8773        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8774        leader.request_autoscroll(Autoscroll::newest(), cx);
 8775        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8776    });
 8777    follower
 8778        .update(cx, |follower, cx| {
 8779            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8780        })
 8781        .unwrap()
 8782        .await
 8783        .unwrap();
 8784    _ = follower.update(cx, |follower, cx| {
 8785        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 8786        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 8787    });
 8788    assert_eq!(*is_still_following.borrow(), true);
 8789
 8790    // Creating a pending selection that precedes another selection
 8791    _ = leader.update(cx, |leader, cx| {
 8792        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8793        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 8794    });
 8795    follower
 8796        .update(cx, |follower, cx| {
 8797            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8798        })
 8799        .unwrap()
 8800        .await
 8801        .unwrap();
 8802    _ = follower.update(cx, |follower, cx| {
 8803        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 8804    });
 8805    assert_eq!(*is_still_following.borrow(), true);
 8806
 8807    // Extend the pending selection so that it surrounds another selection
 8808    _ = leader.update(cx, |leader, cx| {
 8809        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 8810    });
 8811    follower
 8812        .update(cx, |follower, cx| {
 8813            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8814        })
 8815        .unwrap()
 8816        .await
 8817        .unwrap();
 8818    _ = follower.update(cx, |follower, cx| {
 8819        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 8820    });
 8821
 8822    // Scrolling locally breaks the follow
 8823    _ = follower.update(cx, |follower, cx| {
 8824        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 8825        follower.set_scroll_anchor(
 8826            ScrollAnchor {
 8827                anchor: top_anchor,
 8828                offset: gpui::Point::new(0.0, 0.5),
 8829            },
 8830            cx,
 8831        );
 8832    });
 8833    assert_eq!(*is_still_following.borrow(), false);
 8834}
 8835
 8836#[gpui::test]
 8837async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 8838    init_test(cx, |_| {});
 8839
 8840    let fs = FakeFs::new(cx.executor());
 8841    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8842    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8843    let pane = workspace
 8844        .update(cx, |workspace, _| workspace.active_pane().clone())
 8845        .unwrap();
 8846
 8847    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8848
 8849    let leader = pane.update(cx, |_, cx| {
 8850        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 8851        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 8852    });
 8853
 8854    // Start following the editor when it has no excerpts.
 8855    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8856    let follower_1 = cx
 8857        .update_window(*workspace.deref(), |_, cx| {
 8858            Editor::from_state_proto(
 8859                workspace.root_view(cx).unwrap(),
 8860                ViewId {
 8861                    creator: Default::default(),
 8862                    id: 0,
 8863                },
 8864                &mut state_message,
 8865                cx,
 8866            )
 8867        })
 8868        .unwrap()
 8869        .unwrap()
 8870        .await
 8871        .unwrap();
 8872
 8873    let update_message = Rc::new(RefCell::new(None));
 8874    follower_1.update(cx, {
 8875        let update = update_message.clone();
 8876        |_, cx| {
 8877            cx.subscribe(&leader, move |_, leader, event, cx| {
 8878                leader
 8879                    .read(cx)
 8880                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8881            })
 8882            .detach();
 8883        }
 8884    });
 8885
 8886    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 8887        (
 8888            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 8889            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 8890        )
 8891    });
 8892
 8893    // Insert some excerpts.
 8894    _ = leader.update(cx, |leader, cx| {
 8895        leader.buffer.update(cx, |multibuffer, cx| {
 8896            let excerpt_ids = multibuffer.push_excerpts(
 8897                buffer_1.clone(),
 8898                [
 8899                    ExcerptRange {
 8900                        context: 1..6,
 8901                        primary: None,
 8902                    },
 8903                    ExcerptRange {
 8904                        context: 12..15,
 8905                        primary: None,
 8906                    },
 8907                    ExcerptRange {
 8908                        context: 0..3,
 8909                        primary: None,
 8910                    },
 8911                ],
 8912                cx,
 8913            );
 8914            multibuffer.insert_excerpts_after(
 8915                excerpt_ids[0],
 8916                buffer_2.clone(),
 8917                [
 8918                    ExcerptRange {
 8919                        context: 8..12,
 8920                        primary: None,
 8921                    },
 8922                    ExcerptRange {
 8923                        context: 0..6,
 8924                        primary: None,
 8925                    },
 8926                ],
 8927                cx,
 8928            );
 8929        });
 8930    });
 8931
 8932    // Apply the update of adding the excerpts.
 8933    follower_1
 8934        .update(cx, |follower, cx| {
 8935            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8936        })
 8937        .await
 8938        .unwrap();
 8939    assert_eq!(
 8940        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8941        leader.update(cx, |editor, cx| editor.text(cx))
 8942    );
 8943    update_message.borrow_mut().take();
 8944
 8945    // Start following separately after it already has excerpts.
 8946    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8947    let follower_2 = cx
 8948        .update_window(*workspace.deref(), |_, cx| {
 8949            Editor::from_state_proto(
 8950                workspace.root_view(cx).unwrap().clone(),
 8951                ViewId {
 8952                    creator: Default::default(),
 8953                    id: 0,
 8954                },
 8955                &mut state_message,
 8956                cx,
 8957            )
 8958        })
 8959        .unwrap()
 8960        .unwrap()
 8961        .await
 8962        .unwrap();
 8963    assert_eq!(
 8964        follower_2.update(cx, |editor, cx| editor.text(cx)),
 8965        leader.update(cx, |editor, cx| editor.text(cx))
 8966    );
 8967
 8968    // Remove some excerpts.
 8969    _ = leader.update(cx, |leader, cx| {
 8970        leader.buffer.update(cx, |multibuffer, cx| {
 8971            let excerpt_ids = multibuffer.excerpt_ids();
 8972            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 8973            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 8974        });
 8975    });
 8976
 8977    // Apply the update of removing the excerpts.
 8978    follower_1
 8979        .update(cx, |follower, cx| {
 8980            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8981        })
 8982        .await
 8983        .unwrap();
 8984    follower_2
 8985        .update(cx, |follower, cx| {
 8986            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8987        })
 8988        .await
 8989        .unwrap();
 8990    update_message.borrow_mut().take();
 8991    assert_eq!(
 8992        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8993        leader.update(cx, |editor, cx| editor.text(cx))
 8994    );
 8995}
 8996
 8997#[gpui::test]
 8998async fn go_to_prev_overlapping_diagnostic(
 8999    executor: BackgroundExecutor,
 9000    cx: &mut gpui::TestAppContext,
 9001) {
 9002    init_test(cx, |_| {});
 9003
 9004    let mut cx = EditorTestContext::new(cx).await;
 9005    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9006
 9007    cx.set_state(indoc! {"
 9008        ˇfn func(abc def: i32) -> u32 {
 9009        }
 9010    "});
 9011
 9012    _ = cx.update(|cx| {
 9013        _ = project.update(cx, |project, cx| {
 9014            project
 9015                .update_diagnostics(
 9016                    LanguageServerId(0),
 9017                    lsp::PublishDiagnosticsParams {
 9018                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9019                        version: None,
 9020                        diagnostics: vec![
 9021                            lsp::Diagnostic {
 9022                                range: lsp::Range::new(
 9023                                    lsp::Position::new(0, 11),
 9024                                    lsp::Position::new(0, 12),
 9025                                ),
 9026                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9027                                ..Default::default()
 9028                            },
 9029                            lsp::Diagnostic {
 9030                                range: lsp::Range::new(
 9031                                    lsp::Position::new(0, 12),
 9032                                    lsp::Position::new(0, 15),
 9033                                ),
 9034                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9035                                ..Default::default()
 9036                            },
 9037                            lsp::Diagnostic {
 9038                                range: lsp::Range::new(
 9039                                    lsp::Position::new(0, 25),
 9040                                    lsp::Position::new(0, 28),
 9041                                ),
 9042                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9043                                ..Default::default()
 9044                            },
 9045                        ],
 9046                    },
 9047                    &[],
 9048                    cx,
 9049                )
 9050                .unwrap()
 9051        });
 9052    });
 9053
 9054    executor.run_until_parked();
 9055
 9056    cx.update_editor(|editor, cx| {
 9057        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9058    });
 9059
 9060    cx.assert_editor_state(indoc! {"
 9061        fn func(abc def: i32) -> ˇu32 {
 9062        }
 9063    "});
 9064
 9065    cx.update_editor(|editor, cx| {
 9066        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9067    });
 9068
 9069    cx.assert_editor_state(indoc! {"
 9070        fn func(abc ˇdef: i32) -> u32 {
 9071        }
 9072    "});
 9073
 9074    cx.update_editor(|editor, cx| {
 9075        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9076    });
 9077
 9078    cx.assert_editor_state(indoc! {"
 9079        fn func(abcˇ def: i32) -> u32 {
 9080        }
 9081    "});
 9082
 9083    cx.update_editor(|editor, cx| {
 9084        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9085    });
 9086
 9087    cx.assert_editor_state(indoc! {"
 9088        fn func(abc def: i32) -> ˇu32 {
 9089        }
 9090    "});
 9091}
 9092
 9093#[gpui::test]
 9094async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9095    init_test(cx, |_| {});
 9096
 9097    let mut cx = EditorTestContext::new(cx).await;
 9098
 9099    let diff_base = r#"
 9100        use some::mod;
 9101
 9102        const A: u32 = 42;
 9103
 9104        fn main() {
 9105            println!("hello");
 9106
 9107            println!("world");
 9108        }
 9109        "#
 9110    .unindent();
 9111
 9112    // Edits are modified, removed, modified, added
 9113    cx.set_state(
 9114        &r#"
 9115        use some::modified;
 9116
 9117        ˇ
 9118        fn main() {
 9119            println!("hello there");
 9120
 9121            println!("around the");
 9122            println!("world");
 9123        }
 9124        "#
 9125        .unindent(),
 9126    );
 9127
 9128    cx.set_diff_base(Some(&diff_base));
 9129    executor.run_until_parked();
 9130
 9131    cx.update_editor(|editor, cx| {
 9132        //Wrap around the bottom of the buffer
 9133        for _ in 0..3 {
 9134            editor.go_to_hunk(&GoToHunk, cx);
 9135        }
 9136    });
 9137
 9138    cx.assert_editor_state(
 9139        &r#"
 9140        ˇuse some::modified;
 9141
 9142
 9143        fn main() {
 9144            println!("hello there");
 9145
 9146            println!("around the");
 9147            println!("world");
 9148        }
 9149        "#
 9150        .unindent(),
 9151    );
 9152
 9153    cx.update_editor(|editor, cx| {
 9154        //Wrap around the top of the buffer
 9155        for _ in 0..2 {
 9156            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9157        }
 9158    });
 9159
 9160    cx.assert_editor_state(
 9161        &r#"
 9162        use some::modified;
 9163
 9164
 9165        fn main() {
 9166        ˇ    println!("hello there");
 9167
 9168            println!("around the");
 9169            println!("world");
 9170        }
 9171        "#
 9172        .unindent(),
 9173    );
 9174
 9175    cx.update_editor(|editor, cx| {
 9176        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9177    });
 9178
 9179    cx.assert_editor_state(
 9180        &r#"
 9181        use some::modified;
 9182
 9183        ˇ
 9184        fn main() {
 9185            println!("hello there");
 9186
 9187            println!("around the");
 9188            println!("world");
 9189        }
 9190        "#
 9191        .unindent(),
 9192    );
 9193
 9194    cx.update_editor(|editor, cx| {
 9195        for _ in 0..3 {
 9196            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9197        }
 9198    });
 9199
 9200    cx.assert_editor_state(
 9201        &r#"
 9202        use some::modified;
 9203
 9204
 9205        fn main() {
 9206        ˇ    println!("hello there");
 9207
 9208            println!("around the");
 9209            println!("world");
 9210        }
 9211        "#
 9212        .unindent(),
 9213    );
 9214
 9215    cx.update_editor(|editor, cx| {
 9216        editor.fold(&Fold, cx);
 9217
 9218        //Make sure that the fold only gets one hunk
 9219        for _ in 0..4 {
 9220            editor.go_to_hunk(&GoToHunk, cx);
 9221        }
 9222    });
 9223
 9224    cx.assert_editor_state(
 9225        &r#"
 9226        ˇuse some::modified;
 9227
 9228
 9229        fn main() {
 9230            println!("hello there");
 9231
 9232            println!("around the");
 9233            println!("world");
 9234        }
 9235        "#
 9236        .unindent(),
 9237    );
 9238}
 9239
 9240#[test]
 9241fn test_split_words() {
 9242    fn split(text: &str) -> Vec<&str> {
 9243        split_words(text).collect()
 9244    }
 9245
 9246    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9247    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9248    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9249    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9250    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9251    assert_eq!(split("helloworld"), &["helloworld"]);
 9252
 9253    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9254}
 9255
 9256#[gpui::test]
 9257async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9258    init_test(cx, |_| {});
 9259
 9260    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9261    let mut assert = |before, after| {
 9262        let _state_context = cx.set_state(before);
 9263        cx.update_editor(|editor, cx| {
 9264            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9265        });
 9266        cx.assert_editor_state(after);
 9267    };
 9268
 9269    // Outside bracket jumps to outside of matching bracket
 9270    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9271    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9272
 9273    // Inside bracket jumps to inside of matching bracket
 9274    assert("console.log(ˇvar);", "console.log(varˇ);");
 9275    assert("console.log(varˇ);", "console.log(ˇvar);");
 9276
 9277    // When outside a bracket and inside, favor jumping to the inside bracket
 9278    assert(
 9279        "console.log('foo', [1, 2, 3]ˇ);",
 9280        "console.log(ˇ'foo', [1, 2, 3]);",
 9281    );
 9282    assert(
 9283        "console.log(ˇ'foo', [1, 2, 3]);",
 9284        "console.log('foo', [1, 2, 3]ˇ);",
 9285    );
 9286
 9287    // Bias forward if two options are equally likely
 9288    assert(
 9289        "let result = curried_fun()ˇ();",
 9290        "let result = curried_fun()()ˇ;",
 9291    );
 9292
 9293    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9294    assert(
 9295        indoc! {"
 9296            function test() {
 9297                console.log('test')ˇ
 9298            }"},
 9299        indoc! {"
 9300            function test() {
 9301                console.logˇ('test')
 9302            }"},
 9303    );
 9304}
 9305
 9306#[gpui::test]
 9307async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9308    init_test(cx, |_| {});
 9309
 9310    let fs = FakeFs::new(cx.executor());
 9311    fs.insert_tree(
 9312        "/a",
 9313        json!({
 9314            "main.rs": "fn main() { let a = 5; }",
 9315            "other.rs": "// Test file",
 9316        }),
 9317    )
 9318    .await;
 9319    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9320
 9321    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9322    language_registry.add(Arc::new(Language::new(
 9323        LanguageConfig {
 9324            name: "Rust".into(),
 9325            matcher: LanguageMatcher {
 9326                path_suffixes: vec!["rs".to_string()],
 9327                ..Default::default()
 9328            },
 9329            brackets: BracketPairConfig {
 9330                pairs: vec![BracketPair {
 9331                    start: "{".to_string(),
 9332                    end: "}".to_string(),
 9333                    close: true,
 9334                    surround: true,
 9335                    newline: true,
 9336                }],
 9337                disabled_scopes_by_bracket_ix: Vec::new(),
 9338            },
 9339            ..Default::default()
 9340        },
 9341        Some(tree_sitter_rust::language()),
 9342    )));
 9343    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9344        "Rust",
 9345        FakeLspAdapter {
 9346            capabilities: lsp::ServerCapabilities {
 9347                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9348                    first_trigger_character: "{".to_string(),
 9349                    more_trigger_character: None,
 9350                }),
 9351                ..Default::default()
 9352            },
 9353            ..Default::default()
 9354        },
 9355    );
 9356
 9357    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9358
 9359    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9360
 9361    let worktree_id = workspace
 9362        .update(cx, |workspace, cx| {
 9363            workspace.project().update(cx, |project, cx| {
 9364                project.worktrees(cx).next().unwrap().read(cx).id()
 9365            })
 9366        })
 9367        .unwrap();
 9368
 9369    let buffer = project
 9370        .update(cx, |project, cx| {
 9371            project.open_local_buffer("/a/main.rs", cx)
 9372        })
 9373        .await
 9374        .unwrap();
 9375    cx.executor().run_until_parked();
 9376    cx.executor().start_waiting();
 9377    let fake_server = fake_servers.next().await.unwrap();
 9378    let editor_handle = workspace
 9379        .update(cx, |workspace, cx| {
 9380            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9381        })
 9382        .unwrap()
 9383        .await
 9384        .unwrap()
 9385        .downcast::<Editor>()
 9386        .unwrap();
 9387
 9388    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9389        assert_eq!(
 9390            params.text_document_position.text_document.uri,
 9391            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9392        );
 9393        assert_eq!(
 9394            params.text_document_position.position,
 9395            lsp::Position::new(0, 21),
 9396        );
 9397
 9398        Ok(Some(vec![lsp::TextEdit {
 9399            new_text: "]".to_string(),
 9400            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9401        }]))
 9402    });
 9403
 9404    editor_handle.update(cx, |editor, cx| {
 9405        editor.focus(cx);
 9406        editor.change_selections(None, cx, |s| {
 9407            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9408        });
 9409        editor.handle_input("{", cx);
 9410    });
 9411
 9412    cx.executor().run_until_parked();
 9413
 9414    _ = buffer.update(cx, |buffer, _| {
 9415        assert_eq!(
 9416            buffer.text(),
 9417            "fn main() { let a = {5}; }",
 9418            "No extra braces from on type formatting should appear in the buffer"
 9419        )
 9420    });
 9421}
 9422
 9423#[gpui::test]
 9424async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9425    init_test(cx, |_| {});
 9426
 9427    let fs = FakeFs::new(cx.executor());
 9428    fs.insert_tree(
 9429        "/a",
 9430        json!({
 9431            "main.rs": "fn main() { let a = 5; }",
 9432            "other.rs": "// Test file",
 9433        }),
 9434    )
 9435    .await;
 9436
 9437    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9438
 9439    let server_restarts = Arc::new(AtomicUsize::new(0));
 9440    let closure_restarts = Arc::clone(&server_restarts);
 9441    let language_server_name = "test language server";
 9442    let language_name: Arc<str> = "Rust".into();
 9443
 9444    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9445    language_registry.add(Arc::new(Language::new(
 9446        LanguageConfig {
 9447            name: Arc::clone(&language_name),
 9448            matcher: LanguageMatcher {
 9449                path_suffixes: vec!["rs".to_string()],
 9450                ..Default::default()
 9451            },
 9452            ..Default::default()
 9453        },
 9454        Some(tree_sitter_rust::language()),
 9455    )));
 9456    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9457        "Rust",
 9458        FakeLspAdapter {
 9459            name: language_server_name,
 9460            initialization_options: Some(json!({
 9461                "testOptionValue": true
 9462            })),
 9463            initializer: Some(Box::new(move |fake_server| {
 9464                let task_restarts = Arc::clone(&closure_restarts);
 9465                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9466                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9467                    futures::future::ready(Ok(()))
 9468                });
 9469            })),
 9470            ..Default::default()
 9471        },
 9472    );
 9473
 9474    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9475    let _buffer = project
 9476        .update(cx, |project, cx| {
 9477            project.open_local_buffer("/a/main.rs", cx)
 9478        })
 9479        .await
 9480        .unwrap();
 9481    let _fake_server = fake_servers.next().await.unwrap();
 9482    update_test_language_settings(cx, |language_settings| {
 9483        language_settings.languages.insert(
 9484            Arc::clone(&language_name),
 9485            LanguageSettingsContent {
 9486                tab_size: NonZeroU32::new(8),
 9487                ..Default::default()
 9488            },
 9489        );
 9490    });
 9491    cx.executor().run_until_parked();
 9492    assert_eq!(
 9493        server_restarts.load(atomic::Ordering::Acquire),
 9494        0,
 9495        "Should not restart LSP server on an unrelated change"
 9496    );
 9497
 9498    update_test_project_settings(cx, |project_settings| {
 9499        project_settings.lsp.insert(
 9500            "Some other server name".into(),
 9501            LspSettings {
 9502                binary: None,
 9503                settings: None,
 9504                initialization_options: Some(json!({
 9505                    "some other init value": false
 9506                })),
 9507            },
 9508        );
 9509    });
 9510    cx.executor().run_until_parked();
 9511    assert_eq!(
 9512        server_restarts.load(atomic::Ordering::Acquire),
 9513        0,
 9514        "Should not restart LSP server on an unrelated LSP settings change"
 9515    );
 9516
 9517    update_test_project_settings(cx, |project_settings| {
 9518        project_settings.lsp.insert(
 9519            language_server_name.into(),
 9520            LspSettings {
 9521                binary: None,
 9522                settings: None,
 9523                initialization_options: Some(json!({
 9524                    "anotherInitValue": false
 9525                })),
 9526            },
 9527        );
 9528    });
 9529    cx.executor().run_until_parked();
 9530    assert_eq!(
 9531        server_restarts.load(atomic::Ordering::Acquire),
 9532        1,
 9533        "Should restart LSP server on a related LSP settings change"
 9534    );
 9535
 9536    update_test_project_settings(cx, |project_settings| {
 9537        project_settings.lsp.insert(
 9538            language_server_name.into(),
 9539            LspSettings {
 9540                binary: None,
 9541                settings: None,
 9542                initialization_options: Some(json!({
 9543                    "anotherInitValue": false
 9544                })),
 9545            },
 9546        );
 9547    });
 9548    cx.executor().run_until_parked();
 9549    assert_eq!(
 9550        server_restarts.load(atomic::Ordering::Acquire),
 9551        1,
 9552        "Should not restart LSP server on a related LSP settings change that is the same"
 9553    );
 9554
 9555    update_test_project_settings(cx, |project_settings| {
 9556        project_settings.lsp.insert(
 9557            language_server_name.into(),
 9558            LspSettings {
 9559                binary: None,
 9560                settings: None,
 9561                initialization_options: None,
 9562            },
 9563        );
 9564    });
 9565    cx.executor().run_until_parked();
 9566    assert_eq!(
 9567        server_restarts.load(atomic::Ordering::Acquire),
 9568        2,
 9569        "Should restart LSP server on another related LSP settings change"
 9570    );
 9571}
 9572
 9573#[gpui::test]
 9574async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 9575    init_test(cx, |_| {});
 9576
 9577    let mut cx = EditorLspTestContext::new_rust(
 9578        lsp::ServerCapabilities {
 9579            completion_provider: Some(lsp::CompletionOptions {
 9580                trigger_characters: Some(vec![".".to_string()]),
 9581                resolve_provider: Some(true),
 9582                ..Default::default()
 9583            }),
 9584            ..Default::default()
 9585        },
 9586        cx,
 9587    )
 9588    .await;
 9589
 9590    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9591    cx.simulate_keystroke(".");
 9592    let completion_item = lsp::CompletionItem {
 9593        label: "some".into(),
 9594        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9595        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9596        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9597            kind: lsp::MarkupKind::Markdown,
 9598            value: "```rust\nSome(2)\n```".to_string(),
 9599        })),
 9600        deprecated: Some(false),
 9601        sort_text: Some("fffffff2".to_string()),
 9602        filter_text: Some("some".to_string()),
 9603        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9604        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9605            range: lsp::Range {
 9606                start: lsp::Position {
 9607                    line: 0,
 9608                    character: 22,
 9609                },
 9610                end: lsp::Position {
 9611                    line: 0,
 9612                    character: 22,
 9613                },
 9614            },
 9615            new_text: "Some(2)".to_string(),
 9616        })),
 9617        additional_text_edits: Some(vec![lsp::TextEdit {
 9618            range: lsp::Range {
 9619                start: lsp::Position {
 9620                    line: 0,
 9621                    character: 20,
 9622                },
 9623                end: lsp::Position {
 9624                    line: 0,
 9625                    character: 22,
 9626                },
 9627            },
 9628            new_text: "".to_string(),
 9629        }]),
 9630        ..Default::default()
 9631    };
 9632
 9633    let closure_completion_item = completion_item.clone();
 9634    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9635        let task_completion_item = closure_completion_item.clone();
 9636        async move {
 9637            Ok(Some(lsp::CompletionResponse::Array(vec![
 9638                task_completion_item,
 9639            ])))
 9640        }
 9641    });
 9642
 9643    request.next().await;
 9644
 9645    cx.condition(|editor, _| editor.context_menu_visible())
 9646        .await;
 9647    let apply_additional_edits = cx.update_editor(|editor, cx| {
 9648        editor
 9649            .confirm_completion(&ConfirmCompletion::default(), cx)
 9650            .unwrap()
 9651    });
 9652    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 9653
 9654    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 9655        let task_completion_item = completion_item.clone();
 9656        async move { Ok(task_completion_item) }
 9657    })
 9658    .next()
 9659    .await
 9660    .unwrap();
 9661    apply_additional_edits.await.unwrap();
 9662    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 9663}
 9664
 9665#[gpui::test]
 9666async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 9667    init_test(cx, |_| {});
 9668
 9669    let mut cx = EditorLspTestContext::new(
 9670        Language::new(
 9671            LanguageConfig {
 9672                matcher: LanguageMatcher {
 9673                    path_suffixes: vec!["jsx".into()],
 9674                    ..Default::default()
 9675                },
 9676                overrides: [(
 9677                    "element".into(),
 9678                    LanguageConfigOverride {
 9679                        word_characters: Override::Set(['-'].into_iter().collect()),
 9680                        ..Default::default()
 9681                    },
 9682                )]
 9683                .into_iter()
 9684                .collect(),
 9685                ..Default::default()
 9686            },
 9687            Some(tree_sitter_typescript::language_tsx()),
 9688        )
 9689        .with_override_query("(jsx_self_closing_element) @element")
 9690        .unwrap(),
 9691        lsp::ServerCapabilities {
 9692            completion_provider: Some(lsp::CompletionOptions {
 9693                trigger_characters: Some(vec![":".to_string()]),
 9694                ..Default::default()
 9695            }),
 9696            ..Default::default()
 9697        },
 9698        cx,
 9699    )
 9700    .await;
 9701
 9702    cx.lsp
 9703        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9704            Ok(Some(lsp::CompletionResponse::Array(vec![
 9705                lsp::CompletionItem {
 9706                    label: "bg-blue".into(),
 9707                    ..Default::default()
 9708                },
 9709                lsp::CompletionItem {
 9710                    label: "bg-red".into(),
 9711                    ..Default::default()
 9712                },
 9713                lsp::CompletionItem {
 9714                    label: "bg-yellow".into(),
 9715                    ..Default::default()
 9716                },
 9717            ])))
 9718        });
 9719
 9720    cx.set_state(r#"<p class="bgˇ" />"#);
 9721
 9722    // Trigger completion when typing a dash, because the dash is an extra
 9723    // word character in the 'element' scope, which contains the cursor.
 9724    cx.simulate_keystroke("-");
 9725    cx.executor().run_until_parked();
 9726    cx.update_editor(|editor, _| {
 9727        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9728            assert_eq!(
 9729                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9730                &["bg-red", "bg-blue", "bg-yellow"]
 9731            );
 9732        } else {
 9733            panic!("expected completion menu to be open");
 9734        }
 9735    });
 9736
 9737    cx.simulate_keystroke("l");
 9738    cx.executor().run_until_parked();
 9739    cx.update_editor(|editor, _| {
 9740        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9741            assert_eq!(
 9742                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9743                &["bg-blue", "bg-yellow"]
 9744            );
 9745        } else {
 9746            panic!("expected completion menu to be open");
 9747        }
 9748    });
 9749
 9750    // When filtering completions, consider the character after the '-' to
 9751    // be the start of a subword.
 9752    cx.set_state(r#"<p class="yelˇ" />"#);
 9753    cx.simulate_keystroke("l");
 9754    cx.executor().run_until_parked();
 9755    cx.update_editor(|editor, _| {
 9756        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9757            assert_eq!(
 9758                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9759                &["bg-yellow"]
 9760            );
 9761        } else {
 9762            panic!("expected completion menu to be open");
 9763        }
 9764    });
 9765}
 9766
 9767#[gpui::test]
 9768async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 9769    init_test(cx, |settings| {
 9770        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9771            FormatterList(vec![Formatter::Prettier].into()),
 9772        ))
 9773    });
 9774
 9775    let fs = FakeFs::new(cx.executor());
 9776    fs.insert_file("/file.ts", Default::default()).await;
 9777
 9778    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
 9779    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9780
 9781    language_registry.add(Arc::new(Language::new(
 9782        LanguageConfig {
 9783            name: "TypeScript".into(),
 9784            matcher: LanguageMatcher {
 9785                path_suffixes: vec!["ts".to_string()],
 9786                ..Default::default()
 9787            },
 9788            ..Default::default()
 9789        },
 9790        Some(tree_sitter_rust::language()),
 9791    )));
 9792    update_test_language_settings(cx, |settings| {
 9793        settings.defaults.prettier = Some(PrettierSettings {
 9794            allowed: true,
 9795            ..PrettierSettings::default()
 9796        });
 9797    });
 9798
 9799    let test_plugin = "test_plugin";
 9800    let _ = language_registry.register_fake_lsp_adapter(
 9801        "TypeScript",
 9802        FakeLspAdapter {
 9803            prettier_plugins: vec![test_plugin],
 9804            ..Default::default()
 9805        },
 9806    );
 9807
 9808    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 9809    let buffer = project
 9810        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
 9811        .await
 9812        .unwrap();
 9813
 9814    let buffer_text = "one\ntwo\nthree\n";
 9815    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9816    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9817    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 9818
 9819    editor
 9820        .update(cx, |editor, cx| {
 9821            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9822        })
 9823        .unwrap()
 9824        .await;
 9825    assert_eq!(
 9826        editor.update(cx, |editor, cx| editor.text(cx)),
 9827        buffer_text.to_string() + prettier_format_suffix,
 9828        "Test prettier formatting was not applied to the original buffer text",
 9829    );
 9830
 9831    update_test_language_settings(cx, |settings| {
 9832        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9833    });
 9834    let format = editor.update(cx, |editor, cx| {
 9835        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9836    });
 9837    format.await.unwrap();
 9838    assert_eq!(
 9839        editor.update(cx, |editor, cx| editor.text(cx)),
 9840        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 9841        "Autoformatting (via test prettier) was not applied to the original buffer text",
 9842    );
 9843}
 9844
 9845#[gpui::test]
 9846async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 9847    init_test(cx, |_| {});
 9848    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9849    let base_text = indoc! {r#"struct Row;
 9850struct Row1;
 9851struct Row2;
 9852
 9853struct Row4;
 9854struct Row5;
 9855struct Row6;
 9856
 9857struct Row8;
 9858struct Row9;
 9859struct Row10;"#};
 9860
 9861    // When addition hunks are not adjacent to carets, no hunk revert is performed
 9862    assert_hunk_revert(
 9863        indoc! {r#"struct Row;
 9864                   struct Row1;
 9865                   struct Row1.1;
 9866                   struct Row1.2;
 9867                   struct Row2;ˇ
 9868
 9869                   struct Row4;
 9870                   struct Row5;
 9871                   struct Row6;
 9872
 9873                   struct Row8;
 9874                   ˇstruct Row9;
 9875                   struct Row9.1;
 9876                   struct Row9.2;
 9877                   struct Row9.3;
 9878                   struct Row10;"#},
 9879        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9880        indoc! {r#"struct Row;
 9881                   struct Row1;
 9882                   struct Row1.1;
 9883                   struct Row1.2;
 9884                   struct Row2;ˇ
 9885
 9886                   struct Row4;
 9887                   struct Row5;
 9888                   struct Row6;
 9889
 9890                   struct Row8;
 9891                   ˇstruct Row9;
 9892                   struct Row9.1;
 9893                   struct Row9.2;
 9894                   struct Row9.3;
 9895                   struct Row10;"#},
 9896        base_text,
 9897        &mut cx,
 9898    );
 9899    // Same for selections
 9900    assert_hunk_revert(
 9901        indoc! {r#"struct Row;
 9902                   struct Row1;
 9903                   struct Row2;
 9904                   struct Row2.1;
 9905                   struct Row2.2;
 9906                   «ˇ
 9907                   struct Row4;
 9908                   struct» Row5;
 9909                   «struct Row6;
 9910                   ˇ»
 9911                   struct Row9.1;
 9912                   struct Row9.2;
 9913                   struct Row9.3;
 9914                   struct Row8;
 9915                   struct Row9;
 9916                   struct Row10;"#},
 9917        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9918        indoc! {r#"struct Row;
 9919                   struct Row1;
 9920                   struct Row2;
 9921                   struct Row2.1;
 9922                   struct Row2.2;
 9923                   «ˇ
 9924                   struct Row4;
 9925                   struct» Row5;
 9926                   «struct Row6;
 9927                   ˇ»
 9928                   struct Row9.1;
 9929                   struct Row9.2;
 9930                   struct Row9.3;
 9931                   struct Row8;
 9932                   struct Row9;
 9933                   struct Row10;"#},
 9934        base_text,
 9935        &mut cx,
 9936    );
 9937
 9938    // When carets and selections intersect the addition hunks, those are reverted.
 9939    // Adjacent carets got merged.
 9940    assert_hunk_revert(
 9941        indoc! {r#"struct Row;
 9942                   ˇ// something on the top
 9943                   struct Row1;
 9944                   struct Row2;
 9945                   struct Roˇw3.1;
 9946                   struct Row2.2;
 9947                   struct Row2.3;ˇ
 9948
 9949                   struct Row4;
 9950                   struct ˇRow5.1;
 9951                   struct Row5.2;
 9952                   struct «Rowˇ»5.3;
 9953                   struct Row5;
 9954                   struct Row6;
 9955                   ˇ
 9956                   struct Row9.1;
 9957                   struct «Rowˇ»9.2;
 9958                   struct «ˇRow»9.3;
 9959                   struct Row8;
 9960                   struct Row9;
 9961                   «ˇ// something on bottom»
 9962                   struct Row10;"#},
 9963        vec![
 9964            DiffHunkStatus::Added,
 9965            DiffHunkStatus::Added,
 9966            DiffHunkStatus::Added,
 9967            DiffHunkStatus::Added,
 9968            DiffHunkStatus::Added,
 9969        ],
 9970        indoc! {r#"struct Row;
 9971                   ˇstruct Row1;
 9972                   struct Row2;
 9973                   ˇ
 9974                   struct Row4;
 9975                   ˇstruct Row5;
 9976                   struct Row6;
 9977                   ˇ
 9978                   ˇstruct Row8;
 9979                   struct Row9;
 9980                   ˇstruct Row10;"#},
 9981        base_text,
 9982        &mut cx,
 9983    );
 9984}
 9985
 9986#[gpui::test]
 9987async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
 9988    init_test(cx, |_| {});
 9989    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9990    let base_text = indoc! {r#"struct Row;
 9991struct Row1;
 9992struct Row2;
 9993
 9994struct Row4;
 9995struct Row5;
 9996struct Row6;
 9997
 9998struct Row8;
 9999struct Row9;
10000struct Row10;"#};
10001
10002    // Modification hunks behave the same as the addition ones.
10003    assert_hunk_revert(
10004        indoc! {r#"struct Row;
10005                   struct Row1;
10006                   struct Row33;
10007                   ˇ
10008                   struct Row4;
10009                   struct Row5;
10010                   struct Row6;
10011                   ˇ
10012                   struct Row99;
10013                   struct Row9;
10014                   struct Row10;"#},
10015        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10016        indoc! {r#"struct Row;
10017                   struct Row1;
10018                   struct Row33;
10019                   ˇ
10020                   struct Row4;
10021                   struct Row5;
10022                   struct Row6;
10023                   ˇ
10024                   struct Row99;
10025                   struct Row9;
10026                   struct Row10;"#},
10027        base_text,
10028        &mut cx,
10029    );
10030    assert_hunk_revert(
10031        indoc! {r#"struct Row;
10032                   struct Row1;
10033                   struct Row33;
10034                   «ˇ
10035                   struct Row4;
10036                   struct» Row5;
10037                   «struct Row6;
10038                   ˇ»
10039                   struct Row99;
10040                   struct Row9;
10041                   struct Row10;"#},
10042        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10043        indoc! {r#"struct Row;
10044                   struct Row1;
10045                   struct Row33;
10046                   «ˇ
10047                   struct Row4;
10048                   struct» Row5;
10049                   «struct Row6;
10050                   ˇ»
10051                   struct Row99;
10052                   struct Row9;
10053                   struct Row10;"#},
10054        base_text,
10055        &mut cx,
10056    );
10057
10058    assert_hunk_revert(
10059        indoc! {r#"ˇstruct Row1.1;
10060                   struct Row1;
10061                   «ˇstr»uct Row22;
10062
10063                   struct ˇRow44;
10064                   struct Row5;
10065                   struct «Rˇ»ow66;ˇ
10066
10067                   «struˇ»ct Row88;
10068                   struct Row9;
10069                   struct Row1011;ˇ"#},
10070        vec![
10071            DiffHunkStatus::Modified,
10072            DiffHunkStatus::Modified,
10073            DiffHunkStatus::Modified,
10074            DiffHunkStatus::Modified,
10075            DiffHunkStatus::Modified,
10076            DiffHunkStatus::Modified,
10077        ],
10078        indoc! {r#"struct Row;
10079                   ˇstruct Row1;
10080                   struct Row2;
10081                   ˇ
10082                   struct Row4;
10083                   ˇstruct Row5;
10084                   struct Row6;
10085                   ˇ
10086                   struct Row8;
10087                   ˇstruct Row9;
10088                   struct Row10;ˇ"#},
10089        base_text,
10090        &mut cx,
10091    );
10092}
10093
10094#[gpui::test]
10095async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10096    init_test(cx, |_| {});
10097    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10098    let base_text = indoc! {r#"struct Row;
10099struct Row1;
10100struct Row2;
10101
10102struct Row4;
10103struct Row5;
10104struct Row6;
10105
10106struct Row8;
10107struct Row9;
10108struct Row10;"#};
10109
10110    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10111    assert_hunk_revert(
10112        indoc! {r#"struct Row;
10113                   struct Row2;
10114
10115                   ˇstruct Row4;
10116                   struct Row5;
10117                   struct Row6;
10118                   ˇ
10119                   struct Row8;
10120                   struct Row10;"#},
10121        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10122        indoc! {r#"struct Row;
10123                   struct Row2;
10124
10125                   ˇstruct Row4;
10126                   struct Row5;
10127                   struct Row6;
10128                   ˇ
10129                   struct Row8;
10130                   struct Row10;"#},
10131        base_text,
10132        &mut cx,
10133    );
10134    assert_hunk_revert(
10135        indoc! {r#"struct Row;
10136                   struct Row2;
10137
10138                   «ˇstruct Row4;
10139                   struct» Row5;
10140                   «struct Row6;
10141                   ˇ»
10142                   struct Row8;
10143                   struct Row10;"#},
10144        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10145        indoc! {r#"struct Row;
10146                   struct Row2;
10147
10148                   «ˇstruct Row4;
10149                   struct» Row5;
10150                   «struct Row6;
10151                   ˇ»
10152                   struct Row8;
10153                   struct Row10;"#},
10154        base_text,
10155        &mut cx,
10156    );
10157
10158    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10159    assert_hunk_revert(
10160        indoc! {r#"struct Row;
10161                   ˇstruct Row2;
10162
10163                   struct Row4;
10164                   struct Row5;
10165                   struct Row6;
10166
10167                   struct Row8;ˇ
10168                   struct Row10;"#},
10169        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10170        indoc! {r#"struct Row;
10171                   struct Row1;
10172                   ˇstruct Row2;
10173
10174                   struct Row4;
10175                   struct Row5;
10176                   struct Row6;
10177
10178                   struct Row8;ˇ
10179                   struct Row9;
10180                   struct Row10;"#},
10181        base_text,
10182        &mut cx,
10183    );
10184    assert_hunk_revert(
10185        indoc! {r#"struct Row;
10186                   struct Row2«ˇ;
10187                   struct Row4;
10188                   struct» Row5;
10189                   «struct Row6;
10190
10191                   struct Row8;ˇ»
10192                   struct Row10;"#},
10193        vec![
10194            DiffHunkStatus::Removed,
10195            DiffHunkStatus::Removed,
10196            DiffHunkStatus::Removed,
10197        ],
10198        indoc! {r#"struct Row;
10199                   struct Row1;
10200                   struct Row2«ˇ;
10201
10202                   struct Row4;
10203                   struct» Row5;
10204                   «struct Row6;
10205
10206                   struct Row8;ˇ»
10207                   struct Row9;
10208                   struct Row10;"#},
10209        base_text,
10210        &mut cx,
10211    );
10212}
10213
10214#[gpui::test]
10215async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10216    init_test(cx, |_| {});
10217
10218    let cols = 4;
10219    let rows = 10;
10220    let sample_text_1 = sample_text(rows, cols, 'a');
10221    assert_eq!(
10222        sample_text_1,
10223        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10224    );
10225    let sample_text_2 = sample_text(rows, cols, 'l');
10226    assert_eq!(
10227        sample_text_2,
10228        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10229    );
10230    let sample_text_3 = sample_text(rows, cols, 'v');
10231    assert_eq!(
10232        sample_text_3,
10233        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10234    );
10235
10236    fn diff_every_buffer_row(
10237        buffer: &Model<Buffer>,
10238        sample_text: String,
10239        cols: usize,
10240        cx: &mut gpui::TestAppContext,
10241    ) {
10242        // revert first character in each row, creating one large diff hunk per buffer
10243        let is_first_char = |offset: usize| offset % cols == 0;
10244        buffer.update(cx, |buffer, cx| {
10245            buffer.set_text(
10246                sample_text
10247                    .chars()
10248                    .enumerate()
10249                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10250                    .collect::<String>(),
10251                cx,
10252            );
10253            buffer.set_diff_base(Some(sample_text), cx);
10254        });
10255        cx.executor().run_until_parked();
10256    }
10257
10258    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10259    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10260
10261    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10262    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10263
10264    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10265    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10266
10267    let multibuffer = cx.new_model(|cx| {
10268        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10269        multibuffer.push_excerpts(
10270            buffer_1.clone(),
10271            [
10272                ExcerptRange {
10273                    context: Point::new(0, 0)..Point::new(3, 0),
10274                    primary: None,
10275                },
10276                ExcerptRange {
10277                    context: Point::new(5, 0)..Point::new(7, 0),
10278                    primary: None,
10279                },
10280                ExcerptRange {
10281                    context: Point::new(9, 0)..Point::new(10, 4),
10282                    primary: None,
10283                },
10284            ],
10285            cx,
10286        );
10287        multibuffer.push_excerpts(
10288            buffer_2.clone(),
10289            [
10290                ExcerptRange {
10291                    context: Point::new(0, 0)..Point::new(3, 0),
10292                    primary: None,
10293                },
10294                ExcerptRange {
10295                    context: Point::new(5, 0)..Point::new(7, 0),
10296                    primary: None,
10297                },
10298                ExcerptRange {
10299                    context: Point::new(9, 0)..Point::new(10, 4),
10300                    primary: None,
10301                },
10302            ],
10303            cx,
10304        );
10305        multibuffer.push_excerpts(
10306            buffer_3.clone(),
10307            [
10308                ExcerptRange {
10309                    context: Point::new(0, 0)..Point::new(3, 0),
10310                    primary: None,
10311                },
10312                ExcerptRange {
10313                    context: Point::new(5, 0)..Point::new(7, 0),
10314                    primary: None,
10315                },
10316                ExcerptRange {
10317                    context: Point::new(9, 0)..Point::new(10, 4),
10318                    primary: None,
10319                },
10320            ],
10321            cx,
10322        );
10323        multibuffer
10324    });
10325
10326    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10327    editor.update(cx, |editor, cx| {
10328        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");
10329        editor.select_all(&SelectAll, cx);
10330        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10331    });
10332    cx.executor().run_until_parked();
10333    // When all ranges are selected, all buffer hunks are reverted.
10334    editor.update(cx, |editor, cx| {
10335        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");
10336    });
10337    buffer_1.update(cx, |buffer, _| {
10338        assert_eq!(buffer.text(), sample_text_1);
10339    });
10340    buffer_2.update(cx, |buffer, _| {
10341        assert_eq!(buffer.text(), sample_text_2);
10342    });
10343    buffer_3.update(cx, |buffer, _| {
10344        assert_eq!(buffer.text(), sample_text_3);
10345    });
10346
10347    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10348    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10349    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10350    editor.update(cx, |editor, cx| {
10351        editor.change_selections(None, cx, |s| {
10352            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10353        });
10354        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10355    });
10356    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10357    // but not affect buffer_2 and its related excerpts.
10358    editor.update(cx, |editor, cx| {
10359        assert_eq!(
10360            editor.text(cx),
10361            "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"
10362        );
10363    });
10364    buffer_1.update(cx, |buffer, _| {
10365        assert_eq!(buffer.text(), sample_text_1);
10366    });
10367    buffer_2.update(cx, |buffer, _| {
10368        assert_eq!(
10369            buffer.text(),
10370            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10371        );
10372    });
10373    buffer_3.update(cx, |buffer, _| {
10374        assert_eq!(
10375            buffer.text(),
10376            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10377        );
10378    });
10379}
10380
10381#[gpui::test]
10382async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10383    init_test(cx, |_| {});
10384
10385    let cols = 4;
10386    let rows = 10;
10387    let sample_text_1 = sample_text(rows, cols, 'a');
10388    assert_eq!(
10389        sample_text_1,
10390        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10391    );
10392    let sample_text_2 = sample_text(rows, cols, 'l');
10393    assert_eq!(
10394        sample_text_2,
10395        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10396    );
10397    let sample_text_3 = sample_text(rows, cols, 'v');
10398    assert_eq!(
10399        sample_text_3,
10400        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10401    );
10402
10403    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10404    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10405    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10406
10407    let multi_buffer = cx.new_model(|cx| {
10408        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10409        multibuffer.push_excerpts(
10410            buffer_1.clone(),
10411            [
10412                ExcerptRange {
10413                    context: Point::new(0, 0)..Point::new(3, 0),
10414                    primary: None,
10415                },
10416                ExcerptRange {
10417                    context: Point::new(5, 0)..Point::new(7, 0),
10418                    primary: None,
10419                },
10420                ExcerptRange {
10421                    context: Point::new(9, 0)..Point::new(10, 4),
10422                    primary: None,
10423                },
10424            ],
10425            cx,
10426        );
10427        multibuffer.push_excerpts(
10428            buffer_2.clone(),
10429            [
10430                ExcerptRange {
10431                    context: Point::new(0, 0)..Point::new(3, 0),
10432                    primary: None,
10433                },
10434                ExcerptRange {
10435                    context: Point::new(5, 0)..Point::new(7, 0),
10436                    primary: None,
10437                },
10438                ExcerptRange {
10439                    context: Point::new(9, 0)..Point::new(10, 4),
10440                    primary: None,
10441                },
10442            ],
10443            cx,
10444        );
10445        multibuffer.push_excerpts(
10446            buffer_3.clone(),
10447            [
10448                ExcerptRange {
10449                    context: Point::new(0, 0)..Point::new(3, 0),
10450                    primary: None,
10451                },
10452                ExcerptRange {
10453                    context: Point::new(5, 0)..Point::new(7, 0),
10454                    primary: None,
10455                },
10456                ExcerptRange {
10457                    context: Point::new(9, 0)..Point::new(10, 4),
10458                    primary: None,
10459                },
10460            ],
10461            cx,
10462        );
10463        multibuffer
10464    });
10465
10466    let fs = FakeFs::new(cx.executor());
10467    fs.insert_tree(
10468        "/a",
10469        json!({
10470            "main.rs": sample_text_1,
10471            "other.rs": sample_text_2,
10472            "lib.rs": sample_text_3,
10473        }),
10474    )
10475    .await;
10476    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10477    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10478    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10479    let multi_buffer_editor = cx.new_view(|cx| {
10480        Editor::new(
10481            EditorMode::Full,
10482            multi_buffer,
10483            Some(project.clone()),
10484            true,
10485            cx,
10486        )
10487    });
10488    let multibuffer_item_id = workspace
10489        .update(cx, |workspace, cx| {
10490            assert!(
10491                workspace.active_item(cx).is_none(),
10492                "active item should be None before the first item is added"
10493            );
10494            workspace.add_item_to_active_pane(
10495                Box::new(multi_buffer_editor.clone()),
10496                None,
10497                true,
10498                cx,
10499            );
10500            let active_item = workspace
10501                .active_item(cx)
10502                .expect("should have an active item after adding the multi buffer");
10503            assert!(
10504                !active_item.is_singleton(cx),
10505                "A multi buffer was expected to active after adding"
10506            );
10507            active_item.item_id()
10508        })
10509        .unwrap();
10510    cx.executor().run_until_parked();
10511
10512    multi_buffer_editor.update(cx, |editor, cx| {
10513        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
10514        editor.open_excerpts(&OpenExcerpts, cx);
10515    });
10516    cx.executor().run_until_parked();
10517    let first_item_id = workspace
10518        .update(cx, |workspace, cx| {
10519            let active_item = workspace
10520                .active_item(cx)
10521                .expect("should have an active item after navigating into the 1st buffer");
10522            let first_item_id = active_item.item_id();
10523            assert_ne!(
10524                first_item_id, multibuffer_item_id,
10525                "Should navigate into the 1st buffer and activate it"
10526            );
10527            assert!(
10528                active_item.is_singleton(cx),
10529                "New active item should be a singleton buffer"
10530            );
10531            assert_eq!(
10532                active_item
10533                    .act_as::<Editor>(cx)
10534                    .expect("should have navigated into an editor for the 1st buffer")
10535                    .read(cx)
10536                    .text(cx),
10537                sample_text_1
10538            );
10539
10540            workspace
10541                .go_back(workspace.active_pane().downgrade(), cx)
10542                .detach_and_log_err(cx);
10543
10544            first_item_id
10545        })
10546        .unwrap();
10547    cx.executor().run_until_parked();
10548    workspace
10549        .update(cx, |workspace, cx| {
10550            let active_item = workspace
10551                .active_item(cx)
10552                .expect("should have an active item after navigating back");
10553            assert_eq!(
10554                active_item.item_id(),
10555                multibuffer_item_id,
10556                "Should navigate back to the multi buffer"
10557            );
10558            assert!(!active_item.is_singleton(cx));
10559        })
10560        .unwrap();
10561
10562    multi_buffer_editor.update(cx, |editor, cx| {
10563        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10564            s.select_ranges(Some(39..40))
10565        });
10566        editor.open_excerpts(&OpenExcerpts, cx);
10567    });
10568    cx.executor().run_until_parked();
10569    let second_item_id = workspace
10570        .update(cx, |workspace, cx| {
10571            let active_item = workspace
10572                .active_item(cx)
10573                .expect("should have an active item after navigating into the 2nd buffer");
10574            let second_item_id = active_item.item_id();
10575            assert_ne!(
10576                second_item_id, multibuffer_item_id,
10577                "Should navigate away from the multibuffer"
10578            );
10579            assert_ne!(
10580                second_item_id, first_item_id,
10581                "Should navigate into the 2nd buffer and activate it"
10582            );
10583            assert!(
10584                active_item.is_singleton(cx),
10585                "New active item should be a singleton buffer"
10586            );
10587            assert_eq!(
10588                active_item
10589                    .act_as::<Editor>(cx)
10590                    .expect("should have navigated into an editor")
10591                    .read(cx)
10592                    .text(cx),
10593                sample_text_2
10594            );
10595
10596            workspace
10597                .go_back(workspace.active_pane().downgrade(), cx)
10598                .detach_and_log_err(cx);
10599
10600            second_item_id
10601        })
10602        .unwrap();
10603    cx.executor().run_until_parked();
10604    workspace
10605        .update(cx, |workspace, cx| {
10606            let active_item = workspace
10607                .active_item(cx)
10608                .expect("should have an active item after navigating back from the 2nd buffer");
10609            assert_eq!(
10610                active_item.item_id(),
10611                multibuffer_item_id,
10612                "Should navigate back from the 2nd buffer to the multi buffer"
10613            );
10614            assert!(!active_item.is_singleton(cx));
10615        })
10616        .unwrap();
10617
10618    multi_buffer_editor.update(cx, |editor, cx| {
10619        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10620            s.select_ranges(Some(60..70))
10621        });
10622        editor.open_excerpts(&OpenExcerpts, cx);
10623    });
10624    cx.executor().run_until_parked();
10625    workspace
10626        .update(cx, |workspace, cx| {
10627            let active_item = workspace
10628                .active_item(cx)
10629                .expect("should have an active item after navigating into the 3rd buffer");
10630            let third_item_id = active_item.item_id();
10631            assert_ne!(
10632                third_item_id, multibuffer_item_id,
10633                "Should navigate into the 3rd buffer and activate it"
10634            );
10635            assert_ne!(third_item_id, first_item_id);
10636            assert_ne!(third_item_id, second_item_id);
10637            assert!(
10638                active_item.is_singleton(cx),
10639                "New active item should be a singleton buffer"
10640            );
10641            assert_eq!(
10642                active_item
10643                    .act_as::<Editor>(cx)
10644                    .expect("should have navigated into an editor")
10645                    .read(cx)
10646                    .text(cx),
10647                sample_text_3
10648            );
10649
10650            workspace
10651                .go_back(workspace.active_pane().downgrade(), cx)
10652                .detach_and_log_err(cx);
10653        })
10654        .unwrap();
10655    cx.executor().run_until_parked();
10656    workspace
10657        .update(cx, |workspace, cx| {
10658            let active_item = workspace
10659                .active_item(cx)
10660                .expect("should have an active item after navigating back from the 3rd buffer");
10661            assert_eq!(
10662                active_item.item_id(),
10663                multibuffer_item_id,
10664                "Should navigate back from the 3rd buffer to the multi buffer"
10665            );
10666            assert!(!active_item.is_singleton(cx));
10667        })
10668        .unwrap();
10669}
10670
10671#[gpui::test]
10672async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10673    init_test(cx, |_| {});
10674
10675    let mut cx = EditorTestContext::new(cx).await;
10676
10677    let diff_base = r#"
10678        use some::mod;
10679
10680        const A: u32 = 42;
10681
10682        fn main() {
10683            println!("hello");
10684
10685            println!("world");
10686        }
10687        "#
10688    .unindent();
10689
10690    cx.set_state(
10691        &r#"
10692        use some::modified;
10693
10694        ˇ
10695        fn main() {
10696            println!("hello there");
10697
10698            println!("around the");
10699            println!("world");
10700        }
10701        "#
10702        .unindent(),
10703    );
10704
10705    cx.set_diff_base(Some(&diff_base));
10706    executor.run_until_parked();
10707    let unexpanded_hunks = vec![
10708        (
10709            "use some::mod;\n".to_string(),
10710            DiffHunkStatus::Modified,
10711            DisplayRow(0)..DisplayRow(1),
10712        ),
10713        (
10714            "const A: u32 = 42;\n".to_string(),
10715            DiffHunkStatus::Removed,
10716            DisplayRow(2)..DisplayRow(2),
10717        ),
10718        (
10719            "    println!(\"hello\");\n".to_string(),
10720            DiffHunkStatus::Modified,
10721            DisplayRow(4)..DisplayRow(5),
10722        ),
10723        (
10724            "".to_string(),
10725            DiffHunkStatus::Added,
10726            DisplayRow(6)..DisplayRow(7),
10727        ),
10728    ];
10729    cx.update_editor(|editor, cx| {
10730        let snapshot = editor.snapshot(cx);
10731        let all_hunks = editor_hunks(editor, &snapshot, cx);
10732        assert_eq!(all_hunks, unexpanded_hunks);
10733    });
10734
10735    cx.update_editor(|editor, cx| {
10736        for _ in 0..4 {
10737            editor.go_to_hunk(&GoToHunk, cx);
10738            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10739        }
10740    });
10741    executor.run_until_parked();
10742    cx.assert_editor_state(
10743        &r#"
10744        use some::modified;
10745
10746        ˇ
10747        fn main() {
10748            println!("hello there");
10749
10750            println!("around the");
10751            println!("world");
10752        }
10753        "#
10754        .unindent(),
10755    );
10756    cx.update_editor(|editor, cx| {
10757        let snapshot = editor.snapshot(cx);
10758        let all_hunks = editor_hunks(editor, &snapshot, cx);
10759        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10760        assert_eq!(
10761            expanded_hunks_background_highlights(editor, cx),
10762            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
10763            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10764        );
10765        assert_eq!(
10766            all_hunks,
10767            vec![
10768                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
10769                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
10770                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
10771                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
10772            ],
10773            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10774            (from modified and removed hunks)"
10775        );
10776        assert_eq!(
10777            all_hunks, all_expanded_hunks,
10778            "Editor hunks should not change and all be expanded"
10779        );
10780    });
10781
10782    cx.update_editor(|editor, cx| {
10783        editor.cancel(&Cancel, cx);
10784
10785        let snapshot = editor.snapshot(cx);
10786        let all_hunks = editor_hunks(editor, &snapshot, cx);
10787        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10788        assert_eq!(
10789            expanded_hunks_background_highlights(editor, cx),
10790            Vec::new(),
10791            "After cancelling in editor, no git highlights should be left"
10792        );
10793        assert_eq!(
10794            all_expanded_hunks,
10795            Vec::new(),
10796            "After cancelling in editor, no hunks should be expanded"
10797        );
10798        assert_eq!(
10799            all_hunks, unexpanded_hunks,
10800            "After cancelling in editor, regular hunks' coordinates should get back to normal"
10801        );
10802    });
10803}
10804
10805#[gpui::test]
10806async fn test_toggled_diff_base_change(
10807    executor: BackgroundExecutor,
10808    cx: &mut gpui::TestAppContext,
10809) {
10810    init_test(cx, |_| {});
10811
10812    let mut cx = EditorTestContext::new(cx).await;
10813
10814    let diff_base = r#"
10815        use some::mod1;
10816        use some::mod2;
10817
10818        const A: u32 = 42;
10819        const B: u32 = 42;
10820        const C: u32 = 42;
10821
10822        fn main(ˇ) {
10823            println!("hello");
10824
10825            println!("world");
10826        }
10827        "#
10828    .unindent();
10829
10830    cx.set_state(
10831        &r#"
10832        use some::mod2;
10833
10834        const A: u32 = 42;
10835        const C: u32 = 42;
10836
10837        fn main(ˇ) {
10838            //println!("hello");
10839
10840            println!("world");
10841            //
10842            //
10843        }
10844        "#
10845        .unindent(),
10846    );
10847
10848    cx.set_diff_base(Some(&diff_base));
10849    executor.run_until_parked();
10850    cx.update_editor(|editor, cx| {
10851        let snapshot = editor.snapshot(cx);
10852        let all_hunks = editor_hunks(editor, &snapshot, cx);
10853        assert_eq!(
10854            all_hunks,
10855            vec![
10856                (
10857                    "use some::mod1;\n".to_string(),
10858                    DiffHunkStatus::Removed,
10859                    DisplayRow(0)..DisplayRow(0)
10860                ),
10861                (
10862                    "const B: u32 = 42;\n".to_string(),
10863                    DiffHunkStatus::Removed,
10864                    DisplayRow(3)..DisplayRow(3)
10865                ),
10866                (
10867                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10868                    DiffHunkStatus::Modified,
10869                    DisplayRow(5)..DisplayRow(7)
10870                ),
10871                (
10872                    "".to_string(),
10873                    DiffHunkStatus::Added,
10874                    DisplayRow(9)..DisplayRow(11)
10875                ),
10876            ]
10877        );
10878    });
10879
10880    cx.update_editor(|editor, cx| {
10881        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10882    });
10883    executor.run_until_parked();
10884    cx.assert_editor_state(
10885        &r#"
10886        use some::mod2;
10887
10888        const A: u32 = 42;
10889        const C: u32 = 42;
10890
10891        fn main(ˇ) {
10892            //println!("hello");
10893
10894            println!("world");
10895            //
10896            //
10897        }
10898        "#
10899        .unindent(),
10900    );
10901    cx.update_editor(|editor, cx| {
10902        let snapshot = editor.snapshot(cx);
10903        let all_hunks = editor_hunks(editor, &snapshot, cx);
10904        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10905        assert_eq!(
10906            expanded_hunks_background_highlights(editor, cx),
10907            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
10908            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10909        );
10910        assert_eq!(
10911            all_hunks,
10912            vec![
10913                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
10914                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
10915                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
10916                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
10917            ],
10918            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10919            (from modified and removed hunks)"
10920        );
10921        assert_eq!(
10922            all_hunks, all_expanded_hunks,
10923            "Editor hunks should not change and all be expanded"
10924        );
10925    });
10926
10927    cx.set_diff_base(Some("new diff base!"));
10928    executor.run_until_parked();
10929
10930    cx.update_editor(|editor, cx| {
10931        let snapshot = editor.snapshot(cx);
10932        let all_hunks = editor_hunks(editor, &snapshot, cx);
10933        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10934        assert_eq!(
10935            expanded_hunks_background_highlights(editor, cx),
10936            Vec::new(),
10937            "After diff base is changed, old git highlights should be removed"
10938        );
10939        assert_eq!(
10940            all_expanded_hunks,
10941            Vec::new(),
10942            "After diff base is changed, old git hunk expansions should be removed"
10943        );
10944        assert_eq!(
10945            all_hunks,
10946            vec![(
10947                "new diff base!".to_string(),
10948                DiffHunkStatus::Modified,
10949                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
10950            )],
10951            "After diff base is changed, hunks should update"
10952        );
10953    });
10954}
10955
10956#[gpui::test]
10957async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10958    init_test(cx, |_| {});
10959
10960    let mut cx = EditorTestContext::new(cx).await;
10961
10962    let diff_base = r#"
10963        use some::mod1;
10964        use some::mod2;
10965
10966        const A: u32 = 42;
10967        const B: u32 = 42;
10968        const C: u32 = 42;
10969
10970        fn main(ˇ) {
10971            println!("hello");
10972
10973            println!("world");
10974        }
10975
10976        fn another() {
10977            println!("another");
10978        }
10979
10980        fn another2() {
10981            println!("another2");
10982        }
10983        "#
10984    .unindent();
10985
10986    cx.set_state(
10987        &r#"
10988        «use some::mod2;
10989
10990        const A: u32 = 42;
10991        const C: u32 = 42;
10992
10993        fn main() {
10994            //println!("hello");
10995
10996            println!("world");
10997            //
10998            //ˇ»
10999        }
11000
11001        fn another() {
11002            println!("another");
11003            println!("another");
11004        }
11005
11006            println!("another2");
11007        }
11008        "#
11009        .unindent(),
11010    );
11011
11012    cx.set_diff_base(Some(&diff_base));
11013    executor.run_until_parked();
11014    cx.update_editor(|editor, cx| {
11015        let snapshot = editor.snapshot(cx);
11016        let all_hunks = editor_hunks(editor, &snapshot, cx);
11017        assert_eq!(
11018            all_hunks,
11019            vec![
11020                (
11021                    "use some::mod1;\n".to_string(),
11022                    DiffHunkStatus::Removed,
11023                    DisplayRow(0)..DisplayRow(0)
11024                ),
11025                (
11026                    "const B: u32 = 42;\n".to_string(),
11027                    DiffHunkStatus::Removed,
11028                    DisplayRow(3)..DisplayRow(3)
11029                ),
11030                (
11031                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11032                    DiffHunkStatus::Modified,
11033                    DisplayRow(5)..DisplayRow(7)
11034                ),
11035                (
11036                    "".to_string(),
11037                    DiffHunkStatus::Added,
11038                    DisplayRow(9)..DisplayRow(11)
11039                ),
11040                (
11041                    "".to_string(),
11042                    DiffHunkStatus::Added,
11043                    DisplayRow(15)..DisplayRow(16)
11044                ),
11045                (
11046                    "fn another2() {\n".to_string(),
11047                    DiffHunkStatus::Removed,
11048                    DisplayRow(18)..DisplayRow(18)
11049                ),
11050            ]
11051        );
11052    });
11053
11054    cx.update_editor(|editor, cx| {
11055        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11056    });
11057    executor.run_until_parked();
11058    cx.assert_editor_state(
11059        &r#"
11060        «use some::mod2;
11061
11062        const A: u32 = 42;
11063        const C: u32 = 42;
11064
11065        fn main() {
11066            //println!("hello");
11067
11068            println!("world");
11069            //
11070            //ˇ»
11071        }
11072
11073        fn another() {
11074            println!("another");
11075            println!("another");
11076        }
11077
11078            println!("another2");
11079        }
11080        "#
11081        .unindent(),
11082    );
11083    cx.update_editor(|editor, cx| {
11084        let snapshot = editor.snapshot(cx);
11085        let all_hunks = editor_hunks(editor, &snapshot, cx);
11086        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11087        assert_eq!(
11088            expanded_hunks_background_highlights(editor, cx),
11089            vec![
11090                DisplayRow(9)..=DisplayRow(10),
11091                DisplayRow(13)..=DisplayRow(14),
11092                DisplayRow(19)..=DisplayRow(19)
11093            ]
11094        );
11095        assert_eq!(
11096            all_hunks,
11097            vec![
11098                (
11099                    "use some::mod1;\n".to_string(),
11100                    DiffHunkStatus::Removed,
11101                    DisplayRow(1)..DisplayRow(1)
11102                ),
11103                (
11104                    "const B: u32 = 42;\n".to_string(),
11105                    DiffHunkStatus::Removed,
11106                    DisplayRow(5)..DisplayRow(5)
11107                ),
11108                (
11109                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11110                    DiffHunkStatus::Modified,
11111                    DisplayRow(9)..DisplayRow(11)
11112                ),
11113                (
11114                    "".to_string(),
11115                    DiffHunkStatus::Added,
11116                    DisplayRow(13)..DisplayRow(15)
11117                ),
11118                (
11119                    "".to_string(),
11120                    DiffHunkStatus::Added,
11121                    DisplayRow(19)..DisplayRow(20)
11122                ),
11123                (
11124                    "fn another2() {\n".to_string(),
11125                    DiffHunkStatus::Removed,
11126                    DisplayRow(23)..DisplayRow(23)
11127                ),
11128            ],
11129        );
11130        assert_eq!(all_hunks, all_expanded_hunks);
11131    });
11132
11133    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11134    cx.executor().run_until_parked();
11135    cx.assert_editor_state(
11136        &r#"
11137        «use some::mod2;
11138
11139        const A: u32 = 42;
11140        const C: u32 = 42;
11141
11142        fn main() {
11143            //println!("hello");
11144
11145            println!("world");
11146            //
11147            //ˇ»
11148        }
11149
11150        fn another() {
11151            println!("another");
11152            println!("another");
11153        }
11154
11155            println!("another2");
11156        }
11157        "#
11158        .unindent(),
11159    );
11160    cx.update_editor(|editor, cx| {
11161        let snapshot = editor.snapshot(cx);
11162        let all_hunks = editor_hunks(editor, &snapshot, cx);
11163        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11164        assert_eq!(
11165            expanded_hunks_background_highlights(editor, cx),
11166            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
11167            "Only one hunk is left not folded, its highlight should be visible"
11168        );
11169        assert_eq!(
11170            all_hunks,
11171            vec![
11172                (
11173                    "use some::mod1;\n".to_string(),
11174                    DiffHunkStatus::Removed,
11175                    DisplayRow(0)..DisplayRow(0)
11176                ),
11177                (
11178                    "const B: u32 = 42;\n".to_string(),
11179                    DiffHunkStatus::Removed,
11180                    DisplayRow(0)..DisplayRow(0)
11181                ),
11182                (
11183                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11184                    DiffHunkStatus::Modified,
11185                    DisplayRow(0)..DisplayRow(0)
11186                ),
11187                (
11188                    "".to_string(),
11189                    DiffHunkStatus::Added,
11190                    DisplayRow(0)..DisplayRow(1)
11191                ),
11192                (
11193                    "".to_string(),
11194                    DiffHunkStatus::Added,
11195                    DisplayRow(5)..DisplayRow(6)
11196                ),
11197                (
11198                    "fn another2() {\n".to_string(),
11199                    DiffHunkStatus::Removed,
11200                    DisplayRow(9)..DisplayRow(9)
11201                ),
11202            ],
11203            "Hunk list should still return shifted folded hunks"
11204        );
11205        assert_eq!(
11206            all_expanded_hunks,
11207            vec![
11208                (
11209                    "".to_string(),
11210                    DiffHunkStatus::Added,
11211                    DisplayRow(5)..DisplayRow(6)
11212                ),
11213                (
11214                    "fn another2() {\n".to_string(),
11215                    DiffHunkStatus::Removed,
11216                    DisplayRow(9)..DisplayRow(9)
11217                ),
11218            ],
11219            "Only non-folded hunks should be left expanded"
11220        );
11221    });
11222
11223    cx.update_editor(|editor, cx| {
11224        editor.select_all(&SelectAll, cx);
11225        editor.unfold_lines(&UnfoldLines, cx);
11226    });
11227    cx.executor().run_until_parked();
11228    cx.assert_editor_state(
11229        &r#"
11230        «use some::mod2;
11231
11232        const A: u32 = 42;
11233        const C: u32 = 42;
11234
11235        fn main() {
11236            //println!("hello");
11237
11238            println!("world");
11239            //
11240            //
11241        }
11242
11243        fn another() {
11244            println!("another");
11245            println!("another");
11246        }
11247
11248            println!("another2");
11249        }
11250        ˇ»"#
11251        .unindent(),
11252    );
11253    cx.update_editor(|editor, cx| {
11254        let snapshot = editor.snapshot(cx);
11255        let all_hunks = editor_hunks(editor, &snapshot, cx);
11256        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11257        assert_eq!(
11258            expanded_hunks_background_highlights(editor, cx),
11259            vec![
11260                DisplayRow(9)..=DisplayRow(10),
11261                DisplayRow(13)..=DisplayRow(14),
11262                DisplayRow(19)..=DisplayRow(19)
11263            ],
11264            "After unfolding, all hunk diffs should be visible again"
11265        );
11266        assert_eq!(
11267            all_hunks,
11268            vec![
11269                (
11270                    "use some::mod1;\n".to_string(),
11271                    DiffHunkStatus::Removed,
11272                    DisplayRow(1)..DisplayRow(1)
11273                ),
11274                (
11275                    "const B: u32 = 42;\n".to_string(),
11276                    DiffHunkStatus::Removed,
11277                    DisplayRow(5)..DisplayRow(5)
11278                ),
11279                (
11280                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11281                    DiffHunkStatus::Modified,
11282                    DisplayRow(9)..DisplayRow(11)
11283                ),
11284                (
11285                    "".to_string(),
11286                    DiffHunkStatus::Added,
11287                    DisplayRow(13)..DisplayRow(15)
11288                ),
11289                (
11290                    "".to_string(),
11291                    DiffHunkStatus::Added,
11292                    DisplayRow(19)..DisplayRow(20)
11293                ),
11294                (
11295                    "fn another2() {\n".to_string(),
11296                    DiffHunkStatus::Removed,
11297                    DisplayRow(23)..DisplayRow(23)
11298                ),
11299            ],
11300        );
11301        assert_eq!(all_hunks, all_expanded_hunks);
11302    });
11303}
11304
11305#[gpui::test]
11306async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11307    init_test(cx, |_| {});
11308
11309    let cols = 4;
11310    let rows = 10;
11311    let sample_text_1 = sample_text(rows, cols, 'a');
11312    assert_eq!(
11313        sample_text_1,
11314        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11315    );
11316    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11317    let sample_text_2 = sample_text(rows, cols, 'l');
11318    assert_eq!(
11319        sample_text_2,
11320        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11321    );
11322    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11323    let sample_text_3 = sample_text(rows, cols, 'v');
11324    assert_eq!(
11325        sample_text_3,
11326        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11327    );
11328    let modified_sample_text_3 =
11329        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11330    let buffer_1 = cx.new_model(|cx| {
11331        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
11332        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
11333        buffer
11334    });
11335    let buffer_2 = cx.new_model(|cx| {
11336        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
11337        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
11338        buffer
11339    });
11340    let buffer_3 = cx.new_model(|cx| {
11341        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
11342        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
11343        buffer
11344    });
11345
11346    let multi_buffer = cx.new_model(|cx| {
11347        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
11348        multibuffer.push_excerpts(
11349            buffer_1.clone(),
11350            [
11351                ExcerptRange {
11352                    context: Point::new(0, 0)..Point::new(3, 0),
11353                    primary: None,
11354                },
11355                ExcerptRange {
11356                    context: Point::new(5, 0)..Point::new(7, 0),
11357                    primary: None,
11358                },
11359                ExcerptRange {
11360                    context: Point::new(9, 0)..Point::new(10, 4),
11361                    primary: None,
11362                },
11363            ],
11364            cx,
11365        );
11366        multibuffer.push_excerpts(
11367            buffer_2.clone(),
11368            [
11369                ExcerptRange {
11370                    context: Point::new(0, 0)..Point::new(3, 0),
11371                    primary: None,
11372                },
11373                ExcerptRange {
11374                    context: Point::new(5, 0)..Point::new(7, 0),
11375                    primary: None,
11376                },
11377                ExcerptRange {
11378                    context: Point::new(9, 0)..Point::new(10, 4),
11379                    primary: None,
11380                },
11381            ],
11382            cx,
11383        );
11384        multibuffer.push_excerpts(
11385            buffer_3.clone(),
11386            [
11387                ExcerptRange {
11388                    context: Point::new(0, 0)..Point::new(3, 0),
11389                    primary: None,
11390                },
11391                ExcerptRange {
11392                    context: Point::new(5, 0)..Point::new(7, 0),
11393                    primary: None,
11394                },
11395                ExcerptRange {
11396                    context: Point::new(9, 0)..Point::new(10, 4),
11397                    primary: None,
11398                },
11399            ],
11400            cx,
11401        );
11402        multibuffer
11403    });
11404
11405    let fs = FakeFs::new(cx.executor());
11406    fs.insert_tree(
11407        "/a",
11408        json!({
11409            "main.rs": modified_sample_text_1,
11410            "other.rs": modified_sample_text_2,
11411            "lib.rs": modified_sample_text_3,
11412        }),
11413    )
11414    .await;
11415
11416    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11417    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11418    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11419    let multi_buffer_editor = cx.new_view(|cx| {
11420        Editor::new(
11421            EditorMode::Full,
11422            multi_buffer,
11423            Some(project.clone()),
11424            true,
11425            cx,
11426        )
11427    });
11428    cx.executor().run_until_parked();
11429
11430    let expected_all_hunks = vec![
11431        (
11432            "bbbb\n".to_string(),
11433            DiffHunkStatus::Removed,
11434            DisplayRow(4)..DisplayRow(4),
11435        ),
11436        (
11437            "nnnn\n".to_string(),
11438            DiffHunkStatus::Modified,
11439            DisplayRow(21)..DisplayRow(22),
11440        ),
11441        (
11442            "".to_string(),
11443            DiffHunkStatus::Added,
11444            DisplayRow(41)..DisplayRow(42),
11445        ),
11446    ];
11447    let expected_all_hunks_shifted = vec![
11448        (
11449            "bbbb\n".to_string(),
11450            DiffHunkStatus::Removed,
11451            DisplayRow(5)..DisplayRow(5),
11452        ),
11453        (
11454            "nnnn\n".to_string(),
11455            DiffHunkStatus::Modified,
11456            DisplayRow(23)..DisplayRow(24),
11457        ),
11458        (
11459            "".to_string(),
11460            DiffHunkStatus::Added,
11461            DisplayRow(43)..DisplayRow(44),
11462        ),
11463    ];
11464
11465    multi_buffer_editor.update(cx, |editor, cx| {
11466        let snapshot = editor.snapshot(cx);
11467        let all_hunks = editor_hunks(editor, &snapshot, cx);
11468        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11469        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11470        assert_eq!(all_hunks, expected_all_hunks);
11471        assert_eq!(all_expanded_hunks, Vec::new());
11472    });
11473
11474    multi_buffer_editor.update(cx, |editor, cx| {
11475        editor.select_all(&SelectAll, cx);
11476        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11477    });
11478    cx.executor().run_until_parked();
11479    multi_buffer_editor.update(cx, |editor, cx| {
11480        let snapshot = editor.snapshot(cx);
11481        let all_hunks = editor_hunks(editor, &snapshot, cx);
11482        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11483        assert_eq!(
11484            expanded_hunks_background_highlights(editor, cx),
11485            vec![
11486                DisplayRow(23)..=DisplayRow(23),
11487                DisplayRow(43)..=DisplayRow(43)
11488            ],
11489        );
11490        assert_eq!(all_hunks, expected_all_hunks_shifted);
11491        assert_eq!(all_hunks, all_expanded_hunks);
11492    });
11493
11494    multi_buffer_editor.update(cx, |editor, cx| {
11495        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11496    });
11497    cx.executor().run_until_parked();
11498    multi_buffer_editor.update(cx, |editor, cx| {
11499        let snapshot = editor.snapshot(cx);
11500        let all_hunks = editor_hunks(editor, &snapshot, cx);
11501        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11502        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11503        assert_eq!(all_hunks, expected_all_hunks);
11504        assert_eq!(all_expanded_hunks, Vec::new());
11505    });
11506
11507    multi_buffer_editor.update(cx, |editor, cx| {
11508        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11509    });
11510    cx.executor().run_until_parked();
11511    multi_buffer_editor.update(cx, |editor, cx| {
11512        let snapshot = editor.snapshot(cx);
11513        let all_hunks = editor_hunks(editor, &snapshot, cx);
11514        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11515        assert_eq!(
11516            expanded_hunks_background_highlights(editor, cx),
11517            vec![
11518                DisplayRow(23)..=DisplayRow(23),
11519                DisplayRow(43)..=DisplayRow(43)
11520            ],
11521        );
11522        assert_eq!(all_hunks, expected_all_hunks_shifted);
11523        assert_eq!(all_hunks, all_expanded_hunks);
11524    });
11525
11526    multi_buffer_editor.update(cx, |editor, cx| {
11527        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11528    });
11529    cx.executor().run_until_parked();
11530    multi_buffer_editor.update(cx, |editor, cx| {
11531        let snapshot = editor.snapshot(cx);
11532        let all_hunks = editor_hunks(editor, &snapshot, cx);
11533        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11534        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11535        assert_eq!(all_hunks, expected_all_hunks);
11536        assert_eq!(all_expanded_hunks, Vec::new());
11537    });
11538}
11539
11540#[gpui::test]
11541async fn test_edits_around_toggled_additions(
11542    executor: BackgroundExecutor,
11543    cx: &mut gpui::TestAppContext,
11544) {
11545    init_test(cx, |_| {});
11546
11547    let mut cx = EditorTestContext::new(cx).await;
11548
11549    let diff_base = r#"
11550        use some::mod1;
11551        use some::mod2;
11552
11553        const A: u32 = 42;
11554
11555        fn main() {
11556            println!("hello");
11557
11558            println!("world");
11559        }
11560        "#
11561    .unindent();
11562    executor.run_until_parked();
11563    cx.set_state(
11564        &r#"
11565        use some::mod1;
11566        use some::mod2;
11567
11568        const A: u32 = 42;
11569        const B: u32 = 42;
11570        const C: u32 = 42;
11571        ˇ
11572
11573        fn main() {
11574            println!("hello");
11575
11576            println!("world");
11577        }
11578        "#
11579        .unindent(),
11580    );
11581
11582    cx.set_diff_base(Some(&diff_base));
11583    executor.run_until_parked();
11584    cx.update_editor(|editor, cx| {
11585        let snapshot = editor.snapshot(cx);
11586        let all_hunks = editor_hunks(editor, &snapshot, cx);
11587        assert_eq!(
11588            all_hunks,
11589            vec![(
11590                "".to_string(),
11591                DiffHunkStatus::Added,
11592                DisplayRow(4)..DisplayRow(7)
11593            )]
11594        );
11595    });
11596    cx.update_editor(|editor, cx| {
11597        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11598    });
11599    executor.run_until_parked();
11600    cx.assert_editor_state(
11601        &r#"
11602        use some::mod1;
11603        use some::mod2;
11604
11605        const A: u32 = 42;
11606        const B: u32 = 42;
11607        const C: u32 = 42;
11608        ˇ
11609
11610        fn main() {
11611            println!("hello");
11612
11613            println!("world");
11614        }
11615        "#
11616        .unindent(),
11617    );
11618    cx.update_editor(|editor, cx| {
11619        let snapshot = editor.snapshot(cx);
11620        let all_hunks = editor_hunks(editor, &snapshot, cx);
11621        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11622        assert_eq!(
11623            all_hunks,
11624            vec![(
11625                "".to_string(),
11626                DiffHunkStatus::Added,
11627                DisplayRow(4)..DisplayRow(7)
11628            )]
11629        );
11630        assert_eq!(
11631            expanded_hunks_background_highlights(editor, cx),
11632            vec![DisplayRow(4)..=DisplayRow(6)]
11633        );
11634        assert_eq!(all_hunks, all_expanded_hunks);
11635    });
11636
11637    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
11638    executor.run_until_parked();
11639    cx.assert_editor_state(
11640        &r#"
11641        use some::mod1;
11642        use some::mod2;
11643
11644        const A: u32 = 42;
11645        const B: u32 = 42;
11646        const C: u32 = 42;
11647        const D: u32 = 42;
11648        ˇ
11649
11650        fn main() {
11651            println!("hello");
11652
11653            println!("world");
11654        }
11655        "#
11656        .unindent(),
11657    );
11658    cx.update_editor(|editor, cx| {
11659        let snapshot = editor.snapshot(cx);
11660        let all_hunks = editor_hunks(editor, &snapshot, cx);
11661        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11662        assert_eq!(
11663            all_hunks,
11664            vec![(
11665                "".to_string(),
11666                DiffHunkStatus::Added,
11667                DisplayRow(4)..DisplayRow(8)
11668            )]
11669        );
11670        assert_eq!(
11671            expanded_hunks_background_highlights(editor, cx),
11672            vec![DisplayRow(4)..=DisplayRow(6)],
11673            "Edited hunk should have one more line added"
11674        );
11675        assert_eq!(
11676            all_hunks, all_expanded_hunks,
11677            "Expanded hunk should also grow with the addition"
11678        );
11679    });
11680
11681    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
11682    executor.run_until_parked();
11683    cx.assert_editor_state(
11684        &r#"
11685        use some::mod1;
11686        use some::mod2;
11687
11688        const A: u32 = 42;
11689        const B: u32 = 42;
11690        const C: u32 = 42;
11691        const D: u32 = 42;
11692        const E: u32 = 42;
11693        ˇ
11694
11695        fn main() {
11696            println!("hello");
11697
11698            println!("world");
11699        }
11700        "#
11701        .unindent(),
11702    );
11703    cx.update_editor(|editor, cx| {
11704        let snapshot = editor.snapshot(cx);
11705        let all_hunks = editor_hunks(editor, &snapshot, cx);
11706        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11707        assert_eq!(
11708            all_hunks,
11709            vec![(
11710                "".to_string(),
11711                DiffHunkStatus::Added,
11712                DisplayRow(4)..DisplayRow(9)
11713            )]
11714        );
11715        assert_eq!(
11716            expanded_hunks_background_highlights(editor, cx),
11717            vec![DisplayRow(4)..=DisplayRow(6)],
11718            "Edited hunk should have one more line added"
11719        );
11720        assert_eq!(all_hunks, all_expanded_hunks);
11721    });
11722
11723    cx.update_editor(|editor, cx| {
11724        editor.move_up(&MoveUp, cx);
11725        editor.delete_line(&DeleteLine, cx);
11726    });
11727    executor.run_until_parked();
11728    cx.assert_editor_state(
11729        &r#"
11730        use some::mod1;
11731        use some::mod2;
11732
11733        const A: u32 = 42;
11734        const B: u32 = 42;
11735        const C: u32 = 42;
11736        const D: u32 = 42;
11737        ˇ
11738
11739        fn main() {
11740            println!("hello");
11741
11742            println!("world");
11743        }
11744        "#
11745        .unindent(),
11746    );
11747    cx.update_editor(|editor, cx| {
11748        let snapshot = editor.snapshot(cx);
11749        let all_hunks = editor_hunks(editor, &snapshot, cx);
11750        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11751        assert_eq!(
11752            all_hunks,
11753            vec![(
11754                "".to_string(),
11755                DiffHunkStatus::Added,
11756                DisplayRow(4)..DisplayRow(8)
11757            )]
11758        );
11759        assert_eq!(
11760            expanded_hunks_background_highlights(editor, cx),
11761            vec![DisplayRow(4)..=DisplayRow(6)],
11762            "Deleting a line should shrint the hunk"
11763        );
11764        assert_eq!(
11765            all_hunks, all_expanded_hunks,
11766            "Expanded hunk should also shrink with the addition"
11767        );
11768    });
11769
11770    cx.update_editor(|editor, cx| {
11771        editor.move_up(&MoveUp, cx);
11772        editor.delete_line(&DeleteLine, cx);
11773        editor.move_up(&MoveUp, cx);
11774        editor.delete_line(&DeleteLine, cx);
11775        editor.move_up(&MoveUp, cx);
11776        editor.delete_line(&DeleteLine, cx);
11777    });
11778    executor.run_until_parked();
11779    cx.assert_editor_state(
11780        &r#"
11781        use some::mod1;
11782        use some::mod2;
11783
11784        const A: u32 = 42;
11785        ˇ
11786
11787        fn main() {
11788            println!("hello");
11789
11790            println!("world");
11791        }
11792        "#
11793        .unindent(),
11794    );
11795    cx.update_editor(|editor, cx| {
11796        let snapshot = editor.snapshot(cx);
11797        let all_hunks = editor_hunks(editor, &snapshot, cx);
11798        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11799        assert_eq!(
11800            all_hunks,
11801            vec![(
11802                "".to_string(),
11803                DiffHunkStatus::Added,
11804                DisplayRow(5)..DisplayRow(6)
11805            )]
11806        );
11807        assert_eq!(
11808            expanded_hunks_background_highlights(editor, cx),
11809            vec![DisplayRow(5)..=DisplayRow(5)]
11810        );
11811        assert_eq!(all_hunks, all_expanded_hunks);
11812    });
11813
11814    cx.update_editor(|editor, cx| {
11815        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
11816        editor.delete_line(&DeleteLine, cx);
11817    });
11818    executor.run_until_parked();
11819    cx.assert_editor_state(
11820        &r#"
11821        ˇ
11822
11823        fn main() {
11824            println!("hello");
11825
11826            println!("world");
11827        }
11828        "#
11829        .unindent(),
11830    );
11831    cx.update_editor(|editor, cx| {
11832        let snapshot = editor.snapshot(cx);
11833        let all_hunks = editor_hunks(editor, &snapshot, cx);
11834        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11835        assert_eq!(
11836            all_hunks,
11837            vec![
11838                (
11839                    "use some::mod1;\nuse some::mod2;\n".to_string(),
11840                    DiffHunkStatus::Removed,
11841                    DisplayRow(0)..DisplayRow(0)
11842                ),
11843                (
11844                    "const A: u32 = 42;\n".to_string(),
11845                    DiffHunkStatus::Removed,
11846                    DisplayRow(2)..DisplayRow(2)
11847                )
11848            ]
11849        );
11850        assert_eq!(
11851            expanded_hunks_background_highlights(editor, cx),
11852            Vec::new(),
11853            "Should close all stale expanded addition hunks"
11854        );
11855        assert_eq!(
11856            all_expanded_hunks,
11857            vec![(
11858                "const A: u32 = 42;\n".to_string(),
11859                DiffHunkStatus::Removed,
11860                DisplayRow(2)..DisplayRow(2)
11861            )],
11862            "Should open hunks that were adjacent to the stale addition one"
11863        );
11864    });
11865}
11866
11867#[gpui::test]
11868async fn test_edits_around_toggled_deletions(
11869    executor: BackgroundExecutor,
11870    cx: &mut gpui::TestAppContext,
11871) {
11872    init_test(cx, |_| {});
11873
11874    let mut cx = EditorTestContext::new(cx).await;
11875
11876    let diff_base = r#"
11877        use some::mod1;
11878        use some::mod2;
11879
11880        const A: u32 = 42;
11881        const B: u32 = 42;
11882        const C: u32 = 42;
11883
11884
11885        fn main() {
11886            println!("hello");
11887
11888            println!("world");
11889        }
11890        "#
11891    .unindent();
11892    executor.run_until_parked();
11893    cx.set_state(
11894        &r#"
11895        use some::mod1;
11896        use some::mod2;
11897
11898        ˇconst B: u32 = 42;
11899        const C: u32 = 42;
11900
11901
11902        fn main() {
11903            println!("hello");
11904
11905            println!("world");
11906        }
11907        "#
11908        .unindent(),
11909    );
11910
11911    cx.set_diff_base(Some(&diff_base));
11912    executor.run_until_parked();
11913    cx.update_editor(|editor, cx| {
11914        let snapshot = editor.snapshot(cx);
11915        let all_hunks = editor_hunks(editor, &snapshot, cx);
11916        assert_eq!(
11917            all_hunks,
11918            vec![(
11919                "const A: u32 = 42;\n".to_string(),
11920                DiffHunkStatus::Removed,
11921                DisplayRow(3)..DisplayRow(3)
11922            )]
11923        );
11924    });
11925    cx.update_editor(|editor, cx| {
11926        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11927    });
11928    executor.run_until_parked();
11929    cx.assert_editor_state(
11930        &r#"
11931        use some::mod1;
11932        use some::mod2;
11933
11934        ˇconst B: u32 = 42;
11935        const C: u32 = 42;
11936
11937
11938        fn main() {
11939            println!("hello");
11940
11941            println!("world");
11942        }
11943        "#
11944        .unindent(),
11945    );
11946    cx.update_editor(|editor, cx| {
11947        let snapshot = editor.snapshot(cx);
11948        let all_hunks = editor_hunks(editor, &snapshot, cx);
11949        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11950        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11951        assert_eq!(
11952            all_hunks,
11953            vec![(
11954                "const A: u32 = 42;\n".to_string(),
11955                DiffHunkStatus::Removed,
11956                DisplayRow(4)..DisplayRow(4)
11957            )]
11958        );
11959        assert_eq!(all_hunks, all_expanded_hunks);
11960    });
11961
11962    cx.update_editor(|editor, cx| {
11963        editor.delete_line(&DeleteLine, cx);
11964    });
11965    executor.run_until_parked();
11966    cx.assert_editor_state(
11967        &r#"
11968        use some::mod1;
11969        use some::mod2;
11970
11971        ˇconst C: u32 = 42;
11972
11973
11974        fn main() {
11975            println!("hello");
11976
11977            println!("world");
11978        }
11979        "#
11980        .unindent(),
11981    );
11982    cx.update_editor(|editor, cx| {
11983        let snapshot = editor.snapshot(cx);
11984        let all_hunks = editor_hunks(editor, &snapshot, cx);
11985        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11986        assert_eq!(
11987            expanded_hunks_background_highlights(editor, cx),
11988            Vec::new(),
11989            "Deleted hunks do not highlight current editor's background"
11990        );
11991        assert_eq!(
11992            all_hunks,
11993            vec![(
11994                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
11995                DiffHunkStatus::Removed,
11996                DisplayRow(5)..DisplayRow(5)
11997            )]
11998        );
11999        assert_eq!(all_hunks, all_expanded_hunks);
12000    });
12001
12002    cx.update_editor(|editor, cx| {
12003        editor.delete_line(&DeleteLine, cx);
12004    });
12005    executor.run_until_parked();
12006    cx.assert_editor_state(
12007        &r#"
12008        use some::mod1;
12009        use some::mod2;
12010
12011        ˇ
12012
12013        fn main() {
12014            println!("hello");
12015
12016            println!("world");
12017        }
12018        "#
12019        .unindent(),
12020    );
12021    cx.update_editor(|editor, cx| {
12022        let snapshot = editor.snapshot(cx);
12023        let all_hunks = editor_hunks(editor, &snapshot, cx);
12024        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12025        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12026        assert_eq!(
12027            all_hunks,
12028            vec![(
12029                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12030                DiffHunkStatus::Removed,
12031                DisplayRow(6)..DisplayRow(6)
12032            )]
12033        );
12034        assert_eq!(all_hunks, all_expanded_hunks);
12035    });
12036
12037    cx.update_editor(|editor, cx| {
12038        editor.handle_input("replacement", cx);
12039    });
12040    executor.run_until_parked();
12041    cx.assert_editor_state(
12042        &r#"
12043        use some::mod1;
12044        use some::mod2;
12045
12046        replacementˇ
12047
12048        fn main() {
12049            println!("hello");
12050
12051            println!("world");
12052        }
12053        "#
12054        .unindent(),
12055    );
12056    cx.update_editor(|editor, cx| {
12057        let snapshot = editor.snapshot(cx);
12058        let all_hunks = editor_hunks(editor, &snapshot, cx);
12059        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12060        assert_eq!(
12061            all_hunks,
12062            vec![(
12063                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
12064                DiffHunkStatus::Modified,
12065                DisplayRow(7)..DisplayRow(8)
12066            )]
12067        );
12068        assert_eq!(
12069            expanded_hunks_background_highlights(editor, cx),
12070            vec![DisplayRow(7)..=DisplayRow(7)],
12071            "Modified expanded hunks should display additions and highlight their background"
12072        );
12073        assert_eq!(all_hunks, all_expanded_hunks);
12074    });
12075}
12076
12077#[gpui::test]
12078async fn test_edits_around_toggled_modifications(
12079    executor: BackgroundExecutor,
12080    cx: &mut gpui::TestAppContext,
12081) {
12082    init_test(cx, |_| {});
12083
12084    let mut cx = EditorTestContext::new(cx).await;
12085
12086    let diff_base = r#"
12087        use some::mod1;
12088        use some::mod2;
12089
12090        const A: u32 = 42;
12091        const B: u32 = 42;
12092        const C: u32 = 42;
12093        const D: u32 = 42;
12094
12095
12096        fn main() {
12097            println!("hello");
12098
12099            println!("world");
12100        }"#
12101    .unindent();
12102    executor.run_until_parked();
12103    cx.set_state(
12104        &r#"
12105        use some::mod1;
12106        use some::mod2;
12107
12108        const A: u32 = 42;
12109        const B: u32 = 42;
12110        const C: u32 = 43ˇ
12111        const D: u32 = 42;
12112
12113
12114        fn main() {
12115            println!("hello");
12116
12117            println!("world");
12118        }"#
12119        .unindent(),
12120    );
12121
12122    cx.set_diff_base(Some(&diff_base));
12123    executor.run_until_parked();
12124    cx.update_editor(|editor, cx| {
12125        let snapshot = editor.snapshot(cx);
12126        let all_hunks = editor_hunks(editor, &snapshot, cx);
12127        assert_eq!(
12128            all_hunks,
12129            vec![(
12130                "const C: u32 = 42;\n".to_string(),
12131                DiffHunkStatus::Modified,
12132                DisplayRow(5)..DisplayRow(6)
12133            )]
12134        );
12135    });
12136    cx.update_editor(|editor, cx| {
12137        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12138    });
12139    executor.run_until_parked();
12140    cx.assert_editor_state(
12141        &r#"
12142        use some::mod1;
12143        use some::mod2;
12144
12145        const A: u32 = 42;
12146        const B: u32 = 42;
12147        const C: u32 = 43ˇ
12148        const D: u32 = 42;
12149
12150
12151        fn main() {
12152            println!("hello");
12153
12154            println!("world");
12155        }"#
12156        .unindent(),
12157    );
12158    cx.update_editor(|editor, cx| {
12159        let snapshot = editor.snapshot(cx);
12160        let all_hunks = editor_hunks(editor, &snapshot, cx);
12161        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12162        assert_eq!(
12163            expanded_hunks_background_highlights(editor, cx),
12164            vec![DisplayRow(6)..=DisplayRow(6)],
12165        );
12166        assert_eq!(
12167            all_hunks,
12168            vec![(
12169                "const C: u32 = 42;\n".to_string(),
12170                DiffHunkStatus::Modified,
12171                DisplayRow(6)..DisplayRow(7)
12172            )]
12173        );
12174        assert_eq!(all_hunks, all_expanded_hunks);
12175    });
12176
12177    cx.update_editor(|editor, cx| {
12178        editor.handle_input("\nnew_line\n", cx);
12179    });
12180    executor.run_until_parked();
12181    cx.assert_editor_state(
12182        &r#"
12183            use some::mod1;
12184            use some::mod2;
12185
12186            const A: u32 = 42;
12187            const B: u32 = 42;
12188            const C: u32 = 43
12189            new_line
12190            ˇ
12191            const D: u32 = 42;
12192
12193
12194            fn main() {
12195                println!("hello");
12196
12197                println!("world");
12198            }"#
12199        .unindent(),
12200    );
12201    cx.update_editor(|editor, cx| {
12202        let snapshot = editor.snapshot(cx);
12203        let all_hunks = editor_hunks(editor, &snapshot, cx);
12204        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12205        assert_eq!(
12206            expanded_hunks_background_highlights(editor, cx),
12207            vec![DisplayRow(6)..=DisplayRow(6)],
12208            "Modified hunk should grow highlighted lines on more text additions"
12209        );
12210        assert_eq!(
12211            all_hunks,
12212            vec![(
12213                "const C: u32 = 42;\n".to_string(),
12214                DiffHunkStatus::Modified,
12215                DisplayRow(6)..DisplayRow(9)
12216            )]
12217        );
12218        assert_eq!(all_hunks, all_expanded_hunks);
12219    });
12220
12221    cx.update_editor(|editor, cx| {
12222        editor.move_up(&MoveUp, cx);
12223        editor.move_up(&MoveUp, cx);
12224        editor.move_up(&MoveUp, cx);
12225        editor.delete_line(&DeleteLine, cx);
12226    });
12227    executor.run_until_parked();
12228    cx.assert_editor_state(
12229        &r#"
12230            use some::mod1;
12231            use some::mod2;
12232
12233            const A: u32 = 42;
12234            ˇconst C: u32 = 43
12235            new_line
12236
12237            const D: u32 = 42;
12238
12239
12240            fn main() {
12241                println!("hello");
12242
12243                println!("world");
12244            }"#
12245        .unindent(),
12246    );
12247    cx.update_editor(|editor, cx| {
12248        let snapshot = editor.snapshot(cx);
12249        let all_hunks = editor_hunks(editor, &snapshot, cx);
12250        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12251        assert_eq!(
12252            expanded_hunks_background_highlights(editor, cx),
12253            vec![DisplayRow(6)..=DisplayRow(8)],
12254        );
12255        assert_eq!(
12256            all_hunks,
12257            vec![(
12258                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12259                DiffHunkStatus::Modified,
12260                DisplayRow(6)..DisplayRow(9)
12261            )],
12262            "Modified hunk should grow deleted lines on text deletions above"
12263        );
12264        assert_eq!(all_hunks, all_expanded_hunks);
12265    });
12266
12267    cx.update_editor(|editor, cx| {
12268        editor.move_up(&MoveUp, cx);
12269        editor.handle_input("v", cx);
12270    });
12271    executor.run_until_parked();
12272    cx.assert_editor_state(
12273        &r#"
12274            use some::mod1;
12275            use some::mod2;
12276
12277            vˇconst A: u32 = 42;
12278            const C: u32 = 43
12279            new_line
12280
12281            const D: u32 = 42;
12282
12283
12284            fn main() {
12285                println!("hello");
12286
12287                println!("world");
12288            }"#
12289        .unindent(),
12290    );
12291    cx.update_editor(|editor, cx| {
12292        let snapshot = editor.snapshot(cx);
12293        let all_hunks = editor_hunks(editor, &snapshot, cx);
12294        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12295        assert_eq!(
12296            expanded_hunks_background_highlights(editor, cx),
12297            vec![DisplayRow(6)..=DisplayRow(9)],
12298            "Modified hunk should grow deleted lines on text modifications above"
12299        );
12300        assert_eq!(
12301            all_hunks,
12302            vec![(
12303                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12304                DiffHunkStatus::Modified,
12305                DisplayRow(6)..DisplayRow(10)
12306            )]
12307        );
12308        assert_eq!(all_hunks, all_expanded_hunks);
12309    });
12310
12311    cx.update_editor(|editor, cx| {
12312        editor.move_down(&MoveDown, cx);
12313        editor.move_down(&MoveDown, cx);
12314        editor.delete_line(&DeleteLine, cx)
12315    });
12316    executor.run_until_parked();
12317    cx.assert_editor_state(
12318        &r#"
12319            use some::mod1;
12320            use some::mod2;
12321
12322            vconst A: u32 = 42;
12323            const C: u32 = 43
12324            ˇ
12325            const D: u32 = 42;
12326
12327
12328            fn main() {
12329                println!("hello");
12330
12331                println!("world");
12332            }"#
12333        .unindent(),
12334    );
12335    cx.update_editor(|editor, cx| {
12336        let snapshot = editor.snapshot(cx);
12337        let all_hunks = editor_hunks(editor, &snapshot, cx);
12338        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12339        assert_eq!(
12340            expanded_hunks_background_highlights(editor, cx),
12341            vec![DisplayRow(6)..=DisplayRow(8)],
12342            "Modified hunk should grow shrink lines on modification lines removal"
12343        );
12344        assert_eq!(
12345            all_hunks,
12346            vec![(
12347                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12348                DiffHunkStatus::Modified,
12349                DisplayRow(6)..DisplayRow(9)
12350            )]
12351        );
12352        assert_eq!(all_hunks, all_expanded_hunks);
12353    });
12354
12355    cx.update_editor(|editor, cx| {
12356        editor.move_up(&MoveUp, cx);
12357        editor.move_up(&MoveUp, cx);
12358        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
12359        editor.delete_line(&DeleteLine, cx)
12360    });
12361    executor.run_until_parked();
12362    cx.assert_editor_state(
12363        &r#"
12364            use some::mod1;
12365            use some::mod2;
12366
12367            ˇ
12368
12369            fn main() {
12370                println!("hello");
12371
12372                println!("world");
12373            }"#
12374        .unindent(),
12375    );
12376    cx.update_editor(|editor, cx| {
12377        let snapshot = editor.snapshot(cx);
12378        let all_hunks = editor_hunks(editor, &snapshot, cx);
12379        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12380        assert_eq!(
12381            expanded_hunks_background_highlights(editor, cx),
12382            Vec::new(),
12383            "Modified hunk should turn into a removed one on all modified lines removal"
12384        );
12385        assert_eq!(
12386            all_hunks,
12387            vec![(
12388                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
12389                    .to_string(),
12390                DiffHunkStatus::Removed,
12391                DisplayRow(7)..DisplayRow(7)
12392            )]
12393        );
12394        assert_eq!(all_hunks, all_expanded_hunks);
12395    });
12396}
12397
12398#[gpui::test]
12399async fn test_multiple_expanded_hunks_merge(
12400    executor: BackgroundExecutor,
12401    cx: &mut gpui::TestAppContext,
12402) {
12403    init_test(cx, |_| {});
12404
12405    let mut cx = EditorTestContext::new(cx).await;
12406
12407    let diff_base = r#"
12408        use some::mod1;
12409        use some::mod2;
12410
12411        const A: u32 = 42;
12412        const B: u32 = 42;
12413        const C: u32 = 42;
12414        const D: u32 = 42;
12415
12416
12417        fn main() {
12418            println!("hello");
12419
12420            println!("world");
12421        }"#
12422    .unindent();
12423    executor.run_until_parked();
12424    cx.set_state(
12425        &r#"
12426        use some::mod1;
12427        use some::mod2;
12428
12429        const A: u32 = 42;
12430        const B: u32 = 42;
12431        const C: u32 = 43ˇ
12432        const D: u32 = 42;
12433
12434
12435        fn main() {
12436            println!("hello");
12437
12438            println!("world");
12439        }"#
12440        .unindent(),
12441    );
12442
12443    cx.set_diff_base(Some(&diff_base));
12444    executor.run_until_parked();
12445    cx.update_editor(|editor, cx| {
12446        let snapshot = editor.snapshot(cx);
12447        let all_hunks = editor_hunks(editor, &snapshot, cx);
12448        assert_eq!(
12449            all_hunks,
12450            vec![(
12451                "const C: u32 = 42;\n".to_string(),
12452                DiffHunkStatus::Modified,
12453                DisplayRow(5)..DisplayRow(6)
12454            )]
12455        );
12456    });
12457    cx.update_editor(|editor, cx| {
12458        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12459    });
12460    executor.run_until_parked();
12461    cx.assert_editor_state(
12462        &r#"
12463        use some::mod1;
12464        use some::mod2;
12465
12466        const A: u32 = 42;
12467        const B: u32 = 42;
12468        const C: u32 = 43ˇ
12469        const D: u32 = 42;
12470
12471
12472        fn main() {
12473            println!("hello");
12474
12475            println!("world");
12476        }"#
12477        .unindent(),
12478    );
12479    cx.update_editor(|editor, cx| {
12480        let snapshot = editor.snapshot(cx);
12481        let all_hunks = editor_hunks(editor, &snapshot, cx);
12482        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12483        assert_eq!(
12484            expanded_hunks_background_highlights(editor, cx),
12485            vec![DisplayRow(6)..=DisplayRow(6)],
12486        );
12487        assert_eq!(
12488            all_hunks,
12489            vec![(
12490                "const C: u32 = 42;\n".to_string(),
12491                DiffHunkStatus::Modified,
12492                DisplayRow(6)..DisplayRow(7)
12493            )]
12494        );
12495        assert_eq!(all_hunks, all_expanded_hunks);
12496    });
12497
12498    cx.update_editor(|editor, cx| {
12499        editor.handle_input("\nnew_line\n", cx);
12500    });
12501    executor.run_until_parked();
12502    cx.assert_editor_state(
12503        &r#"
12504            use some::mod1;
12505            use some::mod2;
12506
12507            const A: u32 = 42;
12508            const B: u32 = 42;
12509            const C: u32 = 43
12510            new_line
12511            ˇ
12512            const D: u32 = 42;
12513
12514
12515            fn main() {
12516                println!("hello");
12517
12518                println!("world");
12519            }"#
12520        .unindent(),
12521    );
12522}
12523
12524async fn setup_indent_guides_editor(
12525    text: &str,
12526    cx: &mut gpui::TestAppContext,
12527) -> (BufferId, EditorTestContext) {
12528    init_test(cx, |_| {});
12529
12530    let mut cx = EditorTestContext::new(cx).await;
12531
12532    let buffer_id = cx.update_editor(|editor, cx| {
12533        editor.set_text(text, cx);
12534        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12535        let buffer_id = buffer_ids[0];
12536        buffer_id
12537    });
12538
12539    (buffer_id, cx)
12540}
12541
12542fn assert_indent_guides(
12543    range: Range<u32>,
12544    expected: Vec<IndentGuide>,
12545    active_indices: Option<Vec<usize>>,
12546    cx: &mut EditorTestContext,
12547) {
12548    let indent_guides = cx.update_editor(|editor, cx| {
12549        let snapshot = editor.snapshot(cx).display_snapshot;
12550        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12551            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12552            true,
12553            &snapshot,
12554            cx,
12555        );
12556
12557        indent_guides.sort_by(|a, b| {
12558            a.depth.cmp(&b.depth).then(
12559                a.start_row
12560                    .cmp(&b.start_row)
12561                    .then(a.end_row.cmp(&b.end_row)),
12562            )
12563        });
12564        indent_guides
12565    });
12566
12567    if let Some(expected) = active_indices {
12568        let active_indices = cx.update_editor(|editor, cx| {
12569            let snapshot = editor.snapshot(cx).display_snapshot;
12570            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12571        });
12572
12573        assert_eq!(
12574            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12575            expected,
12576            "Active indent guide indices do not match"
12577        );
12578    }
12579
12580    let expected: Vec<_> = expected
12581        .into_iter()
12582        .map(|guide| MultiBufferIndentGuide {
12583            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12584            buffer: guide,
12585        })
12586        .collect();
12587
12588    assert_eq!(indent_guides, expected, "Indent guides do not match");
12589}
12590
12591fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12592    IndentGuide {
12593        buffer_id,
12594        start_row,
12595        end_row,
12596        depth,
12597        tab_size: 4,
12598        settings: IndentGuideSettings {
12599            enabled: true,
12600            line_width: 1,
12601            active_line_width: 1,
12602            ..Default::default()
12603        },
12604    }
12605}
12606
12607#[gpui::test]
12608async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12609    let (buffer_id, mut cx) = setup_indent_guides_editor(
12610        &"
12611    fn main() {
12612        let a = 1;
12613    }"
12614        .unindent(),
12615        cx,
12616    )
12617    .await;
12618
12619    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12620}
12621
12622#[gpui::test]
12623async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12624    let (buffer_id, mut cx) = setup_indent_guides_editor(
12625        &"
12626    fn main() {
12627        let a = 1;
12628        let b = 2;
12629    }"
12630        .unindent(),
12631        cx,
12632    )
12633    .await;
12634
12635    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12636}
12637
12638#[gpui::test]
12639async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12640    let (buffer_id, mut cx) = setup_indent_guides_editor(
12641        &"
12642    fn main() {
12643        let a = 1;
12644        if a == 3 {
12645            let b = 2;
12646        } else {
12647            let c = 3;
12648        }
12649    }"
12650        .unindent(),
12651        cx,
12652    )
12653    .await;
12654
12655    assert_indent_guides(
12656        0..8,
12657        vec![
12658            indent_guide(buffer_id, 1, 6, 0),
12659            indent_guide(buffer_id, 3, 3, 1),
12660            indent_guide(buffer_id, 5, 5, 1),
12661        ],
12662        None,
12663        &mut cx,
12664    );
12665}
12666
12667#[gpui::test]
12668async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12669    let (buffer_id, mut cx) = setup_indent_guides_editor(
12670        &"
12671    fn main() {
12672        let a = 1;
12673            let b = 2;
12674        let c = 3;
12675    }"
12676        .unindent(),
12677        cx,
12678    )
12679    .await;
12680
12681    assert_indent_guides(
12682        0..5,
12683        vec![
12684            indent_guide(buffer_id, 1, 3, 0),
12685            indent_guide(buffer_id, 2, 2, 1),
12686        ],
12687        None,
12688        &mut cx,
12689    );
12690}
12691
12692#[gpui::test]
12693async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12694    let (buffer_id, mut cx) = setup_indent_guides_editor(
12695        &"
12696        fn main() {
12697            let a = 1;
12698
12699            let c = 3;
12700        }"
12701        .unindent(),
12702        cx,
12703    )
12704    .await;
12705
12706    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12707}
12708
12709#[gpui::test]
12710async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12711    let (buffer_id, mut cx) = setup_indent_guides_editor(
12712        &"
12713        fn main() {
12714            let a = 1;
12715
12716            let c = 3;
12717
12718            if a == 3 {
12719                let b = 2;
12720            } else {
12721                let c = 3;
12722            }
12723        }"
12724        .unindent(),
12725        cx,
12726    )
12727    .await;
12728
12729    assert_indent_guides(
12730        0..11,
12731        vec![
12732            indent_guide(buffer_id, 1, 9, 0),
12733            indent_guide(buffer_id, 6, 6, 1),
12734            indent_guide(buffer_id, 8, 8, 1),
12735        ],
12736        None,
12737        &mut cx,
12738    );
12739}
12740
12741#[gpui::test]
12742async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12743    let (buffer_id, mut cx) = setup_indent_guides_editor(
12744        &"
12745        fn main() {
12746            let a = 1;
12747
12748            let c = 3;
12749
12750            if a == 3 {
12751                let b = 2;
12752            } else {
12753                let c = 3;
12754            }
12755        }"
12756        .unindent(),
12757        cx,
12758    )
12759    .await;
12760
12761    assert_indent_guides(
12762        1..11,
12763        vec![
12764            indent_guide(buffer_id, 1, 9, 0),
12765            indent_guide(buffer_id, 6, 6, 1),
12766            indent_guide(buffer_id, 8, 8, 1),
12767        ],
12768        None,
12769        &mut cx,
12770    );
12771}
12772
12773#[gpui::test]
12774async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12775    let (buffer_id, mut cx) = setup_indent_guides_editor(
12776        &"
12777        fn main() {
12778            let a = 1;
12779
12780            let c = 3;
12781
12782            if a == 3 {
12783                let b = 2;
12784            } else {
12785                let c = 3;
12786            }
12787        }"
12788        .unindent(),
12789        cx,
12790    )
12791    .await;
12792
12793    assert_indent_guides(
12794        1..10,
12795        vec![
12796            indent_guide(buffer_id, 1, 9, 0),
12797            indent_guide(buffer_id, 6, 6, 1),
12798            indent_guide(buffer_id, 8, 8, 1),
12799        ],
12800        None,
12801        &mut cx,
12802    );
12803}
12804
12805#[gpui::test]
12806async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12807    let (buffer_id, mut cx) = setup_indent_guides_editor(
12808        &"
12809        block1
12810            block2
12811                block3
12812                    block4
12813            block2
12814        block1
12815        block1"
12816            .unindent(),
12817        cx,
12818    )
12819    .await;
12820
12821    assert_indent_guides(
12822        1..10,
12823        vec![
12824            indent_guide(buffer_id, 1, 4, 0),
12825            indent_guide(buffer_id, 2, 3, 1),
12826            indent_guide(buffer_id, 3, 3, 2),
12827        ],
12828        None,
12829        &mut cx,
12830    );
12831}
12832
12833#[gpui::test]
12834async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12835    let (buffer_id, mut cx) = setup_indent_guides_editor(
12836        &"
12837        block1
12838            block2
12839                block3
12840
12841        block1
12842        block1"
12843            .unindent(),
12844        cx,
12845    )
12846    .await;
12847
12848    assert_indent_guides(
12849        0..6,
12850        vec![
12851            indent_guide(buffer_id, 1, 2, 0),
12852            indent_guide(buffer_id, 2, 2, 1),
12853        ],
12854        None,
12855        &mut cx,
12856    );
12857}
12858
12859#[gpui::test]
12860async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12861    let (buffer_id, mut cx) = setup_indent_guides_editor(
12862        &"
12863        block1
12864
12865
12866
12867            block2
12868        "
12869        .unindent(),
12870        cx,
12871    )
12872    .await;
12873
12874    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12875}
12876
12877#[gpui::test]
12878async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12879    let (buffer_id, mut cx) = setup_indent_guides_editor(
12880        &"
12881        def a:
12882        \tb = 3
12883        \tif True:
12884        \t\tc = 4
12885        \t\td = 5
12886        \tprint(b)
12887        "
12888        .unindent(),
12889        cx,
12890    )
12891    .await;
12892
12893    assert_indent_guides(
12894        0..6,
12895        vec![
12896            indent_guide(buffer_id, 1, 6, 0),
12897            indent_guide(buffer_id, 3, 4, 1),
12898        ],
12899        None,
12900        &mut cx,
12901    );
12902}
12903
12904#[gpui::test]
12905async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12906    let (buffer_id, mut cx) = setup_indent_guides_editor(
12907        &"
12908    fn main() {
12909        let a = 1;
12910    }"
12911        .unindent(),
12912        cx,
12913    )
12914    .await;
12915
12916    cx.update_editor(|editor, cx| {
12917        editor.change_selections(None, cx, |s| {
12918            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12919        });
12920    });
12921
12922    assert_indent_guides(
12923        0..3,
12924        vec![indent_guide(buffer_id, 1, 1, 0)],
12925        Some(vec![0]),
12926        &mut cx,
12927    );
12928}
12929
12930#[gpui::test]
12931async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12932    let (buffer_id, mut cx) = setup_indent_guides_editor(
12933        &"
12934    fn main() {
12935        if 1 == 2 {
12936            let a = 1;
12937        }
12938    }"
12939        .unindent(),
12940        cx,
12941    )
12942    .await;
12943
12944    cx.update_editor(|editor, cx| {
12945        editor.change_selections(None, cx, |s| {
12946            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12947        });
12948    });
12949
12950    assert_indent_guides(
12951        0..4,
12952        vec![
12953            indent_guide(buffer_id, 1, 3, 0),
12954            indent_guide(buffer_id, 2, 2, 1),
12955        ],
12956        Some(vec![1]),
12957        &mut cx,
12958    );
12959
12960    cx.update_editor(|editor, cx| {
12961        editor.change_selections(None, cx, |s| {
12962            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12963        });
12964    });
12965
12966    assert_indent_guides(
12967        0..4,
12968        vec![
12969            indent_guide(buffer_id, 1, 3, 0),
12970            indent_guide(buffer_id, 2, 2, 1),
12971        ],
12972        Some(vec![1]),
12973        &mut cx,
12974    );
12975
12976    cx.update_editor(|editor, cx| {
12977        editor.change_selections(None, cx, |s| {
12978            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
12979        });
12980    });
12981
12982    assert_indent_guides(
12983        0..4,
12984        vec![
12985            indent_guide(buffer_id, 1, 3, 0),
12986            indent_guide(buffer_id, 2, 2, 1),
12987        ],
12988        Some(vec![0]),
12989        &mut cx,
12990    );
12991}
12992
12993#[gpui::test]
12994async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
12995    let (buffer_id, mut cx) = setup_indent_guides_editor(
12996        &"
12997    fn main() {
12998        let a = 1;
12999
13000        let b = 2;
13001    }"
13002        .unindent(),
13003        cx,
13004    )
13005    .await;
13006
13007    cx.update_editor(|editor, cx| {
13008        editor.change_selections(None, cx, |s| {
13009            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13010        });
13011    });
13012
13013    assert_indent_guides(
13014        0..5,
13015        vec![indent_guide(buffer_id, 1, 3, 0)],
13016        Some(vec![0]),
13017        &mut cx,
13018    );
13019}
13020
13021#[gpui::test]
13022async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13023    let (buffer_id, mut cx) = setup_indent_guides_editor(
13024        &"
13025    def m:
13026        a = 1
13027        pass"
13028            .unindent(),
13029        cx,
13030    )
13031    .await;
13032
13033    cx.update_editor(|editor, cx| {
13034        editor.change_selections(None, cx, |s| {
13035            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13036        });
13037    });
13038
13039    assert_indent_guides(
13040        0..3,
13041        vec![indent_guide(buffer_id, 1, 2, 0)],
13042        Some(vec![0]),
13043        &mut cx,
13044    );
13045}
13046
13047#[gpui::test]
13048fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13049    init_test(cx, |_| {});
13050
13051    let editor = cx.add_window(|cx| {
13052        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13053        build_editor(buffer, cx)
13054    });
13055
13056    let render_args = Arc::new(Mutex::new(None));
13057    let snapshot = editor
13058        .update(cx, |editor, cx| {
13059            let snapshot = editor.buffer().read(cx).snapshot(cx);
13060            let range =
13061                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13062
13063            struct RenderArgs {
13064                row: MultiBufferRow,
13065                folded: bool,
13066                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13067            }
13068
13069            let crease = Crease::new(
13070                range,
13071                FoldPlaceholder::test(),
13072                {
13073                    let toggle_callback = render_args.clone();
13074                    move |row, folded, callback, _cx| {
13075                        *toggle_callback.lock() = Some(RenderArgs {
13076                            row,
13077                            folded,
13078                            callback,
13079                        });
13080                        div()
13081                    }
13082                },
13083                |_row, _folded, _cx| div(),
13084            );
13085
13086            editor.insert_creases(Some(crease), cx);
13087            let snapshot = editor.snapshot(cx);
13088            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13089            snapshot
13090        })
13091        .unwrap();
13092
13093    let render_args = render_args.lock().take().unwrap();
13094    assert_eq!(render_args.row, MultiBufferRow(1));
13095    assert_eq!(render_args.folded, false);
13096    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13097
13098    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13099        .unwrap();
13100    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13101    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13102
13103    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13104        .unwrap();
13105    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13106    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13107}
13108
13109#[gpui::test]
13110async fn test_input_text(cx: &mut gpui::TestAppContext) {
13111    init_test(cx, |_| {});
13112    let mut cx = EditorTestContext::new(cx).await;
13113
13114    cx.set_state(
13115        &r#"ˇone
13116        two
13117
13118        three
13119        fourˇ
13120        five
13121
13122        siˇx"#
13123            .unindent(),
13124    );
13125
13126    cx.dispatch_action(HandleInput(String::new()));
13127    cx.assert_editor_state(
13128        &r#"ˇone
13129        two
13130
13131        three
13132        fourˇ
13133        five
13134
13135        siˇx"#
13136            .unindent(),
13137    );
13138
13139    cx.dispatch_action(HandleInput("AAAA".to_string()));
13140    cx.assert_editor_state(
13141        &r#"AAAAˇone
13142        two
13143
13144        three
13145        fourAAAAˇ
13146        five
13147
13148        siAAAAˇx"#
13149            .unindent(),
13150    );
13151}
13152
13153#[gpui::test]
13154async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13155    init_test(cx, |_| {});
13156
13157    let mut cx = EditorTestContext::new(cx).await;
13158    cx.set_state(
13159        r#"let foo = 1;
13160let foo = 2;
13161let foo = 3;
13162let fooˇ = 4;
13163let foo = 5;
13164let foo = 6;
13165let foo = 7;
13166let foo = 8;
13167let foo = 9;
13168let foo = 10;
13169let foo = 11;
13170let foo = 12;
13171let foo = 13;
13172let foo = 14;
13173let foo = 15;"#,
13174    );
13175
13176    cx.update_editor(|e, cx| {
13177        assert_eq!(
13178            e.next_scroll_position,
13179            NextScrollCursorCenterTopBottom::Center,
13180            "Default next scroll direction is center",
13181        );
13182
13183        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13184        assert_eq!(
13185            e.next_scroll_position,
13186            NextScrollCursorCenterTopBottom::Top,
13187            "After center, next scroll direction should be top",
13188        );
13189
13190        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13191        assert_eq!(
13192            e.next_scroll_position,
13193            NextScrollCursorCenterTopBottom::Bottom,
13194            "After top, next scroll direction should be bottom",
13195        );
13196
13197        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13198        assert_eq!(
13199            e.next_scroll_position,
13200            NextScrollCursorCenterTopBottom::Center,
13201            "After bottom, scrolling should start over",
13202        );
13203
13204        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13205        assert_eq!(
13206            e.next_scroll_position,
13207            NextScrollCursorCenterTopBottom::Top,
13208            "Scrolling continues if retriggered fast enough"
13209        );
13210    });
13211
13212    cx.executor()
13213        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13214    cx.executor().run_until_parked();
13215    cx.update_editor(|e, _| {
13216        assert_eq!(
13217            e.next_scroll_position,
13218            NextScrollCursorCenterTopBottom::Center,
13219            "If scrolling is not triggered fast enough, it should reset"
13220        );
13221    });
13222}
13223
13224fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13225    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13226    point..point
13227}
13228
13229fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13230    let (text, ranges) = marked_text_ranges(marked_text, true);
13231    assert_eq!(view.text(cx), text);
13232    assert_eq!(
13233        view.selections.ranges(cx),
13234        ranges,
13235        "Assert selections are {}",
13236        marked_text
13237    );
13238}
13239
13240pub fn handle_signature_help_request(
13241    cx: &mut EditorLspTestContext,
13242    mocked_response: lsp::SignatureHelp,
13243) -> impl Future<Output = ()> {
13244    let mut request =
13245        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13246            let mocked_response = mocked_response.clone();
13247            async move { Ok(Some(mocked_response)) }
13248        });
13249
13250    async move {
13251        request.next().await;
13252    }
13253}
13254
13255/// Handle completion request passing a marked string specifying where the completion
13256/// should be triggered from using '|' character, what range should be replaced, and what completions
13257/// should be returned using '<' and '>' to delimit the range
13258pub fn handle_completion_request(
13259    cx: &mut EditorLspTestContext,
13260    marked_string: &str,
13261    completions: Vec<&'static str>,
13262    counter: Arc<AtomicUsize>,
13263) -> impl Future<Output = ()> {
13264    let complete_from_marker: TextRangeMarker = '|'.into();
13265    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13266    let (_, mut marked_ranges) = marked_text_ranges_by(
13267        marked_string,
13268        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13269    );
13270
13271    let complete_from_position =
13272        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13273    let replace_range =
13274        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13275
13276    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13277        let completions = completions.clone();
13278        counter.fetch_add(1, atomic::Ordering::Release);
13279        async move {
13280            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13281            assert_eq!(
13282                params.text_document_position.position,
13283                complete_from_position
13284            );
13285            Ok(Some(lsp::CompletionResponse::Array(
13286                completions
13287                    .iter()
13288                    .map(|completion_text| lsp::CompletionItem {
13289                        label: completion_text.to_string(),
13290                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13291                            range: replace_range,
13292                            new_text: completion_text.to_string(),
13293                        })),
13294                        ..Default::default()
13295                    })
13296                    .collect(),
13297            )))
13298        }
13299    });
13300
13301    async move {
13302        request.next().await;
13303    }
13304}
13305
13306fn handle_resolve_completion_request(
13307    cx: &mut EditorLspTestContext,
13308    edits: Option<Vec<(&'static str, &'static str)>>,
13309) -> impl Future<Output = ()> {
13310    let edits = edits.map(|edits| {
13311        edits
13312            .iter()
13313            .map(|(marked_string, new_text)| {
13314                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13315                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13316                lsp::TextEdit::new(replace_range, new_text.to_string())
13317            })
13318            .collect::<Vec<_>>()
13319    });
13320
13321    let mut request =
13322        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13323            let edits = edits.clone();
13324            async move {
13325                Ok(lsp::CompletionItem {
13326                    additional_text_edits: edits,
13327                    ..Default::default()
13328                })
13329            }
13330        });
13331
13332    async move {
13333        request.next().await;
13334    }
13335}
13336
13337pub(crate) fn update_test_language_settings(
13338    cx: &mut TestAppContext,
13339    f: impl Fn(&mut AllLanguageSettingsContent),
13340) {
13341    _ = cx.update(|cx| {
13342        SettingsStore::update_global(cx, |store, cx| {
13343            store.update_user_settings::<AllLanguageSettings>(cx, f);
13344        });
13345    });
13346}
13347
13348pub(crate) fn update_test_project_settings(
13349    cx: &mut TestAppContext,
13350    f: impl Fn(&mut ProjectSettings),
13351) {
13352    _ = cx.update(|cx| {
13353        SettingsStore::update_global(cx, |store, cx| {
13354            store.update_user_settings::<ProjectSettings>(cx, f);
13355        });
13356    });
13357}
13358
13359pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13360    _ = cx.update(|cx| {
13361        assets::Assets.load_test_fonts(cx);
13362        let store = SettingsStore::test(cx);
13363        cx.set_global(store);
13364        theme::init(theme::LoadThemes::JustBase, cx);
13365        release_channel::init(SemanticVersion::default(), cx);
13366        client::init_settings(cx);
13367        language::init(cx);
13368        Project::init_settings(cx);
13369        workspace::init_settings(cx);
13370        crate::init(cx);
13371    });
13372
13373    update_test_language_settings(cx, f);
13374}
13375
13376pub(crate) fn rust_lang() -> Arc<Language> {
13377    Arc::new(Language::new(
13378        LanguageConfig {
13379            name: "Rust".into(),
13380            matcher: LanguageMatcher {
13381                path_suffixes: vec!["rs".to_string()],
13382                ..Default::default()
13383            },
13384            ..Default::default()
13385        },
13386        Some(tree_sitter_rust::language()),
13387    ))
13388}
13389
13390#[track_caller]
13391fn assert_hunk_revert(
13392    not_reverted_text_with_selections: &str,
13393    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13394    expected_reverted_text_with_selections: &str,
13395    base_text: &str,
13396    cx: &mut EditorLspTestContext,
13397) {
13398    cx.set_state(not_reverted_text_with_selections);
13399    cx.update_editor(|editor, cx| {
13400        editor
13401            .buffer()
13402            .read(cx)
13403            .as_singleton()
13404            .unwrap()
13405            .update(cx, |buffer, cx| {
13406                buffer.set_diff_base(Some(base_text.into()), cx);
13407            });
13408    });
13409    cx.executor().run_until_parked();
13410
13411    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13412        let snapshot = editor.buffer().read(cx).snapshot(cx);
13413        let reverted_hunk_statuses = snapshot
13414            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13415            .map(|hunk| hunk_status(&hunk))
13416            .collect::<Vec<_>>();
13417
13418        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13419        reverted_hunk_statuses
13420    });
13421    cx.executor().run_until_parked();
13422    cx.assert_editor_state(expected_reverted_text_with_selections);
13423    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13424}