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