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