editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_hunks,
    6        editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
    7        expanded_hunks, expanded_hunks_background_highlights, select_ranges,
    8    },
    9    JoinLines,
   10};
   11use futures::StreamExt;
   12use gpui::{
   13    div, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   14    WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher, Override,
   24    ParsedMarkdown, Point,
   25};
   26use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   27use multi_buffer::MultiBufferIndentGuide;
   28use parking_lot::Mutex;
   29use project::FakeFs;
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::sync::atomic;
   36use std::sync::atomic::AtomicUsize;
   37use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   38use unindent::Unindent;
   39use util::{
   40    assert_set_eq,
   41    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   42};
   43use workspace::{
   44    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   45    NavigationEntry, ViewId,
   46};
   47
   48#[gpui::test]
   49fn test_edit_events(cx: &mut TestAppContext) {
   50    init_test(cx, |_| {});
   51
   52    let buffer = cx.new_model(|cx| {
   53        let mut buffer = language::Buffer::local("123456", cx);
   54        buffer.set_group_interval(Duration::from_secs(1));
   55        buffer
   56    });
   57
   58    let events = Rc::new(RefCell::new(Vec::new()));
   59    let editor1 = cx.add_window({
   60        let events = events.clone();
   61        |cx| {
   62            let view = cx.view().clone();
   63            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   64                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   65                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   66                _ => {}
   67            })
   68            .detach();
   69            Editor::for_buffer(buffer.clone(), None, cx)
   70        }
   71    });
   72
   73    let editor2 = cx.add_window({
   74        let events = events.clone();
   75        |cx| {
   76            cx.subscribe(
   77                &cx.view().clone(),
   78                move |_, _, event: &EditorEvent, _| match event {
   79                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   80                    EditorEvent::BufferEdited => {
   81                        events.borrow_mut().push(("editor2", "buffer edited"))
   82                    }
   83                    _ => {}
   84                },
   85            )
   86            .detach();
   87            Editor::for_buffer(buffer.clone(), None, cx)
   88        }
   89    });
   90
   91    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   92
   93    // Mutating editor 1 will emit an `Edited` event only for that editor.
   94    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   95    assert_eq!(
   96        mem::take(&mut *events.borrow_mut()),
   97        [
   98            ("editor1", "edited"),
   99            ("editor1", "buffer edited"),
  100            ("editor2", "buffer edited"),
  101        ]
  102    );
  103
  104    // Mutating editor 2 will emit an `Edited` event only for that editor.
  105    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  106    assert_eq!(
  107        mem::take(&mut *events.borrow_mut()),
  108        [
  109            ("editor2", "edited"),
  110            ("editor1", "buffer edited"),
  111            ("editor2", "buffer edited"),
  112        ]
  113    );
  114
  115    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  116    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  117    assert_eq!(
  118        mem::take(&mut *events.borrow_mut()),
  119        [
  120            ("editor1", "edited"),
  121            ("editor1", "buffer edited"),
  122            ("editor2", "buffer edited"),
  123        ]
  124    );
  125
  126    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  127    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  128    assert_eq!(
  129        mem::take(&mut *events.borrow_mut()),
  130        [
  131            ("editor1", "edited"),
  132            ("editor1", "buffer edited"),
  133            ("editor2", "buffer edited"),
  134        ]
  135    );
  136
  137    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  138    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  139    assert_eq!(
  140        mem::take(&mut *events.borrow_mut()),
  141        [
  142            ("editor2", "edited"),
  143            ("editor1", "buffer edited"),
  144            ("editor2", "buffer edited"),
  145        ]
  146    );
  147
  148    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  149    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  150    assert_eq!(
  151        mem::take(&mut *events.borrow_mut()),
  152        [
  153            ("editor2", "edited"),
  154            ("editor1", "buffer edited"),
  155            ("editor2", "buffer edited"),
  156        ]
  157    );
  158
  159    // No event is emitted when the mutation is a no-op.
  160    _ = editor2.update(cx, |editor, cx| {
  161        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  162
  163        editor.backspace(&Backspace, cx);
  164    });
  165    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  166}
  167
  168#[gpui::test]
  169fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  170    init_test(cx, |_| {});
  171
  172    let mut now = Instant::now();
  173    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  174    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  175    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  176    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  177
  178    _ = editor.update(cx, |editor, cx| {
  179        editor.start_transaction_at(now, cx);
  180        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  181
  182        editor.insert("cd", cx);
  183        editor.end_transaction_at(now, cx);
  184        assert_eq!(editor.text(cx), "12cd56");
  185        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  186
  187        editor.start_transaction_at(now, cx);
  188        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  189        editor.insert("e", cx);
  190        editor.end_transaction_at(now, cx);
  191        assert_eq!(editor.text(cx), "12cde6");
  192        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  193
  194        now += group_interval + Duration::from_millis(1);
  195        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  196
  197        // Simulate an edit in another editor
  198        _ = buffer.update(cx, |buffer, cx| {
  199            buffer.start_transaction_at(now, cx);
  200            buffer.edit([(0..1, "a")], None, cx);
  201            buffer.edit([(1..1, "b")], None, cx);
  202            buffer.end_transaction_at(now, cx);
  203        });
  204
  205        assert_eq!(editor.text(cx), "ab2cde6");
  206        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  207
  208        // Last transaction happened past the group interval in a different editor.
  209        // Undo it individually and don't restore selections.
  210        editor.undo(&Undo, cx);
  211        assert_eq!(editor.text(cx), "12cde6");
  212        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  213
  214        // First two transactions happened within the group interval in this editor.
  215        // Undo them together and restore selections.
  216        editor.undo(&Undo, cx);
  217        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  218        assert_eq!(editor.text(cx), "123456");
  219        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  220
  221        // Redo the first two transactions together.
  222        editor.redo(&Redo, cx);
  223        assert_eq!(editor.text(cx), "12cde6");
  224        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  225
  226        // Redo the last transaction on its own.
  227        editor.redo(&Redo, cx);
  228        assert_eq!(editor.text(cx), "ab2cde6");
  229        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  230
  231        // Test empty transactions.
  232        editor.start_transaction_at(now, cx);
  233        editor.end_transaction_at(now, cx);
  234        editor.undo(&Undo, cx);
  235        assert_eq!(editor.text(cx), "12cde6");
  236    });
  237}
  238
  239#[gpui::test]
  240fn test_ime_composition(cx: &mut TestAppContext) {
  241    init_test(cx, |_| {});
  242
  243    let buffer = cx.new_model(|cx| {
  244        let mut buffer = language::Buffer::local("abcde", cx);
  245        // Ensure automatic grouping doesn't occur.
  246        buffer.set_group_interval(Duration::ZERO);
  247        buffer
  248    });
  249
  250    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  251    cx.add_window(|cx| {
  252        let mut editor = build_editor(buffer.clone(), cx);
  253
  254        // Start a new IME composition.
  255        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  256        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  257        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  258        assert_eq!(editor.text(cx), "äbcde");
  259        assert_eq!(
  260            editor.marked_text_ranges(cx),
  261            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  262        );
  263
  264        // Finalize IME composition.
  265        editor.replace_text_in_range(None, "ā", cx);
  266        assert_eq!(editor.text(cx), "ābcde");
  267        assert_eq!(editor.marked_text_ranges(cx), None);
  268
  269        // IME composition edits are grouped and are undone/redone at once.
  270        editor.undo(&Default::default(), cx);
  271        assert_eq!(editor.text(cx), "abcde");
  272        assert_eq!(editor.marked_text_ranges(cx), None);
  273        editor.redo(&Default::default(), cx);
  274        assert_eq!(editor.text(cx), "ābcde");
  275        assert_eq!(editor.marked_text_ranges(cx), None);
  276
  277        // Start a new IME composition.
  278        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  279        assert_eq!(
  280            editor.marked_text_ranges(cx),
  281            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  282        );
  283
  284        // Undoing during an IME composition cancels it.
  285        editor.undo(&Default::default(), cx);
  286        assert_eq!(editor.text(cx), "ābcde");
  287        assert_eq!(editor.marked_text_ranges(cx), None);
  288
  289        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  290        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  291        assert_eq!(editor.text(cx), "ābcdè");
  292        assert_eq!(
  293            editor.marked_text_ranges(cx),
  294            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  295        );
  296
  297        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  298        editor.replace_text_in_range(Some(4..999), "ę", cx);
  299        assert_eq!(editor.text(cx), "ābcdę");
  300        assert_eq!(editor.marked_text_ranges(cx), None);
  301
  302        // Start a new IME composition with multiple cursors.
  303        editor.change_selections(None, cx, |s| {
  304            s.select_ranges([
  305                OffsetUtf16(1)..OffsetUtf16(1),
  306                OffsetUtf16(3)..OffsetUtf16(3),
  307                OffsetUtf16(5)..OffsetUtf16(5),
  308            ])
  309        });
  310        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  311        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  312        assert_eq!(
  313            editor.marked_text_ranges(cx),
  314            Some(vec![
  315                OffsetUtf16(0)..OffsetUtf16(3),
  316                OffsetUtf16(4)..OffsetUtf16(7),
  317                OffsetUtf16(8)..OffsetUtf16(11)
  318            ])
  319        );
  320
  321        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  322        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  323        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  324        assert_eq!(
  325            editor.marked_text_ranges(cx),
  326            Some(vec![
  327                OffsetUtf16(1)..OffsetUtf16(2),
  328                OffsetUtf16(5)..OffsetUtf16(6),
  329                OffsetUtf16(9)..OffsetUtf16(10)
  330            ])
  331        );
  332
  333        // Finalize IME composition with multiple cursors.
  334        editor.replace_text_in_range(Some(9..10), "2", cx);
  335        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  336        assert_eq!(editor.marked_text_ranges(cx), None);
  337
  338        editor
  339    });
  340}
  341
  342#[gpui::test]
  343fn test_selection_with_mouse(cx: &mut TestAppContext) {
  344    init_test(cx, |_| {});
  345
  346    let editor = cx.add_window(|cx| {
  347        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  348        build_editor(buffer, cx)
  349    });
  350
  351    _ = editor.update(cx, |view, cx| {
  352        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  353    });
  354    assert_eq!(
  355        editor
  356            .update(cx, |view, cx| view.selections.display_ranges(cx))
  357            .unwrap(),
  358        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  359    );
  360
  361    _ = editor.update(cx, |view, cx| {
  362        view.update_selection(
  363            DisplayPoint::new(DisplayRow(3), 3),
  364            0,
  365            gpui::Point::<f32>::default(),
  366            cx,
  367        );
  368    });
  369
  370    assert_eq!(
  371        editor
  372            .update(cx, |view, cx| view.selections.display_ranges(cx))
  373            .unwrap(),
  374        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  375    );
  376
  377    _ = editor.update(cx, |view, cx| {
  378        view.update_selection(
  379            DisplayPoint::new(DisplayRow(1), 1),
  380            0,
  381            gpui::Point::<f32>::default(),
  382            cx,
  383        );
  384    });
  385
  386    assert_eq!(
  387        editor
  388            .update(cx, |view, cx| view.selections.display_ranges(cx))
  389            .unwrap(),
  390        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  391    );
  392
  393    _ = editor.update(cx, |view, cx| {
  394        view.end_selection(cx);
  395        view.update_selection(
  396            DisplayPoint::new(DisplayRow(3), 3),
  397            0,
  398            gpui::Point::<f32>::default(),
  399            cx,
  400        );
  401    });
  402
  403    assert_eq!(
  404        editor
  405            .update(cx, |view, cx| view.selections.display_ranges(cx))
  406            .unwrap(),
  407        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  408    );
  409
  410    _ = editor.update(cx, |view, cx| {
  411        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  412        view.update_selection(
  413            DisplayPoint::new(DisplayRow(0), 0),
  414            0,
  415            gpui::Point::<f32>::default(),
  416            cx,
  417        );
  418    });
  419
  420    assert_eq!(
  421        editor
  422            .update(cx, |view, cx| view.selections.display_ranges(cx))
  423            .unwrap(),
  424        [
  425            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  426            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  427        ]
  428    );
  429
  430    _ = editor.update(cx, |view, cx| {
  431        view.end_selection(cx);
  432    });
  433
  434    assert_eq!(
  435        editor
  436            .update(cx, |view, cx| view.selections.display_ranges(cx))
  437            .unwrap(),
  438        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  439    );
  440}
  441
  442#[gpui::test]
  443fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  444    init_test(cx, |_| {});
  445
  446    let editor = cx.add_window(|cx| {
  447        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  448        build_editor(buffer, cx)
  449    });
  450
  451    _ = editor.update(cx, |view, cx| {
  452        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  453    });
  454
  455    _ = editor.update(cx, |view, cx| {
  456        view.end_selection(cx);
  457    });
  458
  459    _ = editor.update(cx, |view, cx| {
  460        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  461    });
  462
  463    _ = editor.update(cx, |view, cx| {
  464        view.end_selection(cx);
  465    });
  466
  467    assert_eq!(
  468        editor
  469            .update(cx, |view, cx| view.selections.display_ranges(cx))
  470            .unwrap(),
  471        [
  472            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  473            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  474        ]
  475    );
  476
  477    _ = editor.update(cx, |view, cx| {
  478        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  479    });
  480
  481    _ = editor.update(cx, |view, cx| {
  482        view.end_selection(cx);
  483    });
  484
  485    assert_eq!(
  486        editor
  487            .update(cx, |view, cx| view.selections.display_ranges(cx))
  488            .unwrap(),
  489        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  490    );
  491}
  492
  493#[gpui::test]
  494fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  495    init_test(cx, |_| {});
  496
  497    let view = cx.add_window(|cx| {
  498        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  499        build_editor(buffer, cx)
  500    });
  501
  502    _ = view.update(cx, |view, cx| {
  503        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  504        assert_eq!(
  505            view.selections.display_ranges(cx),
  506            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  507        );
  508    });
  509
  510    _ = view.update(cx, |view, cx| {
  511        view.update_selection(
  512            DisplayPoint::new(DisplayRow(3), 3),
  513            0,
  514            gpui::Point::<f32>::default(),
  515            cx,
  516        );
  517        assert_eq!(
  518            view.selections.display_ranges(cx),
  519            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  520        );
  521    });
  522
  523    _ = view.update(cx, |view, cx| {
  524        view.cancel(&Cancel, cx);
  525        view.update_selection(
  526            DisplayPoint::new(DisplayRow(1), 1),
  527            0,
  528            gpui::Point::<f32>::default(),
  529            cx,
  530        );
  531        assert_eq!(
  532            view.selections.display_ranges(cx),
  533            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  534        );
  535    });
  536}
  537
  538#[gpui::test]
  539fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  540    init_test(cx, |_| {});
  541
  542    let view = cx.add_window(|cx| {
  543        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  544        build_editor(buffer, cx)
  545    });
  546
  547    _ = view.update(cx, |view, cx| {
  548        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  549        assert_eq!(
  550            view.selections.display_ranges(cx),
  551            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  552        );
  553
  554        view.move_down(&Default::default(), cx);
  555        assert_eq!(
  556            view.selections.display_ranges(cx),
  557            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  558        );
  559
  560        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  561        assert_eq!(
  562            view.selections.display_ranges(cx),
  563            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  564        );
  565
  566        view.move_up(&Default::default(), cx);
  567        assert_eq!(
  568            view.selections.display_ranges(cx),
  569            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  570        );
  571    });
  572}
  573
  574#[gpui::test]
  575fn test_clone(cx: &mut TestAppContext) {
  576    init_test(cx, |_| {});
  577
  578    let (text, selection_ranges) = marked_text_ranges(
  579        indoc! {"
  580            one
  581            two
  582            threeˇ
  583            four
  584            fiveˇ
  585        "},
  586        true,
  587    );
  588
  589    let editor = cx.add_window(|cx| {
  590        let buffer = MultiBuffer::build_simple(&text, cx);
  591        build_editor(buffer, cx)
  592    });
  593
  594    _ = editor.update(cx, |editor, cx| {
  595        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  596        editor.fold_ranges(
  597            [
  598                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  599                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  600            ],
  601            true,
  602            cx,
  603        );
  604    });
  605
  606    let cloned_editor = editor
  607        .update(cx, |editor, cx| {
  608            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  609        })
  610        .unwrap()
  611        .unwrap();
  612
  613    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  614    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  615
  616    assert_eq!(
  617        cloned_editor
  618            .update(cx, |e, cx| e.display_text(cx))
  619            .unwrap(),
  620        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  621    );
  622    assert_eq!(
  623        cloned_snapshot
  624            .folds_in_range(0..text.len())
  625            .collect::<Vec<_>>(),
  626        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  627    );
  628    assert_set_eq!(
  629        cloned_editor
  630            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  631            .unwrap(),
  632        editor
  633            .update(cx, |editor, cx| editor.selections.ranges(cx))
  634            .unwrap()
  635    );
  636    assert_set_eq!(
  637        cloned_editor
  638            .update(cx, |e, cx| e.selections.display_ranges(cx))
  639            .unwrap(),
  640        editor
  641            .update(cx, |e, cx| e.selections.display_ranges(cx))
  642            .unwrap()
  643    );
  644}
  645
  646#[gpui::test]
  647async fn test_navigation_history(cx: &mut TestAppContext) {
  648    init_test(cx, |_| {});
  649
  650    use workspace::item::Item;
  651
  652    let fs = FakeFs::new(cx.executor());
  653    let project = Project::test(fs, [], cx).await;
  654    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  655    let pane = workspace
  656        .update(cx, |workspace, _| workspace.active_pane().clone())
  657        .unwrap();
  658
  659    _ = workspace.update(cx, |_v, cx| {
  660        cx.new_view(|cx| {
  661            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  662            let mut editor = build_editor(buffer.clone(), cx);
  663            let handle = cx.view();
  664            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  665
  666            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  667                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  668            }
  669
  670            // Move the cursor a small distance.
  671            // Nothing is added to the navigation history.
  672            editor.change_selections(None, cx, |s| {
  673                s.select_display_ranges([
  674                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  675                ])
  676            });
  677            editor.change_selections(None, cx, |s| {
  678                s.select_display_ranges([
  679                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  680                ])
  681            });
  682            assert!(pop_history(&mut editor, cx).is_none());
  683
  684            // Move the cursor a large distance.
  685            // The history can jump back to the previous position.
  686            editor.change_selections(None, cx, |s| {
  687                s.select_display_ranges([
  688                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  689                ])
  690            });
  691            let nav_entry = pop_history(&mut editor, cx).unwrap();
  692            editor.navigate(nav_entry.data.unwrap(), cx);
  693            assert_eq!(nav_entry.item.id(), cx.entity_id());
  694            assert_eq!(
  695                editor.selections.display_ranges(cx),
  696                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  697            );
  698            assert!(pop_history(&mut editor, cx).is_none());
  699
  700            // Move the cursor a small distance via the mouse.
  701            // Nothing is added to the navigation history.
  702            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  703            editor.end_selection(cx);
  704            assert_eq!(
  705                editor.selections.display_ranges(cx),
  706                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  707            );
  708            assert!(pop_history(&mut editor, cx).is_none());
  709
  710            // Move the cursor a large distance via the mouse.
  711            // The history can jump back to the previous position.
  712            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  713            editor.end_selection(cx);
  714            assert_eq!(
  715                editor.selections.display_ranges(cx),
  716                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  717            );
  718            let nav_entry = pop_history(&mut editor, cx).unwrap();
  719            editor.navigate(nav_entry.data.unwrap(), cx);
  720            assert_eq!(nav_entry.item.id(), cx.entity_id());
  721            assert_eq!(
  722                editor.selections.display_ranges(cx),
  723                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  724            );
  725            assert!(pop_history(&mut editor, cx).is_none());
  726
  727            // Set scroll position to check later
  728            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  729            let original_scroll_position = editor.scroll_manager.anchor();
  730
  731            // Jump to the end of the document and adjust scroll
  732            editor.move_to_end(&MoveToEnd, cx);
  733            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  734            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  735
  736            let nav_entry = pop_history(&mut editor, cx).unwrap();
  737            editor.navigate(nav_entry.data.unwrap(), cx);
  738            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  739
  740            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  741            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  742            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  743            let invalid_point = Point::new(9999, 0);
  744            editor.navigate(
  745                Box::new(NavigationData {
  746                    cursor_anchor: invalid_anchor,
  747                    cursor_position: invalid_point,
  748                    scroll_anchor: ScrollAnchor {
  749                        anchor: invalid_anchor,
  750                        offset: Default::default(),
  751                    },
  752                    scroll_top_row: invalid_point.row,
  753                }),
  754                cx,
  755            );
  756            assert_eq!(
  757                editor.selections.display_ranges(cx),
  758                &[editor.max_point(cx)..editor.max_point(cx)]
  759            );
  760            assert_eq!(
  761                editor.scroll_position(cx),
  762                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  763            );
  764
  765            editor
  766        })
  767    });
  768}
  769
  770#[gpui::test]
  771fn test_cancel(cx: &mut TestAppContext) {
  772    init_test(cx, |_| {});
  773
  774    let view = cx.add_window(|cx| {
  775        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  776        build_editor(buffer, cx)
  777    });
  778
  779    _ = view.update(cx, |view, cx| {
  780        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  781        view.update_selection(
  782            DisplayPoint::new(DisplayRow(1), 1),
  783            0,
  784            gpui::Point::<f32>::default(),
  785            cx,
  786        );
  787        view.end_selection(cx);
  788
  789        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  790        view.update_selection(
  791            DisplayPoint::new(DisplayRow(0), 3),
  792            0,
  793            gpui::Point::<f32>::default(),
  794            cx,
  795        );
  796        view.end_selection(cx);
  797        assert_eq!(
  798            view.selections.display_ranges(cx),
  799            [
  800                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  801                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  802            ]
  803        );
  804    });
  805
  806    _ = view.update(cx, |view, cx| {
  807        view.cancel(&Cancel, cx);
  808        assert_eq!(
  809            view.selections.display_ranges(cx),
  810            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  811        );
  812    });
  813
  814    _ = view.update(cx, |view, cx| {
  815        view.cancel(&Cancel, cx);
  816        assert_eq!(
  817            view.selections.display_ranges(cx),
  818            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  819        );
  820    });
  821}
  822
  823#[gpui::test]
  824fn test_fold_action(cx: &mut TestAppContext) {
  825    init_test(cx, |_| {});
  826
  827    let view = cx.add_window(|cx| {
  828        let buffer = MultiBuffer::build_simple(
  829            &"
  830                impl Foo {
  831                    // Hello!
  832
  833                    fn a() {
  834                        1
  835                    }
  836
  837                    fn b() {
  838                        2
  839                    }
  840
  841                    fn c() {
  842                        3
  843                    }
  844                }
  845            "
  846            .unindent(),
  847            cx,
  848        );
  849        build_editor(buffer.clone(), cx)
  850    });
  851
  852    _ = view.update(cx, |view, cx| {
  853        view.change_selections(None, cx, |s| {
  854            s.select_display_ranges([
  855                DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(12), 0)
  856            ]);
  857        });
  858        view.fold(&Fold, cx);
  859        assert_eq!(
  860            view.display_text(cx),
  861            "
  862                impl Foo {
  863                    // Hello!
  864
  865                    fn a() {
  866                        1
  867                    }
  868
  869                    fn b() {⋯
  870                    }
  871
  872                    fn c() {⋯
  873                    }
  874                }
  875            "
  876            .unindent(),
  877        );
  878
  879        view.fold(&Fold, cx);
  880        assert_eq!(
  881            view.display_text(cx),
  882            "
  883                impl Foo {⋯
  884                }
  885            "
  886            .unindent(),
  887        );
  888
  889        view.unfold_lines(&UnfoldLines, cx);
  890        assert_eq!(
  891            view.display_text(cx),
  892            "
  893                impl Foo {
  894                    // Hello!
  895
  896                    fn a() {
  897                        1
  898                    }
  899
  900                    fn b() {⋯
  901                    }
  902
  903                    fn c() {⋯
  904                    }
  905                }
  906            "
  907            .unindent(),
  908        );
  909
  910        view.unfold_lines(&UnfoldLines, cx);
  911        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  912    });
  913}
  914
  915#[gpui::test]
  916fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  917    init_test(cx, |_| {});
  918
  919    let view = cx.add_window(|cx| {
  920        let buffer = MultiBuffer::build_simple(
  921            &"
  922                class Foo:
  923                    # Hello!
  924
  925                    def a():
  926                        print(1)
  927
  928                    def b():
  929                        print(2)
  930
  931                    def c():
  932                        print(3)
  933            "
  934            .unindent(),
  935            cx,
  936        );
  937        build_editor(buffer.clone(), cx)
  938    });
  939
  940    _ = view.update(cx, |view, cx| {
  941        view.change_selections(None, cx, |s| {
  942            s.select_display_ranges([
  943                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(10), 0)
  944            ]);
  945        });
  946        view.fold(&Fold, cx);
  947        assert_eq!(
  948            view.display_text(cx),
  949            "
  950                class Foo:
  951                    # Hello!
  952
  953                    def a():
  954                        print(1)
  955
  956                    def b():⋯
  957
  958                    def c():⋯
  959            "
  960            .unindent(),
  961        );
  962
  963        view.fold(&Fold, cx);
  964        assert_eq!(
  965            view.display_text(cx),
  966            "
  967                class Foo:⋯
  968            "
  969            .unindent(),
  970        );
  971
  972        view.unfold_lines(&UnfoldLines, cx);
  973        assert_eq!(
  974            view.display_text(cx),
  975            "
  976                class Foo:
  977                    # Hello!
  978
  979                    def a():
  980                        print(1)
  981
  982                    def b():⋯
  983
  984                    def c():⋯
  985            "
  986            .unindent(),
  987        );
  988
  989        view.unfold_lines(&UnfoldLines, cx);
  990        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  991    });
  992}
  993
  994#[gpui::test]
  995fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  996    init_test(cx, |_| {});
  997
  998    let view = cx.add_window(|cx| {
  999        let buffer = MultiBuffer::build_simple(
 1000            &"
 1001                class Foo:
 1002                    # Hello!
 1003
 1004                    def a():
 1005                        print(1)
 1006
 1007                    def b():
 1008                        print(2)
 1009
 1010
 1011                    def c():
 1012                        print(3)
 1013
 1014
 1015            "
 1016            .unindent(),
 1017            cx,
 1018        );
 1019        build_editor(buffer.clone(), cx)
 1020    });
 1021
 1022    _ = view.update(cx, |view, cx| {
 1023        view.change_selections(None, cx, |s| {
 1024            s.select_display_ranges([
 1025                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1026            ]);
 1027        });
 1028        view.fold(&Fold, cx);
 1029        assert_eq!(
 1030            view.display_text(cx),
 1031            "
 1032                class Foo:
 1033                    # Hello!
 1034
 1035                    def a():
 1036                        print(1)
 1037
 1038                    def b():⋯
 1039
 1040
 1041                    def c():⋯
 1042
 1043
 1044            "
 1045            .unindent(),
 1046        );
 1047
 1048        view.fold(&Fold, cx);
 1049        assert_eq!(
 1050            view.display_text(cx),
 1051            "
 1052                class Foo:⋯
 1053
 1054
 1055            "
 1056            .unindent(),
 1057        );
 1058
 1059        view.unfold_lines(&UnfoldLines, cx);
 1060        assert_eq!(
 1061            view.display_text(cx),
 1062            "
 1063                class Foo:
 1064                    # Hello!
 1065
 1066                    def a():
 1067                        print(1)
 1068
 1069                    def b():⋯
 1070
 1071
 1072                    def c():⋯
 1073
 1074
 1075            "
 1076            .unindent(),
 1077        );
 1078
 1079        view.unfold_lines(&UnfoldLines, cx);
 1080        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1081    });
 1082}
 1083
 1084#[gpui::test]
 1085fn test_move_cursor(cx: &mut TestAppContext) {
 1086    init_test(cx, |_| {});
 1087
 1088    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1089    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1090
 1091    _ = buffer.update(cx, |buffer, cx| {
 1092        buffer.edit(
 1093            vec![
 1094                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1095                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1096            ],
 1097            None,
 1098            cx,
 1099        );
 1100    });
 1101    _ = view.update(cx, |view, cx| {
 1102        assert_eq!(
 1103            view.selections.display_ranges(cx),
 1104            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1105        );
 1106
 1107        view.move_down(&MoveDown, cx);
 1108        assert_eq!(
 1109            view.selections.display_ranges(cx),
 1110            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1111        );
 1112
 1113        view.move_right(&MoveRight, cx);
 1114        assert_eq!(
 1115            view.selections.display_ranges(cx),
 1116            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1117        );
 1118
 1119        view.move_left(&MoveLeft, cx);
 1120        assert_eq!(
 1121            view.selections.display_ranges(cx),
 1122            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1123        );
 1124
 1125        view.move_up(&MoveUp, cx);
 1126        assert_eq!(
 1127            view.selections.display_ranges(cx),
 1128            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1129        );
 1130
 1131        view.move_to_end(&MoveToEnd, cx);
 1132        assert_eq!(
 1133            view.selections.display_ranges(cx),
 1134            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1135        );
 1136
 1137        view.move_to_beginning(&MoveToBeginning, cx);
 1138        assert_eq!(
 1139            view.selections.display_ranges(cx),
 1140            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1141        );
 1142
 1143        view.change_selections(None, cx, |s| {
 1144            s.select_display_ranges([
 1145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1146            ]);
 1147        });
 1148        view.select_to_beginning(&SelectToBeginning, cx);
 1149        assert_eq!(
 1150            view.selections.display_ranges(cx),
 1151            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1152        );
 1153
 1154        view.select_to_end(&SelectToEnd, cx);
 1155        assert_eq!(
 1156            view.selections.display_ranges(cx),
 1157            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1158        );
 1159    });
 1160}
 1161
 1162// TODO: Re-enable this test
 1163#[cfg(target_os = "macos")]
 1164#[gpui::test]
 1165fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1166    init_test(cx, |_| {});
 1167
 1168    let view = cx.add_window(|cx| {
 1169        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1170        build_editor(buffer.clone(), cx)
 1171    });
 1172
 1173    assert_eq!('ⓐ'.len_utf8(), 3);
 1174    assert_eq!('α'.len_utf8(), 2);
 1175
 1176    _ = view.update(cx, |view, cx| {
 1177        view.fold_ranges(
 1178            vec![
 1179                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1180                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1181                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1182            ],
 1183            true,
 1184            cx,
 1185        );
 1186        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1187
 1188        view.move_right(&MoveRight, cx);
 1189        assert_eq!(
 1190            view.selections.display_ranges(cx),
 1191            &[empty_range(0, "".len())]
 1192        );
 1193        view.move_right(&MoveRight, cx);
 1194        assert_eq!(
 1195            view.selections.display_ranges(cx),
 1196            &[empty_range(0, "ⓐⓑ".len())]
 1197        );
 1198        view.move_right(&MoveRight, cx);
 1199        assert_eq!(
 1200            view.selections.display_ranges(cx),
 1201            &[empty_range(0, "ⓐⓑ⋯".len())]
 1202        );
 1203
 1204        view.move_down(&MoveDown, cx);
 1205        assert_eq!(
 1206            view.selections.display_ranges(cx),
 1207            &[empty_range(1, "ab⋯e".len())]
 1208        );
 1209        view.move_left(&MoveLeft, cx);
 1210        assert_eq!(
 1211            view.selections.display_ranges(cx),
 1212            &[empty_range(1, "ab⋯".len())]
 1213        );
 1214        view.move_left(&MoveLeft, cx);
 1215        assert_eq!(
 1216            view.selections.display_ranges(cx),
 1217            &[empty_range(1, "ab".len())]
 1218        );
 1219        view.move_left(&MoveLeft, cx);
 1220        assert_eq!(
 1221            view.selections.display_ranges(cx),
 1222            &[empty_range(1, "a".len())]
 1223        );
 1224
 1225        view.move_down(&MoveDown, cx);
 1226        assert_eq!(
 1227            view.selections.display_ranges(cx),
 1228            &[empty_range(2, "α".len())]
 1229        );
 1230        view.move_right(&MoveRight, cx);
 1231        assert_eq!(
 1232            view.selections.display_ranges(cx),
 1233            &[empty_range(2, "αβ".len())]
 1234        );
 1235        view.move_right(&MoveRight, cx);
 1236        assert_eq!(
 1237            view.selections.display_ranges(cx),
 1238            &[empty_range(2, "αβ⋯".len())]
 1239        );
 1240        view.move_right(&MoveRight, cx);
 1241        assert_eq!(
 1242            view.selections.display_ranges(cx),
 1243            &[empty_range(2, "αβ⋯ε".len())]
 1244        );
 1245
 1246        view.move_up(&MoveUp, cx);
 1247        assert_eq!(
 1248            view.selections.display_ranges(cx),
 1249            &[empty_range(1, "ab⋯e".len())]
 1250        );
 1251        view.move_down(&MoveDown, cx);
 1252        assert_eq!(
 1253            view.selections.display_ranges(cx),
 1254            &[empty_range(2, "αβ⋯ε".len())]
 1255        );
 1256        view.move_up(&MoveUp, cx);
 1257        assert_eq!(
 1258            view.selections.display_ranges(cx),
 1259            &[empty_range(1, "ab⋯e".len())]
 1260        );
 1261
 1262        view.move_up(&MoveUp, cx);
 1263        assert_eq!(
 1264            view.selections.display_ranges(cx),
 1265            &[empty_range(0, "ⓐⓑ".len())]
 1266        );
 1267        view.move_left(&MoveLeft, cx);
 1268        assert_eq!(
 1269            view.selections.display_ranges(cx),
 1270            &[empty_range(0, "".len())]
 1271        );
 1272        view.move_left(&MoveLeft, cx);
 1273        assert_eq!(
 1274            view.selections.display_ranges(cx),
 1275            &[empty_range(0, "".len())]
 1276        );
 1277    });
 1278}
 1279
 1280#[gpui::test]
 1281fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1282    init_test(cx, |_| {});
 1283
 1284    let view = cx.add_window(|cx| {
 1285        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1286        build_editor(buffer.clone(), cx)
 1287    });
 1288    _ = view.update(cx, |view, cx| {
 1289        view.change_selections(None, cx, |s| {
 1290            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1291        });
 1292        view.move_down(&MoveDown, cx);
 1293        assert_eq!(
 1294            view.selections.display_ranges(cx),
 1295            &[empty_range(1, "abcd".len())]
 1296        );
 1297
 1298        view.move_down(&MoveDown, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(2, "αβγ".len())]
 1302        );
 1303
 1304        view.move_down(&MoveDown, cx);
 1305        assert_eq!(
 1306            view.selections.display_ranges(cx),
 1307            &[empty_range(3, "abcd".len())]
 1308        );
 1309
 1310        view.move_down(&MoveDown, cx);
 1311        assert_eq!(
 1312            view.selections.display_ranges(cx),
 1313            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1314        );
 1315
 1316        view.move_up(&MoveUp, cx);
 1317        assert_eq!(
 1318            view.selections.display_ranges(cx),
 1319            &[empty_range(3, "abcd".len())]
 1320        );
 1321
 1322        view.move_up(&MoveUp, cx);
 1323        assert_eq!(
 1324            view.selections.display_ranges(cx),
 1325            &[empty_range(2, "αβγ".len())]
 1326        );
 1327    });
 1328}
 1329
 1330#[gpui::test]
 1331fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1332    init_test(cx, |_| {});
 1333    let move_to_beg = MoveToBeginningOfLine {
 1334        stop_at_soft_wraps: true,
 1335    };
 1336
 1337    let move_to_end = MoveToEndOfLine {
 1338        stop_at_soft_wraps: true,
 1339    };
 1340
 1341    let view = cx.add_window(|cx| {
 1342        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1343        build_editor(buffer, cx)
 1344    });
 1345    _ = view.update(cx, |view, cx| {
 1346        view.change_selections(None, cx, |s| {
 1347            s.select_display_ranges([
 1348                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1349                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1350            ]);
 1351        });
 1352    });
 1353
 1354    _ = view.update(cx, |view, cx| {
 1355        view.move_to_beginning_of_line(&move_to_beg, cx);
 1356        assert_eq!(
 1357            view.selections.display_ranges(cx),
 1358            &[
 1359                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1360                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1361            ]
 1362        );
 1363    });
 1364
 1365    _ = view.update(cx, |view, cx| {
 1366        view.move_to_beginning_of_line(&move_to_beg, cx);
 1367        assert_eq!(
 1368            view.selections.display_ranges(cx),
 1369            &[
 1370                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1371                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1372            ]
 1373        );
 1374    });
 1375
 1376    _ = view.update(cx, |view, cx| {
 1377        view.move_to_beginning_of_line(&move_to_beg, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[
 1381                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1382                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1383            ]
 1384        );
 1385    });
 1386
 1387    _ = view.update(cx, |view, cx| {
 1388        view.move_to_end_of_line(&move_to_end, cx);
 1389        assert_eq!(
 1390            view.selections.display_ranges(cx),
 1391            &[
 1392                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1393                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1394            ]
 1395        );
 1396    });
 1397
 1398    // Moving to the end of line again is a no-op.
 1399    _ = view.update(cx, |view, cx| {
 1400        view.move_to_end_of_line(&move_to_end, cx);
 1401        assert_eq!(
 1402            view.selections.display_ranges(cx),
 1403            &[
 1404                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1405                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1406            ]
 1407        );
 1408    });
 1409
 1410    _ = view.update(cx, |view, cx| {
 1411        view.move_left(&MoveLeft, cx);
 1412        view.select_to_beginning_of_line(
 1413            &SelectToBeginningOfLine {
 1414                stop_at_soft_wraps: true,
 1415            },
 1416            cx,
 1417        );
 1418        assert_eq!(
 1419            view.selections.display_ranges(cx),
 1420            &[
 1421                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1422                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1423            ]
 1424        );
 1425    });
 1426
 1427    _ = view.update(cx, |view, cx| {
 1428        view.select_to_beginning_of_line(
 1429            &SelectToBeginningOfLine {
 1430                stop_at_soft_wraps: true,
 1431            },
 1432            cx,
 1433        );
 1434        assert_eq!(
 1435            view.selections.display_ranges(cx),
 1436            &[
 1437                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1438                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1439            ]
 1440        );
 1441    });
 1442
 1443    _ = view.update(cx, |view, cx| {
 1444        view.select_to_beginning_of_line(
 1445            &SelectToBeginningOfLine {
 1446                stop_at_soft_wraps: true,
 1447            },
 1448            cx,
 1449        );
 1450        assert_eq!(
 1451            view.selections.display_ranges(cx),
 1452            &[
 1453                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1454                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1455            ]
 1456        );
 1457    });
 1458
 1459    _ = view.update(cx, |view, cx| {
 1460        view.select_to_end_of_line(
 1461            &SelectToEndOfLine {
 1462                stop_at_soft_wraps: true,
 1463            },
 1464            cx,
 1465        );
 1466        assert_eq!(
 1467            view.selections.display_ranges(cx),
 1468            &[
 1469                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1470                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1471            ]
 1472        );
 1473    });
 1474
 1475    _ = view.update(cx, |view, cx| {
 1476        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1477        assert_eq!(view.display_text(cx), "ab\n  de");
 1478        assert_eq!(
 1479            view.selections.display_ranges(cx),
 1480            &[
 1481                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1482                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1483            ]
 1484        );
 1485    });
 1486
 1487    _ = view.update(cx, |view, cx| {
 1488        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1489        assert_eq!(view.display_text(cx), "\n");
 1490        assert_eq!(
 1491            view.selections.display_ranges(cx),
 1492            &[
 1493                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1494                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1495            ]
 1496        );
 1497    });
 1498}
 1499
 1500#[gpui::test]
 1501fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1502    init_test(cx, |_| {});
 1503    let move_to_beg = MoveToBeginningOfLine {
 1504        stop_at_soft_wraps: false,
 1505    };
 1506
 1507    let move_to_end = MoveToEndOfLine {
 1508        stop_at_soft_wraps: false,
 1509    };
 1510
 1511    let view = cx.add_window(|cx| {
 1512        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1513        build_editor(buffer, cx)
 1514    });
 1515
 1516    _ = view.update(cx, |view, cx| {
 1517        view.set_wrap_width(Some(140.0.into()), cx);
 1518
 1519        // We expect the following lines after wrapping
 1520        // ```
 1521        // thequickbrownfox
 1522        // jumpedoverthelazydo
 1523        // gs
 1524        // ```
 1525        // The final `gs` was soft-wrapped onto a new line.
 1526        assert_eq!(
 1527            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1528            view.display_text(cx),
 1529        );
 1530
 1531        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1532        // Start the cursor at the `k` on the first line
 1533        view.change_selections(None, cx, |s| {
 1534            s.select_display_ranges([
 1535                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1536            ]);
 1537        });
 1538
 1539        // Moving to the beginning of the line should put us at the beginning of the line.
 1540        view.move_to_beginning_of_line(&move_to_beg, cx);
 1541        assert_eq!(
 1542            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1543            view.selections.display_ranges(cx)
 1544        );
 1545
 1546        // Moving to the end of the line should put us at the end of the line.
 1547        view.move_to_end_of_line(&move_to_end, cx);
 1548        assert_eq!(
 1549            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1550            view.selections.display_ranges(cx)
 1551        );
 1552
 1553        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1554        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1555        view.change_selections(None, cx, |s| {
 1556            s.select_display_ranges([
 1557                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1558            ]);
 1559        });
 1560
 1561        // Moving to the beginning of the line should put us at the start of the second line of
 1562        // display text, i.e., the `j`.
 1563        view.move_to_beginning_of_line(&move_to_beg, cx);
 1564        assert_eq!(
 1565            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1566            view.selections.display_ranges(cx)
 1567        );
 1568
 1569        // Moving to the beginning of the line again should be a no-op.
 1570        view.move_to_beginning_of_line(&move_to_beg, cx);
 1571        assert_eq!(
 1572            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1573            view.selections.display_ranges(cx)
 1574        );
 1575
 1576        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1577        // next display line.
 1578        view.move_to_end_of_line(&move_to_end, cx);
 1579        assert_eq!(
 1580            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1581            view.selections.display_ranges(cx)
 1582        );
 1583
 1584        // Moving to the end of the line again should be a no-op.
 1585        view.move_to_end_of_line(&move_to_end, cx);
 1586        assert_eq!(
 1587            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1588            view.selections.display_ranges(cx)
 1589        );
 1590    });
 1591}
 1592
 1593#[gpui::test]
 1594fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1595    init_test(cx, |_| {});
 1596
 1597    let view = cx.add_window(|cx| {
 1598        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1599        build_editor(buffer, cx)
 1600    });
 1601    _ = view.update(cx, |view, cx| {
 1602        view.change_selections(None, cx, |s| {
 1603            s.select_display_ranges([
 1604                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1605                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1606            ])
 1607        });
 1608
 1609        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1610        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1611
 1612        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1613        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1614
 1615        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1616        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1617
 1618        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1619        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1620
 1621        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1622        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1623
 1624        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1625        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1626
 1627        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1628        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1629
 1630        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1631        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1632
 1633        view.move_right(&MoveRight, cx);
 1634        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1635        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1636
 1637        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1638        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1639
 1640        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1641        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1642    });
 1643}
 1644
 1645#[gpui::test]
 1646fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1647    init_test(cx, |_| {});
 1648
 1649    let view = cx.add_window(|cx| {
 1650        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1651        build_editor(buffer, cx)
 1652    });
 1653
 1654    _ = view.update(cx, |view, cx| {
 1655        view.set_wrap_width(Some(140.0.into()), cx);
 1656        assert_eq!(
 1657            view.display_text(cx),
 1658            "use one::{\n    two::three::\n    four::five\n};"
 1659        );
 1660
 1661        view.change_selections(None, cx, |s| {
 1662            s.select_display_ranges([
 1663                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1664            ]);
 1665        });
 1666
 1667        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1668        assert_eq!(
 1669            view.selections.display_ranges(cx),
 1670            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1671        );
 1672
 1673        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1674        assert_eq!(
 1675            view.selections.display_ranges(cx),
 1676            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1677        );
 1678
 1679        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1680        assert_eq!(
 1681            view.selections.display_ranges(cx),
 1682            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1683        );
 1684
 1685        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1686        assert_eq!(
 1687            view.selections.display_ranges(cx),
 1688            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1689        );
 1690
 1691        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1692        assert_eq!(
 1693            view.selections.display_ranges(cx),
 1694            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1695        );
 1696
 1697        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1698        assert_eq!(
 1699            view.selections.display_ranges(cx),
 1700            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1701        );
 1702    });
 1703}
 1704
 1705#[gpui::test]
 1706async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1707    init_test(cx, |_| {});
 1708    let mut cx = EditorTestContext::new(cx).await;
 1709
 1710    let line_height = cx.editor(|editor, cx| {
 1711        editor
 1712            .style()
 1713            .unwrap()
 1714            .text
 1715            .line_height_in_pixels(cx.rem_size())
 1716    });
 1717    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1718
 1719    cx.set_state(
 1720        &r#"ˇone
 1721        two
 1722
 1723        three
 1724        fourˇ
 1725        five
 1726
 1727        six"#
 1728            .unindent(),
 1729    );
 1730
 1731    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1732    cx.assert_editor_state(
 1733        &r#"one
 1734        two
 1735        ˇ
 1736        three
 1737        four
 1738        five
 1739        ˇ
 1740        six"#
 1741            .unindent(),
 1742    );
 1743
 1744    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1745    cx.assert_editor_state(
 1746        &r#"one
 1747        two
 1748
 1749        three
 1750        four
 1751        five
 1752        ˇ
 1753        sixˇ"#
 1754            .unindent(),
 1755    );
 1756
 1757    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1758    cx.assert_editor_state(
 1759        &r#"one
 1760        two
 1761
 1762        three
 1763        four
 1764        five
 1765
 1766        sixˇ"#
 1767            .unindent(),
 1768    );
 1769
 1770    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1771    cx.assert_editor_state(
 1772        &r#"one
 1773        two
 1774
 1775        three
 1776        four
 1777        five
 1778        ˇ
 1779        six"#
 1780            .unindent(),
 1781    );
 1782
 1783    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1784    cx.assert_editor_state(
 1785        &r#"one
 1786        two
 1787        ˇ
 1788        three
 1789        four
 1790        five
 1791
 1792        six"#
 1793            .unindent(),
 1794    );
 1795
 1796    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1797    cx.assert_editor_state(
 1798        &r#"ˇone
 1799        two
 1800
 1801        three
 1802        four
 1803        five
 1804
 1805        six"#
 1806            .unindent(),
 1807    );
 1808}
 1809
 1810#[gpui::test]
 1811async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1812    init_test(cx, |_| {});
 1813    let mut cx = EditorTestContext::new(cx).await;
 1814    let line_height = cx.editor(|editor, cx| {
 1815        editor
 1816            .style()
 1817            .unwrap()
 1818            .text
 1819            .line_height_in_pixels(cx.rem_size())
 1820    });
 1821    let window = cx.window;
 1822    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1823
 1824    cx.set_state(
 1825        &r#"ˇone
 1826        two
 1827        three
 1828        four
 1829        five
 1830        six
 1831        seven
 1832        eight
 1833        nine
 1834        ten
 1835        "#,
 1836    );
 1837
 1838    cx.update_editor(|editor, cx| {
 1839        assert_eq!(
 1840            editor.snapshot(cx).scroll_position(),
 1841            gpui::Point::new(0., 0.)
 1842        );
 1843        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1844        assert_eq!(
 1845            editor.snapshot(cx).scroll_position(),
 1846            gpui::Point::new(0., 3.)
 1847        );
 1848        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1849        assert_eq!(
 1850            editor.snapshot(cx).scroll_position(),
 1851            gpui::Point::new(0., 6.)
 1852        );
 1853        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1854        assert_eq!(
 1855            editor.snapshot(cx).scroll_position(),
 1856            gpui::Point::new(0., 3.)
 1857        );
 1858
 1859        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1860        assert_eq!(
 1861            editor.snapshot(cx).scroll_position(),
 1862            gpui::Point::new(0., 1.)
 1863        );
 1864        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1865        assert_eq!(
 1866            editor.snapshot(cx).scroll_position(),
 1867            gpui::Point::new(0., 3.)
 1868        );
 1869    });
 1870}
 1871
 1872#[gpui::test]
 1873async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1874    init_test(cx, |_| {});
 1875    let mut cx = EditorTestContext::new(cx).await;
 1876
 1877    let line_height = cx.update_editor(|editor, cx| {
 1878        editor.set_vertical_scroll_margin(2, cx);
 1879        editor
 1880            .style()
 1881            .unwrap()
 1882            .text
 1883            .line_height_in_pixels(cx.rem_size())
 1884    });
 1885    let window = cx.window;
 1886    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1887
 1888    cx.set_state(
 1889        &r#"ˇone
 1890            two
 1891            three
 1892            four
 1893            five
 1894            six
 1895            seven
 1896            eight
 1897            nine
 1898            ten
 1899        "#,
 1900    );
 1901    cx.update_editor(|editor, cx| {
 1902        assert_eq!(
 1903            editor.snapshot(cx).scroll_position(),
 1904            gpui::Point::new(0., 0.0)
 1905        );
 1906    });
 1907
 1908    // Add a cursor below the visible area. Since both cursors cannot fit
 1909    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1910    // allows the vertical scroll margin below that cursor.
 1911    cx.update_editor(|editor, cx| {
 1912        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1913            selections.select_ranges([
 1914                Point::new(0, 0)..Point::new(0, 0),
 1915                Point::new(6, 0)..Point::new(6, 0),
 1916            ]);
 1917        })
 1918    });
 1919    cx.update_editor(|editor, cx| {
 1920        assert_eq!(
 1921            editor.snapshot(cx).scroll_position(),
 1922            gpui::Point::new(0., 3.0)
 1923        );
 1924    });
 1925
 1926    // Move down. The editor cursor scrolls down to track the newest cursor.
 1927    cx.update_editor(|editor, cx| {
 1928        editor.move_down(&Default::default(), cx);
 1929    });
 1930    cx.update_editor(|editor, cx| {
 1931        assert_eq!(
 1932            editor.snapshot(cx).scroll_position(),
 1933            gpui::Point::new(0., 4.0)
 1934        );
 1935    });
 1936
 1937    // Add a cursor above the visible area. Since both cursors fit on screen,
 1938    // the editor scrolls to show both.
 1939    cx.update_editor(|editor, cx| {
 1940        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1941            selections.select_ranges([
 1942                Point::new(1, 0)..Point::new(1, 0),
 1943                Point::new(6, 0)..Point::new(6, 0),
 1944            ]);
 1945        })
 1946    });
 1947    cx.update_editor(|editor, cx| {
 1948        assert_eq!(
 1949            editor.snapshot(cx).scroll_position(),
 1950            gpui::Point::new(0., 1.0)
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1957    init_test(cx, |_| {});
 1958    let mut cx = EditorTestContext::new(cx).await;
 1959
 1960    let line_height = cx.editor(|editor, cx| {
 1961        editor
 1962            .style()
 1963            .unwrap()
 1964            .text
 1965            .line_height_in_pixels(cx.rem_size())
 1966    });
 1967    let window = cx.window;
 1968    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1969    cx.set_state(
 1970        &r#"
 1971        ˇone
 1972        two
 1973        threeˇ
 1974        four
 1975        five
 1976        six
 1977        seven
 1978        eight
 1979        nine
 1980        ten
 1981        "#
 1982        .unindent(),
 1983    );
 1984
 1985    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1986    cx.assert_editor_state(
 1987        &r#"
 1988        one
 1989        two
 1990        three
 1991        ˇfour
 1992        five
 1993        sixˇ
 1994        seven
 1995        eight
 1996        nine
 1997        ten
 1998        "#
 1999        .unindent(),
 2000    );
 2001
 2002    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2003    cx.assert_editor_state(
 2004        &r#"
 2005        one
 2006        two
 2007        three
 2008        four
 2009        five
 2010        six
 2011        ˇseven
 2012        eight
 2013        nineˇ
 2014        ten
 2015        "#
 2016        .unindent(),
 2017    );
 2018
 2019    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2020    cx.assert_editor_state(
 2021        &r#"
 2022        one
 2023        two
 2024        three
 2025        ˇfour
 2026        five
 2027        sixˇ
 2028        seven
 2029        eight
 2030        nine
 2031        ten
 2032        "#
 2033        .unindent(),
 2034    );
 2035
 2036    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2037    cx.assert_editor_state(
 2038        &r#"
 2039        ˇone
 2040        two
 2041        threeˇ
 2042        four
 2043        five
 2044        six
 2045        seven
 2046        eight
 2047        nine
 2048        ten
 2049        "#
 2050        .unindent(),
 2051    );
 2052
 2053    // Test select collapsing
 2054    cx.update_editor(|editor, cx| {
 2055        editor.move_page_down(&MovePageDown::default(), cx);
 2056        editor.move_page_down(&MovePageDown::default(), cx);
 2057        editor.move_page_down(&MovePageDown::default(), cx);
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"
 2061        one
 2062        two
 2063        three
 2064        four
 2065        five
 2066        six
 2067        seven
 2068        eight
 2069        nine
 2070        ˇten
 2071        ˇ"#
 2072        .unindent(),
 2073    );
 2074}
 2075
 2076#[gpui::test]
 2077async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2078    init_test(cx, |_| {});
 2079    let mut cx = EditorTestContext::new(cx).await;
 2080    cx.set_state("one «two threeˇ» four");
 2081    cx.update_editor(|editor, cx| {
 2082        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2083        assert_eq!(editor.text(cx), " four");
 2084    });
 2085}
 2086
 2087#[gpui::test]
 2088fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2089    init_test(cx, |_| {});
 2090
 2091    let view = cx.add_window(|cx| {
 2092        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2093        build_editor(buffer.clone(), cx)
 2094    });
 2095
 2096    _ = view.update(cx, |view, cx| {
 2097        view.change_selections(None, cx, |s| {
 2098            s.select_display_ranges([
 2099                // an empty selection - the preceding word fragment is deleted
 2100                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2101                // characters selected - they are deleted
 2102                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2103            ])
 2104        });
 2105        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
 2106        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2107    });
 2108
 2109    _ = view.update(cx, |view, cx| {
 2110        view.change_selections(None, cx, |s| {
 2111            s.select_display_ranges([
 2112                // an empty selection - the following word fragment is deleted
 2113                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2114                // characters selected - they are deleted
 2115                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2116            ])
 2117        });
 2118        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
 2119        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2120    });
 2121}
 2122
 2123#[gpui::test]
 2124fn test_newline(cx: &mut TestAppContext) {
 2125    init_test(cx, |_| {});
 2126
 2127    let view = cx.add_window(|cx| {
 2128        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2129        build_editor(buffer.clone(), cx)
 2130    });
 2131
 2132    _ = view.update(cx, |view, cx| {
 2133        view.change_selections(None, cx, |s| {
 2134            s.select_display_ranges([
 2135                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2136                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2137                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2138            ])
 2139        });
 2140
 2141        view.newline(&Newline, cx);
 2142        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2143    });
 2144}
 2145
 2146#[gpui::test]
 2147fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2148    init_test(cx, |_| {});
 2149
 2150    let editor = cx.add_window(|cx| {
 2151        let buffer = MultiBuffer::build_simple(
 2152            "
 2153                a
 2154                b(
 2155                    X
 2156                )
 2157                c(
 2158                    X
 2159                )
 2160            "
 2161            .unindent()
 2162            .as_str(),
 2163            cx,
 2164        );
 2165        let mut editor = build_editor(buffer.clone(), cx);
 2166        editor.change_selections(None, cx, |s| {
 2167            s.select_ranges([
 2168                Point::new(2, 4)..Point::new(2, 5),
 2169                Point::new(5, 4)..Point::new(5, 5),
 2170            ])
 2171        });
 2172        editor
 2173    });
 2174
 2175    _ = editor.update(cx, |editor, cx| {
 2176        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2177        editor.buffer.update(cx, |buffer, cx| {
 2178            buffer.edit(
 2179                [
 2180                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2181                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2182                ],
 2183                None,
 2184                cx,
 2185            );
 2186            assert_eq!(
 2187                buffer.read(cx).text(),
 2188                "
 2189                    a
 2190                    b()
 2191                    c()
 2192                "
 2193                .unindent()
 2194            );
 2195        });
 2196        assert_eq!(
 2197            editor.selections.ranges(cx),
 2198            &[
 2199                Point::new(1, 2)..Point::new(1, 2),
 2200                Point::new(2, 2)..Point::new(2, 2),
 2201            ],
 2202        );
 2203
 2204        editor.newline(&Newline, cx);
 2205        assert_eq!(
 2206            editor.text(cx),
 2207            "
 2208                a
 2209                b(
 2210                )
 2211                c(
 2212                )
 2213            "
 2214            .unindent()
 2215        );
 2216
 2217        // The selections are moved after the inserted newlines
 2218        assert_eq!(
 2219            editor.selections.ranges(cx),
 2220            &[
 2221                Point::new(2, 0)..Point::new(2, 0),
 2222                Point::new(4, 0)..Point::new(4, 0),
 2223            ],
 2224        );
 2225    });
 2226}
 2227
 2228#[gpui::test]
 2229async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2230    init_test(cx, |settings| {
 2231        settings.defaults.tab_size = NonZeroU32::new(4)
 2232    });
 2233
 2234    let language = Arc::new(
 2235        Language::new(
 2236            LanguageConfig::default(),
 2237            Some(tree_sitter_rust::language()),
 2238        )
 2239        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2240        .unwrap(),
 2241    );
 2242
 2243    let mut cx = EditorTestContext::new(cx).await;
 2244    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2245    cx.set_state(indoc! {"
 2246        const a: ˇA = (
 2247 2248                «const_functionˇ»(ˇ),
 2249                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2250 2251        ˇ);ˇ
 2252    "});
 2253
 2254    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2255    cx.assert_editor_state(indoc! {"
 2256        ˇ
 2257        const a: A = (
 2258            ˇ
 2259            (
 2260                ˇ
 2261                ˇ
 2262                const_function(),
 2263                ˇ
 2264                ˇ
 2265                ˇ
 2266                ˇ
 2267                something_else,
 2268                ˇ
 2269            )
 2270            ˇ
 2271            ˇ
 2272        );
 2273    "});
 2274}
 2275
 2276#[gpui::test]
 2277async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2278    init_test(cx, |settings| {
 2279        settings.defaults.tab_size = NonZeroU32::new(4)
 2280    });
 2281
 2282    let language = Arc::new(
 2283        Language::new(
 2284            LanguageConfig::default(),
 2285            Some(tree_sitter_rust::language()),
 2286        )
 2287        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2288        .unwrap(),
 2289    );
 2290
 2291    let mut cx = EditorTestContext::new(cx).await;
 2292    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2293    cx.set_state(indoc! {"
 2294        const a: ˇA = (
 2295 2296                «const_functionˇ»(ˇ),
 2297                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2298 2299        ˇ);ˇ
 2300    "});
 2301
 2302    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2303    cx.assert_editor_state(indoc! {"
 2304        const a: A = (
 2305            ˇ
 2306            (
 2307                ˇ
 2308                const_function(),
 2309                ˇ
 2310                ˇ
 2311                something_else,
 2312                ˇ
 2313                ˇ
 2314                ˇ
 2315                ˇ
 2316            )
 2317            ˇ
 2318        );
 2319        ˇ
 2320        ˇ
 2321    "});
 2322}
 2323
 2324#[gpui::test]
 2325async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2326    init_test(cx, |settings| {
 2327        settings.defaults.tab_size = NonZeroU32::new(4)
 2328    });
 2329
 2330    let language = Arc::new(Language::new(
 2331        LanguageConfig {
 2332            line_comments: vec!["//".into()],
 2333            ..LanguageConfig::default()
 2334        },
 2335        None,
 2336    ));
 2337    {
 2338        let mut cx = EditorTestContext::new(cx).await;
 2339        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2340        cx.set_state(indoc! {"
 2341        // Fooˇ
 2342    "});
 2343
 2344        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2345        cx.assert_editor_state(indoc! {"
 2346        // Foo
 2347        //ˇ
 2348    "});
 2349        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2350        cx.set_state(indoc! {"
 2351        ˇ// Foo
 2352    "});
 2353        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2354        cx.assert_editor_state(indoc! {"
 2355
 2356        ˇ// Foo
 2357    "});
 2358    }
 2359    // Ensure that comment continuations can be disabled.
 2360    update_test_language_settings(cx, |settings| {
 2361        settings.defaults.extend_comment_on_newline = Some(false);
 2362    });
 2363    let mut cx = EditorTestContext::new(cx).await;
 2364    cx.set_state(indoc! {"
 2365        // Fooˇ
 2366    "});
 2367    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2368    cx.assert_editor_state(indoc! {"
 2369        // Foo
 2370        ˇ
 2371    "});
 2372}
 2373
 2374#[gpui::test]
 2375fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2376    init_test(cx, |_| {});
 2377
 2378    let editor = cx.add_window(|cx| {
 2379        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2380        let mut editor = build_editor(buffer.clone(), cx);
 2381        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2382        editor
 2383    });
 2384
 2385    _ = editor.update(cx, |editor, cx| {
 2386        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2387        editor.buffer.update(cx, |buffer, cx| {
 2388            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2389            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2390        });
 2391        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2392
 2393        editor.insert("Z", cx);
 2394        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2395
 2396        // The selections are moved after the inserted characters
 2397        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2398    });
 2399}
 2400
 2401#[gpui::test]
 2402async fn test_tab(cx: &mut gpui::TestAppContext) {
 2403    init_test(cx, |settings| {
 2404        settings.defaults.tab_size = NonZeroU32::new(3)
 2405    });
 2406
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state(indoc! {"
 2409        ˇabˇc
 2410        ˇ🏀ˇ🏀ˇefg
 2411 2412    "});
 2413    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2414    cx.assert_editor_state(indoc! {"
 2415           ˇab ˇc
 2416           ˇ🏀  ˇ🏀  ˇefg
 2417        d  ˇ
 2418    "});
 2419
 2420    cx.set_state(indoc! {"
 2421        a
 2422        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2423    "});
 2424    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2425    cx.assert_editor_state(indoc! {"
 2426        a
 2427           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2428    "});
 2429}
 2430
 2431#[gpui::test]
 2432async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2433    init_test(cx, |_| {});
 2434
 2435    let mut cx = EditorTestContext::new(cx).await;
 2436    let language = Arc::new(
 2437        Language::new(
 2438            LanguageConfig::default(),
 2439            Some(tree_sitter_rust::language()),
 2440        )
 2441        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2442        .unwrap(),
 2443    );
 2444    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2445
 2446    // cursors that are already at the suggested indent level insert
 2447    // a soft tab. cursors that are to the left of the suggested indent
 2448    // auto-indent their line.
 2449    cx.set_state(indoc! {"
 2450        ˇ
 2451        const a: B = (
 2452            c(
 2453                d(
 2454        ˇ
 2455                )
 2456        ˇ
 2457        ˇ    )
 2458        );
 2459    "});
 2460    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2461    cx.assert_editor_state(indoc! {"
 2462            ˇ
 2463        const a: B = (
 2464            c(
 2465                d(
 2466                    ˇ
 2467                )
 2468                ˇ
 2469            ˇ)
 2470        );
 2471    "});
 2472
 2473    // handle auto-indent when there are multiple cursors on the same line
 2474    cx.set_state(indoc! {"
 2475        const a: B = (
 2476            c(
 2477        ˇ    ˇ
 2478        ˇ    )
 2479        );
 2480    "});
 2481    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2482    cx.assert_editor_state(indoc! {"
 2483        const a: B = (
 2484            c(
 2485                ˇ
 2486            ˇ)
 2487        );
 2488    "});
 2489}
 2490
 2491#[gpui::test]
 2492async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2493    init_test(cx, |settings| {
 2494        settings.defaults.tab_size = NonZeroU32::new(4)
 2495    });
 2496
 2497    let language = Arc::new(
 2498        Language::new(
 2499            LanguageConfig::default(),
 2500            Some(tree_sitter_rust::language()),
 2501        )
 2502        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2503        .unwrap(),
 2504    );
 2505
 2506    let mut cx = EditorTestContext::new(cx).await;
 2507    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2508    cx.set_state(indoc! {"
 2509        fn a() {
 2510            if b {
 2511        \t ˇc
 2512            }
 2513        }
 2514    "});
 2515
 2516    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2517    cx.assert_editor_state(indoc! {"
 2518        fn a() {
 2519            if b {
 2520                ˇc
 2521            }
 2522        }
 2523    "});
 2524}
 2525
 2526#[gpui::test]
 2527async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2528    init_test(cx, |settings| {
 2529        settings.defaults.tab_size = NonZeroU32::new(4);
 2530    });
 2531
 2532    let mut cx = EditorTestContext::new(cx).await;
 2533
 2534    cx.set_state(indoc! {"
 2535          «oneˇ» «twoˇ»
 2536        three
 2537         four
 2538    "});
 2539    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2540    cx.assert_editor_state(indoc! {"
 2541            «oneˇ» «twoˇ»
 2542        three
 2543         four
 2544    "});
 2545
 2546    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2547    cx.assert_editor_state(indoc! {"
 2548        «oneˇ» «twoˇ»
 2549        three
 2550         four
 2551    "});
 2552
 2553    // select across line ending
 2554    cx.set_state(indoc! {"
 2555        one two
 2556        t«hree
 2557        ˇ» four
 2558    "});
 2559    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2560    cx.assert_editor_state(indoc! {"
 2561        one two
 2562            t«hree
 2563        ˇ» four
 2564    "});
 2565
 2566    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2567    cx.assert_editor_state(indoc! {"
 2568        one two
 2569        t«hree
 2570        ˇ» four
 2571    "});
 2572
 2573    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2574    cx.set_state(indoc! {"
 2575        one two
 2576        ˇthree
 2577            four
 2578    "});
 2579    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2580    cx.assert_editor_state(indoc! {"
 2581        one two
 2582            ˇthree
 2583            four
 2584    "});
 2585
 2586    cx.set_state(indoc! {"
 2587        one two
 2588        ˇ    three
 2589            four
 2590    "});
 2591    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2592    cx.assert_editor_state(indoc! {"
 2593        one two
 2594        ˇthree
 2595            four
 2596    "});
 2597}
 2598
 2599#[gpui::test]
 2600async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2601    init_test(cx, |settings| {
 2602        settings.defaults.hard_tabs = Some(true);
 2603    });
 2604
 2605    let mut cx = EditorTestContext::new(cx).await;
 2606
 2607    // select two ranges on one line
 2608    cx.set_state(indoc! {"
 2609        «oneˇ» «twoˇ»
 2610        three
 2611        four
 2612    "});
 2613    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2614    cx.assert_editor_state(indoc! {"
 2615        \t«oneˇ» «twoˇ»
 2616        three
 2617        four
 2618    "});
 2619    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2620    cx.assert_editor_state(indoc! {"
 2621        \t\t«oneˇ» «twoˇ»
 2622        three
 2623        four
 2624    "});
 2625    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2626    cx.assert_editor_state(indoc! {"
 2627        \t«oneˇ» «twoˇ»
 2628        three
 2629        four
 2630    "});
 2631    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2632    cx.assert_editor_state(indoc! {"
 2633        «oneˇ» «twoˇ»
 2634        three
 2635        four
 2636    "});
 2637
 2638    // select across a line ending
 2639    cx.set_state(indoc! {"
 2640        one two
 2641        t«hree
 2642        ˇ»four
 2643    "});
 2644    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2645    cx.assert_editor_state(indoc! {"
 2646        one two
 2647        \tt«hree
 2648        ˇ»four
 2649    "});
 2650    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2651    cx.assert_editor_state(indoc! {"
 2652        one two
 2653        \t\tt«hree
 2654        ˇ»four
 2655    "});
 2656    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2657    cx.assert_editor_state(indoc! {"
 2658        one two
 2659        \tt«hree
 2660        ˇ»four
 2661    "});
 2662    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2663    cx.assert_editor_state(indoc! {"
 2664        one two
 2665        t«hree
 2666        ˇ»four
 2667    "});
 2668
 2669    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2670    cx.set_state(indoc! {"
 2671        one two
 2672        ˇthree
 2673        four
 2674    "});
 2675    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2676    cx.assert_editor_state(indoc! {"
 2677        one two
 2678        ˇthree
 2679        four
 2680    "});
 2681    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        one two
 2684        \tˇthree
 2685        four
 2686    "});
 2687    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2688    cx.assert_editor_state(indoc! {"
 2689        one two
 2690        ˇthree
 2691        four
 2692    "});
 2693}
 2694
 2695#[gpui::test]
 2696fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2697    init_test(cx, |settings| {
 2698        settings.languages.extend([
 2699            (
 2700                "TOML".into(),
 2701                LanguageSettingsContent {
 2702                    tab_size: NonZeroU32::new(2),
 2703                    ..Default::default()
 2704                },
 2705            ),
 2706            (
 2707                "Rust".into(),
 2708                LanguageSettingsContent {
 2709                    tab_size: NonZeroU32::new(4),
 2710                    ..Default::default()
 2711                },
 2712            ),
 2713        ]);
 2714    });
 2715
 2716    let toml_language = Arc::new(Language::new(
 2717        LanguageConfig {
 2718            name: "TOML".into(),
 2719            ..Default::default()
 2720        },
 2721        None,
 2722    ));
 2723    let rust_language = Arc::new(Language::new(
 2724        LanguageConfig {
 2725            name: "Rust".into(),
 2726            ..Default::default()
 2727        },
 2728        None,
 2729    ));
 2730
 2731    let toml_buffer =
 2732        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2733    let rust_buffer = cx.new_model(|cx| {
 2734        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2735    });
 2736    let multibuffer = cx.new_model(|cx| {
 2737        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 2738        multibuffer.push_excerpts(
 2739            toml_buffer.clone(),
 2740            [ExcerptRange {
 2741                context: Point::new(0, 0)..Point::new(2, 0),
 2742                primary: None,
 2743            }],
 2744            cx,
 2745        );
 2746        multibuffer.push_excerpts(
 2747            rust_buffer.clone(),
 2748            [ExcerptRange {
 2749                context: Point::new(0, 0)..Point::new(1, 0),
 2750                primary: None,
 2751            }],
 2752            cx,
 2753        );
 2754        multibuffer
 2755    });
 2756
 2757    cx.add_window(|cx| {
 2758        let mut editor = build_editor(multibuffer, cx);
 2759
 2760        assert_eq!(
 2761            editor.text(cx),
 2762            indoc! {"
 2763                a = 1
 2764                b = 2
 2765
 2766                const c: usize = 3;
 2767            "}
 2768        );
 2769
 2770        select_ranges(
 2771            &mut editor,
 2772            indoc! {"
 2773                «aˇ» = 1
 2774                b = 2
 2775
 2776                «const c:ˇ» usize = 3;
 2777            "},
 2778            cx,
 2779        );
 2780
 2781        editor.tab(&Tab, cx);
 2782        assert_text_with_selections(
 2783            &mut editor,
 2784            indoc! {"
 2785                  «aˇ» = 1
 2786                b = 2
 2787
 2788                    «const c:ˇ» usize = 3;
 2789            "},
 2790            cx,
 2791        );
 2792        editor.tab_prev(&TabPrev, cx);
 2793        assert_text_with_selections(
 2794            &mut editor,
 2795            indoc! {"
 2796                «aˇ» = 1
 2797                b = 2
 2798
 2799                «const c:ˇ» usize = 3;
 2800            "},
 2801            cx,
 2802        );
 2803
 2804        editor
 2805    });
 2806}
 2807
 2808#[gpui::test]
 2809async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2810    init_test(cx, |_| {});
 2811
 2812    let mut cx = EditorTestContext::new(cx).await;
 2813
 2814    // Basic backspace
 2815    cx.set_state(indoc! {"
 2816        onˇe two three
 2817        fou«rˇ» five six
 2818        seven «ˇeight nine
 2819        »ten
 2820    "});
 2821    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2822    cx.assert_editor_state(indoc! {"
 2823        oˇe two three
 2824        fouˇ five six
 2825        seven ˇten
 2826    "});
 2827
 2828    // Test backspace inside and around indents
 2829    cx.set_state(indoc! {"
 2830        zero
 2831            ˇone
 2832                ˇtwo
 2833            ˇ ˇ ˇ  three
 2834        ˇ  ˇ  four
 2835    "});
 2836    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2837    cx.assert_editor_state(indoc! {"
 2838        zero
 2839        ˇone
 2840            ˇtwo
 2841        ˇ  threeˇ  four
 2842    "});
 2843
 2844    // Test backspace with line_mode set to true
 2845    cx.update_editor(|e, _| e.selections.line_mode = true);
 2846    cx.set_state(indoc! {"
 2847        The ˇquick ˇbrown
 2848        fox jumps over
 2849        the lazy dog
 2850        ˇThe qu«ick bˇ»rown"});
 2851    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        ˇfox jumps over
 2854        the lazy dogˇ"});
 2855}
 2856
 2857#[gpui::test]
 2858async fn test_delete(cx: &mut gpui::TestAppContext) {
 2859    init_test(cx, |_| {});
 2860
 2861    let mut cx = EditorTestContext::new(cx).await;
 2862    cx.set_state(indoc! {"
 2863        onˇe two three
 2864        fou«rˇ» five six
 2865        seven «ˇeight nine
 2866        »ten
 2867    "});
 2868    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2869    cx.assert_editor_state(indoc! {"
 2870        onˇ two three
 2871        fouˇ five six
 2872        seven ˇten
 2873    "});
 2874
 2875    // Test backspace with line_mode set to true
 2876    cx.update_editor(|e, _| e.selections.line_mode = true);
 2877    cx.set_state(indoc! {"
 2878        The ˇquick ˇbrown
 2879        fox «ˇjum»ps over
 2880        the lazy dog
 2881        ˇThe qu«ick bˇ»rown"});
 2882    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2883    cx.assert_editor_state("ˇthe lazy dogˇ");
 2884}
 2885
 2886#[gpui::test]
 2887fn test_delete_line(cx: &mut TestAppContext) {
 2888    init_test(cx, |_| {});
 2889
 2890    let view = cx.add_window(|cx| {
 2891        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2892        build_editor(buffer, cx)
 2893    });
 2894    _ = view.update(cx, |view, cx| {
 2895        view.change_selections(None, cx, |s| {
 2896            s.select_display_ranges([
 2897                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2898                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2899                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2900            ])
 2901        });
 2902        view.delete_line(&DeleteLine, cx);
 2903        assert_eq!(view.display_text(cx), "ghi");
 2904        assert_eq!(
 2905            view.selections.display_ranges(cx),
 2906            vec![
 2907                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2908                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2909            ]
 2910        );
 2911    });
 2912
 2913    let view = cx.add_window(|cx| {
 2914        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2915        build_editor(buffer, cx)
 2916    });
 2917    _ = view.update(cx, |view, cx| {
 2918        view.change_selections(None, cx, |s| {
 2919            s.select_display_ranges([
 2920                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 2921            ])
 2922        });
 2923        view.delete_line(&DeleteLine, cx);
 2924        assert_eq!(view.display_text(cx), "ghi\n");
 2925        assert_eq!(
 2926            view.selections.display_ranges(cx),
 2927            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 2928        );
 2929    });
 2930}
 2931
 2932#[gpui::test]
 2933fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 2934    init_test(cx, |_| {});
 2935
 2936    cx.add_window(|cx| {
 2937        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 2938        let mut editor = build_editor(buffer.clone(), cx);
 2939        let buffer = buffer.read(cx).as_singleton().unwrap();
 2940
 2941        assert_eq!(
 2942            editor.selections.ranges::<Point>(cx),
 2943            &[Point::new(0, 0)..Point::new(0, 0)]
 2944        );
 2945
 2946        // When on single line, replace newline at end by space
 2947        editor.join_lines(&JoinLines, cx);
 2948        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2949        assert_eq!(
 2950            editor.selections.ranges::<Point>(cx),
 2951            &[Point::new(0, 3)..Point::new(0, 3)]
 2952        );
 2953
 2954        // When multiple lines are selected, remove newlines that are spanned by the selection
 2955        editor.change_selections(None, cx, |s| {
 2956            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 2957        });
 2958        editor.join_lines(&JoinLines, cx);
 2959        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 2960        assert_eq!(
 2961            editor.selections.ranges::<Point>(cx),
 2962            &[Point::new(0, 11)..Point::new(0, 11)]
 2963        );
 2964
 2965        // Undo should be transactional
 2966        editor.undo(&Undo, cx);
 2967        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2968        assert_eq!(
 2969            editor.selections.ranges::<Point>(cx),
 2970            &[Point::new(0, 5)..Point::new(2, 2)]
 2971        );
 2972
 2973        // When joining an empty line don't insert a space
 2974        editor.change_selections(None, cx, |s| {
 2975            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 2976        });
 2977        editor.join_lines(&JoinLines, cx);
 2978        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 2979        assert_eq!(
 2980            editor.selections.ranges::<Point>(cx),
 2981            [Point::new(2, 3)..Point::new(2, 3)]
 2982        );
 2983
 2984        // We can remove trailing newlines
 2985        editor.join_lines(&JoinLines, cx);
 2986        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2987        assert_eq!(
 2988            editor.selections.ranges::<Point>(cx),
 2989            [Point::new(2, 3)..Point::new(2, 3)]
 2990        );
 2991
 2992        // We don't blow up on the last line
 2993        editor.join_lines(&JoinLines, cx);
 2994        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2995        assert_eq!(
 2996            editor.selections.ranges::<Point>(cx),
 2997            [Point::new(2, 3)..Point::new(2, 3)]
 2998        );
 2999
 3000        // reset to test indentation
 3001        editor.buffer.update(cx, |buffer, cx| {
 3002            buffer.edit(
 3003                [
 3004                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3005                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3006                ],
 3007                None,
 3008                cx,
 3009            )
 3010        });
 3011
 3012        // We remove any leading spaces
 3013        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3014        editor.change_selections(None, cx, |s| {
 3015            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3016        });
 3017        editor.join_lines(&JoinLines, cx);
 3018        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3019
 3020        // We don't insert a space for a line containing only spaces
 3021        editor.join_lines(&JoinLines, cx);
 3022        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3023
 3024        // We ignore any leading tabs
 3025        editor.join_lines(&JoinLines, cx);
 3026        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3027
 3028        editor
 3029    });
 3030}
 3031
 3032#[gpui::test]
 3033fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3034    init_test(cx, |_| {});
 3035
 3036    cx.add_window(|cx| {
 3037        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3038        let mut editor = build_editor(buffer.clone(), cx);
 3039        let buffer = buffer.read(cx).as_singleton().unwrap();
 3040
 3041        editor.change_selections(None, cx, |s| {
 3042            s.select_ranges([
 3043                Point::new(0, 2)..Point::new(1, 1),
 3044                Point::new(1, 2)..Point::new(1, 2),
 3045                Point::new(3, 1)..Point::new(3, 2),
 3046            ])
 3047        });
 3048
 3049        editor.join_lines(&JoinLines, cx);
 3050        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3051
 3052        assert_eq!(
 3053            editor.selections.ranges::<Point>(cx),
 3054            [
 3055                Point::new(0, 7)..Point::new(0, 7),
 3056                Point::new(1, 3)..Point::new(1, 3)
 3057            ]
 3058        );
 3059        editor
 3060    });
 3061}
 3062
 3063#[gpui::test]
 3064async fn test_join_lines_with_git_diff_base(
 3065    executor: BackgroundExecutor,
 3066    cx: &mut gpui::TestAppContext,
 3067) {
 3068    init_test(cx, |_| {});
 3069
 3070    let mut cx = EditorTestContext::new(cx).await;
 3071
 3072    let diff_base = r#"
 3073        Line 0
 3074        Line 1
 3075        Line 2
 3076        Line 3
 3077        "#
 3078    .unindent();
 3079
 3080    cx.set_state(
 3081        &r#"
 3082        ˇLine 0
 3083        Line 1
 3084        Line 2
 3085        Line 3
 3086        "#
 3087        .unindent(),
 3088    );
 3089
 3090    cx.set_diff_base(Some(&diff_base));
 3091    executor.run_until_parked();
 3092
 3093    // Join lines
 3094    cx.update_editor(|editor, cx| {
 3095        editor.join_lines(&JoinLines, cx);
 3096    });
 3097    executor.run_until_parked();
 3098
 3099    cx.assert_editor_state(
 3100        &r#"
 3101        Line 0ˇ Line 1
 3102        Line 2
 3103        Line 3
 3104        "#
 3105        .unindent(),
 3106    );
 3107    // Join again
 3108    cx.update_editor(|editor, cx| {
 3109        editor.join_lines(&JoinLines, cx);
 3110    });
 3111    executor.run_until_parked();
 3112
 3113    cx.assert_editor_state(
 3114        &r#"
 3115        Line 0 Line 1ˇ Line 2
 3116        Line 3
 3117        "#
 3118        .unindent(),
 3119    );
 3120}
 3121
 3122#[gpui::test]
 3123async fn test_custom_newlines_cause_no_false_positive_diffs(
 3124    executor: BackgroundExecutor,
 3125    cx: &mut gpui::TestAppContext,
 3126) {
 3127    init_test(cx, |_| {});
 3128    let mut cx = EditorTestContext::new(cx).await;
 3129    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3130    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3131    executor.run_until_parked();
 3132
 3133    cx.update_editor(|editor, cx| {
 3134        assert_eq!(
 3135            editor
 3136                .buffer()
 3137                .read(cx)
 3138                .snapshot(cx)
 3139                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3140                .collect::<Vec<_>>(),
 3141            Vec::new(),
 3142            "Should not have any diffs for files with custom newlines"
 3143        );
 3144    });
 3145}
 3146
 3147#[gpui::test]
 3148async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3149    init_test(cx, |_| {});
 3150
 3151    let mut cx = EditorTestContext::new(cx).await;
 3152
 3153    // Test sort_lines_case_insensitive()
 3154    cx.set_state(indoc! {"
 3155        «z
 3156        y
 3157        x
 3158        Z
 3159        Y
 3160        Xˇ»
 3161    "});
 3162    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3163    cx.assert_editor_state(indoc! {"
 3164        «x
 3165        X
 3166        y
 3167        Y
 3168        z
 3169        Zˇ»
 3170    "});
 3171
 3172    // Test reverse_lines()
 3173    cx.set_state(indoc! {"
 3174        «5
 3175        4
 3176        3
 3177        2
 3178        1ˇ»
 3179    "});
 3180    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3181    cx.assert_editor_state(indoc! {"
 3182        «1
 3183        2
 3184        3
 3185        4
 3186        5ˇ»
 3187    "});
 3188
 3189    // Skip testing shuffle_line()
 3190
 3191    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3192    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3193
 3194    // Don't manipulate when cursor is on single line, but expand the selection
 3195    cx.set_state(indoc! {"
 3196        ddˇdd
 3197        ccc
 3198        bb
 3199        a
 3200    "});
 3201    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3202    cx.assert_editor_state(indoc! {"
 3203        «ddddˇ»
 3204        ccc
 3205        bb
 3206        a
 3207    "});
 3208
 3209    // Basic manipulate case
 3210    // Start selection moves to column 0
 3211    // End of selection shrinks to fit shorter line
 3212    cx.set_state(indoc! {"
 3213        dd«d
 3214        ccc
 3215        bb
 3216        aaaaaˇ»
 3217    "});
 3218    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3219    cx.assert_editor_state(indoc! {"
 3220        «aaaaa
 3221        bb
 3222        ccc
 3223        dddˇ»
 3224    "});
 3225
 3226    // Manipulate case with newlines
 3227    cx.set_state(indoc! {"
 3228        dd«d
 3229        ccc
 3230
 3231        bb
 3232        aaaaa
 3233
 3234        ˇ»
 3235    "});
 3236    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3237    cx.assert_editor_state(indoc! {"
 3238        «
 3239
 3240        aaaaa
 3241        bb
 3242        ccc
 3243        dddˇ»
 3244
 3245    "});
 3246
 3247    // Adding new line
 3248    cx.set_state(indoc! {"
 3249        aa«a
 3250        bbˇ»b
 3251    "});
 3252    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3253    cx.assert_editor_state(indoc! {"
 3254        «aaa
 3255        bbb
 3256        added_lineˇ»
 3257    "});
 3258
 3259    // Removing line
 3260    cx.set_state(indoc! {"
 3261        aa«a
 3262        bbbˇ»
 3263    "});
 3264    cx.update_editor(|e, cx| {
 3265        e.manipulate_lines(cx, |lines| {
 3266            lines.pop();
 3267        })
 3268    });
 3269    cx.assert_editor_state(indoc! {"
 3270        «aaaˇ»
 3271    "});
 3272
 3273    // Removing all lines
 3274    cx.set_state(indoc! {"
 3275        aa«a
 3276        bbbˇ»
 3277    "});
 3278    cx.update_editor(|e, cx| {
 3279        e.manipulate_lines(cx, |lines| {
 3280            lines.drain(..);
 3281        })
 3282    });
 3283    cx.assert_editor_state(indoc! {"
 3284        ˇ
 3285    "});
 3286}
 3287
 3288#[gpui::test]
 3289async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3290    init_test(cx, |_| {});
 3291
 3292    let mut cx = EditorTestContext::new(cx).await;
 3293
 3294    // Consider continuous selection as single selection
 3295    cx.set_state(indoc! {"
 3296        Aaa«aa
 3297        cˇ»c«c
 3298        bb
 3299        aaaˇ»aa
 3300    "});
 3301    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3302    cx.assert_editor_state(indoc! {"
 3303        «Aaaaa
 3304        ccc
 3305        bb
 3306        aaaaaˇ»
 3307    "});
 3308
 3309    cx.set_state(indoc! {"
 3310        Aaa«aa
 3311        cˇ»c«c
 3312        bb
 3313        aaaˇ»aa
 3314    "});
 3315    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3316    cx.assert_editor_state(indoc! {"
 3317        «Aaaaa
 3318        ccc
 3319        bbˇ»
 3320    "});
 3321
 3322    // Consider non continuous selection as distinct dedup operations
 3323    cx.set_state(indoc! {"
 3324        «aaaaa
 3325        bb
 3326        aaaaa
 3327        aaaaaˇ»
 3328
 3329        aaa«aaˇ»
 3330    "});
 3331    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3332    cx.assert_editor_state(indoc! {"
 3333        «aaaaa
 3334        bbˇ»
 3335
 3336        «aaaaaˇ»
 3337    "});
 3338}
 3339
 3340#[gpui::test]
 3341async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3342    init_test(cx, |_| {});
 3343
 3344    let mut cx = EditorTestContext::new(cx).await;
 3345
 3346    cx.set_state(indoc! {"
 3347        «Aaa
 3348        aAa
 3349        Aaaˇ»
 3350    "});
 3351    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3352    cx.assert_editor_state(indoc! {"
 3353        «Aaa
 3354        aAaˇ»
 3355    "});
 3356
 3357    cx.set_state(indoc! {"
 3358        «Aaa
 3359        aAa
 3360        aaAˇ»
 3361    "});
 3362    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3363    cx.assert_editor_state(indoc! {"
 3364        «Aaaˇ»
 3365    "});
 3366}
 3367
 3368#[gpui::test]
 3369async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3370    init_test(cx, |_| {});
 3371
 3372    let mut cx = EditorTestContext::new(cx).await;
 3373
 3374    // Manipulate with multiple selections on a single line
 3375    cx.set_state(indoc! {"
 3376        dd«dd
 3377        cˇ»c«c
 3378        bb
 3379        aaaˇ»aa
 3380    "});
 3381    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3382    cx.assert_editor_state(indoc! {"
 3383        «aaaaa
 3384        bb
 3385        ccc
 3386        ddddˇ»
 3387    "});
 3388
 3389    // Manipulate with multiple disjoin selections
 3390    cx.set_state(indoc! {"
 3391 3392        4
 3393        3
 3394        2
 3395        1ˇ»
 3396
 3397        dd«dd
 3398        ccc
 3399        bb
 3400        aaaˇ»aa
 3401    "});
 3402    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3403    cx.assert_editor_state(indoc! {"
 3404        «1
 3405        2
 3406        3
 3407        4
 3408        5ˇ»
 3409
 3410        «aaaaa
 3411        bb
 3412        ccc
 3413        ddddˇ»
 3414    "});
 3415
 3416    // Adding lines on each selection
 3417    cx.set_state(indoc! {"
 3418 3419        1ˇ»
 3420
 3421        bb«bb
 3422        aaaˇ»aa
 3423    "});
 3424    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3425    cx.assert_editor_state(indoc! {"
 3426        «2
 3427        1
 3428        added lineˇ»
 3429
 3430        «bbbb
 3431        aaaaa
 3432        added lineˇ»
 3433    "});
 3434
 3435    // Removing lines on each selection
 3436    cx.set_state(indoc! {"
 3437 3438        1ˇ»
 3439
 3440        bb«bb
 3441        aaaˇ»aa
 3442    "});
 3443    cx.update_editor(|e, cx| {
 3444        e.manipulate_lines(cx, |lines| {
 3445            lines.pop();
 3446        })
 3447    });
 3448    cx.assert_editor_state(indoc! {"
 3449        «2ˇ»
 3450
 3451        «bbbbˇ»
 3452    "});
 3453}
 3454
 3455#[gpui::test]
 3456async fn test_manipulate_text(cx: &mut TestAppContext) {
 3457    init_test(cx, |_| {});
 3458
 3459    let mut cx = EditorTestContext::new(cx).await;
 3460
 3461    // Test convert_to_upper_case()
 3462    cx.set_state(indoc! {"
 3463        «hello worldˇ»
 3464    "});
 3465    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3466    cx.assert_editor_state(indoc! {"
 3467        «HELLO WORLDˇ»
 3468    "});
 3469
 3470    // Test convert_to_lower_case()
 3471    cx.set_state(indoc! {"
 3472        «HELLO WORLDˇ»
 3473    "});
 3474    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3475    cx.assert_editor_state(indoc! {"
 3476        «hello worldˇ»
 3477    "});
 3478
 3479    // Test multiple line, single selection case
 3480    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3481    cx.set_state(indoc! {"
 3482        «The quick brown
 3483        fox jumps over
 3484        the lazy dogˇ»
 3485    "});
 3486    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3487    cx.assert_editor_state(indoc! {"
 3488        «The Quick Brown
 3489        Fox Jumps Over
 3490        The Lazy Dogˇ»
 3491    "});
 3492
 3493    // Test multiple line, single selection case
 3494    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3495    cx.set_state(indoc! {"
 3496        «The quick brown
 3497        fox jumps over
 3498        the lazy dogˇ»
 3499    "});
 3500    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3501    cx.assert_editor_state(indoc! {"
 3502        «TheQuickBrown
 3503        FoxJumpsOver
 3504        TheLazyDogˇ»
 3505    "});
 3506
 3507    // From here on out, test more complex cases of manipulate_text()
 3508
 3509    // Test no selection case - should affect words cursors are in
 3510    // Cursor at beginning, middle, and end of word
 3511    cx.set_state(indoc! {"
 3512        ˇhello big beauˇtiful worldˇ
 3513    "});
 3514    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3515    cx.assert_editor_state(indoc! {"
 3516        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3517    "});
 3518
 3519    // Test multiple selections on a single line and across multiple lines
 3520    cx.set_state(indoc! {"
 3521        «Theˇ» quick «brown
 3522        foxˇ» jumps «overˇ»
 3523        the «lazyˇ» dog
 3524    "});
 3525    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3526    cx.assert_editor_state(indoc! {"
 3527        «THEˇ» quick «BROWN
 3528        FOXˇ» jumps «OVERˇ»
 3529        the «LAZYˇ» dog
 3530    "});
 3531
 3532    // Test case where text length grows
 3533    cx.set_state(indoc! {"
 3534        «tschüߡ»
 3535    "});
 3536    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3537    cx.assert_editor_state(indoc! {"
 3538        «TSCHÜSSˇ»
 3539    "});
 3540
 3541    // Test to make sure we don't crash when text shrinks
 3542    cx.set_state(indoc! {"
 3543        aaa_bbbˇ
 3544    "});
 3545    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3546    cx.assert_editor_state(indoc! {"
 3547        «aaaBbbˇ»
 3548    "});
 3549
 3550    // Test to make sure we all aware of the fact that each word can grow and shrink
 3551    // Final selections should be aware of this fact
 3552    cx.set_state(indoc! {"
 3553        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3554    "});
 3555    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3556    cx.assert_editor_state(indoc! {"
 3557        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3558    "});
 3559
 3560    cx.set_state(indoc! {"
 3561        «hElLo, WoRld!ˇ»
 3562    "});
 3563    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3564    cx.assert_editor_state(indoc! {"
 3565        «HeLlO, wOrLD!ˇ»
 3566    "});
 3567}
 3568
 3569#[gpui::test]
 3570fn test_duplicate_line(cx: &mut TestAppContext) {
 3571    init_test(cx, |_| {});
 3572
 3573    let view = cx.add_window(|cx| {
 3574        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3575        build_editor(buffer, cx)
 3576    });
 3577    _ = view.update(cx, |view, cx| {
 3578        view.change_selections(None, cx, |s| {
 3579            s.select_display_ranges([
 3580                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3581                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3582                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3583                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3584            ])
 3585        });
 3586        view.duplicate_line_down(&DuplicateLineDown, cx);
 3587        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3588        assert_eq!(
 3589            view.selections.display_ranges(cx),
 3590            vec![
 3591                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3592                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3593                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3594                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3595            ]
 3596        );
 3597    });
 3598
 3599    let view = cx.add_window(|cx| {
 3600        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3601        build_editor(buffer, cx)
 3602    });
 3603    _ = view.update(cx, |view, cx| {
 3604        view.change_selections(None, cx, |s| {
 3605            s.select_display_ranges([
 3606                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3607                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3608            ])
 3609        });
 3610        view.duplicate_line_down(&DuplicateLineDown, cx);
 3611        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3612        assert_eq!(
 3613            view.selections.display_ranges(cx),
 3614            vec![
 3615                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3616                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3617            ]
 3618        );
 3619    });
 3620
 3621    // With `move_upwards` the selections stay in place, except for
 3622    // the lines inserted above them
 3623    let view = cx.add_window(|cx| {
 3624        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3625        build_editor(buffer, cx)
 3626    });
 3627    _ = view.update(cx, |view, cx| {
 3628        view.change_selections(None, cx, |s| {
 3629            s.select_display_ranges([
 3630                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3631                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3632                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3633                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3634            ])
 3635        });
 3636        view.duplicate_line_up(&DuplicateLineUp, cx);
 3637        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3638        assert_eq!(
 3639            view.selections.display_ranges(cx),
 3640            vec![
 3641                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3642                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3643                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3644                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3645            ]
 3646        );
 3647    });
 3648
 3649    let view = cx.add_window(|cx| {
 3650        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3651        build_editor(buffer, cx)
 3652    });
 3653    _ = view.update(cx, |view, cx| {
 3654        view.change_selections(None, cx, |s| {
 3655            s.select_display_ranges([
 3656                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3657                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3658            ])
 3659        });
 3660        view.duplicate_line_up(&DuplicateLineUp, cx);
 3661        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3662        assert_eq!(
 3663            view.selections.display_ranges(cx),
 3664            vec![
 3665                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3666                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3667            ]
 3668        );
 3669    });
 3670}
 3671
 3672#[gpui::test]
 3673fn test_move_line_up_down(cx: &mut TestAppContext) {
 3674    init_test(cx, |_| {});
 3675
 3676    let view = cx.add_window(|cx| {
 3677        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3678        build_editor(buffer, cx)
 3679    });
 3680    _ = view.update(cx, |view, cx| {
 3681        view.fold_ranges(
 3682            vec![
 3683                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3684                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3685                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3686            ],
 3687            true,
 3688            cx,
 3689        );
 3690        view.change_selections(None, cx, |s| {
 3691            s.select_display_ranges([
 3692                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3693                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3694                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3695                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3696            ])
 3697        });
 3698        assert_eq!(
 3699            view.display_text(cx),
 3700            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3701        );
 3702
 3703        view.move_line_up(&MoveLineUp, cx);
 3704        assert_eq!(
 3705            view.display_text(cx),
 3706            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3707        );
 3708        assert_eq!(
 3709            view.selections.display_ranges(cx),
 3710            vec![
 3711                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3712                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3713                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3714                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3715            ]
 3716        );
 3717    });
 3718
 3719    _ = view.update(cx, |view, cx| {
 3720        view.move_line_down(&MoveLineDown, cx);
 3721        assert_eq!(
 3722            view.display_text(cx),
 3723            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3724        );
 3725        assert_eq!(
 3726            view.selections.display_ranges(cx),
 3727            vec![
 3728                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3729                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3730                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3731                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3732            ]
 3733        );
 3734    });
 3735
 3736    _ = view.update(cx, |view, cx| {
 3737        view.move_line_down(&MoveLineDown, cx);
 3738        assert_eq!(
 3739            view.display_text(cx),
 3740            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3741        );
 3742        assert_eq!(
 3743            view.selections.display_ranges(cx),
 3744            vec![
 3745                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3746                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3747                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3748                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3749            ]
 3750        );
 3751    });
 3752
 3753    _ = view.update(cx, |view, cx| {
 3754        view.move_line_up(&MoveLineUp, cx);
 3755        assert_eq!(
 3756            view.display_text(cx),
 3757            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3758        );
 3759        assert_eq!(
 3760            view.selections.display_ranges(cx),
 3761            vec![
 3762                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3763                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3764                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3765                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3766            ]
 3767        );
 3768    });
 3769}
 3770
 3771#[gpui::test]
 3772fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3773    init_test(cx, |_| {});
 3774
 3775    let editor = cx.add_window(|cx| {
 3776        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3777        build_editor(buffer, cx)
 3778    });
 3779    _ = editor.update(cx, |editor, cx| {
 3780        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3781        editor.insert_blocks(
 3782            [BlockProperties {
 3783                style: BlockStyle::Fixed,
 3784                position: snapshot.anchor_after(Point::new(2, 0)),
 3785                disposition: BlockDisposition::Below,
 3786                height: 1,
 3787                render: Box::new(|_| div().into_any()),
 3788                priority: 0,
 3789            }],
 3790            Some(Autoscroll::fit()),
 3791            cx,
 3792        );
 3793        editor.change_selections(None, cx, |s| {
 3794            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3795        });
 3796        editor.move_line_down(&MoveLineDown, cx);
 3797    });
 3798}
 3799
 3800#[gpui::test]
 3801fn test_transpose(cx: &mut TestAppContext) {
 3802    init_test(cx, |_| {});
 3803
 3804    _ = cx.add_window(|cx| {
 3805        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3806        editor.set_style(EditorStyle::default(), cx);
 3807        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3808        editor.transpose(&Default::default(), cx);
 3809        assert_eq!(editor.text(cx), "bac");
 3810        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3811
 3812        editor.transpose(&Default::default(), cx);
 3813        assert_eq!(editor.text(cx), "bca");
 3814        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3815
 3816        editor.transpose(&Default::default(), cx);
 3817        assert_eq!(editor.text(cx), "bac");
 3818        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3819
 3820        editor
 3821    });
 3822
 3823    _ = cx.add_window(|cx| {
 3824        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3825        editor.set_style(EditorStyle::default(), cx);
 3826        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3827        editor.transpose(&Default::default(), cx);
 3828        assert_eq!(editor.text(cx), "acb\nde");
 3829        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3830
 3831        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3832        editor.transpose(&Default::default(), cx);
 3833        assert_eq!(editor.text(cx), "acbd\ne");
 3834        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3835
 3836        editor.transpose(&Default::default(), cx);
 3837        assert_eq!(editor.text(cx), "acbde\n");
 3838        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3839
 3840        editor.transpose(&Default::default(), cx);
 3841        assert_eq!(editor.text(cx), "acbd\ne");
 3842        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3843
 3844        editor
 3845    });
 3846
 3847    _ = cx.add_window(|cx| {
 3848        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3849        editor.set_style(EditorStyle::default(), cx);
 3850        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3851        editor.transpose(&Default::default(), cx);
 3852        assert_eq!(editor.text(cx), "bacd\ne");
 3853        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3854
 3855        editor.transpose(&Default::default(), cx);
 3856        assert_eq!(editor.text(cx), "bcade\n");
 3857        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3858
 3859        editor.transpose(&Default::default(), cx);
 3860        assert_eq!(editor.text(cx), "bcda\ne");
 3861        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3862
 3863        editor.transpose(&Default::default(), cx);
 3864        assert_eq!(editor.text(cx), "bcade\n");
 3865        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3866
 3867        editor.transpose(&Default::default(), cx);
 3868        assert_eq!(editor.text(cx), "bcaed\n");
 3869        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3870
 3871        editor
 3872    });
 3873
 3874    _ = cx.add_window(|cx| {
 3875        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3876        editor.set_style(EditorStyle::default(), cx);
 3877        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3878        editor.transpose(&Default::default(), cx);
 3879        assert_eq!(editor.text(cx), "🏀🍐✋");
 3880        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3881
 3882        editor.transpose(&Default::default(), cx);
 3883        assert_eq!(editor.text(cx), "🏀✋🍐");
 3884        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3885
 3886        editor.transpose(&Default::default(), cx);
 3887        assert_eq!(editor.text(cx), "🏀🍐✋");
 3888        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3889
 3890        editor
 3891    });
 3892}
 3893
 3894#[gpui::test]
 3895async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 3896    init_test(cx, |_| {});
 3897
 3898    let mut cx = EditorTestContext::new(cx).await;
 3899
 3900    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 3901    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3902    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 3903
 3904    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 3905    cx.set_state("two ˇfour ˇsix ˇ");
 3906    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3907    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 3908
 3909    // Paste again but with only two cursors. Since the number of cursors doesn't
 3910    // match the number of slices in the clipboard, the entire clipboard text
 3911    // is pasted at each cursor.
 3912    cx.set_state("ˇtwo one✅ four three six five ˇ");
 3913    cx.update_editor(|e, cx| {
 3914        e.handle_input("( ", cx);
 3915        e.paste(&Paste, cx);
 3916        e.handle_input(") ", cx);
 3917    });
 3918    cx.assert_editor_state(
 3919        &([
 3920            "( one✅ ",
 3921            "three ",
 3922            "five ) ˇtwo one✅ four three six five ( one✅ ",
 3923            "three ",
 3924            "five ) ˇ",
 3925        ]
 3926        .join("\n")),
 3927    );
 3928
 3929    // Cut with three selections, one of which is full-line.
 3930    cx.set_state(indoc! {"
 3931        1«2ˇ»3
 3932        4ˇ567
 3933        «8ˇ»9"});
 3934    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3935    cx.assert_editor_state(indoc! {"
 3936        1ˇ3
 3937        ˇ9"});
 3938
 3939    // Paste with three selections, noticing how the copied selection that was full-line
 3940    // gets inserted before the second cursor.
 3941    cx.set_state(indoc! {"
 3942        1ˇ3
 3943 3944        «oˇ»ne"});
 3945    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3946    cx.assert_editor_state(indoc! {"
 3947        12ˇ3
 3948        4567
 3949 3950        8ˇne"});
 3951
 3952    // Copy with a single cursor only, which writes the whole line into the clipboard.
 3953    cx.set_state(indoc! {"
 3954        The quick brown
 3955        fox juˇmps over
 3956        the lazy dog"});
 3957    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 3958    assert_eq!(
 3959        cx.read_from_clipboard()
 3960            .and_then(|item| item.text().as_deref().map(str::to_string)),
 3961        Some("fox jumps over\n".to_string())
 3962    );
 3963
 3964    // Paste with three selections, noticing how the copied full-line selection is inserted
 3965    // before the empty selections but replaces the selection that is non-empty.
 3966    cx.set_state(indoc! {"
 3967        Tˇhe quick brown
 3968        «foˇ»x jumps over
 3969        tˇhe lazy dog"});
 3970    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3971    cx.assert_editor_state(indoc! {"
 3972        fox jumps over
 3973        Tˇhe quick brown
 3974        fox jumps over
 3975        ˇx jumps over
 3976        fox jumps over
 3977        tˇhe lazy dog"});
 3978}
 3979
 3980#[gpui::test]
 3981async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 3982    init_test(cx, |_| {});
 3983
 3984    let mut cx = EditorTestContext::new(cx).await;
 3985    let language = Arc::new(Language::new(
 3986        LanguageConfig::default(),
 3987        Some(tree_sitter_rust::language()),
 3988    ));
 3989    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3990
 3991    // Cut an indented block, without the leading whitespace.
 3992    cx.set_state(indoc! {"
 3993        const a: B = (
 3994            c(),
 3995            «d(
 3996                e,
 3997                f
 3998            )ˇ»
 3999        );
 4000    "});
 4001    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4002    cx.assert_editor_state(indoc! {"
 4003        const a: B = (
 4004            c(),
 4005            ˇ
 4006        );
 4007    "});
 4008
 4009    // Paste it at the same position.
 4010    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4011    cx.assert_editor_state(indoc! {"
 4012        const a: B = (
 4013            c(),
 4014            d(
 4015                e,
 4016                f
 4017 4018        );
 4019    "});
 4020
 4021    // Paste it at a line with a lower indent level.
 4022    cx.set_state(indoc! {"
 4023        ˇ
 4024        const a: B = (
 4025            c(),
 4026        );
 4027    "});
 4028    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4029    cx.assert_editor_state(indoc! {"
 4030        d(
 4031            e,
 4032            f
 4033 4034        const a: B = (
 4035            c(),
 4036        );
 4037    "});
 4038
 4039    // Cut an indented block, with the leading whitespace.
 4040    cx.set_state(indoc! {"
 4041        const a: B = (
 4042            c(),
 4043        «    d(
 4044                e,
 4045                f
 4046            )
 4047        ˇ»);
 4048    "});
 4049    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4050    cx.assert_editor_state(indoc! {"
 4051        const a: B = (
 4052            c(),
 4053        ˇ);
 4054    "});
 4055
 4056    // Paste it at the same position.
 4057    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4058    cx.assert_editor_state(indoc! {"
 4059        const a: B = (
 4060            c(),
 4061            d(
 4062                e,
 4063                f
 4064            )
 4065        ˇ);
 4066    "});
 4067
 4068    // Paste it at a line with a higher indent level.
 4069    cx.set_state(indoc! {"
 4070        const a: B = (
 4071            c(),
 4072            d(
 4073                e,
 4074 4075            )
 4076        );
 4077    "});
 4078    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4079    cx.assert_editor_state(indoc! {"
 4080        const a: B = (
 4081            c(),
 4082            d(
 4083                e,
 4084                f    d(
 4085                    e,
 4086                    f
 4087                )
 4088        ˇ
 4089            )
 4090        );
 4091    "});
 4092}
 4093
 4094#[gpui::test]
 4095fn test_select_all(cx: &mut TestAppContext) {
 4096    init_test(cx, |_| {});
 4097
 4098    let view = cx.add_window(|cx| {
 4099        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4100        build_editor(buffer, cx)
 4101    });
 4102    _ = view.update(cx, |view, cx| {
 4103        view.select_all(&SelectAll, cx);
 4104        assert_eq!(
 4105            view.selections.display_ranges(cx),
 4106            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4107        );
 4108    });
 4109}
 4110
 4111#[gpui::test]
 4112fn test_select_line(cx: &mut TestAppContext) {
 4113    init_test(cx, |_| {});
 4114
 4115    let view = cx.add_window(|cx| {
 4116        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4117        build_editor(buffer, cx)
 4118    });
 4119    _ = view.update(cx, |view, cx| {
 4120        view.change_selections(None, cx, |s| {
 4121            s.select_display_ranges([
 4122                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4123                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4124                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4125                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4126            ])
 4127        });
 4128        view.select_line(&SelectLine, cx);
 4129        assert_eq!(
 4130            view.selections.display_ranges(cx),
 4131            vec![
 4132                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4133                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4134            ]
 4135        );
 4136    });
 4137
 4138    _ = view.update(cx, |view, cx| {
 4139        view.select_line(&SelectLine, cx);
 4140        assert_eq!(
 4141            view.selections.display_ranges(cx),
 4142            vec![
 4143                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4144                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4145            ]
 4146        );
 4147    });
 4148
 4149    _ = view.update(cx, |view, cx| {
 4150        view.select_line(&SelectLine, cx);
 4151        assert_eq!(
 4152            view.selections.display_ranges(cx),
 4153            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4154        );
 4155    });
 4156}
 4157
 4158#[gpui::test]
 4159fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4160    init_test(cx, |_| {});
 4161
 4162    let view = cx.add_window(|cx| {
 4163        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4164        build_editor(buffer, cx)
 4165    });
 4166    _ = view.update(cx, |view, cx| {
 4167        view.fold_ranges(
 4168            vec![
 4169                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4170                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4171                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4172            ],
 4173            true,
 4174            cx,
 4175        );
 4176        view.change_selections(None, cx, |s| {
 4177            s.select_display_ranges([
 4178                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4179                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4180                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4181                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4182            ])
 4183        });
 4184        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4185    });
 4186
 4187    _ = view.update(cx, |view, cx| {
 4188        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4189        assert_eq!(
 4190            view.display_text(cx),
 4191            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4192        );
 4193        assert_eq!(
 4194            view.selections.display_ranges(cx),
 4195            [
 4196                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4197                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4198                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4199                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4200            ]
 4201        );
 4202    });
 4203
 4204    _ = view.update(cx, |view, cx| {
 4205        view.change_selections(None, cx, |s| {
 4206            s.select_display_ranges([
 4207                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4208            ])
 4209        });
 4210        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4211        assert_eq!(
 4212            view.display_text(cx),
 4213            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4214        );
 4215        assert_eq!(
 4216            view.selections.display_ranges(cx),
 4217            [
 4218                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4219                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4220                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4221                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4222                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4223                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4224                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4225                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4226            ]
 4227        );
 4228    });
 4229}
 4230
 4231#[gpui::test]
 4232async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4233    init_test(cx, |_| {});
 4234
 4235    let mut cx = EditorTestContext::new(cx).await;
 4236
 4237    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4238    cx.set_state(indoc!(
 4239        r#"abc
 4240           defˇghi
 4241
 4242           jk
 4243           nlmo
 4244           "#
 4245    ));
 4246
 4247    cx.update_editor(|editor, cx| {
 4248        editor.add_selection_above(&Default::default(), cx);
 4249    });
 4250
 4251    cx.assert_editor_state(indoc!(
 4252        r#"abcˇ
 4253           defˇghi
 4254
 4255           jk
 4256           nlmo
 4257           "#
 4258    ));
 4259
 4260    cx.update_editor(|editor, cx| {
 4261        editor.add_selection_above(&Default::default(), cx);
 4262    });
 4263
 4264    cx.assert_editor_state(indoc!(
 4265        r#"abcˇ
 4266            defˇghi
 4267
 4268            jk
 4269            nlmo
 4270            "#
 4271    ));
 4272
 4273    cx.update_editor(|view, cx| {
 4274        view.add_selection_below(&Default::default(), cx);
 4275    });
 4276
 4277    cx.assert_editor_state(indoc!(
 4278        r#"abc
 4279           defˇghi
 4280
 4281           jk
 4282           nlmo
 4283           "#
 4284    ));
 4285
 4286    cx.update_editor(|view, cx| {
 4287        view.undo_selection(&Default::default(), cx);
 4288    });
 4289
 4290    cx.assert_editor_state(indoc!(
 4291        r#"abcˇ
 4292           defˇghi
 4293
 4294           jk
 4295           nlmo
 4296           "#
 4297    ));
 4298
 4299    cx.update_editor(|view, cx| {
 4300        view.redo_selection(&Default::default(), cx);
 4301    });
 4302
 4303    cx.assert_editor_state(indoc!(
 4304        r#"abc
 4305           defˇghi
 4306
 4307           jk
 4308           nlmo
 4309           "#
 4310    ));
 4311
 4312    cx.update_editor(|view, cx| {
 4313        view.add_selection_below(&Default::default(), cx);
 4314    });
 4315
 4316    cx.assert_editor_state(indoc!(
 4317        r#"abc
 4318           defˇghi
 4319
 4320           jk
 4321           nlmˇo
 4322           "#
 4323    ));
 4324
 4325    cx.update_editor(|view, cx| {
 4326        view.add_selection_below(&Default::default(), cx);
 4327    });
 4328
 4329    cx.assert_editor_state(indoc!(
 4330        r#"abc
 4331           defˇghi
 4332
 4333           jk
 4334           nlmˇo
 4335           "#
 4336    ));
 4337
 4338    // change selections
 4339    cx.set_state(indoc!(
 4340        r#"abc
 4341           def«ˇg»hi
 4342
 4343           jk
 4344           nlmo
 4345           "#
 4346    ));
 4347
 4348    cx.update_editor(|view, cx| {
 4349        view.add_selection_below(&Default::default(), cx);
 4350    });
 4351
 4352    cx.assert_editor_state(indoc!(
 4353        r#"abc
 4354           def«ˇg»hi
 4355
 4356           jk
 4357           nlm«ˇo»
 4358           "#
 4359    ));
 4360
 4361    cx.update_editor(|view, cx| {
 4362        view.add_selection_below(&Default::default(), cx);
 4363    });
 4364
 4365    cx.assert_editor_state(indoc!(
 4366        r#"abc
 4367           def«ˇg»hi
 4368
 4369           jk
 4370           nlm«ˇo»
 4371           "#
 4372    ));
 4373
 4374    cx.update_editor(|view, cx| {
 4375        view.add_selection_above(&Default::default(), cx);
 4376    });
 4377
 4378    cx.assert_editor_state(indoc!(
 4379        r#"abc
 4380           def«ˇg»hi
 4381
 4382           jk
 4383           nlmo
 4384           "#
 4385    ));
 4386
 4387    cx.update_editor(|view, cx| {
 4388        view.add_selection_above(&Default::default(), cx);
 4389    });
 4390
 4391    cx.assert_editor_state(indoc!(
 4392        r#"abc
 4393           def«ˇg»hi
 4394
 4395           jk
 4396           nlmo
 4397           "#
 4398    ));
 4399
 4400    // Change selections again
 4401    cx.set_state(indoc!(
 4402        r#"a«bc
 4403           defgˇ»hi
 4404
 4405           jk
 4406           nlmo
 4407           "#
 4408    ));
 4409
 4410    cx.update_editor(|view, cx| {
 4411        view.add_selection_below(&Default::default(), cx);
 4412    });
 4413
 4414    cx.assert_editor_state(indoc!(
 4415        r#"a«bcˇ»
 4416           d«efgˇ»hi
 4417
 4418           j«kˇ»
 4419           nlmo
 4420           "#
 4421    ));
 4422
 4423    cx.update_editor(|view, cx| {
 4424        view.add_selection_below(&Default::default(), cx);
 4425    });
 4426    cx.assert_editor_state(indoc!(
 4427        r#"a«bcˇ»
 4428           d«efgˇ»hi
 4429
 4430           j«kˇ»
 4431           n«lmoˇ»
 4432           "#
 4433    ));
 4434    cx.update_editor(|view, cx| {
 4435        view.add_selection_above(&Default::default(), cx);
 4436    });
 4437
 4438    cx.assert_editor_state(indoc!(
 4439        r#"a«bcˇ»
 4440           d«efgˇ»hi
 4441
 4442           j«kˇ»
 4443           nlmo
 4444           "#
 4445    ));
 4446
 4447    // Change selections again
 4448    cx.set_state(indoc!(
 4449        r#"abc
 4450           d«ˇefghi
 4451
 4452           jk
 4453           nlm»o
 4454           "#
 4455    ));
 4456
 4457    cx.update_editor(|view, cx| {
 4458        view.add_selection_above(&Default::default(), cx);
 4459    });
 4460
 4461    cx.assert_editor_state(indoc!(
 4462        r#"a«ˇbc»
 4463           d«ˇef»ghi
 4464
 4465           j«ˇk»
 4466           n«ˇlm»o
 4467           "#
 4468    ));
 4469
 4470    cx.update_editor(|view, cx| {
 4471        view.add_selection_below(&Default::default(), cx);
 4472    });
 4473
 4474    cx.assert_editor_state(indoc!(
 4475        r#"abc
 4476           d«ˇef»ghi
 4477
 4478           j«ˇk»
 4479           n«ˇlm»o
 4480           "#
 4481    ));
 4482}
 4483
 4484#[gpui::test]
 4485async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4486    init_test(cx, |_| {});
 4487
 4488    let mut cx = EditorTestContext::new(cx).await;
 4489    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4490
 4491    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4492        .unwrap();
 4493    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4494
 4495    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4496        .unwrap();
 4497    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4498
 4499    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4500    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4501
 4502    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4503    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4504
 4505    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4506        .unwrap();
 4507    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4508
 4509    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4510        .unwrap();
 4511    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4512}
 4513
 4514#[gpui::test]
 4515async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4516    init_test(cx, |_| {});
 4517
 4518    let mut cx = EditorTestContext::new(cx).await;
 4519    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4520
 4521    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4522        .unwrap();
 4523    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4524}
 4525
 4526#[gpui::test]
 4527async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4528    init_test(cx, |_| {});
 4529
 4530    let mut cx = EditorTestContext::new(cx).await;
 4531    cx.set_state(
 4532        r#"let foo = 2;
 4533lˇet foo = 2;
 4534let fooˇ = 2;
 4535let foo = 2;
 4536let foo = ˇ2;"#,
 4537    );
 4538
 4539    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4540        .unwrap();
 4541    cx.assert_editor_state(
 4542        r#"let foo = 2;
 4543«letˇ» foo = 2;
 4544let «fooˇ» = 2;
 4545let foo = 2;
 4546let foo = «2ˇ»;"#,
 4547    );
 4548
 4549    // noop for multiple selections with different contents
 4550    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4551        .unwrap();
 4552    cx.assert_editor_state(
 4553        r#"let foo = 2;
 4554«letˇ» foo = 2;
 4555let «fooˇ» = 2;
 4556let foo = 2;
 4557let foo = «2ˇ»;"#,
 4558    );
 4559}
 4560
 4561#[gpui::test]
 4562async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4563    init_test(cx, |_| {});
 4564
 4565    let mut cx = EditorTestContext::new_multibuffer(
 4566        cx,
 4567        [
 4568            &indoc! {
 4569                "aaa\n«bbb\nccc\n»ddd"
 4570            },
 4571            &indoc! {
 4572                "aaa\n«bbb\nccc\n»ddd"
 4573            },
 4574        ],
 4575    );
 4576
 4577    cx.assert_editor_state(indoc! {"
 4578        ˇbbb
 4579        ccc
 4580
 4581        bbb
 4582        ccc
 4583        "});
 4584    cx.dispatch_action(SelectPrevious::default());
 4585    cx.assert_editor_state(indoc! {"
 4586                «bbbˇ»
 4587                ccc
 4588
 4589                bbb
 4590                ccc
 4591                "});
 4592    cx.dispatch_action(SelectPrevious::default());
 4593    cx.assert_editor_state(indoc! {"
 4594                «bbbˇ»
 4595                ccc
 4596
 4597                «bbbˇ»
 4598                ccc
 4599                "});
 4600}
 4601
 4602#[gpui::test]
 4603async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 4604    init_test(cx, |_| {});
 4605
 4606    let mut cx = EditorTestContext::new(cx).await;
 4607    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4608
 4609    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4610        .unwrap();
 4611    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4612
 4613    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4614        .unwrap();
 4615    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4616
 4617    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4618    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4619
 4620    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4621    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4622
 4623    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4624        .unwrap();
 4625    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 4626
 4627    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4628        .unwrap();
 4629    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 4630
 4631    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4632        .unwrap();
 4633    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4634}
 4635
 4636#[gpui::test]
 4637async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4638    init_test(cx, |_| {});
 4639
 4640    let mut cx = EditorTestContext::new(cx).await;
 4641    cx.set_state(
 4642        r#"let foo = 2;
 4643lˇet foo = 2;
 4644let fooˇ = 2;
 4645let foo = 2;
 4646let foo = ˇ2;"#,
 4647    );
 4648
 4649    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4650        .unwrap();
 4651    cx.assert_editor_state(
 4652        r#"let foo = 2;
 4653«letˇ» foo = 2;
 4654let «fooˇ» = 2;
 4655let foo = 2;
 4656let foo = «2ˇ»;"#,
 4657    );
 4658
 4659    // noop for multiple selections with different contents
 4660    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4661        .unwrap();
 4662    cx.assert_editor_state(
 4663        r#"let foo = 2;
 4664«letˇ» foo = 2;
 4665let «fooˇ» = 2;
 4666let foo = 2;
 4667let foo = «2ˇ»;"#,
 4668    );
 4669}
 4670
 4671#[gpui::test]
 4672async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 4673    init_test(cx, |_| {});
 4674
 4675    let mut cx = EditorTestContext::new(cx).await;
 4676    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 4677
 4678    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4679        .unwrap();
 4680    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4681
 4682    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4683        .unwrap();
 4684    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4685
 4686    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4687    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4688
 4689    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4690    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4691
 4692    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4693        .unwrap();
 4694    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 4695
 4696    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4697        .unwrap();
 4698    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4699}
 4700
 4701#[gpui::test]
 4702async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 4703    init_test(cx, |_| {});
 4704
 4705    let language = Arc::new(Language::new(
 4706        LanguageConfig::default(),
 4707        Some(tree_sitter_rust::language()),
 4708    ));
 4709
 4710    let text = r#"
 4711        use mod1::mod2::{mod3, mod4};
 4712
 4713        fn fn_1(param1: bool, param2: &str) {
 4714            let var1 = "text";
 4715        }
 4716    "#
 4717    .unindent();
 4718
 4719    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4720    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4721    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4722
 4723    editor
 4724        .condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 4725        .await;
 4726
 4727    editor.update(cx, |view, cx| {
 4728        view.change_selections(None, cx, |s| {
 4729            s.select_display_ranges([
 4730                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4731                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4732                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4733            ]);
 4734        });
 4735        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4736    });
 4737    editor.update(cx, |editor, cx| {
 4738        assert_text_with_selections(
 4739            editor,
 4740            indoc! {r#"
 4741                use mod1::mod2::{mod3, «mod4ˇ»};
 4742
 4743                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4744                    let var1 = "«textˇ»";
 4745                }
 4746            "#},
 4747            cx,
 4748        );
 4749    });
 4750
 4751    editor.update(cx, |view, cx| {
 4752        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4753    });
 4754    editor.update(cx, |editor, cx| {
 4755        assert_text_with_selections(
 4756            editor,
 4757            indoc! {r#"
 4758                use mod1::mod2::«{mod3, mod4}ˇ»;
 4759
 4760                «ˇfn fn_1(param1: bool, param2: &str) {
 4761                    let var1 = "text";
 4762 4763            "#},
 4764            cx,
 4765        );
 4766    });
 4767
 4768    editor.update(cx, |view, cx| {
 4769        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4770    });
 4771    assert_eq!(
 4772        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4773        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4774    );
 4775
 4776    // Trying to expand the selected syntax node one more time has no effect.
 4777    editor.update(cx, |view, cx| {
 4778        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4779    });
 4780    assert_eq!(
 4781        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4782        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4783    );
 4784
 4785    editor.update(cx, |view, cx| {
 4786        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4787    });
 4788    editor.update(cx, |editor, cx| {
 4789        assert_text_with_selections(
 4790            editor,
 4791            indoc! {r#"
 4792                use mod1::mod2::«{mod3, mod4}ˇ»;
 4793
 4794                «ˇfn fn_1(param1: bool, param2: &str) {
 4795                    let var1 = "text";
 4796 4797            "#},
 4798            cx,
 4799        );
 4800    });
 4801
 4802    editor.update(cx, |view, cx| {
 4803        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4804    });
 4805    editor.update(cx, |editor, cx| {
 4806        assert_text_with_selections(
 4807            editor,
 4808            indoc! {r#"
 4809                use mod1::mod2::{mod3, «mod4ˇ»};
 4810
 4811                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4812                    let var1 = "«textˇ»";
 4813                }
 4814            "#},
 4815            cx,
 4816        );
 4817    });
 4818
 4819    editor.update(cx, |view, cx| {
 4820        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4821    });
 4822    editor.update(cx, |editor, cx| {
 4823        assert_text_with_selections(
 4824            editor,
 4825            indoc! {r#"
 4826                use mod1::mod2::{mod3, mo«ˇ»d4};
 4827
 4828                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 4829                    let var1 = "te«ˇ»xt";
 4830                }
 4831            "#},
 4832            cx,
 4833        );
 4834    });
 4835
 4836    // Trying to shrink the selected syntax node one more time has no effect.
 4837    editor.update(cx, |view, cx| {
 4838        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4839    });
 4840    editor.update(cx, |editor, cx| {
 4841        assert_text_with_selections(
 4842            editor,
 4843            indoc! {r#"
 4844                use mod1::mod2::{mod3, mo«ˇ»d4};
 4845
 4846                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 4847                    let var1 = "te«ˇ»xt";
 4848                }
 4849            "#},
 4850            cx,
 4851        );
 4852    });
 4853
 4854    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 4855    // a fold.
 4856    editor.update(cx, |view, cx| {
 4857        view.fold_ranges(
 4858            vec![
 4859                (
 4860                    Point::new(0, 21)..Point::new(0, 24),
 4861                    FoldPlaceholder::test(),
 4862                ),
 4863                (
 4864                    Point::new(3, 20)..Point::new(3, 22),
 4865                    FoldPlaceholder::test(),
 4866                ),
 4867            ],
 4868            true,
 4869            cx,
 4870        );
 4871        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4872    });
 4873    editor.update(cx, |editor, cx| {
 4874        assert_text_with_selections(
 4875            editor,
 4876            indoc! {r#"
 4877                use mod1::mod2::«{mod3, mod4}ˇ»;
 4878
 4879                fn fn_1«ˇ(param1: bool, param2: &str)» {
 4880                    «let var1 = "text";ˇ»
 4881                }
 4882            "#},
 4883            cx,
 4884        );
 4885    });
 4886}
 4887
 4888#[gpui::test]
 4889async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 4890    init_test(cx, |_| {});
 4891
 4892    let language = Arc::new(
 4893        Language::new(
 4894            LanguageConfig {
 4895                brackets: BracketPairConfig {
 4896                    pairs: vec![
 4897                        BracketPair {
 4898                            start: "{".to_string(),
 4899                            end: "}".to_string(),
 4900                            close: false,
 4901                            surround: false,
 4902                            newline: true,
 4903                        },
 4904                        BracketPair {
 4905                            start: "(".to_string(),
 4906                            end: ")".to_string(),
 4907                            close: false,
 4908                            surround: false,
 4909                            newline: true,
 4910                        },
 4911                    ],
 4912                    ..Default::default()
 4913                },
 4914                ..Default::default()
 4915            },
 4916            Some(tree_sitter_rust::language()),
 4917        )
 4918        .with_indents_query(
 4919            r#"
 4920                (_ "(" ")" @end) @indent
 4921                (_ "{" "}" @end) @indent
 4922            "#,
 4923        )
 4924        .unwrap(),
 4925    );
 4926
 4927    let text = "fn a() {}";
 4928
 4929    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4930    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4931    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4932    editor
 4933        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 4934        .await;
 4935
 4936    _ = editor.update(cx, |editor, cx| {
 4937        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 4938        editor.newline(&Newline, cx);
 4939        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 4940        assert_eq!(
 4941            editor.selections.ranges(cx),
 4942            &[
 4943                Point::new(1, 4)..Point::new(1, 4),
 4944                Point::new(3, 4)..Point::new(3, 4),
 4945                Point::new(5, 0)..Point::new(5, 0)
 4946            ]
 4947        );
 4948    });
 4949}
 4950
 4951#[gpui::test]
 4952async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 4953    init_test(cx, |_| {});
 4954
 4955    let mut cx = EditorTestContext::new(cx).await;
 4956
 4957    let language = Arc::new(Language::new(
 4958        LanguageConfig {
 4959            brackets: BracketPairConfig {
 4960                pairs: vec![
 4961                    BracketPair {
 4962                        start: "{".to_string(),
 4963                        end: "}".to_string(),
 4964                        close: true,
 4965                        surround: true,
 4966                        newline: true,
 4967                    },
 4968                    BracketPair {
 4969                        start: "(".to_string(),
 4970                        end: ")".to_string(),
 4971                        close: true,
 4972                        surround: true,
 4973                        newline: true,
 4974                    },
 4975                    BracketPair {
 4976                        start: "/*".to_string(),
 4977                        end: " */".to_string(),
 4978                        close: true,
 4979                        surround: true,
 4980                        newline: true,
 4981                    },
 4982                    BracketPair {
 4983                        start: "[".to_string(),
 4984                        end: "]".to_string(),
 4985                        close: false,
 4986                        surround: false,
 4987                        newline: true,
 4988                    },
 4989                    BracketPair {
 4990                        start: "\"".to_string(),
 4991                        end: "\"".to_string(),
 4992                        close: true,
 4993                        surround: true,
 4994                        newline: false,
 4995                    },
 4996                    BracketPair {
 4997                        start: "<".to_string(),
 4998                        end: ">".to_string(),
 4999                        close: false,
 5000                        surround: true,
 5001                        newline: true,
 5002                    },
 5003                ],
 5004                ..Default::default()
 5005            },
 5006            autoclose_before: "})]".to_string(),
 5007            ..Default::default()
 5008        },
 5009        Some(tree_sitter_rust::language()),
 5010    ));
 5011
 5012    cx.language_registry().add(language.clone());
 5013    cx.update_buffer(|buffer, cx| {
 5014        buffer.set_language(Some(language), cx);
 5015    });
 5016
 5017    cx.set_state(
 5018        &r#"
 5019            🏀ˇ
 5020            εˇ
 5021            ❤️ˇ
 5022        "#
 5023        .unindent(),
 5024    );
 5025
 5026    // autoclose multiple nested brackets at multiple cursors
 5027    cx.update_editor(|view, cx| {
 5028        view.handle_input("{", cx);
 5029        view.handle_input("{", cx);
 5030        view.handle_input("{", cx);
 5031    });
 5032    cx.assert_editor_state(
 5033        &"
 5034            🏀{{{ˇ}}}
 5035            ε{{{ˇ}}}
 5036            ❤️{{{ˇ}}}
 5037        "
 5038        .unindent(),
 5039    );
 5040
 5041    // insert a different closing bracket
 5042    cx.update_editor(|view, cx| {
 5043        view.handle_input(")", cx);
 5044    });
 5045    cx.assert_editor_state(
 5046        &"
 5047            🏀{{{)ˇ}}}
 5048            ε{{{)ˇ}}}
 5049            ❤️{{{)ˇ}}}
 5050        "
 5051        .unindent(),
 5052    );
 5053
 5054    // skip over the auto-closed brackets when typing a closing bracket
 5055    cx.update_editor(|view, cx| {
 5056        view.move_right(&MoveRight, cx);
 5057        view.handle_input("}", cx);
 5058        view.handle_input("}", cx);
 5059        view.handle_input("}", cx);
 5060    });
 5061    cx.assert_editor_state(
 5062        &"
 5063            🏀{{{)}}}}ˇ
 5064            ε{{{)}}}}ˇ
 5065            ❤️{{{)}}}}ˇ
 5066        "
 5067        .unindent(),
 5068    );
 5069
 5070    // autoclose multi-character pairs
 5071    cx.set_state(
 5072        &"
 5073            ˇ
 5074            ˇ
 5075        "
 5076        .unindent(),
 5077    );
 5078    cx.update_editor(|view, cx| {
 5079        view.handle_input("/", cx);
 5080        view.handle_input("*", cx);
 5081    });
 5082    cx.assert_editor_state(
 5083        &"
 5084            /*ˇ */
 5085            /*ˇ */
 5086        "
 5087        .unindent(),
 5088    );
 5089
 5090    // one cursor autocloses a multi-character pair, one cursor
 5091    // does not autoclose.
 5092    cx.set_state(
 5093        &"
 5094 5095            ˇ
 5096        "
 5097        .unindent(),
 5098    );
 5099    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5100    cx.assert_editor_state(
 5101        &"
 5102            /*ˇ */
 5103 5104        "
 5105        .unindent(),
 5106    );
 5107
 5108    // Don't autoclose if the next character isn't whitespace and isn't
 5109    // listed in the language's "autoclose_before" section.
 5110    cx.set_state("ˇa b");
 5111    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5112    cx.assert_editor_state("{ˇa b");
 5113
 5114    // Don't autoclose if `close` is false for the bracket pair
 5115    cx.set_state("ˇ");
 5116    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5117    cx.assert_editor_state("");
 5118
 5119    // Surround with brackets if text is selected
 5120    cx.set_state("«aˇ» b");
 5121    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5122    cx.assert_editor_state("{«aˇ»} b");
 5123
 5124    // Autclose pair where the start and end characters are the same
 5125    cx.set_state("");
 5126    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5127    cx.assert_editor_state("a\"ˇ\"");
 5128    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5129    cx.assert_editor_state("a\"\"ˇ");
 5130
 5131    // Don't autoclose pair if autoclose is disabled
 5132    cx.set_state("ˇ");
 5133    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5134    cx.assert_editor_state("");
 5135
 5136    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5137    cx.set_state("«aˇ» b");
 5138    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5139    cx.assert_editor_state("<«aˇ»> b");
 5140}
 5141
 5142#[gpui::test]
 5143async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5144    init_test(cx, |settings| {
 5145        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5146    });
 5147
 5148    let mut cx = EditorTestContext::new(cx).await;
 5149
 5150    let language = Arc::new(Language::new(
 5151        LanguageConfig {
 5152            brackets: BracketPairConfig {
 5153                pairs: vec![
 5154                    BracketPair {
 5155                        start: "{".to_string(),
 5156                        end: "}".to_string(),
 5157                        close: true,
 5158                        surround: true,
 5159                        newline: true,
 5160                    },
 5161                    BracketPair {
 5162                        start: "(".to_string(),
 5163                        end: ")".to_string(),
 5164                        close: true,
 5165                        surround: true,
 5166                        newline: true,
 5167                    },
 5168                    BracketPair {
 5169                        start: "[".to_string(),
 5170                        end: "]".to_string(),
 5171                        close: false,
 5172                        surround: false,
 5173                        newline: true,
 5174                    },
 5175                ],
 5176                ..Default::default()
 5177            },
 5178            autoclose_before: "})]".to_string(),
 5179            ..Default::default()
 5180        },
 5181        Some(tree_sitter_rust::language()),
 5182    ));
 5183
 5184    cx.language_registry().add(language.clone());
 5185    cx.update_buffer(|buffer, cx| {
 5186        buffer.set_language(Some(language), cx);
 5187    });
 5188
 5189    cx.set_state(
 5190        &"
 5191            ˇ
 5192            ˇ
 5193            ˇ
 5194        "
 5195        .unindent(),
 5196    );
 5197
 5198    // ensure only matching closing brackets are skipped over
 5199    cx.update_editor(|view, cx| {
 5200        view.handle_input("}", cx);
 5201        view.move_left(&MoveLeft, cx);
 5202        view.handle_input(")", cx);
 5203        view.move_left(&MoveLeft, cx);
 5204    });
 5205    cx.assert_editor_state(
 5206        &"
 5207            ˇ)}
 5208            ˇ)}
 5209            ˇ)}
 5210        "
 5211        .unindent(),
 5212    );
 5213
 5214    // skip-over closing brackets at multiple cursors
 5215    cx.update_editor(|view, cx| {
 5216        view.handle_input(")", cx);
 5217        view.handle_input("}", cx);
 5218    });
 5219    cx.assert_editor_state(
 5220        &"
 5221            )}ˇ
 5222            )}ˇ
 5223            )}ˇ
 5224        "
 5225        .unindent(),
 5226    );
 5227
 5228    // ignore non-close brackets
 5229    cx.update_editor(|view, cx| {
 5230        view.handle_input("]", cx);
 5231        view.move_left(&MoveLeft, cx);
 5232        view.handle_input("]", cx);
 5233    });
 5234    cx.assert_editor_state(
 5235        &"
 5236            )}]ˇ]
 5237            )}]ˇ]
 5238            )}]ˇ]
 5239        "
 5240        .unindent(),
 5241    );
 5242}
 5243
 5244#[gpui::test]
 5245async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5246    init_test(cx, |_| {});
 5247
 5248    let mut cx = EditorTestContext::new(cx).await;
 5249
 5250    let html_language = Arc::new(
 5251        Language::new(
 5252            LanguageConfig {
 5253                name: "HTML".into(),
 5254                brackets: BracketPairConfig {
 5255                    pairs: vec![
 5256                        BracketPair {
 5257                            start: "<".into(),
 5258                            end: ">".into(),
 5259                            close: true,
 5260                            ..Default::default()
 5261                        },
 5262                        BracketPair {
 5263                            start: "{".into(),
 5264                            end: "}".into(),
 5265                            close: true,
 5266                            ..Default::default()
 5267                        },
 5268                        BracketPair {
 5269                            start: "(".into(),
 5270                            end: ")".into(),
 5271                            close: true,
 5272                            ..Default::default()
 5273                        },
 5274                    ],
 5275                    ..Default::default()
 5276                },
 5277                autoclose_before: "})]>".into(),
 5278                ..Default::default()
 5279            },
 5280            Some(tree_sitter_html::language()),
 5281        )
 5282        .with_injection_query(
 5283            r#"
 5284            (script_element
 5285                (raw_text) @content
 5286                (#set! "language" "javascript"))
 5287            "#,
 5288        )
 5289        .unwrap(),
 5290    );
 5291
 5292    let javascript_language = Arc::new(Language::new(
 5293        LanguageConfig {
 5294            name: "JavaScript".into(),
 5295            brackets: BracketPairConfig {
 5296                pairs: vec![
 5297                    BracketPair {
 5298                        start: "/*".into(),
 5299                        end: " */".into(),
 5300                        close: true,
 5301                        ..Default::default()
 5302                    },
 5303                    BracketPair {
 5304                        start: "{".into(),
 5305                        end: "}".into(),
 5306                        close: true,
 5307                        ..Default::default()
 5308                    },
 5309                    BracketPair {
 5310                        start: "(".into(),
 5311                        end: ")".into(),
 5312                        close: true,
 5313                        ..Default::default()
 5314                    },
 5315                ],
 5316                ..Default::default()
 5317            },
 5318            autoclose_before: "})]>".into(),
 5319            ..Default::default()
 5320        },
 5321        Some(tree_sitter_typescript::language_tsx()),
 5322    ));
 5323
 5324    cx.language_registry().add(html_language.clone());
 5325    cx.language_registry().add(javascript_language.clone());
 5326
 5327    cx.update_buffer(|buffer, cx| {
 5328        buffer.set_language(Some(html_language), cx);
 5329    });
 5330
 5331    cx.set_state(
 5332        &r#"
 5333            <body>ˇ
 5334                <script>
 5335                    var x = 1;ˇ
 5336                </script>
 5337            </body>ˇ
 5338        "#
 5339        .unindent(),
 5340    );
 5341
 5342    // Precondition: different languages are active at different locations.
 5343    cx.update_editor(|editor, cx| {
 5344        let snapshot = editor.snapshot(cx);
 5345        let cursors = editor.selections.ranges::<usize>(cx);
 5346        let languages = cursors
 5347            .iter()
 5348            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5349            .collect::<Vec<_>>();
 5350        assert_eq!(
 5351            languages,
 5352            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5353        );
 5354    });
 5355
 5356    // Angle brackets autoclose in HTML, but not JavaScript.
 5357    cx.update_editor(|editor, cx| {
 5358        editor.handle_input("<", cx);
 5359        editor.handle_input("a", cx);
 5360    });
 5361    cx.assert_editor_state(
 5362        &r#"
 5363            <body><aˇ>
 5364                <script>
 5365                    var x = 1;<aˇ
 5366                </script>
 5367            </body><aˇ>
 5368        "#
 5369        .unindent(),
 5370    );
 5371
 5372    // Curly braces and parens autoclose in both HTML and JavaScript.
 5373    cx.update_editor(|editor, cx| {
 5374        editor.handle_input(" b=", cx);
 5375        editor.handle_input("{", cx);
 5376        editor.handle_input("c", cx);
 5377        editor.handle_input("(", cx);
 5378    });
 5379    cx.assert_editor_state(
 5380        &r#"
 5381            <body><a b={c(ˇ)}>
 5382                <script>
 5383                    var x = 1;<a b={c(ˇ)}
 5384                </script>
 5385            </body><a b={c(ˇ)}>
 5386        "#
 5387        .unindent(),
 5388    );
 5389
 5390    // Brackets that were already autoclosed are skipped.
 5391    cx.update_editor(|editor, cx| {
 5392        editor.handle_input(")", cx);
 5393        editor.handle_input("d", cx);
 5394        editor.handle_input("}", cx);
 5395    });
 5396    cx.assert_editor_state(
 5397        &r#"
 5398            <body><a b={c()d}ˇ>
 5399                <script>
 5400                    var x = 1;<a b={c()d}ˇ
 5401                </script>
 5402            </body><a b={c()d}ˇ>
 5403        "#
 5404        .unindent(),
 5405    );
 5406    cx.update_editor(|editor, cx| {
 5407        editor.handle_input(">", cx);
 5408    });
 5409    cx.assert_editor_state(
 5410        &r#"
 5411            <body><a b={c()d}>ˇ
 5412                <script>
 5413                    var x = 1;<a b={c()d}>ˇ
 5414                </script>
 5415            </body><a b={c()d}>ˇ
 5416        "#
 5417        .unindent(),
 5418    );
 5419
 5420    // Reset
 5421    cx.set_state(
 5422        &r#"
 5423            <body>ˇ
 5424                <script>
 5425                    var x = 1;ˇ
 5426                </script>
 5427            </body>ˇ
 5428        "#
 5429        .unindent(),
 5430    );
 5431
 5432    cx.update_editor(|editor, cx| {
 5433        editor.handle_input("<", cx);
 5434    });
 5435    cx.assert_editor_state(
 5436        &r#"
 5437            <body><ˇ>
 5438                <script>
 5439                    var x = 1;<ˇ
 5440                </script>
 5441            </body><ˇ>
 5442        "#
 5443        .unindent(),
 5444    );
 5445
 5446    // When backspacing, the closing angle brackets are removed.
 5447    cx.update_editor(|editor, cx| {
 5448        editor.backspace(&Backspace, cx);
 5449    });
 5450    cx.assert_editor_state(
 5451        &r#"
 5452            <body>ˇ
 5453                <script>
 5454                    var x = 1;ˇ
 5455                </script>
 5456            </body>ˇ
 5457        "#
 5458        .unindent(),
 5459    );
 5460
 5461    // Block comments autoclose in JavaScript, but not HTML.
 5462    cx.update_editor(|editor, cx| {
 5463        editor.handle_input("/", cx);
 5464        editor.handle_input("*", cx);
 5465    });
 5466    cx.assert_editor_state(
 5467        &r#"
 5468            <body>/*ˇ
 5469                <script>
 5470                    var x = 1;/*ˇ */
 5471                </script>
 5472            </body>/*ˇ
 5473        "#
 5474        .unindent(),
 5475    );
 5476}
 5477
 5478#[gpui::test]
 5479async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5480    init_test(cx, |_| {});
 5481
 5482    let mut cx = EditorTestContext::new(cx).await;
 5483
 5484    let rust_language = Arc::new(
 5485        Language::new(
 5486            LanguageConfig {
 5487                name: "Rust".into(),
 5488                brackets: serde_json::from_value(json!([
 5489                    { "start": "{", "end": "}", "close": true, "newline": true },
 5490                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5491                ]))
 5492                .unwrap(),
 5493                autoclose_before: "})]>".into(),
 5494                ..Default::default()
 5495            },
 5496            Some(tree_sitter_rust::language()),
 5497        )
 5498        .with_override_query("(string_literal) @string")
 5499        .unwrap(),
 5500    );
 5501
 5502    cx.language_registry().add(rust_language.clone());
 5503    cx.update_buffer(|buffer, cx| {
 5504        buffer.set_language(Some(rust_language), cx);
 5505    });
 5506
 5507    cx.set_state(
 5508        &r#"
 5509            let x = ˇ
 5510        "#
 5511        .unindent(),
 5512    );
 5513
 5514    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5515    cx.update_editor(|editor, cx| {
 5516        editor.handle_input("\"", cx);
 5517    });
 5518    cx.assert_editor_state(
 5519        &r#"
 5520            let x = "ˇ"
 5521        "#
 5522        .unindent(),
 5523    );
 5524
 5525    // Inserting another quotation mark. The cursor moves across the existing
 5526    // automatically-inserted quotation mark.
 5527    cx.update_editor(|editor, cx| {
 5528        editor.handle_input("\"", cx);
 5529    });
 5530    cx.assert_editor_state(
 5531        &r#"
 5532            let x = ""ˇ
 5533        "#
 5534        .unindent(),
 5535    );
 5536
 5537    // Reset
 5538    cx.set_state(
 5539        &r#"
 5540            let x = ˇ
 5541        "#
 5542        .unindent(),
 5543    );
 5544
 5545    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5546    cx.update_editor(|editor, cx| {
 5547        editor.handle_input("\"", cx);
 5548        editor.handle_input(" ", cx);
 5549        editor.move_left(&Default::default(), cx);
 5550        editor.handle_input("\\", cx);
 5551        editor.handle_input("\"", cx);
 5552    });
 5553    cx.assert_editor_state(
 5554        &r#"
 5555            let x = "\"ˇ "
 5556        "#
 5557        .unindent(),
 5558    );
 5559
 5560    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5561    // mark. Nothing is inserted.
 5562    cx.update_editor(|editor, cx| {
 5563        editor.move_right(&Default::default(), cx);
 5564        editor.handle_input("\"", cx);
 5565    });
 5566    cx.assert_editor_state(
 5567        &r#"
 5568            let x = "\" "ˇ
 5569        "#
 5570        .unindent(),
 5571    );
 5572}
 5573
 5574#[gpui::test]
 5575async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5576    init_test(cx, |_| {});
 5577
 5578    let language = Arc::new(Language::new(
 5579        LanguageConfig {
 5580            brackets: BracketPairConfig {
 5581                pairs: vec![
 5582                    BracketPair {
 5583                        start: "{".to_string(),
 5584                        end: "}".to_string(),
 5585                        close: true,
 5586                        surround: true,
 5587                        newline: true,
 5588                    },
 5589                    BracketPair {
 5590                        start: "/* ".to_string(),
 5591                        end: "*/".to_string(),
 5592                        close: true,
 5593                        surround: true,
 5594                        ..Default::default()
 5595                    },
 5596                ],
 5597                ..Default::default()
 5598            },
 5599            ..Default::default()
 5600        },
 5601        Some(tree_sitter_rust::language()),
 5602    ));
 5603
 5604    let text = r#"
 5605        a
 5606        b
 5607        c
 5608    "#
 5609    .unindent();
 5610
 5611    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5612    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5613    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5614    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5615        .await;
 5616
 5617    _ = view.update(cx, |view, cx| {
 5618        view.change_selections(None, cx, |s| {
 5619            s.select_display_ranges([
 5620                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5621                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5622                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5623            ])
 5624        });
 5625
 5626        view.handle_input("{", cx);
 5627        view.handle_input("{", cx);
 5628        view.handle_input("{", cx);
 5629        assert_eq!(
 5630            view.text(cx),
 5631            "
 5632                {{{a}}}
 5633                {{{b}}}
 5634                {{{c}}}
 5635            "
 5636            .unindent()
 5637        );
 5638        assert_eq!(
 5639            view.selections.display_ranges(cx),
 5640            [
 5641                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5642                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5643                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5644            ]
 5645        );
 5646
 5647        view.undo(&Undo, cx);
 5648        view.undo(&Undo, cx);
 5649        view.undo(&Undo, cx);
 5650        assert_eq!(
 5651            view.text(cx),
 5652            "
 5653                a
 5654                b
 5655                c
 5656            "
 5657            .unindent()
 5658        );
 5659        assert_eq!(
 5660            view.selections.display_ranges(cx),
 5661            [
 5662                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5663                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5664                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5665            ]
 5666        );
 5667
 5668        // Ensure inserting the first character of a multi-byte bracket pair
 5669        // doesn't surround the selections with the bracket.
 5670        view.handle_input("/", cx);
 5671        assert_eq!(
 5672            view.text(cx),
 5673            "
 5674                /
 5675                /
 5676                /
 5677            "
 5678            .unindent()
 5679        );
 5680        assert_eq!(
 5681            view.selections.display_ranges(cx),
 5682            [
 5683                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5684                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5685                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5686            ]
 5687        );
 5688
 5689        view.undo(&Undo, cx);
 5690        assert_eq!(
 5691            view.text(cx),
 5692            "
 5693                a
 5694                b
 5695                c
 5696            "
 5697            .unindent()
 5698        );
 5699        assert_eq!(
 5700            view.selections.display_ranges(cx),
 5701            [
 5702                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5703                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5704                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5705            ]
 5706        );
 5707
 5708        // Ensure inserting the last character of a multi-byte bracket pair
 5709        // doesn't surround the selections with the bracket.
 5710        view.handle_input("*", cx);
 5711        assert_eq!(
 5712            view.text(cx),
 5713            "
 5714                *
 5715                *
 5716                *
 5717            "
 5718            .unindent()
 5719        );
 5720        assert_eq!(
 5721            view.selections.display_ranges(cx),
 5722            [
 5723                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5724                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5725                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5726            ]
 5727        );
 5728    });
 5729}
 5730
 5731#[gpui::test]
 5732async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 5733    init_test(cx, |_| {});
 5734
 5735    let language = Arc::new(Language::new(
 5736        LanguageConfig {
 5737            brackets: BracketPairConfig {
 5738                pairs: vec![BracketPair {
 5739                    start: "{".to_string(),
 5740                    end: "}".to_string(),
 5741                    close: true,
 5742                    surround: true,
 5743                    newline: true,
 5744                }],
 5745                ..Default::default()
 5746            },
 5747            autoclose_before: "}".to_string(),
 5748            ..Default::default()
 5749        },
 5750        Some(tree_sitter_rust::language()),
 5751    ));
 5752
 5753    let text = r#"
 5754        a
 5755        b
 5756        c
 5757    "#
 5758    .unindent();
 5759
 5760    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5761    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5762    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5763    editor
 5764        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5765        .await;
 5766
 5767    _ = editor.update(cx, |editor, cx| {
 5768        editor.change_selections(None, cx, |s| {
 5769            s.select_ranges([
 5770                Point::new(0, 1)..Point::new(0, 1),
 5771                Point::new(1, 1)..Point::new(1, 1),
 5772                Point::new(2, 1)..Point::new(2, 1),
 5773            ])
 5774        });
 5775
 5776        editor.handle_input("{", cx);
 5777        editor.handle_input("{", cx);
 5778        editor.handle_input("_", cx);
 5779        assert_eq!(
 5780            editor.text(cx),
 5781            "
 5782                a{{_}}
 5783                b{{_}}
 5784                c{{_}}
 5785            "
 5786            .unindent()
 5787        );
 5788        assert_eq!(
 5789            editor.selections.ranges::<Point>(cx),
 5790            [
 5791                Point::new(0, 4)..Point::new(0, 4),
 5792                Point::new(1, 4)..Point::new(1, 4),
 5793                Point::new(2, 4)..Point::new(2, 4)
 5794            ]
 5795        );
 5796
 5797        editor.backspace(&Default::default(), cx);
 5798        editor.backspace(&Default::default(), cx);
 5799        assert_eq!(
 5800            editor.text(cx),
 5801            "
 5802                a{}
 5803                b{}
 5804                c{}
 5805            "
 5806            .unindent()
 5807        );
 5808        assert_eq!(
 5809            editor.selections.ranges::<Point>(cx),
 5810            [
 5811                Point::new(0, 2)..Point::new(0, 2),
 5812                Point::new(1, 2)..Point::new(1, 2),
 5813                Point::new(2, 2)..Point::new(2, 2)
 5814            ]
 5815        );
 5816
 5817        editor.delete_to_previous_word_start(&Default::default(), cx);
 5818        assert_eq!(
 5819            editor.text(cx),
 5820            "
 5821                a
 5822                b
 5823                c
 5824            "
 5825            .unindent()
 5826        );
 5827        assert_eq!(
 5828            editor.selections.ranges::<Point>(cx),
 5829            [
 5830                Point::new(0, 1)..Point::new(0, 1),
 5831                Point::new(1, 1)..Point::new(1, 1),
 5832                Point::new(2, 1)..Point::new(2, 1)
 5833            ]
 5834        );
 5835    });
 5836}
 5837
 5838#[gpui::test]
 5839async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 5840    init_test(cx, |settings| {
 5841        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5842    });
 5843
 5844    let mut cx = EditorTestContext::new(cx).await;
 5845
 5846    let language = Arc::new(Language::new(
 5847        LanguageConfig {
 5848            brackets: BracketPairConfig {
 5849                pairs: vec![
 5850                    BracketPair {
 5851                        start: "{".to_string(),
 5852                        end: "}".to_string(),
 5853                        close: true,
 5854                        surround: true,
 5855                        newline: true,
 5856                    },
 5857                    BracketPair {
 5858                        start: "(".to_string(),
 5859                        end: ")".to_string(),
 5860                        close: true,
 5861                        surround: true,
 5862                        newline: true,
 5863                    },
 5864                    BracketPair {
 5865                        start: "[".to_string(),
 5866                        end: "]".to_string(),
 5867                        close: false,
 5868                        surround: true,
 5869                        newline: true,
 5870                    },
 5871                ],
 5872                ..Default::default()
 5873            },
 5874            autoclose_before: "})]".to_string(),
 5875            ..Default::default()
 5876        },
 5877        Some(tree_sitter_rust::language()),
 5878    ));
 5879
 5880    cx.language_registry().add(language.clone());
 5881    cx.update_buffer(|buffer, cx| {
 5882        buffer.set_language(Some(language), cx);
 5883    });
 5884
 5885    cx.set_state(
 5886        &"
 5887            {(ˇ)}
 5888            [[ˇ]]
 5889            {(ˇ)}
 5890        "
 5891        .unindent(),
 5892    );
 5893
 5894    cx.update_editor(|view, cx| {
 5895        view.backspace(&Default::default(), cx);
 5896        view.backspace(&Default::default(), cx);
 5897    });
 5898
 5899    cx.assert_editor_state(
 5900        &"
 5901            ˇ
 5902            ˇ]]
 5903            ˇ
 5904        "
 5905        .unindent(),
 5906    );
 5907
 5908    cx.update_editor(|view, cx| {
 5909        view.handle_input("{", cx);
 5910        view.handle_input("{", cx);
 5911        view.move_right(&MoveRight, cx);
 5912        view.move_right(&MoveRight, cx);
 5913        view.move_left(&MoveLeft, cx);
 5914        view.move_left(&MoveLeft, cx);
 5915        view.backspace(&Default::default(), cx);
 5916    });
 5917
 5918    cx.assert_editor_state(
 5919        &"
 5920            {ˇ}
 5921            {ˇ}]]
 5922            {ˇ}
 5923        "
 5924        .unindent(),
 5925    );
 5926
 5927    cx.update_editor(|view, cx| {
 5928        view.backspace(&Default::default(), cx);
 5929    });
 5930
 5931    cx.assert_editor_state(
 5932        &"
 5933            ˇ
 5934            ˇ]]
 5935            ˇ
 5936        "
 5937        .unindent(),
 5938    );
 5939}
 5940
 5941#[gpui::test]
 5942async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 5943    init_test(cx, |_| {});
 5944
 5945    let language = Arc::new(Language::new(
 5946        LanguageConfig::default(),
 5947        Some(tree_sitter_rust::language()),
 5948    ));
 5949
 5950    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 5951    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5952    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5953    editor
 5954        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5955        .await;
 5956
 5957    _ = editor.update(cx, |editor, cx| {
 5958        editor.set_auto_replace_emoji_shortcode(true);
 5959
 5960        editor.handle_input("Hello ", cx);
 5961        editor.handle_input(":wave", cx);
 5962        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 5963
 5964        editor.handle_input(":", cx);
 5965        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 5966
 5967        editor.handle_input(" :smile", cx);
 5968        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 5969
 5970        editor.handle_input(":", cx);
 5971        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 5972
 5973        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 5974        editor.handle_input(":wave", cx);
 5975        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 5976
 5977        editor.handle_input(":", cx);
 5978        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 5979
 5980        editor.handle_input(":1", cx);
 5981        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 5982
 5983        editor.handle_input(":", cx);
 5984        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 5985
 5986        // Ensure shortcode does not get replaced when it is part of a word
 5987        editor.handle_input(" Test:wave", cx);
 5988        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 5989
 5990        editor.handle_input(":", cx);
 5991        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 5992
 5993        editor.set_auto_replace_emoji_shortcode(false);
 5994
 5995        // Ensure shortcode does not get replaced when auto replace is off
 5996        editor.handle_input(" :wave", cx);
 5997        assert_eq!(
 5998            editor.text(cx),
 5999            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6000        );
 6001
 6002        editor.handle_input(":", cx);
 6003        assert_eq!(
 6004            editor.text(cx),
 6005            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6006        );
 6007    });
 6008}
 6009
 6010#[gpui::test]
 6011async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6012    init_test(cx, |_| {});
 6013
 6014    let (text, insertion_ranges) = marked_text_ranges(
 6015        indoc! {"
 6016            a.ˇ b
 6017            a.ˇ b
 6018            a.ˇ b
 6019        "},
 6020        false,
 6021    );
 6022
 6023    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6024    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6025
 6026    _ = editor.update(cx, |editor, cx| {
 6027        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6028
 6029        editor
 6030            .insert_snippet(&insertion_ranges, snippet, cx)
 6031            .unwrap();
 6032
 6033        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6034            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6035            assert_eq!(editor.text(cx), expected_text);
 6036            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6037        }
 6038
 6039        assert(
 6040            editor,
 6041            cx,
 6042            indoc! {"
 6043                a.f(«one», two, «three») b
 6044                a.f(«one», two, «three») b
 6045                a.f(«one», two, «three») b
 6046            "},
 6047        );
 6048
 6049        // Can't move earlier than the first tab stop
 6050        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6051        assert(
 6052            editor,
 6053            cx,
 6054            indoc! {"
 6055                a.f(«one», two, «three») b
 6056                a.f(«one», two, «three») b
 6057                a.f(«one», two, «three») b
 6058            "},
 6059        );
 6060
 6061        assert!(editor.move_to_next_snippet_tabstop(cx));
 6062        assert(
 6063            editor,
 6064            cx,
 6065            indoc! {"
 6066                a.f(one, «two», three) b
 6067                a.f(one, «two», three) b
 6068                a.f(one, «two», three) b
 6069            "},
 6070        );
 6071
 6072        editor.move_to_prev_snippet_tabstop(cx);
 6073        assert(
 6074            editor,
 6075            cx,
 6076            indoc! {"
 6077                a.f(«one», two, «three») b
 6078                a.f(«one», two, «three») b
 6079                a.f(«one», two, «three») b
 6080            "},
 6081        );
 6082
 6083        assert!(editor.move_to_next_snippet_tabstop(cx));
 6084        assert(
 6085            editor,
 6086            cx,
 6087            indoc! {"
 6088                a.f(one, «two», three) b
 6089                a.f(one, «two», three) b
 6090                a.f(one, «two», three) b
 6091            "},
 6092        );
 6093        assert!(editor.move_to_next_snippet_tabstop(cx));
 6094        assert(
 6095            editor,
 6096            cx,
 6097            indoc! {"
 6098                a.f(one, two, three)ˇ b
 6099                a.f(one, two, three)ˇ b
 6100                a.f(one, two, three)ˇ b
 6101            "},
 6102        );
 6103
 6104        // As soon as the last tab stop is reached, snippet state is gone
 6105        editor.move_to_prev_snippet_tabstop(cx);
 6106        assert(
 6107            editor,
 6108            cx,
 6109            indoc! {"
 6110                a.f(one, two, three)ˇ b
 6111                a.f(one, two, three)ˇ b
 6112                a.f(one, two, three)ˇ b
 6113            "},
 6114        );
 6115    });
 6116}
 6117
 6118#[gpui::test]
 6119async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6120    init_test(cx, |_| {});
 6121
 6122    let fs = FakeFs::new(cx.executor());
 6123    fs.insert_file("/file.rs", Default::default()).await;
 6124
 6125    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6126
 6127    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6128    language_registry.add(rust_lang());
 6129    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6130        "Rust",
 6131        FakeLspAdapter {
 6132            capabilities: lsp::ServerCapabilities {
 6133                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6134                ..Default::default()
 6135            },
 6136            ..Default::default()
 6137        },
 6138    );
 6139
 6140    let buffer = project
 6141        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6142        .await
 6143        .unwrap();
 6144
 6145    cx.executor().start_waiting();
 6146    let fake_server = fake_servers.next().await.unwrap();
 6147
 6148    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6149    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6150    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6151    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6152
 6153    let save = editor
 6154        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6155        .unwrap();
 6156    fake_server
 6157        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6158            assert_eq!(
 6159                params.text_document.uri,
 6160                lsp::Url::from_file_path("/file.rs").unwrap()
 6161            );
 6162            assert_eq!(params.options.tab_size, 4);
 6163            Ok(Some(vec![lsp::TextEdit::new(
 6164                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6165                ", ".to_string(),
 6166            )]))
 6167        })
 6168        .next()
 6169        .await;
 6170    cx.executor().start_waiting();
 6171    save.await;
 6172
 6173    assert_eq!(
 6174        editor.update(cx, |editor, cx| editor.text(cx)),
 6175        "one, two\nthree\n"
 6176    );
 6177    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6178
 6179    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6180    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6181
 6182    // Ensure we can still save even if formatting hangs.
 6183    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6184        assert_eq!(
 6185            params.text_document.uri,
 6186            lsp::Url::from_file_path("/file.rs").unwrap()
 6187        );
 6188        futures::future::pending::<()>().await;
 6189        unreachable!()
 6190    });
 6191    let save = editor
 6192        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6193        .unwrap();
 6194    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6195    cx.executor().start_waiting();
 6196    save.await;
 6197    assert_eq!(
 6198        editor.update(cx, |editor, cx| editor.text(cx)),
 6199        "one\ntwo\nthree\n"
 6200    );
 6201    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6202
 6203    // For non-dirty buffer, no formatting request should be sent
 6204    let save = editor
 6205        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6206        .unwrap();
 6207    let _pending_format_request = fake_server
 6208        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6209            panic!("Should not be invoked on non-dirty buffer");
 6210        })
 6211        .next();
 6212    cx.executor().start_waiting();
 6213    save.await;
 6214
 6215    // Set rust language override and assert overridden tabsize is sent to language server
 6216    update_test_language_settings(cx, |settings| {
 6217        settings.languages.insert(
 6218            "Rust".into(),
 6219            LanguageSettingsContent {
 6220                tab_size: NonZeroU32::new(8),
 6221                ..Default::default()
 6222            },
 6223        );
 6224    });
 6225
 6226    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6227    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6228    let save = editor
 6229        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6230        .unwrap();
 6231    fake_server
 6232        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6233            assert_eq!(
 6234                params.text_document.uri,
 6235                lsp::Url::from_file_path("/file.rs").unwrap()
 6236            );
 6237            assert_eq!(params.options.tab_size, 8);
 6238            Ok(Some(vec![]))
 6239        })
 6240        .next()
 6241        .await;
 6242    cx.executor().start_waiting();
 6243    save.await;
 6244}
 6245
 6246#[gpui::test]
 6247async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6248    init_test(cx, |_| {});
 6249
 6250    let cols = 4;
 6251    let rows = 10;
 6252    let sample_text_1 = sample_text(rows, cols, 'a');
 6253    assert_eq!(
 6254        sample_text_1,
 6255        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6256    );
 6257    let sample_text_2 = sample_text(rows, cols, 'l');
 6258    assert_eq!(
 6259        sample_text_2,
 6260        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6261    );
 6262    let sample_text_3 = sample_text(rows, cols, 'v');
 6263    assert_eq!(
 6264        sample_text_3,
 6265        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6266    );
 6267
 6268    let fs = FakeFs::new(cx.executor());
 6269    fs.insert_tree(
 6270        "/a",
 6271        json!({
 6272            "main.rs": sample_text_1,
 6273            "other.rs": sample_text_2,
 6274            "lib.rs": sample_text_3,
 6275        }),
 6276    )
 6277    .await;
 6278
 6279    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6280    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6281    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6282
 6283    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6284    language_registry.add(rust_lang());
 6285    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6286        "Rust",
 6287        FakeLspAdapter {
 6288            capabilities: lsp::ServerCapabilities {
 6289                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6290                ..Default::default()
 6291            },
 6292            ..Default::default()
 6293        },
 6294    );
 6295
 6296    let worktree = project.update(cx, |project, cx| {
 6297        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6298        assert_eq!(worktrees.len(), 1);
 6299        worktrees.pop().unwrap()
 6300    });
 6301    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6302
 6303    let buffer_1 = project
 6304        .update(cx, |project, cx| {
 6305            project.open_buffer((worktree_id, "main.rs"), cx)
 6306        })
 6307        .await
 6308        .unwrap();
 6309    let buffer_2 = project
 6310        .update(cx, |project, cx| {
 6311            project.open_buffer((worktree_id, "other.rs"), cx)
 6312        })
 6313        .await
 6314        .unwrap();
 6315    let buffer_3 = project
 6316        .update(cx, |project, cx| {
 6317            project.open_buffer((worktree_id, "lib.rs"), cx)
 6318        })
 6319        .await
 6320        .unwrap();
 6321
 6322    let multi_buffer = cx.new_model(|cx| {
 6323        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 6324        multi_buffer.push_excerpts(
 6325            buffer_1.clone(),
 6326            [
 6327                ExcerptRange {
 6328                    context: Point::new(0, 0)..Point::new(3, 0),
 6329                    primary: None,
 6330                },
 6331                ExcerptRange {
 6332                    context: Point::new(5, 0)..Point::new(7, 0),
 6333                    primary: None,
 6334                },
 6335                ExcerptRange {
 6336                    context: Point::new(9, 0)..Point::new(10, 4),
 6337                    primary: None,
 6338                },
 6339            ],
 6340            cx,
 6341        );
 6342        multi_buffer.push_excerpts(
 6343            buffer_2.clone(),
 6344            [
 6345                ExcerptRange {
 6346                    context: Point::new(0, 0)..Point::new(3, 0),
 6347                    primary: None,
 6348                },
 6349                ExcerptRange {
 6350                    context: Point::new(5, 0)..Point::new(7, 0),
 6351                    primary: None,
 6352                },
 6353                ExcerptRange {
 6354                    context: Point::new(9, 0)..Point::new(10, 4),
 6355                    primary: None,
 6356                },
 6357            ],
 6358            cx,
 6359        );
 6360        multi_buffer.push_excerpts(
 6361            buffer_3.clone(),
 6362            [
 6363                ExcerptRange {
 6364                    context: Point::new(0, 0)..Point::new(3, 0),
 6365                    primary: None,
 6366                },
 6367                ExcerptRange {
 6368                    context: Point::new(5, 0)..Point::new(7, 0),
 6369                    primary: None,
 6370                },
 6371                ExcerptRange {
 6372                    context: Point::new(9, 0)..Point::new(10, 4),
 6373                    primary: None,
 6374                },
 6375            ],
 6376            cx,
 6377        );
 6378        multi_buffer
 6379    });
 6380    let multi_buffer_editor = cx.new_view(|cx| {
 6381        Editor::new(
 6382            EditorMode::Full,
 6383            multi_buffer,
 6384            Some(project.clone()),
 6385            true,
 6386            cx,
 6387        )
 6388    });
 6389
 6390    multi_buffer_editor.update(cx, |editor, cx| {
 6391        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6392        editor.insert("|one|two|three|", cx);
 6393    });
 6394    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6395    multi_buffer_editor.update(cx, |editor, cx| {
 6396        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6397            s.select_ranges(Some(60..70))
 6398        });
 6399        editor.insert("|four|five|six|", cx);
 6400    });
 6401    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6402
 6403    // First two buffers should be edited, but not the third one.
 6404    assert_eq!(
 6405        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6406        "a|one|two|three|aa\nbbbb\ncccc\n\nffff\ngggg\n\njjjj\nllll\nmmmm\nnnnn|four|five|six|\nr\n\nuuuu\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 6407    );
 6408    buffer_1.update(cx, |buffer, _| {
 6409        assert!(buffer.is_dirty());
 6410        assert_eq!(
 6411            buffer.text(),
 6412            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6413        )
 6414    });
 6415    buffer_2.update(cx, |buffer, _| {
 6416        assert!(buffer.is_dirty());
 6417        assert_eq!(
 6418            buffer.text(),
 6419            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6420        )
 6421    });
 6422    buffer_3.update(cx, |buffer, _| {
 6423        assert!(!buffer.is_dirty());
 6424        assert_eq!(buffer.text(), sample_text_3,)
 6425    });
 6426
 6427    cx.executor().start_waiting();
 6428    let save = multi_buffer_editor
 6429        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6430        .unwrap();
 6431
 6432    let fake_server = fake_servers.next().await.unwrap();
 6433    fake_server
 6434        .server
 6435        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6436            Ok(Some(vec![lsp::TextEdit::new(
 6437                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6438                format!("[{} formatted]", params.text_document.uri),
 6439            )]))
 6440        })
 6441        .detach();
 6442    save.await;
 6443
 6444    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6445    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6446    assert_eq!(
 6447        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6448        "a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 6449    );
 6450    buffer_1.update(cx, |buffer, _| {
 6451        assert!(!buffer.is_dirty());
 6452        assert_eq!(
 6453            buffer.text(),
 6454            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6455        )
 6456    });
 6457    buffer_2.update(cx, |buffer, _| {
 6458        assert!(!buffer.is_dirty());
 6459        assert_eq!(
 6460            buffer.text(),
 6461            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6462        )
 6463    });
 6464    buffer_3.update(cx, |buffer, _| {
 6465        assert!(!buffer.is_dirty());
 6466        assert_eq!(buffer.text(), sample_text_3,)
 6467    });
 6468}
 6469
 6470#[gpui::test]
 6471async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6472    init_test(cx, |_| {});
 6473
 6474    let fs = FakeFs::new(cx.executor());
 6475    fs.insert_file("/file.rs", Default::default()).await;
 6476
 6477    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6478
 6479    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6480    language_registry.add(rust_lang());
 6481    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6482        "Rust",
 6483        FakeLspAdapter {
 6484            capabilities: lsp::ServerCapabilities {
 6485                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6486                ..Default::default()
 6487            },
 6488            ..Default::default()
 6489        },
 6490    );
 6491
 6492    let buffer = project
 6493        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6494        .await
 6495        .unwrap();
 6496
 6497    cx.executor().start_waiting();
 6498    let fake_server = fake_servers.next().await.unwrap();
 6499
 6500    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6501    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6502    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6503    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6504
 6505    let save = editor
 6506        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6507        .unwrap();
 6508    fake_server
 6509        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6510            assert_eq!(
 6511                params.text_document.uri,
 6512                lsp::Url::from_file_path("/file.rs").unwrap()
 6513            );
 6514            assert_eq!(params.options.tab_size, 4);
 6515            Ok(Some(vec![lsp::TextEdit::new(
 6516                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6517                ", ".to_string(),
 6518            )]))
 6519        })
 6520        .next()
 6521        .await;
 6522    cx.executor().start_waiting();
 6523    save.await;
 6524    assert_eq!(
 6525        editor.update(cx, |editor, cx| editor.text(cx)),
 6526        "one, two\nthree\n"
 6527    );
 6528    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6529
 6530    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6531    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6532
 6533    // Ensure we can still save even if formatting hangs.
 6534    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6535        move |params, _| async move {
 6536            assert_eq!(
 6537                params.text_document.uri,
 6538                lsp::Url::from_file_path("/file.rs").unwrap()
 6539            );
 6540            futures::future::pending::<()>().await;
 6541            unreachable!()
 6542        },
 6543    );
 6544    let save = editor
 6545        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6546        .unwrap();
 6547    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6548    cx.executor().start_waiting();
 6549    save.await;
 6550    assert_eq!(
 6551        editor.update(cx, |editor, cx| editor.text(cx)),
 6552        "one\ntwo\nthree\n"
 6553    );
 6554    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6555
 6556    // For non-dirty buffer, no formatting request should be sent
 6557    let save = editor
 6558        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6559        .unwrap();
 6560    let _pending_format_request = fake_server
 6561        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6562            panic!("Should not be invoked on non-dirty buffer");
 6563        })
 6564        .next();
 6565    cx.executor().start_waiting();
 6566    save.await;
 6567
 6568    // Set Rust language override and assert overridden tabsize is sent to language server
 6569    update_test_language_settings(cx, |settings| {
 6570        settings.languages.insert(
 6571            "Rust".into(),
 6572            LanguageSettingsContent {
 6573                tab_size: NonZeroU32::new(8),
 6574                ..Default::default()
 6575            },
 6576        );
 6577    });
 6578
 6579    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6580    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6581    let save = editor
 6582        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6583        .unwrap();
 6584    fake_server
 6585        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6586            assert_eq!(
 6587                params.text_document.uri,
 6588                lsp::Url::from_file_path("/file.rs").unwrap()
 6589            );
 6590            assert_eq!(params.options.tab_size, 8);
 6591            Ok(Some(vec![]))
 6592        })
 6593        .next()
 6594        .await;
 6595    cx.executor().start_waiting();
 6596    save.await;
 6597}
 6598
 6599#[gpui::test]
 6600async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6601    init_test(cx, |settings| {
 6602        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 6603            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 6604        ))
 6605    });
 6606
 6607    let fs = FakeFs::new(cx.executor());
 6608    fs.insert_file("/file.rs", Default::default()).await;
 6609
 6610    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6611
 6612    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6613    language_registry.add(Arc::new(Language::new(
 6614        LanguageConfig {
 6615            name: "Rust".into(),
 6616            matcher: LanguageMatcher {
 6617                path_suffixes: vec!["rs".to_string()],
 6618                ..Default::default()
 6619            },
 6620            ..LanguageConfig::default()
 6621        },
 6622        Some(tree_sitter_rust::language()),
 6623    )));
 6624    update_test_language_settings(cx, |settings| {
 6625        // Enable Prettier formatting for the same buffer, and ensure
 6626        // LSP is called instead of Prettier.
 6627        settings.defaults.prettier = Some(PrettierSettings {
 6628            allowed: true,
 6629            ..PrettierSettings::default()
 6630        });
 6631    });
 6632    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6633        "Rust",
 6634        FakeLspAdapter {
 6635            capabilities: lsp::ServerCapabilities {
 6636                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6637                ..Default::default()
 6638            },
 6639            ..Default::default()
 6640        },
 6641    );
 6642
 6643    let buffer = project
 6644        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6645        .await
 6646        .unwrap();
 6647
 6648    cx.executor().start_waiting();
 6649    let fake_server = fake_servers.next().await.unwrap();
 6650
 6651    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6652    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6653    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6654
 6655    let format = editor
 6656        .update(cx, |editor, cx| {
 6657            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6658        })
 6659        .unwrap();
 6660    fake_server
 6661        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6662            assert_eq!(
 6663                params.text_document.uri,
 6664                lsp::Url::from_file_path("/file.rs").unwrap()
 6665            );
 6666            assert_eq!(params.options.tab_size, 4);
 6667            Ok(Some(vec![lsp::TextEdit::new(
 6668                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6669                ", ".to_string(),
 6670            )]))
 6671        })
 6672        .next()
 6673        .await;
 6674    cx.executor().start_waiting();
 6675    format.await;
 6676    assert_eq!(
 6677        editor.update(cx, |editor, cx| editor.text(cx)),
 6678        "one, two\nthree\n"
 6679    );
 6680
 6681    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6682    // Ensure we don't lock if formatting hangs.
 6683    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6684        assert_eq!(
 6685            params.text_document.uri,
 6686            lsp::Url::from_file_path("/file.rs").unwrap()
 6687        );
 6688        futures::future::pending::<()>().await;
 6689        unreachable!()
 6690    });
 6691    let format = editor
 6692        .update(cx, |editor, cx| {
 6693            editor.perform_format(project, FormatTrigger::Manual, cx)
 6694        })
 6695        .unwrap();
 6696    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6697    cx.executor().start_waiting();
 6698    format.await;
 6699    assert_eq!(
 6700        editor.update(cx, |editor, cx| editor.text(cx)),
 6701        "one\ntwo\nthree\n"
 6702    );
 6703}
 6704
 6705#[gpui::test]
 6706async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6707    init_test(cx, |_| {});
 6708
 6709    let mut cx = EditorLspTestContext::new_rust(
 6710        lsp::ServerCapabilities {
 6711            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6712            ..Default::default()
 6713        },
 6714        cx,
 6715    )
 6716    .await;
 6717
 6718    cx.set_state(indoc! {"
 6719        one.twoˇ
 6720    "});
 6721
 6722    // The format request takes a long time. When it completes, it inserts
 6723    // a newline and an indent before the `.`
 6724    cx.lsp
 6725        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6726            let executor = cx.background_executor().clone();
 6727            async move {
 6728                executor.timer(Duration::from_millis(100)).await;
 6729                Ok(Some(vec![lsp::TextEdit {
 6730                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6731                    new_text: "\n    ".into(),
 6732                }]))
 6733            }
 6734        });
 6735
 6736    // Submit a format request.
 6737    let format_1 = cx
 6738        .update_editor(|editor, cx| editor.format(&Format, cx))
 6739        .unwrap();
 6740    cx.executor().run_until_parked();
 6741
 6742    // Submit a second format request.
 6743    let format_2 = cx
 6744        .update_editor(|editor, cx| editor.format(&Format, cx))
 6745        .unwrap();
 6746    cx.executor().run_until_parked();
 6747
 6748    // Wait for both format requests to complete
 6749    cx.executor().advance_clock(Duration::from_millis(200));
 6750    cx.executor().start_waiting();
 6751    format_1.await.unwrap();
 6752    cx.executor().start_waiting();
 6753    format_2.await.unwrap();
 6754
 6755    // The formatting edits only happens once.
 6756    cx.assert_editor_state(indoc! {"
 6757        one
 6758            .twoˇ
 6759    "});
 6760}
 6761
 6762#[gpui::test]
 6763async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6764    init_test(cx, |settings| {
 6765        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 6766    });
 6767
 6768    let mut cx = EditorLspTestContext::new_rust(
 6769        lsp::ServerCapabilities {
 6770            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6771            ..Default::default()
 6772        },
 6773        cx,
 6774    )
 6775    .await;
 6776
 6777    // Set up a buffer white some trailing whitespace and no trailing newline.
 6778    cx.set_state(
 6779        &[
 6780            "one ",   //
 6781            "twoˇ",   //
 6782            "three ", //
 6783            "four",   //
 6784        ]
 6785        .join("\n"),
 6786    );
 6787
 6788    // Submit a format request.
 6789    let format = cx
 6790        .update_editor(|editor, cx| editor.format(&Format, cx))
 6791        .unwrap();
 6792
 6793    // Record which buffer changes have been sent to the language server
 6794    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6795    cx.lsp
 6796        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6797            let buffer_changes = buffer_changes.clone();
 6798            move |params, _| {
 6799                buffer_changes.lock().extend(
 6800                    params
 6801                        .content_changes
 6802                        .into_iter()
 6803                        .map(|e| (e.range.unwrap(), e.text)),
 6804                );
 6805            }
 6806        });
 6807
 6808    // Handle formatting requests to the language server.
 6809    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6810        let buffer_changes = buffer_changes.clone();
 6811        move |_, _| {
 6812            // When formatting is requested, trailing whitespace has already been stripped,
 6813            // and the trailing newline has already been added.
 6814            assert_eq!(
 6815                &buffer_changes.lock()[1..],
 6816                &[
 6817                    (
 6818                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6819                        "".into()
 6820                    ),
 6821                    (
 6822                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6823                        "".into()
 6824                    ),
 6825                    (
 6826                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6827                        "\n".into()
 6828                    ),
 6829                ]
 6830            );
 6831
 6832            // Insert blank lines between each line of the buffer.
 6833            async move {
 6834                Ok(Some(vec![
 6835                    lsp::TextEdit {
 6836                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6837                        new_text: "\n".into(),
 6838                    },
 6839                    lsp::TextEdit {
 6840                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6841                        new_text: "\n".into(),
 6842                    },
 6843                ]))
 6844            }
 6845        }
 6846    });
 6847
 6848    // After formatting the buffer, the trailing whitespace is stripped,
 6849    // a newline is appended, and the edits provided by the language server
 6850    // have been applied.
 6851    format.await.unwrap();
 6852    cx.assert_editor_state(
 6853        &[
 6854            "one",   //
 6855            "",      //
 6856            "twoˇ",  //
 6857            "",      //
 6858            "three", //
 6859            "four",  //
 6860            "",      //
 6861        ]
 6862        .join("\n"),
 6863    );
 6864
 6865    // Undoing the formatting undoes the trailing whitespace removal, the
 6866    // trailing newline, and the LSP edits.
 6867    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6868    cx.assert_editor_state(
 6869        &[
 6870            "one ",   //
 6871            "twoˇ",   //
 6872            "three ", //
 6873            "four",   //
 6874        ]
 6875        .join("\n"),
 6876    );
 6877}
 6878
 6879#[gpui::test]
 6880async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 6881    cx: &mut gpui::TestAppContext,
 6882) {
 6883    init_test(cx, |_| {});
 6884
 6885    cx.update(|cx| {
 6886        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6887            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6888                settings.auto_signature_help = Some(true);
 6889            });
 6890        });
 6891    });
 6892
 6893    let mut cx = EditorLspTestContext::new_rust(
 6894        lsp::ServerCapabilities {
 6895            signature_help_provider: Some(lsp::SignatureHelpOptions {
 6896                ..Default::default()
 6897            }),
 6898            ..Default::default()
 6899        },
 6900        cx,
 6901    )
 6902    .await;
 6903
 6904    let language = Language::new(
 6905        LanguageConfig {
 6906            name: "Rust".into(),
 6907            brackets: BracketPairConfig {
 6908                pairs: vec![
 6909                    BracketPair {
 6910                        start: "{".to_string(),
 6911                        end: "}".to_string(),
 6912                        close: true,
 6913                        surround: true,
 6914                        newline: true,
 6915                    },
 6916                    BracketPair {
 6917                        start: "(".to_string(),
 6918                        end: ")".to_string(),
 6919                        close: true,
 6920                        surround: true,
 6921                        newline: true,
 6922                    },
 6923                    BracketPair {
 6924                        start: "/*".to_string(),
 6925                        end: " */".to_string(),
 6926                        close: true,
 6927                        surround: true,
 6928                        newline: true,
 6929                    },
 6930                    BracketPair {
 6931                        start: "[".to_string(),
 6932                        end: "]".to_string(),
 6933                        close: false,
 6934                        surround: false,
 6935                        newline: true,
 6936                    },
 6937                    BracketPair {
 6938                        start: "\"".to_string(),
 6939                        end: "\"".to_string(),
 6940                        close: true,
 6941                        surround: true,
 6942                        newline: false,
 6943                    },
 6944                    BracketPair {
 6945                        start: "<".to_string(),
 6946                        end: ">".to_string(),
 6947                        close: false,
 6948                        surround: true,
 6949                        newline: true,
 6950                    },
 6951                ],
 6952                ..Default::default()
 6953            },
 6954            autoclose_before: "})]".to_string(),
 6955            ..Default::default()
 6956        },
 6957        Some(tree_sitter_rust::language()),
 6958    );
 6959    let language = Arc::new(language);
 6960
 6961    cx.language_registry().add(language.clone());
 6962    cx.update_buffer(|buffer, cx| {
 6963        buffer.set_language(Some(language), cx);
 6964    });
 6965
 6966    cx.set_state(
 6967        &r#"
 6968            fn main() {
 6969                sampleˇ
 6970            }
 6971        "#
 6972        .unindent(),
 6973    );
 6974
 6975    cx.update_editor(|view, cx| {
 6976        view.handle_input("(", cx);
 6977    });
 6978    cx.assert_editor_state(
 6979        &"
 6980            fn main() {
 6981                sample(ˇ)
 6982            }
 6983        "
 6984        .unindent(),
 6985    );
 6986
 6987    let mocked_response = lsp::SignatureHelp {
 6988        signatures: vec![lsp::SignatureInformation {
 6989            label: "fn sample(param1: u8, param2: u8)".to_string(),
 6990            documentation: None,
 6991            parameters: Some(vec![
 6992                lsp::ParameterInformation {
 6993                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 6994                    documentation: None,
 6995                },
 6996                lsp::ParameterInformation {
 6997                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 6998                    documentation: None,
 6999                },
 7000            ]),
 7001            active_parameter: None,
 7002        }],
 7003        active_signature: Some(0),
 7004        active_parameter: Some(0),
 7005    };
 7006    handle_signature_help_request(&mut cx, mocked_response).await;
 7007
 7008    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7009        .await;
 7010
 7011    cx.editor(|editor, _| {
 7012        let signature_help_state = editor.signature_help_state.popover().cloned();
 7013        assert!(signature_help_state.is_some());
 7014        let ParsedMarkdown {
 7015            text, highlights, ..
 7016        } = signature_help_state.unwrap().parsed_content;
 7017        assert_eq!(text, "param1: u8, param2: u8");
 7018        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7019    });
 7020}
 7021
 7022#[gpui::test]
 7023async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7024    init_test(cx, |_| {});
 7025
 7026    cx.update(|cx| {
 7027        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7028            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7029                settings.auto_signature_help = Some(false);
 7030                settings.show_signature_help_after_edits = Some(false);
 7031            });
 7032        });
 7033    });
 7034
 7035    let mut cx = EditorLspTestContext::new_rust(
 7036        lsp::ServerCapabilities {
 7037            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7038                ..Default::default()
 7039            }),
 7040            ..Default::default()
 7041        },
 7042        cx,
 7043    )
 7044    .await;
 7045
 7046    let language = Language::new(
 7047        LanguageConfig {
 7048            name: "Rust".into(),
 7049            brackets: BracketPairConfig {
 7050                pairs: vec![
 7051                    BracketPair {
 7052                        start: "{".to_string(),
 7053                        end: "}".to_string(),
 7054                        close: true,
 7055                        surround: true,
 7056                        newline: true,
 7057                    },
 7058                    BracketPair {
 7059                        start: "(".to_string(),
 7060                        end: ")".to_string(),
 7061                        close: true,
 7062                        surround: true,
 7063                        newline: true,
 7064                    },
 7065                    BracketPair {
 7066                        start: "/*".to_string(),
 7067                        end: " */".to_string(),
 7068                        close: true,
 7069                        surround: true,
 7070                        newline: true,
 7071                    },
 7072                    BracketPair {
 7073                        start: "[".to_string(),
 7074                        end: "]".to_string(),
 7075                        close: false,
 7076                        surround: false,
 7077                        newline: true,
 7078                    },
 7079                    BracketPair {
 7080                        start: "\"".to_string(),
 7081                        end: "\"".to_string(),
 7082                        close: true,
 7083                        surround: true,
 7084                        newline: false,
 7085                    },
 7086                    BracketPair {
 7087                        start: "<".to_string(),
 7088                        end: ">".to_string(),
 7089                        close: false,
 7090                        surround: true,
 7091                        newline: true,
 7092                    },
 7093                ],
 7094                ..Default::default()
 7095            },
 7096            autoclose_before: "})]".to_string(),
 7097            ..Default::default()
 7098        },
 7099        Some(tree_sitter_rust::language()),
 7100    );
 7101    let language = Arc::new(language);
 7102
 7103    cx.language_registry().add(language.clone());
 7104    cx.update_buffer(|buffer, cx| {
 7105        buffer.set_language(Some(language), cx);
 7106    });
 7107
 7108    // Ensure that signature_help is not called when no signature help is enabled.
 7109    cx.set_state(
 7110        &r#"
 7111            fn main() {
 7112                sampleˇ
 7113            }
 7114        "#
 7115        .unindent(),
 7116    );
 7117    cx.update_editor(|view, cx| {
 7118        view.handle_input("(", cx);
 7119    });
 7120    cx.assert_editor_state(
 7121        &"
 7122            fn main() {
 7123                sample(ˇ)
 7124            }
 7125        "
 7126        .unindent(),
 7127    );
 7128    cx.editor(|editor, _| {
 7129        assert!(editor.signature_help_state.task().is_none());
 7130    });
 7131
 7132    let mocked_response = lsp::SignatureHelp {
 7133        signatures: vec![lsp::SignatureInformation {
 7134            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7135            documentation: None,
 7136            parameters: Some(vec![
 7137                lsp::ParameterInformation {
 7138                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7139                    documentation: None,
 7140                },
 7141                lsp::ParameterInformation {
 7142                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7143                    documentation: None,
 7144                },
 7145            ]),
 7146            active_parameter: None,
 7147        }],
 7148        active_signature: Some(0),
 7149        active_parameter: Some(0),
 7150    };
 7151
 7152    // Ensure that signature_help is called when enabled afte edits
 7153    cx.update(|cx| {
 7154        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7155            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7156                settings.auto_signature_help = Some(false);
 7157                settings.show_signature_help_after_edits = Some(true);
 7158            });
 7159        });
 7160    });
 7161    cx.set_state(
 7162        &r#"
 7163            fn main() {
 7164                sampleˇ
 7165            }
 7166        "#
 7167        .unindent(),
 7168    );
 7169    cx.update_editor(|view, cx| {
 7170        view.handle_input("(", cx);
 7171    });
 7172    cx.assert_editor_state(
 7173        &"
 7174            fn main() {
 7175                sample(ˇ)
 7176            }
 7177        "
 7178        .unindent(),
 7179    );
 7180    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7181    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7182        .await;
 7183    cx.update_editor(|editor, _| {
 7184        let signature_help_state = editor.signature_help_state.popover().cloned();
 7185        assert!(signature_help_state.is_some());
 7186        let ParsedMarkdown {
 7187            text, highlights, ..
 7188        } = signature_help_state.unwrap().parsed_content;
 7189        assert_eq!(text, "param1: u8, param2: u8");
 7190        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7191        editor.signature_help_state = SignatureHelpState::default();
 7192    });
 7193
 7194    // Ensure that signature_help is called when auto signature help override is enabled
 7195    cx.update(|cx| {
 7196        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7197            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7198                settings.auto_signature_help = Some(true);
 7199                settings.show_signature_help_after_edits = Some(false);
 7200            });
 7201        });
 7202    });
 7203    cx.set_state(
 7204        &r#"
 7205            fn main() {
 7206                sampleˇ
 7207            }
 7208        "#
 7209        .unindent(),
 7210    );
 7211    cx.update_editor(|view, cx| {
 7212        view.handle_input("(", cx);
 7213    });
 7214    cx.assert_editor_state(
 7215        &"
 7216            fn main() {
 7217                sample(ˇ)
 7218            }
 7219        "
 7220        .unindent(),
 7221    );
 7222    handle_signature_help_request(&mut cx, mocked_response).await;
 7223    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7224        .await;
 7225    cx.editor(|editor, _| {
 7226        let signature_help_state = editor.signature_help_state.popover().cloned();
 7227        assert!(signature_help_state.is_some());
 7228        let ParsedMarkdown {
 7229            text, highlights, ..
 7230        } = signature_help_state.unwrap().parsed_content;
 7231        assert_eq!(text, "param1: u8, param2: u8");
 7232        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7233    });
 7234}
 7235
 7236#[gpui::test]
 7237async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7238    init_test(cx, |_| {});
 7239    cx.update(|cx| {
 7240        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7241            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7242                settings.auto_signature_help = Some(true);
 7243            });
 7244        });
 7245    });
 7246
 7247    let mut cx = EditorLspTestContext::new_rust(
 7248        lsp::ServerCapabilities {
 7249            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7250                ..Default::default()
 7251            }),
 7252            ..Default::default()
 7253        },
 7254        cx,
 7255    )
 7256    .await;
 7257
 7258    // A test that directly calls `show_signature_help`
 7259    cx.update_editor(|editor, cx| {
 7260        editor.show_signature_help(&ShowSignatureHelp, cx);
 7261    });
 7262
 7263    let mocked_response = lsp::SignatureHelp {
 7264        signatures: vec![lsp::SignatureInformation {
 7265            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7266            documentation: None,
 7267            parameters: Some(vec![
 7268                lsp::ParameterInformation {
 7269                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7270                    documentation: None,
 7271                },
 7272                lsp::ParameterInformation {
 7273                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7274                    documentation: None,
 7275                },
 7276            ]),
 7277            active_parameter: None,
 7278        }],
 7279        active_signature: Some(0),
 7280        active_parameter: Some(0),
 7281    };
 7282    handle_signature_help_request(&mut cx, mocked_response).await;
 7283
 7284    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7285        .await;
 7286
 7287    cx.editor(|editor, _| {
 7288        let signature_help_state = editor.signature_help_state.popover().cloned();
 7289        assert!(signature_help_state.is_some());
 7290        let ParsedMarkdown {
 7291            text, highlights, ..
 7292        } = signature_help_state.unwrap().parsed_content;
 7293        assert_eq!(text, "param1: u8, param2: u8");
 7294        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7295    });
 7296
 7297    // When exiting outside from inside the brackets, `signature_help` is closed.
 7298    cx.set_state(indoc! {"
 7299        fn main() {
 7300            sample(ˇ);
 7301        }
 7302
 7303        fn sample(param1: u8, param2: u8) {}
 7304    "});
 7305
 7306    cx.update_editor(|editor, cx| {
 7307        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7308    });
 7309
 7310    let mocked_response = lsp::SignatureHelp {
 7311        signatures: Vec::new(),
 7312        active_signature: None,
 7313        active_parameter: None,
 7314    };
 7315    handle_signature_help_request(&mut cx, mocked_response).await;
 7316
 7317    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7318        .await;
 7319
 7320    cx.editor(|editor, _| {
 7321        assert!(!editor.signature_help_state.is_shown());
 7322    });
 7323
 7324    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7325    cx.set_state(indoc! {"
 7326        fn main() {
 7327            sample(ˇ);
 7328        }
 7329
 7330        fn sample(param1: u8, param2: u8) {}
 7331    "});
 7332
 7333    let mocked_response = lsp::SignatureHelp {
 7334        signatures: vec![lsp::SignatureInformation {
 7335            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7336            documentation: None,
 7337            parameters: Some(vec![
 7338                lsp::ParameterInformation {
 7339                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7340                    documentation: None,
 7341                },
 7342                lsp::ParameterInformation {
 7343                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7344                    documentation: None,
 7345                },
 7346            ]),
 7347            active_parameter: None,
 7348        }],
 7349        active_signature: Some(0),
 7350        active_parameter: Some(0),
 7351    };
 7352    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7353    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7354        .await;
 7355    cx.editor(|editor, _| {
 7356        assert!(editor.signature_help_state.is_shown());
 7357    });
 7358
 7359    // Restore the popover with more parameter input
 7360    cx.set_state(indoc! {"
 7361        fn main() {
 7362            sample(param1, param2ˇ);
 7363        }
 7364
 7365        fn sample(param1: u8, param2: u8) {}
 7366    "});
 7367
 7368    let mocked_response = lsp::SignatureHelp {
 7369        signatures: vec![lsp::SignatureInformation {
 7370            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7371            documentation: None,
 7372            parameters: Some(vec![
 7373                lsp::ParameterInformation {
 7374                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7375                    documentation: None,
 7376                },
 7377                lsp::ParameterInformation {
 7378                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7379                    documentation: None,
 7380                },
 7381            ]),
 7382            active_parameter: None,
 7383        }],
 7384        active_signature: Some(0),
 7385        active_parameter: Some(1),
 7386    };
 7387    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7388    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7389        .await;
 7390
 7391    // When selecting a range, the popover is gone.
 7392    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7393    cx.update_editor(|editor, cx| {
 7394        editor.change_selections(None, cx, |s| {
 7395            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7396        })
 7397    });
 7398    cx.assert_editor_state(indoc! {"
 7399        fn main() {
 7400            sample(param1, «ˇparam2»);
 7401        }
 7402
 7403        fn sample(param1: u8, param2: u8) {}
 7404    "});
 7405    cx.editor(|editor, _| {
 7406        assert!(!editor.signature_help_state.is_shown());
 7407    });
 7408
 7409    // When unselecting again, the popover is back if within the brackets.
 7410    cx.update_editor(|editor, cx| {
 7411        editor.change_selections(None, cx, |s| {
 7412            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7413        })
 7414    });
 7415    cx.assert_editor_state(indoc! {"
 7416        fn main() {
 7417            sample(param1, ˇparam2);
 7418        }
 7419
 7420        fn sample(param1: u8, param2: u8) {}
 7421    "});
 7422    handle_signature_help_request(&mut cx, mocked_response).await;
 7423    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7424        .await;
 7425    cx.editor(|editor, _| {
 7426        assert!(editor.signature_help_state.is_shown());
 7427    });
 7428
 7429    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7430    cx.update_editor(|editor, cx| {
 7431        editor.change_selections(None, cx, |s| {
 7432            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7433            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7434        })
 7435    });
 7436    cx.assert_editor_state(indoc! {"
 7437        fn main() {
 7438            sample(param1, ˇparam2);
 7439        }
 7440
 7441        fn sample(param1: u8, param2: u8) {}
 7442    "});
 7443
 7444    let mocked_response = lsp::SignatureHelp {
 7445        signatures: vec![lsp::SignatureInformation {
 7446            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7447            documentation: None,
 7448            parameters: Some(vec![
 7449                lsp::ParameterInformation {
 7450                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7451                    documentation: None,
 7452                },
 7453                lsp::ParameterInformation {
 7454                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7455                    documentation: None,
 7456                },
 7457            ]),
 7458            active_parameter: None,
 7459        }],
 7460        active_signature: Some(0),
 7461        active_parameter: Some(1),
 7462    };
 7463    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7464    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7465        .await;
 7466    cx.update_editor(|editor, cx| {
 7467        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7468    });
 7469    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7470        .await;
 7471    cx.update_editor(|editor, cx| {
 7472        editor.change_selections(None, cx, |s| {
 7473            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7474        })
 7475    });
 7476    cx.assert_editor_state(indoc! {"
 7477        fn main() {
 7478            sample(param1, «ˇparam2»);
 7479        }
 7480
 7481        fn sample(param1: u8, param2: u8) {}
 7482    "});
 7483    cx.update_editor(|editor, cx| {
 7484        editor.change_selections(None, cx, |s| {
 7485            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7486        })
 7487    });
 7488    cx.assert_editor_state(indoc! {"
 7489        fn main() {
 7490            sample(param1, ˇparam2);
 7491        }
 7492
 7493        fn sample(param1: u8, param2: u8) {}
 7494    "});
 7495    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7496        .await;
 7497}
 7498
 7499#[gpui::test]
 7500async fn test_completion(cx: &mut gpui::TestAppContext) {
 7501    init_test(cx, |_| {});
 7502
 7503    let mut cx = EditorLspTestContext::new_rust(
 7504        lsp::ServerCapabilities {
 7505            completion_provider: Some(lsp::CompletionOptions {
 7506                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7507                resolve_provider: Some(true),
 7508                ..Default::default()
 7509            }),
 7510            ..Default::default()
 7511        },
 7512        cx,
 7513    )
 7514    .await;
 7515    let counter = Arc::new(AtomicUsize::new(0));
 7516
 7517    cx.set_state(indoc! {"
 7518        oneˇ
 7519        two
 7520        three
 7521    "});
 7522    cx.simulate_keystroke(".");
 7523    handle_completion_request(
 7524        &mut cx,
 7525        indoc! {"
 7526            one.|<>
 7527            two
 7528            three
 7529        "},
 7530        vec!["first_completion", "second_completion"],
 7531        counter.clone(),
 7532    )
 7533    .await;
 7534    cx.condition(|editor, _| editor.context_menu_visible())
 7535        .await;
 7536    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7537
 7538    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7539        editor.context_menu_next(&Default::default(), cx);
 7540        editor
 7541            .confirm_completion(&ConfirmCompletion::default(), cx)
 7542            .unwrap()
 7543    });
 7544    cx.assert_editor_state(indoc! {"
 7545        one.second_completionˇ
 7546        two
 7547        three
 7548    "});
 7549
 7550    handle_resolve_completion_request(
 7551        &mut cx,
 7552        Some(vec![
 7553            (
 7554                //This overlaps with the primary completion edit which is
 7555                //misbehavior from the LSP spec, test that we filter it out
 7556                indoc! {"
 7557                    one.second_ˇcompletion
 7558                    two
 7559                    threeˇ
 7560                "},
 7561                "overlapping additional edit",
 7562            ),
 7563            (
 7564                indoc! {"
 7565                    one.second_completion
 7566                    two
 7567                    threeˇ
 7568                "},
 7569                "\nadditional edit",
 7570            ),
 7571        ]),
 7572    )
 7573    .await;
 7574    apply_additional_edits.await.unwrap();
 7575    cx.assert_editor_state(indoc! {"
 7576        one.second_completionˇ
 7577        two
 7578        three
 7579        additional edit
 7580    "});
 7581
 7582    cx.set_state(indoc! {"
 7583        one.second_completion
 7584        twoˇ
 7585        threeˇ
 7586        additional edit
 7587    "});
 7588    cx.simulate_keystroke(" ");
 7589    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7590    cx.simulate_keystroke("s");
 7591    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7592
 7593    cx.assert_editor_state(indoc! {"
 7594        one.second_completion
 7595        two sˇ
 7596        three sˇ
 7597        additional edit
 7598    "});
 7599    handle_completion_request(
 7600        &mut cx,
 7601        indoc! {"
 7602            one.second_completion
 7603            two s
 7604            three <s|>
 7605            additional edit
 7606        "},
 7607        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7608        counter.clone(),
 7609    )
 7610    .await;
 7611    cx.condition(|editor, _| editor.context_menu_visible())
 7612        .await;
 7613    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 7614
 7615    cx.simulate_keystroke("i");
 7616
 7617    handle_completion_request(
 7618        &mut cx,
 7619        indoc! {"
 7620            one.second_completion
 7621            two si
 7622            three <si|>
 7623            additional edit
 7624        "},
 7625        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7626        counter.clone(),
 7627    )
 7628    .await;
 7629    cx.condition(|editor, _| editor.context_menu_visible())
 7630        .await;
 7631    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 7632
 7633    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7634        editor
 7635            .confirm_completion(&ConfirmCompletion::default(), cx)
 7636            .unwrap()
 7637    });
 7638    cx.assert_editor_state(indoc! {"
 7639        one.second_completion
 7640        two sixth_completionˇ
 7641        three sixth_completionˇ
 7642        additional edit
 7643    "});
 7644
 7645    handle_resolve_completion_request(&mut cx, None).await;
 7646    apply_additional_edits.await.unwrap();
 7647
 7648    _ = cx.update(|cx| {
 7649        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7650            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7651                settings.show_completions_on_input = Some(false);
 7652            });
 7653        })
 7654    });
 7655    cx.set_state("editorˇ");
 7656    cx.simulate_keystroke(".");
 7657    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7658    cx.simulate_keystroke("c");
 7659    cx.simulate_keystroke("l");
 7660    cx.simulate_keystroke("o");
 7661    cx.assert_editor_state("editor.cloˇ");
 7662    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7663    cx.update_editor(|editor, cx| {
 7664        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 7665    });
 7666    handle_completion_request(
 7667        &mut cx,
 7668        "editor.<clo|>",
 7669        vec!["close", "clobber"],
 7670        counter.clone(),
 7671    )
 7672    .await;
 7673    cx.condition(|editor, _| editor.context_menu_visible())
 7674        .await;
 7675    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 7676
 7677    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7678        editor
 7679            .confirm_completion(&ConfirmCompletion::default(), cx)
 7680            .unwrap()
 7681    });
 7682    cx.assert_editor_state("editor.closeˇ");
 7683    handle_resolve_completion_request(&mut cx, None).await;
 7684    apply_additional_edits.await.unwrap();
 7685}
 7686
 7687#[gpui::test]
 7688async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 7689    init_test(cx, |_| {});
 7690    let mut cx = EditorLspTestContext::new_rust(
 7691        lsp::ServerCapabilities {
 7692            completion_provider: Some(lsp::CompletionOptions {
 7693                trigger_characters: Some(vec![".".to_string()]),
 7694                ..Default::default()
 7695            }),
 7696            ..Default::default()
 7697        },
 7698        cx,
 7699    )
 7700    .await;
 7701    cx.lsp
 7702        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 7703            Ok(Some(lsp::CompletionResponse::Array(vec![
 7704                lsp::CompletionItem {
 7705                    label: "first".into(),
 7706                    ..Default::default()
 7707                },
 7708                lsp::CompletionItem {
 7709                    label: "last".into(),
 7710                    ..Default::default()
 7711                },
 7712            ])))
 7713        });
 7714    cx.set_state("variableˇ");
 7715    cx.simulate_keystroke(".");
 7716    cx.executor().run_until_parked();
 7717
 7718    cx.update_editor(|editor, _| {
 7719        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7720            assert_eq!(
 7721                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 7722                &["first", "last"]
 7723            );
 7724        } else {
 7725            panic!("expected completion menu to be open");
 7726        }
 7727    });
 7728
 7729    cx.update_editor(|editor, cx| {
 7730        editor.move_page_down(&MovePageDown::default(), cx);
 7731        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7732            assert!(
 7733                menu.selected_item == 1,
 7734                "expected PageDown to select the last item from the context menu"
 7735            );
 7736        } else {
 7737            panic!("expected completion menu to stay open after PageDown");
 7738        }
 7739    });
 7740
 7741    cx.update_editor(|editor, cx| {
 7742        editor.move_page_up(&MovePageUp::default(), cx);
 7743        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 7744            assert!(
 7745                menu.selected_item == 0,
 7746                "expected PageUp to select the first item from the context menu"
 7747            );
 7748        } else {
 7749            panic!("expected completion menu to stay open after PageUp");
 7750        }
 7751    });
 7752}
 7753
 7754#[gpui::test]
 7755async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 7756    init_test(cx, |_| {});
 7757
 7758    let mut cx = EditorLspTestContext::new_rust(
 7759        lsp::ServerCapabilities {
 7760            completion_provider: Some(lsp::CompletionOptions {
 7761                trigger_characters: Some(vec![".".to_string()]),
 7762                resolve_provider: Some(true),
 7763                ..Default::default()
 7764            }),
 7765            ..Default::default()
 7766        },
 7767        cx,
 7768    )
 7769    .await;
 7770
 7771    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 7772    cx.simulate_keystroke(".");
 7773    let completion_item = lsp::CompletionItem {
 7774        label: "Some".into(),
 7775        kind: Some(lsp::CompletionItemKind::SNIPPET),
 7776        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 7777        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 7778            kind: lsp::MarkupKind::Markdown,
 7779            value: "```rust\nSome(2)\n```".to_string(),
 7780        })),
 7781        deprecated: Some(false),
 7782        sort_text: Some("Some".to_string()),
 7783        filter_text: Some("Some".to_string()),
 7784        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 7785        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 7786            range: lsp::Range {
 7787                start: lsp::Position {
 7788                    line: 0,
 7789                    character: 22,
 7790                },
 7791                end: lsp::Position {
 7792                    line: 0,
 7793                    character: 22,
 7794                },
 7795            },
 7796            new_text: "Some(2)".to_string(),
 7797        })),
 7798        additional_text_edits: Some(vec![lsp::TextEdit {
 7799            range: lsp::Range {
 7800                start: lsp::Position {
 7801                    line: 0,
 7802                    character: 20,
 7803                },
 7804                end: lsp::Position {
 7805                    line: 0,
 7806                    character: 22,
 7807                },
 7808            },
 7809            new_text: "".to_string(),
 7810        }]),
 7811        ..Default::default()
 7812    };
 7813
 7814    let closure_completion_item = completion_item.clone();
 7815    let counter = Arc::new(AtomicUsize::new(0));
 7816    let counter_clone = counter.clone();
 7817    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 7818        let task_completion_item = closure_completion_item.clone();
 7819        counter_clone.fetch_add(1, atomic::Ordering::Release);
 7820        async move {
 7821            Ok(Some(lsp::CompletionResponse::Array(vec![
 7822                task_completion_item,
 7823            ])))
 7824        }
 7825    });
 7826
 7827    cx.condition(|editor, _| editor.context_menu_visible())
 7828        .await;
 7829    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 7830    assert!(request.next().await.is_some());
 7831    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7832
 7833    cx.simulate_keystroke("S");
 7834    cx.simulate_keystroke("o");
 7835    cx.simulate_keystroke("m");
 7836    cx.condition(|editor, _| editor.context_menu_visible())
 7837        .await;
 7838    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 7839    assert!(request.next().await.is_some());
 7840    assert!(request.next().await.is_some());
 7841    assert!(request.next().await.is_some());
 7842    request.close();
 7843    assert!(request.next().await.is_none());
 7844    assert_eq!(
 7845        counter.load(atomic::Ordering::Acquire),
 7846        4,
 7847        "With the completions menu open, only one LSP request should happen per input"
 7848    );
 7849}
 7850
 7851#[gpui::test]
 7852async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 7853    init_test(cx, |_| {});
 7854    let mut cx = EditorTestContext::new(cx).await;
 7855    let language = Arc::new(Language::new(
 7856        LanguageConfig {
 7857            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 7858            ..Default::default()
 7859        },
 7860        Some(tree_sitter_rust::language()),
 7861    ));
 7862    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 7863
 7864    // If multiple selections intersect a line, the line is only toggled once.
 7865    cx.set_state(indoc! {"
 7866        fn a() {
 7867            «//b();
 7868            ˇ»// «c();
 7869            //ˇ»  d();
 7870        }
 7871    "});
 7872
 7873    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7874
 7875    cx.assert_editor_state(indoc! {"
 7876        fn a() {
 7877            «b();
 7878            c();
 7879            ˇ» d();
 7880        }
 7881    "});
 7882
 7883    // The comment prefix is inserted at the same column for every line in a
 7884    // selection.
 7885    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7886
 7887    cx.assert_editor_state(indoc! {"
 7888        fn a() {
 7889            // «b();
 7890            // c();
 7891            ˇ»//  d();
 7892        }
 7893    "});
 7894
 7895    // If a selection ends at the beginning of a line, that line is not toggled.
 7896    cx.set_selections_state(indoc! {"
 7897        fn a() {
 7898            // b();
 7899            «// c();
 7900        ˇ»    //  d();
 7901        }
 7902    "});
 7903
 7904    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7905
 7906    cx.assert_editor_state(indoc! {"
 7907        fn a() {
 7908            // b();
 7909            «c();
 7910        ˇ»    //  d();
 7911        }
 7912    "});
 7913
 7914    // If a selection span a single line and is empty, the line is toggled.
 7915    cx.set_state(indoc! {"
 7916        fn a() {
 7917            a();
 7918            b();
 7919        ˇ
 7920        }
 7921    "});
 7922
 7923    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7924
 7925    cx.assert_editor_state(indoc! {"
 7926        fn a() {
 7927            a();
 7928            b();
 7929        //•ˇ
 7930        }
 7931    "});
 7932
 7933    // If a selection span multiple lines, empty lines are not toggled.
 7934    cx.set_state(indoc! {"
 7935        fn a() {
 7936            «a();
 7937
 7938            c();ˇ»
 7939        }
 7940    "});
 7941
 7942    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7943
 7944    cx.assert_editor_state(indoc! {"
 7945        fn a() {
 7946            // «a();
 7947
 7948            // c();ˇ»
 7949        }
 7950    "});
 7951
 7952    // If a selection includes multiple comment prefixes, all lines are uncommented.
 7953    cx.set_state(indoc! {"
 7954        fn a() {
 7955            «// a();
 7956            /// b();
 7957            //! c();ˇ»
 7958        }
 7959    "});
 7960
 7961    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 7962
 7963    cx.assert_editor_state(indoc! {"
 7964        fn a() {
 7965            «a();
 7966            b();
 7967            c();ˇ»
 7968        }
 7969    "});
 7970}
 7971
 7972#[gpui::test]
 7973async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 7974    init_test(cx, |_| {});
 7975
 7976    let language = Arc::new(Language::new(
 7977        LanguageConfig {
 7978            line_comments: vec!["// ".into()],
 7979            ..Default::default()
 7980        },
 7981        Some(tree_sitter_rust::language()),
 7982    ));
 7983
 7984    let mut cx = EditorTestContext::new(cx).await;
 7985
 7986    cx.language_registry().add(language.clone());
 7987    cx.update_buffer(|buffer, cx| {
 7988        buffer.set_language(Some(language), cx);
 7989    });
 7990
 7991    let toggle_comments = &ToggleComments {
 7992        advance_downwards: true,
 7993    };
 7994
 7995    // Single cursor on one line -> advance
 7996    // Cursor moves horizontally 3 characters as well on non-blank line
 7997    cx.set_state(indoc!(
 7998        "fn a() {
 7999             ˇdog();
 8000             cat();
 8001        }"
 8002    ));
 8003    cx.update_editor(|editor, cx| {
 8004        editor.toggle_comments(toggle_comments, cx);
 8005    });
 8006    cx.assert_editor_state(indoc!(
 8007        "fn a() {
 8008             // dog();
 8009             catˇ();
 8010        }"
 8011    ));
 8012
 8013    // Single selection on one line -> don't advance
 8014    cx.set_state(indoc!(
 8015        "fn a() {
 8016             «dog()ˇ»;
 8017             cat();
 8018        }"
 8019    ));
 8020    cx.update_editor(|editor, cx| {
 8021        editor.toggle_comments(toggle_comments, cx);
 8022    });
 8023    cx.assert_editor_state(indoc!(
 8024        "fn a() {
 8025             // «dog()ˇ»;
 8026             cat();
 8027        }"
 8028    ));
 8029
 8030    // Multiple cursors on one line -> advance
 8031    cx.set_state(indoc!(
 8032        "fn a() {
 8033             ˇdˇog();
 8034             cat();
 8035        }"
 8036    ));
 8037    cx.update_editor(|editor, cx| {
 8038        editor.toggle_comments(toggle_comments, cx);
 8039    });
 8040    cx.assert_editor_state(indoc!(
 8041        "fn a() {
 8042             // dog();
 8043             catˇ(ˇ);
 8044        }"
 8045    ));
 8046
 8047    // Multiple cursors on one line, with selection -> don't advance
 8048    cx.set_state(indoc!(
 8049        "fn a() {
 8050             ˇdˇog«()ˇ»;
 8051             cat();
 8052        }"
 8053    ));
 8054    cx.update_editor(|editor, cx| {
 8055        editor.toggle_comments(toggle_comments, cx);
 8056    });
 8057    cx.assert_editor_state(indoc!(
 8058        "fn a() {
 8059             // ˇdˇog«()ˇ»;
 8060             cat();
 8061        }"
 8062    ));
 8063
 8064    // Single cursor on one line -> advance
 8065    // Cursor moves to column 0 on blank line
 8066    cx.set_state(indoc!(
 8067        "fn a() {
 8068             ˇdog();
 8069
 8070             cat();
 8071        }"
 8072    ));
 8073    cx.update_editor(|editor, cx| {
 8074        editor.toggle_comments(toggle_comments, cx);
 8075    });
 8076    cx.assert_editor_state(indoc!(
 8077        "fn a() {
 8078             // dog();
 8079        ˇ
 8080             cat();
 8081        }"
 8082    ));
 8083
 8084    // Single cursor on one line -> advance
 8085    // Cursor starts and ends at column 0
 8086    cx.set_state(indoc!(
 8087        "fn a() {
 8088         ˇ    dog();
 8089             cat();
 8090        }"
 8091    ));
 8092    cx.update_editor(|editor, cx| {
 8093        editor.toggle_comments(toggle_comments, cx);
 8094    });
 8095    cx.assert_editor_state(indoc!(
 8096        "fn a() {
 8097             // dog();
 8098         ˇ    cat();
 8099        }"
 8100    ));
 8101}
 8102
 8103#[gpui::test]
 8104async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8105    init_test(cx, |_| {});
 8106
 8107    let mut cx = EditorTestContext::new(cx).await;
 8108
 8109    let html_language = Arc::new(
 8110        Language::new(
 8111            LanguageConfig {
 8112                name: "HTML".into(),
 8113                block_comment: Some(("<!-- ".into(), " -->".into())),
 8114                ..Default::default()
 8115            },
 8116            Some(tree_sitter_html::language()),
 8117        )
 8118        .with_injection_query(
 8119            r#"
 8120            (script_element
 8121                (raw_text) @content
 8122                (#set! "language" "javascript"))
 8123            "#,
 8124        )
 8125        .unwrap(),
 8126    );
 8127
 8128    let javascript_language = Arc::new(Language::new(
 8129        LanguageConfig {
 8130            name: "JavaScript".into(),
 8131            line_comments: vec!["// ".into()],
 8132            ..Default::default()
 8133        },
 8134        Some(tree_sitter_typescript::language_tsx()),
 8135    ));
 8136
 8137    cx.language_registry().add(html_language.clone());
 8138    cx.language_registry().add(javascript_language.clone());
 8139    cx.update_buffer(|buffer, cx| {
 8140        buffer.set_language(Some(html_language), cx);
 8141    });
 8142
 8143    // Toggle comments for empty selections
 8144    cx.set_state(
 8145        &r#"
 8146            <p>A</p>ˇ
 8147            <p>B</p>ˇ
 8148            <p>C</p>ˇ
 8149        "#
 8150        .unindent(),
 8151    );
 8152    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8153    cx.assert_editor_state(
 8154        &r#"
 8155            <!-- <p>A</p>ˇ -->
 8156            <!-- <p>B</p>ˇ -->
 8157            <!-- <p>C</p>ˇ -->
 8158        "#
 8159        .unindent(),
 8160    );
 8161    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8162    cx.assert_editor_state(
 8163        &r#"
 8164            <p>A</p>ˇ
 8165            <p>B</p>ˇ
 8166            <p>C</p>ˇ
 8167        "#
 8168        .unindent(),
 8169    );
 8170
 8171    // Toggle comments for mixture of empty and non-empty selections, where
 8172    // multiple selections occupy a given line.
 8173    cx.set_state(
 8174        &r#"
 8175            <p>A«</p>
 8176            <p>ˇ»B</p>ˇ
 8177            <p>C«</p>
 8178            <p>ˇ»D</p>ˇ
 8179        "#
 8180        .unindent(),
 8181    );
 8182
 8183    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8184    cx.assert_editor_state(
 8185        &r#"
 8186            <!-- <p>A«</p>
 8187            <p>ˇ»B</p>ˇ -->
 8188            <!-- <p>C«</p>
 8189            <p>ˇ»D</p>ˇ -->
 8190        "#
 8191        .unindent(),
 8192    );
 8193    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8194    cx.assert_editor_state(
 8195        &r#"
 8196            <p>A«</p>
 8197            <p>ˇ»B</p>ˇ
 8198            <p>C«</p>
 8199            <p>ˇ»D</p>ˇ
 8200        "#
 8201        .unindent(),
 8202    );
 8203
 8204    // Toggle comments when different languages are active for different
 8205    // selections.
 8206    cx.set_state(
 8207        &r#"
 8208            ˇ<script>
 8209                ˇvar x = new Y();
 8210            ˇ</script>
 8211        "#
 8212        .unindent(),
 8213    );
 8214    cx.executor().run_until_parked();
 8215    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8216    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8217    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8218    cx.assert_editor_state(
 8219        &r#"
 8220            <!-- ˇ<script> -->
 8221                // ˇvar x = new Y();
 8222            // ˇ</script>
 8223        "#
 8224        .unindent(),
 8225    );
 8226}
 8227
 8228#[gpui::test]
 8229fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8230    init_test(cx, |_| {});
 8231
 8232    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8233    let multibuffer = cx.new_model(|cx| {
 8234        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8235        multibuffer.push_excerpts(
 8236            buffer.clone(),
 8237            [
 8238                ExcerptRange {
 8239                    context: Point::new(0, 0)..Point::new(0, 4),
 8240                    primary: None,
 8241                },
 8242                ExcerptRange {
 8243                    context: Point::new(1, 0)..Point::new(1, 4),
 8244                    primary: None,
 8245                },
 8246            ],
 8247            cx,
 8248        );
 8249        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8250        multibuffer
 8251    });
 8252
 8253    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8254    _ = view.update(cx, |view, cx| {
 8255        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8256        view.change_selections(None, cx, |s| {
 8257            s.select_ranges([
 8258                Point::new(0, 0)..Point::new(0, 0),
 8259                Point::new(1, 0)..Point::new(1, 0),
 8260            ])
 8261        });
 8262
 8263        view.handle_input("X", cx);
 8264        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8265        assert_eq!(
 8266            view.selections.ranges(cx),
 8267            [
 8268                Point::new(0, 1)..Point::new(0, 1),
 8269                Point::new(1, 1)..Point::new(1, 1),
 8270            ]
 8271        );
 8272
 8273        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8274        view.change_selections(None, cx, |s| {
 8275            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8276        });
 8277        view.backspace(&Default::default(), cx);
 8278        assert_eq!(view.text(cx), "Xa\nbbb");
 8279        assert_eq!(
 8280            view.selections.ranges(cx),
 8281            [Point::new(1, 0)..Point::new(1, 0)]
 8282        );
 8283
 8284        view.change_selections(None, cx, |s| {
 8285            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8286        });
 8287        view.backspace(&Default::default(), cx);
 8288        assert_eq!(view.text(cx), "X\nbb");
 8289        assert_eq!(
 8290            view.selections.ranges(cx),
 8291            [Point::new(0, 1)..Point::new(0, 1)]
 8292        );
 8293    });
 8294}
 8295
 8296#[gpui::test]
 8297fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8298    init_test(cx, |_| {});
 8299
 8300    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8301    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8302        indoc! {"
 8303            [aaaa
 8304            (bbbb]
 8305            cccc)",
 8306        },
 8307        markers.clone(),
 8308    );
 8309    let excerpt_ranges = markers.into_iter().map(|marker| {
 8310        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8311        ExcerptRange {
 8312            context,
 8313            primary: None,
 8314        }
 8315    });
 8316    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8317    let multibuffer = cx.new_model(|cx| {
 8318        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8319        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8320        multibuffer
 8321    });
 8322
 8323    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8324    _ = view.update(cx, |view, cx| {
 8325        let (expected_text, selection_ranges) = marked_text_ranges(
 8326            indoc! {"
 8327                aaaa
 8328                bˇbbb
 8329                bˇbbˇb
 8330                cccc"
 8331            },
 8332            true,
 8333        );
 8334        assert_eq!(view.text(cx), expected_text);
 8335        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8336
 8337        view.handle_input("X", cx);
 8338
 8339        let (expected_text, expected_selections) = marked_text_ranges(
 8340            indoc! {"
 8341                aaaa
 8342                bXˇbbXb
 8343                bXˇbbXˇb
 8344                cccc"
 8345            },
 8346            false,
 8347        );
 8348        assert_eq!(view.text(cx), expected_text);
 8349        assert_eq!(view.selections.ranges(cx), expected_selections);
 8350
 8351        view.newline(&Newline, cx);
 8352        let (expected_text, expected_selections) = marked_text_ranges(
 8353            indoc! {"
 8354                aaaa
 8355                bX
 8356                ˇbbX
 8357                b
 8358                bX
 8359                ˇbbX
 8360                ˇb
 8361                cccc"
 8362            },
 8363            false,
 8364        );
 8365        assert_eq!(view.text(cx), expected_text);
 8366        assert_eq!(view.selections.ranges(cx), expected_selections);
 8367    });
 8368}
 8369
 8370#[gpui::test]
 8371fn test_refresh_selections(cx: &mut TestAppContext) {
 8372    init_test(cx, |_| {});
 8373
 8374    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8375    let mut excerpt1_id = None;
 8376    let multibuffer = cx.new_model(|cx| {
 8377        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8378        excerpt1_id = multibuffer
 8379            .push_excerpts(
 8380                buffer.clone(),
 8381                [
 8382                    ExcerptRange {
 8383                        context: Point::new(0, 0)..Point::new(1, 4),
 8384                        primary: None,
 8385                    },
 8386                    ExcerptRange {
 8387                        context: Point::new(1, 0)..Point::new(2, 4),
 8388                        primary: None,
 8389                    },
 8390                ],
 8391                cx,
 8392            )
 8393            .into_iter()
 8394            .next();
 8395        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8396        multibuffer
 8397    });
 8398
 8399    let editor = cx.add_window(|cx| {
 8400        let mut editor = build_editor(multibuffer.clone(), cx);
 8401        let snapshot = editor.snapshot(cx);
 8402        editor.change_selections(None, cx, |s| {
 8403            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8404        });
 8405        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8406        assert_eq!(
 8407            editor.selections.ranges(cx),
 8408            [
 8409                Point::new(1, 3)..Point::new(1, 3),
 8410                Point::new(2, 1)..Point::new(2, 1),
 8411            ]
 8412        );
 8413        editor
 8414    });
 8415
 8416    // Refreshing selections is a no-op when excerpts haven't changed.
 8417    _ = editor.update(cx, |editor, cx| {
 8418        editor.change_selections(None, cx, |s| s.refresh());
 8419        assert_eq!(
 8420            editor.selections.ranges(cx),
 8421            [
 8422                Point::new(1, 3)..Point::new(1, 3),
 8423                Point::new(2, 1)..Point::new(2, 1),
 8424            ]
 8425        );
 8426    });
 8427
 8428    _ = multibuffer.update(cx, |multibuffer, cx| {
 8429        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8430    });
 8431    _ = editor.update(cx, |editor, cx| {
 8432        // Removing an excerpt causes the first selection to become degenerate.
 8433        assert_eq!(
 8434            editor.selections.ranges(cx),
 8435            [
 8436                Point::new(0, 0)..Point::new(0, 0),
 8437                Point::new(0, 1)..Point::new(0, 1)
 8438            ]
 8439        );
 8440
 8441        // Refreshing selections will relocate the first selection to the original buffer
 8442        // location.
 8443        editor.change_selections(None, cx, |s| s.refresh());
 8444        assert_eq!(
 8445            editor.selections.ranges(cx),
 8446            [
 8447                Point::new(0, 1)..Point::new(0, 1),
 8448                Point::new(0, 3)..Point::new(0, 3)
 8449            ]
 8450        );
 8451        assert!(editor.selections.pending_anchor().is_some());
 8452    });
 8453}
 8454
 8455#[gpui::test]
 8456fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8457    init_test(cx, |_| {});
 8458
 8459    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8460    let mut excerpt1_id = None;
 8461    let multibuffer = cx.new_model(|cx| {
 8462        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8463        excerpt1_id = multibuffer
 8464            .push_excerpts(
 8465                buffer.clone(),
 8466                [
 8467                    ExcerptRange {
 8468                        context: Point::new(0, 0)..Point::new(1, 4),
 8469                        primary: None,
 8470                    },
 8471                    ExcerptRange {
 8472                        context: Point::new(1, 0)..Point::new(2, 4),
 8473                        primary: None,
 8474                    },
 8475                ],
 8476                cx,
 8477            )
 8478            .into_iter()
 8479            .next();
 8480        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8481        multibuffer
 8482    });
 8483
 8484    let editor = cx.add_window(|cx| {
 8485        let mut editor = build_editor(multibuffer.clone(), cx);
 8486        let snapshot = editor.snapshot(cx);
 8487        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8488        assert_eq!(
 8489            editor.selections.ranges(cx),
 8490            [Point::new(1, 3)..Point::new(1, 3)]
 8491        );
 8492        editor
 8493    });
 8494
 8495    _ = multibuffer.update(cx, |multibuffer, cx| {
 8496        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8497    });
 8498    _ = editor.update(cx, |editor, cx| {
 8499        assert_eq!(
 8500            editor.selections.ranges(cx),
 8501            [Point::new(0, 0)..Point::new(0, 0)]
 8502        );
 8503
 8504        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8505        editor.change_selections(None, cx, |s| s.refresh());
 8506        assert_eq!(
 8507            editor.selections.ranges(cx),
 8508            [Point::new(0, 3)..Point::new(0, 3)]
 8509        );
 8510        assert!(editor.selections.pending_anchor().is_some());
 8511    });
 8512}
 8513
 8514#[gpui::test]
 8515async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8516    init_test(cx, |_| {});
 8517
 8518    let language = Arc::new(
 8519        Language::new(
 8520            LanguageConfig {
 8521                brackets: BracketPairConfig {
 8522                    pairs: vec![
 8523                        BracketPair {
 8524                            start: "{".to_string(),
 8525                            end: "}".to_string(),
 8526                            close: true,
 8527                            surround: true,
 8528                            newline: true,
 8529                        },
 8530                        BracketPair {
 8531                            start: "/* ".to_string(),
 8532                            end: " */".to_string(),
 8533                            close: true,
 8534                            surround: true,
 8535                            newline: true,
 8536                        },
 8537                    ],
 8538                    ..Default::default()
 8539                },
 8540                ..Default::default()
 8541            },
 8542            Some(tree_sitter_rust::language()),
 8543        )
 8544        .with_indents_query("")
 8545        .unwrap(),
 8546    );
 8547
 8548    let text = concat!(
 8549        "{   }\n",     //
 8550        "  x\n",       //
 8551        "  /*   */\n", //
 8552        "x\n",         //
 8553        "{{} }\n",     //
 8554    );
 8555
 8556    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 8557    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8558    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8559    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 8560        .await;
 8561
 8562    _ = view.update(cx, |view, cx| {
 8563        view.change_selections(None, cx, |s| {
 8564            s.select_display_ranges([
 8565                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 8566                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 8567                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 8568            ])
 8569        });
 8570        view.newline(&Newline, cx);
 8571
 8572        assert_eq!(
 8573            view.buffer().read(cx).read(cx).text(),
 8574            concat!(
 8575                "{ \n",    // Suppress rustfmt
 8576                "\n",      //
 8577                "}\n",     //
 8578                "  x\n",   //
 8579                "  /* \n", //
 8580                "  \n",    //
 8581                "  */\n",  //
 8582                "x\n",     //
 8583                "{{} \n",  //
 8584                "}\n",     //
 8585            )
 8586        );
 8587    });
 8588}
 8589
 8590#[gpui::test]
 8591fn test_highlighted_ranges(cx: &mut TestAppContext) {
 8592    init_test(cx, |_| {});
 8593
 8594    let editor = cx.add_window(|cx| {
 8595        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 8596        build_editor(buffer.clone(), cx)
 8597    });
 8598
 8599    _ = editor.update(cx, |editor, cx| {
 8600        struct Type1;
 8601        struct Type2;
 8602
 8603        let buffer = editor.buffer.read(cx).snapshot(cx);
 8604
 8605        let anchor_range =
 8606            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 8607
 8608        editor.highlight_background::<Type1>(
 8609            &[
 8610                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 8611                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 8612                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 8613                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 8614            ],
 8615            |_| Hsla::red(),
 8616            cx,
 8617        );
 8618        editor.highlight_background::<Type2>(
 8619            &[
 8620                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 8621                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 8622                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 8623                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 8624            ],
 8625            |_| Hsla::green(),
 8626            cx,
 8627        );
 8628
 8629        let snapshot = editor.snapshot(cx);
 8630        let mut highlighted_ranges = editor.background_highlights_in_range(
 8631            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 8632            &snapshot,
 8633            cx.theme().colors(),
 8634        );
 8635        // Enforce a consistent ordering based on color without relying on the ordering of the
 8636        // highlight's `TypeId` which is non-executor.
 8637        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 8638        assert_eq!(
 8639            highlighted_ranges,
 8640            &[
 8641                (
 8642                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 8643                    Hsla::red(),
 8644                ),
 8645                (
 8646                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8647                    Hsla::red(),
 8648                ),
 8649                (
 8650                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 8651                    Hsla::green(),
 8652                ),
 8653                (
 8654                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 8655                    Hsla::green(),
 8656                ),
 8657            ]
 8658        );
 8659        assert_eq!(
 8660            editor.background_highlights_in_range(
 8661                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 8662                &snapshot,
 8663                cx.theme().colors(),
 8664            ),
 8665            &[(
 8666                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 8667                Hsla::red(),
 8668            )]
 8669        );
 8670    });
 8671}
 8672
 8673#[gpui::test]
 8674async fn test_following(cx: &mut gpui::TestAppContext) {
 8675    init_test(cx, |_| {});
 8676
 8677    let fs = FakeFs::new(cx.executor());
 8678    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8679
 8680    let buffer = project.update(cx, |project, cx| {
 8681        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 8682        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 8683    });
 8684    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 8685    let follower = cx.update(|cx| {
 8686        cx.open_window(
 8687            WindowOptions {
 8688                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 8689                    gpui::Point::new(px(0.), px(0.)),
 8690                    gpui::Point::new(px(10.), px(80.)),
 8691                ))),
 8692                ..Default::default()
 8693            },
 8694            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 8695        )
 8696        .unwrap()
 8697    });
 8698
 8699    let is_still_following = Rc::new(RefCell::new(true));
 8700    let follower_edit_event_count = Rc::new(RefCell::new(0));
 8701    let pending_update = Rc::new(RefCell::new(None));
 8702    _ = follower.update(cx, {
 8703        let update = pending_update.clone();
 8704        let is_still_following = is_still_following.clone();
 8705        let follower_edit_event_count = follower_edit_event_count.clone();
 8706        |_, cx| {
 8707            cx.subscribe(
 8708                &leader.root_view(cx).unwrap(),
 8709                move |_, leader, event, cx| {
 8710                    leader
 8711                        .read(cx)
 8712                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8713                },
 8714            )
 8715            .detach();
 8716
 8717            cx.subscribe(
 8718                &follower.root_view(cx).unwrap(),
 8719                move |_, _, event: &EditorEvent, _cx| {
 8720                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 8721                        *is_still_following.borrow_mut() = false;
 8722                    }
 8723
 8724                    if let EditorEvent::BufferEdited = event {
 8725                        *follower_edit_event_count.borrow_mut() += 1;
 8726                    }
 8727                },
 8728            )
 8729            .detach();
 8730        }
 8731    });
 8732
 8733    // Update the selections only
 8734    _ = leader.update(cx, |leader, cx| {
 8735        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8736    });
 8737    follower
 8738        .update(cx, |follower, cx| {
 8739            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8740        })
 8741        .unwrap()
 8742        .await
 8743        .unwrap();
 8744    _ = follower.update(cx, |follower, cx| {
 8745        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 8746    });
 8747    assert_eq!(*is_still_following.borrow(), true);
 8748    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8749
 8750    // Update the scroll position only
 8751    _ = leader.update(cx, |leader, cx| {
 8752        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8753    });
 8754    follower
 8755        .update(cx, |follower, cx| {
 8756            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8757        })
 8758        .unwrap()
 8759        .await
 8760        .unwrap();
 8761    assert_eq!(
 8762        follower
 8763            .update(cx, |follower, cx| follower.scroll_position(cx))
 8764            .unwrap(),
 8765        gpui::Point::new(1.5, 3.5)
 8766    );
 8767    assert_eq!(*is_still_following.borrow(), true);
 8768    assert_eq!(*follower_edit_event_count.borrow(), 0);
 8769
 8770    // Update the selections and scroll position. The follower's scroll position is updated
 8771    // via autoscroll, not via the leader's exact scroll position.
 8772    _ = leader.update(cx, |leader, cx| {
 8773        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8774        leader.request_autoscroll(Autoscroll::newest(), cx);
 8775        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 8776    });
 8777    follower
 8778        .update(cx, |follower, cx| {
 8779            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8780        })
 8781        .unwrap()
 8782        .await
 8783        .unwrap();
 8784    _ = follower.update(cx, |follower, cx| {
 8785        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 8786        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 8787    });
 8788    assert_eq!(*is_still_following.borrow(), true);
 8789
 8790    // Creating a pending selection that precedes another selection
 8791    _ = leader.update(cx, |leader, cx| {
 8792        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 8793        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 8794    });
 8795    follower
 8796        .update(cx, |follower, cx| {
 8797            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8798        })
 8799        .unwrap()
 8800        .await
 8801        .unwrap();
 8802    _ = follower.update(cx, |follower, cx| {
 8803        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 8804    });
 8805    assert_eq!(*is_still_following.borrow(), true);
 8806
 8807    // Extend the pending selection so that it surrounds another selection
 8808    _ = leader.update(cx, |leader, cx| {
 8809        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 8810    });
 8811    follower
 8812        .update(cx, |follower, cx| {
 8813            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 8814        })
 8815        .unwrap()
 8816        .await
 8817        .unwrap();
 8818    _ = follower.update(cx, |follower, cx| {
 8819        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 8820    });
 8821
 8822    // Scrolling locally breaks the follow
 8823    _ = follower.update(cx, |follower, cx| {
 8824        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 8825        follower.set_scroll_anchor(
 8826            ScrollAnchor {
 8827                anchor: top_anchor,
 8828                offset: gpui::Point::new(0.0, 0.5),
 8829            },
 8830            cx,
 8831        );
 8832    });
 8833    assert_eq!(*is_still_following.borrow(), false);
 8834}
 8835
 8836#[gpui::test]
 8837async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 8838    init_test(cx, |_| {});
 8839
 8840    let fs = FakeFs::new(cx.executor());
 8841    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8842    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8843    let pane = workspace
 8844        .update(cx, |workspace, _| workspace.active_pane().clone())
 8845        .unwrap();
 8846
 8847    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8848
 8849    let leader = pane.update(cx, |_, cx| {
 8850        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 8851        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 8852    });
 8853
 8854    // Start following the editor when it has no excerpts.
 8855    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8856    let follower_1 = cx
 8857        .update_window(*workspace.deref(), |_, cx| {
 8858            Editor::from_state_proto(
 8859                workspace.root_view(cx).unwrap(),
 8860                ViewId {
 8861                    creator: Default::default(),
 8862                    id: 0,
 8863                },
 8864                &mut state_message,
 8865                cx,
 8866            )
 8867        })
 8868        .unwrap()
 8869        .unwrap()
 8870        .await
 8871        .unwrap();
 8872
 8873    let update_message = Rc::new(RefCell::new(None));
 8874    follower_1.update(cx, {
 8875        let update = update_message.clone();
 8876        |_, cx| {
 8877            cx.subscribe(&leader, move |_, leader, event, cx| {
 8878                leader
 8879                    .read(cx)
 8880                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 8881            })
 8882            .detach();
 8883        }
 8884    });
 8885
 8886    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 8887        (
 8888            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 8889            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 8890        )
 8891    });
 8892
 8893    // Insert some excerpts.
 8894    _ = leader.update(cx, |leader, cx| {
 8895        leader.buffer.update(cx, |multibuffer, cx| {
 8896            let excerpt_ids = multibuffer.push_excerpts(
 8897                buffer_1.clone(),
 8898                [
 8899                    ExcerptRange {
 8900                        context: 1..6,
 8901                        primary: None,
 8902                    },
 8903                    ExcerptRange {
 8904                        context: 12..15,
 8905                        primary: None,
 8906                    },
 8907                    ExcerptRange {
 8908                        context: 0..3,
 8909                        primary: None,
 8910                    },
 8911                ],
 8912                cx,
 8913            );
 8914            multibuffer.insert_excerpts_after(
 8915                excerpt_ids[0],
 8916                buffer_2.clone(),
 8917                [
 8918                    ExcerptRange {
 8919                        context: 8..12,
 8920                        primary: None,
 8921                    },
 8922                    ExcerptRange {
 8923                        context: 0..6,
 8924                        primary: None,
 8925                    },
 8926                ],
 8927                cx,
 8928            );
 8929        });
 8930    });
 8931
 8932    // Apply the update of adding the excerpts.
 8933    follower_1
 8934        .update(cx, |follower, cx| {
 8935            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8936        })
 8937        .await
 8938        .unwrap();
 8939    assert_eq!(
 8940        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8941        leader.update(cx, |editor, cx| editor.text(cx))
 8942    );
 8943    update_message.borrow_mut().take();
 8944
 8945    // Start following separately after it already has excerpts.
 8946    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 8947    let follower_2 = cx
 8948        .update_window(*workspace.deref(), |_, cx| {
 8949            Editor::from_state_proto(
 8950                workspace.root_view(cx).unwrap().clone(),
 8951                ViewId {
 8952                    creator: Default::default(),
 8953                    id: 0,
 8954                },
 8955                &mut state_message,
 8956                cx,
 8957            )
 8958        })
 8959        .unwrap()
 8960        .unwrap()
 8961        .await
 8962        .unwrap();
 8963    assert_eq!(
 8964        follower_2.update(cx, |editor, cx| editor.text(cx)),
 8965        leader.update(cx, |editor, cx| editor.text(cx))
 8966    );
 8967
 8968    // Remove some excerpts.
 8969    _ = leader.update(cx, |leader, cx| {
 8970        leader.buffer.update(cx, |multibuffer, cx| {
 8971            let excerpt_ids = multibuffer.excerpt_ids();
 8972            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 8973            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 8974        });
 8975    });
 8976
 8977    // Apply the update of removing the excerpts.
 8978    follower_1
 8979        .update(cx, |follower, cx| {
 8980            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8981        })
 8982        .await
 8983        .unwrap();
 8984    follower_2
 8985        .update(cx, |follower, cx| {
 8986            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 8987        })
 8988        .await
 8989        .unwrap();
 8990    update_message.borrow_mut().take();
 8991    assert_eq!(
 8992        follower_1.update(cx, |editor, cx| editor.text(cx)),
 8993        leader.update(cx, |editor, cx| editor.text(cx))
 8994    );
 8995}
 8996
 8997#[gpui::test]
 8998async fn go_to_prev_overlapping_diagnostic(
 8999    executor: BackgroundExecutor,
 9000    cx: &mut gpui::TestAppContext,
 9001) {
 9002    init_test(cx, |_| {});
 9003
 9004    let mut cx = EditorTestContext::new(cx).await;
 9005    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9006
 9007    cx.set_state(indoc! {"
 9008        ˇfn func(abc def: i32) -> u32 {
 9009        }
 9010    "});
 9011
 9012    _ = cx.update(|cx| {
 9013        _ = project.update(cx, |project, cx| {
 9014            project
 9015                .update_diagnostics(
 9016                    LanguageServerId(0),
 9017                    lsp::PublishDiagnosticsParams {
 9018                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9019                        version: None,
 9020                        diagnostics: vec![
 9021                            lsp::Diagnostic {
 9022                                range: lsp::Range::new(
 9023                                    lsp::Position::new(0, 11),
 9024                                    lsp::Position::new(0, 12),
 9025                                ),
 9026                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9027                                ..Default::default()
 9028                            },
 9029                            lsp::Diagnostic {
 9030                                range: lsp::Range::new(
 9031                                    lsp::Position::new(0, 12),
 9032                                    lsp::Position::new(0, 15),
 9033                                ),
 9034                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9035                                ..Default::default()
 9036                            },
 9037                            lsp::Diagnostic {
 9038                                range: lsp::Range::new(
 9039                                    lsp::Position::new(0, 25),
 9040                                    lsp::Position::new(0, 28),
 9041                                ),
 9042                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9043                                ..Default::default()
 9044                            },
 9045                        ],
 9046                    },
 9047                    &[],
 9048                    cx,
 9049                )
 9050                .unwrap()
 9051        });
 9052    });
 9053
 9054    executor.run_until_parked();
 9055
 9056    cx.update_editor(|editor, cx| {
 9057        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9058    });
 9059
 9060    cx.assert_editor_state(indoc! {"
 9061        fn func(abc def: i32) -> ˇu32 {
 9062        }
 9063    "});
 9064
 9065    cx.update_editor(|editor, cx| {
 9066        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9067    });
 9068
 9069    cx.assert_editor_state(indoc! {"
 9070        fn func(abc ˇdef: i32) -> u32 {
 9071        }
 9072    "});
 9073
 9074    cx.update_editor(|editor, cx| {
 9075        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9076    });
 9077
 9078    cx.assert_editor_state(indoc! {"
 9079        fn func(abcˇ def: i32) -> u32 {
 9080        }
 9081    "});
 9082
 9083    cx.update_editor(|editor, cx| {
 9084        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9085    });
 9086
 9087    cx.assert_editor_state(indoc! {"
 9088        fn func(abc def: i32) -> ˇu32 {
 9089        }
 9090    "});
 9091}
 9092
 9093#[gpui::test]
 9094async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9095    init_test(cx, |_| {});
 9096
 9097    let mut cx = EditorTestContext::new(cx).await;
 9098
 9099    cx.set_state(indoc! {"
 9100        fn func(abˇc def: i32) -> u32 {
 9101        }
 9102    "});
 9103    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9104
 9105    cx.update(|cx| {
 9106        project.update(cx, |project, cx| {
 9107            project.update_diagnostics(
 9108                LanguageServerId(0),
 9109                lsp::PublishDiagnosticsParams {
 9110                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9111                    version: None,
 9112                    diagnostics: vec![lsp::Diagnostic {
 9113                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9114                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9115                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9116                        ..Default::default()
 9117                    }],
 9118                },
 9119                &[],
 9120                cx,
 9121            )
 9122        })
 9123    }).unwrap();
 9124    cx.run_until_parked();
 9125    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9126    cx.run_until_parked();
 9127    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9128}
 9129
 9130#[gpui::test]
 9131async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9132    init_test(cx, |_| {});
 9133
 9134    let mut cx = EditorTestContext::new(cx).await;
 9135
 9136    let diff_base = r#"
 9137        use some::mod;
 9138
 9139        const A: u32 = 42;
 9140
 9141        fn main() {
 9142            println!("hello");
 9143
 9144            println!("world");
 9145        }
 9146        "#
 9147    .unindent();
 9148
 9149    // Edits are modified, removed, modified, added
 9150    cx.set_state(
 9151        &r#"
 9152        use some::modified;
 9153
 9154        ˇ
 9155        fn main() {
 9156            println!("hello there");
 9157
 9158            println!("around the");
 9159            println!("world");
 9160        }
 9161        "#
 9162        .unindent(),
 9163    );
 9164
 9165    cx.set_diff_base(Some(&diff_base));
 9166    executor.run_until_parked();
 9167
 9168    cx.update_editor(|editor, cx| {
 9169        //Wrap around the bottom of the buffer
 9170        for _ in 0..3 {
 9171            editor.go_to_hunk(&GoToHunk, cx);
 9172        }
 9173    });
 9174
 9175    cx.assert_editor_state(
 9176        &r#"
 9177        ˇuse some::modified;
 9178
 9179
 9180        fn main() {
 9181            println!("hello there");
 9182
 9183            println!("around the");
 9184            println!("world");
 9185        }
 9186        "#
 9187        .unindent(),
 9188    );
 9189
 9190    cx.update_editor(|editor, cx| {
 9191        //Wrap around the top of the buffer
 9192        for _ in 0..2 {
 9193            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9194        }
 9195    });
 9196
 9197    cx.assert_editor_state(
 9198        &r#"
 9199        use some::modified;
 9200
 9201
 9202        fn main() {
 9203        ˇ    println!("hello there");
 9204
 9205            println!("around the");
 9206            println!("world");
 9207        }
 9208        "#
 9209        .unindent(),
 9210    );
 9211
 9212    cx.update_editor(|editor, cx| {
 9213        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9214    });
 9215
 9216    cx.assert_editor_state(
 9217        &r#"
 9218        use some::modified;
 9219
 9220        ˇ
 9221        fn main() {
 9222            println!("hello there");
 9223
 9224            println!("around the");
 9225            println!("world");
 9226        }
 9227        "#
 9228        .unindent(),
 9229    );
 9230
 9231    cx.update_editor(|editor, cx| {
 9232        for _ in 0..3 {
 9233            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9234        }
 9235    });
 9236
 9237    cx.assert_editor_state(
 9238        &r#"
 9239        use some::modified;
 9240
 9241
 9242        fn main() {
 9243        ˇ    println!("hello there");
 9244
 9245            println!("around the");
 9246            println!("world");
 9247        }
 9248        "#
 9249        .unindent(),
 9250    );
 9251
 9252    cx.update_editor(|editor, cx| {
 9253        editor.fold(&Fold, cx);
 9254
 9255        //Make sure that the fold only gets one hunk
 9256        for _ in 0..4 {
 9257            editor.go_to_hunk(&GoToHunk, cx);
 9258        }
 9259    });
 9260
 9261    cx.assert_editor_state(
 9262        &r#"
 9263        ˇuse some::modified;
 9264
 9265
 9266        fn main() {
 9267            println!("hello there");
 9268
 9269            println!("around the");
 9270            println!("world");
 9271        }
 9272        "#
 9273        .unindent(),
 9274    );
 9275}
 9276
 9277#[test]
 9278fn test_split_words() {
 9279    fn split(text: &str) -> Vec<&str> {
 9280        split_words(text).collect()
 9281    }
 9282
 9283    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9284    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9285    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9286    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9287    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9288    assert_eq!(split("helloworld"), &["helloworld"]);
 9289
 9290    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9291}
 9292
 9293#[gpui::test]
 9294async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9295    init_test(cx, |_| {});
 9296
 9297    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9298    let mut assert = |before, after| {
 9299        let _state_context = cx.set_state(before);
 9300        cx.update_editor(|editor, cx| {
 9301            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9302        });
 9303        cx.assert_editor_state(after);
 9304    };
 9305
 9306    // Outside bracket jumps to outside of matching bracket
 9307    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9308    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9309
 9310    // Inside bracket jumps to inside of matching bracket
 9311    assert("console.log(ˇvar);", "console.log(varˇ);");
 9312    assert("console.log(varˇ);", "console.log(ˇvar);");
 9313
 9314    // When outside a bracket and inside, favor jumping to the inside bracket
 9315    assert(
 9316        "console.log('foo', [1, 2, 3]ˇ);",
 9317        "console.log(ˇ'foo', [1, 2, 3]);",
 9318    );
 9319    assert(
 9320        "console.log(ˇ'foo', [1, 2, 3]);",
 9321        "console.log('foo', [1, 2, 3]ˇ);",
 9322    );
 9323
 9324    // Bias forward if two options are equally likely
 9325    assert(
 9326        "let result = curried_fun()ˇ();",
 9327        "let result = curried_fun()()ˇ;",
 9328    );
 9329
 9330    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9331    assert(
 9332        indoc! {"
 9333            function test() {
 9334                console.log('test')ˇ
 9335            }"},
 9336        indoc! {"
 9337            function test() {
 9338                console.logˇ('test')
 9339            }"},
 9340    );
 9341}
 9342
 9343#[gpui::test]
 9344async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9345    init_test(cx, |_| {});
 9346
 9347    let fs = FakeFs::new(cx.executor());
 9348    fs.insert_tree(
 9349        "/a",
 9350        json!({
 9351            "main.rs": "fn main() { let a = 5; }",
 9352            "other.rs": "// Test file",
 9353        }),
 9354    )
 9355    .await;
 9356    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9357
 9358    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9359    language_registry.add(Arc::new(Language::new(
 9360        LanguageConfig {
 9361            name: "Rust".into(),
 9362            matcher: LanguageMatcher {
 9363                path_suffixes: vec!["rs".to_string()],
 9364                ..Default::default()
 9365            },
 9366            brackets: BracketPairConfig {
 9367                pairs: vec![BracketPair {
 9368                    start: "{".to_string(),
 9369                    end: "}".to_string(),
 9370                    close: true,
 9371                    surround: true,
 9372                    newline: true,
 9373                }],
 9374                disabled_scopes_by_bracket_ix: Vec::new(),
 9375            },
 9376            ..Default::default()
 9377        },
 9378        Some(tree_sitter_rust::language()),
 9379    )));
 9380    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9381        "Rust",
 9382        FakeLspAdapter {
 9383            capabilities: lsp::ServerCapabilities {
 9384                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9385                    first_trigger_character: "{".to_string(),
 9386                    more_trigger_character: None,
 9387                }),
 9388                ..Default::default()
 9389            },
 9390            ..Default::default()
 9391        },
 9392    );
 9393
 9394    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9395
 9396    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9397
 9398    let worktree_id = workspace
 9399        .update(cx, |workspace, cx| {
 9400            workspace.project().update(cx, |project, cx| {
 9401                project.worktrees(cx).next().unwrap().read(cx).id()
 9402            })
 9403        })
 9404        .unwrap();
 9405
 9406    let buffer = project
 9407        .update(cx, |project, cx| {
 9408            project.open_local_buffer("/a/main.rs", cx)
 9409        })
 9410        .await
 9411        .unwrap();
 9412    cx.executor().run_until_parked();
 9413    cx.executor().start_waiting();
 9414    let fake_server = fake_servers.next().await.unwrap();
 9415    let editor_handle = workspace
 9416        .update(cx, |workspace, cx| {
 9417            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9418        })
 9419        .unwrap()
 9420        .await
 9421        .unwrap()
 9422        .downcast::<Editor>()
 9423        .unwrap();
 9424
 9425    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9426        assert_eq!(
 9427            params.text_document_position.text_document.uri,
 9428            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9429        );
 9430        assert_eq!(
 9431            params.text_document_position.position,
 9432            lsp::Position::new(0, 21),
 9433        );
 9434
 9435        Ok(Some(vec![lsp::TextEdit {
 9436            new_text: "]".to_string(),
 9437            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9438        }]))
 9439    });
 9440
 9441    editor_handle.update(cx, |editor, cx| {
 9442        editor.focus(cx);
 9443        editor.change_selections(None, cx, |s| {
 9444            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9445        });
 9446        editor.handle_input("{", cx);
 9447    });
 9448
 9449    cx.executor().run_until_parked();
 9450
 9451    _ = buffer.update(cx, |buffer, _| {
 9452        assert_eq!(
 9453            buffer.text(),
 9454            "fn main() { let a = {5}; }",
 9455            "No extra braces from on type formatting should appear in the buffer"
 9456        )
 9457    });
 9458}
 9459
 9460#[gpui::test]
 9461async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9462    init_test(cx, |_| {});
 9463
 9464    let fs = FakeFs::new(cx.executor());
 9465    fs.insert_tree(
 9466        "/a",
 9467        json!({
 9468            "main.rs": "fn main() { let a = 5; }",
 9469            "other.rs": "// Test file",
 9470        }),
 9471    )
 9472    .await;
 9473
 9474    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9475
 9476    let server_restarts = Arc::new(AtomicUsize::new(0));
 9477    let closure_restarts = Arc::clone(&server_restarts);
 9478    let language_server_name = "test language server";
 9479    let language_name: Arc<str> = "Rust".into();
 9480
 9481    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9482    language_registry.add(Arc::new(Language::new(
 9483        LanguageConfig {
 9484            name: Arc::clone(&language_name),
 9485            matcher: LanguageMatcher {
 9486                path_suffixes: vec!["rs".to_string()],
 9487                ..Default::default()
 9488            },
 9489            ..Default::default()
 9490        },
 9491        Some(tree_sitter_rust::language()),
 9492    )));
 9493    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 9494        "Rust",
 9495        FakeLspAdapter {
 9496            name: language_server_name,
 9497            initialization_options: Some(json!({
 9498                "testOptionValue": true
 9499            })),
 9500            initializer: Some(Box::new(move |fake_server| {
 9501                let task_restarts = Arc::clone(&closure_restarts);
 9502                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9503                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9504                    futures::future::ready(Ok(()))
 9505                });
 9506            })),
 9507            ..Default::default()
 9508        },
 9509    );
 9510
 9511    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9512    let _buffer = project
 9513        .update(cx, |project, cx| {
 9514            project.open_local_buffer("/a/main.rs", cx)
 9515        })
 9516        .await
 9517        .unwrap();
 9518    let _fake_server = fake_servers.next().await.unwrap();
 9519    update_test_language_settings(cx, |language_settings| {
 9520        language_settings.languages.insert(
 9521            Arc::clone(&language_name),
 9522            LanguageSettingsContent {
 9523                tab_size: NonZeroU32::new(8),
 9524                ..Default::default()
 9525            },
 9526        );
 9527    });
 9528    cx.executor().run_until_parked();
 9529    assert_eq!(
 9530        server_restarts.load(atomic::Ordering::Acquire),
 9531        0,
 9532        "Should not restart LSP server on an unrelated change"
 9533    );
 9534
 9535    update_test_project_settings(cx, |project_settings| {
 9536        project_settings.lsp.insert(
 9537            "Some other server name".into(),
 9538            LspSettings {
 9539                binary: None,
 9540                settings: None,
 9541                initialization_options: Some(json!({
 9542                    "some other init value": false
 9543                })),
 9544            },
 9545        );
 9546    });
 9547    cx.executor().run_until_parked();
 9548    assert_eq!(
 9549        server_restarts.load(atomic::Ordering::Acquire),
 9550        0,
 9551        "Should not restart LSP server on an unrelated LSP settings change"
 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: Some(json!({
 9561                    "anotherInitValue": false
 9562                })),
 9563            },
 9564        );
 9565    });
 9566    cx.executor().run_until_parked();
 9567    assert_eq!(
 9568        server_restarts.load(atomic::Ordering::Acquire),
 9569        1,
 9570        "Should restart LSP server on a related LSP settings change"
 9571    );
 9572
 9573    update_test_project_settings(cx, |project_settings| {
 9574        project_settings.lsp.insert(
 9575            language_server_name.into(),
 9576            LspSettings {
 9577                binary: None,
 9578                settings: None,
 9579                initialization_options: Some(json!({
 9580                    "anotherInitValue": false
 9581                })),
 9582            },
 9583        );
 9584    });
 9585    cx.executor().run_until_parked();
 9586    assert_eq!(
 9587        server_restarts.load(atomic::Ordering::Acquire),
 9588        1,
 9589        "Should not restart LSP server on a related LSP settings change that is the same"
 9590    );
 9591
 9592    update_test_project_settings(cx, |project_settings| {
 9593        project_settings.lsp.insert(
 9594            language_server_name.into(),
 9595            LspSettings {
 9596                binary: None,
 9597                settings: None,
 9598                initialization_options: None,
 9599            },
 9600        );
 9601    });
 9602    cx.executor().run_until_parked();
 9603    assert_eq!(
 9604        server_restarts.load(atomic::Ordering::Acquire),
 9605        2,
 9606        "Should restart LSP server on another related LSP settings change"
 9607    );
 9608}
 9609
 9610#[gpui::test]
 9611async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 9612    init_test(cx, |_| {});
 9613
 9614    let mut cx = EditorLspTestContext::new_rust(
 9615        lsp::ServerCapabilities {
 9616            completion_provider: Some(lsp::CompletionOptions {
 9617                trigger_characters: Some(vec![".".to_string()]),
 9618                resolve_provider: Some(true),
 9619                ..Default::default()
 9620            }),
 9621            ..Default::default()
 9622        },
 9623        cx,
 9624    )
 9625    .await;
 9626
 9627    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9628    cx.simulate_keystroke(".");
 9629    let completion_item = lsp::CompletionItem {
 9630        label: "some".into(),
 9631        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9632        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9633        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9634            kind: lsp::MarkupKind::Markdown,
 9635            value: "```rust\nSome(2)\n```".to_string(),
 9636        })),
 9637        deprecated: Some(false),
 9638        sort_text: Some("fffffff2".to_string()),
 9639        filter_text: Some("some".to_string()),
 9640        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9641        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9642            range: lsp::Range {
 9643                start: lsp::Position {
 9644                    line: 0,
 9645                    character: 22,
 9646                },
 9647                end: lsp::Position {
 9648                    line: 0,
 9649                    character: 22,
 9650                },
 9651            },
 9652            new_text: "Some(2)".to_string(),
 9653        })),
 9654        additional_text_edits: Some(vec![lsp::TextEdit {
 9655            range: lsp::Range {
 9656                start: lsp::Position {
 9657                    line: 0,
 9658                    character: 20,
 9659                },
 9660                end: lsp::Position {
 9661                    line: 0,
 9662                    character: 22,
 9663                },
 9664            },
 9665            new_text: "".to_string(),
 9666        }]),
 9667        ..Default::default()
 9668    };
 9669
 9670    let closure_completion_item = completion_item.clone();
 9671    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9672        let task_completion_item = closure_completion_item.clone();
 9673        async move {
 9674            Ok(Some(lsp::CompletionResponse::Array(vec![
 9675                task_completion_item,
 9676            ])))
 9677        }
 9678    });
 9679
 9680    request.next().await;
 9681
 9682    cx.condition(|editor, _| editor.context_menu_visible())
 9683        .await;
 9684    let apply_additional_edits = cx.update_editor(|editor, cx| {
 9685        editor
 9686            .confirm_completion(&ConfirmCompletion::default(), cx)
 9687            .unwrap()
 9688    });
 9689    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 9690
 9691    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 9692        let task_completion_item = completion_item.clone();
 9693        async move { Ok(task_completion_item) }
 9694    })
 9695    .next()
 9696    .await
 9697    .unwrap();
 9698    apply_additional_edits.await.unwrap();
 9699    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 9700}
 9701
 9702#[gpui::test]
 9703async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 9704    init_test(cx, |_| {});
 9705
 9706    let mut cx = EditorLspTestContext::new(
 9707        Language::new(
 9708            LanguageConfig {
 9709                matcher: LanguageMatcher {
 9710                    path_suffixes: vec!["jsx".into()],
 9711                    ..Default::default()
 9712                },
 9713                overrides: [(
 9714                    "element".into(),
 9715                    LanguageConfigOverride {
 9716                        word_characters: Override::Set(['-'].into_iter().collect()),
 9717                        ..Default::default()
 9718                    },
 9719                )]
 9720                .into_iter()
 9721                .collect(),
 9722                ..Default::default()
 9723            },
 9724            Some(tree_sitter_typescript::language_tsx()),
 9725        )
 9726        .with_override_query("(jsx_self_closing_element) @element")
 9727        .unwrap(),
 9728        lsp::ServerCapabilities {
 9729            completion_provider: Some(lsp::CompletionOptions {
 9730                trigger_characters: Some(vec![":".to_string()]),
 9731                ..Default::default()
 9732            }),
 9733            ..Default::default()
 9734        },
 9735        cx,
 9736    )
 9737    .await;
 9738
 9739    cx.lsp
 9740        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9741            Ok(Some(lsp::CompletionResponse::Array(vec![
 9742                lsp::CompletionItem {
 9743                    label: "bg-blue".into(),
 9744                    ..Default::default()
 9745                },
 9746                lsp::CompletionItem {
 9747                    label: "bg-red".into(),
 9748                    ..Default::default()
 9749                },
 9750                lsp::CompletionItem {
 9751                    label: "bg-yellow".into(),
 9752                    ..Default::default()
 9753                },
 9754            ])))
 9755        });
 9756
 9757    cx.set_state(r#"<p class="bgˇ" />"#);
 9758
 9759    // Trigger completion when typing a dash, because the dash is an extra
 9760    // word character in the 'element' scope, which contains the cursor.
 9761    cx.simulate_keystroke("-");
 9762    cx.executor().run_until_parked();
 9763    cx.update_editor(|editor, _| {
 9764        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9765            assert_eq!(
 9766                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9767                &["bg-red", "bg-blue", "bg-yellow"]
 9768            );
 9769        } else {
 9770            panic!("expected completion menu to be open");
 9771        }
 9772    });
 9773
 9774    cx.simulate_keystroke("l");
 9775    cx.executor().run_until_parked();
 9776    cx.update_editor(|editor, _| {
 9777        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9778            assert_eq!(
 9779                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9780                &["bg-blue", "bg-yellow"]
 9781            );
 9782        } else {
 9783            panic!("expected completion menu to be open");
 9784        }
 9785    });
 9786
 9787    // When filtering completions, consider the character after the '-' to
 9788    // be the start of a subword.
 9789    cx.set_state(r#"<p class="yelˇ" />"#);
 9790    cx.simulate_keystroke("l");
 9791    cx.executor().run_until_parked();
 9792    cx.update_editor(|editor, _| {
 9793        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 9794            assert_eq!(
 9795                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 9796                &["bg-yellow"]
 9797            );
 9798        } else {
 9799            panic!("expected completion menu to be open");
 9800        }
 9801    });
 9802}
 9803
 9804#[gpui::test]
 9805async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 9806    init_test(cx, |settings| {
 9807        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9808            FormatterList(vec![Formatter::Prettier].into()),
 9809        ))
 9810    });
 9811
 9812    let fs = FakeFs::new(cx.executor());
 9813    fs.insert_file("/file.ts", Default::default()).await;
 9814
 9815    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
 9816    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9817
 9818    language_registry.add(Arc::new(Language::new(
 9819        LanguageConfig {
 9820            name: "TypeScript".into(),
 9821            matcher: LanguageMatcher {
 9822                path_suffixes: vec!["ts".to_string()],
 9823                ..Default::default()
 9824            },
 9825            ..Default::default()
 9826        },
 9827        Some(tree_sitter_rust::language()),
 9828    )));
 9829    update_test_language_settings(cx, |settings| {
 9830        settings.defaults.prettier = Some(PrettierSettings {
 9831            allowed: true,
 9832            ..PrettierSettings::default()
 9833        });
 9834    });
 9835
 9836    let test_plugin = "test_plugin";
 9837    let _ = language_registry.register_fake_lsp_adapter(
 9838        "TypeScript",
 9839        FakeLspAdapter {
 9840            prettier_plugins: vec![test_plugin],
 9841            ..Default::default()
 9842        },
 9843    );
 9844
 9845    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 9846    let buffer = project
 9847        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
 9848        .await
 9849        .unwrap();
 9850
 9851    let buffer_text = "one\ntwo\nthree\n";
 9852    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9853    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9854    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 9855
 9856    editor
 9857        .update(cx, |editor, cx| {
 9858            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9859        })
 9860        .unwrap()
 9861        .await;
 9862    assert_eq!(
 9863        editor.update(cx, |editor, cx| editor.text(cx)),
 9864        buffer_text.to_string() + prettier_format_suffix,
 9865        "Test prettier formatting was not applied to the original buffer text",
 9866    );
 9867
 9868    update_test_language_settings(cx, |settings| {
 9869        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9870    });
 9871    let format = editor.update(cx, |editor, cx| {
 9872        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 9873    });
 9874    format.await.unwrap();
 9875    assert_eq!(
 9876        editor.update(cx, |editor, cx| editor.text(cx)),
 9877        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 9878        "Autoformatting (via test prettier) was not applied to the original buffer text",
 9879    );
 9880}
 9881
 9882#[gpui::test]
 9883async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 9884    init_test(cx, |_| {});
 9885    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9886    let base_text = indoc! {r#"struct Row;
 9887struct Row1;
 9888struct Row2;
 9889
 9890struct Row4;
 9891struct Row5;
 9892struct Row6;
 9893
 9894struct Row8;
 9895struct Row9;
 9896struct Row10;"#};
 9897
 9898    // When addition hunks are not adjacent to carets, no hunk revert is performed
 9899    assert_hunk_revert(
 9900        indoc! {r#"struct Row;
 9901                   struct Row1;
 9902                   struct Row1.1;
 9903                   struct Row1.2;
 9904                   struct Row2;ˇ
 9905
 9906                   struct Row4;
 9907                   struct Row5;
 9908                   struct Row6;
 9909
 9910                   struct Row8;
 9911                   ˇstruct Row9;
 9912                   struct Row9.1;
 9913                   struct Row9.2;
 9914                   struct Row9.3;
 9915                   struct Row10;"#},
 9916        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9917        indoc! {r#"struct Row;
 9918                   struct Row1;
 9919                   struct Row1.1;
 9920                   struct Row1.2;
 9921                   struct Row2;ˇ
 9922
 9923                   struct Row4;
 9924                   struct Row5;
 9925                   struct Row6;
 9926
 9927                   struct Row8;
 9928                   ˇstruct Row9;
 9929                   struct Row9.1;
 9930                   struct Row9.2;
 9931                   struct Row9.3;
 9932                   struct Row10;"#},
 9933        base_text,
 9934        &mut cx,
 9935    );
 9936    // Same for selections
 9937    assert_hunk_revert(
 9938        indoc! {r#"struct Row;
 9939                   struct Row1;
 9940                   struct Row2;
 9941                   struct Row2.1;
 9942                   struct Row2.2;
 9943                   «ˇ
 9944                   struct Row4;
 9945                   struct» Row5;
 9946                   «struct Row6;
 9947                   ˇ»
 9948                   struct Row9.1;
 9949                   struct Row9.2;
 9950                   struct Row9.3;
 9951                   struct Row8;
 9952                   struct Row9;
 9953                   struct Row10;"#},
 9954        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 9955        indoc! {r#"struct Row;
 9956                   struct Row1;
 9957                   struct Row2;
 9958                   struct Row2.1;
 9959                   struct Row2.2;
 9960                   «ˇ
 9961                   struct Row4;
 9962                   struct» Row5;
 9963                   «struct Row6;
 9964                   ˇ»
 9965                   struct Row9.1;
 9966                   struct Row9.2;
 9967                   struct Row9.3;
 9968                   struct Row8;
 9969                   struct Row9;
 9970                   struct Row10;"#},
 9971        base_text,
 9972        &mut cx,
 9973    );
 9974
 9975    // When carets and selections intersect the addition hunks, those are reverted.
 9976    // Adjacent carets got merged.
 9977    assert_hunk_revert(
 9978        indoc! {r#"struct Row;
 9979                   ˇ// something on the top
 9980                   struct Row1;
 9981                   struct Row2;
 9982                   struct Roˇw3.1;
 9983                   struct Row2.2;
 9984                   struct Row2.3;ˇ
 9985
 9986                   struct Row4;
 9987                   struct ˇRow5.1;
 9988                   struct Row5.2;
 9989                   struct «Rowˇ»5.3;
 9990                   struct Row5;
 9991                   struct Row6;
 9992                   ˇ
 9993                   struct Row9.1;
 9994                   struct «Rowˇ»9.2;
 9995                   struct «ˇRow»9.3;
 9996                   struct Row8;
 9997                   struct Row9;
 9998                   «ˇ// something on bottom»
 9999                   struct Row10;"#},
10000        vec![
10001            DiffHunkStatus::Added,
10002            DiffHunkStatus::Added,
10003            DiffHunkStatus::Added,
10004            DiffHunkStatus::Added,
10005            DiffHunkStatus::Added,
10006        ],
10007        indoc! {r#"struct Row;
10008                   ˇstruct Row1;
10009                   struct Row2;
10010                   ˇ
10011                   struct Row4;
10012                   ˇstruct Row5;
10013                   struct Row6;
10014                   ˇ
10015                   ˇstruct Row8;
10016                   struct Row9;
10017                   ˇstruct Row10;"#},
10018        base_text,
10019        &mut cx,
10020    );
10021}
10022
10023#[gpui::test]
10024async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10025    init_test(cx, |_| {});
10026    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10027    let base_text = indoc! {r#"struct Row;
10028struct Row1;
10029struct Row2;
10030
10031struct Row4;
10032struct Row5;
10033struct Row6;
10034
10035struct Row8;
10036struct Row9;
10037struct Row10;"#};
10038
10039    // Modification hunks behave the same as the addition ones.
10040    assert_hunk_revert(
10041        indoc! {r#"struct Row;
10042                   struct Row1;
10043                   struct Row33;
10044                   ˇ
10045                   struct Row4;
10046                   struct Row5;
10047                   struct Row6;
10048                   ˇ
10049                   struct Row99;
10050                   struct Row9;
10051                   struct Row10;"#},
10052        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10053        indoc! {r#"struct Row;
10054                   struct Row1;
10055                   struct Row33;
10056                   ˇ
10057                   struct Row4;
10058                   struct Row5;
10059                   struct Row6;
10060                   ˇ
10061                   struct Row99;
10062                   struct Row9;
10063                   struct Row10;"#},
10064        base_text,
10065        &mut cx,
10066    );
10067    assert_hunk_revert(
10068        indoc! {r#"struct Row;
10069                   struct Row1;
10070                   struct Row33;
10071                   «ˇ
10072                   struct Row4;
10073                   struct» Row5;
10074                   «struct Row6;
10075                   ˇ»
10076                   struct Row99;
10077                   struct Row9;
10078                   struct Row10;"#},
10079        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10080        indoc! {r#"struct Row;
10081                   struct Row1;
10082                   struct Row33;
10083                   «ˇ
10084                   struct Row4;
10085                   struct» Row5;
10086                   «struct Row6;
10087                   ˇ»
10088                   struct Row99;
10089                   struct Row9;
10090                   struct Row10;"#},
10091        base_text,
10092        &mut cx,
10093    );
10094
10095    assert_hunk_revert(
10096        indoc! {r#"ˇstruct Row1.1;
10097                   struct Row1;
10098                   «ˇstr»uct Row22;
10099
10100                   struct ˇRow44;
10101                   struct Row5;
10102                   struct «Rˇ»ow66;ˇ
10103
10104                   «struˇ»ct Row88;
10105                   struct Row9;
10106                   struct Row1011;ˇ"#},
10107        vec![
10108            DiffHunkStatus::Modified,
10109            DiffHunkStatus::Modified,
10110            DiffHunkStatus::Modified,
10111            DiffHunkStatus::Modified,
10112            DiffHunkStatus::Modified,
10113            DiffHunkStatus::Modified,
10114        ],
10115        indoc! {r#"struct Row;
10116                   ˇstruct Row1;
10117                   struct Row2;
10118                   ˇ
10119                   struct Row4;
10120                   ˇstruct Row5;
10121                   struct Row6;
10122                   ˇ
10123                   struct Row8;
10124                   ˇstruct Row9;
10125                   struct Row10;ˇ"#},
10126        base_text,
10127        &mut cx,
10128    );
10129}
10130
10131#[gpui::test]
10132async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10133    init_test(cx, |_| {});
10134    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10135    let base_text = indoc! {r#"struct Row;
10136struct Row1;
10137struct Row2;
10138
10139struct Row4;
10140struct Row5;
10141struct Row6;
10142
10143struct Row8;
10144struct Row9;
10145struct Row10;"#};
10146
10147    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10148    assert_hunk_revert(
10149        indoc! {r#"struct Row;
10150                   struct Row2;
10151
10152                   ˇstruct Row4;
10153                   struct Row5;
10154                   struct Row6;
10155                   ˇ
10156                   struct Row8;
10157                   struct Row10;"#},
10158        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
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        base_text,
10169        &mut cx,
10170    );
10171    assert_hunk_revert(
10172        indoc! {r#"struct Row;
10173                   struct Row2;
10174
10175                   «ˇstruct Row4;
10176                   struct» Row5;
10177                   «struct Row6;
10178                   ˇ»
10179                   struct Row8;
10180                   struct Row10;"#},
10181        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10182        indoc! {r#"struct Row;
10183                   struct Row2;
10184
10185                   «ˇstruct Row4;
10186                   struct» Row5;
10187                   «struct Row6;
10188                   ˇ»
10189                   struct Row8;
10190                   struct Row10;"#},
10191        base_text,
10192        &mut cx,
10193    );
10194
10195    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10196    assert_hunk_revert(
10197        indoc! {r#"struct Row;
10198                   ˇstruct Row2;
10199
10200                   struct Row4;
10201                   struct Row5;
10202                   struct Row6;
10203
10204                   struct Row8;ˇ
10205                   struct Row10;"#},
10206        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10207        indoc! {r#"struct Row;
10208                   struct Row1;
10209                   ˇstruct Row2;
10210
10211                   struct Row4;
10212                   struct Row5;
10213                   struct Row6;
10214
10215                   struct Row8;ˇ
10216                   struct Row9;
10217                   struct Row10;"#},
10218        base_text,
10219        &mut cx,
10220    );
10221    assert_hunk_revert(
10222        indoc! {r#"struct Row;
10223                   struct Row2«ˇ;
10224                   struct Row4;
10225                   struct» Row5;
10226                   «struct Row6;
10227
10228                   struct Row8;ˇ»
10229                   struct Row10;"#},
10230        vec![
10231            DiffHunkStatus::Removed,
10232            DiffHunkStatus::Removed,
10233            DiffHunkStatus::Removed,
10234        ],
10235        indoc! {r#"struct Row;
10236                   struct Row1;
10237                   struct Row2«ˇ;
10238
10239                   struct Row4;
10240                   struct» Row5;
10241                   «struct Row6;
10242
10243                   struct Row8;ˇ»
10244                   struct Row9;
10245                   struct Row10;"#},
10246        base_text,
10247        &mut cx,
10248    );
10249}
10250
10251#[gpui::test]
10252async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10253    init_test(cx, |_| {});
10254
10255    let cols = 4;
10256    let rows = 10;
10257    let sample_text_1 = sample_text(rows, cols, 'a');
10258    assert_eq!(
10259        sample_text_1,
10260        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10261    );
10262    let sample_text_2 = sample_text(rows, cols, 'l');
10263    assert_eq!(
10264        sample_text_2,
10265        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10266    );
10267    let sample_text_3 = sample_text(rows, cols, 'v');
10268    assert_eq!(
10269        sample_text_3,
10270        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10271    );
10272
10273    fn diff_every_buffer_row(
10274        buffer: &Model<Buffer>,
10275        sample_text: String,
10276        cols: usize,
10277        cx: &mut gpui::TestAppContext,
10278    ) {
10279        // revert first character in each row, creating one large diff hunk per buffer
10280        let is_first_char = |offset: usize| offset % cols == 0;
10281        buffer.update(cx, |buffer, cx| {
10282            buffer.set_text(
10283                sample_text
10284                    .chars()
10285                    .enumerate()
10286                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10287                    .collect::<String>(),
10288                cx,
10289            );
10290            buffer.set_diff_base(Some(sample_text), cx);
10291        });
10292        cx.executor().run_until_parked();
10293    }
10294
10295    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10296    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10297
10298    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10299    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10300
10301    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10302    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10303
10304    let multibuffer = cx.new_model(|cx| {
10305        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10306        multibuffer.push_excerpts(
10307            buffer_1.clone(),
10308            [
10309                ExcerptRange {
10310                    context: Point::new(0, 0)..Point::new(3, 0),
10311                    primary: None,
10312                },
10313                ExcerptRange {
10314                    context: Point::new(5, 0)..Point::new(7, 0),
10315                    primary: None,
10316                },
10317                ExcerptRange {
10318                    context: Point::new(9, 0)..Point::new(10, 4),
10319                    primary: None,
10320                },
10321            ],
10322            cx,
10323        );
10324        multibuffer.push_excerpts(
10325            buffer_2.clone(),
10326            [
10327                ExcerptRange {
10328                    context: Point::new(0, 0)..Point::new(3, 0),
10329                    primary: None,
10330                },
10331                ExcerptRange {
10332                    context: Point::new(5, 0)..Point::new(7, 0),
10333                    primary: None,
10334                },
10335                ExcerptRange {
10336                    context: Point::new(9, 0)..Point::new(10, 4),
10337                    primary: None,
10338                },
10339            ],
10340            cx,
10341        );
10342        multibuffer.push_excerpts(
10343            buffer_3.clone(),
10344            [
10345                ExcerptRange {
10346                    context: Point::new(0, 0)..Point::new(3, 0),
10347                    primary: None,
10348                },
10349                ExcerptRange {
10350                    context: Point::new(5, 0)..Point::new(7, 0),
10351                    primary: None,
10352                },
10353                ExcerptRange {
10354                    context: Point::new(9, 0)..Point::new(10, 4),
10355                    primary: None,
10356                },
10357            ],
10358            cx,
10359        );
10360        multibuffer
10361    });
10362
10363    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10364    editor.update(cx, |editor, cx| {
10365        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");
10366        editor.select_all(&SelectAll, cx);
10367        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10368    });
10369    cx.executor().run_until_parked();
10370    // When all ranges are selected, all buffer hunks are reverted.
10371    editor.update(cx, |editor, cx| {
10372        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");
10373    });
10374    buffer_1.update(cx, |buffer, _| {
10375        assert_eq!(buffer.text(), sample_text_1);
10376    });
10377    buffer_2.update(cx, |buffer, _| {
10378        assert_eq!(buffer.text(), sample_text_2);
10379    });
10380    buffer_3.update(cx, |buffer, _| {
10381        assert_eq!(buffer.text(), sample_text_3);
10382    });
10383
10384    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10385    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10386    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10387    editor.update(cx, |editor, cx| {
10388        editor.change_selections(None, cx, |s| {
10389            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10390        });
10391        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10392    });
10393    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10394    // but not affect buffer_2 and its related excerpts.
10395    editor.update(cx, |editor, cx| {
10396        assert_eq!(
10397            editor.text(cx),
10398            "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"
10399        );
10400    });
10401    buffer_1.update(cx, |buffer, _| {
10402        assert_eq!(buffer.text(), sample_text_1);
10403    });
10404    buffer_2.update(cx, |buffer, _| {
10405        assert_eq!(
10406            buffer.text(),
10407            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10408        );
10409    });
10410    buffer_3.update(cx, |buffer, _| {
10411        assert_eq!(
10412            buffer.text(),
10413            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10414        );
10415    });
10416}
10417
10418#[gpui::test]
10419async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10420    init_test(cx, |_| {});
10421
10422    let cols = 4;
10423    let rows = 10;
10424    let sample_text_1 = sample_text(rows, cols, 'a');
10425    assert_eq!(
10426        sample_text_1,
10427        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10428    );
10429    let sample_text_2 = sample_text(rows, cols, 'l');
10430    assert_eq!(
10431        sample_text_2,
10432        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10433    );
10434    let sample_text_3 = sample_text(rows, cols, 'v');
10435    assert_eq!(
10436        sample_text_3,
10437        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10438    );
10439
10440    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10441    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10442    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10443
10444    let multi_buffer = cx.new_model(|cx| {
10445        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10446        multibuffer.push_excerpts(
10447            buffer_1.clone(),
10448            [
10449                ExcerptRange {
10450                    context: Point::new(0, 0)..Point::new(3, 0),
10451                    primary: None,
10452                },
10453                ExcerptRange {
10454                    context: Point::new(5, 0)..Point::new(7, 0),
10455                    primary: None,
10456                },
10457                ExcerptRange {
10458                    context: Point::new(9, 0)..Point::new(10, 4),
10459                    primary: None,
10460                },
10461            ],
10462            cx,
10463        );
10464        multibuffer.push_excerpts(
10465            buffer_2.clone(),
10466            [
10467                ExcerptRange {
10468                    context: Point::new(0, 0)..Point::new(3, 0),
10469                    primary: None,
10470                },
10471                ExcerptRange {
10472                    context: Point::new(5, 0)..Point::new(7, 0),
10473                    primary: None,
10474                },
10475                ExcerptRange {
10476                    context: Point::new(9, 0)..Point::new(10, 4),
10477                    primary: None,
10478                },
10479            ],
10480            cx,
10481        );
10482        multibuffer.push_excerpts(
10483            buffer_3.clone(),
10484            [
10485                ExcerptRange {
10486                    context: Point::new(0, 0)..Point::new(3, 0),
10487                    primary: None,
10488                },
10489                ExcerptRange {
10490                    context: Point::new(5, 0)..Point::new(7, 0),
10491                    primary: None,
10492                },
10493                ExcerptRange {
10494                    context: Point::new(9, 0)..Point::new(10, 4),
10495                    primary: None,
10496                },
10497            ],
10498            cx,
10499        );
10500        multibuffer
10501    });
10502
10503    let fs = FakeFs::new(cx.executor());
10504    fs.insert_tree(
10505        "/a",
10506        json!({
10507            "main.rs": sample_text_1,
10508            "other.rs": sample_text_2,
10509            "lib.rs": sample_text_3,
10510        }),
10511    )
10512    .await;
10513    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10514    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10515    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10516    let multi_buffer_editor = cx.new_view(|cx| {
10517        Editor::new(
10518            EditorMode::Full,
10519            multi_buffer,
10520            Some(project.clone()),
10521            true,
10522            cx,
10523        )
10524    });
10525    let multibuffer_item_id = workspace
10526        .update(cx, |workspace, cx| {
10527            assert!(
10528                workspace.active_item(cx).is_none(),
10529                "active item should be None before the first item is added"
10530            );
10531            workspace.add_item_to_active_pane(
10532                Box::new(multi_buffer_editor.clone()),
10533                None,
10534                true,
10535                cx,
10536            );
10537            let active_item = workspace
10538                .active_item(cx)
10539                .expect("should have an active item after adding the multi buffer");
10540            assert!(
10541                !active_item.is_singleton(cx),
10542                "A multi buffer was expected to active after adding"
10543            );
10544            active_item.item_id()
10545        })
10546        .unwrap();
10547    cx.executor().run_until_parked();
10548
10549    multi_buffer_editor.update(cx, |editor, cx| {
10550        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
10551        editor.open_excerpts(&OpenExcerpts, cx);
10552    });
10553    cx.executor().run_until_parked();
10554    let first_item_id = workspace
10555        .update(cx, |workspace, cx| {
10556            let active_item = workspace
10557                .active_item(cx)
10558                .expect("should have an active item after navigating into the 1st buffer");
10559            let first_item_id = active_item.item_id();
10560            assert_ne!(
10561                first_item_id, multibuffer_item_id,
10562                "Should navigate into the 1st buffer and activate it"
10563            );
10564            assert!(
10565                active_item.is_singleton(cx),
10566                "New active item should be a singleton buffer"
10567            );
10568            assert_eq!(
10569                active_item
10570                    .act_as::<Editor>(cx)
10571                    .expect("should have navigated into an editor for the 1st buffer")
10572                    .read(cx)
10573                    .text(cx),
10574                sample_text_1
10575            );
10576
10577            workspace
10578                .go_back(workspace.active_pane().downgrade(), cx)
10579                .detach_and_log_err(cx);
10580
10581            first_item_id
10582        })
10583        .unwrap();
10584    cx.executor().run_until_parked();
10585    workspace
10586        .update(cx, |workspace, cx| {
10587            let active_item = workspace
10588                .active_item(cx)
10589                .expect("should have an active item after navigating back");
10590            assert_eq!(
10591                active_item.item_id(),
10592                multibuffer_item_id,
10593                "Should navigate back to the multi buffer"
10594            );
10595            assert!(!active_item.is_singleton(cx));
10596        })
10597        .unwrap();
10598
10599    multi_buffer_editor.update(cx, |editor, cx| {
10600        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10601            s.select_ranges(Some(39..40))
10602        });
10603        editor.open_excerpts(&OpenExcerpts, cx);
10604    });
10605    cx.executor().run_until_parked();
10606    let second_item_id = workspace
10607        .update(cx, |workspace, cx| {
10608            let active_item = workspace
10609                .active_item(cx)
10610                .expect("should have an active item after navigating into the 2nd buffer");
10611            let second_item_id = active_item.item_id();
10612            assert_ne!(
10613                second_item_id, multibuffer_item_id,
10614                "Should navigate away from the multibuffer"
10615            );
10616            assert_ne!(
10617                second_item_id, first_item_id,
10618                "Should navigate into the 2nd buffer and activate it"
10619            );
10620            assert!(
10621                active_item.is_singleton(cx),
10622                "New active item should be a singleton buffer"
10623            );
10624            assert_eq!(
10625                active_item
10626                    .act_as::<Editor>(cx)
10627                    .expect("should have navigated into an editor")
10628                    .read(cx)
10629                    .text(cx),
10630                sample_text_2
10631            );
10632
10633            workspace
10634                .go_back(workspace.active_pane().downgrade(), cx)
10635                .detach_and_log_err(cx);
10636
10637            second_item_id
10638        })
10639        .unwrap();
10640    cx.executor().run_until_parked();
10641    workspace
10642        .update(cx, |workspace, cx| {
10643            let active_item = workspace
10644                .active_item(cx)
10645                .expect("should have an active item after navigating back from the 2nd buffer");
10646            assert_eq!(
10647                active_item.item_id(),
10648                multibuffer_item_id,
10649                "Should navigate back from the 2nd buffer to the multi buffer"
10650            );
10651            assert!(!active_item.is_singleton(cx));
10652        })
10653        .unwrap();
10654
10655    multi_buffer_editor.update(cx, |editor, cx| {
10656        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10657            s.select_ranges(Some(60..70))
10658        });
10659        editor.open_excerpts(&OpenExcerpts, cx);
10660    });
10661    cx.executor().run_until_parked();
10662    workspace
10663        .update(cx, |workspace, cx| {
10664            let active_item = workspace
10665                .active_item(cx)
10666                .expect("should have an active item after navigating into the 3rd buffer");
10667            let third_item_id = active_item.item_id();
10668            assert_ne!(
10669                third_item_id, multibuffer_item_id,
10670                "Should navigate into the 3rd buffer and activate it"
10671            );
10672            assert_ne!(third_item_id, first_item_id);
10673            assert_ne!(third_item_id, second_item_id);
10674            assert!(
10675                active_item.is_singleton(cx),
10676                "New active item should be a singleton buffer"
10677            );
10678            assert_eq!(
10679                active_item
10680                    .act_as::<Editor>(cx)
10681                    .expect("should have navigated into an editor")
10682                    .read(cx)
10683                    .text(cx),
10684                sample_text_3
10685            );
10686
10687            workspace
10688                .go_back(workspace.active_pane().downgrade(), cx)
10689                .detach_and_log_err(cx);
10690        })
10691        .unwrap();
10692    cx.executor().run_until_parked();
10693    workspace
10694        .update(cx, |workspace, cx| {
10695            let active_item = workspace
10696                .active_item(cx)
10697                .expect("should have an active item after navigating back from the 3rd buffer");
10698            assert_eq!(
10699                active_item.item_id(),
10700                multibuffer_item_id,
10701                "Should navigate back from the 3rd buffer to the multi buffer"
10702            );
10703            assert!(!active_item.is_singleton(cx));
10704        })
10705        .unwrap();
10706}
10707
10708#[gpui::test]
10709async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10710    init_test(cx, |_| {});
10711
10712    let mut cx = EditorTestContext::new(cx).await;
10713
10714    let diff_base = r#"
10715        use some::mod;
10716
10717        const A: u32 = 42;
10718
10719        fn main() {
10720            println!("hello");
10721
10722            println!("world");
10723        }
10724        "#
10725    .unindent();
10726
10727    cx.set_state(
10728        &r#"
10729        use some::modified;
10730
10731        ˇ
10732        fn main() {
10733            println!("hello there");
10734
10735            println!("around the");
10736            println!("world");
10737        }
10738        "#
10739        .unindent(),
10740    );
10741
10742    cx.set_diff_base(Some(&diff_base));
10743    executor.run_until_parked();
10744    let unexpanded_hunks = vec![
10745        (
10746            "use some::mod;\n".to_string(),
10747            DiffHunkStatus::Modified,
10748            DisplayRow(0)..DisplayRow(1),
10749        ),
10750        (
10751            "const A: u32 = 42;\n".to_string(),
10752            DiffHunkStatus::Removed,
10753            DisplayRow(2)..DisplayRow(2),
10754        ),
10755        (
10756            "    println!(\"hello\");\n".to_string(),
10757            DiffHunkStatus::Modified,
10758            DisplayRow(4)..DisplayRow(5),
10759        ),
10760        (
10761            "".to_string(),
10762            DiffHunkStatus::Added,
10763            DisplayRow(6)..DisplayRow(7),
10764        ),
10765    ];
10766    cx.update_editor(|editor, cx| {
10767        let snapshot = editor.snapshot(cx);
10768        let all_hunks = editor_hunks(editor, &snapshot, cx);
10769        assert_eq!(all_hunks, unexpanded_hunks);
10770    });
10771
10772    cx.update_editor(|editor, cx| {
10773        for _ in 0..4 {
10774            editor.go_to_hunk(&GoToHunk, cx);
10775            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10776        }
10777    });
10778    executor.run_until_parked();
10779    cx.assert_editor_state(
10780        &r#"
10781        use some::modified;
10782
10783        ˇ
10784        fn main() {
10785            println!("hello there");
10786
10787            println!("around the");
10788            println!("world");
10789        }
10790        "#
10791        .unindent(),
10792    );
10793    cx.update_editor(|editor, cx| {
10794        let snapshot = editor.snapshot(cx);
10795        let all_hunks = editor_hunks(editor, &snapshot, cx);
10796        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10797        assert_eq!(
10798            expanded_hunks_background_highlights(editor, cx),
10799            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
10800            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10801        );
10802        assert_eq!(
10803            all_hunks,
10804            vec![
10805                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
10806                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
10807                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
10808                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
10809            ],
10810            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10811            (from modified and removed hunks)"
10812        );
10813        assert_eq!(
10814            all_hunks, all_expanded_hunks,
10815            "Editor hunks should not change and all be expanded"
10816        );
10817    });
10818
10819    cx.update_editor(|editor, cx| {
10820        editor.cancel(&Cancel, cx);
10821
10822        let snapshot = editor.snapshot(cx);
10823        let all_hunks = editor_hunks(editor, &snapshot, cx);
10824        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10825        assert_eq!(
10826            expanded_hunks_background_highlights(editor, cx),
10827            Vec::new(),
10828            "After cancelling in editor, no git highlights should be left"
10829        );
10830        assert_eq!(
10831            all_expanded_hunks,
10832            Vec::new(),
10833            "After cancelling in editor, no hunks should be expanded"
10834        );
10835        assert_eq!(
10836            all_hunks, unexpanded_hunks,
10837            "After cancelling in editor, regular hunks' coordinates should get back to normal"
10838        );
10839    });
10840}
10841
10842#[gpui::test]
10843async fn test_toggled_diff_base_change(
10844    executor: BackgroundExecutor,
10845    cx: &mut gpui::TestAppContext,
10846) {
10847    init_test(cx, |_| {});
10848
10849    let mut cx = EditorTestContext::new(cx).await;
10850
10851    let diff_base = r#"
10852        use some::mod1;
10853        use some::mod2;
10854
10855        const A: u32 = 42;
10856        const B: u32 = 42;
10857        const C: u32 = 42;
10858
10859        fn main(ˇ) {
10860            println!("hello");
10861
10862            println!("world");
10863        }
10864        "#
10865    .unindent();
10866
10867    cx.set_state(
10868        &r#"
10869        use some::mod2;
10870
10871        const A: u32 = 42;
10872        const C: u32 = 42;
10873
10874        fn main(ˇ) {
10875            //println!("hello");
10876
10877            println!("world");
10878            //
10879            //
10880        }
10881        "#
10882        .unindent(),
10883    );
10884
10885    cx.set_diff_base(Some(&diff_base));
10886    executor.run_until_parked();
10887    cx.update_editor(|editor, cx| {
10888        let snapshot = editor.snapshot(cx);
10889        let all_hunks = editor_hunks(editor, &snapshot, cx);
10890        assert_eq!(
10891            all_hunks,
10892            vec![
10893                (
10894                    "use some::mod1;\n".to_string(),
10895                    DiffHunkStatus::Removed,
10896                    DisplayRow(0)..DisplayRow(0)
10897                ),
10898                (
10899                    "const B: u32 = 42;\n".to_string(),
10900                    DiffHunkStatus::Removed,
10901                    DisplayRow(3)..DisplayRow(3)
10902                ),
10903                (
10904                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10905                    DiffHunkStatus::Modified,
10906                    DisplayRow(5)..DisplayRow(7)
10907                ),
10908                (
10909                    "".to_string(),
10910                    DiffHunkStatus::Added,
10911                    DisplayRow(9)..DisplayRow(11)
10912                ),
10913            ]
10914        );
10915    });
10916
10917    cx.update_editor(|editor, cx| {
10918        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10919    });
10920    executor.run_until_parked();
10921    cx.assert_editor_state(
10922        &r#"
10923        use some::mod2;
10924
10925        const A: u32 = 42;
10926        const C: u32 = 42;
10927
10928        fn main(ˇ) {
10929            //println!("hello");
10930
10931            println!("world");
10932            //
10933            //
10934        }
10935        "#
10936        .unindent(),
10937    );
10938    cx.update_editor(|editor, cx| {
10939        let snapshot = editor.snapshot(cx);
10940        let all_hunks = editor_hunks(editor, &snapshot, cx);
10941        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10942        assert_eq!(
10943            expanded_hunks_background_highlights(editor, cx),
10944            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
10945            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
10946        );
10947        assert_eq!(
10948            all_hunks,
10949            vec![
10950                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
10951                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
10952                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
10953                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
10954            ],
10955            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
10956            (from modified and removed hunks)"
10957        );
10958        assert_eq!(
10959            all_hunks, all_expanded_hunks,
10960            "Editor hunks should not change and all be expanded"
10961        );
10962    });
10963
10964    cx.set_diff_base(Some("new diff base!"));
10965    executor.run_until_parked();
10966
10967    cx.update_editor(|editor, cx| {
10968        let snapshot = editor.snapshot(cx);
10969        let all_hunks = editor_hunks(editor, &snapshot, cx);
10970        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
10971        assert_eq!(
10972            expanded_hunks_background_highlights(editor, cx),
10973            Vec::new(),
10974            "After diff base is changed, old git highlights should be removed"
10975        );
10976        assert_eq!(
10977            all_expanded_hunks,
10978            Vec::new(),
10979            "After diff base is changed, old git hunk expansions should be removed"
10980        );
10981        assert_eq!(
10982            all_hunks,
10983            vec![(
10984                "new diff base!".to_string(),
10985                DiffHunkStatus::Modified,
10986                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
10987            )],
10988            "After diff base is changed, hunks should update"
10989        );
10990    });
10991}
10992
10993#[gpui::test]
10994async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10995    init_test(cx, |_| {});
10996
10997    let mut cx = EditorTestContext::new(cx).await;
10998
10999    let diff_base = r#"
11000        use some::mod1;
11001        use some::mod2;
11002
11003        const A: u32 = 42;
11004        const B: u32 = 42;
11005        const C: u32 = 42;
11006
11007        fn main(ˇ) {
11008            println!("hello");
11009
11010            println!("world");
11011        }
11012
11013        fn another() {
11014            println!("another");
11015        }
11016
11017        fn another2() {
11018            println!("another2");
11019        }
11020        "#
11021    .unindent();
11022
11023    cx.set_state(
11024        &r#"
11025        «use some::mod2;
11026
11027        const A: u32 = 42;
11028        const C: u32 = 42;
11029
11030        fn main() {
11031            //println!("hello");
11032
11033            println!("world");
11034            //
11035            //ˇ»
11036        }
11037
11038        fn another() {
11039            println!("another");
11040            println!("another");
11041        }
11042
11043            println!("another2");
11044        }
11045        "#
11046        .unindent(),
11047    );
11048
11049    cx.set_diff_base(Some(&diff_base));
11050    executor.run_until_parked();
11051    cx.update_editor(|editor, cx| {
11052        let snapshot = editor.snapshot(cx);
11053        let all_hunks = editor_hunks(editor, &snapshot, cx);
11054        assert_eq!(
11055            all_hunks,
11056            vec![
11057                (
11058                    "use some::mod1;\n".to_string(),
11059                    DiffHunkStatus::Removed,
11060                    DisplayRow(0)..DisplayRow(0)
11061                ),
11062                (
11063                    "const B: u32 = 42;\n".to_string(),
11064                    DiffHunkStatus::Removed,
11065                    DisplayRow(3)..DisplayRow(3)
11066                ),
11067                (
11068                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11069                    DiffHunkStatus::Modified,
11070                    DisplayRow(5)..DisplayRow(7)
11071                ),
11072                (
11073                    "".to_string(),
11074                    DiffHunkStatus::Added,
11075                    DisplayRow(9)..DisplayRow(11)
11076                ),
11077                (
11078                    "".to_string(),
11079                    DiffHunkStatus::Added,
11080                    DisplayRow(15)..DisplayRow(16)
11081                ),
11082                (
11083                    "fn another2() {\n".to_string(),
11084                    DiffHunkStatus::Removed,
11085                    DisplayRow(18)..DisplayRow(18)
11086                ),
11087            ]
11088        );
11089    });
11090
11091    cx.update_editor(|editor, cx| {
11092        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11093    });
11094    executor.run_until_parked();
11095    cx.assert_editor_state(
11096        &r#"
11097        «use some::mod2;
11098
11099        const A: u32 = 42;
11100        const C: u32 = 42;
11101
11102        fn main() {
11103            //println!("hello");
11104
11105            println!("world");
11106            //
11107            //ˇ»
11108        }
11109
11110        fn another() {
11111            println!("another");
11112            println!("another");
11113        }
11114
11115            println!("another2");
11116        }
11117        "#
11118        .unindent(),
11119    );
11120    cx.update_editor(|editor, cx| {
11121        let snapshot = editor.snapshot(cx);
11122        let all_hunks = editor_hunks(editor, &snapshot, cx);
11123        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11124        assert_eq!(
11125            expanded_hunks_background_highlights(editor, cx),
11126            vec![
11127                DisplayRow(9)..=DisplayRow(10),
11128                DisplayRow(13)..=DisplayRow(14),
11129                DisplayRow(19)..=DisplayRow(19)
11130            ]
11131        );
11132        assert_eq!(
11133            all_hunks,
11134            vec![
11135                (
11136                    "use some::mod1;\n".to_string(),
11137                    DiffHunkStatus::Removed,
11138                    DisplayRow(1)..DisplayRow(1)
11139                ),
11140                (
11141                    "const B: u32 = 42;\n".to_string(),
11142                    DiffHunkStatus::Removed,
11143                    DisplayRow(5)..DisplayRow(5)
11144                ),
11145                (
11146                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11147                    DiffHunkStatus::Modified,
11148                    DisplayRow(9)..DisplayRow(11)
11149                ),
11150                (
11151                    "".to_string(),
11152                    DiffHunkStatus::Added,
11153                    DisplayRow(13)..DisplayRow(15)
11154                ),
11155                (
11156                    "".to_string(),
11157                    DiffHunkStatus::Added,
11158                    DisplayRow(19)..DisplayRow(20)
11159                ),
11160                (
11161                    "fn another2() {\n".to_string(),
11162                    DiffHunkStatus::Removed,
11163                    DisplayRow(23)..DisplayRow(23)
11164                ),
11165            ],
11166        );
11167        assert_eq!(all_hunks, all_expanded_hunks);
11168    });
11169
11170    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11171    cx.executor().run_until_parked();
11172    cx.assert_editor_state(
11173        &r#"
11174        «use some::mod2;
11175
11176        const A: u32 = 42;
11177        const C: u32 = 42;
11178
11179        fn main() {
11180            //println!("hello");
11181
11182            println!("world");
11183            //
11184            //ˇ»
11185        }
11186
11187        fn another() {
11188            println!("another");
11189            println!("another");
11190        }
11191
11192            println!("another2");
11193        }
11194        "#
11195        .unindent(),
11196    );
11197    cx.update_editor(|editor, cx| {
11198        let snapshot = editor.snapshot(cx);
11199        let all_hunks = editor_hunks(editor, &snapshot, cx);
11200        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11201        assert_eq!(
11202            expanded_hunks_background_highlights(editor, cx),
11203            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
11204            "Only one hunk is left not folded, its highlight should be visible"
11205        );
11206        assert_eq!(
11207            all_hunks,
11208            vec![
11209                (
11210                    "use some::mod1;\n".to_string(),
11211                    DiffHunkStatus::Removed,
11212                    DisplayRow(0)..DisplayRow(0)
11213                ),
11214                (
11215                    "const B: u32 = 42;\n".to_string(),
11216                    DiffHunkStatus::Removed,
11217                    DisplayRow(0)..DisplayRow(0)
11218                ),
11219                (
11220                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11221                    DiffHunkStatus::Modified,
11222                    DisplayRow(0)..DisplayRow(0)
11223                ),
11224                (
11225                    "".to_string(),
11226                    DiffHunkStatus::Added,
11227                    DisplayRow(0)..DisplayRow(1)
11228                ),
11229                (
11230                    "".to_string(),
11231                    DiffHunkStatus::Added,
11232                    DisplayRow(5)..DisplayRow(6)
11233                ),
11234                (
11235                    "fn another2() {\n".to_string(),
11236                    DiffHunkStatus::Removed,
11237                    DisplayRow(9)..DisplayRow(9)
11238                ),
11239            ],
11240            "Hunk list should still return shifted folded hunks"
11241        );
11242        assert_eq!(
11243            all_expanded_hunks,
11244            vec![
11245                (
11246                    "".to_string(),
11247                    DiffHunkStatus::Added,
11248                    DisplayRow(5)..DisplayRow(6)
11249                ),
11250                (
11251                    "fn another2() {\n".to_string(),
11252                    DiffHunkStatus::Removed,
11253                    DisplayRow(9)..DisplayRow(9)
11254                ),
11255            ],
11256            "Only non-folded hunks should be left expanded"
11257        );
11258    });
11259
11260    cx.update_editor(|editor, cx| {
11261        editor.select_all(&SelectAll, cx);
11262        editor.unfold_lines(&UnfoldLines, cx);
11263    });
11264    cx.executor().run_until_parked();
11265    cx.assert_editor_state(
11266        &r#"
11267        «use some::mod2;
11268
11269        const A: u32 = 42;
11270        const C: u32 = 42;
11271
11272        fn main() {
11273            //println!("hello");
11274
11275            println!("world");
11276            //
11277            //
11278        }
11279
11280        fn another() {
11281            println!("another");
11282            println!("another");
11283        }
11284
11285            println!("another2");
11286        }
11287        ˇ»"#
11288        .unindent(),
11289    );
11290    cx.update_editor(|editor, cx| {
11291        let snapshot = editor.snapshot(cx);
11292        let all_hunks = editor_hunks(editor, &snapshot, cx);
11293        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11294        assert_eq!(
11295            expanded_hunks_background_highlights(editor, cx),
11296            vec![
11297                DisplayRow(9)..=DisplayRow(10),
11298                DisplayRow(13)..=DisplayRow(14),
11299                DisplayRow(19)..=DisplayRow(19)
11300            ],
11301            "After unfolding, all hunk diffs should be visible again"
11302        );
11303        assert_eq!(
11304            all_hunks,
11305            vec![
11306                (
11307                    "use some::mod1;\n".to_string(),
11308                    DiffHunkStatus::Removed,
11309                    DisplayRow(1)..DisplayRow(1)
11310                ),
11311                (
11312                    "const B: u32 = 42;\n".to_string(),
11313                    DiffHunkStatus::Removed,
11314                    DisplayRow(5)..DisplayRow(5)
11315                ),
11316                (
11317                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11318                    DiffHunkStatus::Modified,
11319                    DisplayRow(9)..DisplayRow(11)
11320                ),
11321                (
11322                    "".to_string(),
11323                    DiffHunkStatus::Added,
11324                    DisplayRow(13)..DisplayRow(15)
11325                ),
11326                (
11327                    "".to_string(),
11328                    DiffHunkStatus::Added,
11329                    DisplayRow(19)..DisplayRow(20)
11330                ),
11331                (
11332                    "fn another2() {\n".to_string(),
11333                    DiffHunkStatus::Removed,
11334                    DisplayRow(23)..DisplayRow(23)
11335                ),
11336            ],
11337        );
11338        assert_eq!(all_hunks, all_expanded_hunks);
11339    });
11340}
11341
11342#[gpui::test]
11343async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11344    init_test(cx, |_| {});
11345
11346    let cols = 4;
11347    let rows = 10;
11348    let sample_text_1 = sample_text(rows, cols, 'a');
11349    assert_eq!(
11350        sample_text_1,
11351        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11352    );
11353    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11354    let sample_text_2 = sample_text(rows, cols, 'l');
11355    assert_eq!(
11356        sample_text_2,
11357        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11358    );
11359    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11360    let sample_text_3 = sample_text(rows, cols, 'v');
11361    assert_eq!(
11362        sample_text_3,
11363        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11364    );
11365    let modified_sample_text_3 =
11366        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11367    let buffer_1 = cx.new_model(|cx| {
11368        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
11369        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
11370        buffer
11371    });
11372    let buffer_2 = cx.new_model(|cx| {
11373        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
11374        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
11375        buffer
11376    });
11377    let buffer_3 = cx.new_model(|cx| {
11378        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
11379        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
11380        buffer
11381    });
11382
11383    let multi_buffer = cx.new_model(|cx| {
11384        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
11385        multibuffer.push_excerpts(
11386            buffer_1.clone(),
11387            [
11388                ExcerptRange {
11389                    context: Point::new(0, 0)..Point::new(3, 0),
11390                    primary: None,
11391                },
11392                ExcerptRange {
11393                    context: Point::new(5, 0)..Point::new(7, 0),
11394                    primary: None,
11395                },
11396                ExcerptRange {
11397                    context: Point::new(9, 0)..Point::new(10, 4),
11398                    primary: None,
11399                },
11400            ],
11401            cx,
11402        );
11403        multibuffer.push_excerpts(
11404            buffer_2.clone(),
11405            [
11406                ExcerptRange {
11407                    context: Point::new(0, 0)..Point::new(3, 0),
11408                    primary: None,
11409                },
11410                ExcerptRange {
11411                    context: Point::new(5, 0)..Point::new(7, 0),
11412                    primary: None,
11413                },
11414                ExcerptRange {
11415                    context: Point::new(9, 0)..Point::new(10, 4),
11416                    primary: None,
11417                },
11418            ],
11419            cx,
11420        );
11421        multibuffer.push_excerpts(
11422            buffer_3.clone(),
11423            [
11424                ExcerptRange {
11425                    context: Point::new(0, 0)..Point::new(3, 0),
11426                    primary: None,
11427                },
11428                ExcerptRange {
11429                    context: Point::new(5, 0)..Point::new(7, 0),
11430                    primary: None,
11431                },
11432                ExcerptRange {
11433                    context: Point::new(9, 0)..Point::new(10, 4),
11434                    primary: None,
11435                },
11436            ],
11437            cx,
11438        );
11439        multibuffer
11440    });
11441
11442    let fs = FakeFs::new(cx.executor());
11443    fs.insert_tree(
11444        "/a",
11445        json!({
11446            "main.rs": modified_sample_text_1,
11447            "other.rs": modified_sample_text_2,
11448            "lib.rs": modified_sample_text_3,
11449        }),
11450    )
11451    .await;
11452
11453    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11454    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11455    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11456    let multi_buffer_editor = cx.new_view(|cx| {
11457        Editor::new(
11458            EditorMode::Full,
11459            multi_buffer,
11460            Some(project.clone()),
11461            true,
11462            cx,
11463        )
11464    });
11465    cx.executor().run_until_parked();
11466
11467    let expected_all_hunks = vec![
11468        (
11469            "bbbb\n".to_string(),
11470            DiffHunkStatus::Removed,
11471            DisplayRow(4)..DisplayRow(4),
11472        ),
11473        (
11474            "nnnn\n".to_string(),
11475            DiffHunkStatus::Modified,
11476            DisplayRow(21)..DisplayRow(22),
11477        ),
11478        (
11479            "".to_string(),
11480            DiffHunkStatus::Added,
11481            DisplayRow(41)..DisplayRow(42),
11482        ),
11483    ];
11484    let expected_all_hunks_shifted = vec![
11485        (
11486            "bbbb\n".to_string(),
11487            DiffHunkStatus::Removed,
11488            DisplayRow(5)..DisplayRow(5),
11489        ),
11490        (
11491            "nnnn\n".to_string(),
11492            DiffHunkStatus::Modified,
11493            DisplayRow(23)..DisplayRow(24),
11494        ),
11495        (
11496            "".to_string(),
11497            DiffHunkStatus::Added,
11498            DisplayRow(43)..DisplayRow(44),
11499        ),
11500    ];
11501
11502    multi_buffer_editor.update(cx, |editor, cx| {
11503        let snapshot = editor.snapshot(cx);
11504        let all_hunks = editor_hunks(editor, &snapshot, cx);
11505        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11506        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11507        assert_eq!(all_hunks, expected_all_hunks);
11508        assert_eq!(all_expanded_hunks, Vec::new());
11509    });
11510
11511    multi_buffer_editor.update(cx, |editor, cx| {
11512        editor.select_all(&SelectAll, cx);
11513        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11514    });
11515    cx.executor().run_until_parked();
11516    multi_buffer_editor.update(cx, |editor, cx| {
11517        let snapshot = editor.snapshot(cx);
11518        let all_hunks = editor_hunks(editor, &snapshot, cx);
11519        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11520        assert_eq!(
11521            expanded_hunks_background_highlights(editor, cx),
11522            vec![
11523                DisplayRow(23)..=DisplayRow(23),
11524                DisplayRow(43)..=DisplayRow(43)
11525            ],
11526        );
11527        assert_eq!(all_hunks, expected_all_hunks_shifted);
11528        assert_eq!(all_hunks, all_expanded_hunks);
11529    });
11530
11531    multi_buffer_editor.update(cx, |editor, cx| {
11532        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11533    });
11534    cx.executor().run_until_parked();
11535    multi_buffer_editor.update(cx, |editor, cx| {
11536        let snapshot = editor.snapshot(cx);
11537        let all_hunks = editor_hunks(editor, &snapshot, cx);
11538        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11539        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11540        assert_eq!(all_hunks, expected_all_hunks);
11541        assert_eq!(all_expanded_hunks, Vec::new());
11542    });
11543
11544    multi_buffer_editor.update(cx, |editor, cx| {
11545        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11546    });
11547    cx.executor().run_until_parked();
11548    multi_buffer_editor.update(cx, |editor, cx| {
11549        let snapshot = editor.snapshot(cx);
11550        let all_hunks = editor_hunks(editor, &snapshot, cx);
11551        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11552        assert_eq!(
11553            expanded_hunks_background_highlights(editor, cx),
11554            vec![
11555                DisplayRow(23)..=DisplayRow(23),
11556                DisplayRow(43)..=DisplayRow(43)
11557            ],
11558        );
11559        assert_eq!(all_hunks, expected_all_hunks_shifted);
11560        assert_eq!(all_hunks, all_expanded_hunks);
11561    });
11562
11563    multi_buffer_editor.update(cx, |editor, cx| {
11564        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11565    });
11566    cx.executor().run_until_parked();
11567    multi_buffer_editor.update(cx, |editor, cx| {
11568        let snapshot = editor.snapshot(cx);
11569        let all_hunks = editor_hunks(editor, &snapshot, cx);
11570        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11571        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11572        assert_eq!(all_hunks, expected_all_hunks);
11573        assert_eq!(all_expanded_hunks, Vec::new());
11574    });
11575}
11576
11577#[gpui::test]
11578async fn test_edits_around_toggled_additions(
11579    executor: BackgroundExecutor,
11580    cx: &mut gpui::TestAppContext,
11581) {
11582    init_test(cx, |_| {});
11583
11584    let mut cx = EditorTestContext::new(cx).await;
11585
11586    let diff_base = r#"
11587        use some::mod1;
11588        use some::mod2;
11589
11590        const A: u32 = 42;
11591
11592        fn main() {
11593            println!("hello");
11594
11595            println!("world");
11596        }
11597        "#
11598    .unindent();
11599    executor.run_until_parked();
11600    cx.set_state(
11601        &r#"
11602        use some::mod1;
11603        use some::mod2;
11604
11605        const A: u32 = 42;
11606        const B: u32 = 42;
11607        const C: u32 = 42;
11608        ˇ
11609
11610        fn main() {
11611            println!("hello");
11612
11613            println!("world");
11614        }
11615        "#
11616        .unindent(),
11617    );
11618
11619    cx.set_diff_base(Some(&diff_base));
11620    executor.run_until_parked();
11621    cx.update_editor(|editor, cx| {
11622        let snapshot = editor.snapshot(cx);
11623        let all_hunks = editor_hunks(editor, &snapshot, cx);
11624        assert_eq!(
11625            all_hunks,
11626            vec![(
11627                "".to_string(),
11628                DiffHunkStatus::Added,
11629                DisplayRow(4)..DisplayRow(7)
11630            )]
11631        );
11632    });
11633    cx.update_editor(|editor, cx| {
11634        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11635    });
11636    executor.run_until_parked();
11637    cx.assert_editor_state(
11638        &r#"
11639        use some::mod1;
11640        use some::mod2;
11641
11642        const A: u32 = 42;
11643        const B: u32 = 42;
11644        const C: u32 = 42;
11645        ˇ
11646
11647        fn main() {
11648            println!("hello");
11649
11650            println!("world");
11651        }
11652        "#
11653        .unindent(),
11654    );
11655    cx.update_editor(|editor, cx| {
11656        let snapshot = editor.snapshot(cx);
11657        let all_hunks = editor_hunks(editor, &snapshot, cx);
11658        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11659        assert_eq!(
11660            all_hunks,
11661            vec![(
11662                "".to_string(),
11663                DiffHunkStatus::Added,
11664                DisplayRow(4)..DisplayRow(7)
11665            )]
11666        );
11667        assert_eq!(
11668            expanded_hunks_background_highlights(editor, cx),
11669            vec![DisplayRow(4)..=DisplayRow(6)]
11670        );
11671        assert_eq!(all_hunks, all_expanded_hunks);
11672    });
11673
11674    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
11675    executor.run_until_parked();
11676    cx.assert_editor_state(
11677        &r#"
11678        use some::mod1;
11679        use some::mod2;
11680
11681        const A: u32 = 42;
11682        const B: u32 = 42;
11683        const C: u32 = 42;
11684        const D: u32 = 42;
11685        ˇ
11686
11687        fn main() {
11688            println!("hello");
11689
11690            println!("world");
11691        }
11692        "#
11693        .unindent(),
11694    );
11695    cx.update_editor(|editor, cx| {
11696        let snapshot = editor.snapshot(cx);
11697        let all_hunks = editor_hunks(editor, &snapshot, cx);
11698        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11699        assert_eq!(
11700            all_hunks,
11701            vec![(
11702                "".to_string(),
11703                DiffHunkStatus::Added,
11704                DisplayRow(4)..DisplayRow(8)
11705            )]
11706        );
11707        assert_eq!(
11708            expanded_hunks_background_highlights(editor, cx),
11709            vec![DisplayRow(4)..=DisplayRow(6)],
11710            "Edited hunk should have one more line added"
11711        );
11712        assert_eq!(
11713            all_hunks, all_expanded_hunks,
11714            "Expanded hunk should also grow with the addition"
11715        );
11716    });
11717
11718    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
11719    executor.run_until_parked();
11720    cx.assert_editor_state(
11721        &r#"
11722        use some::mod1;
11723        use some::mod2;
11724
11725        const A: u32 = 42;
11726        const B: u32 = 42;
11727        const C: u32 = 42;
11728        const D: u32 = 42;
11729        const E: u32 = 42;
11730        ˇ
11731
11732        fn main() {
11733            println!("hello");
11734
11735            println!("world");
11736        }
11737        "#
11738        .unindent(),
11739    );
11740    cx.update_editor(|editor, cx| {
11741        let snapshot = editor.snapshot(cx);
11742        let all_hunks = editor_hunks(editor, &snapshot, cx);
11743        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11744        assert_eq!(
11745            all_hunks,
11746            vec![(
11747                "".to_string(),
11748                DiffHunkStatus::Added,
11749                DisplayRow(4)..DisplayRow(9)
11750            )]
11751        );
11752        assert_eq!(
11753            expanded_hunks_background_highlights(editor, cx),
11754            vec![DisplayRow(4)..=DisplayRow(6)],
11755            "Edited hunk should have one more line added"
11756        );
11757        assert_eq!(all_hunks, all_expanded_hunks);
11758    });
11759
11760    cx.update_editor(|editor, cx| {
11761        editor.move_up(&MoveUp, cx);
11762        editor.delete_line(&DeleteLine, cx);
11763    });
11764    executor.run_until_parked();
11765    cx.assert_editor_state(
11766        &r#"
11767        use some::mod1;
11768        use some::mod2;
11769
11770        const A: u32 = 42;
11771        const B: u32 = 42;
11772        const C: u32 = 42;
11773        const D: u32 = 42;
11774        ˇ
11775
11776        fn main() {
11777            println!("hello");
11778
11779            println!("world");
11780        }
11781        "#
11782        .unindent(),
11783    );
11784    cx.update_editor(|editor, cx| {
11785        let snapshot = editor.snapshot(cx);
11786        let all_hunks = editor_hunks(editor, &snapshot, cx);
11787        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11788        assert_eq!(
11789            all_hunks,
11790            vec![(
11791                "".to_string(),
11792                DiffHunkStatus::Added,
11793                DisplayRow(4)..DisplayRow(8)
11794            )]
11795        );
11796        assert_eq!(
11797            expanded_hunks_background_highlights(editor, cx),
11798            vec![DisplayRow(4)..=DisplayRow(6)],
11799            "Deleting a line should shrint the hunk"
11800        );
11801        assert_eq!(
11802            all_hunks, all_expanded_hunks,
11803            "Expanded hunk should also shrink with the addition"
11804        );
11805    });
11806
11807    cx.update_editor(|editor, cx| {
11808        editor.move_up(&MoveUp, cx);
11809        editor.delete_line(&DeleteLine, cx);
11810        editor.move_up(&MoveUp, cx);
11811        editor.delete_line(&DeleteLine, cx);
11812        editor.move_up(&MoveUp, cx);
11813        editor.delete_line(&DeleteLine, cx);
11814    });
11815    executor.run_until_parked();
11816    cx.assert_editor_state(
11817        &r#"
11818        use some::mod1;
11819        use some::mod2;
11820
11821        const A: u32 = 42;
11822        ˇ
11823
11824        fn main() {
11825            println!("hello");
11826
11827            println!("world");
11828        }
11829        "#
11830        .unindent(),
11831    );
11832    cx.update_editor(|editor, cx| {
11833        let snapshot = editor.snapshot(cx);
11834        let all_hunks = editor_hunks(editor, &snapshot, cx);
11835        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11836        assert_eq!(
11837            all_hunks,
11838            vec![(
11839                "".to_string(),
11840                DiffHunkStatus::Added,
11841                DisplayRow(5)..DisplayRow(6)
11842            )]
11843        );
11844        assert_eq!(
11845            expanded_hunks_background_highlights(editor, cx),
11846            vec![DisplayRow(5)..=DisplayRow(5)]
11847        );
11848        assert_eq!(all_hunks, all_expanded_hunks);
11849    });
11850
11851    cx.update_editor(|editor, cx| {
11852        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
11853        editor.delete_line(&DeleteLine, cx);
11854    });
11855    executor.run_until_parked();
11856    cx.assert_editor_state(
11857        &r#"
11858        ˇ
11859
11860        fn main() {
11861            println!("hello");
11862
11863            println!("world");
11864        }
11865        "#
11866        .unindent(),
11867    );
11868    cx.update_editor(|editor, cx| {
11869        let snapshot = editor.snapshot(cx);
11870        let all_hunks = editor_hunks(editor, &snapshot, cx);
11871        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11872        assert_eq!(
11873            all_hunks,
11874            vec![
11875                (
11876                    "use some::mod1;\nuse some::mod2;\n".to_string(),
11877                    DiffHunkStatus::Removed,
11878                    DisplayRow(0)..DisplayRow(0)
11879                ),
11880                (
11881                    "const A: u32 = 42;\n".to_string(),
11882                    DiffHunkStatus::Removed,
11883                    DisplayRow(2)..DisplayRow(2)
11884                )
11885            ]
11886        );
11887        assert_eq!(
11888            expanded_hunks_background_highlights(editor, cx),
11889            Vec::new(),
11890            "Should close all stale expanded addition hunks"
11891        );
11892        assert_eq!(
11893            all_expanded_hunks,
11894            vec![(
11895                "const A: u32 = 42;\n".to_string(),
11896                DiffHunkStatus::Removed,
11897                DisplayRow(2)..DisplayRow(2)
11898            )],
11899            "Should open hunks that were adjacent to the stale addition one"
11900        );
11901    });
11902}
11903
11904#[gpui::test]
11905async fn test_edits_around_toggled_deletions(
11906    executor: BackgroundExecutor,
11907    cx: &mut gpui::TestAppContext,
11908) {
11909    init_test(cx, |_| {});
11910
11911    let mut cx = EditorTestContext::new(cx).await;
11912
11913    let diff_base = r#"
11914        use some::mod1;
11915        use some::mod2;
11916
11917        const A: u32 = 42;
11918        const B: u32 = 42;
11919        const C: u32 = 42;
11920
11921
11922        fn main() {
11923            println!("hello");
11924
11925            println!("world");
11926        }
11927        "#
11928    .unindent();
11929    executor.run_until_parked();
11930    cx.set_state(
11931        &r#"
11932        use some::mod1;
11933        use some::mod2;
11934
11935        ˇconst B: u32 = 42;
11936        const C: u32 = 42;
11937
11938
11939        fn main() {
11940            println!("hello");
11941
11942            println!("world");
11943        }
11944        "#
11945        .unindent(),
11946    );
11947
11948    cx.set_diff_base(Some(&diff_base));
11949    executor.run_until_parked();
11950    cx.update_editor(|editor, cx| {
11951        let snapshot = editor.snapshot(cx);
11952        let all_hunks = editor_hunks(editor, &snapshot, cx);
11953        assert_eq!(
11954            all_hunks,
11955            vec![(
11956                "const A: u32 = 42;\n".to_string(),
11957                DiffHunkStatus::Removed,
11958                DisplayRow(3)..DisplayRow(3)
11959            )]
11960        );
11961    });
11962    cx.update_editor(|editor, cx| {
11963        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11964    });
11965    executor.run_until_parked();
11966    cx.assert_editor_state(
11967        &r#"
11968        use some::mod1;
11969        use some::mod2;
11970
11971        ˇconst B: u32 = 42;
11972        const C: u32 = 42;
11973
11974
11975        fn main() {
11976            println!("hello");
11977
11978            println!("world");
11979        }
11980        "#
11981        .unindent(),
11982    );
11983    cx.update_editor(|editor, cx| {
11984        let snapshot = editor.snapshot(cx);
11985        let all_hunks = editor_hunks(editor, &snapshot, cx);
11986        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11987        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11988        assert_eq!(
11989            all_hunks,
11990            vec![(
11991                "const A: u32 = 42;\n".to_string(),
11992                DiffHunkStatus::Removed,
11993                DisplayRow(4)..DisplayRow(4)
11994            )]
11995        );
11996        assert_eq!(all_hunks, all_expanded_hunks);
11997    });
11998
11999    cx.update_editor(|editor, cx| {
12000        editor.delete_line(&DeleteLine, cx);
12001    });
12002    executor.run_until_parked();
12003    cx.assert_editor_state(
12004        &r#"
12005        use some::mod1;
12006        use some::mod2;
12007
12008        ˇconst C: u32 = 42;
12009
12010
12011        fn main() {
12012            println!("hello");
12013
12014            println!("world");
12015        }
12016        "#
12017        .unindent(),
12018    );
12019    cx.update_editor(|editor, cx| {
12020        let snapshot = editor.snapshot(cx);
12021        let all_hunks = editor_hunks(editor, &snapshot, cx);
12022        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12023        assert_eq!(
12024            expanded_hunks_background_highlights(editor, cx),
12025            Vec::new(),
12026            "Deleted hunks do not highlight current editor's background"
12027        );
12028        assert_eq!(
12029            all_hunks,
12030            vec![(
12031                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
12032                DiffHunkStatus::Removed,
12033                DisplayRow(5)..DisplayRow(5)
12034            )]
12035        );
12036        assert_eq!(all_hunks, all_expanded_hunks);
12037    });
12038
12039    cx.update_editor(|editor, cx| {
12040        editor.delete_line(&DeleteLine, cx);
12041    });
12042    executor.run_until_parked();
12043    cx.assert_editor_state(
12044        &r#"
12045        use some::mod1;
12046        use some::mod2;
12047
12048        ˇ
12049
12050        fn main() {
12051            println!("hello");
12052
12053            println!("world");
12054        }
12055        "#
12056        .unindent(),
12057    );
12058    cx.update_editor(|editor, cx| {
12059        let snapshot = editor.snapshot(cx);
12060        let all_hunks = editor_hunks(editor, &snapshot, cx);
12061        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12062        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12063        assert_eq!(
12064            all_hunks,
12065            vec![(
12066                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12067                DiffHunkStatus::Removed,
12068                DisplayRow(6)..DisplayRow(6)
12069            )]
12070        );
12071        assert_eq!(all_hunks, all_expanded_hunks);
12072    });
12073
12074    cx.update_editor(|editor, cx| {
12075        editor.handle_input("replacement", cx);
12076    });
12077    executor.run_until_parked();
12078    cx.assert_editor_state(
12079        &r#"
12080        use some::mod1;
12081        use some::mod2;
12082
12083        replacementˇ
12084
12085        fn main() {
12086            println!("hello");
12087
12088            println!("world");
12089        }
12090        "#
12091        .unindent(),
12092    );
12093    cx.update_editor(|editor, cx| {
12094        let snapshot = editor.snapshot(cx);
12095        let all_hunks = editor_hunks(editor, &snapshot, cx);
12096        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12097        assert_eq!(
12098            all_hunks,
12099            vec![(
12100                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
12101                DiffHunkStatus::Modified,
12102                DisplayRow(7)..DisplayRow(8)
12103            )]
12104        );
12105        assert_eq!(
12106            expanded_hunks_background_highlights(editor, cx),
12107            vec![DisplayRow(7)..=DisplayRow(7)],
12108            "Modified expanded hunks should display additions and highlight their background"
12109        );
12110        assert_eq!(all_hunks, all_expanded_hunks);
12111    });
12112}
12113
12114#[gpui::test]
12115async fn test_edits_around_toggled_modifications(
12116    executor: BackgroundExecutor,
12117    cx: &mut gpui::TestAppContext,
12118) {
12119    init_test(cx, |_| {});
12120
12121    let mut cx = EditorTestContext::new(cx).await;
12122
12123    let diff_base = r#"
12124        use some::mod1;
12125        use some::mod2;
12126
12127        const A: u32 = 42;
12128        const B: u32 = 42;
12129        const C: u32 = 42;
12130        const D: u32 = 42;
12131
12132
12133        fn main() {
12134            println!("hello");
12135
12136            println!("world");
12137        }"#
12138    .unindent();
12139    executor.run_until_parked();
12140    cx.set_state(
12141        &r#"
12142        use some::mod1;
12143        use some::mod2;
12144
12145        const A: u32 = 42;
12146        const B: u32 = 42;
12147        const C: u32 = 43ˇ
12148        const D: u32 = 42;
12149
12150
12151        fn main() {
12152            println!("hello");
12153
12154            println!("world");
12155        }"#
12156        .unindent(),
12157    );
12158
12159    cx.set_diff_base(Some(&diff_base));
12160    executor.run_until_parked();
12161    cx.update_editor(|editor, cx| {
12162        let snapshot = editor.snapshot(cx);
12163        let all_hunks = editor_hunks(editor, &snapshot, cx);
12164        assert_eq!(
12165            all_hunks,
12166            vec![(
12167                "const C: u32 = 42;\n".to_string(),
12168                DiffHunkStatus::Modified,
12169                DisplayRow(5)..DisplayRow(6)
12170            )]
12171        );
12172    });
12173    cx.update_editor(|editor, cx| {
12174        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12175    });
12176    executor.run_until_parked();
12177    cx.assert_editor_state(
12178        &r#"
12179        use some::mod1;
12180        use some::mod2;
12181
12182        const A: u32 = 42;
12183        const B: u32 = 42;
12184        const C: u32 = 43ˇ
12185        const D: u32 = 42;
12186
12187
12188        fn main() {
12189            println!("hello");
12190
12191            println!("world");
12192        }"#
12193        .unindent(),
12194    );
12195    cx.update_editor(|editor, cx| {
12196        let snapshot = editor.snapshot(cx);
12197        let all_hunks = editor_hunks(editor, &snapshot, cx);
12198        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12199        assert_eq!(
12200            expanded_hunks_background_highlights(editor, cx),
12201            vec![DisplayRow(6)..=DisplayRow(6)],
12202        );
12203        assert_eq!(
12204            all_hunks,
12205            vec![(
12206                "const C: u32 = 42;\n".to_string(),
12207                DiffHunkStatus::Modified,
12208                DisplayRow(6)..DisplayRow(7)
12209            )]
12210        );
12211        assert_eq!(all_hunks, all_expanded_hunks);
12212    });
12213
12214    cx.update_editor(|editor, cx| {
12215        editor.handle_input("\nnew_line\n", cx);
12216    });
12217    executor.run_until_parked();
12218    cx.assert_editor_state(
12219        &r#"
12220            use some::mod1;
12221            use some::mod2;
12222
12223            const A: u32 = 42;
12224            const B: u32 = 42;
12225            const C: u32 = 43
12226            new_line
12227            ˇ
12228            const D: u32 = 42;
12229
12230
12231            fn main() {
12232                println!("hello");
12233
12234                println!("world");
12235            }"#
12236        .unindent(),
12237    );
12238    cx.update_editor(|editor, cx| {
12239        let snapshot = editor.snapshot(cx);
12240        let all_hunks = editor_hunks(editor, &snapshot, cx);
12241        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12242        assert_eq!(
12243            expanded_hunks_background_highlights(editor, cx),
12244            vec![DisplayRow(6)..=DisplayRow(6)],
12245            "Modified hunk should grow highlighted lines on more text additions"
12246        );
12247        assert_eq!(
12248            all_hunks,
12249            vec![(
12250                "const C: u32 = 42;\n".to_string(),
12251                DiffHunkStatus::Modified,
12252                DisplayRow(6)..DisplayRow(9)
12253            )]
12254        );
12255        assert_eq!(all_hunks, all_expanded_hunks);
12256    });
12257
12258    cx.update_editor(|editor, cx| {
12259        editor.move_up(&MoveUp, cx);
12260        editor.move_up(&MoveUp, cx);
12261        editor.move_up(&MoveUp, cx);
12262        editor.delete_line(&DeleteLine, cx);
12263    });
12264    executor.run_until_parked();
12265    cx.assert_editor_state(
12266        &r#"
12267            use some::mod1;
12268            use some::mod2;
12269
12270            const A: u32 = 42;
12271            ˇconst C: u32 = 43
12272            new_line
12273
12274            const D: u32 = 42;
12275
12276
12277            fn main() {
12278                println!("hello");
12279
12280                println!("world");
12281            }"#
12282        .unindent(),
12283    );
12284    cx.update_editor(|editor, cx| {
12285        let snapshot = editor.snapshot(cx);
12286        let all_hunks = editor_hunks(editor, &snapshot, cx);
12287        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12288        assert_eq!(
12289            expanded_hunks_background_highlights(editor, cx),
12290            vec![DisplayRow(6)..=DisplayRow(8)],
12291        );
12292        assert_eq!(
12293            all_hunks,
12294            vec![(
12295                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12296                DiffHunkStatus::Modified,
12297                DisplayRow(6)..DisplayRow(9)
12298            )],
12299            "Modified hunk should grow deleted lines on text deletions above"
12300        );
12301        assert_eq!(all_hunks, all_expanded_hunks);
12302    });
12303
12304    cx.update_editor(|editor, cx| {
12305        editor.move_up(&MoveUp, cx);
12306        editor.handle_input("v", cx);
12307    });
12308    executor.run_until_parked();
12309    cx.assert_editor_state(
12310        &r#"
12311            use some::mod1;
12312            use some::mod2;
12313
12314            vˇconst A: u32 = 42;
12315            const C: u32 = 43
12316            new_line
12317
12318            const D: u32 = 42;
12319
12320
12321            fn main() {
12322                println!("hello");
12323
12324                println!("world");
12325            }"#
12326        .unindent(),
12327    );
12328    cx.update_editor(|editor, cx| {
12329        let snapshot = editor.snapshot(cx);
12330        let all_hunks = editor_hunks(editor, &snapshot, cx);
12331        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12332        assert_eq!(
12333            expanded_hunks_background_highlights(editor, cx),
12334            vec![DisplayRow(6)..=DisplayRow(9)],
12335            "Modified hunk should grow deleted lines on text modifications above"
12336        );
12337        assert_eq!(
12338            all_hunks,
12339            vec![(
12340                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12341                DiffHunkStatus::Modified,
12342                DisplayRow(6)..DisplayRow(10)
12343            )]
12344        );
12345        assert_eq!(all_hunks, all_expanded_hunks);
12346    });
12347
12348    cx.update_editor(|editor, cx| {
12349        editor.move_down(&MoveDown, cx);
12350        editor.move_down(&MoveDown, cx);
12351        editor.delete_line(&DeleteLine, cx)
12352    });
12353    executor.run_until_parked();
12354    cx.assert_editor_state(
12355        &r#"
12356            use some::mod1;
12357            use some::mod2;
12358
12359            vconst A: u32 = 42;
12360            const C: u32 = 43
12361            ˇ
12362            const D: u32 = 42;
12363
12364
12365            fn main() {
12366                println!("hello");
12367
12368                println!("world");
12369            }"#
12370        .unindent(),
12371    );
12372    cx.update_editor(|editor, cx| {
12373        let snapshot = editor.snapshot(cx);
12374        let all_hunks = editor_hunks(editor, &snapshot, cx);
12375        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12376        assert_eq!(
12377            expanded_hunks_background_highlights(editor, cx),
12378            vec![DisplayRow(6)..=DisplayRow(8)],
12379            "Modified hunk should grow shrink lines on modification lines removal"
12380        );
12381        assert_eq!(
12382            all_hunks,
12383            vec![(
12384                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12385                DiffHunkStatus::Modified,
12386                DisplayRow(6)..DisplayRow(9)
12387            )]
12388        );
12389        assert_eq!(all_hunks, all_expanded_hunks);
12390    });
12391
12392    cx.update_editor(|editor, cx| {
12393        editor.move_up(&MoveUp, cx);
12394        editor.move_up(&MoveUp, cx);
12395        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
12396        editor.delete_line(&DeleteLine, cx)
12397    });
12398    executor.run_until_parked();
12399    cx.assert_editor_state(
12400        &r#"
12401            use some::mod1;
12402            use some::mod2;
12403
12404            ˇ
12405
12406            fn main() {
12407                println!("hello");
12408
12409                println!("world");
12410            }"#
12411        .unindent(),
12412    );
12413    cx.update_editor(|editor, cx| {
12414        let snapshot = editor.snapshot(cx);
12415        let all_hunks = editor_hunks(editor, &snapshot, cx);
12416        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12417        assert_eq!(
12418            expanded_hunks_background_highlights(editor, cx),
12419            Vec::new(),
12420            "Modified hunk should turn into a removed one on all modified lines removal"
12421        );
12422        assert_eq!(
12423            all_hunks,
12424            vec![(
12425                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
12426                    .to_string(),
12427                DiffHunkStatus::Removed,
12428                DisplayRow(7)..DisplayRow(7)
12429            )]
12430        );
12431        assert_eq!(all_hunks, all_expanded_hunks);
12432    });
12433}
12434
12435#[gpui::test]
12436async fn test_multiple_expanded_hunks_merge(
12437    executor: BackgroundExecutor,
12438    cx: &mut gpui::TestAppContext,
12439) {
12440    init_test(cx, |_| {});
12441
12442    let mut cx = EditorTestContext::new(cx).await;
12443
12444    let diff_base = r#"
12445        use some::mod1;
12446        use some::mod2;
12447
12448        const A: u32 = 42;
12449        const B: u32 = 42;
12450        const C: u32 = 42;
12451        const D: u32 = 42;
12452
12453
12454        fn main() {
12455            println!("hello");
12456
12457            println!("world");
12458        }"#
12459    .unindent();
12460    executor.run_until_parked();
12461    cx.set_state(
12462        &r#"
12463        use some::mod1;
12464        use some::mod2;
12465
12466        const A: u32 = 42;
12467        const B: u32 = 42;
12468        const C: u32 = 43ˇ
12469        const D: u32 = 42;
12470
12471
12472        fn main() {
12473            println!("hello");
12474
12475            println!("world");
12476        }"#
12477        .unindent(),
12478    );
12479
12480    cx.set_diff_base(Some(&diff_base));
12481    executor.run_until_parked();
12482    cx.update_editor(|editor, cx| {
12483        let snapshot = editor.snapshot(cx);
12484        let all_hunks = editor_hunks(editor, &snapshot, cx);
12485        assert_eq!(
12486            all_hunks,
12487            vec![(
12488                "const C: u32 = 42;\n".to_string(),
12489                DiffHunkStatus::Modified,
12490                DisplayRow(5)..DisplayRow(6)
12491            )]
12492        );
12493    });
12494    cx.update_editor(|editor, cx| {
12495        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12496    });
12497    executor.run_until_parked();
12498    cx.assert_editor_state(
12499        &r#"
12500        use some::mod1;
12501        use some::mod2;
12502
12503        const A: u32 = 42;
12504        const B: u32 = 42;
12505        const C: u32 = 43ˇ
12506        const D: u32 = 42;
12507
12508
12509        fn main() {
12510            println!("hello");
12511
12512            println!("world");
12513        }"#
12514        .unindent(),
12515    );
12516    cx.update_editor(|editor, cx| {
12517        let snapshot = editor.snapshot(cx);
12518        let all_hunks = editor_hunks(editor, &snapshot, cx);
12519        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
12520        assert_eq!(
12521            expanded_hunks_background_highlights(editor, cx),
12522            vec![DisplayRow(6)..=DisplayRow(6)],
12523        );
12524        assert_eq!(
12525            all_hunks,
12526            vec![(
12527                "const C: u32 = 42;\n".to_string(),
12528                DiffHunkStatus::Modified,
12529                DisplayRow(6)..DisplayRow(7)
12530            )]
12531        );
12532        assert_eq!(all_hunks, all_expanded_hunks);
12533    });
12534
12535    cx.update_editor(|editor, cx| {
12536        editor.handle_input("\nnew_line\n", cx);
12537    });
12538    executor.run_until_parked();
12539    cx.assert_editor_state(
12540        &r#"
12541            use some::mod1;
12542            use some::mod2;
12543
12544            const A: u32 = 42;
12545            const B: u32 = 42;
12546            const C: u32 = 43
12547            new_line
12548            ˇ
12549            const D: u32 = 42;
12550
12551
12552            fn main() {
12553                println!("hello");
12554
12555                println!("world");
12556            }"#
12557        .unindent(),
12558    );
12559}
12560
12561async fn setup_indent_guides_editor(
12562    text: &str,
12563    cx: &mut gpui::TestAppContext,
12564) -> (BufferId, EditorTestContext) {
12565    init_test(cx, |_| {});
12566
12567    let mut cx = EditorTestContext::new(cx).await;
12568
12569    let buffer_id = cx.update_editor(|editor, cx| {
12570        editor.set_text(text, cx);
12571        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12572        let buffer_id = buffer_ids[0];
12573        buffer_id
12574    });
12575
12576    (buffer_id, cx)
12577}
12578
12579fn assert_indent_guides(
12580    range: Range<u32>,
12581    expected: Vec<IndentGuide>,
12582    active_indices: Option<Vec<usize>>,
12583    cx: &mut EditorTestContext,
12584) {
12585    let indent_guides = cx.update_editor(|editor, cx| {
12586        let snapshot = editor.snapshot(cx).display_snapshot;
12587        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12588            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12589            true,
12590            &snapshot,
12591            cx,
12592        );
12593
12594        indent_guides.sort_by(|a, b| {
12595            a.depth.cmp(&b.depth).then(
12596                a.start_row
12597                    .cmp(&b.start_row)
12598                    .then(a.end_row.cmp(&b.end_row)),
12599            )
12600        });
12601        indent_guides
12602    });
12603
12604    if let Some(expected) = active_indices {
12605        let active_indices = cx.update_editor(|editor, cx| {
12606            let snapshot = editor.snapshot(cx).display_snapshot;
12607            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12608        });
12609
12610        assert_eq!(
12611            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12612            expected,
12613            "Active indent guide indices do not match"
12614        );
12615    }
12616
12617    let expected: Vec<_> = expected
12618        .into_iter()
12619        .map(|guide| MultiBufferIndentGuide {
12620            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12621            buffer: guide,
12622        })
12623        .collect();
12624
12625    assert_eq!(indent_guides, expected, "Indent guides do not match");
12626}
12627
12628fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12629    IndentGuide {
12630        buffer_id,
12631        start_row,
12632        end_row,
12633        depth,
12634        tab_size: 4,
12635        settings: IndentGuideSettings {
12636            enabled: true,
12637            line_width: 1,
12638            active_line_width: 1,
12639            ..Default::default()
12640        },
12641    }
12642}
12643
12644#[gpui::test]
12645async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12646    let (buffer_id, mut cx) = setup_indent_guides_editor(
12647        &"
12648    fn main() {
12649        let a = 1;
12650    }"
12651        .unindent(),
12652        cx,
12653    )
12654    .await;
12655
12656    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12657}
12658
12659#[gpui::test]
12660async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12661    let (buffer_id, mut cx) = setup_indent_guides_editor(
12662        &"
12663    fn main() {
12664        let a = 1;
12665        let b = 2;
12666    }"
12667        .unindent(),
12668        cx,
12669    )
12670    .await;
12671
12672    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12673}
12674
12675#[gpui::test]
12676async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12677    let (buffer_id, mut cx) = setup_indent_guides_editor(
12678        &"
12679    fn main() {
12680        let a = 1;
12681        if a == 3 {
12682            let b = 2;
12683        } else {
12684            let c = 3;
12685        }
12686    }"
12687        .unindent(),
12688        cx,
12689    )
12690    .await;
12691
12692    assert_indent_guides(
12693        0..8,
12694        vec![
12695            indent_guide(buffer_id, 1, 6, 0),
12696            indent_guide(buffer_id, 3, 3, 1),
12697            indent_guide(buffer_id, 5, 5, 1),
12698        ],
12699        None,
12700        &mut cx,
12701    );
12702}
12703
12704#[gpui::test]
12705async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12706    let (buffer_id, mut cx) = setup_indent_guides_editor(
12707        &"
12708    fn main() {
12709        let a = 1;
12710            let b = 2;
12711        let c = 3;
12712    }"
12713        .unindent(),
12714        cx,
12715    )
12716    .await;
12717
12718    assert_indent_guides(
12719        0..5,
12720        vec![
12721            indent_guide(buffer_id, 1, 3, 0),
12722            indent_guide(buffer_id, 2, 2, 1),
12723        ],
12724        None,
12725        &mut cx,
12726    );
12727}
12728
12729#[gpui::test]
12730async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12731    let (buffer_id, mut cx) = setup_indent_guides_editor(
12732        &"
12733        fn main() {
12734            let a = 1;
12735
12736            let c = 3;
12737        }"
12738        .unindent(),
12739        cx,
12740    )
12741    .await;
12742
12743    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12744}
12745
12746#[gpui::test]
12747async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12748    let (buffer_id, mut cx) = setup_indent_guides_editor(
12749        &"
12750        fn main() {
12751            let a = 1;
12752
12753            let c = 3;
12754
12755            if a == 3 {
12756                let b = 2;
12757            } else {
12758                let c = 3;
12759            }
12760        }"
12761        .unindent(),
12762        cx,
12763    )
12764    .await;
12765
12766    assert_indent_guides(
12767        0..11,
12768        vec![
12769            indent_guide(buffer_id, 1, 9, 0),
12770            indent_guide(buffer_id, 6, 6, 1),
12771            indent_guide(buffer_id, 8, 8, 1),
12772        ],
12773        None,
12774        &mut cx,
12775    );
12776}
12777
12778#[gpui::test]
12779async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12780    let (buffer_id, mut cx) = setup_indent_guides_editor(
12781        &"
12782        fn main() {
12783            let a = 1;
12784
12785            let c = 3;
12786
12787            if a == 3 {
12788                let b = 2;
12789            } else {
12790                let c = 3;
12791            }
12792        }"
12793        .unindent(),
12794        cx,
12795    )
12796    .await;
12797
12798    assert_indent_guides(
12799        1..11,
12800        vec![
12801            indent_guide(buffer_id, 1, 9, 0),
12802            indent_guide(buffer_id, 6, 6, 1),
12803            indent_guide(buffer_id, 8, 8, 1),
12804        ],
12805        None,
12806        &mut cx,
12807    );
12808}
12809
12810#[gpui::test]
12811async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12812    let (buffer_id, mut cx) = setup_indent_guides_editor(
12813        &"
12814        fn main() {
12815            let a = 1;
12816
12817            let c = 3;
12818
12819            if a == 3 {
12820                let b = 2;
12821            } else {
12822                let c = 3;
12823            }
12824        }"
12825        .unindent(),
12826        cx,
12827    )
12828    .await;
12829
12830    assert_indent_guides(
12831        1..10,
12832        vec![
12833            indent_guide(buffer_id, 1, 9, 0),
12834            indent_guide(buffer_id, 6, 6, 1),
12835            indent_guide(buffer_id, 8, 8, 1),
12836        ],
12837        None,
12838        &mut cx,
12839    );
12840}
12841
12842#[gpui::test]
12843async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12844    let (buffer_id, mut cx) = setup_indent_guides_editor(
12845        &"
12846        block1
12847            block2
12848                block3
12849                    block4
12850            block2
12851        block1
12852        block1"
12853            .unindent(),
12854        cx,
12855    )
12856    .await;
12857
12858    assert_indent_guides(
12859        1..10,
12860        vec![
12861            indent_guide(buffer_id, 1, 4, 0),
12862            indent_guide(buffer_id, 2, 3, 1),
12863            indent_guide(buffer_id, 3, 3, 2),
12864        ],
12865        None,
12866        &mut cx,
12867    );
12868}
12869
12870#[gpui::test]
12871async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12872    let (buffer_id, mut cx) = setup_indent_guides_editor(
12873        &"
12874        block1
12875            block2
12876                block3
12877
12878        block1
12879        block1"
12880            .unindent(),
12881        cx,
12882    )
12883    .await;
12884
12885    assert_indent_guides(
12886        0..6,
12887        vec![
12888            indent_guide(buffer_id, 1, 2, 0),
12889            indent_guide(buffer_id, 2, 2, 1),
12890        ],
12891        None,
12892        &mut cx,
12893    );
12894}
12895
12896#[gpui::test]
12897async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12898    let (buffer_id, mut cx) = setup_indent_guides_editor(
12899        &"
12900        block1
12901
12902
12903
12904            block2
12905        "
12906        .unindent(),
12907        cx,
12908    )
12909    .await;
12910
12911    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12912}
12913
12914#[gpui::test]
12915async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12916    let (buffer_id, mut cx) = setup_indent_guides_editor(
12917        &"
12918        def a:
12919        \tb = 3
12920        \tif True:
12921        \t\tc = 4
12922        \t\td = 5
12923        \tprint(b)
12924        "
12925        .unindent(),
12926        cx,
12927    )
12928    .await;
12929
12930    assert_indent_guides(
12931        0..6,
12932        vec![
12933            indent_guide(buffer_id, 1, 6, 0),
12934            indent_guide(buffer_id, 3, 4, 1),
12935        ],
12936        None,
12937        &mut cx,
12938    );
12939}
12940
12941#[gpui::test]
12942async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12943    let (buffer_id, mut cx) = setup_indent_guides_editor(
12944        &"
12945    fn main() {
12946        let a = 1;
12947    }"
12948        .unindent(),
12949        cx,
12950    )
12951    .await;
12952
12953    cx.update_editor(|editor, cx| {
12954        editor.change_selections(None, cx, |s| {
12955            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12956        });
12957    });
12958
12959    assert_indent_guides(
12960        0..3,
12961        vec![indent_guide(buffer_id, 1, 1, 0)],
12962        Some(vec![0]),
12963        &mut cx,
12964    );
12965}
12966
12967#[gpui::test]
12968async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12969    let (buffer_id, mut cx) = setup_indent_guides_editor(
12970        &"
12971    fn main() {
12972        if 1 == 2 {
12973            let a = 1;
12974        }
12975    }"
12976        .unindent(),
12977        cx,
12978    )
12979    .await;
12980
12981    cx.update_editor(|editor, cx| {
12982        editor.change_selections(None, cx, |s| {
12983            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12984        });
12985    });
12986
12987    assert_indent_guides(
12988        0..4,
12989        vec![
12990            indent_guide(buffer_id, 1, 3, 0),
12991            indent_guide(buffer_id, 2, 2, 1),
12992        ],
12993        Some(vec![1]),
12994        &mut cx,
12995    );
12996
12997    cx.update_editor(|editor, cx| {
12998        editor.change_selections(None, cx, |s| {
12999            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13000        });
13001    });
13002
13003    assert_indent_guides(
13004        0..4,
13005        vec![
13006            indent_guide(buffer_id, 1, 3, 0),
13007            indent_guide(buffer_id, 2, 2, 1),
13008        ],
13009        Some(vec![1]),
13010        &mut cx,
13011    );
13012
13013    cx.update_editor(|editor, cx| {
13014        editor.change_selections(None, cx, |s| {
13015            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13016        });
13017    });
13018
13019    assert_indent_guides(
13020        0..4,
13021        vec![
13022            indent_guide(buffer_id, 1, 3, 0),
13023            indent_guide(buffer_id, 2, 2, 1),
13024        ],
13025        Some(vec![0]),
13026        &mut cx,
13027    );
13028}
13029
13030#[gpui::test]
13031async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13032    let (buffer_id, mut cx) = setup_indent_guides_editor(
13033        &"
13034    fn main() {
13035        let a = 1;
13036
13037        let b = 2;
13038    }"
13039        .unindent(),
13040        cx,
13041    )
13042    .await;
13043
13044    cx.update_editor(|editor, cx| {
13045        editor.change_selections(None, cx, |s| {
13046            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13047        });
13048    });
13049
13050    assert_indent_guides(
13051        0..5,
13052        vec![indent_guide(buffer_id, 1, 3, 0)],
13053        Some(vec![0]),
13054        &mut cx,
13055    );
13056}
13057
13058#[gpui::test]
13059async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13060    let (buffer_id, mut cx) = setup_indent_guides_editor(
13061        &"
13062    def m:
13063        a = 1
13064        pass"
13065            .unindent(),
13066        cx,
13067    )
13068    .await;
13069
13070    cx.update_editor(|editor, cx| {
13071        editor.change_selections(None, cx, |s| {
13072            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13073        });
13074    });
13075
13076    assert_indent_guides(
13077        0..3,
13078        vec![indent_guide(buffer_id, 1, 2, 0)],
13079        Some(vec![0]),
13080        &mut cx,
13081    );
13082}
13083
13084#[gpui::test]
13085fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13086    init_test(cx, |_| {});
13087
13088    let editor = cx.add_window(|cx| {
13089        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13090        build_editor(buffer, cx)
13091    });
13092
13093    let render_args = Arc::new(Mutex::new(None));
13094    let snapshot = editor
13095        .update(cx, |editor, cx| {
13096            let snapshot = editor.buffer().read(cx).snapshot(cx);
13097            let range =
13098                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13099
13100            struct RenderArgs {
13101                row: MultiBufferRow,
13102                folded: bool,
13103                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13104            }
13105
13106            let crease = Crease::new(
13107                range,
13108                FoldPlaceholder::test(),
13109                {
13110                    let toggle_callback = render_args.clone();
13111                    move |row, folded, callback, _cx| {
13112                        *toggle_callback.lock() = Some(RenderArgs {
13113                            row,
13114                            folded,
13115                            callback,
13116                        });
13117                        div()
13118                    }
13119                },
13120                |_row, _folded, _cx| div(),
13121            );
13122
13123            editor.insert_creases(Some(crease), cx);
13124            let snapshot = editor.snapshot(cx);
13125            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13126            snapshot
13127        })
13128        .unwrap();
13129
13130    let render_args = render_args.lock().take().unwrap();
13131    assert_eq!(render_args.row, MultiBufferRow(1));
13132    assert_eq!(render_args.folded, false);
13133    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13134
13135    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13136        .unwrap();
13137    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13138    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13139
13140    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13141        .unwrap();
13142    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13143    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13144}
13145
13146#[gpui::test]
13147async fn test_input_text(cx: &mut gpui::TestAppContext) {
13148    init_test(cx, |_| {});
13149    let mut cx = EditorTestContext::new(cx).await;
13150
13151    cx.set_state(
13152        &r#"ˇone
13153        two
13154
13155        three
13156        fourˇ
13157        five
13158
13159        siˇx"#
13160            .unindent(),
13161    );
13162
13163    cx.dispatch_action(HandleInput(String::new()));
13164    cx.assert_editor_state(
13165        &r#"ˇone
13166        two
13167
13168        three
13169        fourˇ
13170        five
13171
13172        siˇx"#
13173            .unindent(),
13174    );
13175
13176    cx.dispatch_action(HandleInput("AAAA".to_string()));
13177    cx.assert_editor_state(
13178        &r#"AAAAˇone
13179        two
13180
13181        three
13182        fourAAAAˇ
13183        five
13184
13185        siAAAAˇx"#
13186            .unindent(),
13187    );
13188}
13189
13190#[gpui::test]
13191async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13192    init_test(cx, |_| {});
13193
13194    let mut cx = EditorTestContext::new(cx).await;
13195    cx.set_state(
13196        r#"let foo = 1;
13197let foo = 2;
13198let foo = 3;
13199let fooˇ = 4;
13200let foo = 5;
13201let foo = 6;
13202let foo = 7;
13203let foo = 8;
13204let foo = 9;
13205let foo = 10;
13206let foo = 11;
13207let foo = 12;
13208let foo = 13;
13209let foo = 14;
13210let foo = 15;"#,
13211    );
13212
13213    cx.update_editor(|e, cx| {
13214        assert_eq!(
13215            e.next_scroll_position,
13216            NextScrollCursorCenterTopBottom::Center,
13217            "Default next scroll direction is center",
13218        );
13219
13220        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13221        assert_eq!(
13222            e.next_scroll_position,
13223            NextScrollCursorCenterTopBottom::Top,
13224            "After center, next scroll direction should be top",
13225        );
13226
13227        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13228        assert_eq!(
13229            e.next_scroll_position,
13230            NextScrollCursorCenterTopBottom::Bottom,
13231            "After top, next scroll direction should be bottom",
13232        );
13233
13234        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13235        assert_eq!(
13236            e.next_scroll_position,
13237            NextScrollCursorCenterTopBottom::Center,
13238            "After bottom, scrolling should start over",
13239        );
13240
13241        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13242        assert_eq!(
13243            e.next_scroll_position,
13244            NextScrollCursorCenterTopBottom::Top,
13245            "Scrolling continues if retriggered fast enough"
13246        );
13247    });
13248
13249    cx.executor()
13250        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13251    cx.executor().run_until_parked();
13252    cx.update_editor(|e, _| {
13253        assert_eq!(
13254            e.next_scroll_position,
13255            NextScrollCursorCenterTopBottom::Center,
13256            "If scrolling is not triggered fast enough, it should reset"
13257        );
13258    });
13259}
13260
13261fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13262    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13263    point..point
13264}
13265
13266fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13267    let (text, ranges) = marked_text_ranges(marked_text, true);
13268    assert_eq!(view.text(cx), text);
13269    assert_eq!(
13270        view.selections.ranges(cx),
13271        ranges,
13272        "Assert selections are {}",
13273        marked_text
13274    );
13275}
13276
13277pub fn handle_signature_help_request(
13278    cx: &mut EditorLspTestContext,
13279    mocked_response: lsp::SignatureHelp,
13280) -> impl Future<Output = ()> {
13281    let mut request =
13282        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13283            let mocked_response = mocked_response.clone();
13284            async move { Ok(Some(mocked_response)) }
13285        });
13286
13287    async move {
13288        request.next().await;
13289    }
13290}
13291
13292/// Handle completion request passing a marked string specifying where the completion
13293/// should be triggered from using '|' character, what range should be replaced, and what completions
13294/// should be returned using '<' and '>' to delimit the range
13295pub fn handle_completion_request(
13296    cx: &mut EditorLspTestContext,
13297    marked_string: &str,
13298    completions: Vec<&'static str>,
13299    counter: Arc<AtomicUsize>,
13300) -> impl Future<Output = ()> {
13301    let complete_from_marker: TextRangeMarker = '|'.into();
13302    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13303    let (_, mut marked_ranges) = marked_text_ranges_by(
13304        marked_string,
13305        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13306    );
13307
13308    let complete_from_position =
13309        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13310    let replace_range =
13311        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13312
13313    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13314        let completions = completions.clone();
13315        counter.fetch_add(1, atomic::Ordering::Release);
13316        async move {
13317            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13318            assert_eq!(
13319                params.text_document_position.position,
13320                complete_from_position
13321            );
13322            Ok(Some(lsp::CompletionResponse::Array(
13323                completions
13324                    .iter()
13325                    .map(|completion_text| lsp::CompletionItem {
13326                        label: completion_text.to_string(),
13327                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13328                            range: replace_range,
13329                            new_text: completion_text.to_string(),
13330                        })),
13331                        ..Default::default()
13332                    })
13333                    .collect(),
13334            )))
13335        }
13336    });
13337
13338    async move {
13339        request.next().await;
13340    }
13341}
13342
13343fn handle_resolve_completion_request(
13344    cx: &mut EditorLspTestContext,
13345    edits: Option<Vec<(&'static str, &'static str)>>,
13346) -> impl Future<Output = ()> {
13347    let edits = edits.map(|edits| {
13348        edits
13349            .iter()
13350            .map(|(marked_string, new_text)| {
13351                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13352                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13353                lsp::TextEdit::new(replace_range, new_text.to_string())
13354            })
13355            .collect::<Vec<_>>()
13356    });
13357
13358    let mut request =
13359        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13360            let edits = edits.clone();
13361            async move {
13362                Ok(lsp::CompletionItem {
13363                    additional_text_edits: edits,
13364                    ..Default::default()
13365                })
13366            }
13367        });
13368
13369    async move {
13370        request.next().await;
13371    }
13372}
13373
13374pub(crate) fn update_test_language_settings(
13375    cx: &mut TestAppContext,
13376    f: impl Fn(&mut AllLanguageSettingsContent),
13377) {
13378    _ = cx.update(|cx| {
13379        SettingsStore::update_global(cx, |store, cx| {
13380            store.update_user_settings::<AllLanguageSettings>(cx, f);
13381        });
13382    });
13383}
13384
13385pub(crate) fn update_test_project_settings(
13386    cx: &mut TestAppContext,
13387    f: impl Fn(&mut ProjectSettings),
13388) {
13389    _ = cx.update(|cx| {
13390        SettingsStore::update_global(cx, |store, cx| {
13391            store.update_user_settings::<ProjectSettings>(cx, f);
13392        });
13393    });
13394}
13395
13396pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13397    _ = cx.update(|cx| {
13398        assets::Assets.load_test_fonts(cx);
13399        let store = SettingsStore::test(cx);
13400        cx.set_global(store);
13401        theme::init(theme::LoadThemes::JustBase, cx);
13402        release_channel::init(SemanticVersion::default(), cx);
13403        client::init_settings(cx);
13404        language::init(cx);
13405        Project::init_settings(cx);
13406        workspace::init_settings(cx);
13407        crate::init(cx);
13408    });
13409
13410    update_test_language_settings(cx, f);
13411}
13412
13413pub(crate) fn rust_lang() -> Arc<Language> {
13414    Arc::new(Language::new(
13415        LanguageConfig {
13416            name: "Rust".into(),
13417            matcher: LanguageMatcher {
13418                path_suffixes: vec!["rs".to_string()],
13419                ..Default::default()
13420            },
13421            ..Default::default()
13422        },
13423        Some(tree_sitter_rust::language()),
13424    ))
13425}
13426
13427#[track_caller]
13428fn assert_hunk_revert(
13429    not_reverted_text_with_selections: &str,
13430    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13431    expected_reverted_text_with_selections: &str,
13432    base_text: &str,
13433    cx: &mut EditorLspTestContext,
13434) {
13435    cx.set_state(not_reverted_text_with_selections);
13436    cx.update_editor(|editor, cx| {
13437        editor
13438            .buffer()
13439            .read(cx)
13440            .as_singleton()
13441            .unwrap()
13442            .update(cx, |buffer, cx| {
13443                buffer.set_diff_base(Some(base_text.into()), cx);
13444            });
13445    });
13446    cx.executor().run_until_parked();
13447
13448    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13449        let snapshot = editor.buffer().read(cx).snapshot(cx);
13450        let reverted_hunk_statuses = snapshot
13451            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13452            .map(|hunk| hunk_status(&hunk))
13453            .collect::<Vec<_>>();
13454
13455        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13456        reverted_hunk_statuses
13457    });
13458    cx.executor().run_until_parked();
13459    cx.assert_editor_state(expected_reverted_text_with_selections);
13460    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13461}