editor_tests.rs

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