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,
   24    LanguageName, Override, 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(
 2106            &DeleteToPreviousWordStart {
 2107                ignore_newlines: false,
 2108            },
 2109            cx,
 2110        );
 2111        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2112    });
 2113
 2114    _ = view.update(cx, |view, cx| {
 2115        view.change_selections(None, cx, |s| {
 2116            s.select_display_ranges([
 2117                // an empty selection - the following word fragment is deleted
 2118                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2119                // characters selected - they are deleted
 2120                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2121            ])
 2122        });
 2123        view.delete_to_next_word_end(
 2124            &DeleteToNextWordEnd {
 2125                ignore_newlines: false,
 2126            },
 2127            cx,
 2128        );
 2129        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2130    });
 2131}
 2132
 2133#[gpui::test]
 2134fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2135    init_test(cx, |_| {});
 2136
 2137    let view = cx.add_window(|cx| {
 2138        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2139        build_editor(buffer.clone(), cx)
 2140    });
 2141    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2142        ignore_newlines: false,
 2143    };
 2144    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2145        ignore_newlines: true,
 2146    };
 2147
 2148    _ = view.update(cx, |view, cx| {
 2149        view.change_selections(None, cx, |s| {
 2150            s.select_display_ranges([
 2151                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2152            ])
 2153        });
 2154        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2155        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2156        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2157        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2158        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2159        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2160        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2161        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2162        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2163        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2164        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2165        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2166    });
 2167}
 2168
 2169#[gpui::test]
 2170fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2171    init_test(cx, |_| {});
 2172
 2173    let view = cx.add_window(|cx| {
 2174        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2175        build_editor(buffer.clone(), cx)
 2176    });
 2177    let del_to_next_word_end = DeleteToNextWordEnd {
 2178        ignore_newlines: false,
 2179    };
 2180    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2181        ignore_newlines: true,
 2182    };
 2183
 2184    _ = view.update(cx, |view, cx| {
 2185        view.change_selections(None, cx, |s| {
 2186            s.select_display_ranges([
 2187                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2188            ])
 2189        });
 2190        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2191        assert_eq!(
 2192            view.buffer.read(cx).read(cx).text(),
 2193            "one\n   two\nthree\n   four"
 2194        );
 2195        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2196        assert_eq!(
 2197            view.buffer.read(cx).read(cx).text(),
 2198            "\n   two\nthree\n   four"
 2199        );
 2200        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2201        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2202        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2203        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2204        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2205        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2206        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2207        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2208    });
 2209}
 2210
 2211#[gpui::test]
 2212fn test_newline(cx: &mut TestAppContext) {
 2213    init_test(cx, |_| {});
 2214
 2215    let view = cx.add_window(|cx| {
 2216        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2217        build_editor(buffer.clone(), cx)
 2218    });
 2219
 2220    _ = view.update(cx, |view, cx| {
 2221        view.change_selections(None, cx, |s| {
 2222            s.select_display_ranges([
 2223                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2224                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2225                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2226            ])
 2227        });
 2228
 2229        view.newline(&Newline, cx);
 2230        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2231    });
 2232}
 2233
 2234#[gpui::test]
 2235fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2236    init_test(cx, |_| {});
 2237
 2238    let editor = cx.add_window(|cx| {
 2239        let buffer = MultiBuffer::build_simple(
 2240            "
 2241                a
 2242                b(
 2243                    X
 2244                )
 2245                c(
 2246                    X
 2247                )
 2248            "
 2249            .unindent()
 2250            .as_str(),
 2251            cx,
 2252        );
 2253        let mut editor = build_editor(buffer.clone(), cx);
 2254        editor.change_selections(None, cx, |s| {
 2255            s.select_ranges([
 2256                Point::new(2, 4)..Point::new(2, 5),
 2257                Point::new(5, 4)..Point::new(5, 5),
 2258            ])
 2259        });
 2260        editor
 2261    });
 2262
 2263    _ = editor.update(cx, |editor, cx| {
 2264        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2265        editor.buffer.update(cx, |buffer, cx| {
 2266            buffer.edit(
 2267                [
 2268                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2269                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2270                ],
 2271                None,
 2272                cx,
 2273            );
 2274            assert_eq!(
 2275                buffer.read(cx).text(),
 2276                "
 2277                    a
 2278                    b()
 2279                    c()
 2280                "
 2281                .unindent()
 2282            );
 2283        });
 2284        assert_eq!(
 2285            editor.selections.ranges(cx),
 2286            &[
 2287                Point::new(1, 2)..Point::new(1, 2),
 2288                Point::new(2, 2)..Point::new(2, 2),
 2289            ],
 2290        );
 2291
 2292        editor.newline(&Newline, cx);
 2293        assert_eq!(
 2294            editor.text(cx),
 2295            "
 2296                a
 2297                b(
 2298                )
 2299                c(
 2300                )
 2301            "
 2302            .unindent()
 2303        );
 2304
 2305        // The selections are moved after the inserted newlines
 2306        assert_eq!(
 2307            editor.selections.ranges(cx),
 2308            &[
 2309                Point::new(2, 0)..Point::new(2, 0),
 2310                Point::new(4, 0)..Point::new(4, 0),
 2311            ],
 2312        );
 2313    });
 2314}
 2315
 2316#[gpui::test]
 2317async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2318    init_test(cx, |settings| {
 2319        settings.defaults.tab_size = NonZeroU32::new(4)
 2320    });
 2321
 2322    let language = Arc::new(
 2323        Language::new(
 2324            LanguageConfig::default(),
 2325            Some(tree_sitter_rust::LANGUAGE.into()),
 2326        )
 2327        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2328        .unwrap(),
 2329    );
 2330
 2331    let mut cx = EditorTestContext::new(cx).await;
 2332    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2333    cx.set_state(indoc! {"
 2334        const a: ˇA = (
 2335 2336                «const_functionˇ»(ˇ),
 2337                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2338 2339        ˇ);ˇ
 2340    "});
 2341
 2342    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2343    cx.assert_editor_state(indoc! {"
 2344        ˇ
 2345        const a: A = (
 2346            ˇ
 2347            (
 2348                ˇ
 2349                ˇ
 2350                const_function(),
 2351                ˇ
 2352                ˇ
 2353                ˇ
 2354                ˇ
 2355                something_else,
 2356                ˇ
 2357            )
 2358            ˇ
 2359            ˇ
 2360        );
 2361    "});
 2362}
 2363
 2364#[gpui::test]
 2365async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2366    init_test(cx, |settings| {
 2367        settings.defaults.tab_size = NonZeroU32::new(4)
 2368    });
 2369
 2370    let language = Arc::new(
 2371        Language::new(
 2372            LanguageConfig::default(),
 2373            Some(tree_sitter_rust::LANGUAGE.into()),
 2374        )
 2375        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2376        .unwrap(),
 2377    );
 2378
 2379    let mut cx = EditorTestContext::new(cx).await;
 2380    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2381    cx.set_state(indoc! {"
 2382        const a: ˇA = (
 2383 2384                «const_functionˇ»(ˇ),
 2385                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2386 2387        ˇ);ˇ
 2388    "});
 2389
 2390    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2391    cx.assert_editor_state(indoc! {"
 2392        const a: A = (
 2393            ˇ
 2394            (
 2395                ˇ
 2396                const_function(),
 2397                ˇ
 2398                ˇ
 2399                something_else,
 2400                ˇ
 2401                ˇ
 2402                ˇ
 2403                ˇ
 2404            )
 2405            ˇ
 2406        );
 2407        ˇ
 2408        ˇ
 2409    "});
 2410}
 2411
 2412#[gpui::test]
 2413async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2414    init_test(cx, |settings| {
 2415        settings.defaults.tab_size = NonZeroU32::new(4)
 2416    });
 2417
 2418    let language = Arc::new(Language::new(
 2419        LanguageConfig {
 2420            line_comments: vec!["//".into()],
 2421            ..LanguageConfig::default()
 2422        },
 2423        None,
 2424    ));
 2425    {
 2426        let mut cx = EditorTestContext::new(cx).await;
 2427        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2428        cx.set_state(indoc! {"
 2429        // Fooˇ
 2430    "});
 2431
 2432        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2433        cx.assert_editor_state(indoc! {"
 2434        // Foo
 2435        //ˇ
 2436    "});
 2437        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2438        cx.set_state(indoc! {"
 2439        ˇ// Foo
 2440    "});
 2441        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2442        cx.assert_editor_state(indoc! {"
 2443
 2444        ˇ// Foo
 2445    "});
 2446    }
 2447    // Ensure that comment continuations can be disabled.
 2448    update_test_language_settings(cx, |settings| {
 2449        settings.defaults.extend_comment_on_newline = Some(false);
 2450    });
 2451    let mut cx = EditorTestContext::new(cx).await;
 2452    cx.set_state(indoc! {"
 2453        // Fooˇ
 2454    "});
 2455    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2456    cx.assert_editor_state(indoc! {"
 2457        // Foo
 2458        ˇ
 2459    "});
 2460}
 2461
 2462#[gpui::test]
 2463fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2464    init_test(cx, |_| {});
 2465
 2466    let editor = cx.add_window(|cx| {
 2467        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2468        let mut editor = build_editor(buffer.clone(), cx);
 2469        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2470        editor
 2471    });
 2472
 2473    _ = editor.update(cx, |editor, cx| {
 2474        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2475        editor.buffer.update(cx, |buffer, cx| {
 2476            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2477            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2478        });
 2479        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2480
 2481        editor.insert("Z", cx);
 2482        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2483
 2484        // The selections are moved after the inserted characters
 2485        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2486    });
 2487}
 2488
 2489#[gpui::test]
 2490async fn test_tab(cx: &mut gpui::TestAppContext) {
 2491    init_test(cx, |settings| {
 2492        settings.defaults.tab_size = NonZeroU32::new(3)
 2493    });
 2494
 2495    let mut cx = EditorTestContext::new(cx).await;
 2496    cx.set_state(indoc! {"
 2497        ˇabˇc
 2498        ˇ🏀ˇ🏀ˇefg
 2499 2500    "});
 2501    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2502    cx.assert_editor_state(indoc! {"
 2503           ˇab ˇc
 2504           ˇ🏀  ˇ🏀  ˇefg
 2505        d  ˇ
 2506    "});
 2507
 2508    cx.set_state(indoc! {"
 2509        a
 2510        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2511    "});
 2512    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2513    cx.assert_editor_state(indoc! {"
 2514        a
 2515           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2516    "});
 2517}
 2518
 2519#[gpui::test]
 2520async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2521    init_test(cx, |_| {});
 2522
 2523    let mut cx = EditorTestContext::new(cx).await;
 2524    let language = Arc::new(
 2525        Language::new(
 2526            LanguageConfig::default(),
 2527            Some(tree_sitter_rust::LANGUAGE.into()),
 2528        )
 2529        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2530        .unwrap(),
 2531    );
 2532    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2533
 2534    // cursors that are already at the suggested indent level insert
 2535    // a soft tab. cursors that are to the left of the suggested indent
 2536    // auto-indent their line.
 2537    cx.set_state(indoc! {"
 2538        ˇ
 2539        const a: B = (
 2540            c(
 2541                d(
 2542        ˇ
 2543                )
 2544        ˇ
 2545        ˇ    )
 2546        );
 2547    "});
 2548    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2549    cx.assert_editor_state(indoc! {"
 2550            ˇ
 2551        const a: B = (
 2552            c(
 2553                d(
 2554                    ˇ
 2555                )
 2556                ˇ
 2557            ˇ)
 2558        );
 2559    "});
 2560
 2561    // handle auto-indent when there are multiple cursors on the same line
 2562    cx.set_state(indoc! {"
 2563        const a: B = (
 2564            c(
 2565        ˇ    ˇ
 2566        ˇ    )
 2567        );
 2568    "});
 2569    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2570    cx.assert_editor_state(indoc! {"
 2571        const a: B = (
 2572            c(
 2573                ˇ
 2574            ˇ)
 2575        );
 2576    "});
 2577}
 2578
 2579#[gpui::test]
 2580async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2581    init_test(cx, |settings| {
 2582        settings.defaults.tab_size = NonZeroU32::new(4)
 2583    });
 2584
 2585    let language = Arc::new(
 2586        Language::new(
 2587            LanguageConfig::default(),
 2588            Some(tree_sitter_rust::LANGUAGE.into()),
 2589        )
 2590        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2591        .unwrap(),
 2592    );
 2593
 2594    let mut cx = EditorTestContext::new(cx).await;
 2595    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2596    cx.set_state(indoc! {"
 2597        fn a() {
 2598            if b {
 2599        \t ˇc
 2600            }
 2601        }
 2602    "});
 2603
 2604    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2605    cx.assert_editor_state(indoc! {"
 2606        fn a() {
 2607            if b {
 2608                ˇc
 2609            }
 2610        }
 2611    "});
 2612}
 2613
 2614#[gpui::test]
 2615async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2616    init_test(cx, |settings| {
 2617        settings.defaults.tab_size = NonZeroU32::new(4);
 2618    });
 2619
 2620    let mut cx = EditorTestContext::new(cx).await;
 2621
 2622    cx.set_state(indoc! {"
 2623          «oneˇ» «twoˇ»
 2624        three
 2625         four
 2626    "});
 2627    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2628    cx.assert_editor_state(indoc! {"
 2629            «oneˇ» «twoˇ»
 2630        three
 2631         four
 2632    "});
 2633
 2634    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2635    cx.assert_editor_state(indoc! {"
 2636        «oneˇ» «twoˇ»
 2637        three
 2638         four
 2639    "});
 2640
 2641    // select across line ending
 2642    cx.set_state(indoc! {"
 2643        one two
 2644        t«hree
 2645        ˇ» four
 2646    "});
 2647    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2648    cx.assert_editor_state(indoc! {"
 2649        one two
 2650            t«hree
 2651        ˇ» four
 2652    "});
 2653
 2654    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2655    cx.assert_editor_state(indoc! {"
 2656        one two
 2657        t«hree
 2658        ˇ» four
 2659    "});
 2660
 2661    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2662    cx.set_state(indoc! {"
 2663        one two
 2664        ˇthree
 2665            four
 2666    "});
 2667    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2668    cx.assert_editor_state(indoc! {"
 2669        one two
 2670            ˇthree
 2671            four
 2672    "});
 2673
 2674    cx.set_state(indoc! {"
 2675        one two
 2676        ˇ    three
 2677            four
 2678    "});
 2679    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2680    cx.assert_editor_state(indoc! {"
 2681        one two
 2682        ˇthree
 2683            four
 2684    "});
 2685}
 2686
 2687#[gpui::test]
 2688async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2689    init_test(cx, |settings| {
 2690        settings.defaults.hard_tabs = Some(true);
 2691    });
 2692
 2693    let mut cx = EditorTestContext::new(cx).await;
 2694
 2695    // select two ranges on one line
 2696    cx.set_state(indoc! {"
 2697        «oneˇ» «twoˇ»
 2698        three
 2699        four
 2700    "});
 2701    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2702    cx.assert_editor_state(indoc! {"
 2703        \t«oneˇ» «twoˇ»
 2704        three
 2705        four
 2706    "});
 2707    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2708    cx.assert_editor_state(indoc! {"
 2709        \t\t«oneˇ» «twoˇ»
 2710        three
 2711        four
 2712    "});
 2713    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2714    cx.assert_editor_state(indoc! {"
 2715        \t«oneˇ» «twoˇ»
 2716        three
 2717        four
 2718    "});
 2719    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2720    cx.assert_editor_state(indoc! {"
 2721        «oneˇ» «twoˇ»
 2722        three
 2723        four
 2724    "});
 2725
 2726    // select across a line ending
 2727    cx.set_state(indoc! {"
 2728        one two
 2729        t«hree
 2730        ˇ»four
 2731    "});
 2732    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2733    cx.assert_editor_state(indoc! {"
 2734        one two
 2735        \tt«hree
 2736        ˇ»four
 2737    "});
 2738    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2739    cx.assert_editor_state(indoc! {"
 2740        one two
 2741        \t\tt«hree
 2742        ˇ»four
 2743    "});
 2744    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2745    cx.assert_editor_state(indoc! {"
 2746        one two
 2747        \tt«hree
 2748        ˇ»four
 2749    "});
 2750    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2751    cx.assert_editor_state(indoc! {"
 2752        one two
 2753        t«hree
 2754        ˇ»four
 2755    "});
 2756
 2757    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2758    cx.set_state(indoc! {"
 2759        one two
 2760        ˇthree
 2761        four
 2762    "});
 2763    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2764    cx.assert_editor_state(indoc! {"
 2765        one two
 2766        ˇthree
 2767        four
 2768    "});
 2769    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2770    cx.assert_editor_state(indoc! {"
 2771        one two
 2772        \tˇthree
 2773        four
 2774    "});
 2775    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2776    cx.assert_editor_state(indoc! {"
 2777        one two
 2778        ˇthree
 2779        four
 2780    "});
 2781}
 2782
 2783#[gpui::test]
 2784fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2785    init_test(cx, |settings| {
 2786        settings.languages.extend([
 2787            (
 2788                "TOML".into(),
 2789                LanguageSettingsContent {
 2790                    tab_size: NonZeroU32::new(2),
 2791                    ..Default::default()
 2792                },
 2793            ),
 2794            (
 2795                "Rust".into(),
 2796                LanguageSettingsContent {
 2797                    tab_size: NonZeroU32::new(4),
 2798                    ..Default::default()
 2799                },
 2800            ),
 2801        ]);
 2802    });
 2803
 2804    let toml_language = Arc::new(Language::new(
 2805        LanguageConfig {
 2806            name: "TOML".into(),
 2807            ..Default::default()
 2808        },
 2809        None,
 2810    ));
 2811    let rust_language = Arc::new(Language::new(
 2812        LanguageConfig {
 2813            name: "Rust".into(),
 2814            ..Default::default()
 2815        },
 2816        None,
 2817    ));
 2818
 2819    let toml_buffer =
 2820        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2821    let rust_buffer = cx.new_model(|cx| {
 2822        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2823    });
 2824    let multibuffer = cx.new_model(|cx| {
 2825        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 2826        multibuffer.push_excerpts(
 2827            toml_buffer.clone(),
 2828            [ExcerptRange {
 2829                context: Point::new(0, 0)..Point::new(2, 0),
 2830                primary: None,
 2831            }],
 2832            cx,
 2833        );
 2834        multibuffer.push_excerpts(
 2835            rust_buffer.clone(),
 2836            [ExcerptRange {
 2837                context: Point::new(0, 0)..Point::new(1, 0),
 2838                primary: None,
 2839            }],
 2840            cx,
 2841        );
 2842        multibuffer
 2843    });
 2844
 2845    cx.add_window(|cx| {
 2846        let mut editor = build_editor(multibuffer, cx);
 2847
 2848        assert_eq!(
 2849            editor.text(cx),
 2850            indoc! {"
 2851                a = 1
 2852                b = 2
 2853
 2854                const c: usize = 3;
 2855            "}
 2856        );
 2857
 2858        select_ranges(
 2859            &mut editor,
 2860            indoc! {"
 2861                «aˇ» = 1
 2862                b = 2
 2863
 2864                «const c:ˇ» usize = 3;
 2865            "},
 2866            cx,
 2867        );
 2868
 2869        editor.tab(&Tab, cx);
 2870        assert_text_with_selections(
 2871            &mut editor,
 2872            indoc! {"
 2873                  «aˇ» = 1
 2874                b = 2
 2875
 2876                    «const c:ˇ» usize = 3;
 2877            "},
 2878            cx,
 2879        );
 2880        editor.tab_prev(&TabPrev, cx);
 2881        assert_text_with_selections(
 2882            &mut editor,
 2883            indoc! {"
 2884                «aˇ» = 1
 2885                b = 2
 2886
 2887                «const c:ˇ» usize = 3;
 2888            "},
 2889            cx,
 2890        );
 2891
 2892        editor
 2893    });
 2894}
 2895
 2896#[gpui::test]
 2897async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2898    init_test(cx, |_| {});
 2899
 2900    let mut cx = EditorTestContext::new(cx).await;
 2901
 2902    // Basic backspace
 2903    cx.set_state(indoc! {"
 2904        onˇe two three
 2905        fou«rˇ» five six
 2906        seven «ˇeight nine
 2907        »ten
 2908    "});
 2909    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2910    cx.assert_editor_state(indoc! {"
 2911        oˇe two three
 2912        fouˇ five six
 2913        seven ˇten
 2914    "});
 2915
 2916    // Test backspace inside and around indents
 2917    cx.set_state(indoc! {"
 2918        zero
 2919            ˇone
 2920                ˇtwo
 2921            ˇ ˇ ˇ  three
 2922        ˇ  ˇ  four
 2923    "});
 2924    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2925    cx.assert_editor_state(indoc! {"
 2926        zero
 2927        ˇone
 2928            ˇtwo
 2929        ˇ  threeˇ  four
 2930    "});
 2931
 2932    // Test backspace with line_mode set to true
 2933    cx.update_editor(|e, _| e.selections.line_mode = true);
 2934    cx.set_state(indoc! {"
 2935        The ˇquick ˇbrown
 2936        fox jumps over
 2937        the lazy dog
 2938        ˇThe qu«ick bˇ»rown"});
 2939    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2940    cx.assert_editor_state(indoc! {"
 2941        ˇfox jumps over
 2942        the lazy dogˇ"});
 2943}
 2944
 2945#[gpui::test]
 2946async fn test_delete(cx: &mut gpui::TestAppContext) {
 2947    init_test(cx, |_| {});
 2948
 2949    let mut cx = EditorTestContext::new(cx).await;
 2950    cx.set_state(indoc! {"
 2951        onˇe two three
 2952        fou«rˇ» five six
 2953        seven «ˇeight nine
 2954        »ten
 2955    "});
 2956    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2957    cx.assert_editor_state(indoc! {"
 2958        onˇ two three
 2959        fouˇ five six
 2960        seven ˇten
 2961    "});
 2962
 2963    // Test backspace with line_mode set to true
 2964    cx.update_editor(|e, _| e.selections.line_mode = true);
 2965    cx.set_state(indoc! {"
 2966        The ˇquick ˇbrown
 2967        fox «ˇjum»ps over
 2968        the lazy dog
 2969        ˇThe qu«ick bˇ»rown"});
 2970    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2971    cx.assert_editor_state("ˇthe lazy dogˇ");
 2972}
 2973
 2974#[gpui::test]
 2975fn test_delete_line(cx: &mut TestAppContext) {
 2976    init_test(cx, |_| {});
 2977
 2978    let view = cx.add_window(|cx| {
 2979        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2980        build_editor(buffer, cx)
 2981    });
 2982    _ = view.update(cx, |view, cx| {
 2983        view.change_selections(None, cx, |s| {
 2984            s.select_display_ranges([
 2985                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2986                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2987                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2988            ])
 2989        });
 2990        view.delete_line(&DeleteLine, cx);
 2991        assert_eq!(view.display_text(cx), "ghi");
 2992        assert_eq!(
 2993            view.selections.display_ranges(cx),
 2994            vec![
 2995                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2996                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2997            ]
 2998        );
 2999    });
 3000
 3001    let view = cx.add_window(|cx| {
 3002        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3003        build_editor(buffer, cx)
 3004    });
 3005    _ = view.update(cx, |view, cx| {
 3006        view.change_selections(None, cx, |s| {
 3007            s.select_display_ranges([
 3008                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3009            ])
 3010        });
 3011        view.delete_line(&DeleteLine, cx);
 3012        assert_eq!(view.display_text(cx), "ghi\n");
 3013        assert_eq!(
 3014            view.selections.display_ranges(cx),
 3015            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3016        );
 3017    });
 3018}
 3019
 3020#[gpui::test]
 3021fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3022    init_test(cx, |_| {});
 3023
 3024    cx.add_window(|cx| {
 3025        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3026        let mut editor = build_editor(buffer.clone(), cx);
 3027        let buffer = buffer.read(cx).as_singleton().unwrap();
 3028
 3029        assert_eq!(
 3030            editor.selections.ranges::<Point>(cx),
 3031            &[Point::new(0, 0)..Point::new(0, 0)]
 3032        );
 3033
 3034        // When on single line, replace newline at end by space
 3035        editor.join_lines(&JoinLines, cx);
 3036        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3037        assert_eq!(
 3038            editor.selections.ranges::<Point>(cx),
 3039            &[Point::new(0, 3)..Point::new(0, 3)]
 3040        );
 3041
 3042        // When multiple lines are selected, remove newlines that are spanned by the selection
 3043        editor.change_selections(None, cx, |s| {
 3044            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3045        });
 3046        editor.join_lines(&JoinLines, cx);
 3047        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3048        assert_eq!(
 3049            editor.selections.ranges::<Point>(cx),
 3050            &[Point::new(0, 11)..Point::new(0, 11)]
 3051        );
 3052
 3053        // Undo should be transactional
 3054        editor.undo(&Undo, cx);
 3055        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3056        assert_eq!(
 3057            editor.selections.ranges::<Point>(cx),
 3058            &[Point::new(0, 5)..Point::new(2, 2)]
 3059        );
 3060
 3061        // When joining an empty line don't insert a space
 3062        editor.change_selections(None, cx, |s| {
 3063            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3064        });
 3065        editor.join_lines(&JoinLines, cx);
 3066        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3067        assert_eq!(
 3068            editor.selections.ranges::<Point>(cx),
 3069            [Point::new(2, 3)..Point::new(2, 3)]
 3070        );
 3071
 3072        // We can remove trailing newlines
 3073        editor.join_lines(&JoinLines, cx);
 3074        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3075        assert_eq!(
 3076            editor.selections.ranges::<Point>(cx),
 3077            [Point::new(2, 3)..Point::new(2, 3)]
 3078        );
 3079
 3080        // We don't blow up on the last line
 3081        editor.join_lines(&JoinLines, cx);
 3082        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3083        assert_eq!(
 3084            editor.selections.ranges::<Point>(cx),
 3085            [Point::new(2, 3)..Point::new(2, 3)]
 3086        );
 3087
 3088        // reset to test indentation
 3089        editor.buffer.update(cx, |buffer, cx| {
 3090            buffer.edit(
 3091                [
 3092                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3093                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3094                ],
 3095                None,
 3096                cx,
 3097            )
 3098        });
 3099
 3100        // We remove any leading spaces
 3101        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3102        editor.change_selections(None, cx, |s| {
 3103            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3104        });
 3105        editor.join_lines(&JoinLines, cx);
 3106        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3107
 3108        // We don't insert a space for a line containing only spaces
 3109        editor.join_lines(&JoinLines, cx);
 3110        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3111
 3112        // We ignore any leading tabs
 3113        editor.join_lines(&JoinLines, cx);
 3114        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3115
 3116        editor
 3117    });
 3118}
 3119
 3120#[gpui::test]
 3121fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3122    init_test(cx, |_| {});
 3123
 3124    cx.add_window(|cx| {
 3125        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3126        let mut editor = build_editor(buffer.clone(), cx);
 3127        let buffer = buffer.read(cx).as_singleton().unwrap();
 3128
 3129        editor.change_selections(None, cx, |s| {
 3130            s.select_ranges([
 3131                Point::new(0, 2)..Point::new(1, 1),
 3132                Point::new(1, 2)..Point::new(1, 2),
 3133                Point::new(3, 1)..Point::new(3, 2),
 3134            ])
 3135        });
 3136
 3137        editor.join_lines(&JoinLines, cx);
 3138        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3139
 3140        assert_eq!(
 3141            editor.selections.ranges::<Point>(cx),
 3142            [
 3143                Point::new(0, 7)..Point::new(0, 7),
 3144                Point::new(1, 3)..Point::new(1, 3)
 3145            ]
 3146        );
 3147        editor
 3148    });
 3149}
 3150
 3151#[gpui::test]
 3152async fn test_join_lines_with_git_diff_base(
 3153    executor: BackgroundExecutor,
 3154    cx: &mut gpui::TestAppContext,
 3155) {
 3156    init_test(cx, |_| {});
 3157
 3158    let mut cx = EditorTestContext::new(cx).await;
 3159
 3160    let diff_base = r#"
 3161        Line 0
 3162        Line 1
 3163        Line 2
 3164        Line 3
 3165        "#
 3166    .unindent();
 3167
 3168    cx.set_state(
 3169        &r#"
 3170        ˇLine 0
 3171        Line 1
 3172        Line 2
 3173        Line 3
 3174        "#
 3175        .unindent(),
 3176    );
 3177
 3178    cx.set_diff_base(Some(&diff_base));
 3179    executor.run_until_parked();
 3180
 3181    // Join lines
 3182    cx.update_editor(|editor, cx| {
 3183        editor.join_lines(&JoinLines, cx);
 3184    });
 3185    executor.run_until_parked();
 3186
 3187    cx.assert_editor_state(
 3188        &r#"
 3189        Line 0ˇ Line 1
 3190        Line 2
 3191        Line 3
 3192        "#
 3193        .unindent(),
 3194    );
 3195    // Join again
 3196    cx.update_editor(|editor, cx| {
 3197        editor.join_lines(&JoinLines, cx);
 3198    });
 3199    executor.run_until_parked();
 3200
 3201    cx.assert_editor_state(
 3202        &r#"
 3203        Line 0 Line 1ˇ Line 2
 3204        Line 3
 3205        "#
 3206        .unindent(),
 3207    );
 3208}
 3209
 3210#[gpui::test]
 3211async fn test_custom_newlines_cause_no_false_positive_diffs(
 3212    executor: BackgroundExecutor,
 3213    cx: &mut gpui::TestAppContext,
 3214) {
 3215    init_test(cx, |_| {});
 3216    let mut cx = EditorTestContext::new(cx).await;
 3217    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3218    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3219    executor.run_until_parked();
 3220
 3221    cx.update_editor(|editor, cx| {
 3222        assert_eq!(
 3223            editor
 3224                .buffer()
 3225                .read(cx)
 3226                .snapshot(cx)
 3227                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3228                .collect::<Vec<_>>(),
 3229            Vec::new(),
 3230            "Should not have any diffs for files with custom newlines"
 3231        );
 3232    });
 3233}
 3234
 3235#[gpui::test]
 3236async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3237    init_test(cx, |_| {});
 3238
 3239    let mut cx = EditorTestContext::new(cx).await;
 3240
 3241    // Test sort_lines_case_insensitive()
 3242    cx.set_state(indoc! {"
 3243        «z
 3244        y
 3245        x
 3246        Z
 3247        Y
 3248        Xˇ»
 3249    "});
 3250    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3251    cx.assert_editor_state(indoc! {"
 3252        «x
 3253        X
 3254        y
 3255        Y
 3256        z
 3257        Zˇ»
 3258    "});
 3259
 3260    // Test reverse_lines()
 3261    cx.set_state(indoc! {"
 3262        «5
 3263        4
 3264        3
 3265        2
 3266        1ˇ»
 3267    "});
 3268    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3269    cx.assert_editor_state(indoc! {"
 3270        «1
 3271        2
 3272        3
 3273        4
 3274        5ˇ»
 3275    "});
 3276
 3277    // Skip testing shuffle_line()
 3278
 3279    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3280    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3281
 3282    // Don't manipulate when cursor is on single line, but expand the selection
 3283    cx.set_state(indoc! {"
 3284        ddˇdd
 3285        ccc
 3286        bb
 3287        a
 3288    "});
 3289    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3290    cx.assert_editor_state(indoc! {"
 3291        «ddddˇ»
 3292        ccc
 3293        bb
 3294        a
 3295    "});
 3296
 3297    // Basic manipulate case
 3298    // Start selection moves to column 0
 3299    // End of selection shrinks to fit shorter line
 3300    cx.set_state(indoc! {"
 3301        dd«d
 3302        ccc
 3303        bb
 3304        aaaaaˇ»
 3305    "});
 3306    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3307    cx.assert_editor_state(indoc! {"
 3308        «aaaaa
 3309        bb
 3310        ccc
 3311        dddˇ»
 3312    "});
 3313
 3314    // Manipulate case with newlines
 3315    cx.set_state(indoc! {"
 3316        dd«d
 3317        ccc
 3318
 3319        bb
 3320        aaaaa
 3321
 3322        ˇ»
 3323    "});
 3324    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3325    cx.assert_editor_state(indoc! {"
 3326        «
 3327
 3328        aaaaa
 3329        bb
 3330        ccc
 3331        dddˇ»
 3332
 3333    "});
 3334
 3335    // Adding new line
 3336    cx.set_state(indoc! {"
 3337        aa«a
 3338        bbˇ»b
 3339    "});
 3340    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3341    cx.assert_editor_state(indoc! {"
 3342        «aaa
 3343        bbb
 3344        added_lineˇ»
 3345    "});
 3346
 3347    // Removing line
 3348    cx.set_state(indoc! {"
 3349        aa«a
 3350        bbbˇ»
 3351    "});
 3352    cx.update_editor(|e, cx| {
 3353        e.manipulate_lines(cx, |lines| {
 3354            lines.pop();
 3355        })
 3356    });
 3357    cx.assert_editor_state(indoc! {"
 3358        «aaaˇ»
 3359    "});
 3360
 3361    // Removing all lines
 3362    cx.set_state(indoc! {"
 3363        aa«a
 3364        bbbˇ»
 3365    "});
 3366    cx.update_editor(|e, cx| {
 3367        e.manipulate_lines(cx, |lines| {
 3368            lines.drain(..);
 3369        })
 3370    });
 3371    cx.assert_editor_state(indoc! {"
 3372        ˇ
 3373    "});
 3374}
 3375
 3376#[gpui::test]
 3377async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3378    init_test(cx, |_| {});
 3379
 3380    let mut cx = EditorTestContext::new(cx).await;
 3381
 3382    // Consider continuous selection as single selection
 3383    cx.set_state(indoc! {"
 3384        Aaa«aa
 3385        cˇ»c«c
 3386        bb
 3387        aaaˇ»aa
 3388    "});
 3389    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3390    cx.assert_editor_state(indoc! {"
 3391        «Aaaaa
 3392        ccc
 3393        bb
 3394        aaaaaˇ»
 3395    "});
 3396
 3397    cx.set_state(indoc! {"
 3398        Aaa«aa
 3399        cˇ»c«c
 3400        bb
 3401        aaaˇ»aa
 3402    "});
 3403    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3404    cx.assert_editor_state(indoc! {"
 3405        «Aaaaa
 3406        ccc
 3407        bbˇ»
 3408    "});
 3409
 3410    // Consider non continuous selection as distinct dedup operations
 3411    cx.set_state(indoc! {"
 3412        «aaaaa
 3413        bb
 3414        aaaaa
 3415        aaaaaˇ»
 3416
 3417        aaa«aaˇ»
 3418    "});
 3419    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3420    cx.assert_editor_state(indoc! {"
 3421        «aaaaa
 3422        bbˇ»
 3423
 3424        «aaaaaˇ»
 3425    "});
 3426}
 3427
 3428#[gpui::test]
 3429async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3430    init_test(cx, |_| {});
 3431
 3432    let mut cx = EditorTestContext::new(cx).await;
 3433
 3434    cx.set_state(indoc! {"
 3435        «Aaa
 3436        aAa
 3437        Aaaˇ»
 3438    "});
 3439    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3440    cx.assert_editor_state(indoc! {"
 3441        «Aaa
 3442        aAaˇ»
 3443    "});
 3444
 3445    cx.set_state(indoc! {"
 3446        «Aaa
 3447        aAa
 3448        aaAˇ»
 3449    "});
 3450    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3451    cx.assert_editor_state(indoc! {"
 3452        «Aaaˇ»
 3453    "});
 3454}
 3455
 3456#[gpui::test]
 3457async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3458    init_test(cx, |_| {});
 3459
 3460    let mut cx = EditorTestContext::new(cx).await;
 3461
 3462    // Manipulate with multiple selections on a single line
 3463    cx.set_state(indoc! {"
 3464        dd«dd
 3465        cˇ»c«c
 3466        bb
 3467        aaaˇ»aa
 3468    "});
 3469    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3470    cx.assert_editor_state(indoc! {"
 3471        «aaaaa
 3472        bb
 3473        ccc
 3474        ddddˇ»
 3475    "});
 3476
 3477    // Manipulate with multiple disjoin selections
 3478    cx.set_state(indoc! {"
 3479 3480        4
 3481        3
 3482        2
 3483        1ˇ»
 3484
 3485        dd«dd
 3486        ccc
 3487        bb
 3488        aaaˇ»aa
 3489    "});
 3490    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3491    cx.assert_editor_state(indoc! {"
 3492        «1
 3493        2
 3494        3
 3495        4
 3496        5ˇ»
 3497
 3498        «aaaaa
 3499        bb
 3500        ccc
 3501        ddddˇ»
 3502    "});
 3503
 3504    // Adding lines on each selection
 3505    cx.set_state(indoc! {"
 3506 3507        1ˇ»
 3508
 3509        bb«bb
 3510        aaaˇ»aa
 3511    "});
 3512    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3513    cx.assert_editor_state(indoc! {"
 3514        «2
 3515        1
 3516        added lineˇ»
 3517
 3518        «bbbb
 3519        aaaaa
 3520        added lineˇ»
 3521    "});
 3522
 3523    // Removing lines on each selection
 3524    cx.set_state(indoc! {"
 3525 3526        1ˇ»
 3527
 3528        bb«bb
 3529        aaaˇ»aa
 3530    "});
 3531    cx.update_editor(|e, cx| {
 3532        e.manipulate_lines(cx, |lines| {
 3533            lines.pop();
 3534        })
 3535    });
 3536    cx.assert_editor_state(indoc! {"
 3537        «2ˇ»
 3538
 3539        «bbbbˇ»
 3540    "});
 3541}
 3542
 3543#[gpui::test]
 3544async fn test_manipulate_text(cx: &mut TestAppContext) {
 3545    init_test(cx, |_| {});
 3546
 3547    let mut cx = EditorTestContext::new(cx).await;
 3548
 3549    // Test convert_to_upper_case()
 3550    cx.set_state(indoc! {"
 3551        «hello worldˇ»
 3552    "});
 3553    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3554    cx.assert_editor_state(indoc! {"
 3555        «HELLO WORLDˇ»
 3556    "});
 3557
 3558    // Test convert_to_lower_case()
 3559    cx.set_state(indoc! {"
 3560        «HELLO WORLDˇ»
 3561    "});
 3562    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3563    cx.assert_editor_state(indoc! {"
 3564        «hello worldˇ»
 3565    "});
 3566
 3567    // Test multiple line, single selection case
 3568    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3569    cx.set_state(indoc! {"
 3570        «The quick brown
 3571        fox jumps over
 3572        the lazy dogˇ»
 3573    "});
 3574    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3575    cx.assert_editor_state(indoc! {"
 3576        «The Quick Brown
 3577        Fox Jumps Over
 3578        The Lazy Dogˇ»
 3579    "});
 3580
 3581    // Test multiple line, single selection case
 3582    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3583    cx.set_state(indoc! {"
 3584        «The quick brown
 3585        fox jumps over
 3586        the lazy dogˇ»
 3587    "});
 3588    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3589    cx.assert_editor_state(indoc! {"
 3590        «TheQuickBrown
 3591        FoxJumpsOver
 3592        TheLazyDogˇ»
 3593    "});
 3594
 3595    // From here on out, test more complex cases of manipulate_text()
 3596
 3597    // Test no selection case - should affect words cursors are in
 3598    // Cursor at beginning, middle, and end of word
 3599    cx.set_state(indoc! {"
 3600        ˇhello big beauˇtiful worldˇ
 3601    "});
 3602    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3603    cx.assert_editor_state(indoc! {"
 3604        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3605    "});
 3606
 3607    // Test multiple selections on a single line and across multiple lines
 3608    cx.set_state(indoc! {"
 3609        «Theˇ» quick «brown
 3610        foxˇ» jumps «overˇ»
 3611        the «lazyˇ» dog
 3612    "});
 3613    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3614    cx.assert_editor_state(indoc! {"
 3615        «THEˇ» quick «BROWN
 3616        FOXˇ» jumps «OVERˇ»
 3617        the «LAZYˇ» dog
 3618    "});
 3619
 3620    // Test case where text length grows
 3621    cx.set_state(indoc! {"
 3622        «tschüߡ»
 3623    "});
 3624    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3625    cx.assert_editor_state(indoc! {"
 3626        «TSCHÜSSˇ»
 3627    "});
 3628
 3629    // Test to make sure we don't crash when text shrinks
 3630    cx.set_state(indoc! {"
 3631        aaa_bbbˇ
 3632    "});
 3633    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3634    cx.assert_editor_state(indoc! {"
 3635        «aaaBbbˇ»
 3636    "});
 3637
 3638    // Test to make sure we all aware of the fact that each word can grow and shrink
 3639    // Final selections should be aware of this fact
 3640    cx.set_state(indoc! {"
 3641        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3642    "});
 3643    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3644    cx.assert_editor_state(indoc! {"
 3645        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3646    "});
 3647
 3648    cx.set_state(indoc! {"
 3649        «hElLo, WoRld!ˇ»
 3650    "});
 3651    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3652    cx.assert_editor_state(indoc! {"
 3653        «HeLlO, wOrLD!ˇ»
 3654    "});
 3655}
 3656
 3657#[gpui::test]
 3658fn test_duplicate_line(cx: &mut TestAppContext) {
 3659    init_test(cx, |_| {});
 3660
 3661    let view = cx.add_window(|cx| {
 3662        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3663        build_editor(buffer, cx)
 3664    });
 3665    _ = view.update(cx, |view, cx| {
 3666        view.change_selections(None, cx, |s| {
 3667            s.select_display_ranges([
 3668                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3669                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3670                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3671                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3672            ])
 3673        });
 3674        view.duplicate_line_down(&DuplicateLineDown, cx);
 3675        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3676        assert_eq!(
 3677            view.selections.display_ranges(cx),
 3678            vec![
 3679                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3680                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3681                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3682                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3683            ]
 3684        );
 3685    });
 3686
 3687    let view = cx.add_window(|cx| {
 3688        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3689        build_editor(buffer, cx)
 3690    });
 3691    _ = view.update(cx, |view, cx| {
 3692        view.change_selections(None, cx, |s| {
 3693            s.select_display_ranges([
 3694                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3695                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3696            ])
 3697        });
 3698        view.duplicate_line_down(&DuplicateLineDown, cx);
 3699        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3700        assert_eq!(
 3701            view.selections.display_ranges(cx),
 3702            vec![
 3703                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3704                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3705            ]
 3706        );
 3707    });
 3708
 3709    // With `move_upwards` the selections stay in place, except for
 3710    // the lines inserted above them
 3711    let view = cx.add_window(|cx| {
 3712        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3713        build_editor(buffer, cx)
 3714    });
 3715    _ = view.update(cx, |view, cx| {
 3716        view.change_selections(None, cx, |s| {
 3717            s.select_display_ranges([
 3718                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3719                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3720                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3721                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3722            ])
 3723        });
 3724        view.duplicate_line_up(&DuplicateLineUp, cx);
 3725        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3726        assert_eq!(
 3727            view.selections.display_ranges(cx),
 3728            vec![
 3729                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3730                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3731                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3732                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3733            ]
 3734        );
 3735    });
 3736
 3737    let view = cx.add_window(|cx| {
 3738        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3739        build_editor(buffer, cx)
 3740    });
 3741    _ = view.update(cx, |view, cx| {
 3742        view.change_selections(None, cx, |s| {
 3743            s.select_display_ranges([
 3744                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3745                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3746            ])
 3747        });
 3748        view.duplicate_line_up(&DuplicateLineUp, cx);
 3749        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3750        assert_eq!(
 3751            view.selections.display_ranges(cx),
 3752            vec![
 3753                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3754                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3755            ]
 3756        );
 3757    });
 3758}
 3759
 3760#[gpui::test]
 3761fn test_move_line_up_down(cx: &mut TestAppContext) {
 3762    init_test(cx, |_| {});
 3763
 3764    let view = cx.add_window(|cx| {
 3765        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3766        build_editor(buffer, cx)
 3767    });
 3768    _ = view.update(cx, |view, cx| {
 3769        view.fold_ranges(
 3770            vec![
 3771                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3772                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3773                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3774            ],
 3775            true,
 3776            cx,
 3777        );
 3778        view.change_selections(None, cx, |s| {
 3779            s.select_display_ranges([
 3780                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3781                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3782                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3783                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3784            ])
 3785        });
 3786        assert_eq!(
 3787            view.display_text(cx),
 3788            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3789        );
 3790
 3791        view.move_line_up(&MoveLineUp, cx);
 3792        assert_eq!(
 3793            view.display_text(cx),
 3794            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3795        );
 3796        assert_eq!(
 3797            view.selections.display_ranges(cx),
 3798            vec![
 3799                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3800                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3801                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3802                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3803            ]
 3804        );
 3805    });
 3806
 3807    _ = view.update(cx, |view, cx| {
 3808        view.move_line_down(&MoveLineDown, cx);
 3809        assert_eq!(
 3810            view.display_text(cx),
 3811            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3812        );
 3813        assert_eq!(
 3814            view.selections.display_ranges(cx),
 3815            vec![
 3816                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3817                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3818                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3819                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3820            ]
 3821        );
 3822    });
 3823
 3824    _ = view.update(cx, |view, cx| {
 3825        view.move_line_down(&MoveLineDown, cx);
 3826        assert_eq!(
 3827            view.display_text(cx),
 3828            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3829        );
 3830        assert_eq!(
 3831            view.selections.display_ranges(cx),
 3832            vec![
 3833                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3834                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3835                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3836                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3837            ]
 3838        );
 3839    });
 3840
 3841    _ = view.update(cx, |view, cx| {
 3842        view.move_line_up(&MoveLineUp, cx);
 3843        assert_eq!(
 3844            view.display_text(cx),
 3845            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3846        );
 3847        assert_eq!(
 3848            view.selections.display_ranges(cx),
 3849            vec![
 3850                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3851                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3852                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3853                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3854            ]
 3855        );
 3856    });
 3857}
 3858
 3859#[gpui::test]
 3860fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3861    init_test(cx, |_| {});
 3862
 3863    let editor = cx.add_window(|cx| {
 3864        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3865        build_editor(buffer, cx)
 3866    });
 3867    _ = editor.update(cx, |editor, cx| {
 3868        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3869        editor.insert_blocks(
 3870            [BlockProperties {
 3871                style: BlockStyle::Fixed,
 3872                position: snapshot.anchor_after(Point::new(2, 0)),
 3873                disposition: BlockDisposition::Below,
 3874                height: 1,
 3875                render: Box::new(|_| div().into_any()),
 3876                priority: 0,
 3877            }],
 3878            Some(Autoscroll::fit()),
 3879            cx,
 3880        );
 3881        editor.change_selections(None, cx, |s| {
 3882            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3883        });
 3884        editor.move_line_down(&MoveLineDown, cx);
 3885    });
 3886}
 3887
 3888#[gpui::test]
 3889fn test_transpose(cx: &mut TestAppContext) {
 3890    init_test(cx, |_| {});
 3891
 3892    _ = cx.add_window(|cx| {
 3893        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3894        editor.set_style(EditorStyle::default(), cx);
 3895        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3896        editor.transpose(&Default::default(), cx);
 3897        assert_eq!(editor.text(cx), "bac");
 3898        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3899
 3900        editor.transpose(&Default::default(), cx);
 3901        assert_eq!(editor.text(cx), "bca");
 3902        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3903
 3904        editor.transpose(&Default::default(), cx);
 3905        assert_eq!(editor.text(cx), "bac");
 3906        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3907
 3908        editor
 3909    });
 3910
 3911    _ = cx.add_window(|cx| {
 3912        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3913        editor.set_style(EditorStyle::default(), cx);
 3914        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3915        editor.transpose(&Default::default(), cx);
 3916        assert_eq!(editor.text(cx), "acb\nde");
 3917        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3918
 3919        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3920        editor.transpose(&Default::default(), cx);
 3921        assert_eq!(editor.text(cx), "acbd\ne");
 3922        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3923
 3924        editor.transpose(&Default::default(), cx);
 3925        assert_eq!(editor.text(cx), "acbde\n");
 3926        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3927
 3928        editor.transpose(&Default::default(), cx);
 3929        assert_eq!(editor.text(cx), "acbd\ne");
 3930        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3931
 3932        editor
 3933    });
 3934
 3935    _ = cx.add_window(|cx| {
 3936        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3937        editor.set_style(EditorStyle::default(), cx);
 3938        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3939        editor.transpose(&Default::default(), cx);
 3940        assert_eq!(editor.text(cx), "bacd\ne");
 3941        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3942
 3943        editor.transpose(&Default::default(), cx);
 3944        assert_eq!(editor.text(cx), "bcade\n");
 3945        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3946
 3947        editor.transpose(&Default::default(), cx);
 3948        assert_eq!(editor.text(cx), "bcda\ne");
 3949        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3950
 3951        editor.transpose(&Default::default(), cx);
 3952        assert_eq!(editor.text(cx), "bcade\n");
 3953        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3954
 3955        editor.transpose(&Default::default(), cx);
 3956        assert_eq!(editor.text(cx), "bcaed\n");
 3957        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3958
 3959        editor
 3960    });
 3961
 3962    _ = cx.add_window(|cx| {
 3963        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3964        editor.set_style(EditorStyle::default(), cx);
 3965        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3966        editor.transpose(&Default::default(), cx);
 3967        assert_eq!(editor.text(cx), "🏀🍐✋");
 3968        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3969
 3970        editor.transpose(&Default::default(), cx);
 3971        assert_eq!(editor.text(cx), "🏀✋🍐");
 3972        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3973
 3974        editor.transpose(&Default::default(), cx);
 3975        assert_eq!(editor.text(cx), "🏀🍐✋");
 3976        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3977
 3978        editor
 3979    });
 3980}
 3981
 3982#[gpui::test]
 3983async fn test_rewrap(cx: &mut TestAppContext) {
 3984    init_test(cx, |_| {});
 3985
 3986    let mut cx = EditorTestContext::new(cx).await;
 3987
 3988    {
 3989        let language = Arc::new(Language::new(
 3990            LanguageConfig {
 3991                line_comments: vec!["// ".into()],
 3992                ..LanguageConfig::default()
 3993            },
 3994            None,
 3995        ));
 3996        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3997
 3998        let unwrapped_text = indoc! {"
 3999            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4000        "};
 4001
 4002        let wrapped_text = indoc! {"
 4003            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4004            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4005            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4006            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4007            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4008            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4009            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4010            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4011            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4012            // porttitor id. Aliquam id accumsan eros.ˇ
 4013        "};
 4014
 4015        cx.set_state(unwrapped_text);
 4016        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4017        cx.assert_editor_state(wrapped_text);
 4018    }
 4019
 4020    // Test that rewrapping works inside of a selection
 4021    {
 4022        let language = Arc::new(Language::new(
 4023            LanguageConfig {
 4024                line_comments: vec!["// ".into()],
 4025                ..LanguageConfig::default()
 4026            },
 4027            None,
 4028        ));
 4029        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4030
 4031        let unwrapped_text = indoc! {"
 4032            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
 4033        "};
 4034
 4035        let wrapped_text = indoc! {"
 4036            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4037            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4038            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4039            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4040            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4041            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4042            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4043            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4044            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4045            // porttitor id. Aliquam id accumsan eros.ˇ
 4046        "};
 4047
 4048        cx.set_state(unwrapped_text);
 4049        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4050        cx.assert_editor_state(wrapped_text);
 4051    }
 4052
 4053    // Test that cursors that expand to the same region are collapsed.
 4054    {
 4055        let language = Arc::new(Language::new(
 4056            LanguageConfig {
 4057                line_comments: vec!["// ".into()],
 4058                ..LanguageConfig::default()
 4059            },
 4060            None,
 4061        ));
 4062        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4063
 4064        let unwrapped_text = indoc! {"
 4065            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4066            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4067            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4068            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4069        "};
 4070
 4071        let wrapped_text = indoc! {"
 4072            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4073            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4074            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4075            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4076            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4077            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4078            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4079            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4080            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4081            // porttitor id. Aliquam id accumsan eros.ˇˇˇˇ
 4082        "};
 4083
 4084        cx.set_state(unwrapped_text);
 4085        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4086        cx.assert_editor_state(wrapped_text);
 4087    }
 4088
 4089    // Test that non-contiguous selections are treated separately.
 4090    {
 4091        let language = Arc::new(Language::new(
 4092            LanguageConfig {
 4093                line_comments: vec!["// ".into()],
 4094                ..LanguageConfig::default()
 4095            },
 4096            None,
 4097        ));
 4098        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4099
 4100        let unwrapped_text = indoc! {"
 4101            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4102            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4103            //
 4104            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4105            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4106        "};
 4107
 4108        let wrapped_text = indoc! {"
 4109            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4110            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4111            // auctor, eu lacinia sapien scelerisque.ˇˇ
 4112            //
 4113            // Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4114            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4115            // blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4116            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4117            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4118            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4119            // vulputate turpis porttitor id. Aliquam id accumsan eros.ˇˇ
 4120        "};
 4121
 4122        cx.set_state(unwrapped_text);
 4123        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4124        cx.assert_editor_state(wrapped_text);
 4125    }
 4126
 4127    // Test that different comment prefixes are supported.
 4128    {
 4129        let language = Arc::new(Language::new(
 4130            LanguageConfig {
 4131                line_comments: vec!["# ".into()],
 4132                ..LanguageConfig::default()
 4133            },
 4134            None,
 4135        ));
 4136        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4137
 4138        let unwrapped_text = indoc! {"
 4139            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4140        "};
 4141
 4142        let wrapped_text = indoc! {"
 4143            # Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4144            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4145            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4146            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4147            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4148            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4149            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4150            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4151            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4152            # accumsan eros.ˇ
 4153        "};
 4154
 4155        cx.set_state(unwrapped_text);
 4156        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4157        cx.assert_editor_state(wrapped_text);
 4158    }
 4159
 4160    // Test that rewrapping is ignored outside of comments in most languages.
 4161    {
 4162        let language = Arc::new(Language::new(
 4163            LanguageConfig {
 4164                line_comments: vec!["// ".into(), "/// ".into()],
 4165                ..LanguageConfig::default()
 4166            },
 4167            Some(tree_sitter_rust::LANGUAGE.into()),
 4168        ));
 4169        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4170
 4171        let unwrapped_text = indoc! {"
 4172            /// Adds two numbers.
 4173            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4174            fn add(a: u32, b: u32) -> u32 {
 4175                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4176            }
 4177        "};
 4178
 4179        let wrapped_text = indoc! {"
 4180            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4181            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4182            fn add(a: u32, b: u32) -> u32 {
 4183                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4184            }
 4185        "};
 4186
 4187        cx.set_state(unwrapped_text);
 4188        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4189        cx.assert_editor_state(wrapped_text);
 4190    }
 4191
 4192    // Test that rewrapping works in Markdown and Plain Text languages.
 4193    {
 4194        let markdown_language = Arc::new(Language::new(
 4195            LanguageConfig {
 4196                name: "Markdown".into(),
 4197                ..LanguageConfig::default()
 4198            },
 4199            None,
 4200        ));
 4201        cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
 4202
 4203        let unwrapped_text = indoc! {"
 4204            # Hello
 4205
 4206            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4207        "};
 4208
 4209        let wrapped_text = indoc! {"
 4210            # Hello
 4211
 4212            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4213            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4214            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4215            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4216            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4217            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4218            Integer sit amet scelerisque nisi.ˇ
 4219        "};
 4220
 4221        cx.set_state(unwrapped_text);
 4222        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4223        cx.assert_editor_state(wrapped_text);
 4224
 4225        let plaintext_language = Arc::new(Language::new(
 4226            LanguageConfig {
 4227                name: "Plain Text".into(),
 4228                ..LanguageConfig::default()
 4229            },
 4230            None,
 4231        ));
 4232        cx.update_buffer(|buffer, cx| buffer.set_language(Some(plaintext_language), cx));
 4233
 4234        let unwrapped_text = indoc! {"
 4235            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4236        "};
 4237
 4238        let wrapped_text = indoc! {"
 4239            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4240            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4241            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4242            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4243            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4244            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4245            Integer sit amet scelerisque nisi.ˇ
 4246        "};
 4247
 4248        cx.set_state(unwrapped_text);
 4249        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4250        cx.assert_editor_state(wrapped_text);
 4251    }
 4252}
 4253
 4254#[gpui::test]
 4255async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4256    init_test(cx, |_| {});
 4257
 4258    let mut cx = EditorTestContext::new(cx).await;
 4259
 4260    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4261    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4262    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4263
 4264    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4265    cx.set_state("two ˇfour ˇsix ˇ");
 4266    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4267    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4268
 4269    // Paste again but with only two cursors. Since the number of cursors doesn't
 4270    // match the number of slices in the clipboard, the entire clipboard text
 4271    // is pasted at each cursor.
 4272    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4273    cx.update_editor(|e, cx| {
 4274        e.handle_input("( ", cx);
 4275        e.paste(&Paste, cx);
 4276        e.handle_input(") ", cx);
 4277    });
 4278    cx.assert_editor_state(
 4279        &([
 4280            "( one✅ ",
 4281            "three ",
 4282            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4283            "three ",
 4284            "five ) ˇ",
 4285        ]
 4286        .join("\n")),
 4287    );
 4288
 4289    // Cut with three selections, one of which is full-line.
 4290    cx.set_state(indoc! {"
 4291        1«2ˇ»3
 4292        4ˇ567
 4293        «8ˇ»9"});
 4294    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4295    cx.assert_editor_state(indoc! {"
 4296        1ˇ3
 4297        ˇ9"});
 4298
 4299    // Paste with three selections, noticing how the copied selection that was full-line
 4300    // gets inserted before the second cursor.
 4301    cx.set_state(indoc! {"
 4302        1ˇ3
 4303 4304        «oˇ»ne"});
 4305    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4306    cx.assert_editor_state(indoc! {"
 4307        12ˇ3
 4308        4567
 4309 4310        8ˇne"});
 4311
 4312    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4313    cx.set_state(indoc! {"
 4314        The quick brown
 4315        fox juˇmps over
 4316        the lazy dog"});
 4317    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4318    assert_eq!(
 4319        cx.read_from_clipboard()
 4320            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4321        Some("fox jumps over\n".to_string())
 4322    );
 4323
 4324    // Paste with three selections, noticing how the copied full-line selection is inserted
 4325    // before the empty selections but replaces the selection that is non-empty.
 4326    cx.set_state(indoc! {"
 4327        Tˇhe quick brown
 4328        «foˇ»x jumps over
 4329        tˇhe lazy dog"});
 4330    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4331    cx.assert_editor_state(indoc! {"
 4332        fox jumps over
 4333        Tˇhe quick brown
 4334        fox jumps over
 4335        ˇx jumps over
 4336        fox jumps over
 4337        tˇhe lazy dog"});
 4338}
 4339
 4340#[gpui::test]
 4341async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4342    init_test(cx, |_| {});
 4343
 4344    let mut cx = EditorTestContext::new(cx).await;
 4345    let language = Arc::new(Language::new(
 4346        LanguageConfig::default(),
 4347        Some(tree_sitter_rust::LANGUAGE.into()),
 4348    ));
 4349    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4350
 4351    // Cut an indented block, without the leading whitespace.
 4352    cx.set_state(indoc! {"
 4353        const a: B = (
 4354            c(),
 4355            «d(
 4356                e,
 4357                f
 4358            )ˇ»
 4359        );
 4360    "});
 4361    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4362    cx.assert_editor_state(indoc! {"
 4363        const a: B = (
 4364            c(),
 4365            ˇ
 4366        );
 4367    "});
 4368
 4369    // Paste it at the same position.
 4370    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4371    cx.assert_editor_state(indoc! {"
 4372        const a: B = (
 4373            c(),
 4374            d(
 4375                e,
 4376                f
 4377 4378        );
 4379    "});
 4380
 4381    // Paste it at a line with a lower indent level.
 4382    cx.set_state(indoc! {"
 4383        ˇ
 4384        const a: B = (
 4385            c(),
 4386        );
 4387    "});
 4388    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4389    cx.assert_editor_state(indoc! {"
 4390        d(
 4391            e,
 4392            f
 4393 4394        const a: B = (
 4395            c(),
 4396        );
 4397    "});
 4398
 4399    // Cut an indented block, with the leading whitespace.
 4400    cx.set_state(indoc! {"
 4401        const a: B = (
 4402            c(),
 4403        «    d(
 4404                e,
 4405                f
 4406            )
 4407        ˇ»);
 4408    "});
 4409    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4410    cx.assert_editor_state(indoc! {"
 4411        const a: B = (
 4412            c(),
 4413        ˇ);
 4414    "});
 4415
 4416    // Paste it at the same position.
 4417    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4418    cx.assert_editor_state(indoc! {"
 4419        const a: B = (
 4420            c(),
 4421            d(
 4422                e,
 4423                f
 4424            )
 4425        ˇ);
 4426    "});
 4427
 4428    // Paste it at a line with a higher indent level.
 4429    cx.set_state(indoc! {"
 4430        const a: B = (
 4431            c(),
 4432            d(
 4433                e,
 4434 4435            )
 4436        );
 4437    "});
 4438    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4439    cx.assert_editor_state(indoc! {"
 4440        const a: B = (
 4441            c(),
 4442            d(
 4443                e,
 4444                f    d(
 4445                    e,
 4446                    f
 4447                )
 4448        ˇ
 4449            )
 4450        );
 4451    "});
 4452}
 4453
 4454#[gpui::test]
 4455fn test_select_all(cx: &mut TestAppContext) {
 4456    init_test(cx, |_| {});
 4457
 4458    let view = cx.add_window(|cx| {
 4459        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4460        build_editor(buffer, cx)
 4461    });
 4462    _ = view.update(cx, |view, cx| {
 4463        view.select_all(&SelectAll, cx);
 4464        assert_eq!(
 4465            view.selections.display_ranges(cx),
 4466            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4467        );
 4468    });
 4469}
 4470
 4471#[gpui::test]
 4472fn test_select_line(cx: &mut TestAppContext) {
 4473    init_test(cx, |_| {});
 4474
 4475    let view = cx.add_window(|cx| {
 4476        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4477        build_editor(buffer, cx)
 4478    });
 4479    _ = view.update(cx, |view, cx| {
 4480        view.change_selections(None, cx, |s| {
 4481            s.select_display_ranges([
 4482                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4483                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4484                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4485                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4486            ])
 4487        });
 4488        view.select_line(&SelectLine, cx);
 4489        assert_eq!(
 4490            view.selections.display_ranges(cx),
 4491            vec![
 4492                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4493                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4494            ]
 4495        );
 4496    });
 4497
 4498    _ = view.update(cx, |view, cx| {
 4499        view.select_line(&SelectLine, cx);
 4500        assert_eq!(
 4501            view.selections.display_ranges(cx),
 4502            vec![
 4503                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4504                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4505            ]
 4506        );
 4507    });
 4508
 4509    _ = view.update(cx, |view, cx| {
 4510        view.select_line(&SelectLine, cx);
 4511        assert_eq!(
 4512            view.selections.display_ranges(cx),
 4513            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4514        );
 4515    });
 4516}
 4517
 4518#[gpui::test]
 4519fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4520    init_test(cx, |_| {});
 4521
 4522    let view = cx.add_window(|cx| {
 4523        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4524        build_editor(buffer, cx)
 4525    });
 4526    _ = view.update(cx, |view, cx| {
 4527        view.fold_ranges(
 4528            vec![
 4529                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4530                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4531                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4532            ],
 4533            true,
 4534            cx,
 4535        );
 4536        view.change_selections(None, cx, |s| {
 4537            s.select_display_ranges([
 4538                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4539                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4540                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4541                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4542            ])
 4543        });
 4544        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4545    });
 4546
 4547    _ = view.update(cx, |view, cx| {
 4548        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4549        assert_eq!(
 4550            view.display_text(cx),
 4551            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4552        );
 4553        assert_eq!(
 4554            view.selections.display_ranges(cx),
 4555            [
 4556                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4557                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4558                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4559                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4560            ]
 4561        );
 4562    });
 4563
 4564    _ = view.update(cx, |view, cx| {
 4565        view.change_selections(None, cx, |s| {
 4566            s.select_display_ranges([
 4567                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4568            ])
 4569        });
 4570        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4571        assert_eq!(
 4572            view.display_text(cx),
 4573            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4574        );
 4575        assert_eq!(
 4576            view.selections.display_ranges(cx),
 4577            [
 4578                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4579                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4580                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4581                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4582                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4583                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4584                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4585                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4586            ]
 4587        );
 4588    });
 4589}
 4590
 4591#[gpui::test]
 4592async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4593    init_test(cx, |_| {});
 4594
 4595    let mut cx = EditorTestContext::new(cx).await;
 4596
 4597    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4598    cx.set_state(indoc!(
 4599        r#"abc
 4600           defˇghi
 4601
 4602           jk
 4603           nlmo
 4604           "#
 4605    ));
 4606
 4607    cx.update_editor(|editor, cx| {
 4608        editor.add_selection_above(&Default::default(), cx);
 4609    });
 4610
 4611    cx.assert_editor_state(indoc!(
 4612        r#"abcˇ
 4613           defˇghi
 4614
 4615           jk
 4616           nlmo
 4617           "#
 4618    ));
 4619
 4620    cx.update_editor(|editor, cx| {
 4621        editor.add_selection_above(&Default::default(), cx);
 4622    });
 4623
 4624    cx.assert_editor_state(indoc!(
 4625        r#"abcˇ
 4626            defˇghi
 4627
 4628            jk
 4629            nlmo
 4630            "#
 4631    ));
 4632
 4633    cx.update_editor(|view, cx| {
 4634        view.add_selection_below(&Default::default(), cx);
 4635    });
 4636
 4637    cx.assert_editor_state(indoc!(
 4638        r#"abc
 4639           defˇghi
 4640
 4641           jk
 4642           nlmo
 4643           "#
 4644    ));
 4645
 4646    cx.update_editor(|view, cx| {
 4647        view.undo_selection(&Default::default(), cx);
 4648    });
 4649
 4650    cx.assert_editor_state(indoc!(
 4651        r#"abcˇ
 4652           defˇghi
 4653
 4654           jk
 4655           nlmo
 4656           "#
 4657    ));
 4658
 4659    cx.update_editor(|view, cx| {
 4660        view.redo_selection(&Default::default(), cx);
 4661    });
 4662
 4663    cx.assert_editor_state(indoc!(
 4664        r#"abc
 4665           defˇghi
 4666
 4667           jk
 4668           nlmo
 4669           "#
 4670    ));
 4671
 4672    cx.update_editor(|view, cx| {
 4673        view.add_selection_below(&Default::default(), cx);
 4674    });
 4675
 4676    cx.assert_editor_state(indoc!(
 4677        r#"abc
 4678           defˇghi
 4679
 4680           jk
 4681           nlmˇo
 4682           "#
 4683    ));
 4684
 4685    cx.update_editor(|view, cx| {
 4686        view.add_selection_below(&Default::default(), cx);
 4687    });
 4688
 4689    cx.assert_editor_state(indoc!(
 4690        r#"abc
 4691           defˇghi
 4692
 4693           jk
 4694           nlmˇo
 4695           "#
 4696    ));
 4697
 4698    // change selections
 4699    cx.set_state(indoc!(
 4700        r#"abc
 4701           def«ˇg»hi
 4702
 4703           jk
 4704           nlmo
 4705           "#
 4706    ));
 4707
 4708    cx.update_editor(|view, cx| {
 4709        view.add_selection_below(&Default::default(), cx);
 4710    });
 4711
 4712    cx.assert_editor_state(indoc!(
 4713        r#"abc
 4714           def«ˇg»hi
 4715
 4716           jk
 4717           nlm«ˇo»
 4718           "#
 4719    ));
 4720
 4721    cx.update_editor(|view, cx| {
 4722        view.add_selection_below(&Default::default(), cx);
 4723    });
 4724
 4725    cx.assert_editor_state(indoc!(
 4726        r#"abc
 4727           def«ˇg»hi
 4728
 4729           jk
 4730           nlm«ˇo»
 4731           "#
 4732    ));
 4733
 4734    cx.update_editor(|view, cx| {
 4735        view.add_selection_above(&Default::default(), cx);
 4736    });
 4737
 4738    cx.assert_editor_state(indoc!(
 4739        r#"abc
 4740           def«ˇg»hi
 4741
 4742           jk
 4743           nlmo
 4744           "#
 4745    ));
 4746
 4747    cx.update_editor(|view, cx| {
 4748        view.add_selection_above(&Default::default(), cx);
 4749    });
 4750
 4751    cx.assert_editor_state(indoc!(
 4752        r#"abc
 4753           def«ˇg»hi
 4754
 4755           jk
 4756           nlmo
 4757           "#
 4758    ));
 4759
 4760    // Change selections again
 4761    cx.set_state(indoc!(
 4762        r#"a«bc
 4763           defgˇ»hi
 4764
 4765           jk
 4766           nlmo
 4767           "#
 4768    ));
 4769
 4770    cx.update_editor(|view, cx| {
 4771        view.add_selection_below(&Default::default(), cx);
 4772    });
 4773
 4774    cx.assert_editor_state(indoc!(
 4775        r#"a«bcˇ»
 4776           d«efgˇ»hi
 4777
 4778           j«kˇ»
 4779           nlmo
 4780           "#
 4781    ));
 4782
 4783    cx.update_editor(|view, cx| {
 4784        view.add_selection_below(&Default::default(), cx);
 4785    });
 4786    cx.assert_editor_state(indoc!(
 4787        r#"a«bcˇ»
 4788           d«efgˇ»hi
 4789
 4790           j«kˇ»
 4791           n«lmoˇ»
 4792           "#
 4793    ));
 4794    cx.update_editor(|view, cx| {
 4795        view.add_selection_above(&Default::default(), cx);
 4796    });
 4797
 4798    cx.assert_editor_state(indoc!(
 4799        r#"a«bcˇ»
 4800           d«efgˇ»hi
 4801
 4802           j«kˇ»
 4803           nlmo
 4804           "#
 4805    ));
 4806
 4807    // Change selections again
 4808    cx.set_state(indoc!(
 4809        r#"abc
 4810           d«ˇefghi
 4811
 4812           jk
 4813           nlm»o
 4814           "#
 4815    ));
 4816
 4817    cx.update_editor(|view, cx| {
 4818        view.add_selection_above(&Default::default(), cx);
 4819    });
 4820
 4821    cx.assert_editor_state(indoc!(
 4822        r#"a«ˇbc»
 4823           d«ˇef»ghi
 4824
 4825           j«ˇk»
 4826           n«ˇlm»o
 4827           "#
 4828    ));
 4829
 4830    cx.update_editor(|view, cx| {
 4831        view.add_selection_below(&Default::default(), cx);
 4832    });
 4833
 4834    cx.assert_editor_state(indoc!(
 4835        r#"abc
 4836           d«ˇef»ghi
 4837
 4838           j«ˇk»
 4839           n«ˇlm»o
 4840           "#
 4841    ));
 4842}
 4843
 4844#[gpui::test]
 4845async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4846    init_test(cx, |_| {});
 4847
 4848    let mut cx = EditorTestContext::new(cx).await;
 4849    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4850
 4851    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4852        .unwrap();
 4853    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4854
 4855    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4856        .unwrap();
 4857    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4858
 4859    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4860    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4861
 4862    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4863    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4864
 4865    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4866        .unwrap();
 4867    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4868
 4869    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4870        .unwrap();
 4871    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4872}
 4873
 4874#[gpui::test]
 4875async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4876    init_test(cx, |_| {});
 4877
 4878    let mut cx = EditorTestContext::new(cx).await;
 4879    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4880
 4881    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4882        .unwrap();
 4883    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4884}
 4885
 4886#[gpui::test]
 4887async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4888    init_test(cx, |_| {});
 4889
 4890    let mut cx = EditorTestContext::new(cx).await;
 4891    cx.set_state(
 4892        r#"let foo = 2;
 4893lˇet foo = 2;
 4894let fooˇ = 2;
 4895let foo = 2;
 4896let foo = ˇ2;"#,
 4897    );
 4898
 4899    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4900        .unwrap();
 4901    cx.assert_editor_state(
 4902        r#"let foo = 2;
 4903«letˇ» foo = 2;
 4904let «fooˇ» = 2;
 4905let foo = 2;
 4906let foo = «2ˇ»;"#,
 4907    );
 4908
 4909    // noop for multiple selections with different contents
 4910    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4911        .unwrap();
 4912    cx.assert_editor_state(
 4913        r#"let foo = 2;
 4914«letˇ» foo = 2;
 4915let «fooˇ» = 2;
 4916let foo = 2;
 4917let foo = «2ˇ»;"#,
 4918    );
 4919}
 4920
 4921#[gpui::test]
 4922async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4923    init_test(cx, |_| {});
 4924
 4925    let mut cx =
 4926        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 4927
 4928    cx.assert_editor_state(indoc! {"
 4929        ˇbbb
 4930        ccc
 4931
 4932        bbb
 4933        ccc
 4934        "});
 4935    cx.dispatch_action(SelectPrevious::default());
 4936    cx.assert_editor_state(indoc! {"
 4937                «bbbˇ»
 4938                ccc
 4939
 4940                bbb
 4941                ccc
 4942                "});
 4943    cx.dispatch_action(SelectPrevious::default());
 4944    cx.assert_editor_state(indoc! {"
 4945                «bbbˇ»
 4946                ccc
 4947
 4948                «bbbˇ»
 4949                ccc
 4950                "});
 4951}
 4952
 4953#[gpui::test]
 4954async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 4955    init_test(cx, |_| {});
 4956
 4957    let mut cx = EditorTestContext::new(cx).await;
 4958    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4959
 4960    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4961        .unwrap();
 4962    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4963
 4964    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4965        .unwrap();
 4966    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4967
 4968    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4969    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4970
 4971    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4972    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4973
 4974    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4975        .unwrap();
 4976    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 4977
 4978    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4979        .unwrap();
 4980    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 4981
 4982    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4983        .unwrap();
 4984    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4985}
 4986
 4987#[gpui::test]
 4988async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4989    init_test(cx, |_| {});
 4990
 4991    let mut cx = EditorTestContext::new(cx).await;
 4992    cx.set_state(
 4993        r#"let foo = 2;
 4994lˇet foo = 2;
 4995let fooˇ = 2;
 4996let foo = 2;
 4997let foo = ˇ2;"#,
 4998    );
 4999
 5000    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5001        .unwrap();
 5002    cx.assert_editor_state(
 5003        r#"let foo = 2;
 5004«letˇ» foo = 2;
 5005let «fooˇ» = 2;
 5006let foo = 2;
 5007let foo = «2ˇ»;"#,
 5008    );
 5009
 5010    // noop for multiple selections with different contents
 5011    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5012        .unwrap();
 5013    cx.assert_editor_state(
 5014        r#"let foo = 2;
 5015«letˇ» foo = 2;
 5016let «fooˇ» = 2;
 5017let foo = 2;
 5018let foo = «2ˇ»;"#,
 5019    );
 5020}
 5021
 5022#[gpui::test]
 5023async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5024    init_test(cx, |_| {});
 5025
 5026    let mut cx = EditorTestContext::new(cx).await;
 5027    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5028
 5029    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5030        .unwrap();
 5031    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5032
 5033    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5034        .unwrap();
 5035    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5036
 5037    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5038    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5039
 5040    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5041    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5042
 5043    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5044        .unwrap();
 5045    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5046
 5047    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5048        .unwrap();
 5049    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5050}
 5051
 5052#[gpui::test]
 5053async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5054    init_test(cx, |_| {});
 5055
 5056    let language = Arc::new(Language::new(
 5057        LanguageConfig::default(),
 5058        Some(tree_sitter_rust::LANGUAGE.into()),
 5059    ));
 5060
 5061    let text = r#"
 5062        use mod1::mod2::{mod3, mod4};
 5063
 5064        fn fn_1(param1: bool, param2: &str) {
 5065            let var1 = "text";
 5066        }
 5067    "#
 5068    .unindent();
 5069
 5070    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5071    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5072    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5073
 5074    editor
 5075        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5076        .await;
 5077
 5078    editor.update(cx, |view, cx| {
 5079        view.change_selections(None, cx, |s| {
 5080            s.select_display_ranges([
 5081                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5082                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5083                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5084            ]);
 5085        });
 5086        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5087    });
 5088    editor.update(cx, |editor, cx| {
 5089        assert_text_with_selections(
 5090            editor,
 5091            indoc! {r#"
 5092                use mod1::mod2::{mod3, «mod4ˇ»};
 5093
 5094                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5095                    let var1 = "«textˇ»";
 5096                }
 5097            "#},
 5098            cx,
 5099        );
 5100    });
 5101
 5102    editor.update(cx, |view, cx| {
 5103        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5104    });
 5105    editor.update(cx, |editor, cx| {
 5106        assert_text_with_selections(
 5107            editor,
 5108            indoc! {r#"
 5109                use mod1::mod2::«{mod3, mod4}ˇ»;
 5110
 5111                «ˇfn fn_1(param1: bool, param2: &str) {
 5112                    let var1 = "text";
 5113 5114            "#},
 5115            cx,
 5116        );
 5117    });
 5118
 5119    editor.update(cx, |view, cx| {
 5120        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5121    });
 5122    assert_eq!(
 5123        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5124        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5125    );
 5126
 5127    // Trying to expand the selected syntax node one more time has no effect.
 5128    editor.update(cx, |view, cx| {
 5129        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5130    });
 5131    assert_eq!(
 5132        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5133        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5134    );
 5135
 5136    editor.update(cx, |view, cx| {
 5137        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5138    });
 5139    editor.update(cx, |editor, cx| {
 5140        assert_text_with_selections(
 5141            editor,
 5142            indoc! {r#"
 5143                use mod1::mod2::«{mod3, mod4}ˇ»;
 5144
 5145                «ˇfn fn_1(param1: bool, param2: &str) {
 5146                    let var1 = "text";
 5147 5148            "#},
 5149            cx,
 5150        );
 5151    });
 5152
 5153    editor.update(cx, |view, cx| {
 5154        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5155    });
 5156    editor.update(cx, |editor, cx| {
 5157        assert_text_with_selections(
 5158            editor,
 5159            indoc! {r#"
 5160                use mod1::mod2::{mod3, «mod4ˇ»};
 5161
 5162                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5163                    let var1 = "«textˇ»";
 5164                }
 5165            "#},
 5166            cx,
 5167        );
 5168    });
 5169
 5170    editor.update(cx, |view, cx| {
 5171        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5172    });
 5173    editor.update(cx, |editor, cx| {
 5174        assert_text_with_selections(
 5175            editor,
 5176            indoc! {r#"
 5177                use mod1::mod2::{mod3, mo«ˇ»d4};
 5178
 5179                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5180                    let var1 = "te«ˇ»xt";
 5181                }
 5182            "#},
 5183            cx,
 5184        );
 5185    });
 5186
 5187    // Trying to shrink the selected syntax node one more time has no effect.
 5188    editor.update(cx, |view, cx| {
 5189        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5190    });
 5191    editor.update(cx, |editor, cx| {
 5192        assert_text_with_selections(
 5193            editor,
 5194            indoc! {r#"
 5195                use mod1::mod2::{mod3, mo«ˇ»d4};
 5196
 5197                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5198                    let var1 = "te«ˇ»xt";
 5199                }
 5200            "#},
 5201            cx,
 5202        );
 5203    });
 5204
 5205    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5206    // a fold.
 5207    editor.update(cx, |view, cx| {
 5208        view.fold_ranges(
 5209            vec![
 5210                (
 5211                    Point::new(0, 21)..Point::new(0, 24),
 5212                    FoldPlaceholder::test(),
 5213                ),
 5214                (
 5215                    Point::new(3, 20)..Point::new(3, 22),
 5216                    FoldPlaceholder::test(),
 5217                ),
 5218            ],
 5219            true,
 5220            cx,
 5221        );
 5222        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5223    });
 5224    editor.update(cx, |editor, cx| {
 5225        assert_text_with_selections(
 5226            editor,
 5227            indoc! {r#"
 5228                use mod1::mod2::«{mod3, mod4}ˇ»;
 5229
 5230                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5231                    «let var1 = "text";ˇ»
 5232                }
 5233            "#},
 5234            cx,
 5235        );
 5236    });
 5237}
 5238
 5239#[gpui::test]
 5240async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5241    init_test(cx, |_| {});
 5242
 5243    let language = Arc::new(
 5244        Language::new(
 5245            LanguageConfig {
 5246                brackets: BracketPairConfig {
 5247                    pairs: vec![
 5248                        BracketPair {
 5249                            start: "{".to_string(),
 5250                            end: "}".to_string(),
 5251                            close: false,
 5252                            surround: false,
 5253                            newline: true,
 5254                        },
 5255                        BracketPair {
 5256                            start: "(".to_string(),
 5257                            end: ")".to_string(),
 5258                            close: false,
 5259                            surround: false,
 5260                            newline: true,
 5261                        },
 5262                    ],
 5263                    ..Default::default()
 5264                },
 5265                ..Default::default()
 5266            },
 5267            Some(tree_sitter_rust::LANGUAGE.into()),
 5268        )
 5269        .with_indents_query(
 5270            r#"
 5271                (_ "(" ")" @end) @indent
 5272                (_ "{" "}" @end) @indent
 5273            "#,
 5274        )
 5275        .unwrap(),
 5276    );
 5277
 5278    let text = "fn a() {}";
 5279
 5280    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5281    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5282    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5283    editor
 5284        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5285        .await;
 5286
 5287    editor.update(cx, |editor, cx| {
 5288        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5289        editor.newline(&Newline, cx);
 5290        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5291        assert_eq!(
 5292            editor.selections.ranges(cx),
 5293            &[
 5294                Point::new(1, 4)..Point::new(1, 4),
 5295                Point::new(3, 4)..Point::new(3, 4),
 5296                Point::new(5, 0)..Point::new(5, 0)
 5297            ]
 5298        );
 5299    });
 5300}
 5301
 5302#[gpui::test]
 5303async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5304    init_test(cx, |_| {});
 5305
 5306    let mut cx = EditorTestContext::new(cx).await;
 5307
 5308    let language = Arc::new(Language::new(
 5309        LanguageConfig {
 5310            brackets: BracketPairConfig {
 5311                pairs: vec![
 5312                    BracketPair {
 5313                        start: "{".to_string(),
 5314                        end: "}".to_string(),
 5315                        close: true,
 5316                        surround: true,
 5317                        newline: true,
 5318                    },
 5319                    BracketPair {
 5320                        start: "(".to_string(),
 5321                        end: ")".to_string(),
 5322                        close: true,
 5323                        surround: true,
 5324                        newline: true,
 5325                    },
 5326                    BracketPair {
 5327                        start: "/*".to_string(),
 5328                        end: " */".to_string(),
 5329                        close: true,
 5330                        surround: true,
 5331                        newline: true,
 5332                    },
 5333                    BracketPair {
 5334                        start: "[".to_string(),
 5335                        end: "]".to_string(),
 5336                        close: false,
 5337                        surround: false,
 5338                        newline: true,
 5339                    },
 5340                    BracketPair {
 5341                        start: "\"".to_string(),
 5342                        end: "\"".to_string(),
 5343                        close: true,
 5344                        surround: true,
 5345                        newline: false,
 5346                    },
 5347                    BracketPair {
 5348                        start: "<".to_string(),
 5349                        end: ">".to_string(),
 5350                        close: false,
 5351                        surround: true,
 5352                        newline: true,
 5353                    },
 5354                ],
 5355                ..Default::default()
 5356            },
 5357            autoclose_before: "})]".to_string(),
 5358            ..Default::default()
 5359        },
 5360        Some(tree_sitter_rust::LANGUAGE.into()),
 5361    ));
 5362
 5363    cx.language_registry().add(language.clone());
 5364    cx.update_buffer(|buffer, cx| {
 5365        buffer.set_language(Some(language), cx);
 5366    });
 5367
 5368    cx.set_state(
 5369        &r#"
 5370            🏀ˇ
 5371            εˇ
 5372            ❤️ˇ
 5373        "#
 5374        .unindent(),
 5375    );
 5376
 5377    // autoclose multiple nested brackets at multiple cursors
 5378    cx.update_editor(|view, cx| {
 5379        view.handle_input("{", cx);
 5380        view.handle_input("{", cx);
 5381        view.handle_input("{", cx);
 5382    });
 5383    cx.assert_editor_state(
 5384        &"
 5385            🏀{{{ˇ}}}
 5386            ε{{{ˇ}}}
 5387            ❤️{{{ˇ}}}
 5388        "
 5389        .unindent(),
 5390    );
 5391
 5392    // insert a different closing bracket
 5393    cx.update_editor(|view, cx| {
 5394        view.handle_input(")", cx);
 5395    });
 5396    cx.assert_editor_state(
 5397        &"
 5398            🏀{{{)ˇ}}}
 5399            ε{{{)ˇ}}}
 5400            ❤️{{{)ˇ}}}
 5401        "
 5402        .unindent(),
 5403    );
 5404
 5405    // skip over the auto-closed brackets when typing a closing bracket
 5406    cx.update_editor(|view, cx| {
 5407        view.move_right(&MoveRight, cx);
 5408        view.handle_input("}", cx);
 5409        view.handle_input("}", cx);
 5410        view.handle_input("}", cx);
 5411    });
 5412    cx.assert_editor_state(
 5413        &"
 5414            🏀{{{)}}}}ˇ
 5415            ε{{{)}}}}ˇ
 5416            ❤️{{{)}}}}ˇ
 5417        "
 5418        .unindent(),
 5419    );
 5420
 5421    // autoclose multi-character pairs
 5422    cx.set_state(
 5423        &"
 5424            ˇ
 5425            ˇ
 5426        "
 5427        .unindent(),
 5428    );
 5429    cx.update_editor(|view, cx| {
 5430        view.handle_input("/", cx);
 5431        view.handle_input("*", cx);
 5432    });
 5433    cx.assert_editor_state(
 5434        &"
 5435            /*ˇ */
 5436            /*ˇ */
 5437        "
 5438        .unindent(),
 5439    );
 5440
 5441    // one cursor autocloses a multi-character pair, one cursor
 5442    // does not autoclose.
 5443    cx.set_state(
 5444        &"
 5445 5446            ˇ
 5447        "
 5448        .unindent(),
 5449    );
 5450    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5451    cx.assert_editor_state(
 5452        &"
 5453            /*ˇ */
 5454 5455        "
 5456        .unindent(),
 5457    );
 5458
 5459    // Don't autoclose if the next character isn't whitespace and isn't
 5460    // listed in the language's "autoclose_before" section.
 5461    cx.set_state("ˇa b");
 5462    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5463    cx.assert_editor_state("{ˇa b");
 5464
 5465    // Don't autoclose if `close` is false for the bracket pair
 5466    cx.set_state("ˇ");
 5467    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5468    cx.assert_editor_state("");
 5469
 5470    // Surround with brackets if text is selected
 5471    cx.set_state("«aˇ» b");
 5472    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5473    cx.assert_editor_state("{«aˇ»} b");
 5474
 5475    // Autclose pair where the start and end characters are the same
 5476    cx.set_state("");
 5477    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5478    cx.assert_editor_state("a\"ˇ\"");
 5479    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5480    cx.assert_editor_state("a\"\"ˇ");
 5481
 5482    // Don't autoclose pair if autoclose is disabled
 5483    cx.set_state("ˇ");
 5484    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5485    cx.assert_editor_state("");
 5486
 5487    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5488    cx.set_state("«aˇ» b");
 5489    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5490    cx.assert_editor_state("<«aˇ»> b");
 5491}
 5492
 5493#[gpui::test]
 5494async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5495    init_test(cx, |settings| {
 5496        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5497    });
 5498
 5499    let mut cx = EditorTestContext::new(cx).await;
 5500
 5501    let language = Arc::new(Language::new(
 5502        LanguageConfig {
 5503            brackets: BracketPairConfig {
 5504                pairs: vec![
 5505                    BracketPair {
 5506                        start: "{".to_string(),
 5507                        end: "}".to_string(),
 5508                        close: true,
 5509                        surround: true,
 5510                        newline: true,
 5511                    },
 5512                    BracketPair {
 5513                        start: "(".to_string(),
 5514                        end: ")".to_string(),
 5515                        close: true,
 5516                        surround: true,
 5517                        newline: true,
 5518                    },
 5519                    BracketPair {
 5520                        start: "[".to_string(),
 5521                        end: "]".to_string(),
 5522                        close: false,
 5523                        surround: false,
 5524                        newline: true,
 5525                    },
 5526                ],
 5527                ..Default::default()
 5528            },
 5529            autoclose_before: "})]".to_string(),
 5530            ..Default::default()
 5531        },
 5532        Some(tree_sitter_rust::LANGUAGE.into()),
 5533    ));
 5534
 5535    cx.language_registry().add(language.clone());
 5536    cx.update_buffer(|buffer, cx| {
 5537        buffer.set_language(Some(language), cx);
 5538    });
 5539
 5540    cx.set_state(
 5541        &"
 5542            ˇ
 5543            ˇ
 5544            ˇ
 5545        "
 5546        .unindent(),
 5547    );
 5548
 5549    // ensure only matching closing brackets are skipped over
 5550    cx.update_editor(|view, cx| {
 5551        view.handle_input("}", cx);
 5552        view.move_left(&MoveLeft, cx);
 5553        view.handle_input(")", cx);
 5554        view.move_left(&MoveLeft, cx);
 5555    });
 5556    cx.assert_editor_state(
 5557        &"
 5558            ˇ)}
 5559            ˇ)}
 5560            ˇ)}
 5561        "
 5562        .unindent(),
 5563    );
 5564
 5565    // skip-over closing brackets at multiple cursors
 5566    cx.update_editor(|view, cx| {
 5567        view.handle_input(")", cx);
 5568        view.handle_input("}", cx);
 5569    });
 5570    cx.assert_editor_state(
 5571        &"
 5572            )}ˇ
 5573            )}ˇ
 5574            )}ˇ
 5575        "
 5576        .unindent(),
 5577    );
 5578
 5579    // ignore non-close brackets
 5580    cx.update_editor(|view, cx| {
 5581        view.handle_input("]", cx);
 5582        view.move_left(&MoveLeft, cx);
 5583        view.handle_input("]", cx);
 5584    });
 5585    cx.assert_editor_state(
 5586        &"
 5587            )}]ˇ]
 5588            )}]ˇ]
 5589            )}]ˇ]
 5590        "
 5591        .unindent(),
 5592    );
 5593}
 5594
 5595#[gpui::test]
 5596async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5597    init_test(cx, |_| {});
 5598
 5599    let mut cx = EditorTestContext::new(cx).await;
 5600
 5601    let html_language = Arc::new(
 5602        Language::new(
 5603            LanguageConfig {
 5604                name: "HTML".into(),
 5605                brackets: BracketPairConfig {
 5606                    pairs: vec![
 5607                        BracketPair {
 5608                            start: "<".into(),
 5609                            end: ">".into(),
 5610                            close: true,
 5611                            ..Default::default()
 5612                        },
 5613                        BracketPair {
 5614                            start: "{".into(),
 5615                            end: "}".into(),
 5616                            close: true,
 5617                            ..Default::default()
 5618                        },
 5619                        BracketPair {
 5620                            start: "(".into(),
 5621                            end: ")".into(),
 5622                            close: true,
 5623                            ..Default::default()
 5624                        },
 5625                    ],
 5626                    ..Default::default()
 5627                },
 5628                autoclose_before: "})]>".into(),
 5629                ..Default::default()
 5630            },
 5631            Some(tree_sitter_html::language()),
 5632        )
 5633        .with_injection_query(
 5634            r#"
 5635            (script_element
 5636                (raw_text) @content
 5637                (#set! "language" "javascript"))
 5638            "#,
 5639        )
 5640        .unwrap(),
 5641    );
 5642
 5643    let javascript_language = Arc::new(Language::new(
 5644        LanguageConfig {
 5645            name: "JavaScript".into(),
 5646            brackets: BracketPairConfig {
 5647                pairs: vec![
 5648                    BracketPair {
 5649                        start: "/*".into(),
 5650                        end: " */".into(),
 5651                        close: true,
 5652                        ..Default::default()
 5653                    },
 5654                    BracketPair {
 5655                        start: "{".into(),
 5656                        end: "}".into(),
 5657                        close: true,
 5658                        ..Default::default()
 5659                    },
 5660                    BracketPair {
 5661                        start: "(".into(),
 5662                        end: ")".into(),
 5663                        close: true,
 5664                        ..Default::default()
 5665                    },
 5666                ],
 5667                ..Default::default()
 5668            },
 5669            autoclose_before: "})]>".into(),
 5670            ..Default::default()
 5671        },
 5672        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5673    ));
 5674
 5675    cx.language_registry().add(html_language.clone());
 5676    cx.language_registry().add(javascript_language.clone());
 5677
 5678    cx.update_buffer(|buffer, cx| {
 5679        buffer.set_language(Some(html_language), cx);
 5680    });
 5681
 5682    cx.set_state(
 5683        &r#"
 5684            <body>ˇ
 5685                <script>
 5686                    var x = 1;ˇ
 5687                </script>
 5688            </body>ˇ
 5689        "#
 5690        .unindent(),
 5691    );
 5692
 5693    // Precondition: different languages are active at different locations.
 5694    cx.update_editor(|editor, cx| {
 5695        let snapshot = editor.snapshot(cx);
 5696        let cursors = editor.selections.ranges::<usize>(cx);
 5697        let languages = cursors
 5698            .iter()
 5699            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5700            .collect::<Vec<_>>();
 5701        assert_eq!(
 5702            languages,
 5703            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5704        );
 5705    });
 5706
 5707    // Angle brackets autoclose in HTML, but not JavaScript.
 5708    cx.update_editor(|editor, cx| {
 5709        editor.handle_input("<", cx);
 5710        editor.handle_input("a", cx);
 5711    });
 5712    cx.assert_editor_state(
 5713        &r#"
 5714            <body><aˇ>
 5715                <script>
 5716                    var x = 1;<aˇ
 5717                </script>
 5718            </body><aˇ>
 5719        "#
 5720        .unindent(),
 5721    );
 5722
 5723    // Curly braces and parens autoclose in both HTML and JavaScript.
 5724    cx.update_editor(|editor, cx| {
 5725        editor.handle_input(" b=", cx);
 5726        editor.handle_input("{", cx);
 5727        editor.handle_input("c", cx);
 5728        editor.handle_input("(", cx);
 5729    });
 5730    cx.assert_editor_state(
 5731        &r#"
 5732            <body><a b={c(ˇ)}>
 5733                <script>
 5734                    var x = 1;<a b={c(ˇ)}
 5735                </script>
 5736            </body><a b={c(ˇ)}>
 5737        "#
 5738        .unindent(),
 5739    );
 5740
 5741    // Brackets that were already autoclosed are skipped.
 5742    cx.update_editor(|editor, cx| {
 5743        editor.handle_input(")", cx);
 5744        editor.handle_input("d", cx);
 5745        editor.handle_input("}", cx);
 5746    });
 5747    cx.assert_editor_state(
 5748        &r#"
 5749            <body><a b={c()d}ˇ>
 5750                <script>
 5751                    var x = 1;<a b={c()d}ˇ
 5752                </script>
 5753            </body><a b={c()d}ˇ>
 5754        "#
 5755        .unindent(),
 5756    );
 5757    cx.update_editor(|editor, cx| {
 5758        editor.handle_input(">", cx);
 5759    });
 5760    cx.assert_editor_state(
 5761        &r#"
 5762            <body><a b={c()d}>ˇ
 5763                <script>
 5764                    var x = 1;<a b={c()d}>ˇ
 5765                </script>
 5766            </body><a b={c()d}>ˇ
 5767        "#
 5768        .unindent(),
 5769    );
 5770
 5771    // Reset
 5772    cx.set_state(
 5773        &r#"
 5774            <body>ˇ
 5775                <script>
 5776                    var x = 1;ˇ
 5777                </script>
 5778            </body>ˇ
 5779        "#
 5780        .unindent(),
 5781    );
 5782
 5783    cx.update_editor(|editor, cx| {
 5784        editor.handle_input("<", cx);
 5785    });
 5786    cx.assert_editor_state(
 5787        &r#"
 5788            <body><ˇ>
 5789                <script>
 5790                    var x = 1;<ˇ
 5791                </script>
 5792            </body><ˇ>
 5793        "#
 5794        .unindent(),
 5795    );
 5796
 5797    // When backspacing, the closing angle brackets are removed.
 5798    cx.update_editor(|editor, cx| {
 5799        editor.backspace(&Backspace, cx);
 5800    });
 5801    cx.assert_editor_state(
 5802        &r#"
 5803            <body>ˇ
 5804                <script>
 5805                    var x = 1;ˇ
 5806                </script>
 5807            </body>ˇ
 5808        "#
 5809        .unindent(),
 5810    );
 5811
 5812    // Block comments autoclose in JavaScript, but not HTML.
 5813    cx.update_editor(|editor, cx| {
 5814        editor.handle_input("/", cx);
 5815        editor.handle_input("*", cx);
 5816    });
 5817    cx.assert_editor_state(
 5818        &r#"
 5819            <body>/*ˇ
 5820                <script>
 5821                    var x = 1;/*ˇ */
 5822                </script>
 5823            </body>/*ˇ
 5824        "#
 5825        .unindent(),
 5826    );
 5827}
 5828
 5829#[gpui::test]
 5830async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5831    init_test(cx, |_| {});
 5832
 5833    let mut cx = EditorTestContext::new(cx).await;
 5834
 5835    let rust_language = Arc::new(
 5836        Language::new(
 5837            LanguageConfig {
 5838                name: "Rust".into(),
 5839                brackets: serde_json::from_value(json!([
 5840                    { "start": "{", "end": "}", "close": true, "newline": true },
 5841                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5842                ]))
 5843                .unwrap(),
 5844                autoclose_before: "})]>".into(),
 5845                ..Default::default()
 5846            },
 5847            Some(tree_sitter_rust::LANGUAGE.into()),
 5848        )
 5849        .with_override_query("(string_literal) @string")
 5850        .unwrap(),
 5851    );
 5852
 5853    cx.language_registry().add(rust_language.clone());
 5854    cx.update_buffer(|buffer, cx| {
 5855        buffer.set_language(Some(rust_language), cx);
 5856    });
 5857
 5858    cx.set_state(
 5859        &r#"
 5860            let x = ˇ
 5861        "#
 5862        .unindent(),
 5863    );
 5864
 5865    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5866    cx.update_editor(|editor, cx| {
 5867        editor.handle_input("\"", cx);
 5868    });
 5869    cx.assert_editor_state(
 5870        &r#"
 5871            let x = "ˇ"
 5872        "#
 5873        .unindent(),
 5874    );
 5875
 5876    // Inserting another quotation mark. The cursor moves across the existing
 5877    // automatically-inserted quotation mark.
 5878    cx.update_editor(|editor, cx| {
 5879        editor.handle_input("\"", cx);
 5880    });
 5881    cx.assert_editor_state(
 5882        &r#"
 5883            let x = ""ˇ
 5884        "#
 5885        .unindent(),
 5886    );
 5887
 5888    // Reset
 5889    cx.set_state(
 5890        &r#"
 5891            let x = ˇ
 5892        "#
 5893        .unindent(),
 5894    );
 5895
 5896    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5897    cx.update_editor(|editor, cx| {
 5898        editor.handle_input("\"", cx);
 5899        editor.handle_input(" ", cx);
 5900        editor.move_left(&Default::default(), cx);
 5901        editor.handle_input("\\", cx);
 5902        editor.handle_input("\"", cx);
 5903    });
 5904    cx.assert_editor_state(
 5905        &r#"
 5906            let x = "\"ˇ "
 5907        "#
 5908        .unindent(),
 5909    );
 5910
 5911    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5912    // mark. Nothing is inserted.
 5913    cx.update_editor(|editor, cx| {
 5914        editor.move_right(&Default::default(), cx);
 5915        editor.handle_input("\"", cx);
 5916    });
 5917    cx.assert_editor_state(
 5918        &r#"
 5919            let x = "\" "ˇ
 5920        "#
 5921        .unindent(),
 5922    );
 5923}
 5924
 5925#[gpui::test]
 5926async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5927    init_test(cx, |_| {});
 5928
 5929    let language = Arc::new(Language::new(
 5930        LanguageConfig {
 5931            brackets: BracketPairConfig {
 5932                pairs: vec![
 5933                    BracketPair {
 5934                        start: "{".to_string(),
 5935                        end: "}".to_string(),
 5936                        close: true,
 5937                        surround: true,
 5938                        newline: true,
 5939                    },
 5940                    BracketPair {
 5941                        start: "/* ".to_string(),
 5942                        end: "*/".to_string(),
 5943                        close: true,
 5944                        surround: true,
 5945                        ..Default::default()
 5946                    },
 5947                ],
 5948                ..Default::default()
 5949            },
 5950            ..Default::default()
 5951        },
 5952        Some(tree_sitter_rust::LANGUAGE.into()),
 5953    ));
 5954
 5955    let text = r#"
 5956        a
 5957        b
 5958        c
 5959    "#
 5960    .unindent();
 5961
 5962    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5963    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5964    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5965    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5966        .await;
 5967
 5968    view.update(cx, |view, cx| {
 5969        view.change_selections(None, cx, |s| {
 5970            s.select_display_ranges([
 5971                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5972                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5973                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5974            ])
 5975        });
 5976
 5977        view.handle_input("{", cx);
 5978        view.handle_input("{", cx);
 5979        view.handle_input("{", cx);
 5980        assert_eq!(
 5981            view.text(cx),
 5982            "
 5983                {{{a}}}
 5984                {{{b}}}
 5985                {{{c}}}
 5986            "
 5987            .unindent()
 5988        );
 5989        assert_eq!(
 5990            view.selections.display_ranges(cx),
 5991            [
 5992                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5993                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5994                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5995            ]
 5996        );
 5997
 5998        view.undo(&Undo, cx);
 5999        view.undo(&Undo, cx);
 6000        view.undo(&Undo, cx);
 6001        assert_eq!(
 6002            view.text(cx),
 6003            "
 6004                a
 6005                b
 6006                c
 6007            "
 6008            .unindent()
 6009        );
 6010        assert_eq!(
 6011            view.selections.display_ranges(cx),
 6012            [
 6013                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6014                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6015                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6016            ]
 6017        );
 6018
 6019        // Ensure inserting the first character of a multi-byte bracket pair
 6020        // doesn't surround the selections with the bracket.
 6021        view.handle_input("/", cx);
 6022        assert_eq!(
 6023            view.text(cx),
 6024            "
 6025                /
 6026                /
 6027                /
 6028            "
 6029            .unindent()
 6030        );
 6031        assert_eq!(
 6032            view.selections.display_ranges(cx),
 6033            [
 6034                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6035                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6036                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6037            ]
 6038        );
 6039
 6040        view.undo(&Undo, cx);
 6041        assert_eq!(
 6042            view.text(cx),
 6043            "
 6044                a
 6045                b
 6046                c
 6047            "
 6048            .unindent()
 6049        );
 6050        assert_eq!(
 6051            view.selections.display_ranges(cx),
 6052            [
 6053                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6054                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6055                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6056            ]
 6057        );
 6058
 6059        // Ensure inserting the last character of a multi-byte bracket pair
 6060        // doesn't surround the selections with the bracket.
 6061        view.handle_input("*", cx);
 6062        assert_eq!(
 6063            view.text(cx),
 6064            "
 6065                *
 6066                *
 6067                *
 6068            "
 6069            .unindent()
 6070        );
 6071        assert_eq!(
 6072            view.selections.display_ranges(cx),
 6073            [
 6074                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6075                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6076                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6077            ]
 6078        );
 6079    });
 6080}
 6081
 6082#[gpui::test]
 6083async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6084    init_test(cx, |_| {});
 6085
 6086    let language = Arc::new(Language::new(
 6087        LanguageConfig {
 6088            brackets: BracketPairConfig {
 6089                pairs: vec![BracketPair {
 6090                    start: "{".to_string(),
 6091                    end: "}".to_string(),
 6092                    close: true,
 6093                    surround: true,
 6094                    newline: true,
 6095                }],
 6096                ..Default::default()
 6097            },
 6098            autoclose_before: "}".to_string(),
 6099            ..Default::default()
 6100        },
 6101        Some(tree_sitter_rust::LANGUAGE.into()),
 6102    ));
 6103
 6104    let text = r#"
 6105        a
 6106        b
 6107        c
 6108    "#
 6109    .unindent();
 6110
 6111    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6112    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6113    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6114    editor
 6115        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6116        .await;
 6117
 6118    editor.update(cx, |editor, cx| {
 6119        editor.change_selections(None, cx, |s| {
 6120            s.select_ranges([
 6121                Point::new(0, 1)..Point::new(0, 1),
 6122                Point::new(1, 1)..Point::new(1, 1),
 6123                Point::new(2, 1)..Point::new(2, 1),
 6124            ])
 6125        });
 6126
 6127        editor.handle_input("{", cx);
 6128        editor.handle_input("{", cx);
 6129        editor.handle_input("_", cx);
 6130        assert_eq!(
 6131            editor.text(cx),
 6132            "
 6133                a{{_}}
 6134                b{{_}}
 6135                c{{_}}
 6136            "
 6137            .unindent()
 6138        );
 6139        assert_eq!(
 6140            editor.selections.ranges::<Point>(cx),
 6141            [
 6142                Point::new(0, 4)..Point::new(0, 4),
 6143                Point::new(1, 4)..Point::new(1, 4),
 6144                Point::new(2, 4)..Point::new(2, 4)
 6145            ]
 6146        );
 6147
 6148        editor.backspace(&Default::default(), cx);
 6149        editor.backspace(&Default::default(), cx);
 6150        assert_eq!(
 6151            editor.text(cx),
 6152            "
 6153                a{}
 6154                b{}
 6155                c{}
 6156            "
 6157            .unindent()
 6158        );
 6159        assert_eq!(
 6160            editor.selections.ranges::<Point>(cx),
 6161            [
 6162                Point::new(0, 2)..Point::new(0, 2),
 6163                Point::new(1, 2)..Point::new(1, 2),
 6164                Point::new(2, 2)..Point::new(2, 2)
 6165            ]
 6166        );
 6167
 6168        editor.delete_to_previous_word_start(&Default::default(), cx);
 6169        assert_eq!(
 6170            editor.text(cx),
 6171            "
 6172                a
 6173                b
 6174                c
 6175            "
 6176            .unindent()
 6177        );
 6178        assert_eq!(
 6179            editor.selections.ranges::<Point>(cx),
 6180            [
 6181                Point::new(0, 1)..Point::new(0, 1),
 6182                Point::new(1, 1)..Point::new(1, 1),
 6183                Point::new(2, 1)..Point::new(2, 1)
 6184            ]
 6185        );
 6186    });
 6187}
 6188
 6189#[gpui::test]
 6190async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6191    init_test(cx, |settings| {
 6192        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6193    });
 6194
 6195    let mut cx = EditorTestContext::new(cx).await;
 6196
 6197    let language = Arc::new(Language::new(
 6198        LanguageConfig {
 6199            brackets: BracketPairConfig {
 6200                pairs: vec![
 6201                    BracketPair {
 6202                        start: "{".to_string(),
 6203                        end: "}".to_string(),
 6204                        close: true,
 6205                        surround: true,
 6206                        newline: true,
 6207                    },
 6208                    BracketPair {
 6209                        start: "(".to_string(),
 6210                        end: ")".to_string(),
 6211                        close: true,
 6212                        surround: true,
 6213                        newline: true,
 6214                    },
 6215                    BracketPair {
 6216                        start: "[".to_string(),
 6217                        end: "]".to_string(),
 6218                        close: false,
 6219                        surround: true,
 6220                        newline: true,
 6221                    },
 6222                ],
 6223                ..Default::default()
 6224            },
 6225            autoclose_before: "})]".to_string(),
 6226            ..Default::default()
 6227        },
 6228        Some(tree_sitter_rust::LANGUAGE.into()),
 6229    ));
 6230
 6231    cx.language_registry().add(language.clone());
 6232    cx.update_buffer(|buffer, cx| {
 6233        buffer.set_language(Some(language), cx);
 6234    });
 6235
 6236    cx.set_state(
 6237        &"
 6238            {(ˇ)}
 6239            [[ˇ]]
 6240            {(ˇ)}
 6241        "
 6242        .unindent(),
 6243    );
 6244
 6245    cx.update_editor(|view, cx| {
 6246        view.backspace(&Default::default(), cx);
 6247        view.backspace(&Default::default(), cx);
 6248    });
 6249
 6250    cx.assert_editor_state(
 6251        &"
 6252            ˇ
 6253            ˇ]]
 6254            ˇ
 6255        "
 6256        .unindent(),
 6257    );
 6258
 6259    cx.update_editor(|view, cx| {
 6260        view.handle_input("{", cx);
 6261        view.handle_input("{", cx);
 6262        view.move_right(&MoveRight, cx);
 6263        view.move_right(&MoveRight, cx);
 6264        view.move_left(&MoveLeft, cx);
 6265        view.move_left(&MoveLeft, cx);
 6266        view.backspace(&Default::default(), cx);
 6267    });
 6268
 6269    cx.assert_editor_state(
 6270        &"
 6271            {ˇ}
 6272            {ˇ}]]
 6273            {ˇ}
 6274        "
 6275        .unindent(),
 6276    );
 6277
 6278    cx.update_editor(|view, cx| {
 6279        view.backspace(&Default::default(), cx);
 6280    });
 6281
 6282    cx.assert_editor_state(
 6283        &"
 6284            ˇ
 6285            ˇ]]
 6286            ˇ
 6287        "
 6288        .unindent(),
 6289    );
 6290}
 6291
 6292#[gpui::test]
 6293async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6294    init_test(cx, |_| {});
 6295
 6296    let language = Arc::new(Language::new(
 6297        LanguageConfig::default(),
 6298        Some(tree_sitter_rust::LANGUAGE.into()),
 6299    ));
 6300
 6301    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6302    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6303    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6304    editor
 6305        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6306        .await;
 6307
 6308    editor.update(cx, |editor, cx| {
 6309        editor.set_auto_replace_emoji_shortcode(true);
 6310
 6311        editor.handle_input("Hello ", cx);
 6312        editor.handle_input(":wave", cx);
 6313        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6314
 6315        editor.handle_input(":", cx);
 6316        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6317
 6318        editor.handle_input(" :smile", cx);
 6319        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6320
 6321        editor.handle_input(":", cx);
 6322        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6323
 6324        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6325        editor.handle_input(":wave", cx);
 6326        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6327
 6328        editor.handle_input(":", cx);
 6329        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6330
 6331        editor.handle_input(":1", cx);
 6332        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6333
 6334        editor.handle_input(":", cx);
 6335        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6336
 6337        // Ensure shortcode does not get replaced when it is part of a word
 6338        editor.handle_input(" Test:wave", cx);
 6339        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6340
 6341        editor.handle_input(":", cx);
 6342        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6343
 6344        editor.set_auto_replace_emoji_shortcode(false);
 6345
 6346        // Ensure shortcode does not get replaced when auto replace is off
 6347        editor.handle_input(" :wave", cx);
 6348        assert_eq!(
 6349            editor.text(cx),
 6350            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6351        );
 6352
 6353        editor.handle_input(":", cx);
 6354        assert_eq!(
 6355            editor.text(cx),
 6356            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6357        );
 6358    });
 6359}
 6360
 6361#[gpui::test]
 6362async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6363    init_test(cx, |_| {});
 6364
 6365    let (text, insertion_ranges) = marked_text_ranges(
 6366        indoc! {"
 6367            a.ˇ b
 6368            a.ˇ b
 6369            a.ˇ b
 6370        "},
 6371        false,
 6372    );
 6373
 6374    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6375    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6376
 6377    editor.update(cx, |editor, cx| {
 6378        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6379
 6380        editor
 6381            .insert_snippet(&insertion_ranges, snippet, cx)
 6382            .unwrap();
 6383
 6384        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6385            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6386            assert_eq!(editor.text(cx), expected_text);
 6387            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6388        }
 6389
 6390        assert(
 6391            editor,
 6392            cx,
 6393            indoc! {"
 6394                a.f(«one», two, «three») b
 6395                a.f(«one», two, «three») b
 6396                a.f(«one», two, «three») b
 6397            "},
 6398        );
 6399
 6400        // Can't move earlier than the first tab stop
 6401        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6402        assert(
 6403            editor,
 6404            cx,
 6405            indoc! {"
 6406                a.f(«one», two, «three») b
 6407                a.f(«one», two, «three») b
 6408                a.f(«one», two, «three») b
 6409            "},
 6410        );
 6411
 6412        assert!(editor.move_to_next_snippet_tabstop(cx));
 6413        assert(
 6414            editor,
 6415            cx,
 6416            indoc! {"
 6417                a.f(one, «two», three) b
 6418                a.f(one, «two», three) b
 6419                a.f(one, «two», three) b
 6420            "},
 6421        );
 6422
 6423        editor.move_to_prev_snippet_tabstop(cx);
 6424        assert(
 6425            editor,
 6426            cx,
 6427            indoc! {"
 6428                a.f(«one», two, «three») b
 6429                a.f(«one», two, «three») b
 6430                a.f(«one», two, «three») b
 6431            "},
 6432        );
 6433
 6434        assert!(editor.move_to_next_snippet_tabstop(cx));
 6435        assert(
 6436            editor,
 6437            cx,
 6438            indoc! {"
 6439                a.f(one, «two», three) b
 6440                a.f(one, «two», three) b
 6441                a.f(one, «two», three) b
 6442            "},
 6443        );
 6444        assert!(editor.move_to_next_snippet_tabstop(cx));
 6445        assert(
 6446            editor,
 6447            cx,
 6448            indoc! {"
 6449                a.f(one, two, three)ˇ b
 6450                a.f(one, two, three)ˇ b
 6451                a.f(one, two, three)ˇ b
 6452            "},
 6453        );
 6454
 6455        // As soon as the last tab stop is reached, snippet state is gone
 6456        editor.move_to_prev_snippet_tabstop(cx);
 6457        assert(
 6458            editor,
 6459            cx,
 6460            indoc! {"
 6461                a.f(one, two, three)ˇ b
 6462                a.f(one, two, three)ˇ b
 6463                a.f(one, two, three)ˇ b
 6464            "},
 6465        );
 6466    });
 6467}
 6468
 6469#[gpui::test]
 6470async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6471    init_test(cx, |_| {});
 6472
 6473    let fs = FakeFs::new(cx.executor());
 6474    fs.insert_file("/file.rs", Default::default()).await;
 6475
 6476    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6477
 6478    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6479    language_registry.add(rust_lang());
 6480    let mut fake_servers = language_registry.register_fake_lsp(
 6481        "Rust",
 6482        FakeLspAdapter {
 6483            capabilities: lsp::ServerCapabilities {
 6484                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6485                ..Default::default()
 6486            },
 6487            ..Default::default()
 6488        },
 6489    );
 6490
 6491    let buffer = project
 6492        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6493        .await
 6494        .unwrap();
 6495
 6496    cx.executor().start_waiting();
 6497    let fake_server = fake_servers.next().await.unwrap();
 6498
 6499    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6500    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6501    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6502    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6503
 6504    let save = editor
 6505        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6506        .unwrap();
 6507    fake_server
 6508        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6509            assert_eq!(
 6510                params.text_document.uri,
 6511                lsp::Url::from_file_path("/file.rs").unwrap()
 6512            );
 6513            assert_eq!(params.options.tab_size, 4);
 6514            Ok(Some(vec![lsp::TextEdit::new(
 6515                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6516                ", ".to_string(),
 6517            )]))
 6518        })
 6519        .next()
 6520        .await;
 6521    cx.executor().start_waiting();
 6522    save.await;
 6523
 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::Formatting, _, _>(move |params, _| async move {
 6535        assert_eq!(
 6536            params.text_document.uri,
 6537            lsp::Url::from_file_path("/file.rs").unwrap()
 6538        );
 6539        futures::future::pending::<()>().await;
 6540        unreachable!()
 6541    });
 6542    let save = editor
 6543        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6544        .unwrap();
 6545    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6546    cx.executor().start_waiting();
 6547    save.await;
 6548    assert_eq!(
 6549        editor.update(cx, |editor, cx| editor.text(cx)),
 6550        "one\ntwo\nthree\n"
 6551    );
 6552    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6553
 6554    // For non-dirty buffer, no formatting request should be sent
 6555    let save = editor
 6556        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6557        .unwrap();
 6558    let _pending_format_request = fake_server
 6559        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6560            panic!("Should not be invoked on non-dirty buffer");
 6561        })
 6562        .next();
 6563    cx.executor().start_waiting();
 6564    save.await;
 6565
 6566    // Set rust language override and assert overridden tabsize is sent to language server
 6567    update_test_language_settings(cx, |settings| {
 6568        settings.languages.insert(
 6569            "Rust".into(),
 6570            LanguageSettingsContent {
 6571                tab_size: NonZeroU32::new(8),
 6572                ..Default::default()
 6573            },
 6574        );
 6575    });
 6576
 6577    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6578    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6579    let save = editor
 6580        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6581        .unwrap();
 6582    fake_server
 6583        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6584            assert_eq!(
 6585                params.text_document.uri,
 6586                lsp::Url::from_file_path("/file.rs").unwrap()
 6587            );
 6588            assert_eq!(params.options.tab_size, 8);
 6589            Ok(Some(vec![]))
 6590        })
 6591        .next()
 6592        .await;
 6593    cx.executor().start_waiting();
 6594    save.await;
 6595}
 6596
 6597#[gpui::test]
 6598async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6599    init_test(cx, |_| {});
 6600
 6601    let cols = 4;
 6602    let rows = 10;
 6603    let sample_text_1 = sample_text(rows, cols, 'a');
 6604    assert_eq!(
 6605        sample_text_1,
 6606        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6607    );
 6608    let sample_text_2 = sample_text(rows, cols, 'l');
 6609    assert_eq!(
 6610        sample_text_2,
 6611        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6612    );
 6613    let sample_text_3 = sample_text(rows, cols, 'v');
 6614    assert_eq!(
 6615        sample_text_3,
 6616        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6617    );
 6618
 6619    let fs = FakeFs::new(cx.executor());
 6620    fs.insert_tree(
 6621        "/a",
 6622        json!({
 6623            "main.rs": sample_text_1,
 6624            "other.rs": sample_text_2,
 6625            "lib.rs": sample_text_3,
 6626        }),
 6627    )
 6628    .await;
 6629
 6630    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6631    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6632    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6633
 6634    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6635    language_registry.add(rust_lang());
 6636    let mut fake_servers = language_registry.register_fake_lsp(
 6637        "Rust",
 6638        FakeLspAdapter {
 6639            capabilities: lsp::ServerCapabilities {
 6640                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6641                ..Default::default()
 6642            },
 6643            ..Default::default()
 6644        },
 6645    );
 6646
 6647    let worktree = project.update(cx, |project, cx| {
 6648        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6649        assert_eq!(worktrees.len(), 1);
 6650        worktrees.pop().unwrap()
 6651    });
 6652    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6653
 6654    let buffer_1 = project
 6655        .update(cx, |project, cx| {
 6656            project.open_buffer((worktree_id, "main.rs"), cx)
 6657        })
 6658        .await
 6659        .unwrap();
 6660    let buffer_2 = project
 6661        .update(cx, |project, cx| {
 6662            project.open_buffer((worktree_id, "other.rs"), cx)
 6663        })
 6664        .await
 6665        .unwrap();
 6666    let buffer_3 = project
 6667        .update(cx, |project, cx| {
 6668            project.open_buffer((worktree_id, "lib.rs"), cx)
 6669        })
 6670        .await
 6671        .unwrap();
 6672
 6673    let multi_buffer = cx.new_model(|cx| {
 6674        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 6675        multi_buffer.push_excerpts(
 6676            buffer_1.clone(),
 6677            [
 6678                ExcerptRange {
 6679                    context: Point::new(0, 0)..Point::new(3, 0),
 6680                    primary: None,
 6681                },
 6682                ExcerptRange {
 6683                    context: Point::new(5, 0)..Point::new(7, 0),
 6684                    primary: None,
 6685                },
 6686                ExcerptRange {
 6687                    context: Point::new(9, 0)..Point::new(10, 4),
 6688                    primary: None,
 6689                },
 6690            ],
 6691            cx,
 6692        );
 6693        multi_buffer.push_excerpts(
 6694            buffer_2.clone(),
 6695            [
 6696                ExcerptRange {
 6697                    context: Point::new(0, 0)..Point::new(3, 0),
 6698                    primary: None,
 6699                },
 6700                ExcerptRange {
 6701                    context: Point::new(5, 0)..Point::new(7, 0),
 6702                    primary: None,
 6703                },
 6704                ExcerptRange {
 6705                    context: Point::new(9, 0)..Point::new(10, 4),
 6706                    primary: None,
 6707                },
 6708            ],
 6709            cx,
 6710        );
 6711        multi_buffer.push_excerpts(
 6712            buffer_3.clone(),
 6713            [
 6714                ExcerptRange {
 6715                    context: Point::new(0, 0)..Point::new(3, 0),
 6716                    primary: None,
 6717                },
 6718                ExcerptRange {
 6719                    context: Point::new(5, 0)..Point::new(7, 0),
 6720                    primary: None,
 6721                },
 6722                ExcerptRange {
 6723                    context: Point::new(9, 0)..Point::new(10, 4),
 6724                    primary: None,
 6725                },
 6726            ],
 6727            cx,
 6728        );
 6729        multi_buffer
 6730    });
 6731    let multi_buffer_editor = cx.new_view(|cx| {
 6732        Editor::new(
 6733            EditorMode::Full,
 6734            multi_buffer,
 6735            Some(project.clone()),
 6736            true,
 6737            cx,
 6738        )
 6739    });
 6740
 6741    multi_buffer_editor.update(cx, |editor, cx| {
 6742        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6743        editor.insert("|one|two|three|", cx);
 6744    });
 6745    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6746    multi_buffer_editor.update(cx, |editor, cx| {
 6747        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6748            s.select_ranges(Some(60..70))
 6749        });
 6750        editor.insert("|four|five|six|", cx);
 6751    });
 6752    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6753
 6754    // First two buffers should be edited, but not the third one.
 6755    assert_eq!(
 6756        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6757        "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}",
 6758    );
 6759    buffer_1.update(cx, |buffer, _| {
 6760        assert!(buffer.is_dirty());
 6761        assert_eq!(
 6762            buffer.text(),
 6763            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6764        )
 6765    });
 6766    buffer_2.update(cx, |buffer, _| {
 6767        assert!(buffer.is_dirty());
 6768        assert_eq!(
 6769            buffer.text(),
 6770            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6771        )
 6772    });
 6773    buffer_3.update(cx, |buffer, _| {
 6774        assert!(!buffer.is_dirty());
 6775        assert_eq!(buffer.text(), sample_text_3,)
 6776    });
 6777
 6778    cx.executor().start_waiting();
 6779    let save = multi_buffer_editor
 6780        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6781        .unwrap();
 6782
 6783    let fake_server = fake_servers.next().await.unwrap();
 6784    fake_server
 6785        .server
 6786        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6787            Ok(Some(vec![lsp::TextEdit::new(
 6788                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6789                format!("[{} formatted]", params.text_document.uri),
 6790            )]))
 6791        })
 6792        .detach();
 6793    save.await;
 6794
 6795    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6796    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6797    assert_eq!(
 6798        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6799        "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}",
 6800    );
 6801    buffer_1.update(cx, |buffer, _| {
 6802        assert!(!buffer.is_dirty());
 6803        assert_eq!(
 6804            buffer.text(),
 6805            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6806        )
 6807    });
 6808    buffer_2.update(cx, |buffer, _| {
 6809        assert!(!buffer.is_dirty());
 6810        assert_eq!(
 6811            buffer.text(),
 6812            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6813        )
 6814    });
 6815    buffer_3.update(cx, |buffer, _| {
 6816        assert!(!buffer.is_dirty());
 6817        assert_eq!(buffer.text(), sample_text_3,)
 6818    });
 6819}
 6820
 6821#[gpui::test]
 6822async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6823    init_test(cx, |_| {});
 6824
 6825    let fs = FakeFs::new(cx.executor());
 6826    fs.insert_file("/file.rs", Default::default()).await;
 6827
 6828    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6829
 6830    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6831    language_registry.add(rust_lang());
 6832    let mut fake_servers = language_registry.register_fake_lsp(
 6833        "Rust",
 6834        FakeLspAdapter {
 6835            capabilities: lsp::ServerCapabilities {
 6836                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6837                ..Default::default()
 6838            },
 6839            ..Default::default()
 6840        },
 6841    );
 6842
 6843    let buffer = project
 6844        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6845        .await
 6846        .unwrap();
 6847
 6848    cx.executor().start_waiting();
 6849    let fake_server = fake_servers.next().await.unwrap();
 6850
 6851    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6852    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6853    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6854    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6855
 6856    let save = editor
 6857        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6858        .unwrap();
 6859    fake_server
 6860        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6861            assert_eq!(
 6862                params.text_document.uri,
 6863                lsp::Url::from_file_path("/file.rs").unwrap()
 6864            );
 6865            assert_eq!(params.options.tab_size, 4);
 6866            Ok(Some(vec![lsp::TextEdit::new(
 6867                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6868                ", ".to_string(),
 6869            )]))
 6870        })
 6871        .next()
 6872        .await;
 6873    cx.executor().start_waiting();
 6874    save.await;
 6875    assert_eq!(
 6876        editor.update(cx, |editor, cx| editor.text(cx)),
 6877        "one, two\nthree\n"
 6878    );
 6879    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6880
 6881    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6882    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6883
 6884    // Ensure we can still save even if formatting hangs.
 6885    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6886        move |params, _| async move {
 6887            assert_eq!(
 6888                params.text_document.uri,
 6889                lsp::Url::from_file_path("/file.rs").unwrap()
 6890            );
 6891            futures::future::pending::<()>().await;
 6892            unreachable!()
 6893        },
 6894    );
 6895    let save = editor
 6896        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6897        .unwrap();
 6898    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6899    cx.executor().start_waiting();
 6900    save.await;
 6901    assert_eq!(
 6902        editor.update(cx, |editor, cx| editor.text(cx)),
 6903        "one\ntwo\nthree\n"
 6904    );
 6905    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6906
 6907    // For non-dirty buffer, no formatting request should be sent
 6908    let save = editor
 6909        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6910        .unwrap();
 6911    let _pending_format_request = fake_server
 6912        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6913            panic!("Should not be invoked on non-dirty buffer");
 6914        })
 6915        .next();
 6916    cx.executor().start_waiting();
 6917    save.await;
 6918
 6919    // Set Rust language override and assert overridden tabsize is sent to language server
 6920    update_test_language_settings(cx, |settings| {
 6921        settings.languages.insert(
 6922            "Rust".into(),
 6923            LanguageSettingsContent {
 6924                tab_size: NonZeroU32::new(8),
 6925                ..Default::default()
 6926            },
 6927        );
 6928    });
 6929
 6930    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6931    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6932    let save = editor
 6933        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6934        .unwrap();
 6935    fake_server
 6936        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6937            assert_eq!(
 6938                params.text_document.uri,
 6939                lsp::Url::from_file_path("/file.rs").unwrap()
 6940            );
 6941            assert_eq!(params.options.tab_size, 8);
 6942            Ok(Some(vec![]))
 6943        })
 6944        .next()
 6945        .await;
 6946    cx.executor().start_waiting();
 6947    save.await;
 6948}
 6949
 6950#[gpui::test]
 6951async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6952    init_test(cx, |settings| {
 6953        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 6954            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 6955        ))
 6956    });
 6957
 6958    let fs = FakeFs::new(cx.executor());
 6959    fs.insert_file("/file.rs", Default::default()).await;
 6960
 6961    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6962
 6963    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6964    language_registry.add(Arc::new(Language::new(
 6965        LanguageConfig {
 6966            name: "Rust".into(),
 6967            matcher: LanguageMatcher {
 6968                path_suffixes: vec!["rs".to_string()],
 6969                ..Default::default()
 6970            },
 6971            ..LanguageConfig::default()
 6972        },
 6973        Some(tree_sitter_rust::LANGUAGE.into()),
 6974    )));
 6975    update_test_language_settings(cx, |settings| {
 6976        // Enable Prettier formatting for the same buffer, and ensure
 6977        // LSP is called instead of Prettier.
 6978        settings.defaults.prettier = Some(PrettierSettings {
 6979            allowed: true,
 6980            ..PrettierSettings::default()
 6981        });
 6982    });
 6983    let mut fake_servers = language_registry.register_fake_lsp(
 6984        "Rust",
 6985        FakeLspAdapter {
 6986            capabilities: lsp::ServerCapabilities {
 6987                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6988                ..Default::default()
 6989            },
 6990            ..Default::default()
 6991        },
 6992    );
 6993
 6994    let buffer = project
 6995        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6996        .await
 6997        .unwrap();
 6998
 6999    cx.executor().start_waiting();
 7000    let fake_server = fake_servers.next().await.unwrap();
 7001
 7002    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7003    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7004    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7005
 7006    let format = editor
 7007        .update(cx, |editor, cx| {
 7008            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 7009        })
 7010        .unwrap();
 7011    fake_server
 7012        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7013            assert_eq!(
 7014                params.text_document.uri,
 7015                lsp::Url::from_file_path("/file.rs").unwrap()
 7016            );
 7017            assert_eq!(params.options.tab_size, 4);
 7018            Ok(Some(vec![lsp::TextEdit::new(
 7019                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7020                ", ".to_string(),
 7021            )]))
 7022        })
 7023        .next()
 7024        .await;
 7025    cx.executor().start_waiting();
 7026    format.await;
 7027    assert_eq!(
 7028        editor.update(cx, |editor, cx| editor.text(cx)),
 7029        "one, two\nthree\n"
 7030    );
 7031
 7032    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7033    // Ensure we don't lock if formatting hangs.
 7034    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7035        assert_eq!(
 7036            params.text_document.uri,
 7037            lsp::Url::from_file_path("/file.rs").unwrap()
 7038        );
 7039        futures::future::pending::<()>().await;
 7040        unreachable!()
 7041    });
 7042    let format = editor
 7043        .update(cx, |editor, cx| {
 7044            editor.perform_format(project, FormatTrigger::Manual, cx)
 7045        })
 7046        .unwrap();
 7047    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7048    cx.executor().start_waiting();
 7049    format.await;
 7050    assert_eq!(
 7051        editor.update(cx, |editor, cx| editor.text(cx)),
 7052        "one\ntwo\nthree\n"
 7053    );
 7054}
 7055
 7056#[gpui::test]
 7057async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7058    init_test(cx, |_| {});
 7059
 7060    let mut cx = EditorLspTestContext::new_rust(
 7061        lsp::ServerCapabilities {
 7062            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7063            ..Default::default()
 7064        },
 7065        cx,
 7066    )
 7067    .await;
 7068
 7069    cx.set_state(indoc! {"
 7070        one.twoˇ
 7071    "});
 7072
 7073    // The format request takes a long time. When it completes, it inserts
 7074    // a newline and an indent before the `.`
 7075    cx.lsp
 7076        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7077            let executor = cx.background_executor().clone();
 7078            async move {
 7079                executor.timer(Duration::from_millis(100)).await;
 7080                Ok(Some(vec![lsp::TextEdit {
 7081                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7082                    new_text: "\n    ".into(),
 7083                }]))
 7084            }
 7085        });
 7086
 7087    // Submit a format request.
 7088    let format_1 = cx
 7089        .update_editor(|editor, cx| editor.format(&Format, cx))
 7090        .unwrap();
 7091    cx.executor().run_until_parked();
 7092
 7093    // Submit a second format request.
 7094    let format_2 = cx
 7095        .update_editor(|editor, cx| editor.format(&Format, cx))
 7096        .unwrap();
 7097    cx.executor().run_until_parked();
 7098
 7099    // Wait for both format requests to complete
 7100    cx.executor().advance_clock(Duration::from_millis(200));
 7101    cx.executor().start_waiting();
 7102    format_1.await.unwrap();
 7103    cx.executor().start_waiting();
 7104    format_2.await.unwrap();
 7105
 7106    // The formatting edits only happens once.
 7107    cx.assert_editor_state(indoc! {"
 7108        one
 7109            .twoˇ
 7110    "});
 7111}
 7112
 7113#[gpui::test]
 7114async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7115    init_test(cx, |settings| {
 7116        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7117    });
 7118
 7119    let mut cx = EditorLspTestContext::new_rust(
 7120        lsp::ServerCapabilities {
 7121            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7122            ..Default::default()
 7123        },
 7124        cx,
 7125    )
 7126    .await;
 7127
 7128    // Set up a buffer white some trailing whitespace and no trailing newline.
 7129    cx.set_state(
 7130        &[
 7131            "one ",   //
 7132            "twoˇ",   //
 7133            "three ", //
 7134            "four",   //
 7135        ]
 7136        .join("\n"),
 7137    );
 7138
 7139    // Submit a format request.
 7140    let format = cx
 7141        .update_editor(|editor, cx| editor.format(&Format, cx))
 7142        .unwrap();
 7143
 7144    // Record which buffer changes have been sent to the language server
 7145    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7146    cx.lsp
 7147        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7148            let buffer_changes = buffer_changes.clone();
 7149            move |params, _| {
 7150                buffer_changes.lock().extend(
 7151                    params
 7152                        .content_changes
 7153                        .into_iter()
 7154                        .map(|e| (e.range.unwrap(), e.text)),
 7155                );
 7156            }
 7157        });
 7158
 7159    // Handle formatting requests to the language server.
 7160    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7161        let buffer_changes = buffer_changes.clone();
 7162        move |_, _| {
 7163            // When formatting is requested, trailing whitespace has already been stripped,
 7164            // and the trailing newline has already been added.
 7165            assert_eq!(
 7166                &buffer_changes.lock()[1..],
 7167                &[
 7168                    (
 7169                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7170                        "".into()
 7171                    ),
 7172                    (
 7173                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7174                        "".into()
 7175                    ),
 7176                    (
 7177                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7178                        "\n".into()
 7179                    ),
 7180                ]
 7181            );
 7182
 7183            // Insert blank lines between each line of the buffer.
 7184            async move {
 7185                Ok(Some(vec![
 7186                    lsp::TextEdit {
 7187                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7188                        new_text: "\n".into(),
 7189                    },
 7190                    lsp::TextEdit {
 7191                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7192                        new_text: "\n".into(),
 7193                    },
 7194                ]))
 7195            }
 7196        }
 7197    });
 7198
 7199    // After formatting the buffer, the trailing whitespace is stripped,
 7200    // a newline is appended, and the edits provided by the language server
 7201    // have been applied.
 7202    format.await.unwrap();
 7203    cx.assert_editor_state(
 7204        &[
 7205            "one",   //
 7206            "",      //
 7207            "twoˇ",  //
 7208            "",      //
 7209            "three", //
 7210            "four",  //
 7211            "",      //
 7212        ]
 7213        .join("\n"),
 7214    );
 7215
 7216    // Undoing the formatting undoes the trailing whitespace removal, the
 7217    // trailing newline, and the LSP edits.
 7218    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7219    cx.assert_editor_state(
 7220        &[
 7221            "one ",   //
 7222            "twoˇ",   //
 7223            "three ", //
 7224            "four",   //
 7225        ]
 7226        .join("\n"),
 7227    );
 7228}
 7229
 7230#[gpui::test]
 7231async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7232    cx: &mut gpui::TestAppContext,
 7233) {
 7234    init_test(cx, |_| {});
 7235
 7236    cx.update(|cx| {
 7237        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7238            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7239                settings.auto_signature_help = Some(true);
 7240            });
 7241        });
 7242    });
 7243
 7244    let mut cx = EditorLspTestContext::new_rust(
 7245        lsp::ServerCapabilities {
 7246            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7247                ..Default::default()
 7248            }),
 7249            ..Default::default()
 7250        },
 7251        cx,
 7252    )
 7253    .await;
 7254
 7255    let language = Language::new(
 7256        LanguageConfig {
 7257            name: "Rust".into(),
 7258            brackets: BracketPairConfig {
 7259                pairs: vec![
 7260                    BracketPair {
 7261                        start: "{".to_string(),
 7262                        end: "}".to_string(),
 7263                        close: true,
 7264                        surround: true,
 7265                        newline: true,
 7266                    },
 7267                    BracketPair {
 7268                        start: "(".to_string(),
 7269                        end: ")".to_string(),
 7270                        close: true,
 7271                        surround: true,
 7272                        newline: true,
 7273                    },
 7274                    BracketPair {
 7275                        start: "/*".to_string(),
 7276                        end: " */".to_string(),
 7277                        close: true,
 7278                        surround: true,
 7279                        newline: true,
 7280                    },
 7281                    BracketPair {
 7282                        start: "[".to_string(),
 7283                        end: "]".to_string(),
 7284                        close: false,
 7285                        surround: false,
 7286                        newline: true,
 7287                    },
 7288                    BracketPair {
 7289                        start: "\"".to_string(),
 7290                        end: "\"".to_string(),
 7291                        close: true,
 7292                        surround: true,
 7293                        newline: false,
 7294                    },
 7295                    BracketPair {
 7296                        start: "<".to_string(),
 7297                        end: ">".to_string(),
 7298                        close: false,
 7299                        surround: true,
 7300                        newline: true,
 7301                    },
 7302                ],
 7303                ..Default::default()
 7304            },
 7305            autoclose_before: "})]".to_string(),
 7306            ..Default::default()
 7307        },
 7308        Some(tree_sitter_rust::LANGUAGE.into()),
 7309    );
 7310    let language = Arc::new(language);
 7311
 7312    cx.language_registry().add(language.clone());
 7313    cx.update_buffer(|buffer, cx| {
 7314        buffer.set_language(Some(language), cx);
 7315    });
 7316
 7317    cx.set_state(
 7318        &r#"
 7319            fn main() {
 7320                sampleˇ
 7321            }
 7322        "#
 7323        .unindent(),
 7324    );
 7325
 7326    cx.update_editor(|view, cx| {
 7327        view.handle_input("(", cx);
 7328    });
 7329    cx.assert_editor_state(
 7330        &"
 7331            fn main() {
 7332                sample(ˇ)
 7333            }
 7334        "
 7335        .unindent(),
 7336    );
 7337
 7338    let mocked_response = lsp::SignatureHelp {
 7339        signatures: vec![lsp::SignatureInformation {
 7340            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7341            documentation: None,
 7342            parameters: Some(vec![
 7343                lsp::ParameterInformation {
 7344                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7345                    documentation: None,
 7346                },
 7347                lsp::ParameterInformation {
 7348                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7349                    documentation: None,
 7350                },
 7351            ]),
 7352            active_parameter: None,
 7353        }],
 7354        active_signature: Some(0),
 7355        active_parameter: Some(0),
 7356    };
 7357    handle_signature_help_request(&mut cx, mocked_response).await;
 7358
 7359    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7360        .await;
 7361
 7362    cx.editor(|editor, _| {
 7363        let signature_help_state = editor.signature_help_state.popover().cloned();
 7364        assert!(signature_help_state.is_some());
 7365        let ParsedMarkdown {
 7366            text, highlights, ..
 7367        } = signature_help_state.unwrap().parsed_content;
 7368        assert_eq!(text, "param1: u8, param2: u8");
 7369        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7370    });
 7371}
 7372
 7373#[gpui::test]
 7374async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7375    init_test(cx, |_| {});
 7376
 7377    cx.update(|cx| {
 7378        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7379            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7380                settings.auto_signature_help = Some(false);
 7381                settings.show_signature_help_after_edits = Some(false);
 7382            });
 7383        });
 7384    });
 7385
 7386    let mut cx = EditorLspTestContext::new_rust(
 7387        lsp::ServerCapabilities {
 7388            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7389                ..Default::default()
 7390            }),
 7391            ..Default::default()
 7392        },
 7393        cx,
 7394    )
 7395    .await;
 7396
 7397    let language = Language::new(
 7398        LanguageConfig {
 7399            name: "Rust".into(),
 7400            brackets: BracketPairConfig {
 7401                pairs: vec![
 7402                    BracketPair {
 7403                        start: "{".to_string(),
 7404                        end: "}".to_string(),
 7405                        close: true,
 7406                        surround: true,
 7407                        newline: true,
 7408                    },
 7409                    BracketPair {
 7410                        start: "(".to_string(),
 7411                        end: ")".to_string(),
 7412                        close: true,
 7413                        surround: true,
 7414                        newline: true,
 7415                    },
 7416                    BracketPair {
 7417                        start: "/*".to_string(),
 7418                        end: " */".to_string(),
 7419                        close: true,
 7420                        surround: true,
 7421                        newline: true,
 7422                    },
 7423                    BracketPair {
 7424                        start: "[".to_string(),
 7425                        end: "]".to_string(),
 7426                        close: false,
 7427                        surround: false,
 7428                        newline: true,
 7429                    },
 7430                    BracketPair {
 7431                        start: "\"".to_string(),
 7432                        end: "\"".to_string(),
 7433                        close: true,
 7434                        surround: true,
 7435                        newline: false,
 7436                    },
 7437                    BracketPair {
 7438                        start: "<".to_string(),
 7439                        end: ">".to_string(),
 7440                        close: false,
 7441                        surround: true,
 7442                        newline: true,
 7443                    },
 7444                ],
 7445                ..Default::default()
 7446            },
 7447            autoclose_before: "})]".to_string(),
 7448            ..Default::default()
 7449        },
 7450        Some(tree_sitter_rust::LANGUAGE.into()),
 7451    );
 7452    let language = Arc::new(language);
 7453
 7454    cx.language_registry().add(language.clone());
 7455    cx.update_buffer(|buffer, cx| {
 7456        buffer.set_language(Some(language), cx);
 7457    });
 7458
 7459    // Ensure that signature_help is not called when no signature help is enabled.
 7460    cx.set_state(
 7461        &r#"
 7462            fn main() {
 7463                sampleˇ
 7464            }
 7465        "#
 7466        .unindent(),
 7467    );
 7468    cx.update_editor(|view, cx| {
 7469        view.handle_input("(", cx);
 7470    });
 7471    cx.assert_editor_state(
 7472        &"
 7473            fn main() {
 7474                sample(ˇ)
 7475            }
 7476        "
 7477        .unindent(),
 7478    );
 7479    cx.editor(|editor, _| {
 7480        assert!(editor.signature_help_state.task().is_none());
 7481    });
 7482
 7483    let mocked_response = lsp::SignatureHelp {
 7484        signatures: vec![lsp::SignatureInformation {
 7485            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7486            documentation: None,
 7487            parameters: Some(vec![
 7488                lsp::ParameterInformation {
 7489                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7490                    documentation: None,
 7491                },
 7492                lsp::ParameterInformation {
 7493                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7494                    documentation: None,
 7495                },
 7496            ]),
 7497            active_parameter: None,
 7498        }],
 7499        active_signature: Some(0),
 7500        active_parameter: Some(0),
 7501    };
 7502
 7503    // Ensure that signature_help is called when enabled afte edits
 7504    cx.update(|cx| {
 7505        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7506            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7507                settings.auto_signature_help = Some(false);
 7508                settings.show_signature_help_after_edits = Some(true);
 7509            });
 7510        });
 7511    });
 7512    cx.set_state(
 7513        &r#"
 7514            fn main() {
 7515                sampleˇ
 7516            }
 7517        "#
 7518        .unindent(),
 7519    );
 7520    cx.update_editor(|view, cx| {
 7521        view.handle_input("(", cx);
 7522    });
 7523    cx.assert_editor_state(
 7524        &"
 7525            fn main() {
 7526                sample(ˇ)
 7527            }
 7528        "
 7529        .unindent(),
 7530    );
 7531    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7532    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7533        .await;
 7534    cx.update_editor(|editor, _| {
 7535        let signature_help_state = editor.signature_help_state.popover().cloned();
 7536        assert!(signature_help_state.is_some());
 7537        let ParsedMarkdown {
 7538            text, highlights, ..
 7539        } = signature_help_state.unwrap().parsed_content;
 7540        assert_eq!(text, "param1: u8, param2: u8");
 7541        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7542        editor.signature_help_state = SignatureHelpState::default();
 7543    });
 7544
 7545    // Ensure that signature_help is called when auto signature help override is enabled
 7546    cx.update(|cx| {
 7547        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7548            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7549                settings.auto_signature_help = Some(true);
 7550                settings.show_signature_help_after_edits = Some(false);
 7551            });
 7552        });
 7553    });
 7554    cx.set_state(
 7555        &r#"
 7556            fn main() {
 7557                sampleˇ
 7558            }
 7559        "#
 7560        .unindent(),
 7561    );
 7562    cx.update_editor(|view, cx| {
 7563        view.handle_input("(", cx);
 7564    });
 7565    cx.assert_editor_state(
 7566        &"
 7567            fn main() {
 7568                sample(ˇ)
 7569            }
 7570        "
 7571        .unindent(),
 7572    );
 7573    handle_signature_help_request(&mut cx, mocked_response).await;
 7574    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7575        .await;
 7576    cx.editor(|editor, _| {
 7577        let signature_help_state = editor.signature_help_state.popover().cloned();
 7578        assert!(signature_help_state.is_some());
 7579        let ParsedMarkdown {
 7580            text, highlights, ..
 7581        } = signature_help_state.unwrap().parsed_content;
 7582        assert_eq!(text, "param1: u8, param2: u8");
 7583        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7584    });
 7585}
 7586
 7587#[gpui::test]
 7588async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7589    init_test(cx, |_| {});
 7590    cx.update(|cx| {
 7591        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7592            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7593                settings.auto_signature_help = Some(true);
 7594            });
 7595        });
 7596    });
 7597
 7598    let mut cx = EditorLspTestContext::new_rust(
 7599        lsp::ServerCapabilities {
 7600            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7601                ..Default::default()
 7602            }),
 7603            ..Default::default()
 7604        },
 7605        cx,
 7606    )
 7607    .await;
 7608
 7609    // A test that directly calls `show_signature_help`
 7610    cx.update_editor(|editor, cx| {
 7611        editor.show_signature_help(&ShowSignatureHelp, cx);
 7612    });
 7613
 7614    let mocked_response = lsp::SignatureHelp {
 7615        signatures: vec![lsp::SignatureInformation {
 7616            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7617            documentation: None,
 7618            parameters: Some(vec![
 7619                lsp::ParameterInformation {
 7620                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7621                    documentation: None,
 7622                },
 7623                lsp::ParameterInformation {
 7624                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7625                    documentation: None,
 7626                },
 7627            ]),
 7628            active_parameter: None,
 7629        }],
 7630        active_signature: Some(0),
 7631        active_parameter: Some(0),
 7632    };
 7633    handle_signature_help_request(&mut cx, mocked_response).await;
 7634
 7635    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7636        .await;
 7637
 7638    cx.editor(|editor, _| {
 7639        let signature_help_state = editor.signature_help_state.popover().cloned();
 7640        assert!(signature_help_state.is_some());
 7641        let ParsedMarkdown {
 7642            text, highlights, ..
 7643        } = signature_help_state.unwrap().parsed_content;
 7644        assert_eq!(text, "param1: u8, param2: u8");
 7645        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7646    });
 7647
 7648    // When exiting outside from inside the brackets, `signature_help` is closed.
 7649    cx.set_state(indoc! {"
 7650        fn main() {
 7651            sample(ˇ);
 7652        }
 7653
 7654        fn sample(param1: u8, param2: u8) {}
 7655    "});
 7656
 7657    cx.update_editor(|editor, cx| {
 7658        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7659    });
 7660
 7661    let mocked_response = lsp::SignatureHelp {
 7662        signatures: Vec::new(),
 7663        active_signature: None,
 7664        active_parameter: None,
 7665    };
 7666    handle_signature_help_request(&mut cx, mocked_response).await;
 7667
 7668    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7669        .await;
 7670
 7671    cx.editor(|editor, _| {
 7672        assert!(!editor.signature_help_state.is_shown());
 7673    });
 7674
 7675    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7676    cx.set_state(indoc! {"
 7677        fn main() {
 7678            sample(ˇ);
 7679        }
 7680
 7681        fn sample(param1: u8, param2: u8) {}
 7682    "});
 7683
 7684    let mocked_response = lsp::SignatureHelp {
 7685        signatures: vec![lsp::SignatureInformation {
 7686            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7687            documentation: None,
 7688            parameters: Some(vec![
 7689                lsp::ParameterInformation {
 7690                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7691                    documentation: None,
 7692                },
 7693                lsp::ParameterInformation {
 7694                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7695                    documentation: None,
 7696                },
 7697            ]),
 7698            active_parameter: None,
 7699        }],
 7700        active_signature: Some(0),
 7701        active_parameter: Some(0),
 7702    };
 7703    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7704    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7705        .await;
 7706    cx.editor(|editor, _| {
 7707        assert!(editor.signature_help_state.is_shown());
 7708    });
 7709
 7710    // Restore the popover with more parameter input
 7711    cx.set_state(indoc! {"
 7712        fn main() {
 7713            sample(param1, param2ˇ);
 7714        }
 7715
 7716        fn sample(param1: u8, param2: u8) {}
 7717    "});
 7718
 7719    let mocked_response = lsp::SignatureHelp {
 7720        signatures: vec![lsp::SignatureInformation {
 7721            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7722            documentation: None,
 7723            parameters: Some(vec![
 7724                lsp::ParameterInformation {
 7725                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7726                    documentation: None,
 7727                },
 7728                lsp::ParameterInformation {
 7729                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7730                    documentation: None,
 7731                },
 7732            ]),
 7733            active_parameter: None,
 7734        }],
 7735        active_signature: Some(0),
 7736        active_parameter: Some(1),
 7737    };
 7738    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7739    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7740        .await;
 7741
 7742    // When selecting a range, the popover is gone.
 7743    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7744    cx.update_editor(|editor, cx| {
 7745        editor.change_selections(None, cx, |s| {
 7746            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7747        })
 7748    });
 7749    cx.assert_editor_state(indoc! {"
 7750        fn main() {
 7751            sample(param1, «ˇparam2»);
 7752        }
 7753
 7754        fn sample(param1: u8, param2: u8) {}
 7755    "});
 7756    cx.editor(|editor, _| {
 7757        assert!(!editor.signature_help_state.is_shown());
 7758    });
 7759
 7760    // When unselecting again, the popover is back if within the brackets.
 7761    cx.update_editor(|editor, cx| {
 7762        editor.change_selections(None, cx, |s| {
 7763            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7764        })
 7765    });
 7766    cx.assert_editor_state(indoc! {"
 7767        fn main() {
 7768            sample(param1, ˇparam2);
 7769        }
 7770
 7771        fn sample(param1: u8, param2: u8) {}
 7772    "});
 7773    handle_signature_help_request(&mut cx, mocked_response).await;
 7774    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7775        .await;
 7776    cx.editor(|editor, _| {
 7777        assert!(editor.signature_help_state.is_shown());
 7778    });
 7779
 7780    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7781    cx.update_editor(|editor, cx| {
 7782        editor.change_selections(None, cx, |s| {
 7783            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7784            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7785        })
 7786    });
 7787    cx.assert_editor_state(indoc! {"
 7788        fn main() {
 7789            sample(param1, ˇparam2);
 7790        }
 7791
 7792        fn sample(param1: u8, param2: u8) {}
 7793    "});
 7794
 7795    let mocked_response = lsp::SignatureHelp {
 7796        signatures: vec![lsp::SignatureInformation {
 7797            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7798            documentation: None,
 7799            parameters: Some(vec![
 7800                lsp::ParameterInformation {
 7801                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7802                    documentation: None,
 7803                },
 7804                lsp::ParameterInformation {
 7805                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7806                    documentation: None,
 7807                },
 7808            ]),
 7809            active_parameter: None,
 7810        }],
 7811        active_signature: Some(0),
 7812        active_parameter: Some(1),
 7813    };
 7814    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7815    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7816        .await;
 7817    cx.update_editor(|editor, cx| {
 7818        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7819    });
 7820    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7821        .await;
 7822    cx.update_editor(|editor, cx| {
 7823        editor.change_selections(None, cx, |s| {
 7824            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7825        })
 7826    });
 7827    cx.assert_editor_state(indoc! {"
 7828        fn main() {
 7829            sample(param1, «ˇparam2»);
 7830        }
 7831
 7832        fn sample(param1: u8, param2: u8) {}
 7833    "});
 7834    cx.update_editor(|editor, cx| {
 7835        editor.change_selections(None, cx, |s| {
 7836            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7837        })
 7838    });
 7839    cx.assert_editor_state(indoc! {"
 7840        fn main() {
 7841            sample(param1, ˇparam2);
 7842        }
 7843
 7844        fn sample(param1: u8, param2: u8) {}
 7845    "});
 7846    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7847        .await;
 7848}
 7849
 7850#[gpui::test]
 7851async fn test_completion(cx: &mut gpui::TestAppContext) {
 7852    init_test(cx, |_| {});
 7853
 7854    let mut cx = EditorLspTestContext::new_rust(
 7855        lsp::ServerCapabilities {
 7856            completion_provider: Some(lsp::CompletionOptions {
 7857                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7858                resolve_provider: Some(true),
 7859                ..Default::default()
 7860            }),
 7861            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 7862            ..Default::default()
 7863        },
 7864        cx,
 7865    )
 7866    .await;
 7867    let counter = Arc::new(AtomicUsize::new(0));
 7868
 7869    cx.set_state(indoc! {"
 7870        oneˇ
 7871        two
 7872        three
 7873    "});
 7874    cx.simulate_keystroke(".");
 7875    handle_completion_request(
 7876        &mut cx,
 7877        indoc! {"
 7878            one.|<>
 7879            two
 7880            three
 7881        "},
 7882        vec!["first_completion", "second_completion"],
 7883        counter.clone(),
 7884    )
 7885    .await;
 7886    cx.condition(|editor, _| editor.context_menu_visible())
 7887        .await;
 7888    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7889
 7890    let _handler = handle_signature_help_request(
 7891        &mut cx,
 7892        lsp::SignatureHelp {
 7893            signatures: vec![lsp::SignatureInformation {
 7894                label: "test signature".to_string(),
 7895                documentation: None,
 7896                parameters: Some(vec![lsp::ParameterInformation {
 7897                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 7898                    documentation: None,
 7899                }]),
 7900                active_parameter: None,
 7901            }],
 7902            active_signature: None,
 7903            active_parameter: None,
 7904        },
 7905    );
 7906    cx.update_editor(|editor, cx| {
 7907        assert!(
 7908            !editor.signature_help_state.is_shown(),
 7909            "No signature help was called for"
 7910        );
 7911        editor.show_signature_help(&ShowSignatureHelp, cx);
 7912    });
 7913    cx.run_until_parked();
 7914    cx.update_editor(|editor, _| {
 7915        assert!(
 7916            !editor.signature_help_state.is_shown(),
 7917            "No signature help should be shown when completions menu is open"
 7918        );
 7919    });
 7920
 7921    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7922        editor.context_menu_next(&Default::default(), cx);
 7923        editor
 7924            .confirm_completion(&ConfirmCompletion::default(), cx)
 7925            .unwrap()
 7926    });
 7927    cx.assert_editor_state(indoc! {"
 7928        one.second_completionˇ
 7929        two
 7930        three
 7931    "});
 7932
 7933    handle_resolve_completion_request(
 7934        &mut cx,
 7935        Some(vec![
 7936            (
 7937                //This overlaps with the primary completion edit which is
 7938                //misbehavior from the LSP spec, test that we filter it out
 7939                indoc! {"
 7940                    one.second_ˇcompletion
 7941                    two
 7942                    threeˇ
 7943                "},
 7944                "overlapping additional edit",
 7945            ),
 7946            (
 7947                indoc! {"
 7948                    one.second_completion
 7949                    two
 7950                    threeˇ
 7951                "},
 7952                "\nadditional edit",
 7953            ),
 7954        ]),
 7955    )
 7956    .await;
 7957    apply_additional_edits.await.unwrap();
 7958    cx.assert_editor_state(indoc! {"
 7959        one.second_completionˇ
 7960        two
 7961        three
 7962        additional edit
 7963    "});
 7964
 7965    cx.set_state(indoc! {"
 7966        one.second_completion
 7967        twoˇ
 7968        threeˇ
 7969        additional edit
 7970    "});
 7971    cx.simulate_keystroke(" ");
 7972    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7973    cx.simulate_keystroke("s");
 7974    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 7975
 7976    cx.assert_editor_state(indoc! {"
 7977        one.second_completion
 7978        two sˇ
 7979        three sˇ
 7980        additional edit
 7981    "});
 7982    handle_completion_request(
 7983        &mut cx,
 7984        indoc! {"
 7985            one.second_completion
 7986            two s
 7987            three <s|>
 7988            additional edit
 7989        "},
 7990        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 7991        counter.clone(),
 7992    )
 7993    .await;
 7994    cx.condition(|editor, _| editor.context_menu_visible())
 7995        .await;
 7996    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 7997
 7998    cx.simulate_keystroke("i");
 7999
 8000    handle_completion_request(
 8001        &mut cx,
 8002        indoc! {"
 8003            one.second_completion
 8004            two si
 8005            three <si|>
 8006            additional edit
 8007        "},
 8008        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8009        counter.clone(),
 8010    )
 8011    .await;
 8012    cx.condition(|editor, _| editor.context_menu_visible())
 8013        .await;
 8014    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8015
 8016    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8017        editor
 8018            .confirm_completion(&ConfirmCompletion::default(), cx)
 8019            .unwrap()
 8020    });
 8021    cx.assert_editor_state(indoc! {"
 8022        one.second_completion
 8023        two sixth_completionˇ
 8024        three sixth_completionˇ
 8025        additional edit
 8026    "});
 8027
 8028    handle_resolve_completion_request(&mut cx, None).await;
 8029    apply_additional_edits.await.unwrap();
 8030
 8031    cx.update(|cx| {
 8032        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8033            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8034                settings.show_completions_on_input = Some(false);
 8035            });
 8036        })
 8037    });
 8038    cx.set_state("editorˇ");
 8039    cx.simulate_keystroke(".");
 8040    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8041    cx.simulate_keystroke("c");
 8042    cx.simulate_keystroke("l");
 8043    cx.simulate_keystroke("o");
 8044    cx.assert_editor_state("editor.cloˇ");
 8045    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8046    cx.update_editor(|editor, cx| {
 8047        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8048    });
 8049    handle_completion_request(
 8050        &mut cx,
 8051        "editor.<clo|>",
 8052        vec!["close", "clobber"],
 8053        counter.clone(),
 8054    )
 8055    .await;
 8056    cx.condition(|editor, _| editor.context_menu_visible())
 8057        .await;
 8058    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8059
 8060    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8061        editor
 8062            .confirm_completion(&ConfirmCompletion::default(), cx)
 8063            .unwrap()
 8064    });
 8065    cx.assert_editor_state("editor.closeˇ");
 8066    handle_resolve_completion_request(&mut cx, None).await;
 8067    apply_additional_edits.await.unwrap();
 8068}
 8069
 8070#[gpui::test]
 8071async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8072    init_test(cx, |_| {});
 8073    let mut cx = EditorLspTestContext::new_rust(
 8074        lsp::ServerCapabilities {
 8075            completion_provider: Some(lsp::CompletionOptions {
 8076                trigger_characters: Some(vec![".".to_string()]),
 8077                ..Default::default()
 8078            }),
 8079            ..Default::default()
 8080        },
 8081        cx,
 8082    )
 8083    .await;
 8084    cx.lsp
 8085        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8086            Ok(Some(lsp::CompletionResponse::Array(vec![
 8087                lsp::CompletionItem {
 8088                    label: "first".into(),
 8089                    ..Default::default()
 8090                },
 8091                lsp::CompletionItem {
 8092                    label: "last".into(),
 8093                    ..Default::default()
 8094                },
 8095            ])))
 8096        });
 8097    cx.set_state("variableˇ");
 8098    cx.simulate_keystroke(".");
 8099    cx.executor().run_until_parked();
 8100
 8101    cx.update_editor(|editor, _| {
 8102        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8103            assert_eq!(
 8104                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8105                &["first", "last"]
 8106            );
 8107        } else {
 8108            panic!("expected completion menu to be open");
 8109        }
 8110    });
 8111
 8112    cx.update_editor(|editor, cx| {
 8113        editor.move_page_down(&MovePageDown::default(), cx);
 8114        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8115            assert!(
 8116                menu.selected_item == 1,
 8117                "expected PageDown to select the last item from the context menu"
 8118            );
 8119        } else {
 8120            panic!("expected completion menu to stay open after PageDown");
 8121        }
 8122    });
 8123
 8124    cx.update_editor(|editor, cx| {
 8125        editor.move_page_up(&MovePageUp::default(), cx);
 8126        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8127            assert!(
 8128                menu.selected_item == 0,
 8129                "expected PageUp to select the first item from the context menu"
 8130            );
 8131        } else {
 8132            panic!("expected completion menu to stay open after PageUp");
 8133        }
 8134    });
 8135}
 8136
 8137#[gpui::test]
 8138async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8139    init_test(cx, |_| {});
 8140
 8141    let mut cx = EditorLspTestContext::new_rust(
 8142        lsp::ServerCapabilities {
 8143            completion_provider: Some(lsp::CompletionOptions {
 8144                trigger_characters: Some(vec![".".to_string()]),
 8145                resolve_provider: Some(true),
 8146                ..Default::default()
 8147            }),
 8148            ..Default::default()
 8149        },
 8150        cx,
 8151    )
 8152    .await;
 8153
 8154    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8155    cx.simulate_keystroke(".");
 8156    let completion_item = lsp::CompletionItem {
 8157        label: "Some".into(),
 8158        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8159        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8160        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8161            kind: lsp::MarkupKind::Markdown,
 8162            value: "```rust\nSome(2)\n```".to_string(),
 8163        })),
 8164        deprecated: Some(false),
 8165        sort_text: Some("Some".to_string()),
 8166        filter_text: Some("Some".to_string()),
 8167        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8168        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8169            range: lsp::Range {
 8170                start: lsp::Position {
 8171                    line: 0,
 8172                    character: 22,
 8173                },
 8174                end: lsp::Position {
 8175                    line: 0,
 8176                    character: 22,
 8177                },
 8178            },
 8179            new_text: "Some(2)".to_string(),
 8180        })),
 8181        additional_text_edits: Some(vec![lsp::TextEdit {
 8182            range: lsp::Range {
 8183                start: lsp::Position {
 8184                    line: 0,
 8185                    character: 20,
 8186                },
 8187                end: lsp::Position {
 8188                    line: 0,
 8189                    character: 22,
 8190                },
 8191            },
 8192            new_text: "".to_string(),
 8193        }]),
 8194        ..Default::default()
 8195    };
 8196
 8197    let closure_completion_item = completion_item.clone();
 8198    let counter = Arc::new(AtomicUsize::new(0));
 8199    let counter_clone = counter.clone();
 8200    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8201        let task_completion_item = closure_completion_item.clone();
 8202        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8203        async move {
 8204            Ok(Some(lsp::CompletionResponse::Array(vec![
 8205                task_completion_item,
 8206            ])))
 8207        }
 8208    });
 8209
 8210    cx.condition(|editor, _| editor.context_menu_visible())
 8211        .await;
 8212    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8213    assert!(request.next().await.is_some());
 8214    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8215
 8216    cx.simulate_keystroke("S");
 8217    cx.simulate_keystroke("o");
 8218    cx.simulate_keystroke("m");
 8219    cx.condition(|editor, _| editor.context_menu_visible())
 8220        .await;
 8221    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8222    assert!(request.next().await.is_some());
 8223    assert!(request.next().await.is_some());
 8224    assert!(request.next().await.is_some());
 8225    request.close();
 8226    assert!(request.next().await.is_none());
 8227    assert_eq!(
 8228        counter.load(atomic::Ordering::Acquire),
 8229        4,
 8230        "With the completions menu open, only one LSP request should happen per input"
 8231    );
 8232}
 8233
 8234#[gpui::test]
 8235async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8236    init_test(cx, |_| {});
 8237    let mut cx = EditorTestContext::new(cx).await;
 8238    let language = Arc::new(Language::new(
 8239        LanguageConfig {
 8240            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8241            ..Default::default()
 8242        },
 8243        Some(tree_sitter_rust::LANGUAGE.into()),
 8244    ));
 8245    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8246
 8247    // If multiple selections intersect a line, the line is only toggled once.
 8248    cx.set_state(indoc! {"
 8249        fn a() {
 8250            «//b();
 8251            ˇ»// «c();
 8252            //ˇ»  d();
 8253        }
 8254    "});
 8255
 8256    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8257
 8258    cx.assert_editor_state(indoc! {"
 8259        fn a() {
 8260            «b();
 8261            c();
 8262            ˇ» d();
 8263        }
 8264    "});
 8265
 8266    // The comment prefix is inserted at the same column for every line in a
 8267    // selection.
 8268    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8269
 8270    cx.assert_editor_state(indoc! {"
 8271        fn a() {
 8272            // «b();
 8273            // c();
 8274            ˇ»//  d();
 8275        }
 8276    "});
 8277
 8278    // If a selection ends at the beginning of a line, that line is not toggled.
 8279    cx.set_selections_state(indoc! {"
 8280        fn a() {
 8281            // b();
 8282            «// c();
 8283        ˇ»    //  d();
 8284        }
 8285    "});
 8286
 8287    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8288
 8289    cx.assert_editor_state(indoc! {"
 8290        fn a() {
 8291            // b();
 8292            «c();
 8293        ˇ»    //  d();
 8294        }
 8295    "});
 8296
 8297    // If a selection span a single line and is empty, the line is toggled.
 8298    cx.set_state(indoc! {"
 8299        fn a() {
 8300            a();
 8301            b();
 8302        ˇ
 8303        }
 8304    "});
 8305
 8306    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8307
 8308    cx.assert_editor_state(indoc! {"
 8309        fn a() {
 8310            a();
 8311            b();
 8312        //•ˇ
 8313        }
 8314    "});
 8315
 8316    // If a selection span multiple lines, empty lines are not toggled.
 8317    cx.set_state(indoc! {"
 8318        fn a() {
 8319            «a();
 8320
 8321            c();ˇ»
 8322        }
 8323    "});
 8324
 8325    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8326
 8327    cx.assert_editor_state(indoc! {"
 8328        fn a() {
 8329            // «a();
 8330
 8331            // c();ˇ»
 8332        }
 8333    "});
 8334
 8335    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8336    cx.set_state(indoc! {"
 8337        fn a() {
 8338            «// a();
 8339            /// b();
 8340            //! c();ˇ»
 8341        }
 8342    "});
 8343
 8344    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8345
 8346    cx.assert_editor_state(indoc! {"
 8347        fn a() {
 8348            «a();
 8349            b();
 8350            c();ˇ»
 8351        }
 8352    "});
 8353}
 8354
 8355#[gpui::test]
 8356async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8357    init_test(cx, |_| {});
 8358
 8359    let language = Arc::new(Language::new(
 8360        LanguageConfig {
 8361            line_comments: vec!["// ".into()],
 8362            ..Default::default()
 8363        },
 8364        Some(tree_sitter_rust::LANGUAGE.into()),
 8365    ));
 8366
 8367    let mut cx = EditorTestContext::new(cx).await;
 8368
 8369    cx.language_registry().add(language.clone());
 8370    cx.update_buffer(|buffer, cx| {
 8371        buffer.set_language(Some(language), cx);
 8372    });
 8373
 8374    let toggle_comments = &ToggleComments {
 8375        advance_downwards: true,
 8376    };
 8377
 8378    // Single cursor on one line -> advance
 8379    // Cursor moves horizontally 3 characters as well on non-blank line
 8380    cx.set_state(indoc!(
 8381        "fn a() {
 8382             ˇdog();
 8383             cat();
 8384        }"
 8385    ));
 8386    cx.update_editor(|editor, cx| {
 8387        editor.toggle_comments(toggle_comments, cx);
 8388    });
 8389    cx.assert_editor_state(indoc!(
 8390        "fn a() {
 8391             // dog();
 8392             catˇ();
 8393        }"
 8394    ));
 8395
 8396    // Single selection on one line -> don't advance
 8397    cx.set_state(indoc!(
 8398        "fn a() {
 8399             «dog()ˇ»;
 8400             cat();
 8401        }"
 8402    ));
 8403    cx.update_editor(|editor, cx| {
 8404        editor.toggle_comments(toggle_comments, cx);
 8405    });
 8406    cx.assert_editor_state(indoc!(
 8407        "fn a() {
 8408             // «dog()ˇ»;
 8409             cat();
 8410        }"
 8411    ));
 8412
 8413    // Multiple cursors on one line -> advance
 8414    cx.set_state(indoc!(
 8415        "fn a() {
 8416             ˇdˇog();
 8417             cat();
 8418        }"
 8419    ));
 8420    cx.update_editor(|editor, cx| {
 8421        editor.toggle_comments(toggle_comments, cx);
 8422    });
 8423    cx.assert_editor_state(indoc!(
 8424        "fn a() {
 8425             // dog();
 8426             catˇ(ˇ);
 8427        }"
 8428    ));
 8429
 8430    // Multiple cursors on one line, with selection -> don't advance
 8431    cx.set_state(indoc!(
 8432        "fn a() {
 8433             ˇdˇog«()ˇ»;
 8434             cat();
 8435        }"
 8436    ));
 8437    cx.update_editor(|editor, cx| {
 8438        editor.toggle_comments(toggle_comments, cx);
 8439    });
 8440    cx.assert_editor_state(indoc!(
 8441        "fn a() {
 8442             // ˇdˇog«()ˇ»;
 8443             cat();
 8444        }"
 8445    ));
 8446
 8447    // Single cursor on one line -> advance
 8448    // Cursor moves to column 0 on blank line
 8449    cx.set_state(indoc!(
 8450        "fn a() {
 8451             ˇdog();
 8452
 8453             cat();
 8454        }"
 8455    ));
 8456    cx.update_editor(|editor, cx| {
 8457        editor.toggle_comments(toggle_comments, cx);
 8458    });
 8459    cx.assert_editor_state(indoc!(
 8460        "fn a() {
 8461             // dog();
 8462        ˇ
 8463             cat();
 8464        }"
 8465    ));
 8466
 8467    // Single cursor on one line -> advance
 8468    // Cursor starts and ends at column 0
 8469    cx.set_state(indoc!(
 8470        "fn a() {
 8471         ˇ    dog();
 8472             cat();
 8473        }"
 8474    ));
 8475    cx.update_editor(|editor, cx| {
 8476        editor.toggle_comments(toggle_comments, cx);
 8477    });
 8478    cx.assert_editor_state(indoc!(
 8479        "fn a() {
 8480             // dog();
 8481         ˇ    cat();
 8482        }"
 8483    ));
 8484}
 8485
 8486#[gpui::test]
 8487async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8488    init_test(cx, |_| {});
 8489
 8490    let mut cx = EditorTestContext::new(cx).await;
 8491
 8492    let html_language = Arc::new(
 8493        Language::new(
 8494            LanguageConfig {
 8495                name: "HTML".into(),
 8496                block_comment: Some(("<!-- ".into(), " -->".into())),
 8497                ..Default::default()
 8498            },
 8499            Some(tree_sitter_html::language()),
 8500        )
 8501        .with_injection_query(
 8502            r#"
 8503            (script_element
 8504                (raw_text) @content
 8505                (#set! "language" "javascript"))
 8506            "#,
 8507        )
 8508        .unwrap(),
 8509    );
 8510
 8511    let javascript_language = Arc::new(Language::new(
 8512        LanguageConfig {
 8513            name: "JavaScript".into(),
 8514            line_comments: vec!["// ".into()],
 8515            ..Default::default()
 8516        },
 8517        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8518    ));
 8519
 8520    cx.language_registry().add(html_language.clone());
 8521    cx.language_registry().add(javascript_language.clone());
 8522    cx.update_buffer(|buffer, cx| {
 8523        buffer.set_language(Some(html_language), cx);
 8524    });
 8525
 8526    // Toggle comments for empty selections
 8527    cx.set_state(
 8528        &r#"
 8529            <p>A</p>ˇ
 8530            <p>B</p>ˇ
 8531            <p>C</p>ˇ
 8532        "#
 8533        .unindent(),
 8534    );
 8535    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8536    cx.assert_editor_state(
 8537        &r#"
 8538            <!-- <p>A</p>ˇ -->
 8539            <!-- <p>B</p>ˇ -->
 8540            <!-- <p>C</p>ˇ -->
 8541        "#
 8542        .unindent(),
 8543    );
 8544    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8545    cx.assert_editor_state(
 8546        &r#"
 8547            <p>A</p>ˇ
 8548            <p>B</p>ˇ
 8549            <p>C</p>ˇ
 8550        "#
 8551        .unindent(),
 8552    );
 8553
 8554    // Toggle comments for mixture of empty and non-empty selections, where
 8555    // multiple selections occupy a given line.
 8556    cx.set_state(
 8557        &r#"
 8558            <p>A«</p>
 8559            <p>ˇ»B</p>ˇ
 8560            <p>C«</p>
 8561            <p>ˇ»D</p>ˇ
 8562        "#
 8563        .unindent(),
 8564    );
 8565
 8566    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8567    cx.assert_editor_state(
 8568        &r#"
 8569            <!-- <p>A«</p>
 8570            <p>ˇ»B</p>ˇ -->
 8571            <!-- <p>C«</p>
 8572            <p>ˇ»D</p>ˇ -->
 8573        "#
 8574        .unindent(),
 8575    );
 8576    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8577    cx.assert_editor_state(
 8578        &r#"
 8579            <p>A«</p>
 8580            <p>ˇ»B</p>ˇ
 8581            <p>C«</p>
 8582            <p>ˇ»D</p>ˇ
 8583        "#
 8584        .unindent(),
 8585    );
 8586
 8587    // Toggle comments when different languages are active for different
 8588    // selections.
 8589    cx.set_state(
 8590        &r#"
 8591            ˇ<script>
 8592                ˇvar x = new Y();
 8593            ˇ</script>
 8594        "#
 8595        .unindent(),
 8596    );
 8597    cx.executor().run_until_parked();
 8598    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8599    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8600    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8601    cx.assert_editor_state(
 8602        &r#"
 8603            <!-- ˇ<script> -->
 8604                // ˇvar x = new Y();
 8605            // ˇ</script>
 8606        "#
 8607        .unindent(),
 8608    );
 8609}
 8610
 8611#[gpui::test]
 8612fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8613    init_test(cx, |_| {});
 8614
 8615    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8616    let multibuffer = cx.new_model(|cx| {
 8617        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8618        multibuffer.push_excerpts(
 8619            buffer.clone(),
 8620            [
 8621                ExcerptRange {
 8622                    context: Point::new(0, 0)..Point::new(0, 4),
 8623                    primary: None,
 8624                },
 8625                ExcerptRange {
 8626                    context: Point::new(1, 0)..Point::new(1, 4),
 8627                    primary: None,
 8628                },
 8629            ],
 8630            cx,
 8631        );
 8632        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8633        multibuffer
 8634    });
 8635
 8636    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8637    view.update(cx, |view, cx| {
 8638        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8639        view.change_selections(None, cx, |s| {
 8640            s.select_ranges([
 8641                Point::new(0, 0)..Point::new(0, 0),
 8642                Point::new(1, 0)..Point::new(1, 0),
 8643            ])
 8644        });
 8645
 8646        view.handle_input("X", cx);
 8647        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8648        assert_eq!(
 8649            view.selections.ranges(cx),
 8650            [
 8651                Point::new(0, 1)..Point::new(0, 1),
 8652                Point::new(1, 1)..Point::new(1, 1),
 8653            ]
 8654        );
 8655
 8656        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8657        view.change_selections(None, cx, |s| {
 8658            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8659        });
 8660        view.backspace(&Default::default(), cx);
 8661        assert_eq!(view.text(cx), "Xa\nbbb");
 8662        assert_eq!(
 8663            view.selections.ranges(cx),
 8664            [Point::new(1, 0)..Point::new(1, 0)]
 8665        );
 8666
 8667        view.change_selections(None, cx, |s| {
 8668            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8669        });
 8670        view.backspace(&Default::default(), cx);
 8671        assert_eq!(view.text(cx), "X\nbb");
 8672        assert_eq!(
 8673            view.selections.ranges(cx),
 8674            [Point::new(0, 1)..Point::new(0, 1)]
 8675        );
 8676    });
 8677}
 8678
 8679#[gpui::test]
 8680fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8681    init_test(cx, |_| {});
 8682
 8683    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8684    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8685        indoc! {"
 8686            [aaaa
 8687            (bbbb]
 8688            cccc)",
 8689        },
 8690        markers.clone(),
 8691    );
 8692    let excerpt_ranges = markers.into_iter().map(|marker| {
 8693        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8694        ExcerptRange {
 8695            context,
 8696            primary: None,
 8697        }
 8698    });
 8699    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8700    let multibuffer = cx.new_model(|cx| {
 8701        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8702        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8703        multibuffer
 8704    });
 8705
 8706    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8707    view.update(cx, |view, cx| {
 8708        let (expected_text, selection_ranges) = marked_text_ranges(
 8709            indoc! {"
 8710                aaaa
 8711                bˇbbb
 8712                bˇbbˇb
 8713                cccc"
 8714            },
 8715            true,
 8716        );
 8717        assert_eq!(view.text(cx), expected_text);
 8718        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8719
 8720        view.handle_input("X", cx);
 8721
 8722        let (expected_text, expected_selections) = marked_text_ranges(
 8723            indoc! {"
 8724                aaaa
 8725                bXˇbbXb
 8726                bXˇbbXˇb
 8727                cccc"
 8728            },
 8729            false,
 8730        );
 8731        assert_eq!(view.text(cx), expected_text);
 8732        assert_eq!(view.selections.ranges(cx), expected_selections);
 8733
 8734        view.newline(&Newline, cx);
 8735        let (expected_text, expected_selections) = marked_text_ranges(
 8736            indoc! {"
 8737                aaaa
 8738                bX
 8739                ˇbbX
 8740                b
 8741                bX
 8742                ˇbbX
 8743                ˇb
 8744                cccc"
 8745            },
 8746            false,
 8747        );
 8748        assert_eq!(view.text(cx), expected_text);
 8749        assert_eq!(view.selections.ranges(cx), expected_selections);
 8750    });
 8751}
 8752
 8753#[gpui::test]
 8754fn test_refresh_selections(cx: &mut TestAppContext) {
 8755    init_test(cx, |_| {});
 8756
 8757    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8758    let mut excerpt1_id = None;
 8759    let multibuffer = cx.new_model(|cx| {
 8760        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8761        excerpt1_id = multibuffer
 8762            .push_excerpts(
 8763                buffer.clone(),
 8764                [
 8765                    ExcerptRange {
 8766                        context: Point::new(0, 0)..Point::new(1, 4),
 8767                        primary: None,
 8768                    },
 8769                    ExcerptRange {
 8770                        context: Point::new(1, 0)..Point::new(2, 4),
 8771                        primary: None,
 8772                    },
 8773                ],
 8774                cx,
 8775            )
 8776            .into_iter()
 8777            .next();
 8778        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8779        multibuffer
 8780    });
 8781
 8782    let editor = cx.add_window(|cx| {
 8783        let mut editor = build_editor(multibuffer.clone(), cx);
 8784        let snapshot = editor.snapshot(cx);
 8785        editor.change_selections(None, cx, |s| {
 8786            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8787        });
 8788        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8789        assert_eq!(
 8790            editor.selections.ranges(cx),
 8791            [
 8792                Point::new(1, 3)..Point::new(1, 3),
 8793                Point::new(2, 1)..Point::new(2, 1),
 8794            ]
 8795        );
 8796        editor
 8797    });
 8798
 8799    // Refreshing selections is a no-op when excerpts haven't changed.
 8800    _ = editor.update(cx, |editor, cx| {
 8801        editor.change_selections(None, cx, |s| s.refresh());
 8802        assert_eq!(
 8803            editor.selections.ranges(cx),
 8804            [
 8805                Point::new(1, 3)..Point::new(1, 3),
 8806                Point::new(2, 1)..Point::new(2, 1),
 8807            ]
 8808        );
 8809    });
 8810
 8811    multibuffer.update(cx, |multibuffer, cx| {
 8812        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8813    });
 8814    _ = editor.update(cx, |editor, cx| {
 8815        // Removing an excerpt causes the first selection to become degenerate.
 8816        assert_eq!(
 8817            editor.selections.ranges(cx),
 8818            [
 8819                Point::new(0, 0)..Point::new(0, 0),
 8820                Point::new(0, 1)..Point::new(0, 1)
 8821            ]
 8822        );
 8823
 8824        // Refreshing selections will relocate the first selection to the original buffer
 8825        // location.
 8826        editor.change_selections(None, cx, |s| s.refresh());
 8827        assert_eq!(
 8828            editor.selections.ranges(cx),
 8829            [
 8830                Point::new(0, 1)..Point::new(0, 1),
 8831                Point::new(0, 3)..Point::new(0, 3)
 8832            ]
 8833        );
 8834        assert!(editor.selections.pending_anchor().is_some());
 8835    });
 8836}
 8837
 8838#[gpui::test]
 8839fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8840    init_test(cx, |_| {});
 8841
 8842    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8843    let mut excerpt1_id = None;
 8844    let multibuffer = cx.new_model(|cx| {
 8845        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 8846        excerpt1_id = multibuffer
 8847            .push_excerpts(
 8848                buffer.clone(),
 8849                [
 8850                    ExcerptRange {
 8851                        context: Point::new(0, 0)..Point::new(1, 4),
 8852                        primary: None,
 8853                    },
 8854                    ExcerptRange {
 8855                        context: Point::new(1, 0)..Point::new(2, 4),
 8856                        primary: None,
 8857                    },
 8858                ],
 8859                cx,
 8860            )
 8861            .into_iter()
 8862            .next();
 8863        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8864        multibuffer
 8865    });
 8866
 8867    let editor = cx.add_window(|cx| {
 8868        let mut editor = build_editor(multibuffer.clone(), cx);
 8869        let snapshot = editor.snapshot(cx);
 8870        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8871        assert_eq!(
 8872            editor.selections.ranges(cx),
 8873            [Point::new(1, 3)..Point::new(1, 3)]
 8874        );
 8875        editor
 8876    });
 8877
 8878    multibuffer.update(cx, |multibuffer, cx| {
 8879        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8880    });
 8881    _ = editor.update(cx, |editor, cx| {
 8882        assert_eq!(
 8883            editor.selections.ranges(cx),
 8884            [Point::new(0, 0)..Point::new(0, 0)]
 8885        );
 8886
 8887        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8888        editor.change_selections(None, cx, |s| s.refresh());
 8889        assert_eq!(
 8890            editor.selections.ranges(cx),
 8891            [Point::new(0, 3)..Point::new(0, 3)]
 8892        );
 8893        assert!(editor.selections.pending_anchor().is_some());
 8894    });
 8895}
 8896
 8897#[gpui::test]
 8898async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8899    init_test(cx, |_| {});
 8900
 8901    let language = Arc::new(
 8902        Language::new(
 8903            LanguageConfig {
 8904                brackets: BracketPairConfig {
 8905                    pairs: vec![
 8906                        BracketPair {
 8907                            start: "{".to_string(),
 8908                            end: "}".to_string(),
 8909                            close: true,
 8910                            surround: true,
 8911                            newline: true,
 8912                        },
 8913                        BracketPair {
 8914                            start: "/* ".to_string(),
 8915                            end: " */".to_string(),
 8916                            close: true,
 8917                            surround: true,
 8918                            newline: true,
 8919                        },
 8920                    ],
 8921                    ..Default::default()
 8922                },
 8923                ..Default::default()
 8924            },
 8925            Some(tree_sitter_rust::LANGUAGE.into()),
 8926        )
 8927        .with_indents_query("")
 8928        .unwrap(),
 8929    );
 8930
 8931    let text = concat!(
 8932        "{   }\n",     //
 8933        "  x\n",       //
 8934        "  /*   */\n", //
 8935        "x\n",         //
 8936        "{{} }\n",     //
 8937    );
 8938
 8939    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 8940    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8941    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8942    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 8943        .await;
 8944
 8945    view.update(cx, |view, cx| {
 8946        view.change_selections(None, cx, |s| {
 8947            s.select_display_ranges([
 8948                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 8949                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 8950                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 8951            ])
 8952        });
 8953        view.newline(&Newline, cx);
 8954
 8955        assert_eq!(
 8956            view.buffer().read(cx).read(cx).text(),
 8957            concat!(
 8958                "{ \n",    // Suppress rustfmt
 8959                "\n",      //
 8960                "}\n",     //
 8961                "  x\n",   //
 8962                "  /* \n", //
 8963                "  \n",    //
 8964                "  */\n",  //
 8965                "x\n",     //
 8966                "{{} \n",  //
 8967                "}\n",     //
 8968            )
 8969        );
 8970    });
 8971}
 8972
 8973#[gpui::test]
 8974fn test_highlighted_ranges(cx: &mut TestAppContext) {
 8975    init_test(cx, |_| {});
 8976
 8977    let editor = cx.add_window(|cx| {
 8978        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 8979        build_editor(buffer.clone(), cx)
 8980    });
 8981
 8982    _ = editor.update(cx, |editor, cx| {
 8983        struct Type1;
 8984        struct Type2;
 8985
 8986        let buffer = editor.buffer.read(cx).snapshot(cx);
 8987
 8988        let anchor_range =
 8989            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 8990
 8991        editor.highlight_background::<Type1>(
 8992            &[
 8993                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 8994                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 8995                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 8996                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 8997            ],
 8998            |_| Hsla::red(),
 8999            cx,
 9000        );
 9001        editor.highlight_background::<Type2>(
 9002            &[
 9003                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9004                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9005                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9006                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9007            ],
 9008            |_| Hsla::green(),
 9009            cx,
 9010        );
 9011
 9012        let snapshot = editor.snapshot(cx);
 9013        let mut highlighted_ranges = editor.background_highlights_in_range(
 9014            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9015            &snapshot,
 9016            cx.theme().colors(),
 9017        );
 9018        // Enforce a consistent ordering based on color without relying on the ordering of the
 9019        // highlight's `TypeId` which is non-executor.
 9020        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9021        assert_eq!(
 9022            highlighted_ranges,
 9023            &[
 9024                (
 9025                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9026                    Hsla::red(),
 9027                ),
 9028                (
 9029                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9030                    Hsla::red(),
 9031                ),
 9032                (
 9033                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9034                    Hsla::green(),
 9035                ),
 9036                (
 9037                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9038                    Hsla::green(),
 9039                ),
 9040            ]
 9041        );
 9042        assert_eq!(
 9043            editor.background_highlights_in_range(
 9044                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9045                &snapshot,
 9046                cx.theme().colors(),
 9047            ),
 9048            &[(
 9049                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9050                Hsla::red(),
 9051            )]
 9052        );
 9053    });
 9054}
 9055
 9056#[gpui::test]
 9057async fn test_following(cx: &mut gpui::TestAppContext) {
 9058    init_test(cx, |_| {});
 9059
 9060    let fs = FakeFs::new(cx.executor());
 9061    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9062
 9063    let buffer = project.update(cx, |project, cx| {
 9064        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9065        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9066    });
 9067    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9068    let follower = cx.update(|cx| {
 9069        cx.open_window(
 9070            WindowOptions {
 9071                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9072                    gpui::Point::new(px(0.), px(0.)),
 9073                    gpui::Point::new(px(10.), px(80.)),
 9074                ))),
 9075                ..Default::default()
 9076            },
 9077            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9078        )
 9079        .unwrap()
 9080    });
 9081
 9082    let is_still_following = Rc::new(RefCell::new(true));
 9083    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9084    let pending_update = Rc::new(RefCell::new(None));
 9085    _ = follower.update(cx, {
 9086        let update = pending_update.clone();
 9087        let is_still_following = is_still_following.clone();
 9088        let follower_edit_event_count = follower_edit_event_count.clone();
 9089        |_, cx| {
 9090            cx.subscribe(
 9091                &leader.root_view(cx).unwrap(),
 9092                move |_, leader, event, cx| {
 9093                    leader
 9094                        .read(cx)
 9095                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9096                },
 9097            )
 9098            .detach();
 9099
 9100            cx.subscribe(
 9101                &follower.root_view(cx).unwrap(),
 9102                move |_, _, event: &EditorEvent, _cx| {
 9103                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9104                        *is_still_following.borrow_mut() = false;
 9105                    }
 9106
 9107                    if let EditorEvent::BufferEdited = event {
 9108                        *follower_edit_event_count.borrow_mut() += 1;
 9109                    }
 9110                },
 9111            )
 9112            .detach();
 9113        }
 9114    });
 9115
 9116    // Update the selections only
 9117    _ = leader.update(cx, |leader, cx| {
 9118        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9119    });
 9120    follower
 9121        .update(cx, |follower, cx| {
 9122            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9123        })
 9124        .unwrap()
 9125        .await
 9126        .unwrap();
 9127    _ = follower.update(cx, |follower, cx| {
 9128        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9129    });
 9130    assert!(*is_still_following.borrow());
 9131    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9132
 9133    // Update the scroll position only
 9134    _ = leader.update(cx, |leader, cx| {
 9135        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9136    });
 9137    follower
 9138        .update(cx, |follower, cx| {
 9139            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9140        })
 9141        .unwrap()
 9142        .await
 9143        .unwrap();
 9144    assert_eq!(
 9145        follower
 9146            .update(cx, |follower, cx| follower.scroll_position(cx))
 9147            .unwrap(),
 9148        gpui::Point::new(1.5, 3.5)
 9149    );
 9150    assert!(*is_still_following.borrow());
 9151    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9152
 9153    // Update the selections and scroll position. The follower's scroll position is updated
 9154    // via autoscroll, not via the leader's exact scroll position.
 9155    _ = leader.update(cx, |leader, cx| {
 9156        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9157        leader.request_autoscroll(Autoscroll::newest(), cx);
 9158        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9159    });
 9160    follower
 9161        .update(cx, |follower, cx| {
 9162            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9163        })
 9164        .unwrap()
 9165        .await
 9166        .unwrap();
 9167    _ = follower.update(cx, |follower, cx| {
 9168        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9169        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9170    });
 9171    assert!(*is_still_following.borrow());
 9172
 9173    // Creating a pending selection that precedes another selection
 9174    _ = leader.update(cx, |leader, cx| {
 9175        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9176        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9177    });
 9178    follower
 9179        .update(cx, |follower, cx| {
 9180            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9181        })
 9182        .unwrap()
 9183        .await
 9184        .unwrap();
 9185    _ = follower.update(cx, |follower, cx| {
 9186        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9187    });
 9188    assert!(*is_still_following.borrow());
 9189
 9190    // Extend the pending selection so that it surrounds another selection
 9191    _ = leader.update(cx, |leader, cx| {
 9192        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9193    });
 9194    follower
 9195        .update(cx, |follower, cx| {
 9196            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9197        })
 9198        .unwrap()
 9199        .await
 9200        .unwrap();
 9201    _ = follower.update(cx, |follower, cx| {
 9202        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9203    });
 9204
 9205    // Scrolling locally breaks the follow
 9206    _ = follower.update(cx, |follower, cx| {
 9207        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9208        follower.set_scroll_anchor(
 9209            ScrollAnchor {
 9210                anchor: top_anchor,
 9211                offset: gpui::Point::new(0.0, 0.5),
 9212            },
 9213            cx,
 9214        );
 9215    });
 9216    assert!(!(*is_still_following.borrow()));
 9217}
 9218
 9219#[gpui::test]
 9220async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9221    init_test(cx, |_| {});
 9222
 9223    let fs = FakeFs::new(cx.executor());
 9224    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9225    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9226    let pane = workspace
 9227        .update(cx, |workspace, _| workspace.active_pane().clone())
 9228        .unwrap();
 9229
 9230    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9231
 9232    let leader = pane.update(cx, |_, cx| {
 9233        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 9234        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9235    });
 9236
 9237    // Start following the editor when it has no excerpts.
 9238    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9239    let follower_1 = cx
 9240        .update_window(*workspace.deref(), |_, cx| {
 9241            Editor::from_state_proto(
 9242                workspace.root_view(cx).unwrap(),
 9243                ViewId {
 9244                    creator: Default::default(),
 9245                    id: 0,
 9246                },
 9247                &mut state_message,
 9248                cx,
 9249            )
 9250        })
 9251        .unwrap()
 9252        .unwrap()
 9253        .await
 9254        .unwrap();
 9255
 9256    let update_message = Rc::new(RefCell::new(None));
 9257    follower_1.update(cx, {
 9258        let update = update_message.clone();
 9259        |_, cx| {
 9260            cx.subscribe(&leader, move |_, leader, event, cx| {
 9261                leader
 9262                    .read(cx)
 9263                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9264            })
 9265            .detach();
 9266        }
 9267    });
 9268
 9269    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9270        (
 9271            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9272            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9273        )
 9274    });
 9275
 9276    // Insert some excerpts.
 9277    leader.update(cx, |leader, cx| {
 9278        leader.buffer.update(cx, |multibuffer, cx| {
 9279            let excerpt_ids = multibuffer.push_excerpts(
 9280                buffer_1.clone(),
 9281                [
 9282                    ExcerptRange {
 9283                        context: 1..6,
 9284                        primary: None,
 9285                    },
 9286                    ExcerptRange {
 9287                        context: 12..15,
 9288                        primary: None,
 9289                    },
 9290                    ExcerptRange {
 9291                        context: 0..3,
 9292                        primary: None,
 9293                    },
 9294                ],
 9295                cx,
 9296            );
 9297            multibuffer.insert_excerpts_after(
 9298                excerpt_ids[0],
 9299                buffer_2.clone(),
 9300                [
 9301                    ExcerptRange {
 9302                        context: 8..12,
 9303                        primary: None,
 9304                    },
 9305                    ExcerptRange {
 9306                        context: 0..6,
 9307                        primary: None,
 9308                    },
 9309                ],
 9310                cx,
 9311            );
 9312        });
 9313    });
 9314
 9315    // Apply the update of adding the excerpts.
 9316    follower_1
 9317        .update(cx, |follower, cx| {
 9318            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9319        })
 9320        .await
 9321        .unwrap();
 9322    assert_eq!(
 9323        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9324        leader.update(cx, |editor, cx| editor.text(cx))
 9325    );
 9326    update_message.borrow_mut().take();
 9327
 9328    // Start following separately after it already has excerpts.
 9329    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9330    let follower_2 = cx
 9331        .update_window(*workspace.deref(), |_, cx| {
 9332            Editor::from_state_proto(
 9333                workspace.root_view(cx).unwrap().clone(),
 9334                ViewId {
 9335                    creator: Default::default(),
 9336                    id: 0,
 9337                },
 9338                &mut state_message,
 9339                cx,
 9340            )
 9341        })
 9342        .unwrap()
 9343        .unwrap()
 9344        .await
 9345        .unwrap();
 9346    assert_eq!(
 9347        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9348        leader.update(cx, |editor, cx| editor.text(cx))
 9349    );
 9350
 9351    // Remove some excerpts.
 9352    leader.update(cx, |leader, cx| {
 9353        leader.buffer.update(cx, |multibuffer, cx| {
 9354            let excerpt_ids = multibuffer.excerpt_ids();
 9355            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9356            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9357        });
 9358    });
 9359
 9360    // Apply the update of removing the excerpts.
 9361    follower_1
 9362        .update(cx, |follower, cx| {
 9363            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9364        })
 9365        .await
 9366        .unwrap();
 9367    follower_2
 9368        .update(cx, |follower, cx| {
 9369            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9370        })
 9371        .await
 9372        .unwrap();
 9373    update_message.borrow_mut().take();
 9374    assert_eq!(
 9375        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9376        leader.update(cx, |editor, cx| editor.text(cx))
 9377    );
 9378}
 9379
 9380#[gpui::test]
 9381async fn go_to_prev_overlapping_diagnostic(
 9382    executor: BackgroundExecutor,
 9383    cx: &mut gpui::TestAppContext,
 9384) {
 9385    init_test(cx, |_| {});
 9386
 9387    let mut cx = EditorTestContext::new(cx).await;
 9388    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9389
 9390    cx.set_state(indoc! {"
 9391        ˇfn func(abc def: i32) -> u32 {
 9392        }
 9393    "});
 9394
 9395    cx.update(|cx| {
 9396        project.update(cx, |project, cx| {
 9397            project
 9398                .update_diagnostics(
 9399                    LanguageServerId(0),
 9400                    lsp::PublishDiagnosticsParams {
 9401                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9402                        version: None,
 9403                        diagnostics: vec![
 9404                            lsp::Diagnostic {
 9405                                range: lsp::Range::new(
 9406                                    lsp::Position::new(0, 11),
 9407                                    lsp::Position::new(0, 12),
 9408                                ),
 9409                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9410                                ..Default::default()
 9411                            },
 9412                            lsp::Diagnostic {
 9413                                range: lsp::Range::new(
 9414                                    lsp::Position::new(0, 12),
 9415                                    lsp::Position::new(0, 15),
 9416                                ),
 9417                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9418                                ..Default::default()
 9419                            },
 9420                            lsp::Diagnostic {
 9421                                range: lsp::Range::new(
 9422                                    lsp::Position::new(0, 25),
 9423                                    lsp::Position::new(0, 28),
 9424                                ),
 9425                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9426                                ..Default::default()
 9427                            },
 9428                        ],
 9429                    },
 9430                    &[],
 9431                    cx,
 9432                )
 9433                .unwrap()
 9434        });
 9435    });
 9436
 9437    executor.run_until_parked();
 9438
 9439    cx.update_editor(|editor, cx| {
 9440        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9441    });
 9442
 9443    cx.assert_editor_state(indoc! {"
 9444        fn func(abc def: i32) -> ˇu32 {
 9445        }
 9446    "});
 9447
 9448    cx.update_editor(|editor, cx| {
 9449        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9450    });
 9451
 9452    cx.assert_editor_state(indoc! {"
 9453        fn func(abc ˇdef: i32) -> u32 {
 9454        }
 9455    "});
 9456
 9457    cx.update_editor(|editor, cx| {
 9458        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9459    });
 9460
 9461    cx.assert_editor_state(indoc! {"
 9462        fn func(abcˇ def: i32) -> u32 {
 9463        }
 9464    "});
 9465
 9466    cx.update_editor(|editor, cx| {
 9467        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9468    });
 9469
 9470    cx.assert_editor_state(indoc! {"
 9471        fn func(abc def: i32) -> ˇu32 {
 9472        }
 9473    "});
 9474}
 9475
 9476#[gpui::test]
 9477async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9478    init_test(cx, |_| {});
 9479
 9480    let mut cx = EditorTestContext::new(cx).await;
 9481
 9482    cx.set_state(indoc! {"
 9483        fn func(abˇc def: i32) -> u32 {
 9484        }
 9485    "});
 9486    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9487
 9488    cx.update(|cx| {
 9489        project.update(cx, |project, cx| {
 9490            project.update_diagnostics(
 9491                LanguageServerId(0),
 9492                lsp::PublishDiagnosticsParams {
 9493                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9494                    version: None,
 9495                    diagnostics: vec![lsp::Diagnostic {
 9496                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9497                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9498                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9499                        ..Default::default()
 9500                    }],
 9501                },
 9502                &[],
 9503                cx,
 9504            )
 9505        })
 9506    }).unwrap();
 9507    cx.run_until_parked();
 9508    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9509    cx.run_until_parked();
 9510    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9511}
 9512
 9513#[gpui::test]
 9514async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9515    init_test(cx, |_| {});
 9516
 9517    let mut cx = EditorTestContext::new(cx).await;
 9518
 9519    let diff_base = r#"
 9520        use some::mod;
 9521
 9522        const A: u32 = 42;
 9523
 9524        fn main() {
 9525            println!("hello");
 9526
 9527            println!("world");
 9528        }
 9529        "#
 9530    .unindent();
 9531
 9532    // Edits are modified, removed, modified, added
 9533    cx.set_state(
 9534        &r#"
 9535        use some::modified;
 9536
 9537        ˇ
 9538        fn main() {
 9539            println!("hello there");
 9540
 9541            println!("around the");
 9542            println!("world");
 9543        }
 9544        "#
 9545        .unindent(),
 9546    );
 9547
 9548    cx.set_diff_base(Some(&diff_base));
 9549    executor.run_until_parked();
 9550
 9551    cx.update_editor(|editor, cx| {
 9552        //Wrap around the bottom of the buffer
 9553        for _ in 0..3 {
 9554            editor.go_to_hunk(&GoToHunk, cx);
 9555        }
 9556    });
 9557
 9558    cx.assert_editor_state(
 9559        &r#"
 9560        ˇuse some::modified;
 9561
 9562
 9563        fn main() {
 9564            println!("hello there");
 9565
 9566            println!("around the");
 9567            println!("world");
 9568        }
 9569        "#
 9570        .unindent(),
 9571    );
 9572
 9573    cx.update_editor(|editor, cx| {
 9574        //Wrap around the top of the buffer
 9575        for _ in 0..2 {
 9576            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9577        }
 9578    });
 9579
 9580    cx.assert_editor_state(
 9581        &r#"
 9582        use some::modified;
 9583
 9584
 9585        fn main() {
 9586        ˇ    println!("hello there");
 9587
 9588            println!("around the");
 9589            println!("world");
 9590        }
 9591        "#
 9592        .unindent(),
 9593    );
 9594
 9595    cx.update_editor(|editor, cx| {
 9596        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9597    });
 9598
 9599    cx.assert_editor_state(
 9600        &r#"
 9601        use some::modified;
 9602
 9603        ˇ
 9604        fn main() {
 9605            println!("hello there");
 9606
 9607            println!("around the");
 9608            println!("world");
 9609        }
 9610        "#
 9611        .unindent(),
 9612    );
 9613
 9614    cx.update_editor(|editor, cx| {
 9615        for _ in 0..3 {
 9616            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9617        }
 9618    });
 9619
 9620    cx.assert_editor_state(
 9621        &r#"
 9622        use some::modified;
 9623
 9624
 9625        fn main() {
 9626        ˇ    println!("hello there");
 9627
 9628            println!("around the");
 9629            println!("world");
 9630        }
 9631        "#
 9632        .unindent(),
 9633    );
 9634
 9635    cx.update_editor(|editor, cx| {
 9636        editor.fold(&Fold, cx);
 9637
 9638        //Make sure that the fold only gets one hunk
 9639        for _ in 0..4 {
 9640            editor.go_to_hunk(&GoToHunk, cx);
 9641        }
 9642    });
 9643
 9644    cx.assert_editor_state(
 9645        &r#"
 9646        ˇuse some::modified;
 9647
 9648
 9649        fn main() {
 9650            println!("hello there");
 9651
 9652            println!("around the");
 9653            println!("world");
 9654        }
 9655        "#
 9656        .unindent(),
 9657    );
 9658}
 9659
 9660#[test]
 9661fn test_split_words() {
 9662    fn split(text: &str) -> Vec<&str> {
 9663        split_words(text).collect()
 9664    }
 9665
 9666    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9667    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9668    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9669    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9670    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9671    assert_eq!(split("helloworld"), &["helloworld"]);
 9672
 9673    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9674}
 9675
 9676#[gpui::test]
 9677async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9678    init_test(cx, |_| {});
 9679
 9680    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9681    let mut assert = |before, after| {
 9682        let _state_context = cx.set_state(before);
 9683        cx.update_editor(|editor, cx| {
 9684            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9685        });
 9686        cx.assert_editor_state(after);
 9687    };
 9688
 9689    // Outside bracket jumps to outside of matching bracket
 9690    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9691    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9692
 9693    // Inside bracket jumps to inside of matching bracket
 9694    assert("console.log(ˇvar);", "console.log(varˇ);");
 9695    assert("console.log(varˇ);", "console.log(ˇvar);");
 9696
 9697    // When outside a bracket and inside, favor jumping to the inside bracket
 9698    assert(
 9699        "console.log('foo', [1, 2, 3]ˇ);",
 9700        "console.log(ˇ'foo', [1, 2, 3]);",
 9701    );
 9702    assert(
 9703        "console.log(ˇ'foo', [1, 2, 3]);",
 9704        "console.log('foo', [1, 2, 3]ˇ);",
 9705    );
 9706
 9707    // Bias forward if two options are equally likely
 9708    assert(
 9709        "let result = curried_fun()ˇ();",
 9710        "let result = curried_fun()()ˇ;",
 9711    );
 9712
 9713    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9714    assert(
 9715        indoc! {"
 9716            function test() {
 9717                console.log('test')ˇ
 9718            }"},
 9719        indoc! {"
 9720            function test() {
 9721                console.logˇ('test')
 9722            }"},
 9723    );
 9724}
 9725
 9726#[gpui::test]
 9727async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9728    init_test(cx, |_| {});
 9729
 9730    let fs = FakeFs::new(cx.executor());
 9731    fs.insert_tree(
 9732        "/a",
 9733        json!({
 9734            "main.rs": "fn main() { let a = 5; }",
 9735            "other.rs": "// Test file",
 9736        }),
 9737    )
 9738    .await;
 9739    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9740
 9741    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9742    language_registry.add(Arc::new(Language::new(
 9743        LanguageConfig {
 9744            name: "Rust".into(),
 9745            matcher: LanguageMatcher {
 9746                path_suffixes: vec!["rs".to_string()],
 9747                ..Default::default()
 9748            },
 9749            brackets: BracketPairConfig {
 9750                pairs: vec![BracketPair {
 9751                    start: "{".to_string(),
 9752                    end: "}".to_string(),
 9753                    close: true,
 9754                    surround: true,
 9755                    newline: true,
 9756                }],
 9757                disabled_scopes_by_bracket_ix: Vec::new(),
 9758            },
 9759            ..Default::default()
 9760        },
 9761        Some(tree_sitter_rust::LANGUAGE.into()),
 9762    )));
 9763    let mut fake_servers = language_registry.register_fake_lsp(
 9764        "Rust",
 9765        FakeLspAdapter {
 9766            capabilities: lsp::ServerCapabilities {
 9767                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9768                    first_trigger_character: "{".to_string(),
 9769                    more_trigger_character: None,
 9770                }),
 9771                ..Default::default()
 9772            },
 9773            ..Default::default()
 9774        },
 9775    );
 9776
 9777    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9778
 9779    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9780
 9781    let worktree_id = workspace
 9782        .update(cx, |workspace, cx| {
 9783            workspace.project().update(cx, |project, cx| {
 9784                project.worktrees(cx).next().unwrap().read(cx).id()
 9785            })
 9786        })
 9787        .unwrap();
 9788
 9789    let buffer = project
 9790        .update(cx, |project, cx| {
 9791            project.open_local_buffer("/a/main.rs", cx)
 9792        })
 9793        .await
 9794        .unwrap();
 9795    cx.executor().run_until_parked();
 9796    cx.executor().start_waiting();
 9797    let fake_server = fake_servers.next().await.unwrap();
 9798    let editor_handle = workspace
 9799        .update(cx, |workspace, cx| {
 9800            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9801        })
 9802        .unwrap()
 9803        .await
 9804        .unwrap()
 9805        .downcast::<Editor>()
 9806        .unwrap();
 9807
 9808    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9809        assert_eq!(
 9810            params.text_document_position.text_document.uri,
 9811            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9812        );
 9813        assert_eq!(
 9814            params.text_document_position.position,
 9815            lsp::Position::new(0, 21),
 9816        );
 9817
 9818        Ok(Some(vec![lsp::TextEdit {
 9819            new_text: "]".to_string(),
 9820            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9821        }]))
 9822    });
 9823
 9824    editor_handle.update(cx, |editor, cx| {
 9825        editor.focus(cx);
 9826        editor.change_selections(None, cx, |s| {
 9827            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9828        });
 9829        editor.handle_input("{", cx);
 9830    });
 9831
 9832    cx.executor().run_until_parked();
 9833
 9834    buffer.update(cx, |buffer, _| {
 9835        assert_eq!(
 9836            buffer.text(),
 9837            "fn main() { let a = {5}; }",
 9838            "No extra braces from on type formatting should appear in the buffer"
 9839        )
 9840    });
 9841}
 9842
 9843#[gpui::test]
 9844async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9845    init_test(cx, |_| {});
 9846
 9847    let fs = FakeFs::new(cx.executor());
 9848    fs.insert_tree(
 9849        "/a",
 9850        json!({
 9851            "main.rs": "fn main() { let a = 5; }",
 9852            "other.rs": "// Test file",
 9853        }),
 9854    )
 9855    .await;
 9856
 9857    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9858
 9859    let server_restarts = Arc::new(AtomicUsize::new(0));
 9860    let closure_restarts = Arc::clone(&server_restarts);
 9861    let language_server_name = "test language server";
 9862    let language_name: LanguageName = "Rust".into();
 9863
 9864    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9865    language_registry.add(Arc::new(Language::new(
 9866        LanguageConfig {
 9867            name: language_name.clone(),
 9868            matcher: LanguageMatcher {
 9869                path_suffixes: vec!["rs".to_string()],
 9870                ..Default::default()
 9871            },
 9872            ..Default::default()
 9873        },
 9874        Some(tree_sitter_rust::LANGUAGE.into()),
 9875    )));
 9876    let mut fake_servers = language_registry.register_fake_lsp(
 9877        "Rust",
 9878        FakeLspAdapter {
 9879            name: language_server_name,
 9880            initialization_options: Some(json!({
 9881                "testOptionValue": true
 9882            })),
 9883            initializer: Some(Box::new(move |fake_server| {
 9884                let task_restarts = Arc::clone(&closure_restarts);
 9885                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9886                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9887                    futures::future::ready(Ok(()))
 9888                });
 9889            })),
 9890            ..Default::default()
 9891        },
 9892    );
 9893
 9894    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9895    let _buffer = project
 9896        .update(cx, |project, cx| {
 9897            project.open_local_buffer("/a/main.rs", cx)
 9898        })
 9899        .await
 9900        .unwrap();
 9901    let _fake_server = fake_servers.next().await.unwrap();
 9902    update_test_language_settings(cx, |language_settings| {
 9903        language_settings.languages.insert(
 9904            language_name.clone(),
 9905            LanguageSettingsContent {
 9906                tab_size: NonZeroU32::new(8),
 9907                ..Default::default()
 9908            },
 9909        );
 9910    });
 9911    cx.executor().run_until_parked();
 9912    assert_eq!(
 9913        server_restarts.load(atomic::Ordering::Acquire),
 9914        0,
 9915        "Should not restart LSP server on an unrelated change"
 9916    );
 9917
 9918    update_test_project_settings(cx, |project_settings| {
 9919        project_settings.lsp.insert(
 9920            "Some other server name".into(),
 9921            LspSettings {
 9922                binary: None,
 9923                settings: None,
 9924                initialization_options: Some(json!({
 9925                    "some other init value": false
 9926                })),
 9927            },
 9928        );
 9929    });
 9930    cx.executor().run_until_parked();
 9931    assert_eq!(
 9932        server_restarts.load(atomic::Ordering::Acquire),
 9933        0,
 9934        "Should not restart LSP server on an unrelated LSP settings change"
 9935    );
 9936
 9937    update_test_project_settings(cx, |project_settings| {
 9938        project_settings.lsp.insert(
 9939            language_server_name.into(),
 9940            LspSettings {
 9941                binary: None,
 9942                settings: None,
 9943                initialization_options: Some(json!({
 9944                    "anotherInitValue": false
 9945                })),
 9946            },
 9947        );
 9948    });
 9949    cx.executor().run_until_parked();
 9950    assert_eq!(
 9951        server_restarts.load(atomic::Ordering::Acquire),
 9952        1,
 9953        "Should restart LSP server on a related LSP settings change"
 9954    );
 9955
 9956    update_test_project_settings(cx, |project_settings| {
 9957        project_settings.lsp.insert(
 9958            language_server_name.into(),
 9959            LspSettings {
 9960                binary: None,
 9961                settings: None,
 9962                initialization_options: Some(json!({
 9963                    "anotherInitValue": false
 9964                })),
 9965            },
 9966        );
 9967    });
 9968    cx.executor().run_until_parked();
 9969    assert_eq!(
 9970        server_restarts.load(atomic::Ordering::Acquire),
 9971        1,
 9972        "Should not restart LSP server on a related LSP settings change that is the same"
 9973    );
 9974
 9975    update_test_project_settings(cx, |project_settings| {
 9976        project_settings.lsp.insert(
 9977            language_server_name.into(),
 9978            LspSettings {
 9979                binary: None,
 9980                settings: None,
 9981                initialization_options: None,
 9982            },
 9983        );
 9984    });
 9985    cx.executor().run_until_parked();
 9986    assert_eq!(
 9987        server_restarts.load(atomic::Ordering::Acquire),
 9988        2,
 9989        "Should restart LSP server on another related LSP settings change"
 9990    );
 9991}
 9992
 9993#[gpui::test]
 9994async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 9995    init_test(cx, |_| {});
 9996
 9997    let mut cx = EditorLspTestContext::new_rust(
 9998        lsp::ServerCapabilities {
 9999            completion_provider: Some(lsp::CompletionOptions {
10000                trigger_characters: Some(vec![".".to_string()]),
10001                resolve_provider: Some(true),
10002                ..Default::default()
10003            }),
10004            ..Default::default()
10005        },
10006        cx,
10007    )
10008    .await;
10009
10010    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10011    cx.simulate_keystroke(".");
10012    let completion_item = lsp::CompletionItem {
10013        label: "some".into(),
10014        kind: Some(lsp::CompletionItemKind::SNIPPET),
10015        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10016        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10017            kind: lsp::MarkupKind::Markdown,
10018            value: "```rust\nSome(2)\n```".to_string(),
10019        })),
10020        deprecated: Some(false),
10021        sort_text: Some("fffffff2".to_string()),
10022        filter_text: Some("some".to_string()),
10023        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10024        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10025            range: lsp::Range {
10026                start: lsp::Position {
10027                    line: 0,
10028                    character: 22,
10029                },
10030                end: lsp::Position {
10031                    line: 0,
10032                    character: 22,
10033                },
10034            },
10035            new_text: "Some(2)".to_string(),
10036        })),
10037        additional_text_edits: Some(vec![lsp::TextEdit {
10038            range: lsp::Range {
10039                start: lsp::Position {
10040                    line: 0,
10041                    character: 20,
10042                },
10043                end: lsp::Position {
10044                    line: 0,
10045                    character: 22,
10046                },
10047            },
10048            new_text: "".to_string(),
10049        }]),
10050        ..Default::default()
10051    };
10052
10053    let closure_completion_item = completion_item.clone();
10054    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10055        let task_completion_item = closure_completion_item.clone();
10056        async move {
10057            Ok(Some(lsp::CompletionResponse::Array(vec![
10058                task_completion_item,
10059            ])))
10060        }
10061    });
10062
10063    request.next().await;
10064
10065    cx.condition(|editor, _| editor.context_menu_visible())
10066        .await;
10067    let apply_additional_edits = cx.update_editor(|editor, cx| {
10068        editor
10069            .confirm_completion(&ConfirmCompletion::default(), cx)
10070            .unwrap()
10071    });
10072    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10073
10074    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10075        let task_completion_item = completion_item.clone();
10076        async move { Ok(task_completion_item) }
10077    })
10078    .next()
10079    .await
10080    .unwrap();
10081    apply_additional_edits.await.unwrap();
10082    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10083}
10084
10085#[gpui::test]
10086async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10087    init_test(cx, |_| {});
10088
10089    let mut cx = EditorLspTestContext::new(
10090        Language::new(
10091            LanguageConfig {
10092                matcher: LanguageMatcher {
10093                    path_suffixes: vec!["jsx".into()],
10094                    ..Default::default()
10095                },
10096                overrides: [(
10097                    "element".into(),
10098                    LanguageConfigOverride {
10099                        word_characters: Override::Set(['-'].into_iter().collect()),
10100                        ..Default::default()
10101                    },
10102                )]
10103                .into_iter()
10104                .collect(),
10105                ..Default::default()
10106            },
10107            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10108        )
10109        .with_override_query("(jsx_self_closing_element) @element")
10110        .unwrap(),
10111        lsp::ServerCapabilities {
10112            completion_provider: Some(lsp::CompletionOptions {
10113                trigger_characters: Some(vec![":".to_string()]),
10114                ..Default::default()
10115            }),
10116            ..Default::default()
10117        },
10118        cx,
10119    )
10120    .await;
10121
10122    cx.lsp
10123        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10124            Ok(Some(lsp::CompletionResponse::Array(vec![
10125                lsp::CompletionItem {
10126                    label: "bg-blue".into(),
10127                    ..Default::default()
10128                },
10129                lsp::CompletionItem {
10130                    label: "bg-red".into(),
10131                    ..Default::default()
10132                },
10133                lsp::CompletionItem {
10134                    label: "bg-yellow".into(),
10135                    ..Default::default()
10136                },
10137            ])))
10138        });
10139
10140    cx.set_state(r#"<p class="bgˇ" />"#);
10141
10142    // Trigger completion when typing a dash, because the dash is an extra
10143    // word character in the 'element' scope, which contains the cursor.
10144    cx.simulate_keystroke("-");
10145    cx.executor().run_until_parked();
10146    cx.update_editor(|editor, _| {
10147        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10148            assert_eq!(
10149                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10150                &["bg-red", "bg-blue", "bg-yellow"]
10151            );
10152        } else {
10153            panic!("expected completion menu to be open");
10154        }
10155    });
10156
10157    cx.simulate_keystroke("l");
10158    cx.executor().run_until_parked();
10159    cx.update_editor(|editor, _| {
10160        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10161            assert_eq!(
10162                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10163                &["bg-blue", "bg-yellow"]
10164            );
10165        } else {
10166            panic!("expected completion menu to be open");
10167        }
10168    });
10169
10170    // When filtering completions, consider the character after the '-' to
10171    // be the start of a subword.
10172    cx.set_state(r#"<p class="yelˇ" />"#);
10173    cx.simulate_keystroke("l");
10174    cx.executor().run_until_parked();
10175    cx.update_editor(|editor, _| {
10176        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10177            assert_eq!(
10178                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10179                &["bg-yellow"]
10180            );
10181        } else {
10182            panic!("expected completion menu to be open");
10183        }
10184    });
10185}
10186
10187#[gpui::test]
10188async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10189    init_test(cx, |settings| {
10190        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10191            FormatterList(vec![Formatter::Prettier].into()),
10192        ))
10193    });
10194
10195    let fs = FakeFs::new(cx.executor());
10196    fs.insert_file("/file.ts", Default::default()).await;
10197
10198    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10199    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10200
10201    language_registry.add(Arc::new(Language::new(
10202        LanguageConfig {
10203            name: "TypeScript".into(),
10204            matcher: LanguageMatcher {
10205                path_suffixes: vec!["ts".to_string()],
10206                ..Default::default()
10207            },
10208            ..Default::default()
10209        },
10210        Some(tree_sitter_rust::LANGUAGE.into()),
10211    )));
10212    update_test_language_settings(cx, |settings| {
10213        settings.defaults.prettier = Some(PrettierSettings {
10214            allowed: true,
10215            ..PrettierSettings::default()
10216        });
10217    });
10218
10219    let test_plugin = "test_plugin";
10220    let _ = language_registry.register_fake_lsp(
10221        "TypeScript",
10222        FakeLspAdapter {
10223            prettier_plugins: vec![test_plugin],
10224            ..Default::default()
10225        },
10226    );
10227
10228    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10229    let buffer = project
10230        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10231        .await
10232        .unwrap();
10233
10234    let buffer_text = "one\ntwo\nthree\n";
10235    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10236    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10237    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10238
10239    editor
10240        .update(cx, |editor, cx| {
10241            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
10242        })
10243        .unwrap()
10244        .await;
10245    assert_eq!(
10246        editor.update(cx, |editor, cx| editor.text(cx)),
10247        buffer_text.to_string() + prettier_format_suffix,
10248        "Test prettier formatting was not applied to the original buffer text",
10249    );
10250
10251    update_test_language_settings(cx, |settings| {
10252        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10253    });
10254    let format = editor.update(cx, |editor, cx| {
10255        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
10256    });
10257    format.await.unwrap();
10258    assert_eq!(
10259        editor.update(cx, |editor, cx| editor.text(cx)),
10260        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10261        "Autoformatting (via test prettier) was not applied to the original buffer text",
10262    );
10263}
10264
10265#[gpui::test]
10266async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10267    init_test(cx, |_| {});
10268    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10269    let base_text = indoc! {r#"struct Row;
10270struct Row1;
10271struct Row2;
10272
10273struct Row4;
10274struct Row5;
10275struct Row6;
10276
10277struct Row8;
10278struct Row9;
10279struct Row10;"#};
10280
10281    // When addition hunks are not adjacent to carets, no hunk revert is performed
10282    assert_hunk_revert(
10283        indoc! {r#"struct Row;
10284                   struct Row1;
10285                   struct Row1.1;
10286                   struct Row1.2;
10287                   struct Row2;ˇ
10288
10289                   struct Row4;
10290                   struct Row5;
10291                   struct Row6;
10292
10293                   struct Row8;
10294                   ˇstruct Row9;
10295                   struct Row9.1;
10296                   struct Row9.2;
10297                   struct Row9.3;
10298                   struct Row10;"#},
10299        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10300        indoc! {r#"struct Row;
10301                   struct Row1;
10302                   struct Row1.1;
10303                   struct Row1.2;
10304                   struct Row2;ˇ
10305
10306                   struct Row4;
10307                   struct Row5;
10308                   struct Row6;
10309
10310                   struct Row8;
10311                   ˇstruct Row9;
10312                   struct Row9.1;
10313                   struct Row9.2;
10314                   struct Row9.3;
10315                   struct Row10;"#},
10316        base_text,
10317        &mut cx,
10318    );
10319    // Same for selections
10320    assert_hunk_revert(
10321        indoc! {r#"struct Row;
10322                   struct Row1;
10323                   struct Row2;
10324                   struct Row2.1;
10325                   struct Row2.2;
10326                   «ˇ
10327                   struct Row4;
10328                   struct» Row5;
10329                   «struct Row6;
10330                   ˇ»
10331                   struct Row9.1;
10332                   struct Row9.2;
10333                   struct Row9.3;
10334                   struct Row8;
10335                   struct Row9;
10336                   struct Row10;"#},
10337        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10338        indoc! {r#"struct Row;
10339                   struct Row1;
10340                   struct Row2;
10341                   struct Row2.1;
10342                   struct Row2.2;
10343                   «ˇ
10344                   struct Row4;
10345                   struct» Row5;
10346                   «struct Row6;
10347                   ˇ»
10348                   struct Row9.1;
10349                   struct Row9.2;
10350                   struct Row9.3;
10351                   struct Row8;
10352                   struct Row9;
10353                   struct Row10;"#},
10354        base_text,
10355        &mut cx,
10356    );
10357
10358    // When carets and selections intersect the addition hunks, those are reverted.
10359    // Adjacent carets got merged.
10360    assert_hunk_revert(
10361        indoc! {r#"struct Row;
10362                   ˇ// something on the top
10363                   struct Row1;
10364                   struct Row2;
10365                   struct Roˇw3.1;
10366                   struct Row2.2;
10367                   struct Row2.3;ˇ
10368
10369                   struct Row4;
10370                   struct ˇRow5.1;
10371                   struct Row5.2;
10372                   struct «Rowˇ»5.3;
10373                   struct Row5;
10374                   struct Row6;
10375                   ˇ
10376                   struct Row9.1;
10377                   struct «Rowˇ»9.2;
10378                   struct «ˇRow»9.3;
10379                   struct Row8;
10380                   struct Row9;
10381                   «ˇ// something on bottom»
10382                   struct Row10;"#},
10383        vec![
10384            DiffHunkStatus::Added,
10385            DiffHunkStatus::Added,
10386            DiffHunkStatus::Added,
10387            DiffHunkStatus::Added,
10388            DiffHunkStatus::Added,
10389        ],
10390        indoc! {r#"struct Row;
10391                   ˇstruct Row1;
10392                   struct Row2;
10393                   ˇ
10394                   struct Row4;
10395                   ˇstruct Row5;
10396                   struct Row6;
10397                   ˇ
10398                   ˇstruct Row8;
10399                   struct Row9;
10400                   ˇstruct Row10;"#},
10401        base_text,
10402        &mut cx,
10403    );
10404}
10405
10406#[gpui::test]
10407async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10408    init_test(cx, |_| {});
10409    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10410    let base_text = indoc! {r#"struct Row;
10411struct Row1;
10412struct Row2;
10413
10414struct Row4;
10415struct Row5;
10416struct Row6;
10417
10418struct Row8;
10419struct Row9;
10420struct Row10;"#};
10421
10422    // Modification hunks behave the same as the addition ones.
10423    assert_hunk_revert(
10424        indoc! {r#"struct Row;
10425                   struct Row1;
10426                   struct Row33;
10427                   ˇ
10428                   struct Row4;
10429                   struct Row5;
10430                   struct Row6;
10431                   ˇ
10432                   struct Row99;
10433                   struct Row9;
10434                   struct Row10;"#},
10435        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10436        indoc! {r#"struct Row;
10437                   struct Row1;
10438                   struct Row33;
10439                   ˇ
10440                   struct Row4;
10441                   struct Row5;
10442                   struct Row6;
10443                   ˇ
10444                   struct Row99;
10445                   struct Row9;
10446                   struct Row10;"#},
10447        base_text,
10448        &mut cx,
10449    );
10450    assert_hunk_revert(
10451        indoc! {r#"struct Row;
10452                   struct Row1;
10453                   struct Row33;
10454                   «ˇ
10455                   struct Row4;
10456                   struct» Row5;
10457                   «struct Row6;
10458                   ˇ»
10459                   struct Row99;
10460                   struct Row9;
10461                   struct Row10;"#},
10462        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10463        indoc! {r#"struct Row;
10464                   struct Row1;
10465                   struct Row33;
10466                   «ˇ
10467                   struct Row4;
10468                   struct» Row5;
10469                   «struct Row6;
10470                   ˇ»
10471                   struct Row99;
10472                   struct Row9;
10473                   struct Row10;"#},
10474        base_text,
10475        &mut cx,
10476    );
10477
10478    assert_hunk_revert(
10479        indoc! {r#"ˇstruct Row1.1;
10480                   struct Row1;
10481                   «ˇstr»uct Row22;
10482
10483                   struct ˇRow44;
10484                   struct Row5;
10485                   struct «Rˇ»ow66;ˇ
10486
10487                   «struˇ»ct Row88;
10488                   struct Row9;
10489                   struct Row1011;ˇ"#},
10490        vec![
10491            DiffHunkStatus::Modified,
10492            DiffHunkStatus::Modified,
10493            DiffHunkStatus::Modified,
10494            DiffHunkStatus::Modified,
10495            DiffHunkStatus::Modified,
10496            DiffHunkStatus::Modified,
10497        ],
10498        indoc! {r#"struct Row;
10499                   ˇstruct Row1;
10500                   struct Row2;
10501                   ˇ
10502                   struct Row4;
10503                   ˇstruct Row5;
10504                   struct Row6;
10505                   ˇ
10506                   struct Row8;
10507                   ˇstruct Row9;
10508                   struct Row10;ˇ"#},
10509        base_text,
10510        &mut cx,
10511    );
10512}
10513
10514#[gpui::test]
10515async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10516    init_test(cx, |_| {});
10517    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10518    let base_text = indoc! {r#"struct Row;
10519struct Row1;
10520struct Row2;
10521
10522struct Row4;
10523struct Row5;
10524struct Row6;
10525
10526struct Row8;
10527struct Row9;
10528struct Row10;"#};
10529
10530    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10531    assert_hunk_revert(
10532        indoc! {r#"struct Row;
10533                   struct Row2;
10534
10535                   ˇstruct Row4;
10536                   struct Row5;
10537                   struct Row6;
10538                   ˇ
10539                   struct Row8;
10540                   struct Row10;"#},
10541        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10542        indoc! {r#"struct Row;
10543                   struct Row2;
10544
10545                   ˇstruct Row4;
10546                   struct Row5;
10547                   struct Row6;
10548                   ˇ
10549                   struct Row8;
10550                   struct Row10;"#},
10551        base_text,
10552        &mut cx,
10553    );
10554    assert_hunk_revert(
10555        indoc! {r#"struct Row;
10556                   struct Row2;
10557
10558                   «ˇstruct Row4;
10559                   struct» Row5;
10560                   «struct Row6;
10561                   ˇ»
10562                   struct Row8;
10563                   struct Row10;"#},
10564        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10565        indoc! {r#"struct Row;
10566                   struct Row2;
10567
10568                   «ˇstruct Row4;
10569                   struct» Row5;
10570                   «struct Row6;
10571                   ˇ»
10572                   struct Row8;
10573                   struct Row10;"#},
10574        base_text,
10575        &mut cx,
10576    );
10577
10578    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10579    assert_hunk_revert(
10580        indoc! {r#"struct Row;
10581                   ˇstruct Row2;
10582
10583                   struct Row4;
10584                   struct Row5;
10585                   struct Row6;
10586
10587                   struct Row8;ˇ
10588                   struct Row10;"#},
10589        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10590        indoc! {r#"struct Row;
10591                   struct Row1;
10592                   ˇstruct Row2;
10593
10594                   struct Row4;
10595                   struct Row5;
10596                   struct Row6;
10597
10598                   struct Row8;ˇ
10599                   struct Row9;
10600                   struct Row10;"#},
10601        base_text,
10602        &mut cx,
10603    );
10604    assert_hunk_revert(
10605        indoc! {r#"struct Row;
10606                   struct Row2«ˇ;
10607                   struct Row4;
10608                   struct» Row5;
10609                   «struct Row6;
10610
10611                   struct Row8;ˇ»
10612                   struct Row10;"#},
10613        vec![
10614            DiffHunkStatus::Removed,
10615            DiffHunkStatus::Removed,
10616            DiffHunkStatus::Removed,
10617        ],
10618        indoc! {r#"struct Row;
10619                   struct Row1;
10620                   struct Row2«ˇ;
10621
10622                   struct Row4;
10623                   struct» Row5;
10624                   «struct Row6;
10625
10626                   struct Row8;ˇ»
10627                   struct Row9;
10628                   struct Row10;"#},
10629        base_text,
10630        &mut cx,
10631    );
10632}
10633
10634#[gpui::test]
10635async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10636    init_test(cx, |_| {});
10637
10638    let cols = 4;
10639    let rows = 10;
10640    let sample_text_1 = sample_text(rows, cols, 'a');
10641    assert_eq!(
10642        sample_text_1,
10643        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10644    );
10645    let sample_text_2 = sample_text(rows, cols, 'l');
10646    assert_eq!(
10647        sample_text_2,
10648        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10649    );
10650    let sample_text_3 = sample_text(rows, cols, 'v');
10651    assert_eq!(
10652        sample_text_3,
10653        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10654    );
10655
10656    fn diff_every_buffer_row(
10657        buffer: &Model<Buffer>,
10658        sample_text: String,
10659        cols: usize,
10660        cx: &mut gpui::TestAppContext,
10661    ) {
10662        // revert first character in each row, creating one large diff hunk per buffer
10663        let is_first_char = |offset: usize| offset % cols == 0;
10664        buffer.update(cx, |buffer, cx| {
10665            buffer.set_text(
10666                sample_text
10667                    .chars()
10668                    .enumerate()
10669                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10670                    .collect::<String>(),
10671                cx,
10672            );
10673            buffer.set_diff_base(Some(sample_text), cx);
10674        });
10675        cx.executor().run_until_parked();
10676    }
10677
10678    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10679    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10680
10681    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10682    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10683
10684    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10685    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10686
10687    let multibuffer = cx.new_model(|cx| {
10688        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10689        multibuffer.push_excerpts(
10690            buffer_1.clone(),
10691            [
10692                ExcerptRange {
10693                    context: Point::new(0, 0)..Point::new(3, 0),
10694                    primary: None,
10695                },
10696                ExcerptRange {
10697                    context: Point::new(5, 0)..Point::new(7, 0),
10698                    primary: None,
10699                },
10700                ExcerptRange {
10701                    context: Point::new(9, 0)..Point::new(10, 4),
10702                    primary: None,
10703                },
10704            ],
10705            cx,
10706        );
10707        multibuffer.push_excerpts(
10708            buffer_2.clone(),
10709            [
10710                ExcerptRange {
10711                    context: Point::new(0, 0)..Point::new(3, 0),
10712                    primary: None,
10713                },
10714                ExcerptRange {
10715                    context: Point::new(5, 0)..Point::new(7, 0),
10716                    primary: None,
10717                },
10718                ExcerptRange {
10719                    context: Point::new(9, 0)..Point::new(10, 4),
10720                    primary: None,
10721                },
10722            ],
10723            cx,
10724        );
10725        multibuffer.push_excerpts(
10726            buffer_3.clone(),
10727            [
10728                ExcerptRange {
10729                    context: Point::new(0, 0)..Point::new(3, 0),
10730                    primary: None,
10731                },
10732                ExcerptRange {
10733                    context: Point::new(5, 0)..Point::new(7, 0),
10734                    primary: None,
10735                },
10736                ExcerptRange {
10737                    context: Point::new(9, 0)..Point::new(10, 4),
10738                    primary: None,
10739                },
10740            ],
10741            cx,
10742        );
10743        multibuffer
10744    });
10745
10746    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10747    editor.update(cx, |editor, cx| {
10748        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");
10749        editor.select_all(&SelectAll, cx);
10750        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10751    });
10752    cx.executor().run_until_parked();
10753    // When all ranges are selected, all buffer hunks are reverted.
10754    editor.update(cx, |editor, cx| {
10755        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");
10756    });
10757    buffer_1.update(cx, |buffer, _| {
10758        assert_eq!(buffer.text(), sample_text_1);
10759    });
10760    buffer_2.update(cx, |buffer, _| {
10761        assert_eq!(buffer.text(), sample_text_2);
10762    });
10763    buffer_3.update(cx, |buffer, _| {
10764        assert_eq!(buffer.text(), sample_text_3);
10765    });
10766
10767    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10768    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10769    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10770    editor.update(cx, |editor, cx| {
10771        editor.change_selections(None, cx, |s| {
10772            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10773        });
10774        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10775    });
10776    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10777    // but not affect buffer_2 and its related excerpts.
10778    editor.update(cx, |editor, cx| {
10779        assert_eq!(
10780            editor.text(cx),
10781            "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"
10782        );
10783    });
10784    buffer_1.update(cx, |buffer, _| {
10785        assert_eq!(buffer.text(), sample_text_1);
10786    });
10787    buffer_2.update(cx, |buffer, _| {
10788        assert_eq!(
10789            buffer.text(),
10790            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10791        );
10792    });
10793    buffer_3.update(cx, |buffer, _| {
10794        assert_eq!(
10795            buffer.text(),
10796            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10797        );
10798    });
10799}
10800
10801#[gpui::test]
10802async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10803    init_test(cx, |_| {});
10804
10805    let cols = 4;
10806    let rows = 10;
10807    let sample_text_1 = sample_text(rows, cols, 'a');
10808    assert_eq!(
10809        sample_text_1,
10810        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10811    );
10812    let sample_text_2 = sample_text(rows, cols, 'l');
10813    assert_eq!(
10814        sample_text_2,
10815        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10816    );
10817    let sample_text_3 = sample_text(rows, cols, 'v');
10818    assert_eq!(
10819        sample_text_3,
10820        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10821    );
10822
10823    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10824    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10825    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10826
10827    let multi_buffer = cx.new_model(|cx| {
10828        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10829        multibuffer.push_excerpts(
10830            buffer_1.clone(),
10831            [
10832                ExcerptRange {
10833                    context: Point::new(0, 0)..Point::new(3, 0),
10834                    primary: None,
10835                },
10836                ExcerptRange {
10837                    context: Point::new(5, 0)..Point::new(7, 0),
10838                    primary: None,
10839                },
10840                ExcerptRange {
10841                    context: Point::new(9, 0)..Point::new(10, 4),
10842                    primary: None,
10843                },
10844            ],
10845            cx,
10846        );
10847        multibuffer.push_excerpts(
10848            buffer_2.clone(),
10849            [
10850                ExcerptRange {
10851                    context: Point::new(0, 0)..Point::new(3, 0),
10852                    primary: None,
10853                },
10854                ExcerptRange {
10855                    context: Point::new(5, 0)..Point::new(7, 0),
10856                    primary: None,
10857                },
10858                ExcerptRange {
10859                    context: Point::new(9, 0)..Point::new(10, 4),
10860                    primary: None,
10861                },
10862            ],
10863            cx,
10864        );
10865        multibuffer.push_excerpts(
10866            buffer_3.clone(),
10867            [
10868                ExcerptRange {
10869                    context: Point::new(0, 0)..Point::new(3, 0),
10870                    primary: None,
10871                },
10872                ExcerptRange {
10873                    context: Point::new(5, 0)..Point::new(7, 0),
10874                    primary: None,
10875                },
10876                ExcerptRange {
10877                    context: Point::new(9, 0)..Point::new(10, 4),
10878                    primary: None,
10879                },
10880            ],
10881            cx,
10882        );
10883        multibuffer
10884    });
10885
10886    let fs = FakeFs::new(cx.executor());
10887    fs.insert_tree(
10888        "/a",
10889        json!({
10890            "main.rs": sample_text_1,
10891            "other.rs": sample_text_2,
10892            "lib.rs": sample_text_3,
10893        }),
10894    )
10895    .await;
10896    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10897    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10898    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10899    let multi_buffer_editor = cx.new_view(|cx| {
10900        Editor::new(
10901            EditorMode::Full,
10902            multi_buffer,
10903            Some(project.clone()),
10904            true,
10905            cx,
10906        )
10907    });
10908    let multibuffer_item_id = workspace
10909        .update(cx, |workspace, cx| {
10910            assert!(
10911                workspace.active_item(cx).is_none(),
10912                "active item should be None before the first item is added"
10913            );
10914            workspace.add_item_to_active_pane(
10915                Box::new(multi_buffer_editor.clone()),
10916                None,
10917                true,
10918                cx,
10919            );
10920            let active_item = workspace
10921                .active_item(cx)
10922                .expect("should have an active item after adding the multi buffer");
10923            assert!(
10924                !active_item.is_singleton(cx),
10925                "A multi buffer was expected to active after adding"
10926            );
10927            active_item.item_id()
10928        })
10929        .unwrap();
10930    cx.executor().run_until_parked();
10931
10932    multi_buffer_editor.update(cx, |editor, cx| {
10933        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
10934        editor.open_excerpts(&OpenExcerpts, cx);
10935    });
10936    cx.executor().run_until_parked();
10937    let first_item_id = workspace
10938        .update(cx, |workspace, cx| {
10939            let active_item = workspace
10940                .active_item(cx)
10941                .expect("should have an active item after navigating into the 1st buffer");
10942            let first_item_id = active_item.item_id();
10943            assert_ne!(
10944                first_item_id, multibuffer_item_id,
10945                "Should navigate into the 1st buffer and activate it"
10946            );
10947            assert!(
10948                active_item.is_singleton(cx),
10949                "New active item should be a singleton buffer"
10950            );
10951            assert_eq!(
10952                active_item
10953                    .act_as::<Editor>(cx)
10954                    .expect("should have navigated into an editor for the 1st buffer")
10955                    .read(cx)
10956                    .text(cx),
10957                sample_text_1
10958            );
10959
10960            workspace
10961                .go_back(workspace.active_pane().downgrade(), cx)
10962                .detach_and_log_err(cx);
10963
10964            first_item_id
10965        })
10966        .unwrap();
10967    cx.executor().run_until_parked();
10968    workspace
10969        .update(cx, |workspace, cx| {
10970            let active_item = workspace
10971                .active_item(cx)
10972                .expect("should have an active item after navigating back");
10973            assert_eq!(
10974                active_item.item_id(),
10975                multibuffer_item_id,
10976                "Should navigate back to the multi buffer"
10977            );
10978            assert!(!active_item.is_singleton(cx));
10979        })
10980        .unwrap();
10981
10982    multi_buffer_editor.update(cx, |editor, cx| {
10983        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
10984            s.select_ranges(Some(39..40))
10985        });
10986        editor.open_excerpts(&OpenExcerpts, cx);
10987    });
10988    cx.executor().run_until_parked();
10989    let second_item_id = workspace
10990        .update(cx, |workspace, cx| {
10991            let active_item = workspace
10992                .active_item(cx)
10993                .expect("should have an active item after navigating into the 2nd buffer");
10994            let second_item_id = active_item.item_id();
10995            assert_ne!(
10996                second_item_id, multibuffer_item_id,
10997                "Should navigate away from the multibuffer"
10998            );
10999            assert_ne!(
11000                second_item_id, first_item_id,
11001                "Should navigate into the 2nd buffer and activate it"
11002            );
11003            assert!(
11004                active_item.is_singleton(cx),
11005                "New active item should be a singleton buffer"
11006            );
11007            assert_eq!(
11008                active_item
11009                    .act_as::<Editor>(cx)
11010                    .expect("should have navigated into an editor")
11011                    .read(cx)
11012                    .text(cx),
11013                sample_text_2
11014            );
11015
11016            workspace
11017                .go_back(workspace.active_pane().downgrade(), cx)
11018                .detach_and_log_err(cx);
11019
11020            second_item_id
11021        })
11022        .unwrap();
11023    cx.executor().run_until_parked();
11024    workspace
11025        .update(cx, |workspace, cx| {
11026            let active_item = workspace
11027                .active_item(cx)
11028                .expect("should have an active item after navigating back from the 2nd buffer");
11029            assert_eq!(
11030                active_item.item_id(),
11031                multibuffer_item_id,
11032                "Should navigate back from the 2nd buffer to the multi buffer"
11033            );
11034            assert!(!active_item.is_singleton(cx));
11035        })
11036        .unwrap();
11037
11038    multi_buffer_editor.update(cx, |editor, cx| {
11039        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11040            s.select_ranges(Some(60..70))
11041        });
11042        editor.open_excerpts(&OpenExcerpts, cx);
11043    });
11044    cx.executor().run_until_parked();
11045    workspace
11046        .update(cx, |workspace, cx| {
11047            let active_item = workspace
11048                .active_item(cx)
11049                .expect("should have an active item after navigating into the 3rd buffer");
11050            let third_item_id = active_item.item_id();
11051            assert_ne!(
11052                third_item_id, multibuffer_item_id,
11053                "Should navigate into the 3rd buffer and activate it"
11054            );
11055            assert_ne!(third_item_id, first_item_id);
11056            assert_ne!(third_item_id, second_item_id);
11057            assert!(
11058                active_item.is_singleton(cx),
11059                "New active item should be a singleton buffer"
11060            );
11061            assert_eq!(
11062                active_item
11063                    .act_as::<Editor>(cx)
11064                    .expect("should have navigated into an editor")
11065                    .read(cx)
11066                    .text(cx),
11067                sample_text_3
11068            );
11069
11070            workspace
11071                .go_back(workspace.active_pane().downgrade(), cx)
11072                .detach_and_log_err(cx);
11073        })
11074        .unwrap();
11075    cx.executor().run_until_parked();
11076    workspace
11077        .update(cx, |workspace, cx| {
11078            let active_item = workspace
11079                .active_item(cx)
11080                .expect("should have an active item after navigating back from the 3rd buffer");
11081            assert_eq!(
11082                active_item.item_id(),
11083                multibuffer_item_id,
11084                "Should navigate back from the 3rd buffer to the multi buffer"
11085            );
11086            assert!(!active_item.is_singleton(cx));
11087        })
11088        .unwrap();
11089}
11090
11091#[gpui::test]
11092async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11093    init_test(cx, |_| {});
11094
11095    let mut cx = EditorTestContext::new(cx).await;
11096
11097    let diff_base = r#"
11098        use some::mod;
11099
11100        const A: u32 = 42;
11101
11102        fn main() {
11103            println!("hello");
11104
11105            println!("world");
11106        }
11107        "#
11108    .unindent();
11109
11110    cx.set_state(
11111        &r#"
11112        use some::modified;
11113
11114        ˇ
11115        fn main() {
11116            println!("hello there");
11117
11118            println!("around the");
11119            println!("world");
11120        }
11121        "#
11122        .unindent(),
11123    );
11124
11125    cx.set_diff_base(Some(&diff_base));
11126    executor.run_until_parked();
11127    let unexpanded_hunks = vec![
11128        (
11129            "use some::mod;\n".to_string(),
11130            DiffHunkStatus::Modified,
11131            DisplayRow(0)..DisplayRow(1),
11132        ),
11133        (
11134            "const A: u32 = 42;\n".to_string(),
11135            DiffHunkStatus::Removed,
11136            DisplayRow(2)..DisplayRow(2),
11137        ),
11138        (
11139            "    println!(\"hello\");\n".to_string(),
11140            DiffHunkStatus::Modified,
11141            DisplayRow(4)..DisplayRow(5),
11142        ),
11143        (
11144            "".to_string(),
11145            DiffHunkStatus::Added,
11146            DisplayRow(6)..DisplayRow(7),
11147        ),
11148    ];
11149    cx.update_editor(|editor, cx| {
11150        let snapshot = editor.snapshot(cx);
11151        let all_hunks = editor_hunks(editor, &snapshot, cx);
11152        assert_eq!(all_hunks, unexpanded_hunks);
11153    });
11154
11155    cx.update_editor(|editor, cx| {
11156        for _ in 0..4 {
11157            editor.go_to_hunk(&GoToHunk, cx);
11158            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11159        }
11160    });
11161    executor.run_until_parked();
11162    cx.assert_editor_state(
11163        &r#"
11164        use some::modified;
11165
11166        ˇ
11167        fn main() {
11168            println!("hello there");
11169
11170            println!("around the");
11171            println!("world");
11172        }
11173        "#
11174        .unindent(),
11175    );
11176    cx.update_editor(|editor, cx| {
11177        let snapshot = editor.snapshot(cx);
11178        let all_hunks = editor_hunks(editor, &snapshot, cx);
11179        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11180        assert_eq!(
11181            expanded_hunks_background_highlights(editor, cx),
11182            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
11183            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
11184        );
11185        assert_eq!(
11186            all_hunks,
11187            vec![
11188                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
11189                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
11190                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
11191                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
11192            ],
11193            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
11194            (from modified and removed hunks)"
11195        );
11196        assert_eq!(
11197            all_hunks, all_expanded_hunks,
11198            "Editor hunks should not change and all be expanded"
11199        );
11200    });
11201
11202    cx.update_editor(|editor, cx| {
11203        editor.cancel(&Cancel, cx);
11204
11205        let snapshot = editor.snapshot(cx);
11206        let all_hunks = editor_hunks(editor, &snapshot, cx);
11207        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11208        assert_eq!(
11209            expanded_hunks_background_highlights(editor, cx),
11210            Vec::new(),
11211            "After cancelling in editor, no git highlights should be left"
11212        );
11213        assert_eq!(
11214            all_expanded_hunks,
11215            Vec::new(),
11216            "After cancelling in editor, no hunks should be expanded"
11217        );
11218        assert_eq!(
11219            all_hunks, unexpanded_hunks,
11220            "After cancelling in editor, regular hunks' coordinates should get back to normal"
11221        );
11222    });
11223}
11224
11225#[gpui::test]
11226async fn test_toggled_diff_base_change(
11227    executor: BackgroundExecutor,
11228    cx: &mut gpui::TestAppContext,
11229) {
11230    init_test(cx, |_| {});
11231
11232    let mut cx = EditorTestContext::new(cx).await;
11233
11234    let diff_base = r#"
11235        use some::mod1;
11236        use some::mod2;
11237
11238        const A: u32 = 42;
11239        const B: u32 = 42;
11240        const C: u32 = 42;
11241
11242        fn main(ˇ) {
11243            println!("hello");
11244
11245            println!("world");
11246        }
11247        "#
11248    .unindent();
11249
11250    cx.set_state(
11251        &r#"
11252        use some::mod2;
11253
11254        const A: u32 = 42;
11255        const C: u32 = 42;
11256
11257        fn main(ˇ) {
11258            //println!("hello");
11259
11260            println!("world");
11261            //
11262            //
11263        }
11264        "#
11265        .unindent(),
11266    );
11267
11268    cx.set_diff_base(Some(&diff_base));
11269    executor.run_until_parked();
11270    cx.update_editor(|editor, cx| {
11271        let snapshot = editor.snapshot(cx);
11272        let all_hunks = editor_hunks(editor, &snapshot, cx);
11273        assert_eq!(
11274            all_hunks,
11275            vec![
11276                (
11277                    "use some::mod1;\n".to_string(),
11278                    DiffHunkStatus::Removed,
11279                    DisplayRow(0)..DisplayRow(0)
11280                ),
11281                (
11282                    "const B: u32 = 42;\n".to_string(),
11283                    DiffHunkStatus::Removed,
11284                    DisplayRow(3)..DisplayRow(3)
11285                ),
11286                (
11287                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11288                    DiffHunkStatus::Modified,
11289                    DisplayRow(5)..DisplayRow(7)
11290                ),
11291                (
11292                    "".to_string(),
11293                    DiffHunkStatus::Added,
11294                    DisplayRow(9)..DisplayRow(11)
11295                ),
11296            ]
11297        );
11298    });
11299
11300    cx.update_editor(|editor, cx| {
11301        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11302    });
11303    executor.run_until_parked();
11304    cx.assert_editor_state(
11305        &r#"
11306        use some::mod2;
11307
11308        const A: u32 = 42;
11309        const C: u32 = 42;
11310
11311        fn main(ˇ) {
11312            //println!("hello");
11313
11314            println!("world");
11315            //
11316            //
11317        }
11318        "#
11319        .unindent(),
11320    );
11321    cx.update_editor(|editor, cx| {
11322        let snapshot = editor.snapshot(cx);
11323        let all_hunks = editor_hunks(editor, &snapshot, cx);
11324        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11325        assert_eq!(
11326            expanded_hunks_background_highlights(editor, cx),
11327            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
11328            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
11329        );
11330        assert_eq!(
11331            all_hunks,
11332            vec![
11333                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
11334                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
11335                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
11336                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
11337            ],
11338            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
11339            (from modified and removed hunks)"
11340        );
11341        assert_eq!(
11342            all_hunks, all_expanded_hunks,
11343            "Editor hunks should not change and all be expanded"
11344        );
11345    });
11346
11347    cx.set_diff_base(Some("new diff base!"));
11348    executor.run_until_parked();
11349
11350    cx.update_editor(|editor, cx| {
11351        let snapshot = editor.snapshot(cx);
11352        let all_hunks = editor_hunks(editor, &snapshot, cx);
11353        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11354        assert_eq!(
11355            expanded_hunks_background_highlights(editor, cx),
11356            Vec::new(),
11357            "After diff base is changed, old git highlights should be removed"
11358        );
11359        assert_eq!(
11360            all_expanded_hunks,
11361            Vec::new(),
11362            "After diff base is changed, old git hunk expansions should be removed"
11363        );
11364        assert_eq!(
11365            all_hunks,
11366            vec![(
11367                "new diff base!".to_string(),
11368                DiffHunkStatus::Modified,
11369                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
11370            )],
11371            "After diff base is changed, hunks should update"
11372        );
11373    });
11374}
11375
11376#[gpui::test]
11377async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11378    init_test(cx, |_| {});
11379
11380    let mut cx = EditorTestContext::new(cx).await;
11381
11382    let diff_base = r#"
11383        use some::mod1;
11384        use some::mod2;
11385
11386        const A: u32 = 42;
11387        const B: u32 = 42;
11388        const C: u32 = 42;
11389
11390        fn main(ˇ) {
11391            println!("hello");
11392
11393            println!("world");
11394        }
11395
11396        fn another() {
11397            println!("another");
11398        }
11399
11400        fn another2() {
11401            println!("another2");
11402        }
11403        "#
11404    .unindent();
11405
11406    cx.set_state(
11407        &r#"
11408        «use some::mod2;
11409
11410        const A: u32 = 42;
11411        const C: u32 = 42;
11412
11413        fn main() {
11414            //println!("hello");
11415
11416            println!("world");
11417            //
11418            //ˇ»
11419        }
11420
11421        fn another() {
11422            println!("another");
11423            println!("another");
11424        }
11425
11426            println!("another2");
11427        }
11428        "#
11429        .unindent(),
11430    );
11431
11432    cx.set_diff_base(Some(&diff_base));
11433    executor.run_until_parked();
11434    cx.update_editor(|editor, cx| {
11435        let snapshot = editor.snapshot(cx);
11436        let all_hunks = editor_hunks(editor, &snapshot, cx);
11437        assert_eq!(
11438            all_hunks,
11439            vec![
11440                (
11441                    "use some::mod1;\n".to_string(),
11442                    DiffHunkStatus::Removed,
11443                    DisplayRow(0)..DisplayRow(0)
11444                ),
11445                (
11446                    "const B: u32 = 42;\n".to_string(),
11447                    DiffHunkStatus::Removed,
11448                    DisplayRow(3)..DisplayRow(3)
11449                ),
11450                (
11451                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11452                    DiffHunkStatus::Modified,
11453                    DisplayRow(5)..DisplayRow(7)
11454                ),
11455                (
11456                    "".to_string(),
11457                    DiffHunkStatus::Added,
11458                    DisplayRow(9)..DisplayRow(11)
11459                ),
11460                (
11461                    "".to_string(),
11462                    DiffHunkStatus::Added,
11463                    DisplayRow(15)..DisplayRow(16)
11464                ),
11465                (
11466                    "fn another2() {\n".to_string(),
11467                    DiffHunkStatus::Removed,
11468                    DisplayRow(18)..DisplayRow(18)
11469                ),
11470            ]
11471        );
11472    });
11473
11474    cx.update_editor(|editor, cx| {
11475        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11476    });
11477    executor.run_until_parked();
11478    cx.assert_editor_state(
11479        &r#"
11480        «use some::mod2;
11481
11482        const A: u32 = 42;
11483        const C: u32 = 42;
11484
11485        fn main() {
11486            //println!("hello");
11487
11488            println!("world");
11489            //
11490            //ˇ»
11491        }
11492
11493        fn another() {
11494            println!("another");
11495            println!("another");
11496        }
11497
11498            println!("another2");
11499        }
11500        "#
11501        .unindent(),
11502    );
11503    cx.update_editor(|editor, cx| {
11504        let snapshot = editor.snapshot(cx);
11505        let all_hunks = editor_hunks(editor, &snapshot, cx);
11506        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11507        assert_eq!(
11508            expanded_hunks_background_highlights(editor, cx),
11509            vec![
11510                DisplayRow(9)..=DisplayRow(10),
11511                DisplayRow(13)..=DisplayRow(14),
11512                DisplayRow(19)..=DisplayRow(19)
11513            ]
11514        );
11515        assert_eq!(
11516            all_hunks,
11517            vec![
11518                (
11519                    "use some::mod1;\n".to_string(),
11520                    DiffHunkStatus::Removed,
11521                    DisplayRow(1)..DisplayRow(1)
11522                ),
11523                (
11524                    "const B: u32 = 42;\n".to_string(),
11525                    DiffHunkStatus::Removed,
11526                    DisplayRow(5)..DisplayRow(5)
11527                ),
11528                (
11529                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11530                    DiffHunkStatus::Modified,
11531                    DisplayRow(9)..DisplayRow(11)
11532                ),
11533                (
11534                    "".to_string(),
11535                    DiffHunkStatus::Added,
11536                    DisplayRow(13)..DisplayRow(15)
11537                ),
11538                (
11539                    "".to_string(),
11540                    DiffHunkStatus::Added,
11541                    DisplayRow(19)..DisplayRow(20)
11542                ),
11543                (
11544                    "fn another2() {\n".to_string(),
11545                    DiffHunkStatus::Removed,
11546                    DisplayRow(23)..DisplayRow(23)
11547                ),
11548            ],
11549        );
11550        assert_eq!(all_hunks, all_expanded_hunks);
11551    });
11552
11553    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11554    cx.executor().run_until_parked();
11555    cx.assert_editor_state(
11556        &r#"
11557        «use some::mod2;
11558
11559        const A: u32 = 42;
11560        const C: u32 = 42;
11561
11562        fn main() {
11563            //println!("hello");
11564
11565            println!("world");
11566            //
11567            //ˇ»
11568        }
11569
11570        fn another() {
11571            println!("another");
11572            println!("another");
11573        }
11574
11575            println!("another2");
11576        }
11577        "#
11578        .unindent(),
11579    );
11580    cx.update_editor(|editor, cx| {
11581        let snapshot = editor.snapshot(cx);
11582        let all_hunks = editor_hunks(editor, &snapshot, cx);
11583        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11584        assert_eq!(
11585            expanded_hunks_background_highlights(editor, cx),
11586            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
11587            "Only one hunk is left not folded, its highlight should be visible"
11588        );
11589        assert_eq!(
11590            all_hunks,
11591            vec![
11592                (
11593                    "use some::mod1;\n".to_string(),
11594                    DiffHunkStatus::Removed,
11595                    DisplayRow(0)..DisplayRow(0)
11596                ),
11597                (
11598                    "const B: u32 = 42;\n".to_string(),
11599                    DiffHunkStatus::Removed,
11600                    DisplayRow(0)..DisplayRow(0)
11601                ),
11602                (
11603                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11604                    DiffHunkStatus::Modified,
11605                    DisplayRow(0)..DisplayRow(0)
11606                ),
11607                (
11608                    "".to_string(),
11609                    DiffHunkStatus::Added,
11610                    DisplayRow(0)..DisplayRow(1)
11611                ),
11612                (
11613                    "".to_string(),
11614                    DiffHunkStatus::Added,
11615                    DisplayRow(5)..DisplayRow(6)
11616                ),
11617                (
11618                    "fn another2() {\n".to_string(),
11619                    DiffHunkStatus::Removed,
11620                    DisplayRow(9)..DisplayRow(9)
11621                ),
11622            ],
11623            "Hunk list should still return shifted folded hunks"
11624        );
11625        assert_eq!(
11626            all_expanded_hunks,
11627            vec![
11628                (
11629                    "".to_string(),
11630                    DiffHunkStatus::Added,
11631                    DisplayRow(5)..DisplayRow(6)
11632                ),
11633                (
11634                    "fn another2() {\n".to_string(),
11635                    DiffHunkStatus::Removed,
11636                    DisplayRow(9)..DisplayRow(9)
11637                ),
11638            ],
11639            "Only non-folded hunks should be left expanded"
11640        );
11641    });
11642
11643    cx.update_editor(|editor, cx| {
11644        editor.select_all(&SelectAll, cx);
11645        editor.unfold_lines(&UnfoldLines, cx);
11646    });
11647    cx.executor().run_until_parked();
11648    cx.assert_editor_state(
11649        &r#"
11650        «use some::mod2;
11651
11652        const A: u32 = 42;
11653        const C: u32 = 42;
11654
11655        fn main() {
11656            //println!("hello");
11657
11658            println!("world");
11659            //
11660            //
11661        }
11662
11663        fn another() {
11664            println!("another");
11665            println!("another");
11666        }
11667
11668            println!("another2");
11669        }
11670        ˇ»"#
11671        .unindent(),
11672    );
11673    cx.update_editor(|editor, cx| {
11674        let snapshot = editor.snapshot(cx);
11675        let all_hunks = editor_hunks(editor, &snapshot, cx);
11676        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11677        assert_eq!(
11678            expanded_hunks_background_highlights(editor, cx),
11679            vec![
11680                DisplayRow(9)..=DisplayRow(10),
11681                DisplayRow(13)..=DisplayRow(14),
11682                DisplayRow(19)..=DisplayRow(19)
11683            ],
11684            "After unfolding, all hunk diffs should be visible again"
11685        );
11686        assert_eq!(
11687            all_hunks,
11688            vec![
11689                (
11690                    "use some::mod1;\n".to_string(),
11691                    DiffHunkStatus::Removed,
11692                    DisplayRow(1)..DisplayRow(1)
11693                ),
11694                (
11695                    "const B: u32 = 42;\n".to_string(),
11696                    DiffHunkStatus::Removed,
11697                    DisplayRow(5)..DisplayRow(5)
11698                ),
11699                (
11700                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
11701                    DiffHunkStatus::Modified,
11702                    DisplayRow(9)..DisplayRow(11)
11703                ),
11704                (
11705                    "".to_string(),
11706                    DiffHunkStatus::Added,
11707                    DisplayRow(13)..DisplayRow(15)
11708                ),
11709                (
11710                    "".to_string(),
11711                    DiffHunkStatus::Added,
11712                    DisplayRow(19)..DisplayRow(20)
11713                ),
11714                (
11715                    "fn another2() {\n".to_string(),
11716                    DiffHunkStatus::Removed,
11717                    DisplayRow(23)..DisplayRow(23)
11718                ),
11719            ],
11720        );
11721        assert_eq!(all_hunks, all_expanded_hunks);
11722    });
11723}
11724
11725#[gpui::test]
11726async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11727    init_test(cx, |_| {});
11728
11729    let cols = 4;
11730    let rows = 10;
11731    let sample_text_1 = sample_text(rows, cols, 'a');
11732    assert_eq!(
11733        sample_text_1,
11734        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11735    );
11736    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11737    let sample_text_2 = sample_text(rows, cols, 'l');
11738    assert_eq!(
11739        sample_text_2,
11740        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11741    );
11742    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11743    let sample_text_3 = sample_text(rows, cols, 'v');
11744    assert_eq!(
11745        sample_text_3,
11746        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11747    );
11748    let modified_sample_text_3 =
11749        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11750    let buffer_1 = cx.new_model(|cx| {
11751        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
11752        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
11753        buffer
11754    });
11755    let buffer_2 = cx.new_model(|cx| {
11756        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
11757        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
11758        buffer
11759    });
11760    let buffer_3 = cx.new_model(|cx| {
11761        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
11762        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
11763        buffer
11764    });
11765
11766    let multi_buffer = cx.new_model(|cx| {
11767        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
11768        multibuffer.push_excerpts(
11769            buffer_1.clone(),
11770            [
11771                ExcerptRange {
11772                    context: Point::new(0, 0)..Point::new(3, 0),
11773                    primary: None,
11774                },
11775                ExcerptRange {
11776                    context: Point::new(5, 0)..Point::new(7, 0),
11777                    primary: None,
11778                },
11779                ExcerptRange {
11780                    context: Point::new(9, 0)..Point::new(10, 4),
11781                    primary: None,
11782                },
11783            ],
11784            cx,
11785        );
11786        multibuffer.push_excerpts(
11787            buffer_2.clone(),
11788            [
11789                ExcerptRange {
11790                    context: Point::new(0, 0)..Point::new(3, 0),
11791                    primary: None,
11792                },
11793                ExcerptRange {
11794                    context: Point::new(5, 0)..Point::new(7, 0),
11795                    primary: None,
11796                },
11797                ExcerptRange {
11798                    context: Point::new(9, 0)..Point::new(10, 4),
11799                    primary: None,
11800                },
11801            ],
11802            cx,
11803        );
11804        multibuffer.push_excerpts(
11805            buffer_3.clone(),
11806            [
11807                ExcerptRange {
11808                    context: Point::new(0, 0)..Point::new(3, 0),
11809                    primary: None,
11810                },
11811                ExcerptRange {
11812                    context: Point::new(5, 0)..Point::new(7, 0),
11813                    primary: None,
11814                },
11815                ExcerptRange {
11816                    context: Point::new(9, 0)..Point::new(10, 4),
11817                    primary: None,
11818                },
11819            ],
11820            cx,
11821        );
11822        multibuffer
11823    });
11824
11825    let fs = FakeFs::new(cx.executor());
11826    fs.insert_tree(
11827        "/a",
11828        json!({
11829            "main.rs": modified_sample_text_1,
11830            "other.rs": modified_sample_text_2,
11831            "lib.rs": modified_sample_text_3,
11832        }),
11833    )
11834    .await;
11835
11836    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11837    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11838    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11839    let multi_buffer_editor = cx.new_view(|cx| {
11840        Editor::new(
11841            EditorMode::Full,
11842            multi_buffer,
11843            Some(project.clone()),
11844            true,
11845            cx,
11846        )
11847    });
11848    cx.executor().run_until_parked();
11849
11850    let expected_all_hunks = vec![
11851        (
11852            "bbbb\n".to_string(),
11853            DiffHunkStatus::Removed,
11854            DisplayRow(4)..DisplayRow(4),
11855        ),
11856        (
11857            "nnnn\n".to_string(),
11858            DiffHunkStatus::Modified,
11859            DisplayRow(21)..DisplayRow(22),
11860        ),
11861        (
11862            "".to_string(),
11863            DiffHunkStatus::Added,
11864            DisplayRow(41)..DisplayRow(42),
11865        ),
11866    ];
11867    let expected_all_hunks_shifted = vec![
11868        (
11869            "bbbb\n".to_string(),
11870            DiffHunkStatus::Removed,
11871            DisplayRow(5)..DisplayRow(5),
11872        ),
11873        (
11874            "nnnn\n".to_string(),
11875            DiffHunkStatus::Modified,
11876            DisplayRow(23)..DisplayRow(24),
11877        ),
11878        (
11879            "".to_string(),
11880            DiffHunkStatus::Added,
11881            DisplayRow(43)..DisplayRow(44),
11882        ),
11883    ];
11884
11885    multi_buffer_editor.update(cx, |editor, cx| {
11886        let snapshot = editor.snapshot(cx);
11887        let all_hunks = editor_hunks(editor, &snapshot, cx);
11888        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11889        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11890        assert_eq!(all_hunks, expected_all_hunks);
11891        assert_eq!(all_expanded_hunks, Vec::new());
11892    });
11893
11894    multi_buffer_editor.update(cx, |editor, cx| {
11895        editor.select_all(&SelectAll, cx);
11896        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11897    });
11898    cx.executor().run_until_parked();
11899    multi_buffer_editor.update(cx, |editor, cx| {
11900        let snapshot = editor.snapshot(cx);
11901        let all_hunks = editor_hunks(editor, &snapshot, cx);
11902        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11903        assert_eq!(
11904            expanded_hunks_background_highlights(editor, cx),
11905            vec![
11906                DisplayRow(23)..=DisplayRow(23),
11907                DisplayRow(43)..=DisplayRow(43)
11908            ],
11909        );
11910        assert_eq!(all_hunks, expected_all_hunks_shifted);
11911        assert_eq!(all_hunks, all_expanded_hunks);
11912    });
11913
11914    multi_buffer_editor.update(cx, |editor, cx| {
11915        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11916    });
11917    cx.executor().run_until_parked();
11918    multi_buffer_editor.update(cx, |editor, cx| {
11919        let snapshot = editor.snapshot(cx);
11920        let all_hunks = editor_hunks(editor, &snapshot, cx);
11921        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11922        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11923        assert_eq!(all_hunks, expected_all_hunks);
11924        assert_eq!(all_expanded_hunks, Vec::new());
11925    });
11926
11927    multi_buffer_editor.update(cx, |editor, cx| {
11928        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11929    });
11930    cx.executor().run_until_parked();
11931    multi_buffer_editor.update(cx, |editor, cx| {
11932        let snapshot = editor.snapshot(cx);
11933        let all_hunks = editor_hunks(editor, &snapshot, cx);
11934        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11935        assert_eq!(
11936            expanded_hunks_background_highlights(editor, cx),
11937            vec![
11938                DisplayRow(23)..=DisplayRow(23),
11939                DisplayRow(43)..=DisplayRow(43)
11940            ],
11941        );
11942        assert_eq!(all_hunks, expected_all_hunks_shifted);
11943        assert_eq!(all_hunks, all_expanded_hunks);
11944    });
11945
11946    multi_buffer_editor.update(cx, |editor, cx| {
11947        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11948    });
11949    cx.executor().run_until_parked();
11950    multi_buffer_editor.update(cx, |editor, cx| {
11951        let snapshot = editor.snapshot(cx);
11952        let all_hunks = editor_hunks(editor, &snapshot, cx);
11953        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
11954        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
11955        assert_eq!(all_hunks, expected_all_hunks);
11956        assert_eq!(all_expanded_hunks, Vec::new());
11957    });
11958}
11959
11960#[gpui::test]
11961async fn test_edits_around_toggled_additions(
11962    executor: BackgroundExecutor,
11963    cx: &mut gpui::TestAppContext,
11964) {
11965    init_test(cx, |_| {});
11966
11967    let mut cx = EditorTestContext::new(cx).await;
11968
11969    let diff_base = r#"
11970        use some::mod1;
11971        use some::mod2;
11972
11973        const A: u32 = 42;
11974
11975        fn main() {
11976            println!("hello");
11977
11978            println!("world");
11979        }
11980        "#
11981    .unindent();
11982    executor.run_until_parked();
11983    cx.set_state(
11984        &r#"
11985        use some::mod1;
11986        use some::mod2;
11987
11988        const A: u32 = 42;
11989        const B: u32 = 42;
11990        const C: u32 = 42;
11991        ˇ
11992
11993        fn main() {
11994            println!("hello");
11995
11996            println!("world");
11997        }
11998        "#
11999        .unindent(),
12000    );
12001
12002    cx.set_diff_base(Some(&diff_base));
12003    executor.run_until_parked();
12004    cx.update_editor(|editor, cx| {
12005        let snapshot = editor.snapshot(cx);
12006        let all_hunks = editor_hunks(editor, &snapshot, cx);
12007        assert_eq!(
12008            all_hunks,
12009            vec![(
12010                "".to_string(),
12011                DiffHunkStatus::Added,
12012                DisplayRow(4)..DisplayRow(7)
12013            )]
12014        );
12015    });
12016    cx.update_editor(|editor, cx| {
12017        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12018    });
12019    executor.run_until_parked();
12020    cx.assert_editor_state(
12021        &r#"
12022        use some::mod1;
12023        use some::mod2;
12024
12025        const A: u32 = 42;
12026        const B: u32 = 42;
12027        const C: u32 = 42;
12028        ˇ
12029
12030        fn main() {
12031            println!("hello");
12032
12033            println!("world");
12034        }
12035        "#
12036        .unindent(),
12037    );
12038    cx.update_editor(|editor, cx| {
12039        let snapshot = editor.snapshot(cx);
12040        let all_hunks = editor_hunks(editor, &snapshot, cx);
12041        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12042        assert_eq!(
12043            all_hunks,
12044            vec![(
12045                "".to_string(),
12046                DiffHunkStatus::Added,
12047                DisplayRow(4)..DisplayRow(7)
12048            )]
12049        );
12050        assert_eq!(
12051            expanded_hunks_background_highlights(editor, cx),
12052            vec![DisplayRow(4)..=DisplayRow(6)]
12053        );
12054        assert_eq!(all_hunks, all_expanded_hunks);
12055    });
12056
12057    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12058    executor.run_until_parked();
12059    cx.assert_editor_state(
12060        &r#"
12061        use some::mod1;
12062        use some::mod2;
12063
12064        const A: u32 = 42;
12065        const B: u32 = 42;
12066        const C: u32 = 42;
12067        const D: u32 = 42;
12068        ˇ
12069
12070        fn main() {
12071            println!("hello");
12072
12073            println!("world");
12074        }
12075        "#
12076        .unindent(),
12077    );
12078    cx.update_editor(|editor, cx| {
12079        let snapshot = editor.snapshot(cx);
12080        let all_hunks = editor_hunks(editor, &snapshot, cx);
12081        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12082        assert_eq!(
12083            all_hunks,
12084            vec![(
12085                "".to_string(),
12086                DiffHunkStatus::Added,
12087                DisplayRow(4)..DisplayRow(8)
12088            )]
12089        );
12090        assert_eq!(
12091            expanded_hunks_background_highlights(editor, cx),
12092            vec![DisplayRow(4)..=DisplayRow(6)],
12093            "Edited hunk should have one more line added"
12094        );
12095        assert_eq!(
12096            all_hunks, all_expanded_hunks,
12097            "Expanded hunk should also grow with the addition"
12098        );
12099    });
12100
12101    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12102    executor.run_until_parked();
12103    cx.assert_editor_state(
12104        &r#"
12105        use some::mod1;
12106        use some::mod2;
12107
12108        const A: u32 = 42;
12109        const B: u32 = 42;
12110        const C: u32 = 42;
12111        const D: u32 = 42;
12112        const E: u32 = 42;
12113        ˇ
12114
12115        fn main() {
12116            println!("hello");
12117
12118            println!("world");
12119        }
12120        "#
12121        .unindent(),
12122    );
12123    cx.update_editor(|editor, cx| {
12124        let snapshot = editor.snapshot(cx);
12125        let all_hunks = editor_hunks(editor, &snapshot, cx);
12126        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12127        assert_eq!(
12128            all_hunks,
12129            vec![(
12130                "".to_string(),
12131                DiffHunkStatus::Added,
12132                DisplayRow(4)..DisplayRow(9)
12133            )]
12134        );
12135        assert_eq!(
12136            expanded_hunks_background_highlights(editor, cx),
12137            vec![DisplayRow(4)..=DisplayRow(6)],
12138            "Edited hunk should have one more line added"
12139        );
12140        assert_eq!(all_hunks, all_expanded_hunks);
12141    });
12142
12143    cx.update_editor(|editor, cx| {
12144        editor.move_up(&MoveUp, cx);
12145        editor.delete_line(&DeleteLine, cx);
12146    });
12147    executor.run_until_parked();
12148    cx.assert_editor_state(
12149        &r#"
12150        use some::mod1;
12151        use some::mod2;
12152
12153        const A: u32 = 42;
12154        const B: u32 = 42;
12155        const C: u32 = 42;
12156        const D: u32 = 42;
12157        ˇ
12158
12159        fn main() {
12160            println!("hello");
12161
12162            println!("world");
12163        }
12164        "#
12165        .unindent(),
12166    );
12167    cx.update_editor(|editor, cx| {
12168        let snapshot = editor.snapshot(cx);
12169        let all_hunks = editor_hunks(editor, &snapshot, cx);
12170        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12171        assert_eq!(
12172            all_hunks,
12173            vec![(
12174                "".to_string(),
12175                DiffHunkStatus::Added,
12176                DisplayRow(4)..DisplayRow(8)
12177            )]
12178        );
12179        assert_eq!(
12180            expanded_hunks_background_highlights(editor, cx),
12181            vec![DisplayRow(4)..=DisplayRow(6)],
12182            "Deleting a line should shrint the hunk"
12183        );
12184        assert_eq!(
12185            all_hunks, all_expanded_hunks,
12186            "Expanded hunk should also shrink with the addition"
12187        );
12188    });
12189
12190    cx.update_editor(|editor, cx| {
12191        editor.move_up(&MoveUp, cx);
12192        editor.delete_line(&DeleteLine, cx);
12193        editor.move_up(&MoveUp, cx);
12194        editor.delete_line(&DeleteLine, cx);
12195        editor.move_up(&MoveUp, cx);
12196        editor.delete_line(&DeleteLine, cx);
12197    });
12198    executor.run_until_parked();
12199    cx.assert_editor_state(
12200        &r#"
12201        use some::mod1;
12202        use some::mod2;
12203
12204        const A: u32 = 42;
12205        ˇ
12206
12207        fn main() {
12208            println!("hello");
12209
12210            println!("world");
12211        }
12212        "#
12213        .unindent(),
12214    );
12215    cx.update_editor(|editor, cx| {
12216        let snapshot = editor.snapshot(cx);
12217        let all_hunks = editor_hunks(editor, &snapshot, cx);
12218        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12219        assert_eq!(
12220            all_hunks,
12221            vec![(
12222                "".to_string(),
12223                DiffHunkStatus::Added,
12224                DisplayRow(5)..DisplayRow(6)
12225            )]
12226        );
12227        assert_eq!(
12228            expanded_hunks_background_highlights(editor, cx),
12229            vec![DisplayRow(5)..=DisplayRow(5)]
12230        );
12231        assert_eq!(all_hunks, all_expanded_hunks);
12232    });
12233
12234    cx.update_editor(|editor, cx| {
12235        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12236        editor.delete_line(&DeleteLine, cx);
12237    });
12238    executor.run_until_parked();
12239    cx.assert_editor_state(
12240        &r#"
12241        ˇ
12242
12243        fn main() {
12244            println!("hello");
12245
12246            println!("world");
12247        }
12248        "#
12249        .unindent(),
12250    );
12251    cx.update_editor(|editor, cx| {
12252        let snapshot = editor.snapshot(cx);
12253        let all_hunks = editor_hunks(editor, &snapshot, cx);
12254        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12255        assert_eq!(
12256            all_hunks,
12257            vec![
12258                (
12259                    "use some::mod1;\nuse some::mod2;\n".to_string(),
12260                    DiffHunkStatus::Removed,
12261                    DisplayRow(0)..DisplayRow(0)
12262                ),
12263                (
12264                    "const A: u32 = 42;\n".to_string(),
12265                    DiffHunkStatus::Removed,
12266                    DisplayRow(2)..DisplayRow(2)
12267                )
12268            ]
12269        );
12270        assert_eq!(
12271            expanded_hunks_background_highlights(editor, cx),
12272            Vec::new(),
12273            "Should close all stale expanded addition hunks"
12274        );
12275        assert_eq!(
12276            all_expanded_hunks,
12277            vec![(
12278                "const A: u32 = 42;\n".to_string(),
12279                DiffHunkStatus::Removed,
12280                DisplayRow(2)..DisplayRow(2)
12281            )],
12282            "Should open hunks that were adjacent to the stale addition one"
12283        );
12284    });
12285}
12286
12287#[gpui::test]
12288async fn test_edits_around_toggled_deletions(
12289    executor: BackgroundExecutor,
12290    cx: &mut gpui::TestAppContext,
12291) {
12292    init_test(cx, |_| {});
12293
12294    let mut cx = EditorTestContext::new(cx).await;
12295
12296    let diff_base = r#"
12297        use some::mod1;
12298        use some::mod2;
12299
12300        const A: u32 = 42;
12301        const B: u32 = 42;
12302        const C: u32 = 42;
12303
12304
12305        fn main() {
12306            println!("hello");
12307
12308            println!("world");
12309        }
12310        "#
12311    .unindent();
12312    executor.run_until_parked();
12313    cx.set_state(
12314        &r#"
12315        use some::mod1;
12316        use some::mod2;
12317
12318        ˇconst B: u32 = 42;
12319        const C: u32 = 42;
12320
12321
12322        fn main() {
12323            println!("hello");
12324
12325            println!("world");
12326        }
12327        "#
12328        .unindent(),
12329    );
12330
12331    cx.set_diff_base(Some(&diff_base));
12332    executor.run_until_parked();
12333    cx.update_editor(|editor, cx| {
12334        let snapshot = editor.snapshot(cx);
12335        let all_hunks = editor_hunks(editor, &snapshot, cx);
12336        assert_eq!(
12337            all_hunks,
12338            vec![(
12339                "const A: u32 = 42;\n".to_string(),
12340                DiffHunkStatus::Removed,
12341                DisplayRow(3)..DisplayRow(3)
12342            )]
12343        );
12344    });
12345    cx.update_editor(|editor, cx| {
12346        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12347    });
12348    executor.run_until_parked();
12349    cx.assert_editor_state(
12350        &r#"
12351        use some::mod1;
12352        use some::mod2;
12353
12354        ˇconst B: u32 = 42;
12355        const C: u32 = 42;
12356
12357
12358        fn main() {
12359            println!("hello");
12360
12361            println!("world");
12362        }
12363        "#
12364        .unindent(),
12365    );
12366    cx.update_editor(|editor, cx| {
12367        let snapshot = editor.snapshot(cx);
12368        let all_hunks = editor_hunks(editor, &snapshot, cx);
12369        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12370        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12371        assert_eq!(
12372            all_hunks,
12373            vec![(
12374                "const A: u32 = 42;\n".to_string(),
12375                DiffHunkStatus::Removed,
12376                DisplayRow(4)..DisplayRow(4)
12377            )]
12378        );
12379        assert_eq!(all_hunks, all_expanded_hunks);
12380    });
12381
12382    cx.update_editor(|editor, cx| {
12383        editor.delete_line(&DeleteLine, cx);
12384    });
12385    executor.run_until_parked();
12386    cx.assert_editor_state(
12387        &r#"
12388        use some::mod1;
12389        use some::mod2;
12390
12391        ˇconst C: u32 = 42;
12392
12393
12394        fn main() {
12395            println!("hello");
12396
12397            println!("world");
12398        }
12399        "#
12400        .unindent(),
12401    );
12402    cx.update_editor(|editor, cx| {
12403        let snapshot = editor.snapshot(cx);
12404        let all_hunks = editor_hunks(editor, &snapshot, cx);
12405        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12406        assert_eq!(
12407            expanded_hunks_background_highlights(editor, cx),
12408            Vec::new(),
12409            "Deleted hunks do not highlight current editor's background"
12410        );
12411        assert_eq!(
12412            all_hunks,
12413            vec![(
12414                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
12415                DiffHunkStatus::Removed,
12416                DisplayRow(5)..DisplayRow(5)
12417            )]
12418        );
12419        assert_eq!(all_hunks, all_expanded_hunks);
12420    });
12421
12422    cx.update_editor(|editor, cx| {
12423        editor.delete_line(&DeleteLine, cx);
12424    });
12425    executor.run_until_parked();
12426    cx.assert_editor_state(
12427        &r#"
12428        use some::mod1;
12429        use some::mod2;
12430
12431        ˇ
12432
12433        fn main() {
12434            println!("hello");
12435
12436            println!("world");
12437        }
12438        "#
12439        .unindent(),
12440    );
12441    cx.update_editor(|editor, cx| {
12442        let snapshot = editor.snapshot(cx);
12443        let all_hunks = editor_hunks(editor, &snapshot, cx);
12444        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12445        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
12446        assert_eq!(
12447            all_hunks,
12448            vec![(
12449                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12450                DiffHunkStatus::Removed,
12451                DisplayRow(6)..DisplayRow(6)
12452            )]
12453        );
12454        assert_eq!(all_hunks, all_expanded_hunks);
12455    });
12456
12457    cx.update_editor(|editor, cx| {
12458        editor.handle_input("replacement", cx);
12459    });
12460    executor.run_until_parked();
12461    cx.assert_editor_state(
12462        &r#"
12463        use some::mod1;
12464        use some::mod2;
12465
12466        replacementˇ
12467
12468        fn main() {
12469            println!("hello");
12470
12471            println!("world");
12472        }
12473        "#
12474        .unindent(),
12475    );
12476    cx.update_editor(|editor, cx| {
12477        let snapshot = editor.snapshot(cx);
12478        let all_hunks = editor_hunks(editor, &snapshot, cx);
12479        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12480        assert_eq!(
12481            all_hunks,
12482            vec![(
12483                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
12484                DiffHunkStatus::Modified,
12485                DisplayRow(7)..DisplayRow(8)
12486            )]
12487        );
12488        assert_eq!(
12489            expanded_hunks_background_highlights(editor, cx),
12490            vec![DisplayRow(7)..=DisplayRow(7)],
12491            "Modified expanded hunks should display additions and highlight their background"
12492        );
12493        assert_eq!(all_hunks, all_expanded_hunks);
12494    });
12495}
12496
12497#[gpui::test]
12498async fn test_edits_around_toggled_modifications(
12499    executor: BackgroundExecutor,
12500    cx: &mut gpui::TestAppContext,
12501) {
12502    init_test(cx, |_| {});
12503
12504    let mut cx = EditorTestContext::new(cx).await;
12505
12506    let diff_base = r#"
12507        use some::mod1;
12508        use some::mod2;
12509
12510        const A: u32 = 42;
12511        const B: u32 = 42;
12512        const C: u32 = 42;
12513        const D: u32 = 42;
12514
12515
12516        fn main() {
12517            println!("hello");
12518
12519            println!("world");
12520        }"#
12521    .unindent();
12522    executor.run_until_parked();
12523    cx.set_state(
12524        &r#"
12525        use some::mod1;
12526        use some::mod2;
12527
12528        const A: u32 = 42;
12529        const B: u32 = 42;
12530        const C: u32 = 43ˇ
12531        const D: u32 = 42;
12532
12533
12534        fn main() {
12535            println!("hello");
12536
12537            println!("world");
12538        }"#
12539        .unindent(),
12540    );
12541
12542    cx.set_diff_base(Some(&diff_base));
12543    executor.run_until_parked();
12544    cx.update_editor(|editor, cx| {
12545        let snapshot = editor.snapshot(cx);
12546        let all_hunks = editor_hunks(editor, &snapshot, cx);
12547        assert_eq!(
12548            all_hunks,
12549            vec![(
12550                "const C: u32 = 42;\n".to_string(),
12551                DiffHunkStatus::Modified,
12552                DisplayRow(5)..DisplayRow(6)
12553            )]
12554        );
12555    });
12556    cx.update_editor(|editor, cx| {
12557        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12558    });
12559    executor.run_until_parked();
12560    cx.assert_editor_state(
12561        &r#"
12562        use some::mod1;
12563        use some::mod2;
12564
12565        const A: u32 = 42;
12566        const B: u32 = 42;
12567        const C: u32 = 43ˇ
12568        const D: u32 = 42;
12569
12570
12571        fn main() {
12572            println!("hello");
12573
12574            println!("world");
12575        }"#
12576        .unindent(),
12577    );
12578    cx.update_editor(|editor, cx| {
12579        let snapshot = editor.snapshot(cx);
12580        let all_hunks = editor_hunks(editor, &snapshot, cx);
12581        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12582        assert_eq!(
12583            expanded_hunks_background_highlights(editor, cx),
12584            vec![DisplayRow(6)..=DisplayRow(6)],
12585        );
12586        assert_eq!(
12587            all_hunks,
12588            vec![(
12589                "const C: u32 = 42;\n".to_string(),
12590                DiffHunkStatus::Modified,
12591                DisplayRow(6)..DisplayRow(7)
12592            )]
12593        );
12594        assert_eq!(all_hunks, all_expanded_hunks);
12595    });
12596
12597    cx.update_editor(|editor, cx| {
12598        editor.handle_input("\nnew_line\n", cx);
12599    });
12600    executor.run_until_parked();
12601    cx.assert_editor_state(
12602        &r#"
12603            use some::mod1;
12604            use some::mod2;
12605
12606            const A: u32 = 42;
12607            const B: u32 = 42;
12608            const C: u32 = 43
12609            new_line
12610            ˇ
12611            const D: u32 = 42;
12612
12613
12614            fn main() {
12615                println!("hello");
12616
12617                println!("world");
12618            }"#
12619        .unindent(),
12620    );
12621    cx.update_editor(|editor, cx| {
12622        let snapshot = editor.snapshot(cx);
12623        let all_hunks = editor_hunks(editor, &snapshot, cx);
12624        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12625        assert_eq!(
12626            expanded_hunks_background_highlights(editor, cx),
12627            vec![DisplayRow(6)..=DisplayRow(6)],
12628            "Modified hunk should grow highlighted lines on more text additions"
12629        );
12630        assert_eq!(
12631            all_hunks,
12632            vec![(
12633                "const C: u32 = 42;\n".to_string(),
12634                DiffHunkStatus::Modified,
12635                DisplayRow(6)..DisplayRow(9)
12636            )]
12637        );
12638        assert_eq!(all_hunks, all_expanded_hunks);
12639    });
12640
12641    cx.update_editor(|editor, cx| {
12642        editor.move_up(&MoveUp, cx);
12643        editor.move_up(&MoveUp, cx);
12644        editor.move_up(&MoveUp, cx);
12645        editor.delete_line(&DeleteLine, cx);
12646    });
12647    executor.run_until_parked();
12648    cx.assert_editor_state(
12649        &r#"
12650            use some::mod1;
12651            use some::mod2;
12652
12653            const A: u32 = 42;
12654            ˇconst C: u32 = 43
12655            new_line
12656
12657            const D: u32 = 42;
12658
12659
12660            fn main() {
12661                println!("hello");
12662
12663                println!("world");
12664            }"#
12665        .unindent(),
12666    );
12667    cx.update_editor(|editor, cx| {
12668        let snapshot = editor.snapshot(cx);
12669        let all_hunks = editor_hunks(editor, &snapshot, cx);
12670        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12671        assert_eq!(
12672            expanded_hunks_background_highlights(editor, cx),
12673            vec![DisplayRow(6)..=DisplayRow(8)],
12674        );
12675        assert_eq!(
12676            all_hunks,
12677            vec![(
12678                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12679                DiffHunkStatus::Modified,
12680                DisplayRow(6)..DisplayRow(9)
12681            )],
12682            "Modified hunk should grow deleted lines on text deletions above"
12683        );
12684        assert_eq!(all_hunks, all_expanded_hunks);
12685    });
12686
12687    cx.update_editor(|editor, cx| {
12688        editor.move_up(&MoveUp, cx);
12689        editor.handle_input("v", cx);
12690    });
12691    executor.run_until_parked();
12692    cx.assert_editor_state(
12693        &r#"
12694            use some::mod1;
12695            use some::mod2;
12696
12697            vˇconst A: u32 = 42;
12698            const C: u32 = 43
12699            new_line
12700
12701            const D: u32 = 42;
12702
12703
12704            fn main() {
12705                println!("hello");
12706
12707                println!("world");
12708            }"#
12709        .unindent(),
12710    );
12711    cx.update_editor(|editor, cx| {
12712        let snapshot = editor.snapshot(cx);
12713        let all_hunks = editor_hunks(editor, &snapshot, cx);
12714        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12715        assert_eq!(
12716            expanded_hunks_background_highlights(editor, cx),
12717            vec![DisplayRow(6)..=DisplayRow(9)],
12718            "Modified hunk should grow deleted lines on text modifications above"
12719        );
12720        assert_eq!(
12721            all_hunks,
12722            vec![(
12723                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12724                DiffHunkStatus::Modified,
12725                DisplayRow(6)..DisplayRow(10)
12726            )]
12727        );
12728        assert_eq!(all_hunks, all_expanded_hunks);
12729    });
12730
12731    cx.update_editor(|editor, cx| {
12732        editor.move_down(&MoveDown, cx);
12733        editor.move_down(&MoveDown, cx);
12734        editor.delete_line(&DeleteLine, cx)
12735    });
12736    executor.run_until_parked();
12737    cx.assert_editor_state(
12738        &r#"
12739            use some::mod1;
12740            use some::mod2;
12741
12742            vconst A: u32 = 42;
12743            const C: u32 = 43
12744            ˇ
12745            const D: u32 = 42;
12746
12747
12748            fn main() {
12749                println!("hello");
12750
12751                println!("world");
12752            }"#
12753        .unindent(),
12754    );
12755    cx.update_editor(|editor, cx| {
12756        let snapshot = editor.snapshot(cx);
12757        let all_hunks = editor_hunks(editor, &snapshot, cx);
12758        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12759        assert_eq!(
12760            expanded_hunks_background_highlights(editor, cx),
12761            vec![DisplayRow(6)..=DisplayRow(8)],
12762            "Modified hunk should grow shrink lines on modification lines removal"
12763        );
12764        assert_eq!(
12765            all_hunks,
12766            vec![(
12767                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
12768                DiffHunkStatus::Modified,
12769                DisplayRow(6)..DisplayRow(9)
12770            )]
12771        );
12772        assert_eq!(all_hunks, all_expanded_hunks);
12773    });
12774
12775    cx.update_editor(|editor, cx| {
12776        editor.move_up(&MoveUp, cx);
12777        editor.move_up(&MoveUp, cx);
12778        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
12779        editor.delete_line(&DeleteLine, cx)
12780    });
12781    executor.run_until_parked();
12782    cx.assert_editor_state(
12783        &r#"
12784            use some::mod1;
12785            use some::mod2;
12786
12787            ˇ
12788
12789            fn main() {
12790                println!("hello");
12791
12792                println!("world");
12793            }"#
12794        .unindent(),
12795    );
12796    cx.update_editor(|editor, cx| {
12797        let snapshot = editor.snapshot(cx);
12798        let all_hunks = editor_hunks(editor, &snapshot, cx);
12799        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12800        assert_eq!(
12801            expanded_hunks_background_highlights(editor, cx),
12802            Vec::new(),
12803            "Modified hunk should turn into a removed one on all modified lines removal"
12804        );
12805        assert_eq!(
12806            all_hunks,
12807            vec![(
12808                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
12809                    .to_string(),
12810                DiffHunkStatus::Removed,
12811                DisplayRow(7)..DisplayRow(7)
12812            )]
12813        );
12814        assert_eq!(all_hunks, all_expanded_hunks);
12815    });
12816}
12817
12818#[gpui::test]
12819async fn test_multiple_expanded_hunks_merge(
12820    executor: BackgroundExecutor,
12821    cx: &mut gpui::TestAppContext,
12822) {
12823    init_test(cx, |_| {});
12824
12825    let mut cx = EditorTestContext::new(cx).await;
12826
12827    let diff_base = r#"
12828        use some::mod1;
12829        use some::mod2;
12830
12831        const A: u32 = 42;
12832        const B: u32 = 42;
12833        const C: u32 = 42;
12834        const D: u32 = 42;
12835
12836
12837        fn main() {
12838            println!("hello");
12839
12840            println!("world");
12841        }"#
12842    .unindent();
12843    executor.run_until_parked();
12844    cx.set_state(
12845        &r#"
12846        use some::mod1;
12847        use some::mod2;
12848
12849        const A: u32 = 42;
12850        const B: u32 = 42;
12851        const C: u32 = 43ˇ
12852        const D: u32 = 42;
12853
12854
12855        fn main() {
12856            println!("hello");
12857
12858            println!("world");
12859        }"#
12860        .unindent(),
12861    );
12862
12863    cx.set_diff_base(Some(&diff_base));
12864    executor.run_until_parked();
12865    cx.update_editor(|editor, cx| {
12866        let snapshot = editor.snapshot(cx);
12867        let all_hunks = editor_hunks(editor, &snapshot, cx);
12868        assert_eq!(
12869            all_hunks,
12870            vec![(
12871                "const C: u32 = 42;\n".to_string(),
12872                DiffHunkStatus::Modified,
12873                DisplayRow(5)..DisplayRow(6)
12874            )]
12875        );
12876    });
12877    cx.update_editor(|editor, cx| {
12878        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12879    });
12880    executor.run_until_parked();
12881    cx.assert_editor_state(
12882        &r#"
12883        use some::mod1;
12884        use some::mod2;
12885
12886        const A: u32 = 42;
12887        const B: u32 = 42;
12888        const C: u32 = 43ˇ
12889        const D: u32 = 42;
12890
12891
12892        fn main() {
12893            println!("hello");
12894
12895            println!("world");
12896        }"#
12897        .unindent(),
12898    );
12899    cx.update_editor(|editor, cx| {
12900        let snapshot = editor.snapshot(cx);
12901        let all_hunks = editor_hunks(editor, &snapshot, cx);
12902        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
12903        assert_eq!(
12904            expanded_hunks_background_highlights(editor, cx),
12905            vec![DisplayRow(6)..=DisplayRow(6)],
12906        );
12907        assert_eq!(
12908            all_hunks,
12909            vec![(
12910                "const C: u32 = 42;\n".to_string(),
12911                DiffHunkStatus::Modified,
12912                DisplayRow(6)..DisplayRow(7)
12913            )]
12914        );
12915        assert_eq!(all_hunks, all_expanded_hunks);
12916    });
12917
12918    cx.update_editor(|editor, cx| {
12919        editor.handle_input("\nnew_line\n", cx);
12920    });
12921    executor.run_until_parked();
12922    cx.assert_editor_state(
12923        &r#"
12924            use some::mod1;
12925            use some::mod2;
12926
12927            const A: u32 = 42;
12928            const B: u32 = 42;
12929            const C: u32 = 43
12930            new_line
12931            ˇ
12932            const D: u32 = 42;
12933
12934
12935            fn main() {
12936                println!("hello");
12937
12938                println!("world");
12939            }"#
12940        .unindent(),
12941    );
12942}
12943
12944async fn setup_indent_guides_editor(
12945    text: &str,
12946    cx: &mut gpui::TestAppContext,
12947) -> (BufferId, EditorTestContext) {
12948    init_test(cx, |_| {});
12949
12950    let mut cx = EditorTestContext::new(cx).await;
12951
12952    let buffer_id = cx.update_editor(|editor, cx| {
12953        editor.set_text(text, cx);
12954        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12955
12956        buffer_ids[0]
12957    });
12958
12959    (buffer_id, cx)
12960}
12961
12962fn assert_indent_guides(
12963    range: Range<u32>,
12964    expected: Vec<IndentGuide>,
12965    active_indices: Option<Vec<usize>>,
12966    cx: &mut EditorTestContext,
12967) {
12968    let indent_guides = cx.update_editor(|editor, cx| {
12969        let snapshot = editor.snapshot(cx).display_snapshot;
12970        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12971            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12972            true,
12973            &snapshot,
12974            cx,
12975        );
12976
12977        indent_guides.sort_by(|a, b| {
12978            a.depth.cmp(&b.depth).then(
12979                a.start_row
12980                    .cmp(&b.start_row)
12981                    .then(a.end_row.cmp(&b.end_row)),
12982            )
12983        });
12984        indent_guides
12985    });
12986
12987    if let Some(expected) = active_indices {
12988        let active_indices = cx.update_editor(|editor, cx| {
12989            let snapshot = editor.snapshot(cx).display_snapshot;
12990            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12991        });
12992
12993        assert_eq!(
12994            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12995            expected,
12996            "Active indent guide indices do not match"
12997        );
12998    }
12999
13000    let expected: Vec<_> = expected
13001        .into_iter()
13002        .map(|guide| MultiBufferIndentGuide {
13003            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
13004            buffer: guide,
13005        })
13006        .collect();
13007
13008    assert_eq!(indent_guides, expected, "Indent guides do not match");
13009}
13010
13011fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13012    IndentGuide {
13013        buffer_id,
13014        start_row,
13015        end_row,
13016        depth,
13017        tab_size: 4,
13018        settings: IndentGuideSettings {
13019            enabled: true,
13020            line_width: 1,
13021            active_line_width: 1,
13022            ..Default::default()
13023        },
13024    }
13025}
13026
13027#[gpui::test]
13028async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13029    let (buffer_id, mut cx) = setup_indent_guides_editor(
13030        &"
13031    fn main() {
13032        let a = 1;
13033    }"
13034        .unindent(),
13035        cx,
13036    )
13037    .await;
13038
13039    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13040}
13041
13042#[gpui::test]
13043async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13044    let (buffer_id, mut cx) = setup_indent_guides_editor(
13045        &"
13046    fn main() {
13047        let a = 1;
13048        let b = 2;
13049    }"
13050        .unindent(),
13051        cx,
13052    )
13053    .await;
13054
13055    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13056}
13057
13058#[gpui::test]
13059async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13060    let (buffer_id, mut cx) = setup_indent_guides_editor(
13061        &"
13062    fn main() {
13063        let a = 1;
13064        if a == 3 {
13065            let b = 2;
13066        } else {
13067            let c = 3;
13068        }
13069    }"
13070        .unindent(),
13071        cx,
13072    )
13073    .await;
13074
13075    assert_indent_guides(
13076        0..8,
13077        vec![
13078            indent_guide(buffer_id, 1, 6, 0),
13079            indent_guide(buffer_id, 3, 3, 1),
13080            indent_guide(buffer_id, 5, 5, 1),
13081        ],
13082        None,
13083        &mut cx,
13084    );
13085}
13086
13087#[gpui::test]
13088async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13089    let (buffer_id, mut cx) = setup_indent_guides_editor(
13090        &"
13091    fn main() {
13092        let a = 1;
13093            let b = 2;
13094        let c = 3;
13095    }"
13096        .unindent(),
13097        cx,
13098    )
13099    .await;
13100
13101    assert_indent_guides(
13102        0..5,
13103        vec![
13104            indent_guide(buffer_id, 1, 3, 0),
13105            indent_guide(buffer_id, 2, 2, 1),
13106        ],
13107        None,
13108        &mut cx,
13109    );
13110}
13111
13112#[gpui::test]
13113async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13114    let (buffer_id, mut cx) = setup_indent_guides_editor(
13115        &"
13116        fn main() {
13117            let a = 1;
13118
13119            let c = 3;
13120        }"
13121        .unindent(),
13122        cx,
13123    )
13124    .await;
13125
13126    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13127}
13128
13129#[gpui::test]
13130async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13131    let (buffer_id, mut cx) = setup_indent_guides_editor(
13132        &"
13133        fn main() {
13134            let a = 1;
13135
13136            let c = 3;
13137
13138            if a == 3 {
13139                let b = 2;
13140            } else {
13141                let c = 3;
13142            }
13143        }"
13144        .unindent(),
13145        cx,
13146    )
13147    .await;
13148
13149    assert_indent_guides(
13150        0..11,
13151        vec![
13152            indent_guide(buffer_id, 1, 9, 0),
13153            indent_guide(buffer_id, 6, 6, 1),
13154            indent_guide(buffer_id, 8, 8, 1),
13155        ],
13156        None,
13157        &mut cx,
13158    );
13159}
13160
13161#[gpui::test]
13162async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13163    let (buffer_id, mut cx) = setup_indent_guides_editor(
13164        &"
13165        fn main() {
13166            let a = 1;
13167
13168            let c = 3;
13169
13170            if a == 3 {
13171                let b = 2;
13172            } else {
13173                let c = 3;
13174            }
13175        }"
13176        .unindent(),
13177        cx,
13178    )
13179    .await;
13180
13181    assert_indent_guides(
13182        1..11,
13183        vec![
13184            indent_guide(buffer_id, 1, 9, 0),
13185            indent_guide(buffer_id, 6, 6, 1),
13186            indent_guide(buffer_id, 8, 8, 1),
13187        ],
13188        None,
13189        &mut cx,
13190    );
13191}
13192
13193#[gpui::test]
13194async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13195    let (buffer_id, mut cx) = setup_indent_guides_editor(
13196        &"
13197        fn main() {
13198            let a = 1;
13199
13200            let c = 3;
13201
13202            if a == 3 {
13203                let b = 2;
13204            } else {
13205                let c = 3;
13206            }
13207        }"
13208        .unindent(),
13209        cx,
13210    )
13211    .await;
13212
13213    assert_indent_guides(
13214        1..10,
13215        vec![
13216            indent_guide(buffer_id, 1, 9, 0),
13217            indent_guide(buffer_id, 6, 6, 1),
13218            indent_guide(buffer_id, 8, 8, 1),
13219        ],
13220        None,
13221        &mut cx,
13222    );
13223}
13224
13225#[gpui::test]
13226async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13227    let (buffer_id, mut cx) = setup_indent_guides_editor(
13228        &"
13229        block1
13230            block2
13231                block3
13232                    block4
13233            block2
13234        block1
13235        block1"
13236            .unindent(),
13237        cx,
13238    )
13239    .await;
13240
13241    assert_indent_guides(
13242        1..10,
13243        vec![
13244            indent_guide(buffer_id, 1, 4, 0),
13245            indent_guide(buffer_id, 2, 3, 1),
13246            indent_guide(buffer_id, 3, 3, 2),
13247        ],
13248        None,
13249        &mut cx,
13250    );
13251}
13252
13253#[gpui::test]
13254async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13255    let (buffer_id, mut cx) = setup_indent_guides_editor(
13256        &"
13257        block1
13258            block2
13259                block3
13260
13261        block1
13262        block1"
13263            .unindent(),
13264        cx,
13265    )
13266    .await;
13267
13268    assert_indent_guides(
13269        0..6,
13270        vec![
13271            indent_guide(buffer_id, 1, 2, 0),
13272            indent_guide(buffer_id, 2, 2, 1),
13273        ],
13274        None,
13275        &mut cx,
13276    );
13277}
13278
13279#[gpui::test]
13280async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13281    let (buffer_id, mut cx) = setup_indent_guides_editor(
13282        &"
13283        block1
13284
13285
13286
13287            block2
13288        "
13289        .unindent(),
13290        cx,
13291    )
13292    .await;
13293
13294    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13295}
13296
13297#[gpui::test]
13298async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13299    let (buffer_id, mut cx) = setup_indent_guides_editor(
13300        &"
13301        def a:
13302        \tb = 3
13303        \tif True:
13304        \t\tc = 4
13305        \t\td = 5
13306        \tprint(b)
13307        "
13308        .unindent(),
13309        cx,
13310    )
13311    .await;
13312
13313    assert_indent_guides(
13314        0..6,
13315        vec![
13316            indent_guide(buffer_id, 1, 6, 0),
13317            indent_guide(buffer_id, 3, 4, 1),
13318        ],
13319        None,
13320        &mut cx,
13321    );
13322}
13323
13324#[gpui::test]
13325async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13326    let (buffer_id, mut cx) = setup_indent_guides_editor(
13327        &"
13328    fn main() {
13329        let a = 1;
13330    }"
13331        .unindent(),
13332        cx,
13333    )
13334    .await;
13335
13336    cx.update_editor(|editor, cx| {
13337        editor.change_selections(None, cx, |s| {
13338            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13339        });
13340    });
13341
13342    assert_indent_guides(
13343        0..3,
13344        vec![indent_guide(buffer_id, 1, 1, 0)],
13345        Some(vec![0]),
13346        &mut cx,
13347    );
13348}
13349
13350#[gpui::test]
13351async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13352    let (buffer_id, mut cx) = setup_indent_guides_editor(
13353        &"
13354    fn main() {
13355        if 1 == 2 {
13356            let a = 1;
13357        }
13358    }"
13359        .unindent(),
13360        cx,
13361    )
13362    .await;
13363
13364    cx.update_editor(|editor, cx| {
13365        editor.change_selections(None, cx, |s| {
13366            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13367        });
13368    });
13369
13370    assert_indent_guides(
13371        0..4,
13372        vec![
13373            indent_guide(buffer_id, 1, 3, 0),
13374            indent_guide(buffer_id, 2, 2, 1),
13375        ],
13376        Some(vec![1]),
13377        &mut cx,
13378    );
13379
13380    cx.update_editor(|editor, cx| {
13381        editor.change_selections(None, cx, |s| {
13382            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13383        });
13384    });
13385
13386    assert_indent_guides(
13387        0..4,
13388        vec![
13389            indent_guide(buffer_id, 1, 3, 0),
13390            indent_guide(buffer_id, 2, 2, 1),
13391        ],
13392        Some(vec![1]),
13393        &mut cx,
13394    );
13395
13396    cx.update_editor(|editor, cx| {
13397        editor.change_selections(None, cx, |s| {
13398            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13399        });
13400    });
13401
13402    assert_indent_guides(
13403        0..4,
13404        vec![
13405            indent_guide(buffer_id, 1, 3, 0),
13406            indent_guide(buffer_id, 2, 2, 1),
13407        ],
13408        Some(vec![0]),
13409        &mut cx,
13410    );
13411}
13412
13413#[gpui::test]
13414async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13415    let (buffer_id, mut cx) = setup_indent_guides_editor(
13416        &"
13417    fn main() {
13418        let a = 1;
13419
13420        let b = 2;
13421    }"
13422        .unindent(),
13423        cx,
13424    )
13425    .await;
13426
13427    cx.update_editor(|editor, cx| {
13428        editor.change_selections(None, cx, |s| {
13429            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13430        });
13431    });
13432
13433    assert_indent_guides(
13434        0..5,
13435        vec![indent_guide(buffer_id, 1, 3, 0)],
13436        Some(vec![0]),
13437        &mut cx,
13438    );
13439}
13440
13441#[gpui::test]
13442async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13443    let (buffer_id, mut cx) = setup_indent_guides_editor(
13444        &"
13445    def m:
13446        a = 1
13447        pass"
13448            .unindent(),
13449        cx,
13450    )
13451    .await;
13452
13453    cx.update_editor(|editor, cx| {
13454        editor.change_selections(None, cx, |s| {
13455            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13456        });
13457    });
13458
13459    assert_indent_guides(
13460        0..3,
13461        vec![indent_guide(buffer_id, 1, 2, 0)],
13462        Some(vec![0]),
13463        &mut cx,
13464    );
13465}
13466
13467#[gpui::test]
13468fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13469    init_test(cx, |_| {});
13470
13471    let editor = cx.add_window(|cx| {
13472        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13473        build_editor(buffer, cx)
13474    });
13475
13476    let render_args = Arc::new(Mutex::new(None));
13477    let snapshot = editor
13478        .update(cx, |editor, cx| {
13479            let snapshot = editor.buffer().read(cx).snapshot(cx);
13480            let range =
13481                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13482
13483            struct RenderArgs {
13484                row: MultiBufferRow,
13485                folded: bool,
13486                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13487            }
13488
13489            let crease = Crease::new(
13490                range,
13491                FoldPlaceholder::test(),
13492                {
13493                    let toggle_callback = render_args.clone();
13494                    move |row, folded, callback, _cx| {
13495                        *toggle_callback.lock() = Some(RenderArgs {
13496                            row,
13497                            folded,
13498                            callback,
13499                        });
13500                        div()
13501                    }
13502                },
13503                |_row, _folded, _cx| div(),
13504            );
13505
13506            editor.insert_creases(Some(crease), cx);
13507            let snapshot = editor.snapshot(cx);
13508            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13509            snapshot
13510        })
13511        .unwrap();
13512
13513    let render_args = render_args.lock().take().unwrap();
13514    assert_eq!(render_args.row, MultiBufferRow(1));
13515    assert!(!render_args.folded);
13516    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13517
13518    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13519        .unwrap();
13520    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13521    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13522
13523    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13524        .unwrap();
13525    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13526    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13527}
13528
13529#[gpui::test]
13530async fn test_input_text(cx: &mut gpui::TestAppContext) {
13531    init_test(cx, |_| {});
13532    let mut cx = EditorTestContext::new(cx).await;
13533
13534    cx.set_state(
13535        &r#"ˇone
13536        two
13537
13538        three
13539        fourˇ
13540        five
13541
13542        siˇx"#
13543            .unindent(),
13544    );
13545
13546    cx.dispatch_action(HandleInput(String::new()));
13547    cx.assert_editor_state(
13548        &r#"ˇone
13549        two
13550
13551        three
13552        fourˇ
13553        five
13554
13555        siˇx"#
13556            .unindent(),
13557    );
13558
13559    cx.dispatch_action(HandleInput("AAAA".to_string()));
13560    cx.assert_editor_state(
13561        &r#"AAAAˇone
13562        two
13563
13564        three
13565        fourAAAAˇ
13566        five
13567
13568        siAAAAˇx"#
13569            .unindent(),
13570    );
13571}
13572
13573#[gpui::test]
13574async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13575    init_test(cx, |_| {});
13576
13577    let mut cx = EditorTestContext::new(cx).await;
13578    cx.set_state(
13579        r#"let foo = 1;
13580let foo = 2;
13581let foo = 3;
13582let fooˇ = 4;
13583let foo = 5;
13584let foo = 6;
13585let foo = 7;
13586let foo = 8;
13587let foo = 9;
13588let foo = 10;
13589let foo = 11;
13590let foo = 12;
13591let foo = 13;
13592let foo = 14;
13593let foo = 15;"#,
13594    );
13595
13596    cx.update_editor(|e, cx| {
13597        assert_eq!(
13598            e.next_scroll_position,
13599            NextScrollCursorCenterTopBottom::Center,
13600            "Default next scroll direction is center",
13601        );
13602
13603        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13604        assert_eq!(
13605            e.next_scroll_position,
13606            NextScrollCursorCenterTopBottom::Top,
13607            "After center, next scroll direction should be top",
13608        );
13609
13610        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13611        assert_eq!(
13612            e.next_scroll_position,
13613            NextScrollCursorCenterTopBottom::Bottom,
13614            "After top, next scroll direction should be bottom",
13615        );
13616
13617        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13618        assert_eq!(
13619            e.next_scroll_position,
13620            NextScrollCursorCenterTopBottom::Center,
13621            "After bottom, scrolling should start over",
13622        );
13623
13624        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13625        assert_eq!(
13626            e.next_scroll_position,
13627            NextScrollCursorCenterTopBottom::Top,
13628            "Scrolling continues if retriggered fast enough"
13629        );
13630    });
13631
13632    cx.executor()
13633        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13634    cx.executor().run_until_parked();
13635    cx.update_editor(|e, _| {
13636        assert_eq!(
13637            e.next_scroll_position,
13638            NextScrollCursorCenterTopBottom::Center,
13639            "If scrolling is not triggered fast enough, it should reset"
13640        );
13641    });
13642}
13643
13644#[gpui::test]
13645async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13646    init_test(cx, |_| {});
13647    let mut cx = EditorLspTestContext::new_rust(
13648        lsp::ServerCapabilities {
13649            definition_provider: Some(lsp::OneOf::Left(true)),
13650            references_provider: Some(lsp::OneOf::Left(true)),
13651            ..lsp::ServerCapabilities::default()
13652        },
13653        cx,
13654    )
13655    .await;
13656
13657    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13658        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13659            move |params, _| async move {
13660                if empty_go_to_definition {
13661                    Ok(None)
13662                } else {
13663                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13664                        uri: params.text_document_position_params.text_document.uri,
13665                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13666                    })))
13667                }
13668            },
13669        );
13670        let references =
13671            cx.lsp
13672                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13673                    Ok(Some(vec![lsp::Location {
13674                        uri: params.text_document_position.text_document.uri,
13675                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13676                    }]))
13677                });
13678        (go_to_definition, references)
13679    };
13680
13681    cx.set_state(
13682        &r#"fn one() {
13683            let mut a = ˇtwo();
13684        }
13685
13686        fn two() {}"#
13687            .unindent(),
13688    );
13689    set_up_lsp_handlers(false, &mut cx);
13690    let navigated = cx
13691        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13692        .await
13693        .expect("Failed to navigate to definition");
13694    assert_eq!(
13695        navigated,
13696        Navigated::Yes,
13697        "Should have navigated to definition from the GetDefinition response"
13698    );
13699    cx.assert_editor_state(
13700        &r#"fn one() {
13701            let mut a = two();
13702        }
13703
13704        fn «twoˇ»() {}"#
13705            .unindent(),
13706    );
13707
13708    let editors = cx.update_workspace(|workspace, cx| {
13709        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13710    });
13711    cx.update_editor(|_, test_editor_cx| {
13712        assert_eq!(
13713            editors.len(),
13714            1,
13715            "Initially, only one, test, editor should be open in the workspace"
13716        );
13717        assert_eq!(
13718            test_editor_cx.view(),
13719            editors.last().expect("Asserted len is 1")
13720        );
13721    });
13722
13723    set_up_lsp_handlers(true, &mut cx);
13724    let navigated = cx
13725        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13726        .await
13727        .expect("Failed to navigate to lookup references");
13728    assert_eq!(
13729        navigated,
13730        Navigated::Yes,
13731        "Should have navigated to references as a fallback after empty GoToDefinition response"
13732    );
13733    // We should not change the selections in the existing file,
13734    // if opening another milti buffer with the references
13735    cx.assert_editor_state(
13736        &r#"fn one() {
13737            let mut a = two();
13738        }
13739
13740        fn «twoˇ»() {}"#
13741            .unindent(),
13742    );
13743    let editors = cx.update_workspace(|workspace, cx| {
13744        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13745    });
13746    cx.update_editor(|_, test_editor_cx| {
13747        assert_eq!(
13748            editors.len(),
13749            2,
13750            "After falling back to references search, we open a new editor with the results"
13751        );
13752        let references_fallback_text = editors
13753            .into_iter()
13754            .find(|new_editor| new_editor != test_editor_cx.view())
13755            .expect("Should have one non-test editor now")
13756            .read(test_editor_cx)
13757            .text(test_editor_cx);
13758        assert_eq!(
13759            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13760            "Should use the range from the references response and not the GoToDefinition one"
13761        );
13762    });
13763}
13764
13765fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13766    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13767    point..point
13768}
13769
13770fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13771    let (text, ranges) = marked_text_ranges(marked_text, true);
13772    assert_eq!(view.text(cx), text);
13773    assert_eq!(
13774        view.selections.ranges(cx),
13775        ranges,
13776        "Assert selections are {}",
13777        marked_text
13778    );
13779}
13780
13781pub fn handle_signature_help_request(
13782    cx: &mut EditorLspTestContext,
13783    mocked_response: lsp::SignatureHelp,
13784) -> impl Future<Output = ()> {
13785    let mut request =
13786        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13787            let mocked_response = mocked_response.clone();
13788            async move { Ok(Some(mocked_response)) }
13789        });
13790
13791    async move {
13792        request.next().await;
13793    }
13794}
13795
13796/// Handle completion request passing a marked string specifying where the completion
13797/// should be triggered from using '|' character, what range should be replaced, and what completions
13798/// should be returned using '<' and '>' to delimit the range
13799pub fn handle_completion_request(
13800    cx: &mut EditorLspTestContext,
13801    marked_string: &str,
13802    completions: Vec<&'static str>,
13803    counter: Arc<AtomicUsize>,
13804) -> impl Future<Output = ()> {
13805    let complete_from_marker: TextRangeMarker = '|'.into();
13806    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13807    let (_, mut marked_ranges) = marked_text_ranges_by(
13808        marked_string,
13809        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13810    );
13811
13812    let complete_from_position =
13813        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13814    let replace_range =
13815        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13816
13817    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13818        let completions = completions.clone();
13819        counter.fetch_add(1, atomic::Ordering::Release);
13820        async move {
13821            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13822            assert_eq!(
13823                params.text_document_position.position,
13824                complete_from_position
13825            );
13826            Ok(Some(lsp::CompletionResponse::Array(
13827                completions
13828                    .iter()
13829                    .map(|completion_text| lsp::CompletionItem {
13830                        label: completion_text.to_string(),
13831                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13832                            range: replace_range,
13833                            new_text: completion_text.to_string(),
13834                        })),
13835                        ..Default::default()
13836                    })
13837                    .collect(),
13838            )))
13839        }
13840    });
13841
13842    async move {
13843        request.next().await;
13844    }
13845}
13846
13847fn handle_resolve_completion_request(
13848    cx: &mut EditorLspTestContext,
13849    edits: Option<Vec<(&'static str, &'static str)>>,
13850) -> impl Future<Output = ()> {
13851    let edits = edits.map(|edits| {
13852        edits
13853            .iter()
13854            .map(|(marked_string, new_text)| {
13855                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13856                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13857                lsp::TextEdit::new(replace_range, new_text.to_string())
13858            })
13859            .collect::<Vec<_>>()
13860    });
13861
13862    let mut request =
13863        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13864            let edits = edits.clone();
13865            async move {
13866                Ok(lsp::CompletionItem {
13867                    additional_text_edits: edits,
13868                    ..Default::default()
13869                })
13870            }
13871        });
13872
13873    async move {
13874        request.next().await;
13875    }
13876}
13877
13878pub(crate) fn update_test_language_settings(
13879    cx: &mut TestAppContext,
13880    f: impl Fn(&mut AllLanguageSettingsContent),
13881) {
13882    cx.update(|cx| {
13883        SettingsStore::update_global(cx, |store, cx| {
13884            store.update_user_settings::<AllLanguageSettings>(cx, f);
13885        });
13886    });
13887}
13888
13889pub(crate) fn update_test_project_settings(
13890    cx: &mut TestAppContext,
13891    f: impl Fn(&mut ProjectSettings),
13892) {
13893    cx.update(|cx| {
13894        SettingsStore::update_global(cx, |store, cx| {
13895            store.update_user_settings::<ProjectSettings>(cx, f);
13896        });
13897    });
13898}
13899
13900pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13901    cx.update(|cx| {
13902        assets::Assets.load_test_fonts(cx);
13903        let store = SettingsStore::test(cx);
13904        cx.set_global(store);
13905        theme::init(theme::LoadThemes::JustBase, cx);
13906        release_channel::init(SemanticVersion::default(), cx);
13907        client::init_settings(cx);
13908        language::init(cx);
13909        Project::init_settings(cx);
13910        workspace::init_settings(cx);
13911        crate::init(cx);
13912    });
13913
13914    update_test_language_settings(cx, f);
13915}
13916
13917pub(crate) fn rust_lang() -> Arc<Language> {
13918    Arc::new(Language::new(
13919        LanguageConfig {
13920            name: "Rust".into(),
13921            matcher: LanguageMatcher {
13922                path_suffixes: vec!["rs".to_string()],
13923                ..Default::default()
13924            },
13925            ..Default::default()
13926        },
13927        Some(tree_sitter_rust::LANGUAGE.into()),
13928    ))
13929}
13930
13931#[track_caller]
13932fn assert_hunk_revert(
13933    not_reverted_text_with_selections: &str,
13934    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13935    expected_reverted_text_with_selections: &str,
13936    base_text: &str,
13937    cx: &mut EditorLspTestContext,
13938) {
13939    cx.set_state(not_reverted_text_with_selections);
13940    cx.update_editor(|editor, cx| {
13941        editor
13942            .buffer()
13943            .read(cx)
13944            .as_singleton()
13945            .unwrap()
13946            .update(cx, |buffer, cx| {
13947                buffer.set_diff_base(Some(base_text.into()), cx);
13948            });
13949    });
13950    cx.executor().run_until_parked();
13951
13952    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13953        let snapshot = editor.buffer().read(cx).snapshot(cx);
13954        let reverted_hunk_statuses = snapshot
13955            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13956            .map(|hunk| hunk_status(&hunk))
13957            .collect::<Vec<_>>();
13958
13959        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13960        reverted_hunk_statuses
13961    });
13962    cx.executor().run_until_parked();
13963    cx.assert_editor_state(expected_reverted_text_with_selections);
13964    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13965}