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().map(|item| item.text().to_owned()),
 3960        Some("fox jumps over\n".to_owned())
 3961    );
 3962
 3963    // Paste with three selections, noticing how the copied full-line selection is inserted
 3964    // before the empty selections but replaces the selection that is non-empty.
 3965    cx.set_state(indoc! {"
 3966        Tˇhe quick brown
 3967        «foˇ»x jumps over
 3968        tˇhe lazy dog"});
 3969    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3970    cx.assert_editor_state(indoc! {"
 3971        fox jumps over
 3972        Tˇhe quick brown
 3973        fox jumps over
 3974        ˇx jumps over
 3975        fox jumps over
 3976        tˇhe lazy dog"});
 3977}
 3978
 3979#[gpui::test]
 3980async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 3981    init_test(cx, |_| {});
 3982
 3983    let mut cx = EditorTestContext::new(cx).await;
 3984    let language = Arc::new(Language::new(
 3985        LanguageConfig::default(),
 3986        Some(tree_sitter_rust::language()),
 3987    ));
 3988    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3989
 3990    // Cut an indented block, without the leading whitespace.
 3991    cx.set_state(indoc! {"
 3992        const a: B = (
 3993            c(),
 3994            «d(
 3995                e,
 3996                f
 3997            )ˇ»
 3998        );
 3999    "});
 4000    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4001    cx.assert_editor_state(indoc! {"
 4002        const a: B = (
 4003            c(),
 4004            ˇ
 4005        );
 4006    "});
 4007
 4008    // Paste it at the same position.
 4009    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4010    cx.assert_editor_state(indoc! {"
 4011        const a: B = (
 4012            c(),
 4013            d(
 4014                e,
 4015                f
 4016 4017        );
 4018    "});
 4019
 4020    // Paste it at a line with a lower indent level.
 4021    cx.set_state(indoc! {"
 4022        ˇ
 4023        const a: B = (
 4024            c(),
 4025        );
 4026    "});
 4027    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4028    cx.assert_editor_state(indoc! {"
 4029        d(
 4030            e,
 4031            f
 4032 4033        const a: B = (
 4034            c(),
 4035        );
 4036    "});
 4037
 4038    // Cut an indented block, with the leading whitespace.
 4039    cx.set_state(indoc! {"
 4040        const a: B = (
 4041            c(),
 4042        «    d(
 4043                e,
 4044                f
 4045            )
 4046        ˇ»);
 4047    "});
 4048    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4049    cx.assert_editor_state(indoc! {"
 4050        const a: B = (
 4051            c(),
 4052        ˇ);
 4053    "});
 4054
 4055    // Paste it at the same position.
 4056    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4057    cx.assert_editor_state(indoc! {"
 4058        const a: B = (
 4059            c(),
 4060            d(
 4061                e,
 4062                f
 4063            )
 4064        ˇ);
 4065    "});
 4066
 4067    // Paste it at a line with a higher indent level.
 4068    cx.set_state(indoc! {"
 4069        const a: B = (
 4070            c(),
 4071            d(
 4072                e,
 4073 4074            )
 4075        );
 4076    "});
 4077    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4078    cx.assert_editor_state(indoc! {"
 4079        const a: B = (
 4080            c(),
 4081            d(
 4082                e,
 4083                f    d(
 4084                    e,
 4085                    f
 4086                )
 4087        ˇ
 4088            )
 4089        );
 4090    "});
 4091}
 4092
 4093#[gpui::test]
 4094fn test_select_all(cx: &mut TestAppContext) {
 4095    init_test(cx, |_| {});
 4096
 4097    let view = cx.add_window(|cx| {
 4098        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4099        build_editor(buffer, cx)
 4100    });
 4101    _ = view.update(cx, |view, cx| {
 4102        view.select_all(&SelectAll, cx);
 4103        assert_eq!(
 4104            view.selections.display_ranges(cx),
 4105            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4106        );
 4107    });
 4108}
 4109
 4110#[gpui::test]
 4111fn test_select_line(cx: &mut TestAppContext) {
 4112    init_test(cx, |_| {});
 4113
 4114    let view = cx.add_window(|cx| {
 4115        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4116        build_editor(buffer, cx)
 4117    });
 4118    _ = view.update(cx, |view, cx| {
 4119        view.change_selections(None, cx, |s| {
 4120            s.select_display_ranges([
 4121                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4122                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4123                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4124                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4125            ])
 4126        });
 4127        view.select_line(&SelectLine, cx);
 4128        assert_eq!(
 4129            view.selections.display_ranges(cx),
 4130            vec![
 4131                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4132                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4133            ]
 4134        );
 4135    });
 4136
 4137    _ = view.update(cx, |view, cx| {
 4138        view.select_line(&SelectLine, cx);
 4139        assert_eq!(
 4140            view.selections.display_ranges(cx),
 4141            vec![
 4142                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4143                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4144            ]
 4145        );
 4146    });
 4147
 4148    _ = view.update(cx, |view, cx| {
 4149        view.select_line(&SelectLine, cx);
 4150        assert_eq!(
 4151            view.selections.display_ranges(cx),
 4152            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4153        );
 4154    });
 4155}
 4156
 4157#[gpui::test]
 4158fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4159    init_test(cx, |_| {});
 4160
 4161    let view = cx.add_window(|cx| {
 4162        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4163        build_editor(buffer, cx)
 4164    });
 4165    _ = view.update(cx, |view, cx| {
 4166        view.fold_ranges(
 4167            vec![
 4168                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4169                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4170                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4171            ],
 4172            true,
 4173            cx,
 4174        );
 4175        view.change_selections(None, cx, |s| {
 4176            s.select_display_ranges([
 4177                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4178                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4179                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4180                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4181            ])
 4182        });
 4183        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4184    });
 4185
 4186    _ = view.update(cx, |view, cx| {
 4187        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4188        assert_eq!(
 4189            view.display_text(cx),
 4190            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4191        );
 4192        assert_eq!(
 4193            view.selections.display_ranges(cx),
 4194            [
 4195                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4196                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4197                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4198                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4199            ]
 4200        );
 4201    });
 4202
 4203    _ = view.update(cx, |view, cx| {
 4204        view.change_selections(None, cx, |s| {
 4205            s.select_display_ranges([
 4206                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4207            ])
 4208        });
 4209        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4210        assert_eq!(
 4211            view.display_text(cx),
 4212            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4213        );
 4214        assert_eq!(
 4215            view.selections.display_ranges(cx),
 4216            [
 4217                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4218                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4219                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4220                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4221                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4222                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4223                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4224                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4225            ]
 4226        );
 4227    });
 4228}
 4229
 4230#[gpui::test]
 4231async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4232    init_test(cx, |_| {});
 4233
 4234    let mut cx = EditorTestContext::new(cx).await;
 4235
 4236    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4237    cx.set_state(indoc!(
 4238        r#"abc
 4239           defˇghi
 4240
 4241           jk
 4242           nlmo
 4243           "#
 4244    ));
 4245
 4246    cx.update_editor(|editor, cx| {
 4247        editor.add_selection_above(&Default::default(), cx);
 4248    });
 4249
 4250    cx.assert_editor_state(indoc!(
 4251        r#"abcˇ
 4252           defˇghi
 4253
 4254           jk
 4255           nlmo
 4256           "#
 4257    ));
 4258
 4259    cx.update_editor(|editor, cx| {
 4260        editor.add_selection_above(&Default::default(), cx);
 4261    });
 4262
 4263    cx.assert_editor_state(indoc!(
 4264        r#"abcˇ
 4265            defˇghi
 4266
 4267            jk
 4268            nlmo
 4269            "#
 4270    ));
 4271
 4272    cx.update_editor(|view, cx| {
 4273        view.add_selection_below(&Default::default(), cx);
 4274    });
 4275
 4276    cx.assert_editor_state(indoc!(
 4277        r#"abc
 4278           defˇghi
 4279
 4280           jk
 4281           nlmo
 4282           "#
 4283    ));
 4284
 4285    cx.update_editor(|view, cx| {
 4286        view.undo_selection(&Default::default(), cx);
 4287    });
 4288
 4289    cx.assert_editor_state(indoc!(
 4290        r#"abcˇ
 4291           defˇghi
 4292
 4293           jk
 4294           nlmo
 4295           "#
 4296    ));
 4297
 4298    cx.update_editor(|view, cx| {
 4299        view.redo_selection(&Default::default(), cx);
 4300    });
 4301
 4302    cx.assert_editor_state(indoc!(
 4303        r#"abc
 4304           defˇghi
 4305
 4306           jk
 4307           nlmo
 4308           "#
 4309    ));
 4310
 4311    cx.update_editor(|view, cx| {
 4312        view.add_selection_below(&Default::default(), cx);
 4313    });
 4314
 4315    cx.assert_editor_state(indoc!(
 4316        r#"abc
 4317           defˇghi
 4318
 4319           jk
 4320           nlmˇo
 4321           "#
 4322    ));
 4323
 4324    cx.update_editor(|view, cx| {
 4325        view.add_selection_below(&Default::default(), cx);
 4326    });
 4327
 4328    cx.assert_editor_state(indoc!(
 4329        r#"abc
 4330           defˇghi
 4331
 4332           jk
 4333           nlmˇo
 4334           "#
 4335    ));
 4336
 4337    // change selections
 4338    cx.set_state(indoc!(
 4339        r#"abc
 4340           def«ˇg»hi
 4341
 4342           jk
 4343           nlmo
 4344           "#
 4345    ));
 4346
 4347    cx.update_editor(|view, cx| {
 4348        view.add_selection_below(&Default::default(), cx);
 4349    });
 4350
 4351    cx.assert_editor_state(indoc!(
 4352        r#"abc
 4353           def«ˇg»hi
 4354
 4355           jk
 4356           nlm«ˇo»
 4357           "#
 4358    ));
 4359
 4360    cx.update_editor(|view, cx| {
 4361        view.add_selection_below(&Default::default(), cx);
 4362    });
 4363
 4364    cx.assert_editor_state(indoc!(
 4365        r#"abc
 4366           def«ˇg»hi
 4367
 4368           jk
 4369           nlm«ˇo»
 4370           "#
 4371    ));
 4372
 4373    cx.update_editor(|view, cx| {
 4374        view.add_selection_above(&Default::default(), cx);
 4375    });
 4376
 4377    cx.assert_editor_state(indoc!(
 4378        r#"abc
 4379           def«ˇg»hi
 4380
 4381           jk
 4382           nlmo
 4383           "#
 4384    ));
 4385
 4386    cx.update_editor(|view, cx| {
 4387        view.add_selection_above(&Default::default(), cx);
 4388    });
 4389
 4390    cx.assert_editor_state(indoc!(
 4391        r#"abc
 4392           def«ˇg»hi
 4393
 4394           jk
 4395           nlmo
 4396           "#
 4397    ));
 4398
 4399    // Change selections again
 4400    cx.set_state(indoc!(
 4401        r#"a«bc
 4402           defgˇ»hi
 4403
 4404           jk
 4405           nlmo
 4406           "#
 4407    ));
 4408
 4409    cx.update_editor(|view, cx| {
 4410        view.add_selection_below(&Default::default(), cx);
 4411    });
 4412
 4413    cx.assert_editor_state(indoc!(
 4414        r#"a«bcˇ»
 4415           d«efgˇ»hi
 4416
 4417           j«kˇ»
 4418           nlmo
 4419           "#
 4420    ));
 4421
 4422    cx.update_editor(|view, cx| {
 4423        view.add_selection_below(&Default::default(), cx);
 4424    });
 4425    cx.assert_editor_state(indoc!(
 4426        r#"a«bcˇ»
 4427           d«efgˇ»hi
 4428
 4429           j«kˇ»
 4430           n«lmoˇ»
 4431           "#
 4432    ));
 4433    cx.update_editor(|view, cx| {
 4434        view.add_selection_above(&Default::default(), cx);
 4435    });
 4436
 4437    cx.assert_editor_state(indoc!(
 4438        r#"a«bcˇ»
 4439           d«efgˇ»hi
 4440
 4441           j«kˇ»
 4442           nlmo
 4443           "#
 4444    ));
 4445
 4446    // Change selections again
 4447    cx.set_state(indoc!(
 4448        r#"abc
 4449           d«ˇefghi
 4450
 4451           jk
 4452           nlm»o
 4453           "#
 4454    ));
 4455
 4456    cx.update_editor(|view, cx| {
 4457        view.add_selection_above(&Default::default(), cx);
 4458    });
 4459
 4460    cx.assert_editor_state(indoc!(
 4461        r#"a«ˇbc»
 4462           d«ˇef»ghi
 4463
 4464           j«ˇk»
 4465           n«ˇlm»o
 4466           "#
 4467    ));
 4468
 4469    cx.update_editor(|view, cx| {
 4470        view.add_selection_below(&Default::default(), cx);
 4471    });
 4472
 4473    cx.assert_editor_state(indoc!(
 4474        r#"abc
 4475           d«ˇef»ghi
 4476
 4477           j«ˇk»
 4478           n«ˇlm»o
 4479           "#
 4480    ));
 4481}
 4482
 4483#[gpui::test]
 4484async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4485    init_test(cx, |_| {});
 4486
 4487    let mut cx = EditorTestContext::new(cx).await;
 4488    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4489
 4490    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4491        .unwrap();
 4492    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4493
 4494    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4495        .unwrap();
 4496    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4497
 4498    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4499    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4500
 4501    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4502    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4503
 4504    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4505        .unwrap();
 4506    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4507
 4508    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4509        .unwrap();
 4510    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4511}
 4512
 4513#[gpui::test]
 4514async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4515    init_test(cx, |_| {});
 4516
 4517    let mut cx = EditorTestContext::new(cx).await;
 4518    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4519
 4520    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4521        .unwrap();
 4522    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4523}
 4524
 4525#[gpui::test]
 4526async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4527    init_test(cx, |_| {});
 4528
 4529    let mut cx = EditorTestContext::new(cx).await;
 4530    cx.set_state(
 4531        r#"let foo = 2;
 4532lˇet foo = 2;
 4533let fooˇ = 2;
 4534let foo = 2;
 4535let foo = ˇ2;"#,
 4536    );
 4537
 4538    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4539        .unwrap();
 4540    cx.assert_editor_state(
 4541        r#"let foo = 2;
 4542«letˇ» foo = 2;
 4543let «fooˇ» = 2;
 4544let foo = 2;
 4545let foo = «2ˇ»;"#,
 4546    );
 4547
 4548    // noop for multiple selections with different contents
 4549    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4550        .unwrap();
 4551    cx.assert_editor_state(
 4552        r#"let foo = 2;
 4553«letˇ» foo = 2;
 4554let «fooˇ» = 2;
 4555let foo = 2;
 4556let foo = «2ˇ»;"#,
 4557    );
 4558}
 4559
 4560#[gpui::test]
 4561async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4562    init_test(cx, |_| {});
 4563
 4564    let mut cx = EditorTestContext::new_multibuffer(
 4565        cx,
 4566        [
 4567            &indoc! {
 4568                "aaa\n«bbb\nccc\n»ddd"
 4569            },
 4570            &indoc! {
 4571                "aaa\n«bbb\nccc\n»ddd"
 4572            },
 4573        ],
 4574    );
 4575
 4576    cx.assert_editor_state(indoc! {"
 4577        ˇbbb
 4578        ccc
 4579
 4580        bbb
 4581        ccc
 4582        "});
 4583    cx.dispatch_action(SelectPrevious::default());
 4584    cx.assert_editor_state(indoc! {"
 4585                «bbbˇ»
 4586                ccc
 4587
 4588                bbb
 4589                ccc
 4590                "});
 4591    cx.dispatch_action(SelectPrevious::default());
 4592    cx.assert_editor_state(indoc! {"
 4593                «bbbˇ»
 4594                ccc
 4595
 4596                «bbbˇ»
 4597                ccc
 4598                "});
 4599}
 4600
 4601#[gpui::test]
 4602async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 4603    init_test(cx, |_| {});
 4604
 4605    let mut cx = EditorTestContext::new(cx).await;
 4606    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4607
 4608    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4609        .unwrap();
 4610    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4611
 4612    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4613        .unwrap();
 4614    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4615
 4616    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4617    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4618
 4619    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4620    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4621
 4622    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4623        .unwrap();
 4624    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 4625
 4626    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4627        .unwrap();
 4628    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 4629
 4630    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4631        .unwrap();
 4632    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4633}
 4634
 4635#[gpui::test]
 4636async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4637    init_test(cx, |_| {});
 4638
 4639    let mut cx = EditorTestContext::new(cx).await;
 4640    cx.set_state(
 4641        r#"let foo = 2;
 4642lˇet foo = 2;
 4643let fooˇ = 2;
 4644let foo = 2;
 4645let foo = ˇ2;"#,
 4646    );
 4647
 4648    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4649        .unwrap();
 4650    cx.assert_editor_state(
 4651        r#"let foo = 2;
 4652«letˇ» foo = 2;
 4653let «fooˇ» = 2;
 4654let foo = 2;
 4655let foo = «2ˇ»;"#,
 4656    );
 4657
 4658    // noop for multiple selections with different contents
 4659    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4660        .unwrap();
 4661    cx.assert_editor_state(
 4662        r#"let foo = 2;
 4663«letˇ» foo = 2;
 4664let «fooˇ» = 2;
 4665let foo = 2;
 4666let foo = «2ˇ»;"#,
 4667    );
 4668}
 4669
 4670#[gpui::test]
 4671async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 4672    init_test(cx, |_| {});
 4673
 4674    let mut cx = EditorTestContext::new(cx).await;
 4675    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 4676
 4677    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4678        .unwrap();
 4679    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4680
 4681    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4682        .unwrap();
 4683    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4684
 4685    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4686    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4687
 4688    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4689    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4690
 4691    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4692        .unwrap();
 4693    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 4694
 4695    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4696        .unwrap();
 4697    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4698}
 4699
 4700#[gpui::test]
 4701async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 4702    init_test(cx, |_| {});
 4703
 4704    let language = Arc::new(Language::new(
 4705        LanguageConfig::default(),
 4706        Some(tree_sitter_rust::language()),
 4707    ));
 4708
 4709    let text = r#"
 4710        use mod1::mod2::{mod3, mod4};
 4711
 4712        fn fn_1(param1: bool, param2: &str) {
 4713            let var1 = "text";
 4714        }
 4715    "#
 4716    .unindent();
 4717
 4718    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4719    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4720    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4721
 4722    editor
 4723        .condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 4724        .await;
 4725
 4726    editor.update(cx, |view, cx| {
 4727        view.change_selections(None, cx, |s| {
 4728            s.select_display_ranges([
 4729                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4730                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4731                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4732            ]);
 4733        });
 4734        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4735    });
 4736    editor.update(cx, |editor, cx| {
 4737        assert_text_with_selections(
 4738            editor,
 4739            indoc! {r#"
 4740                use mod1::mod2::{mod3, «mod4ˇ»};
 4741
 4742                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4743                    let var1 = "«textˇ»";
 4744                }
 4745            "#},
 4746            cx,
 4747        );
 4748    });
 4749
 4750    editor.update(cx, |view, cx| {
 4751        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4752    });
 4753    editor.update(cx, |editor, cx| {
 4754        assert_text_with_selections(
 4755            editor,
 4756            indoc! {r#"
 4757                use mod1::mod2::«{mod3, mod4}ˇ»;
 4758
 4759                «ˇfn fn_1(param1: bool, param2: &str) {
 4760                    let var1 = "text";
 4761 4762            "#},
 4763            cx,
 4764        );
 4765    });
 4766
 4767    editor.update(cx, |view, cx| {
 4768        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4769    });
 4770    assert_eq!(
 4771        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4772        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4773    );
 4774
 4775    // Trying to expand the selected syntax node one more time has no effect.
 4776    editor.update(cx, |view, cx| {
 4777        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4778    });
 4779    assert_eq!(
 4780        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4781        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4782    );
 4783
 4784    editor.update(cx, |view, cx| {
 4785        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4786    });
 4787    editor.update(cx, |editor, cx| {
 4788        assert_text_with_selections(
 4789            editor,
 4790            indoc! {r#"
 4791                use mod1::mod2::«{mod3, mod4}ˇ»;
 4792
 4793                «ˇfn fn_1(param1: bool, param2: &str) {
 4794                    let var1 = "text";
 4795 4796            "#},
 4797            cx,
 4798        );
 4799    });
 4800
 4801    editor.update(cx, |view, cx| {
 4802        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4803    });
 4804    editor.update(cx, |editor, cx| {
 4805        assert_text_with_selections(
 4806            editor,
 4807            indoc! {r#"
 4808                use mod1::mod2::{mod3, «mod4ˇ»};
 4809
 4810                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4811                    let var1 = "«textˇ»";
 4812                }
 4813            "#},
 4814            cx,
 4815        );
 4816    });
 4817
 4818    editor.update(cx, |view, cx| {
 4819        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4820    });
 4821    editor.update(cx, |editor, cx| {
 4822        assert_text_with_selections(
 4823            editor,
 4824            indoc! {r#"
 4825                use mod1::mod2::{mod3, mo«ˇ»d4};
 4826
 4827                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 4828                    let var1 = "te«ˇ»xt";
 4829                }
 4830            "#},
 4831            cx,
 4832        );
 4833    });
 4834
 4835    // Trying to shrink the selected syntax node one more time has no effect.
 4836    editor.update(cx, |view, cx| {
 4837        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4838    });
 4839    editor.update(cx, |editor, cx| {
 4840        assert_text_with_selections(
 4841            editor,
 4842            indoc! {r#"
 4843                use mod1::mod2::{mod3, mo«ˇ»d4};
 4844
 4845                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 4846                    let var1 = "te«ˇ»xt";
 4847                }
 4848            "#},
 4849            cx,
 4850        );
 4851    });
 4852
 4853    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 4854    // a fold.
 4855    editor.update(cx, |view, cx| {
 4856        view.fold_ranges(
 4857            vec![
 4858                (
 4859                    Point::new(0, 21)..Point::new(0, 24),
 4860                    FoldPlaceholder::test(),
 4861                ),
 4862                (
 4863                    Point::new(3, 20)..Point::new(3, 22),
 4864                    FoldPlaceholder::test(),
 4865                ),
 4866            ],
 4867            true,
 4868            cx,
 4869        );
 4870        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4871    });
 4872    editor.update(cx, |editor, cx| {
 4873        assert_text_with_selections(
 4874            editor,
 4875            indoc! {r#"
 4876                use mod1::mod2::«{mod3, mod4}ˇ»;
 4877
 4878                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4879                    «let var1 = "text";ˇ»
 4880                }
 4881            "#},
 4882            cx,
 4883        );
 4884    });
 4885}
 4886
 4887#[gpui::test]
 4888async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 4889    init_test(cx, |_| {});
 4890
 4891    let language = Arc::new(
 4892        Language::new(
 4893            LanguageConfig {
 4894                brackets: BracketPairConfig {
 4895                    pairs: vec![
 4896                        BracketPair {
 4897                            start: "{".to_string(),
 4898                            end: "}".to_string(),
 4899                            close: false,
 4900                            surround: false,
 4901                            newline: true,
 4902                        },
 4903                        BracketPair {
 4904                            start: "(".to_string(),
 4905                            end: ")".to_string(),
 4906                            close: false,
 4907                            surround: false,
 4908                            newline: true,
 4909                        },
 4910                    ],
 4911                    ..Default::default()
 4912                },
 4913                ..Default::default()
 4914            },
 4915            Some(tree_sitter_rust::language()),
 4916        )
 4917        .with_indents_query(
 4918            r#"
 4919                (_ "(" ")" @end) @indent
 4920                (_ "{" "}" @end) @indent
 4921            "#,
 4922        )
 4923        .unwrap(),
 4924    );
 4925
 4926    let text = "fn a() {}";
 4927
 4928    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4929    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4930    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4931    editor
 4932        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 4933        .await;
 4934
 4935    _ = editor.update(cx, |editor, cx| {
 4936        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 4937        editor.newline(&Newline, cx);
 4938        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 4939        assert_eq!(
 4940            editor.selections.ranges(cx),
 4941            &[
 4942                Point::new(1, 4)..Point::new(1, 4),
 4943                Point::new(3, 4)..Point::new(3, 4),
 4944                Point::new(5, 0)..Point::new(5, 0)
 4945            ]
 4946        );
 4947    });
 4948}
 4949
 4950#[gpui::test]
 4951async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 4952    init_test(cx, |_| {});
 4953
 4954    let mut cx = EditorTestContext::new(cx).await;
 4955
 4956    let language = Arc::new(Language::new(
 4957        LanguageConfig {
 4958            brackets: BracketPairConfig {
 4959                pairs: vec![
 4960                    BracketPair {
 4961                        start: "{".to_string(),
 4962                        end: "}".to_string(),
 4963                        close: true,
 4964                        surround: true,
 4965                        newline: true,
 4966                    },
 4967                    BracketPair {
 4968                        start: "(".to_string(),
 4969                        end: ")".to_string(),
 4970                        close: true,
 4971                        surround: true,
 4972                        newline: true,
 4973                    },
 4974                    BracketPair {
 4975                        start: "/*".to_string(),
 4976                        end: " */".to_string(),
 4977                        close: true,
 4978                        surround: true,
 4979                        newline: true,
 4980                    },
 4981                    BracketPair {
 4982                        start: "[".to_string(),
 4983                        end: "]".to_string(),
 4984                        close: false,
 4985                        surround: false,
 4986                        newline: true,
 4987                    },
 4988                    BracketPair {
 4989                        start: "\"".to_string(),
 4990                        end: "\"".to_string(),
 4991                        close: true,
 4992                        surround: true,
 4993                        newline: false,
 4994                    },
 4995                    BracketPair {
 4996                        start: "<".to_string(),
 4997                        end: ">".to_string(),
 4998                        close: false,
 4999                        surround: true,
 5000                        newline: true,
 5001                    },
 5002                ],
 5003                ..Default::default()
 5004            },
 5005            autoclose_before: "})]".to_string(),
 5006            ..Default::default()
 5007        },
 5008        Some(tree_sitter_rust::language()),
 5009    ));
 5010
 5011    cx.language_registry().add(language.clone());
 5012    cx.update_buffer(|buffer, cx| {
 5013        buffer.set_language(Some(language), cx);
 5014    });
 5015
 5016    cx.set_state(
 5017        &r#"
 5018            🏀ˇ
 5019            εˇ
 5020            ❤️ˇ
 5021        "#
 5022        .unindent(),
 5023    );
 5024
 5025    // autoclose multiple nested brackets at multiple cursors
 5026    cx.update_editor(|view, cx| {
 5027        view.handle_input("{", cx);
 5028        view.handle_input("{", cx);
 5029        view.handle_input("{", cx);
 5030    });
 5031    cx.assert_editor_state(
 5032        &"
 5033            🏀{{{ˇ}}}
 5034            ε{{{ˇ}}}
 5035            ❤️{{{ˇ}}}
 5036        "
 5037        .unindent(),
 5038    );
 5039
 5040    // insert a different closing bracket
 5041    cx.update_editor(|view, cx| {
 5042        view.handle_input(")", cx);
 5043    });
 5044    cx.assert_editor_state(
 5045        &"
 5046            🏀{{{)ˇ}}}
 5047            ε{{{)ˇ}}}
 5048            ❤️{{{)ˇ}}}
 5049        "
 5050        .unindent(),
 5051    );
 5052
 5053    // skip over the auto-closed brackets when typing a closing bracket
 5054    cx.update_editor(|view, cx| {
 5055        view.move_right(&MoveRight, cx);
 5056        view.handle_input("}", cx);
 5057        view.handle_input("}", cx);
 5058        view.handle_input("}", cx);
 5059    });
 5060    cx.assert_editor_state(
 5061        &"
 5062            🏀{{{)}}}}ˇ
 5063            ε{{{)}}}}ˇ
 5064            ❤️{{{)}}}}ˇ
 5065        "
 5066        .unindent(),
 5067    );
 5068
 5069    // autoclose multi-character pairs
 5070    cx.set_state(
 5071        &"
 5072            ˇ
 5073            ˇ
 5074        "
 5075        .unindent(),
 5076    );
 5077    cx.update_editor(|view, cx| {
 5078        view.handle_input("/", cx);
 5079        view.handle_input("*", cx);
 5080    });
 5081    cx.assert_editor_state(
 5082        &"
 5083            /*ˇ */
 5084            /*ˇ */
 5085        "
 5086        .unindent(),
 5087    );
 5088
 5089    // one cursor autocloses a multi-character pair, one cursor
 5090    // does not autoclose.
 5091    cx.set_state(
 5092        &"
 5093 5094            ˇ
 5095        "
 5096        .unindent(),
 5097    );
 5098    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5099    cx.assert_editor_state(
 5100        &"
 5101            /*ˇ */
 5102 5103        "
 5104        .unindent(),
 5105    );
 5106
 5107    // Don't autoclose if the next character isn't whitespace and isn't
 5108    // listed in the language's "autoclose_before" section.
 5109    cx.set_state("ˇa b");
 5110    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5111    cx.assert_editor_state("{ˇa b");
 5112
 5113    // Don't autoclose if `close` is false for the bracket pair
 5114    cx.set_state("ˇ");
 5115    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5116    cx.assert_editor_state("");
 5117
 5118    // Surround with brackets if text is selected
 5119    cx.set_state("«aˇ» b");
 5120    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5121    cx.assert_editor_state("{«aˇ»} b");
 5122
 5123    // Autclose pair where the start and end characters are the same
 5124    cx.set_state("");
 5125    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5126    cx.assert_editor_state("a\"ˇ\"");
 5127    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5128    cx.assert_editor_state("a\"\"ˇ");
 5129
 5130    // Don't autoclose pair if autoclose is disabled
 5131    cx.set_state("ˇ");
 5132    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5133    cx.assert_editor_state("");
 5134
 5135    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5136    cx.set_state("«aˇ» b");
 5137    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5138    cx.assert_editor_state("<«aˇ»> b");
 5139}
 5140
 5141#[gpui::test]
 5142async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5143    init_test(cx, |settings| {
 5144        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5145    });
 5146
 5147    let mut cx = EditorTestContext::new(cx).await;
 5148
 5149    let language = Arc::new(Language::new(
 5150        LanguageConfig {
 5151            brackets: BracketPairConfig {
 5152                pairs: vec![
 5153                    BracketPair {
 5154                        start: "{".to_string(),
 5155                        end: "}".to_string(),
 5156                        close: true,
 5157                        surround: true,
 5158                        newline: true,
 5159                    },
 5160                    BracketPair {
 5161                        start: "(".to_string(),
 5162                        end: ")".to_string(),
 5163                        close: true,
 5164                        surround: true,
 5165                        newline: true,
 5166                    },
 5167                    BracketPair {
 5168                        start: "[".to_string(),
 5169                        end: "]".to_string(),
 5170                        close: false,
 5171                        surround: false,
 5172                        newline: true,
 5173                    },
 5174                ],
 5175                ..Default::default()
 5176            },
 5177            autoclose_before: "})]".to_string(),
 5178            ..Default::default()
 5179        },
 5180        Some(tree_sitter_rust::language()),
 5181    ));
 5182
 5183    cx.language_registry().add(language.clone());
 5184    cx.update_buffer(|buffer, cx| {
 5185        buffer.set_language(Some(language), cx);
 5186    });
 5187
 5188    cx.set_state(
 5189        &"
 5190            ˇ
 5191            ˇ
 5192            ˇ
 5193        "
 5194        .unindent(),
 5195    );
 5196
 5197    // ensure only matching closing brackets are skipped over
 5198    cx.update_editor(|view, cx| {
 5199        view.handle_input("}", cx);
 5200        view.move_left(&MoveLeft, cx);
 5201        view.handle_input(")", cx);
 5202        view.move_left(&MoveLeft, cx);
 5203    });
 5204    cx.assert_editor_state(
 5205        &"
 5206            ˇ)}
 5207            ˇ)}
 5208            ˇ)}
 5209        "
 5210        .unindent(),
 5211    );
 5212
 5213    // skip-over closing brackets at multiple cursors
 5214    cx.update_editor(|view, cx| {
 5215        view.handle_input(")", cx);
 5216        view.handle_input("}", cx);
 5217    });
 5218    cx.assert_editor_state(
 5219        &"
 5220            )}ˇ
 5221            )}ˇ
 5222            )}ˇ
 5223        "
 5224        .unindent(),
 5225    );
 5226
 5227    // ignore non-close brackets
 5228    cx.update_editor(|view, cx| {
 5229        view.handle_input("]", cx);
 5230        view.move_left(&MoveLeft, cx);
 5231        view.handle_input("]", cx);
 5232    });
 5233    cx.assert_editor_state(
 5234        &"
 5235            )}]ˇ]
 5236            )}]ˇ]
 5237            )}]ˇ]
 5238        "
 5239        .unindent(),
 5240    );
 5241}
 5242
 5243#[gpui::test]
 5244async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5245    init_test(cx, |_| {});
 5246
 5247    let mut cx = EditorTestContext::new(cx).await;
 5248
 5249    let html_language = Arc::new(
 5250        Language::new(
 5251            LanguageConfig {
 5252                name: "HTML".into(),
 5253                brackets: BracketPairConfig {
 5254                    pairs: vec![
 5255                        BracketPair {
 5256                            start: "<".into(),
 5257                            end: ">".into(),
 5258                            close: true,
 5259                            ..Default::default()
 5260                        },
 5261                        BracketPair {
 5262                            start: "{".into(),
 5263                            end: "}".into(),
 5264                            close: true,
 5265                            ..Default::default()
 5266                        },
 5267                        BracketPair {
 5268                            start: "(".into(),
 5269                            end: ")".into(),
 5270                            close: true,
 5271                            ..Default::default()
 5272                        },
 5273                    ],
 5274                    ..Default::default()
 5275                },
 5276                autoclose_before: "})]>".into(),
 5277                ..Default::default()
 5278            },
 5279            Some(tree_sitter_html::language()),
 5280        )
 5281        .with_injection_query(
 5282            r#"
 5283            (script_element
 5284                (raw_text) @content
 5285                (#set! "language" "javascript"))
 5286            "#,
 5287        )
 5288        .unwrap(),
 5289    );
 5290
 5291    let javascript_language = Arc::new(Language::new(
 5292        LanguageConfig {
 5293            name: "JavaScript".into(),
 5294            brackets: BracketPairConfig {
 5295                pairs: vec![
 5296                    BracketPair {
 5297                        start: "/*".into(),
 5298                        end: " */".into(),
 5299                        close: true,
 5300                        ..Default::default()
 5301                    },
 5302                    BracketPair {
 5303                        start: "{".into(),
 5304                        end: "}".into(),
 5305                        close: true,
 5306                        ..Default::default()
 5307                    },
 5308                    BracketPair {
 5309                        start: "(".into(),
 5310                        end: ")".into(),
 5311                        close: true,
 5312                        ..Default::default()
 5313                    },
 5314                ],
 5315                ..Default::default()
 5316            },
 5317            autoclose_before: "})]>".into(),
 5318            ..Default::default()
 5319        },
 5320        Some(tree_sitter_typescript::language_tsx()),
 5321    ));
 5322
 5323    cx.language_registry().add(html_language.clone());
 5324    cx.language_registry().add(javascript_language.clone());
 5325
 5326    cx.update_buffer(|buffer, cx| {
 5327        buffer.set_language(Some(html_language), cx);
 5328    });
 5329
 5330    cx.set_state(
 5331        &r#"
 5332            <body>ˇ
 5333                <script>
 5334                    var x = 1;ˇ
 5335                </script>
 5336            </body>ˇ
 5337        "#
 5338        .unindent(),
 5339    );
 5340
 5341    // Precondition: different languages are active at different locations.
 5342    cx.update_editor(|editor, cx| {
 5343        let snapshot = editor.snapshot(cx);
 5344        let cursors = editor.selections.ranges::<usize>(cx);
 5345        let languages = cursors
 5346            .iter()
 5347            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5348            .collect::<Vec<_>>();
 5349        assert_eq!(
 5350            languages,
 5351            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5352        );
 5353    });
 5354
 5355    // Angle brackets autoclose in HTML, but not JavaScript.
 5356    cx.update_editor(|editor, cx| {
 5357        editor.handle_input("<", cx);
 5358        editor.handle_input("a", cx);
 5359    });
 5360    cx.assert_editor_state(
 5361        &r#"
 5362            <body><aˇ>
 5363                <script>
 5364                    var x = 1;<aˇ
 5365                </script>
 5366            </body><aˇ>
 5367        "#
 5368        .unindent(),
 5369    );
 5370
 5371    // Curly braces and parens autoclose in both HTML and JavaScript.
 5372    cx.update_editor(|editor, cx| {
 5373        editor.handle_input(" b=", cx);
 5374        editor.handle_input("{", cx);
 5375        editor.handle_input("c", cx);
 5376        editor.handle_input("(", cx);
 5377    });
 5378    cx.assert_editor_state(
 5379        &r#"
 5380            <body><a b={c(ˇ)}>
 5381                <script>
 5382                    var x = 1;<a b={c(ˇ)}
 5383                </script>
 5384            </body><a b={c(ˇ)}>
 5385        "#
 5386        .unindent(),
 5387    );
 5388
 5389    // Brackets that were already autoclosed are skipped.
 5390    cx.update_editor(|editor, cx| {
 5391        editor.handle_input(")", cx);
 5392        editor.handle_input("d", cx);
 5393        editor.handle_input("}", cx);
 5394    });
 5395    cx.assert_editor_state(
 5396        &r#"
 5397            <body><a b={c()d}ˇ>
 5398                <script>
 5399                    var x = 1;<a b={c()d}ˇ
 5400                </script>
 5401            </body><a b={c()d}ˇ>
 5402        "#
 5403        .unindent(),
 5404    );
 5405    cx.update_editor(|editor, cx| {
 5406        editor.handle_input(">", cx);
 5407    });
 5408    cx.assert_editor_state(
 5409        &r#"
 5410            <body><a b={c()d}>ˇ
 5411                <script>
 5412                    var x = 1;<a b={c()d}>ˇ
 5413                </script>
 5414            </body><a b={c()d}>ˇ
 5415        "#
 5416        .unindent(),
 5417    );
 5418
 5419    // Reset
 5420    cx.set_state(
 5421        &r#"
 5422            <body>ˇ
 5423                <script>
 5424                    var x = 1;ˇ
 5425                </script>
 5426            </body>ˇ
 5427        "#
 5428        .unindent(),
 5429    );
 5430
 5431    cx.update_editor(|editor, cx| {
 5432        editor.handle_input("<", cx);
 5433    });
 5434    cx.assert_editor_state(
 5435        &r#"
 5436            <body><ˇ>
 5437                <script>
 5438                    var x = 1;<ˇ
 5439                </script>
 5440            </body><ˇ>
 5441        "#
 5442        .unindent(),
 5443    );
 5444
 5445    // When backspacing, the closing angle brackets are removed.
 5446    cx.update_editor(|editor, cx| {
 5447        editor.backspace(&Backspace, cx);
 5448    });
 5449    cx.assert_editor_state(
 5450        &r#"
 5451            <body>ˇ
 5452                <script>
 5453                    var x = 1;ˇ
 5454                </script>
 5455            </body>ˇ
 5456        "#
 5457        .unindent(),
 5458    );
 5459
 5460    // Block comments autoclose in JavaScript, but not HTML.
 5461    cx.update_editor(|editor, cx| {
 5462        editor.handle_input("/", cx);
 5463        editor.handle_input("*", cx);
 5464    });
 5465    cx.assert_editor_state(
 5466        &r#"
 5467            <body>/*ˇ
 5468                <script>
 5469                    var x = 1;/*ˇ */
 5470                </script>
 5471            </body>/*ˇ
 5472        "#
 5473        .unindent(),
 5474    );
 5475}
 5476
 5477#[gpui::test]
 5478async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5479    init_test(cx, |_| {});
 5480
 5481    let mut cx = EditorTestContext::new(cx).await;
 5482
 5483    let rust_language = Arc::new(
 5484        Language::new(
 5485            LanguageConfig {
 5486                name: "Rust".into(),
 5487                brackets: serde_json::from_value(json!([
 5488                    { "start": "{", "end": "}", "close": true, "newline": true },
 5489                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5490                ]))
 5491                .unwrap(),
 5492                autoclose_before: "})]>".into(),
 5493                ..Default::default()
 5494            },
 5495            Some(tree_sitter_rust::language()),
 5496        )
 5497        .with_override_query("(string_literal) @string")
 5498        .unwrap(),
 5499    );
 5500
 5501    cx.language_registry().add(rust_language.clone());
 5502    cx.update_buffer(|buffer, cx| {
 5503        buffer.set_language(Some(rust_language), cx);
 5504    });
 5505
 5506    cx.set_state(
 5507        &r#"
 5508            let x = ˇ
 5509        "#
 5510        .unindent(),
 5511    );
 5512
 5513    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5514    cx.update_editor(|editor, cx| {
 5515        editor.handle_input("\"", cx);
 5516    });
 5517    cx.assert_editor_state(
 5518        &r#"
 5519            let x = "ˇ"
 5520        "#
 5521        .unindent(),
 5522    );
 5523
 5524    // Inserting another quotation mark. The cursor moves across the existing
 5525    // automatically-inserted quotation mark.
 5526    cx.update_editor(|editor, cx| {
 5527        editor.handle_input("\"", cx);
 5528    });
 5529    cx.assert_editor_state(
 5530        &r#"
 5531            let x = ""ˇ
 5532        "#
 5533        .unindent(),
 5534    );
 5535
 5536    // Reset
 5537    cx.set_state(
 5538        &r#"
 5539            let x = ˇ
 5540        "#
 5541        .unindent(),
 5542    );
 5543
 5544    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5545    cx.update_editor(|editor, cx| {
 5546        editor.handle_input("\"", cx);
 5547        editor.handle_input(" ", cx);
 5548        editor.move_left(&Default::default(), cx);
 5549        editor.handle_input("\\", cx);
 5550        editor.handle_input("\"", cx);
 5551    });
 5552    cx.assert_editor_state(
 5553        &r#"
 5554            let x = "\"ˇ "
 5555        "#
 5556        .unindent(),
 5557    );
 5558
 5559    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5560    // mark. Nothing is inserted.
 5561    cx.update_editor(|editor, cx| {
 5562        editor.move_right(&Default::default(), cx);
 5563        editor.handle_input("\"", cx);
 5564    });
 5565    cx.assert_editor_state(
 5566        &r#"
 5567            let x = "\" "ˇ
 5568        "#
 5569        .unindent(),
 5570    );
 5571}
 5572
 5573#[gpui::test]
 5574async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5575    init_test(cx, |_| {});
 5576
 5577    let language = Arc::new(Language::new(
 5578        LanguageConfig {
 5579            brackets: BracketPairConfig {
 5580                pairs: vec![
 5581                    BracketPair {
 5582                        start: "{".to_string(),
 5583                        end: "}".to_string(),
 5584                        close: true,
 5585                        surround: true,
 5586                        newline: true,
 5587                    },
 5588                    BracketPair {
 5589                        start: "/* ".to_string(),
 5590                        end: "*/".to_string(),
 5591                        close: true,
 5592                        surround: true,
 5593                        ..Default::default()
 5594                    },
 5595                ],
 5596                ..Default::default()
 5597            },
 5598            ..Default::default()
 5599        },
 5600        Some(tree_sitter_rust::language()),
 5601    ));
 5602
 5603    let text = r#"
 5604        a
 5605        b
 5606        c
 5607    "#
 5608    .unindent();
 5609
 5610    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5611    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5612    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5613    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5614        .await;
 5615
 5616    _ = view.update(cx, |view, cx| {
 5617        view.change_selections(None, cx, |s| {
 5618            s.select_display_ranges([
 5619                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5620                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5621                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5622            ])
 5623        });
 5624
 5625        view.handle_input("{", cx);
 5626        view.handle_input("{", cx);
 5627        view.handle_input("{", cx);
 5628        assert_eq!(
 5629            view.text(cx),
 5630            "
 5631                {{{a}}}
 5632                {{{b}}}
 5633                {{{c}}}
 5634            "
 5635            .unindent()
 5636        );
 5637        assert_eq!(
 5638            view.selections.display_ranges(cx),
 5639            [
 5640                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5641                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5642                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5643            ]
 5644        );
 5645
 5646        view.undo(&Undo, cx);
 5647        view.undo(&Undo, cx);
 5648        view.undo(&Undo, cx);
 5649        assert_eq!(
 5650            view.text(cx),
 5651            "
 5652                a
 5653                b
 5654                c
 5655            "
 5656            .unindent()
 5657        );
 5658        assert_eq!(
 5659            view.selections.display_ranges(cx),
 5660            [
 5661                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5662                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5663                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5664            ]
 5665        );
 5666
 5667        // Ensure inserting the first character of a multi-byte bracket pair
 5668        // doesn't surround the selections with the bracket.
 5669        view.handle_input("/", cx);
 5670        assert_eq!(
 5671            view.text(cx),
 5672            "
 5673                /
 5674                /
 5675                /
 5676            "
 5677            .unindent()
 5678        );
 5679        assert_eq!(
 5680            view.selections.display_ranges(cx),
 5681            [
 5682                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5683                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5684                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5685            ]
 5686        );
 5687
 5688        view.undo(&Undo, cx);
 5689        assert_eq!(
 5690            view.text(cx),
 5691            "
 5692                a
 5693                b
 5694                c
 5695            "
 5696            .unindent()
 5697        );
 5698        assert_eq!(
 5699            view.selections.display_ranges(cx),
 5700            [
 5701                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5702                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5703                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5704            ]
 5705        );
 5706
 5707        // Ensure inserting the last character of a multi-byte bracket pair
 5708        // doesn't surround the selections with the bracket.
 5709        view.handle_input("*", cx);
 5710        assert_eq!(
 5711            view.text(cx),
 5712            "
 5713                *
 5714                *
 5715                *
 5716            "
 5717            .unindent()
 5718        );
 5719        assert_eq!(
 5720            view.selections.display_ranges(cx),
 5721            [
 5722                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5723                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5724                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5725            ]
 5726        );
 5727    });
 5728}
 5729
 5730#[gpui::test]
 5731async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 5732    init_test(cx, |_| {});
 5733
 5734    let language = Arc::new(Language::new(
 5735        LanguageConfig {
 5736            brackets: BracketPairConfig {
 5737                pairs: vec![BracketPair {
 5738                    start: "{".to_string(),
 5739                    end: "}".to_string(),
 5740                    close: true,
 5741                    surround: true,
 5742                    newline: true,
 5743                }],
 5744                ..Default::default()
 5745            },
 5746            autoclose_before: "}".to_string(),
 5747            ..Default::default()
 5748        },
 5749        Some(tree_sitter_rust::language()),
 5750    ));
 5751
 5752    let text = r#"
 5753        a
 5754        b
 5755        c
 5756    "#
 5757    .unindent();
 5758
 5759    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5760    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5761    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5762    editor
 5763        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5764        .await;
 5765
 5766    _ = editor.update(cx, |editor, cx| {
 5767        editor.change_selections(None, cx, |s| {
 5768            s.select_ranges([
 5769                Point::new(0, 1)..Point::new(0, 1),
 5770                Point::new(1, 1)..Point::new(1, 1),
 5771                Point::new(2, 1)..Point::new(2, 1),
 5772            ])
 5773        });
 5774
 5775        editor.handle_input("{", cx);
 5776        editor.handle_input("{", cx);
 5777        editor.handle_input("_", cx);
 5778        assert_eq!(
 5779            editor.text(cx),
 5780            "
 5781                a{{_}}
 5782                b{{_}}
 5783                c{{_}}
 5784            "
 5785            .unindent()
 5786        );
 5787        assert_eq!(
 5788            editor.selections.ranges::<Point>(cx),
 5789            [
 5790                Point::new(0, 4)..Point::new(0, 4),
 5791                Point::new(1, 4)..Point::new(1, 4),
 5792                Point::new(2, 4)..Point::new(2, 4)
 5793            ]
 5794        );
 5795
 5796        editor.backspace(&Default::default(), cx);
 5797        editor.backspace(&Default::default(), cx);
 5798        assert_eq!(
 5799            editor.text(cx),
 5800            "
 5801                a{}
 5802                b{}
 5803                c{}
 5804            "
 5805            .unindent()
 5806        );
 5807        assert_eq!(
 5808            editor.selections.ranges::<Point>(cx),
 5809            [
 5810                Point::new(0, 2)..Point::new(0, 2),
 5811                Point::new(1, 2)..Point::new(1, 2),
 5812                Point::new(2, 2)..Point::new(2, 2)
 5813            ]
 5814        );
 5815
 5816        editor.delete_to_previous_word_start(&Default::default(), cx);
 5817        assert_eq!(
 5818            editor.text(cx),
 5819            "
 5820                a
 5821                b
 5822                c
 5823            "
 5824            .unindent()
 5825        );
 5826        assert_eq!(
 5827            editor.selections.ranges::<Point>(cx),
 5828            [
 5829                Point::new(0, 1)..Point::new(0, 1),
 5830                Point::new(1, 1)..Point::new(1, 1),
 5831                Point::new(2, 1)..Point::new(2, 1)
 5832            ]
 5833        );
 5834    });
 5835}
 5836
 5837#[gpui::test]
 5838async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 5839    init_test(cx, |settings| {
 5840        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5841    });
 5842
 5843    let mut cx = EditorTestContext::new(cx).await;
 5844
 5845    let language = Arc::new(Language::new(
 5846        LanguageConfig {
 5847            brackets: BracketPairConfig {
 5848                pairs: vec![
 5849                    BracketPair {
 5850                        start: "{".to_string(),
 5851                        end: "}".to_string(),
 5852                        close: true,
 5853                        surround: true,
 5854                        newline: true,
 5855                    },
 5856                    BracketPair {
 5857                        start: "(".to_string(),
 5858                        end: ")".to_string(),
 5859                        close: true,
 5860                        surround: true,
 5861                        newline: true,
 5862                    },
 5863                    BracketPair {
 5864                        start: "[".to_string(),
 5865                        end: "]".to_string(),
 5866                        close: false,
 5867                        surround: true,
 5868                        newline: true,
 5869                    },
 5870                ],
 5871                ..Default::default()
 5872            },
 5873            autoclose_before: "})]".to_string(),
 5874            ..Default::default()
 5875        },
 5876        Some(tree_sitter_rust::language()),
 5877    ));
 5878
 5879    cx.language_registry().add(language.clone());
 5880    cx.update_buffer(|buffer, cx| {
 5881        buffer.set_language(Some(language), cx);
 5882    });
 5883
 5884    cx.set_state(
 5885        &"
 5886            {(ˇ)}
 5887            [[ˇ]]
 5888            {(ˇ)}
 5889        "
 5890        .unindent(),
 5891    );
 5892
 5893    cx.update_editor(|view, cx| {
 5894        view.backspace(&Default::default(), cx);
 5895        view.backspace(&Default::default(), cx);
 5896    });
 5897
 5898    cx.assert_editor_state(
 5899        &"
 5900            ˇ
 5901            ˇ]]
 5902            ˇ
 5903        "
 5904        .unindent(),
 5905    );
 5906
 5907    cx.update_editor(|view, cx| {
 5908        view.handle_input("{", cx);
 5909        view.handle_input("{", cx);
 5910        view.move_right(&MoveRight, cx);
 5911        view.move_right(&MoveRight, cx);
 5912        view.move_left(&MoveLeft, cx);
 5913        view.move_left(&MoveLeft, cx);
 5914        view.backspace(&Default::default(), cx);
 5915    });
 5916
 5917    cx.assert_editor_state(
 5918        &"
 5919            {ˇ}
 5920            {ˇ}]]
 5921            {ˇ}
 5922        "
 5923        .unindent(),
 5924    );
 5925
 5926    cx.update_editor(|view, cx| {
 5927        view.backspace(&Default::default(), cx);
 5928    });
 5929
 5930    cx.assert_editor_state(
 5931        &"
 5932            ˇ
 5933            ˇ]]
 5934            ˇ
 5935        "
 5936        .unindent(),
 5937    );
 5938}
 5939
 5940#[gpui::test]
 5941async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 5942    init_test(cx, |_| {});
 5943
 5944    let language = Arc::new(Language::new(
 5945        LanguageConfig::default(),
 5946        Some(tree_sitter_rust::language()),
 5947    ));
 5948
 5949    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 5950    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5951    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5952    editor
 5953        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5954        .await;
 5955
 5956    _ = editor.update(cx, |editor, cx| {
 5957        editor.set_auto_replace_emoji_shortcode(true);
 5958
 5959        editor.handle_input("Hello ", cx);
 5960        editor.handle_input(":wave", cx);
 5961        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 5962
 5963        editor.handle_input(":", cx);
 5964        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 5965
 5966        editor.handle_input(" :smile", cx);
 5967        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 5968
 5969        editor.handle_input(":", cx);
 5970        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 5971
 5972        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 5973        editor.handle_input(":wave", cx);
 5974        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 5975
 5976        editor.handle_input(":", cx);
 5977        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 5978
 5979        editor.handle_input(":1", cx);
 5980        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 5981
 5982        editor.handle_input(":", cx);
 5983        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 5984
 5985        // Ensure shortcode does not get replaced when it is part of a word
 5986        editor.handle_input(" Test:wave", cx);
 5987        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 5988
 5989        editor.handle_input(":", cx);
 5990        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 5991
 5992        editor.set_auto_replace_emoji_shortcode(false);
 5993
 5994        // Ensure shortcode does not get replaced when auto replace is off
 5995        editor.handle_input(" :wave", cx);
 5996        assert_eq!(
 5997            editor.text(cx),
 5998            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 5999        );
 6000
 6001        editor.handle_input(":", cx);
 6002        assert_eq!(
 6003            editor.text(cx),
 6004            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6005        );
 6006    });
 6007}
 6008
 6009#[gpui::test]
 6010async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6011    init_test(cx, |_| {});
 6012
 6013    let (text, insertion_ranges) = marked_text_ranges(
 6014        indoc! {"
 6015            a.ˇ b
 6016            a.ˇ b
 6017            a.ˇ b
 6018        "},
 6019        false,
 6020    );
 6021
 6022    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6023    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6024
 6025    _ = editor.update(cx, |editor, cx| {
 6026        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6027
 6028        editor
 6029            .insert_snippet(&insertion_ranges, snippet, cx)
 6030            .unwrap();
 6031
 6032        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6033            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6034            assert_eq!(editor.text(cx), expected_text);
 6035            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6036        }
 6037
 6038        assert(
 6039            editor,
 6040            cx,
 6041            indoc! {"
 6042                a.f(«one», two, «three») b
 6043                a.f(«one», two, «three») b
 6044                a.f(«one», two, «three») b
 6045            "},
 6046        );
 6047
 6048        // Can't move earlier than the first tab stop
 6049        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6050        assert(
 6051            editor,
 6052            cx,
 6053            indoc! {"
 6054                a.f(«one», two, «three») b
 6055                a.f(«one», two, «three») b
 6056                a.f(«one», two, «three») b
 6057            "},
 6058        );
 6059
 6060        assert!(editor.move_to_next_snippet_tabstop(cx));
 6061        assert(
 6062            editor,
 6063            cx,
 6064            indoc! {"
 6065                a.f(one, «two», three) b
 6066                a.f(one, «two», three) b
 6067                a.f(one, «two», three) b
 6068            "},
 6069        );
 6070
 6071        editor.move_to_prev_snippet_tabstop(cx);
 6072        assert(
 6073            editor,
 6074            cx,
 6075            indoc! {"
 6076                a.f(«one», two, «three») b
 6077                a.f(«one», two, «three») b
 6078                a.f(«one», two, «three») b
 6079            "},
 6080        );
 6081
 6082        assert!(editor.move_to_next_snippet_tabstop(cx));
 6083        assert(
 6084            editor,
 6085            cx,
 6086            indoc! {"
 6087                a.f(one, «two», three) b
 6088                a.f(one, «two», three) b
 6089                a.f(one, «two», three) b
 6090            "},
 6091        );
 6092        assert!(editor.move_to_next_snippet_tabstop(cx));
 6093        assert(
 6094            editor,
 6095            cx,
 6096            indoc! {"
 6097                a.f(one, two, three)ˇ b
 6098                a.f(one, two, three)ˇ b
 6099                a.f(one, two, three)ˇ b
 6100            "},
 6101        );
 6102
 6103        // As soon as the last tab stop is reached, snippet state is gone
 6104        editor.move_to_prev_snippet_tabstop(cx);
 6105        assert(
 6106            editor,
 6107            cx,
 6108            indoc! {"
 6109                a.f(one, two, three)ˇ b
 6110                a.f(one, two, three)ˇ b
 6111                a.f(one, two, three)ˇ b
 6112            "},
 6113        );
 6114    });
 6115}
 6116
 6117#[gpui::test]
 6118async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6119    init_test(cx, |_| {});
 6120
 6121    let fs = FakeFs::new(cx.executor());
 6122    fs.insert_file("/file.rs", Default::default()).await;
 6123
 6124    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6125
 6126    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6127    language_registry.add(rust_lang());
 6128    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6129        "Rust",
 6130        FakeLspAdapter {
 6131            capabilities: lsp::ServerCapabilities {
 6132                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6133                ..Default::default()
 6134            },
 6135            ..Default::default()
 6136        },
 6137    );
 6138
 6139    let buffer = project
 6140        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6141        .await
 6142        .unwrap();
 6143
 6144    cx.executor().start_waiting();
 6145    let fake_server = fake_servers.next().await.unwrap();
 6146
 6147    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6148    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6149    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6150    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6151
 6152    let save = editor
 6153        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6154        .unwrap();
 6155    fake_server
 6156        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6157            assert_eq!(
 6158                params.text_document.uri,
 6159                lsp::Url::from_file_path("/file.rs").unwrap()
 6160            );
 6161            assert_eq!(params.options.tab_size, 4);
 6162            Ok(Some(vec![lsp::TextEdit::new(
 6163                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6164                ", ".to_string(),
 6165            )]))
 6166        })
 6167        .next()
 6168        .await;
 6169    cx.executor().start_waiting();
 6170    save.await;
 6171
 6172    assert_eq!(
 6173        editor.update(cx, |editor, cx| editor.text(cx)),
 6174        "one, two\nthree\n"
 6175    );
 6176    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6177
 6178    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6179    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6180
 6181    // Ensure we can still save even if formatting hangs.
 6182    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6183        assert_eq!(
 6184            params.text_document.uri,
 6185            lsp::Url::from_file_path("/file.rs").unwrap()
 6186        );
 6187        futures::future::pending::<()>().await;
 6188        unreachable!()
 6189    });
 6190    let save = editor
 6191        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6192        .unwrap();
 6193    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6194    cx.executor().start_waiting();
 6195    save.await;
 6196    assert_eq!(
 6197        editor.update(cx, |editor, cx| editor.text(cx)),
 6198        "one\ntwo\nthree\n"
 6199    );
 6200    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6201
 6202    // For non-dirty buffer, no formatting request should be sent
 6203    let save = editor
 6204        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6205        .unwrap();
 6206    let _pending_format_request = fake_server
 6207        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6208            panic!("Should not be invoked on non-dirty buffer");
 6209        })
 6210        .next();
 6211    cx.executor().start_waiting();
 6212    save.await;
 6213
 6214    // Set rust language override and assert overridden tabsize is sent to language server
 6215    update_test_language_settings(cx, |settings| {
 6216        settings.languages.insert(
 6217            "Rust".into(),
 6218            LanguageSettingsContent {
 6219                tab_size: NonZeroU32::new(8),
 6220                ..Default::default()
 6221            },
 6222        );
 6223    });
 6224
 6225    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6226    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6227    let save = editor
 6228        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6229        .unwrap();
 6230    fake_server
 6231        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6232            assert_eq!(
 6233                params.text_document.uri,
 6234                lsp::Url::from_file_path("/file.rs").unwrap()
 6235            );
 6236            assert_eq!(params.options.tab_size, 8);
 6237            Ok(Some(vec![]))
 6238        })
 6239        .next()
 6240        .await;
 6241    cx.executor().start_waiting();
 6242    save.await;
 6243}
 6244
 6245#[gpui::test]
 6246async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6247    init_test(cx, |_| {});
 6248
 6249    let cols = 4;
 6250    let rows = 10;
 6251    let sample_text_1 = sample_text(rows, cols, 'a');
 6252    assert_eq!(
 6253        sample_text_1,
 6254        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6255    );
 6256    let sample_text_2 = sample_text(rows, cols, 'l');
 6257    assert_eq!(
 6258        sample_text_2,
 6259        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6260    );
 6261    let sample_text_3 = sample_text(rows, cols, 'v');
 6262    assert_eq!(
 6263        sample_text_3,
 6264        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6265    );
 6266
 6267    let fs = FakeFs::new(cx.executor());
 6268    fs.insert_tree(
 6269        "/a",
 6270        json!({
 6271            "main.rs": sample_text_1,
 6272            "other.rs": sample_text_2,
 6273            "lib.rs": sample_text_3,
 6274        }),
 6275    )
 6276    .await;
 6277
 6278    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6279    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6280    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6281
 6282    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6283    language_registry.add(rust_lang());
 6284    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6285        "Rust",
 6286        FakeLspAdapter {
 6287            capabilities: lsp::ServerCapabilities {
 6288                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6289                ..Default::default()
 6290            },
 6291            ..Default::default()
 6292        },
 6293    );
 6294
 6295    let worktree = project.update(cx, |project, cx| {
 6296        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6297        assert_eq!(worktrees.len(), 1);
 6298        worktrees.pop().unwrap()
 6299    });
 6300    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6301
 6302    let buffer_1 = project
 6303        .update(cx, |project, cx| {
 6304            project.open_buffer((worktree_id, "main.rs"), cx)
 6305        })
 6306        .await
 6307        .unwrap();
 6308    let buffer_2 = project
 6309        .update(cx, |project, cx| {
 6310            project.open_buffer((worktree_id, "other.rs"), cx)
 6311        })
 6312        .await
 6313        .unwrap();
 6314    let buffer_3 = project
 6315        .update(cx, |project, cx| {
 6316            project.open_buffer((worktree_id, "lib.rs"), cx)
 6317        })
 6318        .await
 6319        .unwrap();
 6320
 6321    let multi_buffer = cx.new_model(|cx| {
 6322        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 6323        multi_buffer.push_excerpts(
 6324            buffer_1.clone(),
 6325            [
 6326                ExcerptRange {
 6327                    context: Point::new(0, 0)..Point::new(3, 0),
 6328                    primary: None,
 6329                },
 6330                ExcerptRange {
 6331                    context: Point::new(5, 0)..Point::new(7, 0),
 6332                    primary: None,
 6333                },
 6334                ExcerptRange {
 6335                    context: Point::new(9, 0)..Point::new(10, 4),
 6336                    primary: None,
 6337                },
 6338            ],
 6339            cx,
 6340        );
 6341        multi_buffer.push_excerpts(
 6342            buffer_2.clone(),
 6343            [
 6344                ExcerptRange {
 6345                    context: Point::new(0, 0)..Point::new(3, 0),
 6346                    primary: None,
 6347                },
 6348                ExcerptRange {
 6349                    context: Point::new(5, 0)..Point::new(7, 0),
 6350                    primary: None,
 6351                },
 6352                ExcerptRange {
 6353                    context: Point::new(9, 0)..Point::new(10, 4),
 6354                    primary: None,
 6355                },
 6356            ],
 6357            cx,
 6358        );
 6359        multi_buffer.push_excerpts(
 6360            buffer_3.clone(),
 6361            [
 6362                ExcerptRange {
 6363                    context: Point::new(0, 0)..Point::new(3, 0),
 6364                    primary: None,
 6365                },
 6366                ExcerptRange {
 6367                    context: Point::new(5, 0)..Point::new(7, 0),
 6368                    primary: None,
 6369                },
 6370                ExcerptRange {
 6371                    context: Point::new(9, 0)..Point::new(10, 4),
 6372                    primary: None,
 6373                },
 6374            ],
 6375            cx,
 6376        );
 6377        multi_buffer
 6378    });
 6379    let multi_buffer_editor = cx.new_view(|cx| {
 6380        Editor::new(
 6381            EditorMode::Full,
 6382            multi_buffer,
 6383            Some(project.clone()),
 6384            true,
 6385            cx,
 6386        )
 6387    });
 6388
 6389    multi_buffer_editor.update(cx, |editor, cx| {
 6390        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6391        editor.insert("|one|two|three|", cx);
 6392    });
 6393    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6394    multi_buffer_editor.update(cx, |editor, cx| {
 6395        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6396            s.select_ranges(Some(60..70))
 6397        });
 6398        editor.insert("|four|five|six|", cx);
 6399    });
 6400    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6401
 6402    // First two buffers should be edited, but not the third one.
 6403    assert_eq!(
 6404        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6405        "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}",
 6406    );
 6407    buffer_1.update(cx, |buffer, _| {
 6408        assert!(buffer.is_dirty());
 6409        assert_eq!(
 6410            buffer.text(),
 6411            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6412        )
 6413    });
 6414    buffer_2.update(cx, |buffer, _| {
 6415        assert!(buffer.is_dirty());
 6416        assert_eq!(
 6417            buffer.text(),
 6418            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6419        )
 6420    });
 6421    buffer_3.update(cx, |buffer, _| {
 6422        assert!(!buffer.is_dirty());
 6423        assert_eq!(buffer.text(), sample_text_3,)
 6424    });
 6425
 6426    cx.executor().start_waiting();
 6427    let save = multi_buffer_editor
 6428        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6429        .unwrap();
 6430
 6431    let fake_server = fake_servers.next().await.unwrap();
 6432    fake_server
 6433        .server
 6434        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6435            Ok(Some(vec![lsp::TextEdit::new(
 6436                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6437                format!("[{} formatted]", params.text_document.uri),
 6438            )]))
 6439        })
 6440        .detach();
 6441    save.await;
 6442
 6443    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6444    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6445    assert_eq!(
 6446        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6447        "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}",
 6448    );
 6449    buffer_1.update(cx, |buffer, _| {
 6450        assert!(!buffer.is_dirty());
 6451        assert_eq!(
 6452            buffer.text(),
 6453            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6454        )
 6455    });
 6456    buffer_2.update(cx, |buffer, _| {
 6457        assert!(!buffer.is_dirty());
 6458        assert_eq!(
 6459            buffer.text(),
 6460            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6461        )
 6462    });
 6463    buffer_3.update(cx, |buffer, _| {
 6464        assert!(!buffer.is_dirty());
 6465        assert_eq!(buffer.text(), sample_text_3,)
 6466    });
 6467}
 6468
 6469#[gpui::test]
 6470async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6471    init_test(cx, |_| {});
 6472
 6473    let fs = FakeFs::new(cx.executor());
 6474    fs.insert_file("/file.rs", Default::default()).await;
 6475
 6476    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6477
 6478    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6479    language_registry.add(rust_lang());
 6480    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6481        "Rust",
 6482        FakeLspAdapter {
 6483            capabilities: lsp::ServerCapabilities {
 6484                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6485                ..Default::default()
 6486            },
 6487            ..Default::default()
 6488        },
 6489    );
 6490
 6491    let buffer = project
 6492        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6493        .await
 6494        .unwrap();
 6495
 6496    cx.executor().start_waiting();
 6497    let fake_server = fake_servers.next().await.unwrap();
 6498
 6499    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6500    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6501    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6502    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6503
 6504    let save = editor
 6505        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6506        .unwrap();
 6507    fake_server
 6508        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6509            assert_eq!(
 6510                params.text_document.uri,
 6511                lsp::Url::from_file_path("/file.rs").unwrap()
 6512            );
 6513            assert_eq!(params.options.tab_size, 4);
 6514            Ok(Some(vec![lsp::TextEdit::new(
 6515                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6516                ", ".to_string(),
 6517            )]))
 6518        })
 6519        .next()
 6520        .await;
 6521    cx.executor().start_waiting();
 6522    save.await;
 6523    assert_eq!(
 6524        editor.update(cx, |editor, cx| editor.text(cx)),
 6525        "one, two\nthree\n"
 6526    );
 6527    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6528
 6529    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6530    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6531
 6532    // Ensure we can still save even if formatting hangs.
 6533    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6534        move |params, _| async move {
 6535            assert_eq!(
 6536                params.text_document.uri,
 6537                lsp::Url::from_file_path("/file.rs").unwrap()
 6538            );
 6539            futures::future::pending::<()>().await;
 6540            unreachable!()
 6541        },
 6542    );
 6543    let save = editor
 6544        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6545        .unwrap();
 6546    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6547    cx.executor().start_waiting();
 6548    save.await;
 6549    assert_eq!(
 6550        editor.update(cx, |editor, cx| editor.text(cx)),
 6551        "one\ntwo\nthree\n"
 6552    );
 6553    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6554
 6555    // For non-dirty buffer, no formatting request should be sent
 6556    let save = editor
 6557        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6558        .unwrap();
 6559    let _pending_format_request = fake_server
 6560        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6561            panic!("Should not be invoked on non-dirty buffer");
 6562        })
 6563        .next();
 6564    cx.executor().start_waiting();
 6565    save.await;
 6566
 6567    // Set Rust language override and assert overridden tabsize is sent to language server
 6568    update_test_language_settings(cx, |settings| {
 6569        settings.languages.insert(
 6570            "Rust".into(),
 6571            LanguageSettingsContent {
 6572                tab_size: NonZeroU32::new(8),
 6573                ..Default::default()
 6574            },
 6575        );
 6576    });
 6577
 6578    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6579    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6580    let save = editor
 6581        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6582        .unwrap();
 6583    fake_server
 6584        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6585            assert_eq!(
 6586                params.text_document.uri,
 6587                lsp::Url::from_file_path("/file.rs").unwrap()
 6588            );
 6589            assert_eq!(params.options.tab_size, 8);
 6590            Ok(Some(vec![]))
 6591        })
 6592        .next()
 6593        .await;
 6594    cx.executor().start_waiting();
 6595    save.await;
 6596}
 6597
 6598#[gpui::test]
 6599async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6600    init_test(cx, |settings| {
 6601        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 6602            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 6603        ))
 6604    });
 6605
 6606    let fs = FakeFs::new(cx.executor());
 6607    fs.insert_file("/file.rs", Default::default()).await;
 6608
 6609    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6610
 6611    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6612    language_registry.add(Arc::new(Language::new(
 6613        LanguageConfig {
 6614            name: "Rust".into(),
 6615            matcher: LanguageMatcher {
 6616                path_suffixes: vec!["rs".to_string()],
 6617                ..Default::default()
 6618            },
 6619            ..LanguageConfig::default()
 6620        },
 6621        Some(tree_sitter_rust::language()),
 6622    )));
 6623    update_test_language_settings(cx, |settings| {
 6624        // Enable Prettier formatting for the same buffer, and ensure
 6625        // LSP is called instead of Prettier.
 6626        settings.defaults.prettier = Some(PrettierSettings {
 6627            allowed: true,
 6628            ..PrettierSettings::default()
 6629        });
 6630    });
 6631    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6632        "Rust",
 6633        FakeLspAdapter {
 6634            capabilities: lsp::ServerCapabilities {
 6635                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6636                ..Default::default()
 6637            },
 6638            ..Default::default()
 6639        },
 6640    );
 6641
 6642    let buffer = project
 6643        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6644        .await
 6645        .unwrap();
 6646
 6647    cx.executor().start_waiting();
 6648    let fake_server = fake_servers.next().await.unwrap();
 6649
 6650    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6651    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6652    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6653
 6654    let format = editor
 6655        .update(cx, |editor, cx| {
 6656            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6657        })
 6658        .unwrap();
 6659    fake_server
 6660        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6661            assert_eq!(
 6662                params.text_document.uri,
 6663                lsp::Url::from_file_path("/file.rs").unwrap()
 6664            );
 6665            assert_eq!(params.options.tab_size, 4);
 6666            Ok(Some(vec![lsp::TextEdit::new(
 6667                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6668                ", ".to_string(),
 6669            )]))
 6670        })
 6671        .next()
 6672        .await;
 6673    cx.executor().start_waiting();
 6674    format.await;
 6675    assert_eq!(
 6676        editor.update(cx, |editor, cx| editor.text(cx)),
 6677        "one, two\nthree\n"
 6678    );
 6679
 6680    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6681    // Ensure we don't lock if formatting hangs.
 6682    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6683        assert_eq!(
 6684            params.text_document.uri,
 6685            lsp::Url::from_file_path("/file.rs").unwrap()
 6686        );
 6687        futures::future::pending::<()>().await;
 6688        unreachable!()
 6689    });
 6690    let format = editor
 6691        .update(cx, |editor, cx| {
 6692            editor.perform_format(project, FormatTrigger::Manual, cx)
 6693        })
 6694        .unwrap();
 6695    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6696    cx.executor().start_waiting();
 6697    format.await;
 6698    assert_eq!(
 6699        editor.update(cx, |editor, cx| editor.text(cx)),
 6700        "one\ntwo\nthree\n"
 6701    );
 6702}
 6703
 6704#[gpui::test]
 6705async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6706    init_test(cx, |_| {});
 6707
 6708    let mut cx = EditorLspTestContext::new_rust(
 6709        lsp::ServerCapabilities {
 6710            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6711            ..Default::default()
 6712        },
 6713        cx,
 6714    )
 6715    .await;
 6716
 6717    cx.set_state(indoc! {"
 6718        one.twoˇ
 6719    "});
 6720
 6721    // The format request takes a long time. When it completes, it inserts
 6722    // a newline and an indent before the `.`
 6723    cx.lsp
 6724        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6725            let executor = cx.background_executor().clone();
 6726            async move {
 6727                executor.timer(Duration::from_millis(100)).await;
 6728                Ok(Some(vec![lsp::TextEdit {
 6729                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6730                    new_text: "\n    ".into(),
 6731                }]))
 6732            }
 6733        });
 6734
 6735    // Submit a format request.
 6736    let format_1 = cx
 6737        .update_editor(|editor, cx| editor.format(&Format, cx))
 6738        .unwrap();
 6739    cx.executor().run_until_parked();
 6740
 6741    // Submit a second format request.
 6742    let format_2 = cx
 6743        .update_editor(|editor, cx| editor.format(&Format, cx))
 6744        .unwrap();
 6745    cx.executor().run_until_parked();
 6746
 6747    // Wait for both format requests to complete
 6748    cx.executor().advance_clock(Duration::from_millis(200));
 6749    cx.executor().start_waiting();
 6750    format_1.await.unwrap();
 6751    cx.executor().start_waiting();
 6752    format_2.await.unwrap();
 6753
 6754    // The formatting edits only happens once.
 6755    cx.assert_editor_state(indoc! {"
 6756        one
 6757            .twoˇ
 6758    "});
 6759}
 6760
 6761#[gpui::test]
 6762async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6763    init_test(cx, |settings| {
 6764        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 6765    });
 6766
 6767    let mut cx = EditorLspTestContext::new_rust(
 6768        lsp::ServerCapabilities {
 6769            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6770            ..Default::default()
 6771        },
 6772        cx,
 6773    )
 6774    .await;
 6775
 6776    // Set up a buffer white some trailing whitespace and no trailing newline.
 6777    cx.set_state(
 6778        &[
 6779            "one ",   //
 6780            "twoˇ",   //
 6781            "three ", //
 6782            "four",   //
 6783        ]
 6784        .join("\n"),
 6785    );
 6786
 6787    // Submit a format request.
 6788    let format = cx
 6789        .update_editor(|editor, cx| editor.format(&Format, cx))
 6790        .unwrap();
 6791
 6792    // Record which buffer changes have been sent to the language server
 6793    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6794    cx.lsp
 6795        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6796            let buffer_changes = buffer_changes.clone();
 6797            move |params, _| {
 6798                buffer_changes.lock().extend(
 6799                    params
 6800                        .content_changes
 6801                        .into_iter()
 6802                        .map(|e| (e.range.unwrap(), e.text)),
 6803                );
 6804            }
 6805        });
 6806
 6807    // Handle formatting requests to the language server.
 6808    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6809        let buffer_changes = buffer_changes.clone();
 6810        move |_, _| {
 6811            // When formatting is requested, trailing whitespace has already been stripped,
 6812            // and the trailing newline has already been added.
 6813            assert_eq!(
 6814                &buffer_changes.lock()[1..],
 6815                &[
 6816                    (
 6817                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6818                        "".into()
 6819                    ),
 6820                    (
 6821                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6822                        "".into()
 6823                    ),
 6824                    (
 6825                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6826                        "\n".into()
 6827                    ),
 6828                ]
 6829            );
 6830
 6831            // Insert blank lines between each line of the buffer.
 6832            async move {
 6833                Ok(Some(vec![
 6834                    lsp::TextEdit {
 6835                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6836                        new_text: "\n".into(),
 6837                    },
 6838                    lsp::TextEdit {
 6839                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6840                        new_text: "\n".into(),
 6841                    },
 6842                ]))
 6843            }
 6844        }
 6845    });
 6846
 6847    // After formatting the buffer, the trailing whitespace is stripped,
 6848    // a newline is appended, and the edits provided by the language server
 6849    // have been applied.
 6850    format.await.unwrap();
 6851    cx.assert_editor_state(
 6852        &[
 6853            "one",   //
 6854            "",      //
 6855            "twoˇ",  //
 6856            "",      //
 6857            "three", //
 6858            "four",  //
 6859            "",      //
 6860        ]
 6861        .join("\n"),
 6862    );
 6863
 6864    // Undoing the formatting undoes the trailing whitespace removal, the
 6865    // trailing newline, and the LSP edits.
 6866    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6867    cx.assert_editor_state(
 6868        &[
 6869            "one ",   //
 6870            "twoˇ",   //
 6871            "three ", //
 6872            "four",   //
 6873        ]
 6874        .join("\n"),
 6875    );
 6876}
 6877
 6878#[gpui::test]
 6879async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 6880    cx: &mut gpui::TestAppContext,
 6881) {
 6882    init_test(cx, |_| {});
 6883
 6884    cx.update(|cx| {
 6885        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6886            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6887                settings.auto_signature_help = Some(true);
 6888            });
 6889        });
 6890    });
 6891
 6892    let mut cx = EditorLspTestContext::new_rust(
 6893        lsp::ServerCapabilities {
 6894            signature_help_provider: Some(lsp::SignatureHelpOptions {
 6895                ..Default::default()
 6896            }),
 6897            ..Default::default()
 6898        },
 6899        cx,
 6900    )
 6901    .await;
 6902
 6903    let language = Language::new(
 6904        LanguageConfig {
 6905            name: "Rust".into(),
 6906            brackets: BracketPairConfig {
 6907                pairs: vec![
 6908                    BracketPair {
 6909                        start: "{".to_string(),
 6910                        end: "}".to_string(),
 6911                        close: true,
 6912                        surround: true,
 6913                        newline: true,
 6914                    },
 6915                    BracketPair {
 6916                        start: "(".to_string(),
 6917                        end: ")".to_string(),
 6918                        close: true,
 6919                        surround: true,
 6920                        newline: true,
 6921                    },
 6922                    BracketPair {
 6923                        start: "/*".to_string(),
 6924                        end: " */".to_string(),
 6925                        close: true,
 6926                        surround: true,
 6927                        newline: true,
 6928                    },
 6929                    BracketPair {
 6930                        start: "[".to_string(),
 6931                        end: "]".to_string(),
 6932                        close: false,
 6933                        surround: false,
 6934                        newline: true,
 6935                    },
 6936                    BracketPair {
 6937                        start: "\"".to_string(),
 6938                        end: "\"".to_string(),
 6939                        close: true,
 6940                        surround: true,
 6941                        newline: false,
 6942                    },
 6943                    BracketPair {
 6944                        start: "<".to_string(),
 6945                        end: ">".to_string(),
 6946                        close: false,
 6947                        surround: true,
 6948                        newline: true,
 6949                    },
 6950                ],
 6951                ..Default::default()
 6952            },
 6953            autoclose_before: "})]".to_string(),
 6954            ..Default::default()
 6955        },
 6956        Some(tree_sitter_rust::language()),
 6957    );
 6958    let language = Arc::new(language);
 6959
 6960    cx.language_registry().add(language.clone());
 6961    cx.update_buffer(|buffer, cx| {
 6962        buffer.set_language(Some(language), cx);
 6963    });
 6964
 6965    cx.set_state(
 6966        &r#"
 6967            fn main() {
 6968                sampleˇ
 6969            }
 6970        "#
 6971        .unindent(),
 6972    );
 6973
 6974    cx.update_editor(|view, cx| {
 6975        view.handle_input("(", cx);
 6976    });
 6977    cx.assert_editor_state(
 6978        &"
 6979            fn main() {
 6980                sample(ˇ)
 6981            }
 6982        "
 6983        .unindent(),
 6984    );
 6985
 6986    let mocked_response = lsp::SignatureHelp {
 6987        signatures: vec![lsp::SignatureInformation {
 6988            label: "fn sample(param1: u8, param2: u8)".to_string(),
 6989            documentation: None,
 6990            parameters: Some(vec![
 6991                lsp::ParameterInformation {
 6992                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 6993                    documentation: None,
 6994                },
 6995                lsp::ParameterInformation {
 6996                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 6997                    documentation: None,
 6998                },
 6999            ]),
 7000            active_parameter: None,
 7001        }],
 7002        active_signature: Some(0),
 7003        active_parameter: Some(0),
 7004    };
 7005    handle_signature_help_request(&mut cx, mocked_response).await;
 7006
 7007    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7008        .await;
 7009
 7010    cx.editor(|editor, _| {
 7011        let signature_help_state = editor.signature_help_state.popover().cloned();
 7012        assert!(signature_help_state.is_some());
 7013        let ParsedMarkdown {
 7014            text, highlights, ..
 7015        } = signature_help_state.unwrap().parsed_content;
 7016        assert_eq!(text, "param1: u8, param2: u8");
 7017        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7018    });
 7019}
 7020
 7021#[gpui::test]
 7022async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7023    init_test(cx, |_| {});
 7024
 7025    cx.update(|cx| {
 7026        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7027            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7028                settings.auto_signature_help = Some(false);
 7029                settings.show_signature_help_after_edits = Some(false);
 7030            });
 7031        });
 7032    });
 7033
 7034    let mut cx = EditorLspTestContext::new_rust(
 7035        lsp::ServerCapabilities {
 7036            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7037                ..Default::default()
 7038            }),
 7039            ..Default::default()
 7040        },
 7041        cx,
 7042    )
 7043    .await;
 7044
 7045    let language = Language::new(
 7046        LanguageConfig {
 7047            name: "Rust".into(),
 7048            brackets: BracketPairConfig {
 7049                pairs: vec![
 7050                    BracketPair {
 7051                        start: "{".to_string(),
 7052                        end: "}".to_string(),
 7053                        close: true,
 7054                        surround: true,
 7055                        newline: true,
 7056                    },
 7057                    BracketPair {
 7058                        start: "(".to_string(),
 7059                        end: ")".to_string(),
 7060                        close: true,
 7061                        surround: true,
 7062                        newline: true,
 7063                    },
 7064                    BracketPair {
 7065                        start: "/*".to_string(),
 7066                        end: " */".to_string(),
 7067                        close: true,
 7068                        surround: true,
 7069                        newline: true,
 7070                    },
 7071                    BracketPair {
 7072                        start: "[".to_string(),
 7073                        end: "]".to_string(),
 7074                        close: false,
 7075                        surround: false,
 7076                        newline: true,
 7077                    },
 7078                    BracketPair {
 7079                        start: "\"".to_string(),
 7080                        end: "\"".to_string(),
 7081                        close: true,
 7082                        surround: true,
 7083                        newline: false,
 7084                    },
 7085                    BracketPair {
 7086                        start: "<".to_string(),
 7087                        end: ">".to_string(),
 7088                        close: false,
 7089                        surround: true,
 7090                        newline: true,
 7091                    },
 7092                ],
 7093                ..Default::default()
 7094            },
 7095            autoclose_before: "})]".to_string(),
 7096            ..Default::default()
 7097        },
 7098        Some(tree_sitter_rust::language()),
 7099    );
 7100    let language = Arc::new(language);
 7101
 7102    cx.language_registry().add(language.clone());
 7103    cx.update_buffer(|buffer, cx| {
 7104        buffer.set_language(Some(language), cx);
 7105    });
 7106
 7107    // Ensure that signature_help is not called when no signature help is enabled.
 7108    cx.set_state(
 7109        &r#"
 7110            fn main() {
 7111                sampleˇ
 7112            }
 7113        "#
 7114        .unindent(),
 7115    );
 7116    cx.update_editor(|view, cx| {
 7117        view.handle_input("(", cx);
 7118    });
 7119    cx.assert_editor_state(
 7120        &"
 7121            fn main() {
 7122                sample(ˇ)
 7123            }
 7124        "
 7125        .unindent(),
 7126    );
 7127    cx.editor(|editor, _| {
 7128        assert!(editor.signature_help_state.task().is_none());
 7129    });
 7130
 7131    let mocked_response = lsp::SignatureHelp {
 7132        signatures: vec![lsp::SignatureInformation {
 7133            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7134            documentation: None,
 7135            parameters: Some(vec![
 7136                lsp::ParameterInformation {
 7137                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7138                    documentation: None,
 7139                },
 7140                lsp::ParameterInformation {
 7141                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7142                    documentation: None,
 7143                },
 7144            ]),
 7145            active_parameter: None,
 7146        }],
 7147        active_signature: Some(0),
 7148        active_parameter: Some(0),
 7149    };
 7150
 7151    // Ensure that signature_help is called when enabled afte edits
 7152    cx.update(|cx| {
 7153        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7154            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7155                settings.auto_signature_help = Some(false);
 7156                settings.show_signature_help_after_edits = Some(true);
 7157            });
 7158        });
 7159    });
 7160    cx.set_state(
 7161        &r#"
 7162            fn main() {
 7163                sampleˇ
 7164            }
 7165        "#
 7166        .unindent(),
 7167    );
 7168    cx.update_editor(|view, cx| {
 7169        view.handle_input("(", cx);
 7170    });
 7171    cx.assert_editor_state(
 7172        &"
 7173            fn main() {
 7174                sample(ˇ)
 7175            }
 7176        "
 7177        .unindent(),
 7178    );
 7179    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7180    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7181        .await;
 7182    cx.update_editor(|editor, _| {
 7183        let signature_help_state = editor.signature_help_state.popover().cloned();
 7184        assert!(signature_help_state.is_some());
 7185        let ParsedMarkdown {
 7186            text, highlights, ..
 7187        } = signature_help_state.unwrap().parsed_content;
 7188        assert_eq!(text, "param1: u8, param2: u8");
 7189        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7190        editor.signature_help_state = SignatureHelpState::default();
 7191    });
 7192
 7193    // Ensure that signature_help is called when auto signature help override is enabled
 7194    cx.update(|cx| {
 7195        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7196            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7197                settings.auto_signature_help = Some(true);
 7198                settings.show_signature_help_after_edits = Some(false);
 7199            });
 7200        });
 7201    });
 7202    cx.set_state(
 7203        &r#"
 7204            fn main() {
 7205                sampleˇ
 7206            }
 7207        "#
 7208        .unindent(),
 7209    );
 7210    cx.update_editor(|view, cx| {
 7211        view.handle_input("(", cx);
 7212    });
 7213    cx.assert_editor_state(
 7214        &"
 7215            fn main() {
 7216                sample(ˇ)
 7217            }
 7218        "
 7219        .unindent(),
 7220    );
 7221    handle_signature_help_request(&mut cx, mocked_response).await;
 7222    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7223        .await;
 7224    cx.editor(|editor, _| {
 7225        let signature_help_state = editor.signature_help_state.popover().cloned();
 7226        assert!(signature_help_state.is_some());
 7227        let ParsedMarkdown {
 7228            text, highlights, ..
 7229        } = signature_help_state.unwrap().parsed_content;
 7230        assert_eq!(text, "param1: u8, param2: u8");
 7231        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7232    });
 7233}
 7234
 7235#[gpui::test]
 7236async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7237    init_test(cx, |_| {});
 7238    cx.update(|cx| {
 7239        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7240            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7241                settings.auto_signature_help = Some(true);
 7242            });
 7243        });
 7244    });
 7245
 7246    let mut cx = EditorLspTestContext::new_rust(
 7247        lsp::ServerCapabilities {
 7248            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7249                ..Default::default()
 7250            }),
 7251            ..Default::default()
 7252        },
 7253        cx,
 7254    )
 7255    .await;
 7256
 7257    // A test that directly calls `show_signature_help`
 7258    cx.update_editor(|editor, cx| {
 7259        editor.show_signature_help(&ShowSignatureHelp, cx);
 7260    });
 7261
 7262    let mocked_response = lsp::SignatureHelp {
 7263        signatures: vec![lsp::SignatureInformation {
 7264            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7265            documentation: None,
 7266            parameters: Some(vec![
 7267                lsp::ParameterInformation {
 7268                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7269                    documentation: None,
 7270                },
 7271                lsp::ParameterInformation {
 7272                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7273                    documentation: None,
 7274                },
 7275            ]),
 7276            active_parameter: None,
 7277        }],
 7278        active_signature: Some(0),
 7279        active_parameter: Some(0),
 7280    };
 7281    handle_signature_help_request(&mut cx, mocked_response).await;
 7282
 7283    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7284        .await;
 7285
 7286    cx.editor(|editor, _| {
 7287        let signature_help_state = editor.signature_help_state.popover().cloned();
 7288        assert!(signature_help_state.is_some());
 7289        let ParsedMarkdown {
 7290            text, highlights, ..
 7291        } = signature_help_state.unwrap().parsed_content;
 7292        assert_eq!(text, "param1: u8, param2: u8");
 7293        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7294    });
 7295
 7296    // When exiting outside from inside the brackets, `signature_help` is closed.
 7297    cx.set_state(indoc! {"
 7298        fn main() {
 7299            sample(ˇ);
 7300        }
 7301
 7302        fn sample(param1: u8, param2: u8) {}
 7303    "});
 7304
 7305    cx.update_editor(|editor, cx| {
 7306        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7307    });
 7308
 7309    let mocked_response = lsp::SignatureHelp {
 7310        signatures: Vec::new(),
 7311        active_signature: None,
 7312        active_parameter: None,
 7313    };
 7314    handle_signature_help_request(&mut cx, mocked_response).await;
 7315
 7316    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7317        .await;
 7318
 7319    cx.editor(|editor, _| {
 7320        assert!(!editor.signature_help_state.is_shown());
 7321    });
 7322
 7323    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7324    cx.set_state(indoc! {"
 7325        fn main() {
 7326            sample(ˇ);
 7327        }
 7328
 7329        fn sample(param1: u8, param2: u8) {}
 7330    "});
 7331
 7332    let mocked_response = lsp::SignatureHelp {
 7333        signatures: vec![lsp::SignatureInformation {
 7334            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7335            documentation: None,
 7336            parameters: Some(vec![
 7337                lsp::ParameterInformation {
 7338                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7339                    documentation: None,
 7340                },
 7341                lsp::ParameterInformation {
 7342                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7343                    documentation: None,
 7344                },
 7345            ]),
 7346            active_parameter: None,
 7347        }],
 7348        active_signature: Some(0),
 7349        active_parameter: Some(0),
 7350    };
 7351    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7352    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7353        .await;
 7354    cx.editor(|editor, _| {
 7355        assert!(editor.signature_help_state.is_shown());
 7356    });
 7357
 7358    // Restore the popover with more parameter input
 7359    cx.set_state(indoc! {"
 7360        fn main() {
 7361            sample(param1, param2ˇ);
 7362        }
 7363
 7364        fn sample(param1: u8, param2: u8) {}
 7365    "});
 7366
 7367    let mocked_response = lsp::SignatureHelp {
 7368        signatures: vec![lsp::SignatureInformation {
 7369            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7370            documentation: None,
 7371            parameters: Some(vec![
 7372                lsp::ParameterInformation {
 7373                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7374                    documentation: None,
 7375                },
 7376                lsp::ParameterInformation {
 7377                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7378                    documentation: None,
 7379                },
 7380            ]),
 7381            active_parameter: None,
 7382        }],
 7383        active_signature: Some(0),
 7384        active_parameter: Some(1),
 7385    };
 7386    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7387    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7388        .await;
 7389
 7390    // When selecting a range, the popover is gone.
 7391    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7392    cx.update_editor(|editor, cx| {
 7393        editor.change_selections(None, cx, |s| {
 7394            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7395        })
 7396    });
 7397    cx.assert_editor_state(indoc! {"
 7398        fn main() {
 7399            sample(param1, «ˇparam2»);
 7400        }
 7401
 7402        fn sample(param1: u8, param2: u8) {}
 7403    "});
 7404    cx.editor(|editor, _| {
 7405        assert!(!editor.signature_help_state.is_shown());
 7406    });
 7407
 7408    // When unselecting again, the popover is back if within the brackets.
 7409    cx.update_editor(|editor, cx| {
 7410        editor.change_selections(None, cx, |s| {
 7411            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7412        })
 7413    });
 7414    cx.assert_editor_state(indoc! {"
 7415        fn main() {
 7416            sample(param1, ˇparam2);
 7417        }
 7418
 7419        fn sample(param1: u8, param2: u8) {}
 7420    "});
 7421    handle_signature_help_request(&mut cx, mocked_response).await;
 7422    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7423        .await;
 7424    cx.editor(|editor, _| {
 7425        assert!(editor.signature_help_state.is_shown());
 7426    });
 7427
 7428    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7429    cx.update_editor(|editor, cx| {
 7430        editor.change_selections(None, cx, |s| {
 7431            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7432            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7433        })
 7434    });
 7435    cx.assert_editor_state(indoc! {"
 7436        fn main() {
 7437            sample(param1, ˇparam2);
 7438        }
 7439
 7440        fn sample(param1: u8, param2: u8) {}
 7441    "});
 7442
 7443    let mocked_response = lsp::SignatureHelp {
 7444        signatures: vec![lsp::SignatureInformation {
 7445            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7446            documentation: None,
 7447            parameters: Some(vec![
 7448                lsp::ParameterInformation {
 7449                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7450                    documentation: None,
 7451                },
 7452                lsp::ParameterInformation {
 7453                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7454                    documentation: None,
 7455                },
 7456            ]),
 7457            active_parameter: None,
 7458        }],
 7459        active_signature: Some(0),
 7460        active_parameter: Some(1),
 7461    };
 7462    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7463    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7464        .await;
 7465    cx.update_editor(|editor, cx| {
 7466        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7467    });
 7468    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7469        .await;
 7470    cx.update_editor(|editor, cx| {
 7471        editor.change_selections(None, cx, |s| {
 7472            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7473        })
 7474    });
 7475    cx.assert_editor_state(indoc! {"
 7476        fn main() {
 7477            sample(param1, «ˇparam2»);
 7478        }
 7479
 7480        fn sample(param1: u8, param2: u8) {}
 7481    "});
 7482    cx.update_editor(|editor, cx| {
 7483        editor.change_selections(None, cx, |s| {
 7484            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7485        })
 7486    });
 7487    cx.assert_editor_state(indoc! {"
 7488        fn main() {
 7489            sample(param1, ˇparam2);
 7490        }
 7491
 7492        fn sample(param1: u8, param2: u8) {}
 7493    "});
 7494    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7495        .await;
 7496}
 7497
 7498#[gpui::test]
 7499async fn test_completion(cx: &mut gpui::TestAppContext) {
 7500    init_test(cx, |_| {});
 7501
 7502    let mut cx = EditorLspTestContext::new_rust(
 7503        lsp::ServerCapabilities {
 7504            completion_provider: Some(lsp::CompletionOptions {
 7505                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7506                resolve_provider: Some(true),
 7507                ..Default::default()
 7508            }),
 7509            ..Default::default()
 7510        },
 7511        cx,
 7512    )
 7513    .await;
 7514    let counter = Arc::new(AtomicUsize::new(0));
 7515
 7516    cx.set_state(indoc! {"
 7517        oneˇ
 7518        two
 7519        three
 7520    "});
 7521    cx.simulate_keystroke(".");
 7522    handle_completion_request(
 7523        &mut cx,
 7524        indoc! {"
 7525            one.|<>
 7526            two
 7527            three
 7528        "},
 7529        vec!["first_completion", "second_completion"],
 7530        counter.clone(),
 7531    )
 7532    .await;
 7533    cx.condition(|editor, _| editor.context_menu_visible())
 7534        .await;
 7535    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7536
 7537    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7538        editor.context_menu_next(&Default::default(), cx);
 7539        editor
 7540            .confirm_completion(&ConfirmCompletion::default(), cx)
 7541            .unwrap()
 7542    });
 7543    cx.assert_editor_state(indoc! {"
 7544        one.second_completionˇ
 7545        two
 7546        three
 7547    "});
 7548
 7549    handle_resolve_completion_request(
 7550        &mut cx,
 7551        Some(vec![
 7552            (
 7553                //This overlaps with the primary completion edit which is
 7554                //misbehavior from the LSP spec, test that we filter it out
 7555                indoc! {"
 7556                    one.second_ˇcompletion
 7557                    two
 7558                    threeˇ
 7559                "},
 7560                "overlapping additional edit",
 7561            ),
 7562            (
 7563                indoc! {"
 7564                    one.second_completion
 7565                    two
 7566                    threeˇ
 7567                "},
 7568                "\nadditional edit",
 7569            ),
 7570        ]),
 7571    )
 7572    .await;
 7573    apply_additional_edits.await.unwrap();
 7574    cx.assert_editor_state(indoc! {"
 7575        one.second_completionˇ
 7576        two
 7577        three
 7578        additional edit
 7579    "});
 7580
 7581    cx.set_state(indoc! {"
 7582        one.second_completion
 7583        twoˇ
 7584        threeˇ
 7585        additional edit
 7586    "});
 7587    cx.simulate_keystroke(" ");
 7588    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7589    cx.simulate_keystroke("s");
 7590    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7591
 7592    cx.assert_editor_state(indoc! {"
 7593        one.second_completion
 7594        two sˇ
 7595        three sˇ
 7596        additional edit
 7597    "});
 7598    handle_completion_request(
 7599        &mut cx,
 7600        indoc! {"
 7601            one.second_completion
 7602            two s
 7603            three <s|>
 7604            additional edit
 7605        "},
 7606        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7607        counter.clone(),
 7608    )
 7609    .await;
 7610    cx.condition(|editor, _| editor.context_menu_visible())
 7611        .await;
 7612    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 7613
 7614    cx.simulate_keystroke("i");
 7615
 7616    handle_completion_request(
 7617        &mut cx,
 7618        indoc! {"
 7619            one.second_completion
 7620            two si
 7621            three <si|>
 7622            additional edit
 7623        "},
 7624        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7625        counter.clone(),
 7626    )
 7627    .await;
 7628    cx.condition(|editor, _| editor.context_menu_visible())
 7629        .await;
 7630    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 7631
 7632    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7633        editor
 7634            .confirm_completion(&ConfirmCompletion::default(), cx)
 7635            .unwrap()
 7636    });
 7637    cx.assert_editor_state(indoc! {"
 7638        one.second_completion
 7639        two sixth_completionˇ
 7640        three sixth_completionˇ
 7641        additional edit
 7642    "});
 7643
 7644    handle_resolve_completion_request(&mut cx, None).await;
 7645    apply_additional_edits.await.unwrap();
 7646
 7647    _ = cx.update(|cx| {
 7648        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7649            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7650                settings.show_completions_on_input = Some(false);
 7651            });
 7652        })
 7653    });
 7654    cx.set_state("editorˇ");
 7655    cx.simulate_keystroke(".");
 7656    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7657    cx.simulate_keystroke("c");
 7658    cx.simulate_keystroke("l");
 7659    cx.simulate_keystroke("o");
 7660    cx.assert_editor_state("editor.cloˇ");
 7661    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7662    cx.update_editor(|editor, cx| {
 7663        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 7664    });
 7665    handle_completion_request(
 7666        &mut cx,
 7667        "editor.<clo|>",
 7668        vec!["close", "clobber"],
 7669        counter.clone(),
 7670    )
 7671    .await;
 7672    cx.condition(|editor, _| editor.context_menu_visible())
 7673        .await;
 7674    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 7675
 7676    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7677        editor
 7678            .confirm_completion(&ConfirmCompletion::default(), cx)
 7679            .unwrap()
 7680    });
 7681    cx.assert_editor_state("editor.closeˇ");
 7682    handle_resolve_completion_request(&mut cx, None).await;
 7683    apply_additional_edits.await.unwrap();
 7684}
 7685
 7686#[gpui::test]
 7687async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 7688    init_test(cx, |_| {});
 7689    let mut cx = EditorLspTestContext::new_rust(
 7690        lsp::ServerCapabilities {
 7691            completion_provider: Some(lsp::CompletionOptions {
 7692                trigger_characters: Some(vec![".".to_string()]),
 7693                ..Default::default()
 7694            }),
 7695            ..Default::default()
 7696        },
 7697        cx,
 7698    )
 7699    .await;
 7700    cx.lsp
 7701        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 7702            Ok(Some(lsp::CompletionResponse::Array(vec![
 7703                lsp::CompletionItem {
 7704                    label: "first".into(),
 7705                    ..Default::default()
 7706                },
 7707                lsp::CompletionItem {
 7708                    label: "last".into(),
 7709                    ..Default::default()
 7710                },
 7711            ])))
 7712        });
 7713    cx.set_state("variableˇ");
 7714    cx.simulate_keystroke(".");
 7715    cx.executor().run_until_parked();
 7716
 7717    cx.update_editor(|editor, _| {
 7718        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7719            assert_eq!(
 7720                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 7721                &["first", "last"]
 7722            );
 7723        } else {
 7724            panic!("expected completion menu to be open");
 7725        }
 7726    });
 7727
 7728    cx.update_editor(|editor, cx| {
 7729        editor.move_page_down(&MovePageDown::default(), cx);
 7730        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7731            assert!(
 7732                menu.selected_item == 1,
 7733                "expected PageDown to select the last item from the context menu"
 7734            );
 7735        } else {
 7736            panic!("expected completion menu to stay open after PageDown");
 7737        }
 7738    });
 7739
 7740    cx.update_editor(|editor, cx| {
 7741        editor.move_page_up(&MovePageUp::default(), cx);
 7742        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7743            assert!(
 7744                menu.selected_item == 0,
 7745                "expected PageUp to select the first item from the context menu"
 7746            );
 7747        } else {
 7748            panic!("expected completion menu to stay open after PageUp");
 7749        }
 7750    });
 7751}
 7752
 7753#[gpui::test]
 7754async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 7755    init_test(cx, |_| {});
 7756
 7757    let mut cx = EditorLspTestContext::new_rust(
 7758        lsp::ServerCapabilities {
 7759            completion_provider: Some(lsp::CompletionOptions {
 7760                trigger_characters: Some(vec![".".to_string()]),
 7761                resolve_provider: Some(true),
 7762                ..Default::default()
 7763            }),
 7764            ..Default::default()
 7765        },
 7766        cx,
 7767    )
 7768    .await;
 7769
 7770    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 7771    cx.simulate_keystroke(".");
 7772    let completion_item = lsp::CompletionItem {
 7773        label: "Some".into(),
 7774        kind: Some(lsp::CompletionItemKind::SNIPPET),
 7775        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 7776        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 7777            kind: lsp::MarkupKind::Markdown,
 7778            value: "```rust\nSome(2)\n```".to_string(),
 7779        })),
 7780        deprecated: Some(false),
 7781        sort_text: Some("Some".to_string()),
 7782        filter_text: Some("Some".to_string()),
 7783        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 7784        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 7785            range: lsp::Range {
 7786                start: lsp::Position {
 7787                    line: 0,
 7788                    character: 22,
 7789                },
 7790                end: lsp::Position {
 7791                    line: 0,
 7792                    character: 22,
 7793                },
 7794            },
 7795            new_text: "Some(2)".to_string(),
 7796        })),
 7797        additional_text_edits: Some(vec![lsp::TextEdit {
 7798            range: lsp::Range {
 7799                start: lsp::Position {
 7800                    line: 0,
 7801                    character: 20,
 7802                },
 7803                end: lsp::Position {
 7804                    line: 0,
 7805                    character: 22,
 7806                },
 7807            },
 7808            new_text: "".to_string(),
 7809        }]),
 7810        ..Default::default()
 7811    };
 7812
 7813    let closure_completion_item = completion_item.clone();
 7814    let counter = Arc::new(AtomicUsize::new(0));
 7815    let counter_clone = counter.clone();
 7816    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 7817        let task_completion_item = closure_completion_item.clone();
 7818        counter_clone.fetch_add(1, atomic::Ordering::Release);
 7819        async move {
 7820            Ok(Some(lsp::CompletionResponse::Array(vec![
 7821                task_completion_item,
 7822            ])))
 7823        }
 7824    });
 7825
 7826    cx.condition(|editor, _| editor.context_menu_visible())
 7827        .await;
 7828    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 7829    assert!(request.next().await.is_some());
 7830    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7831
 7832    cx.simulate_keystroke("S");
 7833    cx.simulate_keystroke("o");
 7834    cx.simulate_keystroke("m");
 7835    cx.condition(|editor, _| editor.context_menu_visible())
 7836        .await;
 7837    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 7838    assert!(request.next().await.is_some());
 7839    assert!(request.next().await.is_some());
 7840    assert!(request.next().await.is_some());
 7841    request.close();
 7842    assert!(request.next().await.is_none());
 7843    assert_eq!(
 7844        counter.load(atomic::Ordering::Acquire),
 7845        4,
 7846        "With the completions menu open, only one LSP request should happen per input"
 7847    );
 7848}
 7849
 7850#[gpui::test]
 7851async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 7852    init_test(cx, |_| {});
 7853    let mut cx = EditorTestContext::new(cx).await;
 7854    let language = Arc::new(Language::new(
 7855        LanguageConfig {
 7856            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 7857            ..Default::default()
 7858        },
 7859        Some(tree_sitter_rust::language()),
 7860    ));
 7861    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 7862
 7863    // If multiple selections intersect a line, the line is only toggled once.
 7864    cx.set_state(indoc! {"
 7865        fn a() {
 7866            «//b();
 7867            ˇ»// «c();
 7868            //ˇ»  d();
 7869        }
 7870    "});
 7871
 7872    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7873
 7874    cx.assert_editor_state(indoc! {"
 7875        fn a() {
 7876            «b();
 7877            c();
 7878            ˇ» d();
 7879        }
 7880    "});
 7881
 7882    // The comment prefix is inserted at the same column for every line in a
 7883    // selection.
 7884    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7885
 7886    cx.assert_editor_state(indoc! {"
 7887        fn a() {
 7888            // «b();
 7889            // c();
 7890            ˇ»//  d();
 7891        }
 7892    "});
 7893
 7894    // If a selection ends at the beginning of a line, that line is not toggled.
 7895    cx.set_selections_state(indoc! {"
 7896        fn a() {
 7897            // b();
 7898            «// c();
 7899        ˇ»    //  d();
 7900        }
 7901    "});
 7902
 7903    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7904
 7905    cx.assert_editor_state(indoc! {"
 7906        fn a() {
 7907            // b();
 7908            «c();
 7909        ˇ»    //  d();
 7910        }
 7911    "});
 7912
 7913    // If a selection span a single line and is empty, the line is toggled.
 7914    cx.set_state(indoc! {"
 7915        fn a() {
 7916            a();
 7917            b();
 7918        ˇ
 7919        }
 7920    "});
 7921
 7922    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7923
 7924    cx.assert_editor_state(indoc! {"
 7925        fn a() {
 7926            a();
 7927            b();
 7928        //•ˇ
 7929        }
 7930    "});
 7931
 7932    // If a selection span multiple lines, empty lines are not toggled.
 7933    cx.set_state(indoc! {"
 7934        fn a() {
 7935            «a();
 7936
 7937            c();ˇ»
 7938        }
 7939    "});
 7940
 7941    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7942
 7943    cx.assert_editor_state(indoc! {"
 7944        fn a() {
 7945            // «a();
 7946
 7947            // c();ˇ»
 7948        }
 7949    "});
 7950
 7951    // If a selection includes multiple comment prefixes, all lines are uncommented.
 7952    cx.set_state(indoc! {"
 7953        fn a() {
 7954            «// a();
 7955            /// b();
 7956            //! c();ˇ»
 7957        }
 7958    "});
 7959
 7960    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7961
 7962    cx.assert_editor_state(indoc! {"
 7963        fn a() {
 7964            «a();
 7965            b();
 7966            c();ˇ»
 7967        }
 7968    "});
 7969}
 7970
 7971#[gpui::test]
 7972async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 7973    init_test(cx, |_| {});
 7974
 7975    let language = Arc::new(Language::new(
 7976        LanguageConfig {
 7977            line_comments: vec!["// ".into()],
 7978            ..Default::default()
 7979        },
 7980        Some(tree_sitter_rust::language()),
 7981    ));
 7982
 7983    let mut cx = EditorTestContext::new(cx).await;
 7984
 7985    cx.language_registry().add(language.clone());
 7986    cx.update_buffer(|buffer, cx| {
 7987        buffer.set_language(Some(language), cx);
 7988    });
 7989
 7990    let toggle_comments = &ToggleComments {
 7991        advance_downwards: true,
 7992    };
 7993
 7994    // Single cursor on one line -> advance
 7995    // Cursor moves horizontally 3 characters as well on non-blank line
 7996    cx.set_state(indoc!(
 7997        "fn a() {
 7998             ˇdog();
 7999             cat();
 8000        }"
 8001    ));
 8002    cx.update_editor(|editor, cx| {
 8003        editor.toggle_comments(toggle_comments, cx);
 8004    });
 8005    cx.assert_editor_state(indoc!(
 8006        "fn a() {
 8007             // dog();
 8008             catˇ();
 8009        }"
 8010    ));
 8011
 8012    // Single selection on one line -> don't advance
 8013    cx.set_state(indoc!(
 8014        "fn a() {
 8015             «dog()ˇ»;
 8016             cat();
 8017        }"
 8018    ));
 8019    cx.update_editor(|editor, cx| {
 8020        editor.toggle_comments(toggle_comments, cx);
 8021    });
 8022    cx.assert_editor_state(indoc!(
 8023        "fn a() {
 8024             // «dog()ˇ»;
 8025             cat();
 8026        }"
 8027    ));
 8028
 8029    // Multiple cursors on one line -> advance
 8030    cx.set_state(indoc!(
 8031        "fn a() {
 8032             ˇdˇog();
 8033             cat();
 8034        }"
 8035    ));
 8036    cx.update_editor(|editor, cx| {
 8037        editor.toggle_comments(toggle_comments, cx);
 8038    });
 8039    cx.assert_editor_state(indoc!(
 8040        "fn a() {
 8041             // dog();
 8042             catˇ(ˇ);
 8043        }"
 8044    ));
 8045
 8046    // Multiple cursors on one line, with selection -> don't advance
 8047    cx.set_state(indoc!(
 8048        "fn a() {
 8049             ˇdˇog«()ˇ»;
 8050             cat();
 8051        }"
 8052    ));
 8053    cx.update_editor(|editor, cx| {
 8054        editor.toggle_comments(toggle_comments, cx);
 8055    });
 8056    cx.assert_editor_state(indoc!(
 8057        "fn a() {
 8058             // ˇdˇog«()ˇ»;
 8059             cat();
 8060        }"
 8061    ));
 8062
 8063    // Single cursor on one line -> advance
 8064    // Cursor moves to column 0 on blank line
 8065    cx.set_state(indoc!(
 8066        "fn a() {
 8067             ˇdog();
 8068
 8069             cat();
 8070        }"
 8071    ));
 8072    cx.update_editor(|editor, cx| {
 8073        editor.toggle_comments(toggle_comments, cx);
 8074    });
 8075    cx.assert_editor_state(indoc!(
 8076        "fn a() {
 8077             // dog();
 8078        ˇ
 8079             cat();
 8080        }"
 8081    ));
 8082
 8083    // Single cursor on one line -> advance
 8084    // Cursor starts and ends at column 0
 8085    cx.set_state(indoc!(
 8086        "fn a() {
 8087         ˇ    dog();
 8088             cat();
 8089        }"
 8090    ));
 8091    cx.update_editor(|editor, cx| {
 8092        editor.toggle_comments(toggle_comments, cx);
 8093    });
 8094    cx.assert_editor_state(indoc!(
 8095        "fn a() {
 8096             // dog();
 8097         ˇ    cat();
 8098        }"
 8099    ));
 8100}
 8101
 8102#[gpui::test]
 8103async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8104    init_test(cx, |_| {});
 8105
 8106    let mut cx = EditorTestContext::new(cx).await;
 8107
 8108    let html_language = Arc::new(
 8109        Language::new(
 8110            LanguageConfig {
 8111                name: "HTML".into(),
 8112                block_comment: Some(("<!-- ".into(), " -->".into())),
 8113                ..Default::default()
 8114            },
 8115            Some(tree_sitter_html::language()),
 8116        )
 8117        .with_injection_query(
 8118            r#"
 8119            (script_element
 8120                (raw_text) @content
 8121                (#set! "language" "javascript"))
 8122            "#,
 8123        )
 8124        .unwrap(),
 8125    );
 8126
 8127    let javascript_language = Arc::new(Language::new(
 8128        LanguageConfig {
 8129            name: "JavaScript".into(),
 8130            line_comments: vec!["// ".into()],
 8131            ..Default::default()
 8132        },
 8133        Some(tree_sitter_typescript::language_tsx()),
 8134    ));
 8135
 8136    cx.language_registry().add(html_language.clone());
 8137    cx.language_registry().add(javascript_language.clone());
 8138    cx.update_buffer(|buffer, cx| {
 8139        buffer.set_language(Some(html_language), cx);
 8140    });
 8141
 8142    // Toggle comments for empty selections
 8143    cx.set_state(
 8144        &r#"
 8145            <p>A</p>ˇ
 8146            <p>B</p>ˇ
 8147            <p>C</p>ˇ
 8148        "#
 8149        .unindent(),
 8150    );
 8151    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8152    cx.assert_editor_state(
 8153        &r#"
 8154            <!-- <p>A</p>ˇ -->
 8155            <!-- <p>B</p>ˇ -->
 8156            <!-- <p>C</p>ˇ -->
 8157        "#
 8158        .unindent(),
 8159    );
 8160    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8161    cx.assert_editor_state(
 8162        &r#"
 8163            <p>A</p>ˇ
 8164            <p>B</p>ˇ
 8165            <p>C</p>ˇ
 8166        "#
 8167        .unindent(),
 8168    );
 8169
 8170    // Toggle comments for mixture of empty and non-empty selections, where
 8171    // multiple selections occupy a given line.
 8172    cx.set_state(
 8173        &r#"
 8174            <p>A«</p>
 8175            <p>ˇ»B</p>ˇ
 8176            <p>C«</p>
 8177            <p>ˇ»D</p>ˇ
 8178        "#
 8179        .unindent(),
 8180    );
 8181
 8182    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8183    cx.assert_editor_state(
 8184        &r#"
 8185            <!-- <p>A«</p>
 8186            <p>ˇ»B</p>ˇ -->
 8187            <!-- <p>C«</p>
 8188            <p>ˇ»D</p>ˇ -->
 8189        "#
 8190        .unindent(),
 8191    );
 8192    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8193    cx.assert_editor_state(
 8194        &r#"
 8195            <p>A«</p>
 8196            <p>ˇ»B</p>ˇ
 8197            <p>C«</p>
 8198            <p>ˇ»D</p>ˇ
 8199        "#
 8200        .unindent(),
 8201    );
 8202
 8203    // Toggle comments when different languages are active for different
 8204    // selections.
 8205    cx.set_state(
 8206        &r#"
 8207            ˇ<script>
 8208                ˇvar x = new Y();
 8209            ˇ</script>
 8210        "#
 8211        .unindent(),
 8212    );
 8213    cx.executor().run_until_parked();
 8214    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8215    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8216    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8217    cx.assert_editor_state(
 8218        &r#"
 8219            <!-- ˇ<script> -->
 8220                // ˇvar x = new Y();
 8221            // ˇ</script>
 8222        "#
 8223        .unindent(),
 8224    );
 8225}
 8226
 8227#[gpui::test]
 8228fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8229    init_test(cx, |_| {});
 8230
 8231    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8232    let multibuffer = cx.new_model(|cx| {
 8233        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8234        multibuffer.push_excerpts(
 8235            buffer.clone(),
 8236            [
 8237                ExcerptRange {
 8238                    context: Point::new(0, 0)..Point::new(0, 4),
 8239                    primary: None,
 8240                },
 8241                ExcerptRange {
 8242                    context: Point::new(1, 0)..Point::new(1, 4),
 8243                    primary: None,
 8244                },
 8245            ],
 8246            cx,
 8247        );
 8248        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8249        multibuffer
 8250    });
 8251
 8252    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8253    _ = view.update(cx, |view, cx| {
 8254        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8255        view.change_selections(None, cx, |s| {
 8256            s.select_ranges([
 8257                Point::new(0, 0)..Point::new(0, 0),
 8258                Point::new(1, 0)..Point::new(1, 0),
 8259            ])
 8260        });
 8261
 8262        view.handle_input("X", cx);
 8263        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8264        assert_eq!(
 8265            view.selections.ranges(cx),
 8266            [
 8267                Point::new(0, 1)..Point::new(0, 1),
 8268                Point::new(1, 1)..Point::new(1, 1),
 8269            ]
 8270        );
 8271
 8272        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8273        view.change_selections(None, cx, |s| {
 8274            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8275        });
 8276        view.backspace(&Default::default(), cx);
 8277        assert_eq!(view.text(cx), "Xa\nbbb");
 8278        assert_eq!(
 8279            view.selections.ranges(cx),
 8280            [Point::new(1, 0)..Point::new(1, 0)]
 8281        );
 8282
 8283        view.change_selections(None, cx, |s| {
 8284            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8285        });
 8286        view.backspace(&Default::default(), cx);
 8287        assert_eq!(view.text(cx), "X\nbb");
 8288        assert_eq!(
 8289            view.selections.ranges(cx),
 8290            [Point::new(0, 1)..Point::new(0, 1)]
 8291        );
 8292    });
 8293}
 8294
 8295#[gpui::test]
 8296fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8297    init_test(cx, |_| {});
 8298
 8299    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8300    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8301        indoc! {"
 8302            [aaaa
 8303            (bbbb]
 8304            cccc)",
 8305        },
 8306        markers.clone(),
 8307    );
 8308    let excerpt_ranges = markers.into_iter().map(|marker| {
 8309        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8310        ExcerptRange {
 8311            context,
 8312            primary: None,
 8313        }
 8314    });
 8315    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8316    let multibuffer = cx.new_model(|cx| {
 8317        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8318        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8319        multibuffer
 8320    });
 8321
 8322    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8323    _ = view.update(cx, |view, cx| {
 8324        let (expected_text, selection_ranges) = marked_text_ranges(
 8325            indoc! {"
 8326                aaaa
 8327                bˇbbb
 8328                bˇbbˇb
 8329                cccc"
 8330            },
 8331            true,
 8332        );
 8333        assert_eq!(view.text(cx), expected_text);
 8334        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8335
 8336        view.handle_input("X", cx);
 8337
 8338        let (expected_text, expected_selections) = marked_text_ranges(
 8339            indoc! {"
 8340                aaaa
 8341                bXˇbbXb
 8342                bXˇbbXˇb
 8343                cccc"
 8344            },
 8345            false,
 8346        );
 8347        assert_eq!(view.text(cx), expected_text);
 8348        assert_eq!(view.selections.ranges(cx), expected_selections);
 8349
 8350        view.newline(&Newline, cx);
 8351        let (expected_text, expected_selections) = marked_text_ranges(
 8352            indoc! {"
 8353                aaaa
 8354                bX
 8355                ˇbbX
 8356                b
 8357                bX
 8358                ˇbbX
 8359                ˇb
 8360                cccc"
 8361            },
 8362            false,
 8363        );
 8364        assert_eq!(view.text(cx), expected_text);
 8365        assert_eq!(view.selections.ranges(cx), expected_selections);
 8366    });
 8367}
 8368
 8369#[gpui::test]
 8370fn test_refresh_selections(cx: &mut TestAppContext) {
 8371    init_test(cx, |_| {});
 8372
 8373    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8374    let mut excerpt1_id = None;
 8375    let multibuffer = cx.new_model(|cx| {
 8376        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8377        excerpt1_id = multibuffer
 8378            .push_excerpts(
 8379                buffer.clone(),
 8380                [
 8381                    ExcerptRange {
 8382                        context: Point::new(0, 0)..Point::new(1, 4),
 8383                        primary: None,
 8384                    },
 8385                    ExcerptRange {
 8386                        context: Point::new(1, 0)..Point::new(2, 4),
 8387                        primary: None,
 8388                    },
 8389                ],
 8390                cx,
 8391            )
 8392            .into_iter()
 8393            .next();
 8394        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8395        multibuffer
 8396    });
 8397
 8398    let editor = cx.add_window(|cx| {
 8399        let mut editor = build_editor(multibuffer.clone(), cx);
 8400        let snapshot = editor.snapshot(cx);
 8401        editor.change_selections(None, cx, |s| {
 8402            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8403        });
 8404        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8405        assert_eq!(
 8406            editor.selections.ranges(cx),
 8407            [
 8408                Point::new(1, 3)..Point::new(1, 3),
 8409                Point::new(2, 1)..Point::new(2, 1),
 8410            ]
 8411        );
 8412        editor
 8413    });
 8414
 8415    // Refreshing selections is a no-op when excerpts haven't changed.
 8416    _ = editor.update(cx, |editor, cx| {
 8417        editor.change_selections(None, cx, |s| s.refresh());
 8418        assert_eq!(
 8419            editor.selections.ranges(cx),
 8420            [
 8421                Point::new(1, 3)..Point::new(1, 3),
 8422                Point::new(2, 1)..Point::new(2, 1),
 8423            ]
 8424        );
 8425    });
 8426
 8427    _ = multibuffer.update(cx, |multibuffer, cx| {
 8428        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8429    });
 8430    _ = editor.update(cx, |editor, cx| {
 8431        // Removing an excerpt causes the first selection to become degenerate.
 8432        assert_eq!(
 8433            editor.selections.ranges(cx),
 8434            [
 8435                Point::new(0, 0)..Point::new(0, 0),
 8436                Point::new(0, 1)..Point::new(0, 1)
 8437            ]
 8438        );
 8439
 8440        // Refreshing selections will relocate the first selection to the original buffer
 8441        // location.
 8442        editor.change_selections(None, cx, |s| s.refresh());
 8443        assert_eq!(
 8444            editor.selections.ranges(cx),
 8445            [
 8446                Point::new(0, 1)..Point::new(0, 1),
 8447                Point::new(0, 3)..Point::new(0, 3)
 8448            ]
 8449        );
 8450        assert!(editor.selections.pending_anchor().is_some());
 8451    });
 8452}
 8453
 8454#[gpui::test]
 8455fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8456    init_test(cx, |_| {});
 8457
 8458    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8459    let mut excerpt1_id = None;
 8460    let multibuffer = cx.new_model(|cx| {
 8461        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8462        excerpt1_id = multibuffer
 8463            .push_excerpts(
 8464                buffer.clone(),
 8465                [
 8466                    ExcerptRange {
 8467                        context: Point::new(0, 0)..Point::new(1, 4),
 8468                        primary: None,
 8469                    },
 8470                    ExcerptRange {
 8471                        context: Point::new(1, 0)..Point::new(2, 4),
 8472                        primary: None,
 8473                    },
 8474                ],
 8475                cx,
 8476            )
 8477            .into_iter()
 8478            .next();
 8479        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8480        multibuffer
 8481    });
 8482
 8483    let editor = cx.add_window(|cx| {
 8484        let mut editor = build_editor(multibuffer.clone(), cx);
 8485        let snapshot = editor.snapshot(cx);
 8486        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8487        assert_eq!(
 8488            editor.selections.ranges(cx),
 8489            [Point::new(1, 3)..Point::new(1, 3)]
 8490        );
 8491        editor
 8492    });
 8493
 8494    _ = multibuffer.update(cx, |multibuffer, cx| {
 8495        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8496    });
 8497    _ = editor.update(cx, |editor, cx| {
 8498        assert_eq!(
 8499            editor.selections.ranges(cx),
 8500            [Point::new(0, 0)..Point::new(0, 0)]
 8501        );
 8502
 8503        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8504        editor.change_selections(None, cx, |s| s.refresh());
 8505        assert_eq!(
 8506            editor.selections.ranges(cx),
 8507            [Point::new(0, 3)..Point::new(0, 3)]
 8508        );
 8509        assert!(editor.selections.pending_anchor().is_some());
 8510    });
 8511}
 8512
 8513#[gpui::test]
 8514async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8515    init_test(cx, |_| {});
 8516
 8517    let language = Arc::new(
 8518        Language::new(
 8519            LanguageConfig {
 8520                brackets: BracketPairConfig {
 8521                    pairs: vec![
 8522                        BracketPair {
 8523                            start: "{".to_string(),
 8524                            end: "}".to_string(),
 8525                            close: true,
 8526                            surround: true,
 8527                            newline: true,
 8528                        },
 8529                        BracketPair {
 8530                            start: "/* ".to_string(),
 8531                            end: " */".to_string(),
 8532                            close: true,
 8533                            surround: true,
 8534                            newline: true,
 8535                        },
 8536                    ],
 8537                    ..Default::default()
 8538                },
 8539                ..Default::default()
 8540            },
 8541            Some(tree_sitter_rust::language()),
 8542        )
 8543        .with_indents_query("")
 8544        .unwrap(),
 8545    );
 8546
 8547    let text = concat!(
 8548        "{   }\n",     //
 8549        "  x\n",       //
 8550        "  /*   */\n", //
 8551        "x\n",         //
 8552        "{{} }\n",     //
 8553    );
 8554
 8555    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 8556    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8557    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8558    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 8559        .await;
 8560
 8561    _ = view.update(cx, |view, cx| {
 8562        view.change_selections(None, cx, |s| {
 8563            s.select_display_ranges([
 8564                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 8565                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 8566                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 8567            ])
 8568        });
 8569        view.newline(&Newline, cx);
 8570
 8571        assert_eq!(
 8572            view.buffer().read(cx).read(cx).text(),
 8573            concat!(
 8574                "{ \n",    // Suppress rustfmt
 8575                "\n",      //
 8576                "}\n",     //
 8577                "  x\n",   //
 8578                "  /* \n", //
 8579                "  \n",    //
 8580                "  */\n",  //
 8581                "x\n",     //
 8582                "{{} \n",  //
 8583                "}\n",     //
 8584            )
 8585        );
 8586    });
 8587}
 8588
 8589#[gpui::test]
 8590fn test_highlighted_ranges(cx: &mut TestAppContext) {
 8591    init_test(cx, |_| {});
 8592
 8593    let editor = cx.add_window(|cx| {
 8594        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 8595        build_editor(buffer.clone(), cx)
 8596    });
 8597
 8598    _ = editor.update(cx, |editor, cx| {
 8599        struct Type1;
 8600        struct Type2;
 8601
 8602        let buffer = editor.buffer.read(cx).snapshot(cx);
 8603
 8604        let anchor_range =
 8605            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 8606
 8607        editor.highlight_background::<Type1>(
 8608            &[
 8609                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 8610                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 8611                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 8612                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 8613            ],
 8614            |_| Hsla::red(),
 8615            cx,
 8616        );
 8617        editor.highlight_background::<Type2>(
 8618            &[
 8619                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 8620                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 8621                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 8622                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 8623            ],
 8624            |_| Hsla::green(),
 8625            cx,
 8626        );
 8627
 8628        let snapshot = editor.snapshot(cx);
 8629        let mut highlighted_ranges = editor.background_highlights_in_range(
 8630            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 8631            &snapshot,
 8632            cx.theme().colors(),
 8633        );
 8634        // Enforce a consistent ordering based on color without relying on the ordering of the
 8635        // highlight's `TypeId` which is non-executor.
 8636        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 8637        assert_eq!(
 8638            highlighted_ranges,
 8639            &[
 8640                (
 8641                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 8642                    Hsla::red(),
 8643                ),
 8644                (
 8645                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8646                    Hsla::red(),
 8647                ),
 8648                (
 8649                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 8650                    Hsla::green(),
 8651                ),
 8652                (
 8653                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 8654                    Hsla::green(),
 8655                ),
 8656            ]
 8657        );
 8658        assert_eq!(
 8659            editor.background_highlights_in_range(
 8660                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 8661                &snapshot,
 8662                cx.theme().colors(),
 8663            ),
 8664            &[(
 8665                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8666                Hsla::red(),
 8667            )]
 8668        );
 8669    });
 8670}
 8671
 8672#[gpui::test]
 8673async fn test_following(cx: &mut gpui::TestAppContext) {
 8674    init_test(cx, |_| {});
 8675
 8676    let fs = FakeFs::new(cx.executor());
 8677    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8678
 8679    let buffer = project.update(cx, |project, cx| {
 8680        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 8681        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 8682    });
 8683    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 8684    let follower = cx.update(|cx| {
 8685        cx.open_window(
 8686            WindowOptions {
 8687                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 8688                    gpui::Point::new(px(0.), px(0.)),
 8689                    gpui::Point::new(px(10.), px(80.)),
 8690                ))),
 8691                ..Default::default()
 8692            },
 8693            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 8694        )
 8695        .unwrap()
 8696    });
 8697
 8698    let is_still_following = Rc::new(RefCell::new(true));
 8699    let follower_edit_event_count = Rc::new(RefCell::new(0));
 8700    let pending_update = Rc::new(RefCell::new(None));
 8701    _ = follower.update(cx, {
 8702        let update = pending_update.clone();
 8703        let is_still_following = is_still_following.clone();
 8704        let follower_edit_event_count = follower_edit_event_count.clone();
 8705        |_, cx| {
 8706            cx.subscribe(
 8707                &leader.root_view(cx).unwrap(),
 8708                move |_, leader, event, cx| {
 8709                    leader
 8710                        .read(cx)
 8711                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8712                },
 8713            )
 8714            .detach();
 8715
 8716            cx.subscribe(
 8717                &follower.root_view(cx).unwrap(),
 8718                move |_, _, event: &EditorEvent, _cx| {
 8719                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 8720                        *is_still_following.borrow_mut() = false;
 8721                    }
 8722
 8723                    if let EditorEvent::BufferEdited = event {
 8724                        *follower_edit_event_count.borrow_mut() += 1;
 8725                    }
 8726                },
 8727            )
 8728            .detach();
 8729        }
 8730    });
 8731
 8732    // Update the selections only
 8733    _ = leader.update(cx, |leader, cx| {
 8734        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8735    });
 8736    follower
 8737        .update(cx, |follower, cx| {
 8738            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8739        })
 8740        .unwrap()
 8741        .await
 8742        .unwrap();
 8743    _ = follower.update(cx, |follower, cx| {
 8744        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 8745    });
 8746    assert_eq!(*is_still_following.borrow(), true);
 8747    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8748
 8749    // Update the scroll position only
 8750    _ = leader.update(cx, |leader, cx| {
 8751        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8752    });
 8753    follower
 8754        .update(cx, |follower, cx| {
 8755            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8756        })
 8757        .unwrap()
 8758        .await
 8759        .unwrap();
 8760    assert_eq!(
 8761        follower
 8762            .update(cx, |follower, cx| follower.scroll_position(cx))
 8763            .unwrap(),
 8764        gpui::Point::new(1.5, 3.5)
 8765    );
 8766    assert_eq!(*is_still_following.borrow(), true);
 8767    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8768
 8769    // Update the selections and scroll position. The follower's scroll position is updated
 8770    // via autoscroll, not via the leader's exact scroll position.
 8771    _ = leader.update(cx, |leader, cx| {
 8772        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8773        leader.request_autoscroll(Autoscroll::newest(), cx);
 8774        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8775    });
 8776    follower
 8777        .update(cx, |follower, cx| {
 8778            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8779        })
 8780        .unwrap()
 8781        .await
 8782        .unwrap();
 8783    _ = follower.update(cx, |follower, cx| {
 8784        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 8785        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 8786    });
 8787    assert_eq!(*is_still_following.borrow(), true);
 8788
 8789    // Creating a pending selection that precedes another selection
 8790    _ = leader.update(cx, |leader, cx| {
 8791        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8792        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 8793    });
 8794    follower
 8795        .update(cx, |follower, cx| {
 8796            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8797        })
 8798        .unwrap()
 8799        .await
 8800        .unwrap();
 8801    _ = follower.update(cx, |follower, cx| {
 8802        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 8803    });
 8804    assert_eq!(*is_still_following.borrow(), true);
 8805
 8806    // Extend the pending selection so that it surrounds another selection
 8807    _ = leader.update(cx, |leader, cx| {
 8808        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 8809    });
 8810    follower
 8811        .update(cx, |follower, cx| {
 8812            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8813        })
 8814        .unwrap()
 8815        .await
 8816        .unwrap();
 8817    _ = follower.update(cx, |follower, cx| {
 8818        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 8819    });
 8820
 8821    // Scrolling locally breaks the follow
 8822    _ = follower.update(cx, |follower, cx| {
 8823        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 8824        follower.set_scroll_anchor(
 8825            ScrollAnchor {
 8826                anchor: top_anchor,
 8827                offset: gpui::Point::new(0.0, 0.5),
 8828            },
 8829            cx,
 8830        );
 8831    });
 8832    assert_eq!(*is_still_following.borrow(), false);
 8833}
 8834
 8835#[gpui::test]
 8836async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 8837    init_test(cx, |_| {});
 8838
 8839    let fs = FakeFs::new(cx.executor());
 8840    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8841    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8842    let pane = workspace
 8843        .update(cx, |workspace, _| workspace.active_pane().clone())
 8844        .unwrap();
 8845
 8846    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8847
 8848    let leader = pane.update(cx, |_, cx| {
 8849        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 8850        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 8851    });
 8852
 8853    // Start following the editor when it has no excerpts.
 8854    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8855    let follower_1 = cx
 8856        .update_window(*workspace.deref(), |_, cx| {
 8857            Editor::from_state_proto(
 8858                workspace.root_view(cx).unwrap(),
 8859                ViewId {
 8860                    creator: Default::default(),
 8861                    id: 0,
 8862                },
 8863                &mut state_message,
 8864                cx,
 8865            )
 8866        })
 8867        .unwrap()
 8868        .unwrap()
 8869        .await
 8870        .unwrap();
 8871
 8872    let update_message = Rc::new(RefCell::new(None));
 8873    follower_1.update(cx, {
 8874        let update = update_message.clone();
 8875        |_, cx| {
 8876            cx.subscribe(&leader, move |_, leader, event, cx| {
 8877                leader
 8878                    .read(cx)
 8879                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8880            })
 8881            .detach();
 8882        }
 8883    });
 8884
 8885    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 8886        (
 8887            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 8888            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 8889        )
 8890    });
 8891
 8892    // Insert some excerpts.
 8893    _ = leader.update(cx, |leader, cx| {
 8894        leader.buffer.update(cx, |multibuffer, cx| {
 8895            let excerpt_ids = multibuffer.push_excerpts(
 8896                buffer_1.clone(),
 8897                [
 8898                    ExcerptRange {
 8899                        context: 1..6,
 8900                        primary: None,
 8901                    },
 8902                    ExcerptRange {
 8903                        context: 12..15,
 8904                        primary: None,
 8905                    },
 8906                    ExcerptRange {
 8907                        context: 0..3,
 8908                        primary: None,
 8909                    },
 8910                ],
 8911                cx,
 8912            );
 8913            multibuffer.insert_excerpts_after(
 8914                excerpt_ids[0],
 8915                buffer_2.clone(),
 8916                [
 8917                    ExcerptRange {
 8918                        context: 8..12,
 8919                        primary: None,
 8920                    },
 8921                    ExcerptRange {
 8922                        context: 0..6,
 8923                        primary: None,
 8924                    },
 8925                ],
 8926                cx,
 8927            );
 8928        });
 8929    });
 8930
 8931    // Apply the update of adding the excerpts.
 8932    follower_1
 8933        .update(cx, |follower, cx| {
 8934            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8935        })
 8936        .await
 8937        .unwrap();
 8938    assert_eq!(
 8939        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8940        leader.update(cx, |editor, cx| editor.text(cx))
 8941    );
 8942    update_message.borrow_mut().take();
 8943
 8944    // Start following separately after it already has excerpts.
 8945    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8946    let follower_2 = cx
 8947        .update_window(*workspace.deref(), |_, cx| {
 8948            Editor::from_state_proto(
 8949                workspace.root_view(cx).unwrap().clone(),
 8950                ViewId {
 8951                    creator: Default::default(),
 8952                    id: 0,
 8953                },
 8954                &mut state_message,
 8955                cx,
 8956            )
 8957        })
 8958        .unwrap()
 8959        .unwrap()
 8960        .await
 8961        .unwrap();
 8962    assert_eq!(
 8963        follower_2.update(cx, |editor, cx| editor.text(cx)),
 8964        leader.update(cx, |editor, cx| editor.text(cx))
 8965    );
 8966
 8967    // Remove some excerpts.
 8968    _ = leader.update(cx, |leader, cx| {
 8969        leader.buffer.update(cx, |multibuffer, cx| {
 8970            let excerpt_ids = multibuffer.excerpt_ids();
 8971            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 8972            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 8973        });
 8974    });
 8975
 8976    // Apply the update of removing the excerpts.
 8977    follower_1
 8978        .update(cx, |follower, cx| {
 8979            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8980        })
 8981        .await
 8982        .unwrap();
 8983    follower_2
 8984        .update(cx, |follower, cx| {
 8985            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8986        })
 8987        .await
 8988        .unwrap();
 8989    update_message.borrow_mut().take();
 8990    assert_eq!(
 8991        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8992        leader.update(cx, |editor, cx| editor.text(cx))
 8993    );
 8994}
 8995
 8996#[gpui::test]
 8997async fn go_to_prev_overlapping_diagnostic(
 8998    executor: BackgroundExecutor,
 8999    cx: &mut gpui::TestAppContext,
 9000) {
 9001    init_test(cx, |_| {});
 9002
 9003    let mut cx = EditorTestContext::new(cx).await;
 9004    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9005
 9006    cx.set_state(indoc! {"
 9007        ˇfn func(abc def: i32) -> u32 {
 9008        }
 9009    "});
 9010
 9011    _ = cx.update(|cx| {
 9012        _ = project.update(cx, |project, cx| {
 9013            project
 9014                .update_diagnostics(
 9015                    LanguageServerId(0),
 9016                    lsp::PublishDiagnosticsParams {
 9017                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9018                        version: None,
 9019                        diagnostics: vec![
 9020                            lsp::Diagnostic {
 9021                                range: lsp::Range::new(
 9022                                    lsp::Position::new(0, 11),
 9023                                    lsp::Position::new(0, 12),
 9024                                ),
 9025                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9026                                ..Default::default()
 9027                            },
 9028                            lsp::Diagnostic {
 9029                                range: lsp::Range::new(
 9030                                    lsp::Position::new(0, 12),
 9031                                    lsp::Position::new(0, 15),
 9032                                ),
 9033                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9034                                ..Default::default()
 9035                            },
 9036                            lsp::Diagnostic {
 9037                                range: lsp::Range::new(
 9038                                    lsp::Position::new(0, 25),
 9039                                    lsp::Position::new(0, 28),
 9040                                ),
 9041                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9042                                ..Default::default()
 9043                            },
 9044                        ],
 9045                    },
 9046                    &[],
 9047                    cx,
 9048                )
 9049                .unwrap()
 9050        });
 9051    });
 9052
 9053    executor.run_until_parked();
 9054
 9055    cx.update_editor(|editor, cx| {
 9056        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9057    });
 9058
 9059    cx.assert_editor_state(indoc! {"
 9060        fn func(abc def: i32) -> ˇu32 {
 9061        }
 9062    "});
 9063
 9064    cx.update_editor(|editor, cx| {
 9065        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9066    });
 9067
 9068    cx.assert_editor_state(indoc! {"
 9069        fn func(abc ˇdef: i32) -> u32 {
 9070        }
 9071    "});
 9072
 9073    cx.update_editor(|editor, cx| {
 9074        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9075    });
 9076
 9077    cx.assert_editor_state(indoc! {"
 9078        fn func(abcˇ def: i32) -> u32 {
 9079        }
 9080    "});
 9081
 9082    cx.update_editor(|editor, cx| {
 9083        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9084    });
 9085
 9086    cx.assert_editor_state(indoc! {"
 9087        fn func(abc def: i32) -> ˇu32 {
 9088        }
 9089    "});
 9090}
 9091
 9092#[gpui::test]
 9093async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9094    init_test(cx, |_| {});
 9095
 9096    let mut cx = EditorTestContext::new(cx).await;
 9097
 9098    let diff_base = r#"
 9099        use some::mod;
 9100
 9101        const A: u32 = 42;
 9102
 9103        fn main() {
 9104            println!("hello");
 9105
 9106            println!("world");
 9107        }
 9108        "#
 9109    .unindent();
 9110
 9111    // Edits are modified, removed, modified, added
 9112    cx.set_state(
 9113        &r#"
 9114        use some::modified;
 9115
 9116        ˇ
 9117        fn main() {
 9118            println!("hello there");
 9119
 9120            println!("around the");
 9121            println!("world");
 9122        }
 9123        "#
 9124        .unindent(),
 9125    );
 9126
 9127    cx.set_diff_base(Some(&diff_base));
 9128    executor.run_until_parked();
 9129
 9130    cx.update_editor(|editor, cx| {
 9131        //Wrap around the bottom of the buffer
 9132        for _ in 0..3 {
 9133            editor.go_to_hunk(&GoToHunk, cx);
 9134        }
 9135    });
 9136
 9137    cx.assert_editor_state(
 9138        &r#"
 9139        ˇuse some::modified;
 9140
 9141
 9142        fn main() {
 9143            println!("hello there");
 9144
 9145            println!("around the");
 9146            println!("world");
 9147        }
 9148        "#
 9149        .unindent(),
 9150    );
 9151
 9152    cx.update_editor(|editor, cx| {
 9153        //Wrap around the top of the buffer
 9154        for _ in 0..2 {
 9155            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9156        }
 9157    });
 9158
 9159    cx.assert_editor_state(
 9160        &r#"
 9161        use some::modified;
 9162
 9163
 9164        fn main() {
 9165        ˇ    println!("hello there");
 9166
 9167            println!("around the");
 9168            println!("world");
 9169        }
 9170        "#
 9171        .unindent(),
 9172    );
 9173
 9174    cx.update_editor(|editor, cx| {
 9175        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9176    });
 9177
 9178    cx.assert_editor_state(
 9179        &r#"
 9180        use some::modified;
 9181
 9182        ˇ
 9183        fn main() {
 9184            println!("hello there");
 9185
 9186            println!("around the");
 9187            println!("world");
 9188        }
 9189        "#
 9190        .unindent(),
 9191    );
 9192
 9193    cx.update_editor(|editor, cx| {
 9194        for _ in 0..3 {
 9195            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9196        }
 9197    });
 9198
 9199    cx.assert_editor_state(
 9200        &r#"
 9201        use some::modified;
 9202
 9203
 9204        fn main() {
 9205        ˇ    println!("hello there");
 9206
 9207            println!("around the");
 9208            println!("world");
 9209        }
 9210        "#
 9211        .unindent(),
 9212    );
 9213
 9214    cx.update_editor(|editor, cx| {
 9215        editor.fold(&Fold, cx);
 9216
 9217        //Make sure that the fold only gets one hunk
 9218        for _ in 0..4 {
 9219            editor.go_to_hunk(&GoToHunk, cx);
 9220        }
 9221    });
 9222
 9223    cx.assert_editor_state(
 9224        &r#"
 9225        ˇuse some::modified;
 9226
 9227
 9228        fn main() {
 9229            println!("hello there");
 9230
 9231            println!("around the");
 9232            println!("world");
 9233        }
 9234        "#
 9235        .unindent(),
 9236    );
 9237}
 9238
 9239#[test]
 9240fn test_split_words() {
 9241    fn split(text: &str) -> Vec<&str> {
 9242        split_words(text).collect()
 9243    }
 9244
 9245    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9246    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9247    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9248    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9249    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9250    assert_eq!(split("helloworld"), &["helloworld"]);
 9251
 9252    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9253}
 9254
 9255#[gpui::test]
 9256async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9257    init_test(cx, |_| {});
 9258
 9259    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9260    let mut assert = |before, after| {
 9261        let _state_context = cx.set_state(before);
 9262        cx.update_editor(|editor, cx| {
 9263            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9264        });
 9265        cx.assert_editor_state(after);
 9266    };
 9267
 9268    // Outside bracket jumps to outside of matching bracket
 9269    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9270    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9271
 9272    // Inside bracket jumps to inside of matching bracket
 9273    assert("console.log(ˇvar);", "console.log(varˇ);");
 9274    assert("console.log(varˇ);", "console.log(ˇvar);");
 9275
 9276    // When outside a bracket and inside, favor jumping to the inside bracket
 9277    assert(
 9278        "console.log('foo', [1, 2, 3]ˇ);",
 9279        "console.log(ˇ'foo', [1, 2, 3]);",
 9280    );
 9281    assert(
 9282        "console.log(ˇ'foo', [1, 2, 3]);",
 9283        "console.log('foo', [1, 2, 3]ˇ);",
 9284    );
 9285
 9286    // Bias forward if two options are equally likely
 9287    assert(
 9288        "let result = curried_fun()ˇ();",
 9289        "let result = curried_fun()()ˇ;",
 9290    );
 9291
 9292    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9293    assert(
 9294        indoc! {"
 9295            function test() {
 9296                console.log('test')ˇ
 9297            }"},
 9298        indoc! {"
 9299            function test() {
 9300                console.logˇ('test')
 9301            }"},
 9302    );
 9303}
 9304
 9305#[gpui::test]
 9306async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9307    init_test(cx, |_| {});
 9308
 9309    let fs = FakeFs::new(cx.executor());
 9310    fs.insert_tree(
 9311        "/a",
 9312        json!({
 9313            "main.rs": "fn main() { let a = 5; }",
 9314            "other.rs": "// Test file",
 9315        }),
 9316    )
 9317    .await;
 9318    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9319
 9320    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9321    language_registry.add(Arc::new(Language::new(
 9322        LanguageConfig {
 9323            name: "Rust".into(),
 9324            matcher: LanguageMatcher {
 9325                path_suffixes: vec!["rs".to_string()],
 9326                ..Default::default()
 9327            },
 9328            brackets: BracketPairConfig {
 9329                pairs: vec![BracketPair {
 9330                    start: "{".to_string(),
 9331                    end: "}".to_string(),
 9332                    close: true,
 9333                    surround: true,
 9334                    newline: true,
 9335                }],
 9336                disabled_scopes_by_bracket_ix: Vec::new(),
 9337            },
 9338            ..Default::default()
 9339        },
 9340        Some(tree_sitter_rust::language()),
 9341    )));
 9342    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9343        "Rust",
 9344        FakeLspAdapter {
 9345            capabilities: lsp::ServerCapabilities {
 9346                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9347                    first_trigger_character: "{".to_string(),
 9348                    more_trigger_character: None,
 9349                }),
 9350                ..Default::default()
 9351            },
 9352            ..Default::default()
 9353        },
 9354    );
 9355
 9356    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9357
 9358    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9359
 9360    let worktree_id = workspace
 9361        .update(cx, |workspace, cx| {
 9362            workspace.project().update(cx, |project, cx| {
 9363                project.worktrees(cx).next().unwrap().read(cx).id()
 9364            })
 9365        })
 9366        .unwrap();
 9367
 9368    let buffer = project
 9369        .update(cx, |project, cx| {
 9370            project.open_local_buffer("/a/main.rs", cx)
 9371        })
 9372        .await
 9373        .unwrap();
 9374    cx.executor().run_until_parked();
 9375    cx.executor().start_waiting();
 9376    let fake_server = fake_servers.next().await.unwrap();
 9377    let editor_handle = workspace
 9378        .update(cx, |workspace, cx| {
 9379            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9380        })
 9381        .unwrap()
 9382        .await
 9383        .unwrap()
 9384        .downcast::<Editor>()
 9385        .unwrap();
 9386
 9387    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9388        assert_eq!(
 9389            params.text_document_position.text_document.uri,
 9390            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9391        );
 9392        assert_eq!(
 9393            params.text_document_position.position,
 9394            lsp::Position::new(0, 21),
 9395        );
 9396
 9397        Ok(Some(vec![lsp::TextEdit {
 9398            new_text: "]".to_string(),
 9399            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9400        }]))
 9401    });
 9402
 9403    editor_handle.update(cx, |editor, cx| {
 9404        editor.focus(cx);
 9405        editor.change_selections(None, cx, |s| {
 9406            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9407        });
 9408        editor.handle_input("{", cx);
 9409    });
 9410
 9411    cx.executor().run_until_parked();
 9412
 9413    _ = buffer.update(cx, |buffer, _| {
 9414        assert_eq!(
 9415            buffer.text(),
 9416            "fn main() { let a = {5}; }",
 9417            "No extra braces from on type formatting should appear in the buffer"
 9418        )
 9419    });
 9420}
 9421
 9422#[gpui::test]
 9423async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9424    init_test(cx, |_| {});
 9425
 9426    let fs = FakeFs::new(cx.executor());
 9427    fs.insert_tree(
 9428        "/a",
 9429        json!({
 9430            "main.rs": "fn main() { let a = 5; }",
 9431            "other.rs": "// Test file",
 9432        }),
 9433    )
 9434    .await;
 9435
 9436    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9437
 9438    let server_restarts = Arc::new(AtomicUsize::new(0));
 9439    let closure_restarts = Arc::clone(&server_restarts);
 9440    let language_server_name = "test language server";
 9441    let language_name: Arc<str> = "Rust".into();
 9442
 9443    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9444    language_registry.add(Arc::new(Language::new(
 9445        LanguageConfig {
 9446            name: Arc::clone(&language_name),
 9447            matcher: LanguageMatcher {
 9448                path_suffixes: vec!["rs".to_string()],
 9449                ..Default::default()
 9450            },
 9451            ..Default::default()
 9452        },
 9453        Some(tree_sitter_rust::language()),
 9454    )));
 9455    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9456        "Rust",
 9457        FakeLspAdapter {
 9458            name: language_server_name,
 9459            initialization_options: Some(json!({
 9460                "testOptionValue": true
 9461            })),
 9462            initializer: Some(Box::new(move |fake_server| {
 9463                let task_restarts = Arc::clone(&closure_restarts);
 9464                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9465                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9466                    futures::future::ready(Ok(()))
 9467                });
 9468            })),
 9469            ..Default::default()
 9470        },
 9471    );
 9472
 9473    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9474    let _buffer = project
 9475        .update(cx, |project, cx| {
 9476            project.open_local_buffer("/a/main.rs", cx)
 9477        })
 9478        .await
 9479        .unwrap();
 9480    let _fake_server = fake_servers.next().await.unwrap();
 9481    update_test_language_settings(cx, |language_settings| {
 9482        language_settings.languages.insert(
 9483            Arc::clone(&language_name),
 9484            LanguageSettingsContent {
 9485                tab_size: NonZeroU32::new(8),
 9486                ..Default::default()
 9487            },
 9488        );
 9489    });
 9490    cx.executor().run_until_parked();
 9491    assert_eq!(
 9492        server_restarts.load(atomic::Ordering::Acquire),
 9493        0,
 9494        "Should not restart LSP server on an unrelated change"
 9495    );
 9496
 9497    update_test_project_settings(cx, |project_settings| {
 9498        project_settings.lsp.insert(
 9499            "Some other server name".into(),
 9500            LspSettings {
 9501                binary: None,
 9502                settings: None,
 9503                initialization_options: Some(json!({
 9504                    "some other init value": false
 9505                })),
 9506            },
 9507        );
 9508    });
 9509    cx.executor().run_until_parked();
 9510    assert_eq!(
 9511        server_restarts.load(atomic::Ordering::Acquire),
 9512        0,
 9513        "Should not restart LSP server on an unrelated LSP settings change"
 9514    );
 9515
 9516    update_test_project_settings(cx, |project_settings| {
 9517        project_settings.lsp.insert(
 9518            language_server_name.into(),
 9519            LspSettings {
 9520                binary: None,
 9521                settings: None,
 9522                initialization_options: Some(json!({
 9523                    "anotherInitValue": false
 9524                })),
 9525            },
 9526        );
 9527    });
 9528    cx.executor().run_until_parked();
 9529    assert_eq!(
 9530        server_restarts.load(atomic::Ordering::Acquire),
 9531        1,
 9532        "Should restart LSP server on a related LSP settings change"
 9533    );
 9534
 9535    update_test_project_settings(cx, |project_settings| {
 9536        project_settings.lsp.insert(
 9537            language_server_name.into(),
 9538            LspSettings {
 9539                binary: None,
 9540                settings: None,
 9541                initialization_options: Some(json!({
 9542                    "anotherInitValue": false
 9543                })),
 9544            },
 9545        );
 9546    });
 9547    cx.executor().run_until_parked();
 9548    assert_eq!(
 9549        server_restarts.load(atomic::Ordering::Acquire),
 9550        1,
 9551        "Should not restart LSP server on a related LSP settings change that is the same"
 9552    );
 9553
 9554    update_test_project_settings(cx, |project_settings| {
 9555        project_settings.lsp.insert(
 9556            language_server_name.into(),
 9557            LspSettings {
 9558                binary: None,
 9559                settings: None,
 9560                initialization_options: None,
 9561            },
 9562        );
 9563    });
 9564    cx.executor().run_until_parked();
 9565    assert_eq!(
 9566        server_restarts.load(atomic::Ordering::Acquire),
 9567        2,
 9568        "Should restart LSP server on another related LSP settings change"
 9569    );
 9570}
 9571
 9572#[gpui::test]
 9573async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 9574    init_test(cx, |_| {});
 9575
 9576    let mut cx = EditorLspTestContext::new_rust(
 9577        lsp::ServerCapabilities {
 9578            completion_provider: Some(lsp::CompletionOptions {
 9579                trigger_characters: Some(vec![".".to_string()]),
 9580                resolve_provider: Some(true),
 9581                ..Default::default()
 9582            }),
 9583            ..Default::default()
 9584        },
 9585        cx,
 9586    )
 9587    .await;
 9588
 9589    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9590    cx.simulate_keystroke(".");
 9591    let completion_item = lsp::CompletionItem {
 9592        label: "some".into(),
 9593        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9594        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9595        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9596            kind: lsp::MarkupKind::Markdown,
 9597            value: "```rust\nSome(2)\n```".to_string(),
 9598        })),
 9599        deprecated: Some(false),
 9600        sort_text: Some("fffffff2".to_string()),
 9601        filter_text: Some("some".to_string()),
 9602        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9603        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9604            range: lsp::Range {
 9605                start: lsp::Position {
 9606                    line: 0,
 9607                    character: 22,
 9608                },
 9609                end: lsp::Position {
 9610                    line: 0,
 9611                    character: 22,
 9612                },
 9613            },
 9614            new_text: "Some(2)".to_string(),
 9615        })),
 9616        additional_text_edits: Some(vec![lsp::TextEdit {
 9617            range: lsp::Range {
 9618                start: lsp::Position {
 9619                    line: 0,
 9620                    character: 20,
 9621                },
 9622                end: lsp::Position {
 9623                    line: 0,
 9624                    character: 22,
 9625                },
 9626            },
 9627            new_text: "".to_string(),
 9628        }]),
 9629        ..Default::default()
 9630    };
 9631
 9632    let closure_completion_item = completion_item.clone();
 9633    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9634        let task_completion_item = closure_completion_item.clone();
 9635        async move {
 9636            Ok(Some(lsp::CompletionResponse::Array(vec![
 9637                task_completion_item,
 9638            ])))
 9639        }
 9640    });
 9641
 9642    request.next().await;
 9643
 9644    cx.condition(|editor, _| editor.context_menu_visible())
 9645        .await;
 9646    let apply_additional_edits = cx.update_editor(|editor, cx| {
 9647        editor
 9648            .confirm_completion(&ConfirmCompletion::default(), cx)
 9649            .unwrap()
 9650    });
 9651    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 9652
 9653    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 9654        let task_completion_item = completion_item.clone();
 9655        async move { Ok(task_completion_item) }
 9656    })
 9657    .next()
 9658    .await
 9659    .unwrap();
 9660    apply_additional_edits.await.unwrap();
 9661    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 9662}
 9663
 9664#[gpui::test]
 9665async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 9666    init_test(cx, |_| {});
 9667
 9668    let mut cx = EditorLspTestContext::new(
 9669        Language::new(
 9670            LanguageConfig {
 9671                matcher: LanguageMatcher {
 9672                    path_suffixes: vec!["jsx".into()],
 9673                    ..Default::default()
 9674                },
 9675                overrides: [(
 9676                    "element".into(),
 9677                    LanguageConfigOverride {
 9678                        word_characters: Override::Set(['-'].into_iter().collect()),
 9679                        ..Default::default()
 9680                    },
 9681                )]
 9682                .into_iter()
 9683                .collect(),
 9684                ..Default::default()
 9685            },
 9686            Some(tree_sitter_typescript::language_tsx()),
 9687        )
 9688        .with_override_query("(jsx_self_closing_element) @element")
 9689        .unwrap(),
 9690        lsp::ServerCapabilities {
 9691            completion_provider: Some(lsp::CompletionOptions {
 9692                trigger_characters: Some(vec![":".to_string()]),
 9693                ..Default::default()
 9694            }),
 9695            ..Default::default()
 9696        },
 9697        cx,
 9698    )
 9699    .await;
 9700
 9701    cx.lsp
 9702        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9703            Ok(Some(lsp::CompletionResponse::Array(vec![
 9704                lsp::CompletionItem {
 9705                    label: "bg-blue".into(),
 9706                    ..Default::default()
 9707                },
 9708                lsp::CompletionItem {
 9709                    label: "bg-red".into(),
 9710                    ..Default::default()
 9711                },
 9712                lsp::CompletionItem {
 9713                    label: "bg-yellow".into(),
 9714                    ..Default::default()
 9715                },
 9716            ])))
 9717        });
 9718
 9719    cx.set_state(r#"<p class="bgˇ" />"#);
 9720
 9721    // Trigger completion when typing a dash, because the dash is an extra
 9722    // word character in the 'element' scope, which contains the cursor.
 9723    cx.simulate_keystroke("-");
 9724    cx.executor().run_until_parked();
 9725    cx.update_editor(|editor, _| {
 9726        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9727            assert_eq!(
 9728                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9729                &["bg-red", "bg-blue", "bg-yellow"]
 9730            );
 9731        } else {
 9732            panic!("expected completion menu to be open");
 9733        }
 9734    });
 9735
 9736    cx.simulate_keystroke("l");
 9737    cx.executor().run_until_parked();
 9738    cx.update_editor(|editor, _| {
 9739        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9740            assert_eq!(
 9741                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9742                &["bg-blue", "bg-yellow"]
 9743            );
 9744        } else {
 9745            panic!("expected completion menu to be open");
 9746        }
 9747    });
 9748
 9749    // When filtering completions, consider the character after the '-' to
 9750    // be the start of a subword.
 9751    cx.set_state(r#"<p class="yelˇ" />"#);
 9752    cx.simulate_keystroke("l");
 9753    cx.executor().run_until_parked();
 9754    cx.update_editor(|editor, _| {
 9755        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9756            assert_eq!(
 9757                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9758                &["bg-yellow"]
 9759            );
 9760        } else {
 9761            panic!("expected completion menu to be open");
 9762        }
 9763    });
 9764}
 9765
 9766#[gpui::test]
 9767async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 9768    init_test(cx, |settings| {
 9769        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9770            FormatterList(vec![Formatter::Prettier].into()),
 9771        ))
 9772    });
 9773
 9774    let fs = FakeFs::new(cx.executor());
 9775    fs.insert_file("/file.ts", Default::default()).await;
 9776
 9777    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
 9778    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9779
 9780    language_registry.add(Arc::new(Language::new(
 9781        LanguageConfig {
 9782            name: "TypeScript".into(),
 9783            matcher: LanguageMatcher {
 9784                path_suffixes: vec!["ts".to_string()],
 9785                ..Default::default()
 9786            },
 9787            ..Default::default()
 9788        },
 9789        Some(tree_sitter_rust::language()),
 9790    )));
 9791    update_test_language_settings(cx, |settings| {
 9792        settings.defaults.prettier = Some(PrettierSettings {
 9793            allowed: true,
 9794            ..PrettierSettings::default()
 9795        });
 9796    });
 9797
 9798    let test_plugin = "test_plugin";
 9799    let _ = language_registry.register_fake_lsp_adapter(
 9800        "TypeScript",
 9801        FakeLspAdapter {
 9802            prettier_plugins: vec![test_plugin],
 9803            ..Default::default()
 9804        },
 9805    );
 9806
 9807    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 9808    let buffer = project
 9809        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
 9810        .await
 9811        .unwrap();
 9812
 9813    let buffer_text = "one\ntwo\nthree\n";
 9814    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9815    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9816    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 9817
 9818    editor
 9819        .update(cx, |editor, cx| {
 9820            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9821        })
 9822        .unwrap()
 9823        .await;
 9824    assert_eq!(
 9825        editor.update(cx, |editor, cx| editor.text(cx)),
 9826        buffer_text.to_string() + prettier_format_suffix,
 9827        "Test prettier formatting was not applied to the original buffer text",
 9828    );
 9829
 9830    update_test_language_settings(cx, |settings| {
 9831        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9832    });
 9833    let format = editor.update(cx, |editor, cx| {
 9834        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9835    });
 9836    format.await.unwrap();
 9837    assert_eq!(
 9838        editor.update(cx, |editor, cx| editor.text(cx)),
 9839        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 9840        "Autoformatting (via test prettier) was not applied to the original buffer text",
 9841    );
 9842}
 9843
 9844#[gpui::test]
 9845async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 9846    init_test(cx, |_| {});
 9847    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9848    let base_text = indoc! {r#"struct Row;
 9849struct Row1;
 9850struct Row2;
 9851
 9852struct Row4;
 9853struct Row5;
 9854struct Row6;
 9855
 9856struct Row8;
 9857struct Row9;
 9858struct Row10;"#};
 9859
 9860    // When addition hunks are not adjacent to carets, no hunk revert is performed
 9861    assert_hunk_revert(
 9862        indoc! {r#"struct Row;
 9863                   struct Row1;
 9864                   struct Row1.1;
 9865                   struct Row1.2;
 9866                   struct Row2;ˇ
 9867
 9868                   struct Row4;
 9869                   struct Row5;
 9870                   struct Row6;
 9871
 9872                   struct Row8;
 9873                   ˇstruct Row9;
 9874                   struct Row9.1;
 9875                   struct Row9.2;
 9876                   struct Row9.3;
 9877                   struct Row10;"#},
 9878        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9879        indoc! {r#"struct Row;
 9880                   struct Row1;
 9881                   struct Row1.1;
 9882                   struct Row1.2;
 9883                   struct Row2;ˇ
 9884
 9885                   struct Row4;
 9886                   struct Row5;
 9887                   struct Row6;
 9888
 9889                   struct Row8;
 9890                   ˇstruct Row9;
 9891                   struct Row9.1;
 9892                   struct Row9.2;
 9893                   struct Row9.3;
 9894                   struct Row10;"#},
 9895        base_text,
 9896        &mut cx,
 9897    );
 9898    // Same for selections
 9899    assert_hunk_revert(
 9900        indoc! {r#"struct Row;
 9901                   struct Row1;
 9902                   struct Row2;
 9903                   struct Row2.1;
 9904                   struct Row2.2;
 9905                   «ˇ
 9906                   struct Row4;
 9907                   struct» Row5;
 9908                   «struct Row6;
 9909                   ˇ»
 9910                   struct Row9.1;
 9911                   struct Row9.2;
 9912                   struct Row9.3;
 9913                   struct Row8;
 9914                   struct Row9;
 9915                   struct Row10;"#},
 9916        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9917        indoc! {r#"struct Row;
 9918                   struct Row1;
 9919                   struct Row2;
 9920                   struct Row2.1;
 9921                   struct Row2.2;
 9922                   «ˇ
 9923                   struct Row4;
 9924                   struct» Row5;
 9925                   «struct Row6;
 9926                   ˇ»
 9927                   struct Row9.1;
 9928                   struct Row9.2;
 9929                   struct Row9.3;
 9930                   struct Row8;
 9931                   struct Row9;
 9932                   struct Row10;"#},
 9933        base_text,
 9934        &mut cx,
 9935    );
 9936
 9937    // When carets and selections intersect the addition hunks, those are reverted.
 9938    // Adjacent carets got merged.
 9939    assert_hunk_revert(
 9940        indoc! {r#"struct Row;
 9941                   ˇ// something on the top
 9942                   struct Row1;
 9943                   struct Row2;
 9944                   struct Roˇw3.1;
 9945                   struct Row2.2;
 9946                   struct Row2.3;ˇ
 9947
 9948                   struct Row4;
 9949                   struct ˇRow5.1;
 9950                   struct Row5.2;
 9951                   struct «Rowˇ»5.3;
 9952                   struct Row5;
 9953                   struct Row6;
 9954                   ˇ
 9955                   struct Row9.1;
 9956                   struct «Rowˇ»9.2;
 9957                   struct «ˇRow»9.3;
 9958                   struct Row8;
 9959                   struct Row9;
 9960                   «ˇ// something on bottom»
 9961                   struct Row10;"#},
 9962        vec![
 9963            DiffHunkStatus::Added,
 9964            DiffHunkStatus::Added,
 9965            DiffHunkStatus::Added,
 9966            DiffHunkStatus::Added,
 9967            DiffHunkStatus::Added,
 9968        ],
 9969        indoc! {r#"struct Row;
 9970                   ˇstruct Row1;
 9971                   struct Row2;
 9972                   ˇ
 9973                   struct Row4;
 9974                   ˇstruct Row5;
 9975                   struct Row6;
 9976                   ˇ
 9977                   ˇstruct Row8;
 9978                   struct Row9;
 9979                   ˇstruct Row10;"#},
 9980        base_text,
 9981        &mut cx,
 9982    );
 9983}
 9984
 9985#[gpui::test]
 9986async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
 9987    init_test(cx, |_| {});
 9988    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9989    let base_text = indoc! {r#"struct Row;
 9990struct Row1;
 9991struct Row2;
 9992
 9993struct Row4;
 9994struct Row5;
 9995struct Row6;
 9996
 9997struct Row8;
 9998struct Row9;
 9999struct Row10;"#};
10000
10001    // Modification hunks behave the same as the addition ones.
10002    assert_hunk_revert(
10003        indoc! {r#"struct Row;
10004                   struct Row1;
10005                   struct Row33;
10006                   ˇ
10007                   struct Row4;
10008                   struct Row5;
10009                   struct Row6;
10010                   ˇ
10011                   struct Row99;
10012                   struct Row9;
10013                   struct Row10;"#},
10014        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10015        indoc! {r#"struct Row;
10016                   struct Row1;
10017                   struct Row33;
10018                   ˇ
10019                   struct Row4;
10020                   struct Row5;
10021                   struct Row6;
10022                   ˇ
10023                   struct Row99;
10024                   struct Row9;
10025                   struct Row10;"#},
10026        base_text,
10027        &mut cx,
10028    );
10029    assert_hunk_revert(
10030        indoc! {r#"struct Row;
10031                   struct Row1;
10032                   struct Row33;
10033                   «ˇ
10034                   struct Row4;
10035                   struct» Row5;
10036                   «struct Row6;
10037                   ˇ»
10038                   struct Row99;
10039                   struct Row9;
10040                   struct Row10;"#},
10041        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10042        indoc! {r#"struct Row;
10043                   struct Row1;
10044                   struct Row33;
10045                   «ˇ
10046                   struct Row4;
10047                   struct» Row5;
10048                   «struct Row6;
10049                   ˇ»
10050                   struct Row99;
10051                   struct Row9;
10052                   struct Row10;"#},
10053        base_text,
10054        &mut cx,
10055    );
10056
10057    assert_hunk_revert(
10058        indoc! {r#"ˇstruct Row1.1;
10059                   struct Row1;
10060                   «ˇstr»uct Row22;
10061
10062                   struct ˇRow44;
10063                   struct Row5;
10064                   struct «Rˇ»ow66;ˇ
10065
10066                   «struˇ»ct Row88;
10067                   struct Row9;
10068                   struct Row1011;ˇ"#},
10069        vec![
10070            DiffHunkStatus::Modified,
10071            DiffHunkStatus::Modified,
10072            DiffHunkStatus::Modified,
10073            DiffHunkStatus::Modified,
10074            DiffHunkStatus::Modified,
10075            DiffHunkStatus::Modified,
10076        ],
10077        indoc! {r#"struct Row;
10078                   ˇstruct Row1;
10079                   struct Row2;
10080                   ˇ
10081                   struct Row4;
10082                   ˇstruct Row5;
10083                   struct Row6;
10084                   ˇ
10085                   struct Row8;
10086                   ˇstruct Row9;
10087                   struct Row10;ˇ"#},
10088        base_text,
10089        &mut cx,
10090    );
10091}
10092
10093#[gpui::test]
10094async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10095    init_test(cx, |_| {});
10096    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10097    let base_text = indoc! {r#"struct Row;
10098struct Row1;
10099struct Row2;
10100
10101struct Row4;
10102struct Row5;
10103struct Row6;
10104
10105struct Row8;
10106struct Row9;
10107struct Row10;"#};
10108
10109    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10110    assert_hunk_revert(
10111        indoc! {r#"struct Row;
10112                   struct Row2;
10113
10114                   ˇstruct Row4;
10115                   struct Row5;
10116                   struct Row6;
10117                   ˇ
10118                   struct Row8;
10119                   struct Row10;"#},
10120        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10121        indoc! {r#"struct Row;
10122                   struct Row2;
10123
10124                   ˇstruct Row4;
10125                   struct Row5;
10126                   struct Row6;
10127                   ˇ
10128                   struct Row8;
10129                   struct Row10;"#},
10130        base_text,
10131        &mut cx,
10132    );
10133    assert_hunk_revert(
10134        indoc! {r#"struct Row;
10135                   struct Row2;
10136
10137                   «ˇstruct Row4;
10138                   struct» Row5;
10139                   «struct Row6;
10140                   ˇ»
10141                   struct Row8;
10142                   struct Row10;"#},
10143        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10144        indoc! {r#"struct Row;
10145                   struct Row2;
10146
10147                   «ˇstruct Row4;
10148                   struct» Row5;
10149                   «struct Row6;
10150                   ˇ»
10151                   struct Row8;
10152                   struct Row10;"#},
10153        base_text,
10154        &mut cx,
10155    );
10156
10157    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10158    assert_hunk_revert(
10159        indoc! {r#"struct Row;
10160                   ˇstruct Row2;
10161
10162                   struct Row4;
10163                   struct Row5;
10164                   struct Row6;
10165
10166                   struct Row8;ˇ
10167                   struct Row10;"#},
10168        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10169        indoc! {r#"struct Row;
10170                   struct Row1;
10171                   ˇstruct Row2;
10172
10173                   struct Row4;
10174                   struct Row5;
10175                   struct Row6;
10176
10177                   struct Row8;ˇ
10178                   struct Row9;
10179                   struct Row10;"#},
10180        base_text,
10181        &mut cx,
10182    );
10183    assert_hunk_revert(
10184        indoc! {r#"struct Row;
10185                   struct Row2«ˇ;
10186                   struct Row4;
10187                   struct» Row5;
10188                   «struct Row6;
10189
10190                   struct Row8;ˇ»
10191                   struct Row10;"#},
10192        vec![
10193            DiffHunkStatus::Removed,
10194            DiffHunkStatus::Removed,
10195            DiffHunkStatus::Removed,
10196        ],
10197        indoc! {r#"struct Row;
10198                   struct Row1;
10199                   struct Row2«ˇ;
10200
10201                   struct Row4;
10202                   struct» Row5;
10203                   «struct Row6;
10204
10205                   struct Row8;ˇ»
10206                   struct Row9;
10207                   struct Row10;"#},
10208        base_text,
10209        &mut cx,
10210    );
10211}
10212
10213#[gpui::test]
10214async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10215    init_test(cx, |_| {});
10216
10217    let cols = 4;
10218    let rows = 10;
10219    let sample_text_1 = sample_text(rows, cols, 'a');
10220    assert_eq!(
10221        sample_text_1,
10222        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10223    );
10224    let sample_text_2 = sample_text(rows, cols, 'l');
10225    assert_eq!(
10226        sample_text_2,
10227        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10228    );
10229    let sample_text_3 = sample_text(rows, cols, 'v');
10230    assert_eq!(
10231        sample_text_3,
10232        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10233    );
10234
10235    fn diff_every_buffer_row(
10236        buffer: &Model<Buffer>,
10237        sample_text: String,
10238        cols: usize,
10239        cx: &mut gpui::TestAppContext,
10240    ) {
10241        // revert first character in each row, creating one large diff hunk per buffer
10242        let is_first_char = |offset: usize| offset % cols == 0;
10243        buffer.update(cx, |buffer, cx| {
10244            buffer.set_text(
10245                sample_text
10246                    .chars()
10247                    .enumerate()
10248                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10249                    .collect::<String>(),
10250                cx,
10251            );
10252            buffer.set_diff_base(Some(sample_text), cx);
10253        });
10254        cx.executor().run_until_parked();
10255    }
10256
10257    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10258    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10259
10260    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10261    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10262
10263    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10264    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10265
10266    let multibuffer = cx.new_model(|cx| {
10267        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10268        multibuffer.push_excerpts(
10269            buffer_1.clone(),
10270            [
10271                ExcerptRange {
10272                    context: Point::new(0, 0)..Point::new(3, 0),
10273                    primary: None,
10274                },
10275                ExcerptRange {
10276                    context: Point::new(5, 0)..Point::new(7, 0),
10277                    primary: None,
10278                },
10279                ExcerptRange {
10280                    context: Point::new(9, 0)..Point::new(10, 4),
10281                    primary: None,
10282                },
10283            ],
10284            cx,
10285        );
10286        multibuffer.push_excerpts(
10287            buffer_2.clone(),
10288            [
10289                ExcerptRange {
10290                    context: Point::new(0, 0)..Point::new(3, 0),
10291                    primary: None,
10292                },
10293                ExcerptRange {
10294                    context: Point::new(5, 0)..Point::new(7, 0),
10295                    primary: None,
10296                },
10297                ExcerptRange {
10298                    context: Point::new(9, 0)..Point::new(10, 4),
10299                    primary: None,
10300                },
10301            ],
10302            cx,
10303        );
10304        multibuffer.push_excerpts(
10305            buffer_3.clone(),
10306            [
10307                ExcerptRange {
10308                    context: Point::new(0, 0)..Point::new(3, 0),
10309                    primary: None,
10310                },
10311                ExcerptRange {
10312                    context: Point::new(5, 0)..Point::new(7, 0),
10313                    primary: None,
10314                },
10315                ExcerptRange {
10316                    context: Point::new(9, 0)..Point::new(10, 4),
10317                    primary: None,
10318                },
10319            ],
10320            cx,
10321        );
10322        multibuffer
10323    });
10324
10325    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10326    editor.update(cx, |editor, cx| {
10327        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");
10328        editor.select_all(&SelectAll, cx);
10329        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10330    });
10331    cx.executor().run_until_parked();
10332    // When all ranges are selected, all buffer hunks are reverted.
10333    editor.update(cx, |editor, cx| {
10334        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");
10335    });
10336    buffer_1.update(cx, |buffer, _| {
10337        assert_eq!(buffer.text(), sample_text_1);
10338    });
10339    buffer_2.update(cx, |buffer, _| {
10340        assert_eq!(buffer.text(), sample_text_2);
10341    });
10342    buffer_3.update(cx, |buffer, _| {
10343        assert_eq!(buffer.text(), sample_text_3);
10344    });
10345
10346    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10347    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10348    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10349    editor.update(cx, |editor, cx| {
10350        editor.change_selections(None, cx, |s| {
10351            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10352        });
10353        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10354    });
10355    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10356    // but not affect buffer_2 and its related excerpts.
10357    editor.update(cx, |editor, cx| {
10358        assert_eq!(
10359            editor.text(cx),
10360            "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"
10361        );
10362    });
10363    buffer_1.update(cx, |buffer, _| {
10364        assert_eq!(buffer.text(), sample_text_1);
10365    });
10366    buffer_2.update(cx, |buffer, _| {
10367        assert_eq!(
10368            buffer.text(),
10369            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10370        );
10371    });
10372    buffer_3.update(cx, |buffer, _| {
10373        assert_eq!(
10374            buffer.text(),
10375            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10376        );
10377    });
10378}
10379
10380#[gpui::test]
10381async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10382    init_test(cx, |_| {});
10383
10384    let cols = 4;
10385    let rows = 10;
10386    let sample_text_1 = sample_text(rows, cols, 'a');
10387    assert_eq!(
10388        sample_text_1,
10389        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10390    );
10391    let sample_text_2 = sample_text(rows, cols, 'l');
10392    assert_eq!(
10393        sample_text_2,
10394        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10395    );
10396    let sample_text_3 = sample_text(rows, cols, 'v');
10397    assert_eq!(
10398        sample_text_3,
10399        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10400    );
10401
10402    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10403    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10404    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10405
10406    let multi_buffer = cx.new_model(|cx| {
10407        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10408        multibuffer.push_excerpts(
10409            buffer_1.clone(),
10410            [
10411                ExcerptRange {
10412                    context: Point::new(0, 0)..Point::new(3, 0),
10413                    primary: None,
10414                },
10415                ExcerptRange {
10416                    context: Point::new(5, 0)..Point::new(7, 0),
10417                    primary: None,
10418                },
10419                ExcerptRange {
10420                    context: Point::new(9, 0)..Point::new(10, 4),
10421                    primary: None,
10422                },
10423            ],
10424            cx,
10425        );
10426        multibuffer.push_excerpts(
10427            buffer_2.clone(),
10428            [
10429                ExcerptRange {
10430                    context: Point::new(0, 0)..Point::new(3, 0),
10431                    primary: None,
10432                },
10433                ExcerptRange {
10434                    context: Point::new(5, 0)..Point::new(7, 0),
10435                    primary: None,
10436                },
10437                ExcerptRange {
10438                    context: Point::new(9, 0)..Point::new(10, 4),
10439                    primary: None,
10440                },
10441            ],
10442            cx,
10443        );
10444        multibuffer.push_excerpts(
10445            buffer_3.clone(),
10446            [
10447                ExcerptRange {
10448                    context: Point::new(0, 0)..Point::new(3, 0),
10449                    primary: None,
10450                },
10451                ExcerptRange {
10452                    context: Point::new(5, 0)..Point::new(7, 0),
10453                    primary: None,
10454                },
10455                ExcerptRange {
10456                    context: Point::new(9, 0)..Point::new(10, 4),
10457                    primary: None,
10458                },
10459            ],
10460            cx,
10461        );
10462        multibuffer
10463    });
10464
10465    let fs = FakeFs::new(cx.executor());
10466    fs.insert_tree(
10467        "/a",
10468        json!({
10469            "main.rs": sample_text_1,
10470            "other.rs": sample_text_2,
10471            "lib.rs": sample_text_3,
10472        }),
10473    )
10474    .await;
10475    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10476    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10477    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10478    let multi_buffer_editor = cx.new_view(|cx| {
10479        Editor::new(
10480            EditorMode::Full,
10481            multi_buffer,
10482            Some(project.clone()),
10483            true,
10484            cx,
10485        )
10486    });
10487    let multibuffer_item_id = workspace
10488        .update(cx, |workspace, cx| {
10489            assert!(
10490                workspace.active_item(cx).is_none(),
10491                "active item should be None before the first item is added"
10492            );
10493            workspace.add_item_to_active_pane(
10494                Box::new(multi_buffer_editor.clone()),
10495                None,
10496                true,
10497                cx,
10498            );
10499            let active_item = workspace
10500                .active_item(cx)
10501                .expect("should have an active item after adding the multi buffer");
10502            assert!(
10503                !active_item.is_singleton(cx),
10504                "A multi buffer was expected to active after adding"
10505            );
10506            active_item.item_id()
10507        })
10508        .unwrap();
10509    cx.executor().run_until_parked();
10510
10511    multi_buffer_editor.update(cx, |editor, cx| {
10512        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
10513        editor.open_excerpts(&OpenExcerpts, cx);
10514    });
10515    cx.executor().run_until_parked();
10516    let first_item_id = workspace
10517        .update(cx, |workspace, cx| {
10518            let active_item = workspace
10519                .active_item(cx)
10520                .expect("should have an active item after navigating into the 1st buffer");
10521            let first_item_id = active_item.item_id();
10522            assert_ne!(
10523                first_item_id, multibuffer_item_id,
10524                "Should navigate into the 1st buffer and activate it"
10525            );
10526            assert!(
10527                active_item.is_singleton(cx),
10528                "New active item should be a singleton buffer"
10529            );
10530            assert_eq!(
10531                active_item
10532                    .act_as::<Editor>(cx)
10533                    .expect("should have navigated into an editor for the 1st buffer")
10534                    .read(cx)
10535                    .text(cx),
10536                sample_text_1
10537            );
10538
10539            workspace
10540                .go_back(workspace.active_pane().downgrade(), cx)
10541                .detach_and_log_err(cx);
10542
10543            first_item_id
10544        })
10545        .unwrap();
10546    cx.executor().run_until_parked();
10547    workspace
10548        .update(cx, |workspace, cx| {
10549            let active_item = workspace
10550                .active_item(cx)
10551                .expect("should have an active item after navigating back");
10552            assert_eq!(
10553                active_item.item_id(),
10554                multibuffer_item_id,
10555                "Should navigate back to the multi buffer"
10556            );
10557            assert!(!active_item.is_singleton(cx));
10558        })
10559        .unwrap();
10560
10561    multi_buffer_editor.update(cx, |editor, cx| {
10562        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10563            s.select_ranges(Some(39..40))
10564        });
10565        editor.open_excerpts(&OpenExcerpts, cx);
10566    });
10567    cx.executor().run_until_parked();
10568    let second_item_id = workspace
10569        .update(cx, |workspace, cx| {
10570            let active_item = workspace
10571                .active_item(cx)
10572                .expect("should have an active item after navigating into the 2nd buffer");
10573            let second_item_id = active_item.item_id();
10574            assert_ne!(
10575                second_item_id, multibuffer_item_id,
10576                "Should navigate away from the multibuffer"
10577            );
10578            assert_ne!(
10579                second_item_id, first_item_id,
10580                "Should navigate into the 2nd buffer and activate it"
10581            );
10582            assert!(
10583                active_item.is_singleton(cx),
10584                "New active item should be a singleton buffer"
10585            );
10586            assert_eq!(
10587                active_item
10588                    .act_as::<Editor>(cx)
10589                    .expect("should have navigated into an editor")
10590                    .read(cx)
10591                    .text(cx),
10592                sample_text_2
10593            );
10594
10595            workspace
10596                .go_back(workspace.active_pane().downgrade(), cx)
10597                .detach_and_log_err(cx);
10598
10599            second_item_id
10600        })
10601        .unwrap();
10602    cx.executor().run_until_parked();
10603    workspace
10604        .update(cx, |workspace, cx| {
10605            let active_item = workspace
10606                .active_item(cx)
10607                .expect("should have an active item after navigating back from the 2nd buffer");
10608            assert_eq!(
10609                active_item.item_id(),
10610                multibuffer_item_id,
10611                "Should navigate back from the 2nd buffer to the multi buffer"
10612            );
10613            assert!(!active_item.is_singleton(cx));
10614        })
10615        .unwrap();
10616
10617    multi_buffer_editor.update(cx, |editor, cx| {
10618        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10619            s.select_ranges(Some(60..70))
10620        });
10621        editor.open_excerpts(&OpenExcerpts, cx);
10622    });
10623    cx.executor().run_until_parked();
10624    workspace
10625        .update(cx, |workspace, cx| {
10626            let active_item = workspace
10627                .active_item(cx)
10628                .expect("should have an active item after navigating into the 3rd buffer");
10629            let third_item_id = active_item.item_id();
10630            assert_ne!(
10631                third_item_id, multibuffer_item_id,
10632                "Should navigate into the 3rd buffer and activate it"
10633            );
10634            assert_ne!(third_item_id, first_item_id);
10635            assert_ne!(third_item_id, second_item_id);
10636            assert!(
10637                active_item.is_singleton(cx),
10638                "New active item should be a singleton buffer"
10639            );
10640            assert_eq!(
10641                active_item
10642                    .act_as::<Editor>(cx)
10643                    .expect("should have navigated into an editor")
10644                    .read(cx)
10645                    .text(cx),
10646                sample_text_3
10647            );
10648
10649            workspace
10650                .go_back(workspace.active_pane().downgrade(), cx)
10651                .detach_and_log_err(cx);
10652        })
10653        .unwrap();
10654    cx.executor().run_until_parked();
10655    workspace
10656        .update(cx, |workspace, cx| {
10657            let active_item = workspace
10658                .active_item(cx)
10659                .expect("should have an active item after navigating back from the 3rd buffer");
10660            assert_eq!(
10661                active_item.item_id(),
10662                multibuffer_item_id,
10663                "Should navigate back from the 3rd buffer to the multi buffer"
10664            );
10665            assert!(!active_item.is_singleton(cx));
10666        })
10667        .unwrap();
10668}
10669
10670#[gpui::test]
10671async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10672    init_test(cx, |_| {});
10673
10674    let mut cx = EditorTestContext::new(cx).await;
10675
10676    let diff_base = r#"
10677        use some::mod;
10678
10679        const A: u32 = 42;
10680
10681        fn main() {
10682            println!("hello");
10683
10684            println!("world");
10685        }
10686        "#
10687    .unindent();
10688
10689    cx.set_state(
10690        &r#"
10691        use some::modified;
10692
10693        ˇ
10694        fn main() {
10695            println!("hello there");
10696
10697            println!("around the");
10698            println!("world");
10699        }
10700        "#
10701        .unindent(),
10702    );
10703
10704    cx.set_diff_base(Some(&diff_base));
10705    executor.run_until_parked();
10706    let unexpanded_hunks = vec![
10707        (
10708            "use some::mod;\n".to_string(),
10709            DiffHunkStatus::Modified,
10710            DisplayRow(0)..DisplayRow(1),
10711        ),
10712        (
10713            "const A: u32 = 42;\n".to_string(),
10714            DiffHunkStatus::Removed,
10715            DisplayRow(2)..DisplayRow(2),
10716        ),
10717        (
10718            "    println!(\"hello\");\n".to_string(),
10719            DiffHunkStatus::Modified,
10720            DisplayRow(4)..DisplayRow(5),
10721        ),
10722        (
10723            "".to_string(),
10724            DiffHunkStatus::Added,
10725            DisplayRow(6)..DisplayRow(7),
10726        ),
10727    ];
10728    cx.update_editor(|editor, cx| {
10729        let snapshot = editor.snapshot(cx);
10730        let all_hunks = editor_hunks(editor, &snapshot, cx);
10731        assert_eq!(all_hunks, unexpanded_hunks);
10732    });
10733
10734    cx.update_editor(|editor, cx| {
10735        for _ in 0..4 {
10736            editor.go_to_hunk(&GoToHunk, cx);
10737            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10738        }
10739    });
10740    executor.run_until_parked();
10741    cx.assert_editor_state(
10742        &r#"
10743        use some::modified;
10744
10745        ˇ
10746        fn main() {
10747            println!("hello there");
10748
10749            println!("around the");
10750            println!("world");
10751        }
10752        "#
10753        .unindent(),
10754    );
10755    cx.update_editor(|editor, cx| {
10756        let snapshot = editor.snapshot(cx);
10757        let all_hunks = editor_hunks(editor, &snapshot, cx);
10758        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10759        assert_eq!(
10760            expanded_hunks_background_highlights(editor, cx),
10761            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
10762            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10763        );
10764        assert_eq!(
10765            all_hunks,
10766            vec![
10767                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
10768                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
10769                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
10770                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
10771            ],
10772            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10773            (from modified and removed hunks)"
10774        );
10775        assert_eq!(
10776            all_hunks, all_expanded_hunks,
10777            "Editor hunks should not change and all be expanded"
10778        );
10779    });
10780
10781    cx.update_editor(|editor, cx| {
10782        editor.cancel(&Cancel, cx);
10783
10784        let snapshot = editor.snapshot(cx);
10785        let all_hunks = editor_hunks(editor, &snapshot, cx);
10786        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10787        assert_eq!(
10788            expanded_hunks_background_highlights(editor, cx),
10789            Vec::new(),
10790            "After cancelling in editor, no git highlights should be left"
10791        );
10792        assert_eq!(
10793            all_expanded_hunks,
10794            Vec::new(),
10795            "After cancelling in editor, no hunks should be expanded"
10796        );
10797        assert_eq!(
10798            all_hunks, unexpanded_hunks,
10799            "After cancelling in editor, regular hunks' coordinates should get back to normal"
10800        );
10801    });
10802}
10803
10804#[gpui::test]
10805async fn test_toggled_diff_base_change(
10806    executor: BackgroundExecutor,
10807    cx: &mut gpui::TestAppContext,
10808) {
10809    init_test(cx, |_| {});
10810
10811    let mut cx = EditorTestContext::new(cx).await;
10812
10813    let diff_base = r#"
10814        use some::mod1;
10815        use some::mod2;
10816
10817        const A: u32 = 42;
10818        const B: u32 = 42;
10819        const C: u32 = 42;
10820
10821        fn main(ˇ) {
10822            println!("hello");
10823
10824            println!("world");
10825        }
10826        "#
10827    .unindent();
10828
10829    cx.set_state(
10830        &r#"
10831        use some::mod2;
10832
10833        const A: u32 = 42;
10834        const C: u32 = 42;
10835
10836        fn main(ˇ) {
10837            //println!("hello");
10838
10839            println!("world");
10840            //
10841            //
10842        }
10843        "#
10844        .unindent(),
10845    );
10846
10847    cx.set_diff_base(Some(&diff_base));
10848    executor.run_until_parked();
10849    cx.update_editor(|editor, cx| {
10850        let snapshot = editor.snapshot(cx);
10851        let all_hunks = editor_hunks(editor, &snapshot, cx);
10852        assert_eq!(
10853            all_hunks,
10854            vec![
10855                (
10856                    "use some::mod1;\n".to_string(),
10857                    DiffHunkStatus::Removed,
10858                    DisplayRow(0)..DisplayRow(0)
10859                ),
10860                (
10861                    "const B: u32 = 42;\n".to_string(),
10862                    DiffHunkStatus::Removed,
10863                    DisplayRow(3)..DisplayRow(3)
10864                ),
10865                (
10866                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10867                    DiffHunkStatus::Modified,
10868                    DisplayRow(5)..DisplayRow(7)
10869                ),
10870                (
10871                    "".to_string(),
10872                    DiffHunkStatus::Added,
10873                    DisplayRow(9)..DisplayRow(11)
10874                ),
10875            ]
10876        );
10877    });
10878
10879    cx.update_editor(|editor, cx| {
10880        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10881    });
10882    executor.run_until_parked();
10883    cx.assert_editor_state(
10884        &r#"
10885        use some::mod2;
10886
10887        const A: u32 = 42;
10888        const C: u32 = 42;
10889
10890        fn main(ˇ) {
10891            //println!("hello");
10892
10893            println!("world");
10894            //
10895            //
10896        }
10897        "#
10898        .unindent(),
10899    );
10900    cx.update_editor(|editor, cx| {
10901        let snapshot = editor.snapshot(cx);
10902        let all_hunks = editor_hunks(editor, &snapshot, cx);
10903        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10904        assert_eq!(
10905            expanded_hunks_background_highlights(editor, cx),
10906            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
10907            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10908        );
10909        assert_eq!(
10910            all_hunks,
10911            vec![
10912                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
10913                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
10914                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
10915                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
10916            ],
10917            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10918            (from modified and removed hunks)"
10919        );
10920        assert_eq!(
10921            all_hunks, all_expanded_hunks,
10922            "Editor hunks should not change and all be expanded"
10923        );
10924    });
10925
10926    cx.set_diff_base(Some("new diff base!"));
10927    executor.run_until_parked();
10928
10929    cx.update_editor(|editor, cx| {
10930        let snapshot = editor.snapshot(cx);
10931        let all_hunks = editor_hunks(editor, &snapshot, cx);
10932        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10933        assert_eq!(
10934            expanded_hunks_background_highlights(editor, cx),
10935            Vec::new(),
10936            "After diff base is changed, old git highlights should be removed"
10937        );
10938        assert_eq!(
10939            all_expanded_hunks,
10940            Vec::new(),
10941            "After diff base is changed, old git hunk expansions should be removed"
10942        );
10943        assert_eq!(
10944            all_hunks,
10945            vec![(
10946                "new diff base!".to_string(),
10947                DiffHunkStatus::Modified,
10948                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
10949            )],
10950            "After diff base is changed, hunks should update"
10951        );
10952    });
10953}
10954
10955#[gpui::test]
10956async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10957    init_test(cx, |_| {});
10958
10959    let mut cx = EditorTestContext::new(cx).await;
10960
10961    let diff_base = r#"
10962        use some::mod1;
10963        use some::mod2;
10964
10965        const A: u32 = 42;
10966        const B: u32 = 42;
10967        const C: u32 = 42;
10968
10969        fn main(ˇ) {
10970            println!("hello");
10971
10972            println!("world");
10973        }
10974
10975        fn another() {
10976            println!("another");
10977        }
10978
10979        fn another2() {
10980            println!("another2");
10981        }
10982        "#
10983    .unindent();
10984
10985    cx.set_state(
10986        &r#"
10987        «use some::mod2;
10988
10989        const A: u32 = 42;
10990        const C: u32 = 42;
10991
10992        fn main() {
10993            //println!("hello");
10994
10995            println!("world");
10996            //
10997            //ˇ»
10998        }
10999
11000        fn another() {
11001            println!("another");
11002            println!("another");
11003        }
11004
11005            println!("another2");
11006        }
11007        "#
11008        .unindent(),
11009    );
11010
11011    cx.set_diff_base(Some(&diff_base));
11012    executor.run_until_parked();
11013    cx.update_editor(|editor, cx| {
11014        let snapshot = editor.snapshot(cx);
11015        let all_hunks = editor_hunks(editor, &snapshot, cx);
11016        assert_eq!(
11017            all_hunks,
11018            vec![
11019                (
11020                    "use some::mod1;\n".to_string(),
11021                    DiffHunkStatus::Removed,
11022                    DisplayRow(0)..DisplayRow(0)
11023                ),
11024                (
11025                    "const B: u32 = 42;\n".to_string(),
11026                    DiffHunkStatus::Removed,
11027                    DisplayRow(3)..DisplayRow(3)
11028                ),
11029                (
11030                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11031                    DiffHunkStatus::Modified,
11032                    DisplayRow(5)..DisplayRow(7)
11033                ),
11034                (
11035                    "".to_string(),
11036                    DiffHunkStatus::Added,
11037                    DisplayRow(9)..DisplayRow(11)
11038                ),
11039                (
11040                    "".to_string(),
11041                    DiffHunkStatus::Added,
11042                    DisplayRow(15)..DisplayRow(16)
11043                ),
11044                (
11045                    "fn another2() {\n".to_string(),
11046                    DiffHunkStatus::Removed,
11047                    DisplayRow(18)..DisplayRow(18)
11048                ),
11049            ]
11050        );
11051    });
11052
11053    cx.update_editor(|editor, cx| {
11054        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11055    });
11056    executor.run_until_parked();
11057    cx.assert_editor_state(
11058        &r#"
11059        «use some::mod2;
11060
11061        const A: u32 = 42;
11062        const C: u32 = 42;
11063
11064        fn main() {
11065            //println!("hello");
11066
11067            println!("world");
11068            //
11069            //ˇ»
11070        }
11071
11072        fn another() {
11073            println!("another");
11074            println!("another");
11075        }
11076
11077            println!("another2");
11078        }
11079        "#
11080        .unindent(),
11081    );
11082    cx.update_editor(|editor, cx| {
11083        let snapshot = editor.snapshot(cx);
11084        let all_hunks = editor_hunks(editor, &snapshot, cx);
11085        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11086        assert_eq!(
11087            expanded_hunks_background_highlights(editor, cx),
11088            vec![
11089                DisplayRow(9)..=DisplayRow(10),
11090                DisplayRow(13)..=DisplayRow(14),
11091                DisplayRow(19)..=DisplayRow(19)
11092            ]
11093        );
11094        assert_eq!(
11095            all_hunks,
11096            vec![
11097                (
11098                    "use some::mod1;\n".to_string(),
11099                    DiffHunkStatus::Removed,
11100                    DisplayRow(1)..DisplayRow(1)
11101                ),
11102                (
11103                    "const B: u32 = 42;\n".to_string(),
11104                    DiffHunkStatus::Removed,
11105                    DisplayRow(5)..DisplayRow(5)
11106                ),
11107                (
11108                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11109                    DiffHunkStatus::Modified,
11110                    DisplayRow(9)..DisplayRow(11)
11111                ),
11112                (
11113                    "".to_string(),
11114                    DiffHunkStatus::Added,
11115                    DisplayRow(13)..DisplayRow(15)
11116                ),
11117                (
11118                    "".to_string(),
11119                    DiffHunkStatus::Added,
11120                    DisplayRow(19)..DisplayRow(20)
11121                ),
11122                (
11123                    "fn another2() {\n".to_string(),
11124                    DiffHunkStatus::Removed,
11125                    DisplayRow(23)..DisplayRow(23)
11126                ),
11127            ],
11128        );
11129        assert_eq!(all_hunks, all_expanded_hunks);
11130    });
11131
11132    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11133    cx.executor().run_until_parked();
11134    cx.assert_editor_state(
11135        &r#"
11136        «use some::mod2;
11137
11138        const A: u32 = 42;
11139        const C: u32 = 42;
11140
11141        fn main() {
11142            //println!("hello");
11143
11144            println!("world");
11145            //
11146            //ˇ»
11147        }
11148
11149        fn another() {
11150            println!("another");
11151            println!("another");
11152        }
11153
11154            println!("another2");
11155        }
11156        "#
11157        .unindent(),
11158    );
11159    cx.update_editor(|editor, cx| {
11160        let snapshot = editor.snapshot(cx);
11161        let all_hunks = editor_hunks(editor, &snapshot, cx);
11162        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11163        assert_eq!(
11164            expanded_hunks_background_highlights(editor, cx),
11165            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
11166            "Only one hunk is left not folded, its highlight should be visible"
11167        );
11168        assert_eq!(
11169            all_hunks,
11170            vec![
11171                (
11172                    "use some::mod1;\n".to_string(),
11173                    DiffHunkStatus::Removed,
11174                    DisplayRow(0)..DisplayRow(0)
11175                ),
11176                (
11177                    "const B: u32 = 42;\n".to_string(),
11178                    DiffHunkStatus::Removed,
11179                    DisplayRow(0)..DisplayRow(0)
11180                ),
11181                (
11182                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11183                    DiffHunkStatus::Modified,
11184                    DisplayRow(0)..DisplayRow(0)
11185                ),
11186                (
11187                    "".to_string(),
11188                    DiffHunkStatus::Added,
11189                    DisplayRow(0)..DisplayRow(1)
11190                ),
11191                (
11192                    "".to_string(),
11193                    DiffHunkStatus::Added,
11194                    DisplayRow(5)..DisplayRow(6)
11195                ),
11196                (
11197                    "fn another2() {\n".to_string(),
11198                    DiffHunkStatus::Removed,
11199                    DisplayRow(9)..DisplayRow(9)
11200                ),
11201            ],
11202            "Hunk list should still return shifted folded hunks"
11203        );
11204        assert_eq!(
11205            all_expanded_hunks,
11206            vec![
11207                (
11208                    "".to_string(),
11209                    DiffHunkStatus::Added,
11210                    DisplayRow(5)..DisplayRow(6)
11211                ),
11212                (
11213                    "fn another2() {\n".to_string(),
11214                    DiffHunkStatus::Removed,
11215                    DisplayRow(9)..DisplayRow(9)
11216                ),
11217            ],
11218            "Only non-folded hunks should be left expanded"
11219        );
11220    });
11221
11222    cx.update_editor(|editor, cx| {
11223        editor.select_all(&SelectAll, cx);
11224        editor.unfold_lines(&UnfoldLines, cx);
11225    });
11226    cx.executor().run_until_parked();
11227    cx.assert_editor_state(
11228        &r#"
11229        «use some::mod2;
11230
11231        const A: u32 = 42;
11232        const C: u32 = 42;
11233
11234        fn main() {
11235            //println!("hello");
11236
11237            println!("world");
11238            //
11239            //
11240        }
11241
11242        fn another() {
11243            println!("another");
11244            println!("another");
11245        }
11246
11247            println!("another2");
11248        }
11249        ˇ»"#
11250        .unindent(),
11251    );
11252    cx.update_editor(|editor, cx| {
11253        let snapshot = editor.snapshot(cx);
11254        let all_hunks = editor_hunks(editor, &snapshot, cx);
11255        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11256        assert_eq!(
11257            expanded_hunks_background_highlights(editor, cx),
11258            vec![
11259                DisplayRow(9)..=DisplayRow(10),
11260                DisplayRow(13)..=DisplayRow(14),
11261                DisplayRow(19)..=DisplayRow(19)
11262            ],
11263            "After unfolding, all hunk diffs should be visible again"
11264        );
11265        assert_eq!(
11266            all_hunks,
11267            vec![
11268                (
11269                    "use some::mod1;\n".to_string(),
11270                    DiffHunkStatus::Removed,
11271                    DisplayRow(1)..DisplayRow(1)
11272                ),
11273                (
11274                    "const B: u32 = 42;\n".to_string(),
11275                    DiffHunkStatus::Removed,
11276                    DisplayRow(5)..DisplayRow(5)
11277                ),
11278                (
11279                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11280                    DiffHunkStatus::Modified,
11281                    DisplayRow(9)..DisplayRow(11)
11282                ),
11283                (
11284                    "".to_string(),
11285                    DiffHunkStatus::Added,
11286                    DisplayRow(13)..DisplayRow(15)
11287                ),
11288                (
11289                    "".to_string(),
11290                    DiffHunkStatus::Added,
11291                    DisplayRow(19)..DisplayRow(20)
11292                ),
11293                (
11294                    "fn another2() {\n".to_string(),
11295                    DiffHunkStatus::Removed,
11296                    DisplayRow(23)..DisplayRow(23)
11297                ),
11298            ],
11299        );
11300        assert_eq!(all_hunks, all_expanded_hunks);
11301    });
11302}
11303
11304#[gpui::test]
11305async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11306    init_test(cx, |_| {});
11307
11308    let cols = 4;
11309    let rows = 10;
11310    let sample_text_1 = sample_text(rows, cols, 'a');
11311    assert_eq!(
11312        sample_text_1,
11313        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11314    );
11315    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11316    let sample_text_2 = sample_text(rows, cols, 'l');
11317    assert_eq!(
11318        sample_text_2,
11319        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11320    );
11321    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11322    let sample_text_3 = sample_text(rows, cols, 'v');
11323    assert_eq!(
11324        sample_text_3,
11325        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11326    );
11327    let modified_sample_text_3 =
11328        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11329    let buffer_1 = cx.new_model(|cx| {
11330        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
11331        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
11332        buffer
11333    });
11334    let buffer_2 = cx.new_model(|cx| {
11335        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
11336        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
11337        buffer
11338    });
11339    let buffer_3 = cx.new_model(|cx| {
11340        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
11341        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
11342        buffer
11343    });
11344
11345    let multi_buffer = cx.new_model(|cx| {
11346        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
11347        multibuffer.push_excerpts(
11348            buffer_1.clone(),
11349            [
11350                ExcerptRange {
11351                    context: Point::new(0, 0)..Point::new(3, 0),
11352                    primary: None,
11353                },
11354                ExcerptRange {
11355                    context: Point::new(5, 0)..Point::new(7, 0),
11356                    primary: None,
11357                },
11358                ExcerptRange {
11359                    context: Point::new(9, 0)..Point::new(10, 4),
11360                    primary: None,
11361                },
11362            ],
11363            cx,
11364        );
11365        multibuffer.push_excerpts(
11366            buffer_2.clone(),
11367            [
11368                ExcerptRange {
11369                    context: Point::new(0, 0)..Point::new(3, 0),
11370                    primary: None,
11371                },
11372                ExcerptRange {
11373                    context: Point::new(5, 0)..Point::new(7, 0),
11374                    primary: None,
11375                },
11376                ExcerptRange {
11377                    context: Point::new(9, 0)..Point::new(10, 4),
11378                    primary: None,
11379                },
11380            ],
11381            cx,
11382        );
11383        multibuffer.push_excerpts(
11384            buffer_3.clone(),
11385            [
11386                ExcerptRange {
11387                    context: Point::new(0, 0)..Point::new(3, 0),
11388                    primary: None,
11389                },
11390                ExcerptRange {
11391                    context: Point::new(5, 0)..Point::new(7, 0),
11392                    primary: None,
11393                },
11394                ExcerptRange {
11395                    context: Point::new(9, 0)..Point::new(10, 4),
11396                    primary: None,
11397                },
11398            ],
11399            cx,
11400        );
11401        multibuffer
11402    });
11403
11404    let fs = FakeFs::new(cx.executor());
11405    fs.insert_tree(
11406        "/a",
11407        json!({
11408            "main.rs": modified_sample_text_1,
11409            "other.rs": modified_sample_text_2,
11410            "lib.rs": modified_sample_text_3,
11411        }),
11412    )
11413    .await;
11414
11415    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11416    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11417    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11418    let multi_buffer_editor = cx.new_view(|cx| {
11419        Editor::new(
11420            EditorMode::Full,
11421            multi_buffer,
11422            Some(project.clone()),
11423            true,
11424            cx,
11425        )
11426    });
11427    cx.executor().run_until_parked();
11428
11429    let expected_all_hunks = vec![
11430        (
11431            "bbbb\n".to_string(),
11432            DiffHunkStatus::Removed,
11433            DisplayRow(4)..DisplayRow(4),
11434        ),
11435        (
11436            "nnnn\n".to_string(),
11437            DiffHunkStatus::Modified,
11438            DisplayRow(21)..DisplayRow(22),
11439        ),
11440        (
11441            "".to_string(),
11442            DiffHunkStatus::Added,
11443            DisplayRow(41)..DisplayRow(42),
11444        ),
11445    ];
11446    let expected_all_hunks_shifted = vec![
11447        (
11448            "bbbb\n".to_string(),
11449            DiffHunkStatus::Removed,
11450            DisplayRow(5)..DisplayRow(5),
11451        ),
11452        (
11453            "nnnn\n".to_string(),
11454            DiffHunkStatus::Modified,
11455            DisplayRow(23)..DisplayRow(24),
11456        ),
11457        (
11458            "".to_string(),
11459            DiffHunkStatus::Added,
11460            DisplayRow(43)..DisplayRow(44),
11461        ),
11462    ];
11463
11464    multi_buffer_editor.update(cx, |editor, cx| {
11465        let snapshot = editor.snapshot(cx);
11466        let all_hunks = editor_hunks(editor, &snapshot, cx);
11467        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11468        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11469        assert_eq!(all_hunks, expected_all_hunks);
11470        assert_eq!(all_expanded_hunks, Vec::new());
11471    });
11472
11473    multi_buffer_editor.update(cx, |editor, cx| {
11474        editor.select_all(&SelectAll, cx);
11475        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11476    });
11477    cx.executor().run_until_parked();
11478    multi_buffer_editor.update(cx, |editor, cx| {
11479        let snapshot = editor.snapshot(cx);
11480        let all_hunks = editor_hunks(editor, &snapshot, cx);
11481        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11482        assert_eq!(
11483            expanded_hunks_background_highlights(editor, cx),
11484            vec![
11485                DisplayRow(23)..=DisplayRow(23),
11486                DisplayRow(43)..=DisplayRow(43)
11487            ],
11488        );
11489        assert_eq!(all_hunks, expected_all_hunks_shifted);
11490        assert_eq!(all_hunks, all_expanded_hunks);
11491    });
11492
11493    multi_buffer_editor.update(cx, |editor, cx| {
11494        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11495    });
11496    cx.executor().run_until_parked();
11497    multi_buffer_editor.update(cx, |editor, cx| {
11498        let snapshot = editor.snapshot(cx);
11499        let all_hunks = editor_hunks(editor, &snapshot, cx);
11500        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11501        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11502        assert_eq!(all_hunks, expected_all_hunks);
11503        assert_eq!(all_expanded_hunks, Vec::new());
11504    });
11505
11506    multi_buffer_editor.update(cx, |editor, cx| {
11507        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11508    });
11509    cx.executor().run_until_parked();
11510    multi_buffer_editor.update(cx, |editor, cx| {
11511        let snapshot = editor.snapshot(cx);
11512        let all_hunks = editor_hunks(editor, &snapshot, cx);
11513        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11514        assert_eq!(
11515            expanded_hunks_background_highlights(editor, cx),
11516            vec![
11517                DisplayRow(23)..=DisplayRow(23),
11518                DisplayRow(43)..=DisplayRow(43)
11519            ],
11520        );
11521        assert_eq!(all_hunks, expected_all_hunks_shifted);
11522        assert_eq!(all_hunks, all_expanded_hunks);
11523    });
11524
11525    multi_buffer_editor.update(cx, |editor, cx| {
11526        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11527    });
11528    cx.executor().run_until_parked();
11529    multi_buffer_editor.update(cx, |editor, cx| {
11530        let snapshot = editor.snapshot(cx);
11531        let all_hunks = editor_hunks(editor, &snapshot, cx);
11532        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11533        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11534        assert_eq!(all_hunks, expected_all_hunks);
11535        assert_eq!(all_expanded_hunks, Vec::new());
11536    });
11537}
11538
11539#[gpui::test]
11540async fn test_edits_around_toggled_additions(
11541    executor: BackgroundExecutor,
11542    cx: &mut gpui::TestAppContext,
11543) {
11544    init_test(cx, |_| {});
11545
11546    let mut cx = EditorTestContext::new(cx).await;
11547
11548    let diff_base = r#"
11549        use some::mod1;
11550        use some::mod2;
11551
11552        const A: u32 = 42;
11553
11554        fn main() {
11555            println!("hello");
11556
11557            println!("world");
11558        }
11559        "#
11560    .unindent();
11561    executor.run_until_parked();
11562    cx.set_state(
11563        &r#"
11564        use some::mod1;
11565        use some::mod2;
11566
11567        const A: u32 = 42;
11568        const B: u32 = 42;
11569        const C: u32 = 42;
11570        ˇ
11571
11572        fn main() {
11573            println!("hello");
11574
11575            println!("world");
11576        }
11577        "#
11578        .unindent(),
11579    );
11580
11581    cx.set_diff_base(Some(&diff_base));
11582    executor.run_until_parked();
11583    cx.update_editor(|editor, cx| {
11584        let snapshot = editor.snapshot(cx);
11585        let all_hunks = editor_hunks(editor, &snapshot, cx);
11586        assert_eq!(
11587            all_hunks,
11588            vec![(
11589                "".to_string(),
11590                DiffHunkStatus::Added,
11591                DisplayRow(4)..DisplayRow(7)
11592            )]
11593        );
11594    });
11595    cx.update_editor(|editor, cx| {
11596        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11597    });
11598    executor.run_until_parked();
11599    cx.assert_editor_state(
11600        &r#"
11601        use some::mod1;
11602        use some::mod2;
11603
11604        const A: u32 = 42;
11605        const B: u32 = 42;
11606        const C: u32 = 42;
11607        ˇ
11608
11609        fn main() {
11610            println!("hello");
11611
11612            println!("world");
11613        }
11614        "#
11615        .unindent(),
11616    );
11617    cx.update_editor(|editor, cx| {
11618        let snapshot = editor.snapshot(cx);
11619        let all_hunks = editor_hunks(editor, &snapshot, cx);
11620        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11621        assert_eq!(
11622            all_hunks,
11623            vec![(
11624                "".to_string(),
11625                DiffHunkStatus::Added,
11626                DisplayRow(4)..DisplayRow(7)
11627            )]
11628        );
11629        assert_eq!(
11630            expanded_hunks_background_highlights(editor, cx),
11631            vec![DisplayRow(4)..=DisplayRow(6)]
11632        );
11633        assert_eq!(all_hunks, all_expanded_hunks);
11634    });
11635
11636    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
11637    executor.run_until_parked();
11638    cx.assert_editor_state(
11639        &r#"
11640        use some::mod1;
11641        use some::mod2;
11642
11643        const A: u32 = 42;
11644        const B: u32 = 42;
11645        const C: u32 = 42;
11646        const D: u32 = 42;
11647        ˇ
11648
11649        fn main() {
11650            println!("hello");
11651
11652            println!("world");
11653        }
11654        "#
11655        .unindent(),
11656    );
11657    cx.update_editor(|editor, cx| {
11658        let snapshot = editor.snapshot(cx);
11659        let all_hunks = editor_hunks(editor, &snapshot, cx);
11660        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11661        assert_eq!(
11662            all_hunks,
11663            vec![(
11664                "".to_string(),
11665                DiffHunkStatus::Added,
11666                DisplayRow(4)..DisplayRow(8)
11667            )]
11668        );
11669        assert_eq!(
11670            expanded_hunks_background_highlights(editor, cx),
11671            vec![DisplayRow(4)..=DisplayRow(6)],
11672            "Edited hunk should have one more line added"
11673        );
11674        assert_eq!(
11675            all_hunks, all_expanded_hunks,
11676            "Expanded hunk should also grow with the addition"
11677        );
11678    });
11679
11680    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
11681    executor.run_until_parked();
11682    cx.assert_editor_state(
11683        &r#"
11684        use some::mod1;
11685        use some::mod2;
11686
11687        const A: u32 = 42;
11688        const B: u32 = 42;
11689        const C: u32 = 42;
11690        const D: u32 = 42;
11691        const E: u32 = 42;
11692        ˇ
11693
11694        fn main() {
11695            println!("hello");
11696
11697            println!("world");
11698        }
11699        "#
11700        .unindent(),
11701    );
11702    cx.update_editor(|editor, cx| {
11703        let snapshot = editor.snapshot(cx);
11704        let all_hunks = editor_hunks(editor, &snapshot, cx);
11705        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11706        assert_eq!(
11707            all_hunks,
11708            vec![(
11709                "".to_string(),
11710                DiffHunkStatus::Added,
11711                DisplayRow(4)..DisplayRow(9)
11712            )]
11713        );
11714        assert_eq!(
11715            expanded_hunks_background_highlights(editor, cx),
11716            vec![DisplayRow(4)..=DisplayRow(6)],
11717            "Edited hunk should have one more line added"
11718        );
11719        assert_eq!(all_hunks, all_expanded_hunks);
11720    });
11721
11722    cx.update_editor(|editor, cx| {
11723        editor.move_up(&MoveUp, cx);
11724        editor.delete_line(&DeleteLine, cx);
11725    });
11726    executor.run_until_parked();
11727    cx.assert_editor_state(
11728        &r#"
11729        use some::mod1;
11730        use some::mod2;
11731
11732        const A: u32 = 42;
11733        const B: u32 = 42;
11734        const C: u32 = 42;
11735        const D: u32 = 42;
11736        ˇ
11737
11738        fn main() {
11739            println!("hello");
11740
11741            println!("world");
11742        }
11743        "#
11744        .unindent(),
11745    );
11746    cx.update_editor(|editor, cx| {
11747        let snapshot = editor.snapshot(cx);
11748        let all_hunks = editor_hunks(editor, &snapshot, cx);
11749        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11750        assert_eq!(
11751            all_hunks,
11752            vec![(
11753                "".to_string(),
11754                DiffHunkStatus::Added,
11755                DisplayRow(4)..DisplayRow(8)
11756            )]
11757        );
11758        assert_eq!(
11759            expanded_hunks_background_highlights(editor, cx),
11760            vec![DisplayRow(4)..=DisplayRow(6)],
11761            "Deleting a line should shrint the hunk"
11762        );
11763        assert_eq!(
11764            all_hunks, all_expanded_hunks,
11765            "Expanded hunk should also shrink with the addition"
11766        );
11767    });
11768
11769    cx.update_editor(|editor, cx| {
11770        editor.move_up(&MoveUp, cx);
11771        editor.delete_line(&DeleteLine, cx);
11772        editor.move_up(&MoveUp, cx);
11773        editor.delete_line(&DeleteLine, cx);
11774        editor.move_up(&MoveUp, cx);
11775        editor.delete_line(&DeleteLine, cx);
11776    });
11777    executor.run_until_parked();
11778    cx.assert_editor_state(
11779        &r#"
11780        use some::mod1;
11781        use some::mod2;
11782
11783        const A: u32 = 42;
11784        ˇ
11785
11786        fn main() {
11787            println!("hello");
11788
11789            println!("world");
11790        }
11791        "#
11792        .unindent(),
11793    );
11794    cx.update_editor(|editor, cx| {
11795        let snapshot = editor.snapshot(cx);
11796        let all_hunks = editor_hunks(editor, &snapshot, cx);
11797        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11798        assert_eq!(
11799            all_hunks,
11800            vec![(
11801                "".to_string(),
11802                DiffHunkStatus::Added,
11803                DisplayRow(5)..DisplayRow(6)
11804            )]
11805        );
11806        assert_eq!(
11807            expanded_hunks_background_highlights(editor, cx),
11808            vec![DisplayRow(5)..=DisplayRow(5)]
11809        );
11810        assert_eq!(all_hunks, all_expanded_hunks);
11811    });
11812
11813    cx.update_editor(|editor, cx| {
11814        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
11815        editor.delete_line(&DeleteLine, cx);
11816    });
11817    executor.run_until_parked();
11818    cx.assert_editor_state(
11819        &r#"
11820        ˇ
11821
11822        fn main() {
11823            println!("hello");
11824
11825            println!("world");
11826        }
11827        "#
11828        .unindent(),
11829    );
11830    cx.update_editor(|editor, cx| {
11831        let snapshot = editor.snapshot(cx);
11832        let all_hunks = editor_hunks(editor, &snapshot, cx);
11833        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11834        assert_eq!(
11835            all_hunks,
11836            vec![
11837                (
11838                    "use some::mod1;\nuse some::mod2;\n".to_string(),
11839                    DiffHunkStatus::Removed,
11840                    DisplayRow(0)..DisplayRow(0)
11841                ),
11842                (
11843                    "const A: u32 = 42;\n".to_string(),
11844                    DiffHunkStatus::Removed,
11845                    DisplayRow(2)..DisplayRow(2)
11846                )
11847            ]
11848        );
11849        assert_eq!(
11850            expanded_hunks_background_highlights(editor, cx),
11851            Vec::new(),
11852            "Should close all stale expanded addition hunks"
11853        );
11854        assert_eq!(
11855            all_expanded_hunks,
11856            vec![(
11857                "const A: u32 = 42;\n".to_string(),
11858                DiffHunkStatus::Removed,
11859                DisplayRow(2)..DisplayRow(2)
11860            )],
11861            "Should open hunks that were adjacent to the stale addition one"
11862        );
11863    });
11864}
11865
11866#[gpui::test]
11867async fn test_edits_around_toggled_deletions(
11868    executor: BackgroundExecutor,
11869    cx: &mut gpui::TestAppContext,
11870) {
11871    init_test(cx, |_| {});
11872
11873    let mut cx = EditorTestContext::new(cx).await;
11874
11875    let diff_base = r#"
11876        use some::mod1;
11877        use some::mod2;
11878
11879        const A: u32 = 42;
11880        const B: u32 = 42;
11881        const C: u32 = 42;
11882
11883
11884        fn main() {
11885            println!("hello");
11886
11887            println!("world");
11888        }
11889        "#
11890    .unindent();
11891    executor.run_until_parked();
11892    cx.set_state(
11893        &r#"
11894        use some::mod1;
11895        use some::mod2;
11896
11897        ˇconst B: u32 = 42;
11898        const C: u32 = 42;
11899
11900
11901        fn main() {
11902            println!("hello");
11903
11904            println!("world");
11905        }
11906        "#
11907        .unindent(),
11908    );
11909
11910    cx.set_diff_base(Some(&diff_base));
11911    executor.run_until_parked();
11912    cx.update_editor(|editor, cx| {
11913        let snapshot = editor.snapshot(cx);
11914        let all_hunks = editor_hunks(editor, &snapshot, cx);
11915        assert_eq!(
11916            all_hunks,
11917            vec![(
11918                "const A: u32 = 42;\n".to_string(),
11919                DiffHunkStatus::Removed,
11920                DisplayRow(3)..DisplayRow(3)
11921            )]
11922        );
11923    });
11924    cx.update_editor(|editor, cx| {
11925        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11926    });
11927    executor.run_until_parked();
11928    cx.assert_editor_state(
11929        &r#"
11930        use some::mod1;
11931        use some::mod2;
11932
11933        ˇconst B: u32 = 42;
11934        const C: u32 = 42;
11935
11936
11937        fn main() {
11938            println!("hello");
11939
11940            println!("world");
11941        }
11942        "#
11943        .unindent(),
11944    );
11945    cx.update_editor(|editor, cx| {
11946        let snapshot = editor.snapshot(cx);
11947        let all_hunks = editor_hunks(editor, &snapshot, cx);
11948        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11949        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11950        assert_eq!(
11951            all_hunks,
11952            vec![(
11953                "const A: u32 = 42;\n".to_string(),
11954                DiffHunkStatus::Removed,
11955                DisplayRow(4)..DisplayRow(4)
11956            )]
11957        );
11958        assert_eq!(all_hunks, all_expanded_hunks);
11959    });
11960
11961    cx.update_editor(|editor, cx| {
11962        editor.delete_line(&DeleteLine, cx);
11963    });
11964    executor.run_until_parked();
11965    cx.assert_editor_state(
11966        &r#"
11967        use some::mod1;
11968        use some::mod2;
11969
11970        ˇconst C: u32 = 42;
11971
11972
11973        fn main() {
11974            println!("hello");
11975
11976            println!("world");
11977        }
11978        "#
11979        .unindent(),
11980    );
11981    cx.update_editor(|editor, cx| {
11982        let snapshot = editor.snapshot(cx);
11983        let all_hunks = editor_hunks(editor, &snapshot, cx);
11984        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11985        assert_eq!(
11986            expanded_hunks_background_highlights(editor, cx),
11987            Vec::new(),
11988            "Deleted hunks do not highlight current editor's background"
11989        );
11990        assert_eq!(
11991            all_hunks,
11992            vec![(
11993                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
11994                DiffHunkStatus::Removed,
11995                DisplayRow(5)..DisplayRow(5)
11996            )]
11997        );
11998        assert_eq!(all_hunks, all_expanded_hunks);
11999    });
12000
12001    cx.update_editor(|editor, cx| {
12002        editor.delete_line(&DeleteLine, cx);
12003    });
12004    executor.run_until_parked();
12005    cx.assert_editor_state(
12006        &r#"
12007        use some::mod1;
12008        use some::mod2;
12009
12010        ˇ
12011
12012        fn main() {
12013            println!("hello");
12014
12015            println!("world");
12016        }
12017        "#
12018        .unindent(),
12019    );
12020    cx.update_editor(|editor, cx| {
12021        let snapshot = editor.snapshot(cx);
12022        let all_hunks = editor_hunks(editor, &snapshot, cx);
12023        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12024        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12025        assert_eq!(
12026            all_hunks,
12027            vec![(
12028                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12029                DiffHunkStatus::Removed,
12030                DisplayRow(6)..DisplayRow(6)
12031            )]
12032        );
12033        assert_eq!(all_hunks, all_expanded_hunks);
12034    });
12035
12036    cx.update_editor(|editor, cx| {
12037        editor.handle_input("replacement", cx);
12038    });
12039    executor.run_until_parked();
12040    cx.assert_editor_state(
12041        &r#"
12042        use some::mod1;
12043        use some::mod2;
12044
12045        replacementˇ
12046
12047        fn main() {
12048            println!("hello");
12049
12050            println!("world");
12051        }
12052        "#
12053        .unindent(),
12054    );
12055    cx.update_editor(|editor, cx| {
12056        let snapshot = editor.snapshot(cx);
12057        let all_hunks = editor_hunks(editor, &snapshot, cx);
12058        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12059        assert_eq!(
12060            all_hunks,
12061            vec![(
12062                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
12063                DiffHunkStatus::Modified,
12064                DisplayRow(7)..DisplayRow(8)
12065            )]
12066        );
12067        assert_eq!(
12068            expanded_hunks_background_highlights(editor, cx),
12069            vec![DisplayRow(7)..=DisplayRow(7)],
12070            "Modified expanded hunks should display additions and highlight their background"
12071        );
12072        assert_eq!(all_hunks, all_expanded_hunks);
12073    });
12074}
12075
12076#[gpui::test]
12077async fn test_edits_around_toggled_modifications(
12078    executor: BackgroundExecutor,
12079    cx: &mut gpui::TestAppContext,
12080) {
12081    init_test(cx, |_| {});
12082
12083    let mut cx = EditorTestContext::new(cx).await;
12084
12085    let diff_base = r#"
12086        use some::mod1;
12087        use some::mod2;
12088
12089        const A: u32 = 42;
12090        const B: u32 = 42;
12091        const C: u32 = 42;
12092        const D: u32 = 42;
12093
12094
12095        fn main() {
12096            println!("hello");
12097
12098            println!("world");
12099        }"#
12100    .unindent();
12101    executor.run_until_parked();
12102    cx.set_state(
12103        &r#"
12104        use some::mod1;
12105        use some::mod2;
12106
12107        const A: u32 = 42;
12108        const B: u32 = 42;
12109        const C: u32 = 43ˇ
12110        const D: u32 = 42;
12111
12112
12113        fn main() {
12114            println!("hello");
12115
12116            println!("world");
12117        }"#
12118        .unindent(),
12119    );
12120
12121    cx.set_diff_base(Some(&diff_base));
12122    executor.run_until_parked();
12123    cx.update_editor(|editor, cx| {
12124        let snapshot = editor.snapshot(cx);
12125        let all_hunks = editor_hunks(editor, &snapshot, cx);
12126        assert_eq!(
12127            all_hunks,
12128            vec![(
12129                "const C: u32 = 42;\n".to_string(),
12130                DiffHunkStatus::Modified,
12131                DisplayRow(5)..DisplayRow(6)
12132            )]
12133        );
12134    });
12135    cx.update_editor(|editor, cx| {
12136        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12137    });
12138    executor.run_until_parked();
12139    cx.assert_editor_state(
12140        &r#"
12141        use some::mod1;
12142        use some::mod2;
12143
12144        const A: u32 = 42;
12145        const B: u32 = 42;
12146        const C: u32 = 43ˇ
12147        const D: u32 = 42;
12148
12149
12150        fn main() {
12151            println!("hello");
12152
12153            println!("world");
12154        }"#
12155        .unindent(),
12156    );
12157    cx.update_editor(|editor, cx| {
12158        let snapshot = editor.snapshot(cx);
12159        let all_hunks = editor_hunks(editor, &snapshot, cx);
12160        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12161        assert_eq!(
12162            expanded_hunks_background_highlights(editor, cx),
12163            vec![DisplayRow(6)..=DisplayRow(6)],
12164        );
12165        assert_eq!(
12166            all_hunks,
12167            vec![(
12168                "const C: u32 = 42;\n".to_string(),
12169                DiffHunkStatus::Modified,
12170                DisplayRow(6)..DisplayRow(7)
12171            )]
12172        );
12173        assert_eq!(all_hunks, all_expanded_hunks);
12174    });
12175
12176    cx.update_editor(|editor, cx| {
12177        editor.handle_input("\nnew_line\n", cx);
12178    });
12179    executor.run_until_parked();
12180    cx.assert_editor_state(
12181        &r#"
12182            use some::mod1;
12183            use some::mod2;
12184
12185            const A: u32 = 42;
12186            const B: u32 = 42;
12187            const C: u32 = 43
12188            new_line
12189            ˇ
12190            const D: u32 = 42;
12191
12192
12193            fn main() {
12194                println!("hello");
12195
12196                println!("world");
12197            }"#
12198        .unindent(),
12199    );
12200    cx.update_editor(|editor, cx| {
12201        let snapshot = editor.snapshot(cx);
12202        let all_hunks = editor_hunks(editor, &snapshot, cx);
12203        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12204        assert_eq!(
12205            expanded_hunks_background_highlights(editor, cx),
12206            vec![DisplayRow(6)..=DisplayRow(6)],
12207            "Modified hunk should grow highlighted lines on more text additions"
12208        );
12209        assert_eq!(
12210            all_hunks,
12211            vec![(
12212                "const C: u32 = 42;\n".to_string(),
12213                DiffHunkStatus::Modified,
12214                DisplayRow(6)..DisplayRow(9)
12215            )]
12216        );
12217        assert_eq!(all_hunks, all_expanded_hunks);
12218    });
12219
12220    cx.update_editor(|editor, cx| {
12221        editor.move_up(&MoveUp, cx);
12222        editor.move_up(&MoveUp, cx);
12223        editor.move_up(&MoveUp, cx);
12224        editor.delete_line(&DeleteLine, cx);
12225    });
12226    executor.run_until_parked();
12227    cx.assert_editor_state(
12228        &r#"
12229            use some::mod1;
12230            use some::mod2;
12231
12232            const A: u32 = 42;
12233            ˇconst C: u32 = 43
12234            new_line
12235
12236            const D: u32 = 42;
12237
12238
12239            fn main() {
12240                println!("hello");
12241
12242                println!("world");
12243            }"#
12244        .unindent(),
12245    );
12246    cx.update_editor(|editor, cx| {
12247        let snapshot = editor.snapshot(cx);
12248        let all_hunks = editor_hunks(editor, &snapshot, cx);
12249        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12250        assert_eq!(
12251            expanded_hunks_background_highlights(editor, cx),
12252            vec![DisplayRow(6)..=DisplayRow(8)],
12253        );
12254        assert_eq!(
12255            all_hunks,
12256            vec![(
12257                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12258                DiffHunkStatus::Modified,
12259                DisplayRow(6)..DisplayRow(9)
12260            )],
12261            "Modified hunk should grow deleted lines on text deletions above"
12262        );
12263        assert_eq!(all_hunks, all_expanded_hunks);
12264    });
12265
12266    cx.update_editor(|editor, cx| {
12267        editor.move_up(&MoveUp, cx);
12268        editor.handle_input("v", cx);
12269    });
12270    executor.run_until_parked();
12271    cx.assert_editor_state(
12272        &r#"
12273            use some::mod1;
12274            use some::mod2;
12275
12276            vˇconst A: u32 = 42;
12277            const C: u32 = 43
12278            new_line
12279
12280            const D: u32 = 42;
12281
12282
12283            fn main() {
12284                println!("hello");
12285
12286                println!("world");
12287            }"#
12288        .unindent(),
12289    );
12290    cx.update_editor(|editor, cx| {
12291        let snapshot = editor.snapshot(cx);
12292        let all_hunks = editor_hunks(editor, &snapshot, cx);
12293        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12294        assert_eq!(
12295            expanded_hunks_background_highlights(editor, cx),
12296            vec![DisplayRow(6)..=DisplayRow(9)],
12297            "Modified hunk should grow deleted lines on text modifications above"
12298        );
12299        assert_eq!(
12300            all_hunks,
12301            vec![(
12302                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12303                DiffHunkStatus::Modified,
12304                DisplayRow(6)..DisplayRow(10)
12305            )]
12306        );
12307        assert_eq!(all_hunks, all_expanded_hunks);
12308    });
12309
12310    cx.update_editor(|editor, cx| {
12311        editor.move_down(&MoveDown, cx);
12312        editor.move_down(&MoveDown, cx);
12313        editor.delete_line(&DeleteLine, cx)
12314    });
12315    executor.run_until_parked();
12316    cx.assert_editor_state(
12317        &r#"
12318            use some::mod1;
12319            use some::mod2;
12320
12321            vconst A: u32 = 42;
12322            const C: u32 = 43
12323            ˇ
12324            const D: u32 = 42;
12325
12326
12327            fn main() {
12328                println!("hello");
12329
12330                println!("world");
12331            }"#
12332        .unindent(),
12333    );
12334    cx.update_editor(|editor, cx| {
12335        let snapshot = editor.snapshot(cx);
12336        let all_hunks = editor_hunks(editor, &snapshot, cx);
12337        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12338        assert_eq!(
12339            expanded_hunks_background_highlights(editor, cx),
12340            vec![DisplayRow(6)..=DisplayRow(8)],
12341            "Modified hunk should grow shrink lines on modification lines removal"
12342        );
12343        assert_eq!(
12344            all_hunks,
12345            vec![(
12346                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12347                DiffHunkStatus::Modified,
12348                DisplayRow(6)..DisplayRow(9)
12349            )]
12350        );
12351        assert_eq!(all_hunks, all_expanded_hunks);
12352    });
12353
12354    cx.update_editor(|editor, cx| {
12355        editor.move_up(&MoveUp, cx);
12356        editor.move_up(&MoveUp, cx);
12357        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
12358        editor.delete_line(&DeleteLine, cx)
12359    });
12360    executor.run_until_parked();
12361    cx.assert_editor_state(
12362        &r#"
12363            use some::mod1;
12364            use some::mod2;
12365
12366            ˇ
12367
12368            fn main() {
12369                println!("hello");
12370
12371                println!("world");
12372            }"#
12373        .unindent(),
12374    );
12375    cx.update_editor(|editor, cx| {
12376        let snapshot = editor.snapshot(cx);
12377        let all_hunks = editor_hunks(editor, &snapshot, cx);
12378        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12379        assert_eq!(
12380            expanded_hunks_background_highlights(editor, cx),
12381            Vec::new(),
12382            "Modified hunk should turn into a removed one on all modified lines removal"
12383        );
12384        assert_eq!(
12385            all_hunks,
12386            vec![(
12387                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
12388                    .to_string(),
12389                DiffHunkStatus::Removed,
12390                DisplayRow(7)..DisplayRow(7)
12391            )]
12392        );
12393        assert_eq!(all_hunks, all_expanded_hunks);
12394    });
12395}
12396
12397#[gpui::test]
12398async fn test_multiple_expanded_hunks_merge(
12399    executor: BackgroundExecutor,
12400    cx: &mut gpui::TestAppContext,
12401) {
12402    init_test(cx, |_| {});
12403
12404    let mut cx = EditorTestContext::new(cx).await;
12405
12406    let diff_base = r#"
12407        use some::mod1;
12408        use some::mod2;
12409
12410        const A: u32 = 42;
12411        const B: u32 = 42;
12412        const C: u32 = 42;
12413        const D: u32 = 42;
12414
12415
12416        fn main() {
12417            println!("hello");
12418
12419            println!("world");
12420        }"#
12421    .unindent();
12422    executor.run_until_parked();
12423    cx.set_state(
12424        &r#"
12425        use some::mod1;
12426        use some::mod2;
12427
12428        const A: u32 = 42;
12429        const B: u32 = 42;
12430        const C: u32 = 43ˇ
12431        const D: u32 = 42;
12432
12433
12434        fn main() {
12435            println!("hello");
12436
12437            println!("world");
12438        }"#
12439        .unindent(),
12440    );
12441
12442    cx.set_diff_base(Some(&diff_base));
12443    executor.run_until_parked();
12444    cx.update_editor(|editor, cx| {
12445        let snapshot = editor.snapshot(cx);
12446        let all_hunks = editor_hunks(editor, &snapshot, cx);
12447        assert_eq!(
12448            all_hunks,
12449            vec![(
12450                "const C: u32 = 42;\n".to_string(),
12451                DiffHunkStatus::Modified,
12452                DisplayRow(5)..DisplayRow(6)
12453            )]
12454        );
12455    });
12456    cx.update_editor(|editor, cx| {
12457        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12458    });
12459    executor.run_until_parked();
12460    cx.assert_editor_state(
12461        &r#"
12462        use some::mod1;
12463        use some::mod2;
12464
12465        const A: u32 = 42;
12466        const B: u32 = 42;
12467        const C: u32 = 43ˇ
12468        const D: u32 = 42;
12469
12470
12471        fn main() {
12472            println!("hello");
12473
12474            println!("world");
12475        }"#
12476        .unindent(),
12477    );
12478    cx.update_editor(|editor, cx| {
12479        let snapshot = editor.snapshot(cx);
12480        let all_hunks = editor_hunks(editor, &snapshot, cx);
12481        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12482        assert_eq!(
12483            expanded_hunks_background_highlights(editor, cx),
12484            vec![DisplayRow(6)..=DisplayRow(6)],
12485        );
12486        assert_eq!(
12487            all_hunks,
12488            vec![(
12489                "const C: u32 = 42;\n".to_string(),
12490                DiffHunkStatus::Modified,
12491                DisplayRow(6)..DisplayRow(7)
12492            )]
12493        );
12494        assert_eq!(all_hunks, all_expanded_hunks);
12495    });
12496
12497    cx.update_editor(|editor, cx| {
12498        editor.handle_input("\nnew_line\n", cx);
12499    });
12500    executor.run_until_parked();
12501    cx.assert_editor_state(
12502        &r#"
12503            use some::mod1;
12504            use some::mod2;
12505
12506            const A: u32 = 42;
12507            const B: u32 = 42;
12508            const C: u32 = 43
12509            new_line
12510            ˇ
12511            const D: u32 = 42;
12512
12513
12514            fn main() {
12515                println!("hello");
12516
12517                println!("world");
12518            }"#
12519        .unindent(),
12520    );
12521}
12522
12523async fn setup_indent_guides_editor(
12524    text: &str,
12525    cx: &mut gpui::TestAppContext,
12526) -> (BufferId, EditorTestContext) {
12527    init_test(cx, |_| {});
12528
12529    let mut cx = EditorTestContext::new(cx).await;
12530
12531    let buffer_id = cx.update_editor(|editor, cx| {
12532        editor.set_text(text, cx);
12533        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12534        let buffer_id = buffer_ids[0];
12535        buffer_id
12536    });
12537
12538    (buffer_id, cx)
12539}
12540
12541fn assert_indent_guides(
12542    range: Range<u32>,
12543    expected: Vec<IndentGuide>,
12544    active_indices: Option<Vec<usize>>,
12545    cx: &mut EditorTestContext,
12546) {
12547    let indent_guides = cx.update_editor(|editor, cx| {
12548        let snapshot = editor.snapshot(cx).display_snapshot;
12549        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12550            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12551            true,
12552            &snapshot,
12553            cx,
12554        );
12555
12556        indent_guides.sort_by(|a, b| {
12557            a.depth.cmp(&b.depth).then(
12558                a.start_row
12559                    .cmp(&b.start_row)
12560                    .then(a.end_row.cmp(&b.end_row)),
12561            )
12562        });
12563        indent_guides
12564    });
12565
12566    if let Some(expected) = active_indices {
12567        let active_indices = cx.update_editor(|editor, cx| {
12568            let snapshot = editor.snapshot(cx).display_snapshot;
12569            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12570        });
12571
12572        assert_eq!(
12573            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12574            expected,
12575            "Active indent guide indices do not match"
12576        );
12577    }
12578
12579    let expected: Vec<_> = expected
12580        .into_iter()
12581        .map(|guide| MultiBufferIndentGuide {
12582            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12583            buffer: guide,
12584        })
12585        .collect();
12586
12587    assert_eq!(indent_guides, expected, "Indent guides do not match");
12588}
12589
12590fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12591    IndentGuide {
12592        buffer_id,
12593        start_row,
12594        end_row,
12595        depth,
12596        tab_size: 4,
12597        settings: IndentGuideSettings {
12598            enabled: true,
12599            line_width: 1,
12600            active_line_width: 1,
12601            ..Default::default()
12602        },
12603    }
12604}
12605
12606#[gpui::test]
12607async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12608    let (buffer_id, mut cx) = setup_indent_guides_editor(
12609        &"
12610    fn main() {
12611        let a = 1;
12612    }"
12613        .unindent(),
12614        cx,
12615    )
12616    .await;
12617
12618    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12619}
12620
12621#[gpui::test]
12622async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12623    let (buffer_id, mut cx) = setup_indent_guides_editor(
12624        &"
12625    fn main() {
12626        let a = 1;
12627        let b = 2;
12628    }"
12629        .unindent(),
12630        cx,
12631    )
12632    .await;
12633
12634    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12635}
12636
12637#[gpui::test]
12638async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12639    let (buffer_id, mut cx) = setup_indent_guides_editor(
12640        &"
12641    fn main() {
12642        let a = 1;
12643        if a == 3 {
12644            let b = 2;
12645        } else {
12646            let c = 3;
12647        }
12648    }"
12649        .unindent(),
12650        cx,
12651    )
12652    .await;
12653
12654    assert_indent_guides(
12655        0..8,
12656        vec![
12657            indent_guide(buffer_id, 1, 6, 0),
12658            indent_guide(buffer_id, 3, 3, 1),
12659            indent_guide(buffer_id, 5, 5, 1),
12660        ],
12661        None,
12662        &mut cx,
12663    );
12664}
12665
12666#[gpui::test]
12667async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12668    let (buffer_id, mut cx) = setup_indent_guides_editor(
12669        &"
12670    fn main() {
12671        let a = 1;
12672            let b = 2;
12673        let c = 3;
12674    }"
12675        .unindent(),
12676        cx,
12677    )
12678    .await;
12679
12680    assert_indent_guides(
12681        0..5,
12682        vec![
12683            indent_guide(buffer_id, 1, 3, 0),
12684            indent_guide(buffer_id, 2, 2, 1),
12685        ],
12686        None,
12687        &mut cx,
12688    );
12689}
12690
12691#[gpui::test]
12692async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12693    let (buffer_id, mut cx) = setup_indent_guides_editor(
12694        &"
12695        fn main() {
12696            let a = 1;
12697
12698            let c = 3;
12699        }"
12700        .unindent(),
12701        cx,
12702    )
12703    .await;
12704
12705    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12706}
12707
12708#[gpui::test]
12709async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12710    let (buffer_id, mut cx) = setup_indent_guides_editor(
12711        &"
12712        fn main() {
12713            let a = 1;
12714
12715            let c = 3;
12716
12717            if a == 3 {
12718                let b = 2;
12719            } else {
12720                let c = 3;
12721            }
12722        }"
12723        .unindent(),
12724        cx,
12725    )
12726    .await;
12727
12728    assert_indent_guides(
12729        0..11,
12730        vec![
12731            indent_guide(buffer_id, 1, 9, 0),
12732            indent_guide(buffer_id, 6, 6, 1),
12733            indent_guide(buffer_id, 8, 8, 1),
12734        ],
12735        None,
12736        &mut cx,
12737    );
12738}
12739
12740#[gpui::test]
12741async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12742    let (buffer_id, mut cx) = setup_indent_guides_editor(
12743        &"
12744        fn main() {
12745            let a = 1;
12746
12747            let c = 3;
12748
12749            if a == 3 {
12750                let b = 2;
12751            } else {
12752                let c = 3;
12753            }
12754        }"
12755        .unindent(),
12756        cx,
12757    )
12758    .await;
12759
12760    assert_indent_guides(
12761        1..11,
12762        vec![
12763            indent_guide(buffer_id, 1, 9, 0),
12764            indent_guide(buffer_id, 6, 6, 1),
12765            indent_guide(buffer_id, 8, 8, 1),
12766        ],
12767        None,
12768        &mut cx,
12769    );
12770}
12771
12772#[gpui::test]
12773async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12774    let (buffer_id, mut cx) = setup_indent_guides_editor(
12775        &"
12776        fn main() {
12777            let a = 1;
12778
12779            let c = 3;
12780
12781            if a == 3 {
12782                let b = 2;
12783            } else {
12784                let c = 3;
12785            }
12786        }"
12787        .unindent(),
12788        cx,
12789    )
12790    .await;
12791
12792    assert_indent_guides(
12793        1..10,
12794        vec![
12795            indent_guide(buffer_id, 1, 9, 0),
12796            indent_guide(buffer_id, 6, 6, 1),
12797            indent_guide(buffer_id, 8, 8, 1),
12798        ],
12799        None,
12800        &mut cx,
12801    );
12802}
12803
12804#[gpui::test]
12805async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12806    let (buffer_id, mut cx) = setup_indent_guides_editor(
12807        &"
12808        block1
12809            block2
12810                block3
12811                    block4
12812            block2
12813        block1
12814        block1"
12815            .unindent(),
12816        cx,
12817    )
12818    .await;
12819
12820    assert_indent_guides(
12821        1..10,
12822        vec![
12823            indent_guide(buffer_id, 1, 4, 0),
12824            indent_guide(buffer_id, 2, 3, 1),
12825            indent_guide(buffer_id, 3, 3, 2),
12826        ],
12827        None,
12828        &mut cx,
12829    );
12830}
12831
12832#[gpui::test]
12833async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12834    let (buffer_id, mut cx) = setup_indent_guides_editor(
12835        &"
12836        block1
12837            block2
12838                block3
12839
12840        block1
12841        block1"
12842            .unindent(),
12843        cx,
12844    )
12845    .await;
12846
12847    assert_indent_guides(
12848        0..6,
12849        vec![
12850            indent_guide(buffer_id, 1, 2, 0),
12851            indent_guide(buffer_id, 2, 2, 1),
12852        ],
12853        None,
12854        &mut cx,
12855    );
12856}
12857
12858#[gpui::test]
12859async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12860    let (buffer_id, mut cx) = setup_indent_guides_editor(
12861        &"
12862        block1
12863
12864
12865
12866            block2
12867        "
12868        .unindent(),
12869        cx,
12870    )
12871    .await;
12872
12873    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12874}
12875
12876#[gpui::test]
12877async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12878    let (buffer_id, mut cx) = setup_indent_guides_editor(
12879        &"
12880        def a:
12881        \tb = 3
12882        \tif True:
12883        \t\tc = 4
12884        \t\td = 5
12885        \tprint(b)
12886        "
12887        .unindent(),
12888        cx,
12889    )
12890    .await;
12891
12892    assert_indent_guides(
12893        0..6,
12894        vec![
12895            indent_guide(buffer_id, 1, 6, 0),
12896            indent_guide(buffer_id, 3, 4, 1),
12897        ],
12898        None,
12899        &mut cx,
12900    );
12901}
12902
12903#[gpui::test]
12904async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12905    let (buffer_id, mut cx) = setup_indent_guides_editor(
12906        &"
12907    fn main() {
12908        let a = 1;
12909    }"
12910        .unindent(),
12911        cx,
12912    )
12913    .await;
12914
12915    cx.update_editor(|editor, cx| {
12916        editor.change_selections(None, cx, |s| {
12917            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12918        });
12919    });
12920
12921    assert_indent_guides(
12922        0..3,
12923        vec![indent_guide(buffer_id, 1, 1, 0)],
12924        Some(vec![0]),
12925        &mut cx,
12926    );
12927}
12928
12929#[gpui::test]
12930async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12931    let (buffer_id, mut cx) = setup_indent_guides_editor(
12932        &"
12933    fn main() {
12934        if 1 == 2 {
12935            let a = 1;
12936        }
12937    }"
12938        .unindent(),
12939        cx,
12940    )
12941    .await;
12942
12943    cx.update_editor(|editor, cx| {
12944        editor.change_selections(None, cx, |s| {
12945            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12946        });
12947    });
12948
12949    assert_indent_guides(
12950        0..4,
12951        vec![
12952            indent_guide(buffer_id, 1, 3, 0),
12953            indent_guide(buffer_id, 2, 2, 1),
12954        ],
12955        Some(vec![1]),
12956        &mut cx,
12957    );
12958
12959    cx.update_editor(|editor, cx| {
12960        editor.change_selections(None, cx, |s| {
12961            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12962        });
12963    });
12964
12965    assert_indent_guides(
12966        0..4,
12967        vec![
12968            indent_guide(buffer_id, 1, 3, 0),
12969            indent_guide(buffer_id, 2, 2, 1),
12970        ],
12971        Some(vec![1]),
12972        &mut cx,
12973    );
12974
12975    cx.update_editor(|editor, cx| {
12976        editor.change_selections(None, cx, |s| {
12977            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
12978        });
12979    });
12980
12981    assert_indent_guides(
12982        0..4,
12983        vec![
12984            indent_guide(buffer_id, 1, 3, 0),
12985            indent_guide(buffer_id, 2, 2, 1),
12986        ],
12987        Some(vec![0]),
12988        &mut cx,
12989    );
12990}
12991
12992#[gpui::test]
12993async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
12994    let (buffer_id, mut cx) = setup_indent_guides_editor(
12995        &"
12996    fn main() {
12997        let a = 1;
12998
12999        let b = 2;
13000    }"
13001        .unindent(),
13002        cx,
13003    )
13004    .await;
13005
13006    cx.update_editor(|editor, cx| {
13007        editor.change_selections(None, cx, |s| {
13008            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13009        });
13010    });
13011
13012    assert_indent_guides(
13013        0..5,
13014        vec![indent_guide(buffer_id, 1, 3, 0)],
13015        Some(vec![0]),
13016        &mut cx,
13017    );
13018}
13019
13020#[gpui::test]
13021async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13022    let (buffer_id, mut cx) = setup_indent_guides_editor(
13023        &"
13024    def m:
13025        a = 1
13026        pass"
13027            .unindent(),
13028        cx,
13029    )
13030    .await;
13031
13032    cx.update_editor(|editor, cx| {
13033        editor.change_selections(None, cx, |s| {
13034            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13035        });
13036    });
13037
13038    assert_indent_guides(
13039        0..3,
13040        vec![indent_guide(buffer_id, 1, 2, 0)],
13041        Some(vec![0]),
13042        &mut cx,
13043    );
13044}
13045
13046#[gpui::test]
13047fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13048    init_test(cx, |_| {});
13049
13050    let editor = cx.add_window(|cx| {
13051        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13052        build_editor(buffer, cx)
13053    });
13054
13055    let render_args = Arc::new(Mutex::new(None));
13056    let snapshot = editor
13057        .update(cx, |editor, cx| {
13058            let snapshot = editor.buffer().read(cx).snapshot(cx);
13059            let range =
13060                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13061
13062            struct RenderArgs {
13063                row: MultiBufferRow,
13064                folded: bool,
13065                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13066            }
13067
13068            let crease = Crease::new(
13069                range,
13070                FoldPlaceholder::test(),
13071                {
13072                    let toggle_callback = render_args.clone();
13073                    move |row, folded, callback, _cx| {
13074                        *toggle_callback.lock() = Some(RenderArgs {
13075                            row,
13076                            folded,
13077                            callback,
13078                        });
13079                        div()
13080                    }
13081                },
13082                |_row, _folded, _cx| div(),
13083            );
13084
13085            editor.insert_creases(Some(crease), cx);
13086            let snapshot = editor.snapshot(cx);
13087            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13088            snapshot
13089        })
13090        .unwrap();
13091
13092    let render_args = render_args.lock().take().unwrap();
13093    assert_eq!(render_args.row, MultiBufferRow(1));
13094    assert_eq!(render_args.folded, false);
13095    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13096
13097    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13098        .unwrap();
13099    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13100    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13101
13102    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13103        .unwrap();
13104    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13105    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13106}
13107
13108fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13109    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13110    point..point
13111}
13112
13113fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13114    let (text, ranges) = marked_text_ranges(marked_text, true);
13115    assert_eq!(view.text(cx), text);
13116    assert_eq!(
13117        view.selections.ranges(cx),
13118        ranges,
13119        "Assert selections are {}",
13120        marked_text
13121    );
13122}
13123
13124pub fn handle_signature_help_request(
13125    cx: &mut EditorLspTestContext,
13126    mocked_response: lsp::SignatureHelp,
13127) -> impl Future<Output = ()> {
13128    let mut request =
13129        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13130            let mocked_response = mocked_response.clone();
13131            async move { Ok(Some(mocked_response)) }
13132        });
13133
13134    async move {
13135        request.next().await;
13136    }
13137}
13138
13139/// Handle completion request passing a marked string specifying where the completion
13140/// should be triggered from using '|' character, what range should be replaced, and what completions
13141/// should be returned using '<' and '>' to delimit the range
13142pub fn handle_completion_request(
13143    cx: &mut EditorLspTestContext,
13144    marked_string: &str,
13145    completions: Vec<&'static str>,
13146    counter: Arc<AtomicUsize>,
13147) -> impl Future<Output = ()> {
13148    let complete_from_marker: TextRangeMarker = '|'.into();
13149    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13150    let (_, mut marked_ranges) = marked_text_ranges_by(
13151        marked_string,
13152        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13153    );
13154
13155    let complete_from_position =
13156        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13157    let replace_range =
13158        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13159
13160    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13161        let completions = completions.clone();
13162        counter.fetch_add(1, atomic::Ordering::Release);
13163        async move {
13164            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13165            assert_eq!(
13166                params.text_document_position.position,
13167                complete_from_position
13168            );
13169            Ok(Some(lsp::CompletionResponse::Array(
13170                completions
13171                    .iter()
13172                    .map(|completion_text| lsp::CompletionItem {
13173                        label: completion_text.to_string(),
13174                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13175                            range: replace_range,
13176                            new_text: completion_text.to_string(),
13177                        })),
13178                        ..Default::default()
13179                    })
13180                    .collect(),
13181            )))
13182        }
13183    });
13184
13185    async move {
13186        request.next().await;
13187    }
13188}
13189
13190fn handle_resolve_completion_request(
13191    cx: &mut EditorLspTestContext,
13192    edits: Option<Vec<(&'static str, &'static str)>>,
13193) -> impl Future<Output = ()> {
13194    let edits = edits.map(|edits| {
13195        edits
13196            .iter()
13197            .map(|(marked_string, new_text)| {
13198                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13199                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13200                lsp::TextEdit::new(replace_range, new_text.to_string())
13201            })
13202            .collect::<Vec<_>>()
13203    });
13204
13205    let mut request =
13206        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13207            let edits = edits.clone();
13208            async move {
13209                Ok(lsp::CompletionItem {
13210                    additional_text_edits: edits,
13211                    ..Default::default()
13212                })
13213            }
13214        });
13215
13216    async move {
13217        request.next().await;
13218    }
13219}
13220
13221pub(crate) fn update_test_language_settings(
13222    cx: &mut TestAppContext,
13223    f: impl Fn(&mut AllLanguageSettingsContent),
13224) {
13225    _ = cx.update(|cx| {
13226        SettingsStore::update_global(cx, |store, cx| {
13227            store.update_user_settings::<AllLanguageSettings>(cx, f);
13228        });
13229    });
13230}
13231
13232pub(crate) fn update_test_project_settings(
13233    cx: &mut TestAppContext,
13234    f: impl Fn(&mut ProjectSettings),
13235) {
13236    _ = cx.update(|cx| {
13237        SettingsStore::update_global(cx, |store, cx| {
13238            store.update_user_settings::<ProjectSettings>(cx, f);
13239        });
13240    });
13241}
13242
13243pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13244    _ = cx.update(|cx| {
13245        assets::Assets.load_test_fonts(cx);
13246        let store = SettingsStore::test(cx);
13247        cx.set_global(store);
13248        theme::init(theme::LoadThemes::JustBase, cx);
13249        release_channel::init(SemanticVersion::default(), cx);
13250        client::init_settings(cx);
13251        language::init(cx);
13252        Project::init_settings(cx);
13253        workspace::init_settings(cx);
13254        crate::init(cx);
13255    });
13256
13257    update_test_language_settings(cx, f);
13258}
13259
13260pub(crate) fn rust_lang() -> Arc<Language> {
13261    Arc::new(Language::new(
13262        LanguageConfig {
13263            name: "Rust".into(),
13264            matcher: LanguageMatcher {
13265                path_suffixes: vec!["rs".to_string()],
13266                ..Default::default()
13267            },
13268            ..Default::default()
13269        },
13270        Some(tree_sitter_rust::language()),
13271    ))
13272}
13273
13274#[track_caller]
13275fn assert_hunk_revert(
13276    not_reverted_text_with_selections: &str,
13277    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13278    expected_reverted_text_with_selections: &str,
13279    base_text: &str,
13280    cx: &mut EditorLspTestContext,
13281) {
13282    cx.set_state(not_reverted_text_with_selections);
13283    cx.update_editor(|editor, cx| {
13284        editor
13285            .buffer()
13286            .read(cx)
13287            .as_singleton()
13288            .unwrap()
13289            .update(cx, |buffer, cx| {
13290                buffer.set_diff_base(Some(base_text.into()), cx);
13291            });
13292    });
13293    cx.executor().run_until_parked();
13294
13295    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13296        let snapshot = editor.buffer().read(cx).snapshot(cx);
13297        let reverted_hunk_statuses = snapshot
13298            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13299            .map(|hunk| hunk_status(&hunk))
13300            .collect::<Vec<_>>();
13301
13302        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13303        reverted_hunk_statuses
13304    });
13305    cx.executor().run_until_parked();
13306    cx.assert_editor_state(expected_reverted_text_with_selections);
13307    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13308}